diff --git a/extras/amalgamator/juce_AmalgamatorMain.cpp b/extras/amalgamator/juce_AmalgamatorMain.cpp new file mode 100644 index 0000000000..792218c831 --- /dev/null +++ b/extras/amalgamator/juce_AmalgamatorMain.cpp @@ -0,0 +1,340 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified 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. + + JUCE 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 JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +#include "juce_AppConfig.h" +#include "../../juce_amalgamated.h" + + +//============================================================================== +static bool matchesWildcard (const String& filename, const StringArray& wildcards) +{ + for (int i = wildcards.size(); --i >= 0;) + if (filename.matchesWildcard (wildcards[i], true)) + return true; + + return false; +} + +static bool canFileBeReincluded (const File& f) +{ + String content (f.loadFileAsString()); + + for (;;) + { + content = content.trimStart(); + + if (content.startsWith (T("//"))) + content = content.fromFirstOccurrenceOf (T("\n"), false, false); + else if (content.startsWith (T("/*"))) + content = content.fromFirstOccurrenceOf (T("*/"), false, false); + else + break; + } + + StringArray lines; + lines.addLines (content); + lines.trim(); + lines.removeEmptyStrings(); + + const String l1 (lines[0].removeCharacters (T(" \t")).trim()); + const String l2 (lines[1].removeCharacters (T(" \t")).trim()); + + if (l1.replace (T("#ifndef"), T("#define")) == l2) + return false; + + return true; +} + +//============================================================================== +static bool parseFile (const File& rootFolder, + const File& newTargetFile, + StringArray& dest, + const File& file, + StringArray& alreadyIncludedFiles, + const StringArray& includesToIgnore, + const StringArray& wildcards, + const bool isOuterFile, + const bool stripUnnecessaryStuff) +{ + printf ("reading: " + file.getFileName() + "\n"); + + if (! file.exists()) + { + printf ("!! ERROR - file doesn't exist!"); + return false; + } + + String content (file.loadFileAsString()); + + if (stripUnnecessaryStuff && ! isOuterFile) + { + if (content.startsWith (T("/*"))) + content = content.fromFirstOccurrenceOf (T("*/"), false, false).trimStart(); + + content = content.replace (T("\r\n\r\n\r\n"), T("\r\n\r\n")); + } + + StringArray lines; + lines.addLines (content); + while (lines[0].trim().isEmpty()) + lines.remove (0); + + for (int i = 0; i < lines.size(); ++i) + { + String line (lines[i]); + + if ((! isOuterFile) && line.contains (T("//================================================================"))) + line = String::empty; + + if (line.trimStart().startsWithChar (T('#')) + && line.removeCharacters (T(" \t")).startsWithIgnoreCase (T("#include\""))) + { + const int endOfInclude = line.indexOfChar (line.indexOfChar (T('\"')) + 1, T('\"')) + 1; + const String lineUpToEndOfInclude (line.substring (0, endOfInclude)); + const String lineAfterInclude (line.substring (endOfInclude)); + + const String filename (line.fromFirstOccurrenceOf (T("\""), false, false) + .upToLastOccurrenceOf (T("\""), false, false)); + const File targetFile (file.getSiblingFile (filename)); + + if (targetFile.exists() + && targetFile.isAChildOf (rootFolder)) + { + if (matchesWildcard (filename.replaceCharacter (T('\\'), T('/')), wildcards) + && ! includesToIgnore.contains (targetFile.getFileName())) + { + if (! alreadyIncludedFiles.contains (targetFile.getFullPathName())) + { + if (! canFileBeReincluded (targetFile)) + alreadyIncludedFiles.add (targetFile.getFullPathName()); + + dest.add (String::empty); + dest.add (T("/********* Start of inlined file: ") + + targetFile.getFileName() + + T(" *********/")); + + if (! parseFile (rootFolder, newTargetFile, + dest, targetFile, alreadyIncludedFiles, includesToIgnore, + wildcards, false, stripUnnecessaryStuff)) + { + return false; + } + + dest.add (T("/********* End of inlined file: ") + + targetFile.getFileName() + + T(" *********/")); + dest.add (String::empty); + + line = lineAfterInclude; + } + else + { + if (stripUnnecessaryStuff) + line = String::empty; + else + line = T("/* ") + lineUpToEndOfInclude + T(" */") + lineAfterInclude; + } + } + else + { + line = lineUpToEndOfInclude.upToFirstOccurrenceOf (T("\""), true, false) + + targetFile.getRelativePathFrom (newTargetFile.getParentDirectory()) + .replaceCharacter (T('\\'), T('/')) + + T("\"") + + lineAfterInclude; + } + } + } + + dest.add (line.trimEnd()); + } + + return true; +} + +//============================================================================== +static bool munge (const File& templateFile, const File& targetFile, const String& wildcard, + const bool stripUnnecessaryStuff, StringArray& alreadyIncludedFiles, + const StringArray& includesToIgnore) +{ + if (! templateFile.existsAsFile()) + { + printf (" The template file doesn't exist!\n\n"); + return false; + } + + StringArray lines, wildcards; + wildcards.addTokens (wildcard, T(";,"), T("'\"")); + wildcards.trim(); + wildcards.removeEmptyStrings(); + + if (! parseFile (targetFile.getParentDirectory(), + targetFile, + lines, templateFile, + alreadyIncludedFiles, + includesToIgnore, + wildcards, + true, stripUnnecessaryStuff)) + { + return false; + } + + //lines.trim(); + //lines.removeEmptyStrings(); + printf ("\nwriting: " + targetFile.getFullPathName() + "...\n\n"); + + for (int i = 0; i < lines.size() - 2; ++i) + { + if (lines[i].isEmpty() && lines[i + 1].isEmpty()) + { + lines.remove (i + 1); + --i; + } + } + + MemoryBlock newData, oldData; + const String newText (lines.joinIntoString (T("\n")) + T("\n")); + newData.append ((const char*) newText, (int) strlen ((const char*) newText)); + targetFile.loadFileAsData (oldData); + + if (oldData == newData) + { + printf ("(No need to write - new file is identical)\n\n"); + return true; + } + + if (! targetFile.replaceWithData (newData.getData(), newData.getSize())) + { + printf ("\n!! ERROR - couldn't write to the target file: " + targetFile.getFullPathName() + "\n\n"); + return false; + } + + return true; +} + +static void findAllFilesIncludedIn (const File& hppTemplate, StringArray& alreadyIncludedFiles) +{ + StringArray lines; + lines.addLines (hppTemplate.loadFileAsString()); + + for (int i = 0; i < lines.size(); ++i) + { + String line (lines[i]); + + if (line.removeCharacters (T(" \t")).startsWithIgnoreCase (T("#include\""))) + { + const String filename (line.fromFirstOccurrenceOf (T("\""), false, false) + .upToLastOccurrenceOf (T("\""), false, false)); + const File targetFile (hppTemplate.getSiblingFile (filename)); + + if (! alreadyIncludedFiles.contains (targetFile.getFullPathName())) + { + alreadyIncludedFiles.add (targetFile.getFullPathName()); + + if (targetFile.getFileName().containsIgnoreCase (T("juce_")) && targetFile.exists()) + findAllFilesIncludedIn (targetFile, alreadyIncludedFiles); + } + } + } +} + +//============================================================================== +static void mungeJuce (const File& juceFolder) +{ + if (! juceFolder.isDirectory()) + { + printf (" The folder supplied must be the root of your Juce directory!\n\n"); + return; + } + + const File hppTemplate (juceFolder.getChildFile (T("src/juce_amalgamated_template.h"))); + const File cppTemplate (juceFolder.getChildFile (T("src/juce_amalgamated_template.cpp"))); + + const File hppTarget (juceFolder.getChildFile (T("juce_amalgamated.h"))); + const File cppTarget (juceFolder.getChildFile (T("juce_amalgamated.cpp"))); + + StringArray alreadyIncludedFiles, includesToIgnore; + + if (! munge (hppTemplate, hppTarget, + "*.h", true, alreadyIncludedFiles, includesToIgnore)) + { + return; + } + + findAllFilesIncludedIn (hppTemplate, alreadyIncludedFiles); + includesToIgnore.add (hppTarget.getFileName()); + + munge (cppTemplate, cppTarget, + "*.cpp;*.c;*.h;*.mm;*.m", true, alreadyIncludedFiles, + includesToIgnore); +} + +//============================================================================== +int main (int argc, char* argv[]) +{ + // If you're running a command-line app, you need to initialise juce manually + // before calling any Juce functionality.. + initialiseJuce_NonGUI(); + + printf ("\n The C++ Amalgamator! Copyright 2008 by Julian Storer - www.rawmaterialsoftware.com\n\n"); + + if (argc == 4) + { + const File templateFile (File::getCurrentWorkingDirectory().getChildFile (argv[1])); + const File targetFile (File::getCurrentWorkingDirectory().getChildFile (argv[2])); + const String wildcard (String (argv[3]).unquoted()); + StringArray alreadyIncludedFiles, includesToIgnore; + + munge (templateFile, targetFile, wildcard, false, alreadyIncludedFiles, includesToIgnore); + } + else if (argc == 2) + { + const File juceFolder (File::getCurrentWorkingDirectory().getChildFile (argv[1])); + mungeJuce (juceFolder); + } + else + { + printf (" Usage: amalgamator TemplateFile TargetFile \"FileToReplaceWildcard\"\n\n"); + printf (" amalgamator will run through a C++ file and replace any\n" + " #include statements with the contents of the file they refer to.\n" + " It'll only do this for files that are within the same parent\n" + " directory as the target file, and will ignore include statements\n" + " that use '<>' instead of quotes. It'll also only include a file once,\n" + " ignoring any repeated instances of it.\n\n" + " The wildcard lets you specify what kind of files will be replaced, so\n" + " \"*.cpp;*.h\" would replace only includes that reference a .cpp or .h file.\n\n" + " Or: just run 'amalgamator YourJuceDirectory' to rebuild the juce files." + ); + } + + return 0; +} + diff --git a/extras/amalgamator/juce_AppConfig.h b/extras/amalgamator/juce_AppConfig.h new file mode 100644 index 0000000000..fa05cdb520 --- /dev/null +++ b/extras/amalgamator/juce_AppConfig.h @@ -0,0 +1,28 @@ + +/* + This file contains settings that you might want to explicitly apply to + the your build. + + Most of these are turned on or off by default, but you can override + that setting here by un-commenting it and giving it a 1 or 0 value. +*/ + +#define JUCE_ONLY_BUILD_CORE_LIBRARY 1 +//#define JUCE_FORCE_DEBUG 1 +//#define JUCE_LOG_ASSERTIONS 1 +//#define JUCE_ASIO 1 +//#define JUCE_ALSA 1 +//#define JUCE_QUICKTIME 1 +//#define JUCE_OPENGL 1 +//#define JUCE_USE_FLAC 1 +//#define JUCE_USE_OGGVORBIS 1 +//#define JUCE_USE_CDBURNER 1 +//#define JUCE_ENABLE_REPAINT_DEBUGGING 1 +//#define JUCE_USE_XINERAMA 1 +//#define JUCE_USE_XSHM 1 +//#define JUCE_PLUGINHOST_VST 1 +//#define JUCE_PLUGINHOST_AU 1 +//#define JUCE_BUILD_GUI_CLASSES 1 +//#define JUCE_CHECK_MEMORY_LEAKS 1 +//#define JUCE_CATCH_UNHANDLED_EXCEPTIONS 1 +//#define JUCE_STRINGS_ARE_UNICODE 1 diff --git a/extras/amalgamator/juce_LibrarySource.cpp b/extras/amalgamator/juce_LibrarySource.cpp new file mode 100644 index 0000000000..5567cbfd82 --- /dev/null +++ b/extras/amalgamator/juce_LibrarySource.cpp @@ -0,0 +1,12 @@ + +/* + This file includes the entire juce source tree via the amalgamated file. + + You could add the amalgamated file directly to your project, but doing it + like this allows you to put your app's config settings in the + juce_AppConfig.h file and have them applied to both the juce headers and + the source code. +*/ + +#include "juce_AppConfig.h" +#include "../../juce_amalgamated.cpp" diff --git a/extras/amalgamator/vc8/Amalgamator.sln b/extras/amalgamator/vc8/Amalgamator.sln new file mode 100644 index 0000000000..7d092650e9 --- /dev/null +++ b/extras/amalgamator/vc8/Amalgamator.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Amalgamator", "Amalgamator.vcproj", "{E616D10E-A3BF-4227-9512-186086D297A6}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E616D10E-A3BF-4227-9512-186086D297A6}.Debug|Win32.ActiveCfg = Debug|Win32 + {E616D10E-A3BF-4227-9512-186086D297A6}.Debug|Win32.Build.0 = Debug|Win32 + {E616D10E-A3BF-4227-9512-186086D297A6}.Release|Win32.ActiveCfg = Release|Win32 + {E616D10E-A3BF-4227-9512-186086D297A6}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/extras/amalgamator/vc8/Amalgamator.vcproj b/extras/amalgamator/vc8/Amalgamator.vcproj new file mode 100644 index 0000000000..c3bc7ebc8b --- /dev/null +++ b/extras/amalgamator/vc8/Amalgamator.vcproj @@ -0,0 +1,193 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp new file mode 100644 index 0000000000..1453c1c0f9 --- /dev/null +++ b/juce_amalgamated.cpp @@ -0,0 +1,259886 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified 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. + + JUCE 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 JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +/* + This monolithic file contains the entire Juce source tree! + + To build an app which uses Juce, all you need to do is to add this + file to your project, and include juce.h in your own cpp files. + +*/ + +//============================================================================== +#ifdef _WIN32 + +/********* Start of inlined file: win32_headers.h *********/ +#ifndef __WIN32_HEADERS_JUCEHEADER__ +#define __WIN32_HEADERS_JUCEHEADER__ + +#ifndef STRICT + #define STRICT 1 +#endif +#define WIN32_LEAN_AND_MEAN + +// don't want to get told about microsoft's mistakes.. +#ifdef _MSC_VER + #pragma warning (push) + #pragma warning (disable : 4100 4201) +#endif + +// use Platform SDK as win2000 unless this is disabled +#ifndef DISABLE_TRANSPARENT_WINDOWS + #define _WIN32_WINNT 0x0500 +#endif + +#define _UNICODE 1 +#define UNICODE 1 + +#include +#include +#include +#include +#include +#include + +#undef PACKED + +#ifdef _MSC_VER + #pragma warning (pop) +#endif + +#endif // __WIN32_HEADERS_JUCEHEADER__ +/********* End of inlined file: win32_headers.h *********/ + + #include + #include + #include + + #if JUCE_QUICKTIME + #include + #include + #include + #include + #include + #undef TARGET_OS_MAC // quicktime sets these, but they confuse some of the 3rd party libs + #undef MACOS + #endif + +#elif defined (LINUX) + +#else + #include + #include + #define __Point__ + #include + #include + #include + #include + #include + #include + #include + #include + + #include + #include + #include + #include +#endif + +//============================================================================== +#define DONT_SET_USING_JUCE_NAMESPACE 1 + +#include "juce_amalgamated.h" + +#define NO_DUMMY_DECL + +#if (defined(_MSC_VER) && (_MSC_VER <= 1200)) + #pragma warning (disable: 4309 4305) +#endif + +//============================================================================== + +/********* Start of inlined file: juce_FileLogger.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +FileLogger::FileLogger (const File& logFile_, + const String& welcomeMessage, + const int maxInitialFileSizeBytes) + : logFile (logFile_) +{ + if (maxInitialFileSizeBytes >= 0) + trimFileSize (maxInitialFileSizeBytes); + + if (! logFile_.exists()) + { + // do this so that the parent directories get created.. + logFile_.create(); + } + + logStream = logFile_.createOutputStream (256); + jassert (logStream != 0); + + String welcome; + welcome << "\r\n**********************************************************\r\n" + << welcomeMessage + << "\r\nLog started: " << Time::getCurrentTime().toString (true, true) + << "\r\n"; + + logMessage (welcome); +} + +FileLogger::~FileLogger() +{ + deleteAndZero (logStream); +} + +void FileLogger::logMessage (const String& message) +{ + if (logStream != 0) + { + Logger::outputDebugString (message); + + const ScopedLock sl (logLock); + (*logStream) << message << T("\r\n"); + logStream->flush(); + } +} + +void FileLogger::trimFileSize (int maxFileSizeBytes) const +{ + if (maxFileSizeBytes <= 0) + { + logFile.deleteFile(); + } + else + { + const int64 fileSize = logFile.getSize(); + + if (fileSize > maxFileSizeBytes) + { + FileInputStream* const in = logFile.createInputStream(); + jassert (in != 0); + + if (in != 0) + { + in->setPosition (fileSize - maxFileSizeBytes); + String content; + + { + MemoryBlock contentToSave; + contentToSave.setSize (maxFileSizeBytes + 4); + contentToSave.fillWith (0); + + in->read (contentToSave.getData(), maxFileSizeBytes); + delete in; + + content = contentToSave.toString(); + } + + int newStart = 0; + + while (newStart < fileSize + && content[newStart] != '\n' + && content[newStart] != '\r') + ++newStart; + + logFile.deleteFile(); + logFile.appendText (content.substring (newStart), false, false); + } + } + } +} + +FileLogger* FileLogger::createDefaultAppLogger (const String& logFileSubDirectoryName, + const String& logFileName, + const String& welcomeMessage, + const int maxInitialFileSizeBytes) +{ +#if JUCE_MAC + File logFile ("~/Library/Logs"); + logFile = logFile.getChildFile (logFileName); + +#else + File logFile (File::getSpecialLocation (File::userApplicationDataDirectory)); + + if (logFile.isDirectory()) + { + logFile = logFile.getChildFile (logFileSubDirectoryName) + .getChildFile (logFileName); + } +#endif + + return new FileLogger (logFile, welcomeMessage, maxInitialFileSizeBytes); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_FileLogger.cpp *********/ + +/********* Start of inlined file: juce_Logger.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +Logger::Logger() +{ +} + +Logger::~Logger() +{ +} + +static Logger* currentLogger = 0; + +void Logger::setCurrentLogger (Logger* const newLogger, + const bool deleteOldLogger) +{ + Logger* const oldLogger = currentLogger; + currentLogger = newLogger; + + if (deleteOldLogger && (oldLogger != 0)) + delete oldLogger; +} + +void Logger::writeToLog (const String& message) +{ + if (currentLogger != 0) + currentLogger->logMessage (message); + else + outputDebugString (message); +} + +#if JUCE_LOG_ASSERTIONS +void JUCE_API juce_LogAssertion (const char* filename, const int lineNum) throw() +{ + String m ("JUCE Assertion failure in "); + m << filename << ", line " << lineNum; + + Logger::writeToLog (m); +} +#endif + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_Logger.cpp *********/ + +/********* Start of inlined file: juce_Random.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +Random::Random (const int64 seedValue) throw() + : seed (seedValue) +{ +} + +Random::~Random() throw() +{ +} + +void Random::setSeed (const int64 newSeed) throw() +{ + seed = newSeed; +} + +int Random::nextInt() throw() +{ + seed = (seed * literal64bit (0x5deece66d) + 11) & literal64bit (0xffffffffffff); + + return (int) (seed >> 16); +} + +int Random::nextInt (const int maxValue) throw() +{ + jassert (maxValue > 0); + return (nextInt() & 0x7fffffff) % maxValue; +} + +int64 Random::nextInt64() throw() +{ + return (((int64) nextInt()) << 32) | (int64) (uint64) (uint32) nextInt(); +} + +bool Random::nextBool() throw() +{ + return (nextInt() & 0x80000000) != 0; +} + +float Random::nextFloat() throw() +{ + return ((uint32) nextInt()) / (float) 0xffffffff; +} + +double Random::nextDouble() throw() +{ + return ((uint32) nextInt()) / (double) 0xffffffff; +} + +static Random sysRand (1); + +Random& Random::getSystemRandom() throw() +{ + return sysRand; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_Random.cpp *********/ + +/********* Start of inlined file: juce_RelativeTime.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +RelativeTime::RelativeTime (const double seconds_) throw() + : seconds (seconds_) +{ +} + +RelativeTime::RelativeTime (const RelativeTime& other) throw() + : seconds (other.seconds) +{ +} + +RelativeTime::~RelativeTime() throw() +{ +} + +const RelativeTime RelativeTime::milliseconds (const int milliseconds) throw() +{ + return RelativeTime (milliseconds * 0.001); +} + +const RelativeTime RelativeTime::milliseconds (const int64 milliseconds) throw() +{ + return RelativeTime (milliseconds * 0.001); +} + +const RelativeTime RelativeTime::minutes (const double numberOfMinutes) throw() +{ + return RelativeTime (numberOfMinutes * 60.0); +} + +const RelativeTime RelativeTime::hours (const double numberOfHours) throw() +{ + return RelativeTime (numberOfHours * (60.0 * 60.0)); +} + +const RelativeTime RelativeTime::days (const double numberOfDays) throw() +{ + return RelativeTime (numberOfDays * (60.0 * 60.0 * 24.0)); +} + +const RelativeTime RelativeTime::weeks (const double numberOfWeeks) throw() +{ + return RelativeTime (numberOfWeeks * (60.0 * 60.0 * 24.0 * 7.0)); +} + +int64 RelativeTime::inMilliseconds() const throw() +{ + return (int64)(seconds * 1000.0); +} + +double RelativeTime::inMinutes() const throw() +{ + return seconds / 60.0; +} + +double RelativeTime::inHours() const throw() +{ + return seconds / (60.0 * 60.0); +} + +double RelativeTime::inDays() const throw() +{ + return seconds / (60.0 * 60.0 * 24.0); +} + +double RelativeTime::inWeeks() const throw() +{ + return seconds / (60.0 * 60.0 * 24.0 * 7.0); +} + +const String RelativeTime::getDescription (const String& returnValueForZeroTime) const throw() +{ + if (seconds < 0.001 && seconds > -0.001) + return returnValueForZeroTime; + + String result; + + if (seconds < 0) + result = T("-"); + + int fieldsShown = 0; + int n = abs ((int) inWeeks()); + if (n > 0) + { + result << n << ((n == 1) ? TRANS(" week ") + : TRANS(" weeks ")); + ++fieldsShown; + } + + n = abs ((int) inDays()) % 7; + if (n > 0) + { + result << n << ((n == 1) ? TRANS(" day ") + : TRANS(" days ")); + ++fieldsShown; + } + + if (fieldsShown < 2) + { + n = abs ((int) inHours()) % 24; + if (n > 0) + { + result << n << ((n == 1) ? TRANS(" hr ") + : TRANS(" hrs ")); + ++fieldsShown; + } + + if (fieldsShown < 2) + { + n = abs ((int) inMinutes()) % 60; + if (n > 0) + { + result << n << ((n == 1) ? TRANS(" min ") + : TRANS(" mins ")); + ++fieldsShown; + } + + if (fieldsShown < 2) + { + n = abs ((int) inSeconds()) % 60; + if (n > 0) + { + result << n << ((n == 1) ? TRANS(" sec ") + : TRANS(" secs ")); + ++fieldsShown; + } + + if (fieldsShown < 1) + { + n = abs ((int) inMilliseconds()) % 1000; + if (n > 0) + { + result << n << TRANS(" ms"); + ++fieldsShown; + } + } + } + } + } + + return result.trimEnd(); +} + +const RelativeTime& RelativeTime::operator= (const RelativeTime& other) throw() +{ + seconds = other.seconds; + return *this; +} + +bool RelativeTime::operator== (const RelativeTime& other) const throw() +{ + return seconds == other.seconds; +} + +bool RelativeTime::operator!= (const RelativeTime& other) const throw() +{ + return seconds != other.seconds; +} + +bool RelativeTime::operator> (const RelativeTime& other) const throw() +{ + return seconds > other.seconds; +} + +bool RelativeTime::operator< (const RelativeTime& other) const throw() +{ + return seconds < other.seconds; +} + +bool RelativeTime::operator>= (const RelativeTime& other) const throw() +{ + return seconds >= other.seconds; +} + +bool RelativeTime::operator<= (const RelativeTime& other) const throw() +{ + return seconds <= other.seconds; +} + +const RelativeTime RelativeTime::operator+ (const RelativeTime& timeToAdd) const throw() +{ + return RelativeTime (seconds + timeToAdd.seconds); +} + +const RelativeTime RelativeTime::operator- (const RelativeTime& timeToSubtract) const throw() +{ + return RelativeTime (seconds - timeToSubtract.seconds); +} + +const RelativeTime RelativeTime::operator+ (const double secondsToAdd) const throw() +{ + return RelativeTime (seconds + secondsToAdd); +} + +const RelativeTime RelativeTime::operator- (const double secondsToSubtract) const throw() +{ + return RelativeTime (seconds - secondsToSubtract); +} + +const RelativeTime& RelativeTime::operator+= (const RelativeTime& timeToAdd) throw() +{ + seconds += timeToAdd.seconds; + return *this; +} + +const RelativeTime& RelativeTime::operator-= (const RelativeTime& timeToSubtract) throw() +{ + seconds -= timeToSubtract.seconds; + return *this; +} + +const RelativeTime& RelativeTime::operator+= (const double secondsToAdd) throw() +{ + seconds += secondsToAdd; + return *this; +} + +const RelativeTime& RelativeTime::operator-= (const double secondsToSubtract) throw() +{ + seconds -= secondsToSubtract; + return *this; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_RelativeTime.cpp *********/ + +/********* Start of inlined file: juce_SystemStats.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +void juce_initialiseStrings(); + +const String SystemStats::getJUCEVersion() throw() +{ + return "JUCE v" + String (JUCE_MAJOR_VERSION) + "." + String (JUCE_MINOR_VERSION); +} + +static bool juceInitialisedNonGUI = false; + +void JUCE_PUBLIC_FUNCTION initialiseJuce_NonGUI() +{ + if (! juceInitialisedNonGUI) + { +#ifdef JUCE_DEBUG + // Some simple test code to keep an eye on things and make sure these functions + // work ok on all platforms. Let me know if any of these assertions fail! + int n = 1; + atomicIncrement (n); + jassert (atomicIncrementAndReturn (n) == 3); + atomicDecrement (n); + jassert (atomicDecrementAndReturn (n) == 1); + + jassert (swapByteOrder ((uint32) 0x11223344) == 0x44332211); + + // quick test to make sure the run-time lib doesn't crash on freeing a null-pointer. + SystemStats* nullPointer = 0; + juce_free (nullPointer); + delete[] nullPointer; + delete nullPointer; +#endif + // Now the real initialisation.. + + juceInitialisedNonGUI = true; + + DBG (SystemStats::getJUCEVersion()); + juce_initialiseStrings(); + SystemStats::initialiseStats(); + Random::getSystemRandom().setSeed (Time::currentTimeMillis()); + } +} + +#if JUCE_WIN32 + // This is imported from the sockets code.. + typedef int (__stdcall juce_CloseWin32SocketLibCall) (void); + extern juce_CloseWin32SocketLibCall* juce_CloseWin32SocketLib; +#endif + +#if JUCE_DEBUG + extern void juce_CheckForDanglingStreams(); +#endif + +void JUCE_PUBLIC_FUNCTION shutdownJuce_NonGUI() +{ + if (juceInitialisedNonGUI) + { +#if JUCE_WIN32 + // need to shut down sockets if they were used.. + if (juce_CloseWin32SocketLib != 0) + (*juce_CloseWin32SocketLib)(); +#endif + + LocalisedStrings::setCurrentMappings (0); + Thread::stopAllThreads (3000); + +#if JUCE_DEBUG + juce_CheckForDanglingStreams(); +#endif + + juceInitialisedNonGUI = false; + } +} + +#ifdef JUCE_DLL + +void* juce_Malloc (const int size) +{ + return malloc (size); +} + +void* juce_Calloc (const int size) +{ + return calloc (1, size); +} + +void* juce_Realloc (void* const block, const int size) +{ + return realloc (block, size); +} + +void juce_Free (void* const block) +{ + free (block); +} + +#if defined (JUCE_DEBUG) && JUCE_MSVC && JUCE_CHECK_MEMORY_LEAKS + +void* juce_DebugMalloc (const int size, const char* file, const int line) +{ + return _malloc_dbg (size, _NORMAL_BLOCK, file, line); +} + +void* juce_DebugCalloc (const int size, const char* file, const int line) +{ + return _calloc_dbg (1, size, _NORMAL_BLOCK, file, line); +} + +void* juce_DebugRealloc (void* const block, const int size, const char* file, const int line) +{ + return _realloc_dbg (block, size, _NORMAL_BLOCK, file, line); +} + +void juce_DebugFree (void* const block) +{ + _free_dbg (block, _NORMAL_BLOCK); +} + +#endif +#endif + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_SystemStats.cpp *********/ + +/********* Start of inlined file: juce_Time.cpp *********/ +#ifdef _MSC_VER + #pragma warning (disable: 4514) + #pragma warning (push) +#endif + +#ifndef JUCE_WIN32 + #include +#else + #include +#endif + +#include + +BEGIN_JUCE_NAMESPACE + +#ifdef _MSC_VER + #pragma warning (pop) + + #ifdef _INC_TIME_INL + #define USE_NEW_SECURE_TIME_FNS + #endif +#endif + +static void millisToLocal (const int64 millis, struct tm& result) throw() +{ + const int64 seconds = millis / 1000; + + if (seconds < literal64bit (86400) || seconds >= literal64bit (2145916800)) + { + // use extended maths for dates beyond 1970 to 2037.. + const int timeZoneAdjustment = 31536000 - (int) (Time (1971, 0, 1, 0, 0).toMilliseconds() / 1000); + const int64 jdm = seconds + timeZoneAdjustment + literal64bit (210866803200); + + const int days = (int) (jdm / literal64bit (86400)); + const int a = 32044 + days; + const int b = (4 * a + 3) / 146097; + const int c = a - (b * 146097) / 4; + const int d = (4 * c + 3) / 1461; + const int e = c - (d * 1461) / 4; + const int m = (5 * e + 2) / 153; + + result.tm_mday = e - (153 * m + 2) / 5 + 1; + result.tm_mon = m + 2 - 12 * (m / 10); + result.tm_year = b * 100 + d - 6700 + (m / 10); + result.tm_wday = (days + 1) % 7; + result.tm_yday = -1; + + int t = (int) (jdm % literal64bit (86400)); + result.tm_hour = t / 3600; + t %= 3600; + result.tm_min = t / 60; + result.tm_sec = t % 60; + result.tm_isdst = -1; + } + else + { + time_t now = (time_t) (seconds); + +#if JUCE_WIN32 + #ifdef USE_NEW_SECURE_TIME_FNS + if (now >= 0 && now <= 0x793406fff) + localtime_s (&result, &now); + else + zeromem (&result, sizeof (result)); + #else + result = *localtime (&now); + #endif +#else + // more thread-safe + localtime_r (&now, &result); +#endif + } +} + +Time::Time() throw() + : millisSinceEpoch (0) +{ +} + +Time::Time (const Time& other) throw() + : millisSinceEpoch (other.millisSinceEpoch) +{ +} + +Time::Time (const int64 ms) throw() + : millisSinceEpoch (ms) +{ +} + +Time::Time (const int year, + const int month, + const int day, + const int hours, + const int minutes, + const int seconds, + const int milliseconds) throw() +{ + jassert (year > 100); // year must be a 4-digit version + + if (year < 1971 || year >= 2038) + { + // use extended maths for dates beyond 1970 to 2037.. + const int timeZoneAdjustment = 31536000 - (int) (Time (1971, 0, 1, 0, 0).toMilliseconds() / 1000); + const int a = (13 - month) / 12; + const int y = year + 4800 - a; + const int jd = day + (153 * (month + 12 * a - 2) + 2) / 5 + + (y * 365) + (y / 4) - (y / 100) + (y / 400) + - 32045; + + const int64 s = ((int64) jd) * literal64bit (86400) - literal64bit (210866803200); + + millisSinceEpoch = 1000 * (s + (hours * 3600 + minutes * 60 + seconds - timeZoneAdjustment)) + + milliseconds; + } + else + { + struct tm t; + t.tm_year = year - 1900; + t.tm_mon = month; + t.tm_mday = day; + t.tm_hour = hours; + t.tm_min = minutes; + t.tm_sec = seconds; + t.tm_isdst = -1; + + millisSinceEpoch = 1000 * (int64) mktime (&t); + + if (millisSinceEpoch < 0) + millisSinceEpoch = 0; + else + millisSinceEpoch += milliseconds; + } +} + +Time::~Time() throw() +{ +} + +const Time& Time::operator= (const Time& other) throw() +{ + millisSinceEpoch = other.millisSinceEpoch; + return *this; +} + +int64 Time::currentTimeMillis() throw() +{ + static uint32 lastCounterResult = 0xffffffff; + static int64 correction = 0; + + const uint32 now = getMillisecondCounter(); + + // check the counter hasn't wrapped (also triggered the first time this function is called) + if (now < lastCounterResult) + { + // double-check it's actually wrapped, in case multi-cpu machines have timers that drift a bit. + if (lastCounterResult == 0xffffffff || now < lastCounterResult - 10) + { + // get the time once using normal library calls, and store the difference needed to + // turn the millisecond counter into a real time. +#if JUCE_WIN32 + struct _timeb t; + #ifdef USE_NEW_SECURE_TIME_FNS + _ftime_s (&t); + #else + _ftime (&t); + #endif + correction = (((int64) t.time) * 1000 + t.millitm) - now; +#else + struct timeval tv; + struct timezone tz; + gettimeofday (&tv, &tz); + correction = (((int64) tv.tv_sec) * 1000 + tv.tv_usec / 1000) - now; +#endif + } + } + + lastCounterResult = now; + + return correction + now; +} + +uint32 juce_millisecondsSinceStartup() throw(); +static uint32 lastMSCounterValue = 0; + +uint32 Time::getMillisecondCounter() throw() +{ + const uint32 now = juce_millisecondsSinceStartup(); + + if (now < lastMSCounterValue) + { + // in multi-threaded apps this might be called concurrently, so + // make sure that our last counter value only increases and doesn't + // go backwards.. + if (now < lastMSCounterValue - 1000) + lastMSCounterValue = now; + } + else + { + lastMSCounterValue = now; + } + + return now; +} + +uint32 Time::getApproximateMillisecondCounter() throw() +{ + jassert (lastMSCounterValue != 0); + return lastMSCounterValue; +} + +void Time::waitForMillisecondCounter (const uint32 targetTime) throw() +{ + for (;;) + { + const uint32 now = getMillisecondCounter(); + + if (now >= targetTime) + break; + + const int toWait = targetTime - now; + + if (toWait > 2) + { + Thread::sleep (jmin (20, toWait >> 1)); + } + else + { + // xxx should consider using mutex_pause on the mac as it apparently + // makes it seem less like a spinlock and avoids lowering the thread pri. + for (int i = 10; --i >= 0;) + Thread::yield(); + } + } +} + +double Time::highResolutionTicksToSeconds (const int64 ticks) throw() +{ + return ticks / (double) getHighResolutionTicksPerSecond(); +} + +int64 Time::secondsToHighResolutionTicks (const double seconds) throw() +{ + return (int64) (seconds * (double) getHighResolutionTicksPerSecond()); +} + +const Time JUCE_CALLTYPE Time::getCurrentTime() throw() +{ + return Time (currentTimeMillis()); +} + +const String Time::toString (const bool includeDate, + const bool includeTime, + const bool includeSeconds, + const bool use24HourClock) const throw() +{ + String result; + + if (includeDate) + { + result << getDayOfMonth() << ' ' + << getMonthName (true) << ' ' + << getYear(); + + if (includeTime) + result << ' '; + } + + if (includeTime) + { + if (includeSeconds) + { + result += String::formatted (T("%d:%02d:%02d "), + (use24HourClock) ? getHours() + : getHoursInAmPmFormat(), + getMinutes(), + getSeconds()); + } + else + { + result += String::formatted (T("%d.%02d"), + (use24HourClock) ? getHours() + : getHoursInAmPmFormat(), + getMinutes()); + } + + if (! use24HourClock) + result << (isAfternoon() ? "pm" : "am"); + } + + return result.trimEnd(); +} + +const String Time::formatted (const tchar* const format) const throw() +{ + tchar buffer[80]; + + struct tm t; + millisToLocal (millisSinceEpoch, t); + + if (CharacterFunctions::ftime (buffer, 79, format, &t) <= 0) + { + int bufferSize = 128; + + for (;;) + { + MemoryBlock mb (bufferSize * sizeof (tchar)); + tchar* const b = (tchar*) mb.getData(); + + if (CharacterFunctions::ftime (b, bufferSize, format, &t) > 0) + return String (b); + + bufferSize += 128; + } + } + + return String (buffer); +} + +int Time::getYear() const throw() +{ + struct tm t; + millisToLocal (millisSinceEpoch, t); + return t.tm_year + 1900; +} + +int Time::getMonth() const throw() +{ + struct tm t; + millisToLocal (millisSinceEpoch, t); + return t.tm_mon; +} + +int Time::getDayOfMonth() const throw() +{ + struct tm t; + millisToLocal (millisSinceEpoch, t); + return t.tm_mday; +} + +int Time::getDayOfWeek() const throw() +{ + struct tm t; + millisToLocal (millisSinceEpoch, t); + return t.tm_wday; +} + +int Time::getHours() const throw() +{ + struct tm t; + millisToLocal (millisSinceEpoch, t); + return t.tm_hour; +} + +int Time::getHoursInAmPmFormat() const throw() +{ + const int hours = getHours(); + + if (hours == 0) + return 12; + else if (hours <= 12) + return hours; + else + return hours - 12; +} + +bool Time::isAfternoon() const throw() +{ + return getHours() >= 12; +} + +static int extendedModulo (const int64 value, const int modulo) throw() +{ + return (int) (value >= 0 ? (value % modulo) + : (value - ((value / modulo) + 1) * modulo)); +} + +int Time::getMinutes() const throw() +{ + return extendedModulo (millisSinceEpoch / 60000, 60); +} + +int Time::getSeconds() const throw() +{ + return extendedModulo (millisSinceEpoch / 1000, 60); +} + +int Time::getMilliseconds() const throw() +{ + return extendedModulo (millisSinceEpoch, 1000); +} + +bool Time::isDaylightSavingTime() const throw() +{ + struct tm t; + millisToLocal (millisSinceEpoch, t); + return t.tm_isdst != 0; +} + +const String Time::getTimeZone() const throw() +{ + String zone[2]; + +#if JUCE_WIN32 + _tzset(); + + #ifdef USE_NEW_SECURE_TIME_FNS + { + char name [128]; + size_t length; + + for (int i = 0; i < 2; ++i) + { + zeromem (name, sizeof (name)); + _get_tzname (&length, name, 127, i); + zone[i] = name; + } + } + #else + const char** const zonePtr = (const char**) _tzname; + zone[0] = zonePtr[0]; + zone[1] = zonePtr[1]; + #endif +#else + tzset(); + const char** const zonePtr = (const char**) tzname; + zone[0] = zonePtr[0]; + zone[1] = zonePtr[1]; +#endif + + if (isDaylightSavingTime()) + { + zone[0] = zone[1]; + + if (zone[0].length() > 3 + && zone[0].containsIgnoreCase (T("daylight")) + && zone[0].contains (T("GMT"))) + zone[0] = "BST"; + } + + return zone[0].substring (0, 3); +} + +const String Time::getMonthName (const bool threeLetterVersion) const throw() +{ + return getMonthName (getMonth(), threeLetterVersion); +} + +const String Time::getWeekdayName (const bool threeLetterVersion) const throw() +{ + return getWeekdayName (getDayOfWeek(), threeLetterVersion); +} + +const String Time::getMonthName (int monthNumber, + const bool threeLetterVersion) throw() +{ + const char* const shortMonthNames[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + const char* const longMonthNames[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; + + monthNumber %= 12; + + return TRANS (threeLetterVersion ? shortMonthNames [monthNumber] + : longMonthNames [monthNumber]); +} + +const String Time::getWeekdayName (int day, + const bool threeLetterVersion) throw() +{ + const char* const shortDayNames[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; + const char* const longDayNames[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; + + day %= 7; + + return TRANS (threeLetterVersion ? shortDayNames [day] + : longDayNames [day]); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_Time.cpp *********/ + +/********* Start of inlined file: juce_BitArray.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +BitArray::BitArray() throw() + : numValues (4), + highestBit (-1), + negative (false) +{ + values = (unsigned int*) juce_calloc (sizeof (unsigned int) * (numValues + 1)); +} + +BitArray::BitArray (const int value) throw() + : numValues (4), + highestBit (31), + negative (value < 0) +{ + values = (unsigned int*) juce_calloc (sizeof (unsigned int) * (numValues + 1)); + values[0] = abs (value); + highestBit = getHighestBit(); +} + +BitArray::BitArray (int64 value) throw() + : numValues (4), + highestBit (63), + negative (value < 0) +{ + values = (unsigned int*) juce_calloc (sizeof (unsigned int) * (numValues + 1)); + + if (value < 0) + value = -value; + + values[0] = (unsigned int) value; + values[1] = (unsigned int) (value >> 32); + highestBit = getHighestBit(); +} + +BitArray::BitArray (const unsigned int value) throw() + : numValues (4), + highestBit (31), + negative (false) +{ + values = (unsigned int*) juce_calloc (sizeof (unsigned int) * (numValues + 1)); + values[0] = value; + highestBit = getHighestBit(); +} + +BitArray::BitArray (const BitArray& other) throw() + : numValues (jmax (4, (other.highestBit >> 5) + 1)), + highestBit (other.getHighestBit()), + negative (other.negative) +{ + const int bytes = sizeof (unsigned int) * (numValues + 1); + values = (unsigned int*) juce_malloc (bytes); + memcpy (values, other.values, bytes); +} + +BitArray::~BitArray() throw() +{ + juce_free (values); +} + +const BitArray& BitArray::operator= (const BitArray& other) throw() +{ + if (this != &other) + { + juce_free (values); + + highestBit = other.getHighestBit(); + numValues = jmax (4, (highestBit >> 5) + 1); + negative = other.negative; + const int memSize = sizeof (unsigned int) * (numValues + 1); + values = (unsigned int*)juce_malloc (memSize); + memcpy (values, other.values, memSize); + } + + return *this; +} + +// result == 0 = the same +// result < 0 = this number is smaller +// result > 0 = this number is bigger +int BitArray::compare (const BitArray& other) const throw() +{ + if (isNegative() == other.isNegative()) + { + const int absComp = compareAbsolute (other); + return isNegative() ? -absComp : absComp; + } + else + { + return isNegative() ? -1 : 1; + } +} + +int BitArray::compareAbsolute (const BitArray& other) const throw() +{ + const int h1 = getHighestBit(); + const int h2 = other.getHighestBit(); + + if (h1 > h2) + return 1; + else if (h1 < h2) + return -1; + + for (int i = (h1 >> 5) + 1; --i >= 0;) + if (values[i] != other.values[i]) + return (values[i] > other.values[i]) ? 1 : -1; + + return 0; +} + +bool BitArray::operator== (const BitArray& other) const throw() +{ + return compare (other) == 0; +} + +bool BitArray::operator!= (const BitArray& other) const throw() +{ + return compare (other) != 0; +} + +bool BitArray::operator[] (const int bit) const throw() +{ + return bit >= 0 && bit <= highestBit + && ((values [bit >> 5] & (1 << (bit & 31))) != 0); +} + +bool BitArray::isEmpty() const throw() +{ + return getHighestBit() < 0; +} + +void BitArray::clear() throw() +{ + if (numValues > 16) + { + juce_free (values); + numValues = 4; + values = (unsigned int*) juce_calloc (sizeof (unsigned int) * (numValues + 1)); + } + else + { + zeromem (values, sizeof (unsigned int) * (numValues + 1)); + } + + highestBit = -1; + negative = false; +} + +void BitArray::setBit (const int bit) throw() +{ + if (bit >= 0) + { + if (bit > highestBit) + { + ensureSize (bit >> 5); + highestBit = bit; + } + + values [bit >> 5] |= (1 << (bit & 31)); + } +} + +void BitArray::setBit (const int bit, + const bool shouldBeSet) throw() +{ + if (shouldBeSet) + setBit (bit); + else + clearBit (bit); +} + +void BitArray::clearBit (const int bit) throw() +{ + if (bit >= 0 && bit <= highestBit) + values [bit >> 5] &= ~(1 << (bit & 31)); +} + +void BitArray::setRange (int startBit, + int numBits, + const bool shouldBeSet) throw() +{ + while (--numBits >= 0) + setBit (startBit++, shouldBeSet); +} + +void BitArray::insertBit (const int bit, + const bool shouldBeSet) throw() +{ + if (bit >= 0) + shiftBits (1, bit); + + setBit (bit, shouldBeSet); +} + +void BitArray::andWith (const BitArray& other) throw() +{ + // this operation will only work with the absolute values + jassert (isNegative() == other.isNegative()); + + int n = numValues; + + while (n > other.numValues) + values[--n] = 0; + + while (--n >= 0) + values[n] &= other.values[n]; + + if (other.highestBit < highestBit) + highestBit = other.highestBit; + + highestBit = getHighestBit(); +} + +void BitArray::orWith (const BitArray& other) throw() +{ + if (other.highestBit < 0) + return; + + // this operation will only work with the absolute values + jassert (isNegative() == other.isNegative()); + + ensureSize (other.highestBit >> 5); + + int n = (other.highestBit >> 5) + 1; + + while (--n >= 0) + values[n] |= other.values[n]; + + if (other.highestBit > highestBit) + highestBit = other.highestBit; + + highestBit = getHighestBit(); +} + +void BitArray::xorWith (const BitArray& other) throw() +{ + if (other.highestBit < 0) + return; + + // this operation will only work with the absolute values + jassert (isNegative() == other.isNegative()); + + ensureSize (other.highestBit >> 5); + + int n = (other.highestBit >> 5) + 1; + + while (--n >= 0) + values[n] ^= other.values[n]; + + if (other.highestBit > highestBit) + highestBit = other.highestBit; + + highestBit = getHighestBit(); +} + +void BitArray::add (const BitArray& other) throw() +{ + if (other.isNegative()) + { + BitArray o (other); + o.negate(); + subtract (o); + return; + } + + if (isNegative()) + { + if (compareAbsolute (other) < 0) + { + BitArray temp (*this); + temp.negate(); + *this = other; + subtract (temp); + } + else + { + negate(); + subtract (other); + negate(); + } + + return; + } + + if (other.highestBit > highestBit) + highestBit = other.highestBit; + + ++highestBit; + + const int numInts = (highestBit >> 5) + 1; + ensureSize (numInts); + + int64 remainder = 0; + + for (int i = 0; i <= numInts; ++i) + { + if (i < numValues) + remainder += values[i]; + + if (i < other.numValues) + remainder += other.values[i]; + + values[i] = (unsigned int) remainder; + remainder >>= 32; + } + + jassert (remainder == 0); + highestBit = getHighestBit(); +} + +void BitArray::subtract (const BitArray& other) throw() +{ + if (other.isNegative()) + { + BitArray o (other); + o.negate(); + add (o); + return; + } + + if (! isNegative()) + { + if (compareAbsolute (other) < 0) + { + BitArray temp (*this); + *this = other; + subtract (temp); + negate(); + return; + } + } + else + { + negate(); + add (other); + negate(); + return; + } + + const int numInts = (highestBit >> 5) + 1; + const int maxOtherInts = (other.highestBit >> 5) + 1; + int64 amountToSubtract = 0; + + for (int i = 0; i <= numInts; ++i) + { + if (i <= maxOtherInts) + amountToSubtract += (int64)other.values[i]; + + if (values[i] >= amountToSubtract) + { + values[i] = (unsigned int) (values[i] - amountToSubtract); + amountToSubtract = 0; + } + else + { + const int64 n = ((int64) values[i] + (((int64) 1) << 32)) - amountToSubtract; + values[i] = (unsigned int) n; + amountToSubtract = 1; + } + } +} + +void BitArray::multiplyBy (const BitArray& other) throw() +{ + BitArray total; + highestBit = getHighestBit(); + const bool wasNegative = isNegative(); + setNegative (false); + + for (int i = 0; i <= highestBit; ++i) + { + if (operator[](i)) + { + BitArray n (other); + n.setNegative (false); + n.shiftBits (i); + total.add (n); + } + } + + *this = total; + negative = wasNegative ^ other.isNegative(); +} + +void BitArray::divideBy (const BitArray& divisor, BitArray& remainder) throw() +{ + jassert (this != &remainder); // (can't handle passing itself in to get the remainder) + + const int divHB = divisor.getHighestBit(); + const int ourHB = getHighestBit(); + + if (divHB < 0 || ourHB < 0) + { + // division by zero + remainder.clear(); + clear(); + } + else + { + remainder = *this; + remainder.setNegative (false); + const bool wasNegative = isNegative(); + clear(); + + BitArray temp (divisor); + temp.setNegative (false); + + int leftShift = ourHB - divHB; + temp.shiftBits (leftShift); + + while (leftShift >= 0) + { + if (remainder.compareAbsolute (temp) >= 0) + { + remainder.subtract (temp); + setBit (leftShift); + } + + if (--leftShift >= 0) + temp.shiftBits (-1); + } + + negative = wasNegative ^ divisor.isNegative(); + remainder.setNegative (wasNegative); + } +} + +void BitArray::modulo (const BitArray& divisor) throw() +{ + BitArray remainder; + divideBy (divisor, remainder); + *this = remainder; +} + +static const BitArray simpleGCD (BitArray* m, BitArray* n) throw() +{ + while (! m->isEmpty()) + { + if (n->compareAbsolute (*m) > 0) + swapVariables (m, n); + + m->subtract (*n); + } + + return *n; +} + +const BitArray BitArray::findGreatestCommonDivisor (BitArray n) const throw() +{ + BitArray m (*this); + + while (! n.isEmpty()) + { + if (abs (m.getHighestBit() - n.getHighestBit()) <= 16) + return simpleGCD (&m, &n); + + BitArray temp1 (m), temp2; + temp1.divideBy (n, temp2); + + m = n; + n = temp2; + } + + return m; +} + +void BitArray::exponentModulo (const BitArray& exponent, + const BitArray& modulus) throw() +{ + BitArray exp (exponent); + exp.modulo (modulus); + + BitArray value (*this); + value.modulo (modulus); + + clear(); + setBit (0); + + while (! exp.isEmpty()) + { + if (exp [0]) + { + multiplyBy (value); + this->modulo (modulus); + } + + value.multiplyBy (value); + value.modulo (modulus); + + exp.shiftBits (-1); + } +} + +void BitArray::inverseModulo (const BitArray& modulus) throw() +{ + const BitArray one (1); + + if (modulus == one || modulus.isNegative()) + { + clear(); + return; + } + + if (isNegative() || compareAbsolute (modulus) >= 0) + this->modulo (modulus); + + if (*this == one) + return; + + if (! (*this)[0]) + { + // not invertible + clear(); + return; + } + + BitArray a1 (modulus); + BitArray a2 (*this); + BitArray b1 (modulus); + BitArray b2 (1); + + while (a2 != one) + { + BitArray temp1, temp2, multiplier (a1); + multiplier.divideBy (a2, temp1); + + temp1 = a2; + temp1.multiplyBy (multiplier); + temp2 = a1; + temp2.subtract (temp1); + a1 = a2; + a2 = temp2; + + temp1 = b2; + temp1.multiplyBy (multiplier); + temp2 = b1; + temp2.subtract (temp1); + b1 = b2; + b2 = temp2; + } + + while (b2.isNegative()) + b2.add (modulus); + + b2.modulo (modulus); + *this = b2; +} + +void BitArray::shiftBits (int bits, const int startBit) throw() +{ + if (highestBit < 0) + return; + + if (startBit > 0) + { + if (bits < 0) + { + // right shift + for (int i = startBit; i <= highestBit; ++i) + setBit (i, operator[] (i - bits)); + + highestBit = getHighestBit(); + } + else if (bits > 0) + { + // left shift + for (int i = highestBit + 1; --i >= startBit;) + setBit (i + bits, operator[] (i)); + + while (--bits >= 0) + clearBit (bits + startBit); + } + } + else + { + if (bits < 0) + { + // right shift + bits = -bits; + + if (bits > highestBit) + { + clear(); + } + else + { + const int wordsToMove = bits >> 5; + int top = 1 + (highestBit >> 5) - wordsToMove; + highestBit -= bits; + + if (wordsToMove > 0) + { + int i; + for (i = 0; i < top; ++i) + values [i] = values [i + wordsToMove]; + + for (i = 0; i < wordsToMove; ++i) + values [top + i] = 0; + + bits &= 31; + } + + if (bits != 0) + { + const int invBits = 32 - bits; + + --top; + for (int i = 0; i < top; ++i) + values[i] = (values[i] >> bits) | (values [i + 1] << invBits); + + values[top] = (values[top] >> bits); + } + + highestBit = getHighestBit(); + } + } + else if (bits > 0) + { + // left shift + ensureSize (((highestBit + bits) >> 5) + 1); + + const int wordsToMove = bits >> 5; + int top = 1 + (highestBit >> 5); + highestBit += bits; + + if (wordsToMove > 0) + { + int i; + for (i = top; --i >= 0;) + values [i + wordsToMove] = values [i]; + + for (i = 0; i < wordsToMove; ++i) + values [i] = 0; + + bits &= 31; + } + + if (bits != 0) + { + const int invBits = 32 - bits; + + for (int i = top + 1 + wordsToMove; --i > wordsToMove;) + values[i] = (values[i] << bits) | (values [i - 1] >> invBits); + + values [wordsToMove] = values [wordsToMove] << bits; + } + + highestBit = getHighestBit(); + } + } +} + +int BitArray::getBitRangeAsInt (const int startBit, int numBits) const throw() +{ + if (numBits > 32) + { + jassertfalse + numBits = 32; + } + + if (startBit == 0) + { + if (numBits < 32) + return values[0] & ((1 << numBits) - 1); + + return values[0]; + } + + int n = 0; + for (int i = numBits; --i >= 0;) + { + n <<= 1; + + if (operator[] (startBit + i)) + n |= 1; + } + + return n; +} + +void BitArray::setBitRangeAsInt (const int startBit, int numBits, unsigned int valueToSet) throw() +{ + if (numBits > 32) + { + jassertfalse + numBits = 32; + } + + for (int i = 0; i < numBits; ++i) + { + setBit (startBit + i, (valueToSet & 1) != 0); + valueToSet >>= 1; + } +} + +void BitArray::fillBitsRandomly (int startBit, int numBits) throw() +{ + highestBit = jmax (highestBit, startBit + numBits); + ensureSize (((startBit + numBits) >> 5) + 1); + + while ((startBit & 31) != 0 && numBits > 0) + { + setBit (startBit++, Random::getSystemRandom().nextBool()); + + --numBits; + } + + while (numBits >= 32) + { + values [startBit >> 5] = (unsigned int) Random::getSystemRandom().nextInt(); + + startBit += 32; + numBits -= 32; + } + + while (--numBits >= 0) + { + setBit (startBit + numBits, Random::getSystemRandom().nextBool()); + } + + highestBit = getHighestBit(); +} + +void BitArray::createRandomNumber (const BitArray& maximumValue) throw() +{ + clear(); + + do + { + fillBitsRandomly (0, maximumValue.getHighestBit() + 1); + } + while (compare (maximumValue) >= 0); +} + +bool BitArray::isNegative() const throw() +{ + return negative && ! isEmpty(); +} + +void BitArray::setNegative (const bool neg) throw() +{ + negative = neg; +} + +void BitArray::negate() throw() +{ + negative = (! negative) && ! isEmpty(); +} + +int BitArray::countNumberOfSetBits() const throw() +{ + int total = 0; + + for (int i = (highestBit >> 5) + 1; --i >= 0;) + { + unsigned int n = values[i]; + + if (n == 0xffffffff) + { + total += 32; + } + else + { + while (n != 0) + { + total += (n & 1); + n >>= 1; + } + } + } + + return total; +} + +int BitArray::getHighestBit() const throw() +{ + for (int i = highestBit + 1; --i >= 0;) + if ((values [i >> 5] & (1 << (i & 31))) != 0) + return i; + + return -1; +} + +int BitArray::findNextSetBit (int i) const throw() +{ + for (; i <= highestBit; ++i) + if ((values [i >> 5] & (1 << (i & 31))) != 0) + return i; + + return -1; +} + +int BitArray::findNextClearBit (int i) const throw() +{ + for (; i <= highestBit; ++i) + if ((values [i >> 5] & (1 << (i & 31))) == 0) + break; + + return i; +} + +void BitArray::ensureSize (const int numVals) throw() +{ + if (numVals + 2 >= numValues) + { + int oldSize = numValues; + numValues = ((numVals + 2) * 3) / 2; + values = (unsigned int*) juce_realloc (values, sizeof (unsigned int) * numValues + 4); + + while (oldSize < numValues) + values [oldSize++] = 0; + } +} + +const String BitArray::toString (const int base) const throw() +{ + String s; + BitArray v (*this); + + if (base == 2 || base == 8 || base == 16) + { + const int bits = (base == 2) ? 1 : (base == 8 ? 3 : 4); + static const tchar* const hexDigits = T("0123456789abcdef"); + + for (;;) + { + const int remainder = v.getBitRangeAsInt (0, bits); + + v.shiftBits (-bits); + + if (remainder == 0 && v.isEmpty()) + break; + + s = String::charToString (hexDigits [remainder]) + s; + } + } + else if (base == 10) + { + const BitArray ten (10); + BitArray remainder; + + for (;;) + { + v.divideBy (ten, remainder); + + if (remainder.isEmpty() && v.isEmpty()) + break; + + s = String (remainder.getBitRangeAsInt (0, 8)) + s; + } + } + else + { + jassertfalse // can't do the specified base + return String::empty; + } + + if (s.isEmpty()) + return T("0"); + + return isNegative() ? T("-") + s : s; +} + +void BitArray::parseString (const String& text, + const int base) throw() +{ + clear(); + const tchar* t = (const tchar*) text; + + if (base == 2 || base == 8 || base == 16) + { + const int bits = (base == 2) ? 1 : (base == 8 ? 3 : 4); + + for (;;) + { + const tchar c = *t++; + const int digit = CharacterFunctions::getHexDigitValue (c); + + if (((unsigned int) digit) < (unsigned int) base) + { + shiftBits (bits); + add (digit); + } + else if (c == 0) + { + break; + } + } + } + else if (base == 10) + { + const BitArray ten ((unsigned int) 10); + + for (;;) + { + const tchar c = *t++; + + if (c >= T('0') && c <= T('9')) + { + multiplyBy (ten); + add ((int) (c - T('0'))); + } + else if (c == 0) + { + break; + } + } + } + + setNegative (text.trimStart().startsWithChar (T('-'))); +} + +const MemoryBlock BitArray::toMemoryBlock() const throw() +{ + const int numBytes = (getHighestBit() + 7) >> 3; + MemoryBlock mb (numBytes); + + for (int i = 0; i < numBytes; ++i) + mb[i] = (uint8) getBitRangeAsInt (i << 3, 8); + + return mb; +} + +void BitArray::loadFromMemoryBlock (const MemoryBlock& data) throw() +{ + clear(); + + for (int i = data.getSize(); --i >= 0;) + this->setBitRangeAsInt (i << 3, 8, data [i]); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_BitArray.cpp *********/ + +/********* Start of inlined file: juce_MemoryBlock.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +MemoryBlock::MemoryBlock() throw() + : data (0), + size (0) +{ +} + +MemoryBlock::MemoryBlock (const int initialSize, + const bool initialiseToZero) throw() +{ + if (initialSize > 0) + { + size = initialSize; + + if (initialiseToZero) + data = (char*) juce_calloc (initialSize); + else + data = (char*) juce_malloc (initialSize); + } + else + { + data = 0; + size = 0; + } +} + +MemoryBlock::MemoryBlock (const MemoryBlock& other) throw() + : data (0), + size (other.size) +{ + if (size > 0) + { + jassert (other.data != 0); + data = (char*) juce_malloc (size); + memcpy (data, other.data, size); + } +} + +MemoryBlock::MemoryBlock (const void* const dataToInitialiseFrom, + const int sizeInBytes) throw() + : data (0), + size (jmax (0, sizeInBytes)) +{ + jassert (sizeInBytes >= 0); + + if (size > 0) + { + jassert (dataToInitialiseFrom != 0); // non-zero size, but a zero pointer passed-in? + + data = (char*) juce_malloc (size); + + if (dataToInitialiseFrom != 0) + memcpy (data, dataToInitialiseFrom, size); + } +} + +MemoryBlock::~MemoryBlock() throw() +{ + jassert (size >= 0); // should never happen + jassert (size == 0 || data != 0); // non-zero size but no data allocated? + + juce_free (data); +} + +const MemoryBlock& MemoryBlock::operator= (const MemoryBlock& other) throw() +{ + if (this != &other) + { + setSize (other.size, false); + memcpy (data, other.data, size); + } + + return *this; +} + +bool MemoryBlock::operator== (const MemoryBlock& other) const throw() +{ + return (size == other.size) + && (memcmp (data, other.data, size) == 0); +} + +bool MemoryBlock::operator!= (const MemoryBlock& other) const throw() +{ + return ! operator== (other); +} + +// this will resize the block to this size +void MemoryBlock::setSize (const int newSize, + const bool initialiseToZero) throw() +{ + if (size != newSize) + { + if (newSize <= 0) + { + juce_free (data); + data = 0; + size = 0; + } + else + { + if (data != 0) + { + data = (char*) juce_realloc (data, newSize); + + if (initialiseToZero && (newSize > size)) + zeromem (data + size, newSize - size); + } + else + { + if (initialiseToZero) + data = (char*) juce_calloc (newSize); + else + data = (char*) juce_malloc (newSize); + } + + size = newSize; + } + } +} + +void MemoryBlock::ensureSize (const int minimumSize, + const bool initialiseToZero) throw() +{ + if (size < minimumSize) + setSize (minimumSize, initialiseToZero); +} + +void MemoryBlock::fillWith (const uint8 value) throw() +{ + memset (data, (int) value, size); +} + +void MemoryBlock::append (const void* const srcData, + const int numBytes) throw() +{ + if (numBytes > 0) + { + const int oldSize = size; + setSize (size + numBytes); + memcpy (data + oldSize, srcData, numBytes); + } +} + +void MemoryBlock::copyFrom (const void* const src, int offset, int num) throw() +{ + const char* d = (const char*) src; + + if (offset < 0) + { + d -= offset; + num -= offset; + offset = 0; + } + + if (offset + num > size) + num = size - offset; + + if (num > 0) + memcpy (data + offset, d, num); +} + +void MemoryBlock::copyTo (void* const dst, int offset, int num) const throw() +{ + char* d = (char*) dst; + + if (offset < 0) + { + zeromem (d, -offset); + d -= offset; + + num += offset; + offset = 0; + } + + if (offset + num > size) + { + const int newNum = size - offset; + zeromem (d + newNum, num - newNum); + num = newNum; + } + + if (num > 0) + memcpy (d, data + offset, num); +} + +void MemoryBlock::removeSection (int startByte, int numBytesToRemove) throw() +{ + if (startByte < 0) + { + numBytesToRemove += startByte; + startByte = 0; + } + + if (startByte + numBytesToRemove >= size) + { + setSize (startByte); + } + else if (numBytesToRemove > 0) + { + memmove (data + startByte, + data + startByte + numBytesToRemove, + size - (startByte + numBytesToRemove)); + + setSize (size - numBytesToRemove); + } +} + +const String MemoryBlock::toString() const throw() +{ + return String (data, size); +} + +int MemoryBlock::getBitRange (const int bitRangeStart, int numBits) const throw() +{ + int res = 0; + + int byte = bitRangeStart >> 3; + int offsetInByte = bitRangeStart & 7; + int bitsSoFar = 0; + + while (numBits > 0 && byte < size) + { + const int bitsThisTime = jmin (numBits, 8 - offsetInByte); + const int mask = (0xff >> (8 - bitsThisTime)) << offsetInByte; + + res |= (((data[byte] & mask) >> offsetInByte) << bitsSoFar); + + bitsSoFar += bitsThisTime; + numBits -= bitsThisTime; + ++byte; + offsetInByte = 0; + } + + return res; +} + +void MemoryBlock::setBitRange (const int bitRangeStart, int numBits, int bitsToSet) throw() +{ + int byte = bitRangeStart >> 3; + int offsetInByte = bitRangeStart & 7; + unsigned int mask = ~((((unsigned int)0xffffffff) << (32 - numBits)) >> (32 - numBits)); + + while (numBits > 0 && byte < size) + { + const int bitsThisTime = jmin (numBits, 8 - offsetInByte); + + const unsigned int tempMask = (mask << offsetInByte) | ~((((unsigned int)0xffffffff) >> offsetInByte) << offsetInByte); + const unsigned int tempBits = bitsToSet << offsetInByte; + + data[byte] = (char)((data[byte] & tempMask) | tempBits); + + ++byte; + numBits -= bitsThisTime; + bitsToSet >>= bitsThisTime; + mask >>= bitsThisTime; + offsetInByte = 0; + } +} + +void MemoryBlock::loadFromHexString (const String& hex) throw() +{ + ensureSize (hex.length() >> 1); + char* dest = data; + int i = 0; + + for (;;) + { + int byte = 0; + + for (int loop = 2; --loop >= 0;) + { + byte <<= 4; + + for (;;) + { + const tchar c = hex [i++]; + + if (c >= T('0') && c <= T('9')) + { + byte |= c - T('0'); + break; + } + else if (c >= T('a') && c <= T('z')) + { + byte |= c - (T('a') - 10); + break; + } + else if (c >= T('A') && c <= T('Z')) + { + byte |= c - (T('A') - 10); + break; + } + else if (c == 0) + { + setSize ((int) (dest - data)); + return; + } + } + } + + *dest++ = (char) byte; + } +} + +static const char* const encodingTable + = ".ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+"; + +const String MemoryBlock::toBase64Encoding() const throw() +{ + const int numChars = ((size << 3) + 5) / 6; + + String destString (size); // store the length, followed by a '.', and then the data. + const int initialLen = destString.length(); + destString.preallocateStorage (initialLen + 2 + numChars); + + tchar* d = const_cast (((const tchar*) destString) + initialLen); + *d++ = T('.'); + + for (int i = 0; i < numChars; ++i) + *d++ = encodingTable [getBitRange (i * 6, 6)]; + + *d++ = 0; + + return destString; +} + +bool MemoryBlock::fromBase64Encoding (const String& s) throw() +{ + const int startPos = s.indexOfChar (T('.')) + 1; + + if (startPos <= 0) + return false; + + const int numBytesNeeded = s.substring (0, startPos - 1).getIntValue(); + + setSize (numBytesNeeded, true); + + const int numChars = s.length() - startPos; + const tchar* const srcChars = ((const tchar*) s) + startPos; + + for (int i = 0; i < numChars; ++i) + { + const char c = (char) srcChars[i]; + + for (int j = 0; j < 64; ++j) + { + if (encodingTable[j] == c) + { + setBitRange (i * 6, 6, j); + break; + } + } + } + + return true; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_MemoryBlock.cpp *********/ + +/********* Start of inlined file: juce_PropertySet.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +PropertySet::PropertySet (const bool ignoreCaseOfKeyNames) throw() + : properties (ignoreCaseOfKeyNames), + fallbackProperties (0), + ignoreCaseOfKeys (ignoreCaseOfKeyNames) +{ +} + +PropertySet::PropertySet (const PropertySet& other) throw() + : properties (other.properties), + fallbackProperties (other.fallbackProperties), + ignoreCaseOfKeys (other.ignoreCaseOfKeys) +{ +} + +const PropertySet& PropertySet::operator= (const PropertySet& other) throw() +{ + properties = other.properties; + fallbackProperties = other.fallbackProperties; + ignoreCaseOfKeys = other.ignoreCaseOfKeys; + + propertyChanged(); + return *this; +} + +PropertySet::~PropertySet() +{ +} + +void PropertySet::clear() +{ + const ScopedLock sl (lock); + + if (properties.size() > 0) + { + properties.clear(); + propertyChanged(); + } +} + +const String PropertySet::getValue (const String& keyName, + const String& defaultValue) const throw() +{ + const ScopedLock sl (lock); + + const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); + + if (index >= 0) + return properties.getAllValues() [index]; + + return fallbackProperties != 0 ? fallbackProperties->getValue (keyName, defaultValue) + : defaultValue; +} + +int PropertySet::getIntValue (const String& keyName, + const int defaultValue) const throw() +{ + const ScopedLock sl (lock); + const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); + + if (index >= 0) + return properties.getAllValues() [index].getIntValue(); + + return fallbackProperties != 0 ? fallbackProperties->getIntValue (keyName, defaultValue) + : defaultValue; +} + +double PropertySet::getDoubleValue (const String& keyName, + const double defaultValue) const throw() +{ + const ScopedLock sl (lock); + const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); + + if (index >= 0) + return properties.getAllValues()[index].getDoubleValue(); + + return fallbackProperties != 0 ? fallbackProperties->getDoubleValue (keyName, defaultValue) + : defaultValue; +} + +bool PropertySet::getBoolValue (const String& keyName, + const bool defaultValue) const throw() +{ + const ScopedLock sl (lock); + const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); + + if (index >= 0) + return properties.getAllValues() [index].getIntValue() != 0; + + return fallbackProperties != 0 ? fallbackProperties->getBoolValue (keyName, defaultValue) + : defaultValue; +} + +XmlElement* PropertySet::getXmlValue (const String& keyName) const +{ + XmlDocument doc (getValue (keyName)); + + return doc.getDocumentElement(); +} + +void PropertySet::setValue (const String& keyName, + const String& value) throw() +{ + jassert (keyName.isNotEmpty()); // shouldn't use an empty key name! + + if (keyName.isNotEmpty()) + { + const ScopedLock sl (lock); + + const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); + + if (index < 0 || properties.getAllValues() [index] != value) + { + properties.set (keyName, value); + propertyChanged(); + } + } +} + +void PropertySet::removeValue (const String& keyName) throw() +{ + if (keyName.isNotEmpty()) + { + const ScopedLock sl (lock); + const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); + + if (index >= 0) + { + properties.remove (keyName); + propertyChanged(); + } + } +} + +void PropertySet::setValue (const String& keyName, const tchar* const value) throw() +{ + setValue (keyName, String (value)); +} + +void PropertySet::setValue (const String& keyName, const int value) throw() +{ + setValue (keyName, String (value)); +} + +void PropertySet::setValue (const String& keyName, const double value) throw() +{ + setValue (keyName, String (value)); +} + +void PropertySet::setValue (const String& keyName, const bool value) throw() +{ + setValue (keyName, String ((value) ? T("1") : T("0"))); +} + +void PropertySet::setValue (const String& keyName, const XmlElement* const xml) +{ + setValue (keyName, (xml == 0) ? String::empty + : xml->createDocument (String::empty, true)); +} + +bool PropertySet::containsKey (const String& keyName) const throw() +{ + const ScopedLock sl (lock); + return properties.getAllKeys().contains (keyName, ignoreCaseOfKeys); +} + +void PropertySet::setFallbackPropertySet (PropertySet* fallbackProperties_) throw() +{ + const ScopedLock sl (lock); + fallbackProperties = fallbackProperties_; +} + +XmlElement* PropertySet::createXml (const String& nodeName) const throw() +{ + const ScopedLock sl (lock); + XmlElement* const xml = new XmlElement (nodeName); + + for (int i = 0; i < properties.getAllKeys().size(); ++i) + { + XmlElement* const e = new XmlElement (T("VALUE")); + + e->setAttribute (T("name"), properties.getAllKeys()[i]); + e->setAttribute (T("val"), properties.getAllValues()[i]); + + xml->addChildElement (e); + } + + return xml; +} + +void PropertySet::restoreFromXml (const XmlElement& xml) throw() +{ + const ScopedLock sl (lock); + clear(); + + forEachXmlChildElementWithTagName (xml, e, T("VALUE")) + { + if (e->hasAttribute (T("name")) + && e->hasAttribute (T("val"))) + { + properties.set (e->getStringAttribute (T("name")), + e->getStringAttribute (T("val"))); + } + } + + if (properties.size() > 0) + propertyChanged(); +} + +void PropertySet::propertyChanged() +{ +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_PropertySet.cpp *********/ + +/********* Start of inlined file: juce_BlowFish.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +static const uint32 initialPValues [18] = +{ + 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, + 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, + 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, + 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, + 0x9216d5d9, 0x8979fb1b +}; + +static const uint32 initialSValues [4 * 256] = +{ + 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, + 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, + 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, + 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, + 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, + 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, + 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, + 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, + 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, + 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, + 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, + 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, + 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, + 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, + 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, + 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, + 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, + 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, + 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, + 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, + 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, + 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, + 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, + 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, + 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, + 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, + 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, + 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, + 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, + 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, + 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, + 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, + 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, + 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, + 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, + 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, + 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, + 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, + 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, + 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, + 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, + 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, + 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, + 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, + 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, + 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, + 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, + 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, + 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, + 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, + 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, + 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, + 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, + 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, + 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, + 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, + 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, + 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, + 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, + 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, + 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, + 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, + 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, + 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a, + 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, + 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, + 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, + 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, + 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, + 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, + 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, + 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, + 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, + 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, + 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, + 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, + 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, + 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, + 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, + 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, + 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, + 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, + 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, + 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, + 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, + 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, + 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, + 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, + 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, + 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, + 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, + 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, + 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, + 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, + 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, + 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, + 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, + 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, + 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, + 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, + 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, + 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, + 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, + 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, + 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, + 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, + 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, + 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, + 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, + 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, + 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, + 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, + 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, + 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, + 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, + 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, + 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, + 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, + 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, + 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, + 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, + 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, + 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, + 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, + 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, + 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, + 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, + 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7, + 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, + 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, + 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, + 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, + 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, + 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, + 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, + 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, + 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, + 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, + 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, + 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, + 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, + 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, + 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, + 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, + 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, + 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, + 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, + 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, + 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, + 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, + 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, + 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, + 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, + 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, + 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, + 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, + 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, + 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, + 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, + 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, + 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, + 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, + 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, + 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, + 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, + 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, + 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, + 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, + 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, + 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, + 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, + 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, + 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, + 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, + 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, + 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, + 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, + 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, + 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, + 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, + 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, + 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, + 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, + 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, + 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, + 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, + 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, + 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, + 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, + 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, + 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, + 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0, + 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, + 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, + 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, + 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, + 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, + 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, + 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, + 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, + 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, + 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, + 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, + 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, + 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, + 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, + 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, + 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, + 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, + 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, + 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, + 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, + 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, + 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, + 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, + 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, + 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, + 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, + 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, + 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, + 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, + 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, + 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, + 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, + 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, + 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, + 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, + 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, + 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, + 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, + 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, + 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, + 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, + 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, + 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, + 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, + 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, + 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, + 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, + 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, + 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, + 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, + 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, + 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, + 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, + 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, + 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, + 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, + 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, + 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, + 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, + 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, + 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, + 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, + 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, + 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6 +}; + +BlowFish::BlowFish (const uint8* keyData, int keyBytes) +{ + p = (uint32*) juce_malloc (18 * sizeof (uint32)); + memcpy (p, initialPValues, sizeof (p)); + + int i, j; + for (i = 4; --i >= 0;) + { + s[i] = (uint32*) juce_malloc (256 * sizeof (uint32)); + memcpy (s[i], initialSValues + i * 256, 256 * sizeof (uint32)); + } + + j = 0; + + for (i = 0; i < 18; ++i) + { + uint32 d = 0; + + for (int k = 0; k < 4; ++k) + { + d = (d << 8) | keyData[j]; + + if (++j >= keyBytes) + j = 0; + } + + p[i] = initialPValues[i] ^ d; + } + + uint32 l = 0, r = 0; + + for (i = 0; i < 18; i += 2) + { + encrypt (l, r); + + p[i] = l; + p[i + 1] = r; + } + + for (i = 0; i < 4; ++i) + { + for (j = 0; j < 256; j += 2) + { + encrypt (l, r); + + s[i][j] = l; + s[i][j + 1] = r; + } + } +} + +BlowFish::~BlowFish() +{ + juce_free (p); + + for (int i = 4; --i >= 0;) + juce_free (s[i]); +} + +uint32 BlowFish::F (uint32 x) const +{ + uint16 a, b, c, d; + uint32 y; + + d = (uint16) (x & 0xff); + x >>= 8; + c = (uint16) (x & 0xff); + x >>= 8; + b = (uint16) (x & 0xff); + x >>= 8; + a = (uint16) (x & 0xff); + + y = s[0][a] + s[1][b]; + y = y ^ s[2][c]; + y = y + s[3][d]; + + return y; +} + +void BlowFish::encrypt (uint32& data1, + uint32& data2) const +{ + uint32 l = data1; + uint32 r = data2; + + for (int i = 0; i < 16; ++i) + { + l = l ^ p[i]; + r = F (l) ^ r; + + const uint32 temp = l; + l = r; + r = temp; + } + + const uint32 temp = l; + l = r; + r = temp; + + r = r ^ p[16]; + l = l ^ p[17]; + + data1 = l; + data2 = r; +} + +void BlowFish::decrypt (uint32& data1, + uint32& data2) const +{ + uint32 l = data1; + uint32 r = data2; + + for (int i = 17; i > 1; --i) + { + l =l ^ p[i]; + r = F (l) ^ r; + + const uint32 temp = l; + l = r; + r = temp; + } + + const uint32 temp = l; + l = r; + r = temp; + + r = r ^ p[1]; + l = l ^ p[0]; + + data1 = l; + data2 = r; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_BlowFish.cpp *********/ + +/********* Start of inlined file: juce_MD5.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +MD5::MD5() +{ + zeromem (result, sizeof (result)); +} + +MD5::MD5 (const MD5& other) +{ + memcpy (result, other.result, sizeof (result)); +} + +const MD5& MD5::operator= (const MD5& other) +{ + memcpy (result, other.result, sizeof (result)); + return *this; +} + +MD5::MD5 (const MemoryBlock& data) +{ + ProcessContext context; + context.processBlock ((const uint8*) data.getData(), data.getSize()); + context.finish (result); +} + +MD5::MD5 (const char* data, const int numBytes) +{ + ProcessContext context; + context.processBlock ((const uint8*) data, numBytes); + context.finish (result); +} + +MD5::MD5 (const String& text) +{ + ProcessContext context; + + const int len = text.length(); + const juce_wchar* const t = text; + + for (int i = 0; i < len; ++i) + { + // force the string into integer-sized unicode characters, to try to make it + // get the same results on all platforms + compilers. + uint32 unicodeChar = (uint32) t[i]; + swapIfBigEndian (unicodeChar); + + context.processBlock ((const uint8*) &unicodeChar, + sizeof (unicodeChar)); + } + + context.finish (result); +} + +void MD5::processStream (InputStream& input, int numBytesToRead) +{ + ProcessContext context; + + if (numBytesToRead < 0) + numBytesToRead = INT_MAX; + + while (numBytesToRead > 0) + { + char tempBuffer [512]; + const int bytesRead = input.read (tempBuffer, jmin (numBytesToRead, sizeof (tempBuffer))); + + if (bytesRead <= 0) + break; + + numBytesToRead -= bytesRead; + + context.processBlock ((const uint8*) tempBuffer, bytesRead); + } + + context.finish (result); +} + +MD5::MD5 (InputStream& input, int numBytesToRead) +{ + processStream (input, numBytesToRead); +} + +MD5::MD5 (const File& file) +{ + FileInputStream* const fin = file.createInputStream(); + + if (fin != 0) + { + processStream (*fin, -1); + delete fin; + } + else + { + zeromem (result, sizeof (result)); + } +} + +MD5::~MD5() +{ +} + +MD5::ProcessContext::ProcessContext() +{ + state[0] = 0x67452301; + state[1] = 0xefcdab89; + state[2] = 0x98badcfe; + state[3] = 0x10325476; + + count[0] = 0; + count[1] = 0; +} + +void MD5::ProcessContext::processBlock (const uint8* const data, int dataSize) +{ + int bufferPos = ((count[0] >> 3) & 0x3F); + + count[0] += (dataSize << 3); + + if (count[0] < ((uint32) dataSize << 3)) + count[1]++; + + count[1] += (dataSize >> 29); + + const int spaceLeft = 64 - bufferPos; + + int i = 0; + + if (dataSize >= spaceLeft) + { + memcpy (buffer + bufferPos, data, spaceLeft); + + transform (buffer); + + i = spaceLeft; + + while (i < dataSize - 63) + { + transform (data + i); + i += 64; + } + + bufferPos = 0; + } + + memcpy (buffer + bufferPos, data + i, dataSize - i); +} + +static void encode (uint8* const output, + const uint32* const input, + const int numBytes) +{ + uint32* const o = (uint32*) output; + + for (int i = 0; i < (numBytes >> 2); ++i) + o[i] = swapIfBigEndian (input [i]); +} + +static void decode (uint32* const output, + const uint8* const input, + const int numBytes) +{ + for (int i = 0; i < (numBytes >> 2); ++i) + output[i] = littleEndianInt ((const char*) input + (i << 2)); +} + +void MD5::ProcessContext::finish (uint8* const result) +{ + unsigned char encodedLength[8]; + encode (encodedLength, count, 8); + + // Pad out to 56 mod 64. + const int index = (uint32) ((count[0] >> 3) & 0x3f); + + const int paddingLength = (index < 56) ? (56 - index) + : (120 - index); + + uint8 paddingBuffer [64]; + zeromem (paddingBuffer, paddingLength); + paddingBuffer [0] = 0x80; + processBlock (paddingBuffer, paddingLength); + + processBlock (encodedLength, 8); + + encode (result, state, 16); + + zeromem (buffer, sizeof (buffer)); +} + +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +static inline uint32 F (const uint32 x, const uint32 y, const uint32 z) { return (x & y) | (~x & z); } +static inline uint32 G (const uint32 x, const uint32 y, const uint32 z) { return (x & z) | (y & ~z); } +static inline uint32 H (const uint32 x, const uint32 y, const uint32 z) { return x ^ y ^ z; } +static inline uint32 I (const uint32 x, const uint32 y, const uint32 z) { return y ^ (x | ~z); } + +static inline uint32 rotateLeft (const uint32 x, const uint32 n) { return (x << n) | (x >> (32 - n)); } + +static inline void FF (uint32& a, const uint32 b, const uint32 c, const uint32 d, const uint32 x, const uint32 s, const uint32 ac) +{ + a += F (b, c, d) + x + ac; + a = rotateLeft (a, s) + b; +} + +static inline void GG (uint32& a, const uint32 b, const uint32 c, const uint32 d, const uint32 x, const uint32 s, const uint32 ac) +{ + a += G (b, c, d) + x + ac; + a = rotateLeft (a, s) + b; +} + +static inline void HH (uint32& a, const uint32 b, const uint32 c, const uint32 d, const uint32 x, const uint32 s, const uint32 ac) +{ + a += H (b, c, d) + x + ac; + a = rotateLeft (a, s) + b; +} + +static inline void II (uint32& a, const uint32 b, const uint32 c, const uint32 d, const uint32 x, const uint32 s, const uint32 ac) +{ + a += I (b, c, d) + x + ac; + a = rotateLeft (a, s) + b; +} + +void MD5::ProcessContext::transform (const uint8* const buffer) +{ + uint32 a = state[0]; + uint32 b = state[1]; + uint32 c = state[2]; + uint32 d = state[3]; + uint32 x[16]; + + decode (x, buffer, 64); + + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + zeromem (x, sizeof (x)); +} + +const MemoryBlock MD5::getRawChecksumData() const +{ + return MemoryBlock (result, 16); +} + +const String MD5::toHexString() const +{ + return String::toHexString (result, 16, 0); +} + +bool MD5::operator== (const MD5& other) const +{ + return memcmp (result, other.result, 16) == 0; +} + +bool MD5::operator!= (const MD5& other) const +{ + return ! operator== (other); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_MD5.cpp *********/ + +/********* Start of inlined file: juce_Primes.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +static void createSmallSieve (const int numBits, BitArray& result) throw() +{ + result.setBit (numBits); + result.clearBit (numBits); // to enlarge the array + + result.setBit (0); + int n = 2; + + do + { + for (int i = n + n; i < numBits; i += n) + result.setBit (i); + + n = result.findNextClearBit (n + 1); + } + while (n <= (numBits >> 1)); +} + +static void bigSieve (const BitArray& base, + const int numBits, + BitArray& result, + const BitArray& smallSieve, + const int smallSieveSize) throw() +{ + jassert (! base[0]); // must be even! + + result.setBit (numBits); + result.clearBit (numBits); // to enlarge the array + + int index = smallSieve.findNextClearBit (0); + + do + { + const int prime = (index << 1) + 1; + + BitArray r (base); + BitArray remainder; + r.divideBy (prime, remainder); + + int i = prime - remainder.getBitRangeAsInt (0, 32); + + if (r.isEmpty()) + i += prime; + + if ((i & 1) == 0) + i += prime; + + i = (i - 1) >> 1; + + while (i < numBits) + { + result.setBit (i); + i += prime; + } + + index = smallSieve.findNextClearBit (index + 1); + } + while (index < smallSieveSize); +} + +static bool findCandidate (const BitArray& base, + const BitArray& sieve, + const int numBits, + BitArray& result, + const int certainty) throw() +{ + for (int i = 0; i < numBits; ++i) + { + if (! sieve[i]) + { + result = base; + result.add (BitArray ((unsigned int) ((i << 1) + 1))); + + if (Primes::isProbablyPrime (result, certainty)) + return true; + } + } + + return false; +} + +const BitArray Primes::createProbablePrime (const int bitLength, + const int certainty) throw() +{ + BitArray smallSieve; + const int smallSieveSize = 15000; + createSmallSieve (smallSieveSize, smallSieve); + + BitArray p; + p.fillBitsRandomly (0, bitLength); + p.setBit (bitLength - 1); + p.clearBit (0); + + const int searchLen = jmax (1024, (bitLength / 20) * 64); + + while (p.getHighestBit() < bitLength) + { + p.add (2 * searchLen); + + BitArray sieve; + bigSieve (p, searchLen, sieve, + smallSieve, smallSieveSize); + + BitArray candidate; + + if (findCandidate (p, sieve, searchLen, candidate, certainty)) + return candidate; + } + + jassertfalse + return BitArray(); +} + +static bool passesMillerRabin (const BitArray& n, int iterations) throw() +{ + const BitArray one (1); + const BitArray two (2); + + BitArray nMinusOne (n); + nMinusOne.subtract (one); + + BitArray d (nMinusOne); + const int s = d.findNextSetBit (0); + d.shiftBits (-s); + + BitArray smallPrimes; + int numBitsInSmallPrimes = 0; + + for (;;) + { + numBitsInSmallPrimes += 256; + createSmallSieve (numBitsInSmallPrimes, smallPrimes); + + const int numPrimesFound = numBitsInSmallPrimes - smallPrimes.countNumberOfSetBits(); + + if (numPrimesFound > iterations + 1) + break; + } + + int smallPrime = 2; + + while (--iterations >= 0) + { + smallPrime = smallPrimes.findNextClearBit (smallPrime + 1); + + BitArray r (smallPrime); + //r.createRandomNumber (nMinusOne); + r.exponentModulo (d, n); + + if (! (r == one || r == nMinusOne)) + { + for (int j = 0; j < s; ++j) + { + r.exponentModulo (two, n); + + if (r == nMinusOne) + break; + } + + if (r != nMinusOne) + return false; + } + } + + return true; +} + +bool Primes::isProbablyPrime (const BitArray& number, + const int certainty) throw() +{ + if (! number[0]) + return false; + + if (number.getHighestBit() <= 10) + { + const int num = number.getBitRangeAsInt (0, 10); + + for (int i = num / 2; --i > 1;) + if (num % i == 0) + return false; + + return true; + } + else + { + const BitArray screen (2 * 3 * 5 * 7 * 11 * 13 * 17 * 19 * 23); + + if (number.findGreatestCommonDivisor (screen) != BitArray (1)) + return false; + + return passesMillerRabin (number, certainty); + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_Primes.cpp *********/ + +/********* Start of inlined file: juce_RSAKey.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +RSAKey::RSAKey() throw() +{ +} + +RSAKey::RSAKey (const String& s) throw() +{ + if (s.containsChar (T(','))) + { + part1.parseString (s.upToFirstOccurrenceOf (T(","), false, false), 16); + part2.parseString (s.fromFirstOccurrenceOf (T(","), false, false), 16); + } + else + { + // the string needs to be two hex numbers, comma-separated.. + jassertfalse; + } +} + +RSAKey::~RSAKey() throw() +{ +} + +const String RSAKey::toString() const throw() +{ + return part1.toString (16) + T(",") + part2.toString (16); +} + +bool RSAKey::applyToValue (BitArray& value) const throw() +{ + if (part1.isEmpty() || part2.isEmpty() + || value.compare (0) <= 0) + { + jassertfalse // using an uninitialised key + value.clear(); + return false; + } + + BitArray result; + + while (! value.isEmpty()) + { + result.multiplyBy (part2); + + BitArray remainder; + value.divideBy (part2, remainder); + + remainder.exponentModulo (part1, part2); + + result.add (remainder); + } + + value = result; + + return true; +} + +static const BitArray findBestCommonDivisor (const BitArray& p, + const BitArray& q) throw() +{ + const BitArray one (1); + + // try 3, 5, 9, 17, etc first because these only contain 2 bits and so + // are fast to divide + multiply + for (int i = 2; i <= 65536; i *= 2) + { + const BitArray e (1 + i); + + if (e.findGreatestCommonDivisor (p) == one + && e.findGreatestCommonDivisor (q) == one) + { + return e; + } + } + + BitArray e (4); + + while (! (e.findGreatestCommonDivisor (p) == one + && e.findGreatestCommonDivisor (q) == one)) + { + e.add (one); + } + + return e; +} + +void RSAKey::createKeyPair (RSAKey& publicKey, + RSAKey& privateKey, + const int numBits) throw() +{ + jassert (numBits > 16); // not much point using less than this.. + + BitArray p (Primes::createProbablePrime (numBits / 2, 30)); + BitArray q (Primes::createProbablePrime (numBits - numBits / 2, 30)); + + BitArray n (p); + n.multiplyBy (q); // n = pq + + const BitArray one (1); + p.subtract (one); + q.subtract (one); + + BitArray m (p); + m.multiplyBy (q); // m = (p - 1)(q - 1) + + const BitArray e (findBestCommonDivisor (p, q)); + + BitArray d (e); + d.inverseModulo (m); + + publicKey.part1 = e; + publicKey.part2 = n; + + privateKey.part1 = d; + privateKey.part2 = n; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_RSAKey.cpp *********/ + +/********* Start of inlined file: juce_InputStream.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +char InputStream::readByte() +{ + char temp = 0; + read (&temp, 1); + return temp; +} + +bool InputStream::readBool() +{ + return readByte() != 0; +} + +short InputStream::readShort() +{ + char temp [2]; + + if (read (temp, 2) == 2) + return (short) littleEndianShort (temp); + else + return 0; +} + +short InputStream::readShortBigEndian() +{ + char temp [2]; + + if (read (temp, 2) == 2) + return (short) bigEndianShort (temp); + else + return 0; +} + +int InputStream::readInt() +{ + char temp [4]; + + if (read (temp, 4) == 4) + return (int) littleEndianInt (temp); + else + return 0; +} + +int InputStream::readIntBigEndian() +{ + char temp [4]; + + if (read (temp, 4) == 4) + return (int) bigEndianInt (temp); + else + return 0; +} + +int InputStream::readCompressedInt() +{ + int num = 0; + + if (! isExhausted()) + { + unsigned char numBytes = readByte(); + const bool negative = (numBytes & 0x80) != 0; + numBytes &= 0x7f; + + if (numBytes <= 4) + { + if (read (&num, numBytes) != numBytes) + return 0; + + if (negative) + num = -num; + } + } + + return num; +} + +int64 InputStream::readInt64() +{ + char temp [8]; + + if (read (temp, 8) == 8) + return (int64) swapIfBigEndian (*(uint64*)temp); + else + return 0; +} + +int64 InputStream::readInt64BigEndian() +{ + char temp [8]; + + if (read (temp, 8) == 8) + return (int64) swapIfLittleEndian (*(uint64*)temp); + else + return 0; +} + +float InputStream::readFloat() +{ + union { int asInt; float asFloat; } n; + n.asInt = readInt(); + return n.asFloat; +} + +float InputStream::readFloatBigEndian() +{ + union { int asInt; float asFloat; } n; + n.asInt = readIntBigEndian(); + return n.asFloat; +} + +double InputStream::readDouble() +{ + union { int64 asInt; double asDouble; } n; + n.asInt = readInt64(); + return n.asDouble; +} + +double InputStream::readDoubleBigEndian() +{ + union { int64 asInt; double asDouble; } n; + n.asInt = readInt64BigEndian(); + return n.asDouble; +} + +const String InputStream::readString() +{ + const int tempBufferSize = 256; + uint8 temp [tempBufferSize]; + int i = 0; + + while ((temp [i++] = readByte()) != 0) + { + if (i == tempBufferSize) + { + // too big for our quick buffer, so read it in blocks.. + String result (String::fromUTF8 (temp, i)); + i = 0; + + for (;;) + { + if ((temp [i++] = readByte()) == 0) + { + result += String::fromUTF8 (temp, i - 1); + break; + } + else if (i == tempBufferSize) + { + result += String::fromUTF8 (temp, i); + i = 0; + } + } + + return result; + } + } + + return String::fromUTF8 (temp, i - 1); +} + +const String InputStream::readNextLine() +{ + String s; + const int maxChars = 256; + tchar buffer [maxChars]; + int charsInBuffer = 0; + + while (! isExhausted()) + { + const uint8 c = readByte(); + const int64 lastPos = getPosition(); + + if (c == '\n') + { + break; + } + else if (c == '\r') + { + if (readByte() != '\n') + setPosition (lastPos); + + break; + } + + buffer [charsInBuffer++] = c; + + if (charsInBuffer == maxChars) + { + s.append (buffer, maxChars); + charsInBuffer = 0; + } + } + + if (charsInBuffer > 0) + s.append (buffer, charsInBuffer); + + return s; +} + +int InputStream::readIntoMemoryBlock (MemoryBlock& block, + int numBytes) +{ + const int64 totalLength = getTotalLength(); + + if (totalLength >= 0) + { + const int totalBytesRemaining = (int) jmin ((int64) 0x7fffffff, + totalLength - getPosition()); + + if (numBytes < 0) + numBytes = totalBytesRemaining; + else if (numBytes > 0) + numBytes = jmin (numBytes, totalBytesRemaining); + else + return 0; + } + + const int originalBlockSize = block.getSize(); + int totalBytesRead = 0; + + if (numBytes > 0) + { + // know how many bytes we want, so we can resize the block first.. + block.setSize (originalBlockSize + numBytes, false); + totalBytesRead = read (((char*) block.getData()) + originalBlockSize, numBytes); + } + else + { + // read until end of stram.. + const int chunkSize = 32768; + + for (;;) + { + block.ensureSize (originalBlockSize + totalBytesRead + chunkSize, false); + + const int bytesJustIn = read (((char*) block.getData()) + + originalBlockSize + + totalBytesRead, + chunkSize); + + if (bytesJustIn == 0) + break; + + totalBytesRead += bytesJustIn; + } + } + + // trim off any excess left at the end + block.setSize (originalBlockSize + totalBytesRead, false); + return totalBytesRead; +} + +const String InputStream::readEntireStreamAsString() +{ + MemoryBlock mb; + const int size = readIntoMemoryBlock (mb); + + return String::createStringFromData ((const char*) mb.getData(), size); +} + +void InputStream::skipNextBytes (int64 numBytesToSkip) +{ + if (numBytesToSkip > 0) + { + const int skipBufferSize = (int) jmin (numBytesToSkip, (int64) 16384); + MemoryBlock temp (skipBufferSize); + + while ((numBytesToSkip > 0) && ! isExhausted()) + { + numBytesToSkip -= read (temp.getData(), (int) jmin (numBytesToSkip, (int64) skipBufferSize)); + } + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_InputStream.cpp *********/ + +/********* Start of inlined file: juce_OutputStream.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +#if JUCE_DEBUG +static CriticalSection activeStreamLock; +static VoidArray activeStreams; + +void juce_CheckForDanglingStreams() +{ + /* + It's always a bad idea to leak any object, but if you're leaking output + streams, then there's a good chance that you're failing to flush a file + to disk properly, which could result in corrupted data and other similar + nastiness.. + */ + jassert (activeStreams.size() == 0); +}; +#endif + +OutputStream::OutputStream() throw() +{ +#if JUCE_DEBUG + activeStreamLock.enter(); + activeStreams.add (this); + activeStreamLock.exit(); +#endif +} + +OutputStream::~OutputStream() +{ +#if JUCE_DEBUG + activeStreamLock.enter(); + activeStreams.removeValue (this); + activeStreamLock.exit(); +#endif +} + +void OutputStream::writeBool (bool b) +{ + writeByte ((b) ? (char) 1 + : (char) 0); +} + +void OutputStream::writeByte (char byte) +{ + write (&byte, 1); +} + +void OutputStream::writeShort (short value) +{ + const unsigned short v = swapIfBigEndian ((unsigned short) value); + write (&v, 2); +} + +void OutputStream::writeShortBigEndian (short value) +{ + const unsigned short v = swapIfLittleEndian ((unsigned short) value); + write (&v, 2); +} + +void OutputStream::writeInt (int value) +{ + const unsigned int v = swapIfBigEndian ((unsigned int) value); + write (&v, 4); +} + +void OutputStream::writeIntBigEndian (int value) +{ + const unsigned int v = swapIfLittleEndian ((unsigned int) value); + write (&v, 4); +} + +void OutputStream::writeCompressedInt (int value) +{ + unsigned int un = (value < 0) ? (unsigned int) -value + : (unsigned int) value; + unsigned int tn = un; + + int numSigBytes = 0; + + do + { + tn >>= 8; + numSigBytes++; + + } while (tn & 0xff); + + if (value < 0) + numSigBytes |= 0x80; + + writeByte ((char) numSigBytes); + write (&un, numSigBytes); +} + +void OutputStream::writeInt64 (int64 value) +{ + const uint64 v = swapIfBigEndian ((uint64) value); + write (&v, 8); +} + +void OutputStream::writeInt64BigEndian (int64 value) +{ + const uint64 v = swapIfLittleEndian ((uint64) value); + write (&v, 8); +} + +void OutputStream::writeFloat (float value) +{ + union { int asInt; float asFloat; } n; + n.asFloat = value; + writeInt (n.asInt); +} + +void OutputStream::writeFloatBigEndian (float value) +{ + union { int asInt; float asFloat; } n; + n.asFloat = value; + writeIntBigEndian (n.asInt); +} + +void OutputStream::writeDouble (double value) +{ + union { int64 asInt; double asDouble; } n; + n.asDouble = value; + writeInt64 (n.asInt); +} + +void OutputStream::writeDoubleBigEndian (double value) +{ + union { int64 asInt; double asDouble; } n; + n.asDouble = value; + writeInt64BigEndian (n.asInt); +} + +void OutputStream::writeString (const String& text) +{ + const int numBytes = text.copyToUTF8 (0); + uint8* const temp = (uint8*) juce_malloc (numBytes); + + text.copyToUTF8 (temp); + write (temp, numBytes); // (numBytes includes the terminating null). + + juce_free (temp); +} + +void OutputStream::printf (const char* pf, ...) +{ + unsigned int bufSize = 256; + char* buf = (char*) juce_malloc (bufSize); + + for (;;) + { + va_list list; + va_start (list, pf); + + const int num = CharacterFunctions::vprintf (buf, bufSize, pf, list); + + if (num > 0) + { + write (buf, num); + break; + } + else if (num == 0) + { + break; + } + + juce_free (buf); + bufSize += 256; + buf = (char*) juce_malloc (bufSize); + } + + juce_free (buf); +} + +OutputStream& OutputStream::operator<< (const int number) +{ + const String s (number); + write ((const char*) s, s.length()); + return *this; +} + +OutputStream& OutputStream::operator<< (const double number) +{ + const String s (number); + write ((const char*) s, s.length()); + return *this; +} + +OutputStream& OutputStream::operator<< (const char character) +{ + writeByte (character); + return *this; +} + +OutputStream& OutputStream::operator<< (const char* const text) +{ + write (text, (int) strlen (text)); + return *this; +} + +OutputStream& OutputStream::operator<< (const juce_wchar* const text) +{ + const String s (text); + write ((const char*) s, s.length()); + return *this; +} + +OutputStream& OutputStream::operator<< (const String& text) +{ + write ((const char*) text, + text.length()); + + return *this; +} + +void OutputStream::writeText (const String& text, + const bool asUnicode, + const bool writeUnicodeHeaderBytes) +{ + if (asUnicode) + { + if (writeUnicodeHeaderBytes) + write ("\x0ff\x0fe", 2); + + const juce_wchar* src = (const juce_wchar*) text; + bool lastCharWasReturn = false; + + while (*src != 0) + { + if (*src == L'\n' && ! lastCharWasReturn) + writeShort ((short) L'\r'); + + lastCharWasReturn = (*src == L'\r'); + writeShort ((short) *src++); + } + } + else + { + const char* src = (const char*) text; + const char* t = src; + + for (;;) + { + if (*t == '\n') + { + if (t > src) + write (src, (int) (t - src)); + + write ("\r\n", 2); + src = t + 1; + } + else if (*t == '\r') + { + if (t[1] == '\n') + ++t; + } + else if (*t == 0) + { + if (t > src) + write (src, (int) (t - src)); + + break; + } + + ++t; + } + } +} + +int OutputStream::writeFromInputStream (InputStream& source, + int numBytesToWrite) +{ + if (numBytesToWrite < 0) + numBytesToWrite = 0x7fffffff; + + int numWritten = 0; + + while (numBytesToWrite > 0 && ! source.isExhausted()) + { + char buffer [8192]; + + const int num = source.read (buffer, jmin (numBytesToWrite, sizeof (buffer))); + + if (num == 0) + break; + + write (buffer, num); + + numBytesToWrite -= num; + numWritten += num; + } + + return numWritten; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_OutputStream.cpp *********/ + +/********* Start of inlined file: juce_DirectoryIterator.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +void* juce_findFileStart (const String& directory, const String& wildCard, String& firstResultFile, + bool* isDirectory, bool* isHidden, int64* fileSize, + Time* modTime, Time* creationTime, bool* isReadOnly) throw(); +bool juce_findFileNext (void* handle, String& resultFile, + bool* isDirectory, bool* isHidden, int64* fileSize, + Time* modTime, Time* creationTime, bool* isReadOnly) throw(); +void juce_findFileClose (void* handle) throw(); + +DirectoryIterator::DirectoryIterator (const File& directory, + bool isRecursive, + const String& wc, + const int whatToLookFor_) throw() + : wildCard (wc), + index (-1), + whatToLookFor (whatToLookFor_), + subIterator (0) +{ + // you have to specify the type of files you're looking for! + jassert ((whatToLookFor_ & (File::findFiles | File::findDirectories)) != 0); + jassert (whatToLookFor_ > 0 && whatToLookFor_ <= 7); + + String path (directory.getFullPathName()); + if (! path.endsWithChar (File::separator)) + path += File::separator; + + String filename; + bool isDirectory, isHidden; + + void* const handle = juce_findFileStart (path, + isRecursive ? T("*") : wc, + filename, &isDirectory, &isHidden, 0, 0, 0, 0); + + if (handle != 0) + { + do + { + if (! filename.containsOnly (T("."))) + { + bool addToList = false; + + if (isDirectory) + { + if (isRecursive + && ((whatToLookFor_ & File::ignoreHiddenFiles) == 0 + || ! isHidden)) + { + dirsFound.add (new File (path + filename, 0)); + } + + addToList = (whatToLookFor_ & File::findDirectories) != 0; + } + else + { + addToList = (whatToLookFor_ & File::findFiles) != 0; + } + + // if it's recursive, we're not relying on the OS iterator + // to do the wildcard match, so do it now.. + if (isRecursive && addToList) + addToList = filename.matchesWildcard (wc, true); + + if (addToList && (whatToLookFor_ & File::ignoreHiddenFiles) != 0) + addToList = ! isHidden; + + if (addToList) + filesFound.add (new File (path + filename, 0)); + } + + } while (juce_findFileNext (handle, filename, &isDirectory, &isHidden, 0, 0, 0, 0)); + + juce_findFileClose (handle); + } +} + +DirectoryIterator::~DirectoryIterator() throw() +{ + if (subIterator != 0) + delete subIterator; +} + +bool DirectoryIterator::next() throw() +{ + if (subIterator != 0) + { + if (subIterator->next()) + return true; + + deleteAndZero (subIterator); + } + + if (index >= filesFound.size() + dirsFound.size() - 1) + return false; + + ++index; + + if (index >= filesFound.size()) + { + subIterator = new DirectoryIterator (*(dirsFound [index - filesFound.size()]), + true, wildCard, whatToLookFor); + return next(); + } + + return true; +} + +const File DirectoryIterator::getFile() const throw() +{ + if (subIterator != 0) + return subIterator->getFile(); + + const File* const f = filesFound [index]; + + return (f != 0) ? *f + : File::nonexistent; +} + +float DirectoryIterator::getEstimatedProgress() const throw() +{ + if (filesFound.size() + dirsFound.size() == 0) + { + return 0.0f; + } + else + { + const float detailedIndex = (subIterator != 0) ? index + subIterator->getEstimatedProgress() + : (float) index; + + return detailedIndex / (filesFound.size() + dirsFound.size()); + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_DirectoryIterator.cpp *********/ + +/********* Start of inlined file: juce_File.cpp *********/ +#ifdef _MSC_VER + #pragma warning (disable: 4514) + #pragma warning (push) +#endif + +#ifndef JUCE_WIN32 + #include +#endif + +BEGIN_JUCE_NAMESPACE + +#ifdef _MSC_VER + #pragma warning (pop) +#endif + +void* juce_fileOpen (const String& path, bool forWriting) throw(); +void juce_fileClose (void* handle) throw(); +int juce_fileWrite (void* handle, const void* buffer, int size) throw(); +int64 juce_fileGetPosition (void* handle) throw(); +int64 juce_fileSetPosition (void* handle, int64 pos) throw(); +void juce_fileFlush (void* handle) throw(); + +bool juce_fileExists (const String& fileName, const bool dontCountDirectories) throw(); +bool juce_isDirectory (const String& fileName) throw(); +int64 juce_getFileSize (const String& fileName) throw(); +bool juce_canWriteToFile (const String& fileName) throw(); +bool juce_setFileReadOnly (const String& fileName, bool isReadOnly) throw(); + +void juce_getFileTimes (const String& fileName, int64& modificationTime, int64& accessTime, int64& creationTime) throw(); +bool juce_setFileTimes (const String& fileName, int64 modificationTime, int64 accessTime, int64 creationTime) throw(); + +bool juce_deleteFile (const String& fileName) throw(); +bool juce_copyFile (const String& source, const String& dest) throw(); +bool juce_moveFile (const String& source, const String& dest) throw(); + +// this must also create all paths involved in the directory. +void juce_createDirectory (const String& fileName) throw(); + +bool juce_launchFile (const String& fileName, const String& parameters) throw(); + +const StringArray juce_getFileSystemRoots() throw(); +const String juce_getVolumeLabel (const String& filenameOnVolume, int& volumeSerialNumber) throw(); + +// starts a directory search operation with a wildcard, returning a handle for +// use in calls to juce_findFileNext. +// juce_firstResultFile gets the name of the file (not the whole pathname) and +// the other pointers, if non-null, are set based on the properties of the file. +void* juce_findFileStart (const String& directory, const String& wildCard, String& firstResultFile, + bool* isDirectory, bool* isHidden, int64* fileSize, Time* modTime, + Time* creationTime, bool* isReadOnly) throw(); + +// returns false when no more files are found +bool juce_findFileNext (void* handle, String& resultFile, + bool* isDirectory, bool* isHidden, int64* fileSize, + Time* modTime, Time* creationTime, bool* isReadOnly) throw(); + +void juce_findFileClose (void* handle) throw(); + +static const String parseAbsolutePath (String path) throw() +{ + if (path.isEmpty()) + return String::empty; + +#if JUCE_WIN32 + // Windows.. + path = path.replaceCharacter (T('/'), T('\\')).unquoted(); + + if (path.startsWithChar (File::separator)) + { + if (path[1] != File::separator) + { + jassertfalse // using a filename that starts with a slash is a bit dodgy on + // Windows, because it needs a drive letter, which in this case + // we'll take from the CWD.. but this is a bit of an assumption that + // could be wrong.. + + path = File::getCurrentWorkingDirectory().getFullPathName().substring (0, 2) + path; + } + } + else if (path.indexOfChar (T(':')) < 0) + { + if (path.isEmpty()) + return String::empty; + + jassertfalse // using a partial filename is a bad way to initialise a file, because + // we don't know what directory to put it in. + // Here we'll assume it's in the CWD, but this might not be what was + // intended.. + + return File::getCurrentWorkingDirectory().getChildFile (path).getFullPathName(); + } +#else + // Mac or Linux.. + path = path.replaceCharacter (T('\\'), T('/')).unquoted(); + + if (path.startsWithChar (T('~'))) + { + const char* homeDir = 0; + + if (path[1] == File::separator || path[1] == 0) + { + // expand a name of the form "~/abc" + path = File::getSpecialLocation (File::userHomeDirectory).getFullPathName() + + path.substring (1); + } + else + { + // expand a name of type "~dave/abc" + const String userName (path.substring (1) + .upToFirstOccurrenceOf (T("/"), false, false)); + + struct passwd* const pw = getpwnam (userName); + if (pw != 0) + { + String home (homeDir); + + if (home.endsWithChar (File::separator)) + home [home.length() - 1] = 0; + + path = String (pw->pw_dir) + + path.substring (userName.length()); + } + } + } + else if (! path.startsWithChar (File::separator)) + { + while (path.startsWith (T("./"))) + path = path.substring (2); + + if (path.isEmpty()) + return String::empty; + + jassertfalse // using a partial filename is a bad way to initialise a file, because + // we don't know what directory to put it in. + // Here we'll assume it's in the CWD, but this might not be what was + // intended.. + + return File::getCurrentWorkingDirectory().getChildFile (path).getFullPathName(); + } +#endif + + int len = path.length(); + while (--len > 0 && path [len] == File::separator) + path [len] = 0; + + return path; +} + +const File File::nonexistent; + +File::File (const String& fullPathName) throw() + : fullPath (parseAbsolutePath (fullPathName)) +{ +} + +File::File (const String& path, int) throw() + : fullPath (path) +{ +} + +File::File (const File& other) throw() + : fullPath (other.fullPath) +{ +} + +const File& File::operator= (const String& newPath) throw() +{ + fullPath = parseAbsolutePath (newPath); + return *this; +} + +const File& File::operator= (const File& other) throw() +{ + fullPath = other.fullPath; + return *this; +} + +#if JUCE_LINUX + #define NAMES_ARE_CASE_SENSITIVE 1 +#endif + +bool File::areFileNamesCaseSensitive() +{ +#if NAMES_ARE_CASE_SENSITIVE + return true; +#else + return false; +#endif +} + +bool File::operator== (const File& other) const throw() +{ + // case-insensitive on Windows, but not on linux. +#if NAMES_ARE_CASE_SENSITIVE + return fullPath == other.fullPath; +#else + return fullPath.equalsIgnoreCase (other.fullPath); +#endif +} + +bool File::operator!= (const File& other) const throw() +{ + return ! operator== (other); +} + +bool File::exists() const throw() +{ + return juce_fileExists (fullPath, false); +} + +bool File::existsAsFile() const throw() +{ + return juce_fileExists (fullPath, true); +} + +bool File::isDirectory() const throw() +{ + return juce_isDirectory (fullPath); +} + +bool File::hasWriteAccess() const throw() +{ + if (exists()) + return juce_canWriteToFile (fullPath); + +#ifndef JUCE_WIN32 + else if ((! isDirectory()) && fullPath.containsChar (separator)) + return getParentDirectory().hasWriteAccess(); + else + return false; +#else + // on windows, it seems that even read-only directories can still be written into, + // so checking the parent directory's permissions would return the wrong result.. + else + return true; +#endif +} + +bool File::setReadOnly (const bool shouldBeReadOnly, + const bool applyRecursively) const throw() +{ + bool worked = true; + + if (applyRecursively && isDirectory()) + { + OwnedArray subFiles; + findChildFiles (subFiles, File::findFilesAndDirectories, false); + + for (int i = subFiles.size(); --i >= 0;) + worked = subFiles[i]->setReadOnly (shouldBeReadOnly, true) && worked; + } + + return juce_setFileReadOnly (fullPath, shouldBeReadOnly) && worked; +} + +bool File::deleteFile() const throw() +{ + return (! exists()) + || juce_deleteFile (fullPath); +} + +bool File::deleteRecursively() const throw() +{ + bool worked = true; + + if (isDirectory()) + { + OwnedArray subFiles; + findChildFiles (subFiles, File::findFilesAndDirectories, false); + + for (int i = subFiles.size(); --i >= 0;) + worked = subFiles[i]->deleteRecursively() && worked; + } + + return deleteFile() && worked; +} + +bool File::moveFileTo (const File& newFile) const throw() +{ + if (newFile.fullPath == fullPath) + return true; + +#if ! NAMES_ARE_CASE_SENSITIVE + if (*this != newFile) +#endif + if (! newFile.deleteFile()) + return false; + + return juce_moveFile (fullPath, newFile.fullPath); +} + +bool File::copyFileTo (const File& newFile) const throw() +{ + if (*this == newFile) + return true; + + if (! newFile.deleteFile()) + return false; + + return juce_copyFile (fullPath, newFile.fullPath); +} + +bool File::copyDirectoryTo (const File& newDirectory) const throw() +{ + if (isDirectory() && newDirectory.createDirectory()) + { + OwnedArray subFiles; + findChildFiles (subFiles, File::findFiles, false); + + int i; + for (i = 0; i < subFiles.size(); ++i) + if (! subFiles[i]->copyFileTo (newDirectory.getChildFile (subFiles[i]->getFileName()))) + return false; + + subFiles.clear(); + findChildFiles (subFiles, File::findDirectories, false); + + for (i = 0; i < subFiles.size(); ++i) + if (! subFiles[i]->copyDirectoryTo (newDirectory.getChildFile (subFiles[i]->getFileName()))) + return false; + + return true; + } + + return false; +} + +const String File::getPathUpToLastSlash() const throw() +{ + const int lastSlash = fullPath.lastIndexOfChar (separator); + + if (lastSlash > 0) + return fullPath.substring (0, lastSlash); + else if (lastSlash == 0) + return separatorString; + else + return fullPath; +} + +const File File::getParentDirectory() const throw() +{ + return File (getPathUpToLastSlash()); +} + +const String File::getFileName() const throw() +{ + return fullPath.substring (fullPath.lastIndexOfChar (separator) + 1); +} + +int File::hashCode() const throw() +{ + return fullPath.hashCode(); +} + +int64 File::hashCode64() const throw() +{ + return fullPath.hashCode64(); +} + +const String File::getFileNameWithoutExtension() const throw() +{ + const int lastSlash = fullPath.lastIndexOfChar (separator) + 1; + const int lastDot = fullPath.lastIndexOfChar (T('.')); + + if (lastDot > lastSlash) + return fullPath.substring (lastSlash, lastDot); + else + return fullPath.substring (lastSlash); +} + +bool File::isAChildOf (const File& potentialParent) const throw() +{ + const String ourPath (getPathUpToLastSlash()); + +#if NAMES_ARE_CASE_SENSITIVE + if (potentialParent.fullPath == ourPath) +#else + if (potentialParent.fullPath.equalsIgnoreCase (ourPath)) +#endif + { + return true; + } + else if (potentialParent.fullPath.length() >= ourPath.length()) + { + return false; + } + else + { + return getParentDirectory().isAChildOf (potentialParent); + } +} + +bool File::isAbsolutePath (const String& path) throw() +{ + return path.startsWithChar (T('/')) || path.startsWithChar (T('\\')) +#if JUCE_WIN32 + || (path.isNotEmpty() && ((const String&) path)[1] == T(':')); +#else + || path.startsWithChar (T('~')); +#endif +} + +const File File::getChildFile (String relativePath) const throw() +{ + if (isAbsolutePath (relativePath)) + { + // the path is really absolute.. + return File (relativePath); + } + else + { + // it's relative, so remove any ../ or ./ bits at the start. + String path (fullPath); + + if (relativePath[0] == T('.')) + { +#if JUCE_WIN32 + relativePath = relativePath.replaceCharacter (T('/'), T('\\')).trimStart(); +#else + relativePath = relativePath.replaceCharacter (T('\\'), T('/')).trimStart(); +#endif + while (relativePath[0] == T('.')) + { + if (relativePath[1] == T('.')) + { + if (relativePath [2] == 0 || relativePath[2] == separator) + { + const int lastSlash = path.lastIndexOfChar (separator); + if (lastSlash > 0) + path = path.substring (0, lastSlash); + + relativePath = relativePath.substring (3); + } + else + { + break; + } + } + else if (relativePath[1] == separator) + { + relativePath = relativePath.substring (2); + } + else + { + break; + } + } + } + + if (! path.endsWithChar (separator)) + path += separator; + + return File (path + relativePath); + } +} + +const File File::getSiblingFile (const String& fileName) const throw() +{ + return getParentDirectory().getChildFile (fileName); +} + +int64 File::getSize() const throw() +{ + return juce_getFileSize (fullPath); +} + +const String File::descriptionOfSizeInBytes (const int64 bytes) +{ + if (bytes == 1) + { + return "1 byte"; + } + else if (bytes < 1024) + { + return String ((int) bytes) + " bytes"; + } + else if (bytes < 1024 * 1024) + { + return String (bytes / 1024.0, 1) + " KB"; + } + else if (bytes < 1024 * 1024 * 1024) + { + return String (bytes / (1024.0 * 1024.0), 1) + " MB"; + } + else + { + return String (bytes / (1024.0 * 1024.0 * 1024.0), 1) + " GB"; + } +} + +bool File::create() const throw() +{ + if (! exists()) + { + const File parentDir (getParentDirectory()); + + if (parentDir == *this || ! parentDir.createDirectory()) + return false; + + void* const fh = juce_fileOpen (fullPath, true); + + if (fh == 0) + return false; + + juce_fileClose (fh); + } + + return true; +} + +bool File::createDirectory() const throw() +{ + if (! isDirectory()) + { + const File parentDir (getParentDirectory()); + + if (parentDir == *this || ! parentDir.createDirectory()) + return false; + + String dir (fullPath); + + while (dir.endsWithChar (separator)) + dir [dir.length() - 1] = 0; + + juce_createDirectory (dir); + + return isDirectory(); + } + + return true; +} + +const Time File::getCreationTime() const throw() +{ + int64 m, a, c; + juce_getFileTimes (fullPath, m, a, c); + return Time (c); +} + +bool File::setCreationTime (const Time& t) const throw() +{ + return juce_setFileTimes (fullPath, 0, 0, t.toMilliseconds()); +} + +const Time File::getLastModificationTime() const throw() +{ + int64 m, a, c; + juce_getFileTimes (fullPath, m, a, c); + return Time (m); +} + +bool File::setLastModificationTime (const Time& t) const throw() +{ + return juce_setFileTimes (fullPath, t.toMilliseconds(), 0, 0); +} + +const Time File::getLastAccessTime() const throw() +{ + int64 m, a, c; + juce_getFileTimes (fullPath, m, a, c); + return Time (a); +} + +bool File::setLastAccessTime (const Time& t) const throw() +{ + return juce_setFileTimes (fullPath, 0, t.toMilliseconds(), 0); +} + +bool File::loadFileAsData (MemoryBlock& destBlock) const throw() +{ + if (! existsAsFile()) + return false; + + FileInputStream in (*this); + return getSize() == in.readIntoMemoryBlock (destBlock); +} + +const String File::loadFileAsString() const throw() +{ + if (! existsAsFile()) + return String::empty; + + FileInputStream in (*this); + return in.readEntireStreamAsString(); +} + +static inline bool fileTypeMatches (const int whatToLookFor, + const bool isDir, + const bool isHidden) +{ + return (whatToLookFor & (isDir ? File::findDirectories + : File::findFiles)) != 0 + && ((! isHidden) + || (whatToLookFor & File::ignoreHiddenFiles) == 0); +} + +int File::findChildFiles (OwnedArray& results, + const int whatToLookFor, + const bool searchRecursively, + const String& wildCardPattern) const throw() +{ + // you have to specify the type of files you're looking for! + jassert ((whatToLookFor & (findFiles | findDirectories)) != 0); + + int total = 0; + + // find child files or directories in this directory first.. + if (isDirectory()) + { + String path (fullPath); + if (! path.endsWithChar (separator)) + path += separator; + + String filename; + bool isDirectory, isHidden; + + void* const handle = juce_findFileStart (path, + wildCardPattern, + filename, + &isDirectory, &isHidden, + 0, 0, 0, 0); + + if (handle != 0) + { + do + { + if (fileTypeMatches (whatToLookFor, isDirectory, isHidden) + && ! filename.containsOnly (T("."))) + { + results.add (new File (path + filename, 0)); + ++total; + } + + } while (juce_findFileNext (handle, filename, &isDirectory, &isHidden, 0, 0, 0, 0)); + + juce_findFileClose (handle); + } + } + else + { + // trying to search for files inside a non-directory? + //jassertfalse + } + + // and recurse down if required. + if (searchRecursively) + { + OwnedArray subDirectories; + findChildFiles (subDirectories, File::findDirectories, false); + + for (int i = 0; i < subDirectories.size(); ++i) + { + total += subDirectories.getUnchecked(i) + ->findChildFiles (results, + whatToLookFor, + true, + wildCardPattern); + } + } + + return total; +} + +int File::getNumberOfChildFiles (const int whatToLookFor, + const String& wildCardPattern) const throw() +{ + // you have to specify the type of files you're looking for! + jassert (whatToLookFor > 0 && whatToLookFor <= 3); + + int count = 0; + + if (isDirectory()) + { + String filename; + bool isDirectory, isHidden; + + void* const handle = juce_findFileStart (fullPath, + wildCardPattern, + filename, + &isDirectory, &isHidden, + 0, 0, 0, 0); + + if (handle != 0) + { + do + { + if (fileTypeMatches (whatToLookFor, isDirectory, isHidden) + && ! filename.containsOnly (T("."))) + { + ++count; + } + + } while (juce_findFileNext (handle, filename, &isDirectory, &isHidden, 0, 0, 0, 0)); + + juce_findFileClose (handle); + } + } + else + { + // trying to search for files inside a non-directory? + jassertfalse + } + + return count; +} + +const File File::getNonexistentChildFile (const String& prefix_, + const String& suffix, + bool putNumbersInBrackets) const throw() +{ + File f (getChildFile (prefix_ + suffix)); + + if (f.exists()) + { + int num = 2; + String prefix (prefix_); + + // remove any bracketed numbers that may already be on the end.. + if (prefix.trim().endsWithChar (T(')'))) + { + putNumbersInBrackets = true; + + const int openBracks = prefix.lastIndexOfChar (T('(')); + const int closeBracks = prefix.lastIndexOfChar (T(')')); + + if (openBracks > 0 + && closeBracks > openBracks + && prefix.substring (openBracks + 1, closeBracks).containsOnly (T("0123456789"))) + { + num = prefix.substring (openBracks + 1, closeBracks).getIntValue() + 1; + prefix = prefix.substring (0, openBracks); + } + } + + // also use brackets if it ends in a digit. + putNumbersInBrackets = putNumbersInBrackets + || CharacterFunctions::isDigit (prefix.getLastCharacter()); + + do + { + if (putNumbersInBrackets) + f = getChildFile (prefix + T('(') + String (num++) + T(')') + suffix); + else + f = getChildFile (prefix + String (num++) + suffix); + + } while (f.exists()); + } + + return f; +} + +const File File::getNonexistentSibling (const bool putNumbersInBrackets) const throw() +{ + if (exists()) + { + return getParentDirectory() + .getNonexistentChildFile (getFileNameWithoutExtension(), + getFileExtension(), + putNumbersInBrackets); + } + else + { + return *this; + } +} + +const String File::getFileExtension() const throw() +{ + String ext; + + if (! isDirectory()) + { + const int indexOfDot = fullPath.lastIndexOfChar (T('.')); + + if (indexOfDot > fullPath.lastIndexOfChar (separator)) + ext = fullPath.substring (indexOfDot); + } + + return ext; +} + +bool File::hasFileExtension (const String& possibleSuffix) const throw() +{ + if (possibleSuffix.isEmpty()) + return fullPath.lastIndexOfChar (T('.')) <= fullPath.lastIndexOfChar (separator); + + if (fullPath.endsWithIgnoreCase (possibleSuffix)) + { + if (possibleSuffix.startsWithChar (T('.'))) + return true; + + const int dotPos = fullPath.length() - possibleSuffix.length() - 1; + + if (dotPos >= 0) + return fullPath [dotPos] == T('.'); + } + + return false; +} + +const File File::withFileExtension (const String& newExtension) const throw() +{ + if (fullPath.isEmpty()) + return File::nonexistent; + + String filePart (getFileName()); + + int i = filePart.lastIndexOfChar (T('.')); + if (i < 0) + i = filePart.length(); + + String newExt (newExtension); + + if (newExt.isNotEmpty() && ! newExt.startsWithChar (T('.'))) + newExt = T(".") + newExt; + + return getSiblingFile (filePart.substring (0, i) + newExt); +} + +bool File::startAsProcess (const String& parameters) const throw() +{ + return exists() + && juce_launchFile (fullPath, parameters); +} + +FileInputStream* File::createInputStream() const throw() +{ + if (existsAsFile()) + return new FileInputStream (*this); + else + return 0; +} + +FileOutputStream* File::createOutputStream (const int bufferSize) const throw() +{ + FileOutputStream* const out = new FileOutputStream (*this, bufferSize); + + if (out->failedToOpen()) + { + delete out; + return 0; + } + else + { + return out; + } +} + +bool File::appendData (const void* const dataToAppend, + const int numberOfBytes) const throw() +{ + if (numberOfBytes > 0) + { + FileOutputStream* const out = createOutputStream(); + + if (out == 0) + return false; + + out->write (dataToAppend, numberOfBytes); + delete out; + } + + return true; +} + +bool File::replaceWithData (const void* const dataToWrite, + const int numberOfBytes) const throw() +{ + jassert (numberOfBytes >= 0); // a negative number of bytes?? + + if (numberOfBytes <= 0) + return deleteFile(); + + const File tempFile (getSiblingFile (T(".") + getFileName()).getNonexistentSibling (false)); + + if (tempFile.appendData (dataToWrite, numberOfBytes) + && tempFile.moveFileTo (*this)) + { + return true; + } + + tempFile.deleteFile(); + return false; +} + +bool File::appendText (const String& text, + const bool asUnicode, + const bool writeUnicodeHeaderBytes) const throw() +{ + FileOutputStream* const out = createOutputStream(); + + if (out != 0) + { + out->writeText (text, asUnicode, writeUnicodeHeaderBytes); + delete out; + + return true; + } + + return false; +} + +bool File::printf (const tchar* pf, ...) const throw() +{ + va_list list; + va_start (list, pf); + + String text; + text.vprintf (pf, list); + + return appendData ((const char*) text, text.length()); +} + +bool File::replaceWithText (const String& textToWrite, + const bool asUnicode, + const bool writeUnicodeHeaderBytes) const throw() +{ + const File tempFile (getSiblingFile (T(".") + getFileName()).getNonexistentSibling (false)); + + if (tempFile.appendText (textToWrite, asUnicode, writeUnicodeHeaderBytes) + && tempFile.moveFileTo (*this)) + { + return true; + } + + tempFile.deleteFile(); + return false; +} + +const String File::createLegalPathName (const String& original) throw() +{ + String s (original); + String start; + + if (s[1] == T(':')) + { + start = s.substring (0, 2); + s = s.substring (2); + } + + return start + s.removeCharacters (T("\"#@,;:<>*^|?")) + .substring (0, 1024); +} + +const String File::createLegalFileName (const String& original) throw() +{ + String s (original.removeCharacters (T("\"#@,;:<>*^|?\\/"))); + + const int maxLength = 128; // only the length of the filename, not the whole path + const int len = s.length(); + + if (len > maxLength) + { + const int lastDot = s.lastIndexOfChar (T('.')); + + if (lastDot > jmax (0, len - 12)) + { + s = s.substring (0, maxLength - (len - lastDot)) + + s.substring (lastDot); + } + else + { + s = s.substring (0, maxLength); + } + } + + return s; +} + +const String File::getRelativePathFrom (const File& dir) const throw() +{ + String thisPath (fullPath); + + { + int len = thisPath.length(); + while (--len >= 0 && thisPath [len] == File::separator) + thisPath [len] = 0; + } + + String dirPath ((dir.existsAsFile()) ? dir.getParentDirectory().getFullPathName() + : dir.fullPath); + + if (! dirPath.endsWithChar (separator)) + dirPath += separator; + + const int len = jmin (thisPath.length(), dirPath.length()); + int commonBitLength = 0; + + for (int i = 0; i < len; ++i) + { +#if NAMES_ARE_CASE_SENSITIVE + if (thisPath[i] != dirPath[i]) +#else + if (CharacterFunctions::toLowerCase (thisPath[i]) + != CharacterFunctions::toLowerCase (dirPath[i])) +#endif + { + break; + } + + ++commonBitLength; + } + + while (commonBitLength > 0 && thisPath [commonBitLength - 1] != File::separator) + --commonBitLength; + + // if the only common bit is the root, then just return the full path.. +#if JUCE_WIN32 + if (commonBitLength <= 0 + || (commonBitLength == 1 && thisPath [1] == File::separator) + || (commonBitLength <= 3 && thisPath [1] == T(':'))) +#else + if (commonBitLength <= 0 + || (commonBitLength == 1 && thisPath [1] == File::separator)) +#endif + return fullPath; + + thisPath = thisPath.substring (commonBitLength); + dirPath = dirPath.substring (commonBitLength); + + while (dirPath.isNotEmpty()) + { +#if JUCE_WIN32 + thisPath = T("..\\") + thisPath; +#else + thisPath = T("../") + thisPath; +#endif + + const int sep = dirPath.indexOfChar (separator); + + if (sep >= 0) + dirPath = dirPath.substring (sep + 1); + else + dirPath = String::empty; + } + + return thisPath; +} + +void File::findFileSystemRoots (OwnedArray& destArray) throw() +{ + const StringArray roots (juce_getFileSystemRoots()); + + for (int i = 0; i < roots.size(); ++i) + destArray.add (new File (roots[i])); +} + +const String File::getVolumeLabel() const throw() +{ + int serialNum; + return juce_getVolumeLabel (fullPath, serialNum); +} + +int File::getVolumeSerialNumber() const throw() +{ + int serialNum; + juce_getVolumeLabel (fullPath, serialNum); + + return serialNum; +} + +const File File::createTempFile (const String& fileNameEnding) throw() +{ + String tempName (T("temp")); + static int tempNum = 0; + tempName << tempNum++ << fileNameEnding; + + const File tempFile (getSpecialLocation (tempDirectory) + .getChildFile (tempName)); + + if (tempFile.exists()) + return createTempFile (fileNameEnding); + else + return tempFile; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_File.cpp *********/ + +/********* Start of inlined file: juce_FileInputStream.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +void* juce_fileOpen (const String& path, bool forWriting) throw(); +void juce_fileClose (void* handle) throw(); +int juce_fileRead (void* handle, void* buffer, int size) throw(); +int64 juce_fileSetPosition (void* handle, int64 pos) throw(); + +FileInputStream::FileInputStream (const File& f) + : file (f), + currentPosition (0), + needToSeek (true) +{ + totalSize = f.getSize(); + + fileHandle = juce_fileOpen (f.getFullPathName(), false); +} + +FileInputStream::~FileInputStream() +{ + juce_fileClose (fileHandle); +} + +int64 FileInputStream::getTotalLength() +{ + return totalSize; +} + +int FileInputStream::read (void* buffer, int bytesToRead) +{ + int num = 0; + + if (needToSeek) + { + if (juce_fileSetPosition (fileHandle, currentPosition) < 0) + return 0; + + needToSeek = false; + } + + num = juce_fileRead (fileHandle, buffer, bytesToRead); + currentPosition += num; + + return num; +} + +bool FileInputStream::isExhausted() +{ + return currentPosition >= totalSize; +} + +int64 FileInputStream::getPosition() +{ + return currentPosition; +} + +bool FileInputStream::setPosition (int64 pos) +{ + pos = jlimit ((int64) 0, totalSize, pos); + + needToSeek |= (currentPosition != pos); + currentPosition = pos; + + return true; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_FileInputStream.cpp *********/ + +/********* Start of inlined file: juce_FileOutputStream.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +void* juce_fileOpen (const String& path, bool forWriting) throw(); +void juce_fileClose (void* handle) throw(); +int juce_fileWrite (void* handle, const void* buffer, int size) throw(); +void juce_fileFlush (void* handle) throw(); +int64 juce_fileGetPosition (void* handle) throw(); +int64 juce_fileSetPosition (void* handle, int64 pos) throw(); + +FileOutputStream::FileOutputStream (const File& f, + const int bufferSize_) + : file (f), + bufferSize (bufferSize_), + bytesInBuffer (0) +{ + fileHandle = juce_fileOpen (f.getFullPathName(), true); + + if (fileHandle != 0) + { + currentPosition = juce_fileGetPosition (fileHandle); + + if (currentPosition < 0) + { + jassertfalse + juce_fileClose (fileHandle); + fileHandle = 0; + } + } + + buffer = (char*) juce_malloc (jmax (bufferSize_, 16)); +} + +FileOutputStream::~FileOutputStream() +{ + flush(); + + juce_fileClose (fileHandle); + juce_free (buffer); +} + +int64 FileOutputStream::getPosition() +{ + return currentPosition; +} + +bool FileOutputStream::setPosition (int64 newPosition) +{ + if (newPosition != currentPosition) + { + flush(); + currentPosition = juce_fileSetPosition (fileHandle, newPosition); + } + + return newPosition == currentPosition; +} + +void FileOutputStream::flush() +{ + if (bytesInBuffer > 0) + { + juce_fileWrite (fileHandle, buffer, bytesInBuffer); + bytesInBuffer = 0; + } + + juce_fileFlush (fileHandle); +} + +bool FileOutputStream::write (const void* const src, const int numBytes) +{ + if (bytesInBuffer + numBytes < bufferSize) + { + memcpy (buffer + bytesInBuffer, src, numBytes); + bytesInBuffer += numBytes; + currentPosition += numBytes; + } + else + { + if (bytesInBuffer > 0) + { + // flush the reservoir + const bool wroteOk = (juce_fileWrite (fileHandle, buffer, bytesInBuffer) == bytesInBuffer); + bytesInBuffer = 0; + + if (! wroteOk) + return false; + } + + if (numBytes < bufferSize) + { + memcpy (buffer + bytesInBuffer, src, numBytes); + bytesInBuffer += numBytes; + currentPosition += numBytes; + } + else + { + const int bytesWritten = juce_fileWrite (fileHandle, src, numBytes); + currentPosition += bytesWritten; + + return bytesWritten == numBytes; + } + } + + return true; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_FileOutputStream.cpp *********/ + +/********* Start of inlined file: juce_FileSearchPath.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +FileSearchPath::FileSearchPath() +{ +} + +FileSearchPath::FileSearchPath (const String& path) +{ + init (path); +} + +FileSearchPath::FileSearchPath (const FileSearchPath& other) + : directories (other.directories) +{ +} + +FileSearchPath::~FileSearchPath() +{ +} + +const FileSearchPath& FileSearchPath::operator= (const String& path) +{ + init (path); + return *this; +} + +void FileSearchPath::init (const String& path) +{ + directories.clear(); + directories.addTokens (path, T(";"), T("\"")); + directories.trim(); + directories.removeEmptyStrings(); + + for (int i = directories.size(); --i >= 0;) + directories.set (i, directories[i].unquoted()); +} + +int FileSearchPath::getNumPaths() const +{ + return directories.size(); +} + +const File FileSearchPath::operator[] (const int index) const +{ + return File (directories [index]); +} + +const String FileSearchPath::toString() const +{ + StringArray directories2 (directories); + for (int i = directories2.size(); --i >= 0;) + if (directories2[i].containsChar (T(';'))) + directories2.set (i, directories2[i].quoted()); + + return directories2.joinIntoString (T(";")); +} + +void FileSearchPath::add (const File& dir, const int insertIndex) +{ + directories.insert (insertIndex, dir.getFullPathName()); +} + +void FileSearchPath::addIfNotAlreadyThere (const File& dir) +{ + for (int i = 0; i < directories.size(); ++i) + if (File (directories[i]) == dir) + return; + + add (dir); +} + +void FileSearchPath::remove (const int index) +{ + directories.remove (index); +} + +void FileSearchPath::addPath (const FileSearchPath& other) +{ + for (int i = 0; i < other.getNumPaths(); ++i) + addIfNotAlreadyThere (other[i]); +} + +void FileSearchPath::removeRedundantPaths() +{ + for (int i = directories.size(); --i >= 0;) + { + const File d1 (directories[i]); + + for (int j = directories.size(); --j >= 0;) + { + const File d2 (directories[j]); + + if ((i != j) && (d1.isAChildOf (d2) || d1 == d2)) + { + directories.remove (i); + break; + } + } + } +} + +void FileSearchPath::removeNonExistentPaths() +{ + for (int i = directories.size(); --i >= 0;) + if (! File (directories[i]).isDirectory()) + directories.remove (i); +} + +int FileSearchPath::findChildFiles (OwnedArray& results, + const int whatToLookFor, + const bool searchRecursively, + const String& wildCardPattern) const +{ + int total = 0; + + for (int i = 0; i < directories.size(); ++i) + total += operator[] (i).findChildFiles (results, + whatToLookFor, + searchRecursively, + wildCardPattern); + + return total; +} + +bool FileSearchPath::isFileInPath (const File& fileToCheck, + const bool checkRecursively) const +{ + for (int i = directories.size(); --i >= 0;) + { + const File d (directories[i]); + + if (checkRecursively) + { + if (fileToCheck.isAChildOf (d)) + return true; + } + else + { + if (fileToCheck.getParentDirectory() == d) + return true; + } + } + + return false; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_FileSearchPath.cpp *********/ + +/********* Start of inlined file: juce_NamedPipe.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +NamedPipe::NamedPipe() + : internal (0) +{ +} + +NamedPipe::~NamedPipe() +{ + close(); +} + +bool NamedPipe::openExisting (const String& pipeName) +{ + currentPipeName = pipeName; + return openInternal (pipeName, false); +} + +bool NamedPipe::createNewPipe (const String& pipeName) +{ + currentPipeName = pipeName; + return openInternal (pipeName, true); +} + +bool NamedPipe::isOpen() const throw() +{ + return internal != 0; +} + +const String NamedPipe::getName() const throw() +{ + return currentPipeName; +} + +// other methods for this class are implemented in the platform-specific files + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_NamedPipe.cpp *********/ + +/********* Start of inlined file: juce_Socket.cpp *********/ +#ifdef _WIN32 + + #include + + #ifdef _MSC_VER + #pragma warning (disable : 4127 4389 4018) + #endif + +#else + #ifndef LINUX + #include + #endif + + #include + #include + #include + #include + #include + #include + #include + #include + #include +#endif + +BEGIN_JUCE_NAMESPACE + +#if JUCE_WIN32 + +typedef int (__stdcall juce_CloseWin32SocketLibCall) (void); +juce_CloseWin32SocketLibCall* juce_CloseWin32SocketLib = 0; + +static void initWin32Sockets() +{ + static CriticalSection lock; + const ScopedLock sl (lock); + + if (juce_CloseWin32SocketLib == 0) + { + WSADATA wsaData; + const WORD wVersionRequested = MAKEWORD (1, 1); + WSAStartup (wVersionRequested, &wsaData); + + juce_CloseWin32SocketLib = &WSACleanup; + } +} + +#endif + +static bool resetSocketOptions (const int handle, const bool isDatagram) throw() +{ + if (handle <= 0) + return false; + + const int sndBufSize = 65536; + const int rcvBufSize = 65536; + const int one = 1; + + return setsockopt (handle, SOL_SOCKET, SO_RCVBUF, (const char*) &rcvBufSize, sizeof (int)) == 0 + && setsockopt (handle, SOL_SOCKET, SO_SNDBUF, (const char*) &sndBufSize, sizeof (int)) == 0 + && (isDatagram || (setsockopt (handle, IPPROTO_TCP, TCP_NODELAY, (const char*) &one, sizeof (int)) == 0)); +} + +static bool bindSocketToPort (const int handle, const int port) throw() +{ + if (handle == 0 || port <= 0) + return false; + + struct sockaddr_in servTmpAddr; + zerostruct (servTmpAddr); + servTmpAddr.sin_family = PF_INET; + servTmpAddr.sin_addr.s_addr = htonl (INADDR_ANY); + servTmpAddr.sin_port = htons ((uint16) port); + + return bind (handle, (struct sockaddr*) &servTmpAddr, sizeof (struct sockaddr_in)) >= 0; +} + +static int readSocket (const int handle, + void* const destBuffer, const int maxBytesToRead, + bool volatile& connected) throw() +{ + int bytesRead = 0; + + while (bytesRead < maxBytesToRead) + { + int bytesThisTime; + +#if JUCE_WIN32 + bytesThisTime = recv (handle, ((char*) destBuffer) + bytesRead, maxBytesToRead - bytesRead, 0); +#else + while ((bytesThisTime = ::read (handle, ((char*) destBuffer) + bytesRead, maxBytesToRead - bytesRead)) < 0 + && errno == EINTR + && connected) + { + } +#endif + + if (bytesThisTime <= 0 || ! connected) + { + if (bytesRead == 0) + bytesRead = -1; + + break; + } + + bytesRead += bytesThisTime; + } + + return bytesRead; +} + +static int waitForReadiness (const int handle, const bool forReading, + const int timeoutMsecs) throw() +{ + struct timeval timeout; + struct timeval* timeoutp; + + if (timeoutMsecs >= 0) + { + timeout.tv_sec = timeoutMsecs / 1000; + timeout.tv_usec = (timeoutMsecs % 1000) * 1000; + timeoutp = &timeout; + } + else + { + timeoutp = 0; + } + + fd_set rset, wset; + FD_ZERO (&rset); + FD_SET (handle, &rset); + FD_ZERO (&wset); + FD_SET (handle, &wset); + + fd_set* const prset = forReading ? &rset : 0; + fd_set* const pwset = forReading ? 0 : &wset; + +#if JUCE_WIN32 + if (select (handle + 1, prset, pwset, 0, timeoutp) < 0) + return -1; +#else + { + int result; + while ((result = select (handle + 1, prset, pwset, 0, timeoutp)) < 0 + && errno == EINTR) + { + } + + if (result < 0) + return -1; + } +#endif + + { + int opt; + +#if defined (JUCE_LINUX) || (defined (JUCE_MAC) && ! MACOS_10_2_OR_EARLIER) + socklen_t len = sizeof (opt); +#else + int len = sizeof (opt); +#endif + + if (getsockopt (handle, SOL_SOCKET, SO_ERROR, (char*) &opt, &len) < 0 + || opt != 0) + return -1; + } + + if ((forReading && FD_ISSET (handle, &rset)) + || ((! forReading) && FD_ISSET (handle, &wset))) + return 1; + + return 0; +} + +static bool setSocketBlockingState (const int handle, const bool shouldBlock) throw() +{ +#if JUCE_WIN32 + u_long nonBlocking = shouldBlock ? 0 : 1; + + if (ioctlsocket (handle, FIONBIO, &nonBlocking) != 0) + return false; +#else + int socketFlags = fcntl (handle, F_GETFL, 0); + + if (socketFlags == -1) + return false; + + if (shouldBlock) + socketFlags &= ~O_NONBLOCK; + else + socketFlags |= O_NONBLOCK; + + if (fcntl (handle, F_SETFL, socketFlags) != 0) + return false; +#endif + + return true; +} + +static bool connectSocket (int volatile& handle, + const bool isDatagram, + void** serverAddress, + const String& hostName, + const int portNumber, + const int timeOutMillisecs) throw() +{ + struct hostent* const hostEnt = gethostbyname (hostName); + + if (hostEnt == 0) + return false; + + struct in_addr targetAddress; + memcpy (&targetAddress.s_addr, + *(hostEnt->h_addr_list), + sizeof (targetAddress.s_addr)); + + struct sockaddr_in servTmpAddr; + zerostruct (servTmpAddr); + servTmpAddr.sin_family = PF_INET; + servTmpAddr.sin_addr = targetAddress; + servTmpAddr.sin_port = htons ((uint16) portNumber); + + if (handle < 0) + handle = (int) socket (AF_INET, isDatagram ? SOCK_DGRAM : SOCK_STREAM, 0); + + if (handle < 0) + return false; + + if (isDatagram) + { + *serverAddress = new struct sockaddr_in(); + *((struct sockaddr_in*) *serverAddress) = servTmpAddr; + + return true; + } + + setSocketBlockingState (handle, false); + + const int result = ::connect (handle, (struct sockaddr*) &servTmpAddr, sizeof (struct sockaddr_in)); + + if (result < 0) + { +#if JUCE_WIN32 + if (result == SOCKET_ERROR && WSAGetLastError() == WSAEWOULDBLOCK) +#else + if (errno == EINPROGRESS) +#endif + { + if (waitForReadiness (handle, false, timeOutMillisecs) != 1) + { + setSocketBlockingState (handle, true); + return false; + } + } + } + + setSocketBlockingState (handle, true); + resetSocketOptions (handle, false); + + return true; +} + +StreamingSocket::StreamingSocket() + : portNumber (0), + handle (-1), + connected (false), + isListener (false) +{ +#if JUCE_WIN32 + initWin32Sockets(); +#endif +} + +StreamingSocket::StreamingSocket (const String& hostName_, + const int portNumber_, + const int handle_) + : hostName (hostName_), + portNumber (portNumber_), + handle (handle_), + connected (true), + isListener (false) +{ +#if JUCE_WIN32 + initWin32Sockets(); +#endif + + resetSocketOptions (handle_, false); +} + +StreamingSocket::~StreamingSocket() +{ + close(); +} + +int StreamingSocket::read (void* destBuffer, const int maxBytesToRead) +{ + return (connected && ! isListener) ? readSocket (handle, destBuffer, maxBytesToRead, connected) + : -1; +} + +int StreamingSocket::write (const void* sourceBuffer, const int numBytesToWrite) +{ + if (isListener || ! connected) + return -1; + +#if JUCE_WIN32 + return send (handle, (const char*) sourceBuffer, numBytesToWrite, 0); +#else + int result; + + while ((result = ::write (handle, sourceBuffer, numBytesToWrite)) < 0 + && errno == EINTR) + { + } + + return result; +#endif +} + +int StreamingSocket::waitUntilReady (const bool readyForReading, + const int timeoutMsecs) const +{ + return connected ? waitForReadiness (handle, readyForReading, timeoutMsecs) + : -1; +} + +bool StreamingSocket::bindToPort (const int port) +{ + return bindSocketToPort (handle, port); +} + +bool StreamingSocket::connect (const String& remoteHostName, + const int remotePortNumber, + const int timeOutMillisecs) +{ + if (isListener) + { + jassertfalse // a listener socket can't connect to another one! + return false; + } + + if (connected) + close(); + + hostName = remoteHostName; + portNumber = remotePortNumber; + isListener = false; + + connected = connectSocket (handle, false, 0, remoteHostName, + remotePortNumber, timeOutMillisecs); + + if (! (connected && resetSocketOptions (handle, false))) + { + close(); + return false; + } + + return true; +} + +void StreamingSocket::close() +{ +#if JUCE_WIN32 + closesocket (handle); + connected = false; +#else + if (connected) + { + connected = false; + + if (isListener) + { + // need to do this to interrupt the accept() function.. + StreamingSocket temp; + temp.connect ("localhost", portNumber, 1000); + } + } + + ::close (handle); +#endif + + hostName = String::empty; + portNumber = 0; + handle = -1; + isListener = false; +} + +bool StreamingSocket::createListener (const int newPortNumber) +{ + if (connected) + close(); + + hostName = "listener"; + portNumber = newPortNumber; + isListener = true; + + struct sockaddr_in servTmpAddr; + zerostruct (servTmpAddr); + servTmpAddr.sin_family = PF_INET; + servTmpAddr.sin_addr.s_addr = htonl (INADDR_ANY); + servTmpAddr.sin_port = htons ((uint16) portNumber); + + handle = (int) socket (AF_INET, SOCK_STREAM, 0); + + if (handle < 0) + return false; + + const int reuse = 1; + setsockopt (handle, SOL_SOCKET, SO_REUSEADDR, (const char*) &reuse, sizeof (reuse)); + + if (bind (handle, (struct sockaddr*) &servTmpAddr, sizeof (struct sockaddr_in)) < 0 + || listen (handle, SOMAXCONN) < 0) + { + close(); + return false; + } + + connected = true; + return true; +} + +StreamingSocket* StreamingSocket::waitForNextConnection() const +{ + jassert (isListener || ! connected); // to call this method, you first have to use createListener() to + // prepare this socket as a listener. + + if (connected && isListener) + { + struct sockaddr address; + +#if defined (JUCE_LINUX) || (defined (JUCE_MAC) && ! MACOS_10_2_OR_EARLIER) + socklen_t len = sizeof (sockaddr); +#else + int len = sizeof (sockaddr); +#endif + const int newSocket = (int) accept (handle, &address, &len); + + if (newSocket >= 0 && connected) + return new StreamingSocket (inet_ntoa (((struct sockaddr_in*) &address)->sin_addr), + portNumber, newSocket); + } + + return 0; +} + +bool StreamingSocket::isLocal() const throw() +{ + return hostName == T("127.0.0.1"); +} + +DatagramSocket::DatagramSocket (const int localPortNumber) + : portNumber (0), + handle (-1), + connected (false), + serverAddress (0) +{ +#if JUCE_WIN32 + initWin32Sockets(); +#endif + + handle = (int) socket (AF_INET, SOCK_DGRAM, 0); + bindToPort (localPortNumber); +} + +DatagramSocket::DatagramSocket (const String& hostName_, const int portNumber_, + const int handle_, const int localPortNumber) + : hostName (hostName_), + portNumber (portNumber_), + handle (handle_), + connected (true), + serverAddress (0) +{ +#if JUCE_WIN32 + initWin32Sockets(); +#endif + + resetSocketOptions (handle_, true); + bindToPort (localPortNumber); +} + +DatagramSocket::~DatagramSocket() +{ + close(); + + delete ((struct sockaddr_in*) serverAddress); + serverAddress = 0; +} + +void DatagramSocket::close() +{ +#if JUCE_WIN32 + closesocket (handle); + connected = false; +#else + connected = false; + ::close (handle); +#endif + + hostName = String::empty; + portNumber = 0; + handle = -1; +} + +bool DatagramSocket::bindToPort (const int port) +{ + return bindSocketToPort (handle, port); +} + +bool DatagramSocket::connect (const String& remoteHostName, + const int remotePortNumber, + const int timeOutMillisecs) +{ + if (connected) + close(); + + hostName = remoteHostName; + portNumber = remotePortNumber; + + connected = connectSocket (handle, true, &serverAddress, + remoteHostName, remotePortNumber, + timeOutMillisecs); + + if (! (connected && resetSocketOptions (handle, true))) + { + close(); + return false; + } + + return true; +} + +DatagramSocket* DatagramSocket::waitForNextConnection() const +{ + struct sockaddr address; + +#if defined (JUCE_LINUX) || (defined (JUCE_MAC) && ! MACOS_10_2_OR_EARLIER) + socklen_t len = sizeof (sockaddr); +#else + int len = sizeof (sockaddr); +#endif + + while (waitUntilReady (true, -1) == 1) + { + char buf[1]; + + if (recvfrom (handle, buf, 0, 0, &address, &len) > 0) + { + return new DatagramSocket (inet_ntoa (((struct sockaddr_in*) &address)->sin_addr), + ntohs (((struct sockaddr_in*) &address)->sin_port), + -1, -1); + } + } + + return 0; +} + +int DatagramSocket::waitUntilReady (const bool readyForReading, + const int timeoutMsecs) const +{ + return connected ? waitForReadiness (handle, readyForReading, timeoutMsecs) + : -1; +} + +int DatagramSocket::read (void* destBuffer, const int maxBytesToRead) +{ + return connected ? readSocket (handle, destBuffer, maxBytesToRead, connected) + : -1; +} + +int DatagramSocket::write (const void* sourceBuffer, const int numBytesToWrite) +{ + // You need to call connect() first to set the server address.. + jassert (serverAddress != 0 && connected); + + return connected ? sendto (handle, (const char*) sourceBuffer, + numBytesToWrite, 0, + (const struct sockaddr*) serverAddress, + sizeof (struct sockaddr_in)) + : -1; +} + +bool DatagramSocket::isLocal() const throw() +{ + return hostName == T("127.0.0.1"); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_Socket.cpp *********/ + +/********* Start of inlined file: juce_URL.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +URL::URL() throw() +{ +} + +URL::URL (const String& url_) + : url (url_) +{ + int i = url.indexOfChar (T('?')); + + if (i >= 0) + { + do + { + const int nextAmp = url.indexOfChar (i + 1, T('&')); + const int equalsPos = url.indexOfChar (i + 1, T('=')); + + if (equalsPos > i + 1) + { + if (nextAmp < 0) + { + parameters.set (removeEscapeChars (url.substring (i + 1, equalsPos)), + removeEscapeChars (url.substring (equalsPos + 1))); + } + else if (nextAmp > 0 && equalsPos < nextAmp) + { + parameters.set (removeEscapeChars (url.substring (i + 1, equalsPos)), + removeEscapeChars (url.substring (equalsPos + 1, nextAmp))); + } + } + + i = nextAmp; + } + while (i >= 0); + + url = url.upToFirstOccurrenceOf (T("?"), false, false); + } +} + +URL::URL (const URL& other) + : url (other.url), + parameters (other.parameters), + filesToUpload (other.filesToUpload), + mimeTypes (other.mimeTypes) +{ +} + +const URL& URL::operator= (const URL& other) +{ + url = other.url; + parameters = other.parameters; + filesToUpload = other.filesToUpload; + mimeTypes = other.mimeTypes; + + return *this; +} + +URL::~URL() throw() +{ +} + +static const String getMangledParameters (const StringPairArray& parameters) +{ + String p; + + for (int i = 0; i < parameters.size(); ++i) + { + if (i > 0) + p += T("&"); + + p << URL::addEscapeChars (parameters.getAllKeys() [i]) + << T("=") + << URL::addEscapeChars (parameters.getAllValues() [i]); + } + + return p; +} + +const String URL::toString (const bool includeGetParameters) const +{ + if (includeGetParameters && parameters.size() > 0) + return url + T("?") + getMangledParameters (parameters); + else + return url; +} + +bool URL::isWellFormed() const +{ + //xxx TODO + return url.isNotEmpty(); +} + +bool URL::isProbablyAWebsiteURL (const String& possibleURL) +{ + return (possibleURL.containsChar (T('.')) + && (! possibleURL.containsChar (T('@'))) + && (! possibleURL.endsWithChar (T('.'))) + && (possibleURL.startsWithIgnoreCase (T("www.")) + || possibleURL.startsWithIgnoreCase (T("http:")) + || possibleURL.startsWithIgnoreCase (T("ftp:")) + || possibleURL.endsWithIgnoreCase (T(".com")) + || possibleURL.endsWithIgnoreCase (T(".net")) + || possibleURL.endsWithIgnoreCase (T(".org")) + || possibleURL.endsWithIgnoreCase (T(".co.uk"))) + || possibleURL.startsWithIgnoreCase (T("file:"))); +} + +bool URL::isProbablyAnEmailAddress (const String& possibleEmailAddress) +{ + const int atSign = possibleEmailAddress.indexOfChar (T('@')); + + return atSign > 0 + && possibleEmailAddress.lastIndexOfChar (T('.')) > (atSign + 1) + && (! possibleEmailAddress.endsWithChar (T('.'))); +} + +void* juce_openInternetFile (const String& url, + const String& headers, + const MemoryBlock& optionalPostData, + const bool isPost, + URL::OpenStreamProgressCallback* callback, + void* callbackContext); + +void juce_closeInternetFile (void* handle); +int juce_readFromInternetFile (void* handle, void* dest, int bytesToRead); +int juce_seekInInternetFile (void* handle, int newPosition); + +class WebInputStream : public InputStream +{ +public: + + WebInputStream (const URL& url, + const bool isPost_, + URL::OpenStreamProgressCallback* const progressCallback_, + void* const progressCallbackContext_) + : position (0), + finished (false), + isPost (isPost_), + progressCallback (progressCallback_), + progressCallbackContext (progressCallbackContext_) + { + server = url.toString (! isPost); + + if (isPost_) + createHeadersAndPostData (url); + + handle = juce_openInternetFile (server, headers, postData, isPost, + progressCallback_, progressCallbackContext_); + } + + ~WebInputStream() + { + juce_closeInternetFile (handle); + } + + bool isError() const throw() + { + return handle == 0; + } + + int64 getTotalLength() + { + return -1; + } + + bool isExhausted() + { + return finished; + } + + int read (void* dest, int bytes) + { + if (finished || isError()) + { + return 0; + } + else + { + const int bytesRead = juce_readFromInternetFile (handle, dest, bytes); + position += bytesRead; + + if (bytesRead == 0) + finished = true; + + return bytesRead; + } + } + + int64 getPosition() + { + return position; + } + + bool setPosition (int64 wantedPos) + { + if (wantedPos != position) + { + finished = false; + + const int actualPos = juce_seekInInternetFile (handle, (int) wantedPos); + + if (actualPos == wantedPos) + { + position = wantedPos; + } + else + { + if (wantedPos < position) + { + juce_closeInternetFile (handle); + + position = 0; + finished = false; + + handle = juce_openInternetFile (server, headers, postData, isPost, + progressCallback, progressCallbackContext); + } + + skipNextBytes (wantedPos - position); + } + } + + return true; + } + + juce_UseDebuggingNewOperator + +private: + String server, headers; + MemoryBlock postData; + int64 position; + bool finished; + const bool isPost; + void* handle; + URL::OpenStreamProgressCallback* const progressCallback; + void* const progressCallbackContext; + + void createHeadersAndPostData (const URL& url) + { + if (url.getFilesToUpload().size() > 0) + { + // need to upload some files, so do it as multi-part... + String boundary (String::toHexString (Random::getSystemRandom().nextInt64())); + + headers << "Content-Type: multipart/form-data; boundary=" << boundary << "\r\n"; + + appendUTF8ToPostData ("--" + boundary); + + int i; + for (i = 0; i < url.getParameters().size(); ++i) + { + String s; + s << "\r\nContent-Disposition: form-data; name=\"" + << url.getParameters().getAllKeys() [i] + << "\"\r\n\r\n" + << url.getParameters().getAllValues() [i] + << "\r\n--" + << boundary; + + appendUTF8ToPostData (s); + } + + for (i = 0; i < url.getFilesToUpload().size(); ++i) + { + const File f (url.getFilesToUpload().getAllValues() [i]); + const String paramName (url.getFilesToUpload().getAllKeys() [i]); + + String s; + s << "\r\nContent-Disposition: form-data; name=\"" + << paramName + << "\"; filename=\"" + << f.getFileName() + << "\"\r\n"; + + const String mimeType (url.getMimeTypesOfUploadFiles() + .getValue (paramName, String::empty)); + + if (mimeType.isNotEmpty()) + s << "Content-Type: " << mimeType << "\r\n"; + + s << "Content-Transfer-Encoding: binary\r\n\r\n"; + + appendUTF8ToPostData (s); + + f.loadFileAsData (postData); + + s = "\r\n--" + boundary; + + appendUTF8ToPostData (s); + } + + appendUTF8ToPostData ("--\r\n"); + } + else + { + // just a short text attachment, so use simple url encoding.. + const String params (getMangledParameters (url.getParameters())); + + headers = "Content-Type: application/x-www-form-urlencoded\r\nContent-length: " + + String ((int) strlen (params.toUTF8())) + + "\r\n"; + + appendUTF8ToPostData (params); + } + } + + void appendUTF8ToPostData (const String& text) throw() + { + postData.append (text.toUTF8(), + (int) strlen (text.toUTF8())); + } + + WebInputStream (const WebInputStream&); + const WebInputStream& operator= (const WebInputStream&); +}; + +InputStream* URL::createInputStream (const bool usePostCommand, + OpenStreamProgressCallback* const progressCallback, + void* const progressCallbackContext) const +{ + WebInputStream* wi = new WebInputStream (*this, usePostCommand, + progressCallback, progressCallbackContext); + + if (wi->isError()) + { + delete wi; + wi = 0; + } + + return wi; +} + +bool URL::readEntireBinaryStream (MemoryBlock& destData, + const bool usePostCommand) const +{ + InputStream* const in = createInputStream (usePostCommand); + + if (in != 0) + { + in->readIntoMemoryBlock (destData, -1); + delete in; + + return true; + } + + return false; +} + +const String URL::readEntireTextStream (const bool usePostCommand) const +{ + String result; + InputStream* const in = createInputStream (usePostCommand); + + if (in != 0) + { + result = in->readEntireStreamAsString(); + delete in; + } + + return result; +} + +XmlElement* URL::readEntireXmlStream (const bool usePostCommand) const +{ + XmlDocument doc (readEntireTextStream (usePostCommand)); + return doc.getDocumentElement(); +} + +const URL URL::withParameter (const String& parameterName, + const String& parameterValue) const +{ + URL u (*this); + u.parameters.set (parameterName, parameterValue); + return u; +} + +const URL URL::withFileToUpload (const String& parameterName, + const File& fileToUpload, + const String& mimeType) const +{ + URL u (*this); + u.filesToUpload.set (parameterName, fileToUpload.getFullPathName()); + u.mimeTypes.set (parameterName, mimeType); + return u; +} + +const StringPairArray& URL::getParameters() const throw() +{ + return parameters; +} + +const StringPairArray& URL::getFilesToUpload() const throw() +{ + return filesToUpload; +} + +const StringPairArray& URL::getMimeTypesOfUploadFiles() const throw() +{ + return mimeTypes; +} + +const String URL::removeEscapeChars (const String& s) +{ + const int len = s.length(); + uint8* const resultUTF8 = (uint8*) juce_calloc (len * 4); + uint8* r = resultUTF8; + + for (int i = 0; i < len; ++i) + { + char c = (char) s[i]; + if (c == 0) + break; + + if (c == '+') + { + c = ' '; + } + else if (c == '%') + { + c = (char) s.substring (i + 1, i + 3).getHexValue32(); + i += 2; + } + + *r++ = c; + } + + const String stringResult (String::fromUTF8 (resultUTF8)); + juce_free (resultUTF8); + return stringResult; +} + +const String URL::addEscapeChars (const String& s) +{ + String result; + result.preallocateStorage (s.length() + 8); + const char* utf8 = s.toUTF8(); + + while (*utf8 != 0) + { + const char c = *utf8++; + + if (c == ' ') + { + result += T('+'); + } + else if (CharacterFunctions::isLetterOrDigit (c) + || CharacterFunctions::indexOfChar ("_-$.*!'(),", c, false) >= 0) + { + result << c; + } + else + { + const int v = (int) (uint8) c; + + if (v < 0x10) + result << T("%0"); + else + result << T('%'); + + result << String::toHexString (v); + } + } + + return result; +} + +extern bool juce_launchFile (const String& fileName, + const String& parameters) throw(); + +bool URL::launchInDefaultBrowser() const +{ + String u (toString (true)); + + if (u.contains (T("@")) && ! u.contains (T(":"))) + u = "mailto:" + u; + + return juce_launchFile (u, String::empty); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_URL.cpp *********/ + +/********* Start of inlined file: juce_BufferedInputStream.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +BufferedInputStream::BufferedInputStream (InputStream* const source_, + const int bufferSize_, + const bool deleteSourceWhenDestroyed_) throw() + : source (source_), + deleteSourceWhenDestroyed (deleteSourceWhenDestroyed_), + bufferSize (jmax (256, bufferSize_)), + position (source_->getPosition()), + lastReadPos (0), + bufferOverlap (128) +{ + const int sourceSize = (int) source_->getTotalLength(); + if (sourceSize >= 0) + bufferSize = jmin (jmax (32, sourceSize), bufferSize); + + bufferStart = position; + buffer = (char*) juce_malloc (bufferSize); +} + +BufferedInputStream::~BufferedInputStream() throw() +{ + if (deleteSourceWhenDestroyed) + delete source; + + juce_free (buffer); +} + +int64 BufferedInputStream::getTotalLength() +{ + return source->getTotalLength(); +} + +int64 BufferedInputStream::getPosition() +{ + return position; +} + +bool BufferedInputStream::setPosition (int64 newPosition) +{ + position = jmax ((int64) 0, newPosition); + return true; +} + +bool BufferedInputStream::isExhausted() +{ + return (position >= lastReadPos) + && source->isExhausted(); +} + +void BufferedInputStream::ensureBuffered() +{ + const int64 bufferEndOverlap = lastReadPos - bufferOverlap; + + if (position < bufferStart || position >= bufferEndOverlap) + { + int bytesRead; + + if (position < lastReadPos + && position >= bufferEndOverlap + && position >= bufferStart) + { + const int bytesToKeep = (int) (lastReadPos - position); + memmove (buffer, buffer + position - bufferStart, bytesToKeep); + + bufferStart = position; + + bytesRead = source->read (buffer + bytesToKeep, + bufferSize - bytesToKeep); + + lastReadPos += bytesRead; + bytesRead += bytesToKeep; + } + else + { + bufferStart = position; + source->setPosition (bufferStart); + bytesRead = source->read (buffer, bufferSize); + lastReadPos = bufferStart + bytesRead; + } + + while (bytesRead < bufferSize) + buffer [bytesRead++] = 0; + } +} + +int BufferedInputStream::read (void* destBuffer, int maxBytesToRead) +{ + if (position >= bufferStart + && position + maxBytesToRead < lastReadPos) + { + memcpy (destBuffer, buffer + (position - bufferStart), maxBytesToRead); + position += maxBytesToRead; + + return maxBytesToRead; + } + else + { + int bytesRead = 0; + + while (maxBytesToRead > 0) + { + ensureBuffered(); + + if (isExhausted()) + break; + + const int bytesAvailable = jmin (maxBytesToRead, (int) (lastReadPos - position)); + memcpy (destBuffer, buffer + (position - bufferStart), bytesAvailable); + maxBytesToRead -= bytesAvailable; + bytesRead += bytesAvailable; + position += bytesAvailable; + destBuffer = (void*) (((char*) destBuffer) + bytesAvailable); + } + + return bytesRead; + } +} + +const String BufferedInputStream::readString() +{ + if (position >= bufferStart + && position < lastReadPos) + { + const int maxChars = (int) (lastReadPos - position); + + const char* const src = buffer + (position - bufferStart); + + for (int i = 0; i < maxChars; ++i) + { + if (src[i] == 0) + { + position += i + 1; + + return String::fromUTF8 ((const uint8*) src, i); + } + } + } + + return InputStream::readString(); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_BufferedInputStream.cpp *********/ + +/********* Start of inlined file: juce_FileInputSource.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +FileInputSource::FileInputSource (const File& file_) throw() + : file (file_) +{ +} + +FileInputSource::~FileInputSource() +{ +} + +InputStream* FileInputSource::createInputStream() +{ + return file.createInputStream(); +} + +InputStream* FileInputSource::createInputStreamFor (const String& relatedItemPath) +{ + return file.getSiblingFile (relatedItemPath).createInputStream(); +} + +int64 FileInputSource::hashCode() const +{ + return file.hashCode(); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_FileInputSource.cpp *********/ + +/********* Start of inlined file: juce_MemoryInputStream.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +MemoryInputStream::MemoryInputStream (const void* const sourceData, + const int sourceDataSize, + const bool keepInternalCopy) throw() + : data ((const char*) sourceData), + dataSize (sourceDataSize), + position (0) +{ + if (keepInternalCopy) + { + internalCopy.append (data, sourceDataSize); + data = (const char*) internalCopy.getData(); + } +} + +MemoryInputStream::~MemoryInputStream() throw() +{ +} + +int64 MemoryInputStream::getTotalLength() +{ + return dataSize; +} + +int MemoryInputStream::read (void* buffer, int howMany) +{ + const int num = jmin (howMany, dataSize - position); + memcpy (buffer, data + position, num); + position += num; + return num; +} + +bool MemoryInputStream::isExhausted() +{ + return (position >= dataSize); +} + +bool MemoryInputStream::setPosition (int64 pos) +{ + position = (int) jlimit ((int64) 0, (int64) dataSize, pos); + + return true; +} + +int64 MemoryInputStream::getPosition() +{ + return position; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_MemoryInputStream.cpp *********/ + +/********* Start of inlined file: juce_MemoryOutputStream.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +MemoryOutputStream::MemoryOutputStream (const int initialSize, + const int blockSizeToIncreaseBy, + MemoryBlock* const memoryBlockToWriteTo) throw() + : data (memoryBlockToWriteTo), + position (0), + size (0), + blockSize (jmax (16, blockSizeToIncreaseBy)), + ownsMemoryBlock (memoryBlockToWriteTo == 0) +{ + if (memoryBlockToWriteTo == 0) + data = new MemoryBlock (initialSize); + else + memoryBlockToWriteTo->setSize (initialSize, false); +} + +MemoryOutputStream::~MemoryOutputStream() throw() +{ + if (ownsMemoryBlock) + delete data; + else + flush(); +} + +void MemoryOutputStream::flush() +{ + if (! ownsMemoryBlock) + data->setSize (size, false); +} + +void MemoryOutputStream::reset() throw() +{ + position = 0; + size = 0; +} + +bool MemoryOutputStream::write (const void* buffer, int howMany) +{ + int storageNeeded = position + howMany + 1; + storageNeeded = storageNeeded - (storageNeeded % blockSize) + blockSize; + + data->ensureSize (storageNeeded); + data->copyFrom (buffer, position, howMany); + position += howMany; + size = jmax (size, position); + + return true; +} + +const char* MemoryOutputStream::getData() throw() +{ + if (data->getSize() > size) + ((char*) data->getData()) [size] = 0; + + return (const char*) data->getData(); +} + +int MemoryOutputStream::getDataSize() const throw() +{ + return size; +} + +int64 MemoryOutputStream::getPosition() +{ + return position; +} + +bool MemoryOutputStream::setPosition (int64 newPosition) +{ + if (newPosition <= size) + { + // ok to seek backwards + position = jlimit (0, size, (int) newPosition); + return true; + } + else + { + // trying to make it bigger isn't a good thing to do.. + return false; + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_MemoryOutputStream.cpp *********/ + +/********* Start of inlined file: juce_SubregionStream.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +SubregionStream::SubregionStream (InputStream* const sourceStream, + const int64 startPositionInSourceStream_, + const int64 lengthOfSourceStream_, + const bool deleteSourceWhenDestroyed_) throw() + : source (sourceStream), + deleteSourceWhenDestroyed (deleteSourceWhenDestroyed_), + startPositionInSourceStream (startPositionInSourceStream_), + lengthOfSourceStream (lengthOfSourceStream_) +{ + setPosition (0); +} + +SubregionStream::~SubregionStream() throw() +{ + if (deleteSourceWhenDestroyed) + delete source; +} + +int64 SubregionStream::getTotalLength() +{ + const int64 srcLen = source->getTotalLength() - startPositionInSourceStream; + + return (lengthOfSourceStream >= 0) ? jmin (lengthOfSourceStream, srcLen) + : srcLen; +} + +int64 SubregionStream::getPosition() +{ + return source->getPosition() - startPositionInSourceStream; +} + +bool SubregionStream::setPosition (int64 newPosition) +{ + return source->setPosition (jmax ((int64) 0, newPosition + startPositionInSourceStream)); +} + +int SubregionStream::read (void* destBuffer, int maxBytesToRead) +{ + if (lengthOfSourceStream < 0) + { + return source->read (destBuffer, maxBytesToRead); + } + else + { + maxBytesToRead = (int) jmin ((int64) maxBytesToRead, lengthOfSourceStream - getPosition()); + + if (maxBytesToRead <= 0) + return 0; + + return source->read (destBuffer, maxBytesToRead); + } +} + +bool SubregionStream::isExhausted() +{ + if (lengthOfSourceStream >= 0) + return (getPosition() >= lengthOfSourceStream) || source->isExhausted(); + else + return source->isExhausted(); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_SubregionStream.cpp *********/ + +/********* Start of inlined file: juce_PerformanceCounter.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +PerformanceCounter::PerformanceCounter (const String& name_, + int runsPerPrintout, + const File& loggingFile) + : name (name_), + numRuns (0), + runsPerPrint (runsPerPrintout), + totalTime (0), + outputFile (loggingFile) +{ + if (outputFile != File::nonexistent) + { + String s ("**** Counter for \""); + s << name_ << "\" started at: " + << Time::getCurrentTime().toString (true, true) + << "\r\n"; + + outputFile.appendText (s, false, false); + } +} + +PerformanceCounter::~PerformanceCounter() +{ + printStatistics(); +} + +void PerformanceCounter::start() +{ + started = Time::getHighResolutionTicks(); +} + +void PerformanceCounter::stop() +{ + const int64 now = Time::getHighResolutionTicks(); + + totalTime += 1000.0 * Time::highResolutionTicksToSeconds (now - started); + + if (++numRuns == runsPerPrint) + printStatistics(); +} + +void PerformanceCounter::printStatistics() +{ + if (numRuns > 0) + { + String s ("Performance count for \""); + s << name << "\" - average over " << numRuns << " run(s) = "; + + const int micros = (int) (totalTime * (1000.0 / numRuns)); + + if (micros > 10000) + s << (micros/1000) << " millisecs"; + else + s << micros << " microsecs"; + + s << ", total = " << String (totalTime / 1000, 5) << " seconds"; + + Logger::outputDebugString (s); + + s << "\r\n"; + + if (outputFile != File::nonexistent) + outputFile.appendText (s, false, false); + + numRuns = 0; + totalTime = 0; + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_PerformanceCounter.cpp *********/ + +/********* Start of inlined file: juce_Uuid.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +Uuid::Uuid() +{ + // do some serious mixing up of our MAC addresses and different types of time info, + // plus a couple of passes of pseudo-random numbers over the whole thing. + SystemStats::getMACAddresses (value.asInt64, 2); + + int i; + for (i = 16; --i >= 0;) + { + Random r (Time::getHighResolutionTicks() + + Random::getSystemRandom().nextInt() + + value.asInt [i & 3]); + + value.asBytes[i] ^= (uint8) r.nextInt(); + } + + value.asInt64 [0] ^= Time::getHighResolutionTicks(); + value.asInt64 [1] ^= Time::currentTimeMillis(); + + for (i = 4; --i >= 0;) + { + Random r (Time::getHighResolutionTicks() ^ value.asInt[i]); + value.asInt[i] ^= r.nextInt(); + } +} + +Uuid::~Uuid() throw() +{ +} + +Uuid::Uuid (const Uuid& other) + : value (other.value) +{ +} + +Uuid& Uuid::operator= (const Uuid& other) +{ + if (this != &other) + value = other.value; + + return *this; +} + +bool Uuid::operator== (const Uuid& other) const +{ + return memcmp (value.asBytes, other.value.asBytes, 16) == 0; +} + +bool Uuid::operator!= (const Uuid& other) const +{ + return ! operator== (other); +} + +bool Uuid::isNull() const throw() +{ + return (value.asInt64 [0] == 0) && (value.asInt64 [1] == 0); +} + +const String Uuid::toString() const +{ + return String::toHexString (value.asBytes, 16, 0); +} + +Uuid::Uuid (const String& uuidString) +{ + operator= (uuidString); +} + +Uuid& Uuid::operator= (const String& uuidString) +{ + int destIndex = 0; + int i = 0; + + for (;;) + { + int byte = 0; + + for (int loop = 2; --loop >= 0;) + { + byte <<= 4; + + for (;;) + { + const tchar c = uuidString [i++]; + + if (c >= T('0') && c <= T('9')) + { + byte |= c - T('0'); + break; + } + else if (c >= T('a') && c <= T('z')) + { + byte |= c - (T('a') - 10); + break; + } + else if (c >= T('A') && c <= T('Z')) + { + byte |= c - (T('A') - 10); + break; + } + else if (c == 0) + { + while (destIndex < 16) + value.asBytes [destIndex++] = 0; + + return *this; + } + } + } + + value.asBytes [destIndex++] = (uint8) byte; + } +} + +Uuid::Uuid (const uint8* const rawData) +{ + operator= (rawData); +} + +Uuid& Uuid::operator= (const uint8* const rawData) +{ + if (rawData != 0) + memcpy (value.asBytes, rawData, 16); + else + zeromem (value.asBytes, 16); + + return *this; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_Uuid.cpp *********/ + +/********* Start of inlined file: juce_ZipFile.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +struct ZipEntryInfo +{ + ZipFile::ZipEntry entry; + int streamOffset; + int compressedSize; + bool compressed; +}; + +class ZipInputStream : public InputStream +{ +public: + + ZipInputStream (ZipFile& file_, + ZipEntryInfo& zei) throw() + : file (file_), + zipEntryInfo (zei), + pos (0), + headerSize (0) + { + char buffer [30]; + + if (file.source->setPosition (zei.streamOffset) + && file.source->read (buffer, 30) == 30 + && littleEndianInt (buffer) == 0x04034b50) + { + headerSize = 30 + littleEndianShort (buffer + 26) + + littleEndianShort (buffer + 28); + } + + if (! file_.isFromCustomStream) + { + source = file_.sourceFile.createInputStream(); + } + else + { + source = 0; +#ifdef JUCE_DEBUG + file_.numOpenStreams++; +#endif + } + } + + ~ZipInputStream() throw() + { +#ifdef JUCE_DEBUG + if (source == 0) + file.numOpenStreams--; +#endif + + delete source; + } + + int64 getTotalLength() throw() + { + return zipEntryInfo.compressedSize; + } + + int read (void* buffer, int howMany) throw() + { + if (headerSize <= 0) + return 0; + + howMany = (int) jmin ((int64) howMany, zipEntryInfo.compressedSize - pos); + int num; + + if (source != 0) + { + source->setPosition (pos + zipEntryInfo.streamOffset + headerSize); + num = source->read (buffer, howMany); + } + else + { + const ScopedLock sl (file.lock); + + file.source->setPosition (pos + zipEntryInfo.streamOffset + headerSize); + num = file.source->read (buffer, howMany); + } + + pos += num; + return num; + } + + bool isExhausted() throw() + { + return pos >= zipEntryInfo.compressedSize; + } + + int64 getPosition() throw() + { + return pos; + } + + bool setPosition (int64 newPos) throw() + { + pos = jlimit ((int64) 0, (int64) zipEntryInfo.compressedSize, newPos); + return true; + } + +private: + + ZipFile& file; + ZipEntryInfo zipEntryInfo; + int64 pos; + int headerSize; + InputStream* source; + + ZipInputStream (const ZipInputStream&); + const ZipInputStream& operator= (const ZipInputStream&); +}; + +ZipFile::ZipFile (InputStream* const source_, + const bool deleteStreamWhenDestroyed_) throw() + : source (source_), + isFromCustomStream (true), + deleteStreamWhenDestroyed (deleteStreamWhenDestroyed_) +#ifdef JUCE_DEBUG + , numOpenStreams (0) +#endif +{ + init(); +} + +ZipFile::ZipFile (const File& file) + : sourceFile (file), + isFromCustomStream (false), + deleteStreamWhenDestroyed (true) +#ifdef JUCE_DEBUG + , numOpenStreams (0) +#endif +{ + source = file.createInputStream(); + init(); +} + +ZipFile::~ZipFile() throw() +{ + for (int i = entries.size(); --i >= 0;) + { + ZipEntryInfo* const zei = (ZipEntryInfo*) entries [i]; + delete zei; + } + + if (deleteStreamWhenDestroyed && (source != 0)) + delete source; + +#ifdef JUCE_DEBUG + // If you hit this assertion, it means you've created a stream to read + // one of the items in the zipfile, but you've forgotten to delete that + // stream object before deleting the file.. Streams can't be kept open + // after the file is deleted because they need to share the input + // stream that the file uses to read itself. + jassert (numOpenStreams == 0); +#endif +} + +int ZipFile::getNumEntries() const throw() +{ + return entries.size(); +} + +const ZipFile::ZipEntry* ZipFile::getEntry (const int index) const throw() +{ + ZipEntryInfo* const zei = (ZipEntryInfo*) entries [index]; + + return (zei != 0) ? &(zei->entry) + : 0; +} + +int ZipFile::getIndexOfFileName (const String& fileName) const throw() +{ + for (int i = 0; i < entries.size(); ++i) + if (((ZipEntryInfo*) entries.getUnchecked (i))->entry.filename == fileName) + return i; + + return -1; +} + +const ZipFile::ZipEntry* ZipFile::getEntry (const String& fileName) const throw() +{ + return getEntry (getIndexOfFileName (fileName)); +} + +InputStream* ZipFile::createStreamForEntry (const int index) +{ + ZipEntryInfo* const zei = (ZipEntryInfo*) entries[index]; + + InputStream* stream = 0; + + if (zei != 0) + { + stream = new ZipInputStream (*this, *zei); + + if (zei->compressed) + { + stream = new GZIPDecompressorInputStream (stream, true, true, + zei->entry.uncompressedSize); + + // (much faster to unzip in big blocks using a buffer..) + stream = new BufferedInputStream (stream, 32768, true); + } + } + + return stream; +} + +class ZipFilenameComparator +{ +public: + static int compareElements (const void* const first, const void* const second) throw() + { + return ((const ZipEntryInfo*) first)->entry.filename + .compare (((const ZipEntryInfo*) second)->entry.filename); + } +}; + +void ZipFile::sortEntriesByFilename() +{ + ZipFilenameComparator sorter; + entries.sort (sorter); +} + +void ZipFile::init() +{ + if (source != 0) + { + numEntries = 0; + int pos = findEndOfZipEntryTable(); + int size = (int) (source->getTotalLength() - pos); + + source->setPosition (pos); + MemoryBlock headerData; + + if (source->readIntoMemoryBlock (headerData, size) != size) + return; + + pos = 0; + + for (int i = 0; i < numEntries; ++i) + { + if (pos + 46 > size) + break; + + const char* const buffer = ((const char*) headerData.getData()) + pos; + + const int fileNameLen = littleEndianShort (buffer + 28); + + if (pos + 46 + fileNameLen > size) + break; + + ZipEntryInfo* const zei = new ZipEntryInfo(); + zei->entry.filename = String (buffer + 46, fileNameLen); + + const int time = littleEndianShort (buffer + 12); + const int date = littleEndianShort (buffer + 14); + + const int year = 1980 + (date >> 9); + const int month = ((date >> 5) & 15) - 1; + const int day = date & 31; + const int hours = time >> 11; + const int minutes = (time >> 5) & 63; + const int seconds = (time & 31) << 1; + + zei->entry.fileTime = Time (year, month, day, hours, minutes, seconds); + + zei->compressed = littleEndianShort (buffer + 10) != 0; + zei->compressedSize = littleEndianInt (buffer + 20); + zei->entry.uncompressedSize = littleEndianInt (buffer + 24); + + zei->streamOffset = littleEndianInt (buffer + 42); + entries.add (zei); + + pos += 46 + fileNameLen + + littleEndianShort (buffer + 30) + + littleEndianShort (buffer + 32); + } + } +} + +int ZipFile::findEndOfZipEntryTable() +{ + BufferedInputStream in (source, 8192, false); + + in.setPosition (in.getTotalLength()); + int64 pos = in.getPosition(); + + char buffer [32]; + zeromem (buffer, sizeof (buffer)); + + while (pos > 0) + { + in.setPosition (pos - 22); + pos = in.getPosition(); + memcpy (buffer + 22, buffer, 4); + + if (in.read (buffer, 22) != 22) + return 0; + + for (int i = 0; i < 22; ++i) + { + if (littleEndianInt (buffer + i) == 0x06054b50) + { + in.setPosition (pos + i); + in.read (buffer, 22); + numEntries = littleEndianShort (buffer + 10); + + return littleEndianInt (buffer + 16); + } + } + } + + return 0; +} + +void ZipFile::uncompressTo (const File& targetDirectory, + const bool shouldOverwriteFiles) +{ + for (int i = 0; i < entries.size(); ++i) + { + const ZipEntryInfo& zei = *(ZipEntryInfo*) entries[i]; + + const File targetFile (targetDirectory.getChildFile (zei.entry.filename)); + + if (zei.entry.filename.endsWithChar (T('/'))) + { + targetFile.createDirectory(); // (entry is a directory, not a file) + } + else + { + InputStream* const in = createStreamForEntry (i); + + if (in != 0) + { + if (shouldOverwriteFiles) + targetFile.deleteFile(); + + if ((! targetFile.exists()) + && targetFile.getParentDirectory().createDirectory()) + { + FileOutputStream* const out = targetFile.createOutputStream(); + + if (out != 0) + { + out->writeFromInputStream (*in, -1); + delete out; + + targetFile.setCreationTime (zei.entry.fileTime); + targetFile.setLastModificationTime (zei.entry.fileTime); + targetFile.setLastAccessTime (zei.entry.fileTime); + } + } + + delete in; + } + } + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ZipFile.cpp *********/ + +/********* Start of inlined file: juce_CharacterFunctions.cpp *********/ +#ifdef _MSC_VER + #pragma warning (disable: 4514 4996) + #pragma warning (push) +#endif + +#include +#include +#include + +#ifdef _MSC_VER + #pragma warning (pop) +#endif + +BEGIN_JUCE_NAMESPACE + +int CharacterFunctions::length (const char* const s) throw() +{ + return (int) strlen (s); +} + +int CharacterFunctions::length (const juce_wchar* const s) throw() +{ +#if MACOS_10_2_OR_EARLIER + int n = 0; + while (s[n] != 0) + ++n; + + return n; +#else + return (int) wcslen (s); +#endif +} + +void CharacterFunctions::copy (char* dest, const char* src, const int maxChars) throw() +{ + strncpy (dest, src, maxChars); +} + +void CharacterFunctions::copy (juce_wchar* dest, const juce_wchar* src, int maxChars) throw() +{ +#if MACOS_10_2_OR_EARLIER + while (--maxChars >= 0 && *src != 0) + *dest++ = *src++; + + *dest = 0; +#else + wcsncpy (dest, src, maxChars); +#endif +} + +void CharacterFunctions::copy (juce_wchar* dest, const char* src, const int maxChars) throw() +{ + mbstowcs (dest, src, maxChars); +} + +void CharacterFunctions::copy (char* dest, const juce_wchar* src, const int maxChars) throw() +{ + wcstombs (dest, src, maxChars); +} + +void CharacterFunctions::append (char* dest, const char* src) throw() +{ + strcat (dest, src); +} + +void CharacterFunctions::append (juce_wchar* dest, const juce_wchar* src) throw() +{ +#if MACOS_10_2_OR_EARLIER + while (*dest != 0) + ++dest; + + while (*src != 0) + *dest++ = *src++; + + *dest = 0; +#else + wcscat (dest, src); +#endif +} + +int CharacterFunctions::compare (const char* const s1, const char* const s2) throw() +{ + return strcmp (s1, s2); +} + +int CharacterFunctions::compare (const juce_wchar* s1, const juce_wchar* s2) throw() +{ + jassert (s1 != 0 && s2 != 0); + +#if MACOS_10_2_OR_EARLIER + for (;;) + { + if (*s1 != *s2) + { + const int diff = *s1 - *s2; + + if (diff != 0) + return diff < 0 ? -1 : 1; + } + else if (*s1 == 0) + break; + + ++s1; + ++s2; + } + + return 0; +#else + return wcscmp (s1, s2); +#endif +} + +int CharacterFunctions::compare (const char* const s1, const char* const s2, const int maxChars) throw() +{ + jassert (s1 != 0 && s2 != 0); + + return strncmp (s1, s2, maxChars); +} + +int CharacterFunctions::compare (const juce_wchar* s1, const juce_wchar* s2, int maxChars) throw() +{ + jassert (s1 != 0 && s2 != 0); + +#if MACOS_10_2_OR_EARLIER + while (--maxChars >= 0) + { + if (*s1 != *s2) + return (*s1 < *s2) ? -1 : 1; + else if (*s1 == 0) + break; + + ++s1; + ++s2; + } + + return 0; +#else + return wcsncmp (s1, s2, maxChars); +#endif +} + +int CharacterFunctions::compareIgnoreCase (const char* const s1, const char* const s2) throw() +{ + jassert (s1 != 0 && s2 != 0); + +#if JUCE_WIN32 + return stricmp (s1, s2); +#else + return strcasecmp (s1, s2); +#endif +} + +int CharacterFunctions::compareIgnoreCase (const juce_wchar* s1, const juce_wchar* s2) throw() +{ + jassert (s1 != 0 && s2 != 0); + +#if JUCE_WIN32 + return _wcsicmp (s1, s2); +#else + for (;;) + { + if (*s1 != *s2) + { + const int diff = toUpperCase (*s1) - toUpperCase (*s2); + + if (diff != 0) + return diff < 0 ? -1 : 1; + } + else if (*s1 == 0) + break; + + ++s1; + ++s2; + } + + return 0; +#endif +} + +int CharacterFunctions::compareIgnoreCase (const char* const s1, const char* const s2, const int maxChars) throw() +{ + jassert (s1 != 0 && s2 != 0); + +#if JUCE_WIN32 + return strnicmp (s1, s2, maxChars); +#else + return strncasecmp (s1, s2, maxChars); +#endif +} + +int CharacterFunctions::compareIgnoreCase (const juce_wchar* s1, const juce_wchar* s2, int maxChars) throw() +{ + jassert (s1 != 0 && s2 != 0); + +#if JUCE_WIN32 + return _wcsnicmp (s1, s2, maxChars); +#else + while (--maxChars >= 0) + { + if (*s1 != *s2) + { + const int diff = toUpperCase (*s1) - toUpperCase (*s2); + + if (diff != 0) + return diff < 0 ? -1 : 1; + } + else if (*s1 == 0) + break; + + ++s1; + ++s2; + } + + return 0; +#endif +} + +const char* CharacterFunctions::find (const char* const haystack, const char* const needle) throw() +{ + return strstr (haystack, needle); +} + +const juce_wchar* CharacterFunctions::find (const juce_wchar* haystack, const juce_wchar* const needle) throw() +{ +#if MACOS_10_2_OR_EARLIER + while (*haystack != 0) + { + const juce_wchar* s1 = haystack; + const juce_wchar* s2 = needle; + + for (;;) + { + if (*s2 == 0) + return haystack; + + if (*s1 != *s2 || *s2 == 0) + break; + + ++s1; + ++s2; + } + + ++haystack; + } + + return 0; +#else + return wcsstr (haystack, needle); +#endif +} + +int CharacterFunctions::indexOfChar (const char* const haystack, const char needle, const bool ignoreCase) throw() +{ + if (haystack != 0) + { + int i = 0; + + if (ignoreCase) + { + const char n1 = toLowerCase (needle); + const char n2 = toUpperCase (needle); + + if (n1 != n2) // if the char is the same in upper/lower case, fall through to the normal search + { + while (haystack[i] != 0) + { + if (haystack[i] == n1 || haystack[i] == n2) + return i; + + ++i; + } + + return -1; + } + + jassert (n1 == needle); + } + + while (haystack[i] != 0) + { + if (haystack[i] == needle) + return i; + + ++i; + } + } + + return -1; +} + +int CharacterFunctions::indexOfChar (const juce_wchar* const haystack, const juce_wchar needle, const bool ignoreCase) throw() +{ + if (haystack != 0) + { + int i = 0; + + if (ignoreCase) + { + const juce_wchar n1 = toLowerCase (needle); + const juce_wchar n2 = toUpperCase (needle); + + if (n1 != n2) // if the char is the same in upper/lower case, fall through to the normal search + { + while (haystack[i] != 0) + { + if (haystack[i] == n1 || haystack[i] == n2) + return i; + + ++i; + } + + return -1; + } + + jassert (n1 == needle); + } + + while (haystack[i] != 0) + { + if (haystack[i] == needle) + return i; + + ++i; + } + } + + return -1; +} + +int CharacterFunctions::indexOfCharFast (const char* const haystack, const char needle) throw() +{ + jassert (haystack != 0); + + int i = 0; + while (haystack[i] != 0) + { + if (haystack[i] == needle) + return i; + + ++i; + } + + return -1; +} + +int CharacterFunctions::indexOfCharFast (const juce_wchar* const haystack, const juce_wchar needle) throw() +{ + jassert (haystack != 0); + + int i = 0; + while (haystack[i] != 0) + { + if (haystack[i] == needle) + return i; + + ++i; + } + + return -1; +} + +int CharacterFunctions::getIntialSectionContainingOnly (const char* const text, const char* const allowedChars) throw() +{ + return allowedChars == 0 ? 0 : (int) strspn (text, allowedChars); +} + +int CharacterFunctions::getIntialSectionContainingOnly (const juce_wchar* const text, const juce_wchar* const allowedChars) throw() +{ + if (allowedChars == 0) + return 0; + + int i = 0; + + for (;;) + { + if (indexOfCharFast (allowedChars, text[i]) < 0) + break; + + ++i; + } + + return i; +} + +int CharacterFunctions::ftime (char* const dest, const int maxChars, const char* const format, const struct tm* const tm) throw() +{ + return (int) strftime (dest, maxChars, format, tm); +} + +int CharacterFunctions::ftime (juce_wchar* const dest, const int maxChars, const juce_wchar* const format, const struct tm* const tm) throw() +{ +#if MACOS_10_2_OR_EARLIER + const String formatTemp (format); + size_t num = strftime ((char*) dest, maxChars, (const char*) formatTemp, tm); + String temp ((char*) dest); + temp.copyToBuffer (dest, num); + dest [num] = 0; + return (int) num; +#else + return (int) wcsftime (dest, maxChars, format, tm); +#endif +} + +int CharacterFunctions::getIntValue (const char* const s) throw() +{ + return atoi (s); +} + +int CharacterFunctions::getIntValue (const juce_wchar* s) throw() +{ +#if JUCE_WIN32 + return _wtoi (s); +#else + int v = 0; + + while (isWhitespace (*s)) + ++s; + + const bool isNeg = *s == T('-'); + if (isNeg) + ++s; + + for (;;) + { + const wchar_t c = *s++; + + if (c >= T('0') && c <= T('9')) + v = v * 10 + (int) (c - T('0')); + else + break; + } + + return isNeg ? -v : v; +#endif +} + +int64 CharacterFunctions::getInt64Value (const char* s) throw() +{ +#if JUCE_LINUX + return atoll (s); +#elif defined (JUCE_WIN32) + return _atoi64 (s); +#else + int64 v = 0; + + while (isWhitespace (*s)) + ++s; + + const bool isNeg = *s == T('-'); + if (isNeg) + ++s; + + for (;;) + { + const char c = *s++; + + if (c >= '0' && c <= '9') + v = v * 10 + (int64) (c - '0'); + else + break; + } + + return isNeg ? -v : v; +#endif +} + +int64 CharacterFunctions::getInt64Value (const juce_wchar* s) throw() +{ +#if JUCE_WIN32 + return _wtoi64 (s); +#else + int64 v = 0; + + while (isWhitespace (*s)) + ++s; + + const bool isNeg = *s == T('-'); + if (isNeg) + ++s; + + for (;;) + { + const juce_wchar c = *s++; + + if (c >= T('0') && c <= T('9')) + v = v * 10 + (int64) (c - T('0')); + else + break; + } + + return isNeg ? -v : v; +#endif +} + +double CharacterFunctions::getDoubleValue (const char* const s) throw() +{ + return atof (s); +} + +double CharacterFunctions::getDoubleValue (const juce_wchar* const s) throw() +{ +#if MACOS_10_2_OR_EARLIER + String temp (s); + return atof ((const char*) temp); +#else + wchar_t* endChar; + return wcstod (s, &endChar); +#endif +} + +char CharacterFunctions::toUpperCase (const char character) throw() +{ + return (char) toupper (character); +} + +juce_wchar CharacterFunctions::toUpperCase (const juce_wchar character) throw() +{ +#if MACOS_10_2_OR_EARLIER + return toupper ((char) character); +#else + return towupper (character); +#endif +} + +void CharacterFunctions::toUpperCase (char* s) throw() +{ +#if JUCE_WIN32 + strupr (s); +#else + while (*s != 0) + { + *s = toUpperCase (*s); + ++s; + } +#endif +} + +void CharacterFunctions::toUpperCase (juce_wchar* s) throw() +{ +#if JUCE_WIN32 + _wcsupr (s); +#else + while (*s != 0) + { + *s = toUpperCase (*s); + ++s; + } +#endif +} + +bool CharacterFunctions::isUpperCase (const char character) throw() +{ + return isupper (character) != 0; +} + +bool CharacterFunctions::isUpperCase (const juce_wchar character) throw() +{ +#if JUCE_WIN32 + return iswupper (character) != 0; +#else + return toLowerCase (character) != character; +#endif +} + +char CharacterFunctions::toLowerCase (const char character) throw() +{ + return (char) tolower (character); +} + +juce_wchar CharacterFunctions::toLowerCase (const juce_wchar character) throw() +{ +#if MACOS_10_2_OR_EARLIER + return tolower ((char) character); +#else + return towlower (character); +#endif +} + +void CharacterFunctions::toLowerCase (char* s) throw() +{ +#if JUCE_WIN32 + strlwr (s); +#else + while (*s != 0) + { + *s = toLowerCase (*s); + ++s; + } +#endif +} + +void CharacterFunctions::toLowerCase (juce_wchar* s) throw() +{ +#if JUCE_WIN32 + _wcslwr (s); +#else + while (*s != 0) + { + *s = toLowerCase (*s); + ++s; + } +#endif +} + +bool CharacterFunctions::isLowerCase (const char character) throw() +{ + return islower (character) != 0; +} + +bool CharacterFunctions::isLowerCase (const juce_wchar character) throw() +{ +#if JUCE_WIN32 + return iswlower (character) != 0; +#else + return toUpperCase (character) != character; +#endif +} + +bool CharacterFunctions::isWhitespace (const char character) throw() +{ + return character == T(' ') || (character <= 13 && character >= 9); +} + +bool CharacterFunctions::isWhitespace (const juce_wchar character) throw() +{ +#if MACOS_10_2_OR_EARLIER + return isWhitespace ((char) character); +#else + return iswspace (character) != 0; +#endif +} + +bool CharacterFunctions::isDigit (const char character) throw() +{ + return (character >= '0' && character <= '9'); +} + +bool CharacterFunctions::isDigit (const juce_wchar character) throw() +{ +#if MACOS_10_2_OR_EARLIER + return isdigit ((char) character) != 0; +#else + return iswdigit (character) != 0; +#endif +} + +bool CharacterFunctions::isLetter (const char character) throw() +{ + return (character >= 'a' && character <= 'z') + || (character >= 'A' && character <= 'Z'); +} + +bool CharacterFunctions::isLetter (const juce_wchar character) throw() +{ +#if MACOS_10_2_OR_EARLIER + return isLetter ((char) character); +#else + return iswalpha (character) != 0; +#endif +} + +bool CharacterFunctions::isLetterOrDigit (const char character) throw() +{ + return (character >= 'a' && character <= 'z') + || (character >= 'A' && character <= 'Z') + || (character >= '0' && character <= '9'); +} + +bool CharacterFunctions::isLetterOrDigit (const juce_wchar character) throw() +{ +#if MACOS_10_2_OR_EARLIER + return isLetterOrDigit ((char) character); +#else + return iswalnum (character) != 0; +#endif +} + +int CharacterFunctions::getHexDigitValue (const tchar digit) throw() +{ + if (digit >= T('0') && digit <= T('9')) + return digit - T('0'); + else if (digit >= T('a') && digit <= T('f')) + return digit - (T('a') - 10); + else if (digit >= T('A') && digit <= T('F')) + return digit - (T('A') - 10); + + return -1; +} + +int CharacterFunctions::printf (char* const dest, const int maxLength, const char* const format, ...) throw() +{ + va_list list; + va_start (list, format); + return vprintf (dest, maxLength, format, list); +} + +int CharacterFunctions::printf (juce_wchar* const dest, const int maxLength, const juce_wchar* const format, ...) throw() +{ + va_list list; + va_start (list, format); + return vprintf (dest, maxLength, format, list); +} + +int CharacterFunctions::vprintf (char* const dest, const int maxLength, const char* const format, va_list& args) throw() +{ +#if JUCE_WIN32 + return (int) _vsnprintf (dest, maxLength, format, args); +#else + return (int) vsnprintf (dest, maxLength, format, args); +#endif +} + +int CharacterFunctions::vprintf (juce_wchar* const dest, const int maxLength, const juce_wchar* const format, va_list& args) throw() +{ +#if MACOS_10_3_OR_EARLIER + const String formatTemp (format); + size_t num = vprintf ((char*) dest, maxLength, formatTemp, args); + String temp ((char*) dest); + temp.copyToBuffer (dest, num); + dest [num] = 0; + return (int) num; +#elif defined (JUCE_WIN32) + return (int) _vsnwprintf (dest, maxLength, format, args); +#else + return (int) vswprintf (dest, maxLength, format, args); +#endif +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_CharacterFunctions.cpp *********/ + +/********* Start of inlined file: juce_LocalisedStrings.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +LocalisedStrings::LocalisedStrings (const String& fileContents) throw() +{ + loadFromText (fileContents); +} + +LocalisedStrings::LocalisedStrings (const File& fileToLoad) throw() +{ + loadFromText (fileToLoad.loadFileAsString()); +} + +LocalisedStrings::~LocalisedStrings() throw() +{ +} + +const String LocalisedStrings::translate (const String& text) const throw() +{ + return translations.getValue (text, text); +} + +static int findCloseQuote (const String& text, int startPos) throw() +{ + tchar lastChar = 0; + + for (;;) + { + const tchar c = text [startPos]; + + if (c == 0 || (c == T('"') && lastChar != T('\\'))) + break; + + lastChar = c; + ++startPos; + } + + return startPos; +} + +static const String unescapeString (const String& s) throw() +{ + return s.replace (T("\\\""), T("\"")) + .replace (T("\\\'"), T("\'")) + .replace (T("\\t"), T("\t")) + .replace (T("\\r"), T("\r")) + .replace (T("\\n"), T("\n")); +} + +void LocalisedStrings::loadFromText (const String& fileContents) throw() +{ + StringArray lines; + lines.addLines (fileContents); + + for (int i = 0; i < lines.size(); ++i) + { + String line (lines[i].trim()); + + if (line.startsWithChar (T('"'))) + { + int closeQuote = findCloseQuote (line, 1); + + const String originalText (unescapeString (line.substring (1, closeQuote))); + + if (originalText.isNotEmpty()) + { + const int openingQuote = findCloseQuote (line, closeQuote + 1); + closeQuote = findCloseQuote (line, openingQuote + 1); + + const String newText (unescapeString (line.substring (openingQuote + 1, closeQuote))); + + if (newText.isNotEmpty()) + translations.set (originalText, newText); + } + } + else if (line.startsWithIgnoreCase (T("language:"))) + { + languageName = line.substring (9).trim(); + } + else if (line.startsWithIgnoreCase (T("countries:"))) + { + countryCodes.addTokens (line.substring (10).trim(), true); + countryCodes.trim(); + countryCodes.removeEmptyStrings(); + } + } +} + +static CriticalSection currentMappingsLock; +static LocalisedStrings* currentMappings = 0; + +void LocalisedStrings::setCurrentMappings (LocalisedStrings* newTranslations) throw() +{ + const ScopedLock sl (currentMappingsLock); + + delete currentMappings; + currentMappings = newTranslations; +} + +LocalisedStrings* LocalisedStrings::getCurrentMappings() throw() +{ + return currentMappings; +} + +const String LocalisedStrings::translateWithCurrentMappings (const String& text) throw() +{ + const ScopedLock sl (currentMappingsLock); + + if (currentMappings != 0) + return currentMappings->translate (text); + + return text; +} + +const String LocalisedStrings::translateWithCurrentMappings (const char* text) throw() +{ + return translateWithCurrentMappings (String (text)); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_LocalisedStrings.cpp *********/ + +/********* Start of inlined file: juce_String.cpp *********/ +#ifdef _MSC_VER + #pragma warning (disable: 4514) + #pragma warning (push) +#endif +#include + +#if JUCE_MSVC + #include +#endif + +BEGIN_JUCE_NAMESPACE + +#ifdef _MSC_VER + #pragma warning (pop) +#endif + +static const char* const emptyCharString = "\0\0\0\0JUCE"; +static const int safeEmptyStringRefCount = 0x3fffffff; +String::InternalRefCountedStringHolder String::emptyString = { safeEmptyStringRefCount, 0, { 0 } }; +static tchar decimalPoint = T('.'); + +void juce_initialiseStrings() +{ + decimalPoint = String::fromUTF8 ((const uint8*) localeconv()->decimal_point) [0]; +} + +void String::deleteInternal() throw() +{ + if (atomicDecrementAndReturn (text->refCount) == 0) + juce_free (text); +} + +void String::createInternal (const int numChars) throw() +{ + jassert (numChars > 0); + + text = (InternalRefCountedStringHolder*) juce_malloc (sizeof (InternalRefCountedStringHolder) + + numChars * sizeof (tchar)); + text->refCount = 1; + text->allocatedNumChars = numChars; + text->text[0] = 0; +} + +void String::createInternal (const tchar* const t, const tchar* const textEnd) throw() +{ + jassert (*(textEnd - 1) == 0); // must have a null terminator + + const int numChars = (int) (textEnd - t); + createInternal (numChars - 1); + memcpy (text->text, t, numChars * sizeof (tchar)); +} + +void String::appendInternal (const tchar* const newText, + const int numExtraChars) throw() +{ + if (numExtraChars > 0) + { + const int oldLen = CharacterFunctions::length (text->text); + const int newTotalLen = oldLen + numExtraChars; + + if (text->refCount > 1) + { + // it's in use by other strings as well, so we need to make a private copy before messing with it.. + InternalRefCountedStringHolder* const newTextHolder + = (InternalRefCountedStringHolder*) juce_malloc (sizeof (InternalRefCountedStringHolder) + + newTotalLen * sizeof (tchar)); + newTextHolder->refCount = 1; + newTextHolder->allocatedNumChars = newTotalLen; + + memcpy (newTextHolder->text, text->text, oldLen * sizeof (tchar)); + memcpy (newTextHolder->text + oldLen, newText, numExtraChars * sizeof (tchar)); + + InternalRefCountedStringHolder* const old = text; + text = newTextHolder; + + if (atomicDecrementAndReturn (old->refCount) == 0) + juce_free (old); + } + else + { + // no other strings using it, so just expand it if needed.. + if (newTotalLen > text->allocatedNumChars) + { + text = (InternalRefCountedStringHolder*) + juce_realloc (text, sizeof (InternalRefCountedStringHolder) + + newTotalLen * sizeof (tchar)); + + text->allocatedNumChars = newTotalLen; + } + + memcpy (text->text + oldLen, newText, numExtraChars * sizeof (tchar)); + } + + text->text [newTotalLen] = 0; + } +} + +void String::dupeInternalIfMultiplyReferenced() throw() +{ + if (text->refCount > 1) + { + InternalRefCountedStringHolder* const old = text; + const int len = old->allocatedNumChars; + + InternalRefCountedStringHolder* const newTextHolder + = (InternalRefCountedStringHolder*) juce_malloc (sizeof (InternalRefCountedStringHolder) + + len * sizeof (tchar)); + + newTextHolder->refCount = 1; + newTextHolder->allocatedNumChars = len; + + memcpy (newTextHolder->text, old->text, (len + 1) * sizeof (tchar)); + + text = newTextHolder; + + if (atomicDecrementAndReturn (old->refCount) == 0) + juce_free (old); + } +} + +const String String::empty; + +String::String() throw() + : text (&emptyString) +{ +} + +String::String (const String& other) throw() + : text (other.text) +{ + atomicIncrement (text->refCount); +} + +String::String (const int numChars, + const int /*dummyVariable*/) throw() +{ + createInternal (numChars); +} + +String::String (const char* const t) throw() +{ + if (t != 0 && *t != 0) + { + const int len = CharacterFunctions::length (t); + createInternal (len); + +#if JUCE_STRINGS_ARE_UNICODE + CharacterFunctions::copy (text->text, t, len + 1); +#else + memcpy (text->text, t, len + 1); +#endif + } + else + { + text = &emptyString; + emptyString.refCount = safeEmptyStringRefCount; + } +} + +String::String (const juce_wchar* const t) throw() +{ + if (t != 0 && *t != 0) + { + const int len = CharacterFunctions::length (t); + createInternal (len); + +#if JUCE_STRINGS_ARE_UNICODE + memcpy (text->text, t, (len + 1) * sizeof (tchar)); +#else + CharacterFunctions::copy (text->text, t, len + 1); +#endif + } + else + { + text = &emptyString; + emptyString.refCount = safeEmptyStringRefCount; + } +} + +String::String (const char* const t, + const int maxChars) throw() +{ + int i; + for (i = 0; i < maxChars; ++i) + if (t[i] == 0) + break; + + if (i > 0) + { + createInternal (i); + +#if JUCE_STRINGS_ARE_UNICODE + CharacterFunctions::copy (text->text, t, i); +#else + memcpy (text->text, t, i); +#endif + + text->text [i] = 0; + } + else + { + text = &emptyString; + emptyString.refCount = safeEmptyStringRefCount; + } +} + +String::String (const juce_wchar* const t, + const int maxChars) throw() +{ + int i; + for (i = 0; i < maxChars; ++i) + if (t[i] == 0) + break; + + if (i > 0) + { + createInternal (i); + +#if JUCE_STRINGS_ARE_UNICODE + memcpy (text->text, t, i * sizeof (tchar)); +#else + CharacterFunctions::copy (text->text, t, i); +#endif + text->text [i] = 0; + } + else + { + text = &emptyString; + emptyString.refCount = safeEmptyStringRefCount; + } +} + +const String String::charToString (const tchar character) throw() +{ + tchar temp[2]; + temp[0] = character; + temp[1] = 0; + + return String (temp); +} + +// pass in a pointer to the END of a buffer.. +static tchar* int64ToCharString (tchar* t, const int64 n) throw() +{ + *--t = 0; + int64 v = (n >= 0) ? n : -n; + + do + { + *--t = (tchar) (T('0') + (int) (v % 10)); + v /= 10; + + } while (v > 0); + + if (n < 0) + *--t = T('-'); + + return t; +} + +static tchar* intToCharString (tchar* t, const int n) throw() +{ + if (n == (int) 0x80000000) // (would cause an overflow) + return int64ToCharString (t, n); + + *--t = 0; + int v = abs (n); + + do + { + *--t = (tchar) (T('0') + (v % 10)); + v /= 10; + + } while (v > 0); + + if (n < 0) + *--t = T('-'); + + return t; +} + +static tchar* uintToCharString (tchar* t, unsigned int v) throw() +{ + *--t = 0; + + do + { + *--t = (tchar) (T('0') + (v % 10)); + v /= 10; + + } while (v > 0); + + return t; +} + +String::String (const int number) throw() +{ + tchar buffer [16]; + tchar* const end = buffer + 16; + + createInternal (intToCharString (end, number), end); +} + +String::String (const unsigned int number) throw() +{ + tchar buffer [16]; + tchar* const end = buffer + 16; + + createInternal (uintToCharString (end, number), end); +} + +String::String (const short number) throw() +{ + tchar buffer [16]; + tchar* const end = buffer + 16; + + createInternal (intToCharString (end, (int) number), end); +} + +String::String (const unsigned short number) throw() +{ + tchar buffer [16]; + tchar* const end = buffer + 16; + + createInternal (uintToCharString (end, (unsigned int) number), end); +} + +String::String (const int64 number) throw() +{ + tchar buffer [32]; + tchar* const end = buffer + 32; + + createInternal (int64ToCharString (end, number), end); +} + +String::String (const uint64 number) throw() +{ + tchar buffer [32]; + tchar* const end = buffer + 32; + tchar* t = end; + + *--t = 0; + int64 v = number; + + do + { + *--t = (tchar) (T('0') + (int) (v % 10)); + v /= 10; + + } while (v > 0); + + createInternal (t, end); +} + +// a double-to-string routine that actually uses the number of dec. places you asked for +// without resorting to exponent notation if the number's too big or small (which is what printf does). +void String::doubleToStringWithDecPlaces (double n, int numDecPlaces) throw() +{ + const int bufSize = 80; + tchar buffer [bufSize]; + int len; + tchar* t; + + if (numDecPlaces > 0 && n > -1.0e20 && n < 1.0e20) + { + int64 v = (int64) (pow (10.0, numDecPlaces) * fabs (n) + 0.5); + + t = buffer + bufSize; + *--t = (tchar) 0; + + while (numDecPlaces >= 0 || v > 0) + { + if (numDecPlaces == 0) + *--t = decimalPoint; + + *--t = (tchar) (T('0') + (v % 10)); + + v /= 10; + --numDecPlaces; + } + + if (n < 0) + *--t = T('-'); + + len = (int) ((buffer + bufSize) - t); + } + else + { + len = CharacterFunctions::printf (buffer, bufSize, T("%.9g"), n) + 1; + t = buffer; + } + + if (len > 1) + { + jassert (len < numElementsInArray (buffer)); + + createInternal (len - 1); + memcpy (text->text, t, len * sizeof (tchar)); + } + else + { + jassert (*t == 0); + text = &emptyString; + emptyString.refCount = safeEmptyStringRefCount; + } +} + +String::String (const float number, + const int numberOfDecimalPlaces) throw() +{ + doubleToStringWithDecPlaces ((double) number, + numberOfDecimalPlaces); +} + +String::String (const double number, + const int numberOfDecimalPlaces) throw() +{ + doubleToStringWithDecPlaces (number, + numberOfDecimalPlaces); +} + +String::~String() throw() +{ + if (atomicDecrementAndReturn (text->refCount) == 0) + juce_free (text); +} + +void String::preallocateStorage (const int numChars) throw() +{ + if (numChars > text->allocatedNumChars) + { + dupeInternalIfMultiplyReferenced(); + + text = (InternalRefCountedStringHolder*) juce_realloc (text, sizeof (InternalRefCountedStringHolder) + + numChars * sizeof (tchar)); + text->allocatedNumChars = numChars; + } +} + +#if JUCE_STRINGS_ARE_UNICODE +String::operator const char*() const throw() +{ + if (isEmpty()) + { + return (const char*) emptyCharString; + } + else + { + String* const mutableThis = const_cast (this); + + mutableThis->dupeInternalIfMultiplyReferenced(); + int len = CharacterFunctions::length (text->text) + 1; + mutableThis->text = (InternalRefCountedStringHolder*) + juce_realloc (text, sizeof (InternalRefCountedStringHolder) + + (len * sizeof (juce_wchar) + len)); + char* otherCopy = (char*) (text->text + len); + --len; + + CharacterFunctions::copy (otherCopy, text->text, len); + otherCopy [len] = 0; + return otherCopy; + } +} + +#else + +String::operator const juce_wchar*() const throw() +{ + if (isEmpty()) + { + return (const juce_wchar*) emptyCharString; + } + else + { + String* const mutableThis = const_cast (this); + + mutableThis->dupeInternalIfMultiplyReferenced(); + int len = CharacterFunctions::length (text->text) + 1; + mutableThis->text = (InternalRefCountedStringHolder*) + juce_realloc (text, sizeof (InternalRefCountedStringHolder) + + (len * sizeof (juce_wchar) + len)); + + juce_wchar* otherCopy = (juce_wchar*) (text->text + len); + --len; + + CharacterFunctions::copy (otherCopy, text->text, len); + otherCopy [len] = 0; + return otherCopy; + } +} + +#endif + +void String::copyToBuffer (char* const destBuffer, + const int maxCharsToCopy) const throw() +{ + const int len = jmin (maxCharsToCopy, length()); + +#if JUCE_STRINGS_ARE_UNICODE + CharacterFunctions::copy (destBuffer, text->text, len); +#else + memcpy (destBuffer, text->text, len * sizeof (tchar)); +#endif + + destBuffer [len] = 0; +} + +void String::copyToBuffer (juce_wchar* const destBuffer, + const int maxCharsToCopy) const throw() +{ + const int len = jmin (maxCharsToCopy, length()); + +#if JUCE_STRINGS_ARE_UNICODE + memcpy (destBuffer, text->text, len * sizeof (juce_wchar)); +#else + CharacterFunctions::copy (destBuffer, text->text, len); +#endif + + destBuffer [len] = 0; +} + +int String::length() const throw() +{ + return CharacterFunctions::length (text->text); +} + +int String::hashCode() const throw() +{ + const tchar* t = text->text; + int result = 0; + + while (*t != (tchar) 0) + result = 31 * result + *t++; + + return result; +} + +int64 String::hashCode64() const throw() +{ + const tchar* t = text->text; + int64 result = 0; + + while (*t != (tchar) 0) + result = 101 * result + *t++; + + return result; +} + +const String& String::operator= (const tchar* const otherText) throw() +{ + if (otherText != 0 && *otherText != 0) + { + const int otherLen = CharacterFunctions::length (otherText); + + if (otherLen > 0) + { + // avoid resizing the memory block if the string is + // shrinking.. + if (text->refCount > 1 + || otherLen > text->allocatedNumChars + || otherLen <= (text->allocatedNumChars >> 1)) + { + deleteInternal(); + createInternal (otherLen); + } + + memcpy (text->text, otherText, (otherLen + 1) * sizeof (tchar)); + + return *this; + } + } + + deleteInternal(); + text = &emptyString; + emptyString.refCount = safeEmptyStringRefCount; + + return *this; +} + +const String& String::operator= (const String& other) throw() +{ + if (this != &other) + { + atomicIncrement (other.text->refCount); + + if (atomicDecrementAndReturn (text->refCount) == 0) + juce_free (text); + + text = other.text; + } + + return *this; +} + +bool String::operator== (const String& other) const throw() +{ + return text == other.text + || CharacterFunctions::compare (text->text, other.text->text) == 0; +} + +bool String::operator== (const tchar* const t) const throw() +{ + return t != 0 ? CharacterFunctions::compare (text->text, t) == 0 + : isEmpty(); +} + +bool String::equalsIgnoreCase (const tchar* t) const throw() +{ + return t != 0 ? CharacterFunctions::compareIgnoreCase (text->text, t) == 0 + : isEmpty(); +} + +bool String::equalsIgnoreCase (const String& other) const throw() +{ + return text == other.text + || CharacterFunctions::compareIgnoreCase (text->text, other.text->text) == 0; +} + +bool String::operator!= (const String& other) const throw() +{ + return text != other.text + && CharacterFunctions::compare (text->text, other.text->text) != 0; +} + +bool String::operator!= (const tchar* const t) const throw() +{ + return t != 0 ? (CharacterFunctions::compare (text->text, t) != 0) + : isNotEmpty(); +} + +bool String::operator> (const String& other) const throw() +{ + return compare (other) > 0; +} + +bool String::operator< (const tchar* const other) const throw() +{ + return compare (other) < 0; +} + +bool String::operator>= (const String& other) const throw() +{ + return compare (other) >= 0; +} + +bool String::operator<= (const tchar* const other) const throw() +{ + return compare (other) <= 0; +} + +int String::compare (const tchar* const other) const throw() +{ + return other != 0 ? CharacterFunctions::compare (text->text, other) + : isEmpty(); +} + +int String::compareIgnoreCase (const tchar* const other) const throw() +{ + return other != 0 ? CharacterFunctions::compareIgnoreCase (text->text, other) + : isEmpty(); +} + +int String::compareLexicographically (const tchar* other) const throw() +{ + if (other == 0) + return isEmpty(); + + const tchar* s1 = text->text; + while (*s1 != 0 && ! CharacterFunctions::isLetterOrDigit (*s1)) + ++s1; + + while (*other != 0 && ! CharacterFunctions::isLetterOrDigit (*other)) + ++other; + + return CharacterFunctions::compareIgnoreCase (s1, other); +} + +const String String::operator+ (const String& other) const throw() +{ + if (*(other.text->text) == 0) + return *this; + + if (isEmpty()) + return other; + + const int len = CharacterFunctions::length (text->text); + const int otherLen = CharacterFunctions::length (other.text->text); + + String result (len + otherLen, (int) 0); + memcpy (result.text->text, text->text, len * sizeof (tchar)); + memcpy (result.text->text + len, other.text->text, otherLen * sizeof (tchar)); + result.text->text [len + otherLen] = 0; + + return result; +} + +const String String::operator+ (const tchar* const textToAppend) const throw() +{ + if (textToAppend == 0 || *textToAppend == 0) + return *this; + + const int len = CharacterFunctions::length (text->text); + const int otherLen = CharacterFunctions::length (textToAppend); + + String result (len + otherLen, (int) 0); + memcpy (result.text->text, text->text, len * sizeof (tchar)); + memcpy (result.text->text + len, textToAppend, otherLen * sizeof (tchar)); + result.text->text [len + otherLen] = 0; + + return result; +} + +const String String::operator+ (const tchar characterToAppend) const throw() +{ + if (characterToAppend == 0) + return *this; + + const int len = CharacterFunctions::length (text->text); + String result ((int) (len + 1), (int) 0); + + memcpy (result.text->text, text->text, len * sizeof (tchar)); + result.text->text[len] = characterToAppend; + result.text->text[len + 1] = 0; + + return result; +} + +const String JUCE_PUBLIC_FUNCTION operator+ (const char* const string1, + const String& string2) throw() +{ + String s (string1); + s += string2; + return s; +} + +const String JUCE_PUBLIC_FUNCTION operator+ (const juce_wchar* const string1, + const String& string2) throw() +{ + String s (string1); + s += string2; + return s; +} + +const String& String::operator+= (const tchar* const t) throw() +{ + if (t != 0) + appendInternal (t, CharacterFunctions::length (t)); + + return *this; +} + +const String& String::operator+= (const String& other) throw() +{ + if (isEmpty()) + operator= (other); + else + appendInternal (other.text->text, + CharacterFunctions::length (other.text->text)); + + return *this; +} + +const String& String::operator+= (const char ch) throw() +{ + char asString[2]; + asString[0] = ch; + asString[1] = 0; + +#if JUCE_STRINGS_ARE_UNICODE + operator+= (String (asString)); +#else + appendInternal (asString, 1); +#endif + + return *this; +} + +const String& String::operator+= (const juce_wchar ch) throw() +{ + juce_wchar asString[2]; + asString[0] = ch; + asString[1] = 0; + +#if JUCE_STRINGS_ARE_UNICODE + appendInternal (asString, 1); +#else + operator+= (String (asString)); +#endif + + return *this; +} + +void String::append (const tchar* const other, + const int howMany) throw() +{ + if (howMany > 0) + { + int i; + for (i = 0; i < howMany; ++i) + if (other[i] == 0) + break; + + appendInternal (other, i); + } +} + +String& String::operator<< (const int number) throw() +{ + tchar buffer [64]; + tchar* const end = buffer + 64; + const tchar* const t = intToCharString (end, number); + appendInternal (t, (int) (end - t) - 1); + + return *this; +} + +String& String::operator<< (const unsigned int number) throw() +{ + tchar buffer [64]; + tchar* const end = buffer + 64; + const tchar* const t = uintToCharString (end, number); + appendInternal (t, (int) (end - t) - 1); + + return *this; +} + +String& String::operator<< (const short number) throw() +{ + tchar buffer [64]; + tchar* const end = buffer + 64; + const tchar* const t = intToCharString (end, (int) number); + appendInternal (t, (int) (end - t) - 1); + + return *this; +} + +String& String::operator<< (const double number) throw() +{ + operator+= (String (number)); + return *this; +} + +String& String::operator<< (const float number) throw() +{ + operator+= (String (number)); + return *this; +} + +String& String::operator<< (const char character) throw() +{ + operator+= (character); + return *this; +} + +String& String::operator<< (const juce_wchar character) throw() +{ + operator+= (character); + return *this; +} + +String& String::operator<< (const char* const t) throw() +{ +#if JUCE_STRINGS_ARE_UNICODE + operator+= (String (t)); +#else + operator+= (t); +#endif + return *this; +} + +String& String::operator<< (const juce_wchar* const t) throw() +{ +#if JUCE_STRINGS_ARE_UNICODE + operator+= (t); +#else + operator+= (String (t)); +#endif + return *this; +} + +String& String::operator<< (const String& t) throw() +{ + operator+= (t); + return *this; +} + +int String::indexOfChar (const tchar character) const throw() +{ + const tchar* t = text->text; + + for (;;) + { + if (*t == character) + return (int) (t - text->text); + + if (*t++ == 0) + return -1; + } +} + +int String::lastIndexOfChar (const tchar character) const throw() +{ + for (int i = CharacterFunctions::length (text->text); --i >= 0;) + if (text->text[i] == character) + return i; + + return -1; +} + +int String::indexOf (const tchar* const t) const throw() +{ + const tchar* const r = CharacterFunctions::find (text->text, t); + return (r == 0) ? -1 + : (int) (r - text->text); +} + +int String::indexOfChar (const int startIndex, + const tchar character) const throw() +{ + if (startIndex >= 0 && startIndex >= CharacterFunctions::length (text->text)) + return -1; + + const tchar* t = text->text + jmax (0, startIndex); + + for (;;) + { + if (*t == character) + return (int) (t - text->text); + + if (*t++ == 0) + return -1; + } +} + +int String::indexOfAnyOf (const tchar* const charactersToLookFor, + const int startIndex, + const bool ignoreCase) const throw() +{ + if (charactersToLookFor == 0 + || (startIndex >= 0 && startIndex >= CharacterFunctions::length (text->text))) + return -1; + + const tchar* t = text->text + jmax (0, startIndex); + + while (*t != 0) + { + if (CharacterFunctions::indexOfChar (charactersToLookFor, *t, ignoreCase) >= 0) + return (int) (t - text->text); + + ++t; + } + + return -1; +} + +int String::indexOf (const int startIndex, + const tchar* const other) const throw() +{ + if (other == 0 || startIndex >= CharacterFunctions::length (text->text)) + return -1; + + const tchar* const found = CharacterFunctions::find (text->text + jmax (0, startIndex), + other); + + return (found == 0) ? -1 + : (int) (found - text->text); +} + +int String::indexOfIgnoreCase (const tchar* const other) const throw() +{ + if (other != 0 && *other != 0) + { + const int len = CharacterFunctions::length (other); + const int end = CharacterFunctions::length (text->text) - len; + + for (int i = 0; i <= end; ++i) + if (CharacterFunctions::compareIgnoreCase (text->text + i, other, len) == 0) + return i; + } + + return -1; +} + +int String::indexOfIgnoreCase (const int startIndex, + const tchar* const other) const throw() +{ + if (other != 0 && *other != 0) + { + const int len = CharacterFunctions::length (other); + const int end = length() - len; + + for (int i = jmax (0, startIndex); i <= end; ++i) + if (CharacterFunctions::compareIgnoreCase (text->text + i, other, len) == 0) + return i; + } + + return -1; +} + +int String::lastIndexOf (const tchar* const other) const throw() +{ + if (other != 0 && *other != 0) + { + const int len = CharacterFunctions::length (other); + int i = length() - len; + + if (i >= 0) + { + const tchar* n = text->text + i; + + while (i >= 0) + { + if (CharacterFunctions::compare (n--, other, len) == 0) + return i; + + --i; + } + } + } + + return -1; +} + +int String::lastIndexOfIgnoreCase (const tchar* const other) const throw() +{ + if (other != 0 && *other != 0) + { + const int len = CharacterFunctions::length (other); + int i = length() - len; + + if (i >= 0) + { + const tchar* n = text->text + i; + + while (i >= 0) + { + if (CharacterFunctions::compareIgnoreCase (n--, other, len) == 0) + return i; + + --i; + } + } + } + + return -1; +} + +int String::lastIndexOfAnyOf (const tchar* const charactersToLookFor, + const bool ignoreCase) const throw() +{ + for (int i = CharacterFunctions::length (text->text); --i >= 0;) + if (CharacterFunctions::indexOfChar (charactersToLookFor, text->text [i], ignoreCase) >= 0) + return i; + + return -1; +} + +bool String::contains (const tchar* const other) const throw() +{ + return indexOf (other) >= 0; +} + +bool String::containsChar (const tchar character) const throw() +{ + return indexOfChar (character) >= 0; +} + +bool String::containsIgnoreCase (const tchar* const t) const throw() +{ + return indexOfIgnoreCase (t) >= 0; +} + +int String::indexOfWholeWord (const tchar* const word) const throw() +{ + if (word != 0 && *word != 0) + { + const int wordLen = CharacterFunctions::length (word); + const int end = length() - wordLen; + const tchar* t = text->text; + + for (int i = 0; i <= end; ++i) + { + if (CharacterFunctions::compare (t, word, wordLen) == 0 + && (i == 0 || ! CharacterFunctions::isLetterOrDigit (* (t - 1))) + && ! CharacterFunctions::isLetterOrDigit (t [wordLen])) + { + return i; + } + + ++t; + } + } + + return -1; +} + +int String::indexOfWholeWordIgnoreCase (const tchar* const word) const throw() +{ + if (word != 0 && *word != 0) + { + const int wordLen = CharacterFunctions::length (word); + const int end = length() - wordLen; + const tchar* t = text->text; + + for (int i = 0; i <= end; ++i) + { + if (CharacterFunctions::compareIgnoreCase (t, word, wordLen) == 0 + && (i == 0 || ! CharacterFunctions::isLetterOrDigit (* (t - 1))) + && ! CharacterFunctions::isLetterOrDigit (t [wordLen])) + { + return i; + } + + ++t; + } + } + + return -1; +} + +bool String::containsWholeWord (const tchar* const wordToLookFor) const throw() +{ + return indexOfWholeWord (wordToLookFor) >= 0; +} + +bool String::containsWholeWordIgnoreCase (const tchar* const wordToLookFor) const throw() +{ + return indexOfWholeWordIgnoreCase (wordToLookFor) >= 0; +} + +static int indexOfMatch (const tchar* const wildcard, + const tchar* const test, + const bool ignoreCase) throw() +{ + int start = 0; + + while (test [start] != 0) + { + int i = 0; + + for (;;) + { + const tchar wc = wildcard [i]; + const tchar c = test [i + start]; + + if (wc == c + || (ignoreCase && CharacterFunctions::toLowerCase (wc) == CharacterFunctions::toLowerCase (c)) + || (wc == T('?') && c != 0)) + { + if (wc == 0) + return start; + + ++i; + } + else + { + if (wc == T('*') && (wildcard [i + 1] == 0 + || indexOfMatch (wildcard + i + 1, + test + start + i, + ignoreCase) >= 0)) + { + return start; + } + + break; + } + } + + ++start; + } + + return -1; +} + +bool String::matchesWildcard (const tchar* wildcard, const bool ignoreCase) const throw() +{ + int i = 0; + + for (;;) + { + const tchar wc = wildcard [i]; + const tchar c = text->text [i]; + + if (wc == c + || (ignoreCase && CharacterFunctions::toLowerCase (wc) == CharacterFunctions::toLowerCase (c)) + || (wc == T('?') && c != 0)) + { + if (wc == 0) + return true; + + ++i; + } + else + { + return wc == T('*') && (wildcard [i + 1] == 0 + || indexOfMatch (wildcard + i + 1, + text->text + i, + ignoreCase) >= 0); + } + } +} + +void String::printf (const tchar* const pf, ...) throw() +{ + va_list list; + va_start (list, pf); + + vprintf (pf, list); +} + +const String String::formatted (const tchar* const pf, ...) throw() +{ + va_list list; + va_start (list, pf); + + String result; + result.vprintf (pf, list); + return result; +} + +void String::vprintf (const tchar* const pf, va_list& args) throw() +{ + tchar stackBuf [256]; + unsigned int bufSize = 256; + tchar* buf = stackBuf; + + deleteInternal(); + + do + { +#if JUCE_LINUX && JUCE_64BIT + va_list tempArgs; + va_copy (tempArgs, args); + const int num = CharacterFunctions::vprintf (buf, bufSize - 1, pf, tempArgs); + va_end (tempArgs); +#else + const int num = CharacterFunctions::vprintf (buf, bufSize - 1, pf, args); +#endif + + if (num > 0) + { + createInternal (num); + memcpy (text->text, buf, (num + 1) * sizeof (tchar)); + break; + } + else if (num == 0) + { + text = &emptyString; + emptyString.refCount = safeEmptyStringRefCount; + break; + } + + if (buf != stackBuf) + juce_free (buf); + + bufSize += 256; + buf = (tchar*) juce_malloc (bufSize * sizeof (tchar)); + } + while (bufSize < 65536); // this is a sanity check to avoid situations where vprintf repeatedly + // returns -1 because of an error rather than because it needs more space. + + if (buf != stackBuf) + juce_free (buf); +} + +const String String::repeatedString (const tchar* const stringToRepeat, + int numberOfTimesToRepeat) throw() +{ + const int len = CharacterFunctions::length (stringToRepeat); + String result ((int) (len * numberOfTimesToRepeat + 1), (int) 0); + + tchar* n = result.text->text; + n[0] = 0; + + while (--numberOfTimesToRepeat >= 0) + { + CharacterFunctions::append (n, stringToRepeat); + n += len; + } + + return result; +} + +const String String::replaceSection (int index, + int numCharsToReplace, + const tchar* const stringToInsert) const throw() +{ + if (index < 0) + { + // a negative index to replace from? + jassertfalse + index = 0; + } + + if (numCharsToReplace < 0) + { + // replacing a negative number of characters? + numCharsToReplace = 0; + jassertfalse; + } + + const int len = length(); + + if (index + numCharsToReplace > len) + { + if (index > len) + { + // replacing beyond the end of the string? + index = len; + jassertfalse + } + + numCharsToReplace = len - index; + } + + const int newStringLen = (stringToInsert != 0) ? CharacterFunctions::length (stringToInsert) : 0; + const int newTotalLen = len + newStringLen - numCharsToReplace; + + String result (newTotalLen, (int) 0); + + memcpy (result.text->text, + text->text, + index * sizeof (tchar)); + + if (newStringLen > 0) + memcpy (result.text->text + index, + stringToInsert, + newStringLen * sizeof (tchar)); + + const int endStringLen = newTotalLen - (index + newStringLen); + + if (endStringLen > 0) + memcpy (result.text->text + (index + newStringLen), + text->text + (index + numCharsToReplace), + endStringLen * sizeof (tchar)); + + result.text->text [newTotalLen] = 0; + + return result; +} + +const String String::replace (const tchar* const stringToReplace, + const tchar* const stringToInsert, + const bool ignoreCase) const throw() +{ + const int stringToReplaceLen = CharacterFunctions::length (stringToReplace); + const int stringToInsertLen = CharacterFunctions::length (stringToInsert); + + int i = 0; + String result (*this); + + while ((i = (ignoreCase ? result.indexOfIgnoreCase (i, stringToReplace) + : result.indexOf (i, stringToReplace))) >= 0) + { + result = result.replaceSection (i, stringToReplaceLen, stringToInsert); + i += stringToInsertLen; + } + + return result; +} + +const String String::replaceCharacter (const tchar charToReplace, + const tchar charToInsert) const throw() +{ + const int index = indexOfChar (charToReplace); + + if (index < 0) + return *this; + + String result (*this); + result.dupeInternalIfMultiplyReferenced(); + + tchar* t = result.text->text + index; + + while (*t != 0) + { + if (*t == charToReplace) + *t = charToInsert; + + ++t; + } + + return result; +} + +const String String::replaceCharacters (const String& charactersToReplace, + const tchar* const charactersToInsertInstead) const throw() +{ + String result (*this); + result.dupeInternalIfMultiplyReferenced(); + + tchar* t = result.text->text; + const int len2 = CharacterFunctions::length (charactersToInsertInstead); + + // the two strings passed in are supposed to be the same length! + jassert (len2 == charactersToReplace.length()); + + while (*t != 0) + { + const int index = charactersToReplace.indexOfChar (*t); + + if (((unsigned int) index) < (unsigned int) len2) + *t = charactersToInsertInstead [index]; + + ++t; + } + + return result; +} + +bool String::startsWith (const tchar* const other) const throw() +{ + return other != 0 + && CharacterFunctions::compare (text->text, other, CharacterFunctions::length (other)) == 0; +} + +bool String::startsWithIgnoreCase (const tchar* const other) const throw() +{ + return other != 0 + && CharacterFunctions::compareIgnoreCase (text->text, other, CharacterFunctions::length (other)) == 0; +} + +bool String::startsWithChar (const tchar character) const throw() +{ + return text->text[0] == character; +} + +bool String::endsWithChar (const tchar character) const throw() +{ + return text->text[0] != 0 + && text->text [length() - 1] == character; +} + +bool String::endsWith (const tchar* const other) const throw() +{ + if (other == 0) + return false; + + const int thisLen = length(); + const int otherLen = CharacterFunctions::length (other); + + return thisLen >= otherLen + && CharacterFunctions::compare (text->text + thisLen - otherLen, other) == 0; +} + +bool String::endsWithIgnoreCase (const tchar* const other) const throw() +{ + if (other == 0) + return false; + + const int thisLen = length(); + const int otherLen = CharacterFunctions::length (other); + + return thisLen >= otherLen + && CharacterFunctions::compareIgnoreCase (text->text + thisLen - otherLen, other) == 0; +} + +const String String::toUpperCase() const throw() +{ + String result (*this); + result.dupeInternalIfMultiplyReferenced(); + CharacterFunctions::toUpperCase (result.text->text); + return result; +} + +const String String::toLowerCase() const throw() +{ + String result (*this); + result.dupeInternalIfMultiplyReferenced(); + CharacterFunctions::toLowerCase (result.text->text); + return result; +} + +tchar& String::operator[] (const int index) throw() +{ + jassert (((unsigned int) index) <= (unsigned int) length()); + + dupeInternalIfMultiplyReferenced(); + + return text->text [index]; +} + +tchar String::getLastCharacter() const throw() +{ + return (isEmpty()) ? ((tchar) 0) + : text->text [CharacterFunctions::length (text->text) - 1]; +} + +const String String::substring (int start, int end) const throw() +{ + if (start < 0) + start = 0; + else if (end <= start) + return empty; + + int len = 0; + const tchar* const t = text->text; + + while (len <= end && t [len] != 0) + ++len; + + if (end >= len) + { + if (start == 0) + return *this; + + end = len; + } + + return String (text->text + start, + end - start); +} + +const String String::substring (const int start) const throw() +{ + if (start <= 0) + return *this; + + const int len = CharacterFunctions::length (text->text); + + if (start >= len) + return empty; + else + return String (text->text + start, + len - start); +} + +const String String::dropLastCharacters (const int numberToDrop) const throw() +{ + return String (text->text, + jmax (0, CharacterFunctions::length (text->text) - numberToDrop)); +} + +const String String::fromFirstOccurrenceOf (const tchar* const sub, + const bool includeSubString, + const bool ignoreCase) const throw() +{ + const int i = ignoreCase ? indexOf (sub) + : indexOfIgnoreCase (sub); + + if (i < 0) + return empty; + else + return substring ((includeSubString) ? i : i + CharacterFunctions::length (sub)); +} + +const String String::fromLastOccurrenceOf (const tchar* const sub, + const bool includeSubString, + const bool ignoreCase) const throw() +{ + const int i = ignoreCase ? lastIndexOf (sub) + : lastIndexOfIgnoreCase (sub); + + if (i < 0) + return *this; + else + return substring ((includeSubString) ? i : i + CharacterFunctions::length (sub)); +} + +const String String::upToFirstOccurrenceOf (const tchar* const sub, + const bool includeSubString, + const bool ignoreCase) const throw() +{ + const int i = ignoreCase ? indexOfIgnoreCase (sub) + : indexOf (sub); + + if (i < 0) + return *this; + else + return substring (0, (includeSubString) ? i + CharacterFunctions::length (sub) : i); +} + +const String String::upToLastOccurrenceOf (const tchar* const sub, + const bool includeSubString, + const bool ignoreCase) const throw() +{ + const int i = ignoreCase ? lastIndexOfIgnoreCase (sub) + : lastIndexOf (sub); + if (i < 0) + return *this; + + return substring (0, (includeSubString) ? i + CharacterFunctions::length (sub) : i); +} + +bool String::isQuotedString() const throw() +{ + const String trimmed (trimStart()); + + return trimmed[0] == T('"') + || trimmed[0] == T('\''); +} + +const String String::unquoted() const throw() +{ + String s (*this); + + if (s[0] == T('"') || s[0] == T('\'')) + s = s.substring (1); + + const int lastCharIndex = s.length() - 1; + + if (lastCharIndex >= 0 + && (s [lastCharIndex] == T('"') || s[lastCharIndex] == T('\''))) + s [lastCharIndex] = 0; + + return s; +} + +const String String::quoted (const tchar quoteCharacter) const throw() +{ + if (isEmpty()) + return charToString (quoteCharacter) + quoteCharacter; + + String t (*this); + + if (! t.startsWithChar (quoteCharacter)) + t = charToString (quoteCharacter) + t; + + if (! t.endsWithChar (quoteCharacter)) + t += quoteCharacter; + + return t; +} + +const String String::trim() const throw() +{ + if (isEmpty()) + return empty; + + int start = 0; + + while (CharacterFunctions::isWhitespace (text->text [start])) + ++start; + + const int len = CharacterFunctions::length (text->text); + int end = len - 1; + + while ((end >= start) && CharacterFunctions::isWhitespace (text->text [end])) + --end; + + ++end; + + if (end <= start) + return empty; + else if (start > 0 || end < len) + return String (text->text + start, end - start); + else + return *this; +} + +const String String::trimStart() const throw() +{ + if (isEmpty()) + return empty; + + const tchar* t = text->text; + + while (CharacterFunctions::isWhitespace (*t)) + ++t; + + if (t == text->text) + return *this; + else + return String (t); +} + +const String String::trimEnd() const throw() +{ + if (isEmpty()) + return empty; + + const tchar* endT = text->text + (CharacterFunctions::length (text->text) - 1); + + while ((endT >= text->text) && CharacterFunctions::isWhitespace (*endT)) + --endT; + + return String (text->text, (int) (++endT - text->text)); +} + +const String String::retainCharacters (const tchar* const charactersToRetain) const throw() +{ + jassert (charactersToRetain != 0); + + if (isEmpty()) + return empty; + + String result (text->allocatedNumChars, (int) 0); + tchar* dst = result.text->text; + const tchar* src = text->text; + + while (*src != 0) + { + if (CharacterFunctions::indexOfCharFast (charactersToRetain, *src) >= 0) + *dst++ = *src; + + ++src; + } + + *dst = 0; + return result; +} + +const String String::removeCharacters (const tchar* const charactersToRemove) const throw() +{ + jassert (charactersToRemove != 0); + + if (isEmpty()) + return empty; + + String result (text->allocatedNumChars, (int) 0); + tchar* dst = result.text->text; + const tchar* src = text->text; + + while (*src != 0) + { + if (CharacterFunctions::indexOfCharFast (charactersToRemove, *src) < 0) + *dst++ = *src; + + ++src; + } + + *dst = 0; + return result; +} + +const String String::initialSectionContainingOnly (const tchar* const permittedCharacters) const throw() +{ + return substring (0, CharacterFunctions::getIntialSectionContainingOnly (text->text, permittedCharacters)); +} + +const String String::initialSectionNotContaining (const tchar* const charactersToStopAt) const throw() +{ + jassert (charactersToStopAt != 0); + + const tchar* const t = text->text; + int i = 0; + + while (t[i] != 0) + { + if (CharacterFunctions::indexOfCharFast (charactersToStopAt, t[i]) >= 0) + return String (text->text, i); + + ++i; + } + + return empty; +} + +bool String::containsOnly (const tchar* const chars) const throw() +{ + jassert (chars != 0); + + const tchar* t = text->text; + + while (*t != 0) + if (CharacterFunctions::indexOfCharFast (chars, *t++) < 0) + return false; + + return true; +} + +bool String::containsAnyOf (const tchar* const chars) const throw() +{ + jassert (chars != 0); + + const tchar* t = text->text; + + while (*t != 0) + if (CharacterFunctions::indexOfCharFast (chars, *t++) >= 0) + return true; + + return false; +} + +int String::getIntValue() const throw() +{ + return CharacterFunctions::getIntValue (text->text); +} + +int String::getTrailingIntValue() const throw() +{ + int n = 0; + int mult = 1; + const tchar* t = text->text + length(); + + while (--t >= text->text) + { + const tchar c = *t; + + if (! CharacterFunctions::isDigit (c)) + { + if (c == T('-')) + n = -n; + + break; + } + + n += mult * (c - T('0')); + mult *= 10; + } + + return n; +} + +int64 String::getLargeIntValue() const throw() +{ + return CharacterFunctions::getInt64Value (text->text); +} + +float String::getFloatValue() const throw() +{ + return (float) CharacterFunctions::getDoubleValue (text->text); +} + +double String::getDoubleValue() const throw() +{ + return CharacterFunctions::getDoubleValue (text->text); +} + +static const tchar* const hexDigits = T("0123456789abcdef"); + +const String String::toHexString (const int number) throw() +{ + tchar buffer[32]; + tchar* const end = buffer + 32; + tchar* t = end; + *--t = 0; + unsigned int v = (unsigned int) number; + + do + { + *--t = hexDigits [v & 15]; + v >>= 4; + + } while (v != 0); + + return String (t, (int) (((char*) end) - (char*) t) - 1); +} + +const String String::toHexString (const int64 number) throw() +{ + tchar buffer[32]; + tchar* const end = buffer + 32; + tchar* t = end; + *--t = 0; + uint64 v = (uint64) number; + + do + { + *--t = hexDigits [(int) (v & 15)]; + v >>= 4; + + } while (v != 0); + + return String (t, (int) (((char*) end) - (char*) t)); +} + +const String String::toHexString (const short number) throw() +{ + return toHexString ((int) (unsigned short) number); +} + +const String String::toHexString (const unsigned char* data, + const int size, + const int groupSize) throw() +{ + if (size <= 0) + return empty; + + int numChars = (size * 2) + 2; + if (groupSize > 0) + numChars += size / groupSize; + + String s (numChars, (int) 0); + + tchar* d = s.text->text; + + for (int i = 0; i < size; ++i) + { + *d++ = hexDigits [(*data) >> 4]; + *d++ = hexDigits [(*data) & 0xf]; + ++data; + + if (groupSize > 0 && (i % groupSize) == 0) + *d++ = T(' '); + } + + if (groupSize > 0) + --d; + + *d = 0; + + return s; +} + +int String::getHexValue32() const throw() +{ + int result = 0; + const tchar* c = text->text; + + for (;;) + { + const int hexValue = CharacterFunctions::getHexDigitValue (*c); + + if (hexValue >= 0) + result = (result << 4) | hexValue; + else if (*c == 0) + break; + + ++c; + } + + return result; +} + +int64 String::getHexValue64() const throw() +{ + int64 result = 0; + const tchar* c = text->text; + + for (;;) + { + const int hexValue = CharacterFunctions::getHexDigitValue (*c); + + if (hexValue >= 0) + result = (result << 4) | hexValue; + else if (*c == 0) + break; + + ++c; + } + + return result; +} + +const String String::createStringFromData (const void* const data_, + const int size) throw() +{ + const char* const data = (const char*) data_; + + if (size <= 0 || data == 0) + { + return empty; + } + else if (size < 2) + { + return charToString (data[0]); + } + else if ((data[0] == (char)-2 && data[1] == (char)-1) + || (data[0] == (char)-1 && data[1] == (char)-2)) + { + // assume it's 16-bit unicode + const bool bigEndian = (data[0] == (char)-2); + const int numChars = size / 2 - 1; + + String result; + result.preallocateStorage (numChars + 2); + + const uint16* const src = (const uint16*) (data + 2); + tchar* const dst = const_cast ((const tchar*) result); + + if (bigEndian) + { + for (int i = 0; i < numChars; ++i) + dst[i] = (tchar) swapIfLittleEndian (src[i]); + } + else + { + for (int i = 0; i < numChars; ++i) + dst[i] = (tchar) swapIfBigEndian (src[i]); + } + + dst [numChars] = 0; + return result; + } + else + { +#if JUCE_STRINGS_ARE_UNICODE && JUCE_LINUX + // (workaround for strange behaviour of mbstowcs) + int i; + for (i = 0; i < size; ++i) + if (data[i] == 0) + break; + + String result; + result.preallocateStorage (i + 1); + tchar* const dst = const_cast ((const tchar*) result); + + for (int j = 0; j < i; ++j) + dst[j] = (juce_wchar) (unsigned char) data[j]; + + dst[i] = 0; + + return result; +#else + return String (data, size); +#endif + } +} + +const char* String::toUTF8() const throw() +{ + if (isEmpty()) + { + return (const char*) emptyCharString; + } + else + { + String* const mutableThis = const_cast (this); + + mutableThis->dupeInternalIfMultiplyReferenced(); + + const int currentLen = CharacterFunctions::length (text->text) + 1; + const int utf8BytesNeeded = copyToUTF8 (0); + + mutableThis->text = (InternalRefCountedStringHolder*) + juce_realloc (text, sizeof (InternalRefCountedStringHolder) + + (currentLen * sizeof (juce_wchar) + utf8BytesNeeded)); + + char* const otherCopy = (char*) (text->text + currentLen); + copyToUTF8 ((uint8*) otherCopy); + + return otherCopy; + } +} + +int String::copyToUTF8 (uint8* const buffer) const throw() +{ +#if JUCE_STRINGS_ARE_UNICODE + int num = 0, index = 0; + + for (;;) + { + const uint32 c = (uint32) text->text [index++]; + + if (c >= 0x80) + { + int numExtraBytes = 1; + + if (c >= 0x800) + { + ++numExtraBytes; + + if (c >= 0x10000) + { + ++numExtraBytes; + + if (c >= 0x200000) + { + ++numExtraBytes; + + if (c >= 0x4000000) + ++numExtraBytes; + } + } + } + + if (buffer != 0) + { + buffer [num++] = (uint8) ((0xff << (7 - numExtraBytes)) | (c >> (numExtraBytes * 6))); + + while (--numExtraBytes >= 0) + buffer [num++] = (uint8) (0x80 | (0x3f & (c >> (numExtraBytes * 6)))); + } + else + { + num += numExtraBytes + 1; + } + } + else + { + if (buffer != 0) + buffer [num] = (uint8) c; + + ++num; + } + + if (c == 0) + break; + } + + return num; + +#else + const int numBytes = length() + 1; + + if (buffer != 0) + copyToBuffer ((char*) buffer, numBytes); + + return numBytes; +#endif +} + +const String String::fromUTF8 (const uint8* const buffer, int bufferSizeBytes) throw() +{ + if (buffer == 0) + return empty; + + if (bufferSizeBytes < 0) + bufferSizeBytes = INT_MAX; + + int numBytes; + for (numBytes = 0; numBytes < bufferSizeBytes; ++numBytes) + if (buffer [numBytes] == 0) + break; + + String result (numBytes + 1, 0); + tchar* dest = result.text->text; + + int i = 0; + while (i < numBytes) + { + const uint8 c = buffer [i++]; + + if ((c & 0x80) != 0) + { + int mask = 0x7f; + int bit = 0x40; + int numExtraValues = 0; + + while (bit != 0 && (c & bit) != 0) + { + bit >>= 1; + mask >>= 1; + ++numExtraValues; + } + + int n = (c & mask); + + while (--numExtraValues >= 0 && i < bufferSizeBytes) + { + const uint8 c = buffer[i]; + + if ((c & 0xc0) != 0x80) + break; + + n <<= 6; + n |= (c & 0x3f); + ++i; + } + + *dest++ = (tchar) n; + } + else + { + *dest++ = (tchar) c; + } + } + + *dest = 0; + return result; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_String.cpp *********/ + +/********* Start of inlined file: juce_StringArray.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +StringArray::StringArray() throw() +{ +} + +StringArray::StringArray (const StringArray& other) throw() +{ + addArray (other); +} + +StringArray::StringArray (const juce_wchar** const strings, + const int numberOfStrings) throw() +{ + for (int i = 0; i < numberOfStrings; ++i) + add (strings [i]); +} + +StringArray::StringArray (const char** const strings, + const int numberOfStrings) throw() +{ + for (int i = 0; i < numberOfStrings; ++i) + add (strings [i]); +} + +StringArray::StringArray (const juce_wchar** const strings) throw() +{ + int i = 0; + + while (strings[i] != 0) + add (strings [i++]); +} + +StringArray::StringArray (const char** const strings) throw() +{ + int i = 0; + + while (strings[i] != 0) + add (strings [i++]); +} + +const StringArray& StringArray::operator= (const StringArray& other) throw() +{ + if (this != &other) + { + clear(); + addArray (other); + } + + return *this; +} + +StringArray::~StringArray() throw() +{ + clear(); +} + +bool StringArray::operator== (const StringArray& other) const throw() +{ + if (other.size() != size()) + return false; + + for (int i = size(); --i >= 0;) + { + if (*(String*) other.strings.getUnchecked(i) + != *(String*) strings.getUnchecked(i)) + { + return false; + } + } + + return true; +} + +bool StringArray::operator!= (const StringArray& other) const throw() +{ + return ! operator== (other); +} + +void StringArray::clear() throw() +{ + for (int i = size(); --i >= 0;) + { + String* const s = (String*) strings.getUnchecked(i); + delete s; + } + + strings.clear(); +} + +const String& StringArray::operator[] (const int index) const throw() +{ + if (((unsigned int) index) < (unsigned int) strings.size()) + return *(const String*) (strings.getUnchecked (index)); + + return String::empty; +} + +void StringArray::add (const String& newString) throw() +{ + strings.add (new String (newString)); +} + +void StringArray::insert (const int index, + const String& newString) throw() +{ + strings.insert (index, new String (newString)); +} + +void StringArray::addIfNotAlreadyThere (const String& newString, + const bool ignoreCase) throw() +{ + if (! contains (newString, ignoreCase)) + add (newString); +} + +void StringArray::addArray (const StringArray& otherArray, + int startIndex, + int numElementsToAdd) throw() +{ + if (startIndex < 0) + { + jassertfalse + startIndex = 0; + } + + if (numElementsToAdd < 0 || startIndex + numElementsToAdd > otherArray.size()) + numElementsToAdd = otherArray.size() - startIndex; + + while (--numElementsToAdd >= 0) + strings.add (new String (*(const String*) otherArray.strings.getUnchecked (startIndex++))); +} + +void StringArray::set (const int index, + const String& newString) throw() +{ + String* const s = (String*) strings [index]; + + if (s != 0) + { + *s = newString; + } + else if (index >= 0) + { + add (newString); + } +} + +bool StringArray::contains (const String& stringToLookFor, + const bool ignoreCase) const throw() +{ + if (ignoreCase) + { + for (int i = size(); --i >= 0;) + if (stringToLookFor.equalsIgnoreCase (*(const String*)(strings.getUnchecked(i)))) + return true; + } + else + { + for (int i = size(); --i >= 0;) + if (stringToLookFor == *(const String*)(strings.getUnchecked(i))) + return true; + } + + return false; +} + +int StringArray::indexOf (const String& stringToLookFor, + const bool ignoreCase, + int i) const throw() +{ + if (i < 0) + i = 0; + + const int numElements = size(); + + if (ignoreCase) + { + while (i < numElements) + { + if (stringToLookFor.equalsIgnoreCase (*(const String*) strings.getUnchecked (i))) + return i; + + ++i; + } + } + else + { + while (i < numElements) + { + if (stringToLookFor == *(const String*) strings.getUnchecked (i)) + return i; + + ++i; + } + } + + return -1; +} + +void StringArray::remove (const int index) throw() +{ + String* const s = (String*) strings [index]; + + if (s != 0) + { + strings.remove (index); + delete s; + } +} + +void StringArray::removeString (const String& stringToRemove, + const bool ignoreCase) throw() +{ + if (ignoreCase) + { + for (int i = size(); --i >= 0;) + if (stringToRemove.equalsIgnoreCase (*(const String*) strings.getUnchecked (i))) + remove (i); + } + else + { + for (int i = size(); --i >= 0;) + if (stringToRemove == *(const String*) strings.getUnchecked (i)) + remove (i); + } +} + +void StringArray::removeEmptyStrings (const bool removeWhitespaceStrings) throw() +{ + if (removeWhitespaceStrings) + { + for (int i = size(); --i >= 0;) + if (((const String*) strings.getUnchecked(i))->trim().isEmpty()) + remove (i); + } + else + { + for (int i = size(); --i >= 0;) + if (((const String*) strings.getUnchecked(i))->isEmpty()) + remove (i); + } +} + +void StringArray::trim() throw() +{ + for (int i = size(); --i >= 0;) + { + String& s = *(String*) strings.getUnchecked(i); + s = s.trim(); + } +} + +class InternalStringArrayComparator +{ +public: + static int compareElements (void* const first, void* const second) throw() + { + return ((const String*) first)->compare (*(const String*) second); + } +}; + +class InsensitiveInternalStringArrayComparator +{ +public: + static int compareElements (void* const first, void* const second) throw() + { + return ((const String*) first)->compareIgnoreCase (*(const String*) second); + } +}; + +void StringArray::sort (const bool ignoreCase) throw() +{ + if (ignoreCase) + { + InsensitiveInternalStringArrayComparator comp; + strings.sort (comp); + } + else + { + InternalStringArrayComparator comp; + strings.sort (comp); + } +} + +void StringArray::move (const int currentIndex, int newIndex) throw() +{ + strings.move (currentIndex, newIndex); +} + +const String StringArray::joinIntoString (const String& separator, + int start, + int numberToJoin) const throw() +{ + const int last = (numberToJoin < 0) ? size() + : jmin (size(), start + numberToJoin); + + if (start < 0) + start = 0; + + if (start >= last) + return String::empty; + + if (start == last - 1) + return *(const String*) strings.getUnchecked (start); + + const int separatorLen = separator.length(); + int charsNeeded = separatorLen * (last - start - 1); + + for (int i = start; i < last; ++i) + charsNeeded += ((const String*) strings.getUnchecked(i))->length(); + + String result; + result.preallocateStorage (charsNeeded); + + tchar* dest = (tchar*) (const tchar*) result; + + while (start < last) + { + const String& s = *(const String*) strings.getUnchecked (start); + const int len = s.length(); + + if (len > 0) + { + s.copyToBuffer (dest, len); + dest += len; + } + + if (++start < last && separatorLen > 0) + { + separator.copyToBuffer (dest, separatorLen); + dest += separatorLen; + } + } + + *dest = 0; + + return result; +} + +int StringArray::addTokens (const tchar* const text, + const bool preserveQuotedStrings) throw() +{ + return addTokens (text, + T(" \n\r\t"), + preserveQuotedStrings ? T("\"") : 0); +} + +int StringArray::addTokens (const tchar* const text, + const tchar* breakCharacters, + const tchar* quoteCharacters) throw() +{ + int num = 0; + + if (text != 0 && *text != 0) + { + if (breakCharacters == 0) + breakCharacters = T(""); + + if (quoteCharacters == 0) + quoteCharacters = T(""); + + bool insideQuotes = false; + tchar currentQuoteChar = 0; + + int i = 0; + int tokenStart = 0; + + for (;;) + { + const tchar c = text[i]; + + bool isBreak = (c == 0); + + if (! (insideQuotes || isBreak)) + { + const tchar* b = breakCharacters; + while (*b != 0) + { + if (*b++ == c) + { + isBreak = true; + break; + } + } + } + + if (! isBreak) + { + bool isQuote = false; + const tchar* q = quoteCharacters; + while (*q != 0) + { + if (*q++ == c) + { + isQuote = true; + break; + } + } + + if (isQuote) + { + if (insideQuotes) + { + // only break out of quotes-mode if we find a matching quote to the + // one that we opened with.. + if (currentQuoteChar == c) + insideQuotes = false; + } + else + { + insideQuotes = true; + currentQuoteChar = c; + } + } + } + else + { + add (String (text + tokenStart, i - tokenStart)); + + ++num; + tokenStart = i + 1; + } + + if (c == 0) + break; + + ++i; + } + } + + return num; +} + +int StringArray::addLines (const tchar* text) throw() +{ + int numLines = 0; + + if (text != 0) + { + while (*text != 0) + { + const tchar* const startOfLine = text; + + while (*text != 0) + { + if (*text == T('\r')) + { + ++text; + if (*text == T('\n')) + ++text; + + break; + } + + if (*text == T('\n')) + { + ++text; + break; + } + + ++text; + } + + const tchar* endOfLine = text; + if (endOfLine > startOfLine && (*(endOfLine - 1) == T('\r') || *(endOfLine - 1) == T('\n'))) + --endOfLine; + + if (endOfLine > startOfLine && (*(endOfLine - 1) == T('\r') || *(endOfLine - 1) == T('\n'))) + --endOfLine; + + add (String (startOfLine, jmax (0, (int) (endOfLine - startOfLine)))); + + ++numLines; + } + } + + return numLines; +} + +void StringArray::removeDuplicates (const bool ignoreCase) throw() +{ + for (int i = 0; i < size() - 1; ++i) + { + const String& s = *(String*) strings.getUnchecked(i); + + int nextIndex = i + 1; + + for (;;) + { + nextIndex = indexOf (s, ignoreCase, nextIndex); + + if (nextIndex < 0) + break; + + remove (nextIndex); + } + } +} + +void StringArray::appendNumbersToDuplicates (const bool ignoreCase, + const bool appendNumberToFirstInstance, + const tchar* const preNumberString, + const tchar* const postNumberString) throw() +{ + for (int i = 0; i < size() - 1; ++i) + { + String& s = *(String*) strings.getUnchecked(i); + + int nextIndex = indexOf (s, ignoreCase, i + 1); + + if (nextIndex >= 0) + { + const String original (s); + + int number = 0; + + if (appendNumberToFirstInstance) + s = original + preNumberString + String (++number) + postNumberString; + else + ++number; + + while (nextIndex >= 0) + { + set (nextIndex, (*this)[nextIndex] + preNumberString + String (++number) + postNumberString); + nextIndex = indexOf (original, ignoreCase, nextIndex + 1); + } + } + } +} + +void StringArray::minimiseStorageOverheads() throw() +{ + strings.minimiseStorageOverheads(); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_StringArray.cpp *********/ + +/********* Start of inlined file: juce_StringPairArray.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +StringPairArray::StringPairArray (const bool ignoreCase_) throw() + : ignoreCase (ignoreCase_) +{ +} + +StringPairArray::StringPairArray (const StringPairArray& other) throw() + : keys (other.keys), + values (other.values), + ignoreCase (other.ignoreCase) +{ +} + +StringPairArray::~StringPairArray() throw() +{ +} + +const StringPairArray& StringPairArray::operator= (const StringPairArray& other) throw() +{ + keys = other.keys; + values = other.values; + + return *this; +} + +bool StringPairArray::operator== (const StringPairArray& other) const throw() +{ + for (int i = keys.size(); --i >= 0;) + if (other [keys[i]] != values[i]) + return false; + + return true; +} + +bool StringPairArray::operator!= (const StringPairArray& other) const throw() +{ + return ! operator== (other); +} + +const String& StringPairArray::operator[] (const String& key) const throw() +{ + return values [keys.indexOf (key, ignoreCase)]; +} + +const String StringPairArray::getValue (const String& key, const String& defaultReturnValue) const +{ + const int i = keys.indexOf (key, ignoreCase); + + if (i >= 0) + return values[i]; + + return defaultReturnValue; +} + +void StringPairArray::set (const String& key, + const String& value) throw() +{ + const int i = keys.indexOf (key, ignoreCase); + + if (i >= 0) + { + values.set (i, value); + } + else + { + keys.add (key); + values.add (value); + } +} + +void StringPairArray::addArray (const StringPairArray& other) +{ + for (int i = 0; i < other.size(); ++i) + set (other.keys[i], other.values[i]); +} + +void StringPairArray::clear() throw() +{ + keys.clear(); + values.clear(); +} + +void StringPairArray::remove (const String& key) throw() +{ + remove (keys.indexOf (key, ignoreCase)); +} + +void StringPairArray::remove (const int index) throw() +{ + keys.remove (index); + values.remove (index); +} + +void StringPairArray::minimiseStorageOverheads() throw() +{ + keys.minimiseStorageOverheads(); + values.minimiseStorageOverheads(); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_StringPairArray.cpp *********/ + +/********* Start of inlined file: juce_XmlDocument.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +static bool isXmlIdentifierChar_Slow (const tchar c) throw() +{ + return CharacterFunctions::isLetterOrDigit (c) + || c == T('_') + || c == T('-') + || c == T(':') + || c == T('.'); +} + +#define isXmlIdentifierChar(c) \ + ((c > 0 && c <= 127) ? identifierLookupTable [(int) c] : isXmlIdentifierChar_Slow (c)) + +XmlDocument::XmlDocument (const String& documentText) throw() + : originalText (documentText), + inputSource (0) +{ +} + +XmlDocument::XmlDocument (const File& file) +{ + inputSource = new FileInputSource (file); +} + +XmlDocument::~XmlDocument() throw() +{ + delete inputSource; +} + +void XmlDocument::setInputSource (InputSource* const newSource) throw() +{ + if (inputSource != newSource) + { + delete inputSource; + inputSource = newSource; + } +} + +XmlElement* XmlDocument::getDocumentElement (const bool onlyReadOuterDocumentElement) +{ + String textToParse (originalText); + + if (textToParse.isEmpty() && inputSource != 0) + { + InputStream* const in = inputSource->createInputStream(); + + if (in != 0) + { + MemoryBlock data; + + in->readIntoMemoryBlock (data, onlyReadOuterDocumentElement ? 8192 : -1); + delete in; + + if (data.getSize() >= 2 + && ((data[0] == (char)-2 && data[1] == (char)-1) + || (data[0] == (char)-1 && data[1] == (char)-2))) + { + textToParse = String::createStringFromData ((const char*) data.getData(), data.getSize()); + } + else + { + textToParse = String::fromUTF8 ((const uint8*) data.getData(), data.getSize()); + } + + if (! onlyReadOuterDocumentElement) + originalText = textToParse; + } + } + + input = textToParse; + lastError = String::empty; + errorOccurred = false; + outOfData = false; + needToLoadDTD = true; + + for (int i = 0; i < 128; ++i) + identifierLookupTable[i] = isXmlIdentifierChar_Slow ((tchar) i); + + if (textToParse.isEmpty()) + { + lastError = "not enough input"; + } + else + { + skipHeader(); + + if (input != 0) + { + XmlElement* const result = readNextElement (! onlyReadOuterDocumentElement); + + if (errorOccurred) + delete result; + else + return result; + } + else + { + lastError = "incorrect xml header"; + } + } + + return 0; +} + +const String& XmlDocument::getLastParseError() const throw() +{ + return lastError; +} + +void XmlDocument::setLastError (const String& desc, const bool carryOn) throw() +{ + lastError = desc; + errorOccurred = ! carryOn; +} + +const String XmlDocument::getFileContents (const String& filename) const +{ + String result; + + if (inputSource != 0) + { + InputStream* const in = inputSource->createInputStreamFor (filename.trim().unquoted()); + + if (in != 0) + { + result = in->readEntireStreamAsString(); + delete in; + } + } + + return result; +} + +tchar XmlDocument::readNextChar() throw() +{ + if (*input != 0) + { + return *input++; + } + else + { + outOfData = true; + return 0; + } +} + +int XmlDocument::findNextTokenLength() throw() +{ + int len = 0; + tchar c = *input; + + while (isXmlIdentifierChar (c)) + c = input [++len]; + + return len; +} + +void XmlDocument::skipHeader() throw() +{ + const tchar* const found = CharacterFunctions::find (input, T("")); + + if (input == 0) + return; + + input += 2; + } + + skipNextWhiteSpace(); + const tchar* docType = CharacterFunctions::find (input, T(" 0) + { + const tchar c = readNextChar(); + + if (outOfData) + return; + + if (c == T('<')) + ++n; + else if (c == T('>')) + --n; + } + + docType += 9; + dtdText = String (docType, (int) (input - (docType + 1))).trim(); +} + +void XmlDocument::skipNextWhiteSpace() throw() +{ + for (;;) + { + tchar c = *input; + + while (CharacterFunctions::isWhitespace (c)) + c = *++input; + + if (c == 0) + { + outOfData = true; + break; + } + else if (c == T('<')) + { + if (input[1] == T('!') + && input[2] == T('-') + && input[3] == T('-')) + { + const tchar* const closeComment = CharacterFunctions::find (input, T("-->")); + + if (closeComment == 0) + { + outOfData = true; + break; + } + + input = closeComment + 3; + continue; + } + else if (input[1] == T('?')) + { + const tchar* const closeBracket = CharacterFunctions::find (input, T("?>")); + + if (closeBracket == 0) + { + outOfData = true; + break; + } + + input = closeBracket + 2; + continue; + } + } + + break; + } +} + +void XmlDocument::readQuotedString (String& result) throw() +{ + const tchar quote = readNextChar(); + + while (! outOfData) + { + const tchar character = readNextChar(); + + if (character == quote) + break; + + if (character == T('&')) + { + --input; + readEntity (result); + } + else + { + --input; + const tchar* const start = input; + + for (;;) + { + const tchar character = *input; + + if (character == quote) + { + result.append (start, (int) (input - start)); + ++input; + + return; + } + else if (character == T('&')) + { + result.append (start, (int) (input - start)); + break; + } + else if (character == 0) + { + outOfData = true; + setLastError ("unmatched quotes", false); + break; + } + + ++input; + } + } + } +} + +XmlElement* XmlDocument::readNextElement (const bool alsoParseSubElements) throw() +{ + XmlElement* node = 0; + + skipNextWhiteSpace(); + if (outOfData) + return 0; + + input = CharacterFunctions::find (input, T("<")); + + if (input != 0) + { + ++input; + int tagLen = findNextTokenLength(); + + if (tagLen == 0) + { + // no tag name - but allow for a gap after the '<' before giving an error + skipNextWhiteSpace(); + tagLen = findNextTokenLength(); + + if (tagLen == 0) + { + setLastError ("tag name missing", false); + return node; + } + } + + node = new XmlElement (input, tagLen); + input += tagLen; + XmlElement::XmlAttributeNode* lastAttribute = 0; + + // look for attributes + for (;;) + { + skipNextWhiteSpace(); + + const tchar c = *input; + + // empty tag.. + if (c == T('/') && input[1] == T('>')) + { + input += 2; + break; + } + + // parse the guts of the element.. + if (c == T('>')) + { + ++input; + skipNextWhiteSpace(); + + if (alsoParseSubElements) + readChildElements (node); + + break; + } + + // get an attribute.. + if (isXmlIdentifierChar (c)) + { + const int attNameLen = findNextTokenLength(); + + if (attNameLen > 0) + { + const tchar* attNameStart = input; + input += attNameLen; + + skipNextWhiteSpace(); + + if (readNextChar() == T('=')) + { + skipNextWhiteSpace(); + + const tchar c = *input; + + if (c == T('"') || c == T('\'')) + { + XmlElement::XmlAttributeNode* const newAtt + = new XmlElement::XmlAttributeNode (String (attNameStart, attNameLen), + String::empty); + + readQuotedString (newAtt->value); + + if (lastAttribute == 0) + node->attributes = newAtt; + else + lastAttribute->next = newAtt; + + lastAttribute = newAtt; + + continue; + } + } + } + } + else + { + if (! outOfData) + setLastError ("illegal character found in " + node->getTagName() + ": '" + c + "'", false); + } + + break; + } + } + + return node; +} + +void XmlDocument::readChildElements (XmlElement* parent) throw() +{ + XmlElement* lastChildNode = 0; + + for (;;) + { + skipNextWhiteSpace(); + + if (outOfData) + { + setLastError ("unmatched tags", false); + break; + } + + if (*input == T('<')) + { + if (input[1] == T('/')) + { + // our close tag.. + input = CharacterFunctions::find (input, T(">")); + ++input; + break; + } + else if (input[1] == T('!') + && input[2] == T('[') + && input[3] == T('C') + && input[4] == T('D') + && input[5] == T('A') + && input[6] == T('T') + && input[7] == T('A') + && input[8] == T('[')) + { + input += 9; + const tchar* const inputStart = input; + + int len = 0; + + for (;;) + { + if (*input == 0) + { + setLastError ("unterminated CDATA section", false); + outOfData = true; + break; + } + else if (input[0] == T(']') + && input[1] == T(']') + && input[2] == T('>')) + { + input += 3; + break; + } + + ++input; + ++len; + } + + XmlElement* const e = new XmlElement ((int) 0); + e->setText (String (inputStart, len)); + + if (lastChildNode != 0) + lastChildNode->nextElement = e; + else + parent->addChildElement (e); + + lastChildNode = e; + } + else + { + // this is some other element, so parse and add it.. + XmlElement* const n = readNextElement (true); + + if (n != 0) + { + if (lastChildNode == 0) + parent->addChildElement (n); + else + lastChildNode->nextElement = n; + + lastChildNode = n; + } + else + { + return; + } + } + } + else + { + // read character block.. + XmlElement* const e = new XmlElement ((int)0); + + if (lastChildNode != 0) + lastChildNode->nextElement = e; + else + parent->addChildElement (e); + + lastChildNode = e; + + String textElementContent; + + for (;;) + { + const tchar c = *input; + + if (c == T('<')) + break; + + if (c == 0) + { + setLastError ("unmatched tags", false); + outOfData = true; + return; + } + + if (c == T('&')) + { + String entity; + readEntity (entity); + + if (entity.startsWithChar (T('<')) && entity [1] != 0) + { + const tchar* const oldInput = input; + const bool oldOutOfData = outOfData; + + input = (const tchar*) entity; + outOfData = false; + + for (;;) + { + XmlElement* const n = readNextElement (true); + + if (n == 0) + break; + + if (lastChildNode == 0) + parent->addChildElement (n); + else + lastChildNode->nextElement = n; + + lastChildNode = n; + } + + input = oldInput; + outOfData = oldOutOfData; + } + else + { + textElementContent += entity; + } + } + else + { + const tchar* start = input; + int len = 0; + + for (;;) + { + const tchar c = *input; + + if (c == T('<') || c == T('&')) + { + break; + } + else if (c == 0) + { + setLastError ("unmatched tags", false); + outOfData = true; + return; + } + + ++input; + ++len; + } + + textElementContent.append (start, len); + } + } + + textElementContent = textElementContent.trim(); + + if (textElementContent.isNotEmpty()) + e->setText (textElementContent); + } + } +} + +void XmlDocument::readEntity (String& result) throw() +{ + // skip over the ampersand + ++input; + + if (CharacterFunctions::compareIgnoreCase (input, T("amp;"), 4) == 0) + { + input += 4; + result += T("&"); + } + else if (CharacterFunctions::compareIgnoreCase (input, T("quot;"), 5) == 0) + { + input += 5; + result += T("\""); + } + else if (CharacterFunctions::compareIgnoreCase (input, T("apos;"), 5) == 0) + { + input += 5; + result += T("\'"); + } + else if (CharacterFunctions::compareIgnoreCase (input, T("lt;"), 3) == 0) + { + input += 3; + result += T("<"); + } + else if (CharacterFunctions::compareIgnoreCase (input, T("gt;"), 3) == 0) + { + input += 3; + result += T(">"); + } + else if (*input == T('#')) + { + int charCode = 0; + ++input; + + if (*input == T('x') || *input == T('X')) + { + ++input; + int numChars = 0; + + while (input[0] != T(';')) + { + const int hexValue = CharacterFunctions::getHexDigitValue (input[0]); + + if (hexValue < 0 || ++numChars > 8) + { + setLastError ("illegal escape sequence", true); + break; + } + + charCode = (charCode << 4) | hexValue; + ++input; + } + + ++input; + } + else if (input[0] >= T('0') && input[0] <= T('9')) + { + int numChars = 0; + + while (input[0] != T(';')) + { + if (++numChars > 12) + { + setLastError ("illegal escape sequence", true); + break; + } + + charCode = charCode * 10 + (input[0] - T('0')); + ++input; + } + + ++input; + } + else + { + setLastError ("illegal escape sequence", true); + result += T("&"); + return; + } + + result << (tchar) charCode; + } + else + { + const tchar* const entityNameStart = input; + const tchar* const closingSemiColon = CharacterFunctions::find (input, T(";")); + + if (closingSemiColon == 0) + { + outOfData = true; + result += T("&"); + } + else + { + input = closingSemiColon + 1; + + result += expandExternalEntity (String (entityNameStart, + (int) (closingSemiColon - entityNameStart))); + } + } +} + +const String XmlDocument::expandEntity (const String& ent) +{ + if (ent.equalsIgnoreCase (T("amp"))) + { + return T("&"); + } + else if (ent.equalsIgnoreCase (T("quot"))) + { + return T("\""); + } + else if (ent.equalsIgnoreCase (T("apos"))) + { + return T("\'"); + } + else if (ent.equalsIgnoreCase (T("lt"))) + { + return T("<"); + } + else if (ent.equalsIgnoreCase (T("gt"))) + { + return T(">"); + } + else if (ent[0] == T('#')) + { + if (ent[1] == T('x') || ent[1] == T('X')) + { + return String::charToString ((tchar) ent.substring (2).getHexValue32()); + } + else if (ent[1] >= T('0') && ent[1] <= T('9')) + { + return String::charToString ((tchar) ent.substring (1).getIntValue()); + } + + setLastError ("illegal escape sequence", false); + return T("&"); + } + else + { + return expandExternalEntity (ent); + } +} + +const String XmlDocument::expandExternalEntity (const String& entity) +{ + if (needToLoadDTD) + { + if (dtdText.isNotEmpty()) + { + while (dtdText.endsWithChar (T('>'))) + dtdText = dtdText.dropLastCharacters (1); + + tokenisedDTD.addTokens (dtdText, true); + + if (tokenisedDTD [tokenisedDTD.size() - 2].equalsIgnoreCase (T("system")) + && tokenisedDTD [tokenisedDTD.size() - 1].isQuotedString()) + { + const String fn (tokenisedDTD [tokenisedDTD.size() - 1]); + + tokenisedDTD.clear(); + tokenisedDTD.addTokens (getFileContents (fn), true); + } + else + { + tokenisedDTD.clear(); + const int openBracket = dtdText.indexOfChar (T('[')); + + if (openBracket > 0) + { + const int closeBracket = dtdText.lastIndexOfChar (T(']')); + + if (closeBracket > openBracket) + tokenisedDTD.addTokens (dtdText.substring (openBracket + 1, + closeBracket), true); + } + } + + for (int i = tokenisedDTD.size(); --i >= 0;) + { + if (tokenisedDTD[i].startsWithChar (T('%')) + && tokenisedDTD[i].endsWithChar (T(';'))) + { + const String parsed (getParameterEntity (tokenisedDTD[i].substring (1, tokenisedDTD[i].length() - 1))); + StringArray newToks; + newToks.addTokens (parsed, true); + + tokenisedDTD.remove (i); + + for (int j = newToks.size(); --j >= 0;) + tokenisedDTD.insert (i, newToks[j]); + } + } + } + + needToLoadDTD = false; + } + + for (int i = 0; i < tokenisedDTD.size(); ++i) + { + if (tokenisedDTD[i] == entity) + { + if (tokenisedDTD[i - 1].equalsIgnoreCase (T("'))) + ent = ent.dropLastCharacters (1); + + ent = ent.trim().unquoted(); + + // check for sub-entities.. + int ampersand = ent.indexOfChar (T('&')); + + while (ampersand >= 0) + { + const int semiColon = ent.indexOf (i + 1, T(";")); + + if (semiColon < 0) + { + setLastError ("entity without terminating semi-colon", false); + break; + } + + const String resolved (expandEntity (ent.substring (i + 1, semiColon))); + + ent = ent.substring (0, ampersand) + + resolved + + ent.substring (semiColon + 1); + + ampersand = ent.indexOfChar (semiColon + 1, T('&')); + } + + return ent; + } + } + } + + setLastError ("unknown entity", true); + + return entity; +} + +const String XmlDocument::getParameterEntity (const String& entity) +{ + for (int i = 0; i < tokenisedDTD.size(); ++i) + { + if (tokenisedDTD[i] == entity) + { + if (tokenisedDTD [i - 1] == T("%") + && tokenisedDTD [i - 2].equalsIgnoreCase (T("'))) + ent = ent.dropLastCharacters (1); + + if (ent.equalsIgnoreCase (T("system"))) + { + String filename (tokenisedDTD [i + 2]); + + while (filename.endsWithChar (T('>'))) + filename = filename.dropLastCharacters (1); + + return getFileContents (filename); + } + else + { + return ent.trim().unquoted(); + } + } + } + } + + return entity; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_XmlDocument.cpp *********/ + +/********* Start of inlined file: juce_XmlElement.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +XmlElement::XmlAttributeNode::XmlAttributeNode (const XmlAttributeNode& other) throw() + : name (other.name), + value (other.value), + next (0) +{ +} + +XmlElement::XmlAttributeNode::XmlAttributeNode (const String& name_, + const String& value_) throw() + : name (name_), + value (value_), + next (0) +{ +} + +XmlElement::XmlElement (const String& tagName_) throw() + : tagName (tagName_), + firstChildElement (0), + nextElement (0), + attributes (0) +{ + // the tag name mustn't be empty, or it'll look like a text element! + jassert (tagName_.trim().isNotEmpty()) +} + +XmlElement::XmlElement (int /*dummy*/) throw() + : firstChildElement (0), + nextElement (0), + attributes (0) +{ +} + +XmlElement::XmlElement (const tchar* const tagName_, + const int nameLen) throw() + : tagName (tagName_, nameLen), + firstChildElement (0), + nextElement (0), + attributes (0) +{ +} + +XmlElement::XmlElement (const XmlElement& other) throw() + : tagName (other.tagName), + firstChildElement (0), + nextElement (0), + attributes (0) +{ + copyChildrenAndAttributesFrom (other); +} + +const XmlElement& XmlElement::operator= (const XmlElement& other) throw() +{ + if (this != &other) + { + removeAllAttributes(); + deleteAllChildElements(); + + tagName = other.tagName; + + copyChildrenAndAttributesFrom (other); + } + + return *this; +} + +void XmlElement::copyChildrenAndAttributesFrom (const XmlElement& other) throw() +{ + XmlElement* child = other.firstChildElement; + XmlElement* lastChild = 0; + + while (child != 0) + { + XmlElement* const copiedChild = new XmlElement (*child); + + if (lastChild != 0) + lastChild->nextElement = copiedChild; + else + firstChildElement = copiedChild; + + lastChild = copiedChild; + child = child->nextElement; + } + + const XmlAttributeNode* att = other.attributes; + XmlAttributeNode* lastAtt = 0; + + while (att != 0) + { + XmlAttributeNode* const newAtt = new XmlAttributeNode (*att); + + if (lastAtt != 0) + lastAtt->next = newAtt; + else + attributes = newAtt; + + lastAtt = newAtt; + att = att->next; + } +} + +XmlElement::~XmlElement() throw() +{ + XmlElement* child = firstChildElement; + + while (child != 0) + { + XmlElement* const nextChild = child->nextElement; + delete child; + child = nextChild; + } + + XmlAttributeNode* att = attributes; + + while (att != 0) + { + XmlAttributeNode* const nextAtt = att->next; + delete att; + att = nextAtt; + } +} + +static bool isLegalXmlChar (const juce_wchar character) +{ + if ((character >= 'a' && character <= 'z') + || (character >= 'A' && character <= 'Z') + || (character >= '0' && character <= '9')) + return true; + + const char* t = " .,;:-()_+=?!'#@[]/\\*%~{}"; + + do + { + if (((juce_wchar) (uint8) *t) == character) + return true; + } + while (*++t != 0); + + return false; +} + +static void escapeIllegalXmlChars (OutputStream& outputStream, + const String& text, + const bool changeNewLines) throw() +{ + const juce_wchar* t = (const juce_wchar*) text; + + for (;;) + { + const juce_wchar character = *t++; + + if (character == 0) + { + break; + } + else if (isLegalXmlChar (character)) + { + outputStream.writeByte ((char) character); + } + else + { + switch (character) + { + case '&': + outputStream.write ("&", 5); + break; + + case '"': + outputStream.write (""", 6); + break; + + case '>': + outputStream.write (">", 4); + break; + + case '<': + outputStream.write ("<", 4); + break; + + case '\n': + if (changeNewLines) + outputStream.write (" ", 5); + else + outputStream.writeByte ((char) character); + + break; + + case '\r': + if (changeNewLines) + outputStream.write (" ", 5); + else + outputStream.writeByte ((char) character); + + break; + + default: + { + String encoded (T("&#")); + encoded << String ((int) (unsigned int) character).trim() + << T(';'); + + outputStream.write ((const char*) encoded, encoded.length()); + } + } + } + } +} + +static void writeSpaces (OutputStream& out, int numSpaces) throw() +{ + if (numSpaces > 0) + { + const char* const blanks = " "; + const int blankSize = (int) sizeof (blanks) - 1; + + while (numSpaces > blankSize) + { + out.write (blanks, blankSize); + numSpaces -= blankSize; + } + + out.write (blanks, numSpaces); + } +} + +void XmlElement::writeElementAsText (OutputStream& outputStream, + const int indentationLevel) const throw() +{ + writeSpaces (outputStream, indentationLevel); + + if (! isTextElement()) + { + outputStream.writeByte ('<'); + const int nameLen = tagName.length(); + outputStream.write ((const char*) tagName, nameLen); + + const int attIndent = indentationLevel + nameLen + 1; + int lineLen = 0; + + const XmlAttributeNode* att = attributes; + while (att != 0) + { + if (lineLen > 60 && indentationLevel >= 0) + { + outputStream.write ("\r\n", 2); + writeSpaces (outputStream, attIndent); + lineLen = 0; + } + + const int attNameLen = att->name.length(); + outputStream.writeByte (' '); + outputStream.write ((const char*) (att->name), attNameLen); + outputStream.write ("=\"", 2); + escapeIllegalXmlChars (outputStream, att->value, true); + outputStream.writeByte ('"'); + lineLen += 4 + attNameLen + att->value.length(); + + att = att->next; + } + + if (firstChildElement != 0) + { + XmlElement* child = firstChildElement; + + if (child->nextElement == 0 && child->isTextElement()) + { + outputStream.writeByte ('>'); + escapeIllegalXmlChars (outputStream, child->getText(), false); + } + else + { + if (indentationLevel >= 0) + outputStream.write (">\r\n", 3); + else + outputStream.writeByte ('>'); + + bool lastWasTextNode = false; + + while (child != 0) + { + if (child->isTextElement()) + { + if ((! lastWasTextNode) && (indentationLevel >= 0)) + writeSpaces (outputStream, indentationLevel + 2); + + escapeIllegalXmlChars (outputStream, child->getText(), false); + lastWasTextNode = true; + } + else + { + if (indentationLevel >= 0) + { + if (lastWasTextNode) + outputStream.write ("\r\n", 2); + + child->writeElementAsText (outputStream, indentationLevel + 2); + } + else + { + child->writeElementAsText (outputStream, indentationLevel); + } + + lastWasTextNode = false; + } + + child = child->nextElement; + } + + if (indentationLevel >= 0) + { + if (lastWasTextNode) + outputStream.write ("\r\n", 2); + + writeSpaces (outputStream, indentationLevel); + } + } + + outputStream.write ("= 0) + outputStream.write (">\r\n", 3); + else + outputStream.writeByte ('>'); + } + else + { + if (indentationLevel >= 0) + outputStream.write ("/>\r\n", 4); + else + outputStream.write ("/>", 2); + } + } + else + { + if (indentationLevel >= 0) + writeSpaces (outputStream, indentationLevel + 2); + + escapeIllegalXmlChars (outputStream, getText(), false); + } +} + +const String XmlElement::createDocument (const String& dtd, + const bool allOnOneLine, + const bool includeXmlHeader, + const tchar* const encoding) const throw() +{ + String doc; + doc.preallocateStorage (1024); + + if (includeXmlHeader) + { + doc << " "; + else + doc += "\"?>\n\n"; + } + + if (dtd.isNotEmpty()) + { + if (allOnOneLine) + doc << dtd << " "; + else + doc << dtd << "\r\n"; + } + + MemoryOutputStream mem (2048, 4096); + writeElementAsText (mem, allOnOneLine ? -1 : 0); + + return doc + String (mem.getData(), + mem.getDataSize()); +} + +bool XmlElement::writeToFile (const File& f, + const String& dtd, + const tchar* const encoding) const throw() +{ + if (f.hasWriteAccess()) + { + const File tempFile (f.getNonexistentSibling()); + + FileOutputStream* const out = tempFile.createOutputStream(); + + if (out != 0) + { + *out << "\r\n\r\n" + << dtd << "\r\n"; + + writeElementAsText (*out, 0); + + delete out; + + if (tempFile.moveFileTo (f)) + return true; + + tempFile.deleteFile(); + } + } + + return false; +} + +bool XmlElement::hasTagName (const tchar* const tagNameWanted) const throw() +{ +#ifdef JUCE_DEBUG + // if debugging, check that the case is actually the same, because + // valid xml is case-sensitive, and although this lets it pass, it's + // better not to.. + if (tagName.equalsIgnoreCase (tagNameWanted)) + { + jassert (tagName == tagNameWanted); + return true; + } + else + { + return false; + } +#else + return tagName.equalsIgnoreCase (tagNameWanted); +#endif +} + +XmlElement* XmlElement::getNextElementWithTagName (const tchar* const requiredTagName) const +{ + XmlElement* e = nextElement; + + while (e != 0 && ! e->hasTagName (requiredTagName)) + e = e->nextElement; + + return e; +} + +int XmlElement::getNumAttributes() const throw() +{ + const XmlAttributeNode* att = attributes; + int count = 0; + + while (att != 0) + { + att = att->next; + ++count; + } + + return count; +} + +const String& XmlElement::getAttributeName (const int index) const throw() +{ + const XmlAttributeNode* att = attributes; + int count = 0; + + while (att != 0) + { + if (count == index) + return att->name; + + att = att->next; + ++count; + } + + return String::empty; +} + +const String& XmlElement::getAttributeValue (const int index) const throw() +{ + const XmlAttributeNode* att = attributes; + int count = 0; + + while (att != 0) + { + if (count == index) + return att->value; + + att = att->next; + ++count; + } + + return String::empty; +} + +bool XmlElement::hasAttribute (const tchar* const attributeName) const throw() +{ + const XmlAttributeNode* att = attributes; + + while (att != 0) + { + if (att->name.equalsIgnoreCase (attributeName)) + return true; + + att = att->next; + } + + return false; +} + +const String XmlElement::getStringAttribute (const tchar* const attributeName, + const tchar* const defaultReturnValue) const throw() +{ + const XmlAttributeNode* att = attributes; + + while (att != 0) + { + if (att->name.equalsIgnoreCase (attributeName)) + return att->value; + + att = att->next; + } + + return defaultReturnValue; +} + +int XmlElement::getIntAttribute (const tchar* const attributeName, + const int defaultReturnValue) const throw() +{ + const XmlAttributeNode* att = attributes; + + while (att != 0) + { + if (att->name.equalsIgnoreCase (attributeName)) + return att->value.getIntValue(); + + att = att->next; + } + + return defaultReturnValue; +} + +double XmlElement::getDoubleAttribute (const tchar* const attributeName, + const double defaultReturnValue) const throw() +{ + const XmlAttributeNode* att = attributes; + + while (att != 0) + { + if (att->name.equalsIgnoreCase (attributeName)) + return att->value.getDoubleValue(); + + att = att->next; + } + + return defaultReturnValue; +} + +bool XmlElement::getBoolAttribute (const tchar* const attributeName, + const bool defaultReturnValue) const throw() +{ + const XmlAttributeNode* att = attributes; + + while (att != 0) + { + if (att->name.equalsIgnoreCase (attributeName)) + { + tchar firstChar = att->value[0]; + + if (CharacterFunctions::isWhitespace (firstChar)) + firstChar = att->value.trimStart() [0]; + + return firstChar == T('1') + || firstChar == T('t') + || firstChar == T('y') + || firstChar == T('T') + || firstChar == T('Y'); + } + + att = att->next; + } + + return defaultReturnValue; +} + +bool XmlElement::compareAttribute (const tchar* const attributeName, + const tchar* const stringToCompareAgainst, + const bool ignoreCase) const throw() +{ + const XmlAttributeNode* att = attributes; + + while (att != 0) + { + if (att->name.equalsIgnoreCase (attributeName)) + { + if (ignoreCase) + return att->value.equalsIgnoreCase (stringToCompareAgainst); + else + return att->value == stringToCompareAgainst; + } + + att = att->next; + } + + return false; +} + +void XmlElement::setAttribute (const tchar* const attributeName, + const String& value) throw() +{ +#ifdef JUCE_DEBUG + // check the identifier being passed in is legal.. + const tchar* t = attributeName; + while (*t != 0) + { + jassert (CharacterFunctions::isLetterOrDigit (*t) + || *t == T('_') + || *t == T('-') + || *t == T(':')); + ++t; + } +#endif + + if (attributes == 0) + { + attributes = new XmlAttributeNode (attributeName, value); + } + else + { + XmlAttributeNode* att = attributes; + + for (;;) + { + if (att->name.equalsIgnoreCase (attributeName)) + { + att->value = value; + break; + } + else if (att->next == 0) + { + att->next = new XmlAttributeNode (attributeName, value); + break; + } + + att = att->next; + } + } +} + +void XmlElement::setAttribute (const tchar* const attributeName, + const tchar* const text) throw() +{ + setAttribute (attributeName, String (text)); +} + +void XmlElement::setAttribute (const tchar* const attributeName, + const int number) throw() +{ + setAttribute (attributeName, String (number)); +} + +void XmlElement::setAttribute (const tchar* const attributeName, + const double number) throw() +{ + tchar buffer [40]; + CharacterFunctions::printf (buffer, numElementsInArray (buffer), T("%.9g"), number); + + setAttribute (attributeName, buffer); +} + +void XmlElement::removeAttribute (const tchar* const attributeName) throw() +{ + XmlAttributeNode* att = attributes; + XmlAttributeNode* lastAtt = 0; + + while (att != 0) + { + if (att->name.equalsIgnoreCase (attributeName)) + { + if (lastAtt == 0) + attributes = att->next; + else + lastAtt->next = att->next; + + delete att; + break; + } + + lastAtt = att; + att = att->next; + } +} + +void XmlElement::removeAllAttributes() throw() +{ + while (attributes != 0) + { + XmlAttributeNode* const nextAtt = attributes->next; + delete attributes; + attributes = nextAtt; + } +} + +int XmlElement::getNumChildElements() const throw() +{ + int count = 0; + const XmlElement* child = firstChildElement; + + while (child != 0) + { + ++count; + child = child->nextElement; + } + + return count; +} + +XmlElement* XmlElement::getChildElement (const int index) const throw() +{ + int count = 0; + XmlElement* child = firstChildElement; + + while (child != 0 && count < index) + { + child = child->nextElement; + ++count; + } + + return child; +} + +XmlElement* XmlElement::getChildByName (const tchar* const childName) const throw() +{ + XmlElement* child = firstChildElement; + + while (child != 0) + { + if (child->hasTagName (childName)) + break; + + child = child->nextElement; + } + + return child; +} + +void XmlElement::addChildElement (XmlElement* const newNode) throw() +{ + if (newNode != 0) + { + if (firstChildElement == 0) + { + firstChildElement = newNode; + } + else + { + XmlElement* child = firstChildElement; + + while (child->nextElement != 0) + child = child->nextElement; + + child->nextElement = newNode; + + // if this is non-zero, then something's probably + // gone wrong.. + jassert (newNode->nextElement == 0); + } + } +} + +void XmlElement::insertChildElement (XmlElement* const newNode, + int indexToInsertAt) throw() +{ + if (newNode != 0) + { + removeChildElement (newNode, false); + + if (indexToInsertAt == 0) + { + newNode->nextElement = firstChildElement; + firstChildElement = newNode; + } + else + { + if (firstChildElement == 0) + { + firstChildElement = newNode; + } + else + { + if (indexToInsertAt < 0) + indexToInsertAt = INT_MAX; + + XmlElement* child = firstChildElement; + + while (child->nextElement != 0 && --indexToInsertAt > 0) + child = child->nextElement; + + newNode->nextElement = child->nextElement; + child->nextElement = newNode; + } + } + } +} + +bool XmlElement::replaceChildElement (XmlElement* const currentChildElement, + XmlElement* const newNode) throw() +{ + if (newNode != 0) + { + XmlElement* child = firstChildElement; + XmlElement* previousNode = 0; + + while (child != 0) + { + if (child == currentChildElement) + { + if (child != newNode) + { + if (previousNode == 0) + firstChildElement = newNode; + else + previousNode->nextElement = newNode; + + newNode->nextElement = child->nextElement; + + delete child; + } + + return true; + } + + previousNode = child; + child = child->nextElement; + } + } + + return false; +} + +void XmlElement::removeChildElement (XmlElement* const childToRemove, + const bool shouldDeleteTheChild) throw() +{ + if (childToRemove != 0) + { + if (firstChildElement == childToRemove) + { + firstChildElement = childToRemove->nextElement; + childToRemove->nextElement = 0; + } + else + { + XmlElement* child = firstChildElement; + XmlElement* last = 0; + + while (child != 0) + { + if (child == childToRemove) + { + if (last == 0) + firstChildElement = child->nextElement; + else + last->nextElement = child->nextElement; + + childToRemove->nextElement = 0; + break; + } + + last = child; + child = child->nextElement; + } + } + + if (shouldDeleteTheChild) + delete childToRemove; + } +} + +bool XmlElement::isEquivalentTo (const XmlElement* const other, + const bool ignoreOrderOfAttributes) const throw() +{ + if (this != other) + { + if (other == 0 || tagName != other->tagName) + { + return false; + } + + if (ignoreOrderOfAttributes) + { + int totalAtts = 0; + const XmlAttributeNode* att = attributes; + + while (att != 0) + { + if (! other->compareAttribute (att->name, att->value)) + return false; + + att = att->next; + ++totalAtts; + } + + if (totalAtts != other->getNumAttributes()) + return false; + } + else + { + const XmlAttributeNode* thisAtt = attributes; + const XmlAttributeNode* otherAtt = other->attributes; + + for (;;) + { + if (thisAtt == 0 || otherAtt == 0) + { + if (thisAtt == otherAtt) // both 0, so it's a match + break; + + return false; + } + + if (thisAtt->name != otherAtt->name + || thisAtt->value != otherAtt->value) + { + return false; + } + + thisAtt = thisAtt->next; + otherAtt = otherAtt->next; + } + } + + const XmlElement* thisChild = firstChildElement; + const XmlElement* otherChild = other->firstChildElement; + + for (;;) + { + if (thisChild == 0 || otherChild == 0) + { + if (thisChild == otherChild) // both 0, so it's a match + break; + + return false; + } + + if (! thisChild->isEquivalentTo (otherChild, ignoreOrderOfAttributes)) + return false; + + thisChild = thisChild->nextElement; + otherChild = otherChild->nextElement; + } + } + + return true; +} + +void XmlElement::deleteAllChildElements() throw() +{ + while (firstChildElement != 0) + { + XmlElement* const nextChild = firstChildElement->nextElement; + delete firstChildElement; + firstChildElement = nextChild; + } +} + +void XmlElement::deleteAllChildElementsWithTagName (const tchar* const name) throw() +{ + XmlElement* child = firstChildElement; + + while (child != 0) + { + if (child->hasTagName (name)) + { + XmlElement* const nextChild = child->nextElement; + removeChildElement (child, true); + child = nextChild; + } + else + { + child = child->nextElement; + } + } +} + +bool XmlElement::containsChildElement (const XmlElement* const possibleChild) const throw() +{ + const XmlElement* child = firstChildElement; + + while (child != 0) + { + if (child == possibleChild) + return true; + + child = child->nextElement; + } + + return false; +} + +XmlElement* XmlElement::findParentElementOf (const XmlElement* const elementToLookFor) throw() +{ + if (this == elementToLookFor || elementToLookFor == 0) + return 0; + + XmlElement* child = firstChildElement; + + while (child != 0) + { + if (elementToLookFor == child) + return this; + + XmlElement* const found = child->findParentElementOf (elementToLookFor); + + if (found != 0) + return found; + + child = child->nextElement; + } + + return 0; +} + +XmlElement** XmlElement::getChildElementsAsArray (const int num) const throw() +{ + XmlElement** const elems = new XmlElement* [num]; + + XmlElement* e = firstChildElement; + int i = 0; + + while (e != 0) + { + elems [i++] = e; + e = e->nextElement; + } + + return elems; +} + +void XmlElement::reorderChildElements (XmlElement** const elems, const int num) throw() +{ + XmlElement* e = firstChildElement = elems[0]; + + for (int i = 1; i < num; ++i) + { + e->nextElement = elems[i]; + e = e->nextElement; + } + + e->nextElement = 0; +} + +bool XmlElement::isTextElement() const throw() +{ + return tagName.isEmpty(); +} + +static const tchar* const juce_xmltextContentAttributeName = T("text"); + +const String XmlElement::getText() const throw() +{ + jassert (isTextElement()); // you're trying to get the text from an element that + // isn't actually a text element.. If this contains text sub-nodes, you + // can use getAllSubText instead to + + return getStringAttribute (juce_xmltextContentAttributeName); +} + +void XmlElement::setText (const String& newText) throw() +{ + if (isTextElement()) + { + setAttribute (juce_xmltextContentAttributeName, newText); + } + else + { + jassertfalse // you can only change the text in a text element, not a normal one. + } +} + +const String XmlElement::getAllSubText() const throw() +{ + String result; + const XmlElement* child = firstChildElement; + + while (child != 0) + { + if (child->isTextElement()) + result += child->getText(); + + child = child->nextElement; + } + + return result; +} + +const String XmlElement::getChildElementAllSubText (const tchar* const childTagName, + const String& defaultReturnValue) const throw() +{ + const XmlElement* const child = getChildByName (childTagName); + + if (child != 0) + return child->getAllSubText(); + + return defaultReturnValue; +} + +XmlElement* XmlElement::createTextElement (const String& text) throw() +{ + XmlElement* const e = new XmlElement ((int) 0); + e->setAttribute (juce_xmltextContentAttributeName, text); + return e; +} + +void XmlElement::addTextElement (const String& text) throw() +{ + addChildElement (createTextElement (text)); +} + +void XmlElement::deleteAllTextElements() throw() +{ + XmlElement* child = firstChildElement; + + while (child != 0) + { + XmlElement* const next = child->nextElement; + + if (child->isTextElement()) + removeChildElement (child, true); + + child = next; + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_XmlElement.cpp *********/ + +/********* Start of inlined file: juce_InterProcessLock.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +// (implemented in the platform-specific code files) + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_InterProcessLock.cpp *********/ + +/********* Start of inlined file: juce_ReadWriteLock.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +ReadWriteLock::ReadWriteLock() throw() + : numWaitingWriters (0), + numWriters (0), + writerThreadId (0) +{ +} + +ReadWriteLock::~ReadWriteLock() throw() +{ + jassert (readerThreads.size() == 0); + jassert (numWriters == 0); +} + +void ReadWriteLock::enterRead() const throw() +{ + const int threadId = Thread::getCurrentThreadId(); + const ScopedLock sl (accessLock); + + for (;;) + { + jassert (readerThreads.size() % 2 == 0); + + int i; + for (i = 0; i < readerThreads.size(); i += 2) + if (readerThreads.getUnchecked(i) == threadId) + break; + + if (i < readerThreads.size() + || numWriters + numWaitingWriters == 0 + || (threadId == writerThreadId && numWriters > 0)) + { + if (i < readerThreads.size()) + { + readerThreads.set (i + 1, readerThreads.getUnchecked (i + 1) + 1); + } + else + { + readerThreads.add (threadId); + readerThreads.add (1); + } + + return; + } + + const ScopedUnlock ul (accessLock); + waitEvent.wait (100); + } +} + +void ReadWriteLock::exitRead() const throw() +{ + const int threadId = Thread::getCurrentThreadId(); + const ScopedLock sl (accessLock); + + for (int i = 0; i < readerThreads.size(); i += 2) + { + if (readerThreads.getUnchecked(i) == threadId) + { + const int newCount = readerThreads.getUnchecked (i + 1) - 1; + + if (newCount == 0) + { + readerThreads.removeRange (i, 2); + waitEvent.signal(); + } + else + { + readerThreads.set (i + 1, newCount); + } + + return; + } + } + + jassertfalse // unlocking a lock that wasn't locked.. +} + +void ReadWriteLock::enterWrite() const throw() +{ + const int threadId = Thread::getCurrentThreadId(); + const ScopedLock sl (accessLock); + + for (;;) + { + if (readerThreads.size() + numWriters == 0 + || threadId == writerThreadId + || (readerThreads.size() == 2 + && readerThreads.getUnchecked(0) == threadId)) + { + writerThreadId = threadId; + ++numWriters; + break; + } + + ++numWaitingWriters; + accessLock.exit(); + waitEvent.wait (100); + accessLock.enter(); + --numWaitingWriters; + } +} + +void ReadWriteLock::exitWrite() const throw() +{ + const ScopedLock sl (accessLock); + + // check this thread actually had the lock.. + jassert (numWriters > 0 && writerThreadId == Thread::getCurrentThreadId()); + + if (--numWriters == 0) + { + writerThreadId = 0; + waitEvent.signal(); + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ReadWriteLock.cpp *********/ + +/********* Start of inlined file: juce_Thread.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +// these functions are implemented in the platform-specific code. +void* juce_createThread (void* userData) throw(); +void juce_killThread (void* handle) throw(); +void juce_setThreadPriority (void* handle, int priority) throw(); +void juce_setCurrentThreadName (const String& name) throw(); +#if JUCE_WIN32 +void juce_CloseThreadHandle (void* handle) throw(); +#endif + +static VoidArray runningThreads (4); +static CriticalSection runningThreadsLock; + +void Thread::threadEntryPoint (Thread* const thread) throw() +{ + runningThreadsLock.enter(); + runningThreads.add (thread); + runningThreadsLock.exit(); + + JUCE_TRY + { + thread->threadId_ = Thread::getCurrentThreadId(); + + if (thread->threadName_.isNotEmpty()) + juce_setCurrentThreadName (thread->threadName_); + + if (thread->startSuspensionEvent_.wait (10000)) + { + if (thread->affinityMask_ != 0) + setCurrentThreadAffinityMask (thread->affinityMask_); + + thread->run(); + } + } + JUCE_CATCH_ALL_ASSERT + + runningThreadsLock.enter(); + jassert (runningThreads.contains (thread)); + runningThreads.removeValue (thread); + runningThreadsLock.exit(); + +#if JUCE_WIN32 + juce_CloseThreadHandle (thread->threadHandle_); +#endif + + thread->threadHandle_ = 0; + thread->threadId_ = 0; +} + +// used to wrap the incoming call from the platform-specific code +void JUCE_API juce_threadEntryPoint (void* userData) +{ + Thread::threadEntryPoint ((Thread*) userData); +} + +Thread::Thread (const String& threadName) + : threadName_ (threadName), + threadHandle_ (0), + threadPriority_ (5), + threadId_ (0), + affinityMask_ (0), + threadShouldExit_ (false) +{ +} + +Thread::~Thread() +{ + stopThread (100); +} + +void Thread::startThread() throw() +{ + const ScopedLock sl (startStopLock); + + threadShouldExit_ = false; + + if (threadHandle_ == 0) + { + threadHandle_ = juce_createThread ((void*) this); + juce_setThreadPriority (threadHandle_, threadPriority_); + startSuspensionEvent_.signal(); + } +} + +void Thread::startThread (const int priority) throw() +{ + const ScopedLock sl (startStopLock); + + if (threadHandle_ == 0) + { + threadPriority_ = priority; + startThread(); + } + else + { + setPriority (priority); + } +} + +bool Thread::isThreadRunning() const throw() +{ + return threadHandle_ != 0; +} + +void Thread::signalThreadShouldExit() throw() +{ + threadShouldExit_ = true; +} + +bool Thread::waitForThreadToExit (const int timeOutMilliseconds) const throw() +{ + // Doh! So how exactly do you expect this thread to wait for itself to stop?? + jassert (getThreadId() != getCurrentThreadId()); + + const int sleepMsPerIteration = 5; + int count = timeOutMilliseconds / sleepMsPerIteration; + + while (isThreadRunning()) + { + if (timeOutMilliseconds > 0 && --count < 0) + return false; + + sleep (sleepMsPerIteration); + } + + return true; +} + +void Thread::stopThread (const int timeOutMilliseconds) throw() +{ + // agh! You can't stop the thread that's calling this method! How on earth + // would that work?? + jassert (getCurrentThreadId() != getThreadId()); + + const ScopedLock sl (startStopLock); + + if (isThreadRunning()) + { + signalThreadShouldExit(); + notify(); + + if (timeOutMilliseconds != 0) + waitForThreadToExit (timeOutMilliseconds); + + if (isThreadRunning()) + { + // very bad karma if this point is reached, as + // there are bound to be locks and events left in + // silly states when a thread is killed by force.. + jassertfalse + Logger::writeToLog ("!! killing thread by force !!"); + + juce_killThread (threadHandle_); + threadHandle_ = 0; + threadId_ = 0; + + const ScopedLock sl (runningThreadsLock); + runningThreads.removeValue (this); + } + } +} + +void Thread::setPriority (const int priority) throw() +{ + const ScopedLock sl (startStopLock); + + threadPriority_ = priority; + juce_setThreadPriority (threadHandle_, priority); +} + +void Thread::setCurrentThreadPriority (const int priority) throw() +{ + juce_setThreadPriority (0, priority); +} + +void Thread::setAffinityMask (const uint32 affinityMask) throw() +{ + affinityMask_ = affinityMask; +} + +int Thread::getThreadId() const throw() +{ + return threadId_; +} + +bool Thread::wait (const int timeOutMilliseconds) const throw() +{ + return defaultEvent_.wait (timeOutMilliseconds); +} + +void Thread::notify() const throw() +{ + defaultEvent_.signal(); +} + +int Thread::getNumRunningThreads() throw() +{ + return runningThreads.size(); +} + +Thread* Thread::getCurrentThread() throw() +{ + const int thisId = getCurrentThreadId(); + Thread* result = 0; + + runningThreadsLock.enter(); + + for (int i = runningThreads.size(); --i >= 0;) + { + Thread* const t = (Thread*) (runningThreads.getUnchecked(i)); + + if (t->threadId_ == thisId) + { + result = t; + break; + } + } + + runningThreadsLock.exit(); + + return result; +} + +void Thread::stopAllThreads (const int timeOutMilliseconds) throw() +{ + runningThreadsLock.enter(); + + for (int i = runningThreads.size(); --i >= 0;) + ((Thread*) runningThreads.getUnchecked(i))->signalThreadShouldExit(); + + runningThreadsLock.exit(); + + for (;;) + { + runningThreadsLock.enter(); + Thread* const t = (Thread*) runningThreads[0]; + runningThreadsLock.exit(); + + if (t == 0) + break; + + t->stopThread (timeOutMilliseconds); + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_Thread.cpp *********/ + +/********* Start of inlined file: juce_ThreadPool.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +ThreadPoolJob::ThreadPoolJob (const String& name) + : jobName (name), + pool (0), + shouldStop (false), + isActive (false), + shouldBeDeleted (false) +{ +} + +ThreadPoolJob::~ThreadPoolJob() +{ + // you mustn't delete a job while it's still in a pool! Use ThreadPool::removeJob() + // to remove it first! + jassert (pool == 0 || ! pool->contains (this)); +} + +const String ThreadPoolJob::getJobName() const +{ + return jobName; +} + +void ThreadPoolJob::setJobName (const String& newName) +{ + jobName = newName; +} + +void ThreadPoolJob::signalJobShouldExit() +{ + shouldStop = true; +} + +class ThreadPoolThread : public Thread +{ + ThreadPool& pool; + bool volatile busy; + + ThreadPoolThread (const ThreadPoolThread&); + const ThreadPoolThread& operator= (const ThreadPoolThread&); + +public: + ThreadPoolThread (ThreadPool& pool_) + : Thread (T("Pool")), + pool (pool_), + busy (false) + { + } + + ~ThreadPoolThread() + { + } + + void run() + { + while (! threadShouldExit()) + { + if (! pool.runNextJob()) + wait (500); + } + } +}; + +ThreadPool::ThreadPool (const int numThreads_, + const bool startThreadsOnlyWhenNeeded, + const int stopThreadsWhenNotUsedTimeoutMs) + : numThreads (jmax (1, numThreads_)), + threadStopTimeout (stopThreadsWhenNotUsedTimeoutMs), + priority (5) +{ + jassert (numThreads_ > 0); // not much point having one of these with no threads in it. + + threads = (Thread**) juce_calloc (sizeof (Thread*) * numThreads); + + for (int i = numThreads; --i >= 0;) + { + threads[i] = new ThreadPoolThread (*this); + + if (! startThreadsOnlyWhenNeeded) + threads[i]->startThread(); + } +} + +ThreadPool::~ThreadPool() +{ + removeAllJobs (true, 4000); + + int i; + for (i = numThreads; --i >= 0;) + threads[i]->signalThreadShouldExit(); + + for (i = numThreads; --i >= 0;) + { + threads[i]->stopThread (500); + delete threads[i]; + } + + juce_free (threads); +} + +void ThreadPool::addJob (ThreadPoolJob* const job) +{ + jassert (job->pool == 0); + + if (job->pool == 0) + { + job->pool = this; + job->shouldStop = false; + job->isActive = false; + + lock.enter(); + jobs.add (job); + + int numRunning = 0; + + int i; + for (i = numThreads; --i >= 0;) + if (threads[i]->isThreadRunning() && ! threads[i]->threadShouldExit()) + ++numRunning; + + if (numRunning < numThreads) + { + bool startedOne = false; + int n = 1000; + + while (--n >= 0 && ! startedOne) + { + for (int i = numThreads; --i >= 0;) + { + if (! threads[i]->isThreadRunning()) + { + threads[i]->startThread(); + startedOne = true; + } + } + + if (! startedOne) + Thread::sleep (5); + } + } + + lock.exit(); + + for (i = numThreads; --i >= 0;) + threads[i]->notify(); + } +} + +int ThreadPool::getNumJobs() const throw() +{ + return jobs.size(); +} + +ThreadPoolJob* ThreadPool::getJob (const int index) const +{ + const ScopedLock sl (lock); + return (ThreadPoolJob*) jobs [index]; +} + +bool ThreadPool::contains (const ThreadPoolJob* const job) const throw() +{ + const ScopedLock sl (lock); + + return jobs.contains ((void*) job); +} + +bool ThreadPool::isJobRunning (const ThreadPoolJob* const job) const +{ + const ScopedLock sl (lock); + + return jobs.contains ((void*) job) && job->isActive; +} + +bool ThreadPool::waitForJobToFinish (const ThreadPoolJob* const job, + const int timeOutMs) const +{ + if (job != 0) + { + const uint32 start = Time::getMillisecondCounter(); + + while (contains (job)) + { + if (timeOutMs >= 0 && Time::getMillisecondCounter() >= start + timeOutMs) + return false; + + Thread::sleep (2); + } + } + + return true; +} + +bool ThreadPool::removeJob (ThreadPoolJob* const job, + const bool interruptIfRunning, + const int timeOutMs) +{ + if (job != 0) + { + lock.enter(); + + if (jobs.contains (job)) + { + if (job->isActive) + { + if (interruptIfRunning) + job->signalJobShouldExit(); + + lock.exit(); + + return waitForJobToFinish (job, timeOutMs); + } + else + { + jobs.removeValue (job); + } + } + + lock.exit(); + } + + return true; +} + +bool ThreadPool::removeAllJobs (const bool interruptRunningJobs, + const int timeOutMs) +{ + lock.enter(); + + for (int i = jobs.size(); --i >= 0;) + { + ThreadPoolJob* const job = (ThreadPoolJob*) jobs.getUnchecked(i); + + if (job->isActive) + { + if (interruptRunningJobs) + job->signalJobShouldExit(); + } + else + { + jobs.remove (i); + } + } + + lock.exit(); + + const uint32 start = Time::getMillisecondCounter(); + + while (jobs.size() > 0) + { + if (timeOutMs >= 0 && Time::getMillisecondCounter() >= start + timeOutMs) + return false; + + Thread::sleep (2); + } + + return true; +} + +const StringArray ThreadPool::getNamesOfAllJobs (const bool onlyReturnActiveJobs) const +{ + StringArray s; + const ScopedLock sl (lock); + + for (int i = 0; i < jobs.size(); ++i) + { + const ThreadPoolJob* const job = (const ThreadPoolJob*) jobs.getUnchecked(i); + if (job->isActive || ! onlyReturnActiveJobs) + s.add (job->getJobName()); + } + + return s; +} + +void ThreadPool::setThreadPriorities (const int newPriority) +{ + if (priority != newPriority) + { + priority = newPriority; + + for (int i = numThreads; --i >= 0;) + threads[i]->setPriority (newPriority); + } +} + +bool ThreadPool::runNextJob() +{ + lock.enter(); + ThreadPoolJob* job = 0; + + for (int i = 0; i < jobs.size(); ++i) + { + job = (ThreadPoolJob*) jobs [i]; + + if (job != 0 && ! (job->isActive || job->shouldStop)) + break; + + job = 0; + } + + if (job != 0) + { + job->isActive = true; + lock.exit(); + + JUCE_TRY + { + ThreadPoolJob::JobStatus result = job->runJob(); + + lastJobEndTime = Time::getApproximateMillisecondCounter(); + + const ScopedLock sl (lock); + + if (jobs.contains (job)) + { + job->isActive = false; + + if (result != ThreadPoolJob::jobNeedsRunningAgain || job->shouldStop) + { + job->pool = 0; + job->shouldStop = true; + jobs.removeValue (job); + + if (result == ThreadPoolJob::jobHasFinishedAndShouldBeDeleted) + delete job; + } + else + { + // move the job to the end of the queue if it wants another go + jobs.move (jobs.indexOf (job), -1); + } + } + } +#if JUCE_CATCH_UNHANDLED_EXCEPTIONS + catch (...) + { + lock.enter(); + jobs.removeValue (job); + lock.exit(); + } +#endif + } + else + { + lock.exit(); + + if (threadStopTimeout > 0 + && Time::getApproximateMillisecondCounter() > lastJobEndTime + threadStopTimeout) + { + lock.enter(); + + if (jobs.size() == 0) + { + for (int i = numThreads; --i >= 0;) + threads[i]->signalThreadShouldExit(); + } + + lock.exit(); + } + else + { + return false; + } + } + + return true; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ThreadPool.cpp *********/ + +/********* Start of inlined file: juce_TimeSliceThread.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +TimeSliceThread::TimeSliceThread (const String& threadName) + : Thread (threadName), + index (0), + clientBeingCalled (0), + clientsChanged (false) +{ +} + +TimeSliceThread::~TimeSliceThread() +{ + stopThread (2000); +} + +void TimeSliceThread::addTimeSliceClient (TimeSliceClient* const client) +{ + const ScopedLock sl (listLock); + clients.addIfNotAlreadyThere (client); + clientsChanged = true; + notify(); +} + +void TimeSliceThread::removeTimeSliceClient (TimeSliceClient* const client) +{ + const ScopedLock sl1 (listLock); + clientsChanged = true; + + // if there's a chance we're in the middle of calling this client, we need to + // also lock the outer lock.. + if (clientBeingCalled == client) + { + const ScopedUnlock ul (listLock); // unlock first to get the order right.. + + const ScopedLock sl1 (callbackLock); + const ScopedLock sl2 (listLock); + + clients.removeValue (client); + } + else + { + clients.removeValue (client); + } +} + +int TimeSliceThread::getNumClients() const throw() +{ + return clients.size(); +} + +TimeSliceClient* TimeSliceThread::getClient (const int index) const throw() +{ + const ScopedLock sl (listLock); + return clients [index]; +} + +void TimeSliceThread::run() +{ + int numCallsSinceBusy = 0; + + while (! threadShouldExit()) + { + int timeToWait = 500; + + { + const ScopedLock sl (callbackLock); + + { + const ScopedLock sl (listLock); + + if (clients.size() > 0) + { + index = (index + 1) % clients.size(); + + clientBeingCalled = clients [index]; + } + else + { + index = 0; + clientBeingCalled = 0; + } + + if (clientsChanged) + { + clientsChanged = false; + numCallsSinceBusy = 0; + } + } + + if (clientBeingCalled != 0) + { + if (clientBeingCalled->useTimeSlice()) + numCallsSinceBusy = 0; + else + ++numCallsSinceBusy; + + if (numCallsSinceBusy >= clients.size()) + timeToWait = 500; + else if (index == 0) + timeToWait = 1; // throw in an occasional pause, to stop everything locking up + else + timeToWait = 0; + } + } + + if (timeToWait > 0) + wait (timeToWait); + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_TimeSliceThread.cpp *********/ + +#if ! JUCE_ONLY_BUILD_CORE_LIBRARY + +/********* Start of inlined file: juce_Application.cpp *********/ + +#if JUCE_MSVC + #pragma warning (push) + #pragma warning (disable: 4245 4514 4100) + #include + #pragma warning (pop) +#endif + +BEGIN_JUCE_NAMESPACE + +void juce_setCurrentExecutableFileName (const String& filename) throw(); +void juce_setCurrentThreadName (const String& name) throw(); + +static JUCEApplication* appInstance = 0; + +JUCEApplication::JUCEApplication() + : appReturnValue (0), + stillInitialising (true) +{ +} + +JUCEApplication::~JUCEApplication() +{ +} + +JUCEApplication* JUCEApplication::getInstance() throw() +{ + return appInstance; +} + +bool JUCEApplication::isInitialising() const throw() +{ + return stillInitialising; +} + +const String JUCEApplication::getApplicationVersion() +{ + return String::empty; +} + +bool JUCEApplication::moreThanOneInstanceAllowed() +{ + return true; +} + +void JUCEApplication::anotherInstanceStarted (const String&) +{ +} + +void JUCEApplication::systemRequestedQuit() +{ + quit(); +} + +void JUCEApplication::quit (const bool useMaximumForce) +{ + MessageManager::getInstance()->postQuitMessage (useMaximumForce); +} + +void JUCEApplication::setApplicationReturnValue (const int newReturnValue) throw() +{ + appReturnValue = newReturnValue; +} + +void JUCEApplication::unhandledException (const std::exception*, + const String&, + const int) +{ + jassertfalse +} + +void JUCEApplication::sendUnhandledException (const std::exception* const e, + const char* const sourceFile, + const int lineNumber) +{ + if (appInstance != 0) + appInstance->unhandledException (e, sourceFile, lineNumber); +} + +ApplicationCommandTarget* JUCEApplication::getNextCommandTarget() +{ + return 0; +} + +void JUCEApplication::getAllCommands (Array & commands) +{ + commands.add (StandardApplicationCommandIDs::quit); +} + +void JUCEApplication::getCommandInfo (const CommandID commandID, ApplicationCommandInfo& result) +{ + if (commandID == StandardApplicationCommandIDs::quit) + { + result.setInfo ("Quit", + "Quits the application", + "Application", + 0); + + result.defaultKeypresses.add (KeyPress (T('q'), ModifierKeys::commandModifier, 0)); + } +} + +bool JUCEApplication::perform (const InvocationInfo& info) +{ + if (info.commandID == StandardApplicationCommandIDs::quit) + { + systemRequestedQuit(); + return true; + } + + return false; +} + +int JUCEApplication::main (String& commandLine, JUCEApplication* const app) +{ + jassert (appInstance == 0); + appInstance = app; + bool useForce = true; + + initialiseJuce_GUI(); + + InterProcessLock* appLock = 0; + + if (! app->moreThanOneInstanceAllowed()) + { + appLock = new InterProcessLock ("juceAppLock_" + app->getApplicationName()); + + if (! appLock->enter(0)) + { + MessageManager::broadcastMessage (app->getApplicationName() + "/" + commandLine); + + delete appInstance; + appInstance = 0; + commandLine = String::empty; + + DBG ("Another instance is running - quitting..."); + return 0; + } + } + + JUCE_TRY + { + juce_setCurrentThreadName ("Juce Message Thread"); + + // let the app do its setting-up.. + app->initialise (commandLine.trim()); + + commandLine = String::empty; + + // register for broadcast new app messages + MessageManager::getInstance()->registerBroadcastListener (app); + + app->stillInitialising = false; + + // now loop until a quit message is received.. + useForce = MessageManager::getInstance()->runDispatchLoop(); + + MessageManager::getInstance()->deregisterBroadcastListener (app); + + if (appLock != 0) + { + appLock->exit(); + delete appLock; + } + } +#if JUCE_CATCH_UNHANDLED_EXCEPTIONS + catch (const std::exception& e) + { + app->unhandledException (&e, __FILE__, __LINE__); + } + catch (...) + { + app->unhandledException (0, __FILE__, __LINE__); + } +#endif + + return shutdownAppAndClearUp (useForce); +} + +int JUCEApplication::shutdownAppAndClearUp (const bool useMaximumForce) +{ + jassert (appInstance != 0); + JUCEApplication* const app = appInstance; + int returnValue = 0; + + static bool reentrancyCheck = false; + + if (! reentrancyCheck) + { + reentrancyCheck = true; + + JUCE_TRY + { + // give the app a chance to clean up.. + app->shutdown(); + } +#if JUCE_CATCH_UNHANDLED_EXCEPTIONS + catch (const std::exception& e) + { + app->unhandledException (&e, __FILE__, __LINE__); + } + catch (...) + { + app->unhandledException (0, __FILE__, __LINE__); + } +#endif + + JUCE_TRY + { + shutdownJuce_GUI(); + + returnValue = app->getApplicationReturnValue(); + + appInstance = 0; + delete app; + } + JUCE_CATCH_ALL_ASSERT + + if (useMaximumForce) + { + Process::terminate(); + } + + reentrancyCheck = false; + } + + return returnValue; +} + +int JUCEApplication::main (int argc, char* argv[], + JUCEApplication* const newApp) +{ + juce_setCurrentExecutableFileName (argv[0]); + + String cmd; + for (int i = 1; i < argc; ++i) + cmd << argv[i] << T(' '); + + return JUCEApplication::main (cmd, newApp); +} + +void JUCEApplication::actionListenerCallback (const String& message) +{ + if (message.startsWith (getApplicationName() + "/")) + anotherInstanceStarted (message.substring (getApplicationName().length() + 1)); +} + +static bool juceInitialisedGUI = false; + +void JUCE_PUBLIC_FUNCTION initialiseJuce_GUI() +{ + if (! juceInitialisedGUI) + { + juceInitialisedGUI = true; + + initialiseJuce_NonGUI(); + MessageManager::getInstance(); + Font::initialiseDefaultFontNames(); + LookAndFeel::setDefaultLookAndFeel (0); + +#if JUCE_WIN32 && JUCE_DEBUG + // This section is just for catching people who mess up their project settings and + // turn RTTI off.. + try + { + TextButton tb (String::empty); + Component* c = &tb; + + // Got an exception here? Then TURN ON RTTI in your compiler settings!! + c = dynamic_cast (c); + } + catch (...) + { + // Ended up here? If so, TURN ON RTTI in your compiler settings!! And if you + // got as far as this catch statement, then why haven't you got exception catching + // turned on in the debugger??? + jassertfalse + } +#endif + } +} + +void JUCE_PUBLIC_FUNCTION shutdownJuce_GUI() +{ + if (juceInitialisedGUI) + { + DeletedAtShutdown::deleteAll(); + + LookAndFeel::clearDefaultLookAndFeel(); + shutdownJuce_NonGUI(); + + juceInitialisedGUI = false; + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_Application.cpp *********/ + +/********* Start of inlined file: juce_ApplicationCommandInfo.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +ApplicationCommandInfo::ApplicationCommandInfo (const CommandID commandID_) throw() + : commandID (commandID_), + flags (0) +{ +} + +void ApplicationCommandInfo::setInfo (const String& shortName_, + const String& description_, + const String& categoryName_, + const int flags_) throw() +{ + shortName = shortName_; + description = description_; + categoryName = categoryName_; + flags = flags_; +} + +void ApplicationCommandInfo::setActive (const bool b) throw() +{ + if (b) + flags &= ~isDisabled; + else + flags |= isDisabled; +} + +void ApplicationCommandInfo::setTicked (const bool b) throw() +{ + if (b) + flags |= isTicked; + else + flags &= ~isTicked; +} + +void ApplicationCommandInfo::addDefaultKeypress (const int keyCode, const ModifierKeys& modifiers) throw() +{ + defaultKeypresses.add (KeyPress (keyCode, modifiers, 0)); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ApplicationCommandInfo.cpp *********/ + +/********* Start of inlined file: juce_ApplicationCommandManager.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +ApplicationCommandManager::ApplicationCommandManager() + : listeners (8), + firstTarget (0) +{ + keyMappings = new KeyPressMappingSet (this); + + Desktop::getInstance().addFocusChangeListener (this); +} + +ApplicationCommandManager::~ApplicationCommandManager() +{ + Desktop::getInstance().removeFocusChangeListener (this); + deleteAndZero (keyMappings); +} + +void ApplicationCommandManager::clearCommands() +{ + commands.clear(); + keyMappings->clearAllKeyPresses(); + triggerAsyncUpdate(); +} + +void ApplicationCommandManager::registerCommand (const ApplicationCommandInfo& newCommand) +{ + // zero isn't a valid command ID! + jassert (newCommand.commandID != 0); + + // the name isn't optional! + jassert (newCommand.shortName.isNotEmpty()); + + if (getCommandForID (newCommand.commandID) == 0) + { + ApplicationCommandInfo* const newInfo = new ApplicationCommandInfo (newCommand); + newInfo->flags &= ~ApplicationCommandInfo::isTicked; + commands.add (newInfo); + + keyMappings->resetToDefaultMapping (newCommand.commandID); + + triggerAsyncUpdate(); + } + else + { + // trying to re-register the same command with different parameters? + jassert (newCommand.shortName == getCommandForID (newCommand.commandID)->shortName + && (newCommand.description == getCommandForID (newCommand.commandID)->description || newCommand.description.isEmpty()) + && newCommand.categoryName == getCommandForID (newCommand.commandID)->categoryName + && newCommand.defaultKeypresses == getCommandForID (newCommand.commandID)->defaultKeypresses + && (newCommand.flags & (ApplicationCommandInfo::wantsKeyUpDownCallbacks | ApplicationCommandInfo::hiddenFromKeyEditor | ApplicationCommandInfo::readOnlyInKeyEditor)) + == (getCommandForID (newCommand.commandID)->flags & (ApplicationCommandInfo::wantsKeyUpDownCallbacks | ApplicationCommandInfo::hiddenFromKeyEditor | ApplicationCommandInfo::readOnlyInKeyEditor))); + } +} + +void ApplicationCommandManager::registerAllCommandsForTarget (ApplicationCommandTarget* target) +{ + if (target != 0) + { + Array commandIDs; + target->getAllCommands (commandIDs); + + for (int i = 0; i < commandIDs.size(); ++i) + { + ApplicationCommandInfo info (commandIDs.getUnchecked(i)); + target->getCommandInfo (info.commandID, info); + + registerCommand (info); + } + } +} + +void ApplicationCommandManager::removeCommand (const CommandID commandID) +{ + for (int i = commands.size(); --i >= 0;) + { + if (commands.getUnchecked (i)->commandID == commandID) + { + commands.remove (i); + triggerAsyncUpdate(); + + const Array keys (keyMappings->getKeyPressesAssignedToCommand (commandID)); + + for (int j = keys.size(); --j >= 0;) + keyMappings->removeKeyPress (keys.getReference (j)); + } + } +} + +void ApplicationCommandManager::commandStatusChanged() +{ + triggerAsyncUpdate(); +} + +const ApplicationCommandInfo* ApplicationCommandManager::getCommandForID (const CommandID commandID) const throw() +{ + for (int i = commands.size(); --i >= 0;) + if (commands.getUnchecked(i)->commandID == commandID) + return commands.getUnchecked(i); + + return 0; +} + +const String ApplicationCommandManager::getNameOfCommand (const CommandID commandID) const throw() +{ + const ApplicationCommandInfo* const ci = getCommandForID (commandID); + + return (ci != 0) ? ci->shortName : String::empty; +} + +const String ApplicationCommandManager::getDescriptionOfCommand (const CommandID commandID) const throw() +{ + const ApplicationCommandInfo* const ci = getCommandForID (commandID); + + return (ci != 0) ? (ci->description.isNotEmpty() ? ci->description : ci->shortName) + : String::empty; +} + +const StringArray ApplicationCommandManager::getCommandCategories() const throw() +{ + StringArray s; + + for (int i = 0; i < commands.size(); ++i) + s.addIfNotAlreadyThere (commands.getUnchecked(i)->categoryName, false); + + return s; +} + +const Array ApplicationCommandManager::getCommandsInCategory (const String& categoryName) const throw() +{ + Array results (4); + + for (int i = 0; i < commands.size(); ++i) + if (commands.getUnchecked(i)->categoryName == categoryName) + results.add (commands.getUnchecked(i)->commandID); + + return results; +} + +bool ApplicationCommandManager::invokeDirectly (const CommandID commandID, const bool asynchronously) +{ + ApplicationCommandTarget::InvocationInfo info (commandID); + info.invocationMethod = ApplicationCommandTarget::InvocationInfo::direct; + + return invoke (info, asynchronously); +} + +bool ApplicationCommandManager::invoke (const ApplicationCommandTarget::InvocationInfo& info_, const bool asynchronously) +{ + // This call isn't thread-safe for use from a non-UI thread without locking the message + // manager first.. + jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager()); + + ApplicationCommandTarget* const target = getFirstCommandTarget (info_.commandID); + + if (target == 0) + return false; + + ApplicationCommandInfo commandInfo (0); + target->getCommandInfo (info_.commandID, commandInfo); + + ApplicationCommandTarget::InvocationInfo info (info_); + info.commandFlags = commandInfo.flags; + + sendListenerInvokeCallback (info); + + const bool ok = target->invoke (info, asynchronously); + + commandStatusChanged(); + + return ok; +} + +ApplicationCommandTarget* ApplicationCommandManager::getFirstCommandTarget (const CommandID) +{ + return firstTarget != 0 ? firstTarget + : findDefaultComponentTarget(); +} + +void ApplicationCommandManager::setFirstCommandTarget (ApplicationCommandTarget* const newTarget) throw() +{ + firstTarget = newTarget; +} + +ApplicationCommandTarget* ApplicationCommandManager::getTargetForCommand (const CommandID commandID, + ApplicationCommandInfo& upToDateInfo) +{ + ApplicationCommandTarget* target = getFirstCommandTarget (commandID); + + if (target == 0) + target = JUCEApplication::getInstance(); + + if (target != 0) + target = target->getTargetForCommand (commandID); + + if (target != 0) + target->getCommandInfo (commandID, upToDateInfo); + + return target; +} + +ApplicationCommandTarget* ApplicationCommandManager::findTargetForComponent (Component* c) +{ + ApplicationCommandTarget* target = dynamic_cast (c); + + if (target == 0 && c != 0) + // (unable to use the syntax findParentComponentOfClass () because of a VC6 compiler bug) + target = c->findParentComponentOfClass ((ApplicationCommandTarget*) 0); + + return target; +} + +ApplicationCommandTarget* ApplicationCommandManager::findDefaultComponentTarget() +{ + Component* c = Component::getCurrentlyFocusedComponent(); + + if (c == 0) + { + TopLevelWindow* const activeWindow = TopLevelWindow::getActiveTopLevelWindow(); + + if (activeWindow != 0) + { + c = activeWindow->getPeer()->getLastFocusedSubcomponent(); + + if (c == 0) + c = activeWindow; + } + } + + if (c == 0) + { + // getting a bit desperate now - try all desktop comps.. + for (int i = Desktop::getInstance().getNumComponents(); --i >= 0;) + { + ApplicationCommandTarget* const target + = findTargetForComponent (Desktop::getInstance().getComponent (i) + ->getPeer()->getLastFocusedSubcomponent()); + + if (target != 0) + return target; + } + } + + if (c != 0) + { + ResizableWindow* const resizableWindow = dynamic_cast (c); + + // if we're focused on a ResizableWindow, chances are that it's the content + // component that really should get the event. And if not, the event will + // still be passed up to the top level window anyway, so let's send it to the + // content comp. + if (resizableWindow != 0 && resizableWindow->getContentComponent() != 0) + c = resizableWindow->getContentComponent(); + + ApplicationCommandTarget* const target = findTargetForComponent (c); + + if (target != 0) + return target; + } + + return JUCEApplication::getInstance(); +} + +void ApplicationCommandManager::addListener (ApplicationCommandManagerListener* const listener) throw() +{ + jassert (listener != 0); + if (listener != 0) + listeners.add (listener); +} + +void ApplicationCommandManager::removeListener (ApplicationCommandManagerListener* const listener) throw() +{ + listeners.removeValue (listener); +} + +void ApplicationCommandManager::sendListenerInvokeCallback (const ApplicationCommandTarget::InvocationInfo& info) const +{ + for (int i = listeners.size(); --i >= 0;) + { + ((ApplicationCommandManagerListener*) listeners.getUnchecked (i))->applicationCommandInvoked (info); + i = jmin (i, listeners.size()); + } +} + +void ApplicationCommandManager::handleAsyncUpdate() +{ + for (int i = listeners.size(); --i >= 0;) + { + ((ApplicationCommandManagerListener*) listeners.getUnchecked (i))->applicationCommandListChanged(); + i = jmin (i, listeners.size()); + } +} + +void ApplicationCommandManager::globalFocusChanged (Component*) +{ + commandStatusChanged(); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ApplicationCommandManager.cpp *********/ + +/********* Start of inlined file: juce_ApplicationCommandTarget.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +ApplicationCommandTarget::ApplicationCommandTarget() + : messageInvoker (0) +{ +} + +ApplicationCommandTarget::~ApplicationCommandTarget() +{ + deleteAndZero (messageInvoker); +} + +bool ApplicationCommandTarget::tryToInvoke (const InvocationInfo& info, const bool async) +{ + if (isCommandActive (info.commandID)) + { + if (async) + { + if (messageInvoker == 0) + messageInvoker = new CommandTargetMessageInvoker (this); + + messageInvoker->postMessage (new Message (0, 0, 0, new ApplicationCommandTarget::InvocationInfo (info))); + return true; + } + else + { + const bool success = perform (info); + + jassert (success); // hmm - your target should have been able to perform this command. If it can't + // do it at the moment for some reason, it should clear the 'isActive' flag when it + // returns the command's info. + return success; + } + } + + return false; +} + +ApplicationCommandTarget* ApplicationCommandTarget::findFirstTargetParentComponent() +{ + Component* c = dynamic_cast (this); + + if (c != 0) + // (unable to use the syntax findParentComponentOfClass () because of a VC6 compiler bug) + return c->findParentComponentOfClass ((ApplicationCommandTarget*) 0); + + return 0; +} + +ApplicationCommandTarget* ApplicationCommandTarget::getTargetForCommand (const CommandID commandID) +{ + ApplicationCommandTarget* target = this; + int depth = 0; + + while (target != 0) + { + Array commandIDs; + target->getAllCommands (commandIDs); + + if (commandIDs.contains (commandID)) + return target; + + target = target->getNextCommandTarget(); + + ++depth; + jassert (depth < 100); // could be a recursive command chain?? + jassert (target != this); // definitely a recursive command chain! + + if (depth > 100 || target == this) + break; + } + + if (target == 0) + { + target = JUCEApplication::getInstance(); + + if (target != 0) + { + Array commandIDs; + target->getAllCommands (commandIDs); + + if (commandIDs.contains (commandID)) + return target; + } + } + + return 0; +} + +bool ApplicationCommandTarget::isCommandActive (const CommandID commandID) +{ + ApplicationCommandInfo info (commandID); + info.flags = ApplicationCommandInfo::isDisabled; + + getCommandInfo (commandID, info); + + return (info.flags & ApplicationCommandInfo::isDisabled) == 0; +} + +bool ApplicationCommandTarget::invoke (const InvocationInfo& info, const bool async) +{ + ApplicationCommandTarget* target = this; + int depth = 0; + + while (target != 0) + { + if (target->tryToInvoke (info, async)) + return true; + + target = target->getNextCommandTarget(); + + ++depth; + jassert (depth < 100); // could be a recursive command chain?? + jassert (target != this); // definitely a recursive command chain! + + if (depth > 100 || target == this) + break; + } + + if (target == 0) + { + target = JUCEApplication::getInstance(); + + if (target != 0) + return target->tryToInvoke (info, async); + } + + return false; +} + +bool ApplicationCommandTarget::invokeDirectly (const CommandID commandID, const bool asynchronously) +{ + ApplicationCommandTarget::InvocationInfo info (commandID); + info.invocationMethod = ApplicationCommandTarget::InvocationInfo::direct; + + return invoke (info, asynchronously); +} + +ApplicationCommandTarget::InvocationInfo::InvocationInfo (const CommandID commandID_) throw() + : commandID (commandID_), + commandFlags (0), + invocationMethod (direct), + originatingComponent (0), + isKeyDown (false), + millisecsSinceKeyPressed (0) +{ +} + +ApplicationCommandTarget::CommandTargetMessageInvoker::CommandTargetMessageInvoker (ApplicationCommandTarget* const owner_) + : owner (owner_) +{ +} + +ApplicationCommandTarget::CommandTargetMessageInvoker::~CommandTargetMessageInvoker() +{ +} + +void ApplicationCommandTarget::CommandTargetMessageInvoker::handleMessage (const Message& message) +{ + InvocationInfo* const info = (InvocationInfo*) message.pointerParameter; + owner->tryToInvoke (*info, false); + delete info; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ApplicationCommandTarget.cpp *********/ + +/********* Start of inlined file: juce_ApplicationProperties.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +juce_ImplementSingleton (ApplicationProperties) + +ApplicationProperties::ApplicationProperties() throw() + : userProps (0), + commonProps (0), + msBeforeSaving (3000), + options (PropertiesFile::storeAsBinary), + commonSettingsAreReadOnly (0) +{ +} + +ApplicationProperties::~ApplicationProperties() +{ + closeFiles(); + clearSingletonInstance(); +} + +void ApplicationProperties::setStorageParameters (const String& applicationName, + const String& fileNameSuffix, + const String& folderName_, + const int millisecondsBeforeSaving, + const int propertiesFileOptions) throw() +{ + appName = applicationName; + fileSuffix = fileNameSuffix; + folderName = folderName_; + msBeforeSaving = millisecondsBeforeSaving; + options = propertiesFileOptions; +} + +bool ApplicationProperties::testWriteAccess (const bool testUserSettings, + const bool testCommonSettings, + const bool showWarningDialogOnFailure) +{ + const bool userOk = (! testUserSettings) || getUserSettings()->save(); + const bool commonOk = (! testCommonSettings) || getCommonSettings (false)->save(); + + if (! (userOk && commonOk)) + { + if (showWarningDialogOnFailure) + { + String filenames; + + if (userProps != 0 && ! userOk) + filenames << '\n' << userProps->getFile().getFullPathName(); + + if (commonProps != 0 && ! commonOk) + filenames << '\n' << commonProps->getFile().getFullPathName(); + + AlertWindow::showMessageBox (AlertWindow::WarningIcon, + appName + TRANS(" - Unable to save settings"), + TRANS("An error occurred when trying to save the application's settings file...\n\nIn order to save and restore its settings, ") + + appName + TRANS(" needs to be able to write to the following files:\n") + + filenames + + TRANS("\n\nMake sure that these files aren't read-only, and that the disk isn't full.")); + } + + return false; + } + + return true; +} + +void ApplicationProperties::openFiles() throw() +{ + // You need to call setStorageParameters() before trying to get hold of the + // properties! + jassert (appName.isNotEmpty()); + + if (appName.isNotEmpty()) + { + if (userProps == 0) + userProps = PropertiesFile::createDefaultAppPropertiesFile (appName, fileSuffix, folderName, + false, msBeforeSaving, options); + + if (commonProps == 0) + commonProps = PropertiesFile::createDefaultAppPropertiesFile (appName, fileSuffix, folderName, + true, msBeforeSaving, options); + + userProps->setFallbackPropertySet (commonProps); + } +} + +PropertiesFile* ApplicationProperties::getUserSettings() throw() +{ + if (userProps == 0) + openFiles(); + + return userProps; +} + +PropertiesFile* ApplicationProperties::getCommonSettings (const bool returnUserPropsIfReadOnly) throw() +{ + if (commonProps == 0) + openFiles(); + + if (returnUserPropsIfReadOnly) + { + if (commonSettingsAreReadOnly == 0) + commonSettingsAreReadOnly = commonProps->save() ? -1 : 1; + + if (commonSettingsAreReadOnly > 0) + return userProps; + } + + return commonProps; +} + +bool ApplicationProperties::saveIfNeeded() +{ + return (userProps == 0 || userProps->saveIfNeeded()) + && (commonProps == 0 || commonProps->saveIfNeeded()); +} + +void ApplicationProperties::closeFiles() +{ + deleteAndZero (userProps); + deleteAndZero (commonProps); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ApplicationProperties.cpp *********/ + +/********* Start of inlined file: juce_DeletedAtShutdown.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +static VoidArray objectsToDelete (16); +static CriticalSection lock; + +DeletedAtShutdown::DeletedAtShutdown() throw() +{ + const ScopedLock sl (lock); + objectsToDelete.add (this); +} + +DeletedAtShutdown::~DeletedAtShutdown() +{ + const ScopedLock sl (lock); + objectsToDelete.removeValue (this); +} + +void DeletedAtShutdown::deleteAll() +{ + // make a local copy of the array, so it can't get into a loop if something + // creates another DeletedAtShutdown object during its destructor. + lock.enter(); + const VoidArray localCopy (objectsToDelete); + lock.exit(); + + for (int i = localCopy.size(); --i >= 0;) + { + JUCE_TRY + { + DeletedAtShutdown* const deletee = (DeletedAtShutdown*) localCopy.getUnchecked(i); + + // double-check that it's not already been deleted during another object's destructor. + lock.enter(); + const bool okToDelete = objectsToDelete.contains (deletee); + lock.exit(); + + if (okToDelete) + delete deletee; + } + JUCE_CATCH_EXCEPTION + } + + // if no objects got re-created during shutdown, this should have been emptied by their + // destructors + jassert (objectsToDelete.size() == 0); + + objectsToDelete.clear(); // just to make sure the array doesn't have any memory still allocated +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_DeletedAtShutdown.cpp *********/ + +/********* Start of inlined file: juce_PropertiesFile.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +static const int propFileMagicNumber = ((int) littleEndianInt ("PROP")); +static const int propFileMagicNumberCompressed = ((int) littleEndianInt ("CPRP")); + +static const tchar* const propertyFileXmlTag = T("PROPERTIES"); +static const tchar* const propertyTagName = T("VALUE"); + +PropertiesFile::PropertiesFile (const File& f, + const int millisecondsBeforeSaving, + const int options_) throw() + : PropertySet (ignoreCaseOfKeyNames), + file (f), + timerInterval (millisecondsBeforeSaving), + options (options_), + needsWriting (false) +{ + // You need to correctly specify just one storage format for the file + jassert ((options_ & (storeAsBinary | storeAsCompressedBinary | storeAsXML)) == storeAsBinary + || (options_ & (storeAsBinary | storeAsCompressedBinary | storeAsXML)) == storeAsCompressedBinary + || (options_ & (storeAsBinary | storeAsCompressedBinary | storeAsXML)) == storeAsXML); + + InputStream* fileStream = f.createInputStream(); + + if (fileStream != 0) + { + int magicNumber = fileStream->readInt(); + + if (magicNumber == propFileMagicNumberCompressed) + { + fileStream = new SubregionStream (fileStream, 4, -1, true); + fileStream = new GZIPDecompressorInputStream (fileStream, true); + + magicNumber = propFileMagicNumber; + } + + if (magicNumber == propFileMagicNumber) + { + BufferedInputStream in (fileStream, 2048, true); + + int numValues = in.readInt(); + + while (--numValues >= 0 && ! in.isExhausted()) + { + const String key (in.readString()); + const String value (in.readString()); + + jassert (key.isNotEmpty()); + if (key.isNotEmpty()) + getAllProperties().set (key, value); + } + } + else + { + // Not a binary props file - let's see if it's XML.. + delete fileStream; + + XmlDocument parser (f); + XmlElement* doc = parser.getDocumentElement (true); + + if (doc != 0 && doc->hasTagName (propertyFileXmlTag)) + { + delete doc; + doc = parser.getDocumentElement(); + + if (doc != 0) + { + forEachXmlChildElementWithTagName (*doc, e, propertyTagName) + { + const String name (e->getStringAttribute (T("name"))); + + if (name.isNotEmpty()) + getAllProperties().set (name, e->getStringAttribute (T("val"))); + } + } + else + { + // must be a pretty broken XML file we're trying to parse here! + jassertfalse + } + + delete doc; + } + } + } +} + +PropertiesFile::~PropertiesFile() +{ + saveIfNeeded(); +} + +bool PropertiesFile::saveIfNeeded() +{ + const ScopedLock sl (getLock()); + + return (! needsWriting) || save(); +} + +bool PropertiesFile::needsToBeSaved() const throw() +{ + const ScopedLock sl (getLock()); + + return needsWriting; +} + +bool PropertiesFile::save() +{ + const ScopedLock sl (getLock()); + + stopTimer(); + + if (file == File::nonexistent + || file.isDirectory() + || ! file.getParentDirectory().createDirectory()) + return false; + + if ((options & storeAsXML) != 0) + { + XmlElement* const doc = new XmlElement (propertyFileXmlTag); + + for (int i = 0; i < getAllProperties().size(); ++i) + { + XmlElement* const e = new XmlElement (propertyTagName); + e->setAttribute (T("name"), getAllProperties().getAllKeys() [i]); + e->setAttribute (T("val"), getAllProperties().getAllValues() [i]); + doc->addChildElement (e); + } + + const bool ok = doc->writeToFile (file, String::empty); + + delete doc; + + return ok; + } + else + { + const File tempFile (file.getNonexistentSibling (false)); + OutputStream* out = tempFile.createOutputStream(); + + if (out != 0) + { + if ((options & storeAsCompressedBinary) != 0) + { + out->writeInt (propFileMagicNumberCompressed); + out->flush(); + + out = new GZIPCompressorOutputStream (out, 9, true); + } + else + { + // have you set up the storage option flags correctly? + jassert ((options & storeAsBinary) != 0); + + out->writeInt (propFileMagicNumber); + } + + const int numProperties = getAllProperties().size(); + + out->writeInt (numProperties); + + for (int i = 0; i < numProperties; ++i) + { + out->writeString (getAllProperties().getAllKeys() [i]); + out->writeString (getAllProperties().getAllValues() [i]); + } + + out->flush(); + delete out; + + if (tempFile.moveFileTo (file)) + { + needsWriting = false; + return true; + } + + tempFile.deleteFile(); + } + } + + return false; +} + +void PropertiesFile::timerCallback() +{ + saveIfNeeded(); +} + +void PropertiesFile::propertyChanged() +{ + sendChangeMessage (this); + + needsWriting = true; + + if (timerInterval > 0) + startTimer (timerInterval); + else if (timerInterval == 0) + saveIfNeeded(); +} + +const File PropertiesFile::getFile() const throw() +{ + return file; +} + +const File PropertiesFile::getDefaultAppSettingsFile (const String& applicationName, + const String& fileNameSuffix, + const String& folderName, + const bool commonToAllUsers) +{ + // mustn't have illegal characters in this name.. + jassert (applicationName == File::createLegalFileName (applicationName)); + +#if JUCE_MAC + File dir (commonToAllUsers ? "/Library/Preferences" + : "~/Library/Preferences"); + + if (folderName.isNotEmpty()) + dir = dir.getChildFile (folderName); +#endif + +#ifdef JUCE_LINUX + const File dir ((commonToAllUsers ? T("/var/") : T("~/")) + + (folderName.isNotEmpty() ? folderName + : (T(".") + applicationName))); +#endif + +#if JUCE_WIN32 + File dir (File::getSpecialLocation (commonToAllUsers ? File::commonApplicationDataDirectory + : File::userApplicationDataDirectory)); + + if (dir == File::nonexistent) + return File::nonexistent; + + dir = dir.getChildFile (folderName.isNotEmpty() ? folderName + : applicationName); + +#endif + + return dir.getChildFile (applicationName) + .withFileExtension (fileNameSuffix); +} + +PropertiesFile* PropertiesFile::createDefaultAppPropertiesFile (const String& applicationName, + const String& fileNameSuffix, + const String& folderName, + const bool commonToAllUsers, + const int millisecondsBeforeSaving, + const int propertiesFileOptions) +{ + const File file (getDefaultAppSettingsFile (applicationName, + fileNameSuffix, + folderName, + commonToAllUsers)); + + jassert (file != File::nonexistent); + + if (file == File::nonexistent) + return 0; + + return new PropertiesFile (file, millisecondsBeforeSaving, propertiesFileOptions); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_PropertiesFile.cpp *********/ + +/********* Start of inlined file: juce_AiffAudioFormat.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +#undef chunkName +#define chunkName(a) (int)littleEndianInt(a) + +#define aiffFormatName TRANS("AIFF file") +static const tchar* const aiffExtensions[] = { T(".aiff"), T(".aif"), 0 }; + +class AiffAudioFormatReader : public AudioFormatReader +{ +public: + int bytesPerFrame; + int64 dataChunkStart; + bool littleEndian; + + AiffAudioFormatReader (InputStream* in) + : AudioFormatReader (in, aiffFormatName) + { + if (input->readInt() == chunkName ("FORM")) + { + const int len = input->readIntBigEndian(); + const int64 end = input->getPosition() + len; + + const int nextType = input->readInt(); + if (nextType == chunkName ("AIFF") || nextType == chunkName ("AIFC")) + { + bool hasGotVer = false; + bool hasGotData = false; + bool hasGotType = false; + + while (input->getPosition() < end) + { + const int type = input->readInt(); + const uint32 length = (uint32) input->readIntBigEndian(); + const int64 chunkEnd = input->getPosition() + length; + + if (type == chunkName ("FVER")) + { + hasGotVer = true; + + const int ver = input->readIntBigEndian(); + if (ver != 0 && ver != (int)0xa2805140) + break; + } + else if (type == chunkName ("COMM")) + { + hasGotType = true; + + numChannels = (unsigned int)input->readShortBigEndian(); + lengthInSamples = input->readIntBigEndian(); + bitsPerSample = input->readShortBigEndian(); + bytesPerFrame = (numChannels * bitsPerSample) >> 3; + + unsigned char sampleRateBytes[10]; + input->read (sampleRateBytes, 10); + const int byte0 = sampleRateBytes[0]; + + if ((byte0 & 0x80) != 0 + || byte0 <= 0x3F || byte0 > 0x40 + || (byte0 == 0x40 && sampleRateBytes[1] > 0x1C)) + break; + + unsigned int sampRate = bigEndianInt ((char*) sampleRateBytes + 2); + sampRate >>= (16414 - bigEndianShort ((char*) sampleRateBytes)); + sampleRate = (int)sampRate; + + if (length <= 18) + { + // some types don't have a chunk large enough to include a compression + // type, so assume it's just big-endian pcm + littleEndian = false; + } + else + { + const int compType = input->readInt(); + + if (compType == chunkName ("NONE") || compType == chunkName ("twos")) + { + littleEndian = false; + } + else if (compType == chunkName ("sowt")) + { + littleEndian = true; + } + else + { + sampleRate = 0; + break; + } + } + } + else if (type == chunkName ("SSND")) + { + hasGotData = true; + + const int offset = input->readIntBigEndian(); + dataChunkStart = input->getPosition() + 4 + offset; + lengthInSamples = (bytesPerFrame > 0) ? jmin (lengthInSamples, (int64) (length / bytesPerFrame)) : 0; + } + else if ((hasGotVer && hasGotData && hasGotType) + || chunkEnd < input->getPosition() + || input->isExhausted()) + { + break; + } + + input->setPosition (chunkEnd); + } + } + } + } + + ~AiffAudioFormatReader() + { + } + + bool read (int** destSamples, + int64 startSampleInFile, + int numSamples) + { + int64 start = startSampleInFile; + int startOffsetInDestBuffer = 0; + + if (startSampleInFile < 0) + { + const int silence = (int) jmin (-startSampleInFile, (int64) numSamples); + + int** destChan = destSamples; + + for (int i = 2; --i >= 0;) + { + if (*destChan != 0) + { + zeromem (*destChan, sizeof (int) * silence); + ++destChan; + } + } + + startOffsetInDestBuffer += silence; + numSamples -= silence; + start = 0; + } + + int numToDo = jlimit (0, numSamples, (int) (lengthInSamples - start)); + + if (numToDo > 0) + { + input->setPosition (dataChunkStart + start * bytesPerFrame); + + int num = numToDo; + int* left = destSamples[0]; + if (left != 0) + left += startOffsetInDestBuffer; + + int* right = destSamples[1]; + if (right != 0) + right += startOffsetInDestBuffer; + + // (keep this a multiple of 3) + const int tempBufSize = 1440 * 4; + char tempBuffer [tempBufSize]; + + while (num > 0) + { + const int numThisTime = jmin (tempBufSize / bytesPerFrame, num); + const int bytesRead = input->read (tempBuffer, numThisTime * bytesPerFrame); + + if (bytesRead < numThisTime * bytesPerFrame) + zeromem (tempBuffer + bytesRead, numThisTime * bytesPerFrame - bytesRead); + + if (bitsPerSample == 16) + { + if (littleEndian) + { + const short* src = (const short*) tempBuffer; + + if (numChannels > 1) + { + if (left == 0) + { + for (int i = numThisTime; --i >= 0;) + { + *right++ = (int) swapIfBigEndian ((unsigned short) *src++) << 16; + ++src; + } + } + else if (right == 0) + { + for (int i = numThisTime; --i >= 0;) + { + ++src; + *left++ = (int) swapIfBigEndian ((unsigned short) *src++) << 16; + } + } + else + { + for (int i = numThisTime; --i >= 0;) + { + *left++ = (int) swapIfBigEndian ((unsigned short) *src++) << 16; + *right++ = (int) swapIfBigEndian ((unsigned short) *src++) << 16; + } + } + } + else + { + for (int i = numThisTime; --i >= 0;) + { + *left++ = (int) swapIfBigEndian ((unsigned short) *src++) << 16; + } + } + } + else + { + const char* src = (const char*) tempBuffer; + + if (numChannels > 1) + { + if (left == 0) + { + for (int i = numThisTime; --i >= 0;) + { + *right++ = bigEndianShort (src) << 16; + src += 4; + } + } + else if (right == 0) + { + for (int i = numThisTime; --i >= 0;) + { + src += 2; + *left++ = bigEndianShort (src) << 16; + src += 2; + } + } + else + { + for (int i = numThisTime; --i >= 0;) + { + *left++ = bigEndianShort (src) << 16; + src += 2; + *right++ = bigEndianShort (src) << 16; + src += 2; + } + } + } + else + { + for (int i = numThisTime; --i >= 0;) + { + *left++ = bigEndianShort (src) << 16; + src += 2; + } + } + } + } + else if (bitsPerSample == 24) + { + const char* src = (const char*)tempBuffer; + + if (littleEndian) + { + if (numChannels > 1) + { + if (left == 0) + { + for (int i = numThisTime; --i >= 0;) + { + *right++ = littleEndian24Bit (src) << 8; + src += 6; + } + } + else if (right == 0) + { + for (int i = numThisTime; --i >= 0;) + { + src += 3; + *left++ = littleEndian24Bit (src) << 8; + src += 3; + } + } + else + { + for (int i = numThisTime; --i >= 0;) + { + *left++ = littleEndian24Bit (src) << 8; + src += 3; + *right++ = littleEndian24Bit (src) << 8; + src += 3; + } + } + } + else + { + for (int i = numThisTime; --i >= 0;) + { + *left++ = littleEndian24Bit (src) << 8; + src += 3; + } + } + } + else + { + if (numChannels > 1) + { + if (left == 0) + { + for (int i = numThisTime; --i >= 0;) + { + *right++ = bigEndian24Bit (src) << 8; + src += 6; + } + } + else if (right == 0) + { + for (int i = numThisTime; --i >= 0;) + { + src += 3; + *left++ = bigEndian24Bit (src) << 8; + src += 3; + } + } + else + { + for (int i = numThisTime; --i >= 0;) + { + *left++ = bigEndian24Bit (src) << 8; + src += 3; + *right++ = bigEndian24Bit (src) << 8; + src += 3; + } + } + } + else + { + for (int i = numThisTime; --i >= 0;) + { + *left++ = bigEndian24Bit (src) << 8; + src += 3; + } + } + } + } + else if (bitsPerSample == 32) + { + const unsigned int* src = (const unsigned int*) tempBuffer; + unsigned int* l = (unsigned int*) left; + unsigned int* r = (unsigned int*) right; + + if (littleEndian) + { + if (numChannels > 1) + { + if (l == 0) + { + for (int i = numThisTime; --i >= 0;) + { + ++src; + *r++ = swapIfBigEndian (*src++); + } + } + else if (r == 0) + { + for (int i = numThisTime; --i >= 0;) + { + *l++ = swapIfBigEndian (*src++); + ++src; + } + } + else + { + for (int i = numThisTime; --i >= 0;) + { + *l++ = swapIfBigEndian (*src++); + *r++ = swapIfBigEndian (*src++); + } + } + } + else + { + for (int i = numThisTime; --i >= 0;) + { + *l++ = swapIfBigEndian (*src++); + } + } + } + else + { + if (numChannels > 1) + { + if (l == 0) + { + for (int i = numThisTime; --i >= 0;) + { + ++src; + *r++ = swapIfLittleEndian (*src++); + } + } + else if (r == 0) + { + for (int i = numThisTime; --i >= 0;) + { + *l++ = swapIfLittleEndian (*src++); + ++src; + } + } + else + { + for (int i = numThisTime; --i >= 0;) + { + *l++ = swapIfLittleEndian (*src++); + *r++ = swapIfLittleEndian (*src++); + } + } + } + else + { + for (int i = numThisTime; --i >= 0;) + { + *l++ = swapIfLittleEndian (*src++); + } + } + } + + left = (int*) l; + right = (int*) r; + } + else if (bitsPerSample == 8) + { + const char* src = (const char*) tempBuffer; + + if (numChannels > 1) + { + if (left == 0) + { + for (int i = numThisTime; --i >= 0;) + { + *right++ = ((int) *src++) << 24; + ++src; + } + } + else if (right == 0) + { + for (int i = numThisTime; --i >= 0;) + { + ++src; + *left++ = ((int) *src++) << 24; + } + } + else + { + for (int i = numThisTime; --i >= 0;) + { + *left++ = ((int) *src++) << 24; + *right++ = ((int) *src++) << 24; + } + } + } + else + { + for (int i = numThisTime; --i >= 0;) + { + *left++ = ((int) *src++) << 24; + } + } + } + + num -= numThisTime; + } + } + + if (numToDo < numSamples) + { + int** destChan = destSamples; + while (*destChan != 0) + { + zeromem ((*destChan) + (startOffsetInDestBuffer + numToDo), + sizeof (int) * (numSamples - numToDo)); + ++destChan; + } + } + + return true; + } + + juce_UseDebuggingNewOperator + +private: + AiffAudioFormatReader (const AiffAudioFormatReader&); + const AiffAudioFormatReader& operator= (const AiffAudioFormatReader&); +}; + +class AiffAudioFormatWriter : public AudioFormatWriter +{ + MemoryBlock tempBlock; + uint32 lengthInSamples, bytesWritten; + int64 headerPosition; + bool writeFailed; + + AiffAudioFormatWriter (const AiffAudioFormatWriter&); + const AiffAudioFormatWriter& operator= (const AiffAudioFormatWriter&); + + void writeHeader() + { + const bool couldSeekOk = output->setPosition (headerPosition); + (void) couldSeekOk; + + // if this fails, you've given it an output stream that can't seek! It needs + // to be able to seek back to write the header + jassert (couldSeekOk); + + const int headerLen = 54; + int audioBytes = lengthInSamples * ((bitsPerSample * numChannels) / 8); + audioBytes += (audioBytes & 1); + + output->writeInt (chunkName ("FORM")); + output->writeIntBigEndian (headerLen + audioBytes - 8); + output->writeInt (chunkName ("AIFF")); + output->writeInt (chunkName ("COMM")); + output->writeIntBigEndian (18); + output->writeShortBigEndian ((short) numChannels); + output->writeIntBigEndian (lengthInSamples); + output->writeShortBigEndian ((short) bitsPerSample); + + uint8 sampleRateBytes[10]; + zeromem (sampleRateBytes, 10); + + if (sampleRate <= 1) + { + sampleRateBytes[0] = 0x3f; + sampleRateBytes[1] = 0xff; + sampleRateBytes[2] = 0x80; + } + else + { + int mask = 0x40000000; + sampleRateBytes[0] = 0x40; + + if (sampleRate >= mask) + { + jassertfalse + sampleRateBytes[1] = 0x1d; + } + else + { + int n = (int) sampleRate; + + int i; + for (i = 0; i <= 32 ; ++i) + { + if ((n & mask) != 0) + break; + + mask >>= 1; + } + + n = n << (i + 1); + + sampleRateBytes[1] = (uint8) (29 - i); + sampleRateBytes[2] = (uint8) ((n >> 24) & 0xff); + sampleRateBytes[3] = (uint8) ((n >> 16) & 0xff); + sampleRateBytes[4] = (uint8) ((n >> 8) & 0xff); + sampleRateBytes[5] = (uint8) (n & 0xff); + } + } + + output->write (sampleRateBytes, 10); + + output->writeInt (chunkName ("SSND")); + output->writeIntBigEndian (audioBytes + 8); + output->writeInt (0); + output->writeInt (0); + + jassert (output->getPosition() == headerLen); + } + +public: + + AiffAudioFormatWriter (OutputStream* out, + const double sampleRate, + const unsigned int chans, + const int bits) + : AudioFormatWriter (out, + aiffFormatName, + sampleRate, + chans, + bits), + lengthInSamples (0), + bytesWritten (0), + writeFailed (false) + { + headerPosition = out->getPosition(); + writeHeader(); + } + + ~AiffAudioFormatWriter() + { + if ((bytesWritten & 1) != 0) + output->writeByte (0); + + writeHeader(); + } + + bool write (const int** data, int numSamples) + { + if (writeFailed) + return false; + + const int bytes = numChannels * numSamples * bitsPerSample / 8; + tempBlock.ensureSize (bytes, false); + char* buffer = (char*) tempBlock.getData(); + + const int* left = data[0]; + const int* right = data[1]; + if (right == 0) + right = left; + + if (bitsPerSample == 16) + { + short* b = (short*) buffer; + + if (numChannels > 1) + { + for (int i = numSamples; --i >= 0;) + { + *b++ = (short) swapIfLittleEndian ((unsigned short) (*left++ >> 16)); + *b++ = (short) swapIfLittleEndian ((unsigned short) (*right++ >> 16)); + } + } + else + { + for (int i = numSamples; --i >= 0;) + { + *b++ = (short) swapIfLittleEndian ((unsigned short) (*left++ >> 16)); + } + } + } + else if (bitsPerSample == 24) + { + char* b = (char*) buffer; + + if (numChannels > 1) + { + for (int i = numSamples; --i >= 0;) + { + bigEndian24BitToChars (*left++ >> 8, b); + b += 3; + bigEndian24BitToChars (*right++ >> 8, b); + b += 3; + } + } + else + { + for (int i = numSamples; --i >= 0;) + { + bigEndian24BitToChars (*left++ >> 8, b); + b += 3; + } + } + } + else if (bitsPerSample == 32) + { + unsigned int* b = (unsigned int*) buffer; + + if (numChannels > 1) + { + for (int i = numSamples; --i >= 0;) + { + *b++ = swapIfLittleEndian ((unsigned int) *left++); + *b++ = swapIfLittleEndian ((unsigned int) *right++); + } + } + else + { + for (int i = numSamples; --i >= 0;) + { + *b++ = swapIfLittleEndian ((unsigned int) *left++); + } + } + } + else if (bitsPerSample == 8) + { + char* b = (char*)buffer; + + if (numChannels > 1) + { + for (int i = numSamples; --i >= 0;) + { + *b++ = (char) (*left++ >> 24); + *b++ = (char) (*right++ >> 24); + } + } + else + { + for (int i = numSamples; --i >= 0;) + { + *b++ = (char) (*left++ >> 24); + } + } + } + + if (bytesWritten + bytes >= (uint32) 0xfff00000 + || ! output->write (buffer, bytes)) + { + // failed to write to disk, so let's try writing the header. + // If it's just run out of disk space, then if it does manage + // to write the header, we'll still have a useable file.. + writeHeader(); + writeFailed = true; + return false; + } + else + { + bytesWritten += bytes; + lengthInSamples += numSamples; + + return true; + } + } + + juce_UseDebuggingNewOperator +}; + +AiffAudioFormat::AiffAudioFormat() + : AudioFormat (aiffFormatName, (const tchar**) aiffExtensions) +{ +} + +AiffAudioFormat::~AiffAudioFormat() +{ +} + +const Array AiffAudioFormat::getPossibleSampleRates() +{ + const int rates[] = { 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000, 0 }; + return Array (rates); +} + +const Array AiffAudioFormat::getPossibleBitDepths() +{ + const int depths[] = { 8, 16, 24, 0 }; + return Array (depths); +} + +bool AiffAudioFormat::canDoStereo() +{ + return true; +} + +bool AiffAudioFormat::canDoMono() +{ + return true; +} + +#if JUCE_MAC +bool AiffAudioFormat::canHandleFile (const File& f) +{ + if (AudioFormat::canHandleFile (f)) + return true; + + const OSType type = PlatformUtilities::getTypeOfFile (f.getFullPathName()); + return type == 'AIFF' || type == 'AIFC' + || type == 'aiff' || type == 'aifc'; +} +#endif + +AudioFormatReader* AiffAudioFormat::createReaderFor (InputStream* sourceStream, + const bool deleteStreamIfOpeningFails) +{ + AiffAudioFormatReader* w = new AiffAudioFormatReader (sourceStream); + + if (w->sampleRate == 0) + { + if (! deleteStreamIfOpeningFails) + w->input = 0; + + deleteAndZero (w); + } + + return w; +} + +AudioFormatWriter* AiffAudioFormat::createWriterFor (OutputStream* out, + double sampleRate, + unsigned int chans, + int bitsPerSample, + const StringPairArray& /*metadataValues*/, + int /*qualityOptionIndex*/) +{ + if (getPossibleBitDepths().contains (bitsPerSample)) + { + return new AiffAudioFormatWriter (out, + sampleRate, + chans, + bitsPerSample); + } + + return 0; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_AiffAudioFormat.cpp *********/ + +/********* Start of inlined file: juce_AudioCDReader.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +#if JUCE_MAC + +// Mac version doesn't need any native code because it's all done with files.. +// Windows + Linux versions are in the platform-dependent code sections. + +static void findCDs (OwnedArray& cds) +{ + File volumes ("/Volumes"); + volumes.findChildFiles (cds, File::findDirectories, false); + + for (int i = cds.size(); --i >= 0;) + if (! cds[i]->getChildFile (".TOC.plist").exists()) + cds.remove (i); +} + +const StringArray AudioCDReader::getAvailableCDNames() +{ + OwnedArray cds; + findCDs (cds); + + StringArray names; + + for (int i = 0; i < cds.size(); ++i) + names.add (cds[i]->getFileName()); + + return names; +} + +AudioCDReader* AudioCDReader::createReaderForCD (const int index) +{ + OwnedArray cds; + findCDs (cds); + + if (cds[index] != 0) + return new AudioCDReader (*cds[index]); + else + return 0; +} + +AudioCDReader::AudioCDReader (const File& volume) + : AudioFormatReader (0, "CD Audio"), + volumeDir (volume), + currentReaderTrack (-1), + reader (0) +{ + sampleRate = 44100.0; + bitsPerSample = 16; + numChannels = 2; + usesFloatingPointData = false; + + refreshTrackLengths(); +} + +AudioCDReader::~AudioCDReader() +{ + if (reader != 0) + delete reader; +} + +static int getTrackNumber (const File& file) +{ + return file.getFileName() + .initialSectionContainingOnly (T("0123456789")) + .getIntValue(); +} + +int AudioCDReader::compareElements (const File* const first, const File* const second) throw() +{ + const int firstTrack = getTrackNumber (*first); + const int secondTrack = getTrackNumber (*second); + + jassert (firstTrack > 0 && secondTrack > 0); + + return firstTrack - secondTrack; +} + +void AudioCDReader::refreshTrackLengths() +{ + tracks.clear(); + trackStartSamples.clear(); + volumeDir.findChildFiles (tracks, File::findFiles | File::ignoreHiddenFiles, false, T("*.aiff")); + + tracks.sort (*this); + + AiffAudioFormat format; + int sample = 0; + + for (int i = 0; i < tracks.size(); ++i) + { + trackStartSamples.add (sample); + + FileInputStream* const in = tracks[i]->createInputStream(); + + if (in != 0) + { + AudioFormatReader* const r = format.createReaderFor (in, true); + + if (r != 0) + { + sample += r->lengthInSamples; + delete r; + } + } + } + + trackStartSamples.add (sample); + lengthInSamples = sample; +} + +bool AudioCDReader::read (int** destSamples, + int64 startSampleInFile, + int numSamples) +{ + while (numSamples > 0) + { + int track = -1; + + for (int i = 0; i < trackStartSamples.size() - 1; ++i) + { + if (startSampleInFile < trackStartSamples.getUnchecked (i + 1)) + { + track = i; + break; + } + } + + if (track < 0) + return false; + + if (track != currentReaderTrack) + { + deleteAndZero (reader); + + if (tracks [track] != 0) + { + FileInputStream* const in = tracks [track]->createInputStream(); + + if (in != 0) + { + BufferedInputStream* const bin = new BufferedInputStream (in, 65536, true); + + AiffAudioFormat format; + reader = format.createReaderFor (bin, true); + + if (reader == 0) + currentReaderTrack = -1; + else + currentReaderTrack = track; + } + } + } + + if (reader == 0) + return false; + + const int startPos = (int) (startSampleInFile - trackStartSamples.getUnchecked (track)); + const int numAvailable = (int) jmin ((int64) numSamples, reader->lengthInSamples - startPos); + + reader->read (destSamples, startPos, numAvailable); + + numSamples -= numAvailable; + startSampleInFile += numAvailable; + } + + return true; +} + +bool AudioCDReader::isCDStillPresent() const +{ + return volumeDir.exists(); +} + +int AudioCDReader::getNumTracks() const +{ + return tracks.size(); +} + +int AudioCDReader::getPositionOfTrackStart (int trackNum) const +{ + return trackStartSamples [trackNum]; +} + +bool AudioCDReader::isTrackAudio (int trackNum) const +{ + return tracks [trackNum] != 0; +} + +void AudioCDReader::enableIndexScanning (bool b) +{ + // any way to do this on a Mac?? +} + +int AudioCDReader::getLastIndex() const +{ + return 0; +} + +const Array AudioCDReader::findIndexesInTrack (const int trackNumber) +{ + return Array (); +} + +int AudioCDReader::getCDDBId() +{ + return 0; //xxx +} + +#endif + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_AudioCDReader.cpp *********/ + +/********* Start of inlined file: juce_AudioFormat.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +AudioFormatReader::AudioFormatReader (InputStream* const in, + const String& formatName_) + : sampleRate (0), + bitsPerSample (0), + lengthInSamples (0), + numChannels (0), + usesFloatingPointData (false), + input (in), + formatName (formatName_) +{ +} + +AudioFormatReader::~AudioFormatReader() +{ + delete input; +} + +static void findMaxMin (const float* src, const int num, + float& maxVal, float& minVal) +{ + float mn = src[0]; + float mx = mn; + + for (int i = 1; i < num; ++i) + { + const float s = src[i]; + if (s > mx) + mx = s; + if (s < mn) + mn = s; + } + + maxVal = mx; + minVal = mn; +} + +void AudioFormatReader::readMaxLevels (int64 startSampleInFile, + int64 numSamples, + float& lowestLeft, float& highestLeft, + float& lowestRight, float& highestRight) +{ + if (numSamples <= 0) + { + lowestLeft = 0; + lowestRight = 0; + highestLeft = 0; + highestRight = 0; + return; + } + + const int bufferSize = (int) jmin (numSamples, (int64) 4096); + MemoryBlock tempSpace (bufferSize * sizeof (int) * 2 + 64); + + int* tempBuffer[3]; + tempBuffer[0] = (int*) tempSpace.getData(); + tempBuffer[1] = ((int*) tempSpace.getData()) + bufferSize; + tempBuffer[2] = 0; + + if (usesFloatingPointData) + { + float lmin = 1.0e6; + float lmax = -lmin; + float rmin = lmin; + float rmax = lmax; + + while (numSamples > 0) + { + const int numToDo = (int) jmin (numSamples, (int64) bufferSize); + read ((int**) tempBuffer, startSampleInFile, numToDo); + + numSamples -= numToDo; + + float bufmin, bufmax; + findMaxMin ((float*) tempBuffer[0], numToDo, bufmax, bufmin); + lmin = jmin (lmin, bufmin); + lmax = jmax (lmax, bufmax); + + if (numChannels > 1) + { + findMaxMin ((float*) tempBuffer[1], numToDo, bufmax, bufmin); + rmin = jmin (rmin, bufmin); + rmax = jmax (rmax, bufmax); + } + } + + if (numChannels <= 1) + { + rmax = lmax; + rmin = lmin; + } + + lowestLeft = lmin; + highestLeft = lmax; + lowestRight = rmin; + highestRight = rmax; + } + else + { + int lmax = INT_MIN; + int lmin = INT_MAX; + int rmax = INT_MIN; + int rmin = INT_MAX; + + while (numSamples > 0) + { + const int numToDo = (int) jmin (numSamples, (int64) bufferSize); + read ((int**) tempBuffer, startSampleInFile, numToDo); + + numSamples -= numToDo; + + for (int j = numChannels; --j >= 0;) + { + int bufMax = INT_MIN; + int bufMin = INT_MAX; + + const int* const b = tempBuffer[j]; + + for (int i = 0; i < numToDo; ++i) + { + const int samp = b[i]; + + if (samp < bufMin) + bufMin = samp; + + if (samp > bufMax) + bufMax = samp; + } + + if (j == 0) + { + lmax = jmax (lmax, bufMax); + lmin = jmin (lmin, bufMin); + } + else + { + rmax = jmax (rmax, bufMax); + rmin = jmin (rmin, bufMin); + } + } + } + + if (numChannels <= 1) + { + rmax = lmax; + rmin = lmin; + } + + lowestLeft = lmin / (float)INT_MAX; + highestLeft = lmax / (float)INT_MAX; + lowestRight = rmin / (float)INT_MAX; + highestRight = rmax / (float)INT_MAX; + } +} + +int64 AudioFormatReader::searchForLevel (int64 startSample, + int64 numSamplesToSearch, + const double magnitudeRangeMinimum, + const double magnitudeRangeMaximum, + const int minimumConsecutiveSamples) +{ + if (numSamplesToSearch == 0) + return -1; + + const int bufferSize = 4096; + MemoryBlock tempSpace (bufferSize * sizeof (int) * 2 + 64); + + int* tempBuffer[3]; + tempBuffer[0] = (int*) tempSpace.getData(); + tempBuffer[1] = ((int*) tempSpace.getData()) + bufferSize; + tempBuffer[2] = 0; + + int consecutive = 0; + int64 firstMatchPos = -1; + + jassert (magnitudeRangeMaximum > magnitudeRangeMinimum); + + const double doubleMin = jlimit (0.0, (double) INT_MAX, magnitudeRangeMinimum * INT_MAX); + const double doubleMax = jlimit (doubleMin, (double) INT_MAX, magnitudeRangeMaximum * INT_MAX); + const int intMagnitudeRangeMinimum = roundDoubleToInt (doubleMin); + const int intMagnitudeRangeMaximum = roundDoubleToInt (doubleMax); + + while (numSamplesToSearch != 0) + { + const int numThisTime = (int) jmin (abs64 (numSamplesToSearch), (int64) bufferSize); + int64 bufferStart = startSample; + + if (numSamplesToSearch < 0) + bufferStart -= numThisTime; + + if (bufferStart >= (int) lengthInSamples) + break; + + read ((int**) tempBuffer, bufferStart, numThisTime); + + int num = numThisTime; + while (--num >= 0) + { + if (numSamplesToSearch < 0) + --startSample; + + bool matches = false; + const int index = (int) (startSample - bufferStart); + + if (usesFloatingPointData) + { + const float sample1 = fabsf (((float*) tempBuffer[0]) [index]); + + if (sample1 >= magnitudeRangeMinimum + && sample1 <= magnitudeRangeMaximum) + { + matches = true; + } + else if (numChannels > 1) + { + const float sample2 = fabsf (((float*) tempBuffer[1]) [index]); + + matches = (sample2 >= magnitudeRangeMinimum + && sample2 <= magnitudeRangeMaximum); + } + } + else + { + const int sample1 = abs (tempBuffer[0] [index]); + + if (sample1 >= intMagnitudeRangeMinimum + && sample1 <= intMagnitudeRangeMaximum) + { + matches = true; + } + else if (numChannels > 1) + { + const int sample2 = abs (tempBuffer[1][index]); + + matches = (sample2 >= intMagnitudeRangeMinimum + && sample2 <= intMagnitudeRangeMaximum); + } + } + + if (matches) + { + if (firstMatchPos < 0) + firstMatchPos = startSample; + + if (++consecutive >= minimumConsecutiveSamples) + { + if (firstMatchPos < 0 || firstMatchPos >= lengthInSamples) + return -1; + + return firstMatchPos; + } + } + else + { + consecutive = 0; + firstMatchPos = -1; + } + + if (numSamplesToSearch > 0) + ++startSample; + } + + if (numSamplesToSearch > 0) + numSamplesToSearch -= numThisTime; + else + numSamplesToSearch += numThisTime; + } + + return -1; +} + +AudioFormatWriter::AudioFormatWriter (OutputStream* const out, + const String& formatName_, + const double rate, + const unsigned int numChannels_, + const unsigned int bitsPerSample_) + : sampleRate (rate), + numChannels (numChannels_), + bitsPerSample (bitsPerSample_), + usesFloatingPointData (false), + output (out), + formatName (formatName_) +{ +} + +AudioFormatWriter::~AudioFormatWriter() +{ + delete output; +} + +bool AudioFormatWriter::writeFromAudioReader (AudioFormatReader& reader, + int64 startSample, + int numSamplesToRead) +{ + const int bufferSize = 16384; + const int maxChans = 128; + AudioSampleBuffer tempBuffer (reader.numChannels, bufferSize); + int* buffers [maxChans]; + + for (int i = maxChans; --i >= 0;) + buffers[i] = 0; + + while (numSamplesToRead > 0) + { + const int numToDo = jmin (numSamplesToRead, bufferSize); + + for (int i = tempBuffer.getNumChannels(); --i >= 0;) + buffers[i] = (int*) tempBuffer.getSampleData (i, 0); + + if (! reader.read (buffers, startSample, numToDo)) + return false; + + if (reader.usesFloatingPointData != isFloatingPoint()) + { + int** bufferChan = buffers; + + while (*bufferChan != 0) + { + int* b = *bufferChan++; + + if (isFloatingPoint()) + { + // int -> float + const double factor = 1.0 / INT_MAX; + + for (int i = 0; i < numToDo; ++i) + ((float*)b)[i] = (float) (factor * b[i]); + } + else + { + // float -> int + for (int i = 0; i < numToDo; ++i) + { + const double samp = *(const float*) b; + + if (samp <= -1.0) + *b++ = INT_MIN; + else if (samp >= 1.0) + *b++ = INT_MAX; + else + *b++ = roundDoubleToInt (INT_MAX * samp); + } + } + } + } + + if (! write ((const int**) buffers, numToDo)) + return false; + + numSamplesToRead -= numToDo; + startSample += numToDo; + } + + return true; +} + +bool AudioFormatWriter::writeFromAudioSource (AudioSource& source, + int numSamplesToRead, + const int samplesPerBlock) +{ + const int maxChans = 128; + AudioSampleBuffer tempBuffer (getNumChannels(), samplesPerBlock); + int* buffers [maxChans]; + + while (numSamplesToRead > 0) + { + const int numToDo = jmin (numSamplesToRead, samplesPerBlock); + + AudioSourceChannelInfo info; + info.buffer = &tempBuffer; + info.startSample = 0; + info.numSamples = numToDo; + info.clearActiveBufferRegion(); + + source.getNextAudioBlock (info); + + int i; + for (i = maxChans; --i >= 0;) + buffers[i] = 0; + + for (i = tempBuffer.getNumChannels(); --i >= 0;) + buffers[i] = (int*) tempBuffer.getSampleData (i, 0); + + if (! isFloatingPoint()) + { + int** bufferChan = buffers; + + while (*bufferChan != 0) + { + int* b = *bufferChan++; + + // float -> int + for (int j = numToDo; --j >= 0;) + { + const double samp = *(const float*) b; + + if (samp <= -1.0) + *b++ = INT_MIN; + else if (samp >= 1.0) + *b++ = INT_MAX; + else + *b++ = roundDoubleToInt (INT_MAX * samp); + } + } + } + + if (! write ((const int**) buffers, numToDo)) + return false; + + numSamplesToRead -= numToDo; + } + + return true; +} + +AudioFormat::AudioFormat (const String& name, + const tchar** const extensions) + : formatName (name), + fileExtensions (extensions) +{ +} + +AudioFormat::~AudioFormat() +{ +} + +const String& AudioFormat::getFormatName() const +{ + return formatName; +} + +const StringArray& AudioFormat::getFileExtensions() const +{ + return fileExtensions; +} + +bool AudioFormat::canHandleFile (const File& f) +{ + for (int i = 0; i < fileExtensions.size(); ++i) + if (f.hasFileExtension (fileExtensions[i])) + return true; + + return false; +} + +bool AudioFormat::isCompressed() +{ + return false; +} + +const StringArray AudioFormat::getQualityOptions() +{ + return StringArray(); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_AudioFormat.cpp *********/ + +/********* Start of inlined file: juce_AudioFormatManager.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +AudioFormatManager::AudioFormatManager() + : knownFormats (4), + defaultFormatIndex (0) +{ +} + +AudioFormatManager::~AudioFormatManager() +{ + clearFormats(); + clearSingletonInstance(); +} + +juce_ImplementSingleton (AudioFormatManager); + +void AudioFormatManager::registerFormat (AudioFormat* newFormat, + const bool makeThisTheDefaultFormat) +{ + jassert (newFormat != 0); + + if (newFormat != 0) + { +#ifdef JUCE_DEBUG + for (int i = getNumKnownFormats(); --i >= 0;) + { + if (getKnownFormat (i)->getFormatName() == newFormat->getFormatName()) + { + jassertfalse // trying to add the same format twice! + } + } +#endif + + if (makeThisTheDefaultFormat) + defaultFormatIndex = knownFormats.size(); + + knownFormats.add (newFormat); + } +} + +void AudioFormatManager::registerBasicFormats() +{ +#if JUCE_MAC + registerFormat (new AiffAudioFormat(), true); + registerFormat (new WavAudioFormat(), false); +#else + registerFormat (new WavAudioFormat(), true); + registerFormat (new AiffAudioFormat(), false); +#endif + +#if JUCE_USE_FLAC + registerFormat (new FlacAudioFormat(), false); +#endif + +#if JUCE_USE_OGGVORBIS + registerFormat (new OggVorbisAudioFormat(), false); +#endif +} + +void AudioFormatManager::clearFormats() +{ + for (int i = getNumKnownFormats(); --i >= 0;) + { + AudioFormat* const af = getKnownFormat(i); + delete af; + } + + knownFormats.clear(); + defaultFormatIndex = 0; +} + +int AudioFormatManager::getNumKnownFormats() const +{ + return knownFormats.size(); +} + +AudioFormat* AudioFormatManager::getKnownFormat (const int index) const +{ + return (AudioFormat*) knownFormats [index]; +} + +AudioFormat* AudioFormatManager::getDefaultFormat() const +{ + return getKnownFormat (defaultFormatIndex); +} + +AudioFormat* AudioFormatManager::findFormatForFileExtension (const String& fileExtension) const +{ + String e (fileExtension); + if (! e.startsWithChar (T('.'))) + e = T(".") + e; + + for (int i = 0; i < getNumKnownFormats(); ++i) + if (getKnownFormat(i)->getFileExtensions().contains (e, true)) + return getKnownFormat(i); + + return 0; +} + +const String AudioFormatManager::getWildcardForAllFormats() const +{ + StringArray allExtensions; + + int i; + for (i = 0; i < getNumKnownFormats(); ++i) + allExtensions.addArray (getKnownFormat (i)->getFileExtensions()); + + allExtensions.trim(); + allExtensions.removeEmptyStrings(); + + String s; + for (i = 0; i < allExtensions.size(); ++i) + { + s << T('*'); + + if (! allExtensions[i].startsWithChar (T('.'))) + s << T('.'); + + s << allExtensions[i]; + + if (i < allExtensions.size() - 1) + s << T(';'); + } + + return s; +} + +AudioFormatReader* AudioFormatManager::createReaderFor (const File& file) +{ + // you need to actually register some formats before the manager can + // use them to open a file! + jassert (knownFormats.size() > 0); + + for (int i = 0; i < getNumKnownFormats(); ++i) + { + AudioFormat* const af = getKnownFormat(i); + + if (af->canHandleFile (file)) + { + InputStream* const in = file.createInputStream(); + + if (in != 0) + { + AudioFormatReader* const r = af->createReaderFor (in, true); + + if (r != 0) + return r; + } + } + } + + return 0; +} + +AudioFormatReader* AudioFormatManager::createReaderFor (InputStream* in) +{ + // you need to actually register some formats before the manager can + // use them to open a file! + jassert (knownFormats.size() > 0); + + if (in != 0) + { + const int64 originalStreamPos = in->getPosition(); + + for (int i = 0; i < getNumKnownFormats(); ++i) + { + AudioFormatReader* const r = getKnownFormat(i)->createReaderFor (in, false); + + if (r != 0) + return r; + + in->setPosition (originalStreamPos); + + // the stream that is passed-in must be capable of being repositioned so + // that all the formats can have a go at opening it. + jassert (in->getPosition() == originalStreamPos); + } + + delete in; + } + + return 0; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_AudioFormatManager.cpp *********/ + +/********* Start of inlined file: juce_AudioSubsectionReader.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +AudioSubsectionReader::AudioSubsectionReader (AudioFormatReader* const source_, + const int64 startSample_, + const int64 length_, + const bool deleteSourceWhenDeleted_) + : AudioFormatReader (0, source_->getFormatName()), + source (source_), + startSample (startSample_), + deleteSourceWhenDeleted (deleteSourceWhenDeleted_) +{ + length = jmin (jmax ((int64) 0, source->lengthInSamples - startSample), length_); + + sampleRate = source->sampleRate; + bitsPerSample = source->bitsPerSample; + lengthInSamples = length; + numChannels = source->numChannels; + usesFloatingPointData = source->usesFloatingPointData; +} + +AudioSubsectionReader::~AudioSubsectionReader() +{ + if (deleteSourceWhenDeleted) + delete source; +} + +bool AudioSubsectionReader::read (int** destSamples, + int64 startSampleInFile, + int numSamples) +{ + if (startSampleInFile < 0 || startSampleInFile + numSamples > length) + { + int** d = destSamples; + while (*d != 0) + { + zeromem (*d, sizeof (int) * numSamples); + ++d; + } + + startSampleInFile = jmax ((int64) 0, startSampleInFile); + numSamples = jmax (0, jmin (numSamples, (int) (length - startSampleInFile))); + } + + return source->read (destSamples, + startSampleInFile + startSample, + numSamples); +} + +void AudioSubsectionReader::readMaxLevels (int64 startSampleInFile, + int64 numSamples, + float& lowestLeft, + float& highestLeft, + float& lowestRight, + float& highestRight) +{ + startSampleInFile = jmax ((int64) 0, startSampleInFile); + numSamples = jmax ((int64) 0, jmin (numSamples, length - startSampleInFile)); + + source->readMaxLevels (startSampleInFile + startSample, + numSamples, + lowestLeft, + highestLeft, + lowestRight, + highestRight); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_AudioSubsectionReader.cpp *********/ + +/********* Start of inlined file: juce_AudioThumbnail.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +const int timeBeforeDeletingReader = 2000; + +struct AudioThumbnailDataFormat +{ + char thumbnailMagic[4]; + int samplesPerThumbSample; + int64 totalSamples; // source samples + int64 numFinishedSamples; // source samples + int numThumbnailSamples; + int numChannels; + int sampleRate; + char future[16]; + char data[1]; +}; + +#if JUCE_BIG_ENDIAN + static void swap (int& n) { n = (int) swapByteOrder ((uint32) n); } + static void swap (int64& n) { n = (int64) swapByteOrder ((uint64) n); } +#endif + +static void swapEndiannessIfNeeded (AudioThumbnailDataFormat* const d) +{ + (void) d; + +#if JUCE_BIG_ENDIAN + swap (d->samplesPerThumbSample); + swap (d->totalSamples); + swap (d->numFinishedSamples); + swap (d->numThumbnailSamples); + swap (d->numChannels); + swap (d->sampleRate); +#endif +} + +AudioThumbnail::AudioThumbnail (const int orginalSamplesPerThumbnailSample_, + AudioFormatManager& formatManagerToUse_, + AudioThumbnailCache& cacheToUse) + : formatManagerToUse (formatManagerToUse_), + cache (cacheToUse), + source (0), + reader (0), + orginalSamplesPerThumbnailSample (orginalSamplesPerThumbnailSample_) +{ + clear(); +} + +AudioThumbnail::~AudioThumbnail() +{ + cache.removeThumbnail (this); + + const ScopedLock sl (readerLock); + deleteAndZero (reader); + + delete source; +} + +void AudioThumbnail::setSource (InputSource* const newSource) +{ + cache.removeThumbnail (this); + timerCallback(); // stops the timer and deletes the reader + + delete source; + source = newSource; + + clear(); + + if (! (cache.loadThumb (*this, newSource->hashCode()) + && isFullyLoaded())) + { + { + const ScopedLock sl (readerLock); + reader = createReader(); + } + + if (reader != 0) + { + initialiseFromAudioFile (*reader); + cache.addThumbnail (this); + } + } + + sendChangeMessage (this); +} + +bool AudioThumbnail::useTimeSlice() +{ + const ScopedLock sl (readerLock); + + if (isFullyLoaded()) + { + if (reader != 0) + startTimer (timeBeforeDeletingReader); + + cache.removeThumbnail (this); + return false; + } + + if (reader == 0) + reader = createReader(); + + if (reader != 0) + { + readNextBlockFromAudioFile (*reader); + stopTimer(); + + sendChangeMessage (this); + + const bool justFinished = isFullyLoaded(); + + if (justFinished) + cache.storeThumb (*this, source->hashCode()); + + return ! justFinished; + } + + return false; +} + +AudioFormatReader* AudioThumbnail::createReader() const +{ + if (source != 0) + { + InputStream* const audioFileStream = source->createInputStream(); + + if (audioFileStream != 0) + return formatManagerToUse.createReaderFor (audioFileStream); + } + + return 0; +} + +void AudioThumbnail::timerCallback() +{ + stopTimer(); + + const ScopedLock sl (readerLock); + deleteAndZero (reader); +} + +void AudioThumbnail::clear() +{ + data.setSize (sizeof (AudioThumbnailDataFormat) + 3); + + AudioThumbnailDataFormat* const d = (AudioThumbnailDataFormat*) data.getData(); + + d->thumbnailMagic[0] = 'j'; + d->thumbnailMagic[1] = 'a'; + d->thumbnailMagic[2] = 't'; + d->thumbnailMagic[3] = 'm'; + + d->samplesPerThumbSample = orginalSamplesPerThumbnailSample; + d->totalSamples = 0; + d->numFinishedSamples = 0; + d->numThumbnailSamples = 0; + d->numChannels = 0; + d->sampleRate = 0; + + numSamplesCached = 0; + cacheNeedsRefilling = true; +} + +void AudioThumbnail::loadFrom (InputStream& input) +{ + data.setSize (0); + input.readIntoMemoryBlock (data); + + AudioThumbnailDataFormat* const d = (AudioThumbnailDataFormat*) data.getData(); + swapEndiannessIfNeeded (d); + + if (! (d->thumbnailMagic[0] == 'j' + && d->thumbnailMagic[1] == 'a' + && d->thumbnailMagic[2] == 't' + && d->thumbnailMagic[3] == 'm')) + { + clear(); + } + + numSamplesCached = 0; + cacheNeedsRefilling = true; +} + +void AudioThumbnail::saveTo (OutputStream& output) const +{ + AudioThumbnailDataFormat* const d = (AudioThumbnailDataFormat*) data.getData(); + swapEndiannessIfNeeded (d); + output.write (data.getData(), data.getSize()); + swapEndiannessIfNeeded (d); +} + +bool AudioThumbnail::initialiseFromAudioFile (AudioFormatReader& reader) +{ + AudioThumbnailDataFormat* d = (AudioThumbnailDataFormat*) data.getData(); + + d->totalSamples = reader.lengthInSamples; + d->numChannels = jmin (2, reader.numChannels); + d->numFinishedSamples = 0; + d->sampleRate = roundDoubleToInt (reader.sampleRate); + d->numThumbnailSamples = (int) (d->totalSamples / d->samplesPerThumbSample) + 1; + + data.setSize (sizeof (AudioThumbnailDataFormat) + 3 + d->numThumbnailSamples * d->numChannels * 2); + + d = (AudioThumbnailDataFormat*) data.getData(); + zeromem (&(d->data[0]), d->numThumbnailSamples * d->numChannels * 2); + + return d->totalSamples > 0; +} + +bool AudioThumbnail::readNextBlockFromAudioFile (AudioFormatReader& reader) +{ + AudioThumbnailDataFormat* const d = (AudioThumbnailDataFormat*) data.getData(); + + if (d->numFinishedSamples < d->totalSamples) + { + const int numToDo = (int) jmin ((int64) 65536, d->totalSamples - d->numFinishedSamples); + + generateSection (reader, + d->numFinishedSamples, + numToDo); + + d->numFinishedSamples += numToDo; + } + + cacheNeedsRefilling = true; + return (d->numFinishedSamples < d->totalSamples); +} + +int AudioThumbnail::getNumChannels() const throw() +{ + const AudioThumbnailDataFormat* const d = (const AudioThumbnailDataFormat*) data.getData(); + jassert (d != 0); + + return d->numChannels; +} + +double AudioThumbnail::getTotalLength() const throw() +{ + const AudioThumbnailDataFormat* const d = (const AudioThumbnailDataFormat*) data.getData(); + jassert (d != 0); + + if (d->sampleRate > 0) + return d->totalSamples / (double)d->sampleRate; + else + return 0.0; +} + +void AudioThumbnail::generateSection (AudioFormatReader& reader, + int64 startSample, + int numSamples) +{ + AudioThumbnailDataFormat* const d = (AudioThumbnailDataFormat*) data.getData(); + jassert (d != 0); + + int firstDataPos = (int) (startSample / d->samplesPerThumbSample); + int lastDataPos = (int) ((startSample + numSamples) / d->samplesPerThumbSample); + + char* l = getChannelData (0); + char* r = getChannelData (1); + + for (int i = firstDataPos; i < lastDataPos; ++i) + { + const int sourceStart = i * d->samplesPerThumbSample; + const int sourceEnd = sourceStart + d->samplesPerThumbSample; + + float lowestLeft, highestLeft, lowestRight, highestRight; + + reader.readMaxLevels (sourceStart, + sourceEnd - sourceStart, + lowestLeft, + highestLeft, + lowestRight, + highestRight); + + int n = i * 2; + + if (r != 0) + { + l [n] = (char) jlimit (-128.0f, 127.0f, lowestLeft * 127.0f); + r [n++] = (char) jlimit (-128.0f, 127.0f, lowestRight * 127.0f); + l [n] = (char) jlimit (-128.0f, 127.0f, highestLeft * 127.0f); + r [n++] = (char) jlimit (-128.0f, 127.0f, highestRight * 127.0f); + } + else + { + l [n++] = (char) jlimit (-128.0f, 127.0f, lowestLeft * 127.0f); + l [n++] = (char) jlimit (-128.0f, 127.0f, highestLeft * 127.0f); + } + } +} + +char* AudioThumbnail::getChannelData (int channel) const +{ + AudioThumbnailDataFormat* const d = (AudioThumbnailDataFormat*) data.getData(); + jassert (d != 0); + + if (channel >= 0 && channel < d->numChannels) + return d->data + (channel * 2 * d->numThumbnailSamples); + + return 0; +} + +bool AudioThumbnail::isFullyLoaded() const throw() +{ + const AudioThumbnailDataFormat* const d = (const AudioThumbnailDataFormat*) data.getData(); + jassert (d != 0); + + return d->numFinishedSamples >= d->totalSamples; +} + +void AudioThumbnail::refillCache (const int numSamples, + double startTime, + const double timePerPixel) +{ + const AudioThumbnailDataFormat* const d = (const AudioThumbnailDataFormat*) data.getData(); + jassert (d != 0); + + if (numSamples <= 0 + || timePerPixel <= 0.0 + || d->sampleRate <= 0) + { + numSamplesCached = 0; + cacheNeedsRefilling = true; + return; + } + + if (numSamples == numSamplesCached + && numChannelsCached == d->numChannels + && startTime == cachedStart + && timePerPixel == cachedTimePerPixel + && ! cacheNeedsRefilling) + { + return; + } + + numSamplesCached = numSamples; + numChannelsCached = d->numChannels; + cachedStart = startTime; + cachedTimePerPixel = timePerPixel; + + cachedLevels.ensureSize (2 * numChannelsCached * numSamples); + + const bool needExtraDetail = (timePerPixel * d->sampleRate <= d->samplesPerThumbSample); + + const ScopedLock sl (readerLock); + + cacheNeedsRefilling = false; + + if (needExtraDetail && reader == 0) + reader = createReader(); + + if (reader != 0 && timePerPixel * d->sampleRate <= d->samplesPerThumbSample) + { + startTimer (timeBeforeDeletingReader); + + char* cacheData = (char*) cachedLevels.getData(); + int sample = roundDoubleToInt (startTime * d->sampleRate); + + for (int i = numSamples; --i >= 0;) + { + const int nextSample = roundDoubleToInt ((startTime + timePerPixel) * d->sampleRate); + + if (sample >= 0) + { + if (sample >= reader->lengthInSamples) + break; + + float lmin, lmax, rmin, rmax; + + reader->readMaxLevels (sample, + jmax (1, nextSample - sample), + lmin, lmax, rmin, rmax); + + cacheData[0] = (char) jlimit (-128, 127, roundFloatToInt (lmin * 127.0f)); + cacheData[1] = (char) jlimit (-128, 127, roundFloatToInt (lmax * 127.0f)); + + if (numChannelsCached > 1) + { + cacheData[2] = (char) jlimit (-128, 127, roundFloatToInt (rmin * 127.0f)); + cacheData[3] = (char) jlimit (-128, 127, roundFloatToInt (rmax * 127.0f)); + } + + cacheData += 2 * numChannelsCached; + } + + startTime += timePerPixel; + sample = nextSample; + } + } + else + { + for (int channelNum = 0; channelNum < numChannelsCached; ++channelNum) + { + char* const data = getChannelData (channelNum); + char* cacheData = ((char*) cachedLevels.getData()) + channelNum * 2; + + const double timeToThumbSampleFactor = d->sampleRate / (double) d->samplesPerThumbSample; + + startTime = cachedStart; + int sample = roundDoubleToInt (startTime * timeToThumbSampleFactor); + const int numFinished = (int) (d->numFinishedSamples / d->samplesPerThumbSample); + + for (int i = numSamples; --i >= 0;) + { + const int nextSample = roundDoubleToInt ((startTime + timePerPixel) * timeToThumbSampleFactor); + + if (sample >= 0 && data != 0) + { + char mx = -128; + char mn = 127; + + while (sample <= nextSample) + { + if (sample >= numFinished) + break; + + const int n = sample << 1; + const char sampMin = data [n]; + const char sampMax = data [n + 1]; + + if (sampMin < mn) + mn = sampMin; + + if (sampMax > mx) + mx = sampMax; + + ++sample; + } + + if (mn <= mx) + { + cacheData[0] = mn; + cacheData[1] = mx; + } + else + { + cacheData[0] = 1; + cacheData[1] = 0; + } + } + else + { + cacheData[0] = 1; + cacheData[1] = 0; + } + + cacheData += numChannelsCached * 2; + startTime += timePerPixel; + sample = nextSample; + } + } + } +} + +void AudioThumbnail::drawChannel (Graphics& g, + int x, int y, int w, int h, + double startTime, + double endTime, + int channelNum, + const float verticalZoomFactor) +{ + refillCache (w, startTime, (endTime - startTime) / w); + + if (numSamplesCached >= w + && channelNum >= 0 + && channelNum < numChannelsCached) + { + const float topY = (float) y; + const float bottomY = topY + h; + const float midY = topY + h * 0.5f; + const float vscale = verticalZoomFactor * h / 256.0f; + + const Rectangle clip (g.getClipBounds()); + const int skipLeft = clip.getX() - x; + w -= skipLeft; + x += skipLeft; + + const char* cacheData = ((const char*) cachedLevels.getData()) + + (channelNum << 1) + + skipLeft * (numChannelsCached << 1); + + while (--w >= 0) + { + const char mn = cacheData[0]; + const char mx = cacheData[1]; + cacheData += numChannelsCached << 1; + + if (mn <= mx) // if the wrong way round, signifies that the sample's not yet known + g.drawLine ((float) x, jmax (midY - mx * vscale - 0.3f, topY), + (float) x, jmin (midY - mn * vscale + 0.3f, bottomY)); + + ++x; + + if (x >= clip.getRight()) + break; + } + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_AudioThumbnail.cpp *********/ + +/********* Start of inlined file: juce_AudioThumbnailCache.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +struct ThumbnailCacheEntry +{ + int64 hash; + uint32 lastUsed; + MemoryBlock data; + + juce_UseDebuggingNewOperator +}; + +AudioThumbnailCache::AudioThumbnailCache (const int maxNumThumbsToStore_) + : TimeSliceThread (T("thumb cache")), + maxNumThumbsToStore (maxNumThumbsToStore_) +{ + startThread (2); +} + +AudioThumbnailCache::~AudioThumbnailCache() +{ +} + +bool AudioThumbnailCache::loadThumb (AudioThumbnail& thumb, const int64 hashCode) +{ + for (int i = thumbs.size(); --i >= 0;) + { + if (thumbs[i]->hash == hashCode) + { + MemoryInputStream in ((const char*) thumbs[i]->data.getData(), + thumbs[i]->data.getSize(), + false); + + thumb.loadFrom (in); + + thumbs[i]->lastUsed = Time::getMillisecondCounter(); + return true; + } + } + + return false; +} + +void AudioThumbnailCache::storeThumb (const AudioThumbnail& thumb, + const int64 hashCode) +{ + MemoryOutputStream out; + thumb.saveTo (out); + + ThumbnailCacheEntry* te = 0; + + for (int i = thumbs.size(); --i >= 0;) + { + if (thumbs[i]->hash == hashCode) + { + te = thumbs[i]; + break; + } + } + + if (te == 0) + { + te = new ThumbnailCacheEntry(); + te->hash = hashCode; + + if (thumbs.size() < maxNumThumbsToStore) + { + thumbs.add (te); + } + else + { + int oldest = 0; + unsigned int oldestTime = Time::getMillisecondCounter() + 1; + + int i; + for (i = thumbs.size(); --i >= 0;) + if (thumbs[i]->lastUsed < oldestTime) + oldest = i; + + thumbs.set (i, te); + } + } + + te->lastUsed = Time::getMillisecondCounter(); + te->data.setSize (0); + te->data.append (out.getData(), out.getDataSize()); +} + +void AudioThumbnailCache::clear() +{ + thumbs.clear(); +} + +void AudioThumbnailCache::addThumbnail (AudioThumbnail* const thumb) +{ + addTimeSliceClient (thumb); +} + +void AudioThumbnailCache::removeThumbnail (AudioThumbnail* const thumb) +{ + removeTimeSliceClient (thumb); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_AudioThumbnailCache.cpp *********/ + +/********* Start of inlined file: juce_QuickTimeAudioFormat.cpp *********/ + +#if JUCE_QUICKTIME + +#if ! defined (_WIN32) + #include + #include + #include + #include + #include +#else + #ifdef _MSC_VER + #pragma warning (push) + #pragma warning (disable : 4100) + #endif + + /* If you've got an include error here, you probably need to install the QuickTime SDK and + add its header directory to your include path. + + Alternatively, if you don't need any QuickTime services, just turn off the JUC_QUICKTIME + flag in juce_Config.h + */ + #include + #include + #include + #include + #include + + #ifdef _MSC_VER + #pragma warning (pop) + #endif +#endif + +BEGIN_JUCE_NAMESPACE + +bool juce_OpenQuickTimeMovieFromStream (InputStream* input, Movie& movie, Handle& dataHandle); + +#define quickTimeFormatName TRANS("QuickTime file") +static const tchar* const quickTimeExtensions[] = { T(".mov"), T(".mp3"), T(".mp4"), 0 }; + +class QTAudioReader : public AudioFormatReader +{ +public: + QTAudioReader (InputStream* const input_, const int trackNum_) + : AudioFormatReader (input_, quickTimeFormatName), + ok (false), + movie (0), + trackNum (trackNum_), + extractor (0), + lastSampleRead (0), + lastThreadId (0), + dataHandle (0) + { + bufferList = (AudioBufferList*) juce_calloc (256); + +#ifdef WIN32 + if (InitializeQTML (0) != noErr) + return; +#endif + if (EnterMovies() != noErr) + return; + +#if JUCE_MAC + EnterMoviesOnThread (0); +#endif + + bool opened = juce_OpenQuickTimeMovieFromStream (input_, movie, dataHandle); + + if (! opened) + return; + + { + const int numTracks = GetMovieTrackCount (movie); + int trackCount = 0; + + for (int i = 1; i <= numTracks; ++i) + { + track = GetMovieIndTrack (movie, i); + media = GetTrackMedia (track); + + OSType mediaType; + GetMediaHandlerDescription (media, &mediaType, 0, 0); + + if (mediaType == SoundMediaType + && trackCount++ == trackNum_) + { + ok = true; + break; + } + } + } + + if (! ok) + return; + + ok = false; + + lengthInSamples = GetMediaDecodeDuration (media); + usesFloatingPointData = false; + + samplesPerFrame = (int) (GetMediaDecodeDuration (media) / GetMediaSampleCount (media)); + + trackUnitsPerFrame = GetMovieTimeScale (movie) * samplesPerFrame + / GetMediaTimeScale (media); + + OSStatus err = MovieAudioExtractionBegin (movie, 0, &extractor); + + unsigned long output_layout_size; + err = MovieAudioExtractionGetPropertyInfo (extractor, + kQTPropertyClass_MovieAudioExtraction_Audio, + kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout, + 0, &output_layout_size, 0); + if (err != noErr) + return; + + AudioChannelLayout* const qt_audio_channel_layout + = (AudioChannelLayout*) juce_calloc (output_layout_size); + + err = MovieAudioExtractionGetProperty (extractor, + kQTPropertyClass_MovieAudioExtraction_Audio, + kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout, + output_layout_size, qt_audio_channel_layout, 0); + + qt_audio_channel_layout->mChannelLayoutTag = kAudioChannelLayoutTag_Stereo; + + err = MovieAudioExtractionSetProperty (extractor, + kQTPropertyClass_MovieAudioExtraction_Audio, + kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout, + sizeof (qt_audio_channel_layout), + qt_audio_channel_layout); + + juce_free (qt_audio_channel_layout); + + err = MovieAudioExtractionGetProperty (extractor, + kQTPropertyClass_MovieAudioExtraction_Audio, + kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription, + sizeof (inputStreamDesc), + &inputStreamDesc, 0); + if (err != noErr) + return; + + inputStreamDesc.mFormatFlags = kAudioFormatFlagIsSignedInteger + | kAudioFormatFlagIsPacked + | kAudioFormatFlagsNativeEndian; + inputStreamDesc.mBitsPerChannel = sizeof (SInt16) * 8; + inputStreamDesc.mChannelsPerFrame = jmin (2, inputStreamDesc.mChannelsPerFrame); + inputStreamDesc.mBytesPerFrame = sizeof (SInt16) * inputStreamDesc.mChannelsPerFrame; + inputStreamDesc.mBytesPerPacket = inputStreamDesc.mBytesPerFrame; + + err = MovieAudioExtractionSetProperty (extractor, + kQTPropertyClass_MovieAudioExtraction_Audio, + kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription, + sizeof (inputStreamDesc), + &inputStreamDesc); + if (err != noErr) + return; + + Boolean allChannelsDiscrete = false; + err = MovieAudioExtractionSetProperty (extractor, + kQTPropertyClass_MovieAudioExtraction_Movie, + kQTMovieAudioExtractionMoviePropertyID_AllChannelsDiscrete, + sizeof (allChannelsDiscrete), + &allChannelsDiscrete); + + if (err != noErr) + return; + + bufferList->mNumberBuffers = 1; + bufferList->mBuffers[0].mNumberChannels = inputStreamDesc.mChannelsPerFrame; + bufferList->mBuffers[0].mDataByteSize = (UInt32) (samplesPerFrame * inputStreamDesc.mBytesPerFrame) + 16; + bufferList->mBuffers[0].mData = malloc (bufferList->mBuffers[0].mDataByteSize); + + sampleRate = inputStreamDesc.mSampleRate; + bitsPerSample = 16; + numChannels = inputStreamDesc.mChannelsPerFrame; + + detachThread(); + ok = true; + } + + ~QTAudioReader() + { + if (dataHandle != 0) + DisposeHandle (dataHandle); + + if (extractor != 0) + { + MovieAudioExtractionEnd (extractor); + extractor = 0; + } + + checkThreadIsAttached(); + DisposeMovie (movie); + + juce_free (bufferList->mBuffers[0].mData); + juce_free (bufferList); + } + + bool read (int** destSamples, + int64 startSample, + int numSamples) + { + checkThreadIsAttached(); + int done = 0; + + while (numSamples > 0) + { + if (! loadFrame ((int) startSample)) + return false; + + const int numToDo = jmin (numSamples, samplesPerFrame); + + for (unsigned int j = 0; j < inputStreamDesc.mChannelsPerFrame; ++j) + { + if (destSamples[j] != 0) + { + const short* const src = ((const short*) bufferList->mBuffers[0].mData) + j; + + for (int i = 0; i < numToDo; ++i) + destSamples[j][done + i] = src [i << 1] << 16; + } + } + + done += numToDo; + startSample += numToDo; + numSamples -= numToDo; + } + + detachThread(); + return true; + } + + bool loadFrame (const int sampleNum) + { + if (lastSampleRead != sampleNum) + { + TimeRecord time; + time.scale = (TimeScale) inputStreamDesc.mSampleRate; + time.base = 0; + time.value.hi = 0; + time.value.lo = (UInt32) sampleNum; + + OSStatus err = MovieAudioExtractionSetProperty (extractor, + kQTPropertyClass_MovieAudioExtraction_Movie, + kQTMovieAudioExtractionMoviePropertyID_CurrentTime, + sizeof (time), &time); + + if (err != noErr) + return false; + } + + bufferList->mBuffers[0].mDataByteSize = inputStreamDesc.mBytesPerFrame * samplesPerFrame; + + UInt32 outFlags = 0; + UInt32 actualNumSamples = samplesPerFrame; + OSStatus err = MovieAudioExtractionFillBuffer (extractor, &actualNumSamples, + bufferList, &outFlags); + + lastSampleRead = sampleNum + samplesPerFrame; + + return err == noErr; + } + + juce_UseDebuggingNewOperator + + bool ok; + +private: + Movie movie; + Media media; + Track track; + const int trackNum; + double trackUnitsPerFrame; + int samplesPerFrame; + int lastSampleRead, lastThreadId; + MovieAudioExtractionRef extractor; + AudioStreamBasicDescription inputStreamDesc; + AudioBufferList* bufferList; + Handle dataHandle; + + /*OSErr readMovieStream (long offset, long size, void* dataPtr) + { + input->setPosition (offset); + input->read (dataPtr, size); + return noErr; + } + + static OSErr readMovieStreamProc (long offset, long size, void* dataPtr, void* userRef) + { + return ((QTAudioReader*) userRef)->readMovieStream (offset, size, dataPtr); + }*/ + + void checkThreadIsAttached() + { +#if JUCE_MAC + if (Thread::getCurrentThreadId() != lastThreadId) + EnterMoviesOnThread (0); + AttachMovieToCurrentThread (movie); +#endif + } + + void detachThread() + { +#if JUCE_MAC + DetachMovieFromCurrentThread (movie); +#endif + } +}; + +QuickTimeAudioFormat::QuickTimeAudioFormat() + : AudioFormat (quickTimeFormatName, (const tchar**) quickTimeExtensions) +{ +} + +QuickTimeAudioFormat::~QuickTimeAudioFormat() +{ +} + +const Array QuickTimeAudioFormat::getPossibleSampleRates() +{ + return Array(); +} + +const Array QuickTimeAudioFormat::getPossibleBitDepths() +{ + return Array(); +} + +bool QuickTimeAudioFormat::canDoStereo() +{ + return true; +} + +bool QuickTimeAudioFormat::canDoMono() +{ + return true; +} + +AudioFormatReader* QuickTimeAudioFormat::createReaderFor (InputStream* sourceStream, + const bool deleteStreamIfOpeningFails) +{ + QTAudioReader* r = new QTAudioReader (sourceStream, 0); + + if (! r->ok) + { + if (! deleteStreamIfOpeningFails) + r->input = 0; + + deleteAndZero (r); + } + + return r; +} + +AudioFormatWriter* QuickTimeAudioFormat::createWriterFor (OutputStream* /*streamToWriteTo*/, + double /*sampleRateToUse*/, + unsigned int /*numberOfChannels*/, + int /*bitsPerSample*/, + const StringPairArray& /*metadataValues*/, + int /*qualityOptionIndex*/) +{ + jassertfalse // not yet implemented! + return 0; +} + +END_JUCE_NAMESPACE + +#endif +/********* End of inlined file: juce_QuickTimeAudioFormat.cpp *********/ + +/********* Start of inlined file: juce_WavAudioFormat.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +#define wavFormatName TRANS("WAV file") +static const tchar* const wavExtensions[] = { T(".wav"), T(".bwf"), 0 }; + +const tchar* const WavAudioFormat::bwavDescription = T("bwav description"); +const tchar* const WavAudioFormat::bwavOriginator = T("bwav originator"); +const tchar* const WavAudioFormat::bwavOriginatorRef = T("bwav originator ref"); +const tchar* const WavAudioFormat::bwavOriginationDate = T("bwav origination date"); +const tchar* const WavAudioFormat::bwavOriginationTime = T("bwav origination time"); +const tchar* const WavAudioFormat::bwavTimeReference = T("bwav time reference"); +const tchar* const WavAudioFormat::bwavCodingHistory = T("bwav coding history"); + +const StringPairArray WavAudioFormat::createBWAVMetadata (const String& description, + const String& originator, + const String& originatorRef, + const Time& date, + const int64 timeReferenceSamples, + const String& codingHistory) +{ + StringPairArray m; + + m.set (bwavDescription, description); + m.set (bwavOriginator, originator); + m.set (bwavOriginatorRef, originatorRef); + m.set (bwavOriginationDate, date.formatted (T("%Y-%m-%d"))); + m.set (bwavOriginationTime, date.formatted (T("%H:%M:%S"))); + m.set (bwavTimeReference, String (timeReferenceSamples)); + m.set (bwavCodingHistory, codingHistory); + + return m; +} + +#if JUCE_MSVC + #pragma pack (push, 1) + #define PACKED +#elif defined (JUCE_GCC) + #define PACKED __attribute__((packed)) +#else + #define PACKED +#endif + +struct BWAVChunk +{ + char description [256]; + char originator [32]; + char originatorRef [32]; + char originationDate [10]; + char originationTime [8]; + uint32 timeRefLow; + uint32 timeRefHigh; + uint16 version; + uint8 umid[64]; + uint8 reserved[190]; + char codingHistory[1]; + + void copyTo (StringPairArray& values) const + { + values.set (WavAudioFormat::bwavDescription, String (description, 256)); + values.set (WavAudioFormat::bwavOriginator, String (originator, 32)); + values.set (WavAudioFormat::bwavOriginatorRef, String (originatorRef, 32)); + values.set (WavAudioFormat::bwavOriginationDate, String (originationDate, 10)); + values.set (WavAudioFormat::bwavOriginationTime, String (originationTime, 8)); + + const uint32 timeLow = swapIfBigEndian (timeRefLow); + const uint32 timeHigh = swapIfBigEndian (timeRefHigh); + const int64 time = (((int64)timeHigh) << 32) + timeLow; + + values.set (WavAudioFormat::bwavTimeReference, String (time)); + values.set (WavAudioFormat::bwavCodingHistory, String (codingHistory)); + } + + static MemoryBlock createFrom (const StringPairArray& values) + { + const int sizeNeeded = sizeof (BWAVChunk) + values [WavAudioFormat::bwavCodingHistory].length(); + MemoryBlock data ((sizeNeeded + 3) & ~3); + data.fillWith (0); + + BWAVChunk* b = (BWAVChunk*) data.getData(); + + // although copyToBuffer may overrun by one byte, that's ok as long as these + // operations get done in the right order + values [WavAudioFormat::bwavDescription].copyToBuffer (b->description, 256); + values [WavAudioFormat::bwavOriginator].copyToBuffer (b->originator, 32); + values [WavAudioFormat::bwavOriginatorRef].copyToBuffer (b->originatorRef, 32); + values [WavAudioFormat::bwavOriginationDate].copyToBuffer (b->originationDate, 10); + values [WavAudioFormat::bwavOriginationTime].copyToBuffer (b->originationTime, 8); + + const int64 time = values [WavAudioFormat::bwavTimeReference].getLargeIntValue(); + b->timeRefLow = swapIfBigEndian ((uint32) (time & 0xffffffff)); + b->timeRefHigh = swapIfBigEndian ((uint32) (time >> 32)); + + values [WavAudioFormat::bwavCodingHistory].copyToBuffer (b->codingHistory, 256 * 1024); + + if (b->description[0] != 0 + || b->originator[0] != 0 + || b->originationDate[0] != 0 + || b->originationTime[0] != 0 + || b->codingHistory[0] != 0 + || time != 0) + { + return data; + } + + return MemoryBlock(); + } + +} PACKED; + +#if JUCE_MSVC + #pragma pack (pop) +#endif + +#undef PACKED + +#undef chunkName +#define chunkName(a) ((int) littleEndianInt(a)) + +class WavAudioFormatReader : public AudioFormatReader +{ + int bytesPerFrame; + int64 dataChunkStart, dataLength; + + WavAudioFormatReader (const WavAudioFormatReader&); + const WavAudioFormatReader& operator= (const WavAudioFormatReader&); + +public: + + WavAudioFormatReader (InputStream* const in) + : AudioFormatReader (in, wavFormatName), + dataLength (0) + { + if (input->readInt() == chunkName ("RIFF")) + { + const uint32 len = (uint32) input->readInt(); + const int64 end = input->getPosition() + len; + bool hasGotType = false; + bool hasGotData = false; + + if (input->readInt() == chunkName ("WAVE")) + { + while (input->getPosition() < end + && ! input->isExhausted()) + { + const int chunkType = input->readInt(); + uint32 length = (uint32) input->readInt(); + const int64 chunkEnd = input->getPosition() + length + (length & 1); + + if (chunkType == chunkName ("fmt ")) + { + // read the format chunk + const short format = input->readShort(); + const short numChans = input->readShort(); + sampleRate = input->readInt(); + const int bytesPerSec = input->readInt(); + + numChannels = numChans; + bytesPerFrame = bytesPerSec / (int)sampleRate; + bitsPerSample = 8 * bytesPerFrame / numChans; + + if (format == 3) + usesFloatingPointData = true; + else if (format != 1) + bytesPerFrame = 0; + + hasGotType = true; + } + else if (chunkType == chunkName ("data")) + { + // get the data chunk's position + dataLength = length; + dataChunkStart = input->getPosition(); + lengthInSamples = (bytesPerFrame > 0) ? (dataLength / bytesPerFrame) : 0; + + hasGotData = true; + } + else if (chunkType == chunkName ("bext")) + { + // Broadcast-wav extension chunk.. + BWAVChunk* const bwav = (BWAVChunk*) juce_calloc (jmax (length + 1, (int) sizeof (BWAVChunk))); + + if (bwav != 0) + { + input->read (bwav, length); + bwav->copyTo (metadataValues); + juce_free (bwav); + } + } + else if ((hasGotType && hasGotData) || chunkEnd <= input->getPosition()) + { + break; + } + + input->setPosition (chunkEnd); + } + } + } + } + + ~WavAudioFormatReader() + { + } + + bool read (int** destSamples, + int64 startSampleInFile, + int numSamples) + { + int64 start = startSampleInFile; + int startOffsetInDestBuffer = 0; + + if (startSampleInFile < 0) + { + const int silence = (int) jmin (-startSampleInFile, (int64) numSamples); + + int** destChan = destSamples; + + for (int i = 2; --i >= 0;) + { + if (*destChan != 0) + { + zeromem (*destChan, sizeof (int) * silence); + ++destChan; + } + } + + startOffsetInDestBuffer += silence; + numSamples -= silence; + start = 0; + } + + const int numToDo = (int) jlimit ((int64) 0, (int64) numSamples, lengthInSamples - start); + + if (numToDo > 0) + { + input->setPosition (dataChunkStart + start * bytesPerFrame); + + int num = numToDo; + int* left = destSamples[0]; + if (left != 0) + left += startOffsetInDestBuffer; + + int* right = destSamples[1]; + if (right != 0) + right += startOffsetInDestBuffer; + + // (keep this a multiple of 3) + const int tempBufSize = 1440 * 4; + char tempBuffer [tempBufSize]; + + while (num > 0) + { + const int numThisTime = jmin (tempBufSize / bytesPerFrame, num); + const int bytesRead = input->read (tempBuffer, numThisTime * bytesPerFrame); + + if (bytesRead < numThisTime * bytesPerFrame) + zeromem (tempBuffer + bytesRead, numThisTime * bytesPerFrame - bytesRead); + + if (bitsPerSample == 16) + { + const short* src = (const short*) tempBuffer; + + if (numChannels > 1) + { + if (left == 0) + { + for (int i = numThisTime; --i >= 0;) + { + ++src; + *right++ = (int) swapIfBigEndian ((unsigned short) *src++) << 16; + } + } + else if (right == 0) + { + for (int i = numThisTime; --i >= 0;) + { + *left++ = (int) swapIfBigEndian ((unsigned short) *src++) << 16; + ++src; + } + } + else + { + for (int i = numThisTime; --i >= 0;) + { + *left++ = (int) swapIfBigEndian ((unsigned short) *src++) << 16; + *right++ = (int) swapIfBigEndian ((unsigned short) *src++) << 16; + } + } + } + else + { + for (int i = numThisTime; --i >= 0;) + { + *left++ = (int) swapIfBigEndian ((unsigned short) *src++) << 16; + } + } + } + else if (bitsPerSample == 24) + { + const char* src = (const char*) tempBuffer; + + if (numChannels > 1) + { + if (left == 0) + { + for (int i = numThisTime; --i >= 0;) + { + src += 6; + *right++ = littleEndian24Bit (src) << 8; + } + } + else if (right == 0) + { + for (int i = numThisTime; --i >= 0;) + { + *left++ = littleEndian24Bit (src) << 8; + src += 6; + } + } + else + { + for (int i = 0; i < numThisTime; ++i) + { + *left++ = littleEndian24Bit (src) << 8; + src += 3; + *right++ = littleEndian24Bit (src) << 8; + src += 3; + } + } + } + else + { + for (int i = 0; i < numThisTime; ++i) + { + *left++ = littleEndian24Bit (src) << 8; + src += 3; + } + } + } + else if (bitsPerSample == 32) + { + const unsigned int* src = (const unsigned int*) tempBuffer; + unsigned int* l = (unsigned int*) left; + unsigned int* r = (unsigned int*) right; + + if (numChannels > 1) + { + if (l == 0) + { + for (int i = numThisTime; --i >= 0;) + { + ++src; + *r++ = swapIfBigEndian (*src++); + } + } + else if (r == 0) + { + for (int i = numThisTime; --i >= 0;) + { + *l++ = swapIfBigEndian (*src++); + ++src; + } + } + else + { + for (int i = numThisTime; --i >= 0;) + { + *l++ = swapIfBigEndian (*src++); + *r++ = swapIfBigEndian (*src++); + } + } + } + else + { + for (int i = numThisTime; --i >= 0;) + { + *l++ = swapIfBigEndian (*src++); + } + } + + left = (int*)l; + right = (int*)r; + } + else if (bitsPerSample == 8) + { + const unsigned char* src = (const unsigned char*) tempBuffer; + + if (numChannels > 1) + { + if (left == 0) + { + for (int i = numThisTime; --i >= 0;) + { + ++src; + *right++ = ((int) *src++ - 128) << 24; + } + } + else if (right == 0) + { + for (int i = numThisTime; --i >= 0;) + { + *left++ = ((int) *src++ - 128) << 24; + ++src; + } + } + else + { + for (int i = numThisTime; --i >= 0;) + { + *left++ = ((int) *src++ - 128) << 24; + *right++ = ((int) *src++ - 128) << 24; + } + } + } + else + { + for (int i = numThisTime; --i >= 0;) + { + *left++ = ((int)*src++ - 128) << 24; + } + } + } + + num -= numThisTime; + } + } + + if (numToDo < numSamples) + { + int** destChan = destSamples; + + while (*destChan != 0) + { + zeromem ((*destChan) + (startOffsetInDestBuffer + numToDo), + sizeof (int) * (numSamples - numToDo)); + ++destChan; + } + } + + return true; + } + + juce_UseDebuggingNewOperator +}; + +class WavAudioFormatWriter : public AudioFormatWriter +{ + MemoryBlock tempBlock, bwavChunk; + uint32 lengthInSamples, bytesWritten; + int64 headerPosition; + bool writeFailed; + + WavAudioFormatWriter (const WavAudioFormatWriter&); + const WavAudioFormatWriter& operator= (const WavAudioFormatWriter&); + + void writeHeader() + { + const bool seekedOk = output->setPosition (headerPosition); + (void) seekedOk; + + // if this fails, you've given it an output stream that can't seek! It needs + // to be able to seek back to write the header + jassert (seekedOk); + + const int bytesPerFrame = numChannels * bitsPerSample / 8; + output->writeInt (chunkName ("RIFF")); + output->writeInt (lengthInSamples * bytesPerFrame + + ((bwavChunk.getSize() > 0) ? (44 + bwavChunk.getSize()) : 36)); + + output->writeInt (chunkName ("WAVE")); + output->writeInt (chunkName ("fmt ")); + output->writeInt (16); + output->writeShort ((bitsPerSample < 32) ? (short) 1 /*WAVE_FORMAT_PCM*/ + : (short) 3 /*WAVE_FORMAT_IEEE_FLOAT*/); + output->writeShort ((short) numChannels); + output->writeInt ((int) sampleRate); + output->writeInt (bytesPerFrame * (int) sampleRate); + output->writeShort ((short) bytesPerFrame); + output->writeShort ((short) bitsPerSample); + + if (bwavChunk.getSize() > 0) + { + output->writeInt (chunkName ("bext")); + output->writeInt (bwavChunk.getSize()); + output->write (bwavChunk.getData(), bwavChunk.getSize()); + } + + output->writeInt (chunkName ("data")); + output->writeInt (lengthInSamples * bytesPerFrame); + + usesFloatingPointData = (bitsPerSample == 32); + } + +public: + + WavAudioFormatWriter (OutputStream* const out, + const double sampleRate, + const unsigned int numChannels_, + const int bits, + const StringPairArray& metadataValues) + : AudioFormatWriter (out, + wavFormatName, + sampleRate, + numChannels_, + bits), + lengthInSamples (0), + bytesWritten (0), + writeFailed (false) + { + if (metadataValues.size() > 0) + bwavChunk = BWAVChunk::createFrom (metadataValues); + + headerPosition = out->getPosition(); + writeHeader(); + } + + ~WavAudioFormatWriter() + { + writeHeader(); + } + + bool write (const int** data, int numSamples) + { + if (writeFailed) + return false; + + const int bytes = numChannels * numSamples * bitsPerSample / 8; + tempBlock.ensureSize (bytes, false); + char* buffer = (char*) tempBlock.getData(); + + const int* left = data[0]; + const int* right = data[1]; + if (right == 0) + right = left; + + if (bitsPerSample == 16) + { + short* b = (short*) buffer; + + if (numChannels > 1) + { + for (int i = numSamples; --i >= 0;) + { + *b++ = (short) swapIfBigEndian ((unsigned short) (*left++ >> 16)); + *b++ = (short) swapIfBigEndian ((unsigned short) (*right++ >> 16)); + } + } + else + { + for (int i = numSamples; --i >= 0;) + { + *b++ = (short) swapIfBigEndian ((unsigned short) (*left++ >> 16)); + } + } + } + else if (bitsPerSample == 24) + { + char* b = (char*) buffer; + + if (numChannels > 1) + { + for (int i = numSamples; --i >= 0;) + { + littleEndian24BitToChars ((*left++) >> 8, b); + b += 3; + littleEndian24BitToChars ((*right++) >> 8, b); + b += 3; + } + } + else + { + for (int i = numSamples; --i >= 0;) + { + littleEndian24BitToChars ((*left++) >> 8, b); + b += 3; + } + } + } + else if (bitsPerSample == 32) + { + unsigned int* b = (unsigned int*) buffer; + + if (numChannels > 1) + { + for (int i = numSamples; --i >= 0;) + { + *b++ = swapIfBigEndian ((unsigned int) *left++); + *b++ = swapIfBigEndian ((unsigned int) *right++); + } + } + else + { + for (int i = numSamples; --i >= 0;) + { + *b++ = swapIfBigEndian ((unsigned int) *left++); + } + } + } + else if (bitsPerSample == 8) + { + unsigned char* b = (unsigned char*) buffer; + + if (numChannels > 1) + { + for (int i = numSamples; --i >= 0;) + { + *b++ = (unsigned char) (128 + (*left++ >> 24)); + *b++ = (unsigned char) (128 + (*right++ >> 24)); + } + } + else + { + for (int i = numSamples; --i >= 0;) + { + *b++ = (unsigned char) (128 + (*left++ >> 24)); + } + } + } + + if (bytesWritten + bytes >= (uint32) 0xfff00000 + || ! output->write (buffer, bytes)) + { + // failed to write to disk, so let's try writing the header. + // If it's just run out of disk space, then if it does manage + // to write the header, we'll still have a useable file.. + writeHeader(); + writeFailed = true; + return false; + } + else + { + bytesWritten += bytes; + lengthInSamples += numSamples; + + return true; + } + } + + juce_UseDebuggingNewOperator +}; + +WavAudioFormat::WavAudioFormat() + : AudioFormat (wavFormatName, (const tchar**) wavExtensions) +{ +} + +WavAudioFormat::~WavAudioFormat() +{ +} + +const Array WavAudioFormat::getPossibleSampleRates() +{ + const int rates[] = { 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000, 0 }; + return Array (rates); +} + +const Array WavAudioFormat::getPossibleBitDepths() +{ + const int depths[] = { 8, 16, 24, 32, 0 }; + return Array (depths); +} + +bool WavAudioFormat::canDoStereo() +{ + return true; +} + +bool WavAudioFormat::canDoMono() +{ + return true; +} + +AudioFormatReader* WavAudioFormat::createReaderFor (InputStream* sourceStream, + const bool deleteStreamIfOpeningFails) +{ + WavAudioFormatReader* r = new WavAudioFormatReader (sourceStream); + + if (r->sampleRate == 0) + { + if (! deleteStreamIfOpeningFails) + r->input = 0; + + deleteAndZero (r); + } + + return r; +} + +AudioFormatWriter* WavAudioFormat::createWriterFor (OutputStream* out, + double sampleRate, + unsigned int numChannels, + int bitsPerSample, + const StringPairArray& metadataValues, + int /*qualityOptionIndex*/) +{ + if (getPossibleBitDepths().contains (bitsPerSample)) + { + return new WavAudioFormatWriter (out, + sampleRate, + numChannels, + bitsPerSample, + metadataValues); + } + + return 0; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_WavAudioFormat.cpp *********/ + +/********* Start of inlined file: juce_AudioFormatReaderSource.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +AudioFormatReaderSource::AudioFormatReaderSource (AudioFormatReader* const reader_, + const bool deleteReaderWhenThisIsDeleted) + : reader (reader_), + deleteReader (deleteReaderWhenThisIsDeleted), + nextPlayPos (0), + looping (false) +{ + jassert (reader != 0); +} + +AudioFormatReaderSource::~AudioFormatReaderSource() +{ + releaseResources(); + + if (deleteReader) + delete reader; +} + +void AudioFormatReaderSource::setNextReadPosition (int newPosition) +{ + nextPlayPos = newPosition; +} + +void AudioFormatReaderSource::setLooping (const bool shouldLoop) throw() +{ + looping = shouldLoop; +} + +int AudioFormatReaderSource::getNextReadPosition() const +{ + return (looping) ? (nextPlayPos % (int) reader->lengthInSamples) + : nextPlayPos; +} + +int AudioFormatReaderSource::getTotalLength() const +{ + return (int) reader->lengthInSamples; +} + +void AudioFormatReaderSource::prepareToPlay (int /*samplesPerBlockExpected*/, + double /*sampleRate*/) +{ +} + +void AudioFormatReaderSource::releaseResources() +{ +} + +void AudioFormatReaderSource::getNextAudioBlock (const AudioSourceChannelInfo& info) +{ + if (info.numSamples > 0) + { + const int start = nextPlayPos; + + if (looping) + { + const int newStart = start % (int) reader->lengthInSamples; + const int newEnd = (start + info.numSamples) % (int) reader->lengthInSamples; + + if (newEnd > newStart) + { + info.buffer->readFromAudioReader (reader, + info.startSample, + newEnd - newStart, + newStart, + true, true); + } + else + { + const int endSamps = (int) reader->lengthInSamples - newStart; + + info.buffer->readFromAudioReader (reader, + info.startSample, + endSamps, + newStart, + true, true); + + info.buffer->readFromAudioReader (reader, + info.startSample + endSamps, + newEnd, + 0, + true, true); + } + + nextPlayPos = newEnd; + } + else + { + info.buffer->readFromAudioReader (reader, + info.startSample, + info.numSamples, + start, + true, true); + + nextPlayPos += info.numSamples; + } + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_AudioFormatReaderSource.cpp *********/ + +/********* Start of inlined file: juce_AudioSourcePlayer.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +AudioSourcePlayer::AudioSourcePlayer() + : source (0), + sampleRate (0), + bufferSize (0), + tempBuffer (2, 8) +{ +} + +AudioSourcePlayer::~AudioSourcePlayer() +{ + setSource (0); +} + +void AudioSourcePlayer::setSource (AudioSource* newSource) +{ + if (source != newSource) + { + AudioSource* const oldSource = source; + + if (newSource != 0 && bufferSize > 0 && sampleRate > 0) + newSource->prepareToPlay (bufferSize, sampleRate); + + { + const ScopedLock sl (readLock); + source = newSource; + } + + if (oldSource != 0) + oldSource->releaseResources(); + } +} + +void AudioSourcePlayer::audioDeviceIOCallback (const float** inputChannelData, + int totalNumInputChannels, + float** outputChannelData, + int totalNumOutputChannels, + int numSamples) +{ + // these should have been prepared by audioDeviceAboutToStart()... + jassert (sampleRate > 0 && bufferSize > 0); + + const ScopedLock sl (readLock); + + if (source != 0) + { + AudioSourceChannelInfo info; + int i, numActiveChans = 0, numInputs = 0, numOutputs = 0; + + // messy stuff needed to compact the channels down into an array + // of non-zero pointers.. + for (i = 0; i < totalNumInputChannels; ++i) + { + if (inputChannelData[i] != 0) + { + inputChans [numInputs++] = inputChannelData[i]; + if (numInputs >= numElementsInArray (inputChans)) + break; + } + } + + for (i = 0; i < totalNumOutputChannels; ++i) + { + if (outputChannelData[i] != 0) + { + outputChans [numOutputs++] = outputChannelData[i]; + if (numOutputs >= numElementsInArray (outputChans)) + break; + } + } + + if (numInputs > numOutputs) + { + // if there aren't enough output channels for the number of + // inputs, we need to create some temporary extra ones (can't + // use the input data in case it gets written to) + tempBuffer.setSize (numInputs - numOutputs, numSamples, + false, false, true); + + for (i = 0; i < numOutputs; ++i) + { + channels[numActiveChans] = outputChans[i]; + memcpy (channels[numActiveChans], inputChans[i], sizeof (float) * numSamples); + ++numActiveChans; + } + + for (i = numOutputs; i < numInputs; ++i) + { + channels[numActiveChans] = tempBuffer.getSampleData (i - numOutputs, 0); + memcpy (channels[numActiveChans], inputChans[i], sizeof (float) * numSamples); + ++numActiveChans; + } + } + else + { + for (i = 0; i < numInputs; ++i) + { + channels[numActiveChans] = outputChans[i]; + memcpy (channels[numActiveChans], inputChans[i], sizeof (float) * numSamples); + ++numActiveChans; + } + + for (i = numInputs; i < numOutputs; ++i) + { + channels[numActiveChans] = outputChans[i]; + zeromem (channels[numActiveChans], sizeof (float) * numSamples); + ++numActiveChans; + } + } + + AudioSampleBuffer buffer (channels, numActiveChans, numSamples); + + info.buffer = &buffer; + info.startSample = 0; + info.numSamples = numSamples; + + source->getNextAudioBlock (info); + } + else + { + for (int i = 0; i < totalNumOutputChannels; ++i) + if (outputChannelData[i] != 0) + zeromem (outputChannelData[i], sizeof (float) * numSamples); + } +} + +void AudioSourcePlayer::audioDeviceAboutToStart (AudioIODevice* device) +{ + sampleRate = device->getCurrentSampleRate(); + bufferSize = device->getCurrentBufferSizeSamples(); + zeromem (channels, sizeof (channels)); + + if (source != 0) + source->prepareToPlay (bufferSize, sampleRate); +} + +void AudioSourcePlayer::audioDeviceStopped() +{ + if (source != 0) + source->releaseResources(); + + sampleRate = 0.0; + bufferSize = 0; + + tempBuffer.setSize (2, 8); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_AudioSourcePlayer.cpp *********/ + +/********* Start of inlined file: juce_AudioTransportSource.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +AudioTransportSource::AudioTransportSource() + : source (0), + resamplerSource (0), + bufferingSource (0), + positionableSource (0), + masterSource (0), + gain (1.0f), + lastGain (1.0f), + playing (false), + stopped (true), + sampleRate (44100.0), + sourceSampleRate (0.0), + blockSize (128), + readAheadBufferSize (0), + isPrepared (false), + inputStreamEOF (false) +{ +} + +AudioTransportSource::~AudioTransportSource() +{ + setSource (0); + + releaseResources(); +} + +void AudioTransportSource::setSource (PositionableAudioSource* const newSource, + int readAheadBufferSize_, + double sourceSampleRateToCorrectFor) +{ + if (source == newSource) + { + if (source == 0) + return; + + setSource (0, 0, 0); // deselect and reselect to avoid releasing resources wrongly + } + + readAheadBufferSize = readAheadBufferSize_; + sourceSampleRate = sourceSampleRateToCorrectFor; + + ResamplingAudioSource* newResamplerSource = 0; + BufferingAudioSource* newBufferingSource = 0; + PositionableAudioSource* newPositionableSource = 0; + AudioSource* newMasterSource = 0; + + ResamplingAudioSource* oldResamplerSource = resamplerSource; + BufferingAudioSource* oldBufferingSource = bufferingSource; + AudioSource* oldMasterSource = masterSource; + + if (newSource != 0) + { + newPositionableSource = newSource; + + if (readAheadBufferSize_ > 0) + newPositionableSource = newBufferingSource + = new BufferingAudioSource (newPositionableSource, false, readAheadBufferSize_); + + newPositionableSource->setNextReadPosition (0); + + if (sourceSampleRateToCorrectFor != 0) + newMasterSource = newResamplerSource + = new ResamplingAudioSource (newPositionableSource, false); + else + newMasterSource = newPositionableSource; + + if (isPrepared) + { + if (newResamplerSource != 0 && sourceSampleRate > 0 && sampleRate > 0) + newResamplerSource->setResamplingRatio (sourceSampleRate / sampleRate); + + newMasterSource->prepareToPlay (blockSize, sampleRate); + } + } + + { + const ScopedLock sl (callbackLock); + + source = newSource; + resamplerSource = newResamplerSource; + bufferingSource = newBufferingSource; + masterSource = newMasterSource; + positionableSource = newPositionableSource; + + playing = false; + } + + if (oldMasterSource != 0) + oldMasterSource->releaseResources(); + + if (oldResamplerSource != 0) + delete oldResamplerSource; + + if (oldBufferingSource != 0) + delete oldBufferingSource; +} + +void AudioTransportSource::start() +{ + if ((! playing) && masterSource != 0) + { + callbackLock.enter(); + playing = true; + stopped = false; + inputStreamEOF = false; + callbackLock.exit(); + + sendChangeMessage (this); + } +} + +void AudioTransportSource::stop() +{ + if (playing) + { + callbackLock.enter(); + playing = false; + callbackLock.exit(); + + int n = 500; + while (--n >= 0 && ! stopped) + Thread::sleep (2); + + sendChangeMessage (this); + } +} + +void AudioTransportSource::setPosition (double newPosition) +{ + if (sampleRate > 0.0) + setNextReadPosition (roundDoubleToInt (newPosition * sampleRate)); +} + +double AudioTransportSource::getCurrentPosition() const +{ + if (sampleRate > 0.0) + return getNextReadPosition() / sampleRate; + else + return 0.0; +} + +void AudioTransportSource::setNextReadPosition (int newPosition) +{ + if (positionableSource != 0) + { + if (sampleRate > 0 && sourceSampleRate > 0) + newPosition = roundDoubleToInt (newPosition * sourceSampleRate / sampleRate); + + positionableSource->setNextReadPosition (newPosition); + } +} + +int AudioTransportSource::getNextReadPosition() const +{ + if (positionableSource != 0) + { + const double ratio = (sampleRate > 0 && sourceSampleRate > 0) ? sampleRate / sourceSampleRate : 1.0; + + return roundDoubleToInt (positionableSource->getNextReadPosition() * ratio); + } + + return 0; +} + +int AudioTransportSource::getTotalLength() const +{ + const ScopedLock sl (callbackLock); + + if (positionableSource != 0) + { + const double ratio = (sampleRate > 0 && sourceSampleRate > 0) ? sampleRate / sourceSampleRate : 1.0; + + return roundDoubleToInt (positionableSource->getTotalLength() * ratio); + } + + return 0; +} + +bool AudioTransportSource::isLooping() const +{ + const ScopedLock sl (callbackLock); + + return positionableSource != 0 + && positionableSource->isLooping(); +} + +void AudioTransportSource::setGain (const float newGain) throw() +{ + gain = newGain; +} + +void AudioTransportSource::prepareToPlay (int samplesPerBlockExpected, + double sampleRate_) +{ + const ScopedLock sl (callbackLock); + + sampleRate = sampleRate_; + blockSize = samplesPerBlockExpected; + + if (masterSource != 0) + masterSource->prepareToPlay (samplesPerBlockExpected, sampleRate); + + if (resamplerSource != 0 && sourceSampleRate != 0) + resamplerSource->setResamplingRatio (sourceSampleRate / sampleRate); + + isPrepared = true; +} + +void AudioTransportSource::releaseResources() +{ + const ScopedLock sl (callbackLock); + + if (masterSource != 0) + masterSource->releaseResources(); + + isPrepared = false; +} + +void AudioTransportSource::getNextAudioBlock (const AudioSourceChannelInfo& info) +{ + const ScopedLock sl (callbackLock); + + inputStreamEOF = false; + + if (masterSource != 0 && ! stopped) + { + masterSource->getNextAudioBlock (info); + + if (! playing) + { + // just stopped playing, so fade out the last block.. + for (int i = info.buffer->getNumChannels(); --i >= 0;) + info.buffer->applyGainRamp (i, info.startSample, jmin (256, info.numSamples), 1.0f, 0.0f); + + if (info.numSamples > 256) + info.buffer->clear (info.startSample + 256, info.numSamples - 256); + } + + if (positionableSource->getNextReadPosition() > positionableSource->getTotalLength() + 1 + && ! positionableSource->isLooping()) + { + playing = false; + inputStreamEOF = true; + sendChangeMessage (this); + } + + stopped = ! playing; + + for (int i = info.buffer->getNumChannels(); --i >= 0;) + { + info.buffer->applyGainRamp (i, info.startSample, info.numSamples, + lastGain, gain); + } + } + else + { + info.clearActiveBufferRegion(); + stopped = true; + } + + lastGain = gain; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_AudioTransportSource.cpp *********/ + +/********* Start of inlined file: juce_BufferingAudioSource.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +class SharedBufferingAudioSourceThread : public DeletedAtShutdown, + public Thread, + private Timer +{ +public: + SharedBufferingAudioSourceThread() + : Thread ("Audio Buffer"), + sources (8) + { + } + + ~SharedBufferingAudioSourceThread() + { + stopThread (10000); + clearSingletonInstance(); + } + + juce_DeclareSingleton (SharedBufferingAudioSourceThread, false) + + void addSource (BufferingAudioSource* source) + { + const ScopedLock sl (lock); + + if (! sources.contains ((void*) source)) + { + sources.add ((void*) source); + startThread(); + + stopTimer(); + } + + notify(); + } + + void removeSource (BufferingAudioSource* source) + { + const ScopedLock sl (lock); + sources.removeValue ((void*) source); + + if (sources.size() == 0) + startTimer (5000); + } + +private: + VoidArray sources; + CriticalSection lock; + + void run() + { + while (! threadShouldExit()) + { + bool busy = false; + + for (int i = sources.size(); --i >= 0;) + { + if (threadShouldExit()) + return; + + const ScopedLock sl (lock); + + BufferingAudioSource* const b = (BufferingAudioSource*) sources[i]; + + if (b != 0 && b->readNextBufferChunk()) + busy = true; + } + + if (! busy) + wait (500); + } + } + + void timerCallback() + { + stopTimer(); + + if (sources.size() == 0) + deleteInstance(); + } + + SharedBufferingAudioSourceThread (const SharedBufferingAudioSourceThread&); + const SharedBufferingAudioSourceThread& operator= (const SharedBufferingAudioSourceThread&); +}; + +juce_ImplementSingleton (SharedBufferingAudioSourceThread) + +BufferingAudioSource::BufferingAudioSource (PositionableAudioSource* source_, + const bool deleteSourceWhenDeleted_, + int numberOfSamplesToBuffer_) + : source (source_), + deleteSourceWhenDeleted (deleteSourceWhenDeleted_), + numberOfSamplesToBuffer (jmax (1024, numberOfSamplesToBuffer_)), + buffer (2, 0), + bufferValidStart (0), + bufferValidEnd (0), + nextPlayPos (0), + wasSourceLooping (false) +{ + jassert (source_ != 0); + + jassert (numberOfSamplesToBuffer_ > 1024); // not much point using this class if you're + // not using a larger buffer.. +} + +BufferingAudioSource::~BufferingAudioSource() +{ + SharedBufferingAudioSourceThread* const thread = SharedBufferingAudioSourceThread::getInstanceWithoutCreating(); + + if (thread != 0) + thread->removeSource (this); + + if (deleteSourceWhenDeleted) + delete source; +} + +void BufferingAudioSource::prepareToPlay (int samplesPerBlockExpected, double sampleRate_) +{ + source->prepareToPlay (samplesPerBlockExpected, sampleRate_); + + sampleRate = sampleRate_; + + buffer.setSize (2, jmax (samplesPerBlockExpected * 2, numberOfSamplesToBuffer)); + buffer.clear(); + + bufferValidStart = 0; + bufferValidEnd = 0; + + SharedBufferingAudioSourceThread::getInstance()->addSource (this); + + while (bufferValidEnd - bufferValidStart < jmin (((int) sampleRate_) / 4, + buffer.getNumSamples() / 2)) + { + SharedBufferingAudioSourceThread::getInstance()->notify(); + Thread::sleep (5); + } +} + +void BufferingAudioSource::releaseResources() +{ + SharedBufferingAudioSourceThread* const thread = SharedBufferingAudioSourceThread::getInstanceWithoutCreating(); + + if (thread != 0) + thread->removeSource (this); + + buffer.setSize (2, 0); + source->releaseResources(); +} + +void BufferingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info) +{ + const ScopedLock sl (bufferStartPosLock); + + const int validStart = jlimit (bufferValidStart, bufferValidEnd, nextPlayPos) - nextPlayPos; + const int validEnd = jlimit (bufferValidStart, bufferValidEnd, nextPlayPos + info.numSamples) - nextPlayPos; + + if (validStart == validEnd) + { + // total cache miss + info.clearActiveBufferRegion(); + } + else + { + if (validStart > 0) + info.buffer->clear (info.startSample, validStart); // partial cache miss at start + + if (validEnd < info.numSamples) + info.buffer->clear (info.startSample + validEnd, + info.numSamples - validEnd); // partial cache miss at end + + if (validStart < validEnd) + { + for (int chan = jmin (2, info.buffer->getNumChannels()); --chan >= 0;) + { + const int startBufferIndex = (validStart + nextPlayPos) % buffer.getNumSamples(); + const int endBufferIndex = (validEnd + nextPlayPos) % buffer.getNumSamples(); + + if (startBufferIndex < endBufferIndex) + { + info.buffer->copyFrom (chan, info.startSample + validStart, + buffer, + chan, startBufferIndex, + validEnd - validStart); + } + else + { + const int initialSize = buffer.getNumSamples() - startBufferIndex; + + info.buffer->copyFrom (chan, info.startSample + validStart, + buffer, + chan, startBufferIndex, + initialSize); + + info.buffer->copyFrom (chan, info.startSample + validStart + initialSize, + buffer, + chan, 0, + (validEnd - validStart) - initialSize); + } + } + } + + nextPlayPos += info.numSamples; + } + + SharedBufferingAudioSourceThread* const thread = SharedBufferingAudioSourceThread::getInstanceWithoutCreating(); + + if (thread != 0) + thread->notify(); +} + +int BufferingAudioSource::getNextReadPosition() const +{ + return (source->isLooping() && nextPlayPos > 0) + ? nextPlayPos % source->getTotalLength() + : nextPlayPos; +} + +void BufferingAudioSource::setNextReadPosition (int newPosition) +{ + const ScopedLock sl (bufferStartPosLock); + + nextPlayPos = newPosition; + + SharedBufferingAudioSourceThread* const thread = SharedBufferingAudioSourceThread::getInstanceWithoutCreating(); + + if (thread != 0) + thread->notify(); +} + +bool BufferingAudioSource::readNextBufferChunk() +{ + bufferStartPosLock.enter(); + + if (wasSourceLooping != isLooping()) + { + wasSourceLooping = isLooping(); + bufferValidStart = 0; + bufferValidEnd = 0; + } + + int newBVS = jmax (0, nextPlayPos); + int newBVE = newBVS + buffer.getNumSamples() - 4; + int sectionToReadStart = 0; + int sectionToReadEnd = 0; + + const int maxChunkSize = 2048; + + if (newBVS < bufferValidStart || newBVS >= bufferValidEnd) + { + newBVE = jmin (newBVE, newBVS + maxChunkSize); + + sectionToReadStart = newBVS; + sectionToReadEnd = newBVE; + + bufferValidStart = 0; + bufferValidEnd = 0; + } + else if (abs (newBVS - bufferValidStart) > 512 + || abs (newBVE - bufferValidEnd) > 512) + { + newBVE = jmin (newBVE, bufferValidEnd + maxChunkSize); + + sectionToReadStart = bufferValidEnd; + sectionToReadEnd = newBVE; + + bufferValidStart = newBVS; + bufferValidEnd = jmin (bufferValidEnd, newBVE); + } + + bufferStartPosLock.exit(); + + if (sectionToReadStart != sectionToReadEnd) + { + const int bufferIndexStart = sectionToReadStart % buffer.getNumSamples(); + const int bufferIndexEnd = sectionToReadEnd % buffer.getNumSamples(); + + if (bufferIndexStart < bufferIndexEnd) + { + readBufferSection (sectionToReadStart, + sectionToReadEnd - sectionToReadStart, + bufferIndexStart); + } + else + { + const int initialSize = buffer.getNumSamples() - bufferIndexStart; + + readBufferSection (sectionToReadStart, + initialSize, + bufferIndexStart); + + readBufferSection (sectionToReadStart + initialSize, + (sectionToReadEnd - sectionToReadStart) - initialSize, + 0); + } + + const ScopedLock sl2 (bufferStartPosLock); + + bufferValidStart = newBVS; + bufferValidEnd = newBVE; + + return true; + } + else + { + return false; + } +} + +void BufferingAudioSource::readBufferSection (int start, int length, int bufferOffset) +{ + if (source->getNextReadPosition() != start) + source->setNextReadPosition (start); + + AudioSourceChannelInfo info; + info.buffer = &buffer; + info.startSample = bufferOffset; + info.numSamples = length; + + source->getNextAudioBlock (info); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_BufferingAudioSource.cpp *********/ + +/********* Start of inlined file: juce_ChannelRemappingAudioSource.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +ChannelRemappingAudioSource::ChannelRemappingAudioSource (AudioSource* const source_, + const bool deleteSourceWhenDeleted_) + : requiredNumberOfChannels (2), + source (source_), + deleteSourceWhenDeleted (deleteSourceWhenDeleted_), + buffer (2, 16) +{ + remappedInfo.buffer = &buffer; + remappedInfo.startSample = 0; +} + +ChannelRemappingAudioSource::~ChannelRemappingAudioSource() +{ + if (deleteSourceWhenDeleted) + delete source; +} + +void ChannelRemappingAudioSource::setNumberOfChannelsToProduce (const int requiredNumberOfChannels_) throw() +{ + const ScopedLock sl (lock); + requiredNumberOfChannels = requiredNumberOfChannels_; +} + +void ChannelRemappingAudioSource::clearAllMappings() throw() +{ + const ScopedLock sl (lock); + + remappedInputs.clear(); + remappedOutputs.clear(); +} + +void ChannelRemappingAudioSource::setInputChannelMapping (const int destIndex, const int sourceIndex) throw() +{ + const ScopedLock sl (lock); + + while (remappedInputs.size() < destIndex) + remappedInputs.add (-1); + + remappedInputs.set (destIndex, sourceIndex); +} + +void ChannelRemappingAudioSource::setOutputChannelMapping (const int sourceIndex, const int destIndex) throw() +{ + const ScopedLock sl (lock); + + while (remappedOutputs.size() < sourceIndex) + remappedOutputs.add (-1); + + remappedOutputs.set (sourceIndex, destIndex); +} + +int ChannelRemappingAudioSource::getRemappedInputChannel (const int inputChannelIndex) const throw() +{ + const ScopedLock sl (lock); + + if (inputChannelIndex >= 0 && inputChannelIndex < remappedInputs.size()) + return remappedInputs.getUnchecked (inputChannelIndex); + + return -1; +} + +int ChannelRemappingAudioSource::getRemappedOutputChannel (const int outputChannelIndex) const throw() +{ + const ScopedLock sl (lock); + + if (outputChannelIndex >= 0 && outputChannelIndex < remappedOutputs.size()) + return remappedOutputs .getUnchecked (outputChannelIndex); + + return -1; +} + +void ChannelRemappingAudioSource::prepareToPlay (int samplesPerBlockExpected, double sampleRate) +{ + source->prepareToPlay (samplesPerBlockExpected, sampleRate); +} + +void ChannelRemappingAudioSource::releaseResources() +{ + source->releaseResources(); +} + +void ChannelRemappingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) +{ + const ScopedLock sl (lock); + + buffer.setSize (requiredNumberOfChannels, bufferToFill.numSamples, false, false, true); + + const int numChans = bufferToFill.buffer->getNumChannels(); + + int i; + for (i = 0; i < buffer.getNumChannels(); ++i) + { + const int remappedChan = getRemappedInputChannel (i); + + if (remappedChan >= 0 && remappedChan < numChans) + { + buffer.copyFrom (i, 0, *bufferToFill.buffer, + remappedChan, + bufferToFill.startSample, + bufferToFill.numSamples); + } + else + { + buffer.clear (i, 0, bufferToFill.numSamples); + } + } + + remappedInfo.numSamples = bufferToFill.numSamples; + + source->getNextAudioBlock (remappedInfo); + + bufferToFill.clearActiveBufferRegion(); + + for (i = 0; i < requiredNumberOfChannels; ++i) + { + const int remappedChan = getRemappedOutputChannel (i); + + if (remappedChan >= 0 && remappedChan < numChans) + { + bufferToFill.buffer->addFrom (remappedChan, bufferToFill.startSample, + buffer, i, 0, bufferToFill.numSamples); + + } + } +} + +XmlElement* ChannelRemappingAudioSource::createXml() const throw() +{ + XmlElement* e = new XmlElement (T("MAPPINGS")); + + String ins, outs; + int i; + + const ScopedLock sl (lock); + + for (i = 0; i < remappedInputs.size(); ++i) + ins << remappedInputs.getUnchecked(i) << T(' '); + + for (i = 0; i < remappedOutputs.size(); ++i) + outs << remappedOutputs.getUnchecked(i) << T(' '); + + e->setAttribute (T("inputs"), ins.trimEnd()); + e->setAttribute (T("outputs"), outs.trimEnd()); + + return e; +} + +void ChannelRemappingAudioSource::restoreFromXml (const XmlElement& e) throw() +{ + if (e.hasTagName (T("MAPPINGS"))) + { + const ScopedLock sl (lock); + + clearAllMappings(); + + StringArray ins, outs; + ins.addTokens (e.getStringAttribute (T("inputs")), false); + outs.addTokens (e.getStringAttribute (T("outputs")), false); + + int i; + for (i = 0; i < ins.size(); ++i) + remappedInputs.add (ins[i].getIntValue()); + + for (i = 0; i < outs.size(); ++i) + remappedOutputs.add (outs[i].getIntValue()); + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ChannelRemappingAudioSource.cpp *********/ + +/********* Start of inlined file: juce_IIRFilterAudioSource.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +IIRFilterAudioSource::IIRFilterAudioSource (AudioSource* const inputSource, + const bool deleteInputWhenDeleted_) + : input (inputSource), + deleteInputWhenDeleted (deleteInputWhenDeleted_) +{ + jassert (inputSource != 0); + + for (int i = 2; --i >= 0;) + iirFilters.add (new IIRFilter()); +} + +IIRFilterAudioSource::~IIRFilterAudioSource() +{ + if (deleteInputWhenDeleted) + delete input; +} + +void IIRFilterAudioSource::setFilterParameters (const IIRFilter& newSettings) +{ + for (int i = iirFilters.size(); --i >= 0;) + iirFilters.getUnchecked(i)->copyCoefficientsFrom (newSettings); +} + +void IIRFilterAudioSource::prepareToPlay (int samplesPerBlockExpected, double sampleRate) +{ + input->prepareToPlay (samplesPerBlockExpected, sampleRate); + + for (int i = iirFilters.size(); --i >= 0;) + iirFilters.getUnchecked(i)->reset(); +} + +void IIRFilterAudioSource::releaseResources() +{ + input->releaseResources(); +} + +void IIRFilterAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) +{ + input->getNextAudioBlock (bufferToFill); + + const int numChannels = bufferToFill.buffer->getNumChannels(); + + while (numChannels > iirFilters.size()) + iirFilters.add (new IIRFilter (*iirFilters.getUnchecked (0))); + + for (int i = 0; i < numChannels; ++i) + iirFilters.getUnchecked(i) + ->processSamples (bufferToFill.buffer->getSampleData (i, bufferToFill.startSample), + bufferToFill.numSamples); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_IIRFilterAudioSource.cpp *********/ + +/********* Start of inlined file: juce_MixerAudioSource.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +MixerAudioSource::MixerAudioSource() + : tempBuffer (2, 0), + currentSampleRate (0.0), + bufferSizeExpected (0) +{ +} + +MixerAudioSource::~MixerAudioSource() +{ + removeAllInputs(); +} + +void MixerAudioSource::addInputSource (AudioSource* input, const bool deleteWhenRemoved) +{ + if (input != 0 && ! inputs.contains (input)) + { + lock.enter(); + double localRate = currentSampleRate; + int localBufferSize = bufferSizeExpected; + lock.exit(); + + if (localRate != 0.0) + input->prepareToPlay (localBufferSize, localRate); + + const ScopedLock sl (lock); + + inputsToDelete.setBit (inputs.size(), deleteWhenRemoved); + inputs.add (input); + } +} + +void MixerAudioSource::removeInputSource (AudioSource* input, const bool deleteInput) +{ + if (input != 0) + { + lock.enter(); + const int index = inputs.indexOf ((void*) input); + + if (index >= 0) + { + inputsToDelete.shiftBits (index, 1); + inputs.remove (index); + } + + lock.exit(); + + if (index >= 0) + { + input->releaseResources(); + + if (deleteInput) + delete input; + } + } +} + +void MixerAudioSource::removeAllInputs() +{ + lock.enter(); + VoidArray inputsCopy (inputs); + BitArray inputsToDeleteCopy (inputsToDelete); + inputs.clear(); + lock.exit(); + + for (int i = inputsCopy.size(); --i >= 0;) + if (inputsToDeleteCopy[i]) + delete (AudioSource*) inputsCopy[i]; +} + +void MixerAudioSource::prepareToPlay (int samplesPerBlockExpected, double sampleRate) +{ + tempBuffer.setSize (2, samplesPerBlockExpected); + + const ScopedLock sl (lock); + + currentSampleRate = sampleRate; + bufferSizeExpected = samplesPerBlockExpected; + + for (int i = inputs.size(); --i >= 0;) + ((AudioSource*) inputs.getUnchecked(i))->prepareToPlay (samplesPerBlockExpected, + sampleRate); +} + +void MixerAudioSource::releaseResources() +{ + const ScopedLock sl (lock); + + for (int i = inputs.size(); --i >= 0;) + ((AudioSource*) inputs.getUnchecked(i))->releaseResources(); + + tempBuffer.setSize (2, 0); + + currentSampleRate = 0; + bufferSizeExpected = 0; +} + +void MixerAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info) +{ + const ScopedLock sl (lock); + + if (inputs.size() > 0) + { + ((AudioSource*) inputs.getUnchecked(0))->getNextAudioBlock (info); + + if (inputs.size() > 1) + { + tempBuffer.setSize (jmax (1, info.buffer->getNumChannels()), + info.buffer->getNumSamples()); + + AudioSourceChannelInfo info2; + info2.buffer = &tempBuffer; + info2.numSamples = info.numSamples; + info2.startSample = 0; + + for (int i = 1; i < inputs.size(); ++i) + { + ((AudioSource*) inputs.getUnchecked(i))->getNextAudioBlock (info2); + + for (int chan = 0; chan < info.buffer->getNumChannels(); ++chan) + info.buffer->addFrom (chan, info.startSample, tempBuffer, chan, 0, info.numSamples); + } + } + } + else + { + info.clearActiveBufferRegion(); + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_MixerAudioSource.cpp *********/ + +/********* Start of inlined file: juce_ResamplingAudioSource.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +ResamplingAudioSource::ResamplingAudioSource (AudioSource* const inputSource, + const bool deleteInputWhenDeleted_) + : input (inputSource), + deleteInputWhenDeleted (deleteInputWhenDeleted_), + ratio (1.0), + lastRatio (1.0), + buffer (2, 0), + sampsInBuffer (0) +{ + jassert (input != 0); +} + +ResamplingAudioSource::~ResamplingAudioSource() +{ + if (deleteInputWhenDeleted) + delete input; +} + +void ResamplingAudioSource::setResamplingRatio (const double samplesInPerOutputSample) +{ + jassert (samplesInPerOutputSample > 0); + + const ScopedLock sl (ratioLock); + ratio = jmax (0.0, samplesInPerOutputSample); +} + +void ResamplingAudioSource::prepareToPlay (int samplesPerBlockExpected, + double sampleRate) +{ + const ScopedLock sl (ratioLock); + + input->prepareToPlay (samplesPerBlockExpected, sampleRate); + + buffer.setSize (2, roundDoubleToInt (samplesPerBlockExpected * ratio) + 32); + buffer.clear(); + sampsInBuffer = 0; + bufferPos = 0; + subSampleOffset = 0.0; + + createLowPass (ratio); + resetFilters(); +} + +void ResamplingAudioSource::releaseResources() +{ + input->releaseResources(); + buffer.setSize (2, 0); +} + +void ResamplingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info) +{ + const ScopedLock sl (ratioLock); + + if (lastRatio != ratio) + { + createLowPass (ratio); + lastRatio = ratio; + } + + const int sampsNeeded = roundDoubleToInt (info.numSamples * ratio) + 2; + + int bufferSize = buffer.getNumSamples(); + + if (bufferSize < sampsNeeded + 8) + { + bufferPos %= bufferSize; + bufferSize = sampsNeeded + 32; + buffer.setSize (buffer.getNumChannels(), bufferSize, true, true); + } + + bufferPos %= bufferSize; + + int endOfBufferPos = bufferPos + sampsInBuffer; + + while (sampsNeeded > sampsInBuffer) + { + endOfBufferPos %= bufferSize; + + int numToDo = jmin (sampsNeeded - sampsInBuffer, + bufferSize - endOfBufferPos); + + AudioSourceChannelInfo readInfo; + readInfo.buffer = &buffer; + readInfo.numSamples = numToDo; + readInfo.startSample = endOfBufferPos; + + input->getNextAudioBlock (readInfo); + + if (ratio > 1.0) + { + // for down-sampling, pre-apply the filter.. + + for (int i = jmin (2, info.buffer->getNumChannels()); --i >= 0;) + applyFilter (buffer.getSampleData (i, endOfBufferPos), numToDo, filterStates[i]); + } + + sampsInBuffer += numToDo; + endOfBufferPos += numToDo; + } + + float* dl = info.buffer->getSampleData (0, info.startSample); + float* dr = (info.buffer->getNumChannels() > 1) ? info.buffer->getSampleData (1, info.startSample) : 0; + + const float* const bl = buffer.getSampleData (0, 0); + const float* const br = buffer.getSampleData (1, 0); + + int nextPos = (bufferPos + 1) % bufferSize; + + for (int m = info.numSamples; --m >= 0;) + { + const float alpha = (float) subSampleOffset; + const float invAlpha = 1.0f - alpha; + + *dl++ = bl [bufferPos] * invAlpha + bl [nextPos] * alpha; + + if (dr != 0) + *dr++ = br [bufferPos] * invAlpha + br [nextPos] * alpha; + + subSampleOffset += ratio; + + jassert (sampsInBuffer > 0); + + while (subSampleOffset >= 1.0) + { + if (++bufferPos >= bufferSize) + bufferPos = 0; + + --sampsInBuffer; + + nextPos = (bufferPos + 1) % bufferSize; + subSampleOffset -= 1.0; + } + } + + if (ratio < 1.0) + { + // for up-sampling, apply the filter after transposing.. + + for (int i = jmin (2, info.buffer->getNumChannels()); --i >= 0;) + applyFilter (info.buffer->getSampleData (i, info.startSample), info.numSamples, filterStates[i]); + } + + jassert (sampsInBuffer >= 0); +} + +void ResamplingAudioSource::createLowPass (const double ratio) +{ + const double proportionalRate = (ratio > 1.0) ? 0.5 / ratio + : 0.5 * ratio; + + const double n = 1.0 / tan (double_Pi * jmax (0.001, proportionalRate)); + const double nSquared = n * n; + const double c1 = 1.0 / (1.0 + sqrt (2.0) * n + nSquared); + + setFilterCoefficients (c1, + c1 * 2.0f, + c1, + 1.0, + c1 * 2.0 * (1.0 - nSquared), + c1 * (1.0 - sqrt (2.0) * n + nSquared)); +} + +void ResamplingAudioSource::setFilterCoefficients (double c1, double c2, double c3, double c4, double c5, double c6) +{ + const double a = 1.0 / c4; + + c1 *= a; + c2 *= a; + c3 *= a; + c5 *= a; + c6 *= a; + + coefficients[0] = c1; + coefficients[1] = c2; + coefficients[2] = c3; + coefficients[3] = c4; + coefficients[4] = c5; + coefficients[5] = c6; +} + +void ResamplingAudioSource::resetFilters() +{ + zeromem (filterStates, sizeof (filterStates)); +} + +void ResamplingAudioSource::applyFilter (float* samples, int num, FilterState& fs) +{ + while (--num >= 0) + { + const double in = *samples; + + double out = coefficients[0] * in + + coefficients[1] * fs.x1 + + coefficients[2] * fs.x2 + - coefficients[4] * fs.y1 + - coefficients[5] * fs.y2; + +#if JUCE_INTEL + if (! (out < -1.0e-8 || out > 1.0e-8)) + out = 0; +#endif + + fs.x2 = fs.x1; + fs.x1 = in; + fs.y2 = fs.y1; + fs.y1 = out; + + *samples++ = (float) out; + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ResamplingAudioSource.cpp *********/ + +/********* Start of inlined file: juce_ToneGeneratorAudioSource.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +ToneGeneratorAudioSource::ToneGeneratorAudioSource() + : frequency (1000.0), + sampleRate (44100.0), + currentPhase (0.0), + phasePerSample (0.0), + amplitude (0.5f) +{ +} + +ToneGeneratorAudioSource::~ToneGeneratorAudioSource() +{ +} + +void ToneGeneratorAudioSource::setAmplitude (const float newAmplitude) +{ + amplitude = newAmplitude; +} + +void ToneGeneratorAudioSource::setFrequency (const double newFrequencyHz) +{ + frequency = newFrequencyHz; + phasePerSample = 0.0; +} + +void ToneGeneratorAudioSource::prepareToPlay (int /*samplesPerBlockExpected*/, + double sampleRate_) +{ + currentPhase = 0.0; + phasePerSample = 0.0; + sampleRate = sampleRate_; +} + +void ToneGeneratorAudioSource::releaseResources() +{ +} + +void ToneGeneratorAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info) +{ + if (phasePerSample == 0.0) + phasePerSample = double_Pi * 2.0 / (sampleRate / frequency); + + for (int i = 0; i < info.numSamples; ++i) + { + const float sample = amplitude * (float) sin (currentPhase); + currentPhase += phasePerSample; + + for (int j = info.buffer->getNumChannels(); --j >= 0;) + *info.buffer->getSampleData (j, info.startSample + i) = sample; + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ToneGeneratorAudioSource.cpp *********/ + +/********* Start of inlined file: juce_AudioDeviceManager.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +AudioDeviceManager::AudioDeviceManager() + : currentAudioDevice (0), + currentCallback (0), + numInputChansNeeded (0), + numOutputChansNeeded (2), + lastExplicitSettings (0), + listNeedsScanning (true), + useInputNames (false), + enabledMidiInputs (4), + midiCallbacks (4), + midiCallbackDevices (4), + defaultMidiOutput (0), + cpuUsageMs (0), + timeToCpuScale (0) +{ + callbackHandler.owner = this; + + AudioIODeviceType::createDeviceTypes (availableDeviceTypes); +} + +AudioDeviceManager::~AudioDeviceManager() +{ + stopDevice(); + deleteAndZero (currentAudioDevice); + deleteAndZero (defaultMidiOutput); + delete lastExplicitSettings; +} + +const String AudioDeviceManager::initialise (const int numInputChannelsNeeded, + const int numOutputChannelsNeeded, + const XmlElement* const e, + const bool selectDefaultDeviceOnFailure, + const String& preferredDefaultDeviceName) +{ + if (listNeedsScanning) + refreshDeviceList(); + + numInputChansNeeded = numInputChannelsNeeded; + numOutputChansNeeded = numOutputChannelsNeeded; + + if (e != 0 && e->hasTagName (T("DEVICESETUP"))) + { + lastExplicitSettings = new XmlElement (*e); + + BitArray ins, outs; + ins.parseString (e->getStringAttribute (T("audioDeviceInChans"), T("11")), 2); + outs.parseString (e->getStringAttribute (T("audioDeviceOutChans"), T("11")), 2); + + String error (setAudioDevice (e->getStringAttribute (T("audioDeviceName")), + e->getIntAttribute (T("audioDeviceBufferSize")), + e->getDoubleAttribute (T("audioDeviceRate")), + e->hasAttribute (T("audioDeviceInChans")) ? &ins : 0, + e->hasAttribute (T("audioDeviceOutChans")) ? &outs : 0, + true)); + + StringArray enabledMidiIns; + forEachXmlChildElementWithTagName (*e, c, T("MIDIINPUT")) + enabledMidiIns.add (c->getStringAttribute (T("name"))); + + const StringArray allMidiIns (MidiInput::getDevices()); + + for (int i = allMidiIns.size(); --i >= 0;) + setMidiInputEnabled (allMidiIns[i], enabledMidiIns.contains (allMidiIns[i])); + + if (error.isNotEmpty() && selectDefaultDeviceOnFailure) + error = initialise (numInputChannelsNeeded, numOutputChannelsNeeded, 0, + false, preferredDefaultDeviceName); + + setDefaultMidiOutput (e->getStringAttribute (T("defaultMidiOutput"))); + + return error; + } + else + { + setInputDeviceNamesUsed (numOutputChannelsNeeded == 0); + + String defaultDevice; + + if (preferredDefaultDeviceName.isNotEmpty()) + { + for (int i = 0; i < availableDeviceTypes.size(); ++i) + { + const StringArray devs (availableDeviceTypes.getUnchecked(i)->getDeviceNames()); + + for (int j = 0; j < devs.size(); ++j) + { + if (devs[j].matchesWildcard (preferredDefaultDeviceName, true)) + { + defaultDevice = devs[j]; + break; + } + } + } + } + + if (defaultDevice.isEmpty() && availableDeviceTypes [0] != 0) + defaultDevice = availableDeviceTypes[0]->getDefaultDeviceName (numOutputChannelsNeeded == 0, + numInputChannelsNeeded, + numOutputChannelsNeeded); + + return setAudioDevice (defaultDevice, 0, 0, 0, 0, false); + } +} + +XmlElement* AudioDeviceManager::createStateXml() const +{ + return lastExplicitSettings != 0 ? new XmlElement (*lastExplicitSettings) : 0; +} + +const StringArray AudioDeviceManager::getAvailableAudioDeviceNames() const +{ + if (listNeedsScanning) + refreshDeviceList(); + + StringArray names; + + for (int i = 0; i < availableDeviceTypes.size(); ++i) + names.addArray (availableDeviceTypes[i]->getDeviceNames (useInputNames)); + + return names; +} + +void AudioDeviceManager::refreshDeviceList() const +{ + listNeedsScanning = false; + + for (int i = 0; i < availableDeviceTypes.size(); ++i) + availableDeviceTypes[i]->scanForDevices(); +} + +void AudioDeviceManager::setInputDeviceNamesUsed (const bool useInputNames_) +{ + useInputNames = useInputNames_; + sendChangeMessage (this); +} + +void AudioDeviceManager::addDeviceNamesToComboBox (ComboBox& combo) const +{ + int n = 0; + + for (int i = 0; i < availableDeviceTypes.size(); ++i) + { + AudioIODeviceType* const type = availableDeviceTypes[i]; + + if (availableDeviceTypes.size() > 1) + combo.addSectionHeading (type->getTypeName() + T(" devices:")); + + const StringArray names (type->getDeviceNames (useInputNames)); + + for (int j = 0; j < names.size(); ++j) + combo.addItem (names[j], ++n); + + combo.addSeparator(); + } + + combo.addItem (TRANS("<< no audio device >>"), -1); +} + +const String AudioDeviceManager::getCurrentAudioDeviceName() const +{ + if (currentAudioDevice != 0) + return currentAudioDevice->getName(); + + return String::empty; +} + +const String AudioDeviceManager::setAudioDevice (const String& deviceNameToUse, + int blockSizeToUse, + double sampleRateToUse, + const BitArray* inChans, + const BitArray* outChans, + const bool treatAsChosenDevice) +{ + stopDevice(); + + String error; + + if (deviceNameToUse.isNotEmpty()) + { + const StringArray devNames (getAvailableAudioDeviceNames()); + + int index = devNames.indexOf (deviceNameToUse, true); + + if (index >= 0) + { + if (currentAudioDevice == 0 + || currentAudioDevice->getLastError().isNotEmpty() + || ! deviceNameToUse.equalsIgnoreCase (currentAudioDevice->getName())) + { + // change of device.. + deleteAndZero (currentAudioDevice); + + int n = 0; + + for (int i = 0; i < availableDeviceTypes.size(); ++i) + { + AudioIODeviceType* const type = availableDeviceTypes[i]; + const StringArray names (type->getDeviceNames (useInputNames)); + + if (index >= n && index < n + names.size()) + { + currentAudioDevice = type->createDevice (deviceNameToUse); + break; + } + + n += names.size(); + } + + error = currentAudioDevice->getLastError(); + + if (error.isNotEmpty()) + { + deleteAndZero (currentAudioDevice); + return error; + } + + inputChannels.clear(); + inputChannels.setRange (0, numInputChansNeeded, true); + outputChannels.clear(); + outputChannels.setRange (0, numOutputChansNeeded, true); + } + + if (inChans != 0) + inputChannels = *inChans; + + if (outChans != 0) + outputChannels = *outChans; + + error = restartDevice (blockSizeToUse, + sampleRateToUse, + inputChannels, + outputChannels); + + if (error.isNotEmpty()) + { + deleteAndZero (currentAudioDevice); + } + } + else + { + deleteAndZero (currentAudioDevice); + error << "No such device: " << deviceNameToUse; + } + } + else + { + deleteAndZero (currentAudioDevice); + } + + if (treatAsChosenDevice && error.isEmpty()) + updateXml(); + + return error; +} + +const String AudioDeviceManager::restartDevice (int blockSizeToUse, + double sampleRateToUse, + const BitArray& inChans, + const BitArray& outChans) +{ + stopDevice(); + + inputChannels = inChans; + outputChannels = outChans; + + if (sampleRateToUse > 0) + { + bool ok = false; + + for (int i = currentAudioDevice->getNumSampleRates(); --i >= 0;) + { + const double sr = currentAudioDevice->getSampleRate (i); + + if (sr == sampleRateToUse) + ok = true; + } + + if (! ok) + sampleRateToUse = 0; + } + + if (sampleRateToUse == 0) + { + double lowestAbove44 = 0.0; + + for (int i = currentAudioDevice->getNumSampleRates(); --i >= 0;) + { + const double sr = currentAudioDevice->getSampleRate (i); + + if (sr >= 44100.0 && (lowestAbove44 == 0 || sr < lowestAbove44)) + lowestAbove44 = sr; + } + + if (lowestAbove44 == 0.0) + sampleRateToUse = currentAudioDevice->getSampleRate (0); + else + sampleRateToUse = lowestAbove44; + } + + const String error (currentAudioDevice->open (inChans, outChans, + sampleRateToUse, blockSizeToUse)); + + if (error.isEmpty()) + currentAudioDevice->start (&callbackHandler); + + sendChangeMessage (this); + return error; +} + +void AudioDeviceManager::stopDevice() +{ + if (currentAudioDevice != 0) + currentAudioDevice->stop(); +} + +void AudioDeviceManager::closeAudioDevice() +{ + if (currentAudioDevice != 0) + { + lastRunningDevice = currentAudioDevice->getName(); + lastRunningBlockSize = currentAudioDevice->getCurrentBufferSizeSamples(); + lastRunningSampleRate = currentAudioDevice->getCurrentSampleRate(); + lastRunningIns = inputChannels; + lastRunningOuts = outputChannels; + + stopDevice(); + + setAudioDevice (String::empty, 0, 0, 0, 0, false); + } +} + +void AudioDeviceManager::restartLastAudioDevice() +{ + if (currentAudioDevice == 0) + { + if (lastRunningDevice.isEmpty()) + { + // This method will only reload the last device that was running + // before closeAudioDevice() was called - you need to actually open + // one first, with setAudioDevice(). + jassertfalse + return; + } + + setAudioDevice (lastRunningDevice, + lastRunningBlockSize, + lastRunningSampleRate, + &lastRunningIns, + &lastRunningOuts, + false); + } +} + +void AudioDeviceManager::setInputChannels (const BitArray& newEnabledChannels, + const bool treatAsChosenDevice) +{ + if (currentAudioDevice != 0 + && newEnabledChannels != inputChannels) + { + setAudioDevice (currentAudioDevice->getName(), + currentAudioDevice->getCurrentBufferSizeSamples(), + currentAudioDevice->getCurrentSampleRate(), + &newEnabledChannels, 0, + treatAsChosenDevice); + } +} + +void AudioDeviceManager::setOutputChannels (const BitArray& newEnabledChannels, + const bool treatAsChosenDevice) +{ + if (currentAudioDevice != 0 + && newEnabledChannels != outputChannels) + { + setAudioDevice (currentAudioDevice->getName(), + currentAudioDevice->getCurrentBufferSizeSamples(), + currentAudioDevice->getCurrentSampleRate(), + 0, &newEnabledChannels, + treatAsChosenDevice); + } +} + +void AudioDeviceManager::updateXml() +{ + delete lastExplicitSettings; + + lastExplicitSettings = new XmlElement (T("DEVICESETUP")); + + lastExplicitSettings->setAttribute (T("audioDeviceName"), getCurrentAudioDeviceName()); + + if (currentAudioDevice != 0) + { + lastExplicitSettings->setAttribute (T("audioDeviceRate"), currentAudioDevice->getCurrentSampleRate()); + + if (currentAudioDevice->getDefaultBufferSize() != currentAudioDevice->getCurrentBufferSizeSamples()) + lastExplicitSettings->setAttribute (T("audioDeviceBufferSize"), currentAudioDevice->getCurrentBufferSizeSamples()); + + lastExplicitSettings->setAttribute (T("audioDeviceInChans"), inputChannels.toString (2)); + lastExplicitSettings->setAttribute (T("audioDeviceOutChans"), outputChannels.toString (2)); + } + + for (int i = 0; i < enabledMidiInputs.size(); ++i) + { + XmlElement* const m = new XmlElement (T("MIDIINPUT")); + m->setAttribute (T("name"), enabledMidiInputs[i]->getName()); + + lastExplicitSettings->addChildElement (m); + } + + if (defaultMidiOutputName.isNotEmpty()) + lastExplicitSettings->setAttribute (T("defaultMidiOutput"), defaultMidiOutputName); +} + +void AudioDeviceManager::setAudioCallback (AudioIODeviceCallback* newCallback) +{ + if (newCallback != currentCallback) + { + AudioIODeviceCallback* lastCallback = currentCallback; + + audioCallbackLock.enter(); + currentCallback = 0; + audioCallbackLock.exit(); + + if (currentAudioDevice != 0) + { + if (lastCallback != 0) + lastCallback->audioDeviceStopped(); + + if (newCallback != 0) + newCallback->audioDeviceAboutToStart (currentAudioDevice); + } + + currentCallback = newCallback; + } +} + +void AudioDeviceManager::audioDeviceIOCallbackInt (const float** inputChannelData, + int totalNumInputChannels, + float** outputChannelData, + int totalNumOutputChannels, + int numSamples) +{ + const ScopedLock sl (audioCallbackLock); + + if (currentCallback != 0) + { + const double callbackStartTime = Time::getMillisecondCounterHiRes(); + + currentCallback->audioDeviceIOCallback (inputChannelData, + totalNumInputChannels, + outputChannelData, + totalNumOutputChannels, + numSamples); + + const double msTaken = Time::getMillisecondCounterHiRes() - callbackStartTime; + const double filterAmount = 0.2; + cpuUsageMs += filterAmount * (msTaken - cpuUsageMs); + } + else + { + for (int i = 0; i < totalNumOutputChannels; ++i) + if (outputChannelData [i] != 0) + zeromem (outputChannelData[i], sizeof (float) * numSamples); + } +} + +void AudioDeviceManager::audioDeviceAboutToStartInt (AudioIODevice* const device) +{ + cpuUsageMs = 0; + + const double sampleRate = device->getCurrentSampleRate(); + const int blockSize = device->getCurrentBufferSizeSamples(); + + if (sampleRate > 0.0 && blockSize > 0) + { + const double msPerBlock = 1000.0 * blockSize / sampleRate; + timeToCpuScale = (msPerBlock > 0.0) ? (1.0 / msPerBlock) : 0.0; + } + + if (currentCallback != 0) + currentCallback->audioDeviceAboutToStart (device); + + sendChangeMessage (this); +} + +void AudioDeviceManager::audioDeviceStoppedInt() +{ + cpuUsageMs = 0; + timeToCpuScale = 0; + sendChangeMessage (this); + + if (currentCallback != 0) + currentCallback->audioDeviceStopped(); +} + +double AudioDeviceManager::getCpuUsage() const +{ + return jlimit (0.0, 1.0, timeToCpuScale * cpuUsageMs); +} + +void AudioDeviceManager::setMidiInputEnabled (const String& name, + const bool enabled) +{ + if (enabled != isMidiInputEnabled (name)) + { + if (enabled) + { + const int index = MidiInput::getDevices().indexOf (name); + + if (index >= 0) + { + MidiInput* const min = MidiInput::openDevice (index, &callbackHandler); + + if (min != 0) + { + enabledMidiInputs.add (min); + min->start(); + } + } + } + else + { + for (int i = enabledMidiInputs.size(); --i >= 0;) + if (enabledMidiInputs[i]->getName() == name) + enabledMidiInputs.remove (i); + } + + updateXml(); + sendChangeMessage (this); + } +} + +bool AudioDeviceManager::isMidiInputEnabled (const String& name) const +{ + for (int i = enabledMidiInputs.size(); --i >= 0;) + if (enabledMidiInputs[i]->getName() == name) + return true; + + return false; +} + +void AudioDeviceManager::addMidiInputCallback (const String& name, + MidiInputCallback* callback) +{ + removeMidiInputCallback (callback); + + if (name.isEmpty()) + { + midiCallbacks.add (callback); + midiCallbackDevices.add (0); + } + else + { + for (int i = enabledMidiInputs.size(); --i >= 0;) + { + if (enabledMidiInputs[i]->getName() == name) + { + const ScopedLock sl (midiCallbackLock); + + if (! midiCallbacks.contains (callback)) + { + midiCallbacks.add (callback); + midiCallbackDevices.add (enabledMidiInputs[i]); + } + + break; + } + } + } +} + +void AudioDeviceManager::removeMidiInputCallback (MidiInputCallback* callback) +{ + const ScopedLock sl (midiCallbackLock); + + const int index = midiCallbacks.indexOf (callback); + midiCallbacks.remove (index); + midiCallbackDevices.remove (index); +} + +void AudioDeviceManager::handleIncomingMidiMessageInt (MidiInput* source, + const MidiMessage& message) +{ + if (! message.isActiveSense()) + { + const bool isDefaultSource = (source == 0 || source == enabledMidiInputs.getFirst()); + + const ScopedLock sl (midiCallbackLock); + + for (int i = midiCallbackDevices.size(); --i >= 0;) + { + MidiInput* const md = midiCallbackDevices.getUnchecked(i); + + if (md == source || (md == 0 && isDefaultSource)) + midiCallbacks.getUnchecked(i)->handleIncomingMidiMessage (source, message); + } + } +} + +void AudioDeviceManager::setDefaultMidiOutput (const String& deviceName) +{ + if (defaultMidiOutputName != deviceName) + { + deleteAndZero (defaultMidiOutput); + defaultMidiOutputName = deviceName; + + if (deviceName.isNotEmpty()) + defaultMidiOutput = MidiOutput::openDevice (MidiOutput::getDevices().indexOf (deviceName)); + + updateXml(); + sendChangeMessage (this); + } +} + +void AudioDeviceManager::CallbackHandler::audioDeviceIOCallback (const float** inputChannelData, + int totalNumInputChannels, + float** outputChannelData, + int totalNumOutputChannels, + int numSamples) +{ + owner->audioDeviceIOCallbackInt (inputChannelData, totalNumInputChannels, outputChannelData, totalNumOutputChannels, numSamples); +} + +void AudioDeviceManager::CallbackHandler::audioDeviceAboutToStart (AudioIODevice* device) +{ + owner->audioDeviceAboutToStartInt (device); +} + +void AudioDeviceManager::CallbackHandler::audioDeviceStopped() +{ + owner->audioDeviceStoppedInt(); +} + +void AudioDeviceManager::CallbackHandler::handleIncomingMidiMessage (MidiInput* source, const MidiMessage& message) +{ + owner->handleIncomingMidiMessageInt (source, message); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_AudioDeviceManager.cpp *********/ + +/********* Start of inlined file: juce_AudioIODevice.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +AudioIODevice::AudioIODevice (const String& deviceName, const String& typeName_) + : name (deviceName), + typeName (typeName_) +{ +} + +AudioIODevice::~AudioIODevice() +{ +} + +bool AudioIODevice::hasControlPanel() const +{ + return false; +} + +bool AudioIODevice::showControlPanel() +{ + jassertfalse // this should only be called for devices which return true from + // their hasControlPanel() method. + return false; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_AudioIODevice.cpp *********/ + +/********* Start of inlined file: juce_AudioIODeviceType.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +AudioIODeviceType::AudioIODeviceType (const tchar* const name) + : typeName (name) +{ +} + +AudioIODeviceType::~AudioIODeviceType() +{ +} + +extern AudioIODeviceType* juce_createDefaultAudioIODeviceType(); + +#if JUCE_WIN32 && JUCE_ASIO + extern AudioIODeviceType* juce_createASIOAudioIODeviceType(); +#endif + +#if JUCE_WIN32 && JUCE_WDM_AUDIO + extern AudioIODeviceType* juce_createWDMAudioIODeviceType(); +#endif + +void AudioIODeviceType::createDeviceTypes (OwnedArray & list) +{ + AudioIODeviceType* const defaultDeviceType = juce_createDefaultAudioIODeviceType(); + + if (defaultDeviceType != 0) + list.add (defaultDeviceType); + +#if JUCE_WIN32 && JUCE_ASIO + list.add (juce_createASIOAudioIODeviceType()); +#endif + +#if JUCE_WIN32 && JUCE_WDM_AUDIO + list.add (juce_createWDMAudioIODeviceType()); +#endif +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_AudioIODeviceType.cpp *********/ + +/********* Start of inlined file: juce_MidiOutput.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +MidiOutput::MidiOutput() throw() + : Thread ("midi out"), + internal (0), + firstMessage (0) +{ +} + +MidiOutput::PendingMessage::PendingMessage (const uint8* const data, + const int len, + const double sampleNumber) throw() + : message (data, len, sampleNumber) +{ +} + +void MidiOutput::sendBlockOfMessages (const MidiBuffer& buffer, + const double millisecondCounterToStartAt, + double samplesPerSecondForBuffer) throw() +{ + // You've got to call startBackgroundThread() for this to actually work.. + jassert (isThreadRunning()); + + // this needs to be a value in the future - RTFM for this method! + jassert (millisecondCounterToStartAt > 0); + + samplesPerSecondForBuffer *= 0.001; + + MidiBuffer::Iterator i (buffer); + + const uint8* data; + int len, time; + + while (i.getNextEvent (data, len, time)) + { + const double eventTime = millisecondCounterToStartAt + samplesPerSecondForBuffer * time; + + PendingMessage* const m + = new PendingMessage (data, len, eventTime); + + const ScopedLock sl (lock); + + if (firstMessage == 0 || firstMessage->message.getTimeStamp() > eventTime) + { + m->next = firstMessage; + firstMessage = m; + } + else + { + PendingMessage* mm = firstMessage; + + while (mm->next != 0 && mm->next->message.getTimeStamp() <= eventTime) + mm = mm->next; + + m->next = mm->next; + mm->next = m; + } + } + + notify(); +} + +void MidiOutput::clearAllPendingMessages() throw() +{ + const ScopedLock sl (lock); + + while (firstMessage != 0) + { + PendingMessage* const m = firstMessage; + firstMessage = firstMessage->next; + delete m; + } +} + +void MidiOutput::startBackgroundThread() throw() +{ + startThread (9); +} + +void MidiOutput::stopBackgroundThread() throw() +{ + stopThread (5000); +} + +void MidiOutput::run() +{ + while (! threadShouldExit()) + { + uint32 now = Time::getMillisecondCounter(); + uint32 eventTime = 0; + uint32 timeToWait = 500; + + lock.enter(); + PendingMessage* message = firstMessage; + + if (message != 0) + { + eventTime = roundDoubleToInt (message->message.getTimeStamp()); + + if (eventTime > now + 20) + { + timeToWait = jmax (10, eventTime - now - 100); + message = 0; + } + else + { + firstMessage = message->next; + } + } + + lock.exit(); + + if (message != 0) + { + if (eventTime > now) + { + Time::waitForMillisecondCounter (eventTime); + + if (threadShouldExit()) + break; + } + + if (eventTime > now - 200) + sendMessageNow (message->message); + + delete message; + } + else + { + jassert (timeToWait < 1000 * 30); + wait (timeToWait); + } + } + + clearAllPendingMessages(); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_MidiOutput.cpp *********/ + +/********* Start of inlined file: juce_AudioDataConverters.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +void AudioDataConverters::convertFloatToInt16LE (const float* source, void* dest, int numSamples, const int destBytesPerSample) +{ + const double maxVal = (double) 0x7fff; + char* intData = (char*) dest; + + for (int i = 0; i < numSamples; ++i) + { + *(uint16*)intData = swapIfBigEndian ((uint16) (short) roundDoubleToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); + intData += destBytesPerSample; + } +} + +void AudioDataConverters::convertFloatToInt16BE (const float* source, void* dest, int numSamples, const int destBytesPerSample) +{ + const double maxVal = (double) 0x7fff; + char* intData = (char*) dest; + + for (int i = 0; i < numSamples; ++i) + { + *(uint16*)intData = swapIfLittleEndian ((uint16) (short) roundDoubleToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); + intData += destBytesPerSample; + } +} + +void AudioDataConverters::convertFloatToInt24LE (const float* source, void* dest, int numSamples, const int destBytesPerSample) +{ + const double maxVal = (double) 0x7fffff; + char* intData = (char*) dest; + + for (int i = 0; i < numSamples; ++i) + { + littleEndian24BitToChars ((uint32) roundDoubleToInt (jlimit (-maxVal, maxVal, maxVal * source[i])), intData); + intData += destBytesPerSample; + } +} + +void AudioDataConverters::convertFloatToInt24BE (const float* source, void* dest, int numSamples, const int destBytesPerSample) +{ + const double maxVal = (double) 0x7fffff; + char* intData = (char*) dest; + + for (int i = 0; i < numSamples; ++i) + { + bigEndian24BitToChars ((uint32) roundDoubleToInt (jlimit (-maxVal, maxVal, maxVal * source[i])), intData); + intData += destBytesPerSample; + } +} + +void AudioDataConverters::convertFloatToInt32LE (const float* source, void* dest, int numSamples, const int destBytesPerSample) +{ + const double maxVal = (double) 0x7fffffff; + char* intData = (char*) dest; + + for (int i = 0; i < numSamples; ++i) + { + *(uint32*)intData = swapIfBigEndian ((uint32) roundDoubleToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); + intData += destBytesPerSample; + } +} + +void AudioDataConverters::convertFloatToInt32BE (const float* source, void* dest, int numSamples, const int destBytesPerSample) +{ + const double maxVal = (double) 0x7fffffff; + char* intData = (char*) dest; + + for (int i = 0; i < numSamples; ++i) + { + *(uint32*)intData = swapIfLittleEndian ((uint32) roundDoubleToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); + intData += destBytesPerSample; + } +} + +void AudioDataConverters::convertFloatToFloat32LE (const float* source, void* dest, int numSamples, const int destBytesPerSample) +{ + char* d = (char*) dest; + + for (int i = 0; i < numSamples; ++i) + { + *(float*)d = source[i]; + +#if JUCE_BIG_ENDIAN + *(uint32*)d = swapByteOrder (*(uint32*)d); +#endif + + d += destBytesPerSample; + } +} + +void AudioDataConverters::convertFloatToFloat32BE (const float* source, void* dest, int numSamples, const int destBytesPerSample) +{ + char* d = (char*) dest; + + for (int i = 0; i < numSamples; ++i) + { + *(float*)d = source[i]; + +#if JUCE_LITTLE_ENDIAN + *(uint32*)d = swapByteOrder (*(uint32*)d); +#endif + + d += destBytesPerSample; + } +} + +void AudioDataConverters::convertInt16LEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample) +{ + const float scale = 1.0f / 0x7fff; + const char* intData = (const char*) source; + + for (int i = 0; i < numSamples; ++i) + { + dest[i] = scale * (short) swapIfBigEndian (*(uint16*)intData); + intData += srcBytesPerSample; + } +} + +void AudioDataConverters::convertInt16BEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample) +{ + const float scale = 1.0f / 0x7fff; + const char* intData = (const char*) source; + + for (int i = 0; i < numSamples; ++i) + { + dest[i] = scale * (short) swapIfLittleEndian (*(uint16*)intData); + intData += srcBytesPerSample; + } +} + +void AudioDataConverters::convertInt24LEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample) +{ + const float scale = 1.0f / 0x7fffff; + const char* intData = (const char*) source; + + for (int i = 0; i < numSamples; ++i) + { + dest[i] = scale * (short) littleEndian24Bit (intData); + intData += srcBytesPerSample; + } +} + +void AudioDataConverters::convertInt24BEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample) +{ + const float scale = 1.0f / 0x7fffff; + const char* intData = (const char*) source; + + for (int i = 0; i < numSamples; ++i) + { + dest[i] = scale * (short) bigEndian24Bit (intData); + intData += srcBytesPerSample; + } +} + +void AudioDataConverters::convertInt32LEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample) +{ + const float scale = 1.0f / 0x7fffffff; + const char* intData = (const char*) source; + + for (int i = 0; i < numSamples; ++i) + { + dest[i] = scale * (int) swapIfBigEndian (*(uint32*) intData); + intData += srcBytesPerSample; + } +} + +void AudioDataConverters::convertInt32BEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample) +{ + const float scale = 1.0f / 0x7fffffff; + const char* intData = (const char*) source; + + for (int i = 0; i < numSamples; ++i) + { + dest[i] = scale * (int) (swapIfLittleEndian (*(uint32*) intData)); + intData += srcBytesPerSample; + } +} + +void AudioDataConverters::convertFloat32LEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample) +{ + const char* s = (const char*) source; + + for (int i = 0; i < numSamples; ++i) + { + dest[i] = *(float*)s; + +#if JUCE_BIG_ENDIAN + uint32* const d = (uint32*) (dest + i); + *d = swapByteOrder (*d); +#endif + + s += srcBytesPerSample; + } +} + +void AudioDataConverters::convertFloat32BEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample) +{ + const char* s = (const char*) source; + + for (int i = 0; i < numSamples; ++i) + { + dest[i] = *(float*)s; + +#if JUCE_LITTLE_ENDIAN + uint32* const d = (uint32*) (dest + i); + *d = swapByteOrder (*d); +#endif + + s += srcBytesPerSample; + } +} + +void AudioDataConverters::convertFloatToFormat (const DataFormat destFormat, + const float* const source, + void* const dest, + const int numSamples) +{ + switch (destFormat) + { + case int16LE: + convertFloatToInt16LE (source, dest, numSamples); + break; + + case int16BE: + convertFloatToInt16BE (source, dest, numSamples); + break; + + case int24LE: + convertFloatToInt24LE (source, dest, numSamples); + break; + + case int24BE: + convertFloatToInt24BE (source, dest, numSamples); + break; + + case int32LE: + convertFloatToInt32LE (source, dest, numSamples); + break; + + case int32BE: + convertFloatToInt32BE (source, dest, numSamples); + break; + + case float32LE: + convertFloatToFloat32LE (source, dest, numSamples); + break; + + case float32BE: + convertFloatToFloat32BE (source, dest, numSamples); + break; + + default: + jassertfalse + break; + } +} + +void AudioDataConverters::convertFormatToFloat (const DataFormat sourceFormat, + const void* const source, + float* const dest, + const int numSamples) +{ + switch (sourceFormat) + { + case int16LE: + convertInt16LEToFloat (source, dest, numSamples); + break; + + case int16BE: + convertInt16BEToFloat (source, dest, numSamples); + break; + + case int24LE: + convertInt24LEToFloat (source, dest, numSamples); + break; + + case int24BE: + convertInt24BEToFloat (source, dest, numSamples); + break; + + case int32LE: + convertInt32LEToFloat (source, dest, numSamples); + break; + + case int32BE: + convertInt32BEToFloat (source, dest, numSamples); + break; + + case float32LE: + convertFloat32LEToFloat (source, dest, numSamples); + break; + + case float32BE: + convertFloat32BEToFloat (source, dest, numSamples); + break; + + default: + jassertfalse + break; + } +} + +void AudioDataConverters::interleaveSamples (const float** const source, + float* const dest, + const int numSamples, + const int numChannels) +{ + for (int chan = 0; chan < numChannels; ++chan) + { + int i = chan; + const float* src = source [chan]; + + for (int j = 0; j < numSamples; ++j) + { + dest [i] = src [j]; + i += numChannels; + } + } +} + +void AudioDataConverters::deinterleaveSamples (const float* const source, + float** const dest, + const int numSamples, + const int numChannels) +{ + for (int chan = 0; chan < numChannels; ++chan) + { + int i = chan; + float* dst = dest [chan]; + + for (int j = 0; j < numSamples; ++j) + { + dst [j] = source [i]; + i += numChannels; + } + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_AudioDataConverters.cpp *********/ + +/********* Start of inlined file: juce_AudioSampleBuffer.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +AudioSampleBuffer::AudioSampleBuffer (const int numChannels_, + const int numSamples) throw() + : numChannels (numChannels_), + size (numSamples) +{ + jassert (numSamples >= 0); + jassert (numChannels_ > 0 && numChannels_ <= maxNumAudioSampleBufferChannels); + + allocatedBytes = numChannels * numSamples * sizeof (float) + 32; + allocatedData = (float*) juce_malloc (allocatedBytes); + + float* chan = allocatedData; + for (int i = 0; i < numChannels_; ++i) + { + channels[i] = chan; + chan += numSamples; + } + + channels [numChannels_] = 0; +} + +AudioSampleBuffer::AudioSampleBuffer (float** dataToReferTo, + const int numChannels_, + const int numSamples) throw() + : numChannels (numChannels_), + size (numSamples), + allocatedBytes (0), + allocatedData (0) +{ + jassert (((unsigned int) numChannels_) <= (unsigned int) maxNumAudioSampleBufferChannels); + + for (int i = 0; i < numChannels_; ++i) + { + // you have to pass in the same number of valid pointers as numChannels + jassert (dataToReferTo[i] != 0); + + channels[i] = dataToReferTo[i]; + } + + channels [numChannels_] = 0; +} + +void AudioSampleBuffer::setDataToReferTo (float** dataToReferTo, + const int numChannels_, + const int numSamples) throw() +{ + jassert (((unsigned int) numChannels_) <= (unsigned int) maxNumAudioSampleBufferChannels); + + juce_free (allocatedData); + allocatedData = 0; + allocatedBytes = 0; + + numChannels = numChannels_; + size = numSamples; + + for (int i = 0; i < numChannels_; ++i) + { + // you have to pass in the same number of valid pointers as numChannels + jassert (dataToReferTo[i] != 0); + + channels[i] = dataToReferTo[i]; + } + + channels [numChannels_] = 0; +} + +AudioSampleBuffer::AudioSampleBuffer (const AudioSampleBuffer& other) throw() + : numChannels (other.numChannels), + size (other.size) +{ + if (other.allocatedData != 0) + { + allocatedBytes = numChannels * size * sizeof (float) + 32; + allocatedData = (float*) juce_malloc (allocatedBytes); + + memcpy (allocatedData, other.allocatedData, allocatedBytes); + + float* chan = allocatedData; + for (int i = 0; i < numChannels; ++i) + { + channels[i] = chan; + chan += size; + } + + channels [numChannels] = 0; + } + else + { + allocatedData = 0; + allocatedBytes = 0; + + memcpy (channels, other.channels, sizeof (channels)); + } +} + +const AudioSampleBuffer& AudioSampleBuffer::operator= (const AudioSampleBuffer& other) throw() +{ + if (this != &other) + { + setSize (other.getNumChannels(), other.getNumSamples(), false, false, false); + + const int numBytes = size * sizeof (float); + + for (int i = 0; i < numChannels; ++i) + memcpy (channels[i], other.channels[i], numBytes); + } + + return *this; +} + +AudioSampleBuffer::~AudioSampleBuffer() throw() +{ + juce_free (allocatedData); +} + +float* AudioSampleBuffer::getSampleData (const int channelNumber, + const int sampleOffset) const throw() +{ + jassert (((unsigned int) channelNumber) < (unsigned int) numChannels); + jassert (((unsigned int) sampleOffset) < (unsigned int) size); + + return channels [channelNumber] + sampleOffset; +} + +void AudioSampleBuffer::setSize (const int newNumChannels, + const int newNumSamples, + const bool keepExistingContent, + const bool clearExtraSpace, + const bool avoidReallocating) throw() +{ + jassert (newNumChannels > 0 && newNumChannels <= maxNumAudioSampleBufferChannels); + + if (newNumSamples != size || newNumChannels != numChannels) + { + const int newTotalBytes = newNumChannels * newNumSamples * sizeof (float) + 32; + + if (keepExistingContent) + { + float* const newData = (clearExtraSpace) ? (float*) juce_calloc (newTotalBytes) + : (float*) juce_malloc (newTotalBytes); + + const int sizeToCopy = sizeof (float) * jmin (newNumSamples, size); + + for (int i = jmin (newNumChannels, numChannels); --i >= 0;) + { + memcpy (newData + i * newNumSamples, + channels[i], + sizeToCopy); + } + + juce_free (allocatedData); + + allocatedData = newData; + allocatedBytes = newTotalBytes; + } + else + { + if (avoidReallocating && allocatedBytes >= newTotalBytes) + { + if (clearExtraSpace) + zeromem (allocatedData, newTotalBytes); + } + else + { + juce_free (allocatedData); + + allocatedData = (clearExtraSpace) ? (float*) juce_calloc (newTotalBytes) + : (float*) juce_malloc (newTotalBytes); + allocatedBytes = newTotalBytes; + } + } + + size = newNumSamples; + numChannels = newNumChannels; + + float* chan = allocatedData; + for (int i = 0; i < newNumChannels; ++i) + { + channels[i] = chan; + chan += size; + } + + channels [newNumChannels] = 0; + } +} + +void AudioSampleBuffer::clear() throw() +{ + for (int i = 0; i < numChannels; ++i) + zeromem (channels[i], size * sizeof (float)); +} + +void AudioSampleBuffer::clear (const int startSample, + const int numSamples) throw() +{ + jassert (startSample >= 0 && startSample + numSamples <= size); + + for (int i = 0; i < numChannels; ++i) + zeromem (channels [i] + startSample, numSamples * sizeof (float)); +} + +void AudioSampleBuffer::clear (const int channel, + const int startSample, + const int numSamples) throw() +{ + jassert (((unsigned int) channel) < (unsigned int) numChannels); + jassert (startSample >= 0 && startSample + numSamples <= size); + + zeromem (channels [channel] + startSample, numSamples * sizeof (float)); +} + +void AudioSampleBuffer::applyGain (const int channel, + const int startSample, + int numSamples, + const float gain) throw() +{ + jassert (((unsigned int) channel) < (unsigned int) numChannels); + jassert (startSample >= 0 && startSample + numSamples <= size); + + if (gain != 1.0f) + { + float* d = channels [channel] + startSample; + + if (gain == 0.0f) + { + zeromem (d, sizeof (float) * numSamples); + } + else + { + while (--numSamples >= 0) + *d++ *= gain; + } + } +} + +void AudioSampleBuffer::applyGainRamp (const int channel, + const int startSample, + int numSamples, + float startGain, + float endGain) throw() +{ + if (startGain == endGain) + { + applyGain (channel, startSample, numSamples, startGain); + } + else + { + jassert (((unsigned int) channel) < (unsigned int) numChannels); + jassert (startSample >= 0 && startSample + numSamples <= size); + + const float increment = (endGain - startGain) / numSamples; + float* d = channels [channel] + startSample; + + while (--numSamples >= 0) + { + *d++ *= startGain; + startGain += increment; + } + } +} + +void AudioSampleBuffer::applyGain (const int startSample, + const int numSamples, + const float gain) throw() +{ + for (int i = 0; i < numChannels; ++i) + applyGain (i, startSample, numSamples, gain); +} + +void AudioSampleBuffer::addFrom (const int destChannel, + const int destStartSample, + const AudioSampleBuffer& source, + const int sourceChannel, + const int sourceStartSample, + int numSamples, + const float gain) throw() +{ + jassert (&source != this || sourceChannel != destChannel); + jassert (((unsigned int) destChannel) < (unsigned int) numChannels); + jassert (destStartSample >= 0 && destStartSample + numSamples <= size); + jassert (((unsigned int) sourceChannel) < (unsigned int) source.numChannels); + jassert (sourceStartSample >= 0 && sourceStartSample + numSamples <= source.size); + + if (gain != 0.0f && numSamples > 0) + { + float* d = channels [destChannel] + destStartSample; + const float* s = source.channels [sourceChannel] + sourceStartSample; + + if (gain != 1.0f) + { + while (--numSamples >= 0) + *d++ += gain * *s++; + } + else + { + while (--numSamples >= 0) + *d++ += *s++; + } + } +} + +void AudioSampleBuffer::addFrom (const int destChannel, + const int destStartSample, + const float* source, + int numSamples, + const float gain) throw() +{ + jassert (((unsigned int) destChannel) < (unsigned int) numChannels); + jassert (destStartSample >= 0 && destStartSample + numSamples <= size); + jassert (source != 0); + + if (gain != 0.0f && numSamples > 0) + { + float* d = channels [destChannel] + destStartSample; + + if (gain != 1.0f) + { + while (--numSamples >= 0) + *d++ += gain * *source++; + } + else + { + while (--numSamples >= 0) + *d++ += *source++; + } + } +} + +void AudioSampleBuffer::addFromWithRamp (const int destChannel, + const int destStartSample, + const float* source, + int numSamples, + float startGain, + const float endGain) throw() +{ + jassert (((unsigned int) destChannel) < (unsigned int) numChannels); + jassert (destStartSample >= 0 && destStartSample + numSamples <= size); + jassert (source != 0); + + if (startGain == endGain) + { + addFrom (destChannel, + destStartSample, + source, + numSamples, + startGain); + } + else + { + if (numSamples > 0 && (startGain != 0.0f || endGain != 0.0f)) + { + const float increment = (endGain - startGain) / numSamples; + float* d = channels [destChannel] + destStartSample; + + while (--numSamples >= 0) + { + *d++ += startGain * *source++; + startGain += increment; + } + } + } +} + +void AudioSampleBuffer::copyFrom (const int destChannel, + const int destStartSample, + const AudioSampleBuffer& source, + const int sourceChannel, + const int sourceStartSample, + int numSamples) throw() +{ + jassert (&source != this || sourceChannel != destChannel); + jassert (((unsigned int) destChannel) < (unsigned int) numChannels); + jassert (destStartSample >= 0 && destStartSample + numSamples <= size); + jassert (((unsigned int) sourceChannel) < (unsigned int) source.numChannels); + jassert (sourceStartSample >= 0 && sourceStartSample + numSamples <= source.size); + + if (numSamples > 0) + { + memcpy (channels [destChannel] + destStartSample, + source.channels [sourceChannel] + sourceStartSample, + sizeof (float) * numSamples); + } +} + +void AudioSampleBuffer::copyFrom (const int destChannel, + const int destStartSample, + const float* source, + int numSamples) throw() +{ + jassert (((unsigned int) destChannel) < (unsigned int) numChannels); + jassert (destStartSample >= 0 && destStartSample + numSamples <= size); + jassert (source != 0); + + if (numSamples > 0) + { + memcpy (channels [destChannel] + destStartSample, + source, + sizeof (float) * numSamples); + } +} + +void AudioSampleBuffer::findMinMax (const int channel, + const int startSample, + int numSamples, + float& minVal, + float& maxVal) const throw() +{ + jassert (((unsigned int) channel) < (unsigned int) numChannels); + jassert (startSample >= 0 && startSample + numSamples <= size); + + if (numSamples <= 0) + { + minVal = 0.0f; + maxVal = 0.0f; + } + else + { + const float* d = channels [channel] + startSample; + + float mn = *d++; + float mx = mn; + + while (--numSamples > 0) // (> 0 rather than >= 0 because we've already taken the first sample) + { + const float samp = *d++; + + if (samp > mx) + mx = samp; + + if (samp < mn) + mn = samp; + } + + maxVal = mx; + minVal = mn; + } +} + +float AudioSampleBuffer::getMagnitude (const int channel, + const int startSample, + const int numSamples) const throw() +{ + jassert (((unsigned int) channel) < (unsigned int) numChannels); + jassert (startSample >= 0 && startSample + numSamples <= size); + + float mn, mx; + findMinMax (channel, startSample, numSamples, mn, mx); + + return jmax (mn, -mn, mx, -mx); +} + +float AudioSampleBuffer::getMagnitude (const int startSample, + const int numSamples) const throw() +{ + float mag = 0.0f; + + for (int i = 0; i < numChannels; ++i) + mag = jmax (mag, getMagnitude (i, startSample, numSamples)); + + return mag; +} + +float AudioSampleBuffer::getRMSLevel (const int channel, + const int startSample, + const int numSamples) const throw() +{ + jassert (((unsigned int) channel) < (unsigned int) numChannels); + jassert (startSample >= 0 && startSample + numSamples <= size); + + if (numSamples <= 0 || channel < 0 || channel >= numChannels) + return 0.0f; + + const float* const data = channels [channel] + startSample; + double sum = 0.0; + + for (int i = 0; i < numSamples; ++i) + { + const float sample = data [i]; + sum += sample * sample; + } + + return (float) sqrt (sum / numSamples); +} + +void AudioSampleBuffer::readFromAudioReader (AudioFormatReader* reader, + const int startSample, + const int numSamples, + const int readerStartSample, + const bool useLeftChan, + const bool useRightChan) throw() +{ + jassert (reader != 0); + jassert (startSample >= 0 && startSample + numSamples <= size); + + if (numSamples > 0) + { + int* chans[3]; + + if (useLeftChan == useRightChan) + { + chans[0] = (int*) getSampleData (0, startSample); + chans[1] = (reader->numChannels > 1 && getNumChannels() > 1) ? (int*) getSampleData (1, startSample) : 0; + } + else if (useLeftChan || (reader->numChannels == 1)) + { + chans[0] = (int*) getSampleData (0, startSample); + chans[1] = 0; + } + else if (useRightChan) + { + chans[0] = 0; + chans[1] = (int*) getSampleData (0, startSample); + } + + chans[2] = 0; + + reader->read (chans, readerStartSample, numSamples); + + if (! reader->usesFloatingPointData) + { + for (int j = 0; j < 2; ++j) + { + float* const d = (float*) (chans[j]); + + if (d != 0) + { + const float multiplier = 1.0f / 0x7fffffff; + + for (int i = 0; i < numSamples; ++i) + d[i] = *(int*)(d + i) * multiplier; + } + } + } + + if (numChannels > 1 && (chans[0] == 0 || chans[1] == 0)) + { + // if this is a stereo buffer and the source was mono, dupe the first channel.. + memcpy (getSampleData (1, startSample), + getSampleData (0, startSample), + sizeof (float) * numSamples); + } + } +} + +void AudioSampleBuffer::writeToAudioWriter (AudioFormatWriter* writer, + const int startSample, + const int numSamples) const throw() +{ + jassert (startSample >= 0 && startSample + numSamples <= size); + + if (numSamples > 0) + { + int* chans [3]; + + if (writer->isFloatingPoint()) + { + chans[0] = (int*) getSampleData (0, startSample); + + if (numChannels > 1) + chans[1] = (int*) getSampleData (1, startSample); + else + chans[1] = 0; + + chans[2] = 0; + writer->write ((const int**) chans, numSamples); + } + else + { + chans[0] = (int*) juce_malloc (sizeof (int) * numSamples * 2); + + if (numChannels > 1) + chans[1] = chans[0] + numSamples; + else + chans[1] = 0; + + chans[2] = 0; + + for (int j = 0; j < 2; ++j) + { + int* const dest = chans[j]; + + if (dest != 0) + { + const float* const src = channels [j] + startSample; + + for (int i = 0; i < numSamples; ++i) + { + const double samp = src[i]; + + if (samp <= -1.0) + dest[i] = INT_MIN; + else if (samp >= 1.0) + dest[i] = INT_MAX; + else + dest[i] = roundDoubleToInt (INT_MAX * samp); + } + } + } + + writer->write ((const int**) chans, numSamples); + + juce_free (chans[0]); + } + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_AudioSampleBuffer.cpp *********/ + +/********* Start of inlined file: juce_IIRFilter.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +IIRFilter::IIRFilter() throw() + : active (false) +{ + reset(); +} + +IIRFilter::IIRFilter (const IIRFilter& other) throw() + : active (other.active) +{ + const ScopedLock sl (other.processLock); + memcpy (coefficients, other.coefficients, sizeof (coefficients)); + reset(); +} + +IIRFilter::~IIRFilter() throw() +{ +} + +void IIRFilter::reset() throw() +{ + const ScopedLock sl (processLock); + + x1 = 0; + x2 = 0; + y1 = 0; + y2 = 0; +} + +void IIRFilter::processSamples (float* const samples, + const int numSamples) throw() +{ + const ScopedLock sl (processLock); + + if (active) + { + for (int i = 0; i < numSamples; ++i) + { + const float in = samples[i]; + + float out = coefficients[0] * in + + coefficients[1] * x1 + + coefficients[2] * x2 + - coefficients[4] * y1 + - coefficients[5] * y2; + +#if JUCE_INTEL + if (! (out < -1.0e-8 || out > 1.0e-8)) + out = 0; +#endif + + x2 = x1; + x1 = in; + y2 = y1; + y1 = out; + + samples[i] = out; + } + } +} + +void IIRFilter::makeLowPass (const double sampleRate, + const double frequency) throw() +{ + jassert (sampleRate > 0); + + const double n = 1.0 / tan (double_Pi * frequency / sampleRate); + const double nSquared = n * n; + const double c1 = 1.0 / (1.0 + sqrt (2.0) * n + nSquared); + + setCoefficients (c1, + c1 * 2.0f, + c1, + 1.0, + c1 * 2.0 * (1.0 - nSquared), + c1 * (1.0 - sqrt (2.0) * n + nSquared)); +} + +void IIRFilter::makeHighPass (const double sampleRate, + const double frequency) throw() +{ + const double n = tan (double_Pi * frequency / sampleRate); + const double nSquared = n * n; + const double c1 = 1.0 / (1.0 + sqrt (2.0) * n + nSquared); + + setCoefficients (c1, + c1 * -2.0f, + c1, + 1.0, + c1 * 2.0 * (nSquared - 1.0), + c1 * (1.0 - sqrt (2.0) * n + nSquared)); +} + +void IIRFilter::makeLowShelf (const double sampleRate, + const double cutOffFrequency, + const double Q, + const float gainFactor) throw() +{ + jassert (sampleRate > 0); + jassert (Q > 0); + + const double A = jmax (0.0f, gainFactor); + const double aminus1 = A - 1.0; + const double aplus1 = A + 1.0; + const double omega = (double_Pi * 2.0 * jmax (cutOffFrequency, 2.0)) / sampleRate; + const double coso = cos (omega); + const double beta = sin (omega) * sqrt (A) / Q; + const double aminus1TimesCoso = aminus1 * coso; + + setCoefficients (A * (aplus1 - aminus1TimesCoso + beta), + A * 2.0 * (aminus1 - aplus1 * coso), + A * (aplus1 - aminus1TimesCoso - beta), + aplus1 + aminus1TimesCoso + beta, + -2.0 * (aminus1 + aplus1 * coso), + aplus1 + aminus1TimesCoso - beta); +} + +void IIRFilter::makeHighShelf (const double sampleRate, + const double cutOffFrequency, + const double Q, + const float gainFactor) throw() +{ + jassert (sampleRate > 0); + jassert (Q > 0); + + const double A = jmax (0.0f, gainFactor); + const double aminus1 = A - 1.0; + const double aplus1 = A + 1.0; + const double omega = (double_Pi * 2.0 * jmax (cutOffFrequency, 2.0)) / sampleRate; + const double coso = cos (omega); + const double beta = sin (omega) * sqrt (A) / Q; + const double aminus1TimesCoso = aminus1 * coso; + + setCoefficients (A * (aplus1 + aminus1TimesCoso + beta), + A * -2.0 * (aminus1 + aplus1 * coso), + A * (aplus1 + aminus1TimesCoso - beta), + aplus1 - aminus1TimesCoso + beta, + 2.0 * (aminus1 - aplus1 * coso), + aplus1 - aminus1TimesCoso - beta); +} + +void IIRFilter::makeBandPass (const double sampleRate, + const double centreFrequency, + const double Q, + const float gainFactor) throw() +{ + jassert (sampleRate > 0); + jassert (Q > 0); + + const double A = jmax (0.0f, gainFactor); + const double omega = (double_Pi * 2.0 * jmax (centreFrequency, 2.0)) / sampleRate; + const double alpha = 0.5 * sin (omega) / Q; + const double c2 = -2.0 * cos (omega); + const double alphaTimesA = alpha * A; + const double alphaOverA = alpha / A; + + setCoefficients (1.0 + alphaTimesA, + c2, + 1.0 - alphaTimesA, + 1.0 + alphaOverA, + c2, + 1.0 - alphaOverA); +} + +void IIRFilter::makeInactive() throw() +{ + const ScopedLock sl (processLock); + active = false; +} + +void IIRFilter::copyCoefficientsFrom (const IIRFilter& other) throw() +{ + const ScopedLock sl (processLock); + + memcpy (coefficients, other.coefficients, sizeof (coefficients)); + active = other.active; +} + +void IIRFilter::setCoefficients (double c1, + double c2, + double c3, + double c4, + double c5, + double c6) throw() +{ + const double a = 1.0 / c4; + + c1 *= a; + c2 *= a; + c3 *= a; + c5 *= a; + c6 *= a; + + const ScopedLock sl (processLock); + + coefficients[0] = (float) c1; + coefficients[1] = (float) c2; + coefficients[2] = (float) c3; + coefficients[3] = (float) c4; + coefficients[4] = (float) c5; + coefficients[5] = (float) c6; + + active = true; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_IIRFilter.cpp *********/ + +/********* Start of inlined file: juce_MidiBuffer.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +MidiBuffer::MidiBuffer() throw() + : ArrayAllocationBase (32), + bytesUsed (0) +{ +} + +MidiBuffer::MidiBuffer (const MidiBuffer& other) throw() + : ArrayAllocationBase (32), + bytesUsed (other.bytesUsed) +{ + ensureAllocatedSize (bytesUsed); + memcpy (elements, other.elements, bytesUsed); +} + +const MidiBuffer& MidiBuffer::operator= (const MidiBuffer& other) throw() +{ + if (this != &other) + { + bytesUsed = other.bytesUsed; + ensureAllocatedSize (bytesUsed); + + if (bytesUsed > 0) + memcpy (elements, other.elements, bytesUsed); + } + + return *this; +} + +MidiBuffer::~MidiBuffer() throw() +{ +} + +void MidiBuffer::clear() throw() +{ + bytesUsed = 0; +} + +void MidiBuffer::clear (const int startSample, + const int numSamples) throw() +{ + uint8* const start = findEventAfter (elements, startSample - 1); + uint8* const end = findEventAfter (start, startSample + numSamples - 1); + + if (end > start) + { + const size_t bytesToMove = (size_t) (bytesUsed - (end - elements)); + + if (bytesToMove > 0) + memmove (start, end, bytesToMove); + + bytesUsed -= (int) (end - start); + } +} + +void MidiBuffer::addEvent (const MidiMessage& m, + const int sampleNumber) throw() +{ + addEvent (m.getRawData(), m.getRawDataSize(), sampleNumber); +} + +static int findActualEventLength (const uint8* const data, + const int maxBytes) throw() +{ + unsigned int byte = (unsigned int) *data; + + int size = 0; + + if (byte == 0xf0 || byte == 0xf7) + { + const uint8* d = data + 1; + + while (d < data + maxBytes) + if (*d++ == 0xf7) + break; + + size = (int) (d - data); + } + else if (byte == 0xff) + { + int n; + const int bytesLeft = MidiMessage::readVariableLengthVal (data + 1, n); + size = jmin (maxBytes, n + 2 + bytesLeft); + } + else if (byte >= 0x80) + { + size = jmin (maxBytes, MidiMessage::getMessageLengthFromFirstByte ((uint8) byte)); + } + + return size; +} + +void MidiBuffer::addEvent (const uint8* const newData, + const int maxBytes, + const int sampleNumber) throw() +{ + const int numBytes = findActualEventLength (newData, maxBytes); + + if (numBytes > 0) + { + ensureAllocatedSize (bytesUsed + numBytes + 6); + + uint8* d = findEventAfter (elements, sampleNumber); + const size_t bytesToMove = (size_t) (bytesUsed - (d - elements)); + + if (bytesToMove > 0) + memmove (d + numBytes + 6, + d, + bytesToMove); + + *(int*) d = sampleNumber; + d += 4; + *(uint16*) d = (uint16) numBytes; + d += 2; + + memcpy (d, newData, numBytes); + + bytesUsed += numBytes + 6; + } +} + +void MidiBuffer::addEvents (const MidiBuffer& otherBuffer, + const int startSample, + const int numSamples, + const int sampleDeltaToAdd) throw() +{ + Iterator i (otherBuffer); + i.setNextSamplePosition (startSample); + + const uint8* data; + int size, position; + + while (i.getNextEvent (data, size, position) + && (position < startSample + numSamples || numSamples < 0)) + { + addEvent (data, size, position + sampleDeltaToAdd); + } +} + +bool MidiBuffer::isEmpty() const throw() +{ + return bytesUsed == 0; +} + +int MidiBuffer::getNumEvents() const throw() +{ + int n = 0; + const uint8* d = elements; + const uint8* const end = elements + bytesUsed; + + while (d < end) + { + d += 4; + d += 2 + *(const uint16*) d; + ++n; + } + + return n; +} + +int MidiBuffer::getFirstEventTime() const throw() +{ + return (bytesUsed > 0) ? *(const int*) elements : 0; +} + +int MidiBuffer::getLastEventTime() const throw() +{ + if (bytesUsed == 0) + return 0; + + const uint8* d = elements; + const uint8* const endData = d + bytesUsed; + + for (;;) + { + const uint8* nextOne = d + 6 + * (const uint16*) (d + 4); + + if (nextOne >= endData) + return *(const int*) d; + + d = nextOne; + } +} + +uint8* MidiBuffer::findEventAfter (uint8* d, const int samplePosition) const throw() +{ + const uint8* const endData = elements + bytesUsed; + + while (d < endData && *(int*) d <= samplePosition) + { + d += 4; + d += 2 + *(uint16*) d; + } + + return d; +} + +MidiBuffer::Iterator::Iterator (const MidiBuffer& buffer) throw() + : buffer (buffer), + data (buffer.elements) +{ +} + +MidiBuffer::Iterator::~Iterator() throw() +{ +} + +void MidiBuffer::Iterator::setNextSamplePosition (const int samplePosition) throw() +{ + data = buffer.elements; + const uint8* dataEnd = buffer.elements + buffer.bytesUsed; + + while (data < dataEnd && *(int*) data < samplePosition) + { + data += 4; + data += 2 + *(uint16*) data; + } +} + +bool MidiBuffer::Iterator::getNextEvent (const uint8* &midiData, + int& numBytes, + int& samplePosition) throw() +{ + if (data >= buffer.elements + buffer.bytesUsed) + return false; + + samplePosition = *(int*) data; + data += 4; + numBytes = *(uint16*) data; + data += 2; + midiData = data; + data += numBytes; + + return true; +} + +bool MidiBuffer::Iterator::getNextEvent (MidiMessage& result, + int& samplePosition) throw() +{ + if (data >= buffer.elements + buffer.bytesUsed) + return false; + + samplePosition = *(int*) data; + data += 4; + const int numBytes = *(uint16*) data; + data += 2; + result = MidiMessage (data, numBytes, samplePosition); + data += numBytes; + + return true; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_MidiBuffer.cpp *********/ + +/********* Start of inlined file: juce_MidiFile.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +struct TempoInfo +{ + double bpm, timestamp; +}; + +struct TimeSigInfo +{ + int numerator, denominator; + double timestamp; +}; + +MidiFile::MidiFile() throw() + : numTracks (0), + timeFormat ((short)(unsigned short)0xe728) +{ +} + +MidiFile::~MidiFile() throw() +{ + clear(); +} + +void MidiFile::clear() throw() +{ + while (numTracks > 0) + delete tracks [--numTracks]; +} + +int MidiFile::getNumTracks() const throw() +{ + return numTracks; +} + +const MidiMessageSequence* MidiFile::getTrack (const int index) const throw() +{ + return (((unsigned int) index) < (unsigned int) numTracks) ? tracks[index] : 0; +} + +void MidiFile::addTrack (const MidiMessageSequence& trackSequence) throw() +{ + jassert (numTracks < numElementsInArray (tracks)); + + if (numTracks < numElementsInArray (tracks)) + tracks [numTracks++] = new MidiMessageSequence (trackSequence); +} + +short MidiFile::getTimeFormat() const throw() +{ + return timeFormat; +} + +void MidiFile::setTicksPerQuarterNote (const int ticks) throw() +{ + timeFormat = (short)ticks; +} + +void MidiFile::setSmpteTimeFormat (const int framesPerSecond, + const int subframeResolution) throw() +{ + timeFormat = (short) (((-framesPerSecond) << 8) | subframeResolution); +} + +void MidiFile::findAllTempoEvents (MidiMessageSequence& tempoChangeEvents) const +{ + for (int i = numTracks; --i >= 0;) + { + const int numEvents = tracks[i]->getNumEvents(); + + for (int j = 0; j < numEvents; ++j) + { + const MidiMessage& m = tracks[i]->getEventPointer (j)->message; + + if (m.isTempoMetaEvent()) + tempoChangeEvents.addEvent (m); + } + } +} + +void MidiFile::findAllTimeSigEvents (MidiMessageSequence& timeSigEvents) const +{ + for (int i = numTracks; --i >= 0;) + { + const int numEvents = tracks[i]->getNumEvents(); + + for (int j = 0; j < numEvents; ++j) + { + const MidiMessage& m = tracks[i]->getEventPointer (j)->message; + + if (m.isTimeSignatureMetaEvent()) + timeSigEvents.addEvent (m); + } + } +} + +double MidiFile::getLastTimestamp() const +{ + double t = 0.0; + + for (int i = numTracks; --i >= 0;) + t = jmax (t, tracks[i]->getEndTime()); + + return t; +} + +static bool parseMidiHeader (const char* &data, + short& timeFormat, + short& fileType, + short& numberOfTracks) +{ + unsigned int ch = (int) bigEndianInt (data); + data += 4; + + if (ch != bigEndianInt ("MThd")) + { + bool ok = false; + + if (ch == bigEndianInt ("RIFF")) + { + for (int i = 0; i < 8; ++i) + { + ch = bigEndianInt (data); + data += 4; + + if (ch == bigEndianInt ("MThd")) + { + ok = true; + break; + } + } + } + + if (! ok) + return false; + } + + unsigned int bytesRemaining = bigEndianInt (data); + data += 4; + fileType = (short)bigEndianShort (data); + data += 2; + numberOfTracks = (short)bigEndianShort (data); + data += 2; + timeFormat = (short)bigEndianShort (data); + data += 2; + bytesRemaining -= 6; + data += bytesRemaining; + + return true; +} + +bool MidiFile::readFrom (InputStream& sourceStream) +{ + clear(); + MemoryBlock data; + + const int maxSensibleMidiFileSize = 2 * 1024 * 1024; + + // (put a sanity-check on the file size, as midi files are generally small) + if (sourceStream.readIntoMemoryBlock (data, maxSensibleMidiFileSize)) + { + int size = data.getSize(); + const char* d = (char*) data.getData(); + short fileType, expectedTracks; + + if (size > 16 && parseMidiHeader (d, timeFormat, fileType, expectedTracks)) + { + size -= (int) (d - (char*) data.getData()); + + int track = 0; + + while (size > 0 && track < expectedTracks) + { + const int chunkType = (int)bigEndianInt (d); + d += 4; + const int chunkSize = (int)bigEndianInt (d); + d += 4; + + if (chunkSize <= 0) + break; + + if (size < 0) + return false; + + if (chunkType == (int)bigEndianInt ("MTrk")) + { + readNextTrack (d, chunkSize); + } + + size -= chunkSize + 8; + d += chunkSize; + ++track; + } + + return true; + } + } + + return false; +} + +// a comparator that puts all the note-offs before note-ons that have the same time +int MidiFile::compareElements (const MidiMessageSequence::MidiEventHolder* const first, + const MidiMessageSequence::MidiEventHolder* const second) throw() +{ + const double diff = (first->message.getTimeStamp() - second->message.getTimeStamp()); + + if (diff == 0) + { + if (first->message.isNoteOff() && second->message.isNoteOn()) + return -1; + else if (first->message.isNoteOn() && second->message.isNoteOff()) + return 1; + else + return 0; + } + else + { + return (diff > 0) ? 1 : -1; + } +} + +void MidiFile::readNextTrack (const char* data, int size) +{ + double time = 0; + char lastStatusByte = 0; + + MidiMessageSequence result; + + while (size > 0) + { + int bytesUsed; + const int delay = MidiMessage::readVariableLengthVal ((const uint8*) data, bytesUsed); + data += bytesUsed; + size -= bytesUsed; + time += delay; + + int messSize = 0; + const MidiMessage mm ((const uint8*) data, size, messSize, lastStatusByte, time); + + if (messSize <= 0) + break; + + size -= messSize; + data += messSize; + + result.addEvent (mm); + + const char firstByte = *(mm.getRawData()); + if ((firstByte & 0xf0) != 0xf0) + lastStatusByte = firstByte; + } + + // use a sort that puts all the note-offs before note-ons that have the same time + result.list.sort (*this, true); + + result.updateMatchedPairs(); + + addTrack (result); +} + +static double convertTicksToSeconds (const double time, + const MidiMessageSequence& tempoEvents, + const int timeFormat) +{ + if (timeFormat > 0) + { + int numer = 4, denom = 4; + double tempoTime = 0.0, correctedTempoTime = 0.0; + const double tickLen = 1.0 / (timeFormat & 0x7fff); + double secsPerTick = 0.5 * tickLen; + const int numEvents = tempoEvents.getNumEvents(); + + for (int i = 0; i < numEvents; ++i) + { + const MidiMessage& m = tempoEvents.getEventPointer(i)->message; + + if (time <= m.getTimeStamp()) + break; + + if (timeFormat > 0) + { + correctedTempoTime = correctedTempoTime + + (m.getTimeStamp() - tempoTime) * secsPerTick; + } + else + { + correctedTempoTime = tickLen * m.getTimeStamp() / (((timeFormat & 0x7fff) >> 8) * (timeFormat & 0xff)); + } + + tempoTime = m.getTimeStamp(); + + if (m.isTempoMetaEvent()) + secsPerTick = tickLen * m.getTempoSecondsPerQuarterNote(); + else if (m.isTimeSignatureMetaEvent()) + m.getTimeSignatureInfo (numer, denom); + + while (i + 1 < numEvents) + { + const MidiMessage& m2 = tempoEvents.getEventPointer(i + 1)->message; + if (m2.getTimeStamp() == tempoTime) + { + ++i; + + if (m2.isTempoMetaEvent()) + secsPerTick = tickLen * m2.getTempoSecondsPerQuarterNote(); + else if (m2.isTimeSignatureMetaEvent()) + m2.getTimeSignatureInfo (numer, denom); + } + else + { + break; + } + } + + } + + return correctedTempoTime + (time - tempoTime) * secsPerTick; + } + else + { + return time / (((timeFormat & 0x7fff) >> 8) * (timeFormat & 0xff)); + } +} + +void MidiFile::convertTimestampTicksToSeconds() +{ + MidiMessageSequence tempoEvents; + findAllTempoEvents (tempoEvents); + findAllTimeSigEvents (tempoEvents); + + for (int i = 0; i < numTracks; ++i) + { + MidiMessageSequence& ms = *tracks[i]; + + for (int j = ms.getNumEvents(); --j >= 0;) + { + MidiMessage& m = ms.getEventPointer(j)->message; + + m.setTimeStamp (convertTicksToSeconds (m.getTimeStamp(), + tempoEvents, + timeFormat)); + } + } +} + +static void writeVariableLengthInt (OutputStream& out, unsigned int v) +{ + unsigned int buffer = v & 0x7F; + + while ((v >>= 7) != 0) + { + buffer <<= 8; + buffer |= ((v & 0x7F) | 0x80); + } + + for (;;) + { + out.writeByte ((char) buffer); + + if (buffer & 0x80) + buffer >>= 8; + else + break; + } +} + +bool MidiFile::writeTo (OutputStream& out) +{ + out.writeIntBigEndian ((int) bigEndianInt ("MThd")); + out.writeIntBigEndian (6); + out.writeShortBigEndian (1); // type + out.writeShortBigEndian (numTracks); + out.writeShortBigEndian (timeFormat); + + for (int i = 0; i < numTracks; ++i) + writeTrack (out, i); + + out.flush(); + + return true; +} + +void MidiFile::writeTrack (OutputStream& mainOut, + const int trackNum) +{ + MemoryOutputStream out; + + const MidiMessageSequence& ms = *tracks[trackNum]; + + int lastTick = 0; + char lastStatusByte = 0; + + for (int i = 0; i < ms.getNumEvents(); ++i) + { + const MidiMessage& mm = ms.getEventPointer(i)->message; + + const int tick = roundDoubleToInt (mm.getTimeStamp()); + const int delta = jmax (0, tick - lastTick); + writeVariableLengthInt (out, delta); + lastTick = tick; + + const char statusByte = *(mm.getRawData()); + + if ((statusByte == lastStatusByte) + && ((statusByte & 0xf0) != 0xf0) + && i > 0 + && mm.getRawDataSize() > 1) + { + out.write (mm.getRawData() + 1, mm.getRawDataSize() - 1); + } + else + { + out.write (mm.getRawData(), mm.getRawDataSize()); + } + + lastStatusByte = statusByte; + } + + out.writeByte (0); + const MidiMessage m (MidiMessage::endOfTrack()); + out.write (m.getRawData(), + m.getRawDataSize()); + + mainOut.writeIntBigEndian ((int)bigEndianInt ("MTrk")); + mainOut.writeIntBigEndian (out.getDataSize()); + mainOut.write (out.getData(), out.getDataSize()); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_MidiFile.cpp *********/ + +/********* Start of inlined file: juce_MidiKeyboardState.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +MidiKeyboardState::MidiKeyboardState() + : listeners (2) +{ + zeromem (noteStates, sizeof (noteStates)); +} + +MidiKeyboardState::~MidiKeyboardState() +{ +} + +void MidiKeyboardState::reset() +{ + const ScopedLock sl (lock); + zeromem (noteStates, sizeof (noteStates)); + eventsToAdd.clear(); +} + +bool MidiKeyboardState::isNoteOn (const int midiChannel, const int n) const throw() +{ + jassert (midiChannel >= 0 && midiChannel <= 16); + + return ((unsigned int) n) < 128 + && (noteStates[n] & (1 << (midiChannel - 1))) != 0; +} + +bool MidiKeyboardState::isNoteOnForChannels (const int midiChannelMask, const int n) const throw() +{ + return ((unsigned int) n) < 128 + && (noteStates[n] & midiChannelMask) != 0; +} + +void MidiKeyboardState::noteOn (const int midiChannel, const int midiNoteNumber, const float velocity) +{ + jassert (midiChannel >= 0 && midiChannel <= 16); + jassert (((unsigned int) midiNoteNumber) < 128); + + const ScopedLock sl (lock); + + if (((unsigned int) midiNoteNumber) < 128) + { + const int timeNow = (int) Time::getMillisecondCounter(); + eventsToAdd.addEvent (MidiMessage::noteOn (midiChannel, midiNoteNumber, velocity), timeNow); + eventsToAdd.clear (0, timeNow - 500); + + noteOnInternal (midiChannel, midiNoteNumber, velocity); + } +} + +void MidiKeyboardState::noteOnInternal (const int midiChannel, const int midiNoteNumber, const float velocity) +{ + if (((unsigned int) midiNoteNumber) < 128) + { + noteStates [midiNoteNumber] |= (1 << (midiChannel - 1)); + + for (int i = listeners.size(); --i >= 0;) + ((MidiKeyboardStateListener*) listeners.getUnchecked(i)) + ->handleNoteOn (this, midiChannel, midiNoteNumber, velocity); + } +} + +void MidiKeyboardState::noteOff (const int midiChannel, const int midiNoteNumber) +{ + const ScopedLock sl (lock); + + if (isNoteOn (midiChannel, midiNoteNumber)) + { + const int timeNow = (int) Time::getMillisecondCounter(); + eventsToAdd.addEvent (MidiMessage::noteOff (midiChannel, midiNoteNumber), timeNow); + eventsToAdd.clear (0, timeNow - 500); + + noteOffInternal (midiChannel, midiNoteNumber); + } +} + +void MidiKeyboardState::noteOffInternal (const int midiChannel, const int midiNoteNumber) +{ + if (isNoteOn (midiChannel, midiNoteNumber)) + { + noteStates [midiNoteNumber] &= ~(1 << (midiChannel - 1)); + + for (int i = listeners.size(); --i >= 0;) + ((MidiKeyboardStateListener*) listeners.getUnchecked(i)) + ->handleNoteOff (this, midiChannel, midiNoteNumber); + } +} + +void MidiKeyboardState::allNotesOff (const int midiChannel) +{ + const ScopedLock sl (lock); + + if (midiChannel <= 0) + { + for (int i = 1; i <= 16; ++i) + allNotesOff (i); + } + else + { + for (int i = 0; i < 128; ++i) + noteOff (midiChannel, i); + } +} + +void MidiKeyboardState::processNextMidiEvent (const MidiMessage& message) +{ + if (message.isNoteOn()) + { + noteOnInternal (message.getChannel(), message.getNoteNumber(), message.getFloatVelocity()); + } + else if (message.isNoteOff()) + { + noteOffInternal (message.getChannel(), message.getNoteNumber()); + } + else if (message.isAllNotesOff()) + { + for (int i = 0; i < 128; ++i) + noteOffInternal (message.getChannel(), i); + } +} + +void MidiKeyboardState::processNextMidiBuffer (MidiBuffer& buffer, + const int startSample, + const int numSamples, + const bool injectIndirectEvents) +{ + MidiBuffer::Iterator i (buffer); + MidiMessage message (0xf4, 0.0); + int time; + + const ScopedLock sl (lock); + + while (i.getNextEvent (message, time)) + processNextMidiEvent (message); + + if (injectIndirectEvents) + { + MidiBuffer::Iterator i2 (eventsToAdd); + const int firstEventToAdd = eventsToAdd.getFirstEventTime(); + const double scaleFactor = numSamples / (double) (eventsToAdd.getLastEventTime() + 1 - firstEventToAdd); + + while (i2.getNextEvent (message, time)) + { + const int pos = jlimit (0, numSamples - 1, roundDoubleToInt ((time - firstEventToAdd) * scaleFactor)); + buffer.addEvent (message, startSample + pos); + } + } + + eventsToAdd.clear(); +} + +void MidiKeyboardState::addListener (MidiKeyboardStateListener* const listener) throw() +{ + const ScopedLock sl (lock); + listeners.addIfNotAlreadyThere (listener); +} + +void MidiKeyboardState::removeListener (MidiKeyboardStateListener* const listener) throw() +{ + const ScopedLock sl (lock); + listeners.removeValue (listener); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_MidiKeyboardState.cpp *********/ + +/********* Start of inlined file: juce_MidiMessage.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +int MidiMessage::readVariableLengthVal (const uint8* data, + int& numBytesUsed) throw() +{ + numBytesUsed = 0; + int v = 0; + int i; + + do + { + i = (int) *data++; + + if (++numBytesUsed > 6) + break; + + v = (v << 7) + (i & 0x7f); + + } while (i & 0x80); + + return v; +} + +int MidiMessage::getMessageLengthFromFirstByte (const uint8 firstByte) throw() +{ + // this method only works for valid starting bytes of a short midi message + jassert (firstByte >= 0x80 + && firstByte != 0xff + && firstByte != 0xf0 + && firstByte != 0xf7); + + static const char messageLengths[] = + { + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 1, 2, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + }; + + return messageLengths [firstByte & 0x7f]; +} + +MidiMessage::MidiMessage (const uint8* const d, + const int dataSize, + const double t) throw() + : timeStamp (t), + message (0), + size (dataSize) +{ + jassert (dataSize > 0); + + if (dataSize <= 4) + data = (uint8*) &message; + else + data = (uint8*) juce_malloc (dataSize); + + memcpy (data, d, dataSize); + + // check that the length matches the data.. + jassert (size > 3 || *d >= 0xf0 || getMessageLengthFromFirstByte (*d) == size); +} + +MidiMessage::MidiMessage (const int byte1, + const double t) throw() + : timeStamp (t), + data ((uint8*) &message), + size (1) +{ + data[0] = (uint8) byte1; + + // check that the length matches the data.. + jassert (byte1 >= 0xf0 || getMessageLengthFromFirstByte ((uint8) byte1) == 1); +} + +MidiMessage::MidiMessage (const int byte1, + const int byte2, + const double t) throw() + : timeStamp (t), + data ((uint8*) &message), + size (2) +{ + data[0] = (uint8) byte1; + data[1] = (uint8) byte2; + + // check that the length matches the data.. + jassert (byte1 >= 0xf0 || getMessageLengthFromFirstByte ((uint8) byte1) == 2); +} + +MidiMessage::MidiMessage (const int byte1, + const int byte2, + const int byte3, + const double t) throw() + : timeStamp (t), + data ((uint8*) &message), + size (3) +{ + data[0] = (uint8) byte1; + data[1] = (uint8) byte2; + data[2] = (uint8) byte3; + + // check that the length matches the data.. + jassert (byte1 >= 0xf0 || getMessageLengthFromFirstByte ((uint8) byte1) == 3); +} + +MidiMessage::MidiMessage (const MidiMessage& other) throw() + : timeStamp (other.timeStamp), + message (other.message), + size (other.size) +{ + if (other.data != (uint8*) &other.message) + { + data = (uint8*) juce_malloc (size); + memcpy (data, other.data, size); + } + else + { + data = (uint8*) &message; + } +} + +MidiMessage::MidiMessage (const MidiMessage& other, + const double newTimeStamp) throw() + : timeStamp (newTimeStamp), + message (other.message), + size (other.size) +{ + if (other.data != (uint8*) &other.message) + { + data = (uint8*) juce_malloc (size); + memcpy (data, other.data, size); + } + else + { + data = (uint8*) &message; + } +} + +MidiMessage::MidiMessage (const uint8* src, + int sz, + int& numBytesUsed, + const uint8 lastStatusByte, + double t) throw() + : timeStamp (t), + data ((uint8*) &message), + message (0) +{ + unsigned int byte = (unsigned int) *src; + + if (byte < 0x80) + { + byte = (unsigned int) (uint8) lastStatusByte; + numBytesUsed = -1; + } + else + { + numBytesUsed = 0; + --sz; + ++src; + } + + if (byte >= 0x80) + { + if (byte == 0xf0) + { + const uint8* d = (const uint8*) src; + + while (d < src + sz) + { + if (*d >= 0x80) // stop if we hit a status byte, and don't include it in this message + { + if (*d == 0xf7) // include an 0xf7 if we hit one + ++d; + + break; + } + + ++d; + } + + size = 1 + (int) (d - src); + + data = (uint8*) juce_malloc (size); + *data = (uint8) byte; + memcpy (data + 1, src, size - 1); + } + else if (byte == 0xff) + { + int n; + const int bytesLeft = readVariableLengthVal (src + 1, n); + size = jmin (sz + 1, n + 2 + bytesLeft); + + data = (uint8*) juce_malloc (size); + *data = (uint8) byte; + memcpy (data + 1, src, size - 1); + } + else + { + size = getMessageLengthFromFirstByte ((uint8) byte); + *data = (uint8) byte; + + if (size > 1) + { + data[1] = src[0]; + + if (size > 2) + data[2] = src[1]; + } + } + + numBytesUsed += size; + } + else + { + message = 0; + size = 0; + } +} + +const MidiMessage& MidiMessage::operator= (const MidiMessage& other) throw() +{ + if (this == &other) + return *this; + + timeStamp = other.timeStamp; + size = other.size; + message = other.message; + + if (data != (uint8*) &message) + juce_free (data); + + if (other.data != (uint8*) &other.message) + { + data = (uint8*) juce_malloc (size); + memcpy (data, other.data, size); + } + else + { + data = (uint8*) &message; + } + + return *this; +} + +MidiMessage::~MidiMessage() throw() +{ + if (data != (uint8*) &message) + juce_free (data); +} + +int MidiMessage::getChannel() const throw() +{ + if ((data[0] & 0xf0) != 0xf0) + return (data[0] & 0xf) + 1; + else + return 0; +} + +bool MidiMessage::isForChannel (const int channel) const throw() +{ + return ((data[0] & 0xf) == channel - 1) + && ((data[0] & 0xf0) != 0xf0); +} + +void MidiMessage::setChannel (const int channel) throw() +{ + if ((data[0] & 0xf0) != (uint8) 0xf0) + data[0] = (uint8) ((data[0] & (uint8)0xf0) + | (uint8)(channel - 1)); +} + +bool MidiMessage::isNoteOn() const throw() +{ + return ((data[0] & 0xf0) == 0x90) + && (data[2] != 0); +} + +bool MidiMessage::isNoteOff() const throw() +{ + return ((data[0] & 0xf0) == 0x80) + || ((data[2] == 0) && ((data[0] & 0xf0) == 0x90)); +} + +bool MidiMessage::isNoteOnOrOff() const throw() +{ + const int d = data[0] & 0xf0; + return (d == 0x90) || (d == 0x80); +} + +int MidiMessage::getNoteNumber() const throw() +{ + return data[1]; +} + +void MidiMessage::setNoteNumber (const int newNoteNumber) throw() +{ + if (isNoteOnOrOff()) + data[1] = (uint8) jlimit (0, 127, newNoteNumber); +} + +uint8 MidiMessage::getVelocity() const throw() +{ + if (isNoteOnOrOff()) + return data[2]; + else + return 0; +} + +float MidiMessage::getFloatVelocity() const throw() +{ + return getVelocity() * (1.0f / 127.0f); +} + +void MidiMessage::setVelocity (const float newVelocity) throw() +{ + if (isNoteOnOrOff()) + data[2] = (uint8) jlimit (0, 0x7f, roundFloatToInt (newVelocity * 127.0f)); +} + +void MidiMessage::multiplyVelocity (const float scaleFactor) throw() +{ + if (isNoteOnOrOff()) + data[2] = (uint8) jlimit (0, 0x7f, roundFloatToInt (scaleFactor * data[2])); +} + +bool MidiMessage::isAftertouch() const throw() +{ + return (data[0] & 0xf0) == 0xa0; +} + +int MidiMessage::getAfterTouchValue() const throw() +{ + return data[2]; +} + +const MidiMessage MidiMessage::aftertouchChange (const int channel, + const int noteNum, + const int aftertouchValue) throw() +{ + jassert (channel > 0 && channel <= 16); + jassert (((unsigned int) noteNum) <= 127); + jassert (((unsigned int) aftertouchValue) <= 127); + + return MidiMessage (0xa0 | jlimit (0, 15, channel - 1), + noteNum & 0x7f, + aftertouchValue & 0x7f); +} + +bool MidiMessage::isChannelPressure() const throw() +{ + return (data[0] & 0xf0) == 0xd0; +} + +int MidiMessage::getChannelPressureValue() const throw() +{ + jassert (isChannelPressure()); + + return data[1]; +} + +const MidiMessage MidiMessage::channelPressureChange (const int channel, + const int pressure) throw() +{ + jassert (channel > 0 && channel <= 16); + jassert (((unsigned int) pressure) <= 127); + + return MidiMessage (0xd0 | jlimit (0, 15, channel - 1), + pressure & 0x7f); +} + +bool MidiMessage::isProgramChange() const throw() +{ + return (data[0] & 0xf0) == 0xc0; +} + +int MidiMessage::getProgramChangeNumber() const throw() +{ + return data[1]; +} + +const MidiMessage MidiMessage::programChange (const int channel, + const int programNumber) throw() +{ + jassert (channel > 0 && channel <= 16); + + return MidiMessage (0xc0 | jlimit (0, 15, channel - 1), + programNumber & 0x7f); +} + +bool MidiMessage::isPitchWheel() const throw() +{ + return (data[0] & 0xf0) == 0xe0; +} + +int MidiMessage::getPitchWheelValue() const throw() +{ + return data[1] | (data[2] << 7); +} + +const MidiMessage MidiMessage::pitchWheel (const int channel, + const int position) throw() +{ + jassert (channel > 0 && channel <= 16); + jassert (((unsigned int) position) <= 0x3fff); + + return MidiMessage (0xe0 | jlimit (0, 15, channel - 1), + position & 127, + (position >> 7) & 127); +} + +bool MidiMessage::isController() const throw() +{ + return (data[0] & 0xf0) == 0xb0; +} + +int MidiMessage::getControllerNumber() const throw() +{ + jassert (isController()); + + return data[1]; +} + +int MidiMessage::getControllerValue() const throw() +{ + jassert (isController()); + + return data[2]; +} + +const MidiMessage MidiMessage::controllerEvent (const int channel, + const int controllerType, + const int value) throw() +{ + // the channel must be between 1 and 16 inclusive + jassert (channel > 0 && channel <= 16); + + return MidiMessage (0xb0 | jlimit (0, 15, channel - 1), + controllerType & 127, + value & 127); +} + +const MidiMessage MidiMessage::noteOn (const int channel, + const int noteNumber, + const float velocity) throw() +{ + return noteOn (channel, noteNumber, (uint8)(velocity * 127.0f)); +} + +const MidiMessage MidiMessage::noteOn (const int channel, + const int noteNumber, + const uint8 velocity) throw() +{ + jassert (channel > 0 && channel <= 16); + jassert (((unsigned int) noteNumber) <= 127); + + return MidiMessage (0x90 | jlimit (0, 15, channel - 1), + noteNumber & 127, + jlimit (0, 127, roundFloatToInt (velocity))); +} + +const MidiMessage MidiMessage::noteOff (const int channel, + const int noteNumber) throw() +{ + jassert (channel > 0 && channel <= 16); + jassert (((unsigned int) noteNumber) <= 127); + + return MidiMessage (0x80 | jlimit (0, 15, channel - 1), noteNumber & 127, 0); +} + +const MidiMessage MidiMessage::allNotesOff (const int channel) throw() +{ + jassert (channel > 0 && channel <= 16); + + return controllerEvent (channel, 123, 0); +} + +bool MidiMessage::isAllNotesOff() const throw() +{ + return (data[0] & 0xf0) == 0xb0 + && data[1] == 123; +} + +const MidiMessage MidiMessage::allSoundOff (const int channel) throw() +{ + return controllerEvent (channel, 120, 0); +} + +bool MidiMessage::isAllSoundOff() const throw() +{ + return (data[0] & 0xf0) == 0xb0 + && data[1] == 120; +} + +const MidiMessage MidiMessage::allControllersOff (const int channel) throw() +{ + return controllerEvent (channel, 121, 0); +} + +const MidiMessage MidiMessage::masterVolume (const float volume) throw() +{ + const int vol = jlimit (0, 0x3fff, roundFloatToInt (volume * 0x4000)); + + uint8 buf[8]; + buf[0] = 0xf0; + buf[1] = 0x7f; + buf[2] = 0x7f; + buf[3] = 0x04; + buf[4] = 0x01; + buf[5] = (uint8) (vol & 0x7f); + buf[6] = (uint8) (vol >> 7); + buf[7] = 0xf7; + + return MidiMessage (buf, 8); +} + +bool MidiMessage::isSysEx() const throw() +{ + return *data == 0xf0; +} + +const MidiMessage MidiMessage::createSysExMessage (const uint8* sysexData, + const int dataSize) throw() +{ + MemoryBlock mm (dataSize + 2); + uint8* const m = (uint8*) mm.getData(); + + m[0] = 0xf0; + memcpy (m + 1, sysexData, dataSize); + m[dataSize + 1] = 0xf7; + + return MidiMessage (m, dataSize + 2); +} + +const uint8* MidiMessage::getSysExData() const throw() +{ + return (isSysEx()) ? getRawData() + 1 + : 0; +} + +int MidiMessage::getSysExDataSize() const throw() +{ + return (isSysEx()) ? size - 2 + : 0; +} + +bool MidiMessage::isMetaEvent() const throw() +{ + return *data == 0xff; +} + +bool MidiMessage::isActiveSense() const throw() +{ + return *data == 0xfe; +} + +int MidiMessage::getMetaEventType() const throw() +{ + if (*data != 0xff) + return -1; + else + return data[1]; +} + +int MidiMessage::getMetaEventLength() const throw() +{ + if (*data == 0xff) + { + int n; + return jmin (size - 2, readVariableLengthVal (data + 2, n)); + } + + return 0; +} + +const uint8* MidiMessage::getMetaEventData() const throw() +{ + int n; + const uint8* d = data + 2; + readVariableLengthVal (d, n); + return d + n; +} + +bool MidiMessage::isTrackMetaEvent() const throw() +{ + return getMetaEventType() == 0; +} + +bool MidiMessage::isEndOfTrackMetaEvent() const throw() +{ + return getMetaEventType() == 47; +} + +bool MidiMessage::isTextMetaEvent() const throw() +{ + const int t = getMetaEventType(); + + return t > 0 && t < 16; +} + +const String MidiMessage::getTextFromTextMetaEvent() const throw() +{ + return String ((const char*) getMetaEventData(), + getMetaEventLength()); +} + +bool MidiMessage::isTrackNameEvent() const throw() +{ + return (data[1] == 3) + && (*data == 0xff); +} + +bool MidiMessage::isTempoMetaEvent() const throw() +{ + return (data[1] == 81) + && (*data == 0xff); +} + +bool MidiMessage::isMidiChannelMetaEvent() const throw() +{ + return (data[1] == 0x20) + && (*data == 0xff) + && (data[2] == 1); +} + +int MidiMessage::getMidiChannelMetaEventChannel() const throw() +{ + return data[3] + 1; +} + +double MidiMessage::getTempoSecondsPerQuarterNote() const throw() +{ + if (! isTempoMetaEvent()) + return 0.0; + + const uint8* const d = getMetaEventData(); + + return (((unsigned int) d[0] << 16) + | ((unsigned int) d[1] << 8) + | d[2]) + / 1000000.0; +} + +double MidiMessage::getTempoMetaEventTickLength (const short timeFormat) const throw() +{ + if (timeFormat > 0) + { + if (! isTempoMetaEvent()) + return 0.5 / timeFormat; + + return getTempoSecondsPerQuarterNote() / timeFormat; + } + else + { + const int frameCode = (-timeFormat) >> 8; + double framesPerSecond; + + switch (frameCode) + { + case 24: framesPerSecond = 24.0; break; + case 25: framesPerSecond = 25.0; break; + case 29: framesPerSecond = 29.97; break; + case 30: framesPerSecond = 30.0; break; + default: framesPerSecond = 30.0; break; + } + + return (1.0 / framesPerSecond) / (timeFormat & 0xff); + } +} + +const MidiMessage MidiMessage::tempoMetaEvent (int microsecondsPerQuarterNote) throw() +{ + uint8 d[8]; + d[0] = 0xff; + d[1] = 81; + d[2] = 3; + d[3] = (uint8) (microsecondsPerQuarterNote >> 16); + d[4] = (uint8) ((microsecondsPerQuarterNote >> 8) & 0xff); + d[5] = (uint8) (microsecondsPerQuarterNote & 0xff); + + return MidiMessage (d, 6, 0.0); +} + +bool MidiMessage::isTimeSignatureMetaEvent() const throw() +{ + return (data[1] == 0x58) + && (*data == (uint8) 0xff); +} + +void MidiMessage::getTimeSignatureInfo (int& numerator, + int& denominator) const throw() +{ + if (isTimeSignatureMetaEvent()) + { + const uint8* const d = getMetaEventData(); + numerator = d[0]; + denominator = 1 << d[1]; + } + else + { + numerator = 4; + denominator = 4; + } +} + +const MidiMessage MidiMessage::timeSignatureMetaEvent (const int numerator, + const int denominator) throw() +{ + uint8 d[8]; + d[0] = 0xff; + d[1] = 0x58; + d[2] = 0x04; + d[3] = (uint8) numerator; + + int n = 1; + int powerOfTwo = 0; + + while (n < denominator) + { + n <<= 1; + ++powerOfTwo; + } + + d[4] = (uint8) powerOfTwo; + d[5] = 0x01; + d[6] = 96; + + return MidiMessage (d, 7, 0.0); +} + +const MidiMessage MidiMessage::midiChannelMetaEvent (const int channel) throw() +{ + uint8 d[8]; + d[0] = 0xff; + d[1] = 0x20; + d[2] = 0x01; + d[3] = (uint8) jlimit (0, 0xff, channel - 1); + + return MidiMessage (d, 4, 0.0); +} + +bool MidiMessage::isKeySignatureMetaEvent() const throw() +{ + return getMetaEventType() == 89; +} + +int MidiMessage::getKeySignatureNumberOfSharpsOrFlats() const throw() +{ + return (int) *getMetaEventData(); +} + +const MidiMessage MidiMessage::endOfTrack() throw() +{ + return MidiMessage (0xff, 0x2f, 0, 0.0); +} + +bool MidiMessage::isSongPositionPointer() const throw() +{ + return *data == 0xf2; +} + +int MidiMessage::getSongPositionPointerMidiBeat() const throw() +{ + return data[1] | (data[2] << 7); +} + +const MidiMessage MidiMessage::songPositionPointer (const int positionInMidiBeats) throw() +{ + return MidiMessage (0xf2, + positionInMidiBeats & 127, + (positionInMidiBeats >> 7) & 127); +} + +bool MidiMessage::isMidiStart() const throw() +{ + return *data == 0xfa; +} + +const MidiMessage MidiMessage::midiStart() throw() +{ + return MidiMessage (0xfa); +} + +bool MidiMessage::isMidiContinue() const throw() +{ + return *data == 0xfb; +} + +const MidiMessage MidiMessage::midiContinue() throw() +{ + return MidiMessage (0xfb); +} + +bool MidiMessage::isMidiStop() const throw() +{ + return *data == 0xfc; +} + +const MidiMessage MidiMessage::midiStop() throw() +{ + return MidiMessage (0xfc); +} + +bool MidiMessage::isMidiClock() const throw() +{ + return *data == 0xf8; +} + +const MidiMessage MidiMessage::midiClock() throw() +{ + return MidiMessage (0xf8); +} + +bool MidiMessage::isQuarterFrame() const throw() +{ + return *data == 0xf1; +} + +int MidiMessage::getQuarterFrameSequenceNumber() const throw() +{ + return ((int) data[1]) >> 4; +} + +int MidiMessage::getQuarterFrameValue() const throw() +{ + return ((int) data[1]) & 0x0f; +} + +const MidiMessage MidiMessage::quarterFrame (const int sequenceNumber, + const int value) throw() +{ + return MidiMessage (0xf1, (sequenceNumber << 4) | value); +} + +bool MidiMessage::isFullFrame() const throw() +{ + return data[0] == 0xf0 + && data[1] == 0x7f + && size >= 10 + && data[3] == 0x01 + && data[4] == 0x01; +} + +void MidiMessage::getFullFrameParameters (int& hours, + int& minutes, + int& seconds, + int& frames, + MidiMessage::SmpteTimecodeType& timecodeType) const throw() +{ + jassert (isFullFrame()); + + timecodeType = (SmpteTimecodeType) (data[5] >> 5); + hours = data[5] & 0x1f; + minutes = data[6]; + seconds = data[7]; + frames = data[8]; +} + +const MidiMessage MidiMessage::fullFrame (const int hours, + const int minutes, + const int seconds, + const int frames, + MidiMessage::SmpteTimecodeType timecodeType) +{ + uint8 d[10]; + d[0] = 0xf0; + d[1] = 0x7f; + d[2] = 0x7f; + d[3] = 0x01; + d[4] = 0x01; + d[5] = (uint8) ((hours & 0x01f) | (timecodeType << 5)); + d[6] = (uint8) minutes; + d[7] = (uint8) seconds; + d[8] = (uint8) frames; + d[9] = 0xf7; + + return MidiMessage (d, 10, 0.0); +} + +bool MidiMessage::isMidiMachineControlMessage() const throw() +{ + return data[0] == 0xf0 + && data[1] == 0x7f + && data[3] == 0x06 + && size > 5; +} + +MidiMessage::MidiMachineControlCommand MidiMessage::getMidiMachineControlCommand() const throw() +{ + jassert (isMidiMachineControlMessage()); + + return (MidiMachineControlCommand) data[4]; +} + +const MidiMessage MidiMessage::midiMachineControlCommand (MidiMessage::MidiMachineControlCommand command) +{ + uint8 d[6]; + d[0] = 0xf0; + d[1] = 0x7f; + d[2] = 0x00; + d[3] = 0x06; + d[4] = (uint8) command; + d[5] = 0xf7; + + return MidiMessage (d, 6, 0.0); +} + +bool MidiMessage::isMidiMachineControlGoto (int& hours, + int& minutes, + int& seconds, + int& frames) const throw() +{ + if (size >= 12 + && data[0] == 0xf0 + && data[1] == 0x7f + && data[3] == 0x06 + && data[4] == 0x44 + && data[5] == 0x06 + && data[6] == 0x01) + { + hours = data[7] % 24; // (that some machines send out hours > 24) + minutes = data[8]; + seconds = data[9]; + frames = data[10]; + + return true; + } + + return false; +} + +const MidiMessage MidiMessage::midiMachineControlGoto (int hours, + int minutes, + int seconds, + int frames) +{ + uint8 d[12]; + d[0] = 0xf0; + d[1] = 0x7f; + d[2] = 0x00; + d[3] = 0x06; + d[4] = 0x44; + d[5] = 0x06; + d[6] = 0x01; + d[7] = (uint8) hours; + d[8] = (uint8) minutes; + d[9] = (uint8) seconds; + d[10] = (uint8) frames; + d[11] = 0xf7; + + return MidiMessage (d, 12, 0.0); +} + +const String MidiMessage::getMidiNoteName (int note, + bool useSharps, + bool includeOctaveNumber, + int octaveNumForMiddleC) throw() +{ + static const char* const sharpNoteNames[] = { "C", "C#", "D", "D#", "E", + "F", "F#", "G", "G#", "A", + "A#", "B" }; + + static const char* const flatNoteNames[] = { "C", "Db", "D", "Eb", "E", + "F", "Gb", "G", "Ab", "A", + "Bb", "B" }; + + if (((unsigned int) note) < 128) + { + const String s ((useSharps) ? sharpNoteNames [note % 12] + : flatNoteNames [note % 12]); + + if (includeOctaveNumber) + return s + String (note / 12 + (octaveNumForMiddleC - 5)); + else + return s; + } + + return String::empty; +} + +const double MidiMessage::getMidiNoteInHertz (int noteNumber) throw() +{ + noteNumber -= 12 * 6 + 9; // now 0 = A440 + return 440.0 * pow (2.0, noteNumber / 12.0); +} + +const String MidiMessage::getGMInstrumentName (int n) throw() +{ + const char *names[] = + { + "Acoustic Grand Piano", "Bright Acoustic Piano", "Electric Grand Piano", "Honky-tonk Piano", + "Electric Piano 1", "Electric Piano 2", "Harpsichord", "Clavinet", "Celesta", "Glockenspiel", + "Music Box", "Vibraphone", "Marimba", "Xylophone", "Tubular Bells", "Dulcimer", "Drawbar Organ", + "Percussive Organ", "Rock Organ", "Church Organ", "Reed Organ", "Accordion", "Harmonica", + "Tango Accordion", "Acoustic Guitar (nylon)", "Acoustic Guitar (steel)", "Electric Guitar (jazz)", + "Electric Guitar (clean)", "Electric Guitar (mute)", "Overdriven Guitar", "Distortion Guitar", + "Guitar Harmonics", "Acoustic Bass", "Electric Bass (finger)", "Electric Bass (pick)", + "Fretless Bass", "Slap Bass 1", "Slap Bass 2", "Synth Bass 1", "Synth Bass 2", "Violin", + "Viola", "Cello", "Contrabass", "Tremolo Strings", "Pizzicato Strings", "Orchestral Harp", + "Timpani", "String Ensemble 1", "String Ensemble 2", "SynthStrings 1", "SynthStrings 2", + "Choir Aahs", "Voice Oohs", "Synth Voice", "Orchestra Hit", "Trumpet", "Trombone", "Tuba", + "Muted Trumpet", "French Horn", "Brass Section", "SynthBrass 1", "SynthBrass 2", "Soprano Sax", + "Alto Sax", "Tenor Sax", "Baritone Sax", "Oboe", "English Horn", "Bassoon", "Clarinet", + "Piccolo", "Flute", "Recorder", "Pan Flute", "Blown Bottle", "Shakuhachi", "Whistle", + "Ocarina", "Lead 1 (square)", "Lead 2 (sawtooth)", "Lead 3 (calliope)", "Lead 4 (chiff)", + "Lead 5 (charang)", "Lead 6 (voice)", "Lead 7 (fifths)", "Lead 8 (bass+lead)", "Pad 1 (new age)", + "Pad 2 (warm)", "Pad 3 (polysynth)", "Pad 4 (choir)", "Pad 5 (bowed)", "Pad 6 (metallic)", + "Pad 7 (halo)", "Pad 8 (sweep)", "FX 1 (rain)", "FX 2 (soundtrack)", "FX 3 (crystal)", + "FX 4 (atmosphere)", "FX 5 (brightness)", "FX 6 (goblins)", "FX 7 (echoes)", "FX 8 (sci-fi)", + "Sitar", "Banjo", "Shamisen", "Koto", "Kalimba", "Bag pipe", "Fiddle", "Shanai", "Tinkle Bell", + "Agogo", "Steel Drums", "Woodblock", "Taiko Drum", "Melodic Tom", "Synth Drum", "Reverse Cymbal", + "Guitar Fret Noise", "Breath Noise", "Seashore", "Bird Tweet", "Telephone Ring", "Helicopter", + "Applause", "Gunshot" + }; + + return (((unsigned int) n) < 128) ? names[n] + : (const char*)0; +} + +const String MidiMessage::getGMInstrumentBankName (int n) throw() +{ + const char* names[] = + { + "Piano", "Chromatic Percussion", "Organ", "Guitar", + "Bass", "Strings", "Ensemble", "Brass", + "Reed", "Pipe", "Synth Lead", "Synth Pad", + "Synth Effects", "Ethnic", "Percussive", "Sound Effects" + }; + + return (((unsigned int) n) <= 15) ? names[n] + : (const char*)0; +} + +const String MidiMessage::getRhythmInstrumentName (int n) throw() +{ + const char* names[] = + { + "Acoustic Bass Drum", "Bass Drum 1", "Side Stick", "Acoustic Snare", + "Hand Clap", "Electric Snare", "Low Floor Tom", "Closed Hi-Hat", "High Floor Tom", + "Pedal Hi-Hat", "Low Tom", "Open Hi-Hat", "Low-Mid Tom", "Hi-Mid Tom", "Crash Cymbal 1", + "High Tom", "Ride Cymbal 1", "Chinese Cymbal", "Ride Bell", "Tambourine", "Splash Cymbal", + "Cowbell", "Crash Cymbal 2", "Vibraslap", "Ride Cymbal 2", "Hi Bongo", "Low Bongo", + "Mute Hi Conga", "Open Hi Conga", "Low Conga", "High Timbale", "Low Timbale", "High Agogo", + "Low Agogo", "Cabasa", "Maracas", "Short Whistle", "Long Whistle", "Short Guiro", + "Long Guiro", "Claves", "Hi Wood Block", "Low Wood Block", "Mute Cuica", "Open Cuica", + "Mute Triangle", "Open Triangle" + }; + + return (n >= 35 && n <= 81) ? names [n - 35] + : (const char*)0; +} + +const String MidiMessage::getControllerName (int n) throw() +{ + const char* names[] = + { + "Bank Select", "Modulation Wheel (coarse)", "Breath controller (coarse)", + 0, "Foot Pedal (coarse)", "Portamento Time (coarse)", + "Data Entry (coarse)", "Volume (coarse)", "Balance (coarse)", + 0, "Pan position (coarse)", "Expression (coarse)", "Effect Control 1 (coarse)", + "Effect Control 2 (coarse)", 0, 0, "General Purpose Slider 1", "General Purpose Slider 2", + "General Purpose Slider 3", "General Purpose Slider 4", 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, "Bank Select (fine)", "Modulation Wheel (fine)", "Breath controller (fine)", + 0, "Foot Pedal (fine)", "Portamento Time (fine)", "Data Entry (fine)", "Volume (fine)", + "Balance (fine)", 0, "Pan position (fine)", "Expression (fine)", "Effect Control 1 (fine)", + "Effect Control 2 (fine)", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "Hold Pedal (on/off)", "Portamento (on/off)", "Sustenuto Pedal (on/off)", "Soft Pedal (on/off)", + "Legato Pedal (on/off)", "Hold 2 Pedal (on/off)", "Sound Variation", "Sound Timbre", + "Sound Release Time", "Sound Attack Time", "Sound Brightness", "Sound Control 6", + "Sound Control 7", "Sound Control 8", "Sound Control 9", "Sound Control 10", + "General Purpose Button 1 (on/off)", "General Purpose Button 2 (on/off)", + "General Purpose Button 3 (on/off)", "General Purpose Button 4 (on/off)", + 0, 0, 0, 0, 0, 0, 0, "Reverb Level", "Tremolo Level", "Chorus Level", "Celeste Level", + "Phaser Level", "Data Button increment", "Data Button decrement", "Non-registered Parameter (fine)", + "Non-registered Parameter (coarse)", "Registered Parameter (fine)", "Registered Parameter (coarse)", + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "All Sound Off", "All Controllers Off", + "Local Keyboard (on/off)", "All Notes Off", "Omni Mode Off", "Omni Mode On", "Mono Operation", + "Poly Operation" + }; + + return (((unsigned int) n) < 128) ? names[n] + : (const char*)0; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_MidiMessage.cpp *********/ + +/********* Start of inlined file: juce_MidiMessageCollector.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +MidiMessageCollector::MidiMessageCollector() + : lastCallbackTime (0), + sampleRate (44100.0001) +{ +} + +MidiMessageCollector::~MidiMessageCollector() +{ +} + +void MidiMessageCollector::reset (const double sampleRate_) +{ + jassert (sampleRate_ > 0); + + const ScopedLock sl (midiCallbackLock); + sampleRate = sampleRate_; + incomingMessages.clear(); + lastCallbackTime = Time::getMillisecondCounterHiRes(); +} + +void MidiMessageCollector::addMessageToQueue (const MidiMessage& message) +{ + // you need to call reset() to set the correct sample rate before using this object + jassert (sampleRate != 44100.0001); + + // the messages that come in here need to be time-stamped correctly - see MidiInput + // for details of what the number should be. + jassert (message.getTimeStamp() != 0); + + const ScopedLock sl (midiCallbackLock); + + const int sampleNumber + = (int) ((message.getTimeStamp() - 0.001 * lastCallbackTime) * sampleRate); + + incomingMessages.addEvent (message, sampleNumber); + + // if the messages don't get used for over a second, we'd better + // get rid of any old ones to avoid the queue getting too big + if (sampleNumber > sampleRate) + incomingMessages.clear (0, sampleNumber - (int) sampleRate); +} + +void MidiMessageCollector::removeNextBlockOfMessages (MidiBuffer& destBuffer, + const int numSamples) +{ + // you need to call reset() to set the correct sample rate before using this object + jassert (sampleRate != 44100.0001); + + const double timeNow = Time::getMillisecondCounterHiRes(); + const double msElapsed = timeNow - lastCallbackTime; + + const ScopedLock sl (midiCallbackLock); + lastCallbackTime = timeNow; + + if (! incomingMessages.isEmpty()) + { + int numSourceSamples = jmax (1, roundDoubleToInt (msElapsed * 0.001 * sampleRate)); + + int startSample = 0; + int scale = 1 << 16; + + const uint8* midiData; + int numBytes, samplePosition; + + MidiBuffer::Iterator iter (incomingMessages); + + if (numSourceSamples > numSamples) + { + // if our list of events is longer than the buffer we're being + // asked for, scale them down to squeeze them all in.. + const int maxBlockLengthToUse = numSamples << 3; + + if (numSourceSamples > maxBlockLengthToUse) + { + startSample = numSourceSamples - maxBlockLengthToUse; + numSourceSamples = maxBlockLengthToUse; + iter.setNextSamplePosition (startSample); + } + + scale = (numSamples << 10) / numSourceSamples; + + while (iter.getNextEvent (midiData, numBytes, samplePosition)) + { + samplePosition = ((samplePosition - startSample) * scale) >> 10; + + destBuffer.addEvent (midiData, numBytes, + jlimit (0, numSamples - 1, samplePosition)); + } + } + else + { + // if our event list is shorter than the number we need, put them + // towards the end of the buffer + startSample = numSamples - numSourceSamples; + + while (iter.getNextEvent (midiData, numBytes, samplePosition)) + { + destBuffer.addEvent (midiData, numBytes, + jlimit (0, numSamples - 1, samplePosition + startSample)); + } + } + + incomingMessages.clear(); + } +} + +void MidiMessageCollector::handleNoteOn (MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity) +{ + MidiMessage m (MidiMessage::noteOn (midiChannel, midiNoteNumber, velocity)); + m.setTimeStamp (Time::getMillisecondCounter() * 0.001); + + addMessageToQueue (m); +} + +void MidiMessageCollector::handleNoteOff (MidiKeyboardState*, int midiChannel, int midiNoteNumber) +{ + MidiMessage m (MidiMessage::noteOff (midiChannel, midiNoteNumber)); + m.setTimeStamp (Time::getMillisecondCounter() * 0.001); + + addMessageToQueue (m); +} + +void MidiMessageCollector::handleIncomingMidiMessage (MidiInput*, const MidiMessage& message) +{ + addMessageToQueue (message); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_MidiMessageCollector.cpp *********/ + +/********* Start of inlined file: juce_MidiMessageSequence.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +MidiMessageSequence::MidiMessageSequence() +{ +} + +MidiMessageSequence::MidiMessageSequence (const MidiMessageSequence& other) +{ + list.ensureStorageAllocated (other.list.size()); + + for (int i = 0; i < other.list.size(); ++i) + list.add (new MidiEventHolder (other.list.getUnchecked(i)->message)); +} + +const MidiMessageSequence& MidiMessageSequence::operator= (const MidiMessageSequence& other) +{ + if (this != &other) + { + clear(); + + for (int i = 0; i < other.list.size(); ++i) + list.add (new MidiEventHolder (other.list.getUnchecked(i)->message)); + } + + return *this; +} + +MidiMessageSequence::~MidiMessageSequence() +{ +} + +void MidiMessageSequence::clear() +{ + list.clear(); +} + +int MidiMessageSequence::getNumEvents() const +{ + return list.size(); +} + +MidiMessageSequence::MidiEventHolder* MidiMessageSequence::getEventPointer (const int index) const +{ + return list [index]; +} + +double MidiMessageSequence::getTimeOfMatchingKeyUp (const int index) const +{ + const MidiEventHolder* const meh = list [index]; + + if (meh != 0 && meh->noteOffObject != 0) + return meh->noteOffObject->message.getTimeStamp(); + else + return 0.0; +} + +int MidiMessageSequence::getIndexOfMatchingKeyUp (const int index) const +{ + const MidiEventHolder* const meh = list [index]; + + return (meh != 0) ? list.indexOf (meh->noteOffObject) : -1; +} + +int MidiMessageSequence::getIndexOf (MidiEventHolder* const event) const +{ + return list.indexOf (event); +} + +int MidiMessageSequence::getNextIndexAtTime (const double timeStamp) const +{ + const int numEvents = list.size(); + + int i; + for (i = 0; i < numEvents; ++i) + if (list.getUnchecked(i)->message.getTimeStamp() >= timeStamp) + break; + + return i; +} + +double MidiMessageSequence::getStartTime() const +{ + if (list.size() > 0) + return list.getUnchecked(0)->message.getTimeStamp(); + else + return 0; +} + +double MidiMessageSequence::getEndTime() const +{ + if (list.size() > 0) + return list.getLast()->message.getTimeStamp(); + else + return 0; +} + +double MidiMessageSequence::getEventTime (const int index) const +{ + if (((unsigned int) index) < (unsigned int) list.size()) + return list.getUnchecked (index)->message.getTimeStamp(); + + return 0.0; +} + +void MidiMessageSequence::addEvent (const MidiMessage& newMessage, + double timeAdjustment) +{ + MidiEventHolder* const newOne = new MidiEventHolder (newMessage); + + timeAdjustment += newMessage.getTimeStamp(); + newOne->message.setTimeStamp (timeAdjustment); + + int i; + for (i = list.size(); --i >= 0;) + if (list.getUnchecked(i)->message.getTimeStamp() <= timeAdjustment) + break; + + list.insert (i + 1, newOne); +} + +void MidiMessageSequence::deleteEvent (const int index, + const bool deleteMatchingNoteUp) +{ + if (((unsigned int) index) < (unsigned int) list.size()) + { + if (deleteMatchingNoteUp) + deleteEvent (getIndexOfMatchingKeyUp (index), false); + + list.remove (index); + } +} + +void MidiMessageSequence::addSequence (const MidiMessageSequence& other, + double timeAdjustment, + double firstAllowableTime, + double endOfAllowableDestTimes) +{ + firstAllowableTime -= timeAdjustment; + endOfAllowableDestTimes -= timeAdjustment; + + for (int i = 0; i < other.list.size(); ++i) + { + const MidiMessage& m = other.list.getUnchecked(i)->message; + const double t = m.getTimeStamp(); + + if (t >= firstAllowableTime && t < endOfAllowableDestTimes) + { + MidiEventHolder* const newOne = new MidiEventHolder (m); + newOne->message.setTimeStamp (timeAdjustment + t); + + list.add (newOne); + } + } + + sort(); +} + +int MidiMessageSequence::compareElements (const MidiMessageSequence::MidiEventHolder* const first, + const MidiMessageSequence::MidiEventHolder* const second) throw() +{ + const double diff = first->message.getTimeStamp() + - second->message.getTimeStamp(); + + return (diff == 0) ? 0 + : ((diff > 0) ? 1 + : -1); +} + +void MidiMessageSequence::sort() +{ + list.sort (*this, true); +} + +void MidiMessageSequence::updateMatchedPairs() +{ + for (int i = 0; i < list.size(); ++i) + { + const MidiMessage& m1 = list.getUnchecked(i)->message; + + if (m1.isNoteOn()) + { + list.getUnchecked(i)->noteOffObject = 0; + const int note = m1.getNoteNumber(); + const int chan = m1.getChannel(); + const int len = list.size(); + + for (int j = i + 1; j < len; ++j) + { + const MidiMessage& m = list.getUnchecked(j)->message; + + if (m.getNoteNumber() == note && m.getChannel() == chan) + { + if (m.isNoteOff()) + { + list.getUnchecked(i)->noteOffObject = list[j]; + break; + } + else if (m.isNoteOn()) + { + list.insert (j, new MidiEventHolder (MidiMessage::noteOff (chan, note))); + list.getUnchecked(j)->message.setTimeStamp (m.getTimeStamp()); + list.getUnchecked(i)->noteOffObject = list[j]; + break; + } + } + } + } + } +} + +void MidiMessageSequence::addTimeToMessages (const double delta) +{ + for (int i = list.size(); --i >= 0;) + list.getUnchecked (i)->message.setTimeStamp (list.getUnchecked (i)->message.getTimeStamp() + + delta); +} + +void MidiMessageSequence::extractMidiChannelMessages (const int channelNumberToExtract, + MidiMessageSequence& destSequence, + const bool alsoIncludeMetaEvents) const +{ + for (int i = 0; i < list.size(); ++i) + { + const MidiMessage& mm = list.getUnchecked(i)->message; + + if (mm.isForChannel (channelNumberToExtract) + || (alsoIncludeMetaEvents && mm.isMetaEvent())) + { + destSequence.addEvent (mm); + } + } +} + +void MidiMessageSequence::extractSysExMessages (MidiMessageSequence& destSequence) const +{ + for (int i = 0; i < list.size(); ++i) + { + const MidiMessage& mm = list.getUnchecked(i)->message; + + if (mm.isSysEx()) + destSequence.addEvent (mm); + } +} + +void MidiMessageSequence::deleteMidiChannelMessages (const int channelNumberToRemove) +{ + for (int i = list.size(); --i >= 0;) + if (list.getUnchecked(i)->message.isForChannel (channelNumberToRemove)) + list.remove(i); +} + +void MidiMessageSequence::deleteSysExMessages() +{ + for (int i = list.size(); --i >= 0;) + if (list.getUnchecked(i)->message.isSysEx()) + list.remove(i); +} + +void MidiMessageSequence::createControllerUpdatesForTime (const int channelNumber, + const double time, + OwnedArray& dest) +{ + bool doneProg = false; + bool donePitchWheel = false; + Array doneControllers (32); + + for (int i = list.size(); --i >= 0;) + { + const MidiMessage& mm = list.getUnchecked(i)->message; + + if (mm.isForChannel (channelNumber) + && mm.getTimeStamp() <= time) + { + if (mm.isProgramChange()) + { + if (! doneProg) + { + dest.add (new MidiMessage (mm, 0.0)); + doneProg = true; + } + } + else if (mm.isController()) + { + if (! doneControllers.contains (mm.getControllerNumber())) + { + dest.add (new MidiMessage (mm, 0.0)); + doneControllers.add (mm.getControllerNumber()); + } + } + else if (mm.isPitchWheel()) + { + if (! donePitchWheel) + { + dest.add (new MidiMessage (mm, 0.0)); + donePitchWheel = true; + } + } + } + } +} + +MidiMessageSequence::MidiEventHolder::MidiEventHolder (const MidiMessage& message_) + : message (message_), + noteOffObject (0) +{ +} + +MidiMessageSequence::MidiEventHolder::~MidiEventHolder() +{ +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_MidiMessageSequence.cpp *********/ + +/********* Start of inlined file: juce_AudioPluginFormat.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +AudioPluginFormat::AudioPluginFormat() throw() +{ +} + +AudioPluginFormat::~AudioPluginFormat() +{ +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_AudioPluginFormat.cpp *********/ + +/********* Start of inlined file: juce_AudioPluginFormatManager.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +AudioPluginFormatManager::AudioPluginFormatManager() throw() +{ +} + +AudioPluginFormatManager::~AudioPluginFormatManager() throw() +{ +} + +juce_ImplementSingleton_SingleThreaded (AudioPluginFormatManager); + +void AudioPluginFormatManager::addDefaultFormats() +{ +#ifdef JUCE_DEBUG + // you should only call this method once! + for (int i = formats.size(); --i >= 0;) + { + #if JUCE_PLUGINHOST_VST + jassert (dynamic_cast (formats[i]) == 0); + #endif + + #if JUCE_PLUGINHOST_AU && JUCE_MAC + jassert (dynamic_cast (formats[i]) == 0); + #endif + + #if JUCE_PLUGINHOST_DX && JUCE_WIN32 + jassert (dynamic_cast (formats[i]) == 0); + #endif + + #if JUCE_PLUGINHOST_LADSPA && JUCE_LINUX + jassert (dynamic_cast (formats[i]) == 0); + #endif + } +#endif + +#if JUCE_PLUGINHOST_VST + formats.add (new VSTPluginFormat()); +#endif + +#if JUCE_PLUGINHOST_AU && JUCE_MAC + formats.add (new AudioUnitPluginFormat()); +#endif + +#if JUCE_PLUGINHOST_DX && JUCE_WIN32 + formats.add (new DirectXPluginFormat()); +#endif + +#if JUCE_PLUGINHOST_LADSPA && JUCE_LINUX + formats.add (new LADSPAPluginFormat()); +#endif +} + +int AudioPluginFormatManager::getNumFormats() throw() +{ + return formats.size(); +} + +AudioPluginFormat* AudioPluginFormatManager::getFormat (const int index) throw() +{ + return formats [index]; +} + +void AudioPluginFormatManager::addFormat (AudioPluginFormat* const format) throw() +{ + formats.add (format); +} + +AudioPluginInstance* AudioPluginFormatManager::createPluginInstance (const PluginDescription& description, + String& errorMessage) const +{ + AudioPluginInstance* result = 0; + + for (int i = 0; i < formats.size(); ++i) + { + result = formats.getUnchecked(i)->createInstanceFromDescription (description); + + if (result != 0) + break; + } + + if (result == 0) + { + if (description.file != File::nonexistent && ! description.file.exists()) + errorMessage = TRANS ("This plug-in file no longer exists"); + else + errorMessage = TRANS ("This plug-in failed to load correctly"); + } + + return result; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_AudioPluginFormatManager.cpp *********/ + +/********* Start of inlined file: juce_AudioPluginInstance.cpp *********/ +#define JUCE_PLUGIN_HOST 1 + +BEGIN_JUCE_NAMESPACE + +AudioPluginInstance::AudioPluginInstance() +{ +} + +AudioPluginInstance::~AudioPluginInstance() +{ +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_AudioPluginInstance.cpp *********/ + +/********* Start of inlined file: juce_KnownPluginList.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +KnownPluginList::KnownPluginList() +{ +} + +KnownPluginList::~KnownPluginList() +{ +} + +void KnownPluginList::clear() +{ + if (types.size() > 0) + { + types.clear(); + sendChangeMessage (this); + } +} + +PluginDescription* KnownPluginList::getTypeForFile (const File& file) const throw() +{ + for (int i = 0; i < types.size(); ++i) + if (types.getUnchecked(i)->file == file) + return types.getUnchecked(i); + + return 0; +} + +PluginDescription* KnownPluginList::getTypeForIdentifierString (const String& identifierString) const throw() +{ + for (int i = 0; i < types.size(); ++i) + if (types.getUnchecked(i)->createIdentifierString() == identifierString) + return types.getUnchecked(i); + + return 0; +} + +bool KnownPluginList::addType (const PluginDescription& type) +{ + for (int i = types.size(); --i >= 0;) + { + if (types.getUnchecked(i)->isDuplicateOf (type)) + { + // strange - found a duplicate plugin with different info.. + jassert (types.getUnchecked(i)->name == type.name); + jassert (types.getUnchecked(i)->isInstrument == type.isInstrument); + + *types.getUnchecked(i) = type; + return false; + } + } + + types.add (new PluginDescription (type)); + sendChangeMessage (this); + return true; +} + +void KnownPluginList::removeType (const int index) throw() +{ + types.remove (index); + sendChangeMessage (this); +} + +bool KnownPluginList::isListingUpToDate (const File& possiblePluginFile) const throw() +{ + if (getTypeForFile (possiblePluginFile) == 0) + return false; + + for (int i = types.size(); --i >= 0;) + { + const PluginDescription* const d = types.getUnchecked(i); + + if (d->file == possiblePluginFile + && d->lastFileModTime != possiblePluginFile.getLastModificationTime()) + { + return false; + } + } + + return true; +} + +bool KnownPluginList::scanAndAddFile (const File& possiblePluginFile, + const bool dontRescanIfAlreadyInList, + OwnedArray & typesFound) +{ + bool addedOne = false; + + if (dontRescanIfAlreadyInList + && getTypeForFile (possiblePluginFile) != 0) + { + bool needsRescanning = false; + + for (int i = types.size(); --i >= 0;) + { + const PluginDescription* const d = types.getUnchecked(i); + + if (d->file == possiblePluginFile) + { + if (d->lastFileModTime != possiblePluginFile.getLastModificationTime()) + needsRescanning = true; + else + typesFound.add (new PluginDescription (*d)); + } + } + + if (! needsRescanning) + return false; + } + + for (int i = 0; i < AudioPluginFormatManager::getInstance()->getNumFormats(); ++i) + { + AudioPluginFormat* const format = AudioPluginFormatManager::getInstance()->getFormat (i); + + OwnedArray found; + format->findAllTypesForFile (found, possiblePluginFile); + + for (int i = 0; i < found.size(); ++i) + { + PluginDescription* const desc = found.getUnchecked(i); + jassert (desc != 0); + + if (addType (*desc)) + addedOne = true; + + typesFound.add (new PluginDescription (*desc)); + } + } + + return addedOne; +} + +void KnownPluginList::scanAndAddDragAndDroppedFiles (const StringArray& files, + OwnedArray & typesFound) +{ + for (int i = 0; i < files.size(); ++i) + { + const File f (files [i]); + + if (! scanAndAddFile (f, true, typesFound)) + { + if (f.isDirectory()) + { + StringArray s; + + { + OwnedArray subFiles; + f.findChildFiles (subFiles, File::findFilesAndDirectories, false); + + for (int j = 0; j < subFiles.size(); ++j) + s.add (subFiles.getUnchecked (j)->getFullPathName()); + } + + scanAndAddDragAndDroppedFiles (s, typesFound); + } + } + } +} + +class PluginSorter +{ +public: + KnownPluginList::SortMethod method; + + PluginSorter() throw() {} + + int compareElements (const PluginDescription* const first, + const PluginDescription* const second) const throw() + { + int diff = 0; + + if (method == KnownPluginList::sortByCategory) + diff = first->category.compareLexicographically (second->category); + else if (method == KnownPluginList::sortByManufacturer) + diff = first->manufacturerName.compareLexicographically (second->manufacturerName); + else if (method == KnownPluginList::sortByFileSystemLocation) + diff = first->file.getParentDirectory().getFullPathName().compare (second->file.getParentDirectory().getFullPathName()); + + if (diff == 0) + diff = first->name.compareLexicographically (second->name); + + return diff; + } +}; + +void KnownPluginList::sort (const SortMethod method) +{ + if (method != defaultOrder) + { + PluginSorter sorter; + sorter.method = method; + types.sort (sorter, true); + + sendChangeMessage (this); + } +} + +XmlElement* KnownPluginList::createXml() const +{ + XmlElement* const e = new XmlElement (T("KNOWNPLUGINS")); + + for (int i = 0; i < types.size(); ++i) + e->addChildElement (types.getUnchecked(i)->createXml()); + + return e; +} + +void KnownPluginList::recreateFromXml (const XmlElement& xml) +{ + clear(); + + if (xml.hasTagName (T("KNOWNPLUGINS"))) + { + forEachXmlChildElement (xml, e) + { + PluginDescription info; + + if (info.loadFromXml (*e)) + addType (info); + } + } +} + +const int menuIdBase = 0x324503f4; + +// This is used to turn a bunch of paths into a nested menu structure. +struct PluginFilesystemTree +{ +private: + String folder; + OwnedArray subFolders; + Array plugins; + + void addPlugin (PluginDescription* const pd, const String& path) + { + if (path.isEmpty()) + { + plugins.add (pd); + } + else + { + const String firstSubFolder (path.upToFirstOccurrenceOf (T("/"), false, false)); + const String remainingPath (path.fromFirstOccurrenceOf (T("/"), false, false)); + + for (int i = subFolders.size(); --i >= 0;) + { + if (subFolders.getUnchecked(i)->folder.equalsIgnoreCase (firstSubFolder)) + { + subFolders.getUnchecked(i)->addPlugin (pd, remainingPath); + return; + } + } + + PluginFilesystemTree* const newFolder = new PluginFilesystemTree(); + newFolder->folder = firstSubFolder; + subFolders.add (newFolder); + + newFolder->addPlugin (pd, remainingPath); + } + } + + // removes any deeply nested folders that don't contain any actual plugins + void optimise() + { + for (int i = subFolders.size(); --i >= 0;) + { + PluginFilesystemTree* const sub = subFolders.getUnchecked(i); + + sub->optimise(); + + if (sub->plugins.size() == 0) + { + for (int j = 0; j < sub->subFolders.size(); ++j) + subFolders.add (sub->subFolders.getUnchecked(j)); + + sub->subFolders.clear (false); + subFolders.remove (i); + } + } + } + +public: + void buildTree (const Array & allPlugins) + { + for (int i = 0; i < allPlugins.size(); ++i) + { + String path (allPlugins.getUnchecked(i)->file.getParentDirectory().getFullPathName()); + + if (path.substring (1, 2) == T(":")) + path = path.substring (2); + + path = path.replaceCharacter (T('\\'), T('/')); + + addPlugin (allPlugins.getUnchecked(i), path); + } + + optimise(); + } + + void addToMenu (PopupMenu& m, const OwnedArray & allPlugins) const + { + int i; + for (i = 0; i < subFolders.size(); ++i) + { + const PluginFilesystemTree* const sub = subFolders.getUnchecked(i); + + PopupMenu subMenu; + sub->addToMenu (subMenu, allPlugins); + m.addSubMenu (sub->folder, subMenu); + } + + for (i = 0; i < plugins.size(); ++i) + { + PluginDescription* const plugin = plugins.getUnchecked(i); + + m.addItem (allPlugins.indexOf (plugin) + menuIdBase, + plugin->name, true, false); + } + } +}; + +void KnownPluginList::addToMenu (PopupMenu& menu, const SortMethod sortMethod) const +{ + Array sorted; + + { + PluginSorter sorter; + sorter.method = sortMethod; + + for (int i = 0; i < types.size(); ++i) + sorted.addSorted (sorter, types.getUnchecked(i)); + } + + if (sortMethod == sortByCategory + || sortMethod == sortByManufacturer) + { + String lastSubMenuName; + PopupMenu sub; + + for (int i = 0; i < sorted.size(); ++i) + { + const PluginDescription* const pd = sorted.getUnchecked(i); + String thisSubMenuName (sortMethod == sortByCategory ? pd->category + : pd->manufacturerName); + + if (thisSubMenuName.trim().isEmpty()) + thisSubMenuName = T("Other"); + + if (thisSubMenuName != lastSubMenuName) + { + if (sub.getNumItems() > 0) + { + menu.addSubMenu (lastSubMenuName, sub); + sub.clear(); + } + + lastSubMenuName = thisSubMenuName; + } + + sub.addItem (types.indexOf (pd) + menuIdBase, pd->name, true, false); + } + + if (sub.getNumItems() > 0) + menu.addSubMenu (lastSubMenuName, sub); + } + else if (sortMethod == sortByFileSystemLocation) + { + PluginFilesystemTree root; + root.buildTree (sorted); + root.addToMenu (menu, types); + } + else + { + for (int i = 0; i < sorted.size(); ++i) + { + const PluginDescription* const pd = sorted.getUnchecked(i); + menu.addItem (types.indexOf (pd) + menuIdBase, pd->name, true, false); + } + } +} + +int KnownPluginList::getIndexChosenByMenu (const int menuResultCode) const +{ + const int i = menuResultCode - menuIdBase; + + return (((unsigned int) i) < (unsigned int) types.size()) ? i : -1; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_KnownPluginList.cpp *********/ + +/********* Start of inlined file: juce_PluginDescription.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +PluginDescription::PluginDescription() throw() + : uid (0), + isInstrument (false), + numInputChannels (0), + numOutputChannels (0) +{ +} + +PluginDescription::~PluginDescription() throw() +{ +} + +PluginDescription::PluginDescription (const PluginDescription& other) throw() + : name (other.name), + pluginFormatName (other.pluginFormatName), + category (other.category), + manufacturerName (other.manufacturerName), + version (other.version), + file (other.file), + lastFileModTime (other.lastFileModTime), + uid (other.uid), + isInstrument (other.isInstrument), + numInputChannels (other.numInputChannels), + numOutputChannels (other.numOutputChannels) +{ +} + +const PluginDescription& PluginDescription::operator= (const PluginDescription& other) throw() +{ + name = other.name; + pluginFormatName = other.pluginFormatName; + category = other.category; + manufacturerName = other.manufacturerName; + version = other.version; + file = other.file; + uid = other.uid; + isInstrument = other.isInstrument; + lastFileModTime = other.lastFileModTime; + numInputChannels = other.numInputChannels; + numOutputChannels = other.numOutputChannels; + + return *this; +} + +bool PluginDescription::isDuplicateOf (const PluginDescription& other) const +{ + return file == other.file + && uid == other.uid; +} + +const String PluginDescription::createIdentifierString() const throw() +{ + return pluginFormatName + + T("-") + name + + T("-") + String::toHexString (file.getFileName().hashCode()) + + T("-") + String::toHexString (uid); +} + +XmlElement* PluginDescription::createXml() const +{ + XmlElement* const e = new XmlElement (T("PLUGIN")); + e->setAttribute (T("name"), name); + e->setAttribute (T("format"), pluginFormatName); + e->setAttribute (T("category"), category); + e->setAttribute (T("manufacturer"), manufacturerName); + e->setAttribute (T("version"), version); + e->setAttribute (T("file"), file.getFullPathName()); + e->setAttribute (T("uid"), String::toHexString (uid)); + e->setAttribute (T("isInstrument"), isInstrument); + e->setAttribute (T("fileTime"), String::toHexString (lastFileModTime.toMilliseconds())); + e->setAttribute (T("numInputs"), numInputChannels); + e->setAttribute (T("numOutputs"), numOutputChannels); + + return e; +} + +bool PluginDescription::loadFromXml (const XmlElement& xml) +{ + if (xml.hasTagName (T("PLUGIN"))) + { + name = xml.getStringAttribute (T("name")); + pluginFormatName = xml.getStringAttribute (T("format")); + category = xml.getStringAttribute (T("category")); + manufacturerName = xml.getStringAttribute (T("manufacturer")); + version = xml.getStringAttribute (T("version")); + file = File (xml.getStringAttribute (T("file"))); + uid = xml.getStringAttribute (T("uid")).getHexValue32(); + isInstrument = xml.getBoolAttribute (T("isInstrument"), false); + lastFileModTime = Time (xml.getStringAttribute (T("fileTime")).getHexValue64()); + numInputChannels = xml.getIntAttribute (T("numInputs")); + numOutputChannels = xml.getIntAttribute (T("numOutputs")); + + return true; + } + + return false; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_PluginDescription.cpp *********/ + +/********* Start of inlined file: juce_PluginDirectoryScanner.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +PluginDirectoryScanner::PluginDirectoryScanner (KnownPluginList& listToAddTo, + AudioPluginFormat& formatToLookFor, + FileSearchPath directoriesToSearch, + const bool recursive, + const File& deadMansPedalFile_) + : list (listToAddTo), + format (formatToLookFor), + deadMansPedalFile (deadMansPedalFile_), + nextIndex (0), + progress (0) +{ + directoriesToSearch.removeRedundantPaths(); + + for (int j = 0; j < directoriesToSearch.getNumPaths(); ++j) + recursiveFileSearch (directoriesToSearch [j], recursive); + + // If any plugins have crashed recently when being loaded, move them to the + // end of the list to give the others a chance to load correctly.. + const StringArray crashedPlugins (getDeadMansPedalFile()); + + for (int i = 0; i < crashedPlugins.size(); ++i) + { + const File f (crashedPlugins[i]); + + for (int j = filesToScan.size(); --j >= 0;) + if (f == *filesToScan.getUnchecked (j)) + filesToScan.move (j, -1); + } +} + +void PluginDirectoryScanner::recursiveFileSearch (const File& dir, const bool recursive) +{ + // avoid allowing the dir iterator to be recursive, because we want to avoid letting it delve inside + // .component or .vst directories. + DirectoryIterator iter (dir, false, "*", File::findFilesAndDirectories); + + while (iter.next()) + { + const File f (iter.getFile()); + bool isPlugin = false; + + if (format.fileMightContainThisPluginType (f)) + { + isPlugin = true; + filesToScan.add (new File (f)); + } + + if (recursive && (! isPlugin) && f.isDirectory()) + recursiveFileSearch (f, true); + } +} + +PluginDirectoryScanner::~PluginDirectoryScanner() +{ +} + +const File PluginDirectoryScanner::getNextPluginFileThatWillBeScanned() const throw() +{ + File* const file = filesToScan [nextIndex]; + + if (file != 0) + return *file; + + return File::nonexistent; +} + +bool PluginDirectoryScanner::scanNextFile (const bool dontRescanIfAlreadyInList) +{ + File* const file = filesToScan [nextIndex]; + + if (file != 0) + { + if (! list.isListingUpToDate (*file)) + { + OwnedArray typesFound; + + // Add this plugin to the end of the dead-man's pedal list in case it crashes... + StringArray crashedPlugins (getDeadMansPedalFile()); + crashedPlugins.removeString (file->getFullPathName()); + crashedPlugins.add (file->getFullPathName()); + setDeadMansPedalFile (crashedPlugins); + + list.scanAndAddFile (*file, + dontRescanIfAlreadyInList, + typesFound); + + // Managed to load without crashing, so remove it from the dead-man's-pedal.. + crashedPlugins.removeString (file->getFullPathName()); + setDeadMansPedalFile (crashedPlugins); + + if (typesFound.size() == 0) + failedFiles.add (file->getFullPathName()); + } + + ++nextIndex; + progress = nextIndex / (float) filesToScan.size(); + } + + return nextIndex < filesToScan.size(); +} + +const StringArray PluginDirectoryScanner::getDeadMansPedalFile() throw() +{ + StringArray lines; + + if (deadMansPedalFile != File::nonexistent) + { + lines.addLines (deadMansPedalFile.loadFileAsString()); + lines.removeEmptyStrings(); + } + + return lines; +} + +void PluginDirectoryScanner::setDeadMansPedalFile (const StringArray& newContents) throw() +{ + if (deadMansPedalFile != File::nonexistent) + deadMansPedalFile.replaceWithText (newContents.joinIntoString ("\n"), true, true); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_PluginDirectoryScanner.cpp *********/ + +/********* Start of inlined file: juce_PluginListComponent.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +PluginListComponent::PluginListComponent (KnownPluginList& listToEdit, + const File& deadMansPedalFile_, + PropertiesFile* const propertiesToUse_) + : list (listToEdit), + deadMansPedalFile (deadMansPedalFile_), + propertiesToUse (propertiesToUse_) +{ + addAndMakeVisible (listBox = new ListBox (String::empty, this)); + + addAndMakeVisible (optionsButton = new TextButton ("Options...")); + optionsButton->addButtonListener (this); + optionsButton->setTriggeredOnMouseDown (true); + + setSize (400, 600); + list.addChangeListener (this); +} + +PluginListComponent::~PluginListComponent() +{ + list.removeChangeListener (this); + deleteAllChildren(); +} + +void PluginListComponent::resized() +{ + listBox->setBounds (0, 0, getWidth(), getHeight() - 30); + optionsButton->changeWidthToFitText (24); + optionsButton->setTopLeftPosition (8, getHeight() - 28); +} + +void PluginListComponent::changeListenerCallback (void*) +{ + listBox->updateContent(); + listBox->repaint(); +} + +int PluginListComponent::getNumRows() +{ + return list.getNumTypes(); +} + +void PluginListComponent::paintListBoxItem (int row, + Graphics& g, + int width, int height, + bool rowIsSelected) +{ + if (rowIsSelected) + g.fillAll (findColour (TextEditor::highlightColourId)); + + const PluginDescription* const pd = list.getType (row); + + if (pd != 0) + { + GlyphArrangement ga; + ga.addCurtailedLineOfText (Font (height * 0.7f, Font::bold), pd->name, 8.0f, height * 0.8f, width - 10.0f, true); + + g.setColour (Colours::black); + ga.draw (g); + + float x, y, r, b; + ga.getBoundingBox (0, -1, x, y, r, b, false); + + String desc; + desc << pd->pluginFormatName + << (pd->isInstrument ? " instrument" : " effect") + << " - " + << pd->numInputChannels << (pd->numInputChannels == 1 ? " in" : " ins") + << " / " + << pd->numOutputChannels << (pd->numOutputChannels == 1 ? " out" : " outs"); + + if (pd->manufacturerName.isNotEmpty()) + desc << " - " << pd->manufacturerName; + + if (pd->version.isNotEmpty()) + desc << " - " << pd->version; + + if (pd->category.isNotEmpty()) + desc << " - category: '" << pd->category << '\''; + + g.setColour (Colours::grey); + + ga.clear(); + ga.addCurtailedLineOfText (Font (height * 0.6f), desc, r + 10.0f, height * 0.8f, width - r - 12.0f, true); + ga.draw (g); + } +} + +void PluginListComponent::deleteKeyPressed (int lastRowSelected) +{ + list.removeType (lastRowSelected); +} + +void PluginListComponent::buttonClicked (Button* b) +{ + if (optionsButton == b) + { + PopupMenu menu; + menu.addItem (1, TRANS("Clear list")); + menu.addItem (5, TRANS("Remove selected plugin from list"), listBox->getNumSelectedRows() > 0); + menu.addItem (6, TRANS("Show folder containing selected plugin"), listBox->getNumSelectedRows() > 0); + menu.addItem (7, TRANS("Remove any plugins whose files no longer exist")); + menu.addSeparator(); + menu.addItem (2, TRANS("Sort alphabetically")); + menu.addItem (3, TRANS("Sort by category")); + menu.addItem (4, TRANS("Sort by manufacturer")); + menu.addSeparator(); + + for (int i = 0; i < AudioPluginFormatManager::getInstance()->getNumFormats(); ++i) + { + AudioPluginFormat* const format = AudioPluginFormatManager::getInstance()->getFormat (i); + + if (format->getDefaultLocationsToSearch().getNumPaths() > 0) + menu.addItem (10 + i, "Scan for new or updated " + format->getName() + " plugins..."); + } + + const int r = menu.showAt (optionsButton); + + if (r == 1) + { + list.clear(); + } + else if (r == 2) + { + list.sort (KnownPluginList::sortAlphabetically); + } + else if (r == 3) + { + list.sort (KnownPluginList::sortByCategory); + } + else if (r == 4) + { + list.sort (KnownPluginList::sortByManufacturer); + } + else if (r == 5) + { + const SparseSet selected (listBox->getSelectedRows()); + + for (int i = list.getNumTypes(); --i >= 0;) + if (selected.contains (i)) + list.removeType (i); + } + else if (r == 6) + { + const PluginDescription* const desc = list.getType (listBox->getSelectedRow()); + + if (desc != 0) + desc->file.getParentDirectory().startAsProcess(); + } + else if (r == 7) + { + for (int i = list.getNumTypes(); --i >= 0;) + { + if (list.getType (i)->file != File::nonexistent + && ! list.getType (i)->file.exists()) + { + list.removeType (i); + } + } + } + else if (r != 0) + { + typeToScan = r - 10; + startTimer (1); + } + } +} + +void PluginListComponent::timerCallback() +{ + stopTimer(); + scanFor (AudioPluginFormatManager::getInstance()->getFormat (typeToScan)); +} + +bool PluginListComponent::isInterestedInFileDrag (const StringArray& /*files*/) +{ + return true; +} + +void PluginListComponent::filesDropped (const StringArray& files, int, int) +{ + OwnedArray typesFound; + list.scanAndAddDragAndDroppedFiles (files, typesFound); +} + +void PluginListComponent::scanFor (AudioPluginFormat* format) +{ + if (format == 0) + return; + + FileSearchPath path (format->getDefaultLocationsToSearch()); + + if (propertiesToUse != 0) + path = propertiesToUse->getValue ("lastPluginScanPath_" + format->getName(), path.toString()); + + { + AlertWindow aw (TRANS("Select folders to scan..."), String::empty, AlertWindow::NoIcon); + FileSearchPathListComponent pathList; + pathList.setSize (500, 300); + pathList.setPath (path); + + aw.addCustomComponent (&pathList); + aw.addButton (TRANS("Scan"), 1, KeyPress::returnKey); + aw.addButton (TRANS("Cancel"), 0, KeyPress (KeyPress::escapeKey)); + + if (aw.runModalLoop() == 0) + return; + + path = pathList.getPath(); + } + + if (propertiesToUse != 0) + { + propertiesToUse->setValue ("lastPluginScanPath_" + format->getName(), path.toString()); + propertiesToUse->saveIfNeeded(); + } + + double progress = 0.0; + + AlertWindow aw (TRANS("Scanning for plugins..."), + TRANS("Searching for all possible plugin files..."), AlertWindow::NoIcon); + + aw.addButton (TRANS("Cancel"), 0, KeyPress (KeyPress::escapeKey)); + aw.addProgressBarComponent (progress); + + aw.enterModalState(); + + MessageManager::getInstance()->dispatchPendingMessages(); + + PluginDirectoryScanner scanner (list, *format, path, true, deadMansPedalFile); + + for (;;) + { + aw.setMessage (TRANS("Testing:\n\n") + + scanner.getNextPluginFileThatWillBeScanned().getFileName()); + + MessageManager::getInstance()->dispatchPendingMessages (500); + + if (! scanner.scanNextFile (true)) + break; + + if (! aw.isCurrentlyModal()) + break; + + progress = scanner.getProgress(); + } + + if (scanner.getFailedFiles().size() > 0) + { + StringArray shortNames; + + for (int i = 0; i < scanner.getFailedFiles().size(); ++i) + shortNames.add (File (scanner.getFailedFiles()[i]).getFileName()); + + AlertWindow::showMessageBox (AlertWindow::InfoIcon, + TRANS("Scan complete"), + TRANS("Note that the following files appeared to be plugin files, but failed to load correctly:\n\n") + + shortNames.joinIntoString (", ")); + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_PluginListComponent.cpp *********/ + +/********* Start of inlined file: juce_AudioUnitPluginFormat.cpp *********/ + +#if JUCE_PLUGINHOST_AU && (! (defined (LINUX) || defined (_WIN32))) + +#include +#include +#include + +BEGIN_JUCE_NAMESPACE + +#if JUCE_MAC + +extern void juce_callAnyTimersSynchronously(); +extern bool juce_isHIViewCreatedByJuce (HIViewRef view); +extern bool juce_isWindowCreatedByJuce (WindowRef window); + +#if MACOS_10_3_OR_EARLIER + #define kAudioUnitType_Generator 'augn' +#endif + +// Change this to disable logging of various activities +#ifndef AU_LOGGING + #define AU_LOGGING 1 +#endif + +#if AU_LOGGING + #define log(a) Logger::writeToLog(a); +#else + #define log(a) +#endif + +static int insideCallback = 0; + +class AudioUnitPluginWindow; + +class AudioUnitPluginInstance : public AudioPluginInstance +{ +public: + + ~AudioUnitPluginInstance(); + + // AudioPluginInstance methods: + + void fillInPluginDescription (PluginDescription& desc) const + { + desc.name = pluginName; + desc.file = file; + desc.uid = ((int) componentDesc.componentType) + ^ ((int) componentDesc.componentSubType) + ^ ((int) componentDesc.componentManufacturer); + desc.lastFileModTime = file.getLastModificationTime(); + desc.pluginFormatName = "AudioUnit"; + desc.category = getCategory(); + desc.manufacturerName = manufacturer; + desc.version = version; + desc.numInputChannels = getNumInputChannels(); + desc.numOutputChannels = getNumOutputChannels(); + desc.isInstrument = (componentDesc.componentType == kAudioUnitType_MusicDevice); + } + + const String getName() const { return pluginName; } + bool acceptsMidi() const { return wantsMidiMessages; } + bool producesMidi() const { return false; } + + // AudioProcessor methods: + + void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock); + void releaseResources(); + void processBlock (AudioSampleBuffer& buffer, + MidiBuffer& midiMessages); + + AudioProcessorEditor* createEditor(); + + const String getInputChannelName (const int index) const; + bool isInputChannelStereoPair (int index) const; + + const String getOutputChannelName (const int index) const; + bool isOutputChannelStereoPair (int index) const; + + int getNumParameters(); + float getParameter (int index); + void setParameter (int index, float newValue); + const String getParameterName (int index); + const String getParameterText (int index); + bool isParameterAutomatable (int index) const; + + int getNumPrograms(); + int getCurrentProgram(); + void setCurrentProgram (int index); + const String getProgramName (int index); + void changeProgramName (int index, const String& newName); + + void getStateInformation (MemoryBlock& destData); + void getCurrentProgramStateInformation (MemoryBlock& destData); + void setStateInformation (const void* data, int sizeInBytes); + void setCurrentProgramStateInformation (const void* data, int sizeInBytes); + + juce_UseDebuggingNewOperator + +private: + friend class AudioUnitPluginWindow; + friend class AudioUnitPluginFormat; + + ComponentDescription componentDesc; + String pluginName, manufacturer, version; + File file; + CriticalSection lock; + bool initialised, wantsMidiMessages, wasPlaying; + + AudioBufferList* outputBufferList; + AudioTimeStamp timeStamp; + AudioSampleBuffer* currentBuffer; + + AudioUnit audioUnit; + Array parameterIds; + + bool getComponentDescFromFile (const File& file); + void initialise(); + + OSStatus renderGetInput (AudioUnitRenderActionFlags* ioActionFlags, + const AudioTimeStamp* inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList* ioData) const; + + static OSStatus renderGetInputCallback (void* inRefCon, + AudioUnitRenderActionFlags* ioActionFlags, + const AudioTimeStamp* inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList* ioData) + { + return ((AudioUnitPluginInstance*) inRefCon) + ->renderGetInput (ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData); + } + + OSStatus getBeatAndTempo (Float64* outCurrentBeat, Float64* outCurrentTempo) const; + OSStatus getMusicalTimeLocation (UInt32* outDeltaSampleOffsetToNextBeat, Float32* outTimeSig_Numerator, + UInt32* outTimeSig_Denominator, Float64* outCurrentMeasureDownBeat) const; + OSStatus getTransportState (Boolean* outIsPlaying, Boolean* outTransportStateChanged, + Float64* outCurrentSampleInTimeLine, Boolean* outIsCycling, + Float64* outCycleStartBeat, Float64* outCycleEndBeat); + + static OSStatus getBeatAndTempoCallback (void* inHostUserData, Float64* outCurrentBeat, Float64* outCurrentTempo) + { + return ((AudioUnitPluginInstance*) inHostUserData)->getBeatAndTempo (outCurrentBeat, outCurrentTempo); + } + + static OSStatus getMusicalTimeLocationCallback (void* inHostUserData, UInt32* outDeltaSampleOffsetToNextBeat, + Float32* outTimeSig_Numerator, UInt32* outTimeSig_Denominator, + Float64* outCurrentMeasureDownBeat) + { + return ((AudioUnitPluginInstance*) inHostUserData) + ->getMusicalTimeLocation (outDeltaSampleOffsetToNextBeat, outTimeSig_Numerator, + outTimeSig_Denominator, outCurrentMeasureDownBeat); + } + + static OSStatus getTransportStateCallback (void* inHostUserData, Boolean* outIsPlaying, Boolean* outTransportStateChanged, + Float64* outCurrentSampleInTimeLine, Boolean* outIsCycling, + Float64* outCycleStartBeat, Float64* outCycleEndBeat) + { + return ((AudioUnitPluginInstance*) inHostUserData) + ->getTransportState (outIsPlaying, outTransportStateChanged, + outCurrentSampleInTimeLine, outIsCycling, + outCycleStartBeat, outCycleEndBeat); + } + + void getNumChannels (int& numIns, int& numOuts) + { + numIns = 0; + numOuts = 0; + + AUChannelInfo supportedChannels [128]; + UInt32 supportedChannelsSize = sizeof (supportedChannels); + + if (AudioUnitGetProperty (audioUnit, kAudioUnitProperty_SupportedNumChannels, kAudioUnitScope_Global, + 0, supportedChannels, &supportedChannelsSize) == noErr + && supportedChannelsSize > 0) + { + for (int i = 0; i < supportedChannelsSize / sizeof (AUChannelInfo); ++i) + { + numIns = jmax (numIns, supportedChannels[i].inChannels); + numOuts = jmax (numOuts, supportedChannels[i].outChannels); + } + } + else + { + // (this really means the plugin will take any number of ins/outs as long + // as they are the same) + numIns = numOuts = 2; + } + } + + const String getCategory() const; + + AudioUnitPluginInstance (const File& file); +}; + +AudioUnitPluginInstance::AudioUnitPluginInstance (const File& file_) + : file (file_), + initialised (false), + wantsMidiMessages (false), + audioUnit (0), + outputBufferList (0), + currentBuffer (0) +{ + try + { + ++insideCallback; + + log (T("Opening AU: ") + file.getFullPathName()); + + if (getComponentDescFromFile (file)) + { + ComponentRecord* const comp = FindNextComponent (0, &componentDesc); + + if (comp != 0) + { + audioUnit = (AudioUnit) OpenComponent (comp); + + wantsMidiMessages = componentDesc.componentType == kAudioUnitType_MusicDevice + || componentDesc.componentType == kAudioUnitType_MusicEffect; + } + } + + --insideCallback; + } + catch (...) + { + --insideCallback; + } +} + +AudioUnitPluginInstance::~AudioUnitPluginInstance() +{ + { + const ScopedLock sl (lock); + + jassert (insideCallback == 0); + + if (audioUnit != 0) + { + AudioUnitUninitialize (audioUnit); + CloseComponent (audioUnit); + audioUnit = 0; + } + } + + juce_free (outputBufferList); +} + +bool AudioUnitPluginInstance::getComponentDescFromFile (const File& file) +{ + zerostruct (componentDesc); + + if (! file.hasFileExtension (T(".component"))) + return false; + + const String filename (file.getFullPathName()); + const char* const utf8 = filename.toUTF8(); + CFURLRef url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*) utf8, + strlen (utf8), file.isDirectory()); + if (url != 0) + { + CFBundleRef bundleRef = CFBundleCreate (kCFAllocatorDefault, url); + CFRelease (url); + + if (bundleRef != 0) + { + CFTypeRef name = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleName")); + + if (name != 0 && CFGetTypeID (name) == CFStringGetTypeID()) + pluginName = PlatformUtilities::cfStringToJuceString ((CFStringRef) name); + + if (pluginName.isEmpty()) + pluginName = file.getFileNameWithoutExtension(); + + CFTypeRef versionString = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleVersion")); + + if (versionString != 0 && CFGetTypeID (versionString) == CFStringGetTypeID()) + version = PlatformUtilities::cfStringToJuceString ((CFStringRef) versionString); + + CFTypeRef manuString = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleGetInfoString")); + + if (manuString != 0 && CFGetTypeID (manuString) == CFStringGetTypeID()) + manufacturer = PlatformUtilities::cfStringToJuceString ((CFStringRef) manuString); + + short resFileId = CFBundleOpenBundleResourceMap (bundleRef); + UseResFile (resFileId); + + for (int i = 1; i <= Count1Resources ('thng'); ++i) + { + Handle h = Get1IndResource ('thng', i); + + if (h != 0) + { + HLock (h); + const uint32* const types = (const uint32*) *h; + + if (types[0] == kAudioUnitType_MusicDevice + || types[0] == kAudioUnitType_MusicEffect + || types[0] == kAudioUnitType_Effect + || types[0] == kAudioUnitType_Generator + || types[0] == kAudioUnitType_Panner) + { + componentDesc.componentType = types[0]; + componentDesc.componentSubType = types[1]; + componentDesc.componentManufacturer = types[2]; + break; + } + + HUnlock (h); + ReleaseResource (h); + } + } + + CFBundleCloseBundleResourceMap (bundleRef, resFileId); + CFRelease (bundleRef); + } + } + + return componentDesc.componentType != 0 && componentDesc.componentSubType != 0; +} + +void AudioUnitPluginInstance::initialise() +{ + if (initialised || audioUnit == 0) + return; + + log (T("Initialising AU: ") + pluginName); + + parameterIds.clear(); + + { + UInt32 paramListSize = 0; + AudioUnitGetProperty (audioUnit, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, + 0, 0, ¶mListSize); + + if (paramListSize > 0) + { + parameterIds.insertMultiple (0, 0, paramListSize / sizeof (int)); + + AudioUnitGetProperty (audioUnit, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, + 0, ¶meterIds.getReference(0), ¶mListSize); + } + } + + { + AURenderCallbackStruct info; + zerostruct (info); + info.inputProcRefCon = this; + info.inputProc = renderGetInputCallback; + + AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, + 0, &info, sizeof (info)); + } + + { + HostCallbackInfo info; + zerostruct (info); + info.hostUserData = this; + info.beatAndTempoProc = getBeatAndTempoCallback; + info.musicalTimeLocationProc = getMusicalTimeLocationCallback; + info.transportStateProc = getTransportStateCallback; + + AudioUnitSetProperty (audioUnit, kAudioUnitProperty_HostCallbacks, kAudioUnitScope_Global, + 0, &info, sizeof (info)); + } + + int numIns, numOuts; + getNumChannels (numIns, numOuts); + setPlayConfigDetails (numIns, numOuts, 0, 0); + + initialised = AudioUnitInitialize (audioUnit) == noErr; + + setLatencySamples (0); +} + +void AudioUnitPluginInstance::prepareToPlay (double sampleRate_, + int samplesPerBlockExpected) +{ + initialise(); + + if (initialised) + { + int numIns, numOuts; + getNumChannels (numIns, numOuts); + + setPlayConfigDetails (numIns, numOuts, sampleRate_, samplesPerBlockExpected); + + Float64 latencySecs = 0.0; + UInt32 latencySize = sizeof (latencySecs); + AudioUnitGetProperty (audioUnit, kAudioUnitProperty_Latency, kAudioUnitScope_Global, + 0, &latencySecs, &latencySize); + + setLatencySamples (roundDoubleToInt (latencySecs * sampleRate_)); + + AudioUnitReset (audioUnit, kAudioUnitScope_Input, 0); + AudioUnitReset (audioUnit, kAudioUnitScope_Output, 0); + AudioUnitReset (audioUnit, kAudioUnitScope_Global, 0); + + AudioStreamBasicDescription stream; + zerostruct (stream); + stream.mSampleRate = sampleRate_; + stream.mFormatID = kAudioFormatLinearPCM; + stream.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved; + stream.mFramesPerPacket = 1; + stream.mBytesPerPacket = 4; + stream.mBytesPerFrame = 4; + stream.mBitsPerChannel = 32; + stream.mChannelsPerFrame = numIns; + + OSStatus err = AudioUnitSetProperty (audioUnit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, + 0, &stream, sizeof (stream)); + + stream.mChannelsPerFrame = numOuts; + + err = AudioUnitSetProperty (audioUnit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Output, + 0, &stream, sizeof (stream)); + + juce_free (outputBufferList); + outputBufferList = (AudioBufferList*) juce_calloc (sizeof (AudioBufferList) + sizeof (AudioBuffer) * (numOuts + 1)); + outputBufferList->mNumberBuffers = numOuts; + + for (int i = numOuts; --i >= 0;) + outputBufferList->mBuffers[i].mNumberChannels = 1; + + zerostruct (timeStamp); + timeStamp.mSampleTime = 0; + timeStamp.mHostTime = AudioGetCurrentHostTime(); + timeStamp.mFlags = kAudioTimeStampSampleTimeValid | kAudioTimeStampHostTimeValid; + + currentBuffer = 0; + wasPlaying = false; + } +} + +void AudioUnitPluginInstance::releaseResources() +{ + if (initialised) + { + AudioUnitReset (audioUnit, kAudioUnitScope_Input, 0); + AudioUnitReset (audioUnit, kAudioUnitScope_Output, 0); + AudioUnitReset (audioUnit, kAudioUnitScope_Global, 0); + + juce_free (outputBufferList); + outputBufferList = 0; + currentBuffer = 0; + } +} + +OSStatus AudioUnitPluginInstance::renderGetInput (AudioUnitRenderActionFlags* ioActionFlags, + const AudioTimeStamp* inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList* ioData) const +{ + if (inBusNumber == 0 + && currentBuffer != 0) + { + jassert (inNumberFrames == currentBuffer->getNumSamples()); // if this ever happens, might need to add extra handling + + for (int i = 0; i < ioData->mNumberBuffers; ++i) + { + if (i < currentBuffer->getNumChannels()) + { + memcpy (ioData->mBuffers[i].mData, + currentBuffer->getSampleData (i, 0), + sizeof (float) * inNumberFrames); + } + else + { + zeromem (ioData->mBuffers[i].mData, sizeof (float) * inNumberFrames); + } + } + } + + return noErr; +} + +void AudioUnitPluginInstance::processBlock (AudioSampleBuffer& buffer, + MidiBuffer& midiMessages) +{ + const int numSamples = buffer.getNumSamples(); + + if (initialised) + { + AudioUnitRenderActionFlags flags = 0; + + timeStamp.mHostTime = AudioGetCurrentHostTime(); + + for (int i = getNumOutputChannels(); --i >= 0;) + { + outputBufferList->mBuffers[i].mDataByteSize = sizeof (float) * numSamples; + outputBufferList->mBuffers[i].mData = buffer.getSampleData (i, 0); + } + + currentBuffer = &buffer; + + if (wantsMidiMessages) + { + const uint8* midiEventData; + int midiEventSize, midiEventPosition; + MidiBuffer::Iterator i (midiMessages); + + while (i.getNextEvent (midiEventData, midiEventSize, midiEventPosition)) + { + if (midiEventSize <= 3) + MusicDeviceMIDIEvent (audioUnit, + midiEventData[0], midiEventData[1], midiEventData[2], + midiEventPosition); + else + MusicDeviceSysEx (audioUnit, midiEventData, midiEventSize); + } + + midiMessages.clear(); + } + + AudioUnitRender (audioUnit, &flags, &timeStamp, + 0, numSamples, outputBufferList); + + timeStamp.mSampleTime += numSamples; + } + else + { + // Not initialised, so just bypass.. + for (int i = getNumInputChannels(); i < getNumOutputChannels(); ++i) + buffer.clear (i, 0, buffer.getNumSamples()); + } +} + +OSStatus AudioUnitPluginInstance::getBeatAndTempo (Float64* outCurrentBeat, Float64* outCurrentTempo) const +{ + AudioPlayHead* const ph = getPlayHead(); + AudioPlayHead::CurrentPositionInfo result; + + if (ph != 0 && ph->getCurrentPosition (result)) + { + *outCurrentBeat = result.ppqPosition; + *outCurrentTempo = result.bpm; + } + else + { + *outCurrentBeat = 0; + *outCurrentTempo = 120.0; + } + + return noErr; +} + +OSStatus AudioUnitPluginInstance::getMusicalTimeLocation (UInt32* outDeltaSampleOffsetToNextBeat, + Float32* outTimeSig_Numerator, + UInt32* outTimeSig_Denominator, + Float64* outCurrentMeasureDownBeat) const +{ + AudioPlayHead* const ph = getPlayHead(); + AudioPlayHead::CurrentPositionInfo result; + + if (ph != 0 && ph->getCurrentPosition (result)) + { + *outTimeSig_Numerator = result.timeSigNumerator; + *outTimeSig_Denominator = result.timeSigDenominator; + + *outDeltaSampleOffsetToNextBeat = 0; //xxx + *outCurrentMeasureDownBeat = result.ppqPositionOfLastBarStart; //xxx wrong + } + else + { + *outDeltaSampleOffsetToNextBeat = 0; + *outTimeSig_Numerator = 4; + *outTimeSig_Denominator = 4; + *outCurrentMeasureDownBeat = 0; + } + + return noErr; +} + +OSStatus AudioUnitPluginInstance::getTransportState (Boolean* outIsPlaying, + Boolean* outTransportStateChanged, + Float64* outCurrentSampleInTimeLine, + Boolean* outIsCycling, + Float64* outCycleStartBeat, + Float64* outCycleEndBeat) +{ + AudioPlayHead* const ph = getPlayHead(); + AudioPlayHead::CurrentPositionInfo result; + + if (ph != 0 && ph->getCurrentPosition (result)) + { + *outIsPlaying = result.isPlaying; + *outTransportStateChanged = result.isPlaying != wasPlaying; + wasPlaying = result.isPlaying; + *outCurrentSampleInTimeLine = roundDoubleToInt (result.timeInSeconds * getSampleRate()); + *outIsCycling = false; + *outCycleStartBeat = 0; + *outCycleEndBeat = 0; + } + else + { + *outIsPlaying = false; + *outTransportStateChanged = false; + *outCurrentSampleInTimeLine = 0; + *outIsCycling = false; + *outCycleStartBeat = 0; + *outCycleEndBeat = 0; + } + + return noErr; +} + +static VoidArray activeWindows; + +class AudioUnitPluginWindow : public AudioProcessorEditor, + public Timer +{ +public: + + AudioUnitPluginWindow (AudioUnitPluginInstance& plugin_) + : AudioProcessorEditor (&plugin_), + plugin (plugin_), + isOpen (false), + pluginWantsKeys (false), + wasShowing (false), + recursiveResize (false), + viewComponent (0), + pluginViewRef (0) + { + movementWatcher = new CompMovementWatcher (this); + + activeWindows.add (this); + + setOpaque (true); + setVisible (true); + setSize (1, 1); + + ComponentDescription viewList [16]; + UInt32 viewListSize = sizeof (viewList); + AudioUnitGetProperty (plugin.audioUnit, kAudioUnitProperty_GetUIComponentList, kAudioUnitScope_Global, + 0, &viewList, &viewListSize); + + componentRecord = FindNextComponent (0, &viewList[0]); + } + + ~AudioUnitPluginWindow() + { + deleteAndZero (movementWatcher); + + closePluginWindow(); + + activeWindows.removeValue (this); + plugin.editorBeingDeleted (this); + } + + bool isValid() const throw() { return componentRecord != 0; } + + void componentMovedOrResized() + { + if (recursiveResize) + return; + + Component* const topComp = getTopLevelComponent(); + + if (topComp->getPeer() != 0) + { + int x = 0, y = 0; + relativePositionToOtherComponent (topComp, x, y); + + recursiveResize = true; + + if (pluginViewRef != 0) + { + HIRect r; + r.origin.x = (float) x; + r.origin.y = (float) y; + r.size.width = (float) getWidth(); + r.size.height = (float) getHeight(); + HIViewSetFrame (pluginViewRef, &r); + } + + recursiveResize = false; + } + } + + void componentVisibilityChanged() + { + const bool isShowingNow = isShowing(); + + if (wasShowing != isShowingNow) + { + wasShowing = isShowingNow; + + if (isShowingNow) + openPluginWindow(); + else + closePluginWindow(); + } + + componentMovedOrResized(); + } + + void componentPeerChanged() + { + closePluginWindow(); + openPluginWindow(); + } + + void timerCallback() + { + if (pluginViewRef != 0) + { + HIRect bounds; + HIViewGetBounds (pluginViewRef, &bounds); + const int w = jmax (32, (int) bounds.size.width); + const int h = jmax (32, (int) bounds.size.height); + + if (w != getWidth() || h != getHeight()) + { + setSize (w, h); + startTimer (50); + } + else + { + startTimer (jlimit (50, 500, getTimerInterval() + 20)); + } + } + } + + bool keyStateChanged() + { + return pluginWantsKeys; + } + + bool keyPressed (const KeyPress&) + { + return pluginWantsKeys; + } + + void paint (Graphics& g) + { + if (isOpen) + { + ComponentPeer* const peer = getPeer(); + + if (peer != 0) + { + peer->addMaskedRegion (getScreenX() - peer->getScreenX(), + getScreenY() - peer->getScreenY(), + getWidth(), getHeight()); + } + } + else + { + g.fillAll (Colours::black); + } + } + + void broughtToFront() + { + activeWindows.removeValue (this); + activeWindows.add (this); + } + + juce_UseDebuggingNewOperator + +private: + AudioUnitPluginInstance& plugin; + bool isOpen, wasShowing, recursiveResize; + bool pluginWantsKeys; + + ComponentRecord* componentRecord; + AudioUnitCarbonView viewComponent; + HIViewRef pluginViewRef; + + void openPluginWindow() + { + if (isOpen || getWindowHandle() == 0 || componentRecord == 0) + return; + + log (T("Opening AU GUI: ") + plugin.getName()); + isOpen = true; + + pluginWantsKeys = true; //xxx any way to find this out? Does it matter? + + viewComponent = (AudioUnitCarbonView) OpenComponent (componentRecord); + + if (viewComponent != 0) + { + Float32Point pos = { getScreenX() - getTopLevelComponent()->getScreenX(), + getScreenY() - getTopLevelComponent()->getScreenY() }; + Float32Point size = { 250, 200 }; + + AudioUnitCarbonViewCreate (viewComponent, + plugin.audioUnit, + (WindowRef) getWindowHandle(), + HIViewGetRoot ((WindowRef) getWindowHandle()), + &pos, &size, + (ControlRef*) &pluginViewRef); + } + + timerCallback(); // to set our comp to the right size + repaint(); + } + + void closePluginWindow() + { + stopTimer(); + + if (isOpen) + { + log (T("Closing AU GUI: ") + plugin.getName()); + isOpen = false; + + if (viewComponent != 0) + CloseComponent (viewComponent); + + pluginViewRef = 0; + } + } + + class CompMovementWatcher : public ComponentMovementWatcher + { + public: + CompMovementWatcher (AudioUnitPluginWindow* const owner_) + : ComponentMovementWatcher (owner_), + owner (owner_) + { + } + + void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) + { + owner->componentMovedOrResized(); + } + + void componentPeerChanged() + { + owner->componentPeerChanged(); + } + + void componentVisibilityChanged (Component&) + { + owner->componentVisibilityChanged(); + } + + private: + AudioUnitPluginWindow* const owner; + }; + + CompMovementWatcher* movementWatcher; +}; + +AudioProcessorEditor* AudioUnitPluginInstance::createEditor() +{ + AudioUnitPluginWindow* w = new AudioUnitPluginWindow (*this); + + if (! w->isValid()) + deleteAndZero (w); + + return w; +} + +const String AudioUnitPluginInstance::getCategory() const +{ + const char* result = 0; + + switch (componentDesc.componentType) + { + case kAudioUnitType_Effect: + case kAudioUnitType_MusicEffect: + result = "Effect"; + break; + case kAudioUnitType_MusicDevice: + result = "Synth"; + break; + case kAudioUnitType_Generator: + result = "Generator"; + break; + case kAudioUnitType_Panner: + result = "Panner"; + break; + default: + break; + } + + return result; +} + +int AudioUnitPluginInstance::getNumParameters() +{ + return parameterIds.size(); +} + +float AudioUnitPluginInstance::getParameter (int index) +{ + const ScopedLock sl (lock); + + Float32 value = 0.0f; + + if (audioUnit != 0 && ((unsigned int) index) < (unsigned int) parameterIds.size()) + { + AudioUnitGetParameter (audioUnit, + (UInt32) parameterIds.getUnchecked (index), + kAudioUnitScope_Global, 0, + &value); + } + + return value; +} + +void AudioUnitPluginInstance::setParameter (int index, float newValue) +{ + const ScopedLock sl (lock); + + if (audioUnit != 0 && ((unsigned int) index) < (unsigned int) parameterIds.size()) + { + AudioUnitSetParameter (audioUnit, + (UInt32) parameterIds.getUnchecked (index), + kAudioUnitScope_Global, 0, + newValue, 0); + } +} + +const String AudioUnitPluginInstance::getParameterName (int index) +{ + AudioUnitParameterInfo info; + zerostruct (info); + UInt32 sz = sizeof (info); + + String name; + + if (AudioUnitGetProperty (audioUnit, + kAudioUnitProperty_ParameterInfo, + kAudioUnitScope_Global, + parameterIds [index], &info, &sz) == noErr) + { + if ((info.flags & kAudioUnitParameterFlag_HasCFNameString) != 0) + name = PlatformUtilities::cfStringToJuceString (info.cfNameString); + else + name = String (info.name, sizeof (info.name)); + } + + return name; +} + +const String AudioUnitPluginInstance::getParameterText (int index) +{ + return String (getParameter (index)); +} + +bool AudioUnitPluginInstance::isParameterAutomatable (int index) const +{ + AudioUnitParameterInfo info; + UInt32 sz = sizeof (info); + + if (AudioUnitGetProperty (audioUnit, + kAudioUnitProperty_ParameterInfo, + kAudioUnitScope_Global, + parameterIds [index], &info, &sz) == noErr) + { + return (info.flags & kAudioUnitParameterFlag_NonRealTime) == 0; + } + + return true; +} + +int AudioUnitPluginInstance::getNumPrograms() +{ + CFArrayRef presets; + UInt32 sz = sizeof (CFArrayRef); + int num = 0; + + if (AudioUnitGetProperty (audioUnit, + kAudioUnitProperty_FactoryPresets, + kAudioUnitScope_Global, + 0, &presets, &sz) == noErr) + { + num = (int) CFArrayGetCount (presets); + CFRelease (presets); + } + + return num; +} + +int AudioUnitPluginInstance::getCurrentProgram() +{ + AUPreset current; + current.presetNumber = 0; + UInt32 sz = sizeof (AUPreset); + + AudioUnitGetProperty (audioUnit, + kAudioUnitProperty_FactoryPresets, + kAudioUnitScope_Global, + 0, ¤t, &sz); + + return current.presetNumber; +} + +void AudioUnitPluginInstance::setCurrentProgram (int newIndex) +{ + AUPreset current; + current.presetNumber = newIndex; + current.presetName = 0; + + AudioUnitSetProperty (audioUnit, + kAudioUnitProperty_FactoryPresets, + kAudioUnitScope_Global, + 0, ¤t, sizeof (AUPreset)); +} + +const String AudioUnitPluginInstance::getProgramName (int index) +{ + String s; + CFArrayRef presets; + UInt32 sz = sizeof (CFArrayRef); + + if (AudioUnitGetProperty (audioUnit, + kAudioUnitProperty_FactoryPresets, + kAudioUnitScope_Global, + 0, &presets, &sz) == noErr) + { + for (CFIndex i = 0; i < CFArrayGetCount (presets); ++i) + { + const AUPreset* p = (const AUPreset*) CFArrayGetValueAtIndex (presets, i); + + if (p != 0 && p->presetNumber == index) + { + s = PlatformUtilities::cfStringToJuceString (p->presetName); + break; + } + } + + CFRelease (presets); + } + + return s; +} + +void AudioUnitPluginInstance::changeProgramName (int index, const String& newName) +{ + jassertfalse // xxx not implemented! +} + +const String AudioUnitPluginInstance::getInputChannelName (const int index) const +{ + if (((unsigned int) index) < (unsigned int) getNumInputChannels()) + return T("Input ") + String (index + 1); + + return String::empty; +} + +bool AudioUnitPluginInstance::isInputChannelStereoPair (int index) const +{ + if (((unsigned int) index) >= (unsigned int) getNumInputChannels()) + return false; + + return true; +} + +const String AudioUnitPluginInstance::getOutputChannelName (const int index) const +{ + if (((unsigned int) index) < (unsigned int) getNumOutputChannels()) + return T("Output ") + String (index + 1); + + return String::empty; +} + +bool AudioUnitPluginInstance::isOutputChannelStereoPair (int index) const +{ + if (((unsigned int) index) >= (unsigned int) getNumOutputChannels()) + return false; + + return true; +} + +void AudioUnitPluginInstance::getStateInformation (MemoryBlock& destData) +{ + getCurrentProgramStateInformation (destData); +} + +void AudioUnitPluginInstance::getCurrentProgramStateInformation (MemoryBlock& destData) +{ + CFPropertyListRef propertyList = 0; + UInt32 sz = sizeof (CFPropertyListRef); + + if (AudioUnitGetProperty (audioUnit, + kAudioUnitProperty_ClassInfo, + kAudioUnitScope_Global, + 0, &propertyList, &sz) == noErr) + { + CFWriteStreamRef stream = CFWriteStreamCreateWithAllocatedBuffers (kCFAllocatorDefault, kCFAllocatorDefault); + CFWriteStreamOpen (stream); + + CFIndex bytesWritten = CFPropertyListWriteToStream (propertyList, stream, kCFPropertyListBinaryFormat_v1_0, 0); + CFWriteStreamClose (stream); + + CFDataRef data = (CFDataRef) CFWriteStreamCopyProperty (stream, kCFStreamPropertyDataWritten); + + destData.setSize (bytesWritten); + destData.copyFrom (CFDataGetBytePtr (data), 0, destData.getSize()); + CFRelease (data); + + CFRelease (stream); + CFRelease (propertyList); + } +} + +void AudioUnitPluginInstance::setStateInformation (const void* data, int sizeInBytes) +{ + setCurrentProgramStateInformation (data, sizeInBytes); +} + +void AudioUnitPluginInstance::setCurrentProgramStateInformation (const void* data, int sizeInBytes) +{ + CFReadStreamRef stream = CFReadStreamCreateWithBytesNoCopy (kCFAllocatorDefault, + (const UInt8*) data, + sizeInBytes, + kCFAllocatorNull); + CFReadStreamOpen (stream); + + CFPropertyListFormat format = kCFPropertyListBinaryFormat_v1_0; + CFPropertyListRef propertyList = CFPropertyListCreateFromStream (kCFAllocatorDefault, + stream, + 0, + kCFPropertyListImmutable, + &format, + 0); + CFRelease (stream); + + if (propertyList != 0) + AudioUnitSetProperty (audioUnit, + kAudioUnitProperty_ClassInfo, + kAudioUnitScope_Global, + 0, &propertyList, sizeof (propertyList)); +} + +AudioUnitPluginFormat::AudioUnitPluginFormat() +{ +} + +AudioUnitPluginFormat::~AudioUnitPluginFormat() +{ +} + +void AudioUnitPluginFormat::findAllTypesForFile (OwnedArray & results, + const File& file) +{ + if (! fileMightContainThisPluginType (file)) + return; + + PluginDescription desc; + desc.file = file; + desc.uid = 0; + + AudioUnitPluginInstance* instance = dynamic_cast (createInstanceFromDescription (desc)); + + if (instance == 0) + return; + + try + { + instance->fillInPluginDescription (desc); + results.add (new PluginDescription (desc)); + } + catch (...) + { + // crashed while loading... + } + + deleteAndZero (instance); +} + +AudioPluginInstance* AudioUnitPluginFormat::createInstanceFromDescription (const PluginDescription& desc) +{ + AudioUnitPluginInstance* result = 0; + + if (fileMightContainThisPluginType (desc.file)) + { + result = new AudioUnitPluginInstance (desc.file); + + if (result->audioUnit != 0) + { + result->initialise(); + } + else + { + deleteAndZero (result); + } + } + + return result; +} + +bool AudioUnitPluginFormat::fileMightContainThisPluginType (const File& f) +{ + return f.hasFileExtension (T(".component")) + && f.isDirectory(); +} + +const FileSearchPath AudioUnitPluginFormat::getDefaultLocationsToSearch() +{ + return FileSearchPath ("~/Library/Audio/Plug-Ins/Components;/Library/Audio/Plug-Ins/Components"); +} + +#endif + +END_JUCE_NAMESPACE + +#undef log + +#endif +/********* End of inlined file: juce_AudioUnitPluginFormat.cpp *********/ + +/********* Start of inlined file: juce_VSTPluginFormat.cpp *********/ + +#if JUCE_PLUGINHOST_VST + +#ifdef _WIN32 + #undef #define _WIN32_WINNT + #define _WIN32_WINNT 0x500 + #undef STRICT + #define STRICT + #include + #include + #pragma warning (disable : 4312) +#elif defined (LINUX) + #include + #include + #include + #include + #include + #undef Font + #undef KeyPress + #undef Drawable + #undef Time +#else + #include +#endif + +BEGIN_JUCE_NAMESPACE + +#undef PRAGMA_ALIGN_SUPPORTED +#define VST_FORCE_DEPRECATED 0 + +#ifdef _MSC_VER + #pragma warning (push) + #pragma warning (disable: 4996) +#endif + +/* Obviously you're going to need the Steinberg vstsdk2.4 folder in + your include path if you want to add VST support. + + If you're not interested in VSTs, you can disable them by changing the + JUCE_PLUGINHOST_VST flag in juce_Config.h +*/ +#include "pluginterfaces/vst2.x/aeffectx.h" + +#ifdef _MSC_VER + #pragma warning (pop) +#endif + +#if JUCE_LINUX + #define Font JUCE_NAMESPACE::Font + #define KeyPress JUCE_NAMESPACE::KeyPress + #define Drawable JUCE_NAMESPACE::Drawable + #define Time JUCE_NAMESPACE::Time +#endif + +#if ! JUCE_WIN32 + #define _fpreset() + #define _clearfp() +#endif + +extern void juce_callAnyTimersSynchronously(); + +const int fxbVersionNum = 1; + +struct fxProgram +{ + long chunkMagic; // 'CcnK' + long byteSize; // of this chunk, excl. magic + byteSize + long fxMagic; // 'FxCk' + long version; + long fxID; // fx unique id + long fxVersion; + long numParams; + char prgName[28]; + float params[1]; // variable no. of parameters +}; + +struct fxSet +{ + long chunkMagic; // 'CcnK' + long byteSize; // of this chunk, excl. magic + byteSize + long fxMagic; // 'FxBk' + long version; + long fxID; // fx unique id + long fxVersion; + long numPrograms; + char future[128]; + fxProgram programs[1]; // variable no. of programs +}; + +struct fxChunkSet +{ + long chunkMagic; // 'CcnK' + long byteSize; // of this chunk, excl. magic + byteSize + long fxMagic; // 'FxCh', 'FPCh', or 'FBCh' + long version; + long fxID; // fx unique id + long fxVersion; + long numPrograms; + char future[128]; + long chunkSize; + char chunk[8]; // variable +}; + +struct fxProgramSet +{ + long chunkMagic; // 'CcnK' + long byteSize; // of this chunk, excl. magic + byteSize + long fxMagic; // 'FxCh', 'FPCh', or 'FBCh' + long version; + long fxID; // fx unique id + long fxVersion; + long numPrograms; + char name[28]; + long chunkSize; + char chunk[8]; // variable +}; + +#ifdef JUCE_LITTLE_ENDIAN + static long swap (const long x) throw() { return (long) swapByteOrder ((uint32) x); } + + static float swapFloat (const float x) throw() + { + union { uint32 asInt; float asFloat; } n; + n.asFloat = x; + n.asInt = swapByteOrder (n.asInt); + return n.asFloat; + } +#else + #define swap(x) (x) + #define swapFloat(x) (x) +#endif + +typedef AEffect* (*MainCall) (audioMasterCallback); + +static VstIntPtr VSTCALLBACK audioMaster (AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt); + +static int shellUIDToCreate = 0; +static int insideVSTCallback = 0; + +class VSTPluginWindow; + +// Change this to disable logging of various VST activities +#ifndef VST_LOGGING + #define VST_LOGGING 1 +#endif + +#if VST_LOGGING + #define log(a) Logger::writeToLog(a); +#else + #define log(a) +#endif + +#if JUCE_MAC +extern bool juce_isHIViewCreatedByJuce (HIViewRef view); +extern bool juce_isWindowCreatedByJuce (WindowRef window); + +#if JUCE_PPC +static void* NewCFMFromMachO (void* const machofp) throw() +{ + void* result = juce_malloc (8); + + ((void**) result)[0] = machofp; + ((void**) result)[1] = result; + + return result; +} +#endif +#endif + +#if JUCE_LINUX + +extern Display* display; +extern XContext improbableNumber; + +typedef void (*EventProcPtr) (XEvent* ev); + +static bool xErrorTriggered; + +static int temporaryErrorHandler (Display*, XErrorEvent*) +{ + xErrorTriggered = true; + return 0; +} + +static int getPropertyFromXWindow (Window handle, Atom atom) +{ + XErrorHandler oldErrorHandler = XSetErrorHandler (temporaryErrorHandler); + xErrorTriggered = false; + + int userSize; + unsigned long bytes, userCount; + unsigned char* data; + Atom userType; + + XGetWindowProperty (display, handle, atom, 0, 1, false, AnyPropertyType, + &userType, &userSize, &userCount, &bytes, &data); + + XSetErrorHandler (oldErrorHandler); + + return (userCount == 1 && ! xErrorTriggered) ? *(int*) data + : 0; +} + +static Window getChildWindow (Window windowToCheck) +{ + Window rootWindow, parentWindow; + Window* childWindows; + unsigned int numChildren; + + XQueryTree (display, + windowToCheck, + &rootWindow, + &parentWindow, + &childWindows, + &numChildren); + + if (numChildren > 0) + return childWindows [0]; + + return 0; +} + +static void translateJuceToXButtonModifiers (const MouseEvent& e, XEvent& ev) throw() +{ + if (e.mods.isLeftButtonDown()) + { + ev.xbutton.button = Button1; + ev.xbutton.state |= Button1Mask; + } + else if (e.mods.isRightButtonDown()) + { + ev.xbutton.button = Button3; + ev.xbutton.state |= Button3Mask; + } + else if (e.mods.isMiddleButtonDown()) + { + ev.xbutton.button = Button2; + ev.xbutton.state |= Button2Mask; + } +} + +static void translateJuceToXMotionModifiers (const MouseEvent& e, XEvent& ev) throw() +{ + if (e.mods.isLeftButtonDown()) + ev.xmotion.state |= Button1Mask; + else if (e.mods.isRightButtonDown()) + ev.xmotion.state |= Button3Mask; + else if (e.mods.isMiddleButtonDown()) + ev.xmotion.state |= Button2Mask; +} + +static void translateJuceToXCrossingModifiers (const MouseEvent& e, XEvent& ev) throw() +{ + if (e.mods.isLeftButtonDown()) + ev.xcrossing.state |= Button1Mask; + else if (e.mods.isRightButtonDown()) + ev.xcrossing.state |= Button3Mask; + else if (e.mods.isMiddleButtonDown()) + ev.xcrossing.state |= Button2Mask; +} + +static void translateJuceToXMouseWheelModifiers (const MouseEvent& e, const float increment, XEvent& ev) throw() +{ + if (increment < 0) + { + ev.xbutton.button = Button5; + ev.xbutton.state |= Button5Mask; + } + else if (increment > 0) + { + ev.xbutton.button = Button4; + ev.xbutton.state |= Button4Mask; + } +} + +#endif + +static VoidArray activeModules; + +class ModuleHandle : public ReferenceCountedObject +{ +public: + + File file; + MainCall moduleMain; + String pluginName; + + static ModuleHandle* findOrCreateModule (const File& file) + { + for (int i = activeModules.size(); --i >= 0;) + { + ModuleHandle* const module = (ModuleHandle*) activeModules.getUnchecked(i); + + if (module->file == file) + return module; + } + + _fpreset(); // (doesn't do any harm) + ++insideVSTCallback; + shellUIDToCreate = 0; + + log ("Attempting to load VST: " + file.getFullPathName()); + + ModuleHandle* m = new ModuleHandle (file); + + if (! m->open()) + deleteAndZero (m); + + --insideVSTCallback; + _fpreset(); // (doesn't do any harm) + + return m; + } + + ModuleHandle (const File& file_) + : file (file_), + moduleMain (0), +#if JUCE_WIN32 || JUCE_LINUX + hModule (0) +#elif JUCE_MAC + fragId (0), + resHandle (0), + bundleRef (0), + resFileId (0) +#endif + { + activeModules.add (this); + +#if JUCE_WIN32 || JUCE_LINUX + fullParentDirectoryPathName = file_.getParentDirectory().getFullPathName(); +#elif JUCE_MAC + PlatformUtilities::makeFSSpecFromPath (&parentDirFSSpec, file_.getParentDirectory().getFullPathName()); +#endif + } + + ~ModuleHandle() + { + activeModules.removeValue (this); + + close(); + } + + juce_UseDebuggingNewOperator + +#if JUCE_WIN32 || JUCE_LINUX + void* hModule; + String fullParentDirectoryPathName; + + bool open() + { +#if JUCE_WIN32 + static bool timePeriodSet = false; + + if (! timePeriodSet) + { + timePeriodSet = true; + timeBeginPeriod (2); + } +#endif + + pluginName = file.getFileNameWithoutExtension(); + + hModule = Process::loadDynamicLibrary (file.getFullPathName()); + + moduleMain = (MainCall) Process::getProcedureEntryPoint (hModule, "VSTPluginMain"); + + if (moduleMain == 0) + moduleMain = (MainCall) Process::getProcedureEntryPoint (hModule, "main"); + + return moduleMain != 0; + } + + void close() + { + _fpreset(); // (doesn't do any harm) + + Process::freeDynamicLibrary (hModule); + } + + void closeEffect (AEffect* eff) + { + eff->dispatcher (eff, effClose, 0, 0, 0, 0); + } + +#else + CFragConnectionID fragId; + Handle resHandle; + CFBundleRef bundleRef; + FSSpec parentDirFSSpec; + short resFileId; + + bool open() + { + bool ok = false; + const String filename (file.getFullPathName()); + + if (file.hasFileExtension (T(".vst"))) + { + const char* const utf8 = filename.toUTF8(); + CFURLRef url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*) utf8, + strlen (utf8), file.isDirectory()); + + if (url != 0) + { + bundleRef = CFBundleCreate (kCFAllocatorDefault, url); + CFRelease (url); + + if (bundleRef != 0) + { + if (CFBundleLoadExecutable (bundleRef)) + { + moduleMain = (MainCall) CFBundleGetFunctionPointerForName (bundleRef, CFSTR("main_macho")); + + if (moduleMain == 0) + moduleMain = (MainCall) CFBundleGetFunctionPointerForName (bundleRef, CFSTR("VSTPluginMain")); + + if (moduleMain != 0) + { + CFTypeRef name = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleName")); + + if (name != 0) + { + if (CFGetTypeID (name) == CFStringGetTypeID()) + { + char buffer[1024]; + + if (CFStringGetCString ((CFStringRef) name, buffer, sizeof (buffer), CFStringGetSystemEncoding())) + pluginName = buffer; + } + } + + if (pluginName.isEmpty()) + pluginName = file.getFileNameWithoutExtension(); + + resFileId = CFBundleOpenBundleResourceMap (bundleRef); + + ok = true; + } + } + + if (! ok) + { + CFBundleUnloadExecutable (bundleRef); + CFRelease (bundleRef); + bundleRef = 0; + } + } + } + } +#if JUCE_PPC + else + { + FSRef fn; + + if (FSPathMakeRef ((UInt8*) (const char*) filename, &fn, 0) == noErr) + { + resFileId = FSOpenResFile (&fn, fsRdPerm); + + if (resFileId != -1) + { + const int numEffs = Count1Resources ('aEff'); + + for (int i = 0; i < numEffs; ++i) + { + resHandle = Get1IndResource ('aEff', i + 1); + + if (resHandle != 0) + { + OSType type; + Str255 name; + SInt16 id; + GetResInfo (resHandle, &id, &type, name); + pluginName = String ((const char*) name + 1, name[0]); + DetachResource (resHandle); + HLock (resHandle); + + Ptr ptr; + Str255 errorText; + + OSErr err = GetMemFragment (*resHandle, GetHandleSize (resHandle), + name, kPrivateCFragCopy, + &fragId, &ptr, errorText); + + if (err == noErr) + { + moduleMain = (MainCall) newMachOFromCFM (ptr); + ok = true; + } + else + { + HUnlock (resHandle); + } + + break; + } + } + + if (! ok) + CloseResFile (resFileId); + } + } + } +#endif + + return ok; + } + + void close() + { +#if JUCE_PPC + if (fragId != 0) + { + if (moduleMain != 0) + disposeMachOFromCFM ((void*) moduleMain); + + CloseConnection (&fragId); + HUnlock (resHandle); + + if (resFileId != 0) + CloseResFile (resFileId); + } + else +#endif + if (bundleRef != 0) + { + CFBundleCloseBundleResourceMap (bundleRef, resFileId); + + if (CFGetRetainCount (bundleRef) == 1) + CFBundleUnloadExecutable (bundleRef); + + if (CFGetRetainCount (bundleRef) > 0) + CFRelease (bundleRef); + } + } + + void closeEffect (AEffect* eff) + { +#if JUCE_PPC + if (fragId != 0) + { + VoidArray thingsToDelete; + thingsToDelete.add ((void*) eff->dispatcher); + thingsToDelete.add ((void*) eff->process); + thingsToDelete.add ((void*) eff->setParameter); + thingsToDelete.add ((void*) eff->getParameter); + thingsToDelete.add ((void*) eff->processReplacing); + + eff->dispatcher (eff, effClose, 0, 0, 0, 0); + + for (int i = thingsToDelete.size(); --i >= 0;) + disposeMachOFromCFM (thingsToDelete[i]); + } + else +#endif + { + eff->dispatcher (eff, effClose, 0, 0, 0, 0); + } + } + +#if JUCE_PPC + static void* newMachOFromCFM (void* cfmfp) + { + if (cfmfp == 0) + return 0; + + UInt32* const mfp = (UInt32*) juce_malloc (sizeof (UInt32) * 6); + + mfp[0] = 0x3d800000 | ((UInt32) cfmfp >> 16); + mfp[1] = 0x618c0000 | ((UInt32) cfmfp & 0xffff); + mfp[2] = 0x800c0000; + mfp[3] = 0x804c0004; + mfp[4] = 0x7c0903a6; + mfp[5] = 0x4e800420; + + MakeDataExecutable (mfp, sizeof (UInt32) * 6); + return mfp; + } + + static void disposeMachOFromCFM (void* ptr) + { + juce_free (ptr); + } + + void coerceAEffectFunctionCalls (AEffect* eff) + { + if (fragId != 0) + { + eff->dispatcher = (AEffectDispatcherProc) newMachOFromCFM ((void*) eff->dispatcher); + eff->process = (AEffectProcessProc) newMachOFromCFM ((void*) eff->process); + eff->setParameter = (AEffectSetParameterProc) newMachOFromCFM ((void*) eff->setParameter); + eff->getParameter = (AEffectGetParameterProc) newMachOFromCFM ((void*) eff->getParameter); + eff->processReplacing = (AEffectProcessProc) newMachOFromCFM ((void*) eff->processReplacing); + } + } +#endif + +#endif +}; + +/** + An instance of a plugin, created by a VSTPluginFormat. + +*/ +class VSTPluginInstance : public AudioPluginInstance, + private Timer, + private AsyncUpdater +{ +public: + + ~VSTPluginInstance(); + + // AudioPluginInstance methods: + + void fillInPluginDescription (PluginDescription& desc) const + { + desc.name = name; + desc.file = module->file; + desc.uid = getUID(); + desc.lastFileModTime = desc.file.getLastModificationTime(); + desc.pluginFormatName = "VST"; + desc.category = getCategory(); + + { + char buffer [kVstMaxVendorStrLen + 8]; + zerostruct (buffer); + dispatch (effGetVendorString, 0, 0, buffer, 0); + desc.manufacturerName = buffer; + } + + desc.version = getVersion(); + desc.numInputChannels = getNumInputChannels(); + desc.numOutputChannels = getNumOutputChannels(); + desc.isInstrument = (effect != 0 && (effect->flags & effFlagsIsSynth) != 0); + } + + const String getName() const { return name; } + int getUID() const throw(); + bool acceptsMidi() const { return wantsMidiMessages; } + bool producesMidi() const { return dispatch (effCanDo, 0, 0, (void*) "sendVstMidiEvent", 0) > 0; } + + // AudioProcessor methods: + + void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock); + void releaseResources(); + void processBlock (AudioSampleBuffer& buffer, + MidiBuffer& midiMessages); + + AudioProcessorEditor* createEditor(); + + const String getInputChannelName (const int index) const; + bool isInputChannelStereoPair (int index) const; + + const String getOutputChannelName (const int index) const; + bool isOutputChannelStereoPair (int index) const; + + int getNumParameters() { return effect != 0 ? effect->numParams : 0; } + float getParameter (int index); + void setParameter (int index, float newValue); + const String getParameterName (int index); + const String getParameterText (int index); + bool isParameterAutomatable (int index) const; + + int getNumPrograms() { return effect != 0 ? effect->numPrograms : 0; } + int getCurrentProgram() { return dispatch (effGetProgram, 0, 0, 0, 0); } + void setCurrentProgram (int index); + const String getProgramName (int index); + void changeProgramName (int index, const String& newName); + + void getStateInformation (MemoryBlock& destData); + void getCurrentProgramStateInformation (MemoryBlock& destData); + void setStateInformation (const void* data, int sizeInBytes); + void setCurrentProgramStateInformation (const void* data, int sizeInBytes); + + void timerCallback(); + void handleAsyncUpdate(); + VstIntPtr handleCallback (VstInt32 opcode, VstInt32 index, VstInt32 value, void *ptr, float opt); + + juce_UseDebuggingNewOperator + +private: + friend class VSTPluginWindow; + friend class VSTPluginFormat; + + AEffect* effect; + String name; + CriticalSection lock; + bool wantsMidiMessages, initialised, isPowerOn; + mutable StringArray programNames; + AudioSampleBuffer tempBuffer; + CriticalSection midiInLock; + MidiBuffer incomingMidi; + void* midiEventsToSend; + int numAllocatedMidiEvents; + VstTimeInfo vstHostTime; + float** channels; + + ReferenceCountedObjectPtr module; + + int dispatch (const int opcode, const int index, const int value, void* const ptr, float opt) const; + bool restoreProgramSettings (const fxProgram* const prog); + const String getCurrentProgramName(); + void setParamsInProgramBlock (fxProgram* const prog) throw(); + void updateStoredProgramNames(); + void initialise(); + void ensureMidiEventSize (int numEventsNeeded); + void freeMidiEvents(); + void handleMidiFromPlugin (const VstEvents* const events); + void createTempParameterStore (MemoryBlock& dest); + void restoreFromTempParameterStore (const MemoryBlock& mb); + const String getParameterLabel (int index) const; + + bool usesChunks() const throw() { return effect != 0 && (effect->flags & effFlagsProgramChunks) != 0; } + void getChunkData (MemoryBlock& mb, bool isPreset, int maxSizeMB) const; + void setChunkData (const char* data, int size, bool isPreset); + bool loadFromFXBFile (const void* data, int numBytes); + bool saveToFXBFile (MemoryBlock& dest, bool isFXB, int maxSizeMB); + + int getVersionNumber() const throw() { return effect != 0 ? effect->version : 0; } + const String getVersion() const throw(); + const String getCategory() const throw(); + + bool hasEditor() const throw() { return effect != 0 && (effect->flags & effFlagsHasEditor) != 0; } + void setPower (const bool on); + + VSTPluginInstance (const ReferenceCountedObjectPtr & module); +}; + +VSTPluginInstance::VSTPluginInstance (const ReferenceCountedObjectPtr & module_) + : effect (0), + wantsMidiMessages (false), + initialised (false), + isPowerOn (false), + numAllocatedMidiEvents (0), + midiEventsToSend (0), + tempBuffer (1, 1), + channels (0), + module (module_) +{ + try + { + _fpreset(); + + ++insideVSTCallback; + + name = module->pluginName; + log (T("Creating VST instance: ") + name); + +#if JUCE_MAC + if (module->resFileId != 0) + UseResFile (module->resFileId); + +#if JUCE_PPC + if (module->fragId != 0) + { + static void* audioMasterCoerced = 0; + if (audioMasterCoerced == 0) + audioMasterCoerced = NewCFMFromMachO ((void*) &audioMaster); + + effect = module->moduleMain ((audioMasterCallback) audioMasterCoerced); + } + else +#endif +#endif + { + effect = module->moduleMain (&audioMaster); + } + + --insideVSTCallback; + + if (effect != 0 && effect->magic == kEffectMagic) + { +#if JUCE_PPC + module->coerceAEffectFunctionCalls (effect); +#endif + + jassert (effect->resvd2 == 0); + jassert (effect->object != 0); + + _fpreset(); // some dodgy plugs fuck around with this + } + else + { + effect = 0; + } + } + catch (...) + { + --insideVSTCallback; + } +} + +VSTPluginInstance::~VSTPluginInstance() +{ + { + const ScopedLock sl (lock); + + jassert (insideVSTCallback == 0); + + if (effect != 0 && effect->magic == kEffectMagic) + { + try + { +#if JUCE_MAC + if (module->resFileId != 0) + UseResFile (module->resFileId); +#endif + + // Must delete any editors before deleting the plugin instance! + jassert (getActiveEditor() == 0); + + _fpreset(); // some dodgy plugs fuck around with this + + module->closeEffect (effect); + } + catch (...) + {} + } + + module = 0; + effect = 0; + } + + freeMidiEvents(); + + juce_free (channels); + channels = 0; +} + +void VSTPluginInstance::initialise() +{ + if (initialised || effect == 0) + return; + + log (T("Initialising VST: ") + module->pluginName); + initialised = true; + + dispatch (effIdentify, 0, 0, 0, 0); + + // this code would ask the plugin for its name, but so few plugins + // actually bother implementing this correctly, that it's better to + // just ignore it and use the file name instead. +/* { + char buffer [256]; + zerostruct (buffer); + dispatch (effGetEffectName, 0, 0, buffer, 0); + + name = String (buffer).trim(); + if (name.isEmpty()) + name = module->pluginName; + } +*/ + + if (getSampleRate() > 0) + dispatch (effSetSampleRate, 0, 0, 0, (float) getSampleRate()); + + if (getBlockSize() > 0) + dispatch (effSetBlockSize, 0, jmax (32, getBlockSize()), 0, 0); + + dispatch (effOpen, 0, 0, 0, 0); + + setPlayConfigDetails (effect->numInputs, effect->numOutputs, + getSampleRate(), getBlockSize()); + + if (getNumPrograms() > 1) + setCurrentProgram (0); + else + dispatch (effSetProgram, 0, 0, 0, 0); + + int i; + for (i = effect->numInputs; --i >= 0;) + dispatch (effConnectInput, i, 1, 0, 0); + + for (i = effect->numOutputs; --i >= 0;) + dispatch (effConnectOutput, i, 1, 0, 0); + + updateStoredProgramNames(); + + wantsMidiMessages = dispatch (effCanDo, 0, 0, (void*) "receiveVstMidiEvent", 0) > 0; + + setLatencySamples (effect->initialDelay); +} + +void VSTPluginInstance::prepareToPlay (double sampleRate_, + int samplesPerBlockExpected) +{ + setPlayConfigDetails (effect->numInputs, effect->numOutputs, + sampleRate_, samplesPerBlockExpected); + + setLatencySamples (effect->initialDelay); + + juce_free (channels); + channels = (float**) juce_calloc (sizeof (float*) * jmax (16, getNumOutputChannels() + 2, getNumInputChannels() + 2)); + + vstHostTime.tempo = 120.0; + vstHostTime.timeSigNumerator = 4; + vstHostTime.timeSigDenominator = 4; + vstHostTime.sampleRate = sampleRate_; + vstHostTime.samplePos = 0; + vstHostTime.flags = kVstNanosValid; /*| kVstTransportPlaying | kVstTempoValid | kVstTimeSigValid*/; + + initialise(); + + if (initialised) + { + wantsMidiMessages = wantsMidiMessages + || (dispatch (effCanDo, 0, 0, (void*) "receiveVstMidiEvent", 0) > 0); + + if (wantsMidiMessages) + ensureMidiEventSize (256); + else + freeMidiEvents(); + + incomingMidi.clear(); + + dispatch (effSetSampleRate, 0, 0, 0, (float) sampleRate_); + dispatch (effSetBlockSize, 0, jmax (16, samplesPerBlockExpected), 0, 0); + + tempBuffer.setSize (jmax (1, effect->numOutputs), samplesPerBlockExpected); + + if (! isPowerOn) + setPower (true); + + // dodgy hack to force some plugins to initialise the sample rate.. + if ((! hasEditor()) && getNumParameters() > 0) + { + const float old = getParameter (0); + setParameter (0, (old < 0.5f) ? 1.0f : 0.0f); + setParameter (0, old); + } + + dispatch (effStartProcess, 0, 0, 0, 0); + } +} + +void VSTPluginInstance::releaseResources() +{ + if (initialised) + { + dispatch (effStopProcess, 0, 0, 0, 0); + setPower (false); + } + + tempBuffer.setSize (1, 1); + incomingMidi.clear(); + + freeMidiEvents(); + juce_free (channels); + channels = 0; +} + +void VSTPluginInstance::processBlock (AudioSampleBuffer& buffer, + MidiBuffer& midiMessages) +{ + const int numSamples = buffer.getNumSamples(); + + if (initialised) + { +#if JUCE_WIN32 + vstHostTime.nanoSeconds = timeGetTime() * 1000000.0; +#elif JUCE_LINUX + timeval micro; + gettimeofday (µ, 0); + vstHostTime.nanoSeconds = micro.tv_usec * 1000.0; +#elif JUCE_MAC + UnsignedWide micro; + Microseconds (µ); + vstHostTime.nanoSeconds = micro.lo * 1000.0; +#endif + + if (wantsMidiMessages) + { + MidiBuffer::Iterator iter (midiMessages); + + int eventIndex = 0; + const uint8* midiData; + int numBytesOfMidiData, samplePosition; + + while (iter.getNextEvent (midiData, numBytesOfMidiData, samplePosition)) + { + if (numBytesOfMidiData < 4) + { + ensureMidiEventSize (eventIndex); + VstMidiEvent* const e + = (VstMidiEvent*) ((VstEvents*) midiEventsToSend)->events [eventIndex++]; + + // check that some plugin hasn't messed up our objects + jassert (e->type == kVstMidiType); + jassert (e->byteSize == 24); + + e->deltaFrames = jlimit (0, numSamples - 1, samplePosition); + e->noteLength = 0; + e->noteOffset = 0; + e->midiData[0] = midiData[0]; + e->midiData[1] = midiData[1]; + e->midiData[2] = midiData[2]; + e->detune = 0; + e->noteOffVelocity = 0; + } + } + + if (midiEventsToSend == 0) + ensureMidiEventSize (1); + + ((VstEvents*) midiEventsToSend)->numEvents = eventIndex; + + try + { + effect->dispatcher (effect, effProcessEvents, 0, 0, midiEventsToSend, 0); + } + catch (...) + {} + } + + int i; + const int maxChans = jmax (effect->numInputs, effect->numOutputs); + + for (i = 0; i < maxChans; ++i) + channels[i] = buffer.getSampleData (i); + + channels [maxChans] = 0; + + _clearfp(); + + if ((effect->flags & effFlagsCanReplacing) != 0) + { + try + { + effect->processReplacing (effect, channels, channels, numSamples); + } + catch (...) + {} + } + else + { + tempBuffer.setSize (effect->numOutputs, numSamples); + tempBuffer.clear(); + + float* outs [64]; + + for (i = effect->numOutputs; --i >= 0;) + outs[i] = tempBuffer.getSampleData (i); + + outs [effect->numOutputs] = 0; + + try + { + effect->process (effect, channels, outs, numSamples); + } + catch (...) + {} + + for (i = effect->numOutputs; --i >= 0;) + buffer.copyFrom (i, 0, outs[i], numSamples); + } + } + else + { + // Not initialised, so just bypass.. + for (int i = getNumInputChannels(); i < getNumOutputChannels(); ++i) + buffer.clear (i, 0, buffer.getNumSamples()); + } + + { + // copy any incoming midi.. + const ScopedLock sl (midiInLock); + + midiMessages = incomingMidi; + incomingMidi.clear(); + } +} + +void VSTPluginInstance::ensureMidiEventSize (int numEventsNeeded) +{ + if (numEventsNeeded > numAllocatedMidiEvents) + { + numEventsNeeded = (numEventsNeeded + 32) & ~31; + + const int size = 20 + sizeof (VstEvent*) * numEventsNeeded; + + if (midiEventsToSend == 0) + midiEventsToSend = juce_calloc (size); + else + midiEventsToSend = juce_realloc (midiEventsToSend, size); + + for (int i = numAllocatedMidiEvents; i < numEventsNeeded; ++i) + { + VstMidiEvent* const e = (VstMidiEvent*) juce_calloc (sizeof (VstMidiEvent)); + e->type = kVstMidiType; + e->byteSize = 24; + + ((VstEvents*) midiEventsToSend)->events[i] = (VstEvent*) e; + } + + numAllocatedMidiEvents = numEventsNeeded; + } +} + +void VSTPluginInstance::freeMidiEvents() +{ + if (midiEventsToSend != 0) + { + for (int i = numAllocatedMidiEvents; --i >= 0;) + juce_free (((VstEvents*) midiEventsToSend)->events[i]); + + juce_free (midiEventsToSend); + midiEventsToSend = 0; + numAllocatedMidiEvents = 0; + } +} + +void VSTPluginInstance::handleMidiFromPlugin (const VstEvents* const events) +{ + if (events != 0) + { + const ScopedLock sl (midiInLock); + + for (int i = 0; i < events->numEvents; ++i) + { + const VstEvent* const e = events->events[i]; + + if (e->type == kVstMidiType) + { + incomingMidi.addEvent ((const uint8*) ((const VstMidiEvent*) e)->midiData, + 3, e->deltaFrames); + } + } + } +} + +static Array activeWindows; + +class VSTPluginWindow : public AudioProcessorEditor, + public Timer +{ +public: + + VSTPluginWindow (VSTPluginInstance& plugin_) + : AudioProcessorEditor (&plugin_), + plugin (plugin_), + isOpen (false), + wasShowing (false), + pluginRefusesToResize (false), + pluginWantsKeys (false), + alreadyInside (false), + recursiveResize (false) + { +#if JUCE_WIN32 + sizeCheckCount = 0; + pluginHWND = 0; +#elif JUCE_LINUX + pluginWindow = None; + pluginProc = None; +#else + pluginViewRef = 0; +#endif + + movementWatcher = new CompMovementWatcher (this); + + activeWindows.add (this); + + setSize (1, 1); + setOpaque (true); + setVisible (true); + } + + ~VSTPluginWindow() + { + deleteAndZero (movementWatcher); + + closePluginWindow(); + + activeWindows.removeValue (this); + plugin.editorBeingDeleted (this); + } + + void componentMovedOrResized() + { + if (recursiveResize) + return; + + Component* const topComp = getTopLevelComponent(); + + if (topComp->getPeer() != 0) + { + int x = 0, y = 0; + relativePositionToOtherComponent (topComp, x, y); + + recursiveResize = true; + +#if JUCE_MAC + if (pluginViewRef != 0) + { + HIRect r; + r.origin.x = (float) x; + r.origin.y = (float) y; + r.size.width = (float) getWidth(); + r.size.height = (float) getHeight(); + HIViewSetFrame (pluginViewRef, &r); + } + else if (pluginWindowRef != 0) + { + Rect r; + r.left = getScreenX(); + r.top = getScreenY(); + r.right = r.left + getWidth(); + r.bottom = r.top + getHeight(); + + WindowGroupRef group = GetWindowGroup (pluginWindowRef); + WindowGroupAttributes atts; + GetWindowGroupAttributes (group, &atts); + ChangeWindowGroupAttributes (group, 0, kWindowGroupAttrMoveTogether); + + SetWindowBounds (pluginWindowRef, kWindowContentRgn, &r); + + if ((atts & kWindowGroupAttrMoveTogether) != 0) + ChangeWindowGroupAttributes (group, kWindowGroupAttrMoveTogether, 0); + } + else + { + repaint(); + } +#elif JUCE_WIN32 + if (pluginHWND != 0) + MoveWindow (pluginHWND, x, y, getWidth(), getHeight(), TRUE); +#elif JUCE_LINUX + if (pluginWindow != 0) + { + XResizeWindow (display, pluginWindow, getWidth(), getHeight()); + XMoveWindow (display, pluginWindow, x, y); + XMapRaised (display, pluginWindow); + } +#endif + + recursiveResize = false; + } + } + + void componentVisibilityChanged() + { + const bool isShowingNow = isShowing(); + + if (wasShowing != isShowingNow) + { + wasShowing = isShowingNow; + + if (isShowingNow) + openPluginWindow(); + else + closePluginWindow(); + } + + componentMovedOrResized(); + } + + void componentPeerChanged() + { + closePluginWindow(); + openPluginWindow(); + } + + bool keyStateChanged() + { + return pluginWantsKeys; + } + + bool keyPressed (const KeyPress&) + { + return pluginWantsKeys; + } + + void paint (Graphics& g) + { + if (isOpen) + { + ComponentPeer* const peer = getPeer(); + + if (peer != 0) + { + peer->addMaskedRegion (getScreenX() - peer->getScreenX(), + getScreenY() - peer->getScreenY(), + getWidth(), getHeight()); + +#if JUCE_MAC + if (pluginViewRef == 0) + { + ERect r; + r.left = getScreenX() - peer->getScreenX(); + r.right = r.left + getWidth(); + r.top = getScreenY() - peer->getScreenY(); + r.bottom = r.top + getHeight(); + + dispatch (effEditDraw, 0, 0, &r, 0); + } +#elif JUCE_LINUX + if (pluginWindow != 0) + { + const Rectangle clip (g.getClipBounds()); + + XEvent ev; + zerostruct (ev); + ev.xexpose.type = Expose; + ev.xexpose.display = display; + ev.xexpose.window = pluginWindow; + ev.xexpose.x = clip.getX(); + ev.xexpose.y = clip.getY(); + ev.xexpose.width = clip.getWidth(); + ev.xexpose.height = clip.getHeight(); + + sendEventToChild (&ev); + } +#endif + } + } + else + { + g.fillAll (Colours::black); + } + } + + void timerCallback() + { +#if JUCE_WIN32 + if (--sizeCheckCount <= 0) + { + sizeCheckCount = 10; + + checkPluginWindowSize(); + } +#endif + + try + { + static bool reentrant = false; + + if (! reentrant) + { + reentrant = true; + plugin.dispatch (effEditIdle, 0, 0, 0, 0); + reentrant = false; + } + } + catch (...) + {} + } + + void mouseDown (const MouseEvent& e) + { +#if JUCE_MAC + if (! alreadyInside) + { + alreadyInside = true; + toFront (true); + dispatch (effEditMouse, e.x, e.y, 0, 0); + alreadyInside = false; + } + else + { + PostEvent (::mouseDown, 0); + } +#elif JUCE_LINUX + + if (pluginWindow == 0) + return; + + toFront (true); + + XEvent ev; + zerostruct (ev); + ev.xbutton.display = display; + ev.xbutton.type = ButtonPress; + ev.xbutton.window = pluginWindow; + ev.xbutton.root = RootWindow (display, DefaultScreen (display)); + ev.xbutton.time = CurrentTime; + ev.xbutton.x = e.x; + ev.xbutton.y = e.y; + ev.xbutton.x_root = e.getScreenX(); + ev.xbutton.y_root = e.getScreenY(); + + translateJuceToXButtonModifiers (e, ev); + + sendEventToChild (&ev); + +#else + (void) e; + + toFront (true); +#endif + } + + void broughtToFront() + { + activeWindows.removeValue (this); + activeWindows.add (this); + +#if JUCE_MAC + dispatch (effEditTop, 0, 0, 0, 0); +#endif + } + + juce_UseDebuggingNewOperator + +private: + VSTPluginInstance& plugin; + bool isOpen, wasShowing, recursiveResize; + bool pluginWantsKeys, pluginRefusesToResize, alreadyInside; + +#if JUCE_WIN32 + HWND pluginHWND; + void* originalWndProc; + int sizeCheckCount; +#elif JUCE_MAC + HIViewRef pluginViewRef; + WindowRef pluginWindowRef; +#elif JUCE_LINUX + Window pluginWindow; + EventProcPtr pluginProc; +#endif + + void openPluginWindow() + { + if (isOpen || getWindowHandle() == 0) + return; + + log (T("Opening VST UI: ") + plugin.name); + isOpen = true; + + ERect* rect = 0; + dispatch (effEditGetRect, 0, 0, &rect, 0); + dispatch (effEditOpen, 0, 0, getWindowHandle(), 0); + + // do this before and after like in the steinberg example + dispatch (effEditGetRect, 0, 0, &rect, 0); + dispatch (effGetProgram, 0, 0, 0, 0); // also in steinberg code + + // Install keyboard hooks + pluginWantsKeys = (dispatch (effKeysRequired, 0, 0, 0, 0) == 0); + +#if JUCE_WIN32 + originalWndProc = 0; + pluginHWND = GetWindow ((HWND) getWindowHandle(), GW_CHILD); + + if (pluginHWND == 0) + { + isOpen = false; + setSize (300, 150); + return; + } + + #pragma warning (push) + #pragma warning (disable: 4244) + + originalWndProc = (void*) GetWindowLongPtr (pluginHWND, GWL_WNDPROC); + + if (! pluginWantsKeys) + SetWindowLongPtr (pluginHWND, GWL_WNDPROC, (LONG_PTR) vstHookWndProc); + + #pragma warning (pop) + + int w, h; + RECT r; + GetWindowRect (pluginHWND, &r); + w = r.right - r.left; + h = r.bottom - r.top; + + if (rect != 0) + { + const int rw = rect->right - rect->left; + const int rh = rect->bottom - rect->top; + + if ((rw > 50 && rh > 50 && rw < 2000 && rh < 2000 && rw != w && rh != h) + || ((w == 0 && rw > 0) || (h == 0 && rh > 0))) + { + // very dodgy logic to decide which size is right. + if (abs (rw - w) > 350 || abs (rh - h) > 350) + { + SetWindowPos (pluginHWND, 0, + 0, 0, rw, rh, + SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER); + + GetWindowRect (pluginHWND, &r); + + w = r.right - r.left; + h = r.bottom - r.top; + + pluginRefusesToResize = (w != rw) || (h != rh); + + w = rw; + h = rh; + } + } + } +#elif JUCE_MAC + HIViewRef root = HIViewGetRoot ((WindowRef) getWindowHandle()); + HIViewFindByID (root, kHIViewWindowContentID, &root); + pluginViewRef = HIViewGetFirstSubview (root); + + while (pluginViewRef != 0 && juce_isHIViewCreatedByJuce (pluginViewRef)) + pluginViewRef = HIViewGetNextView (pluginViewRef); + + pluginWindowRef = 0; + + if (pluginViewRef == 0) + { + WindowGroupRef ourGroup = GetWindowGroup ((WindowRef) getWindowHandle()); + //DebugPrintWindowGroup (ourGroup); + //DebugPrintAllWindowGroups(); + + GetIndexedWindow (ourGroup, 1, + kWindowGroupContentsVisible, + &pluginWindowRef); + + if (pluginWindowRef == (WindowRef) getWindowHandle() + || juce_isWindowCreatedByJuce (pluginWindowRef)) + pluginWindowRef = 0; + } + + int w = 250, h = 150; + + if (rect != 0) + { + w = rect->right - rect->left; + h = rect->bottom - rect->top; + + if (w == 0 || h == 0) + { + w = 250; + h = 150; + } + } + +#elif JUCE_LINUX + pluginWindow = getChildWindow ((Window) getWindowHandle()); + + if (pluginWindow != 0) + pluginProc = (EventProcPtr) getPropertyFromXWindow (pluginWindow, + XInternAtom (display, "_XEventProc", False)); + + int w = 250, h = 150; + + if (rect != 0) + { + w = rect->right - rect->left; + h = rect->bottom - rect->top; + + if (w == 0 || h == 0) + { + w = 250; + h = 150; + } + } + + if (pluginWindow != 0) + XMapRaised (display, pluginWindow); +#endif + + // double-check it's not too tiny + w = jmax (w, 32); + h = jmax (h, 32); + + setSize (w, h); + +#if JUCE_WIN32 + checkPluginWindowSize(); +#endif + + startTimer (18 + JUCE_NAMESPACE::Random::getSystemRandom().nextInt (5)); + repaint(); + } + + void closePluginWindow() + { + if (isOpen) + { + log (T("Closing VST UI: ") + plugin.getName()); + isOpen = false; + + dispatch (effEditClose, 0, 0, 0, 0); + +#if JUCE_WIN32 + #pragma warning (push) + #pragma warning (disable: 4244) + + if (pluginHWND != 0 && IsWindow (pluginHWND)) + SetWindowLongPtr (pluginHWND, GWL_WNDPROC, (LONG_PTR) originalWndProc); + + #pragma warning (pop) + + stopTimer(); + + if (pluginHWND != 0 && IsWindow (pluginHWND)) + DestroyWindow (pluginHWND); + + pluginHWND = 0; +#elif JUCE_MAC + dispatch (effEditSleep, 0, 0, 0, 0); + pluginViewRef = 0; + stopTimer(); +#elif JUCE_LINUX + stopTimer(); + pluginWindow = 0; + pluginProc = 0; +#endif + } + } + +#if JUCE_WIN32 + void checkPluginWindowSize() throw() + { + RECT r; + GetWindowRect (pluginHWND, &r); + const int w = r.right - r.left; + const int h = r.bottom - r.top; + + if (isShowing() && w > 0 && h > 0 + && (w != getWidth() || h != getHeight()) + && ! pluginRefusesToResize) + { + setSize (w, h); + sizeCheckCount = 0; + } + } +#endif + + class CompMovementWatcher : public ComponentMovementWatcher + { + public: + CompMovementWatcher (VSTPluginWindow* const owner_) + : ComponentMovementWatcher (owner_), + owner (owner_) + { + } + + void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) + { + owner->componentMovedOrResized(); + } + + void componentPeerChanged() + { + owner->componentPeerChanged(); + } + + void componentVisibilityChanged (Component&) + { + owner->componentVisibilityChanged(); + } + + private: + VSTPluginWindow* const owner; + }; + + CompMovementWatcher* movementWatcher; + + int dispatch (const int opcode, const int index, const int value, void* const ptr, float opt) + { + return plugin.dispatch (opcode, index, value, ptr, opt); + } + + // hooks to get keyboard events from VST windows.. +#if JUCE_WIN32 + static LRESULT CALLBACK vstHookWndProc (HWND hW, UINT message, WPARAM wParam, LPARAM lParam) + { + for (int i = activeWindows.size(); --i >= 0;) + { + const VSTPluginWindow* const w = (const VSTPluginWindow*) activeWindows.getUnchecked (i); + + if (w->pluginHWND == hW) + { + if (message == WM_CHAR + || message == WM_KEYDOWN + || message == WM_SYSKEYDOWN + || message == WM_KEYUP + || message == WM_SYSKEYUP + || message == WM_APPCOMMAND) + { + SendMessage ((HWND) w->getTopLevelComponent()->getWindowHandle(), + message, wParam, lParam); + } + + return CallWindowProc ((WNDPROC) (w->originalWndProc), + (HWND) w->pluginHWND, + message, + wParam, + lParam); + } + } + + return DefWindowProc (hW, message, wParam, lParam); + } +#endif + +#if JUCE_LINUX + + // overload mouse/keyboard events to forward them to the plugin's inner window.. + void sendEventToChild (XEvent* event) + { + if (pluginProc != 0) + { + // if the plugin publishes an event procedure, pass the event directly.. + pluginProc (event); + } + else if (pluginWindow != 0) + { + // if the plugin has a window, then send the event to the window so that + // its message thread will pick it up.. + XSendEvent (display, pluginWindow, False, 0L, event); + XFlush (display); + } + } + + void mouseEnter (const MouseEvent& e) + { + if (pluginWindow != 0) + { + XEvent ev; + zerostruct (ev); + ev.xcrossing.display = display; + ev.xcrossing.type = EnterNotify; + ev.xcrossing.window = pluginWindow; + ev.xcrossing.root = RootWindow (display, DefaultScreen (display)); + ev.xcrossing.time = CurrentTime; + ev.xcrossing.x = e.x; + ev.xcrossing.y = e.y; + ev.xcrossing.x_root = e.getScreenX(); + ev.xcrossing.y_root = e.getScreenY(); + ev.xcrossing.mode = NotifyNormal; // NotifyGrab, NotifyUngrab + ev.xcrossing.detail = NotifyAncestor; // NotifyVirtual, NotifyInferior, NotifyNonlinear,NotifyNonlinearVirtual + + translateJuceToXCrossingModifiers (e, ev); + + sendEventToChild (&ev); + } + } + + void mouseExit (const MouseEvent& e) + { + if (pluginWindow != 0) + { + XEvent ev; + zerostruct (ev); + ev.xcrossing.display = display; + ev.xcrossing.type = LeaveNotify; + ev.xcrossing.window = pluginWindow; + ev.xcrossing.root = RootWindow (display, DefaultScreen (display)); + ev.xcrossing.time = CurrentTime; + ev.xcrossing.x = e.x; + ev.xcrossing.y = e.y; + ev.xcrossing.x_root = e.getScreenX(); + ev.xcrossing.y_root = e.getScreenY(); + ev.xcrossing.mode = NotifyNormal; // NotifyGrab, NotifyUngrab + ev.xcrossing.detail = NotifyAncestor; // NotifyVirtual, NotifyInferior, NotifyNonlinear,NotifyNonlinearVirtual + ev.xcrossing.focus = hasKeyboardFocus (true); // TODO - yes ? + + translateJuceToXCrossingModifiers (e, ev); + + sendEventToChild (&ev); + } + } + + void mouseMove (const MouseEvent& e) + { + if (pluginWindow != 0) + { + XEvent ev; + zerostruct (ev); + ev.xmotion.display = display; + ev.xmotion.type = MotionNotify; + ev.xmotion.window = pluginWindow; + ev.xmotion.root = RootWindow (display, DefaultScreen (display)); + ev.xmotion.time = CurrentTime; + ev.xmotion.is_hint = NotifyNormal; + ev.xmotion.x = e.x; + ev.xmotion.y = e.y; + ev.xmotion.x_root = e.getScreenX(); + ev.xmotion.y_root = e.getScreenY(); + + sendEventToChild (&ev); + } + } + + void mouseDrag (const MouseEvent& e) + { + if (pluginWindow != 0) + { + XEvent ev; + zerostruct (ev); + ev.xmotion.display = display; + ev.xmotion.type = MotionNotify; + ev.xmotion.window = pluginWindow; + ev.xmotion.root = RootWindow (display, DefaultScreen (display)); + ev.xmotion.time = CurrentTime; + ev.xmotion.x = e.x ; + ev.xmotion.y = e.y; + ev.xmotion.x_root = e.getScreenX(); + ev.xmotion.y_root = e.getScreenY(); + ev.xmotion.is_hint = NotifyNormal; + + translateJuceToXMotionModifiers (e, ev); + sendEventToChild (&ev); + } + } + + void mouseUp (const MouseEvent& e) + { + if (pluginWindow != 0) + { + XEvent ev; + zerostruct (ev); + ev.xbutton.display = display; + ev.xbutton.type = ButtonRelease; + ev.xbutton.window = pluginWindow; + ev.xbutton.root = RootWindow (display, DefaultScreen (display)); + ev.xbutton.time = CurrentTime; + ev.xbutton.x = e.x; + ev.xbutton.y = e.y; + ev.xbutton.x_root = e.getScreenX(); + ev.xbutton.y_root = e.getScreenY(); + + translateJuceToXButtonModifiers (e, ev); + sendEventToChild (&ev); + } + } + + void mouseWheelMove (const MouseEvent& e, + float incrementX, + float incrementY) + { + if (pluginWindow != 0) + { + XEvent ev; + zerostruct (ev); + ev.xbutton.display = display; + ev.xbutton.type = ButtonPress; + ev.xbutton.window = pluginWindow; + ev.xbutton.root = RootWindow (display, DefaultScreen (display)); + ev.xbutton.time = CurrentTime; + ev.xbutton.x = e.x; + ev.xbutton.y = e.y; + ev.xbutton.x_root = e.getScreenX(); + ev.xbutton.y_root = e.getScreenY(); + + translateJuceToXMouseWheelModifiers (e, incrementY, ev); + sendEventToChild (&ev); + + // TODO - put a usleep here ? + + ev.xbutton.type = ButtonRelease; + sendEventToChild (&ev); + } + } +#endif + +}; + +AudioProcessorEditor* VSTPluginInstance::createEditor() +{ + if (hasEditor()) + return new VSTPluginWindow (*this); + + return 0; +} + +void VSTPluginInstance::handleAsyncUpdate() +{ + // indicates that something about the plugin has changed.. + updateHostDisplay(); +} + +bool VSTPluginInstance::restoreProgramSettings (const fxProgram* const prog) +{ + if (swap (prog->chunkMagic) == 'CcnK' && swap (prog->fxMagic) == 'FxCk') + { + changeProgramName (getCurrentProgram(), prog->prgName); + + for (int i = 0; i < swap (prog->numParams); ++i) + setParameter (i, swapFloat (prog->params[i])); + + return true; + } + + return false; +} + +bool VSTPluginInstance::loadFromFXBFile (const void* const data, + const int dataSize) +{ + if (dataSize < 28) + return false; + + const fxSet* const set = (const fxSet*) data; + + if ((swap (set->chunkMagic) != 'CcnK' && swap (set->chunkMagic) != 'KncC') + || swap (set->version) > fxbVersionNum) + return false; + + if (swap (set->fxMagic) == 'FxBk') + { + // bank of programs + if (swap (set->numPrograms) >= 0) + { + const int oldProg = getCurrentProgram(); + const int numParams = swap (((const fxProgram*) (set->programs))->numParams); + const int progLen = sizeof (fxProgram) + (numParams - 1) * sizeof (float); + + for (int i = 0; i < swap (set->numPrograms); ++i) + { + if (i != oldProg) + { + const fxProgram* const prog = (const fxProgram*) (((const char*) (set->programs)) + i * progLen); + if (((const char*) prog) - ((const char*) set) >= dataSize) + return false; + + if (swap (set->numPrograms) > 0) + setCurrentProgram (i); + + if (! restoreProgramSettings (prog)) + return false; + } + } + + if (swap (set->numPrograms) > 0) + setCurrentProgram (oldProg); + + const fxProgram* const prog = (const fxProgram*) (((const char*) (set->programs)) + oldProg * progLen); + if (((const char*) prog) - ((const char*) set) >= dataSize) + return false; + + if (! restoreProgramSettings (prog)) + return false; + } + } + else if (swap (set->fxMagic) == 'FxCk') + { + // single program + const fxProgram* const prog = (const fxProgram*) data; + + if (swap (prog->chunkMagic) != 'CcnK') + return false; + + changeProgramName (getCurrentProgram(), prog->prgName); + + for (int i = 0; i < swap (prog->numParams); ++i) + setParameter (i, swapFloat (prog->params[i])); + } + else if (swap (set->fxMagic) == 'FBCh' || swap (set->fxMagic) == 'hCBF') + { + // non-preset chunk + const fxChunkSet* const cset = (const fxChunkSet*) data; + + if (swap (cset->chunkSize) + sizeof (fxChunkSet) - 8 > (unsigned int) dataSize) + return false; + + setChunkData (cset->chunk, swap (cset->chunkSize), false); + } + else if (swap (set->fxMagic) == 'FPCh' || swap (set->fxMagic) == 'hCPF') + { + // preset chunk + const fxProgramSet* const cset = (const fxProgramSet*) data; + + if (swap (cset->chunkSize) + sizeof (fxProgramSet) - 8 > (unsigned int) dataSize) + return false; + + setChunkData (cset->chunk, swap (cset->chunkSize), true); + + changeProgramName (getCurrentProgram(), cset->name); + } + else + { + return false; + } + + return true; +} + +void VSTPluginInstance::setParamsInProgramBlock (fxProgram* const prog) throw() +{ + const int numParams = getNumParameters(); + + prog->chunkMagic = swap ('CcnK'); + prog->byteSize = 0; + prog->fxMagic = swap ('FxCk'); + prog->version = swap (fxbVersionNum); + prog->fxID = swap (getUID()); + prog->fxVersion = swap (getVersionNumber()); + prog->numParams = swap (numParams); + + getCurrentProgramName().copyToBuffer (prog->prgName, sizeof (prog->prgName) - 1); + + for (int i = 0; i < numParams; ++i) + prog->params[i] = swapFloat (getParameter (i)); +} + +bool VSTPluginInstance::saveToFXBFile (MemoryBlock& dest, bool isFXB, int maxSizeMB) +{ + const int numPrograms = getNumPrograms(); + const int numParams = getNumParameters(); + + if (usesChunks()) + { + if (isFXB) + { + MemoryBlock chunk; + getChunkData (chunk, false, maxSizeMB); + + const int totalLen = sizeof (fxChunkSet) + chunk.getSize() - 8; + dest.setSize (totalLen, true); + + fxChunkSet* const set = (fxChunkSet*) dest.getData(); + set->chunkMagic = swap ('CcnK'); + set->byteSize = 0; + set->fxMagic = swap ('FBCh'); + set->version = swap (fxbVersionNum); + set->fxID = swap (getUID()); + set->fxVersion = swap (getVersionNumber()); + set->numPrograms = swap (numPrograms); + set->chunkSize = swap (chunk.getSize()); + + chunk.copyTo (set->chunk, 0, chunk.getSize()); + } + else + { + MemoryBlock chunk; + getChunkData (chunk, true, maxSizeMB); + + const int totalLen = sizeof (fxProgramSet) + chunk.getSize() - 8; + dest.setSize (totalLen, true); + + fxProgramSet* const set = (fxProgramSet*) dest.getData(); + set->chunkMagic = swap ('CcnK'); + set->byteSize = 0; + set->fxMagic = swap ('FPCh'); + set->version = swap (fxbVersionNum); + set->fxID = swap (getUID()); + set->fxVersion = swap (getVersionNumber()); + set->numPrograms = swap (numPrograms); + set->chunkSize = swap (chunk.getSize()); + + getCurrentProgramName().copyToBuffer (set->name, sizeof (set->name) - 1); + chunk.copyTo (set->chunk, 0, chunk.getSize()); + } + } + else + { + if (isFXB) + { + const int progLen = sizeof (fxProgram) + (numParams - 1) * sizeof (float); + const int len = (sizeof (fxSet) - sizeof (fxProgram)) + progLen * jmax (1, numPrograms); + dest.setSize (len, true); + + fxSet* const set = (fxSet*) dest.getData(); + set->chunkMagic = swap ('CcnK'); + set->byteSize = 0; + set->fxMagic = swap ('FxBk'); + set->version = swap (fxbVersionNum); + set->fxID = swap (getUID()); + set->fxVersion = swap (getVersionNumber()); + set->numPrograms = swap (numPrograms); + + const int oldProgram = getCurrentProgram(); + MemoryBlock oldSettings; + createTempParameterStore (oldSettings); + + setParamsInProgramBlock ((fxProgram*) (((char*) (set->programs)) + oldProgram * progLen)); + + for (int i = 0; i < numPrograms; ++i) + { + if (i != oldProgram) + { + setCurrentProgram (i); + setParamsInProgramBlock ((fxProgram*) (((char*) (set->programs)) + i * progLen)); + } + } + + setCurrentProgram (oldProgram); + restoreFromTempParameterStore (oldSettings); + } + else + { + const int totalLen = sizeof (fxProgram) + (numParams - 1) * sizeof (float); + dest.setSize (totalLen, true); + + setParamsInProgramBlock ((fxProgram*) dest.getData()); + } + } + + return true; +} + +void VSTPluginInstance::getChunkData (MemoryBlock& mb, bool isPreset, int maxSizeMB) const +{ + if (usesChunks()) + { + void* data = 0; + const int bytes = dispatch (effGetChunk, isPreset ? 1 : 0, 0, &data, 0.0f); + + if (data != 0 && bytes <= maxSizeMB * 1024 * 1024) + { + mb.setSize (bytes); + mb.copyFrom (data, 0, bytes); + } + } +} + +void VSTPluginInstance::setChunkData (const char* data, int size, bool isPreset) +{ + if (size > 0 && usesChunks()) + { + dispatch (effSetChunk, isPreset ? 1 : 0, size, (void*) data, 0.0f); + + if (! isPreset) + updateStoredProgramNames(); + } +} + +void VSTPluginInstance::timerCallback() +{ + if (dispatch (effIdle, 0, 0, 0, 0) == 0) + stopTimer(); +} + +int VSTPluginInstance::dispatch (const int opcode, const int index, const int value, void* const ptr, float opt) const +{ + const ScopedLock sl (lock); + + ++insideVSTCallback; + int result = 0; + + try + { + if (effect != 0) + { +#if JUCE_MAC + if (module->resFileId != 0) + UseResFile (module->resFileId); + + CGrafPtr oldPort; + + if (getActiveEditor() != 0) + { + int x = 0, y = 0; + getActiveEditor()->relativePositionToOtherComponent (getActiveEditor()->getTopLevelComponent(), x, y); + + GetPort (&oldPort); + SetPortWindowPort ((WindowRef) getActiveEditor()->getWindowHandle()); + SetOrigin (-x, -y); + } +#endif + + result = effect->dispatcher (effect, opcode, index, value, ptr, opt); + +#if JUCE_MAC + if (getActiveEditor() != 0) + SetPort (oldPort); + + module->resFileId = CurResFile(); +#endif + + --insideVSTCallback; + return result; + } + } + catch (...) + { + //char s[512]; + //sprintf (s, "dispatcher (%d, %d, %d, %x, %f)", opcode, index, value, (int)ptr, opt); + } + + --insideVSTCallback; + return result; +} + +// handles non plugin-specific callbacks.. +static VstIntPtr handleGeneralCallback (VstInt32 opcode, VstInt32 index, VstInt32 value, void *ptr, float opt) +{ + (void) index; + (void) value; + (void) opt; + + switch (opcode) + { + case audioMasterCanDo: + { + static const char* canDos[] = { "supplyIdle", + "sendVstEvents", + "sendVstMidiEvent", + "sendVstTimeInfo", + "receiveVstEvents", + "receiveVstMidiEvent", + "supportShell", + "shellCategory" }; + + for (int i = 0; i < numElementsInArray (canDos); ++i) + if (strcmp (canDos[i], (const char*) ptr) == 0) + return 1; + + return 0; + } + + case audioMasterVersion: + return 0x2400; + case audioMasterCurrentId: + return shellUIDToCreate; + case audioMasterGetNumAutomatableParameters: + return 0; + case audioMasterGetAutomationState: + return 1; + + case audioMasterGetVendorVersion: + return 0x0101; + case audioMasterGetVendorString: + case audioMasterGetProductString: + JUCEApplication::getInstance() + ->getApplicationName().copyToBuffer ((char*) ptr, jmin (kVstMaxVendorStrLen, kVstMaxProductStrLen) - 1); + break; + + case audioMasterGetSampleRate: + return 44100; + + case audioMasterGetBlockSize: + return 512; + + case audioMasterSetOutputSampleRate: + return 0; + + default: + DBG ("*** Unhandled VST Callback: " + String ((int) opcode)); + break; + } + + return 0; +} + +// handles callbacks for a specific plugin +VstIntPtr VSTPluginInstance::handleCallback (VstInt32 opcode, VstInt32 index, VstInt32 value, void *ptr, float opt) +{ + switch (opcode) + { + case audioMasterAutomate: + sendParamChangeMessageToListeners (index, opt); + break; + + case audioMasterProcessEvents: + handleMidiFromPlugin ((const VstEvents*) ptr); + break; + + case audioMasterGetTime: + #ifdef _MSC_VER + #pragma warning (push) + #pragma warning (disable: 4311) + #endif + + return (VstIntPtr) &vstHostTime; + + #ifdef _MSC_VER + #pragma warning (pop) + #endif + break; + + case audioMasterIdle: + if (insideVSTCallback == 0 && MessageManager::getInstance()->isThisTheMessageThread()) + { + ++insideVSTCallback; +#if JUCE_MAC + if (getActiveEditor() != 0) + dispatch (effEditIdle, 0, 0, 0, 0); +#endif + const MessageManagerLock mml; + + juce_callAnyTimersSynchronously(); + + handleUpdateNowIfNeeded(); + + for (int i = ComponentPeer::getNumPeers(); --i >= 0;) + ComponentPeer::getPeer (i)->performAnyPendingRepaintsNow(); + + --insideVSTCallback; + } + break; + + case audioMasterUpdateDisplay: + triggerAsyncUpdate(); + break; + + case audioMasterTempoAt: + // returns (10000 * bpm) + break; + + case audioMasterNeedIdle: + startTimer (50); + break; + + case audioMasterSizeWindow: + if (getActiveEditor() != 0) + getActiveEditor()->setSize (index, value); + + return 1; + + case audioMasterGetSampleRate: + return (VstIntPtr) getSampleRate(); + + case audioMasterGetBlockSize: + return (VstIntPtr) getBlockSize(); + + case audioMasterWantMidi: + wantsMidiMessages = true; + break; + + case audioMasterGetDirectory: + #if JUCE_MAC + return (VstIntPtr) (void*) &module->parentDirFSSpec; + #else + return (VstIntPtr) (pointer_sized_uint) (const char*) module->fullParentDirectoryPathName; + #endif + + case audioMasterGetAutomationState: + // returns 0: not supported, 1: off, 2:read, 3:write, 4:read/write + break; + + // none of these are handled (yet).. + case audioMasterBeginEdit: + case audioMasterEndEdit: + case audioMasterSetTime: + case audioMasterPinConnected: + case audioMasterGetParameterQuantization: + case audioMasterIOChanged: + case audioMasterGetInputLatency: + case audioMasterGetOutputLatency: + case audioMasterGetPreviousPlug: + case audioMasterGetNextPlug: + case audioMasterWillReplaceOrAccumulate: + case audioMasterGetCurrentProcessLevel: + case audioMasterOfflineStart: + case audioMasterOfflineRead: + case audioMasterOfflineWrite: + case audioMasterOfflineGetCurrentPass: + case audioMasterOfflineGetCurrentMetaPass: + case audioMasterVendorSpecific: + case audioMasterSetIcon: + case audioMasterGetLanguage: + case audioMasterOpenWindow: + case audioMasterCloseWindow: + break; + + default: + return handleGeneralCallback (opcode, index, value, ptr, opt); + } + + return 0; +} + +// entry point for all callbacks from the plugin +static VstIntPtr VSTCALLBACK audioMaster (AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt) +{ + try + { + if (effect != 0 && effect->resvd2 != 0) + { + return ((VSTPluginInstance*)(effect->resvd2)) + ->handleCallback (opcode, index, value, ptr, opt); + } + + return handleGeneralCallback (opcode, index, value, ptr, opt); + } + catch (...) + { + return 0; + } +} + +const String VSTPluginInstance::getVersion() const throw() +{ + int v = dispatch (effGetVendorVersion, 0, 0, 0, 0); + + String s; + + if (v != 0) + { + int versionBits[4]; + int n = 0; + + while (v != 0) + { + versionBits [n++] = (v & 0xff); + v >>= 8; + } + + s << 'V'; + + while (n > 0) + { + s << versionBits [--n]; + + if (n > 0) + s << '.'; + } + } + + return s; +} + +int VSTPluginInstance::getUID() const throw() +{ + int uid = effect != 0 ? effect->uniqueID : 0; + + if (uid == 0) + uid = module->file.hashCode(); + + return uid; +} + +const String VSTPluginInstance::getCategory() const throw() +{ + const char* result = 0; + + switch (dispatch (effGetPlugCategory, 0, 0, 0, 0)) + { + case kPlugCategEffect: + result = "Effect"; + break; + + case kPlugCategSynth: + result = "Synth"; + break; + + case kPlugCategAnalysis: + result = "Anaylsis"; + break; + + case kPlugCategMastering: + result = "Mastering"; + break; + + case kPlugCategSpacializer: + result = "Spacial"; + break; + + case kPlugCategRoomFx: + result = "Reverb"; + break; + + case kPlugSurroundFx: + result = "Surround"; + break; + + case kPlugCategRestoration: + result = "Restoration"; + break; + + case kPlugCategGenerator: + result = "Tone generation"; + break; + + default: + break; + } + + return result; +} + +float VSTPluginInstance::getParameter (int index) +{ + if (effect != 0 && ((unsigned int) index) < (unsigned int) effect->numParams) + { + try + { + const ScopedLock sl (lock); + return effect->getParameter (effect, index); + } + catch (...) + { + } + } + + return 0.0f; +} + +void VSTPluginInstance::setParameter (int index, float newValue) +{ + if (effect != 0 && ((unsigned int) index) < (unsigned int) effect->numParams) + { + try + { + const ScopedLock sl (lock); + + if (effect->getParameter (effect, index) != newValue) + effect->setParameter (effect, index, newValue); + } + catch (...) + { + } + } +} + +const String VSTPluginInstance::getParameterName (int index) +{ + if (effect != 0) + { + jassert (index >= 0 && index < effect->numParams); + + char nm [256]; + zerostruct (nm); + dispatch (effGetParamName, index, 0, nm, 0); + return String (nm).trim(); + } + + return String::empty; +} + +const String VSTPluginInstance::getParameterLabel (int index) const +{ + if (effect != 0) + { + jassert (index >= 0 && index < effect->numParams); + + char nm [256]; + zerostruct (nm); + dispatch (effGetParamLabel, index, 0, nm, 0); + return String (nm).trim(); + } + + return String::empty; +} + +const String VSTPluginInstance::getParameterText (int index) +{ + if (effect != 0) + { + jassert (index >= 0 && index < effect->numParams); + + char nm [256]; + zerostruct (nm); + dispatch (effGetParamDisplay, index, 0, nm, 0); + return String (nm).trim(); + } + + return String::empty; +} + +bool VSTPluginInstance::isParameterAutomatable (int index) const +{ + if (effect != 0) + { + jassert (index >= 0 && index < effect->numParams); + return dispatch (effCanBeAutomated, index, 0, 0, 0) != 0; + } + + return false; +} + +void VSTPluginInstance::createTempParameterStore (MemoryBlock& dest) +{ + dest.setSize (64 + 4 * getNumParameters()); + dest.fillWith (0); + + getCurrentProgramName().copyToBuffer ((char*) dest.getData(), 63); + + float* const p = (float*) (((char*) dest.getData()) + 64); + for (int i = 0; i < getNumParameters(); ++i) + p[i] = getParameter(i); +} + +void VSTPluginInstance::restoreFromTempParameterStore (const MemoryBlock& m) +{ + changeProgramName (getCurrentProgram(), (const char*) m.getData()); + + float* p = (float*) (((char*) m.getData()) + 64); + for (int i = 0; i < getNumParameters(); ++i) + setParameter (i, p[i]); +} + +void VSTPluginInstance::setCurrentProgram (int newIndex) +{ + if (getNumPrograms() > 0 && newIndex != getCurrentProgram()) + dispatch (effSetProgram, 0, jlimit (0, getNumPrograms() - 1, newIndex), 0, 0); +} + +const String VSTPluginInstance::getProgramName (int index) +{ + if (index == getCurrentProgram()) + { + return getCurrentProgramName(); + } + else if (effect != 0) + { + char nm [256]; + zerostruct (nm); + + if (dispatch (effGetProgramNameIndexed, + jlimit (0, getNumPrograms(), index), + -1, nm, 0) != 0) + { + return String (nm).trim(); + } + } + + return programNames [index]; +} + +void VSTPluginInstance::changeProgramName (int index, const String& newName) +{ + if (index == getCurrentProgram()) + { + if (getNumPrograms() > 0 && newName != getCurrentProgramName()) + dispatch (effSetProgramName, 0, 0, (void*) (const char*) newName.substring (0, 24), 0.0f); + } + else + { + jassertfalse // xxx not implemented! + } +} + +void VSTPluginInstance::updateStoredProgramNames() +{ + if (effect != 0 && getNumPrograms() > 0) + { + char nm [256]; + zerostruct (nm); + + // only do this if the plugin can't use indexed names.. + if (dispatch (effGetProgramNameIndexed, 0, -1, nm, 0) == 0) + { + const int oldProgram = getCurrentProgram(); + MemoryBlock oldSettings; + createTempParameterStore (oldSettings); + + for (int i = 0; i < getNumPrograms(); ++i) + { + setCurrentProgram (i); + getCurrentProgramName(); // (this updates the list) + } + + setCurrentProgram (oldProgram); + restoreFromTempParameterStore (oldSettings); + } + } +} + +const String VSTPluginInstance::getCurrentProgramName() +{ + if (effect != 0) + { + char nm [256]; + zerostruct (nm); + dispatch (effGetProgramName, 0, 0, nm, 0); + + const int index = getCurrentProgram(); + if (programNames[index].isEmpty()) + { + while (programNames.size() < index) + programNames.add (String::empty); + + programNames.set (index, String (nm).trim()); + } + + return String (nm).trim(); + } + + return String::empty; +} + +const String VSTPluginInstance::getInputChannelName (const int index) const +{ + if (index >= 0 && index < getNumInputChannels()) + { + VstPinProperties pinProps; + if (dispatch (effGetInputProperties, index, 0, &pinProps, 0.0f) != 0) + return String (pinProps.label, sizeof (pinProps.label)); + } + + return String::empty; +} + +bool VSTPluginInstance::isInputChannelStereoPair (int index) const +{ + if (index < 0 || index >= getNumInputChannels()) + return false; + + VstPinProperties pinProps; + if (dispatch (effGetInputProperties, index, 0, &pinProps, 0.0f) != 0) + return (pinProps.flags & kVstPinIsStereo) != 0; + + return true; +} + +const String VSTPluginInstance::getOutputChannelName (const int index) const +{ + if (index >= 0 && index < getNumOutputChannels()) + { + VstPinProperties pinProps; + if (dispatch (effGetOutputProperties, index, 0, &pinProps, 0.0f) != 0) + return String (pinProps.label, sizeof (pinProps.label)); + } + + return String::empty; +} + +bool VSTPluginInstance::isOutputChannelStereoPair (int index) const +{ + if (index < 0 || index >= getNumOutputChannels()) + return false; + + VstPinProperties pinProps; + if (dispatch (effGetOutputProperties, index, 0, &pinProps, 0.0f) != 0) + return (pinProps.flags & kVstPinIsStereo) != 0; + + return true; +} + +void VSTPluginInstance::setPower (const bool on) +{ + dispatch (effMainsChanged, 0, on ? 1 : 0, 0, 0); + isPowerOn = on; +} + +const int defaultMaxSizeMB = 64; + +void VSTPluginInstance::getStateInformation (MemoryBlock& destData) +{ + saveToFXBFile (destData, true, defaultMaxSizeMB); +} + +void VSTPluginInstance::getCurrentProgramStateInformation (MemoryBlock& destData) +{ + saveToFXBFile (destData, false, defaultMaxSizeMB); +} + +void VSTPluginInstance::setStateInformation (const void* data, int sizeInBytes) +{ + loadFromFXBFile (data, sizeInBytes); +} + +void VSTPluginInstance::setCurrentProgramStateInformation (const void* data, int sizeInBytes) +{ + loadFromFXBFile (data, sizeInBytes); +} + +VSTPluginFormat::VSTPluginFormat() +{ +} + +VSTPluginFormat::~VSTPluginFormat() +{ +} + +void VSTPluginFormat::findAllTypesForFile (OwnedArray & results, + const File& file) +{ + if (! fileMightContainThisPluginType (file)) + return; + + PluginDescription desc; + desc.file = file; + desc.uid = 0; + + VSTPluginInstance* instance = dynamic_cast (createInstanceFromDescription (desc)); + + if (instance == 0) + return; + + try + { +#if JUCE_MAC + if (instance->module->resFileId != 0) + UseResFile (instance->module->resFileId); +#endif + + instance->fillInPluginDescription (desc); + + VstPlugCategory category = (VstPlugCategory) instance->dispatch (effGetPlugCategory, 0, 0, 0, 0); + + if (category != kPlugCategShell) + { + // Normal plugin... + results.add (new PluginDescription (desc)); + + ++insideVSTCallback; + instance->dispatch (effOpen, 0, 0, 0, 0); + --insideVSTCallback; + } + else + { + // It's a shell plugin, so iterate all the subtypes... + char shellEffectName [64]; + + for (;;) + { + zerostruct (shellEffectName); + const int uid = instance->dispatch (effShellGetNextPlugin, 0, 0, shellEffectName, 0); + + if (uid == 0) + { + break; + } + else + { + desc.uid = uid; + desc.name = shellEffectName; + + bool alreadyThere = false; + + for (int i = results.size(); --i >= 0;) + { + PluginDescription* const d = results.getUnchecked(i); + + if (d->isDuplicateOf (desc)) + { + alreadyThere = true; + break; + } + } + + if (! alreadyThere) + results.add (new PluginDescription (desc)); + } + } + } + } + catch (...) + { + // crashed while loading... + } + + deleteAndZero (instance); +} + +AudioPluginInstance* VSTPluginFormat::createInstanceFromDescription (const PluginDescription& desc) +{ + VSTPluginInstance* result = 0; + + if (fileMightContainThisPluginType (desc.file)) + { + const File previousWorkingDirectory (File::getCurrentWorkingDirectory()); + desc.file.getParentDirectory().setAsCurrentWorkingDirectory(); + + const ReferenceCountedObjectPtr module (ModuleHandle::findOrCreateModule (desc.file)); + + if (module != 0) + { + shellUIDToCreate = desc.uid; + + result = new VSTPluginInstance (module); + + if (result->effect != 0) + { + result->effect->resvd2 = (VstIntPtr) (pointer_sized_int) result; + result->initialise(); + } + else + { + deleteAndZero (result); + } + } + + previousWorkingDirectory.setAsCurrentWorkingDirectory(); + } + + return result; +} + +bool VSTPluginFormat::fileMightContainThisPluginType (const File& f) +{ +#if JUCE_MAC + if (f.isDirectory() && f.hasFileExtension (T(".vst"))) + return true; + +#if JUCE_PPC + FSRef fileRef; + if (PlatformUtilities::makeFSRefFromPath (&fileRef, f.getFullPathName())) + { + const short resFileId = FSOpenResFile (&fileRef, fsRdPerm); + + if (resFileId != -1) + { + const int numEffects = Count1Resources ('aEff'); + CloseResFile (resFileId); + + if (numEffects > 0) + return true; + } + } +#endif + + return false; +#elif JUCE_WIN32 + return f.existsAsFile() + && f.hasFileExtension (T(".dll")); +#elif JUCE_LINUX + return f.existsAsFile() + && f.hasFileExtension (T(".so")); +#endif +} + +const FileSearchPath VSTPluginFormat::getDefaultLocationsToSearch() +{ +#if JUCE_MAC + return FileSearchPath ("~/Library/Audio/Plug-Ins/VST;/Library/Audio/Plug-Ins/VST"); +#elif JUCE_WIN32 + const String programFiles (File::getSpecialLocation (File::globalApplicationsDirectory).getFullPathName()); + + return FileSearchPath (programFiles + "\\Steinberg\\VstPlugins"); +#elif JUCE_LINUX + return FileSearchPath ("/usr/lib/vst"); +#endif +} + +END_JUCE_NAMESPACE + +#undef log + +#endif +/********* End of inlined file: juce_VSTPluginFormat.cpp *********/ + +/********* Start of inlined file: juce_AudioProcessor.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +AudioProcessor::AudioProcessor() + : playHead (0), + activeEditor (0), + sampleRate (0), + blockSize (0), + numInputChannels (0), + numOutputChannels (0), + latencySamples (0), + suspended (false), + nonRealtime (false) +{ +} + +AudioProcessor::~AudioProcessor() +{ + // ooh, nasty - the editor should have been deleted before the filter + // that it refers to is deleted.. + jassert (activeEditor == 0); + +#ifdef JUCE_DEBUG + // This will fail if you've called beginParameterChangeGesture() for one + // or more parameters without having made a corresponding call to endParameterChangeGesture... + jassert (changingParams.countNumberOfSetBits() == 0); +#endif +} + +void AudioProcessor::setPlayHead (AudioPlayHead* const newPlayHead) throw() +{ + playHead = newPlayHead; +} + +void AudioProcessor::addListener (AudioProcessorListener* const newListener) throw() +{ + const ScopedLock sl (listenerLock); + listeners.addIfNotAlreadyThere (newListener); +} + +void AudioProcessor::removeListener (AudioProcessorListener* const listenerToRemove) throw() +{ + const ScopedLock sl (listenerLock); + listeners.removeValue (listenerToRemove); +} + +void AudioProcessor::setPlayConfigDetails (const int numIns, + const int numOuts, + const double sampleRate_, + const int blockSize_) throw() +{ + numInputChannels = numIns; + numOutputChannels = numOuts; + sampleRate = sampleRate_; + blockSize = blockSize_; +} + +void AudioProcessor::setNonRealtime (const bool nonRealtime_) throw() +{ + nonRealtime = nonRealtime_; +} + +void AudioProcessor::setLatencySamples (const int newLatency) +{ + if (latencySamples != newLatency) + { + latencySamples = newLatency; + updateHostDisplay(); + } +} + +void AudioProcessor::setParameterNotifyingHost (const int parameterIndex, + const float newValue) +{ + setParameter (parameterIndex, newValue); + sendParamChangeMessageToListeners (parameterIndex, newValue); +} + +void AudioProcessor::sendParamChangeMessageToListeners (const int parameterIndex, const float newValue) +{ + jassert (((unsigned int) parameterIndex) < (unsigned int) getNumParameters()); + + for (int i = listeners.size(); --i >= 0;) + { + listenerLock.enter(); + AudioProcessorListener* const l = (AudioProcessorListener*) listeners [i]; + listenerLock.exit(); + + if (l != 0) + l->audioProcessorParameterChanged (this, parameterIndex, newValue); + } +} + +void AudioProcessor::beginParameterChangeGesture (int parameterIndex) +{ + jassert (((unsigned int) parameterIndex) < (unsigned int) getNumParameters()); + +#ifdef JUCE_DEBUG + // This means you've called beginParameterChangeGesture twice in succession without a matching + // call to endParameterChangeGesture. That might be fine in most hosts, but better to avoid doing it. + jassert (! changingParams [parameterIndex]); + changingParams.setBit (parameterIndex); +#endif + + for (int i = listeners.size(); --i >= 0;) + { + listenerLock.enter(); + AudioProcessorListener* const l = (AudioProcessorListener*) listeners [i]; + listenerLock.exit(); + + if (l != 0) + l->audioProcessorParameterChangeGestureBegin (this, parameterIndex); + } +} + +void AudioProcessor::endParameterChangeGesture (int parameterIndex) +{ + jassert (((unsigned int) parameterIndex) < (unsigned int) getNumParameters()); + +#ifdef JUCE_DEBUG + // This means you've called endParameterChangeGesture without having previously called + // endParameterChangeGesture. That might be fine in most hosts, but better to keep the + // calls matched correctly. + jassert (changingParams [parameterIndex]); + changingParams.clearBit (parameterIndex); +#endif + + for (int i = listeners.size(); --i >= 0;) + { + listenerLock.enter(); + AudioProcessorListener* const l = (AudioProcessorListener*) listeners [i]; + listenerLock.exit(); + + if (l != 0) + l->audioProcessorParameterChangeGestureEnd (this, parameterIndex); + } +} + +void AudioProcessor::updateHostDisplay() +{ + for (int i = listeners.size(); --i >= 0;) + { + listenerLock.enter(); + AudioProcessorListener* const l = (AudioProcessorListener*) listeners [i]; + listenerLock.exit(); + + if (l != 0) + l->audioProcessorChanged (this); + } +} + +bool AudioProcessor::isParameterAutomatable (int /*index*/) const +{ + return true; +} + +void AudioProcessor::suspendProcessing (const bool shouldBeSuspended) +{ + const ScopedLock sl (callbackLock); + suspended = shouldBeSuspended; +} + +void AudioProcessor::editorBeingDeleted (AudioProcessorEditor* const editor) throw() +{ + const ScopedLock sl (callbackLock); + + jassert (activeEditor == editor); + + if (activeEditor == editor) + activeEditor = 0; +} + +AudioProcessorEditor* AudioProcessor::createEditorIfNeeded() +{ + if (activeEditor != 0) + return activeEditor; + + AudioProcessorEditor* const ed = createEditor(); + + if (ed != 0) + { + // you must give your editor comp a size before returning it.. + jassert (ed->getWidth() > 0 && ed->getHeight() > 0); + + const ScopedLock sl (callbackLock); + activeEditor = ed; + } + + return ed; +} + +void AudioProcessor::getCurrentProgramStateInformation (JUCE_NAMESPACE::MemoryBlock& destData) +{ + getStateInformation (destData); +} + +void AudioProcessor::setCurrentProgramStateInformation (const void* data, int sizeInBytes) +{ + setStateInformation (data, sizeInBytes); +} + +// magic number to identify memory blocks that we've stored as XML +const uint32 magicXmlNumber = 0x21324356; + +void AudioProcessor::copyXmlToBinary (const XmlElement& xml, + JUCE_NAMESPACE::MemoryBlock& destData) +{ + const String xmlString (xml.createDocument (String::empty, true, false)); + const int stringLength = xmlString.length(); + + destData.setSize (stringLength + 10); + + char* const d = (char*) destData.getData(); + *(uint32*) d = swapIfBigEndian ((const uint32) magicXmlNumber); + *(uint32*) (d + 4) = swapIfBigEndian ((const uint32) stringLength); + + xmlString.copyToBuffer (d + 8, stringLength); +} + +XmlElement* AudioProcessor::getXmlFromBinary (const void* data, + const int sizeInBytes) +{ + if (sizeInBytes > 8 + && littleEndianInt ((const char*) data) == magicXmlNumber) + { + const uint32 stringLength = littleEndianInt (((const char*) data) + 4); + + if (stringLength > 0) + { + XmlDocument doc (String (((const char*) data) + 8, + jmin ((sizeInBytes - 8), stringLength))); + + return doc.getDocumentElement(); + } + } + + return 0; +} + +void AudioProcessorListener::audioProcessorParameterChangeGestureBegin (AudioProcessor*, int) +{ +} + +void AudioProcessorListener::audioProcessorParameterChangeGestureEnd (AudioProcessor*, int) +{ +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_AudioProcessor.cpp *********/ + +/********* Start of inlined file: juce_AudioProcessorEditor.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +AudioProcessorEditor::AudioProcessorEditor (AudioProcessor* const owner_) + : owner (owner_) +{ + // the filter must be valid.. + jassert (owner != 0); +} + +AudioProcessorEditor::~AudioProcessorEditor() +{ + // if this fails, then the wrapper hasn't called editorBeingDeleted() on the + // filter for some reason.. + jassert (owner->getActiveEditor() != this); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_AudioProcessorEditor.cpp *********/ + +/********* Start of inlined file: juce_AudioProcessorGraph.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +const int AudioProcessorGraph::midiChannelIndex = 0x1000; + +AudioProcessorGraph::Node::Node (const uint32 id_, + AudioProcessor* const processor_) throw() + : id (id_), + processor (processor_), + isPrepared (false) +{ + jassert (processor_ != 0); +} + +AudioProcessorGraph::Node::~Node() +{ + delete processor; +} + +void AudioProcessorGraph::Node::prepare (const double sampleRate, const int blockSize, + AudioProcessorGraph* const graph) +{ + if (! isPrepared) + { + isPrepared = true; + + AudioProcessorGraph::AudioGraphIOProcessor* const ioProc + = dynamic_cast (processor); + + if (ioProc != 0) + ioProc->setParentGraph (graph); + + processor->setPlayConfigDetails (processor->getNumInputChannels(), + processor->getNumOutputChannels(), + sampleRate, blockSize); + + processor->prepareToPlay (sampleRate, blockSize); + } +} + +void AudioProcessorGraph::Node::unprepare() +{ + if (isPrepared) + { + isPrepared = false; + processor->releaseResources(); + } +} + +AudioProcessorGraph::AudioProcessorGraph() + : lastNodeId (0), + renderingBuffers (1, 1) +{ +} + +AudioProcessorGraph::~AudioProcessorGraph() +{ + clearRenderingSequence(); + clear(); +} + +const String AudioProcessorGraph::getName() const +{ + return "Audio Graph"; +} + +void AudioProcessorGraph::clear() +{ + nodes.clear(); + connections.clear(); + triggerAsyncUpdate(); +} + +AudioProcessorGraph::Node* AudioProcessorGraph::getNodeForId (const uint32 nodeId) const throw() +{ + for (int i = nodes.size(); --i >= 0;) + if (nodes.getUnchecked(i)->id == nodeId) + return nodes.getUnchecked(i); + + return 0; +} + +AudioProcessorGraph::Node* AudioProcessorGraph::addNode (AudioProcessor* const newProcessor, + uint32 nodeId) +{ + if (newProcessor == 0) + { + jassertfalse + return 0; + } + + if (nodeId == 0) + { + nodeId = ++lastNodeId; + } + else + { + // you can't add a node with an id that already exists in the graph.. + jassert (getNodeForId (nodeId) == 0); + removeNode (nodeId); + } + + lastNodeId = nodeId; + + Node* const n = new Node (nodeId, newProcessor); + nodes.add (n); + triggerAsyncUpdate(); + + AudioProcessorGraph::AudioGraphIOProcessor* const ioProc + = dynamic_cast (n->processor); + + if (ioProc != 0) + ioProc->setParentGraph (this); + + return n; +} + +bool AudioProcessorGraph::removeNode (const uint32 nodeId) +{ + disconnectNode (nodeId); + + for (int i = nodes.size(); --i >= 0;) + { + if (nodes.getUnchecked(i)->id == nodeId) + { + AudioProcessorGraph::AudioGraphIOProcessor* const ioProc + = dynamic_cast (nodes.getUnchecked(i)->processor); + + if (ioProc != 0) + ioProc->setParentGraph (0); + + nodes.remove (i); + triggerAsyncUpdate(); + + return true; + } + } + + return false; +} + +const AudioProcessorGraph::Connection* AudioProcessorGraph::getConnectionBetween (const uint32 sourceNodeId, + const int sourceChannelIndex, + const uint32 destNodeId, + const int destChannelIndex) const throw() +{ + for (int i = connections.size(); --i >= 0;) + { + const Connection* const c = connections.getUnchecked(i); + + if (c->sourceNodeId == sourceNodeId + && c->destNodeId == destNodeId + && c->sourceChannelIndex == sourceChannelIndex + && c->destChannelIndex == destChannelIndex) + { + return c; + } + } + + return 0; +} + +bool AudioProcessorGraph::isConnected (const uint32 possibleSourceNodeId, + const uint32 possibleDestNodeId) const throw() +{ + for (int i = connections.size(); --i >= 0;) + { + const Connection* const c = connections.getUnchecked(i); + + if (c->sourceNodeId == possibleSourceNodeId + && c->destNodeId == possibleDestNodeId) + { + return true; + } + } + + return false; +} + +bool AudioProcessorGraph::canConnect (const uint32 sourceNodeId, + const int sourceChannelIndex, + const uint32 destNodeId, + const int destChannelIndex) const throw() +{ + if (sourceChannelIndex < 0 + || destChannelIndex < 0 + || sourceNodeId == destNodeId + || (destChannelIndex == midiChannelIndex) != (sourceChannelIndex == midiChannelIndex)) + return false; + + const Node* const source = getNodeForId (sourceNodeId); + + if (source == 0 + || (sourceChannelIndex != midiChannelIndex && sourceChannelIndex >= source->processor->getNumOutputChannels()) + || (sourceChannelIndex == midiChannelIndex && ! source->processor->producesMidi())) + return false; + + const Node* const dest = getNodeForId (destNodeId); + + if (dest == 0 + || (destChannelIndex != midiChannelIndex && destChannelIndex >= dest->processor->getNumInputChannels()) + || (destChannelIndex == midiChannelIndex && ! dest->processor->acceptsMidi())) + return false; + + return getConnectionBetween (sourceNodeId, sourceChannelIndex, + destNodeId, destChannelIndex) == 0; +} + +bool AudioProcessorGraph::addConnection (const uint32 sourceNodeId, + const int sourceChannelIndex, + const uint32 destNodeId, + const int destChannelIndex) +{ + if (! canConnect (sourceNodeId, sourceChannelIndex, destNodeId, destChannelIndex)) + return false; + + Connection* const c = new Connection(); + c->sourceNodeId = sourceNodeId; + c->sourceChannelIndex = sourceChannelIndex; + c->destNodeId = destNodeId; + c->destChannelIndex = destChannelIndex; + + connections.add (c); + triggerAsyncUpdate(); + + return true; +} + +void AudioProcessorGraph::removeConnection (const int index) +{ + connections.remove (index); + triggerAsyncUpdate(); +} + +bool AudioProcessorGraph::removeConnection (const uint32 sourceNodeId, const int sourceChannelIndex, + const uint32 destNodeId, const int destChannelIndex) +{ + bool doneAnything = false; + + for (int i = connections.size(); --i >= 0;) + { + const Connection* const c = connections.getUnchecked(i); + + if (c->sourceNodeId == sourceNodeId + && c->destNodeId == destNodeId + && c->sourceChannelIndex == sourceChannelIndex + && c->destChannelIndex == destChannelIndex) + { + removeConnection (i); + doneAnything = true; + triggerAsyncUpdate(); + } + } + + return doneAnything; +} + +bool AudioProcessorGraph::disconnectNode (const uint32 nodeId) +{ + bool doneAnything = false; + + for (int i = connections.size(); --i >= 0;) + { + const Connection* const c = connections.getUnchecked(i); + + if (c->sourceNodeId == nodeId || c->destNodeId == nodeId) + { + removeConnection (i); + doneAnything = true; + triggerAsyncUpdate(); + } + } + + return doneAnything; +} + +bool AudioProcessorGraph::removeIllegalConnections() +{ + bool doneAnything = false; + + for (int i = connections.size(); --i >= 0;) + { + const Connection* const c = connections.getUnchecked(i); + + const Node* const source = getNodeForId (c->sourceNodeId); + const Node* const dest = getNodeForId (c->destNodeId); + + if (source == 0 || dest == 0 + || (c->sourceChannelIndex != midiChannelIndex + && (((unsigned int) c->sourceChannelIndex) >= (unsigned int) source->processor->getNumOutputChannels())) + || (c->sourceChannelIndex == midiChannelIndex + && ! source->processor->producesMidi()) + || (c->destChannelIndex != midiChannelIndex + && (((unsigned int) c->destChannelIndex) >= (unsigned int) dest->processor->getNumInputChannels())) + || (c->destChannelIndex == midiChannelIndex + && ! dest->processor->acceptsMidi())) + { + removeConnection (i); + doneAnything = true; + triggerAsyncUpdate(); + } + } + + return doneAnything; +} + +namespace GraphRenderingOps +{ + +class AudioGraphRenderingOp +{ +public: + AudioGraphRenderingOp() throw() {} + virtual ~AudioGraphRenderingOp() throw() {} + + virtual void perform (AudioSampleBuffer& sharedBufferChans, + const OwnedArray & sharedMidiBuffers, + const int numSamples) throw() = 0; + + juce_UseDebuggingNewOperator +}; + +class ClearChannelOp : public AudioGraphRenderingOp +{ +public: + ClearChannelOp (const int channelNum_) throw() + : channelNum (channelNum_) + {} + + ~ClearChannelOp() throw() {} + + void perform (AudioSampleBuffer& sharedBufferChans, const OwnedArray &, const int numSamples) throw() + { + sharedBufferChans.clear (channelNum, 0, numSamples); + } + +private: + const int channelNum; + + ClearChannelOp (const ClearChannelOp&); + const ClearChannelOp& operator= (const ClearChannelOp&); +}; + +class CopyChannelOp : public AudioGraphRenderingOp +{ +public: + CopyChannelOp (const int srcChannelNum_, const int dstChannelNum_) throw() + : srcChannelNum (srcChannelNum_), + dstChannelNum (dstChannelNum_) + {} + + ~CopyChannelOp() throw() {} + + void perform (AudioSampleBuffer& sharedBufferChans, const OwnedArray &, const int numSamples) throw() + { + sharedBufferChans.copyFrom (dstChannelNum, 0, sharedBufferChans, srcChannelNum, 0, numSamples); + } + +private: + const int srcChannelNum, dstChannelNum; + + CopyChannelOp (const CopyChannelOp&); + const CopyChannelOp& operator= (const CopyChannelOp&); +}; + +class AddChannelOp : public AudioGraphRenderingOp +{ +public: + AddChannelOp (const int srcChannelNum_, const int dstChannelNum_) throw() + : srcChannelNum (srcChannelNum_), + dstChannelNum (dstChannelNum_) + {} + + ~AddChannelOp() throw() {} + + void perform (AudioSampleBuffer& sharedBufferChans, + const OwnedArray & sharedMidiBuffers, + const int numSamples) throw() + { + if (dstChannelNum != AudioProcessorGraph::midiChannelIndex) + sharedBufferChans.addFrom (dstChannelNum, 0, sharedBufferChans, srcChannelNum, 0, numSamples); + else + sharedMidiBuffers.getUnchecked (dstChannelNum) + ->addEvents (*sharedMidiBuffers.getUnchecked (srcChannelNum), + 0, numSamples, 0); + } + +private: + const int srcChannelNum, dstChannelNum; + + AddChannelOp (const AddChannelOp&); + const AddChannelOp& operator= (const AddChannelOp&); +}; + +class ClearMidiBufferOp : public AudioGraphRenderingOp +{ +public: + ClearMidiBufferOp (const int bufferNum_) throw() + : bufferNum (bufferNum_) + {} + + ~ClearMidiBufferOp() throw() {} + + void perform (AudioSampleBuffer&, const OwnedArray & sharedMidiBuffers, const int) throw() + { + sharedMidiBuffers.getUnchecked (bufferNum)->clear(); + } + +private: + const int bufferNum; + + ClearMidiBufferOp (const ClearMidiBufferOp&); + const ClearMidiBufferOp& operator= (const ClearMidiBufferOp&); +}; + +class CopyMidiBufferOp : public AudioGraphRenderingOp +{ +public: + CopyMidiBufferOp (const int srcBufferNum_, const int dstBufferNum_) throw() + : srcBufferNum (srcBufferNum_), + dstBufferNum (dstBufferNum_) + {} + + ~CopyMidiBufferOp() throw() {} + + void perform (AudioSampleBuffer&, const OwnedArray & sharedMidiBuffers, const int) throw() + { + *sharedMidiBuffers.getUnchecked (dstBufferNum) = *sharedMidiBuffers.getUnchecked (srcBufferNum); + } + +private: + const int srcBufferNum, dstBufferNum; + + CopyMidiBufferOp (const CopyMidiBufferOp&); + const CopyMidiBufferOp& operator= (const CopyMidiBufferOp&); +}; + +class AddMidiBufferOp : public AudioGraphRenderingOp +{ +public: + AddMidiBufferOp (const int srcBufferNum_, const int dstBufferNum_) throw() + : srcBufferNum (srcBufferNum_), + dstBufferNum (dstBufferNum_) + {} + + ~AddMidiBufferOp() throw() {} + + void perform (AudioSampleBuffer&, const OwnedArray & sharedMidiBuffers, const int numSamples) throw() + { + sharedMidiBuffers.getUnchecked (dstBufferNum) + ->addEvents (*sharedMidiBuffers.getUnchecked (srcBufferNum), 0, numSamples, 0); + } + +private: + const int srcBufferNum, dstBufferNum; + + AddMidiBufferOp (const AddMidiBufferOp&); + const AddMidiBufferOp& operator= (const AddMidiBufferOp&); +}; + +class ProcessBufferOp : public AudioGraphRenderingOp +{ +public: + ProcessBufferOp (const AudioProcessorGraph::Node::Ptr& node_, + const Array & audioChannelsToUse_, + const int totalChans_, + const int midiBufferToUse_) throw() + : node (node_), + processor (node_->processor), + audioChannelsToUse (audioChannelsToUse_), + totalChans (totalChans_), + midiBufferToUse (midiBufferToUse_) + { + channels = (float**) juce_calloc (sizeof (float*) * totalChans_); + } + + ~ProcessBufferOp() throw() + { + juce_free (channels); + } + + void perform (AudioSampleBuffer& sharedBufferChans, const OwnedArray & sharedMidiBuffers, const int numSamples) throw() + { + for (int i = totalChans; --i >= 0;) + channels[i] = sharedBufferChans.getSampleData (audioChannelsToUse.getUnchecked (i), 0); + + AudioSampleBuffer buffer (channels, totalChans, numSamples); + + processor->processBlock (buffer, *sharedMidiBuffers.getUnchecked (midiBufferToUse)); + } + + const AudioProcessorGraph::Node::Ptr node; + AudioProcessor* const processor; + +private: + Array audioChannelsToUse; + float** channels; + int totalChans; + int midiBufferToUse; + + ProcessBufferOp (const ProcessBufferOp&); + const ProcessBufferOp& operator= (const ProcessBufferOp&); +}; + +/** Used to calculate the correct sequence of rendering ops needed, based on + the best re-use of shared buffers at each stage. +*/ +class RenderingOpSequenceCalculator +{ +public: + + RenderingOpSequenceCalculator (AudioProcessorGraph& graph_, + const VoidArray& orderedNodes_, + VoidArray& renderingOps) + : graph (graph_), + orderedNodes (orderedNodes_) + { + nodeIds.add (-2); // first buffer is read-only zeros + channels.add (0); + + midiNodeIds.add (-2); + + for (int i = 0; i < orderedNodes.size(); ++i) + { + createRenderingOpsForNode ((AudioProcessorGraph::Node*) orderedNodes.getUnchecked(i), + renderingOps, i); + + markAnyUnusedBuffersAsFree (i); + } + } + + int getNumBuffersNeeded() const throw() { return nodeIds.size(); } + + int getNumMidiBuffersNeeded() const throw() { return midiNodeIds.size(); } + + juce_UseDebuggingNewOperator + +private: + AudioProcessorGraph& graph; + const VoidArray& orderedNodes; + Array nodeIds, channels, midiNodeIds; + + void createRenderingOpsForNode (AudioProcessorGraph::Node* const node, + VoidArray& renderingOps, + const int ourRenderingIndex) + { + const int numIns = node->processor->getNumInputChannels(); + const int numOuts = node->processor->getNumOutputChannels(); + const int totalChans = jmax (numIns, numOuts); + + Array audioChannelsToUse; + int midiBufferToUse = -1; + + for (int inputChan = 0; inputChan < numIns; ++inputChan) + { + // get a list of all the inputs to this node + Array sourceNodes, sourceOutputChans; + + for (int i = graph.getNumConnections(); --i >= 0;) + { + const AudioProcessorGraph::Connection* const c = graph.getConnection (i); + + if (c->destNodeId == node->id && c->destChannelIndex == inputChan) + { + sourceNodes.add (c->sourceNodeId); + sourceOutputChans.add (c->sourceChannelIndex); + } + } + + int bufIndex = -1; + + if (sourceNodes.size() == 0) + { + // unconnected input channel + + if (inputChan >= numOuts) + { + bufIndex = getReadOnlyEmptyBuffer(); + jassert (bufIndex >= 0); + } + else + { + bufIndex = getFreeBuffer (false); + renderingOps.add (new ClearChannelOp (bufIndex)); + } + } + else if (sourceNodes.size() == 1) + { + // channel with a straightforward single input.. + const int srcNode = sourceNodes.getUnchecked(0); + const int srcChan = sourceOutputChans.getUnchecked(0); + + bufIndex = getBufferContaining (srcNode, srcChan); + jassert (bufIndex >= 0); + + if (inputChan < numOuts + && isBufferNeededLater (ourRenderingIndex, + inputChan, + srcNode, srcChan)) + { + // can't mess up this channel because it's needed later by another node, so we + // need to use a copy of it.. + const int newFreeBuffer = getFreeBuffer (false); + + renderingOps.add (new CopyChannelOp (bufIndex, newFreeBuffer)); + + bufIndex = newFreeBuffer; + } + } + else + { + // channel with a mix of several inputs.. + + // try to find a re-usable channel from our inputs.. + int reusableInputIndex = -1; + + for (int i = 0; i < sourceNodes.size(); ++i) + { + const int sourceBufIndex = getBufferContaining (sourceNodes.getUnchecked(i), + sourceOutputChans.getUnchecked(i)); + + jassert (sourceBufIndex >= 0); + + if (! isBufferNeededLater (ourRenderingIndex, + inputChan, + sourceNodes.getUnchecked(i), + sourceOutputChans.getUnchecked(i))) + { + // we've found one of our input chans that can be re-used.. + reusableInputIndex = i; + bufIndex = sourceBufIndex; + break; + } + } + + if (reusableInputIndex < 0) + { + // can't re-use any of our input chans, so get a new one and copy everything into it.. + bufIndex = getFreeBuffer (false); + jassert (bufIndex != 0); + + const int srcIndex = getBufferContaining (sourceNodes.getUnchecked (0), + sourceOutputChans.getUnchecked (0)); + jassert (srcIndex >= 0); + + renderingOps.add (new CopyChannelOp (srcIndex, bufIndex)); + + reusableInputIndex = 0; + } + + for (int j = 0; j < sourceNodes.size(); ++j) + { + if (j != reusableInputIndex) + { + const int srcIndex = getBufferContaining (sourceNodes.getUnchecked(j), + sourceOutputChans.getUnchecked(j)); + jassert (srcIndex >= 0); + + renderingOps.add (new AddChannelOp (srcIndex, bufIndex)); + } + } + } + + jassert (bufIndex >= 0); + audioChannelsToUse.add (bufIndex); + + if (inputChan < numOuts) + markBufferAsContaining (bufIndex, node->id, inputChan); + } + + for (int outputChan = numIns; outputChan < numOuts; ++outputChan) + { + const int bufIndex = getFreeBuffer (false); + jassert (bufIndex != 0); + audioChannelsToUse.add (bufIndex); + + markBufferAsContaining (bufIndex, node->id, outputChan); + } + + // Now the same thing for midi.. + Array midiSourceNodes; + + for (int i = graph.getNumConnections(); --i >= 0;) + { + const AudioProcessorGraph::Connection* const c = graph.getConnection (i); + + if (c->destNodeId == node->id && c->destChannelIndex == AudioProcessorGraph::midiChannelIndex) + midiSourceNodes.add (c->sourceNodeId); + } + + if (midiSourceNodes.size() == 0) + { + // No midi inputs.. + midiBufferToUse = getFreeBuffer (true); // need to pick a buffer even if the processor doesn't use midi + + if (node->processor->acceptsMidi() || node->processor->producesMidi()) + renderingOps.add (new ClearMidiBufferOp (midiBufferToUse)); + } + else if (midiSourceNodes.size() == 1) + { + // One midi input.. + midiBufferToUse = getBufferContaining (midiSourceNodes.getUnchecked(0), + AudioProcessorGraph::midiChannelIndex); + + jassert (midiBufferToUse >= 0); + + if (midiBufferToUse >= 0) + { + if (isBufferNeededLater (ourRenderingIndex, + AudioProcessorGraph::midiChannelIndex, + midiSourceNodes.getUnchecked(0), + AudioProcessorGraph::midiChannelIndex)) + { + // can't mess up this channel because it's needed later by another node, so we + // need to use a copy of it.. + const int newFreeBuffer = getFreeBuffer (true); + renderingOps.add (new CopyMidiBufferOp (midiBufferToUse, newFreeBuffer)); + midiBufferToUse = newFreeBuffer; + } + } + } + else + { + // More than one midi input being mixed.. + int reusableInputIndex = -1; + + for (int i = 0; i < midiSourceNodes.size(); ++i) + { + const int sourceBufIndex = getBufferContaining (midiSourceNodes.getUnchecked(i), + AudioProcessorGraph::midiChannelIndex); + + jassert (sourceBufIndex >= 0); + + if (! isBufferNeededLater (ourRenderingIndex, + AudioProcessorGraph::midiChannelIndex, + midiSourceNodes.getUnchecked(i), + AudioProcessorGraph::midiChannelIndex)) + { + // we've found one of our input buffers that can be re-used.. + reusableInputIndex = i; + midiBufferToUse = sourceBufIndex; + break; + } + } + + if (reusableInputIndex < 0) + { + // can't re-use any of our input buffers, so get a new one and copy everything into it.. + midiBufferToUse = getFreeBuffer (true); + jassert (midiBufferToUse >= 0); + + const int srcIndex = getBufferContaining (midiSourceNodes.getUnchecked(0), + AudioProcessorGraph::midiChannelIndex); + jassert (srcIndex >= 0); + + renderingOps.add (new CopyMidiBufferOp (srcIndex, midiBufferToUse)); + + reusableInputIndex = 0; + } + + for (int j = 0; j < midiSourceNodes.size(); ++j) + { + if (j != reusableInputIndex) + { + const int srcIndex = getBufferContaining (midiSourceNodes.getUnchecked(j), + AudioProcessorGraph::midiChannelIndex); + jassert (srcIndex >= 0); + + renderingOps.add (new AddMidiBufferOp (srcIndex, midiBufferToUse)); + } + } + } + + if (node->processor->producesMidi()) + markBufferAsContaining (midiBufferToUse, node->id, + AudioProcessorGraph::midiChannelIndex); + + renderingOps.add (new ProcessBufferOp (node, audioChannelsToUse, + totalChans, midiBufferToUse)); + } + + int getFreeBuffer (const bool forMidi) + { + if (forMidi) + { + for (int i = 1; i < midiNodeIds.size(); ++i) + if (midiNodeIds.getUnchecked(i) < 0) + return i; + + midiNodeIds.add (-1); + return midiNodeIds.size() - 1; + } + else + { + for (int i = 1; i < nodeIds.size(); ++i) + if (nodeIds.getUnchecked(i) < 0) + return i; + + nodeIds.add (-1); + channels.add (0); + return nodeIds.size() - 1; + } + } + + int getReadOnlyEmptyBuffer() const throw() + { + return 0; + } + + int getBufferContaining (const int nodeId, const int outputChannel) const throw() + { + if (outputChannel == AudioProcessorGraph::midiChannelIndex) + { + for (int i = midiNodeIds.size(); --i >= 0;) + if (midiNodeIds.getUnchecked(i) == nodeId) + return i; + } + else + { + for (int i = nodeIds.size(); --i >= 0;) + if (nodeIds.getUnchecked(i) == nodeId + && channels.getUnchecked(i) == outputChannel) + return i; + } + + return -1; + } + + void markAnyUnusedBuffersAsFree (const int stepIndex) + { + int i; + for (i = 0; i < nodeIds.size(); ++i) + { + if (nodeIds.getUnchecked(i) >= 0 + && ! isBufferNeededLater (stepIndex, -1, + nodeIds.getUnchecked(i), + channels.getUnchecked(i))) + { + nodeIds.set (i, -1); + } + } + + for (i = 0; i < midiNodeIds.size(); ++i) + { + if (midiNodeIds.getUnchecked(i) >= 0 + && ! isBufferNeededLater (stepIndex, -1, + midiNodeIds.getUnchecked(i), + AudioProcessorGraph::midiChannelIndex)) + { + midiNodeIds.set (i, -1); + } + } + } + + bool isBufferNeededLater (int stepIndexToSearchFrom, + int inputChannelOfIndexToIgnore, + const int nodeId, + const int outputChanIndex) const throw() + { + while (stepIndexToSearchFrom < orderedNodes.size()) + { + const AudioProcessorGraph::Node* const node = (const AudioProcessorGraph::Node*) orderedNodes.getUnchecked (stepIndexToSearchFrom); + + if (outputChanIndex == AudioProcessorGraph::midiChannelIndex) + { + if (inputChannelOfIndexToIgnore != AudioProcessorGraph::midiChannelIndex + && graph.getConnectionBetween (nodeId, AudioProcessorGraph::midiChannelIndex, + node->id, AudioProcessorGraph::midiChannelIndex) != 0) + return true; + } + else + { + for (int i = 0; i < node->processor->getNumInputChannels(); ++i) + if (i != inputChannelOfIndexToIgnore + && graph.getConnectionBetween (nodeId, outputChanIndex, + node->id, i) != 0) + return true; + } + + inputChannelOfIndexToIgnore = -1; + ++stepIndexToSearchFrom; + } + + return false; + } + + void markBufferAsContaining (int bufferNum, int nodeId, int outputIndex) + { + if (outputIndex == AudioProcessorGraph::midiChannelIndex) + { + jassert (bufferNum > 0 && bufferNum < midiNodeIds.size()); + + midiNodeIds.set (bufferNum, nodeId); + } + else + { + jassert (bufferNum > 0 && bufferNum < nodeIds.size()); + + nodeIds.set (bufferNum, nodeId); + channels.set (bufferNum, outputIndex); + } + } + + RenderingOpSequenceCalculator (const RenderingOpSequenceCalculator&); + const RenderingOpSequenceCalculator& operator= (const RenderingOpSequenceCalculator&); +}; + +} + +void AudioProcessorGraph::clearRenderingSequence() +{ + const ScopedLock sl (renderLock); + + for (int i = renderingOps.size(); --i >= 0;) + { + GraphRenderingOps::AudioGraphRenderingOp* const r + = (GraphRenderingOps::AudioGraphRenderingOp*) renderingOps.getUnchecked(i); + + renderingOps.remove (i); + delete r; + } +} + +bool AudioProcessorGraph::isAnInputTo (const uint32 possibleInputId, + const uint32 possibleDestinationId, + const int recursionCheck) const throw() +{ + if (recursionCheck > 0) + { + for (int i = connections.size(); --i >= 0;) + { + const AudioProcessorGraph::Connection* const c = connections.getUnchecked (i); + + if (c->destNodeId == possibleDestinationId + && (c->sourceNodeId == possibleInputId + || isAnInputTo (possibleInputId, c->sourceNodeId, recursionCheck - 1))) + return true; + } + } + + return false; +} + +void AudioProcessorGraph::buildRenderingSequence() +{ + VoidArray newRenderingOps; + int numRenderingBuffersNeeded = 2; + int numMidiBuffersNeeded = 1; + + { + MessageManagerLock mml; + + VoidArray orderedNodes; + + int i; + for (i = 0; i < nodes.size(); ++i) + { + Node* const node = nodes.getUnchecked(i); + + node->prepare (getSampleRate(), getBlockSize(), this); + + int j = 0; + for (; j < orderedNodes.size(); ++j) + if (isAnInputTo (node->id, + ((Node*) orderedNodes.getUnchecked (j))->id, + nodes.size() + 1)) + break; + + orderedNodes.insert (j, node); + } + + GraphRenderingOps::RenderingOpSequenceCalculator calculator (*this, orderedNodes, newRenderingOps); + + numRenderingBuffersNeeded = calculator.getNumBuffersNeeded(); + numMidiBuffersNeeded = calculator.getNumMidiBuffersNeeded(); + } + + VoidArray oldRenderingOps (renderingOps); + + { + // swap over to the new set of rendering sequence.. + const ScopedLock sl (renderLock); + + renderingBuffers.setSize (numRenderingBuffersNeeded, getBlockSize()); + renderingBuffers.clear(); + + for (int i = midiBuffers.size(); --i >= 0;) + midiBuffers.getUnchecked(i)->clear(); + + while (midiBuffers.size() < numMidiBuffersNeeded) + midiBuffers.add (new MidiBuffer()); + + renderingOps = newRenderingOps; + } + + for (int i = oldRenderingOps.size(); --i >= 0;) + delete (GraphRenderingOps::AudioGraphRenderingOp*) oldRenderingOps.getUnchecked(i); +} + +void AudioProcessorGraph::handleAsyncUpdate() +{ + buildRenderingSequence(); +} + +void AudioProcessorGraph::prepareToPlay (double /*sampleRate*/, int /*estimatedSamplesPerBlock*/) +{ + currentAudioIOBuffer = 0; + currentMidiIOBuffer = 0; + + clearRenderingSequence(); + buildRenderingSequence(); +} + +void AudioProcessorGraph::releaseResources() +{ + for (int i = 0; i < nodes.size(); ++i) + nodes.getUnchecked(i)->unprepare(); + + renderingBuffers.setSize (1, 1); + midiBuffers.clear(); + + currentAudioIOBuffer = 0; + currentMidiIOBuffer = 0; +} + +void AudioProcessorGraph::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) +{ + const ScopedLock sl (renderLock); + + currentAudioIOBuffer = &buffer; + currentMidiIOBuffer = &midiMessages; + + const int numSamples = buffer.getNumSamples(); + + for (int i = 0; i < renderingOps.size(); ++i) + { + GraphRenderingOps::AudioGraphRenderingOp* const op + = (GraphRenderingOps::AudioGraphRenderingOp*) renderingOps.getUnchecked(i); + + op->perform (renderingBuffers, midiBuffers, numSamples); + } +} + +const String AudioProcessorGraph::getInputChannelName (const int channelIndex) const +{ + return "Input " + String (channelIndex + 1); +} + +const String AudioProcessorGraph::getOutputChannelName (const int channelIndex) const +{ + return "Output " + String (channelIndex + 1); +} + +bool AudioProcessorGraph::isInputChannelStereoPair (int /*index*/) const +{ + return true; +} + +bool AudioProcessorGraph::isOutputChannelStereoPair (int /*index*/) const +{ + return true; +} + +bool AudioProcessorGraph::acceptsMidi() const +{ + return true; +} + +bool AudioProcessorGraph::producesMidi() const +{ + return true; +} + +void AudioProcessorGraph::getStateInformation (JUCE_NAMESPACE::MemoryBlock& /*destData*/) +{ +} + +void AudioProcessorGraph::setStateInformation (const void* /*data*/, int /*sizeInBytes*/) +{ +} + +AudioProcessorGraph::AudioGraphIOProcessor::AudioGraphIOProcessor (const IODeviceType type_) + : type (type_), + graph (0) +{ +} + +AudioProcessorGraph::AudioGraphIOProcessor::~AudioGraphIOProcessor() +{ +} + +const String AudioProcessorGraph::AudioGraphIOProcessor::getName() const +{ + switch (type) + { + case audioOutputNode: + return "Audio Output"; + case audioInputNode: + return "Audio Input"; + case midiOutputNode: + return "Midi Output"; + case midiInputNode: + return "Midi Input"; + default: + break; + } + + return String::empty; +} + +void AudioProcessorGraph::AudioGraphIOProcessor::fillInPluginDescription (PluginDescription& d) const +{ + d.name = getName(); + d.uid = d.name.hashCode(); + d.category = "I/O devices"; + d.pluginFormatName = "Internal"; + d.manufacturerName = "Raw Material Software"; + d.version = "1.0"; + d.isInstrument = false; + + d.numInputChannels = getNumInputChannels(); + if (type == audioOutputNode && graph != 0) + d.numInputChannels = graph->getNumInputChannels(); + + d.numOutputChannels = getNumOutputChannels(); + if (type == audioInputNode && graph != 0) + d.numOutputChannels = graph->getNumOutputChannels(); +} + +void AudioProcessorGraph::AudioGraphIOProcessor::prepareToPlay (double, int) +{ + jassert (graph != 0); +} + +void AudioProcessorGraph::AudioGraphIOProcessor::releaseResources() +{ +} + +void AudioProcessorGraph::AudioGraphIOProcessor::processBlock (AudioSampleBuffer& buffer, + MidiBuffer& midiMessages) +{ + jassert (graph != 0); + + switch (type) + { + case audioOutputNode: + { + for (int i = jmin (graph->currentAudioIOBuffer->getNumChannels(), + buffer.getNumChannels()); --i >= 0;) + { + graph->currentAudioIOBuffer->addFrom (i, 0, buffer, i, 0, buffer.getNumSamples()); + } + + break; + } + + case audioInputNode: + { + for (int i = jmin (graph->currentAudioIOBuffer->getNumChannels(), + buffer.getNumChannels()); --i >= 0;) + { + buffer.addFrom (i, 0, *graph->currentAudioIOBuffer, i, 0, buffer.getNumSamples()); + } + + break; + } + + case midiOutputNode: + graph->currentMidiIOBuffer->addEvents (midiMessages, 0, buffer.getNumSamples(), 0); + break; + + case midiInputNode: + midiMessages.addEvents (*graph->currentMidiIOBuffer, 0, buffer.getNumSamples(), 0); + break; + + default: + break; + } +} + +bool AudioProcessorGraph::AudioGraphIOProcessor::acceptsMidi() const +{ + return type == midiOutputNode; +} + +bool AudioProcessorGraph::AudioGraphIOProcessor::producesMidi() const +{ + return type == midiInputNode; +} + +const String AudioProcessorGraph::AudioGraphIOProcessor::getInputChannelName (const int channelIndex) const +{ + switch (type) + { + case audioOutputNode: + return "Output " + String (channelIndex + 1); + case midiOutputNode: + return "Midi Output"; + default: + break; + } + + return String::empty; +} + +const String AudioProcessorGraph::AudioGraphIOProcessor::getOutputChannelName (const int channelIndex) const +{ + switch (type) + { + case audioInputNode: + return "Input " + String (channelIndex + 1); + case midiInputNode: + return "Midi Input"; + default: + break; + } + + return String::empty; +} + +bool AudioProcessorGraph::AudioGraphIOProcessor::isInputChannelStereoPair (int /*index*/) const +{ + return type == audioInputNode || type == audioOutputNode; +} + +bool AudioProcessorGraph::AudioGraphIOProcessor::isOutputChannelStereoPair (int index) const +{ + return isInputChannelStereoPair (index); +} + +bool AudioProcessorGraph::AudioGraphIOProcessor::isInput() const throw() +{ + return type == audioInputNode || type == midiInputNode; +} + +bool AudioProcessorGraph::AudioGraphIOProcessor::isOutput() const throw() +{ + return type == audioOutputNode || type == midiOutputNode; +} + +AudioProcessorEditor* AudioProcessorGraph::AudioGraphIOProcessor::createEditor() +{ + return 0; +} + +int AudioProcessorGraph::AudioGraphIOProcessor::getNumParameters() { return 0; } +const String AudioProcessorGraph::AudioGraphIOProcessor::getParameterName (int) { return String::empty; } + +float AudioProcessorGraph::AudioGraphIOProcessor::getParameter (int) { return 0.0f; } +const String AudioProcessorGraph::AudioGraphIOProcessor::getParameterText (int) { return String::empty; } +void AudioProcessorGraph::AudioGraphIOProcessor::setParameter (int, float) { } + +int AudioProcessorGraph::AudioGraphIOProcessor::getNumPrograms() { return 0; } +int AudioProcessorGraph::AudioGraphIOProcessor::getCurrentProgram() { return 0; } +void AudioProcessorGraph::AudioGraphIOProcessor::setCurrentProgram (int) { } + +const String AudioProcessorGraph::AudioGraphIOProcessor::getProgramName (int) { return String::empty; } +void AudioProcessorGraph::AudioGraphIOProcessor::changeProgramName (int, const String&) { } + +void AudioProcessorGraph::AudioGraphIOProcessor::getStateInformation (JUCE_NAMESPACE::MemoryBlock&) +{ +} + +void AudioProcessorGraph::AudioGraphIOProcessor::setStateInformation (const void*, int) +{ +} + +void AudioProcessorGraph::AudioGraphIOProcessor::setParentGraph (AudioProcessorGraph* const newGraph) throw() +{ + graph = newGraph; + + if (graph != 0) + { + setPlayConfigDetails (type == audioOutputNode ? graph->getNumInputChannels() : 0, + type == audioInputNode ? graph->getNumOutputChannels() : 0, + getSampleRate(), + getBlockSize()); + + updateHostDisplay(); + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_AudioProcessorGraph.cpp *********/ + +/********* Start of inlined file: juce_AudioProcessorPlayer.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +AudioProcessorPlayer::AudioProcessorPlayer() + : processor (0), + sampleRate (0), + blockSize (0), + isPrepared (false), + numInputChans (0), + numOutputChans (0), + tempBuffer (1, 1) +{ +} + +AudioProcessorPlayer::~AudioProcessorPlayer() +{ + setProcessor (0); +} + +void AudioProcessorPlayer::setProcessor (AudioProcessor* const processorToPlay) +{ + if (processor != processorToPlay) + { + if (processorToPlay != 0 && sampleRate > 0 && blockSize > 0) + { + processorToPlay->setPlayConfigDetails (numInputChans, numOutputChans, + sampleRate, blockSize); + + processorToPlay->prepareToPlay (sampleRate, blockSize); + } + + lock.enter(); + AudioProcessor* const oldOne = isPrepared ? processor : 0; + processor = processorToPlay; + isPrepared = true; + lock.exit(); + + if (oldOne != 0) + oldOne->releaseResources(); + } +} + +void AudioProcessorPlayer::audioDeviceIOCallback (const float** inputChannelData, + int totalNumInputChannels, + float** outputChannelData, + int totalNumOutputChannels, + int numSamples) +{ + // these should have been prepared by audioDeviceAboutToStart()... + jassert (sampleRate > 0 && blockSize > 0); + + incomingMidi.clear(); + messageCollector.removeNextBlockOfMessages (incomingMidi, numSamples); + + int i, numActiveChans = 0, numInputs = 0, numOutputs = 0; + + // messy stuff needed to compact the channels down into an array + // of non-zero pointers.. + for (i = 0; i < totalNumInputChannels; ++i) + { + if (inputChannelData[i] != 0) + { + inputChans [numInputs++] = inputChannelData[i]; + if (numInputs >= numElementsInArray (inputChans)) + break; + } + } + + for (i = 0; i < totalNumOutputChannels; ++i) + { + if (outputChannelData[i] != 0) + { + outputChans [numOutputs++] = outputChannelData[i]; + if (numOutputs >= numElementsInArray (outputChans)) + break; + } + } + + if (numInputs > numOutputs) + { + // if there aren't enough output channels for the number of + // inputs, we need to create some temporary extra ones (can't + // use the input data in case it gets written to) + tempBuffer.setSize (numInputs - numOutputs, numSamples, + false, false, true); + + for (i = 0; i < numOutputs; ++i) + { + channels[numActiveChans] = outputChans[i]; + memcpy (channels[numActiveChans], inputChans[i], sizeof (float) * numSamples); + ++numActiveChans; + } + + for (i = numOutputs; i < numInputs; ++i) + { + channels[numActiveChans] = tempBuffer.getSampleData (i - numOutputs, 0); + memcpy (channels[numActiveChans], inputChans[i], sizeof (float) * numSamples); + ++numActiveChans; + } + } + else + { + for (i = 0; i < numInputs; ++i) + { + channels[numActiveChans] = outputChans[i]; + memcpy (channels[numActiveChans], inputChans[i], sizeof (float) * numSamples); + ++numActiveChans; + } + + for (i = numInputs; i < numOutputs; ++i) + { + channels[numActiveChans] = outputChans[i]; + zeromem (channels[numActiveChans], sizeof (float) * numSamples); + ++numActiveChans; + } + } + + AudioSampleBuffer buffer (channels, numActiveChans, numSamples); + + const ScopedLock sl (lock); + + if (processor != 0) + processor->processBlock (buffer, incomingMidi); +} + +void AudioProcessorPlayer::audioDeviceAboutToStart (AudioIODevice* device) +{ + const ScopedLock sl (lock); + + sampleRate = device->getCurrentSampleRate(); + blockSize = device->getCurrentBufferSizeSamples(); + numInputChans = device->getActiveInputChannels().countNumberOfSetBits(); + numOutputChans = device->getActiveOutputChannels().countNumberOfSetBits(); + + messageCollector.reset (sampleRate); + zeromem (channels, sizeof (channels)); + + if (processor != 0) + { + if (isPrepared) + processor->releaseResources(); + + AudioProcessor* const oldProcessor = processor; + setProcessor (0); + setProcessor (oldProcessor); + } +} + +void AudioProcessorPlayer::audioDeviceStopped() +{ + const ScopedLock sl (lock); + + if (processor != 0 && isPrepared) + processor->releaseResources(); + + sampleRate = 0.0; + blockSize = 0; + isPrepared = false; + tempBuffer.setSize (1, 1); +} + +void AudioProcessorPlayer::handleIncomingMidiMessage (MidiInput*, const MidiMessage& message) +{ + messageCollector.addMessageToQueue (message); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_AudioProcessorPlayer.cpp *********/ + +/********* Start of inlined file: juce_GenericAudioProcessorEditor.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +class ProcessorParameterPropertyComp : public PropertyComponent, + public AudioProcessorListener, + public AsyncUpdater +{ +public: + ProcessorParameterPropertyComp (const String& name, + AudioProcessor* const owner_, + const int index_) + : PropertyComponent (name), + owner (owner_), + index (index_) + { + addAndMakeVisible (slider = new ParamSlider (owner_, index_)); + owner_->addListener (this); + } + + ~ProcessorParameterPropertyComp() + { + owner->removeListener (this); + deleteAllChildren(); + } + + void refresh() + { + slider->setValue (owner->getParameter (index), false); + } + + void audioProcessorChanged (AudioProcessor*) {} + + void audioProcessorParameterChanged (AudioProcessor*, int parameterIndex, float) + { + if (parameterIndex == index) + triggerAsyncUpdate(); + } + + void handleAsyncUpdate() + { + refresh(); + } + + juce_UseDebuggingNewOperator + +private: + AudioProcessor* const owner; + const int index; + Slider* slider; + + class ParamSlider : public Slider + { + public: + ParamSlider (AudioProcessor* const owner_, const int index_) + : Slider (String::empty), + owner (owner_), + index (index_) + { + setRange (0.0, 1.0, 0.0); + setSliderStyle (Slider::LinearBar); + setTextBoxIsEditable (false); + setScrollWheelEnabled (false); + } + + ~ParamSlider() + { + } + + void valueChanged() + { + const float newVal = (float) getValue(); + + if (owner->getParameter (index) != newVal) + owner->setParameter (index, newVal); + } + + const String getTextFromValue (double /*value*/) + { + return owner->getParameterText (index); + } + + juce_UseDebuggingNewOperator + + private: + AudioProcessor* const owner; + const int index; + }; +}; + +GenericAudioProcessorEditor::GenericAudioProcessorEditor (AudioProcessor* const owner) + : AudioProcessorEditor (owner) +{ + setOpaque (true); + + addAndMakeVisible (panel = new PropertyPanel()); + + Array params; + + const int numParams = owner->getNumParameters(); + int totalHeight = 0; + + for (int i = 0; i < numParams; ++i) + { + String name (owner->getParameterName (i)); + if (name.trim().isEmpty()) + name = "Unnamed"; + + ProcessorParameterPropertyComp* const pc = new ProcessorParameterPropertyComp (name, owner, i); + params.add (pc); + totalHeight += pc->getPreferredHeight(); + } + + panel->addProperties (params); + + setSize (400, jlimit (25, 400, totalHeight)); +} + +GenericAudioProcessorEditor::~GenericAudioProcessorEditor() +{ + deleteAllChildren(); +} + +void GenericAudioProcessorEditor::paint (Graphics& g) +{ + g.fillAll (Colours::white); +} + +void GenericAudioProcessorEditor::resized() +{ + panel->setSize (getWidth(), getHeight()); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_GenericAudioProcessorEditor.cpp *********/ + +/********* Start of inlined file: juce_Sampler.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +SamplerSound::SamplerSound (const String& name_, + AudioFormatReader& source, + const BitArray& midiNotes_, + const int midiNoteForNormalPitch, + const double attackTimeSecs, + const double releaseTimeSecs, + const double maxSampleLengthSeconds) + : name (name_), + midiNotes (midiNotes_), + midiRootNote (midiNoteForNormalPitch) +{ + sourceSampleRate = source.sampleRate; + + if (sourceSampleRate <= 0 || source.lengthInSamples <= 0) + { + data = 0; + length = 0; + attackSamples = 0; + releaseSamples = 0; + } + else + { + length = jmin ((int) source.lengthInSamples, + (int) (maxSampleLengthSeconds * sourceSampleRate)); + + data = new AudioSampleBuffer (jmin (2, source.numChannels), length + 4); + + data->readFromAudioReader (&source, 0, length + 4, 0, true, true); + + attackSamples = roundDoubleToInt (attackTimeSecs * sourceSampleRate); + releaseSamples = roundDoubleToInt (releaseTimeSecs * sourceSampleRate); + } +} + +SamplerSound::~SamplerSound() +{ + delete data; + data = 0; +} + +bool SamplerSound::appliesToNote (const int midiNoteNumber) +{ + return midiNotes [midiNoteNumber]; +} + +bool SamplerSound::appliesToChannel (const int /*midiChannel*/) +{ + return true; +} + +SamplerVoice::SamplerVoice() + : pitchRatio (0.0), + sourceSamplePosition (0.0), + lgain (0.0f), + rgain (0.0f), + isInAttack (false), + isInRelease (false) +{ +} + +SamplerVoice::~SamplerVoice() +{ +} + +bool SamplerVoice::canPlaySound (SynthesiserSound* sound) +{ + return dynamic_cast (sound) != 0; +} + +void SamplerVoice::startNote (const int midiNoteNumber, + const float velocity, + SynthesiserSound* s, + const int /*currentPitchWheelPosition*/) +{ + const SamplerSound* const sound = dynamic_cast (s); + jassert (sound != 0); // this object can only play SamplerSounds! + + if (sound != 0) + { + const double targetFreq = MidiMessage::getMidiNoteInHertz (midiNoteNumber); + const double naturalFreq = MidiMessage::getMidiNoteInHertz (sound->midiRootNote); + + pitchRatio = (targetFreq * sound->sourceSampleRate) / (naturalFreq * getSampleRate()); + + sourceSamplePosition = 0.0; + lgain = velocity; + rgain = velocity; + + isInAttack = (sound->attackSamples > 0); + isInRelease = false; + + if (isInAttack) + { + attackReleaseLevel = 0.0f; + attackDelta = (float) (pitchRatio / sound->attackSamples); + } + else + { + attackReleaseLevel = 1.0f; + attackDelta = 0.0f; + } + + if (sound->releaseSamples > 0) + { + releaseDelta = (float) (-pitchRatio / sound->releaseSamples); + } + else + { + releaseDelta = 0.0f; + } + } +} + +void SamplerVoice::stopNote (const bool allowTailOff) +{ + if (allowTailOff) + { + isInAttack = false; + isInRelease = true; + } + else + { + clearCurrentNote(); + } +} + +void SamplerVoice::pitchWheelMoved (const int /*newValue*/) +{ +} + +void SamplerVoice::controllerMoved (const int /*controllerNumber*/, + const int /*newValue*/) +{ +} + +void SamplerVoice::renderNextBlock (AudioSampleBuffer& outputBuffer, int startSample, int numSamples) +{ + const SamplerSound* const playingSound = (SamplerSound*) (SynthesiserSound*) getCurrentlyPlayingSound(); + + if (playingSound != 0) + { + const float* const inL = playingSound->data->getSampleData (0, 0); + const float* const inR = playingSound->data->getNumChannels() > 1 + ? playingSound->data->getSampleData (1, 0) : 0; + + float* outL = outputBuffer.getSampleData (0, startSample); + float* outR = outputBuffer.getNumChannels() > 1 ? outputBuffer.getSampleData (1, startSample) : 0; + + while (--numSamples >= 0) + { + const int pos = (int) sourceSamplePosition; + const float alpha = (float) (sourceSamplePosition - pos); + const float invAlpha = 1.0f - alpha; + + // just using a very simple linear interpolation here.. + float l = (inL [pos] * invAlpha + inL [pos + 1] * alpha); + float r = (inR != 0) ? (inR [pos] * invAlpha + inR [pos + 1] * alpha) + : l; + + l *= lgain; + r *= rgain; + + if (isInAttack) + { + l *= attackReleaseLevel; + r *= attackReleaseLevel; + + attackReleaseLevel += attackDelta; + + if (attackReleaseLevel >= 1.0f) + { + attackReleaseLevel = 1.0f; + isInAttack = false; + } + } + else if (isInRelease) + { + l *= attackReleaseLevel; + r *= attackReleaseLevel; + + attackReleaseLevel += releaseDelta; + + if (attackReleaseLevel <= 0.0f) + { + stopNote (false); + break; + } + } + + if (outR != 0) + { + *outL++ += l; + *outR++ += r; + } + else + { + *outL++ += (l + r) * 0.5f; + } + + sourceSamplePosition += pitchRatio; + + if (sourceSamplePosition > playingSound->length) + { + stopNote (false); + break; + } + } + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_Sampler.cpp *********/ + +/********* Start of inlined file: juce_Synthesiser.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +SynthesiserSound::SynthesiserSound() +{ +} + +SynthesiserSound::~SynthesiserSound() +{ +} + +SynthesiserVoice::SynthesiserVoice() + : currentSampleRate (44100.0), + currentlyPlayingNote (-1), + noteOnTime (0), + currentlyPlayingSound (0) +{ +} + +SynthesiserVoice::~SynthesiserVoice() +{ +} + +bool SynthesiserVoice::isPlayingChannel (const int midiChannel) const +{ + return currentlyPlayingSound != 0 + && currentlyPlayingSound->appliesToChannel (midiChannel); +} + +void SynthesiserVoice::setCurrentPlaybackSampleRate (const double newRate) +{ + currentSampleRate = newRate; +} + +void SynthesiserVoice::clearCurrentNote() +{ + currentlyPlayingNote = -1; + currentlyPlayingSound = 0; +} + +Synthesiser::Synthesiser() + : voices (2), + sounds (2), + sampleRate (0), + lastNoteOnCounter (0), + shouldStealNotes (true) +{ + zeromem (lastPitchWheelValues, sizeof (lastPitchWheelValues)); +} + +Synthesiser::~Synthesiser() +{ +} + +SynthesiserVoice* Synthesiser::getVoice (const int index) const throw() +{ + const ScopedLock sl (lock); + return voices [index]; +} + +void Synthesiser::clearVoices() +{ + const ScopedLock sl (lock); + voices.clear(); +} + +void Synthesiser::addVoice (SynthesiserVoice* const newVoice) +{ + const ScopedLock sl (lock); + voices.add (newVoice); +} + +void Synthesiser::removeVoice (const int index) +{ + const ScopedLock sl (lock); + voices.remove (index); +} + +void Synthesiser::clearSounds() +{ + const ScopedLock sl (lock); + sounds.clear(); +} + +void Synthesiser::addSound (const SynthesiserSound::Ptr& newSound) +{ + const ScopedLock sl (lock); + sounds.add (newSound); +} + +void Synthesiser::removeSound (const int index) +{ + const ScopedLock sl (lock); + sounds.remove (index); +} + +void Synthesiser::setNoteStealingEnabled (const bool shouldStealNotes_) +{ + shouldStealNotes = shouldStealNotes_; +} + +void Synthesiser::setCurrentPlaybackSampleRate (const double newRate) +{ + if (sampleRate != newRate) + { + const ScopedLock sl (lock); + + allNotesOff (0, false); + + sampleRate = newRate; + + for (int i = voices.size(); --i >= 0;) + voices.getUnchecked (i)->setCurrentPlaybackSampleRate (newRate); + } +} + +void Synthesiser::renderNextBlock (AudioSampleBuffer& outputBuffer, + const MidiBuffer& midiData, + int startSample, + int numSamples) +{ + // must set the sample rate before using this! + jassert (sampleRate != 0); + + const ScopedLock sl (lock); + + MidiBuffer::Iterator midiIterator (midiData); + midiIterator.setNextSamplePosition (startSample); + MidiMessage m (0xf4, 0.0); + + while (numSamples > 0) + { + int midiEventPos; + const bool useEvent = midiIterator.getNextEvent (m, midiEventPos) + && midiEventPos < startSample + numSamples; + + const int numThisTime = useEvent ? midiEventPos - startSample + : numSamples; + + if (numThisTime > 0) + { + for (int i = voices.size(); --i >= 0;) + voices.getUnchecked (i)->renderNextBlock (outputBuffer, startSample, numThisTime); + } + + if (useEvent) + { + if (m.isNoteOn()) + { + const int channel = m.getChannel(); + + noteOn (channel, + m.getNoteNumber(), + m.getFloatVelocity()); + } + else if (m.isNoteOff()) + { + noteOff (m.getChannel(), + m.getNoteNumber(), + true); + } + else if (m.isAllNotesOff() || m.isAllSoundOff()) + { + allNotesOff (m.getChannel(), true); + } + else if (m.isPitchWheel()) + { + const int channel = m.getChannel(); + const int wheelPos = m.getPitchWheelValue(); + lastPitchWheelValues [channel - 1] = wheelPos; + + handlePitchWheel (channel, wheelPos); + } + else if (m.isController()) + { + handleController (m.getChannel(), + m.getControllerNumber(), + m.getControllerValue()); + } + } + + startSample += numThisTime; + numSamples -= numThisTime; + } +} + +void Synthesiser::noteOn (const int midiChannel, + const int midiNoteNumber, + const float velocity) +{ + const ScopedLock sl (lock); + + for (int i = sounds.size(); --i >= 0;) + { + SynthesiserSound* const sound = sounds.getUnchecked(i); + + if (sound->appliesToNote (midiNoteNumber) + && sound->appliesToChannel (midiChannel)) + { + startVoice (findFreeVoice (sound, shouldStealNotes), + sound, midiChannel, midiNoteNumber, velocity); + } + } +} + +void Synthesiser::startVoice (SynthesiserVoice* const voice, + SynthesiserSound* const sound, + const int midiChannel, + const int midiNoteNumber, + const float velocity) +{ + if (voice != 0 && sound != 0) + { + voice->startNote (midiNoteNumber, + velocity, + sound, + lastPitchWheelValues [midiChannel - 1]); + + voice->currentlyPlayingNote = midiNoteNumber; + voice->noteOnTime = ++lastNoteOnCounter; + voice->currentlyPlayingSound = sound; + } +} + +void Synthesiser::noteOff (const int midiChannel, + const int midiNoteNumber, + const bool allowTailOff) +{ + const ScopedLock sl (lock); + + for (int i = voices.size(); --i >= 0;) + { + SynthesiserVoice* const voice = voices.getUnchecked (i); + + if (voice->getCurrentlyPlayingNote() == midiNoteNumber) + { + SynthesiserSound* const sound = voice->getCurrentlyPlayingSound(); + + if (sound != 0 + && sound->appliesToNote (midiNoteNumber) + && sound->appliesToChannel (midiChannel)) + { + voice->stopNote (allowTailOff); + + // the subclass MUST call clearCurrentNote() if it's not tailing off! RTFM for stopNote()! + jassert (allowTailOff || (voice->getCurrentlyPlayingNote() < 0 && voice->getCurrentlyPlayingSound() == 0)); + } + } + } +} + +void Synthesiser::allNotesOff (const int midiChannel, + const bool allowTailOff) +{ + const ScopedLock sl (lock); + + for (int i = voices.size(); --i >= 0;) + { + SynthesiserVoice* const voice = voices.getUnchecked (i); + + if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel)) + voice->stopNote (allowTailOff); + } +} + +void Synthesiser::handlePitchWheel (const int midiChannel, + const int wheelValue) +{ + const ScopedLock sl (lock); + + for (int i = voices.size(); --i >= 0;) + { + SynthesiserVoice* const voice = voices.getUnchecked (i); + + if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel)) + { + voice->pitchWheelMoved (wheelValue); + } + } +} + +void Synthesiser::handleController (const int midiChannel, + const int controllerNumber, + const int controllerValue) +{ + const ScopedLock sl (lock); + + for (int i = voices.size(); --i >= 0;) + { + SynthesiserVoice* const voice = voices.getUnchecked (i); + + if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel)) + voice->controllerMoved (controllerNumber, controllerValue); + } +} + +SynthesiserVoice* Synthesiser::findFreeVoice (SynthesiserSound* soundToPlay, + const bool stealIfNoneAvailable) const +{ + const ScopedLock sl (lock); + + for (int i = voices.size(); --i >= 0;) + if (voices.getUnchecked (i)->getCurrentlyPlayingNote() < 0 + && voices.getUnchecked (i)->canPlaySound (soundToPlay)) + return voices.getUnchecked (i); + + if (stealIfNoneAvailable) + { + // currently this just steals the one that's been playing the longest, but could be made a bit smarter.. + SynthesiserVoice* oldest = 0; + + for (int i = voices.size(); --i >= 0;) + { + SynthesiserVoice* const voice = voices.getUnchecked (i); + + if (voice->canPlaySound (soundToPlay) + && (oldest == 0 || oldest->noteOnTime > voice->noteOnTime)) + oldest = voice; + } + + jassert (oldest != 0); + return oldest; + } + + return 0; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_Synthesiser.cpp *********/ + +/********* Start of inlined file: juce_FileBasedDocument.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +FileBasedDocument::FileBasedDocument (const String& fileExtension_, + const String& fileWildcard_, + const String& openFileDialogTitle_, + const String& saveFileDialogTitle_) + : changedSinceSave (false), + fileExtension (fileExtension_), + fileWildcard (fileWildcard_), + openFileDialogTitle (openFileDialogTitle_), + saveFileDialogTitle (saveFileDialogTitle_) +{ +} + +FileBasedDocument::~FileBasedDocument() +{ +} + +void FileBasedDocument::setChangedFlag (const bool hasChanged) +{ + changedSinceSave = hasChanged; +} + +void FileBasedDocument::changed() +{ + changedSinceSave = true; + sendChangeMessage (this); +} + +void FileBasedDocument::setFile (const File& newFile) +{ + if (documentFile != newFile) + { + documentFile = newFile; + changedSinceSave = true; + } +} + +bool FileBasedDocument::loadFrom (const File& newFile, + const bool showMessageOnFailure) +{ + MouseCursor::showWaitCursor(); + + const File oldFile (documentFile); + documentFile = newFile; + + String error; + + if (newFile.existsAsFile()) + { + error = loadDocument (newFile); + + if (error.isEmpty()) + { + setChangedFlag (false); + MouseCursor::hideWaitCursor(); + + setLastDocumentOpened (newFile); + return true; + } + } + else + { + error = "The file doesn't exist"; + } + + documentFile = oldFile; + MouseCursor::hideWaitCursor(); + + if (showMessageOnFailure) + { + AlertWindow::showMessageBox (AlertWindow::WarningIcon, + TRANS("Failed to open file..."), + TRANS("There was an error while trying to load the file:\n\n") + + newFile.getFullPathName() + + T("\n\n") + + error); + } + + return false; +} + +bool FileBasedDocument::loadFromUserSpecifiedFile (const bool showMessageOnFailure) +{ + FileChooser fc (openFileDialogTitle, + getLastDocumentOpened(), + fileWildcard); + + if (fc.browseForFileToOpen()) + return loadFrom (fc.getResult(), showMessageOnFailure); + + return false; +} + +FileBasedDocument::SaveResult FileBasedDocument::save (const bool askUserForFileIfNotSpecified, + const bool showMessageOnFailure) +{ + return saveAs (documentFile, + false, + askUserForFileIfNotSpecified, + showMessageOnFailure); +} + +FileBasedDocument::SaveResult FileBasedDocument::saveAs (const File& newFile, + const bool warnAboutOverwritingExistingFiles, + const bool askUserForFileIfNotSpecified, + const bool showMessageOnFailure) +{ + if (newFile == File::nonexistent) + { + if (askUserForFileIfNotSpecified) + { + return saveAsInteractive (true); + } + else + { + // can't save to an unspecified file + jassertfalse + return failedToWriteToFile; + } + } + + if (warnAboutOverwritingExistingFiles && newFile.exists()) + { + if (! AlertWindow::showOkCancelBox (AlertWindow::WarningIcon, + TRANS("File already exists"), + TRANS("There's already a file called:\n\n") + + newFile.getFullPathName() + + TRANS("\n\nAre you sure you want to overwrite it?"), + TRANS("overwrite"), + TRANS("cancel"))) + { + return userCancelledSave; + } + } + + MouseCursor::showWaitCursor(); + + const File oldFile (documentFile); + documentFile = newFile; + + String error (saveDocument (newFile)); + + if (error.isEmpty()) + { + setChangedFlag (false); + MouseCursor::hideWaitCursor(); + + return savedOk; + } + + documentFile = oldFile; + MouseCursor::hideWaitCursor(); + + if (showMessageOnFailure) + { + AlertWindow::showMessageBox (AlertWindow::WarningIcon, + TRANS("Error writing to file..."), + TRANS("An error occurred while trying to save \"") + + getDocumentTitle() + + TRANS("\" to the file:\n\n") + + newFile.getFullPathName() + + T("\n\n") + + error); + } + + return failedToWriteToFile; +} + +FileBasedDocument::SaveResult FileBasedDocument::saveIfNeededAndUserAgrees() +{ + if (! hasChangedSinceSaved()) + return savedOk; + + const int r = AlertWindow::showYesNoCancelBox (AlertWindow::QuestionIcon, + TRANS("Closing document..."), + TRANS("Do you want to save the changes to \"") + + getDocumentTitle() + T("\"?"), + TRANS("save"), + TRANS("discard changes"), + TRANS("cancel")); + + if (r == 1) + { + // save changes + return save (true, true); + } + else if (r == 2) + { + // discard changes + return savedOk; + } + + return userCancelledSave; +} + +FileBasedDocument::SaveResult FileBasedDocument::saveAsInteractive (const bool warnAboutOverwritingExistingFiles) +{ + File f; + + if (documentFile.existsAsFile()) + f = documentFile; + else + f = getLastDocumentOpened(); + + String legalFilename (File::createLegalFileName (getDocumentTitle())); + + if (legalFilename.isEmpty()) + legalFilename = "unnamed"; + + if (f.existsAsFile() || f.getParentDirectory().isDirectory()) + f = f.getSiblingFile (legalFilename); + else + f = File::getSpecialLocation (File::userDocumentsDirectory).getChildFile (legalFilename); + + f = f.withFileExtension (fileExtension) + .getNonexistentSibling (true); + + FileChooser fc (saveFileDialogTitle, f, fileWildcard); + + if (fc.browseForFileToSave (warnAboutOverwritingExistingFiles)) + { + setLastDocumentOpened (fc.getResult()); + + File chosen (fc.getResult()); + if (chosen.getFileExtension().isEmpty()) + chosen = chosen.withFileExtension (fileExtension); + + return saveAs (chosen, false, false, true); + } + + return userCancelledSave; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_FileBasedDocument.cpp *********/ + +/********* Start of inlined file: juce_RecentlyOpenedFilesList.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +RecentlyOpenedFilesList::RecentlyOpenedFilesList() + : maxNumberOfItems (10) +{ +} + +RecentlyOpenedFilesList::~RecentlyOpenedFilesList() +{ +} + +void RecentlyOpenedFilesList::setMaxNumberOfItems (const int newMaxNumber) +{ + maxNumberOfItems = jmax (1, newMaxNumber); + + while (getNumFiles() > maxNumberOfItems) + files.remove (getNumFiles() - 1); +} + +int RecentlyOpenedFilesList::getNumFiles() const +{ + return files.size(); +} + +const File RecentlyOpenedFilesList::getFile (const int index) const +{ + return File (files [index]); +} + +void RecentlyOpenedFilesList::clear() +{ + files.clear(); +} + +void RecentlyOpenedFilesList::addFile (const File& file) +{ + const String path (file.getFullPathName()); + + files.removeString (path, true); + files.insert (0, path); + + setMaxNumberOfItems (maxNumberOfItems); +} + +void RecentlyOpenedFilesList::removeNonExistentFiles() +{ + for (int i = getNumFiles(); --i >= 0;) + if (! getFile(i).exists()) + files.remove (i); +} + +int RecentlyOpenedFilesList::createPopupMenuItems (PopupMenu& menuToAddTo, + const int baseItemId, + const bool showFullPaths, + const bool dontAddNonExistentFiles, + const File** filesToAvoid) +{ + int num = 0; + + for (int i = 0; i < getNumFiles(); ++i) + { + const File f (getFile(i)); + + if ((! dontAddNonExistentFiles) || f.exists()) + { + bool needsAvoiding = false; + + if (filesToAvoid != 0) + { + const File** files = filesToAvoid; + + while (*files != 0) + { + if (f == **files) + { + needsAvoiding = true; + break; + } + + ++files; + } + } + + if (! needsAvoiding) + { + menuToAddTo.addItem (baseItemId + i, + showFullPaths ? f.getFullPathName() + : f.getFileName()); + ++num; + } + } + } + + return num; +} + +const String RecentlyOpenedFilesList::toString() const +{ + return files.joinIntoString (T("\n")); +} + +void RecentlyOpenedFilesList::restoreFromString (const String& stringifiedVersion) +{ + clear(); + files.addLines (stringifiedVersion); + + setMaxNumberOfItems (maxNumberOfItems); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_RecentlyOpenedFilesList.cpp *********/ + +/********* Start of inlined file: juce_UndoManager.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +UndoManager::UndoManager (const int maxNumberOfUnitsToKeep, + const int minimumTransactions) + : totalUnitsStored (0), + nextIndex (0), + newTransaction (true), + reentrancyCheck (false) +{ + setMaxNumberOfStoredUnits (maxNumberOfUnitsToKeep, + minimumTransactions); +} + +UndoManager::~UndoManager() +{ + clearUndoHistory(); +} + +void UndoManager::clearUndoHistory() +{ + transactions.clear(); + transactionNames.clear(); + totalUnitsStored = 0; + nextIndex = 0; + sendChangeMessage (this); +} + +int UndoManager::getNumberOfUnitsTakenUpByStoredCommands() const +{ + return totalUnitsStored; +} + +void UndoManager::setMaxNumberOfStoredUnits (const int maxNumberOfUnitsToKeep, + const int minimumTransactions) +{ + maxNumUnitsToKeep = jmax (1, maxNumberOfUnitsToKeep); + minimumTransactionsToKeep = jmax (1, minimumTransactions); +} + +bool UndoManager::perform (UndoableAction* const command, const String& actionName) +{ + if (command != 0) + { + if (actionName.isNotEmpty()) + currentTransactionName = actionName; + + if (reentrancyCheck) + { + jassertfalse // don't call perform() recursively from the UndoableAction::perform() or + // undo() methods, or else these actions won't actually get done. + + return false; + } + else + { + bool success = false; + + JUCE_TRY + { + success = command->perform(); + } + JUCE_CATCH_EXCEPTION + + jassert (success); + if (success) + { + if (nextIndex > 0 && ! newTransaction) + { + OwnedArray* commandSet = transactions [nextIndex - 1]; + + jassert (commandSet != 0); + if (commandSet == 0) + return false; + + commandSet->add (command); + } + else + { + OwnedArray* commandSet = new OwnedArray(); + commandSet->add (command); + transactions.insert (nextIndex, commandSet); + transactionNames.insert (nextIndex, currentTransactionName); + ++nextIndex; + } + + totalUnitsStored += command->getSizeInUnits(); + newTransaction = false; + } + + while (nextIndex < transactions.size()) + { + const OwnedArray * const lastSet = transactions.getLast(); + + for (int i = lastSet->size(); --i >= 0;) + totalUnitsStored -= lastSet->getUnchecked (i)->getSizeInUnits(); + + transactions.removeLast(); + transactionNames.remove (transactionNames.size() - 1); + } + + while (nextIndex > 0 + && totalUnitsStored > maxNumUnitsToKeep + && transactions.size() > minimumTransactionsToKeep) + { + const OwnedArray * const firstSet = transactions.getFirst(); + + for (int i = firstSet->size(); --i >= 0;) + totalUnitsStored -= firstSet->getUnchecked (i)->getSizeInUnits(); + + jassert (totalUnitsStored >= 0); // something fishy going on if this fails! + + transactions.remove (0); + transactionNames.remove (0); + --nextIndex; + } + + sendChangeMessage (this); + + return success; + } + } + + return false; +} + +void UndoManager::beginNewTransaction (const String& actionName) +{ + newTransaction = true; + currentTransactionName = actionName; +} + +void UndoManager::setCurrentTransactionName (const String& newName) +{ + currentTransactionName = newName; +} + +bool UndoManager::canUndo() const +{ + return nextIndex > 0; +} + +bool UndoManager::canRedo() const +{ + return nextIndex < transactions.size(); +} + +const String UndoManager::getUndoDescription() const +{ + return transactionNames [nextIndex - 1]; +} + +const String UndoManager::getRedoDescription() const +{ + return transactionNames [nextIndex]; +} + +bool UndoManager::undo() +{ + const OwnedArray* const commandSet = transactions [nextIndex - 1]; + + if (commandSet == 0) + return false; + + reentrancyCheck = true; + bool failed = false; + + for (int i = commandSet->size(); --i >= 0;) + { + if (! commandSet->getUnchecked(i)->undo()) + { + jassertfalse + failed = true; + break; + } + } + + reentrancyCheck = false; + + if (failed) + { + clearUndoHistory(); + } + else + { + --nextIndex; + } + + beginNewTransaction(); + + sendChangeMessage (this); + return true; +} + +bool UndoManager::redo() +{ + const OwnedArray* const commandSet = transactions [nextIndex]; + + if (commandSet == 0) + return false; + + reentrancyCheck = true; + bool failed = false; + + for (int i = 0; i < commandSet->size(); ++i) + { + if (! commandSet->getUnchecked(i)->perform()) + { + jassertfalse + failed = true; + break; + } + } + + reentrancyCheck = false; + + if (failed) + { + clearUndoHistory(); + } + else + { + ++nextIndex; + } + + beginNewTransaction(); + + sendChangeMessage (this); + return true; +} + +bool UndoManager::undoCurrentTransactionOnly() +{ + return newTransaction ? false + : undo(); +} + +void UndoManager::getActionsInCurrentTransaction (Array & actionsFound) const +{ + const OwnedArray * const commandSet = transactions [nextIndex - 1]; + + if (commandSet != 0 && ! newTransaction) + { + for (int i = 0; i < commandSet->size(); ++i) + actionsFound.add (commandSet->getUnchecked(i)); + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_UndoManager.cpp *********/ + +/********* Start of inlined file: juce_ActionBroadcaster.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +ActionBroadcaster::ActionBroadcaster() throw() +{ + // are you trying to create this object before or after juce has been intialised?? + jassert (MessageManager::instance != 0); +} + +ActionBroadcaster::~ActionBroadcaster() +{ + // all event-based objects must be deleted BEFORE juce is shut down! + jassert (MessageManager::instance != 0); +} + +void ActionBroadcaster::addActionListener (ActionListener* const listener) +{ + actionListenerList.addActionListener (listener); +} + +void ActionBroadcaster::removeActionListener (ActionListener* const listener) +{ + jassert (actionListenerList.isValidMessageListener()); + + if (actionListenerList.isValidMessageListener()) + actionListenerList.removeActionListener (listener); +} + +void ActionBroadcaster::removeAllActionListeners() +{ + actionListenerList.removeAllActionListeners(); +} + +void ActionBroadcaster::sendActionMessage (const String& message) const +{ + actionListenerList.sendActionMessage (message); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ActionBroadcaster.cpp *********/ + +/********* Start of inlined file: juce_ActionListenerList.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +// special message of our own with a string in it +class ActionMessage : public Message +{ +public: + const String message; + + ActionMessage (const String& messageText, + void* const listener_) throw() + : message (messageText) + { + pointerParameter = listener_; + } + + ~ActionMessage() throw() + { + } + +private: + ActionMessage (const ActionMessage&); + const ActionMessage& operator= (const ActionMessage&); +}; + +ActionListenerList::ActionListenerList() throw() +{ +} + +ActionListenerList::~ActionListenerList() throw() +{ +} + +void ActionListenerList::addActionListener (ActionListener* const listener) throw() +{ + const ScopedLock sl (actionListenerLock_); + + jassert (listener != 0); + jassert (! actionListeners_.contains (listener)); // trying to add a listener to the list twice! + + if (listener != 0) + actionListeners_.add (listener); +} + +void ActionListenerList::removeActionListener (ActionListener* const listener) throw() +{ + const ScopedLock sl (actionListenerLock_); + + jassert (actionListeners_.contains (listener)); // trying to remove a listener that isn't on the list! + + actionListeners_.removeValue (listener); +} + +void ActionListenerList::removeAllActionListeners() throw() +{ + const ScopedLock sl (actionListenerLock_); + actionListeners_.clear(); +} + +void ActionListenerList::sendActionMessage (const String& message) const +{ + const ScopedLock sl (actionListenerLock_); + + for (int i = actionListeners_.size(); --i >= 0;) + { + postMessage (new ActionMessage (message, + (ActionListener*) actionListeners_.getUnchecked(i))); + } +} + +void ActionListenerList::handleMessage (const Message& message) +{ + const ActionMessage& am = (const ActionMessage&) message; + + if (actionListeners_.contains (am.pointerParameter)) + ((ActionListener*) am.pointerParameter)->actionListenerCallback (am.message); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ActionListenerList.cpp *********/ + +/********* Start of inlined file: juce_AsyncUpdater.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +AsyncUpdater::AsyncUpdater() throw() + : asyncMessagePending (false) +{ + internalAsyncHandler.owner = this; +} + +AsyncUpdater::~AsyncUpdater() +{ +} + +void AsyncUpdater::triggerAsyncUpdate() throw() +{ + if (! asyncMessagePending) + { + asyncMessagePending = true; + internalAsyncHandler.postMessage (new Message()); + } +} + +void AsyncUpdater::cancelPendingUpdate() throw() +{ + asyncMessagePending = false; +} + +void AsyncUpdater::handleUpdateNowIfNeeded() +{ + if (asyncMessagePending) + { + asyncMessagePending = false; + handleAsyncUpdate(); + } +} + +void AsyncUpdater::AsyncUpdaterInternal::handleMessage (const Message&) +{ + owner->handleUpdateNowIfNeeded(); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_AsyncUpdater.cpp *********/ + +/********* Start of inlined file: juce_ChangeBroadcaster.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +ChangeBroadcaster::ChangeBroadcaster() throw() +{ + // are you trying to create this object before or after juce has been intialised?? + jassert (MessageManager::instance != 0); +} + +ChangeBroadcaster::~ChangeBroadcaster() +{ + // all event-based objects must be deleted BEFORE juce is shut down! + jassert (MessageManager::instance != 0); +} + +void ChangeBroadcaster::addChangeListener (ChangeListener* const listener) throw() +{ + changeListenerList.addChangeListener (listener); +} + +void ChangeBroadcaster::removeChangeListener (ChangeListener* const listener) throw() +{ + jassert (changeListenerList.isValidMessageListener()); + + if (changeListenerList.isValidMessageListener()) + changeListenerList.removeChangeListener (listener); +} + +void ChangeBroadcaster::removeAllChangeListeners() throw() +{ + changeListenerList.removeAllChangeListeners(); +} + +void ChangeBroadcaster::sendChangeMessage (void* objectThatHasChanged) throw() +{ + changeListenerList.sendChangeMessage (objectThatHasChanged); +} + +void ChangeBroadcaster::sendSynchronousChangeMessage (void* objectThatHasChanged) +{ + changeListenerList.sendSynchronousChangeMessage (objectThatHasChanged); +} + +void ChangeBroadcaster::dispatchPendingMessages() +{ + changeListenerList.dispatchPendingMessages(); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ChangeBroadcaster.cpp *********/ + +/********* Start of inlined file: juce_ChangeListenerList.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +ChangeListenerList::ChangeListenerList() throw() + : lastChangedObject (0), + messagePending (false) +{ +} + +ChangeListenerList::~ChangeListenerList() throw() +{ +} + +void ChangeListenerList::addChangeListener (ChangeListener* const listener) throw() +{ + const ScopedLock sl (lock); + + jassert (listener != 0); + + if (listener != 0) + listeners.add (listener); +} + +void ChangeListenerList::removeChangeListener (ChangeListener* const listener) throw() +{ + const ScopedLock sl (lock); + listeners.removeValue (listener); +} + +void ChangeListenerList::removeAllChangeListeners() throw() +{ + const ScopedLock sl (lock); + listeners.clear(); +} + +void ChangeListenerList::sendChangeMessage (void* const objectThatHasChanged) throw() +{ + const ScopedLock sl (lock); + + if ((! messagePending) && (listeners.size() > 0)) + { + lastChangedObject = objectThatHasChanged; + postMessage (new Message (0, 0, 0, objectThatHasChanged)); + messagePending = true; + } +} + +void ChangeListenerList::handleMessage (const Message& message) +{ + sendSynchronousChangeMessage (message.pointerParameter); +} + +void ChangeListenerList::sendSynchronousChangeMessage (void* const objectThatHasChanged) +{ + const ScopedLock sl (lock); + messagePending = false; + + for (int i = listeners.size(); --i >= 0;) + { + ChangeListener* const l = (ChangeListener*) listeners.getUnchecked (i); + + { + const ScopedUnlock tempUnlocker (lock); + l->changeListenerCallback (objectThatHasChanged); + } + + i = jmin (i, listeners.size()); + } +} + +void ChangeListenerList::dispatchPendingMessages() +{ + if (messagePending) + sendSynchronousChangeMessage (lastChangedObject); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ChangeListenerList.cpp *********/ + +/********* Start of inlined file: juce_InterprocessConnection.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +InterprocessConnection::InterprocessConnection (const bool callbacksOnMessageThread, + const uint32 magicMessageHeaderNumber) + : Thread ("Juce IPC connection"), + socket (0), + pipe (0), + callbackConnectionState (false), + useMessageThread (callbacksOnMessageThread), + magicMessageHeader (magicMessageHeaderNumber), + pipeReceiveMessageTimeout (-1) +{ +} + +InterprocessConnection::~InterprocessConnection() +{ + callbackConnectionState = false; + disconnect(); +} + +bool InterprocessConnection::connectToSocket (const String& hostName, + const int portNumber, + const int timeOutMillisecs) +{ + disconnect(); + + const ScopedLock sl (pipeAndSocketLock); + socket = new StreamingSocket(); + + if (socket->connect (hostName, portNumber, timeOutMillisecs)) + { + connectionMadeInt(); + startThread(); + return true; + } + else + { + deleteAndZero (socket); + return false; + } +} + +bool InterprocessConnection::connectToPipe (const String& pipeName, + const int pipeReceiveMessageTimeoutMs) +{ + disconnect(); + + NamedPipe* const newPipe = new NamedPipe(); + + if (newPipe->openExisting (pipeName)) + { + const ScopedLock sl (pipeAndSocketLock); + pipeReceiveMessageTimeout = pipeReceiveMessageTimeoutMs; + initialiseWithPipe (newPipe); + return true; + } + else + { + delete newPipe; + return false; + } +} + +bool InterprocessConnection::createPipe (const String& pipeName, + const int pipeReceiveMessageTimeoutMs) +{ + disconnect(); + + NamedPipe* const newPipe = new NamedPipe(); + + if (newPipe->createNewPipe (pipeName)) + { + const ScopedLock sl (pipeAndSocketLock); + pipeReceiveMessageTimeout = pipeReceiveMessageTimeoutMs; + initialiseWithPipe (newPipe); + return true; + } + else + { + delete newPipe; + return false; + } +} + +void InterprocessConnection::disconnect() +{ + if (socket != 0) + socket->close(); + + if (pipe != 0) + { + pipe->cancelPendingReads(); + pipe->close(); + } + + stopThread (4000); + + { + const ScopedLock sl (pipeAndSocketLock); + deleteAndZero (socket); + deleteAndZero (pipe); + } + + connectionLostInt(); +} + +bool InterprocessConnection::isConnected() const +{ + const ScopedLock sl (pipeAndSocketLock); + + return ((socket != 0 && socket->isConnected()) + || (pipe != 0 && pipe->isOpen())) + && isThreadRunning(); +} + +const String InterprocessConnection::getConnectedHostName() const +{ + if (pipe != 0) + { + return "localhost"; + } + else if (socket != 0) + { + if (! socket->isLocal()) + return socket->getHostName(); + + return "localhost"; + } + + return String::empty; +} + +bool InterprocessConnection::sendMessage (const MemoryBlock& message) +{ + uint32 messageHeader[2]; + messageHeader [0] = swapIfBigEndian (magicMessageHeader); + messageHeader [1] = swapIfBigEndian ((uint32) message.getSize()); + + MemoryBlock messageData (sizeof (messageHeader) + message.getSize()); + messageData.copyFrom (messageHeader, 0, sizeof (messageHeader)); + messageData.copyFrom (message.getData(), sizeof (messageHeader), message.getSize()); + + int bytesWritten = 0; + + const ScopedLock sl (pipeAndSocketLock); + + if (socket != 0) + { + bytesWritten = socket->write (messageData.getData(), messageData.getSize()); + } + else if (pipe != 0) + { + bytesWritten = pipe->write (messageData.getData(), messageData.getSize()); + } + + if (bytesWritten < 0) + { + // error.. + return false; + } + + return (bytesWritten == messageData.getSize()); +} + +void InterprocessConnection::initialiseWithSocket (StreamingSocket* const socket_) +{ + jassert (socket == 0); + socket = socket_; + connectionMadeInt(); + startThread(); +} + +void InterprocessConnection::initialiseWithPipe (NamedPipe* const pipe_) +{ + jassert (pipe == 0); + pipe = pipe_; + connectionMadeInt(); + startThread(); +} + +const int messageMagicNumber = 0xb734128b; + +void InterprocessConnection::handleMessage (const Message& message) +{ + if (message.intParameter1 == messageMagicNumber) + { + switch (message.intParameter2) + { + case 0: + { + MemoryBlock* const data = (MemoryBlock*) message.pointerParameter; + messageReceived (*data); + delete data; + break; + } + + case 1: + connectionMade(); + break; + + case 2: + connectionLost(); + break; + } + } +} + +void InterprocessConnection::connectionMadeInt() +{ + if (! callbackConnectionState) + { + callbackConnectionState = true; + + if (useMessageThread) + postMessage (new Message (messageMagicNumber, 1, 0, 0)); + else + connectionMade(); + } +} + +void InterprocessConnection::connectionLostInt() +{ + if (callbackConnectionState) + { + callbackConnectionState = false; + + if (useMessageThread) + postMessage (new Message (messageMagicNumber, 2, 0, 0)); + else + connectionLost(); + } +} + +void InterprocessConnection::deliverDataInt (const MemoryBlock& data) +{ + jassert (callbackConnectionState); + + if (useMessageThread) + postMessage (new Message (messageMagicNumber, 0, 0, new MemoryBlock (data))); + else + messageReceived (data); +} + +bool InterprocessConnection::readNextMessageInt() +{ + const int maximumMessageSize = 1024 * 1024 * 10; // sanity check + + uint32 messageHeader[2]; + const int bytes = (socket != 0) ? socket->read (messageHeader, sizeof (messageHeader)) + : pipe->read (messageHeader, sizeof (messageHeader), pipeReceiveMessageTimeout); + + if (bytes == sizeof (messageHeader) + && swapIfBigEndian (messageHeader[0]) == magicMessageHeader) + { + const int bytesInMessage = (int) swapIfBigEndian (messageHeader[1]); + + if (bytesInMessage > 0 && bytesInMessage < maximumMessageSize) + { + MemoryBlock messageData (bytesInMessage, true); + int bytesRead = 0; + + while (bytesRead < bytesInMessage) + { + if (threadShouldExit()) + return false; + + const int numThisTime = jmin (bytesInMessage, 65536); + const int bytesIn = (socket != 0) ? socket->read (((char*) messageData.getData()) + bytesRead, numThisTime) + : pipe->read (((char*) messageData.getData()) + bytesRead, numThisTime, + pipeReceiveMessageTimeout); + + if (bytesIn <= 0) + break; + + bytesRead += bytesIn; + } + + if (bytesRead >= 0) + deliverDataInt (messageData); + } + } + else if (bytes < 0) + { + { + const ScopedLock sl (pipeAndSocketLock); + deleteAndZero (socket); + } + + connectionLostInt(); + return false; + } + + return true; +} + +void InterprocessConnection::run() +{ + while (! threadShouldExit()) + { + if (socket != 0) + { + const int ready = socket->waitUntilReady (true, 0); + + if (ready < 0) + { + { + const ScopedLock sl (pipeAndSocketLock); + deleteAndZero (socket); + } + + connectionLostInt(); + break; + } + else if (ready > 0) + { + if (! readNextMessageInt()) + break; + } + else + { + Thread::sleep (2); + } + } + else if (pipe != 0) + { + if (! pipe->isOpen()) + { + { + const ScopedLock sl (pipeAndSocketLock); + deleteAndZero (pipe); + } + + connectionLostInt(); + break; + } + else + { + if (! readNextMessageInt()) + break; + } + } + else + { + break; + } + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_InterprocessConnection.cpp *********/ + +/********* Start of inlined file: juce_InterprocessConnectionServer.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +InterprocessConnectionServer::InterprocessConnectionServer() + : Thread ("Juce IPC server"), + socket (0) +{ +} + +InterprocessConnectionServer::~InterprocessConnectionServer() +{ + stop(); +} + +bool InterprocessConnectionServer::beginWaitingForSocket (const int portNumber) +{ + stop(); + + socket = new StreamingSocket(); + + if (socket->createListener (portNumber)) + { + startThread(); + return true; + } + + deleteAndZero (socket); + return false; +} + +void InterprocessConnectionServer::stop() +{ + signalThreadShouldExit(); + + if (socket != 0) + socket->close(); + + stopThread (4000); + + deleteAndZero (socket); +} + +void InterprocessConnectionServer::run() +{ + while ((! threadShouldExit()) && socket != 0) + { + StreamingSocket* const clientSocket = socket->waitForNextConnection(); + + if (clientSocket != 0) + { + InterprocessConnection* newConnection = createConnectionObject(); + + if (newConnection != 0) + { + newConnection->initialiseWithSocket (clientSocket); + } + else + { + delete clientSocket; + } + } + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_InterprocessConnectionServer.cpp *********/ + +/********* Start of inlined file: juce_Message.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +Message::Message() throw() +{ +} + +Message::~Message() throw() +{ +} + +Message::Message (const int intParameter1_, + const int intParameter2_, + const int intParameter3_, + void* const pointerParameter_) throw() + : intParameter1 (intParameter1_), + intParameter2 (intParameter2_), + intParameter3 (intParameter3_), + pointerParameter (pointerParameter_) +{ +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_Message.cpp *********/ + +/********* Start of inlined file: juce_MessageListener.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +MessageListener::MessageListener() throw() +{ + // are you trying to create a messagelistener before or after juce has been intialised?? + jassert (MessageManager::instance != 0); + + if (MessageManager::instance != 0) + MessageManager::instance->messageListeners.add (this); +} + +MessageListener::~MessageListener() +{ + if (MessageManager::instance != 0) + MessageManager::instance->messageListeners.removeValue (this); +} + +void MessageListener::postMessage (Message* const message) const throw() +{ + message->messageRecipient = const_cast (this); + + if (MessageManager::instance == 0) + MessageManager::getInstance(); + + MessageManager::instance->postMessageToQueue (message); +} + +bool MessageListener::isValidMessageListener() const throw() +{ + return (MessageManager::instance != 0) + && MessageManager::instance->messageListeners.contains (this); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_MessageListener.cpp *********/ + +/********* Start of inlined file: juce_MessageManager.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +// platform-specific functions.. +bool juce_dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages); +bool juce_postMessageToSystemQueue (void* message); + +MessageManager* MessageManager::instance = 0; + +static const int quitMessageId = 0xfffff321; + +MessageManager::MessageManager() throw() + : broadcastListeners (0), + quitMessagePosted (false), + quitMessageReceived (false), + useMaximumForceWhenQuitting (true), + messageCounter (0), + lastMessageCounter (-1), + isInMessageDispatcher (0), + needToGetRidOfWaitCursor (false), + timeBeforeWaitCursor (0), + lastActivityCheckOkTime (0) +{ + currentLockingThreadId = messageThreadId = Thread::getCurrentThreadId(); +} + +MessageManager::~MessageManager() throw() +{ + jassert (instance == this); + instance = 0; + deleteAndZero (broadcastListeners); + + doPlatformSpecificShutdown(); +} + +MessageManager* MessageManager::getInstance() throw() +{ + if (instance == 0) + { + instance = new MessageManager(); + doPlatformSpecificInitialisation(); + + instance->setTimeBeforeShowingWaitCursor (500); + } + + return instance; +} + +void MessageManager::postMessageToQueue (Message* const message) +{ + if (quitMessagePosted || ! juce_postMessageToSystemQueue (message)) + delete message; +} + +// not for public use.. +void MessageManager::deliverMessage (void* message) +{ + const MessageManagerLock lock; + + Message* const m = (Message*) message; + MessageListener* const recipient = m->messageRecipient; + + if (messageListeners.contains (recipient)) + { + JUCE_TRY + { + recipient->handleMessage (*m); + } + JUCE_CATCH_EXCEPTION + + if (needToGetRidOfWaitCursor) + { + needToGetRidOfWaitCursor = false; + MouseCursor::hideWaitCursor(); + } + + ++messageCounter; + } + else if (recipient == 0 && m->intParameter1 == quitMessageId) + { + quitMessageReceived = true; + useMaximumForceWhenQuitting = (m->intParameter2 != 0); + } + + delete m; +} + +bool MessageManager::dispatchNextMessage (const bool returnImmediatelyIfNoMessages, + bool* const wasAMessageDispatched) +{ + if (quitMessageReceived) + { + if (wasAMessageDispatched != 0) + *wasAMessageDispatched = false; + + return false; + } + + ++isInMessageDispatcher; + + bool result = false; + + JUCE_TRY + { + result = juce_dispatchNextMessageOnSystemQueue (returnImmediatelyIfNoMessages); + + if (wasAMessageDispatched != 0) + *wasAMessageDispatched = result; + + if (instance == 0) + return false; + } + JUCE_CATCH_EXCEPTION + + --isInMessageDispatcher; + ++messageCounter; + + return result || ! returnImmediatelyIfNoMessages; +} + +void MessageManager::dispatchPendingMessages (int maxNumberOfMessagesToDispatch) +{ + jassert (isThisTheMessageThread()); // must only be called by the message thread + + while (--maxNumberOfMessagesToDispatch >= 0 && ! quitMessageReceived) + { + ++isInMessageDispatcher; + + bool carryOn = false; + + JUCE_TRY + { + carryOn = juce_dispatchNextMessageOnSystemQueue (true); + } + JUCE_CATCH_EXCEPTION + + --isInMessageDispatcher; + ++messageCounter; + + if (! carryOn) + break; + } +} + +bool MessageManager::runDispatchLoop() +{ + jassert (isThisTheMessageThread()); // must only be called by the message thread + + while (dispatchNextMessage()) + { + } + + return useMaximumForceWhenQuitting; +} + +void MessageManager::postQuitMessage (const bool useMaximumForce) +{ + Message* const m = new Message (quitMessageId, (useMaximumForce) ? 1 : 0, 0, 0); + m->messageRecipient = 0; + postMessageToQueue (m); + + quitMessagePosted = true; +} + +bool MessageManager::hasQuitMessageBeenPosted() const throw() +{ + return quitMessagePosted; +} + +void MessageManager::deliverBroadcastMessage (const String& value) +{ + if (broadcastListeners != 0) + broadcastListeners->sendActionMessage (value); +} + +void MessageManager::registerBroadcastListener (ActionListener* const listener) throw() +{ + if (broadcastListeners == 0) + broadcastListeners = new ActionListenerList(); + + broadcastListeners->addActionListener (listener); +} + +void MessageManager::deregisterBroadcastListener (ActionListener* const listener) throw() +{ + if (broadcastListeners != 0) + broadcastListeners->removeActionListener (listener); +} + +// This gets called occasionally by the timer thread (to save using an extra thread +// for it). +void MessageManager::inactivityCheckCallback() throw() +{ + if (instance != 0) + instance->inactivityCheckCallbackInt(); +} + +void MessageManager::inactivityCheckCallbackInt() throw() +{ + const unsigned int now = Time::getApproximateMillisecondCounter(); + + if (isInMessageDispatcher > 0 + && lastMessageCounter == messageCounter + && timeBeforeWaitCursor > 0 + && lastActivityCheckOkTime > 0 + && ! ModifierKeys::getCurrentModifiersRealtime().isAnyMouseButtonDown()) + { + if (now >= lastActivityCheckOkTime + timeBeforeWaitCursor + && ! needToGetRidOfWaitCursor) + { + // been in the same message call too long.. + MouseCursor::showWaitCursor(); + needToGetRidOfWaitCursor = true; + } + } + else + { + lastActivityCheckOkTime = now; + lastMessageCounter = messageCounter; + } +} + +void MessageManager::delayWaitCursor() throw() +{ + if (instance != 0) + { + instance->messageCounter++; + + if (instance->needToGetRidOfWaitCursor) + { + instance->needToGetRidOfWaitCursor = false; + MouseCursor::hideWaitCursor(); + } + } +} + +void MessageManager::setTimeBeforeShowingWaitCursor (const int millisecs) throw() +{ + // if this is a bit too small you'll get a lot of unwanted hourglass cursors.. + jassert (millisecs <= 0 || millisecs > 200); + + timeBeforeWaitCursor = millisecs; + + if (millisecs > 0) + startTimer (millisecs / 2); // (see timerCallback() for explanation of this) + else + stopTimer(); +} + +void MessageManager::timerCallback() +{ + // dummy callback - the message manager is just a Timer to ensure that there are always + // some events coming in - otherwise it'll show the egg-timer/beachball-of-death. + ++messageCounter; +} + +int MessageManager::getTimeBeforeShowingWaitCursor() const throw() +{ + return timeBeforeWaitCursor; +} + +bool MessageManager::isThisTheMessageThread() const throw() +{ + return Thread::getCurrentThreadId() == messageThreadId; +} + +void MessageManager::setCurrentMessageThread (const int threadId) throw() +{ + messageThreadId = threadId; +} + +bool MessageManager::currentThreadHasLockedMessageManager() const throw() +{ + return Thread::getCurrentThreadId() == currentLockingThreadId; +} + +MessageManagerLock::MessageManagerLock() throw() + : locked (false) +{ + if (MessageManager::instance != 0) + { + MessageManager::instance->messageDispatchLock.enter(); + lastLockingThreadId = MessageManager::instance->currentLockingThreadId; + MessageManager::instance->currentLockingThreadId = Thread::getCurrentThreadId(); + locked = true; + } +} + +MessageManagerLock::MessageManagerLock (Thread* const thread) throw() + : locked (false) +{ + jassert (thread != 0); // This will only work if you give it a valid thread! + + if (MessageManager::instance != 0) + { + for (;;) + { + if (MessageManager::instance->messageDispatchLock.tryEnter()) + { + locked = true; + lastLockingThreadId = MessageManager::instance->currentLockingThreadId; + MessageManager::instance->currentLockingThreadId = Thread::getCurrentThreadId(); + break; + } + + if (thread != 0 && thread->threadShouldExit()) + break; + + Thread::sleep (1); + } + } +} + +MessageManagerLock::~MessageManagerLock() throw() +{ + if (locked && MessageManager::instance != 0) + { + MessageManager::instance->currentLockingThreadId = lastLockingThreadId; + MessageManager::instance->messageDispatchLock.exit(); + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_MessageManager.cpp *********/ + +/********* Start of inlined file: juce_MultiTimer.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +class InternalMultiTimerCallback : public Timer +{ +public: + InternalMultiTimerCallback (const int timerId_, MultiTimer& owner_) + : timerId (timerId_), + owner (owner_) + { + } + + ~InternalMultiTimerCallback() + { + } + + void timerCallback() + { + owner.timerCallback (timerId); + } + + const int timerId; + +private: + MultiTimer& owner; +}; + +MultiTimer::MultiTimer() throw() +{ +} + +MultiTimer::MultiTimer (const MultiTimer&) throw() +{ +} + +MultiTimer::~MultiTimer() +{ + const ScopedLock sl (timerListLock); + + for (int i = timers.size(); --i >= 0;) + delete (InternalMultiTimerCallback*) timers.getUnchecked(i); + + timers.clear(); +} + +void MultiTimer::startTimer (const int timerId, const int intervalInMilliseconds) throw() +{ + const ScopedLock sl (timerListLock); + + for (int i = timers.size(); --i >= 0;) + { + InternalMultiTimerCallback* const t = (InternalMultiTimerCallback*) timers.getUnchecked(i); + + if (t->timerId == timerId) + { + t->startTimer (intervalInMilliseconds); + return; + } + } + + InternalMultiTimerCallback* const newTimer = new InternalMultiTimerCallback (timerId, *this); + timers.add (newTimer); + newTimer->startTimer (intervalInMilliseconds); +} + +void MultiTimer::stopTimer (const int timerId) throw() +{ + const ScopedLock sl (timerListLock); + + for (int i = timers.size(); --i >= 0;) + { + InternalMultiTimerCallback* const t = (InternalMultiTimerCallback*) timers.getUnchecked(i); + + if (t->timerId == timerId) + t->stopTimer(); + } +} + +bool MultiTimer::isTimerRunning (const int timerId) const throw() +{ + const ScopedLock sl (timerListLock); + + for (int i = timers.size(); --i >= 0;) + { + const InternalMultiTimerCallback* const t = (InternalMultiTimerCallback*) timers.getUnchecked(i); + if (t->timerId == timerId) + return t->isTimerRunning(); + } + + return false; +} + +int MultiTimer::getTimerInterval (const int timerId) const throw() +{ + const ScopedLock sl (timerListLock); + + for (int i = timers.size(); --i >= 0;) + { + const InternalMultiTimerCallback* const t = (InternalMultiTimerCallback*) timers.getUnchecked(i); + if (t->timerId == timerId) + return t->getTimerInterval(); + } + + return 0; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_MultiTimer.cpp *********/ + +/********* Start of inlined file: juce_Timer.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +class InternalTimerThread : private Thread, + private MessageListener, + private DeletedAtShutdown, + private AsyncUpdater +{ +private: + friend class Timer; + static InternalTimerThread* instance; + static CriticalSection lock; + + Timer* volatile firstTimer; + bool volatile callbackNeeded; + + InternalTimerThread (const InternalTimerThread&); + const InternalTimerThread& operator= (const InternalTimerThread&); + + void addTimer (Timer* const t) throw() + { +#ifdef JUCE_DEBUG + Timer* tt = firstTimer; + + while (tt != 0) + { + // trying to add a timer that's already here - shouldn't get to this point, + // so if you get this assertion, let me know! + jassert (tt != t); + + tt = tt->next; + } + + jassert (t->previous == 0 && t->next == 0); +#endif + + Timer* i = firstTimer; + + if (i == 0 || i->countdownMs > t->countdownMs) + { + t->next = firstTimer; + firstTimer = t; + } + else + { + while (i->next != 0 && i->next->countdownMs <= t->countdownMs) + i = i->next; + + jassert (i != 0); + + t->next = i->next; + t->previous = i; + i->next = t; + } + + if (t->next != 0) + t->next->previous = t; + + jassert ((t->next == 0 || t->next->countdownMs >= t->countdownMs) + && (t->previous == 0 || t->previous->countdownMs <= t->countdownMs)); + + notify(); + } + + void removeTimer (Timer* const t) throw() + { +#ifdef JUCE_DEBUG + Timer* tt = firstTimer; + bool found = false; + + while (tt != 0) + { + if (tt == t) + { + found = true; + break; + } + + tt = tt->next; + } + + // trying to remove a timer that's not here - shouldn't get to this point, + // so if you get this assertion, let me know! + jassert (found); +#endif + + if (t->previous != 0) + { + jassert (firstTimer != t); + t->previous->next = t->next; + } + else + { + jassert (firstTimer == t); + firstTimer = t->next; + } + + if (t->next != 0) + t->next->previous = t->previous; + + t->next = 0; + t->previous = 0; + } + + void decrementAllCounters (const int numMillisecs) const + { + Timer* t = firstTimer; + + while (t != 0) + { + t->countdownMs -= numMillisecs; + t = t->next; + } + } + + void handleAsyncUpdate() + { + startThread (7); + } + +public: + InternalTimerThread() + : Thread ("Juce Timer"), + firstTimer (0), + callbackNeeded (false) + { + triggerAsyncUpdate(); + } + + ~InternalTimerThread() throw() + { + stopThread (4000); + + jassert (instance == this || instance == 0); + if (instance == this) + instance = 0; + } + + void run() + { + uint32 lastTime = Time::getMillisecondCounter(); + uint32 lastMessageManagerCallback = lastTime; + + while (! threadShouldExit()) + { + uint32 now = Time::getMillisecondCounter(); + + if (now <= lastTime) + { + wait (2); + continue; + } + + const int elapsed = now - lastTime; + lastTime = now; + + lock.enter(); + decrementAllCounters (elapsed); + const int timeUntilFirstTimer = (firstTimer != 0) ? firstTimer->countdownMs + : 1000; + lock.exit(); + + if (timeUntilFirstTimer <= 0) + { + callbackNeeded = true; + postMessage (new Message()); + + // sometimes, our message could get discarded by the OS (particularly when running as an RTAS when the app has a modal loop), + // so this is how long to wait before assuming the message has been lost and trying again. + const uint32 messageDeliveryTimeout = now + 2000; + + while (callbackNeeded) + { + wait (4); + + if (threadShouldExit()) + return; + + now = Time::getMillisecondCounter(); + + if (now > lastMessageManagerCallback + 200) + { + lastMessageManagerCallback = now; + MessageManager::inactivityCheckCallback(); + } + + if (now > messageDeliveryTimeout) + break; + } + } + else + { + // don't wait for too long because running this loop also helps keep the + // Time::getApproximateMillisecondTimer value stay up-to-date + wait (jlimit (1, 50, timeUntilFirstTimer)); + } + + if (now > lastMessageManagerCallback + 200) + { + lastMessageManagerCallback = now; + MessageManager::inactivityCheckCallback(); + } + } + } + + void handleMessage (const Message&) + { + const ScopedLock sl (lock); + + while (firstTimer != 0 && firstTimer->countdownMs <= 0) + { + Timer* const t = firstTimer; + t->countdownMs = t->periodMs; + + removeTimer (t); + addTimer (t); + + const ScopedUnlock ul (lock); + callbackNeeded = false; + + JUCE_TRY + { + t->timerCallback(); + } + JUCE_CATCH_EXCEPTION + } + + callbackNeeded = false; + } + + static void callAnyTimersSynchronously() + { + if (InternalTimerThread::instance != 0) + { + const Message m; + InternalTimerThread::instance->handleMessage (m); + } + } + + static inline void add (Timer* const tim) throw() + { + if (instance == 0) + instance = new InternalTimerThread(); + + instance->addTimer (tim); + } + + static inline void remove (Timer* const tim) throw() + { + if (instance != 0) + instance->removeTimer (tim); + } + + static inline void resetCounter (Timer* const tim, + const int newCounter) throw() + { + if (instance != 0) + { + tim->countdownMs = newCounter; + tim->periodMs = newCounter; + + if ((tim->next != 0 && tim->next->countdownMs < tim->countdownMs) + || (tim->previous != 0 && tim->previous->countdownMs > tim->countdownMs)) + { + instance->removeTimer (tim); + instance->addTimer (tim); + } + } + } +}; + +InternalTimerThread* InternalTimerThread::instance = 0; +CriticalSection InternalTimerThread::lock; + +void juce_callAnyTimersSynchronously() +{ + InternalTimerThread::callAnyTimersSynchronously(); +} + +#ifdef JUCE_DEBUG +static SortedSet activeTimers; +#endif + +Timer::Timer() throw() + : countdownMs (0), + periodMs (0), + previous (0), + next (0) +{ +#ifdef JUCE_DEBUG + activeTimers.add (this); +#endif +} + +Timer::Timer (const Timer&) throw() + : countdownMs (0), + periodMs (0), + previous (0), + next (0) +{ +#ifdef JUCE_DEBUG + activeTimers.add (this); +#endif +} + +Timer::~Timer() +{ + stopTimer(); + +#ifdef JUCE_DEBUG + activeTimers.removeValue (this); +#endif +} + +void Timer::startTimer (const int interval) throw() +{ + const ScopedLock sl (InternalTimerThread::lock); + +#ifdef JUCE_DEBUG + // this isn't a valid object! Your timer might be a dangling pointer or something.. + jassert (activeTimers.contains (this)); +#endif + + if (periodMs == 0) + { + countdownMs = interval; + periodMs = jmax (1, interval); + InternalTimerThread::add (this); + } + else + { + InternalTimerThread::resetCounter (this, interval); + } +} + +void Timer::stopTimer() throw() +{ + const ScopedLock sl (InternalTimerThread::lock); + +#ifdef JUCE_DEBUG + // this isn't a valid object! Your timer might be a dangling pointer or something.. + jassert (activeTimers.contains (this)); +#endif + + if (periodMs > 0) + { + InternalTimerThread::remove (this); + periodMs = 0; + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_Timer.cpp *********/ + +/********* Start of inlined file: juce_Component.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +Component* Component::componentUnderMouse = 0; +Component* Component::currentlyFocusedComponent = 0; + +static Array modalComponentStack (4), modalComponentReturnValueKeys (4); +static Array modalReturnValues (4); + +static const int customCommandMessage = 0x7fff0001; +static const int exitModalStateMessage = 0x7fff0002; + +// these are also used by ComponentPeer +int64 juce_recentMouseDownTimes [4] = { 0, 0, 0, 0 }; +int juce_recentMouseDownX [4] = { 0, 0, 0, 0 }; +int juce_recentMouseDownY [4] = { 0, 0, 0, 0 }; +Component* juce_recentMouseDownComponent [4] = { 0, 0, 0, 0 }; +int juce_LastMousePosX = 0; +int juce_LastMousePosY = 0; +int juce_MouseClickCounter = 0; +bool juce_MouseHasMovedSignificantlySincePressed = false; + +static int countMouseClicks() throw() +{ + int numClicks = 0; + + if (juce_recentMouseDownTimes[0] != 0) + { + if (! juce_MouseHasMovedSignificantlySincePressed) + ++numClicks; + + for (int i = 1; i < numElementsInArray (juce_recentMouseDownTimes); ++i) + { + if (juce_recentMouseDownTimes[0] - juce_recentMouseDownTimes [i] + < (int) (MouseEvent::getDoubleClickTimeout() * (1.0 + 0.25 * (i - 1))) + && abs (juce_recentMouseDownX[0] - juce_recentMouseDownX[i]) < 8 + && abs (juce_recentMouseDownY[0] - juce_recentMouseDownY[i]) < 8 + && juce_recentMouseDownComponent[0] == juce_recentMouseDownComponent [i]) + { + ++numClicks; + } + else + { + break; + } + } + } + + return numClicks; +} + +static int unboundedMouseOffsetX = 0; +static int unboundedMouseOffsetY = 0; +static bool isUnboundedMouseModeOn = false; +static bool isCursorVisibleUntilOffscreen; + +#define checkMessageManagerIsLocked jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager()); + +static uint32 nextComponentUID = 0; + +Component::Component() throw() + : parentComponent_ (0), + componentUID (++nextComponentUID), + numDeepMouseListeners (0), + childComponentList_ (16), + lookAndFeel_ (0), + effect_ (0), + bufferedImage_ (0), + mouseListeners_ (0), + keyListeners_ (0), + componentListeners_ (0), + propertySet_ (0), + componentFlags_ (0) +{ +} + +Component::Component (const String& name) throw() + : componentName_ (name), + parentComponent_ (0), + componentUID (++nextComponentUID), + numDeepMouseListeners (0), + childComponentList_ (16), + lookAndFeel_ (0), + effect_ (0), + bufferedImage_ (0), + mouseListeners_ (0), + keyListeners_ (0), + componentListeners_ (0), + propertySet_ (0), + componentFlags_ (0) +{ +} + +Component::~Component() +{ + if (parentComponent_ != 0) + { + parentComponent_->removeChildComponent (this); + } + else if ((currentlyFocusedComponent == this) + || isParentOf (currentlyFocusedComponent)) + { + giveAwayFocus(); + } + + if (componentUnderMouse == this) + componentUnderMouse = 0; + + if (flags.hasHeavyweightPeerFlag) + removeFromDesktop(); + + modalComponentStack.removeValue (this); + + for (int i = childComponentList_.size(); --i >= 0;) + childComponentList_.getUnchecked(i)->parentComponent_ = 0; + + delete bufferedImage_; + delete mouseListeners_; + delete keyListeners_; + delete componentListeners_; + delete propertySet_; +} + +void Component::setName (const String& name) +{ + // if component methods are being called from threads other than the message + // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. + checkMessageManagerIsLocked + + if (componentName_ != name) + { + componentName_ = name; + + if (flags.hasHeavyweightPeerFlag) + { + ComponentPeer* const peer = getPeer(); + + jassert (peer != 0); + if (peer != 0) + peer->setTitle (name); + } + + if (componentListeners_ != 0) + { + const ComponentDeletionWatcher deletionChecker (this); + + for (int i = componentListeners_->size(); --i >= 0;) + { + ((ComponentListener*) componentListeners_->getUnchecked (i)) + ->componentNameChanged (*this); + + if (deletionChecker.hasBeenDeleted()) + return; + + i = jmin (i, componentListeners_->size()); + } + } + + } +} + +void Component::setVisible (bool shouldBeVisible) +{ + if (flags.visibleFlag != shouldBeVisible) + { + // if component methods are being called from threads other than the message + // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. + checkMessageManagerIsLocked + + const ComponentDeletionWatcher deletionChecker (this); + + flags.visibleFlag = shouldBeVisible; + + internalRepaint (0, 0, getWidth(), getHeight()); + + sendFakeMouseMove(); + + if (! shouldBeVisible) + { + if (currentlyFocusedComponent == this + || isParentOf (currentlyFocusedComponent)) + { + if (parentComponent_ != 0) + parentComponent_->grabKeyboardFocus(); + else + giveAwayFocus(); + } + } + + sendVisibilityChangeMessage(); + + if ((! deletionChecker.hasBeenDeleted()) && flags.hasHeavyweightPeerFlag) + { + ComponentPeer* const peer = getPeer(); + + jassert (peer != 0); + if (peer != 0) + { + peer->setVisible (shouldBeVisible); + internalHierarchyChanged(); + } + } + } +} + +void Component::visibilityChanged() +{ +} + +void Component::sendVisibilityChangeMessage() +{ + const ComponentDeletionWatcher deletionChecker (this); + + visibilityChanged(); + + if ((! deletionChecker.hasBeenDeleted()) && componentListeners_ != 0) + { + for (int i = componentListeners_->size(); --i >= 0;) + { + ((ComponentListener*) componentListeners_->getUnchecked (i)) + ->componentVisibilityChanged (*this); + + if (deletionChecker.hasBeenDeleted()) + return; + + i = jmin (i, componentListeners_->size()); + } + } +} + +bool Component::isShowing() const throw() +{ + if (flags.visibleFlag) + { + if (parentComponent_ != 0) + { + return parentComponent_->isShowing(); + } + else + { + const ComponentPeer* const peer = getPeer(); + + return peer != 0 && ! peer->isMinimised(); + } + } + + return false; +} + +class FadeOutProxyComponent : public Component, + public Timer +{ +public: + FadeOutProxyComponent (Component* comp, + const int fadeLengthMs, + const int deltaXToMove, + const int deltaYToMove, + const float scaleFactorAtEnd) + : lastTime (0), + alpha (1.0f), + scale (1.0f) + { + image = comp->createComponentSnapshot (Rectangle (0, 0, comp->getWidth(), comp->getHeight())); + setBounds (comp->getBounds()); + comp->getParentComponent()->addAndMakeVisible (this); + toBehind (comp); + + alphaChangePerMs = -1.0f / (float)fadeLengthMs; + + centreX = comp->getX() + comp->getWidth() * 0.5f; + xChangePerMs = deltaXToMove / (float)fadeLengthMs; + + centreY = comp->getY() + comp->getHeight() * 0.5f; + yChangePerMs = deltaYToMove / (float)fadeLengthMs; + + scaleChangePerMs = (scaleFactorAtEnd - 1.0f) / (float)fadeLengthMs; + + setInterceptsMouseClicks (false, false); + + // 30 fps is enough for a fade, but we need a higher rate if it's moving as well.. + startTimer (1000 / ((deltaXToMove == 0 && deltaYToMove == 0) ? 30 : 50)); + } + + ~FadeOutProxyComponent() + { + delete image; + } + + void paint (Graphics& g) + { + g.setOpacity (alpha); + + g.drawImage (image, + 0, 0, getWidth(), getHeight(), + 0, 0, image->getWidth(), image->getHeight()); + } + + void timerCallback() + { + const uint32 now = Time::getMillisecondCounter(); + + if (lastTime == 0) + lastTime = now; + + const int msPassed = (now > lastTime) ? now - lastTime : 0; + lastTime = now; + + alpha += alphaChangePerMs * msPassed; + + if (alpha > 0) + { + if (xChangePerMs != 0.0f || yChangePerMs != 0.0f || scaleChangePerMs != 0.0f) + { + centreX += xChangePerMs * msPassed; + centreY += yChangePerMs * msPassed; + scale += scaleChangePerMs * msPassed; + + const int w = roundFloatToInt (image->getWidth() * scale); + const int h = roundFloatToInt (image->getHeight() * scale); + + setBounds (roundFloatToInt (centreX) - w / 2, + roundFloatToInt (centreY) - h / 2, + w, h); + } + + repaint(); + } + else + { + delete this; + } + } + + juce_UseDebuggingNewOperator + +private: + Image* image; + uint32 lastTime; + float alpha, alphaChangePerMs; + float centreX, xChangePerMs; + float centreY, yChangePerMs; + float scale, scaleChangePerMs; + + FadeOutProxyComponent (const FadeOutProxyComponent&); + const FadeOutProxyComponent& operator= (const FadeOutProxyComponent&); +}; + +void Component::fadeOutComponent (const int millisecondsToFade, + const int deltaXToMove, + const int deltaYToMove, + const float scaleFactorAtEnd) +{ + //xxx won't work for comps without parents + if (isShowing() && millisecondsToFade > 0) + new FadeOutProxyComponent (this, millisecondsToFade, + deltaXToMove, deltaYToMove, scaleFactorAtEnd); + + setVisible (false); +} + +bool Component::isValidComponent() const throw() +{ + return (this != 0) && isValidMessageListener(); +} + +void* Component::getWindowHandle() const throw() +{ + const ComponentPeer* const peer = getPeer(); + + if (peer != 0) + return peer->getNativeHandle(); + + return 0; +} + +void Component::addToDesktop (int styleWanted, void* nativeWindowToAttachTo) +{ + // if component methods are being called from threads other than the message + // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. + checkMessageManagerIsLocked + + if (! isOpaque()) + styleWanted |= ComponentPeer::windowIsSemiTransparent; + + int currentStyleFlags = 0; + + // don't use getPeer(), so that we only get the peer that's specifically + // for this comp, and not for one of its parents. + ComponentPeer* peer = ComponentPeer::getPeerFor (this); + + if (peer != 0) + currentStyleFlags = peer->getStyleFlags(); + + if (styleWanted != currentStyleFlags || ! flags.hasHeavyweightPeerFlag) + { + const ComponentDeletionWatcher deletionChecker (this); + +#if JUCE_LINUX + // it's wise to give the component a non-zero size before + // putting it on the desktop, as X windows get confused by this, and + // a (1, 1) minimum size is enforced here. + setSize (jmax (1, getWidth()), + jmax (1, getHeight())); +#endif + + int x = 0, y = 0; + relativePositionToGlobal (x, y); + + bool wasFullscreen = false; + bool wasMinimised = false; + ComponentBoundsConstrainer* currentConstainer = 0; + Rectangle oldNonFullScreenBounds; + + if (peer != 0) + { + wasFullscreen = peer->isFullScreen(); + wasMinimised = peer->isMinimised(); + currentConstainer = peer->getConstrainer(); + oldNonFullScreenBounds = peer->getNonFullScreenBounds(); + + removeFromDesktop(); + } + + if (parentComponent_ != 0) + parentComponent_->removeChildComponent (this); + + if (! deletionChecker.hasBeenDeleted()) + { + flags.hasHeavyweightPeerFlag = true; + + peer = createNewPeer (styleWanted, nativeWindowToAttachTo); + + Desktop::getInstance().addDesktopComponent (this); + + bounds_.setPosition (x, y); + peer->setBounds (x, y, getWidth(), getHeight(), false); + + peer->setVisible (isVisible()); + + if (wasFullscreen) + { + peer->setFullScreen (true); + peer->setNonFullScreenBounds (oldNonFullScreenBounds); + } + + if (wasMinimised) + peer->setMinimised (true); + + if (isAlwaysOnTop()) + peer->setAlwaysOnTop (true); + + peer->setConstrainer (currentConstainer); + + repaint(); + } + + internalHierarchyChanged(); + } +} + +void Component::removeFromDesktop() +{ + // if component methods are being called from threads other than the message + // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. + checkMessageManagerIsLocked + + if (flags.hasHeavyweightPeerFlag) + { + ComponentPeer* const peer = ComponentPeer::getPeerFor (this); + + flags.hasHeavyweightPeerFlag = false; + + jassert (peer != 0); + delete peer; + + Desktop::getInstance().removeDesktopComponent (this); + } +} + +bool Component::isOnDesktop() const throw() +{ + return flags.hasHeavyweightPeerFlag; +} + +void Component::userTriedToCloseWindow() +{ + /* This means that the user's trying to get rid of your window with the 'close window' system + menu option (on windows) or possibly the task manager - you should really handle this + and delete or hide your component in an appropriate way. + + If you want to ignore the event and don't want to trigger this assertion, just override + this method and do nothing. + */ + jassertfalse +} + +void Component::minimisationStateChanged (bool) +{ +} + +void Component::setOpaque (const bool shouldBeOpaque) throw() +{ + if (shouldBeOpaque != flags.opaqueFlag) + { + flags.opaqueFlag = shouldBeOpaque; + + if (flags.hasHeavyweightPeerFlag) + { + const ComponentPeer* const peer = ComponentPeer::getPeerFor (this); + + if (peer != 0) + { + // to make it recreate the heavyweight window + addToDesktop (peer->getStyleFlags()); + } + } + + repaint(); + } +} + +bool Component::isOpaque() const throw() +{ + return flags.opaqueFlag; +} + +void Component::setBufferedToImage (const bool shouldBeBuffered) throw() +{ + if (shouldBeBuffered != flags.bufferToImageFlag) + { + deleteAndZero (bufferedImage_); + flags.bufferToImageFlag = shouldBeBuffered; + } +} + +void Component::toFront (const bool setAsForeground) +{ + // if component methods are being called from threads other than the message + // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. + checkMessageManagerIsLocked + + if (flags.hasHeavyweightPeerFlag) + { + ComponentPeer* const peer = getPeer(); + + if (peer != 0) + { + peer->toFront (setAsForeground); + + if (setAsForeground && ! hasKeyboardFocus (true)) + grabKeyboardFocus(); + } + } + else if (parentComponent_ != 0) + { + if (parentComponent_->childComponentList_.getLast() != this) + { + const int index = parentComponent_->childComponentList_.indexOf (this); + + if (index >= 0) + { + int insertIndex = -1; + + if (! flags.alwaysOnTopFlag) + { + insertIndex = parentComponent_->childComponentList_.size() - 1; + + while (insertIndex > 0 + && parentComponent_->childComponentList_.getUnchecked (insertIndex)->isAlwaysOnTop()) + { + --insertIndex; + } + } + + if (index != insertIndex) + { + parentComponent_->childComponentList_.move (index, insertIndex); + sendFakeMouseMove(); + + repaintParent(); + } + } + } + + if (setAsForeground) + { + internalBroughtToFront(); + grabKeyboardFocus(); + } + } +} + +void Component::toBehind (Component* const other) +{ + if (other != 0) + { + // the two components must belong to the same parent.. + jassert (parentComponent_ == other->parentComponent_); + + if (parentComponent_ != 0) + { + const int index = parentComponent_->childComponentList_.indexOf (this); + int otherIndex = parentComponent_->childComponentList_.indexOf (other); + + if (index >= 0 + && otherIndex >= 0 + && index != otherIndex - 1 + && other != this) + { + if (index < otherIndex) + --otherIndex; + + parentComponent_->childComponentList_.move (index, otherIndex); + + sendFakeMouseMove(); + repaintParent(); + } + } + else if (isOnDesktop()) + { + jassert (other->isOnDesktop()); + + if (other->isOnDesktop()) + { + ComponentPeer* const us = getPeer(); + ComponentPeer* const them = other->getPeer(); + + jassert (us != 0 && them != 0); + if (us != 0 && them != 0) + us->toBehind (them); + } + } + } +} + +void Component::toBack() +{ + if (isOnDesktop()) + { + jassertfalse //xxx need to add this to native window + } + else if (parentComponent_ != 0 + && parentComponent_->childComponentList_.getFirst() != this) + { + const int index = parentComponent_->childComponentList_.indexOf (this); + + if (index > 0) + { + int insertIndex = 0; + + if (flags.alwaysOnTopFlag) + { + while (insertIndex < parentComponent_->childComponentList_.size() + && ! parentComponent_->childComponentList_.getUnchecked (insertIndex)->isAlwaysOnTop()) + { + ++insertIndex; + } + } + + if (index != insertIndex) + { + parentComponent_->childComponentList_.move (index, insertIndex); + + sendFakeMouseMove(); + repaintParent(); + } + } + } +} + +void Component::setAlwaysOnTop (const bool shouldStayOnTop) +{ + if (shouldStayOnTop != flags.alwaysOnTopFlag) + { + flags.alwaysOnTopFlag = shouldStayOnTop; + + if (isOnDesktop()) + { + ComponentPeer* const peer = getPeer(); + + jassert (peer != 0); + if (peer != 0) + { + if (! peer->setAlwaysOnTop (shouldStayOnTop)) + { + // some kinds of peer can't change their always-on-top status, so + // for these, we'll need to create a new window + const int oldFlags = peer->getStyleFlags(); + removeFromDesktop(); + addToDesktop (oldFlags); + } + } + } + + if (shouldStayOnTop) + toFront (false); + + internalHierarchyChanged(); + } +} + +bool Component::isAlwaysOnTop() const throw() +{ + return flags.alwaysOnTopFlag; +} + +int Component::proportionOfWidth (const float proportion) const throw() +{ + return roundDoubleToInt (proportion * bounds_.getWidth()); +} + +int Component::proportionOfHeight (const float proportion) const throw() +{ + return roundDoubleToInt (proportion * bounds_.getHeight()); +} + +int Component::getParentWidth() const throw() +{ + return (parentComponent_ != 0) ? parentComponent_->getWidth() + : getParentMonitorArea().getWidth(); +} + +int Component::getParentHeight() const throw() +{ + return (parentComponent_ != 0) ? parentComponent_->getHeight() + : getParentMonitorArea().getHeight(); +} + +int Component::getScreenX() const throw() +{ + return (parentComponent_ != 0) ? parentComponent_->getScreenX() + getX() + : (flags.hasHeavyweightPeerFlag ? getPeer()->getScreenX() + : getX()); +} + +int Component::getScreenY() const throw() +{ + return (parentComponent_ != 0) ? parentComponent_->getScreenY() + getY() + : (flags.hasHeavyweightPeerFlag ? getPeer()->getScreenY() + : getY()); +} + +void Component::relativePositionToGlobal (int& x, int& y) const throw() +{ + const Component* c = this; + + do + { + if (c->flags.hasHeavyweightPeerFlag) + { + c->getPeer()->relativePositionToGlobal (x, y); + break; + } + + x += c->getX(); + y += c->getY(); + c = c->parentComponent_; + } + while (c != 0); +} + +void Component::globalPositionToRelative (int& x, int& y) const throw() +{ + if (flags.hasHeavyweightPeerFlag) + { + getPeer()->globalPositionToRelative (x, y); + } + else + { + if (parentComponent_ != 0) + parentComponent_->globalPositionToRelative (x, y); + + x -= getX(); + y -= getY(); + } +} + +void Component::relativePositionToOtherComponent (const Component* const targetComponent, int& x, int& y) const throw() +{ + if (targetComponent != 0) + { + const Component* c = this; + + do + { + if (c == targetComponent) + return; + + if (c->flags.hasHeavyweightPeerFlag) + { + c->getPeer()->relativePositionToGlobal (x, y); + break; + } + + x += c->getX(); + y += c->getY(); + c = c->parentComponent_; + } + while (c != 0); + + targetComponent->globalPositionToRelative (x, y); + } +} + +void Component::setBounds (int x, int y, int w, int h) +{ + // if component methods are being called from threads other than the message + // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. + checkMessageManagerIsLocked + + if (w < 0) w = 0; + if (h < 0) h = 0; + + const bool wasResized = (getWidth() != w || getHeight() != h); + const bool wasMoved = (getX() != x || getY() != y); + +#ifdef JUCE_DEBUG + // It's a very bad idea to try to resize a window during its paint() method! + jassert (! (flags.isInsidePaintCall && wasResized && isOnDesktop())); +#endif + + if (wasMoved || wasResized) + { + if (flags.visibleFlag) + { + // send a fake mouse move to trigger enter/exit messages if needed.. + sendFakeMouseMove(); + + if (! flags.hasHeavyweightPeerFlag) + repaintParent(); + } + + bounds_.setBounds (x, y, w, h); + + if (wasResized) + repaint(); + else if (! flags.hasHeavyweightPeerFlag) + repaintParent(); + + if (flags.hasHeavyweightPeerFlag) + { + ComponentPeer* const peer = getPeer(); + + if (peer != 0) + { + if (wasMoved && wasResized) + peer->setBounds (getX(), getY(), getWidth(), getHeight(), false); + else if (wasMoved) + peer->setPosition (getX(), getY()); + else if (wasResized) + peer->setSize (getWidth(), getHeight()); + } + } + + sendMovedResizedMessages (wasMoved, wasResized); + } +} + +void Component::sendMovedResizedMessages (const bool wasMoved, const bool wasResized) +{ + JUCE_TRY + { + if (wasMoved) + moved(); + + if (wasResized) + { + resized(); + + for (int i = childComponentList_.size(); --i >= 0;) + { + childComponentList_.getUnchecked(i)->parentSizeChanged(); + + i = jmin (i, childComponentList_.size()); + } + } + + if (parentComponent_ != 0) + parentComponent_->childBoundsChanged (this); + + if (componentListeners_ != 0) + { + const ComponentDeletionWatcher deletionChecker (this); + + for (int i = componentListeners_->size(); --i >= 0;) + { + ((ComponentListener*) componentListeners_->getUnchecked (i)) + ->componentMovedOrResized (*this, wasMoved, wasResized); + + if (deletionChecker.hasBeenDeleted()) + return; + + i = jmin (i, componentListeners_->size()); + } + } + } + JUCE_CATCH_EXCEPTION +} + +void Component::setSize (const int w, const int h) +{ + setBounds (getX(), getY(), w, h); +} + +void Component::setTopLeftPosition (const int x, const int y) +{ + setBounds (x, y, getWidth(), getHeight()); +} + +void Component::setTopRightPosition (const int x, const int y) +{ + setTopLeftPosition (x - getWidth(), y); +} + +void Component::setBounds (const Rectangle& r) +{ + setBounds (r.getX(), + r.getY(), + r.getWidth(), + r.getHeight()); +} + +void Component::setBoundsRelative (const float x, const float y, + const float w, const float h) +{ + const int pw = getParentWidth(); + const int ph = getParentHeight(); + + setBounds (roundFloatToInt (x * pw), + roundFloatToInt (y * ph), + roundFloatToInt (w * pw), + roundFloatToInt (h * ph)); +} + +void Component::setCentrePosition (const int x, const int y) +{ + setTopLeftPosition (x - getWidth() / 2, + y - getHeight() / 2); +} + +void Component::setCentreRelative (const float x, const float y) +{ + setCentrePosition (roundFloatToInt (getParentWidth() * x), + roundFloatToInt (getParentHeight() * y)); +} + +void Component::centreWithSize (const int width, const int height) +{ + setBounds ((getParentWidth() - width) / 2, + (getParentHeight() - height) / 2, + width, + height); +} + +void Component::setBoundsInset (const BorderSize& borders) +{ + setBounds (borders.getLeft(), + borders.getTop(), + getParentWidth() - (borders.getLeftAndRight()), + getParentHeight() - (borders.getTopAndBottom())); +} + +void Component::setBoundsToFit (int x, int y, int width, int height, + const Justification& justification, + const bool onlyReduceInSize) +{ + // it's no good calling this method unless both the component and + // target rectangle have a finite size. + jassert (getWidth() > 0 && getHeight() > 0 && width > 0 && height > 0); + + if (getWidth() > 0 && getHeight() > 0 + && width > 0 && height > 0) + { + int newW, newH; + + if (onlyReduceInSize && getWidth() <= width && getHeight() <= height) + { + newW = getWidth(); + newH = getHeight(); + } + else + { + const double imageRatio = getHeight() / (double) getWidth(); + const double targetRatio = height / (double) width; + + if (imageRatio <= targetRatio) + { + newW = width; + newH = jmin (height, roundDoubleToInt (newW * imageRatio)); + } + else + { + newH = height; + newW = jmin (width, roundDoubleToInt (newH / imageRatio)); + } + } + + if (newW > 0 && newH > 0) + { + int newX, newY; + justification.applyToRectangle (newX, newY, newW, newH, + x, y, width, height); + + setBounds (newX, newY, newW, newH); + } + } +} + +bool Component::hitTest (int x, int y) +{ + if (! flags.ignoresMouseClicksFlag) + return true; + + if (flags.allowChildMouseClicksFlag) + { + for (int i = getNumChildComponents(); --i >= 0;) + { + Component* const c = getChildComponent (i); + + if (c->isVisible() + && c->bounds_.contains (x, y) + && c->hitTest (x - c->getX(), + y - c->getY())) + { + return true; + } + } + } + + return false; +} + +void Component::setInterceptsMouseClicks (const bool allowClicks, + const bool allowClicksOnChildComponents) throw() +{ + flags.ignoresMouseClicksFlag = ! allowClicks; + flags.allowChildMouseClicksFlag = allowClicksOnChildComponents; +} + +void Component::getInterceptsMouseClicks (bool& allowsClicksOnThisComponent, + bool& allowsClicksOnChildComponents) const throw() +{ + allowsClicksOnThisComponent = ! flags.ignoresMouseClicksFlag; + allowsClicksOnChildComponents = flags.allowChildMouseClicksFlag; +} + +bool Component::contains (const int x, const int y) +{ + if (((unsigned int) x) < (unsigned int) getWidth() + && ((unsigned int) y) < (unsigned int) getHeight() + && hitTest (x, y)) + { + if (parentComponent_ != 0) + { + return parentComponent_->contains (x + getX(), + y + getY()); + } + else if (flags.hasHeavyweightPeerFlag) + { + const ComponentPeer* const peer = getPeer(); + + if (peer != 0) + return peer->contains (x, y, true); + } + } + + return false; +} + +bool Component::reallyContains (int x, int y, const bool returnTrueIfWithinAChild) +{ + if (! contains (x, y)) + return false; + + Component* p = this; + + while (p->parentComponent_ != 0) + { + x += p->getX(); + y += p->getY(); + + p = p->parentComponent_; + } + + const Component* const c = p->getComponentAt (x, y); + + return (c == this) || (returnTrueIfWithinAChild && isParentOf (c)); +} + +Component* Component::getComponentAt (const int x, const int y) +{ + if (flags.visibleFlag + && ((unsigned int) x) < (unsigned int) getWidth() + && ((unsigned int) y) < (unsigned int) getHeight() + && hitTest (x, y)) + { + for (int i = childComponentList_.size(); --i >= 0;) + { + Component* const child = childComponentList_.getUnchecked(i); + + Component* const c = child->getComponentAt (x - child->getX(), + y - child->getY()); + + if (c != 0) + return c; + } + + return this; + } + + return 0; +} + +void Component::addChildComponent (Component* const child, int zOrder) +{ + // if component methods are being called from threads other than the message + // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. + checkMessageManagerIsLocked + + if (child != 0 && child->parentComponent_ != this) + { + if (child->parentComponent_ != 0) + child->parentComponent_->removeChildComponent (child); + else + child->removeFromDesktop(); + + child->parentComponent_ = this; + + if (child->isVisible()) + child->repaintParent(); + + if (! child->isAlwaysOnTop()) + { + if (zOrder < 0) + zOrder = childComponentList_.size(); + + while (zOrder > 0) + { + if (! childComponentList_.getUnchecked (zOrder - 1)->isAlwaysOnTop()) + break; + + --zOrder; + } + } + + childComponentList_.insert (zOrder, child); + + child->internalHierarchyChanged(); + internalChildrenChanged(); + } +} + +void Component::addAndMakeVisible (Component* const child, int zOrder) +{ + if (child != 0) + { + child->setVisible (true); + addChildComponent (child, zOrder); + } +} + +void Component::removeChildComponent (Component* const child) +{ + removeChildComponent (childComponentList_.indexOf (child)); +} + +Component* Component::removeChildComponent (const int index) +{ + // if component methods are being called from threads other than the message + // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. + checkMessageManagerIsLocked + + Component* const child = childComponentList_ [index]; + + if (child != 0) + { + sendFakeMouseMove(); + child->repaintParent(); + + childComponentList_.remove (index); + child->parentComponent_ = 0; + + JUCE_TRY + { + if ((currentlyFocusedComponent == child) + || child->isParentOf (currentlyFocusedComponent)) + { + // get rid first to force the grabKeyboardFocus to change to us. + giveAwayFocus(); + grabKeyboardFocus(); + } + } +#if JUCE_CATCH_UNHANDLED_EXCEPTIONS + catch (const std::exception& e) + { + currentlyFocusedComponent = 0; + Desktop::getInstance().triggerFocusCallback(); + JUCEApplication::sendUnhandledException (&e, __FILE__, __LINE__); + } + catch (...) + { + currentlyFocusedComponent = 0; + Desktop::getInstance().triggerFocusCallback(); + JUCEApplication::sendUnhandledException (0, __FILE__, __LINE__); + } +#endif + + child->internalHierarchyChanged(); + internalChildrenChanged(); + } + + return child; +} + +void Component::removeAllChildren() +{ + for (int i = childComponentList_.size(); --i >= 0;) + removeChildComponent (i); +} + +void Component::deleteAllChildren() +{ + for (int i = childComponentList_.size(); --i >= 0;) + delete (removeChildComponent (i)); +} + +int Component::getNumChildComponents() const throw() +{ + return childComponentList_.size(); +} + +Component* Component::getChildComponent (const int index) const throw() +{ + return childComponentList_ [index]; +} + +int Component::getIndexOfChildComponent (const Component* const child) const throw() +{ + return childComponentList_.indexOf (const_cast (child)); +} + +Component* Component::getTopLevelComponent() const throw() +{ + const Component* comp = this; + + while (comp->parentComponent_ != 0) + comp = comp->parentComponent_; + + return (Component*) comp; +} + +bool Component::isParentOf (const Component* possibleChild) const throw() +{ + while (possibleChild->isValidComponent()) + { + possibleChild = possibleChild->parentComponent_; + + if (possibleChild == this) + return true; + } + + return false; +} + +void Component::parentHierarchyChanged() +{ +} + +void Component::childrenChanged() +{ +} + +void Component::internalChildrenChanged() +{ + const ComponentDeletionWatcher deletionChecker (this); + const bool hasListeners = componentListeners_ != 0; + + childrenChanged(); + + if (hasListeners) + { + if (deletionChecker.hasBeenDeleted()) + return; + + for (int i = componentListeners_->size(); --i >= 0;) + { + ((ComponentListener*) componentListeners_->getUnchecked (i)) + ->componentChildrenChanged (*this); + + if (deletionChecker.hasBeenDeleted()) + return; + + i = jmin (i, componentListeners_->size()); + } + } +} + +void Component::internalHierarchyChanged() +{ + parentHierarchyChanged(); + + const ComponentDeletionWatcher deletionChecker (this); + + if (componentListeners_ != 0) + { + for (int i = componentListeners_->size(); --i >= 0;) + { + ((ComponentListener*) componentListeners_->getUnchecked (i)) + ->componentParentHierarchyChanged (*this); + + if (deletionChecker.hasBeenDeleted()) + return; + + i = jmin (i, componentListeners_->size()); + } + } + + for (int i = childComponentList_.size(); --i >= 0;) + { + childComponentList_.getUnchecked (i)->internalHierarchyChanged(); + + // you really shouldn't delete the parent component during a callback telling you + // that it's changed.. + jassert (! deletionChecker.hasBeenDeleted()); + if (deletionChecker.hasBeenDeleted()) + return; + + i = jmin (i, childComponentList_.size()); + } +} + +void* Component::runModalLoopCallback (void* userData) +{ + return (void*) (pointer_sized_int) ((Component*) userData)->runModalLoop(); +} + +int Component::runModalLoop() +{ + if (! MessageManager::getInstance()->isThisTheMessageThread()) + { + // use a callback so this can be called from non-gui threads + return (int) (pointer_sized_int) + MessageManager::getInstance() + ->callFunctionOnMessageThread (&runModalLoopCallback, (void*) this); + } + + Component* const prevFocused = getCurrentlyFocusedComponent(); + + ComponentDeletionWatcher* deletionChecker = 0; + if (prevFocused != 0) + deletionChecker = new ComponentDeletionWatcher (prevFocused); + + if (! isCurrentlyModal()) + enterModalState(); + + JUCE_TRY + { + while (flags.currentlyModalFlag && flags.visibleFlag) + { + if (! MessageManager::getInstance()->dispatchNextMessage()) + break; + + // check whether this component was deleted during the last message + if (! isValidMessageListener()) + break; + } + } +#if JUCE_CATCH_UNHANDLED_EXCEPTIONS + catch (const std::exception& e) + { + JUCEApplication::sendUnhandledException (&e, __FILE__, __LINE__); + return 0; + } + catch (...) + { + JUCEApplication::sendUnhandledException (0, __FILE__, __LINE__); + return 0; + } +#endif + + const int modalIndex = modalComponentReturnValueKeys.indexOf (this); + int returnValue = 0; + + if (modalIndex >= 0) + { + modalComponentReturnValueKeys.remove (modalIndex); + returnValue = modalReturnValues.remove (modalIndex); + } + + modalComponentStack.removeValue (this); + + if (deletionChecker != 0) + { + if (! deletionChecker->hasBeenDeleted()) + prevFocused->grabKeyboardFocus(); + + delete deletionChecker; + } + + return returnValue; +} + +void Component::enterModalState (const bool takeKeyboardFocus) +{ + // if component methods are being called from threads other than the message + // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. + checkMessageManagerIsLocked + + // Check for an attempt to make a component modal when it already is! + // This can cause nasty problems.. + jassert (! flags.currentlyModalFlag); + + if (! isCurrentlyModal()) + { + modalComponentStack.add (this); + modalComponentReturnValueKeys.add (this); + modalReturnValues.add (0); + + flags.currentlyModalFlag = true; + setVisible (true); + + if (takeKeyboardFocus) + grabKeyboardFocus(); + } +} + +void Component::exitModalState (const int returnValue) +{ + if (isCurrentlyModal()) + { + if (MessageManager::getInstance()->isThisTheMessageThread()) + { + const int modalIndex = modalComponentReturnValueKeys.indexOf (this); + + if (modalIndex >= 0) + { + modalReturnValues.set (modalIndex, returnValue); + } + else + { + modalComponentReturnValueKeys.add (this); + modalReturnValues.add (returnValue); + } + + modalComponentStack.removeValue (this); + + flags.currentlyModalFlag = false; + } + else + { + postMessage (new Message (exitModalStateMessage, returnValue, 0, 0)); + } + } +} + +bool Component::isCurrentlyModal() const throw() +{ + return flags.currentlyModalFlag + && getCurrentlyModalComponent() == this; +} + +bool Component::isCurrentlyBlockedByAnotherModalComponent() const throw() +{ + Component* const mc = getCurrentlyModalComponent(); + + return mc != 0 + && mc != this + && (! mc->isParentOf (this)) + && ! mc->canModalEventBeSentToComponent (this); +} + +Component* JUCE_CALLTYPE Component::getCurrentlyModalComponent() throw() +{ + Component* const c = (Component*) modalComponentStack.getLast(); + + return c->isValidComponent() ? c : 0; +} + +void Component::setBroughtToFrontOnMouseClick (const bool shouldBeBroughtToFront) throw() +{ + flags.bringToFrontOnClickFlag = shouldBeBroughtToFront; +} + +bool Component::isBroughtToFrontOnMouseClick() const throw() +{ + return flags.bringToFrontOnClickFlag; +} + +void Component::setMouseCursor (const MouseCursor& cursor) throw() +{ + cursor_ = cursor; + + if (flags.visibleFlag) + { + int mx, my; + getMouseXYRelative (mx, my); + + if (flags.draggingFlag || reallyContains (mx, my, false)) + { + internalUpdateMouseCursor (false); + } + } +} + +const MouseCursor Component::getMouseCursor() +{ + return cursor_; +} + +void Component::updateMouseCursor() const throw() +{ + sendFakeMouseMove(); +} + +void Component::internalUpdateMouseCursor (const bool forcedUpdate) throw() +{ + ComponentPeer* const peer = getPeer(); + + if (peer != 0) + { + MouseCursor mc (getMouseCursor()); + + if (isUnboundedMouseModeOn && (unboundedMouseOffsetX != 0 + || unboundedMouseOffsetY != 0 + || ! isCursorVisibleUntilOffscreen)) + { + mc = MouseCursor::NoCursor; + } + + static void* currentCursorHandle = 0; + + if (forcedUpdate || mc.getHandle() != currentCursorHandle) + { + currentCursorHandle = mc.getHandle(); + mc.showInWindow (peer); + } + } +} + +void Component::setRepaintsOnMouseActivity (const bool shouldRepaint) throw() +{ + flags.repaintOnMouseActivityFlag = shouldRepaint; +} + +void Component::repaintParent() throw() +{ + if (flags.visibleFlag) + internalRepaint (0, 0, getWidth(), getHeight()); +} + +void Component::repaint() throw() +{ + repaint (0, 0, getWidth(), getHeight()); +} + +void Component::repaint (const int x, const int y, + const int w, const int h) throw() +{ + deleteAndZero (bufferedImage_); + + if (flags.visibleFlag) + internalRepaint (x, y, w, h); +} + +void Component::internalRepaint (int x, int y, int w, int h) +{ + // if component methods are being called from threads other than the message + // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. + checkMessageManagerIsLocked + + if (x < 0) + { + w += x; + x = 0; + } + + if (x + w > getWidth()) + w = getWidth() - x; + + if (w > 0) + { + if (y < 0) + { + h += y; + y = 0; + } + + if (y + h > getHeight()) + h = getHeight() - y; + + if (h > 0) + { + if (parentComponent_ != 0) + { + x += getX(); + y += getY(); + + if (parentComponent_->flags.visibleFlag) + parentComponent_->internalRepaint (x, y, w, h); + } + else if (flags.hasHeavyweightPeerFlag) + { + ComponentPeer* const peer = getPeer(); + + if (peer != 0) + peer->repaint (x, y, w, h); + } + } + } +} + +void Component::paintEntireComponent (Graphics& originalContext) +{ + jassert (! originalContext.isClipEmpty()); + +#ifdef JUCE_DEBUG + flags.isInsidePaintCall = true; +#endif + + Graphics* g = &originalContext; + Image* effectImage = 0; + + if (effect_ != 0) + { + effectImage = new Image (flags.opaqueFlag ? Image::RGB : Image::ARGB, + getWidth(), getHeight(), + ! flags.opaqueFlag); + + g = new Graphics (*effectImage); + } + + g->saveState(); + clipObscuredRegions (*g, g->getClipBounds(), 0, 0); + + if (! g->isClipEmpty()) + { + if (bufferedImage_ != 0) + { + g->setColour (Colours::black); + g->drawImageAt (bufferedImage_, 0, 0); + } + else + { + if (flags.bufferToImageFlag) + { + if (bufferedImage_ == 0) + { + bufferedImage_ = new Image (flags.opaqueFlag ? Image::RGB : Image::ARGB, + getWidth(), getHeight(), ! flags.opaqueFlag); + + Graphics imG (*bufferedImage_); + paint (imG); + } + + g->setColour (Colours::black); + g->drawImageAt (bufferedImage_, 0, 0); + } + else + { + paint (*g); + g->resetToDefaultState(); + } + } + } + + g->restoreState(); + + for (int i = 0; i < childComponentList_.size(); ++i) + { + Component* const child = childComponentList_.getUnchecked (i); + + if (child->isVisible()) + { + g->saveState(); + + if (g->reduceClipRegion (child->getX(), child->getY(), + child->getWidth(), child->getHeight())) + { + for (int j = i + 1; j < childComponentList_.size(); ++j) + { + const Component* const sibling = childComponentList_.getUnchecked (j); + + if (sibling->flags.opaqueFlag && sibling->isVisible()) + g->excludeClipRegion (sibling->getX(), sibling->getY(), + sibling->getWidth(), sibling->getHeight()); + } + + if (! g->isClipEmpty()) + { + g->setOrigin (child->getX(), child->getY()); + + child->paintEntireComponent (*g); + } + } + + g->restoreState(); + } + } + + JUCE_TRY + { + g->saveState(); + paintOverChildren (*g); + g->restoreState(); + } + JUCE_CATCH_EXCEPTION + + if (effect_ != 0) + { + delete g; + + effect_->applyEffect (*effectImage, originalContext); + delete effectImage; + } + +#ifdef JUCE_DEBUG + flags.isInsidePaintCall = false; +#endif +} + +Image* Component::createComponentSnapshot (const Rectangle& areaToGrab, + const bool clipImageToComponentBounds) +{ + Rectangle r (areaToGrab); + + if (clipImageToComponentBounds) + r = r.getIntersection (Rectangle (0, 0, getWidth(), getHeight())); + + Image* const componentImage = new Image (flags.opaqueFlag ? Image::RGB : Image::ARGB, + jmax (1, r.getWidth()), + jmax (1, r.getHeight()), + true); + + Graphics imageContext (*componentImage); + imageContext.setOrigin (-r.getX(), + -r.getY()); + + paintEntireComponent (imageContext); + + return componentImage; +} + +void Component::setComponentEffect (ImageEffectFilter* const effect) +{ + if (effect_ != effect) + { + effect_ = effect; + repaint(); + } +} + +LookAndFeel& Component::getLookAndFeel() const throw() +{ + const Component* c = this; + + do + { + if (c->lookAndFeel_ != 0) + return *(c->lookAndFeel_); + + c = c->parentComponent_; + } + while (c != 0); + + return LookAndFeel::getDefaultLookAndFeel(); +} + +void Component::setLookAndFeel (LookAndFeel* const newLookAndFeel) +{ + if (lookAndFeel_ != newLookAndFeel) + { + lookAndFeel_ = newLookAndFeel; + + sendLookAndFeelChange(); + } +} + +void Component::lookAndFeelChanged() +{ +} + +void Component::sendLookAndFeelChange() +{ + repaint(); + + lookAndFeelChanged(); + + // (it's not a great idea to do anything that would delete this component + // during the lookAndFeelChanged() callback) + jassert (isValidComponent()); + + const ComponentDeletionWatcher deletionChecker (this); + + for (int i = childComponentList_.size(); --i >= 0;) + { + childComponentList_.getUnchecked (i)->sendLookAndFeelChange(); + + if (deletionChecker.hasBeenDeleted()) + return; + + i = jmin (i, childComponentList_.size()); + } +} + +static const String getColourPropertyName (const int colourId) throw() +{ + String s; + s.preallocateStorage (18); + s << T("jcclr_") << colourId; + return s; +} + +const Colour Component::findColour (const int colourId, const bool inheritFromParent) const throw() +{ + const String customColour (getComponentProperty (getColourPropertyName (colourId), + inheritFromParent, + String::empty)); + + if (customColour.isNotEmpty()) + return Colour (customColour.getIntValue()); + + return getLookAndFeel().findColour (colourId); +} + +bool Component::isColourSpecified (const int colourId) const throw() +{ + return getComponentProperty (getColourPropertyName (colourId), + false, + String::empty).isNotEmpty(); +} + +void Component::removeColour (const int colourId) +{ + if (isColourSpecified (colourId)) + { + removeComponentProperty (getColourPropertyName (colourId)); + colourChanged(); + } +} + +void Component::setColour (const int colourId, const Colour& colour) +{ + const String colourName (getColourPropertyName (colourId)); + const String customColour (getComponentProperty (colourName, false, String::empty)); + + if (customColour.isEmpty() || Colour (customColour.getIntValue()) != colour) + { + setComponentProperty (colourName, colour); + colourChanged(); + } +} + +void Component::copyAllExplicitColoursTo (Component& target) const throw() +{ + if (propertySet_ != 0) + { + const StringPairArray& props = propertySet_->getAllProperties(); + const StringArray& keys = props.getAllKeys(); + + for (int i = 0; i < keys.size(); ++i) + { + if (keys[i].startsWith (T("jcclr_"))) + { + target.setComponentProperty (keys[i], + props.getAllValues() [i]); + } + } + + target.colourChanged(); + } +} + +void Component::colourChanged() +{ +} + +const Rectangle Component::getUnclippedArea() const +{ + int x = 0, y = 0, w = getWidth(), h = getHeight(); + + Component* p = parentComponent_; + int px = getX(); + int py = getY(); + + while (p != 0) + { + if (! Rectangle::intersectRectangles (x, y, w, h, -px, -py, p->getWidth(), p->getHeight())) + return Rectangle(); + + px += p->getX(); + py += p->getY(); + p = p->parentComponent_; + } + + return Rectangle (x, y, w, h); +} + +void Component::clipObscuredRegions (Graphics& g, const Rectangle& clipRect, + const int deltaX, const int deltaY) const throw() +{ + for (int i = childComponentList_.size(); --i >= 0;) + { + const Component* const c = childComponentList_.getUnchecked(i); + + if (c->isVisible()) + { + Rectangle newClip (clipRect.getIntersection (c->bounds_)); + + if (! newClip.isEmpty()) + { + if (c->isOpaque()) + { + g.excludeClipRegion (deltaX + newClip.getX(), + deltaY + newClip.getY(), + newClip.getWidth(), + newClip.getHeight()); + } + else + { + newClip.translate (-c->getX(), -c->getY()); + c->clipObscuredRegions (g, newClip, + c->getX() + deltaX, + c->getY() + deltaY); + } + } + } + } +} + +void Component::getVisibleArea (RectangleList& result, + const bool includeSiblings) const +{ + result.clear(); + const Rectangle unclipped (getUnclippedArea()); + + if (! unclipped.isEmpty()) + { + result.add (unclipped); + + if (includeSiblings) + { + const Component* const c = getTopLevelComponent(); + + int x = 0, y = 0; + c->relativePositionToOtherComponent (this, x, y); + + c->subtractObscuredRegions (result, x, y, + Rectangle (0, 0, c->getWidth(), c->getHeight()), + this); + } + + subtractObscuredRegions (result, 0, 0, unclipped, 0); + result.consolidate(); + } +} + +void Component::subtractObscuredRegions (RectangleList& result, + const int deltaX, + const int deltaY, + const Rectangle& clipRect, + const Component* const compToAvoid) const throw() +{ + for (int i = childComponentList_.size(); --i >= 0;) + { + const Component* const c = childComponentList_.getUnchecked(i); + + if (c != compToAvoid && c->isVisible()) + { + if (c->isOpaque()) + { + Rectangle childBounds (c->bounds_.getIntersection (clipRect)); + childBounds.translate (deltaX, deltaY); + + result.subtract (childBounds); + } + else + { + Rectangle newClip (clipRect.getIntersection (c->bounds_)); + newClip.translate (-c->getX(), -c->getY()); + + c->subtractObscuredRegions (result, + c->getX() + deltaX, + c->getY() + deltaY, + newClip, + compToAvoid); + } + } + } +} + +void Component::mouseEnter (const MouseEvent&) +{ + // base class does nothing +} + +void Component::mouseExit (const MouseEvent&) +{ + // base class does nothing +} + +void Component::mouseDown (const MouseEvent&) +{ + // base class does nothing +} + +void Component::mouseUp (const MouseEvent&) +{ + // base class does nothing +} + +void Component::mouseDrag (const MouseEvent&) +{ + // base class does nothing +} + +void Component::mouseMove (const MouseEvent&) +{ + // base class does nothing +} + +void Component::mouseDoubleClick (const MouseEvent&) +{ + // base class does nothing +} + +void Component::mouseWheelMove (const MouseEvent& e, float wheelIncrementX, float wheelIncrementY) +{ + // the base class just passes this event up to its parent.. + + if (parentComponent_ != 0) + parentComponent_->mouseWheelMove (e.getEventRelativeTo (parentComponent_), + wheelIncrementX, wheelIncrementY); +} + +void Component::resized() +{ + // base class does nothing +} + +void Component::moved() +{ + // base class does nothing +} + +void Component::childBoundsChanged (Component*) +{ + // base class does nothing +} + +void Component::parentSizeChanged() +{ + // base class does nothing +} + +void Component::addComponentListener (ComponentListener* const newListener) throw() +{ + if (componentListeners_ == 0) + componentListeners_ = new VoidArray (4); + + componentListeners_->addIfNotAlreadyThere (newListener); +} + +void Component::removeComponentListener (ComponentListener* const listenerToRemove) throw() +{ + jassert (isValidComponent()); + + if (componentListeners_ != 0) + componentListeners_->removeValue (listenerToRemove); +} + +void Component::inputAttemptWhenModal() +{ + getTopLevelComponent()->toFront (true); + + getLookAndFeel().playAlertSound(); +} + +bool Component::canModalEventBeSentToComponent (const Component*) +{ + return false; +} + +void Component::internalModalInputAttempt() +{ + Component* const current = getCurrentlyModalComponent(); + + if (current != 0) + current->inputAttemptWhenModal(); +} + +void Component::paint (Graphics&) +{ + // all painting is done in the subclasses + + jassert (! isOpaque()); // if your component's opaque, you've gotta paint it! +} + +void Component::paintOverChildren (Graphics&) +{ + // all painting is done in the subclasses +} + +void Component::handleMessage (const Message& message) +{ + if (message.intParameter1 == exitModalStateMessage) + { + exitModalState (message.intParameter2); + } + else if (message.intParameter1 == customCommandMessage) + { + handleCommandMessage (message.intParameter2); + } +} + +void Component::postCommandMessage (const int commandId) throw() +{ + postMessage (new Message (customCommandMessage, commandId, 0, 0)); +} + +void Component::handleCommandMessage (int) +{ + // used by subclasses +} + +void Component::addMouseListener (MouseListener* const newListener, + const bool wantsEventsForAllNestedChildComponents) throw() +{ + // if component methods are being called from threads other than the message + // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. + checkMessageManagerIsLocked + + if (mouseListeners_ == 0) + mouseListeners_ = new VoidArray (4); + + if (! mouseListeners_->contains (newListener)) + { + if (wantsEventsForAllNestedChildComponents) + { + mouseListeners_->insert (0, newListener); + ++numDeepMouseListeners; + } + else + { + mouseListeners_->add (newListener); + } + } +} + +void Component::removeMouseListener (MouseListener* const listenerToRemove) throw() +{ + // if component methods are being called from threads other than the message + // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. + checkMessageManagerIsLocked + + if (mouseListeners_ != 0) + { + const int index = mouseListeners_->indexOf (listenerToRemove); + + if (index >= 0) + { + if (index < numDeepMouseListeners) + --numDeepMouseListeners; + + mouseListeners_->remove (index); + } + } +} + +void Component::internalMouseEnter (int x, int y, int64 time) +{ + if (isCurrentlyBlockedByAnotherModalComponent()) + { + // if something else is modal, always just show a normal mouse cursor + if (componentUnderMouse == this) + { + ComponentPeer* const peer = getPeer(); + + if (peer != 0) + { + MouseCursor mc (MouseCursor::NormalCursor); + mc.showInWindow (peer); + } + } + + return; + } + + if (! flags.mouseInsideFlag) + { + flags.mouseInsideFlag = true; + flags.mouseOverFlag = true; + flags.draggingFlag = false; + + if (isValidComponent()) + { + const ComponentDeletionWatcher deletionChecker (this); + + if (flags.repaintOnMouseActivityFlag) + repaint(); + + const MouseEvent me (x, y, + ModifierKeys::getCurrentModifiers(), + this, + Time (time), + x, y, + Time (time), + 0, false); + + mouseEnter (me); + + if (deletionChecker.hasBeenDeleted()) + return; + + Desktop::getInstance().resetTimer(); + + for (int i = Desktop::getInstance().mouseListeners.size(); --i >= 0;) + { + ((MouseListener*) Desktop::getInstance().mouseListeners[i])->mouseEnter (me); + + if (deletionChecker.hasBeenDeleted()) + return; + + i = jmin (i, Desktop::getInstance().mouseListeners.size()); + } + + if (mouseListeners_ != 0) + { + for (int i = mouseListeners_->size(); --i >= 0;) + { + ((MouseListener*) mouseListeners_->getUnchecked(i))->mouseEnter (me); + + if (deletionChecker.hasBeenDeleted()) + return; + + i = jmin (i, mouseListeners_->size()); + } + } + + const Component* p = parentComponent_; + + while (p != 0) + { + const ComponentDeletionWatcher parentDeletionChecker (p); + + for (int i = p->numDeepMouseListeners; --i >= 0;) + { + ((MouseListener*) (p->mouseListeners_->getUnchecked(i)))->mouseEnter (me); + + if (deletionChecker.hasBeenDeleted() || parentDeletionChecker.hasBeenDeleted()) + return; + + i = jmin (i, p->numDeepMouseListeners); + } + + p = p->parentComponent_; + } + } + } + + if (componentUnderMouse == this) + internalUpdateMouseCursor (true); +} + +void Component::internalMouseExit (int x, int y, int64 time) +{ + const ComponentDeletionWatcher deletionChecker (this); + + if (flags.draggingFlag) + { + internalMouseUp (ModifierKeys::getCurrentModifiers().getRawFlags(), x, y, time); + + if (deletionChecker.hasBeenDeleted()) + return; + } + + enableUnboundedMouseMovement (false); + + if (flags.mouseInsideFlag || flags.mouseOverFlag) + { + flags.mouseInsideFlag = false; + flags.mouseOverFlag = false; + flags.draggingFlag = false; + + if (flags.repaintOnMouseActivityFlag) + repaint(); + + const MouseEvent me (x, y, + ModifierKeys::getCurrentModifiers(), + this, + Time (time), + x, y, + Time (time), + 0, false); + mouseExit (me); + + if (deletionChecker.hasBeenDeleted()) + return; + + Desktop::getInstance().resetTimer(); + + for (int i = Desktop::getInstance().mouseListeners.size(); --i >= 0;) + { + ((MouseListener*) Desktop::getInstance().mouseListeners[i])->mouseExit (me); + + if (deletionChecker.hasBeenDeleted()) + return; + + i = jmin (i, Desktop::getInstance().mouseListeners.size()); + } + + if (mouseListeners_ != 0) + { + for (int i = mouseListeners_->size(); --i >= 0;) + { + ((MouseListener*) mouseListeners_->getUnchecked (i))->mouseExit (me); + + if (deletionChecker.hasBeenDeleted()) + return; + + i = jmin (i, mouseListeners_->size()); + } + } + + const Component* p = parentComponent_; + + while (p != 0) + { + const ComponentDeletionWatcher parentDeletionChecker (p); + + for (int i = p->numDeepMouseListeners; --i >= 0;) + { + ((MouseListener*) (p->mouseListeners_->getUnchecked (i)))->mouseExit (me); + + if (deletionChecker.hasBeenDeleted() || parentDeletionChecker.hasBeenDeleted()) + return; + + i = jmin (i, p->numDeepMouseListeners); + } + + p = p->parentComponent_; + } + } +} + +class InternalDragRepeater : public Timer +{ +public: + InternalDragRepeater() + {} + + ~InternalDragRepeater() + {} + + void timerCallback() + { + Component* const c = Component::getComponentUnderMouse(); + + if (c != 0 && c->isMouseButtonDown()) + { + int x, y; + c->getMouseXYRelative (x, y); + + // the offsets have been added on, so must be taken off before calling the + // drag.. otherwise they'll be added twice + x -= unboundedMouseOffsetX; + y -= unboundedMouseOffsetY; + + c->internalMouseDrag (x, y, Time::currentTimeMillis()); + } + } + + juce_UseDebuggingNewOperator +}; + +static InternalDragRepeater* dragRepeater = 0; + +void Component::beginDragAutoRepeat (const int interval) +{ + if (interval > 0) + { + if (dragRepeater == 0) + dragRepeater = new InternalDragRepeater(); + + if (dragRepeater->getTimerInterval() != interval) + dragRepeater->startTimer (interval); + } + else + { + deleteAndZero (dragRepeater); + } +} + +void Component::internalMouseDown (const int x, const int y) +{ + const ComponentDeletionWatcher deletionChecker (this); + + if (isCurrentlyBlockedByAnotherModalComponent()) + { + internalModalInputAttempt(); + + if (deletionChecker.hasBeenDeleted()) + return; + + // If processing the input attempt has exited the modal loop, we'll allow the event + // to be delivered.. + if (isCurrentlyBlockedByAnotherModalComponent()) + { + // allow blocked mouse-events to go to global listeners.. + const MouseEvent me (x, y, + ModifierKeys::getCurrentModifiers(), + this, + Time (juce_recentMouseDownTimes[0]), + x, y, + Time (juce_recentMouseDownTimes[0]), + countMouseClicks(), + false); + + Desktop::getInstance().resetTimer(); + + for (int i = Desktop::getInstance().mouseListeners.size(); --i >= 0;) + { + ((MouseListener*) Desktop::getInstance().mouseListeners[i])->mouseDown (me); + + if (deletionChecker.hasBeenDeleted()) + return; + + i = jmin (i, Desktop::getInstance().mouseListeners.size()); + } + + return; + } + } + + { + Component* c = this; + + while (c != 0) + { + if (c->isBroughtToFrontOnMouseClick()) + { + c->toFront (true); + + if (deletionChecker.hasBeenDeleted()) + return; + } + + c = c->parentComponent_; + } + } + + if (! flags.dontFocusOnMouseClickFlag) + grabFocusInternal (focusChangedByMouseClick); + + if (! deletionChecker.hasBeenDeleted()) + { + flags.draggingFlag = true; + flags.mouseOverFlag = true; + + if (flags.repaintOnMouseActivityFlag) + repaint(); + + const MouseEvent me (x, y, + ModifierKeys::getCurrentModifiers(), + this, + Time (juce_recentMouseDownTimes[0]), + x, y, + Time (juce_recentMouseDownTimes[0]), + countMouseClicks(), + false); + mouseDown (me); + + if (deletionChecker.hasBeenDeleted()) + return; + + Desktop::getInstance().resetTimer(); + + for (int i = Desktop::getInstance().mouseListeners.size(); --i >= 0;) + { + ((MouseListener*) Desktop::getInstance().mouseListeners[i])->mouseDown (me); + + if (deletionChecker.hasBeenDeleted()) + return; + + i = jmin (i, Desktop::getInstance().mouseListeners.size()); + } + + if (mouseListeners_ != 0) + { + for (int i = mouseListeners_->size(); --i >= 0;) + { + ((MouseListener*) mouseListeners_->getUnchecked (i))->mouseDown (me); + + if (deletionChecker.hasBeenDeleted()) + return; + + i = jmin (i, mouseListeners_->size()); + } + } + + const Component* p = parentComponent_; + + while (p != 0) + { + const ComponentDeletionWatcher parentDeletionChecker (p); + + for (int i = p->numDeepMouseListeners; --i >= 0;) + { + ((MouseListener*) (p->mouseListeners_->getUnchecked (i)))->mouseDown (me); + + if (deletionChecker.hasBeenDeleted() || parentDeletionChecker.hasBeenDeleted()) + return; + + i = jmin (i, p->numDeepMouseListeners); + } + + p = p->parentComponent_; + } + } +} + +void Component::internalMouseUp (const int oldModifiers, int x, int y, const int64 time) +{ + if (isValidComponent() && flags.draggingFlag) + { + flags.draggingFlag = false; + deleteAndZero (dragRepeater); + + x += unboundedMouseOffsetX; + y += unboundedMouseOffsetY; + juce_LastMousePosX = x; + juce_LastMousePosY = y; + relativePositionToGlobal (juce_LastMousePosX, juce_LastMousePosY); + + const ComponentDeletionWatcher deletionChecker (this); + + if (flags.repaintOnMouseActivityFlag) + repaint(); + + int mdx = juce_recentMouseDownX[0]; + int mdy = juce_recentMouseDownY[0]; + globalPositionToRelative (mdx, mdy); + + const MouseEvent me (x, y, + oldModifiers, + this, + Time (time), + mdx, mdy, + Time (juce_recentMouseDownTimes [0]), + countMouseClicks(), + juce_MouseHasMovedSignificantlySincePressed + || juce_recentMouseDownTimes[0] + 300 < time); + + mouseUp (me); + + if (deletionChecker.hasBeenDeleted()) + return; + + Desktop::getInstance().resetTimer(); + + for (int i = Desktop::getInstance().mouseListeners.size(); --i >= 0;) + { + ((MouseListener*) Desktop::getInstance().mouseListeners[i])->mouseUp (me); + + if (deletionChecker.hasBeenDeleted()) + return; + + i = jmin (i, Desktop::getInstance().mouseListeners.size()); + } + + if (mouseListeners_ != 0) + { + for (int i = mouseListeners_->size(); --i >= 0;) + { + ((MouseListener*) mouseListeners_->getUnchecked (i))->mouseUp (me); + + if (deletionChecker.hasBeenDeleted()) + return; + + i = jmin (i, mouseListeners_->size()); + } + } + + { + const Component* p = parentComponent_; + + while (p != 0) + { + const ComponentDeletionWatcher parentDeletionChecker (p); + + for (int i = p->numDeepMouseListeners; --i >= 0;) + { + ((MouseListener*) (p->mouseListeners_->getUnchecked (i)))->mouseUp (me); + + if (deletionChecker.hasBeenDeleted() || parentDeletionChecker.hasBeenDeleted()) + return; + + i = jmin (i, p->numDeepMouseListeners); + } + + p = p->parentComponent_; + } + } + + // check for double-click + if (me.getNumberOfClicks() >= 2) + { + const int numListeners = (mouseListeners_ != 0) ? mouseListeners_->size() : 0; + + mouseDoubleClick (me); + + int i; + for (i = Desktop::getInstance().mouseListeners.size(); --i >= 0;) + { + ((MouseListener*) Desktop::getInstance().mouseListeners[i])->mouseDoubleClick (me); + + if (deletionChecker.hasBeenDeleted()) + return; + + i = jmin (i, Desktop::getInstance().mouseListeners.size()); + } + + for (i = numListeners; --i >= 0;) + { + if (deletionChecker.hasBeenDeleted() || mouseListeners_ == 0) + return; + + MouseListener* const ml = (MouseListener*)((*mouseListeners_)[i]); + if (ml != 0) + ml->mouseDoubleClick (me); + } + + if (deletionChecker.hasBeenDeleted()) + return; + + const Component* p = parentComponent_; + + while (p != 0) + { + const ComponentDeletionWatcher parentDeletionChecker (p); + + for (i = p->numDeepMouseListeners; --i >= 0;) + { + ((MouseListener*) (p->mouseListeners_->getUnchecked (i)))->mouseDoubleClick (me); + + if (deletionChecker.hasBeenDeleted() || parentDeletionChecker.hasBeenDeleted()) + return; + + i = jmin (i, p->numDeepMouseListeners); + } + + p = p->parentComponent_; + } + } + } + + enableUnboundedMouseMovement (false); +} + +void Component::internalMouseDrag (int x, int y, const int64 time) +{ + if (isValidComponent() && flags.draggingFlag) + { + flags.mouseOverFlag = reallyContains (x, y, false); + + x += unboundedMouseOffsetX; + y += unboundedMouseOffsetY; + juce_LastMousePosX = x; + juce_LastMousePosY = y; + relativePositionToGlobal (juce_LastMousePosX, juce_LastMousePosY); + + juce_MouseHasMovedSignificantlySincePressed + = juce_MouseHasMovedSignificantlySincePressed + || abs (juce_recentMouseDownX[0] - juce_LastMousePosX) >= 4 + || abs (juce_recentMouseDownY[0] - juce_LastMousePosY) >= 4; + + const ComponentDeletionWatcher deletionChecker (this); + + int mdx = juce_recentMouseDownX[0]; + int mdy = juce_recentMouseDownY[0]; + globalPositionToRelative (mdx, mdy); + + const MouseEvent me (x, y, + ModifierKeys::getCurrentModifiers(), + this, + Time (time), + mdx, mdy, + Time (juce_recentMouseDownTimes[0]), + countMouseClicks(), + juce_MouseHasMovedSignificantlySincePressed + || juce_recentMouseDownTimes[0] + 300 < time); + + mouseDrag (me); + + if (deletionChecker.hasBeenDeleted()) + return; + + Desktop::getInstance().resetTimer(); + + for (int i = Desktop::getInstance().mouseListeners.size(); --i >= 0;) + { + ((MouseListener*) Desktop::getInstance().mouseListeners[i])->mouseDrag (me); + + if (deletionChecker.hasBeenDeleted()) + return; + + i = jmin (i, Desktop::getInstance().mouseListeners.size()); + } + + if (mouseListeners_ != 0) + { + for (int i = mouseListeners_->size(); --i >= 0;) + { + ((MouseListener*) mouseListeners_->getUnchecked (i))->mouseDrag (me); + + if (deletionChecker.hasBeenDeleted()) + return; + + i = jmin (i, mouseListeners_->size()); + } + } + + const Component* p = parentComponent_; + + while (p != 0) + { + const ComponentDeletionWatcher parentDeletionChecker (p); + + for (int i = p->numDeepMouseListeners; --i >= 0;) + { + ((MouseListener*) (p->mouseListeners_->getUnchecked (i)))->mouseDrag (me); + + if (deletionChecker.hasBeenDeleted() || parentDeletionChecker.hasBeenDeleted()) + return; + + i = jmin (i, p->numDeepMouseListeners); + } + + p = p->parentComponent_; + } + + if (this == componentUnderMouse) + { + if (isUnboundedMouseModeOn) + { + Rectangle screenArea (getParentMonitorArea().expanded (-2, -2)); + + int mx, my; + Desktop::getMousePosition (mx, my); + + if (! screenArea.contains (mx, my)) + { + int deltaX = 0, deltaY = 0; + + if (mx <= screenArea.getX() || mx >= screenArea.getRight()) + deltaX = getScreenX() + getWidth() / 2 - mx; + + if (my <= screenArea.getY() || my >= screenArea.getBottom()) + deltaY = getScreenY() + getHeight() / 2 - my; + + unboundedMouseOffsetX -= deltaX; + unboundedMouseOffsetY -= deltaY; + + Desktop::setMousePosition (mx + deltaX, + my + deltaY); + } + else if (isCursorVisibleUntilOffscreen + && (unboundedMouseOffsetX != 0 || unboundedMouseOffsetY != 0) + && screenArea.contains (mx + unboundedMouseOffsetX, + my + unboundedMouseOffsetY)) + { + mx += unboundedMouseOffsetX; + my += unboundedMouseOffsetY; + unboundedMouseOffsetX = 0; + unboundedMouseOffsetY = 0; + + Desktop::setMousePosition (mx, my); + } + } + + internalUpdateMouseCursor (false); + } + } +} + +void Component::internalMouseMove (const int x, const int y, const int64 time) +{ + const ComponentDeletionWatcher deletionChecker (this); + + if (isValidComponent()) + { + const MouseEvent me (x, y, + ModifierKeys::getCurrentModifiers(), + this, + Time (time), + x, y, + Time (time), + 0, false); + + if (isCurrentlyBlockedByAnotherModalComponent()) + { + // allow blocked mouse-events to go to global listeners.. + Desktop::getInstance().sendMouseMove(); + } + else + { + if (this == componentUnderMouse) + internalUpdateMouseCursor (false); + + flags.mouseOverFlag = true; + + mouseMove (me); + + if (deletionChecker.hasBeenDeleted()) + return; + + Desktop::getInstance().resetTimer(); + + for (int i = Desktop::getInstance().mouseListeners.size(); --i >= 0;) + { + ((MouseListener*) Desktop::getInstance().mouseListeners[i])->mouseMove (me); + + if (deletionChecker.hasBeenDeleted()) + return; + + i = jmin (i, Desktop::getInstance().mouseListeners.size()); + } + + if (mouseListeners_ != 0) + { + for (int i = mouseListeners_->size(); --i >= 0;) + { + ((MouseListener*) mouseListeners_->getUnchecked (i))->mouseMove (me); + + if (deletionChecker.hasBeenDeleted()) + return; + + i = jmin (i, mouseListeners_->size()); + } + } + + const Component* p = parentComponent_; + + while (p != 0) + { + const ComponentDeletionWatcher parentDeletionChecker (p); + + for (int i = p->numDeepMouseListeners; --i >= 0;) + { + ((MouseListener*) (p->mouseListeners_->getUnchecked (i)))->mouseMove (me); + + if (deletionChecker.hasBeenDeleted() || parentDeletionChecker.hasBeenDeleted()) + return; + + i = jmin (i, p->numDeepMouseListeners); + } + + p = p->parentComponent_; + } + } + } +} + +void Component::internalMouseWheel (const int intAmountX, const int intAmountY, const int64 time) +{ + const ComponentDeletionWatcher deletionChecker (this); + + const float wheelIncrementX = intAmountX * (1.0f / 256.0f); + const float wheelIncrementY = intAmountY * (1.0f / 256.0f); + + int mx, my; + getMouseXYRelative (mx, my); + + const MouseEvent me (mx, my, + ModifierKeys::getCurrentModifiers(), + this, + Time (time), + mx, my, + Time (time), + 0, false); + + if (isCurrentlyBlockedByAnotherModalComponent()) + { + // allow blocked mouse-events to go to global listeners.. + for (int i = Desktop::getInstance().mouseListeners.size(); --i >= 0;) + { + ((MouseListener*) Desktop::getInstance().mouseListeners[i])->mouseWheelMove (me, wheelIncrementX, wheelIncrementY); + + if (deletionChecker.hasBeenDeleted()) + return; + + i = jmin (i, Desktop::getInstance().mouseListeners.size()); + } + } + else + { + mouseWheelMove (me, wheelIncrementX, wheelIncrementY); + + if (deletionChecker.hasBeenDeleted()) + return; + + for (int i = Desktop::getInstance().mouseListeners.size(); --i >= 0;) + { + ((MouseListener*) Desktop::getInstance().mouseListeners[i])->mouseWheelMove (me, wheelIncrementX, wheelIncrementY); + + if (deletionChecker.hasBeenDeleted()) + return; + + i = jmin (i, Desktop::getInstance().mouseListeners.size()); + } + + if (mouseListeners_ != 0) + { + for (int i = mouseListeners_->size(); --i >= 0;) + { + ((MouseListener*) mouseListeners_->getUnchecked (i))->mouseWheelMove (me, wheelIncrementX, wheelIncrementY); + + if (deletionChecker.hasBeenDeleted()) + return; + + i = jmin (i, mouseListeners_->size()); + } + } + + const Component* p = parentComponent_; + + while (p != 0) + { + const ComponentDeletionWatcher parentDeletionChecker (p); + + for (int i = p->numDeepMouseListeners; --i >= 0;) + { + ((MouseListener*) (p->mouseListeners_->getUnchecked (i)))->mouseWheelMove (me, wheelIncrementX, wheelIncrementY); + + if (deletionChecker.hasBeenDeleted() || parentDeletionChecker.hasBeenDeleted()) + return; + + i = jmin (i, p->numDeepMouseListeners); + } + + p = p->parentComponent_; + } + + sendFakeMouseMove(); + } +} + +void Component::sendFakeMouseMove() const +{ + ComponentPeer* const peer = getPeer(); + + if (peer != 0) + peer->sendFakeMouseMove(); +} + +void Component::broughtToFront() +{ +} + +void Component::internalBroughtToFront() +{ + if (isValidComponent()) + { + if (flags.hasHeavyweightPeerFlag) + Desktop::getInstance().componentBroughtToFront (this); + + const ComponentDeletionWatcher deletionChecker (this); + broughtToFront(); + + if (deletionChecker.hasBeenDeleted()) + return; + + if (componentListeners_ != 0) + { + for (int i = componentListeners_->size(); --i >= 0;) + { + ((ComponentListener*) componentListeners_->getUnchecked (i)) + ->componentBroughtToFront (*this); + + if (deletionChecker.hasBeenDeleted()) + return; + + i = jmin (i, componentListeners_->size()); + } + } + + // when brought to the front and there's a modal component blocking this one, + // we need to bring the modal one to the front instead.. + + Component* const cm = getCurrentlyModalComponent(); + + if (cm != 0 && cm->getTopLevelComponent() != getTopLevelComponent()) + { + cm->getTopLevelComponent()->toFront (false); + } + } +} + +void Component::focusGained (FocusChangeType) +{ + // base class does nothing +} + +void Component::internalFocusGain (const FocusChangeType cause) +{ + const ComponentDeletionWatcher deletionChecker (this); + + focusGained (cause); + + if (! deletionChecker.hasBeenDeleted()) + internalChildFocusChange (cause); +} + +void Component::focusLost (FocusChangeType) +{ + // base class does nothing +} + +void Component::internalFocusLoss (const FocusChangeType cause) +{ + const ComponentDeletionWatcher deletionChecker (this); + + focusLost (focusChangedDirectly); + + if (! deletionChecker.hasBeenDeleted()) + internalChildFocusChange (cause); +} + +void Component::focusOfChildComponentChanged (FocusChangeType /*cause*/) +{ + // base class does nothing +} + +void Component::internalChildFocusChange (FocusChangeType cause) +{ + const bool childIsNowFocused = hasKeyboardFocus (true); + + if (flags.childCompFocusedFlag != childIsNowFocused) + { + flags.childCompFocusedFlag = childIsNowFocused; + + const ComponentDeletionWatcher deletionChecker (this); + focusOfChildComponentChanged (cause); + + if (deletionChecker.hasBeenDeleted()) + return; + } + + if (parentComponent_ != 0) + parentComponent_->internalChildFocusChange (cause); +} + +bool Component::isEnabled() const throw() +{ + return (! flags.isDisabledFlag) + && (parentComponent_ == 0 || parentComponent_->isEnabled()); +} + +void Component::setEnabled (const bool shouldBeEnabled) +{ + if (flags.isDisabledFlag == shouldBeEnabled) + { + flags.isDisabledFlag = ! shouldBeEnabled; + + // if any parent components are disabled, setting our flag won't make a difference, + // so no need to send a change message + if (parentComponent_ == 0 || parentComponent_->isEnabled()) + sendEnablementChangeMessage(); + } +} + +void Component::sendEnablementChangeMessage() +{ + const ComponentDeletionWatcher deletionChecker (this); + + enablementChanged(); + + if (deletionChecker.hasBeenDeleted()) + return; + + for (int i = getNumChildComponents(); --i >= 0;) + { + Component* const c = getChildComponent (i); + + if (c != 0) + { + c->sendEnablementChangeMessage(); + + if (deletionChecker.hasBeenDeleted()) + return; + } + } +} + +void Component::enablementChanged() +{ +} + +void Component::setWantsKeyboardFocus (const bool wantsFocus) throw() +{ + flags.wantsFocusFlag = wantsFocus; +} + +void Component::setMouseClickGrabsKeyboardFocus (const bool shouldGrabFocus) +{ + flags.dontFocusOnMouseClickFlag = ! shouldGrabFocus; +} + +bool Component::getMouseClickGrabsKeyboardFocus() const throw() +{ + return ! flags.dontFocusOnMouseClickFlag; +} + +bool Component::getWantsKeyboardFocus() const throw() +{ + return flags.wantsFocusFlag && ! flags.isDisabledFlag; +} + +void Component::setFocusContainer (const bool isFocusContainer) throw() +{ + flags.isFocusContainerFlag = isFocusContainer; +} + +bool Component::isFocusContainer() const throw() +{ + return flags.isFocusContainerFlag; +} + +int Component::getExplicitFocusOrder() const throw() +{ + return getComponentPropertyInt (T("_jexfo"), false, 0); +} + +void Component::setExplicitFocusOrder (const int newFocusOrderIndex) throw() +{ + setComponentProperty (T("_jexfo"), newFocusOrderIndex); +} + +KeyboardFocusTraverser* Component::createFocusTraverser() +{ + if (flags.isFocusContainerFlag || parentComponent_ == 0) + return new KeyboardFocusTraverser(); + + return parentComponent_->createFocusTraverser(); +} + +void Component::takeKeyboardFocus (const FocusChangeType cause) +{ + // give the focus to this component + if (currentlyFocusedComponent != this) + { + JUCE_TRY + { + // get the focus onto our desktop window + ComponentPeer* const peer = getPeer(); + + if (peer != 0) + { + const ComponentDeletionWatcher deletionChecker (this); + + peer->grabFocus(); + + if (peer->isFocused() && currentlyFocusedComponent != this) + { + Component* const componentLosingFocus = currentlyFocusedComponent; + + currentlyFocusedComponent = this; + + Desktop::getInstance().triggerFocusCallback(); + + // call this after setting currentlyFocusedComponent so that the one that's + // losing it has a chance to see where focus is going + if (componentLosingFocus->isValidComponent()) + componentLosingFocus->internalFocusLoss (cause); + + if (currentlyFocusedComponent == this) + { + focusGained (cause); + + if (! deletionChecker.hasBeenDeleted()) + internalChildFocusChange (cause); + } + } + } + } +#if JUCE_CATCH_UNHANDLED_EXCEPTIONS + catch (const std::exception& e) + { + currentlyFocusedComponent = 0; + Desktop::getInstance().triggerFocusCallback(); + JUCEApplication::sendUnhandledException (&e, __FILE__, __LINE__); + } + catch (...) + { + currentlyFocusedComponent = 0; + Desktop::getInstance().triggerFocusCallback(); + JUCEApplication::sendUnhandledException (0, __FILE__, __LINE__); + } +#endif + } +} + +void Component::grabFocusInternal (const FocusChangeType cause, const bool canTryParent) +{ + if (isShowing()) + { + if (flags.wantsFocusFlag && (isEnabled() || parentComponent_ == 0)) + { + takeKeyboardFocus (cause); + } + else + { + if (isParentOf (currentlyFocusedComponent) + && currentlyFocusedComponent->isShowing()) + { + // do nothing if the focused component is actually a child of ours.. + } + else + { + // find the default child component.. + KeyboardFocusTraverser* const traverser = createFocusTraverser(); + + if (traverser != 0) + { + Component* const defaultComp = traverser->getDefaultComponent (this); + delete traverser; + + if (defaultComp != 0) + { + defaultComp->grabFocusInternal (cause, false); + return; + } + } + + if (canTryParent && parentComponent_ != 0) + { + // if no children want it and we're allowed to try our parent comp, + // then pass up to parent, which will try our siblings. + parentComponent_->grabFocusInternal (cause, true); + } + } + } + } +} + +void Component::grabKeyboardFocus() +{ + // if component methods are being called from threads other than the message + // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. + checkMessageManagerIsLocked + + grabFocusInternal (focusChangedDirectly); +} + +void Component::moveKeyboardFocusToSibling (const bool moveToNext) +{ + // if component methods are being called from threads other than the message + // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. + checkMessageManagerIsLocked + + if (parentComponent_ != 0) + { + KeyboardFocusTraverser* const traverser = createFocusTraverser(); + + if (traverser != 0) + { + Component* const nextComp = moveToNext ? traverser->getNextComponent (this) + : traverser->getPreviousComponent (this); + delete traverser; + + if (nextComp != 0) + { + if (nextComp->isCurrentlyBlockedByAnotherModalComponent()) + { + const ComponentDeletionWatcher deletionChecker (nextComp); + internalModalInputAttempt(); + + if (deletionChecker.hasBeenDeleted() + || nextComp->isCurrentlyBlockedByAnotherModalComponent()) + return; + } + + nextComp->grabFocusInternal (focusChangedByTabKey); + return; + } + } + + parentComponent_->moveKeyboardFocusToSibling (moveToNext); + } +} + +bool Component::hasKeyboardFocus (const bool trueIfChildIsFocused) const throw() +{ + return (currentlyFocusedComponent == this) + || (trueIfChildIsFocused && isParentOf (currentlyFocusedComponent)); +} + +Component* JUCE_CALLTYPE Component::getCurrentlyFocusedComponent() throw() +{ + return currentlyFocusedComponent; +} + +void Component::giveAwayFocus() +{ + // use a copy so we can clear the value before the call + Component* const componentLosingFocus = currentlyFocusedComponent; + currentlyFocusedComponent = 0; + Desktop::getInstance().triggerFocusCallback(); + + if (componentLosingFocus->isValidComponent()) + componentLosingFocus->internalFocusLoss (focusChangedDirectly); +} + +bool Component::isMouseOver() const throw() +{ + return flags.mouseOverFlag; +} + +bool Component::isMouseButtonDown() const throw() +{ + return flags.draggingFlag; +} + +bool Component::isMouseOverOrDragging() const throw() +{ + return flags.mouseOverFlag || flags.draggingFlag; +} + +bool JUCE_CALLTYPE Component::isMouseButtonDownAnywhere() throw() +{ + return ModifierKeys::getCurrentModifiers().isAnyMouseButtonDown(); +} + +void Component::getMouseXYRelative (int& mx, int& my) const throw() +{ + Desktop::getMousePosition (mx, my); + globalPositionToRelative (mx, my); + + mx += unboundedMouseOffsetX; + my += unboundedMouseOffsetY; +} + +void Component::enableUnboundedMouseMovement (bool enable, + bool keepCursorVisibleUntilOffscreen) throw() +{ + enable = enable && isMouseButtonDown(); + isCursorVisibleUntilOffscreen = keepCursorVisibleUntilOffscreen; + + if (enable != isUnboundedMouseModeOn) + { + if ((! enable) && ((! isCursorVisibleUntilOffscreen) + || unboundedMouseOffsetX != 0 + || unboundedMouseOffsetY != 0)) + { + // when released, return the mouse to within the component's bounds + + int mx, my; + getMouseXYRelative (mx, my); + + mx = jlimit (0, getWidth(), mx); + my = jlimit (0, getHeight(), my); + + relativePositionToGlobal (mx, my); + + Desktop::setMousePosition (mx, my); + } + + isUnboundedMouseModeOn = enable; + unboundedMouseOffsetX = 0; + unboundedMouseOffsetY = 0; + + internalUpdateMouseCursor (true); + } +} + +Component* JUCE_CALLTYPE Component::getComponentUnderMouse() throw() +{ + return componentUnderMouse; +} + +const Rectangle Component::getParentMonitorArea() const throw() +{ + int centreX = getWidth() / 2; + int centreY = getHeight() / 2; + relativePositionToGlobal (centreX, centreY); + + return Desktop::getInstance().getMonitorAreaContaining (centreX, centreY); +} + +void Component::addKeyListener (KeyListener* const newListener) throw() +{ + if (keyListeners_ == 0) + keyListeners_ = new VoidArray (4); + + keyListeners_->addIfNotAlreadyThere (newListener); +} + +void Component::removeKeyListener (KeyListener* const listenerToRemove) throw() +{ + if (keyListeners_ != 0) + keyListeners_->removeValue (listenerToRemove); +} + +bool Component::keyPressed (const KeyPress&) +{ + return false; +} + +bool Component::keyStateChanged() +{ + return false; +} + +void Component::modifierKeysChanged (const ModifierKeys& modifiers) +{ + if (parentComponent_ != 0) + parentComponent_->modifierKeysChanged (modifiers); +} + +void Component::internalModifierKeysChanged() +{ + sendFakeMouseMove(); + + modifierKeysChanged (ModifierKeys::getCurrentModifiers()); +} + +ComponentPeer* Component::getPeer() const throw() +{ + if (flags.hasHeavyweightPeerFlag) + return ComponentPeer::getPeerFor (this); + else if (parentComponent_ != 0) + return parentComponent_->getPeer(); + else + return 0; +} + +const String Component::getComponentProperty (const String& keyName, + const bool useParentComponentIfNotFound, + const String& defaultReturnValue) const throw() +{ + if (propertySet_ != 0 && ((! useParentComponentIfNotFound) || propertySet_->containsKey (keyName))) + return propertySet_->getValue (keyName, defaultReturnValue); + + if (useParentComponentIfNotFound && (parentComponent_ != 0)) + return parentComponent_->getComponentProperty (keyName, true, defaultReturnValue); + + return defaultReturnValue; +} + +int Component::getComponentPropertyInt (const String& keyName, + const bool useParentComponentIfNotFound, + const int defaultReturnValue) const throw() +{ + if (propertySet_ != 0 && ((! useParentComponentIfNotFound) || propertySet_->containsKey (keyName))) + return propertySet_->getIntValue (keyName, defaultReturnValue); + + if (useParentComponentIfNotFound && (parentComponent_ != 0)) + return parentComponent_->getComponentPropertyInt (keyName, true, defaultReturnValue); + + return defaultReturnValue; +} + +double Component::getComponentPropertyDouble (const String& keyName, + const bool useParentComponentIfNotFound, + const double defaultReturnValue) const throw() +{ + if (propertySet_ != 0 && ((! useParentComponentIfNotFound) || propertySet_->containsKey (keyName))) + return propertySet_->getDoubleValue (keyName, defaultReturnValue); + + if (useParentComponentIfNotFound && (parentComponent_ != 0)) + return parentComponent_->getComponentPropertyDouble (keyName, true, defaultReturnValue); + + return defaultReturnValue; +} + +bool Component::getComponentPropertyBool (const String& keyName, + const bool useParentComponentIfNotFound, + const bool defaultReturnValue) const throw() +{ + if (propertySet_ != 0 && ((! useParentComponentIfNotFound) || propertySet_->containsKey (keyName))) + return propertySet_->getBoolValue (keyName, defaultReturnValue); + + if (useParentComponentIfNotFound && (parentComponent_ != 0)) + return parentComponent_->getComponentPropertyBool (keyName, true, defaultReturnValue); + + return defaultReturnValue; +} + +const Colour Component::getComponentPropertyColour (const String& keyName, + const bool useParentComponentIfNotFound, + const Colour& defaultReturnValue) const throw() +{ + return Colour ((uint32) getComponentPropertyInt (keyName, + useParentComponentIfNotFound, + defaultReturnValue.getARGB())); +} + +void Component::setComponentProperty (const String& keyName, const String& value) throw() +{ + if (propertySet_ == 0) + propertySet_ = new PropertySet(); + + propertySet_->setValue (keyName, value); +} + +void Component::setComponentProperty (const String& keyName, const int value) throw() +{ + if (propertySet_ == 0) + propertySet_ = new PropertySet(); + + propertySet_->setValue (keyName, value); +} + +void Component::setComponentProperty (const String& keyName, const double value) throw() +{ + if (propertySet_ == 0) + propertySet_ = new PropertySet(); + + propertySet_->setValue (keyName, value); +} + +void Component::setComponentProperty (const String& keyName, const bool value) throw() +{ + if (propertySet_ == 0) + propertySet_ = new PropertySet(); + + propertySet_->setValue (keyName, value); +} + +void Component::setComponentProperty (const String& keyName, const Colour& colour) throw() +{ + setComponentProperty (keyName, (int) colour.getARGB()); +} + +void Component::removeComponentProperty (const String& keyName) throw() +{ + if (propertySet_ != 0) + propertySet_->removeValue (keyName); +} + +ComponentDeletionWatcher::ComponentDeletionWatcher (const Component* const componentToWatch_) throw() + : componentToWatch (componentToWatch_), + componentUID (componentToWatch_->getComponentUID()) +{ + // not possible to check on an already-deleted object.. + jassert (componentToWatch_->isValidComponent()); +} + +ComponentDeletionWatcher::~ComponentDeletionWatcher() throw() {} + +bool ComponentDeletionWatcher::hasBeenDeleted() const throw() +{ + return ! (componentToWatch->isValidComponent() + && componentToWatch->getComponentUID() == componentUID); +} + +const Component* ComponentDeletionWatcher::getComponent() const throw() +{ + return hasBeenDeleted() ? 0 : componentToWatch; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_Component.cpp *********/ + +/********* Start of inlined file: juce_ComponentListener.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +void ComponentListener::componentMovedOrResized (Component&, bool, bool) +{ +} + +void ComponentListener::componentBroughtToFront (Component&) +{ +} + +void ComponentListener::componentVisibilityChanged (Component&) +{ +} + +void ComponentListener::componentChildrenChanged (Component&) +{ +} + +void ComponentListener::componentParentHierarchyChanged (Component&) +{ +} + +void ComponentListener::componentNameChanged (Component&) +{ +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ComponentListener.cpp *********/ + +/********* Start of inlined file: juce_Desktop.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +extern void juce_updateMultiMonitorInfo (Array & monitorCoords, + const bool clipToWorkArea) throw(); + +static Desktop* juce_desktopInstance = 0; + +Desktop::Desktop() throw() + : mouseListeners (2), + desktopComponents (4), + monitorCoordsClipped (2), + monitorCoordsUnclipped (2), + lastMouseX (0), + lastMouseY (0) +{ + refreshMonitorSizes(); +} + +Desktop::~Desktop() throw() +{ + jassert (juce_desktopInstance == this); + juce_desktopInstance = 0; + + // doh! If you don't delete all your windows before exiting, you're going to + // be leaking memory! + jassert (desktopComponents.size() == 0); +} + +Desktop& JUCE_CALLTYPE Desktop::getInstance() throw() +{ + if (juce_desktopInstance == 0) + juce_desktopInstance = new Desktop(); + + return *juce_desktopInstance; +} + +void Desktop::refreshMonitorSizes() throw() +{ + const Array oldClipped (monitorCoordsClipped); + const Array oldUnclipped (monitorCoordsUnclipped); + + monitorCoordsClipped.clear(); + monitorCoordsUnclipped.clear(); + juce_updateMultiMonitorInfo (monitorCoordsClipped, true); + juce_updateMultiMonitorInfo (monitorCoordsUnclipped, false); + jassert (monitorCoordsClipped.size() > 0 + && monitorCoordsClipped.size() == monitorCoordsUnclipped.size()); + + if (oldClipped != monitorCoordsClipped + || oldUnclipped != monitorCoordsUnclipped) + { + for (int i = ComponentPeer::getNumPeers(); --i >= 0;) + { + ComponentPeer* const p = ComponentPeer::getPeer (i); + if (p != 0) + p->handleScreenSizeChange(); + } + } +} + +int Desktop::getNumDisplayMonitors() const throw() +{ + return monitorCoordsClipped.size(); +} + +const Rectangle Desktop::getDisplayMonitorCoordinates (const int index, const bool clippedToWorkArea) const throw() +{ + return clippedToWorkArea ? monitorCoordsClipped [index] + : monitorCoordsUnclipped [index]; +} + +const RectangleList Desktop::getAllMonitorDisplayAreas (const bool clippedToWorkArea) const throw() +{ + RectangleList rl; + + for (int i = 0; i < getNumDisplayMonitors(); ++i) + rl.addWithoutMerging (getDisplayMonitorCoordinates (i, clippedToWorkArea)); + + return rl; +} + +const Rectangle Desktop::getMainMonitorArea (const bool clippedToWorkArea) const throw() +{ + return getDisplayMonitorCoordinates (0, clippedToWorkArea); +} + +const Rectangle Desktop::getMonitorAreaContaining (int cx, int cy, const bool clippedToWorkArea) const throw() +{ + Rectangle best (getMainMonitorArea (clippedToWorkArea)); + double bestDistance = 1.0e10; + + for (int i = getNumDisplayMonitors(); --i >= 0;) + { + const Rectangle rect (getDisplayMonitorCoordinates (i, clippedToWorkArea)); + + if (rect.contains (cx, cy)) + return rect; + + const double distance = juce_hypot ((double) (rect.getCentreX() - cx), + (double) (rect.getCentreY() - cy)); + + if (distance < bestDistance) + { + bestDistance = distance; + best = rect; + } + } + + return best; +} + +int Desktop::getNumComponents() const throw() +{ + return desktopComponents.size(); +} + +Component* Desktop::getComponent (const int index) const throw() +{ + return (Component*) desktopComponents [index]; +} + +Component* Desktop::findComponentAt (const int screenX, + const int screenY) const +{ + for (int i = desktopComponents.size(); --i >= 0;) + { + Component* const c = (Component*) desktopComponents.getUnchecked(i); + + int x = screenX, y = screenY; + c->globalPositionToRelative (x, y); + + if (c->contains (x, y)) + return c->getComponentAt (x, y); + } + + return 0; +} + +void Desktop::addDesktopComponent (Component* const c) throw() +{ + jassert (c != 0); + jassert (! desktopComponents.contains (c)); + desktopComponents.addIfNotAlreadyThere (c); +} + +void Desktop::removeDesktopComponent (Component* const c) throw() +{ + desktopComponents.removeValue (c); +} + +void Desktop::componentBroughtToFront (Component* const c) throw() +{ + const int index = desktopComponents.indexOf (c); + jassert (index >= 0); + + if (index >= 0) + desktopComponents.move (index, -1); +} + +// from Component.cpp +extern int juce_recentMouseDownX [4]; +extern int juce_recentMouseDownY [4]; +extern int juce_MouseClickCounter; + +void Desktop::getLastMouseDownPosition (int& x, int& y) throw() +{ + x = juce_recentMouseDownX [0]; + y = juce_recentMouseDownY [0]; +} + +int Desktop::getMouseButtonClickCounter() throw() +{ + return juce_MouseClickCounter; +} + +void Desktop::addGlobalMouseListener (MouseListener* const listener) throw() +{ + jassert (listener != 0); + + if (listener != 0) + { + mouseListeners.add (listener); + resetTimer(); + } +} + +void Desktop::removeGlobalMouseListener (MouseListener* const listener) throw() +{ + mouseListeners.removeValue (listener); + resetTimer(); +} + +void Desktop::addFocusChangeListener (FocusChangeListener* const listener) throw() +{ + jassert (listener != 0); + + if (listener != 0) + focusListeners.add (listener); +} + +void Desktop::removeFocusChangeListener (FocusChangeListener* const listener) throw() +{ + focusListeners.removeValue (listener); +} + +void Desktop::triggerFocusCallback() throw() +{ + triggerAsyncUpdate(); +} + +void Desktop::handleAsyncUpdate() +{ + for (int i = focusListeners.size(); --i >= 0;) + { + ((FocusChangeListener*) focusListeners.getUnchecked (i))->globalFocusChanged (Component::getCurrentlyFocusedComponent()); + i = jmin (i, focusListeners.size()); + } +} + +void Desktop::timerCallback() +{ + int x, y; + getMousePosition (x, y); + + if (lastMouseX != x || lastMouseY != y) + sendMouseMove(); +} + +void Desktop::sendMouseMove() +{ + if (mouseListeners.size() > 0) + { + startTimer (20); + + int x, y; + getMousePosition (x, y); + lastMouseX = x; + lastMouseY = y; + + Component* const target = findComponentAt (x, y); + + if (target != 0) + { + target->globalPositionToRelative (x, y); + + ComponentDeletionWatcher deletionChecker (target); + + const MouseEvent me (x, y, + ModifierKeys::getCurrentModifiers(), + target, + Time::getCurrentTime(), + x, y, + Time::getCurrentTime(), + 0, false); + + for (int i = mouseListeners.size(); --i >= 0;) + { + if (ModifierKeys::getCurrentModifiers().isAnyMouseButtonDown()) + ((MouseListener*) mouseListeners[i])->mouseDrag (me); + else + ((MouseListener*) mouseListeners[i])->mouseMove (me); + + if (deletionChecker.hasBeenDeleted()) + return; + + i = jmin (i, mouseListeners.size()); + } + } + } +} + +void Desktop::resetTimer() throw() +{ + if (mouseListeners.size() == 0) + stopTimer(); + else + startTimer (100); + + getMousePosition (lastMouseX, lastMouseY); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_Desktop.cpp *********/ + +/********* Start of inlined file: juce_ArrowButton.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +ArrowButton::ArrowButton (const String& name, + float arrowDirectionInRadians, + const Colour& arrowColour) + : Button (name), + colour (arrowColour) +{ + path.lineTo (0.0f, 1.0f); + path.lineTo (1.0f, 0.5f); + path.closeSubPath(); + + path.applyTransform (AffineTransform::rotation (float_Pi * 2.0f * arrowDirectionInRadians, + 0.5f, 0.5f)); + + setComponentEffect (&shadow); + buttonStateChanged(); +} + +ArrowButton::~ArrowButton() +{ +} + +void ArrowButton::paintButton (Graphics& g, + bool /*isMouseOverButton*/, + bool /*isButtonDown*/) +{ + g.setColour (colour); + + g.fillPath (path, path.getTransformToScaleToFit ((float) offset, + (float) offset, + (float) (getWidth() - 3), + (float) (getHeight() - 3), + false)); +} + +void ArrowButton::buttonStateChanged() +{ + offset = (isDown()) ? 1 : 0; + + shadow.setShadowProperties ((isDown()) ? 1.2f : 3.0f, + 0.3f, -1, 0); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ArrowButton.cpp *********/ + +/********* Start of inlined file: juce_Button.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +Button::Button (const String& name) + : Component (name), + shortcuts (2), + keySource (0), + text (name), + buttonListeners (2), + repeatTimer (0), + buttonPressTime (0), + lastTimeCallbackTime (0), + commandManagerToUse (0), + autoRepeatDelay (-1), + autoRepeatSpeed (0), + autoRepeatMinimumDelay (-1), + radioGroupId (0), + commandID (0), + connectedEdgeFlags (0), + buttonState (buttonNormal), + isOn (false), + clickTogglesState (false), + needsToRelease (false), + needsRepainting (false), + isKeyDown (false), + triggerOnMouseDown (false), + generateTooltip (false) +{ + setWantsKeyboardFocus (true); +} + +Button::~Button() +{ + if (commandManagerToUse != 0) + commandManagerToUse->removeListener (this); + + delete repeatTimer; + clearShortcuts(); +} + +void Button::setButtonText (const String& newText) throw() +{ + if (text != newText) + { + text = newText; + repaint(); + } +} + +void Button::setTooltip (const String& newTooltip) +{ + SettableTooltipClient::setTooltip (newTooltip); + generateTooltip = false; +} + +const String Button::getTooltip() +{ + if (generateTooltip && commandManagerToUse != 0 && commandID != 0) + { + String tt (commandManagerToUse->getDescriptionOfCommand (commandID)); + + Array keyPresses (commandManagerToUse->getKeyMappings()->getKeyPressesAssignedToCommand (commandID)); + + for (int i = 0; i < keyPresses.size(); ++i) + { + const String key (keyPresses.getReference(i).getTextDescription()); + + if (key.length() == 1) + tt << " [shortcut: '" << key << "']"; + else + tt << " [" << key << ']'; + } + + return tt; + } + + return SettableTooltipClient::getTooltip(); +} + +void Button::setConnectedEdges (const int connectedEdgeFlags_) throw() +{ + if (connectedEdgeFlags != connectedEdgeFlags_) + { + connectedEdgeFlags = connectedEdgeFlags_; + repaint(); + } +} + +void Button::setToggleState (const bool shouldBeOn, + const bool sendChangeNotification) +{ + if (shouldBeOn != isOn) + { + const ComponentDeletionWatcher deletionWatcher (this); + + isOn = shouldBeOn; + repaint(); + + if (sendChangeNotification) + sendClickMessage (ModifierKeys()); + + if ((! deletionWatcher.hasBeenDeleted()) && isOn) + turnOffOtherButtonsInGroup (sendChangeNotification); + } +} + +void Button::setClickingTogglesState (const bool shouldToggle) throw() +{ + clickTogglesState = shouldToggle; + + // if you've got clickTogglesState turned on, you shouldn't also connect the button + // up to be a command invoker. Instead, your command handler must flip the state of whatever + // it is that this button represents, and the button will update its state to reflect this + // in the applicationCommandListChanged() method. + jassert (commandManagerToUse == 0 || ! clickTogglesState); +} + +bool Button::getClickingTogglesState() const throw() +{ + return clickTogglesState; +} + +void Button::setRadioGroupId (const int newGroupId) +{ + if (radioGroupId != newGroupId) + { + radioGroupId = newGroupId; + + if (isOn) + turnOffOtherButtonsInGroup (true); + } +} + +void Button::turnOffOtherButtonsInGroup (const bool sendChangeNotification) +{ + Component* const p = getParentComponent(); + + if (p != 0 && radioGroupId != 0) + { + const ComponentDeletionWatcher deletionWatcher (this); + + for (int i = p->getNumChildComponents(); --i >= 0;) + { + Component* const c = p->getChildComponent (i); + + if (c != this) + { + Button* const b = dynamic_cast (c); + + if (b != 0 && b->getRadioGroupId() == radioGroupId) + { + b->setToggleState (false, sendChangeNotification); + + if (deletionWatcher.hasBeenDeleted()) + return; + } + } + } + } +} + +void Button::enablementChanged() +{ + updateState (0); + repaint(); +} + +Button::ButtonState Button::updateState (const MouseEvent* const e) throw() +{ + ButtonState state = buttonNormal; + + if (isEnabled() && isVisible() && ! isCurrentlyBlockedByAnotherModalComponent()) + { + int mx, my; + + if (e == 0) + { + getMouseXYRelative (mx, my); + } + else + { + const MouseEvent e2 (e->getEventRelativeTo (this)); + mx = e2.x; + my = e2.y; + } + + const bool over = reallyContains (mx, my, true); + const bool down = isMouseButtonDown(); + + if ((down && (over || (triggerOnMouseDown && buttonState == buttonDown))) || isKeyDown) + state = buttonDown; + else if (over) + state = buttonOver; + } + + setState (state); + return state; +} + +void Button::setState (const ButtonState newState) +{ + if (buttonState != newState) + { + buttonState = newState; + repaint(); + + if (buttonState == buttonDown) + { + buttonPressTime = Time::getApproximateMillisecondCounter(); + lastTimeCallbackTime = buttonPressTime; + } + + sendStateMessage(); + } +} + +bool Button::isDown() const throw() +{ + return buttonState == buttonDown; +} + +bool Button::isOver() const throw() +{ + return buttonState != buttonNormal; +} + +void Button::buttonStateChanged() +{ +} + +uint32 Button::getMillisecondsSinceButtonDown() const throw() +{ + const uint32 now = Time::getApproximateMillisecondCounter(); + return now > buttonPressTime ? now - buttonPressTime : 0; +} + +void Button::setTriggeredOnMouseDown (const bool isTriggeredOnMouseDown) throw() +{ + triggerOnMouseDown = isTriggeredOnMouseDown; +} + +void Button::clicked() +{ +} + +void Button::clicked (const ModifierKeys& /*modifiers*/) +{ + clicked(); +} + +static const int clickMessageId = 0x2f3f4f99; + +void Button::triggerClick() +{ + postCommandMessage (clickMessageId); +} + +void Button::internalClickCallback (const ModifierKeys& modifiers) +{ + if (clickTogglesState) + setToggleState ((radioGroupId != 0) || ! isOn, false); + + sendClickMessage (modifiers); +} + +void Button::flashButtonState() throw() +{ + if (isEnabled()) + { + needsToRelease = true; + setState (buttonDown); + getRepeatTimer().startTimer (100); + } +} + +void Button::handleCommandMessage (int commandId) +{ + if (commandId == clickMessageId) + { + if (isEnabled()) + { + flashButtonState(); + internalClickCallback (ModifierKeys::getCurrentModifiers()); + } + } + else + { + Component::handleCommandMessage (commandId); + } +} + +void Button::addButtonListener (ButtonListener* const newListener) throw() +{ + jassert (newListener != 0); + jassert (! buttonListeners.contains (newListener)); // trying to add a listener to the list twice! + + if (newListener != 0) + buttonListeners.add (newListener); +} + +void Button::removeButtonListener (ButtonListener* const listener) throw() +{ + jassert (buttonListeners.contains (listener)); // trying to remove a listener that isn't on the list! + + buttonListeners.removeValue (listener); +} + +void Button::sendClickMessage (const ModifierKeys& modifiers) +{ + const ComponentDeletionWatcher cdw (this); + + if (commandManagerToUse != 0 && commandID != 0) + { + ApplicationCommandTarget::InvocationInfo info (commandID); + info.invocationMethod = ApplicationCommandTarget::InvocationInfo::fromButton; + info.originatingComponent = this; + + commandManagerToUse->invoke (info, true); + } + + clicked (modifiers); + + if (! cdw.hasBeenDeleted()) + { + for (int i = buttonListeners.size(); --i >= 0;) + { + ButtonListener* const bl = (ButtonListener*) buttonListeners[i]; + + if (bl != 0) + { + bl->buttonClicked (this); + + if (cdw.hasBeenDeleted()) + return; + } + } + } +} + +void Button::sendStateMessage() +{ + const ComponentDeletionWatcher cdw (this); + + buttonStateChanged(); + + if (cdw.hasBeenDeleted()) + return; + + for (int i = buttonListeners.size(); --i >= 0;) + { + ButtonListener* const bl = (ButtonListener*) buttonListeners[i]; + + if (bl != 0) + { + bl->buttonStateChanged (this); + + if (cdw.hasBeenDeleted()) + return; + } + } +} + +void Button::paint (Graphics& g) +{ + if (needsToRelease && isEnabled()) + { + needsToRelease = false; + needsRepainting = true; + } + + paintButton (g, isOver(), isDown()); +} + +void Button::mouseEnter (const MouseEvent& e) +{ + updateState (&e); +} + +void Button::mouseExit (const MouseEvent& e) +{ + updateState (&e); +} + +void Button::mouseDown (const MouseEvent& e) +{ + updateState (&e); + + if (isDown()) + { + if (autoRepeatDelay >= 0) + getRepeatTimer().startTimer (autoRepeatDelay); + + if (triggerOnMouseDown) + internalClickCallback (e.mods); + } +} + +void Button::mouseUp (const MouseEvent& e) +{ + const bool wasDown = isDown(); + updateState (&e); + + if (wasDown && isOver() && ! triggerOnMouseDown) + internalClickCallback (e.mods); +} + +void Button::mouseDrag (const MouseEvent& e) +{ + const ButtonState oldState = buttonState; + updateState (&e); + + if (autoRepeatDelay >= 0 && buttonState != oldState && isDown()) + getRepeatTimer().startTimer (autoRepeatSpeed); +} + +void Button::focusGained (FocusChangeType) +{ + updateState (0); + repaint(); +} + +void Button::focusLost (FocusChangeType) +{ + updateState (0); + repaint(); +} + +void Button::setVisible (bool shouldBeVisible) +{ + if (shouldBeVisible != isVisible()) + { + Component::setVisible (shouldBeVisible); + + if (! shouldBeVisible) + needsToRelease = false; + + updateState (0); + } + else + { + Component::setVisible (shouldBeVisible); + } +} + +void Button::parentHierarchyChanged() +{ + Component* const newKeySource = (shortcuts.size() == 0) ? 0 : getTopLevelComponent(); + + if (newKeySource != keySource) + { + if (keySource->isValidComponent()) + keySource->removeKeyListener (this); + + keySource = newKeySource; + + if (keySource->isValidComponent()) + keySource->addKeyListener (this); + } +} + +void Button::setCommandToTrigger (ApplicationCommandManager* const commandManagerToUse_, + const int commandID_, + const bool generateTooltip_) +{ + commandID = commandID_; + generateTooltip = generateTooltip_; + + if (commandManagerToUse != commandManagerToUse_) + { + if (commandManagerToUse != 0) + commandManagerToUse->removeListener (this); + + commandManagerToUse = commandManagerToUse_; + + if (commandManagerToUse != 0) + commandManagerToUse->addListener (this); + + // if you've got clickTogglesState turned on, you shouldn't also connect the button + // up to be a command invoker. Instead, your command handler must flip the state of whatever + // it is that this button represents, and the button will update its state to reflect this + // in the applicationCommandListChanged() method. + jassert (commandManagerToUse == 0 || ! clickTogglesState); + } + + if (commandManagerToUse != 0) + applicationCommandListChanged(); + else + setEnabled (true); +} + +void Button::applicationCommandInvoked (const ApplicationCommandTarget::InvocationInfo& info) +{ + if (info.commandID == commandID + && (info.commandFlags & ApplicationCommandInfo::dontTriggerVisualFeedback) == 0) + { + flashButtonState(); + } +} + +void Button::applicationCommandListChanged() +{ + if (commandManagerToUse != 0) + { + ApplicationCommandInfo info (0); + + ApplicationCommandTarget* const target = commandManagerToUse->getTargetForCommand (commandID, info); + + setEnabled (target != 0 && (info.flags & ApplicationCommandInfo::isDisabled) == 0); + + if (target != 0) + setToggleState ((info.flags & ApplicationCommandInfo::isTicked) != 0, false); + } +} + +void Button::addShortcut (const KeyPress& key) +{ + if (key.isValid()) + { + jassert (! isRegisteredForShortcut (key)); // already registered! + + shortcuts.add (key); + parentHierarchyChanged(); + } +} + +void Button::clearShortcuts() +{ + shortcuts.clear(); + + parentHierarchyChanged(); +} + +bool Button::isShortcutPressed() const throw() +{ + if (! isCurrentlyBlockedByAnotherModalComponent()) + { + for (int i = shortcuts.size(); --i >= 0;) + if (shortcuts.getReference(i).isCurrentlyDown()) + return true; + } + + return false; +} + +bool Button::isRegisteredForShortcut (const KeyPress& key) const throw() +{ + for (int i = shortcuts.size(); --i >= 0;) + if (key == shortcuts.getReference(i)) + return true; + + return false; +} + +bool Button::keyStateChanged (Component*) +{ + if (! isEnabled()) + return false; + + const bool wasDown = isKeyDown; + isKeyDown = isShortcutPressed(); + + if (autoRepeatDelay >= 0 && (isKeyDown && ! wasDown)) + getRepeatTimer().startTimer (autoRepeatDelay); + + updateState (0); + + if (isEnabled() && wasDown && ! isKeyDown) + internalClickCallback (ModifierKeys::getCurrentModifiers()); + + return isKeyDown || wasDown; +} + +bool Button::keyPressed (const KeyPress&, Component*) +{ + // returning true will avoid forwarding events for keys that we're using as shortcuts + return isShortcutPressed(); +} + +bool Button::keyPressed (const KeyPress& key) +{ + if (isEnabled() && key.isKeyCode (KeyPress::returnKey)) + { + triggerClick(); + return true; + } + + return false; +} + +void Button::setRepeatSpeed (const int initialDelayMillisecs, + const int repeatMillisecs, + const int minimumDelayInMillisecs) throw() +{ + autoRepeatDelay = initialDelayMillisecs; + autoRepeatSpeed = repeatMillisecs; + autoRepeatMinimumDelay = jmin (autoRepeatSpeed, minimumDelayInMillisecs); +} + +void Button::repeatTimerCallback() throw() +{ + if (needsRepainting) + { + getRepeatTimer().stopTimer(); + updateState (0); + needsRepainting = false; + } + else if (autoRepeatSpeed > 0 && (isKeyDown || (updateState (0) == buttonDown))) + { + int repeatSpeed = autoRepeatSpeed; + + if (autoRepeatMinimumDelay >= 0) + { + double timeHeldDown = jmin (1.0, getMillisecondsSinceButtonDown() / 4000.0); + timeHeldDown *= timeHeldDown; + + repeatSpeed = repeatSpeed + (int) (timeHeldDown * (autoRepeatMinimumDelay - repeatSpeed)); + } + + repeatSpeed = jmax (1, repeatSpeed); + + getRepeatTimer().startTimer (repeatSpeed); + + const uint32 now = Time::getApproximateMillisecondCounter(); + const int numTimesToCallback + = (now > lastTimeCallbackTime) ? jmax (1, (now - lastTimeCallbackTime) / repeatSpeed) : 1; + + lastTimeCallbackTime = now; + + const ComponentDeletionWatcher cdw (this); + + for (int i = numTimesToCallback; --i >= 0;) + { + internalClickCallback (ModifierKeys::getCurrentModifiers()); + + if (cdw.hasBeenDeleted() || ! isDown()) + return; + } + } + else if (! needsToRelease) + { + getRepeatTimer().stopTimer(); + } +} + +class InternalButtonRepeatTimer : public Timer +{ +public: + InternalButtonRepeatTimer (Button& owner_) throw() + : owner (owner_) + { + } + + ~InternalButtonRepeatTimer() + { + } + + void timerCallback() + { + owner.repeatTimerCallback(); + } + +private: + Button& owner; + + InternalButtonRepeatTimer (const InternalButtonRepeatTimer&); + const InternalButtonRepeatTimer& operator= (const InternalButtonRepeatTimer&); +}; + +Timer& Button::getRepeatTimer() throw() +{ + if (repeatTimer == 0) + repeatTimer = new InternalButtonRepeatTimer (*this); + + return *repeatTimer; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_Button.cpp *********/ + +/********* Start of inlined file: juce_DrawableButton.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +DrawableButton::DrawableButton (const String& name, + const DrawableButton::ButtonStyle buttonStyle) + : Button (name), + style (buttonStyle), + normalImage (0), + overImage (0), + downImage (0), + disabledImage (0), + normalImageOn (0), + overImageOn (0), + downImageOn (0), + disabledImageOn (0), + edgeIndent (3) +{ + if (buttonStyle == ImageOnButtonBackground) + { + backgroundOff = Colour (0xffbbbbff); + backgroundOn = Colour (0xff3333ff); + } + else + { + backgroundOff = Colours::transparentBlack; + backgroundOn = Colour (0xaabbbbff); + } +} + +DrawableButton::~DrawableButton() +{ + deleteImages(); +} + +void DrawableButton::deleteImages() +{ + deleteAndZero (normalImage); + deleteAndZero (overImage); + deleteAndZero (downImage); + deleteAndZero (disabledImage); + deleteAndZero (normalImageOn); + deleteAndZero (overImageOn); + deleteAndZero (downImageOn); + deleteAndZero (disabledImageOn); +} + +void DrawableButton::setImages (const Drawable* normal, + const Drawable* over, + const Drawable* down, + const Drawable* disabled, + const Drawable* normalOn, + const Drawable* overOn, + const Drawable* downOn, + const Drawable* disabledOn) +{ + deleteImages(); + + jassert (normal != 0); // you really need to give it at least a normal image.. + + if (normal != 0) + normalImage = normal->createCopy(); + + if (over != 0) + overImage = over->createCopy(); + + if (down != 0) + downImage = down->createCopy(); + + if (disabled != 0) + disabledImage = disabled->createCopy(); + + if (normalOn != 0) + normalImageOn = normalOn->createCopy(); + + if (overOn != 0) + overImageOn = overOn->createCopy(); + + if (downOn != 0) + downImageOn = downOn->createCopy(); + + if (disabledOn != 0) + disabledImageOn = disabledOn->createCopy(); + + repaint(); +} + +void DrawableButton::setButtonStyle (const DrawableButton::ButtonStyle newStyle) +{ + if (style != newStyle) + { + style = newStyle; + repaint(); + } +} + +void DrawableButton::setBackgroundColours (const Colour& toggledOffColour, + const Colour& toggledOnColour) +{ + if (backgroundOff != toggledOffColour + || backgroundOn != toggledOnColour) + { + backgroundOff = toggledOffColour; + backgroundOn = toggledOnColour; + + repaint(); + } +} + +const Colour& DrawableButton::getBackgroundColour() const throw() +{ + return getToggleState() ? backgroundOn + : backgroundOff; +} + +void DrawableButton::setEdgeIndent (const int numPixelsIndent) +{ + edgeIndent = numPixelsIndent; + repaint(); +} + +void DrawableButton::paintButton (Graphics& g, + bool isMouseOverButton, + bool isButtonDown) +{ + Rectangle imageSpace; + + if (style == ImageOnButtonBackground) + { + const int insetX = getWidth() / 4; + const int insetY = getHeight() / 4; + + imageSpace.setBounds (insetX, insetY, getWidth() - insetX * 2, getHeight() - insetY * 2); + + getLookAndFeel().drawButtonBackground (g, *this, + getBackgroundColour(), + isMouseOverButton, + isButtonDown); + } + else + { + g.fillAll (getBackgroundColour()); + + const int textH = (style == ImageAboveTextLabel) + ? jmin (16, proportionOfHeight (0.25f)) + : 0; + + const int indentX = jmin (edgeIndent, proportionOfWidth (0.3f)); + const int indentY = jmin (edgeIndent, proportionOfHeight (0.3f)); + + imageSpace.setBounds (indentX, indentY, + getWidth() - indentX * 2, + getHeight() - indentY * 2 - textH); + + if (textH > 0) + { + g.setFont ((float) textH); + + g.setColour (Colours::black.withAlpha (isEnabled() ? 1.0f : 0.4f)); + g.drawFittedText (getName(), + 2, getHeight() - textH - 1, + getWidth() - 4, textH, + Justification::centred, 1); + } + } + + g.setImageResamplingQuality (Graphics::mediumResamplingQuality); + g.setOpacity (1.0f); + + const Drawable* imageToDraw = 0; + + if (isEnabled()) + { + imageToDraw = getCurrentImage(); + } + else + { + imageToDraw = getToggleState() ? disabledImageOn + : disabledImage; + + if (imageToDraw == 0) + { + g.setOpacity (0.4f); + imageToDraw = getNormalImage(); + } + } + + if (imageToDraw != 0) + { + if (style == ImageRaw) + { + imageToDraw->draw (g); + } + else + { + imageToDraw->drawWithin (g, + imageSpace.getX(), + imageSpace.getY(), + imageSpace.getWidth(), + imageSpace.getHeight(), + RectanglePlacement::centred); + } + } +} + +const Drawable* DrawableButton::getCurrentImage() const throw() +{ + if (isDown()) + return getDownImage(); + + if (isOver()) + return getOverImage(); + + return getNormalImage(); +} + +const Drawable* DrawableButton::getNormalImage() const throw() +{ + return (getToggleState() && normalImageOn != 0) ? normalImageOn + : normalImage; +} + +const Drawable* DrawableButton::getOverImage() const throw() +{ + const Drawable* d = normalImage; + + if (getToggleState()) + { + if (overImageOn != 0) + d = overImageOn; + else if (normalImageOn != 0) + d = normalImageOn; + else if (overImage != 0) + d = overImage; + } + else + { + if (overImage != 0) + d = overImage; + } + + return d; +} + +const Drawable* DrawableButton::getDownImage() const throw() +{ + const Drawable* d = normalImage; + + if (getToggleState()) + { + if (downImageOn != 0) + d = downImageOn; + else if (overImageOn != 0) + d = overImageOn; + else if (normalImageOn != 0) + d = normalImageOn; + else if (downImage != 0) + d = downImage; + else + d = getOverImage(); + } + else + { + if (downImage != 0) + d = downImage; + else + d = getOverImage(); + } + + return d; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_DrawableButton.cpp *********/ + +/********* Start of inlined file: juce_HyperlinkButton.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +HyperlinkButton::HyperlinkButton (const String& linkText, + const URL& linkURL) + : Button (linkText), + url (linkURL), + font (14.0f, Font::underlined), + resizeFont (true), + justification (Justification::centred) +{ + setMouseCursor (MouseCursor::PointingHandCursor); + setTooltip (linkURL.toString (false)); +} + +HyperlinkButton::~HyperlinkButton() +{ +} + +void HyperlinkButton::setFont (const Font& newFont, + const bool resizeToMatchComponentHeight, + const Justification& justificationType) +{ + font = newFont; + resizeFont = resizeToMatchComponentHeight; + justification = justificationType; + repaint(); +} + +void HyperlinkButton::setURL (const URL& newURL) throw() +{ + url = newURL; + setTooltip (newURL.toString (false)); +} + +const Font HyperlinkButton::getFontToUse() const +{ + Font f (font); + + if (resizeFont) + f.setHeight (getHeight() * 0.7f); + + return f; +} + +void HyperlinkButton::changeWidthToFitText() +{ + setSize (getFontToUse().getStringWidth (getName()) + 6, getHeight()); +} + +void HyperlinkButton::colourChanged() +{ + repaint(); +} + +void HyperlinkButton::clicked() +{ + if (url.isWellFormed()) + url.launchInDefaultBrowser(); +} + +void HyperlinkButton::paintButton (Graphics& g, + bool isMouseOverButton, + bool isButtonDown) +{ + const Colour textColour (findColour (textColourId)); + + if (isEnabled()) + g.setColour ((isMouseOverButton) ? textColour.darker ((isButtonDown) ? 1.3f : 0.4f) + : textColour); + else + g.setColour (textColour.withMultipliedAlpha (0.4f)); + + g.setFont (getFontToUse()); + + g.drawText (getButtonText(), + 2, 0, getWidth() - 2, getHeight(), + justification.getOnlyHorizontalFlags() | Justification::verticallyCentred, + true); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_HyperlinkButton.cpp *********/ + +/********* Start of inlined file: juce_ImageButton.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +ImageButton::ImageButton (const String& text) + : Button (text), + scaleImageToFit (true), + preserveProportions (true), + alphaThreshold (0), + imageX (0), + imageY (0), + imageW (0), + imageH (0), + normalImage (0), + overImage (0), + downImage (0) +{ +} + +ImageButton::~ImageButton() +{ + deleteImages(); +} + +void ImageButton::deleteImages() +{ + if (normalImage != 0) + { + if (ImageCache::isImageInCache (normalImage)) + ImageCache::release (normalImage); + else + delete normalImage; + } + + if (overImage != 0) + { + if (ImageCache::isImageInCache (overImage)) + ImageCache::release (overImage); + else + delete overImage; + } + + if (downImage != 0) + { + if (ImageCache::isImageInCache (downImage)) + ImageCache::release (downImage); + else + delete downImage; + } +} + +void ImageButton::setImages (const bool resizeButtonNowToFitThisImage, + const bool rescaleImagesWhenButtonSizeChanges, + const bool preserveImageProportions, + Image* const normalImage_, + const float imageOpacityWhenNormal, + const Colour& overlayColourWhenNormal, + Image* const overImage_, + const float imageOpacityWhenOver, + const Colour& overlayColourWhenOver, + Image* const downImage_, + const float imageOpacityWhenDown, + const Colour& overlayColourWhenDown, + const float hitTestAlphaThreshold) +{ + deleteImages(); + + normalImage = normalImage_; + overImage = overImage_; + downImage = downImage_; + + if (resizeButtonNowToFitThisImage && normalImage != 0) + { + imageW = normalImage->getWidth(); + imageH = normalImage->getHeight(); + + setSize (imageW, imageH); + } + + scaleImageToFit = rescaleImagesWhenButtonSizeChanges; + preserveProportions = preserveImageProportions; + + normalOpacity = imageOpacityWhenNormal; + normalOverlay = overlayColourWhenNormal; + overOpacity = imageOpacityWhenOver; + overOverlay = overlayColourWhenOver; + downOpacity = imageOpacityWhenDown; + downOverlay = overlayColourWhenDown; + + alphaThreshold = (unsigned char) jlimit (0, 0xff, roundFloatToInt (255.0f * hitTestAlphaThreshold)); + + repaint(); +} + +Image* ImageButton::getCurrentImage() const +{ + if (isDown()) + return getDownImage(); + + if (isOver()) + return getOverImage(); + + return getNormalImage(); +} + +Image* ImageButton::getNormalImage() const throw() +{ + return normalImage; +} + +Image* ImageButton::getOverImage() const throw() +{ + return (overImage != 0) ? overImage + : normalImage; +} + +Image* ImageButton::getDownImage() const throw() +{ + return (downImage != 0) ? downImage + : getOverImage(); +} + +void ImageButton::paintButton (Graphics& g, + bool isMouseOverButton, + bool isButtonDown) +{ + if (! isEnabled()) + { + isMouseOverButton = false; + isButtonDown = false; + } + + Image* const im = getCurrentImage(); + + if (im != 0) + { + const int iw = im->getWidth(); + const int ih = im->getHeight(); + imageW = getWidth(); + imageH = getHeight(); + imageX = (imageW - iw) >> 1; + imageY = (imageH - ih) >> 1; + + if (scaleImageToFit) + { + if (preserveProportions) + { + int newW, newH; + const float imRatio = ih / (float)iw; + const float destRatio = imageH / (float)imageW; + + if (imRatio > destRatio) + { + newW = roundFloatToInt (imageH / imRatio); + newH = imageH; + } + else + { + newW = imageW; + newH = roundFloatToInt (imageW * imRatio); + } + + imageX = (imageW - newW) / 2; + imageY = (imageH - newH) / 2; + imageW = newW; + imageH = newH; + } + else + { + imageX = 0; + imageY = 0; + } + } + + const Colour& overlayColour = (isButtonDown) ? downOverlay + : ((isMouseOverButton) ? overOverlay + : normalOverlay); + + if (! overlayColour.isOpaque()) + { + g.setOpacity ((isButtonDown) ? downOpacity + : ((isMouseOverButton) ? overOpacity + : normalOpacity)); + + if (scaleImageToFit) + g.drawImage (im, imageX, imageY, imageW, imageH, 0, 0, iw, ih, false); + else + g.drawImageAt (im, imageX, imageY, false); + } + + if (! overlayColour.isTransparent()) + { + g.setColour (overlayColour); + + if (scaleImageToFit) + g.drawImage (im, imageX, imageY, imageW, imageH, 0, 0, iw, ih, true); + else + g.drawImageAt (im, imageX, imageY, true); + } + } +} + +bool ImageButton::hitTest (int x, int y) +{ + if (alphaThreshold == 0) + return true; + + Image* const im = getCurrentImage(); + + return im == 0 + || (imageW > 0 && imageH > 0 + && alphaThreshold < im->getPixelAt (((x - imageX) * im->getWidth()) / imageW, + ((y - imageY) * im->getHeight()) / imageH).getAlpha()); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ImageButton.cpp *********/ + +/********* Start of inlined file: juce_ShapeButton.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +ShapeButton::ShapeButton (const String& text, + const Colour& normalColour_, + const Colour& overColour_, + const Colour& downColour_) + : Button (text), + normalColour (normalColour_), + overColour (overColour_), + downColour (downColour_), + maintainShapeProportions (false), + outlineWidth (0.0f) +{ +} + +ShapeButton::~ShapeButton() +{ +} + +void ShapeButton::setColours (const Colour& newNormalColour, + const Colour& newOverColour, + const Colour& newDownColour) +{ + normalColour = newNormalColour; + overColour = newOverColour; + downColour = newDownColour; +} + +void ShapeButton::setOutline (const Colour& newOutlineColour, + const float newOutlineWidth) +{ + outlineColour = newOutlineColour; + outlineWidth = newOutlineWidth; +} + +void ShapeButton::setShape (const Path& newShape, + const bool resizeNowToFitThisShape, + const bool maintainShapeProportions_, + const bool hasShadow) +{ + shape = newShape; + maintainShapeProportions = maintainShapeProportions_; + + shadow.setShadowProperties (3.0f, 0.5f, 0, 0); + setComponentEffect ((hasShadow) ? &shadow : 0); + + if (resizeNowToFitThisShape) + { + float x, y, w, h; + shape.getBounds (x, y, w, h); + shape.applyTransform (AffineTransform::translation (-x, -y)); + + if (hasShadow) + { + w += 4.0f; + h += 4.0f; + shape.applyTransform (AffineTransform::translation (2.0f, 2.0f)); + } + + setSize (1 + (int) (w + outlineWidth), + 1 + (int) (h + outlineWidth)); + } +} + +void ShapeButton::paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown) +{ + if (! isEnabled()) + { + isMouseOverButton = false; + isButtonDown = false; + } + + g.setColour ((isButtonDown) ? downColour + : (isMouseOverButton) ? overColour + : normalColour); + + int w = getWidth(); + int h = getHeight(); + + if (getComponentEffect() != 0) + { + w -= 4; + h -= 4; + } + + const float offset = (outlineWidth * 0.5f) + (isButtonDown ? 1.5f : 0.0f); + + const AffineTransform trans (shape.getTransformToScaleToFit (offset, offset, + w - offset - outlineWidth, + h - offset - outlineWidth, + maintainShapeProportions)); + g.fillPath (shape, trans); + + if (outlineWidth > 0.0f) + { + g.setColour (outlineColour); + g.strokePath (shape, PathStrokeType (outlineWidth), trans); + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ShapeButton.cpp *********/ + +/********* Start of inlined file: juce_TextButton.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +TextButton::TextButton (const String& name, + const String& toolTip) + : Button (name) +{ + setTooltip (toolTip); +} + +TextButton::~TextButton() +{ +} + +void TextButton::paintButton (Graphics& g, + bool isMouseOverButton, + bool isButtonDown) +{ + getLookAndFeel().drawButtonBackground (g, *this, + findColour (getToggleState() ? buttonOnColourId + : buttonColourId), + isMouseOverButton, + isButtonDown); + + getLookAndFeel().drawButtonText (g, *this, + isMouseOverButton, + isButtonDown); +} + +void TextButton::colourChanged() +{ + repaint(); +} + +const Font TextButton::getFont() +{ + return Font (jmin (15.0f, getHeight() * 0.6f)); +} + +void TextButton::changeWidthToFitText (const int newHeight) +{ + if (newHeight >= 0) + setSize (jmax (1, getWidth()), newHeight); + + setSize (getFont().getStringWidth (getButtonText()) + getHeight(), + getHeight()); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_TextButton.cpp *********/ + +/********* Start of inlined file: juce_ToggleButton.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +ToggleButton::ToggleButton (const String& buttonText) + : Button (buttonText) +{ + setClickingTogglesState (true); +} + +ToggleButton::~ToggleButton() +{ +} + +void ToggleButton::paintButton (Graphics& g, + bool isMouseOverButton, + bool isButtonDown) +{ + getLookAndFeel().drawToggleButton (g, *this, + isMouseOverButton, + isButtonDown); +} + +void ToggleButton::changeWidthToFitText() +{ + getLookAndFeel().changeToggleButtonWidthToFitText (*this); +} + +void ToggleButton::colourChanged() +{ + repaint(); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ToggleButton.cpp *********/ + +/********* Start of inlined file: juce_ToolbarButton.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +ToolbarButton::ToolbarButton (const int itemId, + const String& buttonText, + Drawable* const normalImage_, + Drawable* const toggledOnImage_) + : ToolbarItemComponent (itemId, buttonText, true), + normalImage (normalImage_), + toggledOnImage (toggledOnImage_) +{ +} + +ToolbarButton::~ToolbarButton() +{ + delete normalImage; + delete toggledOnImage; +} + +bool ToolbarButton::getToolbarItemSizes (int toolbarDepth, + bool /*isToolbarVertical*/, + int& preferredSize, + int& minSize, int& maxSize) +{ + preferredSize = minSize = maxSize = toolbarDepth; + return true; +} + +void ToolbarButton::paintButtonArea (Graphics& g, + int width, int height, + bool /*isMouseOver*/, + bool /*isMouseDown*/) +{ + Drawable* d = normalImage; + + if (getToggleState() && toggledOnImage != 0) + d = toggledOnImage; + + if (! isEnabled()) + { + Image im (Image::ARGB, width, height, true); + Graphics g2 (im); + d->drawWithin (g2, 0, 0, width, height, RectanglePlacement::centred); + im.desaturate(); + + g.drawImageAt (&im, 0, 0); + } + else + { + d->drawWithin (g, 0, 0, width, height, RectanglePlacement::centred); + } +} + +void ToolbarButton::contentAreaChanged (const Rectangle&) +{ +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ToolbarButton.cpp *********/ + +/********* Start of inlined file: juce_ComboBox.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +ComboBox::ComboBox (const String& name) + : Component (name), + items (4), + currentIndex (-1), + isButtonDown (false), + separatorPending (false), + menuActive (false), + listeners (2), + label (0) +{ + noChoicesMessage = TRANS("(no choices)"); + setRepaintsOnMouseActivity (true); + + lookAndFeelChanged(); +} + +ComboBox::~ComboBox() +{ + if (menuActive) + PopupMenu::dismissAllActiveMenus(); + + deleteAllChildren(); +} + +void ComboBox::setEditableText (const bool isEditable) +{ + label->setEditable (isEditable, isEditable, false); + + setWantsKeyboardFocus (! isEditable); + resized(); +} + +bool ComboBox::isTextEditable() const throw() +{ + return label->isEditable(); +} + +void ComboBox::setJustificationType (const Justification& justification) throw() +{ + label->setJustificationType (justification); +} + +const Justification ComboBox::getJustificationType() const throw() +{ + return label->getJustificationType(); +} + +void ComboBox::setTooltip (const String& newTooltip) +{ + SettableTooltipClient::setTooltip (newTooltip); + label->setTooltip (newTooltip); +} + +void ComboBox::addItem (const String& newItemText, + const int newItemId) throw() +{ + // you can't add empty strings to the list.. + jassert (newItemText.isNotEmpty()); + + // IDs must be non-zero, as zero is used to indicate a lack of selecion. + jassert (newItemId != 0); + + // you shouldn't use duplicate item IDs! + jassert (getItemForId (newItemId) == 0); + + if (newItemText.isNotEmpty() && newItemId != 0) + { + if (separatorPending) + { + separatorPending = false; + + ItemInfo* const item = new ItemInfo(); + item->itemId = 0; + item->isEnabled = false; + item->isHeading = false; + items.add (item); + } + + ItemInfo* const item = new ItemInfo(); + item->name = newItemText; + item->itemId = newItemId; + item->isEnabled = true; + item->isHeading = false; + items.add (item); + } +} + +void ComboBox::addSeparator() throw() +{ + separatorPending = (items.size() > 0); +} + +void ComboBox::addSectionHeading (const String& headingName) throw() +{ + // you can't add empty strings to the list.. + jassert (headingName.isNotEmpty()); + + if (headingName.isNotEmpty()) + { + if (separatorPending) + { + separatorPending = false; + + ItemInfo* const item = new ItemInfo(); + item->itemId = 0; + item->isEnabled = false; + item->isHeading = false; + items.add (item); + } + + ItemInfo* const item = new ItemInfo(); + item->name = headingName; + item->itemId = 0; + item->isEnabled = true; + item->isHeading = true; + items.add (item); + } +} + +void ComboBox::setItemEnabled (const int itemId, + const bool isEnabled) throw() +{ + ItemInfo* const item = getItemForId (itemId); + + if (item != 0) + item->isEnabled = isEnabled; +} + +void ComboBox::changeItemText (const int itemId, + const String& newText) throw() +{ + ItemInfo* const item = getItemForId (itemId); + + jassert (item != 0); + + if (item != 0) + item->name = newText; +} + +void ComboBox::clear (const bool dontSendChangeMessage) +{ + items.clear(); + separatorPending = false; + + if (! label->isEditable()) + setSelectedItemIndex (-1, dontSendChangeMessage); +} + +ComboBox::ItemInfo* ComboBox::getItemForId (const int itemId) const throw() +{ + jassert (itemId != 0); + + if (itemId != 0) + { + for (int i = items.size(); --i >= 0;) + if (items.getUnchecked(i)->itemId == itemId) + return items.getUnchecked(i); + } + + return 0; +} + +ComboBox::ItemInfo* ComboBox::getItemForIndex (const int index) const throw() +{ + int n = 0; + + for (int i = 0; i < items.size(); ++i) + { + ItemInfo* const item = items.getUnchecked(i); + + if (item->isRealItem()) + { + if (n++ == index) + return item; + } + } + + return 0; +} + +int ComboBox::getNumItems() const throw() +{ + int n = 0; + + for (int i = items.size(); --i >= 0;) + { + ItemInfo* const item = items.getUnchecked(i); + + if (item->isRealItem()) + ++n; + } + + return n; +} + +const String ComboBox::getItemText (const int index) const throw() +{ + ItemInfo* const item = getItemForIndex (index); + + if (item != 0) + return item->name; + + return String::empty; +} + +int ComboBox::getItemId (const int index) const throw() +{ + ItemInfo* const item = getItemForIndex (index); + + return (item != 0) ? item->itemId : 0; +} + +bool ComboBox::ItemInfo::isSeparator() const throw() +{ + return name.isEmpty(); +} + +bool ComboBox::ItemInfo::isRealItem() const throw() +{ + return ! (isHeading || name.isEmpty()); +} + +int ComboBox::getSelectedItemIndex() const throw() +{ + return (currentIndex >= 0 && getText() == getItemText (currentIndex)) + ? currentIndex + : -1; +} + +void ComboBox::setSelectedItemIndex (const int index, + const bool dontSendChangeMessage) throw() +{ + if (currentIndex != index || label->getText() != getItemText (currentIndex)) + { + if (((unsigned int) index) < (unsigned int) getNumItems()) + currentIndex = index; + else + currentIndex = -1; + + label->setText (getItemText (currentIndex), false); + + if (! dontSendChangeMessage) + triggerAsyncUpdate(); + } +} + +void ComboBox::setSelectedId (const int newItemId, + const bool dontSendChangeMessage) throw() +{ + for (int i = getNumItems(); --i >= 0;) + { + if (getItemId(i) == newItemId) + { + setSelectedItemIndex (i, dontSendChangeMessage); + break; + } + } +} + +int ComboBox::getSelectedId() const throw() +{ + const ItemInfo* const item = getItemForIndex (currentIndex); + + return (item != 0 && getText() == item->name) + ? item->itemId + : 0; +} + +void ComboBox::addListener (ComboBoxListener* const listener) throw() +{ + jassert (listener != 0); + if (listener != 0) + listeners.add (listener); +} + +void ComboBox::removeListener (ComboBoxListener* const listener) throw() +{ + listeners.removeValue (listener); +} + +void ComboBox::handleAsyncUpdate() +{ + for (int i = listeners.size(); --i >= 0;) + { + ((ComboBoxListener*) listeners.getUnchecked (i))->comboBoxChanged (this); + i = jmin (i, listeners.size()); + } +} + +const String ComboBox::getText() const throw() +{ + return label->getText(); +} + +void ComboBox::setText (const String& newText, + const bool dontSendChangeMessage) throw() +{ + for (int i = items.size(); --i >= 0;) + { + ItemInfo* const item = items.getUnchecked(i); + + if (item->isRealItem() + && item->name == newText) + { + setSelectedId (item->itemId, dontSendChangeMessage); + return; + } + } + + currentIndex = -1; + + if (label->getText() != newText) + { + label->setText (newText, false); + + if (! dontSendChangeMessage) + triggerAsyncUpdate(); + } + + repaint(); +} + +void ComboBox::showEditor() +{ + jassert (isTextEditable()); // you probably shouldn't do this to a non-editable combo box? + + label->showEditor(); +} + +void ComboBox::setTextWhenNothingSelected (const String& newMessage) throw() +{ + textWhenNothingSelected = newMessage; + repaint(); +} + +const String ComboBox::getTextWhenNothingSelected() const throw() +{ + return textWhenNothingSelected; +} + +void ComboBox::setTextWhenNoChoicesAvailable (const String& newMessage) throw() +{ + noChoicesMessage = newMessage; +} + +const String ComboBox::getTextWhenNoChoicesAvailable() const throw() +{ + return noChoicesMessage; +} + +void ComboBox::paint (Graphics& g) +{ + getLookAndFeel().drawComboBox (g, + getWidth(), + getHeight(), + isButtonDown, + label->getRight(), + 0, + getWidth() - label->getRight(), + getHeight(), + *this); + + if (textWhenNothingSelected.isNotEmpty() + && label->getText().isEmpty() + && ! label->isBeingEdited()) + { + g.setColour (findColour (textColourId).withMultipliedAlpha (0.5f)); + g.setFont (label->getFont()); + g.drawFittedText (textWhenNothingSelected, + label->getX() + 2, label->getY() + 1, + label->getWidth() - 4, label->getHeight() - 2, + label->getJustificationType(), + jmax (1, (int) (label->getHeight() / label->getFont().getHeight()))); + } +} + +void ComboBox::resized() +{ + if (getHeight() > 0 && getWidth() > 0) + { + label->setBounds (1, 1, + getWidth() + 3 - getHeight(), + getHeight() - 2); + + label->setFont (getLookAndFeel().getComboBoxFont (*this)); + } +} + +void ComboBox::enablementChanged() +{ + repaint(); +} + +void ComboBox::lookAndFeelChanged() +{ + repaint(); + + Label* const newLabel = getLookAndFeel().createComboBoxTextBox (*this); + + if (label != 0) + { + newLabel->setEditable (label->isEditable()); + newLabel->setJustificationType (label->getJustificationType()); + newLabel->setTooltip (label->getTooltip()); + newLabel->setText (label->getText(), false); + } + + delete label; + label = newLabel; + + addAndMakeVisible (newLabel); + + newLabel->addListener (this); + newLabel->addMouseListener (this, false); + + newLabel->setColour (Label::backgroundColourId, Colours::transparentBlack); + newLabel->setColour (Label::textColourId, findColour (ComboBox::textColourId)); + + newLabel->setColour (TextEditor::textColourId, findColour (ComboBox::textColourId)); + newLabel->setColour (TextEditor::backgroundColourId, Colours::transparentBlack); + newLabel->setColour (TextEditor::highlightColourId, findColour (TextEditor::highlightColourId)); + newLabel->setColour (TextEditor::outlineColourId, Colours::transparentBlack); + + resized(); +} + +void ComboBox::colourChanged() +{ + lookAndFeelChanged(); +} + +bool ComboBox::keyPressed (const KeyPress& key) +{ + bool used = false; + + if (key.isKeyCode (KeyPress::upKey) + || key.isKeyCode (KeyPress::leftKey)) + { + setSelectedItemIndex (jmax (0, currentIndex - 1)); + used = true; + } + else if (key.isKeyCode (KeyPress::downKey) + || key.isKeyCode (KeyPress::rightKey)) + { + setSelectedItemIndex (jmin (currentIndex + 1, getNumItems() - 1)); + used = true; + } + else if (key.isKeyCode (KeyPress::returnKey)) + { + showPopup(); + used = true; + } + + return used; +} + +bool ComboBox::keyStateChanged() +{ + // only forward key events that aren't used by this component + return KeyPress::isKeyCurrentlyDown (KeyPress::upKey) + || KeyPress::isKeyCurrentlyDown (KeyPress::leftKey) + || KeyPress::isKeyCurrentlyDown (KeyPress::downKey) + || KeyPress::isKeyCurrentlyDown (KeyPress::rightKey); +} + +void ComboBox::focusGained (FocusChangeType) +{ + repaint(); +} + +void ComboBox::focusLost (FocusChangeType) +{ + repaint(); +} + +void ComboBox::labelTextChanged (Label*) +{ + triggerAsyncUpdate(); +} + +void ComboBox::showPopup() +{ + if (! menuActive) + { + const int currentId = getSelectedId(); + ComponentDeletionWatcher deletionWatcher (this); + + PopupMenu menu; + + menu.setLookAndFeel (&getLookAndFeel()); + + for (int i = 0; i < items.size(); ++i) + { + const ItemInfo* const item = items.getUnchecked(i); + + if (item->isSeparator()) + menu.addSeparator(); + else if (item->isHeading) + menu.addSectionHeader (item->name); + else + menu.addItem (item->itemId, item->name, + item->isEnabled, item->itemId == currentId); + } + + if (items.size() == 0) + menu.addItem (1, noChoicesMessage, false); + + const int itemHeight = jlimit (12, 24, getHeight()); + + menuActive = true; + const int resultId = menu.showAt (this, currentId, + getWidth(), 1, itemHeight); + + if (deletionWatcher.hasBeenDeleted()) + return; + + menuActive = false; + + if (resultId != 0) + setSelectedId (resultId); + } +} + +void ComboBox::mouseDown (const MouseEvent& e) +{ + beginDragAutoRepeat (300); + + isButtonDown = isEnabled(); + + if (isButtonDown + && (e.eventComponent == this || ! label->isEditable())) + { + showPopup(); + } +} + +void ComboBox::mouseDrag (const MouseEvent& e) +{ + beginDragAutoRepeat (50); + + if (isButtonDown && ! e.mouseWasClicked()) + showPopup(); +} + +void ComboBox::mouseUp (const MouseEvent& e2) +{ + if (isButtonDown) + { + isButtonDown = false; + repaint(); + + const MouseEvent e (e2.getEventRelativeTo (this)); + + if (reallyContains (e.x, e.y, true) + && (e2.eventComponent == this || ! label->isEditable())) + { + showPopup(); + } + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ComboBox.cpp *********/ + +/********* Start of inlined file: juce_Label.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +Label::Label (const String& componentName, + const String& labelText) + : Component (componentName), + text (labelText), + font (15.0f), + justification (Justification::centredLeft), + editor (0), + listeners (2), + ownerComponent (0), + deletionWatcher (0), + editSingleClick (false), + editDoubleClick (false), + lossOfFocusDiscardsChanges (false) +{ + setColour (TextEditor::textColourId, Colours::black); + setColour (TextEditor::backgroundColourId, Colours::transparentBlack); + setColour (TextEditor::outlineColourId, Colours::transparentBlack); +} + +Label::~Label() +{ + if (ownerComponent != 0 && ! deletionWatcher->hasBeenDeleted()) + ownerComponent->removeComponentListener (this); + + deleteAndZero (deletionWatcher); + + if (editor != 0) + delete editor; +} + +void Label::setText (const String& newText, + const bool broadcastChangeMessage) +{ + hideEditor (true); + + if (text != newText) + { + text = newText; + + if (broadcastChangeMessage) + triggerAsyncUpdate(); + + repaint(); + + if (ownerComponent != 0 && ! deletionWatcher->hasBeenDeleted()) + componentMovedOrResized (*ownerComponent, true, true); + } +} + +const String Label::getText (const bool returnActiveEditorContents) const throw() +{ + return (returnActiveEditorContents && isBeingEdited()) + ? editor->getText() + : text; +} + +void Label::setFont (const Font& newFont) throw() +{ + font = newFont; + repaint(); +} + +const Font& Label::getFont() const throw() +{ + return font; +} + +void Label::setEditable (const bool editOnSingleClick, + const bool editOnDoubleClick, + const bool lossOfFocusDiscardsChanges_) throw() +{ + editSingleClick = editOnSingleClick; + editDoubleClick = editOnDoubleClick; + lossOfFocusDiscardsChanges = lossOfFocusDiscardsChanges_; + + setWantsKeyboardFocus (editOnSingleClick || editOnDoubleClick); + setFocusContainer (editOnSingleClick || editOnDoubleClick); +} + +void Label::setJustificationType (const Justification& justification_) throw() +{ + justification = justification_; + repaint(); +} + +void Label::attachToComponent (Component* owner, + const bool onLeft) +{ + if (ownerComponent != 0 && ! deletionWatcher->hasBeenDeleted()) + ownerComponent->removeComponentListener (this); + + deleteAndZero (deletionWatcher); + ownerComponent = owner; + + leftOfOwnerComp = onLeft; + + if (ownerComponent != 0) + { + deletionWatcher = new ComponentDeletionWatcher (owner); + + setVisible (owner->isVisible()); + ownerComponent->addComponentListener (this); + componentParentHierarchyChanged (*ownerComponent); + componentMovedOrResized (*ownerComponent, true, true); + } +} + +void Label::componentMovedOrResized (Component& component, + bool /*wasMoved*/, + bool /*wasResized*/) +{ + if (leftOfOwnerComp) + { + setSize (jmin (getFont().getStringWidth (text) + 8, component.getX()), + component.getHeight()); + + setTopRightPosition (component.getX(), component.getY()); + } + else + { + setSize (component.getWidth(), + 8 + roundFloatToInt (getFont().getHeight())); + + setTopLeftPosition (component.getX(), component.getY() - getHeight()); + } +} + +void Label::componentParentHierarchyChanged (Component& component) +{ + if (component.getParentComponent() != 0) + component.getParentComponent()->addChildComponent (this); +} + +void Label::componentVisibilityChanged (Component& component) +{ + setVisible (component.isVisible()); +} + +void Label::textWasEdited() +{ +} + +void Label::showEditor() +{ + if (editor == 0) + { + addAndMakeVisible (editor = createEditorComponent()); + editor->setText (getText()); + editor->addListener (this); + editor->grabKeyboardFocus(); + editor->setHighlightedRegion (0, text.length()); + editor->addListener (this); + + resized(); + repaint(); + + enterModalState(); + editor->grabKeyboardFocus(); + } +} + +bool Label::updateFromTextEditorContents() +{ + jassert (editor != 0); + const String newText (editor->getText()); + + if (text != newText) + { + text = newText; + + triggerAsyncUpdate(); + repaint(); + + if (ownerComponent != 0 && ! deletionWatcher->hasBeenDeleted()) + componentMovedOrResized (*ownerComponent, true, true); + + return true; + } + + return false; +} + +void Label::hideEditor (const bool discardCurrentEditorContents) +{ + if (editor != 0) + { + const bool changed = (! discardCurrentEditorContents) + && updateFromTextEditorContents(); + + deleteAndZero (editor); + repaint(); + + if (changed) + textWasEdited(); + + exitModalState (0); + } +} + +void Label::inputAttemptWhenModal() +{ + if (editor != 0) + { + if (lossOfFocusDiscardsChanges) + textEditorEscapeKeyPressed (*editor); + else + textEditorReturnKeyPressed (*editor); + } +} + +bool Label::isBeingEdited() const throw() +{ + return editor != 0; +} + +TextEditor* Label::createEditorComponent() +{ + TextEditor* const ed = new TextEditor (getName()); + ed->setFont (font); + + // copy these colours from our own settings.. + const int cols[] = { TextEditor::backgroundColourId, + TextEditor::textColourId, + TextEditor::highlightColourId, + TextEditor::highlightedTextColourId, + TextEditor::caretColourId, + TextEditor::outlineColourId, + TextEditor::focusedOutlineColourId, + TextEditor::shadowColourId }; + + for (int i = 0; i < numElementsInArray (cols); ++i) + ed->setColour (cols[i], findColour (cols[i])); + + return ed; +} + +void Label::paint (Graphics& g) +{ + g.fillAll (findColour (backgroundColourId)); + + if (editor == 0) + { + const float alpha = isEnabled() ? 1.0f : 0.5f; + + g.setColour (findColour (textColourId).withMultipliedAlpha (alpha)); + g.setFont (font); + g.drawFittedText (text, + 3, 1, getWidth() - 6, getHeight() - 2, + justification, + jmax (1, (int) (getHeight() / font.getHeight()))); + + g.setColour (findColour (outlineColourId).withMultipliedAlpha (alpha)); + g.drawRect (0, 0, getWidth(), getHeight()); + } + else if (isEnabled()) + { + g.setColour (editor->findColour (TextEditor::backgroundColourId) + .overlaidWith (findColour (outlineColourId))); + + g.drawRect (0, 0, getWidth(), getHeight()); + } +} + +void Label::mouseUp (const MouseEvent& e) +{ + if (editSingleClick + && e.mouseWasClicked() + && contains (e.x, e.y) + && ! e.mods.isPopupMenu()) + { + showEditor(); + } +} + +void Label::mouseDoubleClick (const MouseEvent& e) +{ + if (editDoubleClick && ! e.mods.isPopupMenu()) + showEditor(); +} + +void Label::resized() +{ + if (editor != 0) + editor->setBoundsInset (BorderSize (0)); +} + +void Label::focusGained (FocusChangeType cause) +{ + if (editSingleClick && cause == focusChangedByTabKey) + showEditor(); +} + +void Label::enablementChanged() +{ + repaint(); +} + +void Label::colourChanged() +{ + repaint(); +} + +// We'll use a custom focus traverser here to make sure focus goes from the +// text editor to another component rather than back to the label itself. +class LabelKeyboardFocusTraverser : public KeyboardFocusTraverser +{ +public: + LabelKeyboardFocusTraverser() {} + + Component* getNextComponent (Component* current) + { + return KeyboardFocusTraverser::getNextComponent (dynamic_cast (current) != 0 + ? current->getParentComponent() : current); + } + + Component* getPreviousComponent (Component* current) + { + return KeyboardFocusTraverser::getPreviousComponent (dynamic_cast (current) != 0 + ? current->getParentComponent() : current); + } +}; + +KeyboardFocusTraverser* Label::createFocusTraverser() +{ + return new LabelKeyboardFocusTraverser(); +} + +void Label::addListener (LabelListener* const listener) throw() +{ + jassert (listener != 0); + if (listener != 0) + listeners.add (listener); +} + +void Label::removeListener (LabelListener* const listener) throw() +{ + listeners.removeValue (listener); +} + +void Label::handleAsyncUpdate() +{ + for (int i = listeners.size(); --i >= 0;) + { + ((LabelListener*) listeners.getUnchecked (i))->labelTextChanged (this); + i = jmin (i, listeners.size()); + } +} + +void Label::textEditorTextChanged (TextEditor& ed) +{ + if (editor != 0) + { + jassert (&ed == editor); + + if (! (hasKeyboardFocus (true) || isCurrentlyBlockedByAnotherModalComponent())) + { + if (lossOfFocusDiscardsChanges) + textEditorEscapeKeyPressed (ed); + else + textEditorReturnKeyPressed (ed); + } + } +} + +void Label::textEditorReturnKeyPressed (TextEditor& ed) +{ + if (editor != 0) + { + jassert (&ed == editor); + (void) ed; + + const bool changed = updateFromTextEditorContents(); + hideEditor (true); + + if (changed) + textWasEdited(); + } +} + +void Label::textEditorEscapeKeyPressed (TextEditor& ed) +{ + if (editor != 0) + { + jassert (&ed == editor); + (void) ed; + + editor->setText (text, false); + hideEditor (true); + } +} + +void Label::textEditorFocusLost (TextEditor& ed) +{ + textEditorTextChanged (ed); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_Label.cpp *********/ + +/********* Start of inlined file: juce_ListBox.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +class ListBoxRowComponent : public Component +{ +public: + ListBoxRowComponent (ListBox& owner_) + : owner (owner_), + row (-1), + selected (false), + isDragging (false) + { + } + + ~ListBoxRowComponent() + { + deleteAllChildren(); + } + + void paint (Graphics& g) + { + if (owner.getModel() != 0) + owner.getModel()->paintListBoxItem (row, g, getWidth(), getHeight(), selected); + } + + void update (const int row_, const bool selected_) + { + if (row != row_ || selected != selected_) + { + repaint(); + row = row_; + selected = selected_; + } + + if (owner.getModel() != 0) + { + Component* const customComp = owner.getModel()->refreshComponentForRow (row_, selected_, getChildComponent (0)); + + if (customComp != 0) + { + addAndMakeVisible (customComp); + customComp->setBounds (0, 0, getWidth(), getHeight()); + + for (int i = getNumChildComponents(); --i >= 0;) + if (getChildComponent (i) != customComp) + delete getChildComponent (i); + } + else + { + deleteAllChildren(); + } + } + } + + void mouseDown (const MouseEvent& e) + { + isDragging = false; + selectRowOnMouseUp = false; + + if (isEnabled()) + { + if (! selected) + { + owner.selectRowsBasedOnModifierKeys (row, e.mods); + + if (owner.getModel() != 0) + owner.getModel()->listBoxItemClicked (row, e); + } + else + { + selectRowOnMouseUp = true; + } + } + } + + void mouseUp (const MouseEvent& e) + { + if (isEnabled() && selectRowOnMouseUp && ! isDragging) + { + owner.selectRowsBasedOnModifierKeys (row, e.mods); + + if (owner.getModel() != 0) + owner.getModel()->listBoxItemClicked (row, e); + } + } + + void mouseDoubleClick (const MouseEvent& e) + { + if (owner.getModel() != 0 && isEnabled()) + owner.getModel()->listBoxItemDoubleClicked (row, e); + } + + void mouseDrag (const MouseEvent& e) + { + if (isEnabled() && owner.getModel() != 0 && ! (e.mouseWasClicked() || isDragging)) + { + const SparseSet selectedRows (owner.getSelectedRows()); + + if (selectedRows.size() > 0) + { + const String dragDescription (owner.getModel()->getDragSourceDescription (selectedRows)); + + if (dragDescription.isNotEmpty()) + { + isDragging = true; + + DragAndDropContainer* const dragContainer + = DragAndDropContainer::findParentDragContainerFor (this); + + if (dragContainer != 0) + { + Image* dragImage = owner.createSnapshotOfSelectedRows(); + dragImage->multiplyAllAlphas (0.6f); + + dragContainer->startDragging (dragDescription, &owner, dragImage, true); + } + else + { + // to be able to do a drag-and-drop operation, the listbox needs to + // be inside a component which is also a DragAndDropContainer. + jassertfalse + } + } + } + } + } + + void resized() + { + if (getNumChildComponents() > 0) + getChildComponent(0)->setBounds (0, 0, getWidth(), getHeight()); + } + + juce_UseDebuggingNewOperator + + bool neededFlag; + +private: + ListBox& owner; + int row; + bool selected, isDragging, selectRowOnMouseUp; + + ListBoxRowComponent (const ListBoxRowComponent&); + const ListBoxRowComponent& operator= (const ListBoxRowComponent&); +}; + +class ListViewport : public Viewport +{ +public: + int firstIndex, firstWholeIndex, lastWholeIndex; + bool hasUpdated; + + ListViewport (ListBox& owner_) + : owner (owner_) + { + setWantsKeyboardFocus (false); + + setViewedComponent (new Component()); + getViewedComponent()->addMouseListener (this, false); + getViewedComponent()->setWantsKeyboardFocus (false); + } + + ~ListViewport() + { + getViewedComponent()->removeMouseListener (this); + getViewedComponent()->deleteAllChildren(); + } + + ListBoxRowComponent* getComponentForRow (const int row) const throw() + { + return (ListBoxRowComponent*) getViewedComponent() + ->getChildComponent (row % jmax (1, getViewedComponent()->getNumChildComponents())); + } + + int getRowNumberOfComponent (Component* const rowComponent) const throw() + { + const int index = getIndexOfChildComponent (rowComponent); + const int num = getViewedComponent()->getNumChildComponents(); + + for (int i = num; --i >= 0;) + if (((firstIndex + i) % jmax (1, num)) == index) + return firstIndex + i; + + return -1; + } + + Component* getComponentForRowIfOnscreen (const int row) const throw() + { + return (row >= firstIndex && row < firstIndex + getViewedComponent()->getNumChildComponents()) + ? getComponentForRow (row) : 0; + } + + void visibleAreaChanged (int, int, int, int) + { + updateVisibleArea (true); + + if (owner.getModel() != 0) + owner.getModel()->listWasScrolled(); + } + + void updateVisibleArea (const bool makeSureItUpdatesContent) + { + hasUpdated = false; + + const int newX = getViewedComponent()->getX(); + int newY = getViewedComponent()->getY(); + const int newW = jmax (owner.minimumRowWidth, getMaximumVisibleWidth()); + const int newH = owner.totalItems * owner.getRowHeight(); + + if (newY + newH < getMaximumVisibleHeight() && newH > getMaximumVisibleHeight()) + newY = getMaximumVisibleHeight() - newH; + + getViewedComponent()->setBounds (newX, newY, newW, newH); + + if (makeSureItUpdatesContent && ! hasUpdated) + updateContents(); + } + + void updateContents() + { + hasUpdated = true; + const int rowHeight = owner.getRowHeight(); + + if (rowHeight > 0) + { + const int y = getViewPositionY(); + const int w = getViewedComponent()->getWidth(); + + const int numNeeded = 2 + getMaximumVisibleHeight() / rowHeight; + + while (numNeeded > getViewedComponent()->getNumChildComponents()) + getViewedComponent()->addAndMakeVisible (new ListBoxRowComponent (owner)); + + jassert (numNeeded >= 0); + + while (numNeeded < getViewedComponent()->getNumChildComponents()) + { + Component* const rowToRemove + = getViewedComponent()->getChildComponent (getViewedComponent()->getNumChildComponents() - 1); + + delete rowToRemove; + } + + firstIndex = y / rowHeight; + firstWholeIndex = (y + rowHeight - 1) / rowHeight; + lastWholeIndex = (y + getMaximumVisibleHeight() - 1) / rowHeight; + + for (int i = 0; i < numNeeded; ++i) + { + const int row = i + firstIndex; + ListBoxRowComponent* const rowComp = getComponentForRow (row); + + if (rowComp != 0) + { + rowComp->setBounds (0, row * rowHeight, w, rowHeight); + rowComp->update (row, owner.isRowSelected (row)); + } + } + } + + if (owner.headerComponent != 0) + owner.headerComponent->setBounds (owner.outlineThickness + getViewedComponent()->getX(), + owner.outlineThickness, + jmax (owner.getWidth() - owner.outlineThickness * 2, + getViewedComponent()->getWidth()), + owner.headerComponent->getHeight()); + } + + void paint (Graphics& g) + { + if (isOpaque()) + g.fillAll (owner.findColour (ListBox::backgroundColourId)); + } + + bool keyPressed (const KeyPress& key) + { + if (key.isKeyCode (KeyPress::upKey) + || key.isKeyCode (KeyPress::downKey) + || key.isKeyCode (KeyPress::pageUpKey) + || key.isKeyCode (KeyPress::pageDownKey) + || key.isKeyCode (KeyPress::homeKey) + || key.isKeyCode (KeyPress::endKey)) + { + // we want to avoid these keypresses going to the viewport, and instead allow + // them to pass up to our listbox.. + return false; + } + + return Viewport::keyPressed (key); + } + + juce_UseDebuggingNewOperator + +private: + ListBox& owner; + + ListViewport (const ListViewport&); + const ListViewport& operator= (const ListViewport&); +}; + +ListBox::ListBox (const String& name, ListBoxModel* const model_) + : Component (name), + model (model_), + headerComponent (0), + totalItems (0), + rowHeight (22), + minimumRowWidth (0), + outlineThickness (0), + lastRowSelected (-1), + mouseMoveSelects (false), + multipleSelection (false), + hasDoneInitialUpdate (false) +{ + addAndMakeVisible (viewport = new ListViewport (*this)); + + setWantsKeyboardFocus (true); +} + +ListBox::~ListBox() +{ + deleteAllChildren(); +} + +void ListBox::setModel (ListBoxModel* const newModel) +{ + if (model != newModel) + { + model = newModel; + updateContent(); + } +} + +void ListBox::setMultipleSelectionEnabled (bool b) +{ + multipleSelection = b; +} + +void ListBox::setMouseMoveSelectsRows (bool b) +{ + mouseMoveSelects = b; + + if (b) + addMouseListener (this, true); +} + +void ListBox::paint (Graphics& g) +{ + if (! hasDoneInitialUpdate) + updateContent(); + + g.fillAll (findColour (backgroundColourId)); +} + +void ListBox::paintOverChildren (Graphics& g) +{ + if (outlineThickness > 0) + { + g.setColour (findColour (outlineColourId)); + g.drawRect (0, 0, getWidth(), getHeight(), outlineThickness); + } +} + +void ListBox::resized() +{ + viewport->setBoundsInset (BorderSize (outlineThickness + ((headerComponent != 0) ? headerComponent->getHeight() : 0), + outlineThickness, + outlineThickness, + outlineThickness)); + + viewport->setSingleStepSizes (20, getRowHeight()); + + viewport->updateVisibleArea (false); +} + +void ListBox::visibilityChanged() +{ + viewport->updateVisibleArea (true); +} + +Viewport* ListBox::getViewport() const throw() +{ + return viewport; +} + +void ListBox::updateContent() +{ + hasDoneInitialUpdate = true; + totalItems = (model != 0) ? model->getNumRows() : 0; + + bool selectionChanged = false; + + if (selected [selected.size() - 1] >= totalItems) + { + selected.removeRange (totalItems, INT_MAX - totalItems); + lastRowSelected = getSelectedRow (0); + selectionChanged = true; + } + + viewport->updateVisibleArea (isVisible()); + viewport->resized(); + + if (selectionChanged && model != 0) + model->selectedRowsChanged (lastRowSelected); +} + +void ListBox::selectRow (const int row, + bool dontScroll, + bool deselectOthersFirst) +{ + selectRowInternal (row, dontScroll, deselectOthersFirst, false); +} + +void ListBox::selectRowInternal (const int row, + bool dontScroll, + bool deselectOthersFirst, + bool isMouseClick) +{ + if (! multipleSelection) + deselectOthersFirst = true; + + if ((! isRowSelected (row)) + || (deselectOthersFirst && getNumSelectedRows() > 1)) + { + if (((unsigned int) row) < (unsigned int) totalItems) + { + if (deselectOthersFirst) + selected.clear(); + + selected.addRange (row, 1); + + if (getHeight() == 0 || getWidth() == 0) + dontScroll = true; + + viewport->hasUpdated = false; + + if (row < viewport->firstWholeIndex && ! dontScroll) + { + viewport->setViewPosition (viewport->getViewPositionX(), + row * getRowHeight()); + } + else if (row >= viewport->lastWholeIndex && ! dontScroll) + { + const int rowsOnScreen = viewport->lastWholeIndex - viewport->firstWholeIndex; + + if (row >= lastRowSelected + rowsOnScreen + && rowsOnScreen < totalItems - 1 + && ! isMouseClick) + { + viewport->setViewPosition (viewport->getViewPositionX(), + jlimit (0, jmax (0, totalItems - rowsOnScreen), row) + * getRowHeight()); + } + else + { + viewport->setViewPosition (viewport->getViewPositionX(), + jmax (0, (row + 1) * getRowHeight() - viewport->getMaximumVisibleHeight())); + } + } + + if (! viewport->hasUpdated) + viewport->updateContents(); + + lastRowSelected = row; + model->selectedRowsChanged (row); + } + else + { + if (deselectOthersFirst) + deselectAllRows(); + } + } +} + +void ListBox::deselectRow (const int row) +{ + if (selected.contains (row)) + { + selected.removeRange (row, 1); + + if (row == lastRowSelected) + lastRowSelected = getSelectedRow (0); + + viewport->updateContents(); + model->selectedRowsChanged (lastRowSelected); + } +} + +void ListBox::setSelectedRows (const SparseSet& setOfRowsToBeSelected, + const bool sendNotificationEventToModel) +{ + selected = setOfRowsToBeSelected; + selected.removeRange (totalItems, INT_MAX - totalItems); + + if (! isRowSelected (lastRowSelected)) + lastRowSelected = getSelectedRow (0); + + viewport->updateContents(); + + if ((model != 0) && sendNotificationEventToModel) + model->selectedRowsChanged (lastRowSelected); +} + +const SparseSet ListBox::getSelectedRows() const +{ + return selected; +} + +void ListBox::selectRangeOfRows (int firstRow, int lastRow) +{ + if (multipleSelection && (firstRow != lastRow)) + { + const int numRows = totalItems - 1; + firstRow = jlimit (0, jmax (0, numRows), firstRow); + lastRow = jlimit (0, jmax (0, numRows), lastRow); + + selected.addRange (jmin (firstRow, lastRow), + abs (firstRow - lastRow) + 1); + + selected.removeRange (lastRow, 1); + } + + selectRowInternal (lastRow, false, false, true); +} + +void ListBox::flipRowSelection (const int row) +{ + if (isRowSelected (row)) + deselectRow (row); + else + selectRowInternal (row, false, false, true); +} + +void ListBox::deselectAllRows() +{ + if (! selected.isEmpty()) + { + selected.clear(); + lastRowSelected = -1; + + viewport->updateContents(); + + if (model != 0) + model->selectedRowsChanged (lastRowSelected); + } +} + +void ListBox::selectRowsBasedOnModifierKeys (const int row, + const ModifierKeys& mods) +{ + if (multipleSelection && mods.isCommandDown()) + { + flipRowSelection (row); + } + else if (multipleSelection && mods.isShiftDown() && lastRowSelected >= 0) + { + selectRangeOfRows (lastRowSelected, row); + } + else if ((! mods.isPopupMenu()) || ! isRowSelected (row)) + { + selectRowInternal (row, false, true, true); + } +} + +int ListBox::getNumSelectedRows() const +{ + return selected.size(); +} + +int ListBox::getSelectedRow (const int index) const +{ + return (((unsigned int) index) < (unsigned int) selected.size()) + ? selected [index] : -1; +} + +bool ListBox::isRowSelected (const int row) const +{ + return selected.contains (row); +} + +int ListBox::getLastRowSelected() const +{ + return (isRowSelected (lastRowSelected)) ? lastRowSelected : -1; +} + +int ListBox::getRowContainingPosition (const int x, const int y) const throw() +{ + if (((unsigned int) x) < (unsigned int) getWidth()) + { + const int row = (viewport->getViewPositionY() + y - viewport->getY()) / rowHeight; + + if (((unsigned int) row) < (unsigned int) totalItems) + return row; + } + + return -1; +} + +int ListBox::getInsertionIndexForPosition (const int x, const int y) const throw() +{ + if (((unsigned int) x) < (unsigned int) getWidth()) + { + const int row = (viewport->getViewPositionY() + y + rowHeight / 2 - viewport->getY()) / rowHeight; + return jlimit (0, totalItems, row); + } + + return -1; +} + +Component* ListBox::getComponentForRowNumber (const int row) const throw() +{ + Component* const listRowComp = viewport->getComponentForRowIfOnscreen (row); + return listRowComp != 0 ? listRowComp->getChildComponent (0) : 0; +} + +int ListBox::getRowNumberOfComponent (Component* const rowComponent) const throw() +{ + return viewport->getRowNumberOfComponent (rowComponent); +} + +const Rectangle ListBox::getRowPosition (const int rowNumber, + const bool relativeToComponentTopLeft) const throw() +{ + const int rowHeight = getRowHeight(); + int y = viewport->getY() + rowHeight * rowNumber; + + if (relativeToComponentTopLeft) + y -= viewport->getViewPositionY(); + + return Rectangle (viewport->getX(), y, + viewport->getViewedComponent()->getWidth(), rowHeight); +} + +void ListBox::setVerticalPosition (const double proportion) +{ + const int offscreen = viewport->getViewedComponent()->getHeight() - viewport->getHeight(); + + viewport->setViewPosition (viewport->getViewPositionX(), + jmax (0, roundDoubleToInt (proportion * offscreen))); +} + +double ListBox::getVerticalPosition() const +{ + const int offscreen = viewport->getViewedComponent()->getHeight() - viewport->getHeight(); + + return (offscreen > 0) ? viewport->getViewPositionY() / (double) offscreen + : 0; +} + +int ListBox::getVisibleRowWidth() const throw() +{ + return viewport->getViewWidth(); +} + +void ListBox::scrollToEnsureRowIsOnscreen (const int row) +{ + if (row < viewport->firstWholeIndex) + { + viewport->setViewPosition (viewport->getViewPositionX(), + row * getRowHeight()); + } + else if (row >= viewport->lastWholeIndex) + { + viewport->setViewPosition (viewport->getViewPositionX(), + jmax (0, (row + 1) * getRowHeight() - viewport->getMaximumVisibleHeight())); + } +} + +bool ListBox::keyPressed (const KeyPress& key) +{ + const int numVisibleRows = viewport->getHeight() / getRowHeight(); + + const bool multiple = multipleSelection + && (lastRowSelected >= 0) + && (key.getModifiers().isShiftDown() + || key.getModifiers().isCtrlDown() + || key.getModifiers().isCommandDown()); + + if (key.isKeyCode (KeyPress::upKey)) + { + if (multiple) + selectRangeOfRows (lastRowSelected, lastRowSelected - 1); + else + selectRow (jmax (0, lastRowSelected - 1)); + } + else if (key.isKeyCode (KeyPress::returnKey) + && isRowSelected (lastRowSelected)) + { + if (model != 0) + model->returnKeyPressed (lastRowSelected); + } + else if (key.isKeyCode (KeyPress::pageUpKey)) + { + if (multiple) + selectRangeOfRows (lastRowSelected, lastRowSelected - numVisibleRows); + else + selectRow (jmax (0, jmax (0, lastRowSelected) - numVisibleRows)); + } + else if (key.isKeyCode (KeyPress::pageDownKey)) + { + if (multiple) + selectRangeOfRows (lastRowSelected, lastRowSelected + numVisibleRows); + else + selectRow (jmin (totalItems - 1, jmax (0, lastRowSelected) + numVisibleRows)); + } + else if (key.isKeyCode (KeyPress::homeKey)) + { + if (multiple && key.getModifiers().isShiftDown()) + selectRangeOfRows (lastRowSelected, 0); + else + selectRow (0); + } + else if (key.isKeyCode (KeyPress::endKey)) + { + if (multiple && key.getModifiers().isShiftDown()) + selectRangeOfRows (lastRowSelected, totalItems - 1); + else + selectRow (totalItems - 1); + } + else if (key.isKeyCode (KeyPress::downKey)) + { + if (multiple) + selectRangeOfRows (lastRowSelected, lastRowSelected + 1); + else + selectRow (jmin (totalItems - 1, jmax (0, lastRowSelected) + 1)); + } + else if ((key.isKeyCode (KeyPress::deleteKey) || key.isKeyCode (KeyPress::backspaceKey)) + && isRowSelected (lastRowSelected)) + { + if (model != 0) + model->deleteKeyPressed (lastRowSelected); + } + else if (multiple && key == KeyPress (T('a'), ModifierKeys::commandModifier, 0)) + { + selectRangeOfRows (0, INT_MAX); + } + else + { + return false; + } + + return true; +} + +bool ListBox::keyStateChanged() +{ + return KeyPress::isKeyCurrentlyDown (KeyPress::upKey) + || KeyPress::isKeyCurrentlyDown (KeyPress::pageUpKey) + || KeyPress::isKeyCurrentlyDown (KeyPress::downKey) + || KeyPress::isKeyCurrentlyDown (KeyPress::pageDownKey) + || KeyPress::isKeyCurrentlyDown (KeyPress::homeKey) + || KeyPress::isKeyCurrentlyDown (KeyPress::endKey) + || KeyPress::isKeyCurrentlyDown (KeyPress::returnKey); +} + +void ListBox::mouseWheelMove (const MouseEvent& e, float wheelIncrementX, float wheelIncrementY) +{ + getHorizontalScrollBar()->mouseWheelMove (e, wheelIncrementX, 0); + getVerticalScrollBar()->mouseWheelMove (e, 0, wheelIncrementY); +} + +void ListBox::mouseMove (const MouseEvent& e) +{ + if (mouseMoveSelects) + { + const MouseEvent e2 (e.getEventRelativeTo (this)); + + selectRow (getRowContainingPosition (e2.x, e2.y), true); + + lastMouseX = e2.x; + lastMouseY = e2.y; + } +} + +void ListBox::mouseExit (const MouseEvent& e) +{ + mouseMove (e); +} + +void ListBox::mouseUp (const MouseEvent& e) +{ + if (e.mouseWasClicked() && model != 0) + model->backgroundClicked(); +} + +void ListBox::setRowHeight (const int newHeight) +{ + rowHeight = jmax (1, newHeight); + viewport->setSingleStepSizes (20, rowHeight); + updateContent(); +} + +int ListBox::getNumRowsOnScreen() const throw() +{ + return viewport->getMaximumVisibleHeight() / rowHeight; +} + +void ListBox::setMinimumContentWidth (const int newMinimumWidth) +{ + minimumRowWidth = newMinimumWidth; + updateContent(); +} + +int ListBox::getVisibleContentWidth() const throw() +{ + return viewport->getMaximumVisibleWidth(); +} + +ScrollBar* ListBox::getVerticalScrollBar() const throw() +{ + return viewport->getVerticalScrollBar(); +} + +ScrollBar* ListBox::getHorizontalScrollBar() const throw() +{ + return viewport->getHorizontalScrollBar(); +} + +void ListBox::colourChanged() +{ + setOpaque (findColour (backgroundColourId).isOpaque()); + viewport->setOpaque (isOpaque()); + repaint(); +} + +void ListBox::setOutlineThickness (const int outlineThickness_) +{ + outlineThickness = outlineThickness_; + resized(); +} + +void ListBox::setHeaderComponent (Component* const newHeaderComponent) +{ + if (headerComponent != newHeaderComponent) + { + if (headerComponent != 0) + delete headerComponent; + + headerComponent = newHeaderComponent; + + addAndMakeVisible (newHeaderComponent); + ListBox::resized(); + } +} + +void ListBox::repaintRow (const int rowNumber) throw() +{ + const Rectangle r (getRowPosition (rowNumber, true)); + repaint (r.getX(), r.getY(), r.getWidth(), r.getHeight()); +} + +Image* ListBox::createSnapshotOfSelectedRows() +{ + Image* snapshot = new Image (Image::ARGB, getWidth(), getHeight(), true); + Graphics g (*snapshot); + + const int firstRow = getRowContainingPosition (0, 0); + + for (int i = getNumRowsOnScreen() + 2; --i >= 0;) + { + Component* rowComp = viewport->getComponentForRowIfOnscreen (firstRow + i); + + if (rowComp != 0 && isRowSelected (firstRow + i)) + { + g.saveState(); + + int x = 0, y = 0; + rowComp->relativePositionToOtherComponent (this, x, y); + + g.setOrigin (x, y); + g.reduceClipRegion (0, 0, rowComp->getWidth(), rowComp->getHeight()); + + rowComp->paintEntireComponent (g); + + g.restoreState(); + } + } + + return snapshot; +} + +Component* ListBoxModel::refreshComponentForRow (int, bool, Component* existingComponentToUpdate) +{ + (void) existingComponentToUpdate; + jassert (existingComponentToUpdate == 0); // indicates a failure in the code the recycles the components + return 0; +} + +void ListBoxModel::listBoxItemClicked (int, const MouseEvent&) +{ +} + +void ListBoxModel::listBoxItemDoubleClicked (int, const MouseEvent&) +{ +} + +void ListBoxModel::backgroundClicked() +{ +} + +void ListBoxModel::selectedRowsChanged (int) +{ +} + +void ListBoxModel::deleteKeyPressed (int) +{ +} + +void ListBoxModel::returnKeyPressed (int) +{ +} + +void ListBoxModel::listWasScrolled() +{ +} + +const String ListBoxModel::getDragSourceDescription (const SparseSet&) +{ + return String::empty; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ListBox.cpp *********/ + +/********* Start of inlined file: juce_ProgressBar.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +ProgressBar::ProgressBar (double& progress_) + : progress (progress_), + displayPercentage (true) +{ + currentValue = jlimit (0.0, 1.0, progress); +} + +ProgressBar::~ProgressBar() +{ +} + +void ProgressBar::setPercentageDisplay (const bool shouldDisplayPercentage) +{ + displayPercentage = shouldDisplayPercentage; + repaint(); +} + +void ProgressBar::setTextToDisplay (const String& text) +{ + displayPercentage = false; + displayedMessage = text; +} + +void ProgressBar::lookAndFeelChanged() +{ + setOpaque (findColour (backgroundColourId).isOpaque()); +} + +void ProgressBar::colourChanged() +{ + lookAndFeelChanged(); +} + +void ProgressBar::paint (Graphics& g) +{ + String text; + + if (displayPercentage) + { + if (currentValue >= 0 && currentValue <= 1.0) + text << roundDoubleToInt (currentValue * 100.0) << T("%"); + } + else + { + text = displayedMessage; + } + + getLookAndFeel().drawProgressBar (g, *this, + getWidth(), getHeight(), + currentValue, text); +} + +void ProgressBar::visibilityChanged() +{ + if (isVisible()) + startTimer (30); + else + stopTimer(); +} + +void ProgressBar::timerCallback() +{ + double newProgress = progress; + + if (currentValue != newProgress + || newProgress < 0 || newProgress >= 1.0 + || currentMessage != displayedMessage) + { + if (currentValue < newProgress + && newProgress >= 0 && newProgress < 1.0 + && currentValue >= 0 && newProgress < 1.0) + { + newProgress = jmin (currentValue + 0.02, newProgress); + } + + currentValue = newProgress; + currentMessage = displayedMessage; + repaint(); + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ProgressBar.cpp *********/ + +/********* Start of inlined file: juce_Slider.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +class SliderPopupDisplayComponent : public BubbleComponent +{ +public: + + SliderPopupDisplayComponent (Slider* const owner_) + : owner (owner_), + font (15.0f, Font::bold) + { + setAlwaysOnTop (true); + } + + ~SliderPopupDisplayComponent() + { + } + + void paintContent (Graphics& g, int w, int h) + { + g.setFont (font); + g.setColour (Colours::black); + + g.drawFittedText (text, 0, 0, w, h, Justification::centred, 1); + } + + void getContentSize (int& w, int& h) + { + w = font.getStringWidth (text) + 18; + h = (int) (font.getHeight() * 1.6f); + } + + void updatePosition (const String& newText) + { + if (text != newText) + { + text = newText; + repaint(); + } + + BubbleComponent::setPosition (owner); + } + + juce_UseDebuggingNewOperator + +private: + Slider* owner; + Font font; + String text; + + SliderPopupDisplayComponent (const SliderPopupDisplayComponent&); + const SliderPopupDisplayComponent& operator= (const SliderPopupDisplayComponent&); +}; + +Slider::Slider (const String& name) + : Component (name), + listeners (2), + currentValue (0.0), + valueMin (0.0), + valueMax (0.0), + minimum (0), + maximum (10), + interval (0), + skewFactor (1.0), + velocityModeSensitivity (1.0), + velocityModeOffset (0.0), + velocityModeThreshold (1), + rotaryStart (float_Pi * 1.2f), + rotaryEnd (float_Pi * 2.8f), + numDecimalPlaces (7), + sliderRegionStart (0), + sliderRegionSize (1), + pixelsForFullDragExtent (250), + style (LinearHorizontal), + textBoxPos (TextBoxLeft), + textBoxWidth (80), + textBoxHeight (20), + incDecButtonMode (incDecButtonsNotDraggable), + editableText (true), + doubleClickToValue (false), + isVelocityBased (false), + userKeyOverridesVelocity (true), + rotaryStop (true), + incDecButtonsSideBySide (false), + sendChangeOnlyOnRelease (false), + popupDisplayEnabled (false), + menuEnabled (false), + menuShown (false), + scrollWheelEnabled (true), + snapsToMousePos (true), + valueBox (0), + incButton (0), + decButton (0), + popupDisplay (0), + parentForPopupDisplay (0) +{ + setWantsKeyboardFocus (false); + setRepaintsOnMouseActivity (true); + + lookAndFeelChanged(); + updateText(); +} + +Slider::~Slider() +{ + deleteAndZero (popupDisplay); + deleteAllChildren(); +} + +void Slider::handleAsyncUpdate() +{ + cancelPendingUpdate(); + + for (int i = listeners.size(); --i >= 0;) + { + ((SliderListener*) listeners.getUnchecked (i))->sliderValueChanged (this); + i = jmin (i, listeners.size()); + } +} + +void Slider::sendDragStart() +{ + startedDragging(); + + for (int i = listeners.size(); --i >= 0;) + { + ((SliderListener*) listeners.getUnchecked (i))->sliderDragStarted (this); + i = jmin (i, listeners.size()); + } +} + +void Slider::sendDragEnd() +{ + stoppedDragging(); + + for (int i = listeners.size(); --i >= 0;) + { + ((SliderListener*) listeners.getUnchecked (i))->sliderDragEnded (this); + i = jmin (i, listeners.size()); + } +} + +void Slider::addListener (SliderListener* const listener) throw() +{ + jassert (listener != 0); + if (listener != 0) + listeners.add (listener); +} + +void Slider::removeListener (SliderListener* const listener) throw() +{ + listeners.removeValue (listener); +} + +void Slider::setSliderStyle (const SliderStyle newStyle) +{ + if (style != newStyle) + { + style = newStyle; + repaint(); + lookAndFeelChanged(); + } +} + +void Slider::setRotaryParameters (const float startAngleRadians, + const float endAngleRadians, + const bool stopAtEnd) +{ + // make sure the values are sensible.. + jassert (rotaryStart >= 0 && rotaryEnd >= 0); + jassert (rotaryStart < float_Pi * 4.0f && rotaryEnd < float_Pi * 4.0f); + jassert (rotaryStart < rotaryEnd); + + rotaryStart = startAngleRadians; + rotaryEnd = endAngleRadians; + rotaryStop = stopAtEnd; +} + +void Slider::setVelocityBasedMode (const bool velBased) throw() +{ + isVelocityBased = velBased; +} + +void Slider::setVelocityModeParameters (const double sensitivity, + const int threshold, + const double offset, + const bool userCanPressKeyToSwapMode) throw() +{ + jassert (threshold >= 0); + jassert (sensitivity > 0); + jassert (offset >= 0); + + velocityModeSensitivity = sensitivity; + velocityModeOffset = offset; + velocityModeThreshold = threshold; + userKeyOverridesVelocity = userCanPressKeyToSwapMode; +} + +void Slider::setSkewFactor (const double factor) throw() +{ + skewFactor = factor; +} + +void Slider::setSkewFactorFromMidPoint (const double sliderValueToShowAtMidPoint) throw() +{ + if (maximum > minimum) + skewFactor = log (0.5) / log ((sliderValueToShowAtMidPoint - minimum) + / (maximum - minimum)); +} + +void Slider::setMouseDragSensitivity (const int distanceForFullScaleDrag) +{ + jassert (distanceForFullScaleDrag > 0); + + pixelsForFullDragExtent = distanceForFullScaleDrag; +} + +void Slider::setIncDecButtonsMode (const IncDecButtonMode mode) +{ + if (incDecButtonMode != mode) + { + incDecButtonMode = mode; + lookAndFeelChanged(); + } +} + +void Slider::setTextBoxStyle (const TextEntryBoxPosition newPosition, + const bool isReadOnly, + const int textEntryBoxWidth, + const int textEntryBoxHeight) +{ + textBoxPos = newPosition; + editableText = ! isReadOnly; + textBoxWidth = textEntryBoxWidth; + textBoxHeight = textEntryBoxHeight; + + repaint(); + lookAndFeelChanged(); +} + +void Slider::setTextBoxIsEditable (const bool shouldBeEditable) throw() +{ + editableText = shouldBeEditable; + + if (valueBox != 0) + valueBox->setEditable (shouldBeEditable && isEnabled()); +} + +void Slider::showTextBox() +{ + jassert (editableText); // this should probably be avoided in read-only sliders. + + if (valueBox != 0) + valueBox->showEditor(); +} + +void Slider::hideTextBox (const bool discardCurrentEditorContents) +{ + if (valueBox != 0) + { + valueBox->hideEditor (discardCurrentEditorContents); + + if (discardCurrentEditorContents) + updateText(); + } +} + +void Slider::setChangeNotificationOnlyOnRelease (const bool onlyNotifyOnRelease) throw() +{ + sendChangeOnlyOnRelease = onlyNotifyOnRelease; +} + +void Slider::setSliderSnapsToMousePosition (const bool shouldSnapToMouse) throw() +{ + snapsToMousePos = shouldSnapToMouse; +} + +void Slider::setPopupDisplayEnabled (const bool enabled, + Component* const parentComponentToUse) throw() +{ + popupDisplayEnabled = enabled; + parentForPopupDisplay = parentComponentToUse; +} + +void Slider::colourChanged() +{ + lookAndFeelChanged(); +} + +void Slider::lookAndFeelChanged() +{ + const String previousTextBoxContent (valueBox != 0 ? valueBox->getText() + : getTextFromValue (currentValue)); + + deleteAllChildren(); + valueBox = 0; + + LookAndFeel& lf = getLookAndFeel(); + + if (textBoxPos != NoTextBox) + { + addAndMakeVisible (valueBox = getLookAndFeel().createSliderTextBox (*this)); + + valueBox->setWantsKeyboardFocus (false); + valueBox->setText (previousTextBoxContent, false); + + valueBox->setEditable (editableText && isEnabled()); + valueBox->addListener (this); + + if (style == LinearBar) + valueBox->addMouseListener (this, false); + } + + if (style == IncDecButtons) + { + addAndMakeVisible (incButton = lf.createSliderButton (true)); + incButton->addButtonListener (this); + + addAndMakeVisible (decButton = lf.createSliderButton (false)); + decButton->addButtonListener (this); + + if (incDecButtonMode != incDecButtonsNotDraggable) + { + incButton->addMouseListener (this, false); + decButton->addMouseListener (this, false); + } + else + { + incButton->setRepeatSpeed (300, 100, 20); + incButton->addMouseListener (decButton, false); + + decButton->setRepeatSpeed (300, 100, 20); + decButton->addMouseListener (incButton, false); + } + } + + setComponentEffect (lf.getSliderEffect()); + + resized(); + repaint(); +} + +void Slider::setRange (const double newMin, + const double newMax, + const double newInt) +{ + if (minimum != newMin + || maximum != newMax + || interval != newInt) + { + minimum = newMin; + maximum = newMax; + interval = newInt; + + // figure out the number of DPs needed to display all values at this + // interval setting. + numDecimalPlaces = 7; + + if (newInt != 0) + { + int v = abs ((int) (newInt * 10000000)); + + while ((v % 10) == 0) + { + --numDecimalPlaces; + v /= 10; + } + } + + // keep the current values inside the new range.. + if (style != TwoValueHorizontal && style != TwoValueVertical) + { + setValue (currentValue, false, false); + } + else + { + setMinValue (getMinValue(), false, false); + setMaxValue (getMaxValue(), false, false); + } + + updateText(); + } +} + +void Slider::triggerChangeMessage (const bool synchronous) +{ + if (synchronous) + handleAsyncUpdate(); + else + triggerAsyncUpdate(); + + valueChanged(); +} + +double Slider::getValue() const throw() +{ + // for a two-value style slider, you should use the getMinValue() and getMaxValue() + // methods to get the two values. + jassert (style != TwoValueHorizontal && style != TwoValueVertical); + + return currentValue; +} + +void Slider::setValue (double newValue, + const bool sendUpdateMessage, + const bool sendMessageSynchronously) +{ + // for a two-value style slider, you should use the setMinValue() and setMaxValue() + // methods to set the two values. + jassert (style != TwoValueHorizontal && style != TwoValueVertical); + + newValue = constrainedValue (newValue); + + if (style == ThreeValueHorizontal || style == ThreeValueVertical) + { + jassert (valueMin <= valueMax); + newValue = jlimit (valueMin, valueMax, newValue); + } + + if (currentValue != newValue) + { + if (valueBox != 0) + valueBox->hideEditor (true); + + currentValue = newValue; + updateText(); + repaint(); + + if (popupDisplay != 0) + { + ((SliderPopupDisplayComponent*) popupDisplay)->updatePosition (getTextFromValue (currentValue)); + popupDisplay->repaint(); + } + + if (sendUpdateMessage) + triggerChangeMessage (sendMessageSynchronously); + } +} + +double Slider::getMinValue() const throw() +{ + // The minimum value only applies to sliders that are in two- or three-value mode. + jassert (style == TwoValueHorizontal || style == TwoValueVertical + || style == ThreeValueHorizontal || style == ThreeValueVertical); + + return valueMin; +} + +double Slider::getMaxValue() const throw() +{ + // The maximum value only applies to sliders that are in two- or three-value mode. + jassert (style == TwoValueHorizontal || style == TwoValueVertical + || style == ThreeValueHorizontal || style == ThreeValueVertical); + + return valueMax; +} + +void Slider::setMinValue (double newValue, const bool sendUpdateMessage, const bool sendMessageSynchronously) +{ + // The minimum value only applies to sliders that are in two- or three-value mode. + jassert (style == TwoValueHorizontal || style == TwoValueVertical + || style == ThreeValueHorizontal || style == ThreeValueVertical); + + newValue = constrainedValue (newValue); + + if (style == TwoValueHorizontal || style == TwoValueVertical) + newValue = jmin (valueMax, newValue); + else + newValue = jmin (currentValue, newValue); + + if (valueMin != newValue) + { + valueMin = newValue; + repaint(); + + if (popupDisplay != 0) + { + ((SliderPopupDisplayComponent*) popupDisplay)->updatePosition (getTextFromValue (valueMin)); + popupDisplay->repaint(); + } + + if (sendUpdateMessage) + triggerChangeMessage (sendMessageSynchronously); + } +} + +void Slider::setMaxValue (double newValue, const bool sendUpdateMessage, const bool sendMessageSynchronously) +{ + // The maximum value only applies to sliders that are in two- or three-value mode. + jassert (style == TwoValueHorizontal || style == TwoValueVertical + || style == ThreeValueHorizontal || style == ThreeValueVertical); + + newValue = constrainedValue (newValue); + + if (style == TwoValueHorizontal || style == TwoValueVertical) + newValue = jmax (valueMin, newValue); + else + newValue = jmax (currentValue, newValue); + + if (valueMax != newValue) + { + valueMax = newValue; + repaint(); + + if (popupDisplay != 0) + { + ((SliderPopupDisplayComponent*) popupDisplay)->updatePosition (getTextFromValue (valueMax)); + popupDisplay->repaint(); + } + + if (sendUpdateMessage) + triggerChangeMessage (sendMessageSynchronously); + } +} + +void Slider::setDoubleClickReturnValue (const bool isDoubleClickEnabled, + const double valueToSetOnDoubleClick) throw() +{ + doubleClickToValue = isDoubleClickEnabled; + doubleClickReturnValue = valueToSetOnDoubleClick; +} + +double Slider::getDoubleClickReturnValue (bool& isEnabled_) const throw() +{ + isEnabled_ = doubleClickToValue; + return doubleClickReturnValue; +} + +void Slider::updateText() +{ + if (valueBox != 0) + valueBox->setText (getTextFromValue (currentValue), false); +} + +void Slider::setTextValueSuffix (const String& suffix) +{ + if (textSuffix != suffix) + { + textSuffix = suffix; + updateText(); + } +} + +const String Slider::getTextFromValue (double v) +{ + if (numDecimalPlaces > 0) + return String (v, numDecimalPlaces) + textSuffix; + else + return String (roundDoubleToInt (v)) + textSuffix; +} + +double Slider::getValueFromText (const String& text) +{ + String t (text.trimStart()); + + if (t.endsWith (textSuffix)) + t = t.substring (0, t.length() - textSuffix.length()); + + while (t.startsWithChar (T('+'))) + t = t.substring (1).trimStart(); + + return t.initialSectionContainingOnly (T("0123456789.,-")) + .getDoubleValue(); +} + +double Slider::proportionOfLengthToValue (double proportion) +{ + if (skewFactor != 1.0 && proportion > 0.0) + proportion = exp (log (proportion) / skewFactor); + + return minimum + (maximum - minimum) * proportion; +} + +double Slider::valueToProportionOfLength (double value) +{ + const double n = (value - minimum) / (maximum - minimum); + + return skewFactor == 1.0 ? n : pow (n, skewFactor); +} + +double Slider::snapValue (double attemptedValue, const bool) +{ + return attemptedValue; +} + +void Slider::startedDragging() +{ +} + +void Slider::stoppedDragging() +{ +} + +void Slider::valueChanged() +{ +} + +void Slider::enablementChanged() +{ + repaint(); +} + +void Slider::setPopupMenuEnabled (const bool menuEnabled_) throw() +{ + menuEnabled = menuEnabled_; +} + +void Slider::setScrollWheelEnabled (const bool enabled) throw() +{ + scrollWheelEnabled = enabled; +} + +void Slider::labelTextChanged (Label* label) +{ + const double newValue = snapValue (getValueFromText (label->getText()), false); + + if (getValue() != newValue) + { + sendDragStart(); + setValue (newValue, true, true); + sendDragEnd(); + } + + updateText(); // force a clean-up of the text, needed in case setValue() hasn't done this. +} + +void Slider::buttonClicked (Button* button) +{ + if (style == IncDecButtons) + { + sendDragStart(); + + if (button == incButton) + setValue (snapValue (getValue() + interval, false), true, true); + else if (button == decButton) + setValue (snapValue (getValue() - interval, false), true, true); + + sendDragEnd(); + } +} + +double Slider::constrainedValue (double value) const throw() +{ + if (interval > 0) + value = minimum + interval * floor ((value - minimum) / interval + 0.5); + + if (value <= minimum || maximum <= minimum) + value = minimum; + else if (value >= maximum) + value = maximum; + + return value; +} + +float Slider::getLinearSliderPos (const double value) +{ + double sliderPosProportional; + + if (maximum > minimum) + { + if (value < minimum) + { + sliderPosProportional = 0.0; + } + else if (value > maximum) + { + sliderPosProportional = 1.0; + } + else + { + sliderPosProportional = valueToProportionOfLength (value); + jassert (sliderPosProportional >= 0 && sliderPosProportional <= 1.0); + } + } + else + { + sliderPosProportional = 0.5; + } + + if (style == LinearVertical || style == IncDecButtons) + sliderPosProportional = 1.0 - sliderPosProportional; + + return (float) (sliderRegionStart + sliderPosProportional * sliderRegionSize); +} + +bool Slider::isHorizontal() const throw() +{ + return style == LinearHorizontal + || style == LinearBar + || style == TwoValueHorizontal + || style == ThreeValueHorizontal; +} + +bool Slider::isVertical() const throw() +{ + return style == LinearVertical + || style == TwoValueVertical + || style == ThreeValueVertical; +} + +bool Slider::incDecDragDirectionIsHorizontal() const throw() +{ + return incDecButtonMode == incDecButtonsDraggable_Horizontal + || (incDecButtonMode == incDecButtonsDraggable_AutoDirection && incDecButtonsSideBySide); +} + +float Slider::getPositionOfValue (const double value) +{ + if (isHorizontal() || isVertical()) + { + return getLinearSliderPos (value); + } + else + { + jassertfalse // not a valid call on a slider that doesn't work linearly! + return 0.0f; + } +} + +void Slider::paint (Graphics& g) +{ + if (style != IncDecButtons) + { + if (style == Rotary || style == RotaryHorizontalDrag || style == RotaryVerticalDrag) + { + const float sliderPos = (float) valueToProportionOfLength (currentValue); + jassert (sliderPos >= 0 && sliderPos <= 1.0f); + + getLookAndFeel().drawRotarySlider (g, + sliderRect.getX(), + sliderRect.getY(), + sliderRect.getWidth(), + sliderRect.getHeight(), + sliderPos, + rotaryStart, rotaryEnd, + *this); + } + else + { + getLookAndFeel().drawLinearSlider (g, + sliderRect.getX(), + sliderRect.getY(), + sliderRect.getWidth(), + sliderRect.getHeight(), + getLinearSliderPos (currentValue), + getLinearSliderPos (valueMin), + getLinearSliderPos (valueMax), + style, + *this); + } + + if (style == LinearBar && valueBox == 0) + { + g.setColour (findColour (Slider::textBoxOutlineColourId)); + g.drawRect (0, 0, getWidth(), getHeight(), 1); + } + } +} + +void Slider::resized() +{ + int minXSpace = 0; + int minYSpace = 0; + + if (textBoxPos == TextBoxLeft || textBoxPos == TextBoxRight) + minXSpace = 30; + else + minYSpace = 15; + + const int tbw = jmax (0, jmin (textBoxWidth, getWidth() - minXSpace)); + const int tbh = jmax (0, jmin (textBoxHeight, getHeight() - minYSpace)); + + if (style == LinearBar) + { + if (valueBox != 0) + valueBox->setBounds (0, 0, getWidth(), getHeight()); + } + else + { + if (textBoxPos == NoTextBox) + { + sliderRect.setBounds (0, 0, getWidth(), getHeight()); + } + else if (textBoxPos == TextBoxLeft) + { + valueBox->setBounds (0, (getHeight() - tbh) / 2, tbw, tbh); + sliderRect.setBounds (tbw, 0, getWidth() - tbw, getHeight()); + } + else if (textBoxPos == TextBoxRight) + { + valueBox->setBounds (getWidth() - tbw, (getHeight() - tbh) / 2, tbw, tbh); + sliderRect.setBounds (0, 0, getWidth() - tbw, getHeight()); + } + else if (textBoxPos == TextBoxAbove) + { + valueBox->setBounds ((getWidth() - tbw) / 2, 0, tbw, tbh); + sliderRect.setBounds (0, tbh, getWidth(), getHeight() - tbh); + } + else if (textBoxPos == TextBoxBelow) + { + valueBox->setBounds ((getWidth() - tbw) / 2, getHeight() - tbh, tbw, tbh); + sliderRect.setBounds (0, 0, getWidth(), getHeight() - tbh); + } + } + + const int indent = getLookAndFeel().getSliderThumbRadius (*this); + + if (style == LinearBar) + { + const int barIndent = 1; + sliderRegionStart = barIndent; + sliderRegionSize = getWidth() - barIndent * 2; + + sliderRect.setBounds (sliderRegionStart, barIndent, + sliderRegionSize, getHeight() - barIndent * 2); + } + else if (isHorizontal()) + { + sliderRegionStart = sliderRect.getX() + indent; + sliderRegionSize = jmax (1, sliderRect.getWidth() - indent * 2); + + sliderRect.setBounds (sliderRegionStart, sliderRect.getY(), + sliderRegionSize, sliderRect.getHeight()); + } + else if (isVertical()) + { + sliderRegionStart = sliderRect.getY() + indent; + sliderRegionSize = jmax (1, sliderRect.getHeight() - indent * 2); + + sliderRect.setBounds (sliderRect.getX(), sliderRegionStart, + sliderRect.getWidth(), sliderRegionSize); + } + else + { + sliderRegionStart = 0; + sliderRegionSize = 100; + } + + if (style == IncDecButtons) + { + Rectangle buttonRect (sliderRect); + + if (textBoxPos == TextBoxLeft || textBoxPos == TextBoxRight) + buttonRect.expand (-2, 0); + else + buttonRect.expand (0, -2); + + incDecButtonsSideBySide = buttonRect.getWidth() > buttonRect.getHeight(); + + if (incDecButtonsSideBySide) + { + decButton->setBounds (buttonRect.getX(), + buttonRect.getY(), + buttonRect.getWidth() / 2, + buttonRect.getHeight()); + + decButton->setConnectedEdges (Button::ConnectedOnRight); + + incButton->setBounds (buttonRect.getCentreX(), + buttonRect.getY(), + buttonRect.getWidth() / 2, + buttonRect.getHeight()); + + incButton->setConnectedEdges (Button::ConnectedOnLeft); + } + else + { + incButton->setBounds (buttonRect.getX(), + buttonRect.getY(), + buttonRect.getWidth(), + buttonRect.getHeight() / 2); + + incButton->setConnectedEdges (Button::ConnectedOnBottom); + + decButton->setBounds (buttonRect.getX(), + buttonRect.getCentreY(), + buttonRect.getWidth(), + buttonRect.getHeight() / 2); + + decButton->setConnectedEdges (Button::ConnectedOnTop); + } + } +} + +void Slider::focusOfChildComponentChanged (FocusChangeType) +{ + repaint(); +} + +void Slider::mouseDown (const MouseEvent& e) +{ + mouseWasHidden = false; + incDecDragged = false; + + if (isEnabled()) + { + if (e.mods.isPopupMenu() && menuEnabled) + { + menuShown = true; + + PopupMenu m; + m.addItem (1, TRANS ("velocity-sensitive mode"), true, isVelocityBased); + m.addSeparator(); + + if (style == Rotary || style == RotaryHorizontalDrag || style == RotaryVerticalDrag) + { + PopupMenu rotaryMenu; + rotaryMenu.addItem (2, TRANS ("use circular dragging"), true, style == Rotary); + rotaryMenu.addItem (3, TRANS ("use left-right dragging"), true, style == RotaryHorizontalDrag); + rotaryMenu.addItem (4, TRANS ("use up-down dragging"), true, style == RotaryVerticalDrag); + + m.addSubMenu (TRANS ("rotary mode"), rotaryMenu); + } + + const int r = m.show(); + + if (r == 1) + { + setVelocityBasedMode (! isVelocityBased); + } + else if (r == 2) + { + setSliderStyle (Rotary); + } + else if (r == 3) + { + setSliderStyle (RotaryHorizontalDrag); + } + else if (r == 4) + { + setSliderStyle (RotaryVerticalDrag); + } + } + else if (maximum > minimum) + { + menuShown = false; + + if (valueBox != 0) + valueBox->hideEditor (true); + + sliderBeingDragged = 0; + + if (style == TwoValueHorizontal + || style == TwoValueVertical + || style == ThreeValueHorizontal + || style == ThreeValueVertical) + { + const float mousePos = (float) (isVertical() ? e.y : e.x); + + const float normalPosDistance = fabsf (getLinearSliderPos (currentValue) - mousePos); + const float minPosDistance = fabsf (getLinearSliderPos (valueMin) - 0.1f - mousePos); + const float maxPosDistance = fabsf (getLinearSliderPos (valueMax) + 0.1f - mousePos); + + if (style == TwoValueHorizontal || style == TwoValueVertical) + { + if (maxPosDistance <= minPosDistance) + sliderBeingDragged = 2; + else + sliderBeingDragged = 1; + } + else if (style == ThreeValueHorizontal || style == ThreeValueVertical) + { + if (normalPosDistance >= minPosDistance && maxPosDistance >= minPosDistance) + sliderBeingDragged = 1; + else if (normalPosDistance >= maxPosDistance) + sliderBeingDragged = 2; + } + } + + minMaxDiff = valueMax - valueMin; + + mouseXWhenLastDragged = e.x; + mouseYWhenLastDragged = e.y; + lastAngle = rotaryStart + (rotaryEnd - rotaryStart) + * valueToProportionOfLength (currentValue); + + if (sliderBeingDragged == 2) + valueWhenLastDragged = valueMax; + else if (sliderBeingDragged == 1) + valueWhenLastDragged = valueMin; + else + valueWhenLastDragged = currentValue; + + valueOnMouseDown = valueWhenLastDragged; + + if (popupDisplayEnabled) + { + SliderPopupDisplayComponent* const popup = new SliderPopupDisplayComponent (this); + popupDisplay = popup; + + if (parentForPopupDisplay != 0) + { + parentForPopupDisplay->addChildComponent (popup); + } + else + { + popup->addToDesktop (0); + } + + popup->setVisible (true); + } + + sendDragStart(); + + mouseDrag (e); + } + } +} + +void Slider::mouseUp (const MouseEvent&) +{ + if (isEnabled() + && (! menuShown) + && (maximum > minimum) + && (style != IncDecButtons || incDecDragged)) + { + restoreMouseIfHidden(); + + if (sendChangeOnlyOnRelease && valueOnMouseDown != currentValue) + triggerChangeMessage (false); + + sendDragEnd(); + + deleteAndZero (popupDisplay); + + if (style == IncDecButtons) + { + incButton->setState (Button::buttonNormal); + decButton->setState (Button::buttonNormal); + } + } +} + +void Slider::restoreMouseIfHidden() +{ + if (mouseWasHidden) + { + mouseWasHidden = false; + + Component* c = Component::getComponentUnderMouse(); + + if (c == 0) + c = this; + + c->enableUnboundedMouseMovement (false); + + const double pos = (sliderBeingDragged == 2) ? getMaxValue() + : ((sliderBeingDragged == 1) ? getMinValue() + : currentValue); + + const int pixelPos = (int) getLinearSliderPos (pos); + + int x = isHorizontal() ? pixelPos : (getWidth() / 2); + int y = isVertical() ? pixelPos : (getHeight() / 2); + + relativePositionToGlobal (x, y); + Desktop::setMousePosition (x, y); + } +} + +void Slider::modifierKeysChanged (const ModifierKeys& modifiers) +{ + if (isEnabled() + && style != IncDecButtons + && style != Rotary + && isVelocityBased == modifiers.isAnyModifierKeyDown()) + { + restoreMouseIfHidden(); + } +} + +static double smallestAngleBetween (double a1, double a2) +{ + return jmin (fabs (a1 - a2), + fabs (a1 + double_Pi * 2.0 - a2), + fabs (a2 + double_Pi * 2.0 - a1)); +} + +void Slider::mouseDrag (const MouseEvent& e) +{ + if (isEnabled() + && (! menuShown) + && (maximum > minimum)) + { + if (style == Rotary) + { + int dx = e.x - sliderRect.getCentreX(); + int dy = e.y - sliderRect.getCentreY(); + + if (dx * dx + dy * dy > 25) + { + double angle = atan2 ((double) dx, (double) -dy); + while (angle < 0.0) + angle += double_Pi * 2.0; + + if (rotaryStop && ! e.mouseWasClicked()) + { + if (fabs (angle - lastAngle) > double_Pi) + { + if (angle >= lastAngle) + angle -= double_Pi * 2.0; + else + angle += double_Pi * 2.0; + } + + if (angle >= lastAngle) + angle = jmin (angle, (double) jmax (rotaryStart, rotaryEnd)); + else + angle = jmax (angle, (double) jmin (rotaryStart, rotaryEnd)); + } + else + { + while (angle < rotaryStart) + angle += double_Pi * 2.0; + + if (angle > rotaryEnd) + { + if (smallestAngleBetween (angle, rotaryStart) <= smallestAngleBetween (angle, rotaryEnd)) + angle = rotaryStart; + else + angle = rotaryEnd; + } + } + + const double proportion = (angle - rotaryStart) / (rotaryEnd - rotaryStart); + + valueWhenLastDragged = proportionOfLengthToValue (jlimit (0.0, 1.0, proportion)); + + lastAngle = angle; + } + } + else + { + if (style == LinearBar && e.mouseWasClicked() + && valueBox != 0 && valueBox->isEditable()) + return; + + if (style == IncDecButtons) + { + if (! incDecDragged) + incDecDragged = e.getDistanceFromDragStart() > 10 && ! e.mouseWasClicked(); + + if (! incDecDragged) + return; + } + + if ((isVelocityBased == (userKeyOverridesVelocity ? e.mods.testFlags (ModifierKeys::ctrlModifier | ModifierKeys::commandModifier | ModifierKeys::altModifier) + : false)) + || ((maximum - minimum) / sliderRegionSize < interval)) + { + const int mousePos = (isHorizontal() || style == RotaryHorizontalDrag) ? e.x : e.y; + + double scaledMousePos = (mousePos - sliderRegionStart) / (double) sliderRegionSize; + + if (style == RotaryHorizontalDrag + || style == RotaryVerticalDrag + || style == IncDecButtons + || ((style == LinearHorizontal || style == LinearVertical || style == LinearBar) + && ! snapsToMousePos)) + { + const int mouseDiff = (style == RotaryHorizontalDrag + || style == LinearHorizontal + || style == LinearBar + || (style == IncDecButtons && incDecDragDirectionIsHorizontal())) + ? e.getDistanceFromDragStartX() + : -e.getDistanceFromDragStartY(); + + double newPos = valueToProportionOfLength (valueOnMouseDown) + + mouseDiff * (1.0 / pixelsForFullDragExtent); + + valueWhenLastDragged = proportionOfLengthToValue (jlimit (0.0, 1.0, newPos)); + + if (style == IncDecButtons) + { + incButton->setState (mouseDiff < 0 ? Button::buttonNormal : Button::buttonDown); + decButton->setState (mouseDiff > 0 ? Button::buttonNormal : Button::buttonDown); + } + } + else + { + if (style == LinearVertical) + scaledMousePos = 1.0 - scaledMousePos; + + valueWhenLastDragged = proportionOfLengthToValue (jlimit (0.0, 1.0, scaledMousePos)); + } + } + else + { + const int mouseDiff = (isHorizontal() || style == RotaryHorizontalDrag + || (style == IncDecButtons && incDecDragDirectionIsHorizontal())) + ? e.x - mouseXWhenLastDragged + : e.y - mouseYWhenLastDragged; + + const double maxSpeed = jmax (200, sliderRegionSize); + double speed = jlimit (0.0, maxSpeed, (double) abs (mouseDiff)); + + if (speed != 0) + { + speed = 0.2 * velocityModeSensitivity + * (1.0 + sin (double_Pi * (1.5 + jmin (0.5, velocityModeOffset + + jmax (0.0, (double) (speed - velocityModeThreshold)) + / maxSpeed)))); + + if (mouseDiff < 0) + speed = -speed; + + if (style == LinearVertical || style == RotaryVerticalDrag + || (style == IncDecButtons && ! incDecDragDirectionIsHorizontal())) + speed = -speed; + + const double currentPos = valueToProportionOfLength (valueWhenLastDragged); + + valueWhenLastDragged = proportionOfLengthToValue (jlimit (0.0, 1.0, currentPos + speed)); + + e.originalComponent->enableUnboundedMouseMovement (true, false); + mouseWasHidden = true; + } + } + } + + valueWhenLastDragged = jlimit (minimum, maximum, valueWhenLastDragged); + + if (sliderBeingDragged == 0) + { + setValue (snapValue (valueWhenLastDragged, true), + ! sendChangeOnlyOnRelease, true); + } + else if (sliderBeingDragged == 1) + { + setMinValue (snapValue (valueWhenLastDragged, true), + ! sendChangeOnlyOnRelease, false); + + if (e.mods.isShiftDown()) + setMaxValue (getMinValue() + minMaxDiff, false); + else + minMaxDiff = valueMax - valueMin; + } + else + { + jassert (sliderBeingDragged == 2); + + setMaxValue (snapValue (valueWhenLastDragged, true), + ! sendChangeOnlyOnRelease, false); + + if (e.mods.isShiftDown()) + setMinValue (getMaxValue() - minMaxDiff, false); + else + minMaxDiff = valueMax - valueMin; + } + + mouseXWhenLastDragged = e.x; + mouseYWhenLastDragged = e.y; + + // (repainting synchronously makes the sliders feel a bit snappier..) + ComponentPeer* const peer = getPeer(); + + if (peer != 0) + peer->performAnyPendingRepaintsNow(); + } +} + +void Slider::mouseDoubleClick (const MouseEvent&) +{ + if (doubleClickToValue + && isEnabled() + && style != IncDecButtons + && minimum <= doubleClickReturnValue + && maximum >= doubleClickReturnValue) + { + sendDragStart(); + setValue (doubleClickReturnValue, true, true); + sendDragEnd(); + } +} + +void Slider::mouseWheelMove (const MouseEvent& e, float wheelIncrementX, float wheelIncrementY) +{ + if (scrollWheelEnabled && isEnabled()) + { + if (maximum > minimum && ! isMouseButtonDownAnywhere()) + { + if (valueBox != 0) + valueBox->hideEditor (false); + + const double proportionDelta = (wheelIncrementX != 0 ? -wheelIncrementX : wheelIncrementY) * 0.15f; + const double currentPos = valueToProportionOfLength (currentValue); + const double newValue = proportionOfLengthToValue (jlimit (0.0, 1.0, currentPos + proportionDelta)); + + double delta = (newValue != currentValue) + ? jmax (fabs (newValue - currentValue), interval) : 0; + + if (currentValue > newValue) + delta = -delta; + + sendDragStart(); + setValue (snapValue (currentValue + delta, false), true, true); + sendDragEnd(); + } + } + else + { + Component::mouseWheelMove (e, wheelIncrementX, wheelIncrementY); + } +} + +void SliderListener::sliderDragStarted (Slider*) +{ +} + +void SliderListener::sliderDragEnded (Slider*) +{ +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_Slider.cpp *********/ + +/********* Start of inlined file: juce_TableHeaderComponent.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +class DragOverlayComp : public Component +{ +public: + DragOverlayComp (Image* const image_) + : image (image_) + { + image->multiplyAllAlphas (0.8f); + setAlwaysOnTop (true); + } + + ~DragOverlayComp() + { + delete image; + } + + void paint (Graphics& g) + { + g.drawImageAt (image, 0, 0); + } + +private: + Image* image; + + DragOverlayComp (const DragOverlayComp&); + const DragOverlayComp& operator= (const DragOverlayComp&); +}; + +TableHeaderComponent::TableHeaderComponent() + : listeners (2), + dragOverlayComp (0), + columnsChanged (false), + columnsResized (false), + sortChanged (false), + menuActive (true), + stretchToFit (false), + columnIdBeingResized (0), + columnIdBeingDragged (0), + columnIdUnderMouse (0), + lastDeliberateWidth (0) +{ +} + +TableHeaderComponent::~TableHeaderComponent() +{ + delete dragOverlayComp; +} + +void TableHeaderComponent::setPopupMenuActive (const bool hasMenu) +{ + menuActive = hasMenu; +} + +bool TableHeaderComponent::isPopupMenuActive() const throw() { return menuActive; } + +int TableHeaderComponent::getNumColumns (const bool onlyCountVisibleColumns) const throw() +{ + if (onlyCountVisibleColumns) + { + int num = 0; + + for (int i = columns.size(); --i >= 0;) + if (columns.getUnchecked(i)->isVisible()) + ++num; + + return num; + } + else + { + return columns.size(); + } +} + +const String TableHeaderComponent::getColumnName (const int columnId) const throw() +{ + const ColumnInfo* const ci = getInfoForId (columnId); + return ci != 0 ? ci->name : String::empty; +} + +void TableHeaderComponent::setColumnName (const int columnId, const String& newName) +{ + ColumnInfo* const ci = getInfoForId (columnId); + + if (ci != 0 && ci->name != newName) + { + ci->name = newName; + sendColumnsChanged(); + } +} + +void TableHeaderComponent::addColumn (const String& columnName, + const int columnId, + const int width, + const int minimumWidth, + const int maximumWidth, + const int propertyFlags, + const int insertIndex) +{ + // can't have a duplicate or null ID! + jassert (columnId != 0 && getIndexOfColumnId (columnId, false) < 0); + jassert (width > 0); + + ColumnInfo* const ci = new ColumnInfo(); + ci->name = columnName; + ci->id = columnId; + ci->width = width; + ci->lastDeliberateWidth = width; + ci->minimumWidth = minimumWidth; + ci->maximumWidth = maximumWidth; + if (ci->maximumWidth < 0) + ci->maximumWidth = INT_MAX; + jassert (ci->maximumWidth >= ci->minimumWidth); + ci->propertyFlags = propertyFlags; + + columns.insert (insertIndex, ci); + sendColumnsChanged(); +} + +void TableHeaderComponent::removeColumn (const int columnIdToRemove) +{ + const int index = getIndexOfColumnId (columnIdToRemove, false); + + if (index >= 0) + { + columns.remove (index); + sortChanged = true; + sendColumnsChanged(); + } +} + +void TableHeaderComponent::removeAllColumns() +{ + if (columns.size() > 0) + { + columns.clear(); + sendColumnsChanged(); + } +} + +void TableHeaderComponent::moveColumn (const int columnId, int newIndex) +{ + const int currentIndex = getIndexOfColumnId (columnId, false); + newIndex = visibleIndexToTotalIndex (newIndex); + + if (columns [currentIndex] != 0 && currentIndex != newIndex) + { + columns.move (currentIndex, newIndex); + sendColumnsChanged(); + } +} + +void TableHeaderComponent::setColumnWidth (const int columnId, const int newWidth) +{ + ColumnInfo* const ci = getInfoForId (columnId); + + if (ci != 0 && ci->width != newWidth) + { + const int numColumns = getNumColumns (true); + + ci->lastDeliberateWidth = ci->width + = jlimit (ci->minimumWidth, ci->maximumWidth, newWidth); + + if (stretchToFit) + { + const int index = getIndexOfColumnId (columnId, true) + 1; + + if (((unsigned int) index) < (unsigned int) numColumns) + { + const int x = getColumnPosition (index).getX(); + + if (lastDeliberateWidth == 0) + lastDeliberateWidth = getTotalWidth(); + + resizeColumnsToFit (visibleIndexToTotalIndex (index), lastDeliberateWidth - x); + } + } + + repaint(); + columnsResized = true; + triggerAsyncUpdate(); + } +} + +int TableHeaderComponent::getIndexOfColumnId (const int columnId, const bool onlyCountVisibleColumns) const throw() +{ + int n = 0; + + for (int i = 0; i < columns.size(); ++i) + { + if ((! onlyCountVisibleColumns) || columns.getUnchecked(i)->isVisible()) + { + if (columns.getUnchecked(i)->id == columnId) + return n; + + ++n; + } + } + + return -1; +} + +int TableHeaderComponent::getColumnIdOfIndex (int index, const bool onlyCountVisibleColumns) const throw() +{ + if (onlyCountVisibleColumns) + index = visibleIndexToTotalIndex (index); + + const ColumnInfo* const ci = columns [index]; + return (ci != 0) ? ci->id : 0; +} + +const Rectangle TableHeaderComponent::getColumnPosition (const int index) const throw() +{ + int x = 0, width = 0, n = 0; + + for (int i = 0; i < columns.size(); ++i) + { + x += width; + + if (columns.getUnchecked(i)->isVisible()) + { + width = columns.getUnchecked(i)->width; + + if (n++ == index) + break; + } + else + { + width = 0; + } + } + + return Rectangle (x, 0, width, getHeight()); +} + +int TableHeaderComponent::getColumnIdAtX (const int xToFind) const throw() +{ + if (xToFind >= 0) + { + int x = 0; + + for (int i = 0; i < columns.size(); ++i) + { + const ColumnInfo* const ci = columns.getUnchecked(i); + + if (ci->isVisible()) + { + x += ci->width; + + if (xToFind < x) + return ci->id; + } + } + } + + return 0; +} + +int TableHeaderComponent::getTotalWidth() const throw() +{ + int w = 0; + + for (int i = columns.size(); --i >= 0;) + if (columns.getUnchecked(i)->isVisible()) + w += columns.getUnchecked(i)->width; + + return w; +} + +void TableHeaderComponent::setStretchToFitActive (const bool shouldStretchToFit) +{ + stretchToFit = shouldStretchToFit; + lastDeliberateWidth = getTotalWidth(); + resized(); +} + +bool TableHeaderComponent::isStretchToFitActive() const throw() +{ + return stretchToFit; +} + +void TableHeaderComponent::resizeAllColumnsToFit (int targetTotalWidth) +{ + if (stretchToFit && getWidth() > 0 + && columnIdBeingResized == 0 && columnIdBeingDragged == 0) + { + lastDeliberateWidth = targetTotalWidth; + resizeColumnsToFit (0, targetTotalWidth); + } +} + +void TableHeaderComponent::resizeColumnsToFit (int firstColumnIndex, int targetTotalWidth) +{ + targetTotalWidth = jmax (targetTotalWidth, 0); + + StretchableObjectResizer sor; + int i; + for (i = firstColumnIndex; i < columns.size(); ++i) + { + ColumnInfo* const ci = columns.getUnchecked(i); + + if (ci->isVisible()) + sor.addItem (ci->lastDeliberateWidth, ci->minimumWidth, ci->maximumWidth); + } + + sor.resizeToFit (targetTotalWidth); + + int visIndex = 0; + for (i = firstColumnIndex; i < columns.size(); ++i) + { + ColumnInfo* const ci = columns.getUnchecked(i); + + if (ci->isVisible()) + { + const int newWidth = jlimit (ci->minimumWidth, ci->maximumWidth, + (int) floor (sor.getItemSize (visIndex++))); + + if (newWidth != ci->width) + { + ci->width = newWidth; + repaint(); + columnsResized = true; + triggerAsyncUpdate(); + } + } + } +} + +void TableHeaderComponent::setColumnVisible (const int columnId, const bool shouldBeVisible) +{ + ColumnInfo* const ci = getInfoForId (columnId); + + if (ci != 0 && shouldBeVisible != ci->isVisible()) + { + if (shouldBeVisible) + ci->propertyFlags |= visible; + else + ci->propertyFlags &= ~visible; + + sendColumnsChanged(); + resized(); + } +} + +bool TableHeaderComponent::isColumnVisible (const int columnId) const +{ + const ColumnInfo* const ci = getInfoForId (columnId); + return ci != 0 && ci->isVisible(); +} + +void TableHeaderComponent::setSortColumnId (const int columnId, const bool sortForwards) +{ + if (getSortColumnId() != columnId || isSortedForwards() != sortForwards) + { + for (int i = columns.size(); --i >= 0;) + columns.getUnchecked(i)->propertyFlags &= ~(sortedForwards | sortedBackwards); + + ColumnInfo* const ci = getInfoForId (columnId); + + if (ci != 0) + ci->propertyFlags |= (sortForwards ? sortedForwards : sortedBackwards); + + reSortTable(); + } +} + +int TableHeaderComponent::getSortColumnId() const throw() +{ + for (int i = columns.size(); --i >= 0;) + if ((columns.getUnchecked(i)->propertyFlags & (sortedForwards | sortedBackwards)) != 0) + return columns.getUnchecked(i)->id; + + return 0; +} + +bool TableHeaderComponent::isSortedForwards() const throw() +{ + for (int i = columns.size(); --i >= 0;) + if ((columns.getUnchecked(i)->propertyFlags & (sortedForwards | sortedBackwards)) != 0) + return (columns.getUnchecked(i)->propertyFlags & sortedForwards) != 0; + + return true; +} + +void TableHeaderComponent::reSortTable() +{ + sortChanged = true; + repaint(); + triggerAsyncUpdate(); +} + +const String TableHeaderComponent::toString() const +{ + String s; + + XmlElement doc (T("TABLELAYOUT")); + + doc.setAttribute (T("sortedCol"), getSortColumnId()); + doc.setAttribute (T("sortForwards"), isSortedForwards()); + + for (int i = 0; i < columns.size(); ++i) + { + const ColumnInfo* const ci = columns.getUnchecked (i); + + XmlElement* const e = new XmlElement (T("COLUMN")); + doc.addChildElement (e); + + e->setAttribute (T("id"), ci->id); + e->setAttribute (T("visible"), ci->isVisible()); + e->setAttribute (T("width"), ci->width); + } + + return doc.createDocument (String::empty, true, false); +} + +void TableHeaderComponent::restoreFromString (const String& storedVersion) +{ + XmlDocument doc (storedVersion); + XmlElement* const storedXml = doc.getDocumentElement(); + + int index = 0; + + if (storedXml != 0 && storedXml->hasTagName (T("TABLELAYOUT"))) + { + forEachXmlChildElement (*storedXml, col) + { + const int tabId = col->getIntAttribute (T("id")); + + ColumnInfo* const ci = getInfoForId (tabId); + + if (ci != 0) + { + columns.move (columns.indexOf (ci), index); + ci->width = col->getIntAttribute (T("width")); + setColumnVisible (tabId, col->getBoolAttribute (T("visible"))); + } + + ++index; + } + + columnsResized = true; + sendColumnsChanged(); + + setSortColumnId (storedXml->getIntAttribute (T("sortedCol")), + storedXml->getBoolAttribute (T("sortForwards"), true)); + } + + delete storedXml; +} + +void TableHeaderComponent::addListener (TableHeaderListener* const newListener) throw() +{ + listeners.addIfNotAlreadyThere (newListener); +} + +void TableHeaderComponent::removeListener (TableHeaderListener* const listenerToRemove) throw() +{ + listeners.removeValue (listenerToRemove); +} + +void TableHeaderComponent::columnClicked (int columnId, const ModifierKeys& mods) +{ + const ColumnInfo* const ci = getInfoForId (columnId); + + if (ci != 0 && (ci->propertyFlags & sortable) != 0 && ! mods.isPopupMenu()) + setSortColumnId (columnId, (ci->propertyFlags & sortedForwards) == 0); +} + +void TableHeaderComponent::addMenuItems (PopupMenu& menu, const int /*columnIdClicked*/) +{ + for (int i = 0; i < columns.size(); ++i) + { + const ColumnInfo* const ci = columns.getUnchecked(i); + + if ((ci->propertyFlags & appearsOnColumnMenu) != 0) + menu.addItem (ci->id, ci->name, + (ci->propertyFlags & (sortedForwards | sortedBackwards)) == 0, + isColumnVisible (ci->id)); + } +} + +void TableHeaderComponent::reactToMenuItem (const int menuReturnId, const int /*columnIdClicked*/) +{ + if (getIndexOfColumnId (menuReturnId, false) >= 0) + setColumnVisible (menuReturnId, ! isColumnVisible (menuReturnId)); +} + +void TableHeaderComponent::paint (Graphics& g) +{ + LookAndFeel& lf = getLookAndFeel(); + + lf.drawTableHeaderBackground (g, *this); + + const Rectangle clip (g.getClipBounds()); + + int x = 0; + for (int i = 0; i < columns.size(); ++i) + { + const ColumnInfo* const ci = columns.getUnchecked(i); + + if (ci->isVisible()) + { + if (x + ci->width > clip.getX() + && (ci->id != columnIdBeingDragged + || dragOverlayComp == 0 + || ! dragOverlayComp->isVisible())) + { + g.saveState(); + g.setOrigin (x, 0); + g.reduceClipRegion (0, 0, ci->width, getHeight()); + + lf.drawTableHeaderColumn (g, ci->name, ci->id, ci->width, getHeight(), + ci->id == columnIdUnderMouse, + ci->id == columnIdUnderMouse && isMouseButtonDown(), + ci->propertyFlags); + + g.restoreState(); + } + + x += ci->width; + + if (x >= clip.getRight()) + break; + } + } +} + +void TableHeaderComponent::resized() +{ +} + +void TableHeaderComponent::mouseMove (const MouseEvent& e) +{ + updateColumnUnderMouse (e.x, e.y); +} + +void TableHeaderComponent::mouseEnter (const MouseEvent& e) +{ + updateColumnUnderMouse (e.x, e.y); +} + +void TableHeaderComponent::mouseExit (const MouseEvent& e) +{ + updateColumnUnderMouse (e.x, e.y); +} + +void TableHeaderComponent::mouseDown (const MouseEvent& e) +{ + repaint(); + columnIdBeingResized = 0; + columnIdBeingDragged = 0; + + if (columnIdUnderMouse != 0) + { + draggingColumnOffset = e.x - getColumnPosition (getIndexOfColumnId (columnIdUnderMouse, true)).getX(); + + if (e.mods.isPopupMenu()) + columnClicked (columnIdUnderMouse, e.mods); + } + + if (menuActive && e.mods.isPopupMenu()) + showColumnChooserMenu (columnIdUnderMouse); +} + +void TableHeaderComponent::mouseDrag (const MouseEvent& e) +{ + if (columnIdBeingResized == 0 + && columnIdBeingDragged == 0 + && ! (e.mouseWasClicked() || e.mods.isPopupMenu())) + { + deleteAndZero (dragOverlayComp); + + columnIdBeingResized = getResizeDraggerAt (e.getMouseDownX()); + + if (columnIdBeingResized != 0) + { + const ColumnInfo* const ci = getInfoForId (columnIdBeingResized); + initialColumnWidth = ci->width; + } + else + { + beginDrag (e); + } + } + + if (columnIdBeingResized != 0) + { + const ColumnInfo* const ci = getInfoForId (columnIdBeingResized); + + if (ci != 0) + { + int w = jlimit (ci->minimumWidth, ci->maximumWidth, + initialColumnWidth + e.getDistanceFromDragStartX()); + + if (stretchToFit) + { + // prevent us dragging a column too far right if we're in stretch-to-fit mode + int minWidthOnRight = 0; + for (int i = getIndexOfColumnId (columnIdBeingResized, false) + 1; i < columns.size(); ++i) + if (columns.getUnchecked (i)->isVisible()) + minWidthOnRight += columns.getUnchecked (i)->minimumWidth; + + const Rectangle currentPos (getColumnPosition (getIndexOfColumnId (columnIdBeingResized, true))); + w = jmax (ci->minimumWidth, jmin (w, getWidth() - minWidthOnRight - currentPos.getX())); + } + + setColumnWidth (columnIdBeingResized, w); + } + } + else if (columnIdBeingDragged != 0) + { + if (e.y >= -50 && e.y < getHeight() + 50) + { + beginDrag (e); + + if (dragOverlayComp != 0) + { + dragOverlayComp->setVisible (true); + dragOverlayComp->setBounds (jlimit (0, + jmax (0, getTotalWidth() - dragOverlayComp->getWidth()), + e.x - draggingColumnOffset), + 0, + dragOverlayComp->getWidth(), + getHeight()); + + for (int i = columns.size(); --i >= 0;) + { + const int currentIndex = getIndexOfColumnId (columnIdBeingDragged, true); + int newIndex = currentIndex; + + if (newIndex > 0) + { + // if the previous column isn't draggable, we can't move our column + // past it, because that'd change the undraggable column's position.. + const ColumnInfo* const previous = columns.getUnchecked (newIndex - 1); + + if ((previous->propertyFlags & draggable) != 0) + { + const int leftOfPrevious = getColumnPosition (newIndex - 1).getX(); + const int rightOfCurrent = getColumnPosition (newIndex).getRight(); + + if (abs (dragOverlayComp->getX() - leftOfPrevious) + < abs (dragOverlayComp->getRight() - rightOfCurrent)) + { + --newIndex; + } + } + } + + if (newIndex < columns.size() - 1) + { + // if the next column isn't draggable, we can't move our column + // past it, because that'd change the undraggable column's position.. + const ColumnInfo* const nextCol = columns.getUnchecked (newIndex + 1); + + if ((nextCol->propertyFlags & draggable) != 0) + { + const int leftOfCurrent = getColumnPosition (newIndex).getX(); + const int rightOfNext = getColumnPosition (newIndex + 1).getRight(); + + if (abs (dragOverlayComp->getX() - leftOfCurrent) + > abs (dragOverlayComp->getRight() - rightOfNext)) + { + ++newIndex; + } + } + } + + if (newIndex != currentIndex) + moveColumn (columnIdBeingDragged, newIndex); + else + break; + } + } + } + else + { + endDrag (draggingColumnOriginalIndex); + } + } +} + +void TableHeaderComponent::beginDrag (const MouseEvent& e) +{ + if (columnIdBeingDragged == 0) + { + columnIdBeingDragged = getColumnIdAtX (e.getMouseDownX()); + + const ColumnInfo* const ci = getInfoForId (columnIdBeingDragged); + + if (ci == 0 || (ci->propertyFlags & draggable) == 0) + { + columnIdBeingDragged = 0; + } + else + { + draggingColumnOriginalIndex = getIndexOfColumnId (columnIdBeingDragged, true); + + const Rectangle columnRect (getColumnPosition (draggingColumnOriginalIndex)); + + const int temp = columnIdBeingDragged; + columnIdBeingDragged = 0; + + addAndMakeVisible (dragOverlayComp = new DragOverlayComp (createComponentSnapshot (columnRect, false))); + columnIdBeingDragged = temp; + + dragOverlayComp->setBounds (columnRect); + + for (int i = listeners.size(); --i >= 0;) + { + listeners.getUnchecked(i)->tableColumnDraggingChanged (this, columnIdBeingDragged); + i = jmin (i, listeners.size() - 1); + } + } + } +} + +void TableHeaderComponent::endDrag (const int finalIndex) +{ + if (columnIdBeingDragged != 0) + { + moveColumn (columnIdBeingDragged, finalIndex); + + columnIdBeingDragged = 0; + repaint(); + + for (int i = listeners.size(); --i >= 0;) + { + listeners.getUnchecked(i)->tableColumnDraggingChanged (this, 0); + i = jmin (i, listeners.size() - 1); + } + } +} + +void TableHeaderComponent::mouseUp (const MouseEvent& e) +{ + mouseDrag (e); + + for (int i = columns.size(); --i >= 0;) + if (columns.getUnchecked (i)->isVisible()) + columns.getUnchecked (i)->lastDeliberateWidth = columns.getUnchecked (i)->width; + + columnIdBeingResized = 0; + repaint(); + + endDrag (getIndexOfColumnId (columnIdBeingDragged, true)); + + updateColumnUnderMouse (e.x, e.y); + + if (columnIdUnderMouse != 0 && e.mouseWasClicked() && ! e.mods.isPopupMenu()) + columnClicked (columnIdUnderMouse, e.mods); + + deleteAndZero (dragOverlayComp); +} + +const MouseCursor TableHeaderComponent::getMouseCursor() +{ + int x, y; + getMouseXYRelative (x, y); + + if (columnIdBeingResized != 0 || (getResizeDraggerAt (x) != 0 && ! isMouseButtonDown())) + return MouseCursor (MouseCursor::LeftRightResizeCursor); + + return Component::getMouseCursor(); +} + +bool TableHeaderComponent::ColumnInfo::isVisible() const throw() +{ + return (propertyFlags & TableHeaderComponent::visible) != 0; +} + +TableHeaderComponent::ColumnInfo* TableHeaderComponent::getInfoForId (const int id) const throw() +{ + for (int i = columns.size(); --i >= 0;) + if (columns.getUnchecked(i)->id == id) + return columns.getUnchecked(i); + + return 0; +} + +int TableHeaderComponent::visibleIndexToTotalIndex (const int visibleIndex) const throw() +{ + int n = 0; + + for (int i = 0; i < columns.size(); ++i) + { + if (columns.getUnchecked(i)->isVisible()) + { + if (n == visibleIndex) + return i; + + ++n; + } + } + + return -1; +} + +void TableHeaderComponent::sendColumnsChanged() +{ + if (stretchToFit && lastDeliberateWidth > 0) + resizeAllColumnsToFit (lastDeliberateWidth); + + repaint(); + columnsChanged = true; + triggerAsyncUpdate(); +} + +void TableHeaderComponent::handleAsyncUpdate() +{ + const bool changed = columnsChanged || sortChanged; + const bool sized = columnsResized || changed; + const bool sorted = sortChanged; + columnsChanged = false; + columnsResized = false; + sortChanged = false; + + if (sorted) + { + for (int i = listeners.size(); --i >= 0;) + { + listeners.getUnchecked(i)->tableSortOrderChanged (this); + i = jmin (i, listeners.size() - 1); + } + } + + if (changed) + { + for (int i = listeners.size(); --i >= 0;) + { + listeners.getUnchecked(i)->tableColumnsChanged (this); + i = jmin (i, listeners.size() - 1); + } + } + + if (sized) + { + for (int i = listeners.size(); --i >= 0;) + { + listeners.getUnchecked(i)->tableColumnsResized (this); + i = jmin (i, listeners.size() - 1); + } + } +} + +int TableHeaderComponent::getResizeDraggerAt (const int mouseX) const throw() +{ + if (((unsigned int) mouseX) < (unsigned int) getWidth()) + { + const int draggableDistance = 3; + int x = 0; + + for (int i = 0; i < columns.size(); ++i) + { + const ColumnInfo* const ci = columns.getUnchecked(i); + + if (ci->isVisible()) + { + if (abs (mouseX - (x + ci->width)) <= draggableDistance + && (ci->propertyFlags & resizable) != 0) + return ci->id; + + x += ci->width; + } + } + } + + return 0; +} + +void TableHeaderComponent::updateColumnUnderMouse (int x, int y) +{ + const int newCol = (reallyContains (x, y, true) && getResizeDraggerAt (x) == 0) + ? getColumnIdAtX (x) : 0; + + if (newCol != columnIdUnderMouse) + { + columnIdUnderMouse = newCol; + repaint(); + } +} + +void TableHeaderComponent::showColumnChooserMenu (const int columnIdClicked) +{ + PopupMenu m; + addMenuItems (m, columnIdClicked); + + if (m.getNumItems() > 0) + { + const int result = m.show(); + + if (result != 0) + reactToMenuItem (result, columnIdClicked); + } +} + +void TableHeaderListener::tableColumnDraggingChanged (TableHeaderComponent*, int) +{ +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_TableHeaderComponent.cpp *********/ + +/********* Start of inlined file: juce_TableListBox.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +static const tchar* const tableColumnPropertyTag = T("_tableColumnID"); + +class TableListRowComp : public Component +{ +public: + TableListRowComp (TableListBox& owner_) + : owner (owner_), + row (-1), + isSelected (false) + { + } + + ~TableListRowComp() + { + deleteAllChildren(); + } + + void paint (Graphics& g) + { + TableListBoxModel* const model = owner.getModel(); + + if (model != 0) + { + const TableHeaderComponent* const header = owner.getHeader(); + + model->paintRowBackground (g, row, getWidth(), getHeight(), isSelected); + + const int numColumns = header->getNumColumns (true); + + for (int i = 0; i < numColumns; ++i) + { + if (! columnsWithComponents [i]) + { + const int columnId = header->getColumnIdOfIndex (i, true); + Rectangle columnRect (header->getColumnPosition (i)); + columnRect.setSize (columnRect.getWidth(), getHeight()); + + g.saveState(); + + g.reduceClipRegion (columnRect); + g.setOrigin (columnRect.getX(), 0); + + model->paintCell (g, row, columnId, columnRect.getWidth(), columnRect.getHeight(), isSelected); + + g.restoreState(); + } + } + } + } + + void update (const int newRow, const bool isNowSelected) + { + if (newRow != row || isNowSelected != isSelected) + { + row = newRow; + isSelected = isNowSelected; + repaint(); + } + + if (row < owner.getNumRows()) + { + jassert (row >= 0); + + const tchar* const tagPropertyName = T("_tableLastUseNum"); + const int newTag = Random::getSystemRandom().nextInt(); + + const TableHeaderComponent* const header = owner.getHeader(); + const int numColumns = header->getNumColumns (true); + int i; + + columnsWithComponents.clear(); + + if (owner.getModel() != 0) + { + for (i = 0; i < numColumns; ++i) + { + const int columnId = header->getColumnIdOfIndex (i, true); + + Component* const newComp + = owner.getModel()->refreshComponentForCell (row, columnId, isSelected, + findChildComponentForColumn (columnId)); + + if (newComp != 0) + { + addAndMakeVisible (newComp); + newComp->setComponentProperty (tagPropertyName, newTag); + newComp->setComponentProperty (tableColumnPropertyTag, columnId); + + const Rectangle columnRect (header->getColumnPosition (i)); + newComp->setBounds (columnRect.getX(), 0, columnRect.getWidth(), getHeight()); + + columnsWithComponents.setBit (i); + } + } + } + + for (i = getNumChildComponents(); --i >= 0;) + { + Component* const c = getChildComponent (i); + + if (c->getComponentPropertyInt (tagPropertyName, false, 0) != newTag) + delete c; + } + } + else + { + columnsWithComponents.clear(); + deleteAllChildren(); + } + } + + void resized() + { + for (int i = getNumChildComponents(); --i >= 0;) + { + Component* const c = getChildComponent (i); + + const int columnId = c->getComponentPropertyInt (tableColumnPropertyTag, false, 0); + + if (columnId != 0) + { + const Rectangle columnRect (owner.getHeader()->getColumnPosition (owner.getHeader()->getIndexOfColumnId (columnId, true))); + c->setBounds (columnRect.getX(), 0, columnRect.getWidth(), getHeight()); + } + } + } + + void mouseDown (const MouseEvent& e) + { + isDragging = false; + selectRowOnMouseUp = false; + + if (isEnabled()) + { + if (! isSelected) + { + owner.selectRowsBasedOnModifierKeys (row, e.mods); + + const int columnId = owner.getHeader()->getColumnIdAtX (e.x); + + if (columnId != 0 && owner.getModel() != 0) + owner.getModel()->cellClicked (row, columnId, e); + } + else + { + selectRowOnMouseUp = true; + } + } + } + + void mouseDrag (const MouseEvent& e) + { + if (isEnabled() && owner.getModel() != 0 && ! (e.mouseWasClicked() || isDragging)) + { + const SparseSet selectedRows (owner.getSelectedRows()); + + if (selectedRows.size() > 0) + { + const String dragDescription (owner.getModel()->getDragSourceDescription (selectedRows)); + + if (dragDescription.isNotEmpty()) + { + isDragging = true; + + DragAndDropContainer* const dragContainer + = DragAndDropContainer::findParentDragContainerFor (this); + + if (dragContainer != 0) + { + Image* dragImage = owner.createSnapshotOfSelectedRows(); + dragImage->multiplyAllAlphas (0.6f); + + dragContainer->startDragging (dragDescription, &owner, dragImage, true); + } + else + { + // to be able to do a drag-and-drop operation, the listbox needs to + // be inside a component which is also a DragAndDropContainer. + jassertfalse + } + } + } + } + } + + void mouseUp (const MouseEvent& e) + { + if (selectRowOnMouseUp && e.mouseWasClicked() && isEnabled()) + { + owner.selectRowsBasedOnModifierKeys (row, e.mods); + + const int columnId = owner.getHeader()->getColumnIdAtX (e.x); + + if (columnId != 0 && owner.getModel() != 0) + owner.getModel()->cellClicked (row, columnId, e); + } + } + + void mouseDoubleClick (const MouseEvent& e) + { + const int columnId = owner.getHeader()->getColumnIdAtX (e.x); + + if (columnId != 0 && owner.getModel() != 0) + owner.getModel()->cellDoubleClicked (row, columnId, e); + } + + juce_UseDebuggingNewOperator + +private: + TableListBox& owner; + int row; + bool isSelected, isDragging, selectRowOnMouseUp; + BitArray columnsWithComponents; + + Component* findChildComponentForColumn (const int columnId) const + { + for (int i = getNumChildComponents(); --i >= 0;) + { + Component* const c = getChildComponent (i); + + if (c->getComponentPropertyInt (tableColumnPropertyTag, false, 0) == columnId) + return c; + } + + return 0; + } + + TableListRowComp (const TableListRowComp&); + const TableListRowComp& operator= (const TableListRowComp&); +}; + +class TableListBoxHeader : public TableHeaderComponent +{ +public: + TableListBoxHeader (TableListBox& owner_) + : owner (owner_) + { + } + + ~TableListBoxHeader() + { + } + + void addMenuItems (PopupMenu& menu, const int columnIdClicked) + { + if (owner.isAutoSizeMenuOptionShown()) + { + menu.addItem (0xf836743, TRANS("Auto-size this column"), columnIdClicked != 0); + menu.addItem (0xf836744, TRANS("Auto-size all columns"), owner.getHeader()->getNumColumns (true) > 0); + menu.addSeparator(); + } + + TableHeaderComponent::addMenuItems (menu, columnIdClicked); + } + + void reactToMenuItem (const int menuReturnId, const int columnIdClicked) + { + if (menuReturnId == 0xf836743) + { + owner.autoSizeColumn (columnIdClicked); + } + else if (menuReturnId == 0xf836744) + { + owner.autoSizeAllColumns(); + } + else + { + TableHeaderComponent::reactToMenuItem (menuReturnId, columnIdClicked); + } + } + + juce_UseDebuggingNewOperator + +private: + TableListBox& owner; + + TableListBoxHeader (const TableListBoxHeader&); + const TableListBoxHeader& operator= (const TableListBoxHeader&); +}; + +TableListBox::TableListBox (const String& name, TableListBoxModel* const model_) + : ListBox (name, 0), + model (model_), + autoSizeOptionsShown (true) +{ + ListBox::model = this; + + header = new TableListBoxHeader (*this); + header->setSize (100, 28); + header->addListener (this); + + setHeaderComponent (header); +} + +TableListBox::~TableListBox() +{ + deleteAllChildren(); +} + +void TableListBox::setModel (TableListBoxModel* const newModel) +{ + if (model != newModel) + { + model = newModel; + updateContent(); + } +} + +int TableListBox::getHeaderHeight() const throw() +{ + return header->getHeight(); +} + +void TableListBox::setHeaderHeight (const int newHeight) +{ + header->setSize (header->getWidth(), newHeight); + resized(); +} + +void TableListBox::autoSizeColumn (const int columnId) +{ + const int width = model != 0 ? model->getColumnAutoSizeWidth (columnId) : 0; + + if (width > 0) + header->setColumnWidth (columnId, width); +} + +void TableListBox::autoSizeAllColumns() +{ + for (int i = 0; i < header->getNumColumns (true); ++i) + autoSizeColumn (header->getColumnIdOfIndex (i, true)); +} + +void TableListBox::setAutoSizeMenuOptionShown (const bool shouldBeShown) +{ + autoSizeOptionsShown = shouldBeShown; +} + +bool TableListBox::isAutoSizeMenuOptionShown() const throw() +{ + return autoSizeOptionsShown; +} + +const Rectangle TableListBox::getCellPosition (const int columnId, + const int rowNumber, + const bool relativeToComponentTopLeft) const +{ + Rectangle headerCell (header->getColumnPosition (header->getIndexOfColumnId (columnId, true))); + + if (relativeToComponentTopLeft) + headerCell.translate (header->getX(), 0); + + const Rectangle row (getRowPosition (rowNumber, relativeToComponentTopLeft)); + + return Rectangle (headerCell.getX(), row.getY(), + headerCell.getWidth(), row.getHeight()); +} + +void TableListBox::scrollToEnsureColumnIsOnscreen (const int columnId) +{ + ScrollBar* const scrollbar = getHorizontalScrollBar(); + + if (scrollbar != 0) + { + const Rectangle pos (header->getColumnPosition (header->getIndexOfColumnId (columnId, true))); + + double x = scrollbar->getCurrentRangeStart(); + const double w = scrollbar->getCurrentRangeSize(); + + if (pos.getX() < x) + x = pos.getX(); + else if (pos.getRight() > x + w) + x += jmax (0.0, pos.getRight() - (x + w)); + + scrollbar->setCurrentRangeStart (x); + } +} + +int TableListBox::getNumRows() +{ + return model != 0 ? model->getNumRows() : 0; +} + +void TableListBox::paintListBoxItem (int, Graphics&, int, int, bool) +{ +} + +Component* TableListBox::refreshComponentForRow (int rowNumber, bool isRowSelected, Component* existingComponentToUpdate) +{ + if (existingComponentToUpdate == 0) + existingComponentToUpdate = new TableListRowComp (*this); + + ((TableListRowComp*) existingComponentToUpdate)->update (rowNumber, isRowSelected); + + return existingComponentToUpdate; +} + +void TableListBox::selectedRowsChanged (int row) +{ + if (model != 0) + model->selectedRowsChanged (row); +} + +void TableListBox::deleteKeyPressed (int row) +{ + if (model != 0) + model->deleteKeyPressed (row); +} + +void TableListBox::returnKeyPressed (int row) +{ + if (model != 0) + model->returnKeyPressed (row); +} + +void TableListBox::backgroundClicked() +{ + if (model != 0) + model->backgroundClicked(); +} + +void TableListBox::listWasScrolled() +{ + if (model != 0) + model->listWasScrolled(); +} + +void TableListBox::tableColumnsChanged (TableHeaderComponent*) +{ + setMinimumContentWidth (header->getTotalWidth()); + repaint(); + updateColumnComponents(); +} + +void TableListBox::tableColumnsResized (TableHeaderComponent*) +{ + setMinimumContentWidth (header->getTotalWidth()); + repaint(); + updateColumnComponents(); +} + +void TableListBox::tableSortOrderChanged (TableHeaderComponent*) +{ + if (model != 0) + model->sortOrderChanged (header->getSortColumnId(), + header->isSortedForwards()); +} + +void TableListBox::tableColumnDraggingChanged (TableHeaderComponent*, int columnIdNowBeingDragged_) +{ + columnIdNowBeingDragged = columnIdNowBeingDragged_; + repaint(); +} + +void TableListBox::resized() +{ + ListBox::resized(); + + header->resizeAllColumnsToFit (getVisibleContentWidth()); + setMinimumContentWidth (header->getTotalWidth()); +} + +void TableListBox::updateColumnComponents() const +{ + const int firstRow = getRowContainingPosition (0, 0); + + for (int i = firstRow + getNumRowsOnScreen() + 2; --i >= firstRow;) + { + TableListRowComp* const rowComp = dynamic_cast (getComponentForRowNumber (i)); + + if (rowComp != 0) + rowComp->resized(); + } +} + +void TableListBoxModel::cellClicked (int, int, const MouseEvent&) +{ +} + +void TableListBoxModel::cellDoubleClicked (int, int, const MouseEvent&) +{ +} + +void TableListBoxModel::backgroundClicked() +{ +} + +void TableListBoxModel::sortOrderChanged (int, const bool) +{ +} + +int TableListBoxModel::getColumnAutoSizeWidth (int) +{ + return 0; +} + +void TableListBoxModel::selectedRowsChanged (int) +{ +} + +void TableListBoxModel::deleteKeyPressed (int) +{ +} + +void TableListBoxModel::returnKeyPressed (int) +{ +} + +void TableListBoxModel::listWasScrolled() +{ +} + +const String TableListBoxModel::getDragSourceDescription (const SparseSet&) +{ + return String::empty; +} + +Component* TableListBoxModel::refreshComponentForCell (int, int, bool, Component* existingComponentToUpdate) +{ + (void) existingComponentToUpdate; + jassert (existingComponentToUpdate == 0); // indicates a failure in the code the recycles the components + return 0; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_TableListBox.cpp *********/ + +/********* Start of inlined file: juce_TextEditor.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +#define SHOULD_WRAP(x, wrapwidth) (((x) - 0.0001f) >= (wrapwidth)) + +// a word or space that can't be broken down any further +struct TextAtom +{ + + String atomText; + float width; + uint16 numChars; + + bool isWhitespace() const throw() { return CharacterFunctions::isWhitespace (atomText[0]); } + bool isNewLine() const throw() { return atomText[0] == T('\r') || atomText[0] == T('\n'); } + + const String getText (const tchar passwordCharacter) const throw() + { + if (passwordCharacter == 0) + return atomText; + else + return String::repeatedString (String::charToString (passwordCharacter), + atomText.length()); + } + + const String getTrimmedText (const tchar passwordCharacter) const throw() + { + if (passwordCharacter == 0) + return atomText.substring (0, numChars); + else if (isNewLine()) + return String::empty; + else + return String::repeatedString (String::charToString (passwordCharacter), numChars); + } +}; + +// a run of text with a single font and colour +class UniformTextSection +{ +public: + + UniformTextSection (const String& text, + const Font& font_, + const Colour& colour_, + const tchar passwordCharacter) throw() + : font (font_), + colour (colour_), + atoms (64) + { + initialiseAtoms (text, passwordCharacter); + } + + UniformTextSection (const UniformTextSection& other) throw() + : font (other.font), + colour (other.colour), + atoms (64) + { + for (int i = 0; i < other.atoms.size(); ++i) + atoms.add (new TextAtom (*(const TextAtom*) other.atoms.getUnchecked(i))); + } + + ~UniformTextSection() throw() + { + // (no need to delete the atoms, as they're explicitly deleted by the caller) + } + + void clear() throw() + { + for (int i = atoms.size(); --i >= 0;) + { + TextAtom* const atom = getAtom(i); + delete atom; + } + + atoms.clear(); + } + + int getNumAtoms() const throw() + { + return atoms.size(); + } + + TextAtom* getAtom (const int index) const throw() + { + return (TextAtom*) atoms.getUnchecked (index); + } + + void append (const UniformTextSection& other, const tchar passwordCharacter) throw() + { + if (other.atoms.size() > 0) + { + TextAtom* const lastAtom = (TextAtom*) atoms.getLast(); + int i = 0; + + if (lastAtom != 0) + { + if (! CharacterFunctions::isWhitespace (lastAtom->atomText.getLastCharacter())) + { + TextAtom* const first = other.getAtom(0); + + if (! CharacterFunctions::isWhitespace (first->atomText[0])) + { + lastAtom->atomText += first->atomText; + lastAtom->numChars = (uint16) (lastAtom->numChars + first->numChars); + lastAtom->width = font.getStringWidthFloat (lastAtom->getText (passwordCharacter)); + delete first; + ++i; + } + } + } + + while (i < other.atoms.size()) + { + atoms.add (other.getAtom(i)); + ++i; + } + } + } + + UniformTextSection* split (const int indexToBreakAt, + const tchar passwordCharacter) throw() + { + UniformTextSection* const section2 = new UniformTextSection (String::empty, + font, colour, + passwordCharacter); + int index = 0; + + for (int i = 0; i < atoms.size(); ++i) + { + TextAtom* const atom = getAtom(i); + + const int nextIndex = index + atom->numChars; + + if (index == indexToBreakAt) + { + int j; + for (j = i; j < atoms.size(); ++j) + section2->atoms.add (getAtom (j)); + + for (j = atoms.size(); --j >= i;) + atoms.remove (j); + + break; + } + else if (indexToBreakAt >= index && indexToBreakAt < nextIndex) + { + TextAtom* const secondAtom = new TextAtom(); + + secondAtom->atomText = atom->atomText.substring (indexToBreakAt - index); + secondAtom->width = font.getStringWidthFloat (secondAtom->getText (passwordCharacter)); + secondAtom->numChars = (uint16) secondAtom->atomText.length(); + + section2->atoms.add (secondAtom); + + atom->atomText = atom->atomText.substring (0, indexToBreakAt - index); + atom->width = font.getStringWidthFloat (atom->getText (passwordCharacter)); + atom->numChars = (uint16) (indexToBreakAt - index); + + int j; + for (j = i + 1; j < atoms.size(); ++j) + section2->atoms.add (getAtom (j)); + + for (j = atoms.size(); --j > i;) + atoms.remove (j); + + break; + } + + index = nextIndex; + } + + return section2; + } + + const String getAllText() const throw() + { + String s; + s.preallocateStorage (getTotalLength()); + + tchar* endOfString = (tchar*) &(s[0]); + + for (int i = 0; i < atoms.size(); ++i) + { + const TextAtom* const atom = getAtom(i); + + memcpy (endOfString, &(atom->atomText[0]), atom->numChars * sizeof (tchar)); + endOfString += atom->numChars; + } + + *endOfString = 0; + + jassert ((endOfString - (tchar*) &(s[0])) <= getTotalLength()); + return s; + } + + const String getTextSubstring (const int startCharacter, + const int endCharacter) const throw() + { + int index = 0; + int totalLen = 0; + int i; + + for (i = 0; i < atoms.size(); ++i) + { + const TextAtom* const atom = getAtom (i); + const int nextIndex = index + atom->numChars; + + if (startCharacter < nextIndex) + { + if (endCharacter <= index) + break; + + const int start = jmax (0, startCharacter - index); + const int end = jmin (endCharacter - index, atom->numChars); + jassert (end >= start); + + totalLen += end - start; + } + + index = nextIndex; + } + + String s; + s.preallocateStorage (totalLen + 1); + tchar* psz = (tchar*) (const tchar*) s; + + index = 0; + + for (i = 0; i < atoms.size(); ++i) + { + const TextAtom* const atom = getAtom (i); + const int nextIndex = index + atom->numChars; + + if (startCharacter < nextIndex) + { + if (endCharacter <= index) + break; + + const int start = jmax (0, startCharacter - index); + const int len = jmin (endCharacter - index, atom->numChars) - start; + + memcpy (psz, ((const tchar*) atom->atomText) + start, len * sizeof (tchar)); + psz += len; + *psz = 0; + } + + index = nextIndex; + } + + return s; + } + + int getTotalLength() const throw() + { + int c = 0; + + for (int i = atoms.size(); --i >= 0;) + c += getAtom(i)->numChars; + + return c; + } + + void setFont (const Font& newFont, + const tchar passwordCharacter) throw() + { + if (font != newFont) + { + font = newFont; + + for (int i = atoms.size(); --i >= 0;) + { + TextAtom* const atom = (TextAtom*) atoms.getUnchecked(i); + atom->width = newFont.getStringWidthFloat (atom->getText (passwordCharacter)); + } + } + } + + juce_UseDebuggingNewOperator + + Font font; + Colour colour; + +private: + VoidArray atoms; + + void initialiseAtoms (const String& textToParse, + const tchar passwordCharacter) throw() + { + int i = 0; + const int len = textToParse.length(); + const tchar* const text = (const tchar*) textToParse; + + while (i < len) + { + int start = i; + + // create a whitespace atom unless it starts with non-ws + if (CharacterFunctions::isWhitespace (text[i]) + && text[i] != T('\r') + && text[i] != T('\n')) + { + while (i < len + && CharacterFunctions::isWhitespace (text[i]) + && text[i] != T('\r') + && text[i] != T('\n')) + { + ++i; + } + } + else + { + if (text[i] == T('\r')) + { + ++i; + + if ((i < len) && (text[i] == T('\n'))) + { + ++start; + ++i; + } + } + else if (text[i] == T('\n')) + { + ++i; + } + else + { + while ((i < len) && ! CharacterFunctions::isWhitespace (text[i])) + ++i; + } + } + + TextAtom* const atom = new TextAtom(); + atom->atomText = String (text + start, i - start); + + atom->width = font.getStringWidthFloat (atom->getText (passwordCharacter)); + atom->numChars = (uint16) (i - start); + + atoms.add (atom); + } + } + + const UniformTextSection& operator= (const UniformTextSection& other); +}; + +class TextEditorIterator +{ +public: + + TextEditorIterator (const VoidArray& sections_, + const float wordWrapWidth_, + const tchar passwordCharacter_) throw() + : indexInText (0), + lineY (0), + lineHeight (0), + maxDescent (0), + atomX (0), + atomRight (0), + atom (0), + currentSection (0), + sections (sections_), + sectionIndex (0), + atomIndex (0), + wordWrapWidth (wordWrapWidth_), + passwordCharacter (passwordCharacter_) + { + jassert (wordWrapWidth_ > 0); + + if (sections.size() > 0) + currentSection = (const UniformTextSection*) sections.getUnchecked (sectionIndex); + + if (currentSection != 0) + { + lineHeight = currentSection->font.getHeight(); + maxDescent = currentSection->font.getDescent(); + } + } + + TextEditorIterator (const TextEditorIterator& other) throw() + : indexInText (other.indexInText), + lineY (other.lineY), + lineHeight (other.lineHeight), + maxDescent (other.maxDescent), + atomX (other.atomX), + atomRight (other.atomRight), + atom (other.atom), + currentSection (other.currentSection), + sections (other.sections), + sectionIndex (other.sectionIndex), + atomIndex (other.atomIndex), + wordWrapWidth (other.wordWrapWidth), + passwordCharacter (other.passwordCharacter), + tempAtom (other.tempAtom) + { + } + + ~TextEditorIterator() throw() + { + } + + bool next() throw() + { + if (atom == &tempAtom) + { + const int numRemaining = tempAtom.atomText.length() - tempAtom.numChars; + + if (numRemaining > 0) + { + tempAtom.atomText = tempAtom.atomText.substring (tempAtom.numChars); + + atomX = 0; + + if (tempAtom.numChars > 0) + lineY += lineHeight; + + indexInText += tempAtom.numChars; + + GlyphArrangement g; + g.addLineOfText (currentSection->font, atom->getText (passwordCharacter), 0.0f, 0.0f); + + int split; + for (split = 0; split < g.getNumGlyphs(); ++split) + if (SHOULD_WRAP (g.getGlyph (split).getRight(), wordWrapWidth)) + break; + + if (split > 0 && split <= numRemaining) + { + tempAtom.numChars = (uint16) split; + tempAtom.width = g.getGlyph (split - 1).getRight(); + atomRight = atomX + tempAtom.width; + return true; + } + } + } + + bool forceNewLine = false; + + if (sectionIndex >= sections.size()) + { + moveToEndOfLastAtom(); + return false; + } + else if (atomIndex >= currentSection->getNumAtoms() - 1) + { + if (atomIndex >= currentSection->getNumAtoms()) + { + if (++sectionIndex >= sections.size()) + { + moveToEndOfLastAtom(); + return false; + } + + atomIndex = 0; + currentSection = (const UniformTextSection*) sections.getUnchecked (sectionIndex); + + lineHeight = jmax (lineHeight, currentSection->font.getHeight()); + maxDescent = jmax (maxDescent, currentSection->font.getDescent()); + } + else + { + const TextAtom* const lastAtom = currentSection->getAtom (atomIndex); + + if (! lastAtom->isWhitespace()) + { + // handle the case where the last atom in a section is actually part of the same + // word as the first atom of the next section... + float right = atomRight + lastAtom->width; + float lineHeight2 = lineHeight; + float maxDescent2 = maxDescent; + + for (int section = sectionIndex + 1; section < sections.size(); ++section) + { + const UniformTextSection* const s = (const UniformTextSection*) sections.getUnchecked (section); + + if (s->getNumAtoms() == 0) + break; + + const TextAtom* const nextAtom = s->getAtom (0); + + if (nextAtom->isWhitespace()) + break; + + right += nextAtom->width; + + lineHeight2 = jmax (lineHeight2, s->font.getHeight()); + maxDescent2 = jmax (maxDescent2, s->font.getDescent()); + + if (SHOULD_WRAP (right, wordWrapWidth)) + { + lineHeight = lineHeight2; + maxDescent = maxDescent2; + + forceNewLine = true; + break; + } + + if (s->getNumAtoms() > 1) + break; + } + } + } + } + + if (atom != 0) + { + atomX = atomRight; + indexInText += atom->numChars; + + if (atom->isNewLine()) + { + atomX = 0; + lineY += lineHeight; + } + } + + atom = currentSection->getAtom (atomIndex); + atomRight = atomX + atom->width; + ++atomIndex; + + if (SHOULD_WRAP (atomRight, wordWrapWidth) || forceNewLine) + { + if (atom->isWhitespace()) + { + // leave whitespace at the end of a line, but truncate it to avoid scrolling + atomRight = jmin (atomRight, wordWrapWidth); + } + else + { + return wrapCurrentAtom(); + } + } + + return true; + } + + bool wrapCurrentAtom() throw() + { + atomRight = atom->width; + + if (SHOULD_WRAP (atomRight, wordWrapWidth)) // atom too big to fit on a line, so break it up.. + { + tempAtom = *atom; + tempAtom.width = 0; + tempAtom.numChars = 0; + atom = &tempAtom; + + if (atomX > 0) + { + atomX = 0; + lineY += lineHeight; + } + + return next(); + } + + atomX = 0; + lineY += lineHeight; + return true; + } + + void draw (Graphics& g, const UniformTextSection*& lastSection) const throw() + { + if (passwordCharacter != 0 || ! atom->isWhitespace()) + { + if (lastSection != currentSection) + { + lastSection = currentSection; + g.setColour (currentSection->colour); + g.setFont (currentSection->font); + } + + jassert (atom->getTrimmedText (passwordCharacter).isNotEmpty()); + + GlyphArrangement ga; + ga.addLineOfText (currentSection->font, + atom->getTrimmedText (passwordCharacter), + atomX, + (float) roundFloatToInt (lineY + lineHeight - maxDescent)); + ga.draw (g); + } + } + + void drawSelection (Graphics& g, + const int selectionStart, + const int selectionEnd) const throw() + { + const int startX = roundFloatToInt (indexToX (selectionStart)); + const int endX = roundFloatToInt (indexToX (selectionEnd)); + + const int y = roundFloatToInt (lineY); + const int nextY = roundFloatToInt (lineY + lineHeight); + + g.fillRect (startX, y, endX - startX, nextY - y); + } + + void drawSelectedText (Graphics& g, + const int selectionStart, + const int selectionEnd, + const Colour& selectedTextColour) const throw() + { + if (passwordCharacter != 0 || ! atom->isWhitespace()) + { + GlyphArrangement ga; + ga.addLineOfText (currentSection->font, + atom->getTrimmedText (passwordCharacter), + atomX, + (float) roundFloatToInt (lineY + lineHeight - maxDescent)); + + if (selectionEnd < indexInText + atom->numChars) + { + GlyphArrangement ga2 (ga); + ga2.removeRangeOfGlyphs (0, selectionEnd - indexInText); + ga.removeRangeOfGlyphs (selectionEnd - indexInText, -1); + + g.setColour (currentSection->colour); + ga2.draw (g); + } + + if (selectionStart > indexInText) + { + GlyphArrangement ga2 (ga); + ga2.removeRangeOfGlyphs (selectionStart - indexInText, -1); + ga.removeRangeOfGlyphs (0, selectionStart - indexInText); + + g.setColour (currentSection->colour); + ga2.draw (g); + } + + g.setColour (selectedTextColour); + ga.draw (g); + } + } + + float indexToX (const int indexToFind) const throw() + { + if (indexToFind <= indexInText) + return atomX; + + if (indexToFind >= indexInText + atom->numChars) + return atomRight; + + GlyphArrangement g; + g.addLineOfText (currentSection->font, + atom->getText (passwordCharacter), + atomX, 0.0f); + + return jmin (atomRight, g.getGlyph (indexToFind - indexInText).getLeft()); + } + + int xToIndex (const float xToFind) const throw() + { + if (xToFind <= atomX || atom->isNewLine()) + return indexInText; + + if (xToFind >= atomRight) + return indexInText + atom->numChars; + + GlyphArrangement g; + g.addLineOfText (currentSection->font, + atom->getText (passwordCharacter), + atomX, 0.0f); + + int j; + for (j = 0; j < atom->numChars; ++j) + if ((g.getGlyph(j).getLeft() + g.getGlyph(j).getRight()) / 2 > xToFind) + break; + + return indexInText + j; + } + + void updateLineHeight() throw() + { + float x = atomRight; + + int tempSectionIndex = sectionIndex; + int tempAtomIndex = atomIndex; + const UniformTextSection* currentSection = (const UniformTextSection*) sections.getUnchecked (tempSectionIndex); + + while (! SHOULD_WRAP (x, wordWrapWidth)) + { + if (tempSectionIndex >= sections.size()) + break; + + bool checkSize = false; + + if (tempAtomIndex >= currentSection->getNumAtoms()) + { + if (++tempSectionIndex >= sections.size()) + break; + + tempAtomIndex = 0; + currentSection = (const UniformTextSection*) sections.getUnchecked (tempSectionIndex); + checkSize = true; + } + + const TextAtom* const atom = currentSection->getAtom (tempAtomIndex); + + if (atom == 0) + break; + + x += atom->width; + + if (SHOULD_WRAP (x, wordWrapWidth) || atom->isNewLine()) + break; + + if (checkSize) + { + lineHeight = jmax (lineHeight, currentSection->font.getHeight()); + maxDescent = jmax (maxDescent, currentSection->font.getDescent()); + } + + ++tempAtomIndex; + } + } + + bool getCharPosition (const int index, float& cx, float& cy, float& lineHeight_) throw() + { + while (next()) + { + if (indexInText + atom->numChars >= index) + { + updateLineHeight(); + + if (indexInText + atom->numChars > index) + { + cx = indexToX (index); + cy = lineY; + lineHeight_ = lineHeight; + return true; + } + } + } + + cx = atomX; + cy = lineY; + lineHeight_ = lineHeight; + return false; + } + + juce_UseDebuggingNewOperator + + int indexInText; + float lineY, lineHeight, maxDescent; + float atomX, atomRight; + const TextAtom* atom; + const UniformTextSection* currentSection; + +private: + const VoidArray& sections; + int sectionIndex, atomIndex; + const float wordWrapWidth; + const tchar passwordCharacter; + TextAtom tempAtom; + + const TextEditorIterator& operator= (const TextEditorIterator&); + + void moveToEndOfLastAtom() throw() + { + if (atom != 0) + { + atomX = atomRight; + + if (atom->isNewLine()) + { + atomX = 0.0f; + lineY += lineHeight; + } + } + } +}; + +class TextEditorInsertAction : public UndoableAction +{ + TextEditor& owner; + const String text; + const int insertIndex, oldCaretPos, newCaretPos; + const Font font; + const Colour colour; + + TextEditorInsertAction (const TextEditorInsertAction&); + const TextEditorInsertAction& operator= (const TextEditorInsertAction&); + +public: + TextEditorInsertAction (TextEditor& owner_, + const String& text_, + const int insertIndex_, + const Font& font_, + const Colour& colour_, + const int oldCaretPos_, + const int newCaretPos_) throw() + : owner (owner_), + text (text_), + insertIndex (insertIndex_), + oldCaretPos (oldCaretPos_), + newCaretPos (newCaretPos_), + font (font_), + colour (colour_) + { + } + + ~TextEditorInsertAction() + { + } + + bool perform() + { + owner.insert (text, insertIndex, font, colour, 0, newCaretPos); + return true; + } + + bool undo() + { + owner.remove (insertIndex, insertIndex + text.length(), 0, oldCaretPos); + return true; + } + + int getSizeInUnits() + { + return text.length() + 16; + } +}; + +class TextEditorRemoveAction : public UndoableAction +{ + TextEditor& owner; + const int startIndex, endIndex, oldCaretPos, newCaretPos; + VoidArray removedSections; + + TextEditorRemoveAction (const TextEditorRemoveAction&); + const TextEditorRemoveAction& operator= (const TextEditorRemoveAction&); + +public: + TextEditorRemoveAction (TextEditor& owner_, + const int startIndex_, + const int endIndex_, + const int oldCaretPos_, + const int newCaretPos_, + const VoidArray& removedSections_) throw() + : owner (owner_), + startIndex (startIndex_), + endIndex (endIndex_), + oldCaretPos (oldCaretPos_), + newCaretPos (newCaretPos_), + removedSections (removedSections_) + { + } + + ~TextEditorRemoveAction() + { + for (int i = removedSections.size(); --i >= 0;) + { + UniformTextSection* const section = (UniformTextSection*) removedSections.getUnchecked (i); + section->clear(); + delete section; + } + } + + bool perform() + { + owner.remove (startIndex, endIndex, 0, newCaretPos); + return true; + } + + bool undo() + { + owner.reinsert (startIndex, removedSections); + owner.moveCursorTo (oldCaretPos, false); + return true; + } + + int getSizeInUnits() + { + int n = 0; + + for (int i = removedSections.size(); --i >= 0;) + { + UniformTextSection* const section = (UniformTextSection*) removedSections.getUnchecked (i); + n += section->getTotalLength(); + } + + return n + 16; + } +}; + +class TextHolderComponent : public Component, + public Timer +{ + TextEditor* const owner; + + TextHolderComponent (const TextHolderComponent&); + const TextHolderComponent& operator= (const TextHolderComponent&); + +public: + TextHolderComponent (TextEditor* const owner_) + : owner (owner_) + { + setWantsKeyboardFocus (false); + setInterceptsMouseClicks (false, true); + } + + ~TextHolderComponent() + { + } + + void paint (Graphics& g) + { + owner->drawContent (g); + } + + void timerCallback() + { + owner->timerCallbackInt(); + } + + const MouseCursor getMouseCursor() + { + return owner->getMouseCursor(); + } +}; + +class TextEditorViewport : public Viewport +{ + TextEditor* const owner; + float lastWordWrapWidth; + + TextEditorViewport (const TextEditorViewport&); + const TextEditorViewport& operator= (const TextEditorViewport&); + +public: + TextEditorViewport (TextEditor* const owner_) + : owner (owner_), + lastWordWrapWidth (0) + { + } + + ~TextEditorViewport() + { + } + + void visibleAreaChanged (int, int, int, int) + { + const float wordWrapWidth = owner->getWordWrapWidth(); + + if (wordWrapWidth != lastWordWrapWidth) + { + lastWordWrapWidth = wordWrapWidth; + owner->updateTextHolderSize(); + } + } +}; + +const int flashSpeedIntervalMs = 380; + +const int textChangeMessageId = 0x10003001; +const int returnKeyMessageId = 0x10003002; +const int escapeKeyMessageId = 0x10003003; +const int focusLossMessageId = 0x10003004; + +TextEditor::TextEditor (const String& name, + const tchar passwordCharacter_) + : Component (name), + borderSize (1, 1, 1, 3), + readOnly (false), + multiline (false), + wordWrap (false), + returnKeyStartsNewLine (false), + caretVisible (true), + popupMenuEnabled (true), + selectAllTextWhenFocused (false), + scrollbarVisible (true), + wasFocused (false), + caretFlashState (true), + keepCursorOnScreen (true), + tabKeyUsed (false), + menuActive (false), + cursorX (0), + cursorY (0), + cursorHeight (0), + maxTextLength (0), + selectionStart (0), + selectionEnd (0), + leftIndent (4), + topIndent (4), + lastTransactionTime (0), + currentFont (14.0f), + totalNumChars (0), + caretPosition (0), + sections (8), + passwordCharacter (passwordCharacter_), + dragType (notDragging), + listeners (2) +{ + setOpaque (true); + + addAndMakeVisible (viewport = new TextEditorViewport (this)); + viewport->setViewedComponent (textHolder = new TextHolderComponent (this)); + viewport->setWantsKeyboardFocus (false); + viewport->setScrollBarsShown (false, false); + + setMouseCursor (MouseCursor::IBeamCursor); + setWantsKeyboardFocus (true); +} + +TextEditor::~TextEditor() +{ + clearInternal (0); + delete viewport; +} + +void TextEditor::newTransaction() throw() +{ + lastTransactionTime = Time::getApproximateMillisecondCounter(); + undoManager.beginNewTransaction(); +} + +void TextEditor::doUndoRedo (const bool isRedo) +{ + if (! isReadOnly()) + { + if ((isRedo) ? undoManager.redo() + : undoManager.undo()) + { + scrollToMakeSureCursorIsVisible(); + repaint(); + textChanged(); + } + } +} + +void TextEditor::setMultiLine (const bool shouldBeMultiLine, + const bool shouldWordWrap) +{ + multiline = shouldBeMultiLine; + wordWrap = shouldWordWrap && shouldBeMultiLine; + + setScrollbarsShown (scrollbarVisible); + + viewport->setViewPosition (0, 0); + + resized(); + scrollToMakeSureCursorIsVisible(); +} + +bool TextEditor::isMultiLine() const throw() +{ + return multiline; +} + +void TextEditor::setScrollbarsShown (bool enabled) throw() +{ + scrollbarVisible = enabled; + + enabled = enabled && isMultiLine(); + + viewport->setScrollBarsShown (enabled, enabled); +} + +void TextEditor::setReadOnly (const bool shouldBeReadOnly) +{ + readOnly = shouldBeReadOnly; + enablementChanged(); +} + +bool TextEditor::isReadOnly() const throw() +{ + return readOnly || ! isEnabled(); +} + +void TextEditor::setReturnKeyStartsNewLine (const bool shouldStartNewLine) +{ + returnKeyStartsNewLine = shouldStartNewLine; +} + +void TextEditor::setTabKeyUsedAsCharacter (const bool shouldTabKeyBeUsed) throw() +{ + tabKeyUsed = shouldTabKeyBeUsed; +} + +void TextEditor::setPopupMenuEnabled (const bool b) throw() +{ + popupMenuEnabled = b; +} + +void TextEditor::setSelectAllWhenFocused (const bool b) throw() +{ + selectAllTextWhenFocused = b; +} + +const Font TextEditor::getFont() const throw() +{ + return currentFont; +} + +void TextEditor::setFont (const Font& newFont) throw() +{ + currentFont = newFont; + scrollToMakeSureCursorIsVisible(); +} + +void TextEditor::applyFontToAllText (const Font& newFont) +{ + currentFont = newFont; + + const Colour overallColour (findColour (textColourId)); + + for (int i = sections.size(); --i >= 0;) + { + UniformTextSection* const uts = (UniformTextSection*) sections.getUnchecked(i); + uts->setFont (newFont, passwordCharacter); + uts->colour = overallColour; + } + + coalesceSimilarSections(); + updateTextHolderSize(); + scrollToMakeSureCursorIsVisible(); + repaint(); +} + +void TextEditor::colourChanged() +{ + setOpaque (findColour (backgroundColourId).isOpaque()); + repaint(); +} + +void TextEditor::setCaretVisible (const bool shouldCaretBeVisible) throw() +{ + caretVisible = shouldCaretBeVisible; + + if (shouldCaretBeVisible) + textHolder->startTimer (flashSpeedIntervalMs); + + setMouseCursor (shouldCaretBeVisible ? MouseCursor::IBeamCursor + : MouseCursor::NormalCursor); +} + +void TextEditor::setInputRestrictions (const int maxLen, + const String& chars) throw() +{ + maxTextLength = jmax (0, maxLen); + allowedCharacters = chars; +} + +void TextEditor::setTextToShowWhenEmpty (const String& text, const Colour& colourToUse) throw() +{ + textToShowWhenEmpty = text; + colourForTextWhenEmpty = colourToUse; +} + +void TextEditor::setPasswordCharacter (const tchar newPasswordCharacter) throw() +{ + if (passwordCharacter != newPasswordCharacter) + { + passwordCharacter = newPasswordCharacter; + resized(); + repaint(); + } +} + +void TextEditor::setScrollBarThickness (const int newThicknessPixels) +{ + viewport->setScrollBarThickness (newThicknessPixels); +} + +void TextEditor::setScrollBarButtonVisibility (const bool buttonsVisible) +{ + viewport->setScrollBarButtonVisibility (buttonsVisible); +} + +void TextEditor::clear() +{ + clearInternal (0); + updateTextHolderSize(); + undoManager.clearUndoHistory(); +} + +void TextEditor::setText (const String& newText, + const bool sendTextChangeMessage) +{ + const int newLength = newText.length(); + + if (newLength != getTotalNumChars() || getText() != newText) + { + const int oldCursorPos = caretPosition; + const bool cursorWasAtEnd = oldCursorPos >= getTotalNumChars(); + + clearInternal (0); + insert (newText, 0, currentFont, findColour (textColourId), 0, caretPosition); + + // if you're adding text with line-feeds to a single-line text editor, it + // ain't gonna look right! + jassert (multiline || ! newText.containsAnyOf (T("\r\n"))); + + if (cursorWasAtEnd && ! isMultiLine()) + moveCursorTo (getTotalNumChars(), false); + else + moveCursorTo (oldCursorPos, false); + + if (sendTextChangeMessage) + textChanged(); + + repaint(); + } + + updateTextHolderSize(); + scrollToMakeSureCursorIsVisible(); + undoManager.clearUndoHistory(); +} + +void TextEditor::textChanged() throw() +{ + updateTextHolderSize(); + postCommandMessage (textChangeMessageId); +} + +void TextEditor::returnPressed() +{ + postCommandMessage (returnKeyMessageId); +} + +void TextEditor::escapePressed() +{ + postCommandMessage (escapeKeyMessageId); +} + +void TextEditor::addListener (TextEditorListener* const newListener) throw() +{ + jassert (newListener != 0) + + if (newListener != 0) + listeners.add (newListener); +} + +void TextEditor::removeListener (TextEditorListener* const listenerToRemove) throw() +{ + listeners.removeValue (listenerToRemove); +} + +void TextEditor::timerCallbackInt() +{ + const bool newState = (! caretFlashState) && ! isCurrentlyBlockedByAnotherModalComponent(); + + if (caretFlashState != newState) + { + caretFlashState = newState; + + if (caretFlashState) + wasFocused = true; + + if (caretVisible + && hasKeyboardFocus (false) + && ! isReadOnly()) + { + repaintCaret(); + } + } + + const unsigned int now = Time::getApproximateMillisecondCounter(); + + if (now > lastTransactionTime + 200) + newTransaction(); +} + +void TextEditor::repaintCaret() +{ + if (! findColour (caretColourId).isTransparent()) + repaint (borderSize.getLeft() + textHolder->getX() + leftIndent + roundFloatToInt (cursorX) - 1, + borderSize.getTop() + textHolder->getY() + topIndent + roundFloatToInt (cursorY) - 1, + 4, + roundFloatToInt (cursorHeight) + 2); +} + +void TextEditor::repaintText (int textStartIndex, int textEndIndex) +{ + if (textStartIndex > textEndIndex && textEndIndex > 0) + swapVariables (textStartIndex, textEndIndex); + + float x = 0, y = 0, lh = currentFont.getHeight(); + + const float wordWrapWidth = getWordWrapWidth(); + + if (wordWrapWidth > 0) + { + TextEditorIterator i (sections, wordWrapWidth, passwordCharacter); + + i.getCharPosition (textStartIndex, x, y, lh); + + const int y1 = (int) y; + int y2; + + if (textEndIndex >= 0) + { + i.getCharPosition (textEndIndex, x, y, lh); + y2 = (int) (y + lh * 2.0f); + } + else + { + y2 = textHolder->getHeight(); + } + + textHolder->repaint (0, y1, textHolder->getWidth(), y2 - y1); + } +} + +void TextEditor::moveCaret (int newCaretPos) throw() +{ + if (newCaretPos < 0) + newCaretPos = 0; + else if (newCaretPos > getTotalNumChars()) + newCaretPos = getTotalNumChars(); + + if (newCaretPos != getCaretPosition()) + { + repaintCaret(); + caretFlashState = true; + caretPosition = newCaretPos; + textHolder->startTimer (flashSpeedIntervalMs); + scrollToMakeSureCursorIsVisible(); + repaintCaret(); + } +} + +void TextEditor::setCaretPosition (const int newIndex) throw() +{ + moveCursorTo (newIndex, false); +} + +int TextEditor::getCaretPosition() const throw() +{ + return caretPosition; +} + +float TextEditor::getWordWrapWidth() const throw() +{ + return (wordWrap) ? (float) (viewport->getMaximumVisibleWidth() - leftIndent - leftIndent / 2) + : 1.0e10f; +} + +void TextEditor::updateTextHolderSize() throw() +{ + const float wordWrapWidth = getWordWrapWidth(); + + if (wordWrapWidth > 0) + { + float maxWidth = 0.0f; + + TextEditorIterator i (sections, wordWrapWidth, passwordCharacter); + + while (i.next()) + maxWidth = jmax (maxWidth, i.atomRight); + + const int w = leftIndent + roundFloatToInt (maxWidth); + const int h = topIndent + roundFloatToInt (jmax (i.lineY + i.lineHeight, + currentFont.getHeight())); + + textHolder->setSize (w + 1, h + 1); + } +} + +int TextEditor::getTextWidth() const throw() +{ + return textHolder->getWidth(); +} + +int TextEditor::getTextHeight() const throw() +{ + return textHolder->getHeight(); +} + +void TextEditor::setIndents (const int newLeftIndent, + const int newTopIndent) throw() +{ + leftIndent = newLeftIndent; + topIndent = newTopIndent; +} + +void TextEditor::setBorder (const BorderSize& border) throw() +{ + borderSize = border; + resized(); +} + +const BorderSize TextEditor::getBorder() const throw() +{ + return borderSize; +} + +void TextEditor::setScrollToShowCursor (const bool shouldScrollToShowCursor) throw() +{ + keepCursorOnScreen = shouldScrollToShowCursor; +} + +void TextEditor::scrollToMakeSureCursorIsVisible() throw() +{ + cursorHeight = currentFont.getHeight(); // (in case the text is empty and the call below doesn't set this value) + + getCharPosition (caretPosition, + cursorX, cursorY, + cursorHeight); + + if (keepCursorOnScreen) + { + int x = viewport->getViewPositionX(); + int y = viewport->getViewPositionY(); + + const int relativeCursorX = roundFloatToInt (cursorX) - x; + const int relativeCursorY = roundFloatToInt (cursorY) - y; + + if (relativeCursorX < jmax (1, proportionOfWidth (0.05f))) + { + x += relativeCursorX - proportionOfWidth (0.2f); + } + else if (relativeCursorX > jmax (0, viewport->getMaximumVisibleWidth() - (wordWrap ? 2 : 10))) + { + x += relativeCursorX + (isMultiLine() ? proportionOfWidth (0.2f) : 10) - viewport->getMaximumVisibleWidth(); + } + + x = jlimit (0, jmax (0, textHolder->getWidth() + 8 - viewport->getMaximumVisibleWidth()), x); + + if (! isMultiLine()) + { + y = (getHeight() - textHolder->getHeight() - topIndent) / -2; + } + else + { + const int curH = roundFloatToInt (cursorHeight); + + if (relativeCursorY < 0) + { + y = jmax (0, relativeCursorY + y); + } + else if (relativeCursorY > jmax (0, viewport->getMaximumVisibleHeight() - topIndent - curH)) + { + y += relativeCursorY + 2 + curH + topIndent - viewport->getMaximumVisibleHeight(); + } + } + + viewport->setViewPosition (x, y); + } +} + +void TextEditor::moveCursorTo (const int newPosition, + const bool isSelecting) throw() +{ + if (isSelecting) + { + moveCaret (newPosition); + + const int oldSelStart = selectionStart; + const int oldSelEnd = selectionEnd; + + if (dragType == notDragging) + { + if (abs (getCaretPosition() - selectionStart) < abs (getCaretPosition() - selectionEnd)) + dragType = draggingSelectionStart; + else + dragType = draggingSelectionEnd; + } + + if (dragType == draggingSelectionStart) + { + selectionStart = getCaretPosition(); + + if (selectionEnd < selectionStart) + { + swapVariables (selectionStart, selectionEnd); + dragType = draggingSelectionEnd; + } + } + else + { + selectionEnd = getCaretPosition(); + + if (selectionEnd < selectionStart) + { + swapVariables (selectionStart, selectionEnd); + dragType = draggingSelectionStart; + } + } + + jassert (selectionStart <= selectionEnd); + jassert (oldSelStart <= oldSelEnd); + + repaintText (jmin (oldSelStart, selectionStart), + jmax (oldSelEnd, selectionEnd)); + } + else + { + dragType = notDragging; + + if (selectionEnd > selectionStart) + repaintText (selectionStart, selectionEnd); + + moveCaret (newPosition); + selectionStart = getCaretPosition(); + selectionEnd = getCaretPosition(); + } +} + +int TextEditor::getTextIndexAt (const int x, + const int y) throw() +{ + return indexAtPosition ((float) (x + viewport->getViewPositionX() - leftIndent), + (float) (y + viewport->getViewPositionY() - topIndent)); +} + +void TextEditor::insertTextAtCursor (String newText) +{ + if (allowedCharacters.isNotEmpty()) + newText = newText.retainCharacters (allowedCharacters); + + if (! isMultiLine()) + newText = newText.replaceCharacters (T("\r\n"), T(" ")); + else + newText = newText.replace (T("\r\n"), T("\n")); + + const int newCaretPos = selectionStart + newText.length(); + const int insertIndex = selectionStart; + + remove (selectionStart, selectionEnd, + &undoManager, + newCaretPos); + + if (maxTextLength > 0) + newText = newText.substring (0, maxTextLength - getTotalNumChars()); + + if (newText.isNotEmpty()) + insert (newText, + insertIndex, + currentFont, + findColour (textColourId), + &undoManager, + newCaretPos); + + textChanged(); +} + +void TextEditor::setHighlightedRegion (int startPos, int numChars) throw() +{ + moveCursorTo (startPos, false); + moveCursorTo (startPos + numChars, true); +} + +void TextEditor::copy() +{ + const String selection (getTextSubstring (selectionStart, selectionEnd)); + + if (selection.isNotEmpty()) + SystemClipboard::copyTextToClipboard (selection); +} + +void TextEditor::paste() +{ + if (! isReadOnly()) + { + const String clip (SystemClipboard::getTextFromClipboard()); + + if (clip.isNotEmpty()) + insertTextAtCursor (clip); + } +} + +void TextEditor::cut() +{ + if (! isReadOnly()) + { + moveCaret (selectionEnd); + insertTextAtCursor (String::empty); + } +} + +void TextEditor::drawContent (Graphics& g) +{ + const float wordWrapWidth = getWordWrapWidth(); + + if (wordWrapWidth > 0) + { + g.setOrigin (leftIndent, topIndent); + const Rectangle clip (g.getClipBounds()); + Colour selectedTextColour; + + TextEditorIterator i (sections, wordWrapWidth, passwordCharacter); + + while (i.lineY + 200.0 < clip.getY() && i.next()) + {} + + if (selectionStart < selectionEnd) + { + g.setColour (findColour (highlightColourId) + .withMultipliedAlpha (hasKeyboardFocus (true) ? 1.0f : 0.5f)); + + selectedTextColour = findColour (highlightedTextColourId); + + TextEditorIterator i2 (i); + + while (i2.next() && i2.lineY < clip.getBottom()) + { + i2.updateLineHeight(); + + if (i2.lineY + i2.lineHeight >= clip.getY() + && selectionEnd >= i2.indexInText + && selectionStart <= i2.indexInText + i2.atom->numChars) + { + i2.drawSelection (g, selectionStart, selectionEnd); + } + } + } + + const UniformTextSection* lastSection = 0; + + while (i.next() && i.lineY < clip.getBottom()) + { + i.updateLineHeight(); + + if (i.lineY + i.lineHeight >= clip.getY()) + { + if (selectionEnd >= i.indexInText + && selectionStart <= i.indexInText + i.atom->numChars) + { + i.drawSelectedText (g, selectionStart, selectionEnd, selectedTextColour); + lastSection = 0; + } + else + { + i.draw (g, lastSection); + } + } + } + } +} + +void TextEditor::paint (Graphics& g) +{ + getLookAndFeel().fillTextEditorBackground (g, getWidth(), getHeight(), *this); +} + +void TextEditor::paintOverChildren (Graphics& g) +{ + if (caretFlashState + && hasKeyboardFocus (false) + && caretVisible + && ! isReadOnly()) + { + g.setColour (findColour (caretColourId)); + + g.fillRect (borderSize.getLeft() + textHolder->getX() + leftIndent + cursorX, + borderSize.getTop() + textHolder->getY() + topIndent + cursorY, + 2.0f, cursorHeight); + } + + if (textToShowWhenEmpty.isNotEmpty() + && (! hasKeyboardFocus (false)) + && getTotalNumChars() == 0) + { + g.setColour (colourForTextWhenEmpty); + g.setFont (getFont()); + + if (isMultiLine()) + { + g.drawText (textToShowWhenEmpty, + 0, 0, getWidth(), getHeight(), + Justification::centred, true); + } + else + { + g.drawText (textToShowWhenEmpty, + leftIndent, topIndent, + viewport->getWidth() - leftIndent, + viewport->getHeight() - topIndent, + Justification::centredLeft, true); + } + } + + getLookAndFeel().drawTextEditorOutline (g, getWidth(), getHeight(), *this); +} + +void TextEditor::mouseDown (const MouseEvent& e) +{ + beginDragAutoRepeat (100); + newTransaction(); + + if (wasFocused || ! selectAllTextWhenFocused) + { + if (! (popupMenuEnabled && e.mods.isPopupMenu())) + { + moveCursorTo (getTextIndexAt (e.x, e.y), + e.mods.isShiftDown()); + } + else + { + PopupMenu m; + addPopupMenuItems (m, &e); + + menuActive = true; + const int result = m.show(); + menuActive = false; + + if (result != 0) + performPopupMenuAction (result); + } + } +} + +void TextEditor::mouseDrag (const MouseEvent& e) +{ + if (wasFocused || ! selectAllTextWhenFocused) + { + if (! (popupMenuEnabled && e.mods.isPopupMenu())) + { + moveCursorTo (getTextIndexAt (e.x, e.y), true); + } + } +} + +void TextEditor::mouseUp (const MouseEvent& e) +{ + newTransaction(); + textHolder->startTimer (flashSpeedIntervalMs); + + if (wasFocused || ! selectAllTextWhenFocused) + { + if (! (popupMenuEnabled && e.mods.isPopupMenu())) + { + moveCaret (getTextIndexAt (e.x, e.y)); + } + } + + wasFocused = true; +} + +void TextEditor::mouseDoubleClick (const MouseEvent& e) +{ + int tokenEnd = getTextIndexAt (e.x, e.y); + int tokenStart = tokenEnd; + + if (e.getNumberOfClicks() > 3) + { + tokenStart = 0; + tokenEnd = getTotalNumChars(); + } + else + { + const String t (getText()); + const int totalLength = getTotalNumChars(); + + while (tokenEnd < totalLength) + { + if (CharacterFunctions::isLetterOrDigit (t [tokenEnd])) + ++tokenEnd; + else + break; + } + + tokenStart = tokenEnd; + + while (tokenStart > 0) + { + if (CharacterFunctions::isLetterOrDigit (t [tokenStart - 1])) + --tokenStart; + else + break; + } + + if (e.getNumberOfClicks() > 2) + { + while (tokenEnd < totalLength) + { + if (t [tokenEnd] != T('\r') && t [tokenEnd] != T('\n')) + ++tokenEnd; + else + break; + } + + while (tokenStart > 0) + { + if (t [tokenStart - 1] != T('\r') && t [tokenStart - 1] != T('\n')) + --tokenStart; + else + break; + } + } + } + + moveCursorTo (tokenEnd, false); + moveCursorTo (tokenStart, true); +} + +void TextEditor::mouseWheelMove (const MouseEvent& e, float wheelIncrementX, float wheelIncrementY) +{ + if (! viewport->useMouseWheelMoveIfNeeded (e, wheelIncrementX, wheelIncrementY)) + Component::mouseWheelMove (e, wheelIncrementX, wheelIncrementY); +} + +bool TextEditor::keyPressed (const KeyPress& key) +{ + if (isReadOnly() && key != KeyPress (T('c'), ModifierKeys::commandModifier, 0)) + return false; + + const bool moveInWholeWordSteps = key.getModifiers().isCtrlDown() || key.getModifiers().isAltDown(); + + if (key.isKeyCode (KeyPress::leftKey) + || key.isKeyCode (KeyPress::upKey)) + { + newTransaction(); + + int newPos; + + if (isMultiLine() && key.isKeyCode (KeyPress::upKey)) + newPos = indexAtPosition (cursorX, cursorY - 1); + else if (moveInWholeWordSteps) + newPos = findWordBreakBefore (getCaretPosition()); + else + newPos = getCaretPosition() - 1; + + moveCursorTo (newPos, key.getModifiers().isShiftDown()); + } + else if (key.isKeyCode (KeyPress::rightKey) + || key.isKeyCode (KeyPress::downKey)) + { + newTransaction(); + + int newPos; + + if (isMultiLine() && key.isKeyCode (KeyPress::downKey)) + newPos = indexAtPosition (cursorX, cursorY + cursorHeight + 1); + else if (moveInWholeWordSteps) + newPos = findWordBreakAfter (getCaretPosition()); + else + newPos = getCaretPosition() + 1; + + moveCursorTo (newPos, key.getModifiers().isShiftDown()); + } + else if (key.isKeyCode (KeyPress::pageDownKey) && isMultiLine()) + { + newTransaction(); + + moveCursorTo (indexAtPosition (cursorX, cursorY + cursorHeight + viewport->getViewHeight()), + key.getModifiers().isShiftDown()); + } + else if (key.isKeyCode (KeyPress::pageUpKey) && isMultiLine()) + { + newTransaction(); + + moveCursorTo (indexAtPosition (cursorX, cursorY - viewport->getViewHeight()), + key.getModifiers().isShiftDown()); + } + else if (key.isKeyCode (KeyPress::homeKey)) + { + newTransaction(); + + if (isMultiLine() && ! moveInWholeWordSteps) + moveCursorTo (indexAtPosition (0.0f, cursorY), + key.getModifiers().isShiftDown()); + else + moveCursorTo (0, key.getModifiers().isShiftDown()); + } + else if (key.isKeyCode (KeyPress::endKey)) + { + newTransaction(); + + if (isMultiLine() && ! moveInWholeWordSteps) + moveCursorTo (indexAtPosition ((float) textHolder->getWidth(), cursorY), + key.getModifiers().isShiftDown()); + else + moveCursorTo (getTotalNumChars(), key.getModifiers().isShiftDown()); + } + else if (key.isKeyCode (KeyPress::backspaceKey)) + { + if (moveInWholeWordSteps) + { + moveCursorTo (findWordBreakBefore (getCaretPosition()), true); + } + else + { + if (selectionStart == selectionEnd && selectionStart > 0) + --selectionStart; + } + + cut(); + } + else if (key.isKeyCode (KeyPress::deleteKey)) + { + if (key.getModifiers().isShiftDown()) + copy(); + + if (selectionStart == selectionEnd + && selectionEnd < getTotalNumChars()) + { + ++selectionEnd; + } + + cut(); + } + else if (key == KeyPress (T('c'), ModifierKeys::commandModifier, 0) + || key == KeyPress (KeyPress::insertKey, ModifierKeys::ctrlModifier, 0)) + { + newTransaction(); + copy(); + } + else if (key == KeyPress (T('x'), ModifierKeys::commandModifier, 0)) + { + newTransaction(); + copy(); + cut(); + } + else if (key == KeyPress (T('v'), ModifierKeys::commandModifier, 0) + || key == KeyPress (KeyPress::insertKey, ModifierKeys::shiftModifier, 0)) + { + newTransaction(); + paste(); + } + else if (key == KeyPress (T('z'), ModifierKeys::commandModifier, 0)) + { + newTransaction(); + doUndoRedo (false); + } + else if (key == KeyPress (T('y'), ModifierKeys::commandModifier, 0)) + { + newTransaction(); + doUndoRedo (true); + } + else if (key == KeyPress (T('a'), ModifierKeys::commandModifier, 0)) + { + newTransaction(); + moveCursorTo (getTotalNumChars(), false); + moveCursorTo (0, true); + } + else if (key == KeyPress::returnKey) + { + newTransaction(); + + if (returnKeyStartsNewLine) + insertTextAtCursor (T("\n")); + else + returnPressed(); + } + else if (key.isKeyCode (KeyPress::escapeKey)) + { + newTransaction(); + moveCursorTo (getCaretPosition(), false); + escapePressed(); + } + else if (key.getTextCharacter() >= ' ' + || (tabKeyUsed && (key.getTextCharacter() == '\t'))) + { + insertTextAtCursor (String::charToString (key.getTextCharacter())); + + lastTransactionTime = Time::getApproximateMillisecondCounter(); + } + else + { + return false; + } + + return true; +} + +bool TextEditor::keyStateChanged() +{ + // (overridden to avoid forwarding key events to the parent) + return true; +} + +const int baseMenuItemID = 0x7fff0000; + +void TextEditor::addPopupMenuItems (PopupMenu& m, const MouseEvent*) +{ + const bool writable = ! isReadOnly(); + + m.addItem (baseMenuItemID + 1, TRANS("cut"), writable); + m.addItem (baseMenuItemID + 2, TRANS("copy"), selectionStart < selectionEnd); + m.addItem (baseMenuItemID + 3, TRANS("paste"), writable); + m.addItem (baseMenuItemID + 4, TRANS("delete"), writable); + m.addSeparator(); + m.addItem (baseMenuItemID + 5, TRANS("select all")); + m.addSeparator(); + m.addItem (baseMenuItemID + 6, TRANS("undo"), undoManager.canUndo()); + m.addItem (baseMenuItemID + 7, TRANS("redo"), undoManager.canRedo()); +} + +void TextEditor::performPopupMenuAction (const int menuItemID) +{ + switch (menuItemID) + { + case baseMenuItemID + 1: + copy(); + cut(); + break; + + case baseMenuItemID + 2: + copy(); + break; + + case baseMenuItemID + 3: + paste(); + break; + + case baseMenuItemID + 4: + cut(); + break; + + case baseMenuItemID + 5: + moveCursorTo (getTotalNumChars(), false); + moveCursorTo (0, true); + break; + + case baseMenuItemID + 6: + doUndoRedo (false); + break; + + case baseMenuItemID + 7: + doUndoRedo (true); + break; + + default: + break; + } +} + +void TextEditor::focusGained (FocusChangeType) +{ + newTransaction(); + + caretFlashState = true; + + if (selectAllTextWhenFocused) + { + moveCursorTo (0, false); + moveCursorTo (getTotalNumChars(), true); + } + + repaint(); + + if (caretVisible) + textHolder->startTimer (flashSpeedIntervalMs); +} + +void TextEditor::focusLost (FocusChangeType) +{ + newTransaction(); + + wasFocused = false; + textHolder->stopTimer(); + caretFlashState = false; + + postCommandMessage (focusLossMessageId); + repaint(); +} + +void TextEditor::resized() +{ + viewport->setBoundsInset (borderSize); + viewport->setSingleStepSizes (16, roundFloatToInt (currentFont.getHeight())); + + updateTextHolderSize(); + + if (! isMultiLine()) + { + scrollToMakeSureCursorIsVisible(); + } + else + { + cursorHeight = currentFont.getHeight(); // (in case the text is empty and the call below doesn't set this value) + + getCharPosition (caretPosition, + cursorX, cursorY, + cursorHeight); + } +} + +void TextEditor::handleCommandMessage (const int commandId) +{ + const ComponentDeletionWatcher deletionChecker (this); + + for (int i = listeners.size(); --i >= 0;) + { + TextEditorListener* const tl = (TextEditorListener*) listeners [i]; + + if (tl != 0) + { + switch (commandId) + { + case textChangeMessageId: + tl->textEditorTextChanged (*this); + break; + + case returnKeyMessageId: + tl->textEditorReturnKeyPressed (*this); + break; + + case escapeKeyMessageId: + tl->textEditorEscapeKeyPressed (*this); + break; + + case focusLossMessageId: + tl->textEditorFocusLost (*this); + break; + + default: + jassertfalse + break; + } + + if (i > 0 && deletionChecker.hasBeenDeleted()) + return; + } + } +} + +void TextEditor::enablementChanged() +{ + setMouseCursor (MouseCursor (isReadOnly() ? MouseCursor::NormalCursor + : MouseCursor::IBeamCursor)); + repaint(); +} + +void TextEditor::clearInternal (UndoManager* const um) throw() +{ + remove (0, getTotalNumChars(), um, caretPosition); +} + +void TextEditor::insert (const String& text, + const int insertIndex, + const Font& font, + const Colour& colour, + UndoManager* const um, + const int caretPositionToMoveTo) throw() +{ + if (text.isNotEmpty()) + { + if (um != 0) + { + um->perform (new TextEditorInsertAction (*this, + text, + insertIndex, + font, + colour, + caretPosition, + caretPositionToMoveTo)); + } + else + { + repaintText (insertIndex, -1); // must do this before and after changing the data, in case + // a line gets moved due to word wrap + + int index = 0; + int nextIndex = 0; + + for (int i = 0; i < sections.size(); ++i) + { + nextIndex = index + ((UniformTextSection*) sections.getUnchecked(i))->getTotalLength(); + + if (insertIndex == index) + { + sections.insert (i, new UniformTextSection (text, + font, colour, + passwordCharacter)); + break; + } + else if (insertIndex > index && insertIndex < nextIndex) + { + splitSection (i, insertIndex - index); + sections.insert (i + 1, new UniformTextSection (text, + font, colour, + passwordCharacter)); + break; + } + + index = nextIndex; + } + + if (nextIndex == insertIndex) + sections.add (new UniformTextSection (text, + font, colour, + passwordCharacter)); + + coalesceSimilarSections(); + totalNumChars = -1; + + moveCursorTo (caretPositionToMoveTo, false); + + repaintText (insertIndex, -1); + } + } +} + +void TextEditor::reinsert (const int insertIndex, + const VoidArray& sectionsToInsert) throw() +{ + int index = 0; + int nextIndex = 0; + + for (int i = 0; i < sections.size(); ++i) + { + nextIndex = index + ((UniformTextSection*) sections.getUnchecked(i))->getTotalLength(); + + if (insertIndex == index) + { + for (int j = sectionsToInsert.size(); --j >= 0;) + sections.insert (i, new UniformTextSection (*(UniformTextSection*) sectionsToInsert.getUnchecked(j))); + + break; + } + else if (insertIndex > index && insertIndex < nextIndex) + { + splitSection (i, insertIndex - index); + + for (int j = sectionsToInsert.size(); --j >= 0;) + sections.insert (i + 1, new UniformTextSection (*(UniformTextSection*) sectionsToInsert.getUnchecked(j))); + + break; + } + + index = nextIndex; + } + + if (nextIndex == insertIndex) + { + for (int j = 0; j < sectionsToInsert.size(); ++j) + sections.add (new UniformTextSection (*(UniformTextSection*) sectionsToInsert.getUnchecked(j))); + } + + coalesceSimilarSections(); + totalNumChars = -1; +} + +void TextEditor::remove (const int startIndex, + int endIndex, + UndoManager* const um, + const int caretPositionToMoveTo) throw() +{ + if (endIndex > startIndex) + { + int index = 0; + + for (int i = 0; i < sections.size(); ++i) + { + const int nextIndex = index + ((UniformTextSection*) sections[i])->getTotalLength(); + + if (startIndex > index && startIndex < nextIndex) + { + splitSection (i, startIndex - index); + --i; + } + else if (endIndex > index && endIndex < nextIndex) + { + splitSection (i, endIndex - index); + --i; + } + else + { + index = nextIndex; + + if (index > endIndex) + break; + } + } + + index = 0; + + if (um != 0) + { + VoidArray removedSections; + + for (int i = 0; i < sections.size(); ++i) + { + if (endIndex <= startIndex) + break; + + UniformTextSection* const section = (UniformTextSection*) sections.getUnchecked (i); + + const int nextIndex = index + section->getTotalLength(); + + if (startIndex <= index && endIndex >= nextIndex) + removedSections.add (new UniformTextSection (*section)); + + index = nextIndex; + } + + um->perform (new TextEditorRemoveAction (*this, + startIndex, + endIndex, + caretPosition, + caretPositionToMoveTo, + removedSections)); + } + else + { + for (int i = 0; i < sections.size(); ++i) + { + if (endIndex <= startIndex) + break; + + UniformTextSection* const section = (UniformTextSection*) sections.getUnchecked (i); + + const int nextIndex = index + section->getTotalLength(); + + if (startIndex <= index && endIndex >= nextIndex) + { + sections.remove(i); + endIndex -= (nextIndex - index); + section->clear(); + delete section; + --i; + } + else + { + index = nextIndex; + } + } + + coalesceSimilarSections(); + totalNumChars = -1; + + moveCursorTo (caretPositionToMoveTo, false); + + repaintText (startIndex, -1); + } + } +} + +const String TextEditor::getText() const throw() +{ + String t; + + for (int i = 0; i < sections.size(); ++i) + t += ((const UniformTextSection*) sections.getUnchecked(i))->getAllText(); + + return t; +} + +const String TextEditor::getTextSubstring (const int startCharacter, const int endCharacter) const throw() +{ + String t; + int index = 0; + + for (int i = 0; i < sections.size(); ++i) + { + const UniformTextSection* const s = (const UniformTextSection*) sections.getUnchecked(i); + const int nextIndex = index + s->getTotalLength(); + + if (startCharacter < nextIndex) + { + if (endCharacter <= index) + break; + + const int start = jmax (index, startCharacter); + t += s->getTextSubstring (start - index, endCharacter - index); + } + + index = nextIndex; + } + + return t; +} + +const String TextEditor::getHighlightedText() const throw() +{ + return getTextSubstring (getHighlightedRegionStart(), + getHighlightedRegionStart() + getHighlightedRegionLength()); +} + +int TextEditor::getTotalNumChars() throw() +{ + if (totalNumChars < 0) + { + totalNumChars = 0; + + for (int i = sections.size(); --i >= 0;) + totalNumChars += ((const UniformTextSection*) sections.getUnchecked(i))->getTotalLength(); + } + + return totalNumChars; +} + +bool TextEditor::isEmpty() const throw() +{ + if (totalNumChars != 0) + { + for (int i = sections.size(); --i >= 0;) + if (((const UniformTextSection*) sections.getUnchecked(i))->getTotalLength() > 0) + return false; + } + + return true; +} + +void TextEditor::getCharPosition (const int index, float& cx, float& cy, float& lineHeight) const throw() +{ + const float wordWrapWidth = getWordWrapWidth(); + + if (wordWrapWidth > 0 && sections.size() > 0) + { + TextEditorIterator i (sections, wordWrapWidth, passwordCharacter); + + i.getCharPosition (index, cx, cy, lineHeight); + } + else + { + cx = cy = 0; + lineHeight = currentFont.getHeight(); + } +} + +int TextEditor::indexAtPosition (const float x, const float y) throw() +{ + const float wordWrapWidth = getWordWrapWidth(); + + if (wordWrapWidth > 0) + { + TextEditorIterator i (sections, wordWrapWidth, passwordCharacter); + + while (i.next()) + { + if (i.lineY + getHeight() > y) + i.updateLineHeight(); + + if (i.lineY + i.lineHeight > y) + { + if (i.lineY > y) + return jmax (0, i.indexInText - 1); + + if (i.atomX >= x) + return i.indexInText; + + if (x < i.atomRight) + return i.xToIndex (x); + } + } + } + + return getTotalNumChars(); +} + +static int getCharacterCategory (const tchar character) throw() +{ + return CharacterFunctions::isLetterOrDigit (character) + ? 2 : (CharacterFunctions::isWhitespace (character) ? 0 : 1); +} + +int TextEditor::findWordBreakAfter (const int position) const throw() +{ + const String t (getTextSubstring (position, position + 512)); + const int totalLength = t.length(); + int i = 0; + + while (i < totalLength && CharacterFunctions::isWhitespace (t[i])) + ++i; + + const int type = getCharacterCategory (t[i]); + + while (i < totalLength && type == getCharacterCategory (t[i])) + ++i; + + while (i < totalLength && CharacterFunctions::isWhitespace (t[i])) + ++i; + + return position + i; +} + +int TextEditor::findWordBreakBefore (const int position) const throw() +{ + if (position <= 0) + return 0; + + const int startOfBuffer = jmax (0, position - 512); + const String t (getTextSubstring (startOfBuffer, position)); + + int i = position - startOfBuffer; + + while (i > 0 && CharacterFunctions::isWhitespace (t [i - 1])) + --i; + + if (i > 0) + { + const int type = getCharacterCategory (t [i - 1]); + + while (i > 0 && type == getCharacterCategory (t [i - 1])) + --i; + } + + jassert (startOfBuffer + i >= 0); + return startOfBuffer + i; +} + +void TextEditor::splitSection (const int sectionIndex, + const int charToSplitAt) throw() +{ + jassert (sections[sectionIndex] != 0); + + sections.insert (sectionIndex + 1, + ((UniformTextSection*) sections.getUnchecked (sectionIndex)) + ->split (charToSplitAt, passwordCharacter)); +} + +void TextEditor::coalesceSimilarSections() throw() +{ + for (int i = 0; i < sections.size() - 1; ++i) + { + UniformTextSection* const s1 = (UniformTextSection*) (sections.getUnchecked (i)); + UniformTextSection* const s2 = (UniformTextSection*) (sections.getUnchecked (i + 1)); + + if (s1->font == s2->font + && s1->colour == s2->colour) + { + s1->append (*s2, passwordCharacter); + sections.remove (i + 1); + delete s2; + --i; + } + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_TextEditor.cpp *********/ + +/********* Start of inlined file: juce_Toolbar.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +const tchar* const Toolbar::toolbarDragDescriptor = T("_toolbarItem_"); + +class ToolbarSpacerComp : public ToolbarItemComponent +{ +public: + ToolbarSpacerComp (const int itemId, const float fixedSize_, const bool drawBar_) + : ToolbarItemComponent (itemId, String::empty, false), + fixedSize (fixedSize_), + drawBar (drawBar_) + { + } + + ~ToolbarSpacerComp() + { + } + + bool getToolbarItemSizes (int toolbarThickness, bool /*isToolbarVertical*/, + int& preferredSize, int& minSize, int& maxSize) + { + if (fixedSize <= 0) + { + preferredSize = toolbarThickness * 2; + minSize = 4; + maxSize = 32768; + } + else + { + maxSize = roundFloatToInt (toolbarThickness * fixedSize); + minSize = drawBar ? maxSize : jmin (4, maxSize); + preferredSize = maxSize; + + if (getEditingMode() == editableOnPalette) + preferredSize = maxSize = toolbarThickness / (drawBar ? 3 : 2); + } + + return true; + } + + void paintButtonArea (Graphics&, int, int, bool, bool) + { + } + + void contentAreaChanged (const Rectangle&) + { + } + + int getResizeOrder() const throw() + { + return fixedSize <= 0 ? 0 : 1; + } + + void paint (Graphics& g) + { + const int w = getWidth(); + const int h = getHeight(); + + if (drawBar) + { + g.setColour (findColour (Toolbar::separatorColourId, true)); + + const float thickness = 0.2f; + + if (isToolbarVertical()) + g.fillRect (w * 0.1f, h * (0.5f - thickness * 0.5f), w * 0.8f, h * thickness); + else + g.fillRect (w * (0.5f - thickness * 0.5f), h * 0.1f, w * thickness, h * 0.8f); + } + + if (getEditingMode() != normalMode && ! drawBar) + { + g.setColour (findColour (Toolbar::separatorColourId, true)); + + const int indentX = jmin (2, (w - 3) / 2); + const int indentY = jmin (2, (h - 3) / 2); + g.drawRect (indentX, indentY, w - indentX * 2, h - indentY * 2, 1); + + if (fixedSize <= 0) + { + float x1, y1, x2, y2, x3, y3, x4, y4, hw, hl; + + if (isToolbarVertical()) + { + x1 = w * 0.5f; + y1 = h * 0.4f; + x2 = x1; + y2 = indentX * 2.0f; + + x3 = x1; + y3 = h * 0.6f; + x4 = x1; + y4 = h - y2; + + hw = w * 0.15f; + hl = w * 0.2f; + } + else + { + x1 = w * 0.4f; + y1 = h * 0.5f; + x2 = indentX * 2.0f; + y2 = y1; + + x3 = w * 0.6f; + y3 = y1; + x4 = w - x2; + y4 = y1; + + hw = h * 0.15f; + hl = h * 0.2f; + } + + Path p; + p.addArrow (x1, y1, x2, y2, 1.5f, hw, hl); + p.addArrow (x3, y3, x4, y4, 1.5f, hw, hl); + g.fillPath (p); + } + } + } + + juce_UseDebuggingNewOperator + +private: + const float fixedSize; + const bool drawBar; + + ToolbarSpacerComp (const ToolbarSpacerComp&); + const ToolbarSpacerComp& operator= (const ToolbarSpacerComp&); +}; + +class MissingItemsComponent : public PopupMenuCustomComponent +{ +public: + MissingItemsComponent (Toolbar& owner_, const int height_) + : PopupMenuCustomComponent (true), + owner (owner_), + height (height_) + { + for (int i = owner_.getNumChildComponents(); --i >= 0;) + { + ToolbarItemComponent* const tc = dynamic_cast (owner_.getChildComponent (i)); + + if (tc != 0 && dynamic_cast (tc) == 0 && ! tc->isVisible()) + { + oldIndexes.insert (0, i); + addAndMakeVisible (tc, 0); + } + } + + layout (400); + } + + ~MissingItemsComponent() + { + // deleting the toolbar while its menu it open?? + jassert (owner.isValidComponent()); + + for (int i = 0; i < getNumChildComponents(); ++i) + { + ToolbarItemComponent* const tc = dynamic_cast (getChildComponent (i)); + + if (tc != 0) + { + tc->setVisible (false); + owner.addChildComponent (tc, oldIndexes.remove (i)); + --i; + } + } + + owner.resized(); + } + + void layout (const int preferredWidth) + { + const int indent = 8; + int x = indent; + int y = indent; + int maxX = 0; + + for (int i = 0; i < getNumChildComponents(); ++i) + { + ToolbarItemComponent* const tc = dynamic_cast (getChildComponent (i)); + + if (tc != 0) + { + int preferredSize = 1, minSize = 1, maxSize = 1; + + if (tc->getToolbarItemSizes (height, false, preferredSize, minSize, maxSize)) + { + if (x + preferredSize > preferredWidth && x > indent) + { + x = indent; + y += height; + } + + tc->setBounds (x, y, preferredSize, height); + + x += preferredSize; + maxX = jmax (maxX, x); + } + } + } + + setSize (maxX + 8, y + height + 8); + } + + void getIdealSize (int& idealWidth, int& idealHeight) + { + idealWidth = getWidth(); + idealHeight = getHeight(); + } + + juce_UseDebuggingNewOperator + +private: + Toolbar& owner; + const int height; + Array oldIndexes; + + MissingItemsComponent (const MissingItemsComponent&); + const MissingItemsComponent& operator= (const MissingItemsComponent&); +}; + +Toolbar::Toolbar() + : vertical (false), + isEditingActive (false), + toolbarStyle (Toolbar::iconsOnly) +{ + addChildComponent (missingItemsButton = getLookAndFeel().createToolbarMissingItemsButton (*this)); + + missingItemsButton->setAlwaysOnTop (true); + missingItemsButton->addButtonListener (this); +} + +Toolbar::~Toolbar() +{ + animator.cancelAllAnimations (true); + deleteAllChildren(); +} + +void Toolbar::setVertical (const bool shouldBeVertical) +{ + if (vertical != shouldBeVertical) + { + vertical = shouldBeVertical; + resized(); + } +} + +void Toolbar::clear() +{ + for (int i = getNumChildComponents(); --i >= 0;) + { + ToolbarItemComponent* const tc = dynamic_cast (getChildComponent (i)); + if (tc != 0) + delete tc; + } + + resized(); +} + +ToolbarItemComponent* Toolbar::createItem (ToolbarItemFactory& factory, const int itemId) +{ + if (itemId == ToolbarItemFactory::separatorBarId) + return new ToolbarSpacerComp (itemId, 0.1f, true); + else if (itemId == ToolbarItemFactory::spacerId) + return new ToolbarSpacerComp (itemId, 0.5f, false); + else if (itemId == ToolbarItemFactory::flexibleSpacerId) + return new ToolbarSpacerComp (itemId, 0, false); + + return factory.createItem (itemId); +} + +void Toolbar::addItemInternal (ToolbarItemFactory& factory, + const int itemId, + const int insertIndex) +{ + // An ID can't be zero - this might indicate a mistake somewhere? + jassert (itemId != 0); + + ToolbarItemComponent* const tc = createItem (factory, itemId); + + if (tc != 0) + { +#ifdef JUCE_DEBUG + Array allowedIds; + factory.getAllToolbarItemIds (allowedIds); + + // If your factory can create an item for a given ID, it must also return + // that ID from its getAllToolbarItemIds() method! + jassert (allowedIds.contains (itemId)); +#endif + + addAndMakeVisible (tc, insertIndex); + } +} + +void Toolbar::addItem (ToolbarItemFactory& factory, + const int itemId, + const int insertIndex) +{ + addItemInternal (factory, itemId, insertIndex); + resized(); +} + +void Toolbar::addDefaultItems (ToolbarItemFactory& factoryToUse) +{ + Array ids; + factoryToUse.getDefaultItemSet (ids); + + clear(); + + for (int i = 0; i < ids.size(); ++i) + addItemInternal (factoryToUse, ids.getUnchecked (i), -1); + + resized(); +} + +void Toolbar::removeToolbarItem (const int itemIndex) +{ + ToolbarItemComponent* const tc = getItemComponent (itemIndex); + + if (tc != 0) + { + delete tc; + resized(); + } +} + +int Toolbar::getNumItems() const throw() +{ + return getNumChildComponents() - 1; +} + +int Toolbar::getItemId (const int itemIndex) const throw() +{ + ToolbarItemComponent* const tc = getItemComponent (itemIndex); + return tc != 0 ? tc->getItemId() : 0; +} + +ToolbarItemComponent* Toolbar::getItemComponent (const int itemIndex) const throw() +{ + if (itemIndex < getNumItems()) + return dynamic_cast (getChildComponent (itemIndex)); + + return 0; +} + +ToolbarItemComponent* Toolbar::getNextActiveComponent (int index, const int delta) const +{ + for (;;) + { + index += delta; + ToolbarItemComponent* const tc = getItemComponent (index); + + if (tc == 0) + break; + + if (tc->isActive) + return tc; + } + + return 0; +} + +void Toolbar::setStyle (const ToolbarItemStyle& newStyle) +{ + if (toolbarStyle != newStyle) + { + toolbarStyle = newStyle; + updateAllItemPositions (false); + } +} + +const String Toolbar::toString() const +{ + String s (T("TB:")); + + for (int i = 0; i < getNumItems(); ++i) + s << getItemId(i) << T(' '); + + return s.trimEnd(); +} + +bool Toolbar::restoreFromString (ToolbarItemFactory& factoryToUse, + const String& savedVersion) +{ + if (! savedVersion.startsWith (T("TB:"))) + return false; + + StringArray tokens; + tokens.addTokens (savedVersion.substring (3), false); + + clear(); + + for (int i = 0; i < tokens.size(); ++i) + addItemInternal (factoryToUse, tokens[i].getIntValue(), -1); + + resized(); + return true; +} + +void Toolbar::paint (Graphics& g) +{ + getLookAndFeel().paintToolbarBackground (g, getWidth(), getHeight(), *this); +} + +int Toolbar::getThickness() const throw() +{ + return vertical ? getWidth() : getHeight(); +} + +int Toolbar::getLength() const throw() +{ + return vertical ? getHeight() : getWidth(); +} + +void Toolbar::setEditingActive (const bool active) +{ + if (isEditingActive != active) + { + isEditingActive = active; + updateAllItemPositions (false); + } +} + +void Toolbar::resized() +{ + updateAllItemPositions (false); +} + +void Toolbar::updateAllItemPositions (const bool animate) +{ + if (getWidth() > 0 && getHeight() > 0) + { + StretchableObjectResizer resizer; + + const int numComponents = getNumChildComponents(); + Array activeComps; + + int i; + for (i = 0; i < numComponents; ++i) + { + ToolbarItemComponent* const tc = dynamic_cast (getChildComponent (i)); + + // have you added a component directly to the toolbar? That's not advisable! Only use addCustomToolbarItem()! + jassert (tc != 0 || getChildComponent(i) == missingItemsButton); + + if (tc != 0) + { + tc->setEditingMode (isEditingActive ? ToolbarItemComponent::editableOnToolbar + : ToolbarItemComponent::normalMode); + + tc->setStyle (toolbarStyle); + + ToolbarSpacerComp* const spacer = dynamic_cast (tc); + + int preferredSize = 1, minSize = 1, maxSize = 1; + + if (tc->getToolbarItemSizes (getThickness(), isVertical(), + preferredSize, minSize, maxSize)) + { + tc->isActive = true; + resizer.addItem (preferredSize, minSize, maxSize, + spacer != 0 ? spacer->getResizeOrder() : 2); + } + else + { + tc->isActive = false; + tc->setVisible (false); + } + } + } + + resizer.resizeToFit (getLength()); + + int totalLength = 0; + + for (i = 0; i < resizer.getNumItems(); ++i) + totalLength += (int) resizer.getItemSize (i); + + const bool itemsOffTheEnd = totalLength > getLength(); + + const int extrasButtonSize = getThickness() / 2; + missingItemsButton->setSize (extrasButtonSize, extrasButtonSize); + missingItemsButton->setVisible (itemsOffTheEnd); + missingItemsButton->setEnabled (! isEditingActive); + + if (vertical) + missingItemsButton->setCentrePosition (getWidth() / 2, + getHeight() - 4 - extrasButtonSize / 2); + else + missingItemsButton->setCentrePosition (getWidth() - 4 - extrasButtonSize / 2, + getHeight() / 2); + + const int maxLength = itemsOffTheEnd ? (vertical ? missingItemsButton->getY() + : missingItemsButton->getX()) - 4 + : getLength(); + + int pos = 0, activeIndex = 0; + for (i = 0; i < getNumChildComponents(); ++i) + { + ToolbarItemComponent* const tc = dynamic_cast (getChildComponent (i)); + + if (tc != 0 && tc->isActive) + { + const int size = (int) resizer.getItemSize (activeIndex++); + + Rectangle newBounds; + if (vertical) + newBounds.setBounds (0, pos, getWidth(), size); + else + newBounds.setBounds (pos, 0, size, getHeight()); + + if (animate) + { + animator.animateComponent (tc, newBounds, 200, 3.0, 0.0); + } + else + { + animator.cancelAnimation (tc, false); + tc->setBounds (newBounds); + } + + pos += size; + tc->setVisible (pos <= maxLength + && ((! tc->isBeingDragged) + || tc->getEditingMode() == ToolbarItemComponent::editableOnPalette)); + } + } + } +} + +void Toolbar::buttonClicked (Button*) +{ + jassert (missingItemsButton->isShowing()); + + if (missingItemsButton->isShowing()) + { + PopupMenu m; + m.addCustomItem (1, new MissingItemsComponent (*this, getThickness())); + m.showAt (missingItemsButton); + } +} + +bool Toolbar::isInterestedInDragSource (const String& sourceDescription, + Component* /*sourceComponent*/) +{ + return sourceDescription == toolbarDragDescriptor && isEditingActive; +} + +void Toolbar::itemDragMove (const String&, Component* sourceComponent, int x, int y) +{ + ToolbarItemComponent* const tc = dynamic_cast (sourceComponent); + + if (tc != 0) + { + if (getNumItems() == 0) + { + if (tc->getEditingMode() == ToolbarItemComponent::editableOnPalette) + { + ToolbarItemPalette* const palette = tc->findParentComponentOfClass ((ToolbarItemPalette*) 0); + + if (palette != 0) + palette->replaceComponent (tc); + } + else + { + jassert (tc->getEditingMode() == ToolbarItemComponent::editableOnToolbar); + } + + addChildComponent (tc); + updateAllItemPositions (false); + } + else + { + for (int i = getNumItems(); --i >= 0;) + { + int currentIndex = getIndexOfChildComponent (tc); + + if (currentIndex < 0) + { + if (tc->getEditingMode() == ToolbarItemComponent::editableOnPalette) + { + ToolbarItemPalette* const palette = tc->findParentComponentOfClass ((ToolbarItemPalette*) 0); + + if (palette != 0) + palette->replaceComponent (tc); + } + else + { + jassert (tc->getEditingMode() == ToolbarItemComponent::editableOnToolbar); + } + + addChildComponent (tc); + currentIndex = getIndexOfChildComponent (tc); + updateAllItemPositions (true); + } + + int newIndex = currentIndex; + + const int dragObjectLeft = vertical ? (y - tc->dragOffsetY) : (x - tc->dragOffsetX); + const int dragObjectRight = dragObjectLeft + (vertical ? tc->getHeight() : tc->getWidth()); + + const Rectangle current (animator.getComponentDestination (getChildComponent (newIndex))); + ToolbarItemComponent* const prev = getNextActiveComponent (newIndex, -1); + + if (prev != 0) + { + const Rectangle previousPos (animator.getComponentDestination (prev)); + + if (abs (dragObjectLeft - (vertical ? previousPos.getY() : previousPos.getX()) + < abs (dragObjectRight - (vertical ? current.getBottom() : current.getRight())))) + { + newIndex = getIndexOfChildComponent (prev); + } + } + + ToolbarItemComponent* const next = getNextActiveComponent (newIndex, 1); + if (next != 0) + { + const Rectangle nextPos (animator.getComponentDestination (next)); + + if (abs (dragObjectLeft - (vertical ? current.getY() : current.getX()) + > abs (dragObjectRight - (vertical ? nextPos.getBottom() : nextPos.getRight())))) + { + newIndex = getIndexOfChildComponent (next) + 1; + } + } + + if (newIndex != currentIndex) + { + removeChildComponent (tc); + addChildComponent (tc, newIndex); + updateAllItemPositions (true); + } + else + { + break; + } + } + } + } +} + +void Toolbar::itemDragExit (const String&, Component* sourceComponent) +{ + ToolbarItemComponent* const tc = dynamic_cast (sourceComponent); + + if (tc != 0) + { + if (isParentOf (tc)) + { + removeChildComponent (tc); + updateAllItemPositions (true); + } + } +} + +void Toolbar::itemDropped (const String&, Component*, int, int) +{ +} + +void Toolbar::mouseDown (const MouseEvent& e) +{ + if (e.mods.isPopupMenu()) + { + } +} + +class ToolbarCustomisationDialog : public DialogWindow +{ +public: + ToolbarCustomisationDialog (ToolbarItemFactory& factory, + Toolbar* const toolbar_, + const int optionFlags) + : DialogWindow (TRANS("Add/remove items from toolbar"), Colours::white, true, true), + toolbar (toolbar_) + { + setContentComponent (new CustomiserPanel (factory, toolbar, optionFlags), true, true); + setResizable (true, true); + setResizeLimits (400, 300, 1500, 1000); + positionNearBar(); + } + + ~ToolbarCustomisationDialog() + { + setContentComponent (0, true); + } + + void closeButtonPressed() + { + setVisible (false); + } + + bool canModalEventBeSentToComponent (const Component* comp) + { + return toolbar->isParentOf (comp); + } + + void positionNearBar() + { + const Rectangle screenSize (toolbar->getParentMonitorArea()); + const int tbx = toolbar->getScreenX(); + const int tby = toolbar->getScreenY(); + const int gap = 8; + + int x, y; + + if (toolbar->isVertical()) + { + y = tby; + + if (tbx > screenSize.getCentreX()) + x = tbx - getWidth() - gap; + else + x = tbx + toolbar->getWidth() + gap; + } + else + { + x = tbx + (toolbar->getWidth() - getWidth()) / 2; + + if (tby > screenSize.getCentreY()) + y = tby - getHeight() - gap; + else + y = tby + toolbar->getHeight() + gap; + } + + setTopLeftPosition (x, y); + } + +private: + Toolbar* const toolbar; + + class CustomiserPanel : public Component, + private ComboBoxListener, + private ButtonListener + { + public: + CustomiserPanel (ToolbarItemFactory& factory_, + Toolbar* const toolbar_, + const int optionFlags) + : factory (factory_), + toolbar (toolbar_), + styleBox (0), + defaultButton (0) + { + addAndMakeVisible (palette = new ToolbarItemPalette (factory, toolbar)); + + if ((optionFlags & (Toolbar::allowIconsOnlyChoice + | Toolbar::allowIconsWithTextChoice + | Toolbar::allowTextOnlyChoice)) != 0) + { + addAndMakeVisible (styleBox = new ComboBox (String::empty)); + styleBox->setEditableText (false); + + if ((optionFlags & Toolbar::allowIconsOnlyChoice) != 0) + styleBox->addItem (TRANS("Show icons only"), 1); + if ((optionFlags & Toolbar::allowIconsWithTextChoice) != 0) + styleBox->addItem (TRANS("Show icons and descriptions"), 2); + if ((optionFlags & Toolbar::allowTextOnlyChoice) != 0) + styleBox->addItem (TRANS("Show descriptions only"), 3); + + if (toolbar_->getStyle() == Toolbar::iconsOnly) + styleBox->setSelectedId (1); + else if (toolbar_->getStyle() == Toolbar::iconsWithText) + styleBox->setSelectedId (2); + else if (toolbar_->getStyle() == Toolbar::textOnly) + styleBox->setSelectedId (3); + + styleBox->addListener (this); + } + + if ((optionFlags & Toolbar::showResetToDefaultsButton) != 0) + { + addAndMakeVisible (defaultButton = new TextButton (TRANS ("Restore to default set of items"))); + defaultButton->addButtonListener (this); + } + + addAndMakeVisible (instructions = new Label (String::empty, + TRANS ("You can drag the items above and drop them onto a toolbar to add them.\n\nItems on the toolbar can also be dragged around to change their order, or dragged off the edge to delete them."))); + instructions->setFont (Font (13.0f)); + + setSize (500, 300); + } + + ~CustomiserPanel() + { + deleteAllChildren(); + } + + void comboBoxChanged (ComboBox*) + { + if (styleBox->getSelectedId() == 1) + toolbar->setStyle (Toolbar::iconsOnly); + else if (styleBox->getSelectedId() == 2) + toolbar->setStyle (Toolbar::iconsWithText); + else if (styleBox->getSelectedId() == 3) + toolbar->setStyle (Toolbar::textOnly); + + palette->resized(); // to make it update the styles + } + + void buttonClicked (Button*) + { + toolbar->addDefaultItems (factory); + } + + void paint (Graphics& g) + { + Colour background; + + DialogWindow* const dw = findParentComponentOfClass ((DialogWindow*) 0); + + if (dw != 0) + background = dw->getBackgroundColour(); + + g.setColour (background.contrasting().withAlpha (0.3f)); + g.fillRect (palette->getX(), palette->getBottom() - 1, palette->getWidth(), 1); + } + + void resized() + { + palette->setBounds (0, 0, getWidth(), getHeight() - 120); + + if (styleBox != 0) + styleBox->setBounds (10, getHeight() - 110, 200, 22); + + if (defaultButton != 0) + { + defaultButton->changeWidthToFitText (22); + defaultButton->setTopLeftPosition (240, getHeight() - 110); + } + + instructions->setBounds (10, getHeight() - 80, getWidth() - 20, 80); + } + + private: + ToolbarItemFactory& factory; + Toolbar* const toolbar; + + Label* instructions; + ToolbarItemPalette* palette; + ComboBox* styleBox; + TextButton* defaultButton; + }; +}; + +void Toolbar::showCustomisationDialog (ToolbarItemFactory& factory, const int optionFlags) +{ + setEditingActive (true); + + ToolbarCustomisationDialog dw (factory, this, optionFlags); + dw.runModalLoop(); + + jassert (isValidComponent()); // ? deleting the toolbar while it's being edited? + setEditingActive (false); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_Toolbar.cpp *********/ + +/********* Start of inlined file: juce_ToolbarItemComponent.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +ToolbarItemFactory::ToolbarItemFactory() +{ +} + +ToolbarItemFactory::~ToolbarItemFactory() +{ +} + +class ItemDragAndDropOverlayComponent : public Component +{ +public: + ItemDragAndDropOverlayComponent() + : isDragging (false) + { + setAlwaysOnTop (true); + setRepaintsOnMouseActivity (true); + setMouseCursor (MouseCursor::DraggingHandCursor); + } + + ~ItemDragAndDropOverlayComponent() + { + } + + void paint (Graphics& g) + { + ToolbarItemComponent* const tc = dynamic_cast (getParentComponent()); + + if (isMouseOverOrDragging() + && tc != 0 + && tc->getEditingMode() == ToolbarItemComponent::editableOnToolbar) + { + g.setColour (findColour (Toolbar::editingModeOutlineColourId, true)); + g.drawRect (0, 0, getWidth(), getHeight(), + jmin (2, (getWidth() - 1) / 2, (getHeight() - 1) / 2)); + } + } + + void mouseDown (const MouseEvent& e) + { + isDragging = false; + ToolbarItemComponent* const tc = dynamic_cast (getParentComponent()); + + if (tc != 0) + { + tc->dragOffsetX = e.x; + tc->dragOffsetY = e.y; + } + } + + void mouseDrag (const MouseEvent& e) + { + if (! (isDragging || e.mouseWasClicked())) + { + isDragging = true; + DragAndDropContainer* const dnd = DragAndDropContainer::findParentDragContainerFor (this); + + if (dnd != 0) + { + dnd->startDragging (Toolbar::toolbarDragDescriptor, getParentComponent(), 0, true); + + ToolbarItemComponent* const tc = dynamic_cast (getParentComponent()); + + if (tc != 0) + { + tc->isBeingDragged = true; + + if (tc->getEditingMode() == ToolbarItemComponent::editableOnToolbar) + tc->setVisible (false); + } + } + } + } + + void mouseUp (const MouseEvent&) + { + isDragging = false; + ToolbarItemComponent* const tc = dynamic_cast (getParentComponent()); + + if (tc != 0) + { + tc->isBeingDragged = false; + + Toolbar* const tb = tc->getToolbar(); + + if (tb != 0) + tb->updateAllItemPositions (true); + else if (tc->getEditingMode() == ToolbarItemComponent::editableOnToolbar) + delete tc; + } + } + + void parentSizeChanged() + { + setBounds (0, 0, getParentWidth(), getParentHeight()); + } + + juce_UseDebuggingNewOperator + +private: + bool isDragging; + + ItemDragAndDropOverlayComponent (const ItemDragAndDropOverlayComponent&); + const ItemDragAndDropOverlayComponent& operator= (const ItemDragAndDropOverlayComponent&); +}; + +ToolbarItemComponent::ToolbarItemComponent (const int itemId_, + const String& labelText, + const bool isBeingUsedAsAButton_) + : Button (labelText), + itemId (itemId_), + mode (normalMode), + toolbarStyle (Toolbar::iconsOnly), + overlayComp (0), + dragOffsetX (0), + dragOffsetY (0), + isActive (true), + isBeingDragged (false), + isBeingUsedAsAButton (isBeingUsedAsAButton_) +{ + // Your item ID can't be 0! + jassert (itemId_ != 0); +} + +ToolbarItemComponent::~ToolbarItemComponent() +{ + jassert (overlayComp == 0 || overlayComp->isValidComponent()); + delete overlayComp; +} + +Toolbar* ToolbarItemComponent::getToolbar() const +{ + return dynamic_cast (getParentComponent()); +} + +bool ToolbarItemComponent::isToolbarVertical() const +{ + const Toolbar* const t = getToolbar(); + return t != 0 && t->isVertical(); +} + +void ToolbarItemComponent::setStyle (const Toolbar::ToolbarItemStyle& newStyle) +{ + if (toolbarStyle != newStyle) + { + toolbarStyle = newStyle; + repaint(); + resized(); + } +} + +void ToolbarItemComponent::paintButton (Graphics& g, bool isMouseOver, bool isMouseDown) +{ + if (isBeingUsedAsAButton) + getLookAndFeel().paintToolbarButtonBackground (g, getWidth(), getHeight(), + isMouseOver, isMouseDown, *this); + + if (toolbarStyle != Toolbar::iconsOnly) + { + const int indent = contentArea.getX(); + int y = indent; + int h = getHeight() - indent * 2; + + if (toolbarStyle == Toolbar::iconsWithText) + { + y = contentArea.getBottom() + indent / 2; + h -= contentArea.getHeight(); + } + + getLookAndFeel().paintToolbarButtonLabel (g, indent, y, getWidth() - indent * 2, h, + getButtonText(), *this); + } + + if (! contentArea.isEmpty()) + { + g.saveState(); + g.setOrigin (contentArea.getX(), contentArea.getY()); + g.reduceClipRegion (0, 0, contentArea.getWidth(), contentArea.getHeight()); + + paintButtonArea (g, contentArea.getWidth(), contentArea.getHeight(), isMouseOver, isMouseDown); + + g.restoreState(); + } +} + +void ToolbarItemComponent::resized() +{ + if (toolbarStyle != Toolbar::textOnly) + { + const int indent = jmin (proportionOfWidth (0.08f), + proportionOfHeight (0.08f)); + + contentArea = Rectangle (indent, indent, + getWidth() - indent * 2, + toolbarStyle == Toolbar::iconsWithText ? proportionOfHeight (0.55f) + : (getHeight() - indent * 2)); + } + else + { + contentArea = Rectangle(); + } + + contentAreaChanged (contentArea); +} + +void ToolbarItemComponent::setEditingMode (const ToolbarEditingMode newMode) +{ + if (mode != newMode) + { + mode = newMode; + repaint(); + + if (mode == normalMode) + { + jassert (overlayComp == 0 || overlayComp->isValidComponent()); + delete overlayComp; + overlayComp = 0; + } + else if (overlayComp == 0) + { + addAndMakeVisible (overlayComp = new ItemDragAndDropOverlayComponent()); + overlayComp->parentSizeChanged(); + } + + resized(); + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ToolbarItemComponent.cpp *********/ + +/********* Start of inlined file: juce_ToolbarItemPalette.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +ToolbarItemPalette::ToolbarItemPalette (ToolbarItemFactory& factory_, + Toolbar* const toolbar_) + : factory (factory_), + toolbar (toolbar_) +{ + Component* const itemHolder = new Component(); + + Array allIds; + factory_.getAllToolbarItemIds (allIds); + + for (int i = 0; i < allIds.size(); ++i) + { + ToolbarItemComponent* const tc = Toolbar::createItem (factory_, allIds.getUnchecked (i)); + + jassert (tc != 0); + if (tc != 0) + { + itemHolder->addAndMakeVisible (tc); + tc->setEditingMode (ToolbarItemComponent::editableOnPalette); + } + } + + viewport = new Viewport(); + viewport->setViewedComponent (itemHolder); + addAndMakeVisible (viewport); +} + +ToolbarItemPalette::~ToolbarItemPalette() +{ + viewport->getViewedComponent()->deleteAllChildren(); + deleteAllChildren(); +} + +void ToolbarItemPalette::resized() +{ + viewport->setBoundsInset (BorderSize (1)); + + Component* const itemHolder = viewport->getViewedComponent(); + + const int indent = 8; + const int preferredWidth = viewport->getWidth() - viewport->getScrollBarThickness() - indent; + const int height = toolbar->getThickness(); + int x = indent; + int y = indent; + int maxX = 0; + + for (int i = 0; i < itemHolder->getNumChildComponents(); ++i) + { + ToolbarItemComponent* const tc = dynamic_cast (itemHolder->getChildComponent (i)); + + if (tc != 0) + { + tc->setStyle (toolbar->getStyle()); + + int preferredSize = 1, minSize = 1, maxSize = 1; + + if (tc->getToolbarItemSizes (height, false, preferredSize, minSize, maxSize)) + { + if (x + preferredSize > preferredWidth && x > indent) + { + x = indent; + y += height; + } + + tc->setBounds (x, y, preferredSize, height); + + x += preferredSize + 8; + maxX = jmax (maxX, x); + } + } + } + + itemHolder->setSize (maxX, y + height + 8); +} + +void ToolbarItemPalette::replaceComponent (ToolbarItemComponent* const comp) +{ + ToolbarItemComponent* const tc = Toolbar::createItem (factory, comp->getItemId()); + + jassert (tc != 0); + + if (tc != 0) + { + tc->setBounds (comp->getBounds()); + tc->setStyle (toolbar->getStyle()); + tc->setEditingMode (comp->getEditingMode()); + viewport->getViewedComponent()->addAndMakeVisible (tc, getIndexOfChildComponent (comp)); + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ToolbarItemPalette.cpp *********/ + +/********* Start of inlined file: juce_TreeView.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +class TreeViewContentComponent : public Component +{ +public: + TreeViewContentComponent (TreeView* const owner_) + : owner (owner_), + isDragging (false) + { + } + + ~TreeViewContentComponent() + { + deleteAllChildren(); + } + + void mouseDown (const MouseEvent& e) + { + isDragging = false; + needSelectionOnMouseUp = false; + + Rectangle pos; + TreeViewItem* const item = findItemAt (e.y, pos); + + if (item != 0 && e.x >= pos.getX()) + { + if (! owner->isMultiSelectEnabled()) + item->setSelected (true, true); + else if (item->isSelected()) + needSelectionOnMouseUp = ! e.mods.isPopupMenu(); + else + selectBasedOnModifiers (item, e.mods); + + MouseEvent e2 (e); + e2.x -= pos.getX(); + e2.y -= pos.getY(); + item->itemClicked (e2); + } + } + + void mouseUp (const MouseEvent& e) + { + Rectangle pos; + TreeViewItem* const item = findItemAt (e.y, pos); + + if (item != 0 && e.mouseWasClicked()) + { + if (needSelectionOnMouseUp) + { + selectBasedOnModifiers (item, e.mods); + } + else if (e.mouseWasClicked()) + { + if (e.x >= pos.getX() - owner->getIndentSize() + && e.x < pos.getX()) + { + item->setOpen (! item->isOpen()); + } + } + } + } + + void mouseDoubleClick (const MouseEvent& e) + { + if (e.getNumberOfClicks() != 3) // ignore triple clicks + { + Rectangle pos; + TreeViewItem* const item = findItemAt (e.y, pos); + + if (item != 0 && e.x >= pos.getX()) + { + MouseEvent e2 (e); + e2.x -= pos.getX(); + e2.y -= pos.getY(); + item->itemDoubleClicked (e2); + } + } + } + + void mouseDrag (const MouseEvent& e) + { + if (isEnabled() && ! (e.mouseWasClicked() || isDragging)) + { + isDragging = true; + + Rectangle pos; + TreeViewItem* const item = findItemAt (e.getMouseDownY(), pos); + + if (item != 0 && e.getMouseDownX() >= pos.getX()) + { + const String dragDescription (item->getDragSourceDescription()); + + if (dragDescription.isNotEmpty()) + { + DragAndDropContainer* const dragContainer + = DragAndDropContainer::findParentDragContainerFor (this); + + if (dragContainer != 0) + { + pos.setSize (pos.getWidth(), item->itemHeight); + Image* dragImage = Component::createComponentSnapshot (pos, true); + dragImage->multiplyAllAlphas (0.6f); + + dragContainer->startDragging (dragDescription, owner, dragImage, true); + } + else + { + // to be able to do a drag-and-drop operation, the treeview needs to + // be inside a component which is also a DragAndDropContainer. + jassertfalse + } + } + } + } + } + + void paint (Graphics& g); + TreeViewItem* findItemAt (int y, Rectangle& itemPosition) const; + + void updateComponents() + { + int xAdjust = 0, yAdjust = 0; + + if ((! owner->rootItemVisible) && owner->rootItem != 0) + { + yAdjust = owner->rootItem->itemHeight; + xAdjust = owner->getIndentSize(); + } + + const int visibleTop = -getY(); + const int visibleBottom = visibleTop + getParentHeight(); + + BitArray itemsToKeep; + TreeViewItem* item = owner->rootItem; + int y = -yAdjust; + + while (item != 0 && y < visibleBottom) + { + y += item->itemHeight; + + if (y >= visibleTop) + { + const int index = rowComponentIds.indexOf (item->uid); + + if (index < 0) + { + Component* const comp = item->createItemComponent(); + + if (comp != 0) + { + addAndMakeVisible (comp); + itemsToKeep.setBit (rowComponentItems.size()); + rowComponentItems.add (item); + rowComponentIds.add (item->uid); + rowComponents.add (comp); + } + } + else + { + itemsToKeep.setBit (index); + } + } + + item = item->getNextVisibleItem (true); + } + + for (int i = rowComponentItems.size(); --i >= 0;) + { + Component* const comp = (Component*) (rowComponents.getUnchecked(i)); + + bool keep = false; + + if ((itemsToKeep[i] || (comp == Component::getComponentUnderMouse() && comp->isMouseButtonDown())) + && isParentOf (comp)) + { + if (itemsToKeep[i]) + { + const TreeViewItem* const item = (TreeViewItem*) rowComponentItems.getUnchecked(i); + + Rectangle pos (item->getItemPosition (false)); + pos.translate (-xAdjust, -yAdjust); + pos.setSize (pos.getWidth() + xAdjust, item->itemHeight); + + if (pos.getBottom() >= visibleTop && pos.getY() < visibleBottom) + { + keep = true; + comp->setBounds (pos); + } + } + else + { + comp->setSize (0, 0); + } + } + + if (! keep) + { + delete comp; + rowComponents.remove (i); + rowComponentIds.remove (i); + rowComponentItems.remove (i); + } + } + } + + void resized() + { + owner->itemsChanged(); + } + + juce_UseDebuggingNewOperator + +private: + TreeView* const owner; + + VoidArray rowComponentItems; + Array rowComponentIds; + VoidArray rowComponents; + bool isDragging, needSelectionOnMouseUp; + + TreeViewContentComponent (const TreeViewContentComponent&); + const TreeViewContentComponent& operator= (const TreeViewContentComponent&); + + void selectBasedOnModifiers (TreeViewItem* const item, const ModifierKeys& modifiers) + { + TreeViewItem* firstSelected = 0; + + if (modifiers.isShiftDown() && ((firstSelected = owner->getSelectedItem (0)) != 0)) + { + TreeViewItem* const lastSelected = owner->getSelectedItem (owner->getNumSelectedItems() - 1); + jassert (lastSelected != 0); + + int rowStart = firstSelected->getRowNumberInTree(); + int rowEnd = lastSelected->getRowNumberInTree(); + if (rowStart > rowEnd) + swapVariables (rowStart, rowEnd); + + int ourRow = item->getRowNumberInTree(); + int otherEnd = ourRow < rowEnd ? rowStart : rowEnd; + + if (ourRow > otherEnd) + swapVariables (ourRow, otherEnd); + + for (int i = ourRow; i <= otherEnd; ++i) + owner->getItemOnRow (i)->setSelected (true, false); + } + else + { + const bool cmd = modifiers.isCommandDown(); + + item->setSelected ((! cmd) || (! item->isSelected()), ! cmd); + } + } +}; + +class TreeViewport : public Viewport +{ +public: + TreeViewport() throw() {} + ~TreeViewport() throw() {} + + void updateComponents() + { + if (getViewedComponent() != 0) + ((TreeViewContentComponent*) getViewedComponent())->updateComponents(); + + repaint(); + } + + void visibleAreaChanged (int, int, int, int) + { + updateComponents(); + } + + juce_UseDebuggingNewOperator + +private: + TreeViewport (const TreeViewport&); + const TreeViewport& operator= (const TreeViewport&); +}; + +TreeView::TreeView (const String& componentName) + : Component (componentName), + rootItem (0), + indentSize (24), + defaultOpenness (false), + needsRecalculating (true), + rootItemVisible (true), + multiSelectEnabled (false) +{ + addAndMakeVisible (viewport = new TreeViewport()); + viewport->setViewedComponent (new TreeViewContentComponent (this)); + viewport->setWantsKeyboardFocus (false); + setWantsKeyboardFocus (true); +} + +TreeView::~TreeView() +{ + if (rootItem != 0) + rootItem->setOwnerView (0); + + deleteAllChildren(); +} + +void TreeView::setRootItem (TreeViewItem* const newRootItem) +{ + if (rootItem != newRootItem) + { + if (newRootItem != 0) + { + jassert (newRootItem->ownerView == 0); // can't use a tree item in more than one tree at once.. + + if (newRootItem->ownerView != 0) + newRootItem->ownerView->setRootItem (0); + } + + if (rootItem != 0) + rootItem->setOwnerView (0); + + rootItem = newRootItem; + + if (newRootItem != 0) + newRootItem->setOwnerView (this); + + needsRecalculating = true; + handleAsyncUpdate(); + + if (rootItem != 0 && (defaultOpenness || ! rootItemVisible)) + { + rootItem->setOpen (false); // force a re-open + rootItem->setOpen (true); + } + } +} + +void TreeView::setRootItemVisible (const bool shouldBeVisible) +{ + rootItemVisible = shouldBeVisible; + + if (rootItem != 0 && (defaultOpenness || ! rootItemVisible)) + { + rootItem->setOpen (false); // force a re-open + rootItem->setOpen (true); + } + + itemsChanged(); +} + +void TreeView::colourChanged() +{ + setOpaque (findColour (backgroundColourId).isOpaque()); + repaint(); +} + +void TreeView::setIndentSize (const int newIndentSize) +{ + if (indentSize != newIndentSize) + { + indentSize = newIndentSize; + resized(); + } +} + +void TreeView::setDefaultOpenness (const bool isOpenByDefault) +{ + if (defaultOpenness != isOpenByDefault) + { + defaultOpenness = isOpenByDefault; + itemsChanged(); + } +} + +void TreeView::setMultiSelectEnabled (const bool canMultiSelect) +{ + multiSelectEnabled = canMultiSelect; +} + +void TreeView::clearSelectedItems() +{ + if (rootItem != 0) + rootItem->deselectAllRecursively(); +} + +int TreeView::getNumSelectedItems() const throw() +{ + return (rootItem != 0) ? rootItem->countSelectedItemsRecursively() : 0; +} + +TreeViewItem* TreeView::getSelectedItem (const int index) const throw() +{ + return (rootItem != 0) ? rootItem->getSelectedItemWithIndex (index) : 0; +} + +int TreeView::getNumRowsInTree() const +{ + if (rootItem != 0) + return rootItem->getNumRows() - (rootItemVisible ? 0 : 1); + + return 0; +} + +TreeViewItem* TreeView::getItemOnRow (int index) const +{ + if (! rootItemVisible) + ++index; + + if (rootItem != 0 && index >= 0) + return rootItem->getItemOnRow (index); + + return 0; +} + +XmlElement* TreeView::getOpennessState (const bool alsoIncludeScrollPosition) const +{ + XmlElement* e = 0; + + if (rootItem != 0) + { + e = rootItem->createXmlOpenness(); + + if (e != 0 && alsoIncludeScrollPosition) + e->setAttribute (T("scrollPos"), viewport->getViewPositionY()); + } + + return e; +} + +void TreeView::restoreOpennessState (const XmlElement& newState) +{ + if (rootItem != 0) + { + rootItem->restoreFromXml (newState); + + if (newState.hasAttribute (T("scrollPos"))) + viewport->setViewPosition (viewport->getViewPositionX(), + newState.getIntAttribute (T("scrollPos"))); + } +} + +void TreeView::paint (Graphics& g) +{ + g.fillAll (findColour (backgroundColourId)); +} + +void TreeView::resized() +{ + viewport->setBounds (0, 0, getWidth(), getHeight()); + itemsChanged(); +} + +void TreeView::moveSelectedRow (int delta) +{ + int rowSelected = 0; + + TreeViewItem* const firstSelected = getSelectedItem (0); + if (firstSelected != 0) + rowSelected = firstSelected->getRowNumberInTree(); + + rowSelected = jlimit (0, getNumRowsInTree() - 1, rowSelected + delta); + + TreeViewItem* item = getItemOnRow (rowSelected); + + if (item != 0) + { + item->setSelected (true, true); + + scrollToKeepItemVisible (item); + } +} + +void TreeView::scrollToKeepItemVisible (TreeViewItem* item) +{ + if (item != 0 && item->ownerView == this) + { + handleAsyncUpdate(); + + item = item->getDeepestOpenParentItem(); + + int y = item->y; + if (! rootItemVisible) + y -= rootItem->itemHeight; + + int viewTop = viewport->getViewPositionY(); + + if (y < viewTop) + { + viewport->setViewPosition (viewport->getViewPositionX(), y); + } + else if (y + item->itemHeight > viewTop + viewport->getViewHeight()) + { + viewport->setViewPosition (viewport->getViewPositionX(), + (y + item->itemHeight) - viewport->getViewHeight()); + } + } +} + +bool TreeView::keyPressed (const KeyPress& key) +{ + if (key.isKeyCode (KeyPress::upKey)) + { + moveSelectedRow (-1); + } + else if (key.isKeyCode (KeyPress::downKey)) + { + moveSelectedRow (1); + } + else if (key.isKeyCode (KeyPress::pageDownKey) || key.isKeyCode (KeyPress::pageUpKey)) + { + if (rootItem != 0) + { + int rowsOnScreen = getHeight() / jmax (1, rootItem->itemHeight); + + if (key.isKeyCode (KeyPress::pageUpKey)) + rowsOnScreen = -rowsOnScreen; + + moveSelectedRow (rowsOnScreen); + } + } + else if (key.isKeyCode (KeyPress::homeKey)) + { + moveSelectedRow (-0x3fffffff); + } + else if (key.isKeyCode (KeyPress::endKey)) + { + moveSelectedRow (0x3fffffff); + } + else if (key.isKeyCode (KeyPress::returnKey)) + { + TreeViewItem* const firstSelected = getSelectedItem (0); + if (firstSelected != 0) + firstSelected->setOpen (! firstSelected->isOpen()); + } + else if (key.isKeyCode (KeyPress::leftKey)) + { + TreeViewItem* const firstSelected = getSelectedItem (0); + + if (firstSelected != 0) + { + if (firstSelected->isOpen()) + { + firstSelected->setOpen (false); + } + else + { + TreeViewItem* parent = firstSelected->parentItem; + + if ((! rootItemVisible) && parent == rootItem) + parent = 0; + + if (parent != 0) + { + parent->setSelected (true, true); + scrollToKeepItemVisible (parent); + } + } + } + } + else if (key.isKeyCode (KeyPress::rightKey)) + { + TreeViewItem* const firstSelected = getSelectedItem (0); + + if (firstSelected != 0) + { + if (firstSelected->isOpen() || ! firstSelected->mightContainSubItems()) + moveSelectedRow (1); + else + firstSelected->setOpen (true); + } + } + else + { + return false; + } + + return true; +} + +void TreeView::itemsChanged() throw() +{ + needsRecalculating = true; + repaint(); + triggerAsyncUpdate(); +} + +void TreeView::handleAsyncUpdate() +{ + if (needsRecalculating) + { + needsRecalculating = false; + + const ScopedLock sl (nodeAlterationLock); + + if (rootItem != 0) + rootItem->updatePositions (0); + + ((TreeViewport*) viewport)->updateComponents(); + + if (rootItem != 0) + { + viewport->getViewedComponent() + ->setSize (jmax (viewport->getMaximumVisibleWidth(), rootItem->totalWidth), + rootItem->totalHeight - (rootItemVisible ? 0 : rootItem->itemHeight)); + } + else + { + viewport->getViewedComponent()->setSize (0, 0); + } + } +} + +void TreeViewContentComponent::paint (Graphics& g) +{ + if (owner->rootItem != 0) + { + owner->handleAsyncUpdate(); + + int w = getWidth(); + + if (! owner->rootItemVisible) + { + const int indentWidth = owner->getIndentSize(); + + g.setOrigin (-indentWidth, -owner->rootItem->itemHeight); + w += indentWidth; + } + + owner->rootItem->paintRecursively (g, w); + } +} + +TreeViewItem* TreeViewContentComponent::findItemAt (int y, Rectangle& itemPosition) const +{ + if (owner->rootItem != 0) + { + owner->handleAsyncUpdate(); + + if (! owner->rootItemVisible) + y += owner->rootItem->itemHeight; + + TreeViewItem* const ti = owner->rootItem->findItemRecursively (y); + + if (ti != 0) + { + itemPosition = ti->getItemPosition (false); + + if (! owner->rootItemVisible) + itemPosition.translate (-owner->getIndentSize(), + -owner->rootItem->itemHeight); + } + + return ti; + } + + return 0; +} + +#define opennessDefault 0 +#define opennessClosed 1 +#define opennessOpen 2 + +TreeViewItem::TreeViewItem() + : ownerView (0), + parentItem (0), + subItems (8), + y (0), + itemHeight (0), + totalHeight (0), + selected (false), + redrawNeeded (true), + drawLinesInside (true), + openness (opennessDefault) +{ + static int nextUID = 0; + uid = nextUID++; +} + +TreeViewItem::~TreeViewItem() +{ +} + +const String TreeViewItem::getUniqueName() const +{ + return String::empty; +} + +void TreeViewItem::itemOpennessChanged (bool) +{ +} + +int TreeViewItem::getNumSubItems() const throw() +{ + return subItems.size(); +} + +TreeViewItem* TreeViewItem::getSubItem (const int index) const throw() +{ + return subItems [index]; +} + +void TreeViewItem::clearSubItems() +{ + if (subItems.size() > 0) + { + if (ownerView != 0) + { + const ScopedLock sl (ownerView->nodeAlterationLock); + subItems.clear(); + treeHasChanged(); + } + else + { + subItems.clear(); + } + } +} + +void TreeViewItem::addSubItem (TreeViewItem* const newItem, const int insertPosition) +{ + if (newItem != 0) + { + newItem->parentItem = this; + newItem->setOwnerView (ownerView); + newItem->y = 0; + newItem->itemHeight = newItem->getItemHeight(); + newItem->totalHeight = 0; + newItem->itemWidth = newItem->getItemWidth(); + newItem->totalWidth = 0; + + if (ownerView != 0) + { + const ScopedLock sl (ownerView->nodeAlterationLock); + subItems.insert (insertPosition, newItem); + treeHasChanged(); + + if (newItem->isOpen()) + newItem->itemOpennessChanged (true); + } + else + { + subItems.insert (insertPosition, newItem); + + if (newItem->isOpen()) + newItem->itemOpennessChanged (true); + } + } +} + +void TreeViewItem::removeSubItem (const int index, const bool deleteItem) +{ + if (ownerView != 0) + ownerView->nodeAlterationLock.enter(); + + if (((unsigned int) index) < (unsigned int) subItems.size()) + { + subItems.remove (index, deleteItem); + treeHasChanged(); + } + + if (ownerView != 0) + ownerView->nodeAlterationLock.exit(); +} + +bool TreeViewItem::isOpen() const throw() +{ + if (openness == opennessDefault) + return ownerView != 0 && ownerView->defaultOpenness; + else + return openness == opennessOpen; +} + +void TreeViewItem::setOpen (const bool shouldBeOpen) +{ + if (isOpen() != shouldBeOpen) + { + openness = shouldBeOpen ? opennessOpen + : opennessClosed; + + treeHasChanged(); + + itemOpennessChanged (isOpen()); + } +} + +bool TreeViewItem::isSelected() const throw() +{ + return selected; +} + +void TreeViewItem::deselectAllRecursively() +{ + setSelected (false, false); + + for (int i = 0; i < subItems.size(); ++i) + subItems.getUnchecked(i)->deselectAllRecursively(); +} + +void TreeViewItem::setSelected (const bool shouldBeSelected, + const bool deselectOtherItemsFirst) +{ + if (deselectOtherItemsFirst) + getTopLevelItem()->deselectAllRecursively(); + + if (shouldBeSelected != selected) + { + selected = shouldBeSelected; + if (ownerView != 0) + ownerView->repaint(); + + itemSelectionChanged (shouldBeSelected); + } +} + +void TreeViewItem::paintItem (Graphics&, int, int) +{ +} + +void TreeViewItem::itemClicked (const MouseEvent&) +{ +} + +void TreeViewItem::itemDoubleClicked (const MouseEvent&) +{ + if (mightContainSubItems()) + setOpen (! isOpen()); +} + +void TreeViewItem::itemSelectionChanged (bool) +{ +} + +const String TreeViewItem::getDragSourceDescription() +{ + return String::empty; +} + +const Rectangle TreeViewItem::getItemPosition (const bool relativeToTreeViewTopLeft) const throw() +{ + const int indentX = getIndentX(); + + int width = itemWidth; + + if (ownerView != 0 && width < 0) + width = ownerView->viewport->getViewWidth() - indentX; + + Rectangle r (indentX, y, jmax (0, width), totalHeight); + + if (relativeToTreeViewTopLeft) + r.setPosition (r.getX() - ownerView->viewport->getViewPositionX(), + r.getY() - ownerView->viewport->getViewPositionY()); + + return r; +} + +void TreeViewItem::treeHasChanged() const throw() +{ + if (ownerView != 0) + ownerView->itemsChanged(); +} + +void TreeViewItem::updatePositions (int newY) +{ + y = newY; + itemHeight = getItemHeight(); + totalHeight = itemHeight; + itemWidth = getItemWidth(); + totalWidth = jmax (itemWidth, 0); + + if (isOpen()) + { + const int ourIndent = getIndentX(); + newY += totalHeight; + + for (int i = 0; i < subItems.size(); ++i) + { + TreeViewItem* const ti = subItems.getUnchecked(i); + + ti->updatePositions (newY); + newY += ti->totalHeight; + totalHeight += ti->totalHeight; + totalWidth = jmax (totalWidth, ti->totalWidth + ourIndent); + } + } +} + +TreeViewItem* TreeViewItem::getDeepestOpenParentItem() throw() +{ + TreeViewItem* result = this; + TreeViewItem* item = this; + + while (item->parentItem != 0) + { + item = item->parentItem; + + if (! item->isOpen()) + result = item; + } + + return result; +} + +void TreeViewItem::setOwnerView (TreeView* const newOwner) throw() +{ + ownerView = newOwner; + + for (int i = subItems.size(); --i >= 0;) + subItems.getUnchecked(i)->setOwnerView (newOwner); +} + +int TreeViewItem::getIndentX() const throw() +{ + const int indentWidth = ownerView->getIndentSize(); + int x = indentWidth; + + TreeViewItem* p = parentItem; + + while (p != 0) + { + x += indentWidth; + p = p->parentItem; + } + + return x; +} + +void TreeViewItem::paintRecursively (Graphics& g, int width) +{ + jassert (ownerView != 0); + if (ownerView == 0) + return; + + const int indent = getIndentX(); + const int itemW = itemWidth < 0 ? width - indent : itemWidth; + + g.setColour (ownerView->findColour (TreeView::linesColourId)); + + const float halfH = itemHeight * 0.5f; + int depth = 0; + TreeViewItem* p = parentItem; + + while (p != 0) + { + ++depth; + p = p->parentItem; + } + + const int indentWidth = ownerView->getIndentSize(); + float x = (depth + 0.5f) * indentWidth; + + if (x > 0) + { + if (depth >= 0) + { + if (parentItem != 0 && parentItem->drawLinesInside) + g.drawLine (x, 0, x, isLastOfSiblings() ? halfH : (float) itemHeight); + + if ((parentItem != 0 && parentItem->drawLinesInside) + || (parentItem == 0 && drawLinesInside)) + g.drawLine (x, halfH, x + indentWidth / 2, halfH); + } + + p = parentItem; + int d = depth; + + while (p != 0 && --d >= 0) + { + x -= (float) indentWidth; + + if ((p->parentItem == 0 || p->parentItem->drawLinesInside) + && ! p->isLastOfSiblings()) + { + g.drawLine (x, 0, x, (float) itemHeight); + } + + p = p->parentItem; + } + + if (mightContainSubItems()) + { + ownerView->getLookAndFeel() + .drawTreeviewPlusMinusBox (g, + depth * indentWidth, 0, + indentWidth, itemHeight, + ! isOpen()); + } + } + + { + g.saveState(); + g.setOrigin (indent, 0); + + if (g.reduceClipRegion (0, 0, itemW, itemHeight)) + paintItem (g, itemW, itemHeight); + + g.restoreState(); + } + + if (isOpen()) + { + const Rectangle clip (g.getClipBounds()); + + for (int i = 0; i < subItems.size(); ++i) + { + TreeViewItem* const ti = subItems.getUnchecked(i); + + const int relY = ti->y - y; + + if (relY >= clip.getBottom()) + break; + + if (relY + ti->totalHeight >= clip.getY()) + { + g.saveState(); + g.setOrigin (0, relY); + + if (g.reduceClipRegion (0, 0, width, ti->totalHeight)) + ti->paintRecursively (g, width); + + g.restoreState(); + } + } + } +} + +bool TreeViewItem::isLastOfSiblings() const throw() +{ + return parentItem == 0 + || parentItem->subItems.getLast() == this; +} + +TreeViewItem* TreeViewItem::getTopLevelItem() throw() +{ + return (parentItem == 0) ? this + : parentItem->getTopLevelItem(); +} + +int TreeViewItem::getNumRows() const throw() +{ + int num = 1; + + if (isOpen()) + { + for (int i = subItems.size(); --i >= 0;) + num += subItems.getUnchecked(i)->getNumRows(); + } + + return num; +} + +TreeViewItem* TreeViewItem::getItemOnRow (int index) throw() +{ + if (index == 0) + return this; + + if (index > 0 && isOpen()) + { + --index; + + for (int i = 0; i < subItems.size(); ++i) + { + TreeViewItem* const item = subItems.getUnchecked(i); + + if (index == 0) + return item; + + const int numRows = item->getNumRows(); + + if (numRows > index) + return item->getItemOnRow (index); + + index -= numRows; + } + } + + return 0; +} + +TreeViewItem* TreeViewItem::findItemRecursively (int y) throw() +{ + if (((unsigned int) y) < (unsigned int) totalHeight) + { + const int h = itemHeight; + + if (y < h) + return this; + + if (isOpen()) + { + y -= h; + + for (int i = 0; i < subItems.size(); ++i) + { + TreeViewItem* const ti = subItems.getUnchecked(i); + + if (ti->totalHeight >= y) + return ti->findItemRecursively (y); + + y -= ti->totalHeight; + } + } + } + + return 0; +} + +int TreeViewItem::countSelectedItemsRecursively() const throw() +{ + int total = 0; + + if (isSelected()) + ++total; + + for (int i = subItems.size(); --i >= 0;) + total += subItems.getUnchecked(i)->countSelectedItemsRecursively(); + + return total; +} + +TreeViewItem* TreeViewItem::getSelectedItemWithIndex (int index) throw() +{ + if (isSelected()) + { + if (index == 0) + return this; + + --index; + } + + if (index >= 0) + { + for (int i = 0; i < subItems.size(); ++i) + { + TreeViewItem* const item = subItems.getUnchecked(i); + + TreeViewItem* const found = item->getSelectedItemWithIndex (index); + + if (found != 0) + return found; + + index -= item->countSelectedItemsRecursively(); + } + } + + return 0; +} + +int TreeViewItem::getRowNumberInTree() const throw() +{ + if (parentItem != 0 && ownerView != 0) + { + int n = 1 + parentItem->getRowNumberInTree(); + + int ourIndex = parentItem->subItems.indexOf (this); + jassert (ourIndex >= 0); + + while (--ourIndex >= 0) + n += parentItem->subItems [ourIndex]->getNumRows(); + + if (parentItem->parentItem == 0 + && ! ownerView->rootItemVisible) + --n; + + return n; + } + else + { + return 0; + } +} + +void TreeViewItem::setLinesDrawnForSubItems (const bool drawLines) throw() +{ + drawLinesInside = drawLines; +} + +TreeViewItem* TreeViewItem::getNextVisibleItem (const bool recurse) const throw() +{ + if (recurse && isOpen() && subItems.size() > 0) + return subItems [0]; + + if (parentItem != 0) + { + const int nextIndex = parentItem->subItems.indexOf (this) + 1; + + if (nextIndex >= parentItem->subItems.size()) + return parentItem->getNextVisibleItem (false); + + return parentItem->subItems [nextIndex]; + } + + return 0; +} + +void TreeViewItem::restoreFromXml (const XmlElement& e) +{ + if (e.hasTagName (T("CLOSED"))) + { + setOpen (false); + } + else if (e.hasTagName (T("OPEN"))) + { + setOpen (true); + + forEachXmlChildElement (e, n) + { + const String id (n->getStringAttribute (T("id"))); + + for (int i = 0; i < subItems.size(); ++i) + { + TreeViewItem* const ti = subItems.getUnchecked(i); + + if (ti->getUniqueName() == id) + { + ti->restoreFromXml (*n); + break; + } + } + } + } +} + +XmlElement* TreeViewItem::createXmlOpenness() const +{ + if (openness != opennessDefault) + { + const String name (getUniqueName()); + + if (name.isNotEmpty()) + { + XmlElement* e; + + if (isOpen()) + { + e = new XmlElement (T("OPEN")); + + for (int i = 0; i < subItems.size(); ++i) + e->addChildElement (subItems.getUnchecked(i)->createXmlOpenness()); + } + else + { + e = new XmlElement (T("CLOSED")); + } + + e->setAttribute (T("id"), name); + + return e; + } + else + { + // trying to save the openness for an element that has no name - this won't + // work because it needs the names to identify what to open. + jassertfalse + } + } + + return 0; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_TreeView.cpp *********/ + +/********* Start of inlined file: juce_DirectoryContentsDisplayComponent.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +DirectoryContentsDisplayComponent::DirectoryContentsDisplayComponent (DirectoryContentsList& listToShow) + : fileList (listToShow), + listeners (2) +{ +} + +DirectoryContentsDisplayComponent::~DirectoryContentsDisplayComponent() +{ +} + +FileBrowserListener::~FileBrowserListener() +{ +} + +void DirectoryContentsDisplayComponent::addListener (FileBrowserListener* const listener) throw() +{ + jassert (listener != 0); + + if (listener != 0) + listeners.add (listener); +} + +void DirectoryContentsDisplayComponent::removeListener (FileBrowserListener* const listener) throw() +{ + listeners.removeValue (listener); +} + +void DirectoryContentsDisplayComponent::sendSelectionChangeMessage() +{ + const ComponentDeletionWatcher deletionWatcher (dynamic_cast (this)); + + for (int i = listeners.size(); --i >= 0;) + { + ((FileBrowserListener*) listeners.getUnchecked (i))->selectionChanged(); + + if (deletionWatcher.hasBeenDeleted()) + return; + + i = jmin (i, listeners.size() - 1); + } +} + +void DirectoryContentsDisplayComponent::sendMouseClickMessage (const File& file, const MouseEvent& e) +{ + if (fileList.getDirectory().exists()) + { + const ComponentDeletionWatcher deletionWatcher (dynamic_cast (this)); + + for (int i = listeners.size(); --i >= 0;) + { + ((FileBrowserListener*) listeners.getUnchecked (i))->fileClicked (file, e); + + if (deletionWatcher.hasBeenDeleted()) + return; + + i = jmin (i, listeners.size() - 1); + } + } +} + +void DirectoryContentsDisplayComponent::sendDoubleClickMessage (const File& file) +{ + if (fileList.getDirectory().exists()) + { + const ComponentDeletionWatcher deletionWatcher (dynamic_cast (this)); + + for (int i = listeners.size(); --i >= 0;) + { + ((FileBrowserListener*) listeners.getUnchecked (i))->fileDoubleClicked (file); + + if (deletionWatcher.hasBeenDeleted()) + return; + + i = jmin (i, listeners.size() - 1); + } + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_DirectoryContentsDisplayComponent.cpp *********/ + +/********* Start of inlined file: juce_DirectoryContentsList.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +void* juce_findFileStart (const String& directory, const String& wildCard, String& firstResultFile, + bool* isDirectory, bool* isHidden, int64* fileSize, Time* modTime, + Time* creationTime, bool* isReadOnly) throw(); +bool juce_findFileNext (void* handle, String& resultFile, + bool* isDirectory, bool* isHidden, int64* fileSize, + Time* modTime, Time* creationTime, bool* isReadOnly) throw(); +void juce_findFileClose (void* handle) throw(); + +DirectoryContentsList::DirectoryContentsList (const FileFilter* const fileFilter_, + TimeSliceThread& thread_) + : fileFilter (fileFilter_), + thread (thread_), + includeDirectories (false), + includeFiles (false), + ignoreHiddenFiles (true), + fileFindHandle (0), + shouldStop (true) +{ +} + +DirectoryContentsList::~DirectoryContentsList() +{ + clear(); +} + +void DirectoryContentsList::setIgnoresHiddenFiles (const bool shouldIgnoreHiddenFiles) +{ + ignoreHiddenFiles = shouldIgnoreHiddenFiles; +} + +const File& DirectoryContentsList::getDirectory() const throw() +{ + return root; +} + +void DirectoryContentsList::setDirectory (const File& directory, + const bool includeDirectories_, + const bool includeFiles_) +{ + if (directory != root + || includeDirectories != includeDirectories_ + || includeFiles != includeFiles_) + { + clear(); + + root = directory; + includeDirectories = includeDirectories_; + includeFiles = includeFiles_; + + refresh(); + } +} + +void DirectoryContentsList::clear() +{ + shouldStop = true; + thread.removeTimeSliceClient (this); + + if (fileFindHandle != 0) + { + juce_findFileClose (fileFindHandle); + fileFindHandle = 0; + } + + if (files.size() > 0) + { + files.clear(); + changed(); + } +} + +void DirectoryContentsList::refresh() +{ + clear(); + + if (root.isDirectory()) + { + String fileFound; + bool fileFoundIsDir, isHidden, isReadOnly; + int64 fileSize; + Time modTime, creationTime; + + String path (root.getFullPathName()); + if (! path.endsWithChar (File::separator)) + path += File::separator; + + jassert (fileFindHandle == 0); + + fileFindHandle = juce_findFileStart (path, T("*"), fileFound, + &fileFoundIsDir, + &isHidden, + &fileSize, + &modTime, + &creationTime, + &isReadOnly); + + if (fileFindHandle != 0 && fileFound.isNotEmpty()) + { + if (addFile (fileFound, fileFoundIsDir, isHidden, + fileSize, modTime, creationTime, isReadOnly)) + { + changed(); + } + } + + shouldStop = false; + + thread.addTimeSliceClient (this); + } +} + +int DirectoryContentsList::getNumFiles() const +{ + return files.size(); +} + +bool DirectoryContentsList::getFileInfo (const int index, + FileInfo& result) const +{ + const ScopedLock sl (fileListLock); + const FileInfo* const info = files [index]; + + if (info != 0) + { + result = *info; + return true; + } + + return false; +} + +const File DirectoryContentsList::getFile (const int index) const +{ + const ScopedLock sl (fileListLock); + const FileInfo* const info = files [index]; + + if (info != 0) + return root.getChildFile (info->filename); + + return File::nonexistent; +} + +bool DirectoryContentsList::isStillLoading() const +{ + return fileFindHandle != 0; +} + +void DirectoryContentsList::changed() +{ + sendChangeMessage (this); +} + +bool DirectoryContentsList::useTimeSlice() +{ + const uint32 startTime = Time::getApproximateMillisecondCounter(); + bool hasChanged = false; + + for (int i = 100; --i >= 0;) + { + if (! checkNextFile (hasChanged)) + { + if (hasChanged) + changed(); + + return false; + } + + if (shouldStop || (Time::getApproximateMillisecondCounter() > startTime + 150)) + break; + } + + if (hasChanged) + changed(); + + return true; +} + +bool DirectoryContentsList::checkNextFile (bool& hasChanged) +{ + if (fileFindHandle != 0) + { + String fileFound; + bool fileFoundIsDir, isHidden, isReadOnly; + int64 fileSize; + Time modTime, creationTime; + + if (juce_findFileNext (fileFindHandle, fileFound, + &fileFoundIsDir, &isHidden, + &fileSize, + &modTime, + &creationTime, + &isReadOnly)) + { + if (addFile (fileFound, fileFoundIsDir, isHidden, fileSize, + modTime, creationTime, isReadOnly)) + { + hasChanged = true; + } + + return true; + } + else + { + juce_findFileClose (fileFindHandle); + fileFindHandle = 0; + } + } + + return false; +} + +int DirectoryContentsList::compareElements (const DirectoryContentsList::FileInfo* const first, + const DirectoryContentsList::FileInfo* const second) throw() +{ +#if JUCE_WIN32 + if (first->isDirectory != second->isDirectory) + return first->isDirectory ? -1 : 1; +#endif + + return first->filename.compareIgnoreCase (second->filename); +} + +bool DirectoryContentsList::addFile (const String& filename, + const bool isDir, + const bool isHidden, + const int64 fileSize, + const Time& modTime, + const Time& creationTime, + const bool isReadOnly) +{ + if (filename == T("..") + || filename == T(".") + || (ignoreHiddenFiles && isHidden)) + return false; + + const File file (root.getChildFile (filename)); + + if (((isDir && includeDirectories) || ((! isDir) && includeFiles)) + && (fileFilter == 0 + || ((! isDir) && fileFilter->isFileSuitable (file)) + || (isDir && fileFilter->isDirectorySuitable (file)))) + { + FileInfo* const info = new FileInfo(); + + info->filename = filename; + info->fileSize = fileSize; + info->modificationTime = modTime; + info->creationTime = creationTime; + info->isDirectory = isDir; + info->isReadOnly = isReadOnly; + + const ScopedLock sl (fileListLock); + + for (int i = files.size(); --i >= 0;) + { + if (files.getUnchecked(i)->filename == info->filename) + { + delete info; + return false; + } + } + + files.addSorted (*this, info); + return true; + } + + return false; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_DirectoryContentsList.cpp *********/ + +/********* Start of inlined file: juce_FileBrowserComponent.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +class DirectoriesOnlyFilter : public FileFilter +{ +public: + DirectoriesOnlyFilter() : FileFilter (String::empty) {} + + bool isFileSuitable (const File&) const { return false; } + bool isDirectorySuitable (const File&) const { return true; } +}; + +FileBrowserComponent::FileBrowserComponent (FileChooserMode mode_, + const File& initialFileOrDirectory, + const FileFilter* fileFilter, + FilePreviewComponent* previewComp_, + const bool useTreeView, + const bool filenameTextBoxIsReadOnly) + : directoriesOnlyFilter (0), + mode (mode_), + listeners (2), + previewComp (previewComp_), + thread ("Juce FileBrowser") +{ + String filename; + + if (initialFileOrDirectory == File::nonexistent) + { + currentRoot = File::getCurrentWorkingDirectory(); + } + else if (initialFileOrDirectory.isDirectory()) + { + currentRoot = initialFileOrDirectory; + } + else + { + currentRoot = initialFileOrDirectory.getParentDirectory(); + filename = initialFileOrDirectory.getFileName(); + } + + if (mode_ == chooseDirectoryMode) + fileFilter = directoriesOnlyFilter = new DirectoriesOnlyFilter(); + + fileList = new DirectoryContentsList (fileFilter, thread); + + if (useTreeView) + { + FileTreeComponent* const tree = new FileTreeComponent (*fileList); + addAndMakeVisible (tree); + fileListComponent = tree; + } + else + { + FileListComponent* const list = new FileListComponent (*fileList); + list->setOutlineThickness (1); + addAndMakeVisible (list); + fileListComponent = list; + } + + fileListComponent->addListener (this); + + addAndMakeVisible (currentPathBox = new ComboBox ("path")); + currentPathBox->setEditableText (true); + + StringArray rootNames, rootPaths; + const BitArray separators (getRoots (rootNames, rootPaths)); + + for (int i = 0; i < rootNames.size(); ++i) + { + if (separators [i]) + currentPathBox->addSeparator(); + + currentPathBox->addItem (rootNames[i], i + 1); + } + + currentPathBox->addSeparator(); + currentPathBox->addListener (this); + + addAndMakeVisible (filenameBox = new TextEditor()); + filenameBox->setMultiLine (false); + filenameBox->setSelectAllWhenFocused (true); + filenameBox->setText (filename, false); + filenameBox->addListener (this); + filenameBox->setReadOnly (filenameTextBoxIsReadOnly); + + Label* label = new Label ("f", (mode == chooseDirectoryMode) ? TRANS("folder:") + : TRANS("file:")); + addAndMakeVisible (label); + label->attachToComponent (filenameBox, true); + + addAndMakeVisible (goUpButton = getLookAndFeel().createFileBrowserGoUpButton()); + + goUpButton->addButtonListener (this); + goUpButton->setTooltip (TRANS ("go up to parent directory")); + + if (previewComp != 0) + addAndMakeVisible (previewComp); + + setRoot (currentRoot); + + thread.startThread (4); +} + +FileBrowserComponent::~FileBrowserComponent() +{ + if (previewComp != 0) + removeChildComponent (previewComp); + + deleteAllChildren(); + + deleteAndZero (fileList); + delete directoriesOnlyFilter; + + thread.stopThread (10000); +} + +void FileBrowserComponent::addListener (FileBrowserListener* const newListener) throw() +{ + jassert (newListener != 0) + + if (newListener != 0) + listeners.add (newListener); +} + +void FileBrowserComponent::removeListener (FileBrowserListener* const listener) throw() +{ + listeners.removeValue (listener); +} + +const File FileBrowserComponent::getCurrentFile() const throw() +{ + return currentRoot.getChildFile (filenameBox->getText()); +} + +bool FileBrowserComponent::currentFileIsValid() const +{ + if (mode == saveFileMode) + return ! getCurrentFile().isDirectory(); + else if (mode == loadFileMode) + return getCurrentFile().existsAsFile(); + else if (mode == chooseDirectoryMode) + return getCurrentFile().isDirectory(); + + jassertfalse + return false; +} + +const File FileBrowserComponent::getRoot() const +{ + return currentRoot; +} + +void FileBrowserComponent::setRoot (const File& newRootDirectory) +{ + if (currentRoot != newRootDirectory) + { + fileListComponent->scrollToTop(); + + if (mode == chooseDirectoryMode) + filenameBox->setText (String::empty, false); + + String path (newRootDirectory.getFullPathName()); + + if (path.isEmpty()) + path += File::separator; + + StringArray rootNames, rootPaths; + getRoots (rootNames, rootPaths); + + if (! rootPaths.contains (path, true)) + { + bool alreadyListed = false; + + for (int i = currentPathBox->getNumItems(); --i >= 0;) + { + if (currentPathBox->getItemText (i).equalsIgnoreCase (path)) + { + alreadyListed = true; + break; + } + } + + if (! alreadyListed) + currentPathBox->addItem (path, currentPathBox->getNumItems() + 2); + } + } + + currentRoot = newRootDirectory; + fileList->setDirectory (currentRoot, true, true); + + String currentRootName (currentRoot.getFullPathName()); + if (currentRootName.isEmpty()) + currentRootName += File::separator; + + currentPathBox->setText (currentRootName, true); + + goUpButton->setEnabled (currentRoot.getParentDirectory().isDirectory() + && currentRoot.getParentDirectory() != currentRoot); +} + +void FileBrowserComponent::goUp() +{ + setRoot (getRoot().getParentDirectory()); +} + +void FileBrowserComponent::refresh() +{ + fileList->refresh(); +} + +const String FileBrowserComponent::getActionVerb() const +{ + return (mode == chooseDirectoryMode) ? TRANS("Choose") + : ((mode == saveFileMode) ? TRANS("Save") : TRANS("Open")); +} + +FilePreviewComponent* FileBrowserComponent::getPreviewComponent() const throw() +{ + return previewComp; +} + +void FileBrowserComponent::resized() +{ + getLookAndFeel() + .layoutFileBrowserComponent (*this, fileListComponent, + previewComp, currentPathBox, + filenameBox, goUpButton); +} + +void FileBrowserComponent::sendListenerChangeMessage() +{ + ComponentDeletionWatcher deletionWatcher (this); + + if (previewComp != 0) + previewComp->selectedFileChanged (getCurrentFile()); + + jassert (! deletionWatcher.hasBeenDeleted()); + + for (int i = listeners.size(); --i >= 0;) + { + ((FileBrowserListener*) listeners.getUnchecked (i))->selectionChanged(); + + if (deletionWatcher.hasBeenDeleted()) + return; + + i = jmin (i, listeners.size() - 1); + } +} + +void FileBrowserComponent::selectionChanged() +{ + const File selected (fileListComponent->getSelectedFile()); + + if ((mode == chooseDirectoryMode && selected.isDirectory()) + || selected.existsAsFile()) + { + filenameBox->setText (selected.getRelativePathFrom (getRoot()), false); + } + + sendListenerChangeMessage(); +} + +void FileBrowserComponent::fileClicked (const File& f, const MouseEvent& e) +{ + ComponentDeletionWatcher deletionWatcher (this); + + for (int i = listeners.size(); --i >= 0;) + { + ((FileBrowserListener*) listeners.getUnchecked (i))->fileClicked (f, e); + + if (deletionWatcher.hasBeenDeleted()) + return; + + i = jmin (i, listeners.size() - 1); + } +} + +void FileBrowserComponent::fileDoubleClicked (const File& f) +{ + if (f.isDirectory()) + { + setRoot (f); + } + else + { + ComponentDeletionWatcher deletionWatcher (this); + + for (int i = listeners.size(); --i >= 0;) + { + ((FileBrowserListener*) listeners.getUnchecked (i))->fileDoubleClicked (f); + + if (deletionWatcher.hasBeenDeleted()) + return; + + i = jmin (i, listeners.size() - 1); + } + } +} + +void FileBrowserComponent::textEditorTextChanged (TextEditor&) +{ + sendListenerChangeMessage(); +} + +void FileBrowserComponent::textEditorReturnKeyPressed (TextEditor&) +{ + if (filenameBox->getText().containsChar (File::separator)) + { + const File f (currentRoot.getChildFile (filenameBox->getText())); + + if (f.isDirectory()) + { + setRoot (f); + filenameBox->setText (String::empty); + } + else + { + setRoot (f.getParentDirectory()); + filenameBox->setText (f.getFileName()); + } + } + else + { + fileDoubleClicked (getCurrentFile()); + } +} + +void FileBrowserComponent::textEditorEscapeKeyPressed (TextEditor&) +{ +} + +void FileBrowserComponent::textEditorFocusLost (TextEditor&) +{ + if (mode != saveFileMode) + selectionChanged(); +} + +void FileBrowserComponent::buttonClicked (Button*) +{ + goUp(); +} + +void FileBrowserComponent::comboBoxChanged (ComboBox*) +{ + const String newText (currentPathBox->getText().trim().unquoted()); + + if (newText.isNotEmpty()) + { + const int index = currentPathBox->getSelectedId() - 1; + + StringArray rootNames, rootPaths; + getRoots (rootNames, rootPaths); + + if (rootPaths [index].isNotEmpty()) + { + setRoot (File (rootPaths [index])); + } + else + { + File f (newText); + + for (;;) + { + if (f.isDirectory()) + { + setRoot (f); + break; + } + + if (f.getParentDirectory() == f) + break; + + f = f.getParentDirectory(); + } + } + } +} + +const BitArray FileBrowserComponent::getRoots (StringArray& rootNames, StringArray& rootPaths) +{ + BitArray separators; + +#if JUCE_WIN32 + OwnedArray roots; + File::findFileSystemRoots (roots); + rootPaths.clear(); + + for (int i = 0; i < roots.size(); ++i) + { + const File* const drive = roots.getUnchecked(i); + + String name (drive->getFullPathName()); + rootPaths.add (name); + + if (drive->isOnHardDisk()) + { + String volume (drive->getVolumeLabel()); + + if (volume.isEmpty()) + volume = TRANS("Hard Drive"); + + name << " [" << drive->getVolumeLabel() << ']'; + } + else if (drive->isOnCDRomDrive()) + { + name << TRANS(" [CD/DVD drive]"); + } + + rootNames.add (name); + } + + separators.setBit (rootPaths.size()); + + rootPaths.add (File::getSpecialLocation (File::userDocumentsDirectory).getFullPathName()); + rootNames.add ("Documents"); + rootPaths.add (File::getSpecialLocation (File::userDesktopDirectory).getFullPathName()); + rootNames.add ("Desktop"); +#endif + +#if JUCE_MAC + rootPaths.add (File::getSpecialLocation (File::userHomeDirectory).getFullPathName()); + rootNames.add ("Home folder"); + rootPaths.add (File::getSpecialLocation (File::userDocumentsDirectory).getFullPathName()); + rootNames.add ("Documents"); + rootPaths.add (File::getSpecialLocation (File::userDesktopDirectory).getFullPathName()); + rootNames.add ("Desktop"); + + separators.setBit (rootPaths.size()); + + OwnedArray volumes; + File vol ("/Volumes"); + vol.findChildFiles (volumes, File::findDirectories, false); + + for (int i = 0; i < volumes.size(); ++i) + { + const File* const volume = volumes.getUnchecked(i); + + if (volume->isDirectory() && ! volume->getFileName().startsWithChar (T('.'))) + { + rootPaths.add (volume->getFullPathName()); + rootNames.add (volume->getFileName()); + } + } +#endif + +#if JUCE_LINUX + rootPaths.add ("/"); + rootNames.add ("/"); + rootPaths.add (File::getSpecialLocation (File::userHomeDirectory).getFullPathName()); + rootNames.add ("Home folder"); + rootPaths.add (File::getSpecialLocation (File::userDesktopDirectory).getFullPathName()); + rootNames.add ("Desktop"); +#endif + + return separators; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_FileBrowserComponent.cpp *********/ + +/********* Start of inlined file: juce_FileChooser.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +FileChooser::FileChooser (const String& chooserBoxTitle, + const File& currentFileOrDirectory, + const String& fileFilters, + const bool useNativeDialogBox_) + : title (chooserBoxTitle), + filters (fileFilters), + startingFile (currentFileOrDirectory), + useNativeDialogBox (useNativeDialogBox_) +{ +#if JUCE_LINUX + useNativeDialogBox = false; +#endif + + if (fileFilters.trim().isEmpty()) + filters = T("*"); +} + +FileChooser::~FileChooser() +{ +} + +bool FileChooser::browseForFileToOpen (FilePreviewComponent* previewComponent) +{ + return showDialog (false, false, false, false, previewComponent); +} + +bool FileChooser::browseForMultipleFilesToOpen (FilePreviewComponent* previewComponent) +{ + return showDialog (false, false, false, true, previewComponent); +} + +bool FileChooser::browseForFileToSave (const bool warnAboutOverwritingExistingFiles) +{ + return showDialog (false, true, warnAboutOverwritingExistingFiles, false, 0); +} + +bool FileChooser::browseForDirectory() +{ + return showDialog (true, false, false, false, 0); +} + +const File FileChooser::getResult() const +{ + // if you've used a multiple-file select, you should use the getResults() method + // to retrieve all the files that were chosen. + jassert (results.size() <= 1); + + const File* const f = results.getFirst(); + + if (f != 0) + return *f; + + return File::nonexistent; +} + +const OwnedArray & FileChooser::getResults() const +{ + return results; +} + +bool FileChooser::showDialog (const bool isDirectory, + const bool isSave, + const bool warnAboutOverwritingExistingFiles, + const bool selectMultipleFiles, + FilePreviewComponent* const previewComponent) +{ + ComponentDeletionWatcher* currentlyFocusedChecker = 0; + Component* const currentlyFocused = Component::getCurrentlyFocusedComponent(); + + if (currentlyFocused != 0) + currentlyFocusedChecker = new ComponentDeletionWatcher (currentlyFocused); + + results.clear(); + + // the preview component needs to be the right size before you pass it in here.. + jassert (previewComponent == 0 || (previewComponent->getWidth() > 10 + && previewComponent->getHeight() > 10)); + +#if JUCE_WIN32 + if (useNativeDialogBox) +#else + if (useNativeDialogBox && (previewComponent == 0)) +#endif + { + showPlatformDialog (results, title, startingFile, filters, + isDirectory, isSave, + warnAboutOverwritingExistingFiles, + selectMultipleFiles, + previewComponent); + } + else + { + jassert (! selectMultipleFiles); // not yet implemented for juce dialogs! + + WildcardFileFilter wildcard (filters, String::empty); + + FileBrowserComponent browserComponent (isDirectory ? FileBrowserComponent::chooseDirectoryMode + : (isSave ? FileBrowserComponent::saveFileMode + : FileBrowserComponent::loadFileMode), + startingFile, &wildcard, previewComponent); + + FileChooserDialogBox box (title, String::empty, + browserComponent, + warnAboutOverwritingExistingFiles, + browserComponent.findColour (AlertWindow::backgroundColourId)); + + if (box.show()) + results.add (new File (browserComponent.getCurrentFile())); + } + + if (currentlyFocused != 0 && ! currentlyFocusedChecker->hasBeenDeleted()) + currentlyFocused->grabKeyboardFocus(); + + delete currentlyFocusedChecker; + + return results.size() > 0; +} + +FilePreviewComponent::FilePreviewComponent() +{ +} + +FilePreviewComponent::~FilePreviewComponent() +{ +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_FileChooser.cpp *********/ + +/********* Start of inlined file: juce_FileChooserDialogBox.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +FileChooserDialogBox::FileChooserDialogBox (const String& name, + const String& instructions, + FileBrowserComponent& chooserComponent, + const bool warnAboutOverwritingExistingFiles_, + const Colour& backgroundColour) + : ResizableWindow (name, backgroundColour, true), + warnAboutOverwritingExistingFiles (warnAboutOverwritingExistingFiles_) +{ + content = new ContentComponent(); + content->setName (name); + content->instructions = instructions; + content->chooserComponent = &chooserComponent; + + content->addAndMakeVisible (&chooserComponent); + + content->okButton = new TextButton (chooserComponent.getActionVerb()); + content->addAndMakeVisible (content->okButton); + content->okButton->addButtonListener (this); + content->okButton->setEnabled (chooserComponent.currentFileIsValid()); + content->okButton->addShortcut (KeyPress (KeyPress::returnKey, 0, 0)); + + content->cancelButton = new TextButton (TRANS("Cancel")); + content->addAndMakeVisible (content->cancelButton); + content->cancelButton->addButtonListener (this); + content->cancelButton->addShortcut (KeyPress (KeyPress::escapeKey, 0, 0)); + + setContentComponent (content); + + setResizable (true, true); + setResizeLimits (300, 300, 1200, 1000); + + content->chooserComponent->addListener (this); +} + +FileChooserDialogBox::~FileChooserDialogBox() +{ + content->chooserComponent->removeListener (this); +} + +bool FileChooserDialogBox::show (int w, int h) +{ + if (w <= 0) + { + Component* const previewComp = content->chooserComponent->getPreviewComponent(); + if (previewComp != 0) + w = 400 + previewComp->getWidth(); + else + w = 600; + } + + if (h <= 0) + h = 500; + + centreWithSize (w, h); + + const bool ok = (runModalLoop() != 0); + setVisible (false); + return ok; +} + +void FileChooserDialogBox::buttonClicked (Button* button) +{ + if (button == content->okButton) + { + if (warnAboutOverwritingExistingFiles + && content->chooserComponent->getMode() == FileBrowserComponent::saveFileMode + && content->chooserComponent->getCurrentFile().exists()) + { + if (! AlertWindow::showOkCancelBox (AlertWindow::WarningIcon, + TRANS("File already exists"), + TRANS("There's already a file called:\n\n") + + content->chooserComponent->getCurrentFile().getFullPathName() + + T("\n\nAre you sure you want to overwrite it?"), + TRANS("overwrite"), + TRANS("cancel"))) + { + return; + } + } + + exitModalState (1); + } + else if (button == content->cancelButton) + closeButtonPressed(); +} + +void FileChooserDialogBox::closeButtonPressed() +{ + setVisible (false); +} + +void FileChooserDialogBox::selectionChanged() +{ + content->okButton->setEnabled (content->chooserComponent->currentFileIsValid()); +} + +void FileChooserDialogBox::fileClicked (const File&, const MouseEvent&) +{ +} + +void FileChooserDialogBox::fileDoubleClicked (const File&) +{ + selectionChanged(); + content->okButton->triggerClick(); +} + +FileChooserDialogBox::ContentComponent::ContentComponent() +{ + setInterceptsMouseClicks (false, true); +} + +FileChooserDialogBox::ContentComponent::~ContentComponent() +{ + delete okButton; + delete cancelButton; +} + +void FileChooserDialogBox::ContentComponent::paint (Graphics& g) +{ + g.setColour (Colours::black); + text.draw (g); +} + +void FileChooserDialogBox::ContentComponent::resized() +{ + getLookAndFeel().createFileChooserHeaderText (getName(), instructions, text, getWidth()); + + float left, top, right, bottom; + text.getBoundingBox (0, text.getNumGlyphs(), left, top, right, bottom, false); + + const int y = roundFloatToInt (bottom) + 10; + const int buttonHeight = 26; + const int buttonY = getHeight() - buttonHeight - 8; + + chooserComponent->setBounds (0, y, getWidth(), buttonY - y - 20); + + okButton->setBounds (proportionOfWidth (0.25f), buttonY, + proportionOfWidth (0.2f), buttonHeight); + + cancelButton->setBounds (proportionOfWidth (0.55f), buttonY, + proportionOfWidth (0.2f), buttonHeight); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_FileChooserDialogBox.cpp *********/ + +/********* Start of inlined file: juce_FileFilter.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +FileFilter::FileFilter (const String& filterDescription) + : description (filterDescription) +{ +} + +FileFilter::~FileFilter() +{ +} + +const String& FileFilter::getDescription() const throw() +{ + return description; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_FileFilter.cpp *********/ + +/********* Start of inlined file: juce_FileListComponent.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +Image* juce_createIconForFile (const File& file); + +FileListComponent::FileListComponent (DirectoryContentsList& listToShow) + : DirectoryContentsDisplayComponent (listToShow), + ListBox (String::empty, 0) +{ + setModel (this); + fileList.addChangeListener (this); +} + +FileListComponent::~FileListComponent() +{ + fileList.removeChangeListener (this); + deleteAllChildren(); +} + +const File FileListComponent::getSelectedFile() const +{ + return fileList.getFile (getSelectedRow()); +} + +void FileListComponent::scrollToTop() +{ + getVerticalScrollBar()->setCurrentRangeStart (0); +} + +void FileListComponent::changeListenerCallback (void*) +{ + updateContent(); +} + +class FileListItemComponent : public Component, + public TimeSliceClient, + public AsyncUpdater +{ +public: + + FileListItemComponent (FileListComponent& owner_, + TimeSliceThread& thread_) throw() + : owner (owner_), + thread (thread_), + icon (0) + { + } + + ~FileListItemComponent() throw() + { + thread.removeTimeSliceClient (this); + + clearIcon(); + } + + void paint (Graphics& g) + { + getLookAndFeel().drawFileBrowserRow (g, getWidth(), getHeight(), + file.getFileName(), + icon, + fileSize, modTime, + isDirectory, highlighted); + } + + void mouseDown (const MouseEvent& e) + { + owner.selectRowsBasedOnModifierKeys (index, e.mods); + owner.sendMouseClickMessage (file, e); + } + + void mouseDoubleClick (const MouseEvent&) + { + owner.sendDoubleClickMessage (file); + } + + void update (const File& root, + const DirectoryContentsList::FileInfo* const fileInfo, + const int index_, + const bool highlighted_) throw() + { + thread.removeTimeSliceClient (this); + + if (highlighted_ != highlighted + || index_ != index) + { + index = index_; + highlighted = highlighted_; + repaint(); + } + + File newFile; + String newFileSize; + String newModTime; + + if (fileInfo != 0) + { + newFile = root.getChildFile (fileInfo->filename); + newFileSize = File::descriptionOfSizeInBytes (fileInfo->fileSize); + newModTime = fileInfo->modificationTime.formatted (T("%d %b '%y %H:%M")); + } + + if (newFile != file + || fileSize != newFileSize + || modTime != newModTime) + { + file = newFile; + fileSize = newFileSize; + modTime = newModTime; + + isDirectory = fileInfo != 0 && fileInfo->isDirectory; + repaint(); + + clearIcon(); + } + + if (file != File::nonexistent + && icon == 0 && ! isDirectory) + { + updateIcon (true); + + if (icon == 0) + thread.addTimeSliceClient (this); + } + } + + bool useTimeSlice() + { + updateIcon (false); + return false; + } + + void handleAsyncUpdate() + { + repaint(); + } + + juce_UseDebuggingNewOperator + +private: + FileListComponent& owner; + TimeSliceThread& thread; + bool highlighted; + int index; + File file; + String fileSize; + String modTime; + Image* icon; + bool isDirectory; + + void clearIcon() throw() + { + ImageCache::release (icon); + icon = 0; + } + + void updateIcon (const bool onlyUpdateIfCached) throw() + { + if (icon == 0) + { + const int hashCode = (file.getFullPathName() + T("_iconCacheSalt")).hashCode(); + Image* im = ImageCache::getFromHashCode (hashCode); + + if (im == 0 && ! onlyUpdateIfCached) + { + im = juce_createIconForFile (file); + + if (im != 0) + ImageCache::addImageToCache (im, hashCode); + } + + if (im != 0) + { + icon = im; + triggerAsyncUpdate(); + } + } + } +}; + +int FileListComponent::getNumRows() +{ + return fileList.getNumFiles(); +} + +void FileListComponent::paintListBoxItem (int, Graphics&, int, int, bool) +{ +} + +Component* FileListComponent::refreshComponentForRow (int row, bool isSelected, Component* existingComponentToUpdate) +{ + FileListItemComponent* comp = dynamic_cast (existingComponentToUpdate); + + if (comp == 0) + { + delete existingComponentToUpdate; + existingComponentToUpdate = comp = new FileListItemComponent (*this, fileList.getTimeSliceThread()); + } + + DirectoryContentsList::FileInfo fileInfo; + + if (fileList.getFileInfo (row, fileInfo)) + comp->update (fileList.getDirectory(), &fileInfo, row, isSelected); + else + comp->update (fileList.getDirectory(), 0, row, isSelected); + + return comp; +} + +void FileListComponent::selectedRowsChanged (int /*lastRowSelected*/) +{ + sendSelectionChangeMessage(); +} + +void FileListComponent::deleteKeyPressed (int /*currentSelectedRow*/) +{ +} + +void FileListComponent::returnKeyPressed (int currentSelectedRow) +{ + sendDoubleClickMessage (fileList.getFile (currentSelectedRow)); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_FileListComponent.cpp *********/ + +/********* Start of inlined file: juce_FilenameComponent.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +FilenameComponent::FilenameComponent (const String& name, + const File& currentFile, + const bool canEditFilename, + const bool isDirectory, + const bool isForSaving, + const String& fileBrowserWildcard, + const String& enforcedSuffix_, + const String& textWhenNothingSelected) + : Component (name), + maxRecentFiles (30), + isDir (isDirectory), + isSaving (isForSaving), + isFileDragOver (false), + wildcard (fileBrowserWildcard), + enforcedSuffix (enforcedSuffix_) +{ + addAndMakeVisible (filenameBox = new ComboBox (T("fn"))); + filenameBox->setEditableText (canEditFilename); + filenameBox->addListener (this); + filenameBox->setTextWhenNothingSelected (textWhenNothingSelected); + filenameBox->setTextWhenNoChoicesAvailable (TRANS("(no recently seleced files)")); + + browseButton = 0; + setBrowseButtonText (T("...")); + + setCurrentFile (currentFile, true); +} + +FilenameComponent::~FilenameComponent() +{ + deleteAllChildren(); +} + +void FilenameComponent::paintOverChildren (Graphics& g) +{ + if (isFileDragOver) + { + g.setColour (Colours::red.withAlpha (0.2f)); + g.drawRect (0, 0, getWidth(), getHeight(), 3); + } +} + +void FilenameComponent::resized() +{ + getLookAndFeel().layoutFilenameComponent (*this, filenameBox, browseButton); +} + +void FilenameComponent::setBrowseButtonText (const String& newBrowseButtonText) +{ + browseButtonText = newBrowseButtonText; + lookAndFeelChanged(); +} + +void FilenameComponent::lookAndFeelChanged() +{ + deleteAndZero (browseButton); + + addAndMakeVisible (browseButton = getLookAndFeel().createFilenameComponentBrowseButton (browseButtonText)); + browseButton->setConnectedEdges (Button::ConnectedOnLeft); + resized(); + + browseButton->addButtonListener (this); +} + +void FilenameComponent::setTooltip (const String& newTooltip) +{ + SettableTooltipClient::setTooltip (newTooltip); + filenameBox->setTooltip (newTooltip); +} + +void FilenameComponent::setDefaultBrowseTarget (const File& newDefaultDirectory) throw() +{ + defaultBrowseFile = newDefaultDirectory; +} + +void FilenameComponent::buttonClicked (Button*) +{ + FileChooser fc (TRANS("Choose a new file"), + getCurrentFile() == File::nonexistent ? defaultBrowseFile + : getCurrentFile(), + wildcard); + + if (isDir ? fc.browseForDirectory() + : (isSaving ? fc.browseForFileToSave (false) + : fc.browseForFileToOpen())) + { + setCurrentFile (fc.getResult(), true); + } +} + +void FilenameComponent::comboBoxChanged (ComboBox*) +{ + setCurrentFile (getCurrentFile(), true); +} + +bool FilenameComponent::isInterestedInFileDrag (const StringArray&) +{ + return true; +} + +void FilenameComponent::filesDropped (const StringArray& filenames, int, int) +{ + isFileDragOver = false; + repaint(); + + const File f (filenames[0]); + + if (f.exists() && (f.isDirectory() == isDir)) + setCurrentFile (f, true); +} + +void FilenameComponent::fileDragEnter (const StringArray&, int, int) +{ + isFileDragOver = true; + repaint(); +} + +void FilenameComponent::fileDragExit (const StringArray&) +{ + isFileDragOver = false; + repaint(); +} + +const File FilenameComponent::getCurrentFile() const +{ + File f (filenameBox->getText()); + + if (enforcedSuffix.isNotEmpty()) + f = f.withFileExtension (enforcedSuffix); + + return f; +} + +void FilenameComponent::setCurrentFile (File newFile, + const bool addToRecentlyUsedList, + const bool sendChangeNotification) +{ + if (enforcedSuffix.isNotEmpty()) + newFile = newFile.withFileExtension (enforcedSuffix); + + if (newFile.getFullPathName() != lastFilename) + { + lastFilename = newFile.getFullPathName(); + + if (addToRecentlyUsedList) + addRecentlyUsedFile (newFile); + + filenameBox->setText (lastFilename, true); + + if (sendChangeNotification) + triggerAsyncUpdate(); + } +} + +void FilenameComponent::setFilenameIsEditable (const bool shouldBeEditable) +{ + filenameBox->setEditableText (shouldBeEditable); +} + +const StringArray FilenameComponent::getRecentlyUsedFilenames() const +{ + StringArray names; + + for (int i = 0; i < filenameBox->getNumItems(); ++i) + names.add (filenameBox->getItemText (i)); + + return names; +} + +void FilenameComponent::setRecentlyUsedFilenames (const StringArray& filenames) +{ + if (filenames != getRecentlyUsedFilenames()) + { + filenameBox->clear(); + + for (int i = 0; i < jmin (filenames.size(), maxRecentFiles); ++i) + filenameBox->addItem (filenames[i], i + 1); + } +} + +void FilenameComponent::setMaxNumberOfRecentFiles (const int newMaximum) +{ + maxRecentFiles = jmax (1, newMaximum); + + setRecentlyUsedFilenames (getRecentlyUsedFilenames()); +} + +void FilenameComponent::addRecentlyUsedFile (const File& file) +{ + StringArray files (getRecentlyUsedFilenames()); + + if (file.getFullPathName().isNotEmpty()) + { + files.removeString (file.getFullPathName(), true); + files.insert (0, file.getFullPathName()); + + setRecentlyUsedFilenames (files); + } +} + +void FilenameComponent::addListener (FilenameComponentListener* const listener) throw() +{ + jassert (listener != 0); + + if (listener != 0) + listeners.add (listener); +} + +void FilenameComponent::removeListener (FilenameComponentListener* const listener) throw() +{ + listeners.removeValue (listener); +} + +void FilenameComponent::handleAsyncUpdate() +{ + for (int i = listeners.size(); --i >= 0;) + { + ((FilenameComponentListener*) listeners.getUnchecked (i))->filenameComponentChanged (this); + i = jmin (i, listeners.size()); + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_FilenameComponent.cpp *********/ + +/********* Start of inlined file: juce_FileSearchPathListComponent.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +FileSearchPathListComponent::FileSearchPathListComponent() +{ + addAndMakeVisible (listBox = new ListBox (String::empty, this)); + listBox->setColour (ListBox::backgroundColourId, Colours::black.withAlpha (0.02f)); + listBox->setColour (ListBox::outlineColourId, Colours::black.withAlpha (0.1f)); + listBox->setOutlineThickness (1); + + addAndMakeVisible (addButton = new TextButton ("+")); + addButton->addButtonListener (this); + addButton->setConnectedEdges (Button::ConnectedOnLeft | Button::ConnectedOnRight | Button::ConnectedOnBottom | Button::ConnectedOnTop); + + addAndMakeVisible (removeButton = new TextButton ("-")); + removeButton->addButtonListener (this); + removeButton->setConnectedEdges (Button::ConnectedOnLeft | Button::ConnectedOnRight | Button::ConnectedOnBottom | Button::ConnectedOnTop); + + addAndMakeVisible (changeButton = new TextButton (TRANS("change..."))); + changeButton->addButtonListener (this); + + addAndMakeVisible (upButton = new DrawableButton (String::empty, DrawableButton::ImageOnButtonBackground)); + upButton->addButtonListener (this); + + { + Path arrowPath; + arrowPath.addArrow (50.0f, 100.0f, 50.0f, 0.0, 40.0f, 100.0f, 50.0f); + DrawablePath arrowImage; + arrowImage.setSolidFill (Colours::black.withAlpha (0.4f)); + arrowImage.setPath (arrowPath); + + ((DrawableButton*) upButton)->setImages (&arrowImage); + } + + addAndMakeVisible (downButton = new DrawableButton (String::empty, DrawableButton::ImageOnButtonBackground)); + downButton->addButtonListener (this); + + { + Path arrowPath; + arrowPath.addArrow (50.0f, 0.0f, 50.0f, 100.0f, 40.0f, 100.0f, 50.0f); + DrawablePath arrowImage; + arrowImage.setSolidFill (Colours::black.withAlpha (0.4f)); + arrowImage.setPath (arrowPath); + + ((DrawableButton*) downButton)->setImages (&arrowImage); + } + + updateButtons(); +} + +FileSearchPathListComponent::~FileSearchPathListComponent() +{ + deleteAllChildren(); +} + +void FileSearchPathListComponent::updateButtons() throw() +{ + const bool anythingSelected = listBox->getNumSelectedRows() > 0; + + removeButton->setEnabled (anythingSelected); + changeButton->setEnabled (anythingSelected); + upButton->setEnabled (anythingSelected); + downButton->setEnabled (anythingSelected); +} + +void FileSearchPathListComponent::changed() throw() +{ + listBox->updateContent(); + listBox->repaint(); + updateButtons(); +} + +void FileSearchPathListComponent::setPath (const FileSearchPath& newPath) +{ + if (newPath.toString() != path.toString()) + { + path = newPath; + changed(); + } +} + +void FileSearchPathListComponent::setDefaultBrowseTarget (const File& newDefaultDirectory) throw() +{ + defaultBrowseTarget = newDefaultDirectory; +} + +int FileSearchPathListComponent::getNumRows() +{ + return path.getNumPaths(); +} + +void FileSearchPathListComponent::paintListBoxItem (int rowNumber, Graphics& g, int width, int height, bool rowIsSelected) +{ + if (rowIsSelected) + g.fillAll (findColour (TextEditor::highlightColourId)); + + g.setColour (findColour (ListBox::textColourId)); + Font f (height * 0.7f); + f.setHorizontalScale (0.9f); + g.setFont (f); + + g.drawText (path [rowNumber].getFullPathName(), + 4, 0, width - 6, height, + Justification::centredLeft, true); +} + +void FileSearchPathListComponent::deleteKeyPressed (int row) +{ + if (((unsigned int) row) < (unsigned int) path.getNumPaths()) + { + path.remove (row); + changed(); + } +} + +void FileSearchPathListComponent::returnKeyPressed (int row) +{ + FileChooser chooser (TRANS("Change folder..."), path [row], T("*")); + + if (chooser.browseForDirectory()) + { + path.remove (row); + path.add (chooser.getResult(), row); + changed(); + } +} + +void FileSearchPathListComponent::listBoxItemDoubleClicked (int row, const MouseEvent&) +{ + returnKeyPressed (row); +} + +void FileSearchPathListComponent::selectedRowsChanged (int) +{ + updateButtons(); +} + +void FileSearchPathListComponent::paint (Graphics& g) +{ + g.fillAll (findColour (backgroundColourId)); +} + +void FileSearchPathListComponent::resized() +{ + const int buttonH = 22; + const int buttonY = getHeight() - buttonH - 4; + listBox->setBounds (2, 2, getWidth() - 4, buttonY - 5); + + addButton->setBounds (2, buttonY, buttonH, buttonH); + removeButton->setBounds (addButton->getRight(), buttonY, buttonH, buttonH); + + ((TextButton*) changeButton)->changeWidthToFitText (buttonH); + downButton->setSize (buttonH * 2, buttonH); + upButton->setSize (buttonH * 2, buttonH); + + downButton->setTopRightPosition (getWidth() - 2, buttonY); + upButton->setTopRightPosition (downButton->getX() - 4, buttonY); + changeButton->setTopRightPosition (upButton->getX() - 8, buttonY); +} + +bool FileSearchPathListComponent::isInterestedInFileDrag (const StringArray&) +{ + return true; +} + +void FileSearchPathListComponent::filesDropped (const StringArray& filenames, int, int mouseY) +{ + for (int i = filenames.size(); --i >= 0;) + { + const File f (filenames[i]); + + if (f.isDirectory()) + { + const int row = listBox->getRowContainingPosition (0, mouseY - listBox->getY()); + path.add (f, row); + changed(); + } + } +} + +void FileSearchPathListComponent::buttonClicked (Button* button) +{ + const int currentRow = listBox->getSelectedRow(); + + if (button == removeButton) + { + deleteKeyPressed (currentRow); + } + else if (button == addButton) + { + File start (defaultBrowseTarget); + + if (start == File::nonexistent) + start = path [0]; + + if (start == File::nonexistent) + start = File::getCurrentWorkingDirectory(); + + FileChooser chooser (TRANS("Add a folder..."), start, T("*")); + + if (chooser.browseForDirectory()) + { + path.add (chooser.getResult(), currentRow); + } + } + else if (button == changeButton) + { + returnKeyPressed (currentRow); + } + else if (button == upButton) + { + if (currentRow > 0 && currentRow < path.getNumPaths()) + { + const File f (path[currentRow]); + path.remove (currentRow); + path.add (f, currentRow - 1); + listBox->selectRow (currentRow - 1); + } + } + else if (button == downButton) + { + if (currentRow >= 0 && currentRow < path.getNumPaths() - 1) + { + const File f (path[currentRow]); + path.remove (currentRow); + path.add (f, currentRow + 1); + listBox->selectRow (currentRow + 1); + } + } + + changed(); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_FileSearchPathListComponent.cpp *********/ + +/********* Start of inlined file: juce_FileTreeComponent.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +Image* juce_createIconForFile (const File& file); + +class FileListTreeItem : public TreeViewItem, + public TimeSliceClient, + public AsyncUpdater, + public ChangeListener +{ +public: + + FileListTreeItem (FileTreeComponent& owner_, + DirectoryContentsList* const parentContentsList_, + const int indexInContentsList_, + const File& file_, + TimeSliceThread& thread_) throw() + : file (file_), + owner (owner_), + parentContentsList (parentContentsList_), + indexInContentsList (indexInContentsList_), + subContentsList (0), + canDeleteSubContentsList (false), + thread (thread_), + icon (0) + { + DirectoryContentsList::FileInfo fileInfo; + + if (parentContentsList_ != 0 + && parentContentsList_->getFileInfo (indexInContentsList_, fileInfo)) + { + fileSize = File::descriptionOfSizeInBytes (fileInfo.fileSize); + modTime = fileInfo.modificationTime.formatted (T("%d %b '%y %H:%M")); + isDirectory = fileInfo.isDirectory; + } + else + { + isDirectory = true; + } + } + + ~FileListTreeItem() throw() + { + thread.removeTimeSliceClient (this); + + clearSubItems(); + ImageCache::release (icon); + + if (canDeleteSubContentsList) + delete subContentsList; + } + + bool mightContainSubItems() { return isDirectory; } + const String getUniqueName() const { return file.getFullPathName(); } + int getItemHeight() const { return 22; } + + const String getDragSourceDescription() { return owner.getDragAndDropDescription(); } + + void itemOpennessChanged (bool isNowOpen) + { + if (isNowOpen) + { + clearSubItems(); + + isDirectory = file.isDirectory(); + + if (isDirectory) + { + if (subContentsList == 0) + { + jassert (parentContentsList != 0); + + DirectoryContentsList* const l = new DirectoryContentsList (parentContentsList->getFilter(), thread); + l->setDirectory (file, true, true); + + setSubContentsList (l); + canDeleteSubContentsList = true; + } + + changeListenerCallback (0); + } + } + } + + void setSubContentsList (DirectoryContentsList* newList) throw() + { + jassert (subContentsList == 0); + subContentsList = newList; + newList->addChangeListener (this); + } + + void changeListenerCallback (void*) + { + clearSubItems(); + + if (isOpen() && subContentsList != 0) + { + for (int i = 0; i < subContentsList->getNumFiles(); ++i) + { + FileListTreeItem* const item + = new FileListTreeItem (owner, subContentsList, i, subContentsList->getFile(i), thread); + + addSubItem (item); + } + } + } + + void paintItem (Graphics& g, int width, int height) + { + if (file != File::nonexistent && ! isDirectory) + { + updateIcon (true); + + if (icon == 0) + thread.addTimeSliceClient (this); + } + + owner.getLookAndFeel() + .drawFileBrowserRow (g, width, height, + file.getFileName(), + icon, + fileSize, modTime, + isDirectory, isSelected()); + } + + void itemClicked (const MouseEvent& e) + { + owner.sendMouseClickMessage (file, e); + } + + void itemDoubleClicked (const MouseEvent& e) + { + TreeViewItem::itemDoubleClicked (e); + + owner.sendDoubleClickMessage (file); + } + + void itemSelectionChanged (bool) + { + owner.sendSelectionChangeMessage(); + } + + bool useTimeSlice() + { + updateIcon (false); + thread.removeTimeSliceClient (this); + return false; + } + + void handleAsyncUpdate() + { + owner.repaint(); + } + + const File file; + + juce_UseDebuggingNewOperator + +private: + FileTreeComponent& owner; + DirectoryContentsList* parentContentsList; + int indexInContentsList; + DirectoryContentsList* subContentsList; + bool isDirectory, canDeleteSubContentsList; + TimeSliceThread& thread; + Image* icon; + String fileSize; + String modTime; + + void updateIcon (const bool onlyUpdateIfCached) throw() + { + if (icon == 0) + { + const int hashCode = (file.getFullPathName() + T("_iconCacheSalt")).hashCode(); + Image* im = ImageCache::getFromHashCode (hashCode); + + if (im == 0 && ! onlyUpdateIfCached) + { + im = juce_createIconForFile (file); + + if (im != 0) + ImageCache::addImageToCache (im, hashCode); + } + + if (im != 0) + { + icon = im; + triggerAsyncUpdate(); + } + } + } +}; + +FileTreeComponent::FileTreeComponent (DirectoryContentsList& listToShow) + : DirectoryContentsDisplayComponent (listToShow) +{ + FileListTreeItem* const root + = new FileListTreeItem (*this, 0, 0, listToShow.getDirectory(), + listToShow.getTimeSliceThread()); + + root->setSubContentsList (&listToShow); + setRootItemVisible (false); + setRootItem (root); +} + +FileTreeComponent::~FileTreeComponent() +{ + TreeViewItem* const root = getRootItem(); + setRootItem (0); + delete root; +} + +const File FileTreeComponent::getSelectedFile() const +{ + return getSelectedFile (0); +} + +const File FileTreeComponent::getSelectedFile (const int index) const throw() +{ + const FileListTreeItem* const item = dynamic_cast (getSelectedItem (index)); + + if (item != 0) + return item->file; + + return File::nonexistent; +} + +void FileTreeComponent::scrollToTop() +{ + getViewport()->getVerticalScrollBar()->setCurrentRangeStart (0); +} + +void FileTreeComponent::setDragAndDropDescription (const String& description) throw() +{ + dragAndDropDescription = description; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_FileTreeComponent.cpp *********/ + +/********* Start of inlined file: juce_ImagePreviewComponent.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +ImagePreviewComponent::ImagePreviewComponent() + : currentThumbnail (0) +{ +} + +ImagePreviewComponent::~ImagePreviewComponent() +{ + delete currentThumbnail; +} + +void ImagePreviewComponent::getThumbSize (int& w, int& h) const +{ + const int availableW = proportionOfWidth (0.97f); + const int availableH = getHeight() - 13 * 4; + + const double scale = jmin (1.0, + availableW / (double) w, + availableH / (double) h); + + w = roundDoubleToInt (scale * w); + h = roundDoubleToInt (scale * h); +} + +void ImagePreviewComponent::selectedFileChanged (const File& file) +{ + if (fileToLoad != file) + { + fileToLoad = file; + startTimer (100); + } +} + +void ImagePreviewComponent::timerCallback() +{ + stopTimer(); + + deleteAndZero (currentThumbnail); + currentDetails = String::empty; + repaint(); + + FileInputStream* const in = fileToLoad.createInputStream(); + + if (in != 0) + { + ImageFileFormat* const format = ImageFileFormat::findImageFormatForStream (*in); + + if (format != 0) + { + currentThumbnail = format->decodeImage (*in); + + if (currentThumbnail != 0) + { + int w = currentThumbnail->getWidth(); + int h = currentThumbnail->getHeight(); + + currentDetails + << fileToLoad.getFileName() << "\n" + << format->getFormatName() << "\n" + << w << " x " << h << " pixels\n" + << File::descriptionOfSizeInBytes (fileToLoad.getSize()); + + getThumbSize (w, h); + + Image* const reduced = currentThumbnail->createCopy (w, h); + + delete currentThumbnail; + currentThumbnail = reduced; + } + } + + delete in; + } +} + +void ImagePreviewComponent::paint (Graphics& g) +{ + if (currentThumbnail != 0) + { + g.setFont (13.0f); + + int w = currentThumbnail->getWidth(); + int h = currentThumbnail->getHeight(); + getThumbSize (w, h); + + const int numLines = 4; + const int totalH = 13 * numLines + h + 4; + const int y = (getHeight() - totalH) / 2; + + g.drawImageWithin (currentThumbnail, + (getWidth() - w) / 2, y, w, h, + RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize, + false); + + g.drawFittedText (currentDetails, + 0, y + h + 4, getWidth(), 100, + Justification::centredTop, numLines); + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ImagePreviewComponent.cpp *********/ + +/********* Start of inlined file: juce_WildcardFileFilter.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +WildcardFileFilter::WildcardFileFilter (const String& wildcardPatterns, + const String& description) + : FileFilter (description.isEmpty() ? wildcardPatterns + : (description + T(" (") + wildcardPatterns + T(")"))) +{ + wildcards.addTokens (wildcardPatterns.toLowerCase(), T(";,"), T("\"'")); + + wildcards.trim(); + wildcards.removeEmptyStrings(); + + // special case for *.*, because people use it to mean "any file", but it + // would actually ignore files with no extension. + for (int i = wildcards.size(); --i >= 0;) + if (wildcards[i] == T("*.*")) + wildcards.set (i, T("*")); +} + +WildcardFileFilter::~WildcardFileFilter() +{ +} + +bool WildcardFileFilter::isFileSuitable (const File& file) const +{ + const String filename (file.getFileName()); + + for (int i = wildcards.size(); --i >= 0;) + if (filename.matchesWildcard (wildcards[i], true)) + return true; + + return false; +} + +bool WildcardFileFilter::isDirectorySuitable (const File&) const +{ + return true; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_WildcardFileFilter.cpp *********/ + +/********* Start of inlined file: juce_KeyboardFocusTraverser.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +KeyboardFocusTraverser::KeyboardFocusTraverser() +{ +} + +KeyboardFocusTraverser::~KeyboardFocusTraverser() +{ +} + +// This will sort a set of components, so that they are ordered in terms of +// left-to-right and then top-to-bottom. +class ScreenPositionComparator +{ +public: + ScreenPositionComparator() {} + + static int compareElements (const Component* const first, const Component* const second) throw() + { + int explicitOrder1 = first->getExplicitFocusOrder(); + if (explicitOrder1 <= 0) + explicitOrder1 = INT_MAX / 2; + + int explicitOrder2 = second->getExplicitFocusOrder(); + if (explicitOrder2 <= 0) + explicitOrder2 = INT_MAX / 2; + + if (explicitOrder1 != explicitOrder2) + return explicitOrder1 - explicitOrder2; + + const int diff = first->getY() - second->getY(); + + return (diff == 0) ? first->getX() - second->getX() + : diff; + } +}; + +static void findAllFocusableComponents (Component* const parent, Array & comps) +{ + if (parent->getNumChildComponents() > 0) + { + Array localComps; + ScreenPositionComparator comparator; + + int i; + for (i = parent->getNumChildComponents(); --i >= 0;) + { + Component* const c = parent->getChildComponent (i); + + if (c->isVisible() && c->isEnabled()) + localComps.addSorted (comparator, c); + } + + for (i = 0; i < localComps.size(); ++i) + { + Component* const c = localComps.getUnchecked (i); + + if (c->getWantsKeyboardFocus()) + comps.add (c); + + if (! c->isFocusContainer()) + findAllFocusableComponents (c, comps); + } + } +} + +static Component* getIncrementedComponent (Component* const current, const int delta) throw() +{ + Component* focusContainer = current->getParentComponent(); + + if (focusContainer != 0) + { + while (focusContainer->getParentComponent() != 0 && ! focusContainer->isFocusContainer()) + focusContainer = focusContainer->getParentComponent(); + + if (focusContainer != 0) + { + Array comps; + findAllFocusableComponents (focusContainer, comps); + + if (comps.size() > 0) + { + const int index = comps.indexOf (current); + return comps [(index + comps.size() + delta) % comps.size()]; + } + } + } + + return 0; +} + +Component* KeyboardFocusTraverser::getNextComponent (Component* current) +{ + return getIncrementedComponent (current, 1); +} + +Component* KeyboardFocusTraverser::getPreviousComponent (Component* current) +{ + return getIncrementedComponent (current, -1); +} + +Component* KeyboardFocusTraverser::getDefaultComponent (Component* parentComponent) +{ + Array comps; + + if (parentComponent != 0) + findAllFocusableComponents (parentComponent, comps); + + return comps.getFirst(); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_KeyboardFocusTraverser.cpp *********/ + +/********* Start of inlined file: juce_KeyListener.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +bool KeyListener::keyStateChanged (Component*) +{ + return false; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_KeyListener.cpp *********/ + +/********* Start of inlined file: juce_KeyMappingEditorComponent.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +// N.B. these two includes are put here deliberately to avoid problems with +// old GCCs failing on long include paths + +const int maxKeys = 3; + +class KeyMappingChangeButton : public Button +{ +public: + KeyMappingChangeButton (KeyMappingEditorComponent* const owner_, + const CommandID commandID_, + const String& keyName, + const int keyNum_) + : Button (keyName), + owner (owner_), + commandID (commandID_), + keyNum (keyNum_) + { + setWantsKeyboardFocus (false); + setTriggeredOnMouseDown (keyNum >= 0); + + if (keyNum_ < 0) + setTooltip (TRANS("adds a new key-mapping")); + else + setTooltip (TRANS("click to change this key-mapping")); + } + + ~KeyMappingChangeButton() + { + } + + void paintButton (Graphics& g, bool isOver, bool isDown) + { + if (keyNum >= 0) + { + if (isEnabled()) + { + const float alpha = isDown ? 0.3f : (isOver ? 0.15f : 0.08f); + g.fillAll (owner->textColour.withAlpha (alpha)); + + g.setOpacity (0.3f); + g.drawBevel (0, 0, getWidth(), getHeight(), 2); + } + + g.setColour (owner->textColour); + g.setFont (getHeight() * 0.6f); + g.drawFittedText (getName(), + 3, 0, getWidth() - 6, getHeight(), + Justification::centred, 1); + } + else + { + const float thickness = 7.0f; + const float indent = 22.0f; + + Path p; + p.addEllipse (0.0f, 0.0f, 100.0f, 100.0f); + p.addRectangle (indent, 50.0f - thickness, 100.0f - indent * 2.0f, thickness * 2.0f); + p.addRectangle (50.0f - thickness, indent, thickness * 2.0f, 50.0f - indent - thickness); + p.addRectangle (50.0f - thickness, 50.0f + thickness, thickness * 2.0f, 50.0f - indent - thickness); + p.setUsingNonZeroWinding (false); + + g.setColour (owner->textColour.withAlpha (isDown ? 0.7f : (isOver ? 0.5f : 0.3f))); + g.fillPath (p, p.getTransformToScaleToFit (2.0f, 2.0f, getWidth() - 4.0f, getHeight() - 4.0f, true)); + } + + if (hasKeyboardFocus (false)) + { + g.setColour (owner->textColour.withAlpha (0.4f)); + g.drawRect (0, 0, getWidth(), getHeight()); + } + } + + void clicked() + { + if (keyNum >= 0) + { + // existing key clicked.. + PopupMenu m; + m.addItem (1, TRANS("change this key-mapping")); + m.addSeparator(); + m.addItem (2, TRANS("remove this key-mapping")); + + const int res = m.show(); + + if (res == 1) + { + owner->assignNewKey (commandID, keyNum); + } + else if (res == 2) + { + owner->getMappings()->removeKeyPress (commandID, keyNum); + } + } + else + { + // + button pressed.. + owner->assignNewKey (commandID, -1); + } + } + + void fitToContent (const int h) throw() + { + if (keyNum < 0) + { + setSize (h, h); + } + else + { + Font f (h * 0.6f); + setSize (jlimit (h * 4, h * 8, 6 + f.getStringWidth (getName())), h); + } + } + + juce_UseDebuggingNewOperator + +private: + KeyMappingEditorComponent* const owner; + const CommandID commandID; + const int keyNum; + + KeyMappingChangeButton (const KeyMappingChangeButton&); + const KeyMappingChangeButton& operator= (const KeyMappingChangeButton&); +}; + +class KeyMappingItemComponent : public Component +{ +public: + KeyMappingItemComponent (KeyMappingEditorComponent* const owner_, + const CommandID commandID_) + : owner (owner_), + commandID (commandID_) + { + setInterceptsMouseClicks (false, true); + + const bool isReadOnly = owner_->isCommandReadOnly (commandID); + + const Array keyPresses (owner_->getMappings()->getKeyPressesAssignedToCommand (commandID)); + + for (int i = 0; i < jmin (maxKeys, keyPresses.size()); ++i) + { + KeyMappingChangeButton* const kb + = new KeyMappingChangeButton (owner_, commandID, + owner_->getDescriptionForKeyPress (keyPresses.getReference (i)), i); + + kb->setEnabled (! isReadOnly); + addAndMakeVisible (kb); + } + + KeyMappingChangeButton* const kb + = new KeyMappingChangeButton (owner_, commandID, String::empty, -1); + + addChildComponent (kb); + kb->setVisible (keyPresses.size() < maxKeys && ! isReadOnly); + } + + ~KeyMappingItemComponent() + { + deleteAllChildren(); + } + + void paint (Graphics& g) + { + g.setFont (getHeight() * 0.7f); + g.setColour (owner->textColour); + + g.drawFittedText (owner->getMappings()->getCommandManager()->getNameOfCommand (commandID), + 4, 0, jmax (40, getChildComponent (0)->getX() - 5), getHeight(), + Justification::centredLeft, true); + } + + void resized() + { + int x = getWidth() - 4; + + for (int i = getNumChildComponents(); --i >= 0;) + { + KeyMappingChangeButton* const kb = dynamic_cast (getChildComponent (i)); + + kb->fitToContent (getHeight() - 2); + kb->setTopRightPosition (x, 1); + x -= kb->getWidth() + 5; + } + } + + juce_UseDebuggingNewOperator + +private: + KeyMappingEditorComponent* const owner; + const CommandID commandID; + + KeyMappingItemComponent (const KeyMappingItemComponent&); + const KeyMappingItemComponent& operator= (const KeyMappingItemComponent&); +}; + +class KeyMappingTreeViewItem : public TreeViewItem +{ +public: + KeyMappingTreeViewItem (KeyMappingEditorComponent* const owner_, + const CommandID commandID_) + : owner (owner_), + commandID (commandID_) + { + } + + ~KeyMappingTreeViewItem() + { + } + + const String getUniqueName() const { return String ((int) commandID) + "_id"; } + bool mightContainSubItems() { return false; } + int getItemHeight() const { return 20; } + + Component* createItemComponent() + { + return new KeyMappingItemComponent (owner, commandID); + } + + juce_UseDebuggingNewOperator + +private: + KeyMappingEditorComponent* const owner; + const CommandID commandID; + + KeyMappingTreeViewItem (const KeyMappingTreeViewItem&); + const KeyMappingTreeViewItem& operator= (const KeyMappingTreeViewItem&); +}; + +class KeyCategoryTreeViewItem : public TreeViewItem +{ +public: + KeyCategoryTreeViewItem (KeyMappingEditorComponent* const owner_, + const String& name) + : owner (owner_), + categoryName (name) + { + } + + ~KeyCategoryTreeViewItem() + { + } + + const String getUniqueName() const { return categoryName + "_cat"; } + bool mightContainSubItems() { return true; } + int getItemHeight() const { return 28; } + + void paintItem (Graphics& g, int width, int height) + { + g.setFont (height * 0.6f, Font::bold); + g.setColour (owner->textColour); + + g.drawText (categoryName, + 2, 0, width - 2, height, + Justification::centredLeft, true); + } + + void itemOpennessChanged (bool isNowOpen) + { + if (isNowOpen) + { + if (getNumSubItems() == 0) + { + Array commands (owner->getMappings()->getCommandManager()->getCommandsInCategory (categoryName)); + + for (int i = 0; i < commands.size(); ++i) + { + if (owner->shouldCommandBeIncluded (commands[i])) + addSubItem (new KeyMappingTreeViewItem (owner, commands[i])); + } + } + } + else + { + clearSubItems(); + } + } + + juce_UseDebuggingNewOperator + +private: + KeyMappingEditorComponent* owner; + String categoryName; + + KeyCategoryTreeViewItem (const KeyCategoryTreeViewItem&); + const KeyCategoryTreeViewItem& operator= (const KeyCategoryTreeViewItem&); +}; + +KeyMappingEditorComponent::KeyMappingEditorComponent (KeyPressMappingSet* const mappingManager, + const bool showResetToDefaultButton) + : mappings (mappingManager), + textColour (Colours::black) +{ + jassert (mappingManager != 0); // can't be null! + + mappingManager->addChangeListener (this); + + setLinesDrawnForSubItems (false); + + resetButton = 0; + + if (showResetToDefaultButton) + { + addAndMakeVisible (resetButton = new TextButton (TRANS("reset to defaults"))); + resetButton->addButtonListener (this); + } + + addAndMakeVisible (tree = new TreeView()); + tree->setColour (TreeView::backgroundColourId, backgroundColour); + tree->setRootItemVisible (false); + tree->setDefaultOpenness (true); + tree->setRootItem (this); +} + +KeyMappingEditorComponent::~KeyMappingEditorComponent() +{ + mappings->removeChangeListener (this); + deleteAllChildren(); +} + +bool KeyMappingEditorComponent::mightContainSubItems() +{ + return true; +} + +const String KeyMappingEditorComponent::getUniqueName() const +{ + return T("keys"); +} + +void KeyMappingEditorComponent::setColours (const Colour& mainBackground, + const Colour& textColour_) +{ + backgroundColour = mainBackground; + textColour = textColour_; + tree->setColour (TreeView::backgroundColourId, backgroundColour); +} + +void KeyMappingEditorComponent::parentHierarchyChanged() +{ + changeListenerCallback (0); +} + +void KeyMappingEditorComponent::resized() +{ + int h = getHeight(); + + if (resetButton != 0) + { + const int buttonHeight = 20; + h -= buttonHeight + 8; + int x = getWidth() - 8; + const int y = h + 6; + + resetButton->changeWidthToFitText (buttonHeight); + resetButton->setTopRightPosition (x, y); + } + + tree->setBounds (0, 0, getWidth(), h); +} + +void KeyMappingEditorComponent::buttonClicked (Button* button) +{ + if (button == resetButton) + { + if (AlertWindow::showOkCancelBox (AlertWindow::QuestionIcon, + TRANS("Reset to defaults"), + TRANS("Are you sure you want to reset all the key-mappings to their default state?"), + TRANS("Reset"))) + { + mappings->resetToDefaultMappings(); + } + } +} + +void KeyMappingEditorComponent::changeListenerCallback (void*) +{ + XmlElement* openness = tree->getOpennessState (true); + + clearSubItems(); + + const StringArray categories (mappings->getCommandManager()->getCommandCategories()); + + for (int i = 0; i < categories.size(); ++i) + { + const Array commands (mappings->getCommandManager()->getCommandsInCategory (categories[i])); + int count = 0; + + for (int j = 0; j < commands.size(); ++j) + if (shouldCommandBeIncluded (commands[j])) + ++count; + + if (count > 0) + addSubItem (new KeyCategoryTreeViewItem (this, categories[i])); + } + + if (openness != 0) + { + tree->restoreOpennessState (*openness); + delete openness; + } +} + +class KeyEntryWindow : public AlertWindow +{ +public: + KeyEntryWindow (KeyMappingEditorComponent* const owner_) + : AlertWindow (TRANS("New key-mapping"), + TRANS("Please press a key combination now..."), + AlertWindow::NoIcon), + owner (owner_) + { + addButton (TRANS("ok"), 1); + addButton (TRANS("cancel"), 0); + + // (avoid return + escape keys getting processed by the buttons..) + for (int i = getNumChildComponents(); --i >= 0;) + getChildComponent (i)->setWantsKeyboardFocus (false); + + setWantsKeyboardFocus (true); + grabKeyboardFocus(); + } + + ~KeyEntryWindow() + { + } + + bool keyPressed (const KeyPress& key) + { + lastPress = key; + String message (TRANS("Key: ") + owner->getDescriptionForKeyPress (key)); + + const CommandID previousCommand = owner->getMappings()->findCommandForKeyPress (key); + + if (previousCommand != 0) + { + message << "\n\n" + << TRANS("(Currently assigned to \"") + << owner->getMappings()->getCommandManager()->getNameOfCommand (previousCommand) + << "\")"; + } + + setMessage (message); + + return true; + } + + bool keyStateChanged() + { + return true; + } + + KeyPress lastPress; + + juce_UseDebuggingNewOperator + +private: + KeyMappingEditorComponent* owner; + + KeyEntryWindow (const KeyEntryWindow&); + const KeyEntryWindow& operator= (const KeyEntryWindow&); +}; + +void KeyMappingEditorComponent::assignNewKey (const CommandID commandID, const int index) +{ + KeyEntryWindow entryWindow (this); + + if (entryWindow.runModalLoop() != 0) + { + entryWindow.setVisible (false); + + if (entryWindow.lastPress.isValid()) + { + const CommandID previousCommand = mappings->findCommandForKeyPress (entryWindow.lastPress); + + if (previousCommand != 0) + { + if (! AlertWindow::showOkCancelBox (AlertWindow::WarningIcon, + TRANS("Change key-mapping"), + TRANS("This key is already assigned to the command \"") + + mappings->getCommandManager()->getNameOfCommand (previousCommand) + + TRANS("\"\n\nDo you want to re-assign it to this new command instead?"), + TRANS("re-assign"), + TRANS("cancel"))) + { + return; + } + } + + mappings->removeKeyPress (entryWindow.lastPress); + + if (index >= 0) + mappings->removeKeyPress (commandID, index); + + mappings->addKeyPress (commandID, entryWindow.lastPress, index); + } + } +} + +bool KeyMappingEditorComponent::shouldCommandBeIncluded (const CommandID commandID) +{ + const ApplicationCommandInfo* const ci = mappings->getCommandManager()->getCommandForID (commandID); + + return (ci != 0) && ((ci->flags & ApplicationCommandInfo::hiddenFromKeyEditor) == 0); +} + +bool KeyMappingEditorComponent::isCommandReadOnly (const CommandID commandID) +{ + const ApplicationCommandInfo* const ci = mappings->getCommandManager()->getCommandForID (commandID); + + return (ci != 0) && ((ci->flags & ApplicationCommandInfo::readOnlyInKeyEditor) != 0); +} + +const String KeyMappingEditorComponent::getDescriptionForKeyPress (const KeyPress& key) +{ + return key.getTextDescription(); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_KeyMappingEditorComponent.cpp *********/ + +/********* Start of inlined file: juce_KeyPress.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +KeyPress::KeyPress() throw() + : keyCode (0), + mods (0), + textCharacter (0) +{ +} + +KeyPress::KeyPress (const int keyCode_, + const ModifierKeys& mods_, + const juce_wchar textCharacter_) throw() + : keyCode (keyCode_), + mods (mods_), + textCharacter (textCharacter_) +{ +} + +KeyPress::KeyPress (const int keyCode_) throw() + : keyCode (keyCode_), + textCharacter (0) +{ +} + +KeyPress::KeyPress (const KeyPress& other) throw() + : keyCode (other.keyCode), + mods (other.mods), + textCharacter (other.textCharacter) +{ +} + +const KeyPress& KeyPress::operator= (const KeyPress& other) throw() +{ + keyCode = other.keyCode; + mods = other.mods; + textCharacter = other.textCharacter; + + return *this; +} + +bool KeyPress::operator== (const KeyPress& other) const throw() +{ + return mods.getRawFlags() == other.mods.getRawFlags() + && (textCharacter == other.textCharacter + || textCharacter == 0 + || other.textCharacter == 0) + && (keyCode == other.keyCode + || (keyCode < 256 + && other.keyCode < 256 + && CharacterFunctions::toLowerCase ((tchar) keyCode) + == CharacterFunctions::toLowerCase ((tchar) other.keyCode))); +} + +bool KeyPress::operator!= (const KeyPress& other) const throw() +{ + return ! operator== (other); +} + +bool KeyPress::isCurrentlyDown() const throw() +{ + return isKeyCurrentlyDown (keyCode) + && (ModifierKeys::getCurrentModifiers().getRawFlags() & ModifierKeys::allKeyboardModifiers) + == (mods.getRawFlags() & ModifierKeys::allKeyboardModifiers); +} + +struct KeyNameAndCode +{ + const char* name; + int code; +}; + +static const KeyNameAndCode keyNameTranslations[] = +{ + { "spacebar", KeyPress::spaceKey }, + { "return", KeyPress::returnKey }, + { "escape", KeyPress::escapeKey }, + { "backspace", KeyPress::backspaceKey }, + { "cursor left", KeyPress::leftKey }, + { "cursor right", KeyPress::rightKey }, + { "cursor up", KeyPress::upKey }, + { "cursor down", KeyPress::downKey }, + { "page up", KeyPress::pageUpKey }, + { "page down", KeyPress::pageDownKey }, + { "home", KeyPress::homeKey }, + { "end", KeyPress::endKey }, + { "delete", KeyPress::deleteKey }, + { "insert", KeyPress::insertKey }, + { "tab", KeyPress::tabKey }, + { "play", KeyPress::playKey }, + { "stop", KeyPress::stopKey }, + { "fast forward", KeyPress::fastForwardKey }, + { "rewind", KeyPress::rewindKey } +}; + +static const tchar* const numberPadPrefix = T("numpad "); + +const KeyPress KeyPress::createFromDescription (const String& desc) throw() +{ + int modifiers = 0; + + if (desc.containsWholeWordIgnoreCase (T("ctrl")) + || desc.containsWholeWordIgnoreCase (T("control")) + || desc.containsWholeWordIgnoreCase (T("ctl"))) + modifiers |= ModifierKeys::ctrlModifier; + + if (desc.containsWholeWordIgnoreCase (T("shift")) + || desc.containsWholeWordIgnoreCase (T("shft"))) + modifiers |= ModifierKeys::shiftModifier; + + if (desc.containsWholeWordIgnoreCase (T("alt")) + || desc.containsWholeWordIgnoreCase (T("option"))) + modifiers |= ModifierKeys::altModifier; + + if (desc.containsWholeWordIgnoreCase (T("command")) + || desc.containsWholeWordIgnoreCase (T("cmd"))) + modifiers |= ModifierKeys::commandModifier; + + int key = 0; + + for (int i = 0; i < numElementsInArray (keyNameTranslations); ++i) + { + if (desc.containsWholeWordIgnoreCase (String (keyNameTranslations[i].name))) + { + key = keyNameTranslations[i].code; + break; + } + } + + if (key == 0) + { + // see if it's a numpad key.. + if (desc.containsIgnoreCase (numberPadPrefix)) + { + const tchar lastChar = desc.trimEnd().getLastCharacter(); + + if (lastChar >= T('0') && lastChar <= T('9')) + key = numberPad0 + lastChar - T('0'); + else if (lastChar == T('+')) + key = numberPadAdd; + else if (lastChar == T('-')) + key = numberPadSubtract; + else if (lastChar == T('*')) + key = numberPadMultiply; + else if (lastChar == T('/')) + key = numberPadDivide; + else if (lastChar == T('.')) + key = numberPadDecimalPoint; + else if (lastChar == T('=')) + key = numberPadEquals; + else if (desc.endsWith (T("separator"))) + key = numberPadSeparator; + else if (desc.endsWith (T("delete"))) + key = numberPadDelete; + } + + if (key == 0) + { + // see if it's a function key.. + for (int i = 1; i <= 12; ++i) + if (desc.containsWholeWordIgnoreCase (T("f") + String (i))) + key = F1Key + i - 1; + + if (key == 0) + { + // give up and use the hex code.. + const int hexCode = desc.fromFirstOccurrenceOf (T("#"), false, false) + .toLowerCase() + .retainCharacters (T("0123456789abcdef")) + .getHexValue32(); + + if (hexCode > 0) + key = hexCode; + else + key = CharacterFunctions::toUpperCase (desc.getLastCharacter()); + } + } + } + + return KeyPress (key, ModifierKeys (modifiers), 0); +} + +const String KeyPress::getTextDescription() const throw() +{ + String desc; + + if (keyCode > 0) + { + // some keyboard layouts use a shift-key to get the slash, but in those cases, we + // want to store it as being a slash, not shift+whatever. + if (textCharacter == T('/')) + return "/"; + + if (mods.isCtrlDown()) + desc << "ctrl + "; + + if (mods.isShiftDown()) + desc << "shift + "; + +#if JUCE_MAC + // only do this on the mac, because on Windows ctrl and command are the same, + // and this would get confusing + if (mods.isCommandDown()) + desc << "command + "; + + if (mods.isAltDown()) + desc << "option + "; +#else + if (mods.isAltDown()) + desc << "alt + "; +#endif + + for (int i = 0; i < numElementsInArray (keyNameTranslations); ++i) + if (keyCode == keyNameTranslations[i].code) + return desc + keyNameTranslations[i].name; + + if (keyCode >= F1Key && keyCode <= F16Key) + desc << 'F' << (1 + keyCode - F1Key); + else if (keyCode >= numberPad0 && keyCode <= numberPad9) + desc << numberPadPrefix << (keyCode - numberPad0); + else if (keyCode >= 33 && keyCode < 176) + desc += CharacterFunctions::toUpperCase ((tchar) keyCode); + else if (keyCode == numberPadAdd) + desc << numberPadPrefix << '+'; + else if (keyCode == numberPadSubtract) + desc << numberPadPrefix << '-'; + else if (keyCode == numberPadMultiply) + desc << numberPadPrefix << '*'; + else if (keyCode == numberPadDivide) + desc << numberPadPrefix << '/'; + else if (keyCode == numberPadSeparator) + desc << numberPadPrefix << "separator"; + else if (keyCode == numberPadDecimalPoint) + desc << numberPadPrefix << '.'; + else if (keyCode == numberPadDelete) + desc << numberPadPrefix << "delete"; + else + desc << '#' << String::toHexString (keyCode); + } + + return desc; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_KeyPress.cpp *********/ + +/********* Start of inlined file: juce_KeyPressMappingSet.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +KeyPressMappingSet::KeyPressMappingSet (ApplicationCommandManager* const commandManager_) throw() + : commandManager (commandManager_) +{ + // A manager is needed to get the descriptions of commands, and will be called when + // a command is invoked. So you can't leave this null.. + jassert (commandManager_ != 0); + + Desktop::getInstance().addFocusChangeListener (this); +} + +KeyPressMappingSet::KeyPressMappingSet (const KeyPressMappingSet& other) throw() + : commandManager (other.commandManager) +{ + Desktop::getInstance().addFocusChangeListener (this); +} + +KeyPressMappingSet::~KeyPressMappingSet() +{ + Desktop::getInstance().removeFocusChangeListener (this); +} + +const Array KeyPressMappingSet::getKeyPressesAssignedToCommand (const CommandID commandID) const throw() +{ + for (int i = 0; i < mappings.size(); ++i) + if (mappings.getUnchecked(i)->commandID == commandID) + return mappings.getUnchecked (i)->keypresses; + + return Array (); +} + +void KeyPressMappingSet::addKeyPress (const CommandID commandID, + const KeyPress& newKeyPress, + int insertIndex) throw() +{ + if (findCommandForKeyPress (newKeyPress) != commandID) + { + removeKeyPress (newKeyPress); + + if (newKeyPress.isValid()) + { + for (int i = mappings.size(); --i >= 0;) + { + if (mappings.getUnchecked(i)->commandID == commandID) + { + mappings.getUnchecked(i)->keypresses.insert (insertIndex, newKeyPress); + + sendChangeMessage (this); + return; + } + } + + const ApplicationCommandInfo* const ci = commandManager->getCommandForID (commandID); + + if (ci != 0) + { + CommandMapping* const cm = new CommandMapping(); + cm->commandID = commandID; + cm->keypresses.add (newKeyPress); + cm->wantsKeyUpDownCallbacks = (ci->flags & ApplicationCommandInfo::wantsKeyUpDownCallbacks) != 0; + + mappings.add (cm); + sendChangeMessage (this); + } + } + } +} + +void KeyPressMappingSet::resetToDefaultMappings() throw() +{ + mappings.clear(); + + for (int i = 0; i < commandManager->getNumCommands(); ++i) + { + const ApplicationCommandInfo* const ci = commandManager->getCommandForIndex (i); + + for (int j = 0; j < ci->defaultKeypresses.size(); ++j) + { + addKeyPress (ci->commandID, + ci->defaultKeypresses.getReference (j)); + } + } + + sendChangeMessage (this); +} + +void KeyPressMappingSet::resetToDefaultMapping (const CommandID commandID) throw() +{ + clearAllKeyPresses (commandID); + + const ApplicationCommandInfo* const ci = commandManager->getCommandForID (commandID); + + for (int j = 0; j < ci->defaultKeypresses.size(); ++j) + { + addKeyPress (ci->commandID, + ci->defaultKeypresses.getReference (j)); + } +} + +void KeyPressMappingSet::clearAllKeyPresses() throw() +{ + if (mappings.size() > 0) + { + sendChangeMessage (this); + mappings.clear(); + } +} + +void KeyPressMappingSet::clearAllKeyPresses (const CommandID commandID) throw() +{ + for (int i = mappings.size(); --i >= 0;) + { + if (mappings.getUnchecked(i)->commandID == commandID) + { + mappings.remove (i); + sendChangeMessage (this); + } + } +} + +void KeyPressMappingSet::removeKeyPress (const KeyPress& keypress) throw() +{ + if (keypress.isValid()) + { + for (int i = mappings.size(); --i >= 0;) + { + CommandMapping* const cm = mappings.getUnchecked(i); + + for (int j = cm->keypresses.size(); --j >= 0;) + { + if (keypress == cm->keypresses [j]) + { + cm->keypresses.remove (j); + sendChangeMessage (this); + } + } + } + } +} + +void KeyPressMappingSet::removeKeyPress (const CommandID commandID, + const int keyPressIndex) throw() +{ + for (int i = mappings.size(); --i >= 0;) + { + if (mappings.getUnchecked(i)->commandID == commandID) + { + mappings.getUnchecked(i)->keypresses.remove (keyPressIndex); + sendChangeMessage (this); + break; + } + } +} + +CommandID KeyPressMappingSet::findCommandForKeyPress (const KeyPress& keyPress) const throw() +{ + for (int i = 0; i < mappings.size(); ++i) + if (mappings.getUnchecked(i)->keypresses.contains (keyPress)) + return mappings.getUnchecked(i)->commandID; + + return 0; +} + +bool KeyPressMappingSet::containsMapping (const CommandID commandID, + const KeyPress& keyPress) const throw() +{ + for (int i = mappings.size(); --i >= 0;) + if (mappings.getUnchecked(i)->commandID == commandID) + return mappings.getUnchecked(i)->keypresses.contains (keyPress); + + return false; +} + +void KeyPressMappingSet::invokeCommand (const CommandID commandID, + const KeyPress& key, + const bool isKeyDown, + const int millisecsSinceKeyPressed, + Component* const originatingComponent) const +{ + ApplicationCommandTarget::InvocationInfo info (commandID); + + info.invocationMethod = ApplicationCommandTarget::InvocationInfo::fromKeyPress; + info.isKeyDown = isKeyDown; + info.keyPress = key; + info.millisecsSinceKeyPressed = millisecsSinceKeyPressed; + info.originatingComponent = originatingComponent; + + commandManager->invoke (info, false); +} + +bool KeyPressMappingSet::restoreFromXml (const XmlElement& xmlVersion) +{ + if (xmlVersion.hasTagName (T("KEYMAPPINGS"))) + { + if (xmlVersion.getBoolAttribute (T("basedOnDefaults"), true)) + { + // if the XML was created as a set of differences from the default mappings, + // (i.e. by calling createXml (true)), then we need to first restore the defaults. + resetToDefaultMappings(); + } + else + { + // if the XML was created calling createXml (false), then we need to clear all + // the keys and treat the xml as describing the entire set of mappings. + clearAllKeyPresses(); + } + + forEachXmlChildElement (xmlVersion, map) + { + const CommandID commandId = map->getStringAttribute (T("commandId")).getHexValue32(); + + if (commandId != 0) + { + const KeyPress key (KeyPress::createFromDescription (map->getStringAttribute (T("key")))); + + if (map->hasTagName (T("MAPPING"))) + { + addKeyPress (commandId, key); + } + else if (map->hasTagName (T("UNMAPPING"))) + { + if (containsMapping (commandId, key)) + removeKeyPress (key); + } + } + } + + return true; + } + + return false; +} + +XmlElement* KeyPressMappingSet::createXml (const bool saveDifferencesFromDefaultSet) const +{ + KeyPressMappingSet* defaultSet = 0; + + if (saveDifferencesFromDefaultSet) + { + defaultSet = new KeyPressMappingSet (commandManager); + defaultSet->resetToDefaultMappings(); + } + + XmlElement* const doc = new XmlElement (T("KEYMAPPINGS")); + + doc->setAttribute (T("basedOnDefaults"), saveDifferencesFromDefaultSet); + + int i; + for (i = 0; i < mappings.size(); ++i) + { + const CommandMapping* const cm = mappings.getUnchecked(i); + + for (int j = 0; j < cm->keypresses.size(); ++j) + { + if (defaultSet == 0 + || ! defaultSet->containsMapping (cm->commandID, cm->keypresses.getReference (j))) + { + XmlElement* const map = new XmlElement (T("MAPPING")); + + map->setAttribute (T("commandId"), String::toHexString ((int) cm->commandID)); + map->setAttribute (T("description"), commandManager->getDescriptionOfCommand (cm->commandID)); + map->setAttribute (T("key"), cm->keypresses.getReference (j).getTextDescription()); + + doc->addChildElement (map); + } + } + } + + if (defaultSet != 0) + { + for (i = 0; i < defaultSet->mappings.size(); ++i) + { + const CommandMapping* const cm = defaultSet->mappings.getUnchecked(i); + + for (int j = 0; j < cm->keypresses.size(); ++j) + { + if (! containsMapping (cm->commandID, cm->keypresses.getReference (j))) + { + XmlElement* const map = new XmlElement (T("UNMAPPING")); + + map->setAttribute (T("commandId"), String::toHexString ((int) cm->commandID)); + map->setAttribute (T("description"), commandManager->getDescriptionOfCommand (cm->commandID)); + map->setAttribute (T("key"), cm->keypresses.getReference (j).getTextDescription()); + + doc->addChildElement (map); + } + } + } + + delete defaultSet; + } + + return doc; +} + +bool KeyPressMappingSet::keyPressed (const KeyPress& key, + Component* originatingComponent) +{ + bool used = false; + + const CommandID commandID = findCommandForKeyPress (key); + + const ApplicationCommandInfo* const ci = commandManager->getCommandForID (commandID); + + if (ci != 0 + && (ci->flags & ApplicationCommandInfo::wantsKeyUpDownCallbacks) == 0) + { + ApplicationCommandInfo info (0); + + if (commandManager->getTargetForCommand (commandID, info) != 0 + && (info.flags & ApplicationCommandInfo::isDisabled) == 0) + { + invokeCommand (commandID, key, true, 0, originatingComponent); + used = true; + } + else + { + if (originatingComponent != 0) + originatingComponent->getLookAndFeel().playAlertSound(); + } + } + + return used; +} + +bool KeyPressMappingSet::keyStateChanged (Component* originatingComponent) +{ + bool used = false; + const uint32 now = Time::getMillisecondCounter(); + + for (int i = mappings.size(); --i >= 0;) + { + CommandMapping* const cm = mappings.getUnchecked(i); + + if (cm->wantsKeyUpDownCallbacks) + { + for (int j = cm->keypresses.size(); --j >= 0;) + { + const KeyPress key (cm->keypresses.getReference (j)); + const bool isDown = key.isCurrentlyDown(); + + int keyPressEntryIndex = 0; + bool wasDown = false; + + for (int k = keysDown.size(); --k >= 0;) + { + if (key == keysDown.getUnchecked(k)->key) + { + keyPressEntryIndex = k; + wasDown = true; + break; + } + } + + if (isDown != wasDown) + { + int millisecs = 0; + + if (isDown) + { + KeyPressTime* const k = new KeyPressTime(); + k->key = key; + k->timeWhenPressed = now; + + keysDown.add (k); + } + else + { + const uint32 pressTime = keysDown.getUnchecked (keyPressEntryIndex)->timeWhenPressed; + + if (now > pressTime) + millisecs = now - pressTime; + + keysDown.remove (keyPressEntryIndex); + } + + invokeCommand (cm->commandID, key, isDown, millisecs, originatingComponent); + used = true; + } + } + } + } + + return used; +} + +void KeyPressMappingSet::globalFocusChanged (Component* focusedComponent) +{ + if (focusedComponent != 0) + focusedComponent->keyStateChanged(); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_KeyPressMappingSet.cpp *********/ + +/********* Start of inlined file: juce_ModifierKeys.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +ModifierKeys::ModifierKeys (const int flags_) throw() + : flags (flags_) +{ +} + +ModifierKeys::ModifierKeys (const ModifierKeys& other) throw() + : flags (other.flags) +{ +} + +const ModifierKeys& ModifierKeys::operator= (const ModifierKeys& other) throw() +{ + flags = other.flags; + return *this; +} + +int ModifierKeys::currentModifierFlags = 0; + +const ModifierKeys ModifierKeys::getCurrentModifiers() throw() +{ + return ModifierKeys (currentModifierFlags); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ModifierKeys.cpp *********/ + +/********* Start of inlined file: juce_ComponentAnimator.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +struct AnimationTask +{ + AnimationTask (Component* const comp) + : component (comp), + watcher (comp) + { + } + + Component* component; + ComponentDeletionWatcher watcher; + Rectangle destination; + int msElapsed, msTotal; + double startSpeed, midSpeed, endSpeed, lastProgress; + double left, top, right, bottom; + + bool useTimeslice (const int elapsed) + { + if (watcher.hasBeenDeleted()) + return false; + + msElapsed += elapsed; + double newProgress = msElapsed / (double) msTotal; + + if (newProgress >= 0 && newProgress < 1.0) + { + newProgress = timeToDistance (newProgress); + const double delta = (newProgress - lastProgress) / (1.0 - lastProgress); + jassert (newProgress >= lastProgress); + lastProgress = newProgress; + + left += (destination.getX() - left) * delta; + top += (destination.getY() - top) * delta; + right += (destination.getRight() - right) * delta; + bottom += (destination.getBottom() - bottom) * delta; + + if (delta < 1.0) + { + const Rectangle newBounds (roundDoubleToInt (left), + roundDoubleToInt (top), + roundDoubleToInt (right - left), + roundDoubleToInt (bottom - top)); + + if (newBounds != destination) + { + component->setBounds (newBounds); + return true; + } + } + } + + component->setBounds (destination); + return false; + } + + void moveToFinalDestination() + { + if (! watcher.hasBeenDeleted()) + component->setBounds (destination); + } + +private: + inline double timeToDistance (const double time) const + { + return (time < 0.5) ? time * (startSpeed + time * (midSpeed - startSpeed)) + : 0.5 * (startSpeed + 0.5 * (midSpeed - startSpeed)) + + (time - 0.5) * (midSpeed + (time - 0.5) * (endSpeed - midSpeed)); + } +}; + +ComponentAnimator::ComponentAnimator() + : lastTime (0) +{ +} + +ComponentAnimator::~ComponentAnimator() +{ + cancelAllAnimations (false); + jassert (tasks.size() == 0); +} + +void* ComponentAnimator::findTaskFor (Component* const component) const +{ + for (int i = tasks.size(); --i >= 0;) + if (component == ((AnimationTask*) tasks.getUnchecked(i))->component) + return tasks.getUnchecked(i); + + return 0; +} + +void ComponentAnimator::animateComponent (Component* const component, + const Rectangle& finalPosition, + const int millisecondsToSpendMoving, + const double startSpeed, + const double endSpeed) +{ + if (component != 0) + { + AnimationTask* at = (AnimationTask*) findTaskFor (component); + + if (at == 0) + { + at = new AnimationTask (component); + tasks.add (at); + } + + at->msElapsed = 0; + at->lastProgress = 0; + at->msTotal = jmax (1, millisecondsToSpendMoving); + at->destination = finalPosition; + + // the speeds must be 0 or greater! + jassert (startSpeed >= 0 && endSpeed >= 0) + + const double invTotalDistance = 4.0 / (startSpeed + endSpeed + 2.0); + at->startSpeed = jmax (0.0, startSpeed * invTotalDistance); + at->midSpeed = invTotalDistance; + at->endSpeed = jmax (0.0, endSpeed * invTotalDistance); + + at->left = component->getX(); + at->top = component->getY(); + at->right = component->getRight(); + at->bottom = component->getBottom(); + + if (! isTimerRunning()) + { + lastTime = Time::getMillisecondCounter(); + startTimer (1000 / 50); + } + } +} + +void ComponentAnimator::cancelAllAnimations (const bool moveComponentsToTheirFinalPositions) +{ + for (int i = tasks.size(); --i >= 0;) + { + AnimationTask* const at = (AnimationTask*) tasks.getUnchecked(i); + + if (moveComponentsToTheirFinalPositions) + at->moveToFinalDestination(); + + delete at; + tasks.remove (i); + } +} + +void ComponentAnimator::cancelAnimation (Component* const component, + const bool moveComponentToItsFinalPosition) +{ + AnimationTask* const at = (AnimationTask*) findTaskFor (component); + + if (at != 0) + { + if (moveComponentToItsFinalPosition) + at->moveToFinalDestination(); + + tasks.removeValue (at); + delete at; + } +} + +const Rectangle ComponentAnimator::getComponentDestination (Component* const component) +{ + AnimationTask* const at = (AnimationTask*) findTaskFor (component); + + if (at != 0) + return at->destination; + else if (component != 0) + return component->getBounds(); + + return Rectangle(); +} + +void ComponentAnimator::timerCallback() +{ + const uint32 timeNow = Time::getMillisecondCounter(); + + if (lastTime == 0 || lastTime == timeNow) + lastTime = timeNow; + + const int elapsed = timeNow - lastTime; + + for (int i = tasks.size(); --i >= 0;) + { + AnimationTask* const at = (AnimationTask*) tasks.getUnchecked(i); + + if (! at->useTimeslice (elapsed)) + { + tasks.remove (i); + delete at; + } + } + + lastTime = timeNow; + + if (tasks.size() == 0) + stopTimer(); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ComponentAnimator.cpp *********/ + +/********* Start of inlined file: juce_ComponentBoundsConstrainer.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +ComponentBoundsConstrainer::ComponentBoundsConstrainer() throw() + : minW (0), + maxW (0x3fffffff), + minH (0), + maxH (0x3fffffff), + minOffTop (0), + minOffLeft (0), + minOffBottom (0), + minOffRight (0), + aspectRatio (0.0) +{ +} + +ComponentBoundsConstrainer::~ComponentBoundsConstrainer() +{ +} + +void ComponentBoundsConstrainer::setMinimumWidth (const int minimumWidth) throw() +{ + minW = minimumWidth; +} + +void ComponentBoundsConstrainer::setMaximumWidth (const int maximumWidth) throw() +{ + maxW = maximumWidth; +} + +void ComponentBoundsConstrainer::setMinimumHeight (const int minimumHeight) throw() +{ + minH = minimumHeight; +} + +void ComponentBoundsConstrainer::setMaximumHeight (const int maximumHeight) throw() +{ + maxH = maximumHeight; +} + +void ComponentBoundsConstrainer::setMinimumSize (const int minimumWidth, const int minimumHeight) throw() +{ + jassert (maxW >= minimumWidth); + jassert (maxH >= minimumHeight); + jassert (minimumWidth > 0 && minimumHeight > 0); + + minW = minimumWidth; + minH = minimumHeight; + + if (minW > maxW) + maxW = minW; + + if (minH > maxH) + maxH = minH; +} + +void ComponentBoundsConstrainer::setMaximumSize (const int maximumWidth, const int maximumHeight) throw() +{ + jassert (maximumWidth >= minW); + jassert (maximumHeight >= minH); + jassert (maximumWidth > 0 && maximumHeight > 0); + + maxW = jmax (minW, maximumWidth); + maxH = jmax (minH, maximumHeight); +} + +void ComponentBoundsConstrainer::setSizeLimits (const int minimumWidth, + const int minimumHeight, + const int maximumWidth, + const int maximumHeight) throw() +{ + jassert (maximumWidth >= minimumWidth); + jassert (maximumHeight >= minimumHeight); + jassert (maximumWidth > 0 && maximumHeight > 0); + jassert (minimumWidth > 0 && minimumHeight > 0); + + minW = jmax (0, minimumWidth); + minH = jmax (0, minimumHeight); + maxW = jmax (minW, maximumWidth); + maxH = jmax (minH, maximumHeight); +} + +void ComponentBoundsConstrainer::setMinimumOnscreenAmounts (const int minimumWhenOffTheTop, + const int minimumWhenOffTheLeft, + const int minimumWhenOffTheBottom, + const int minimumWhenOffTheRight) throw() +{ + minOffTop = minimumWhenOffTheTop; + minOffLeft = minimumWhenOffTheLeft; + minOffBottom = minimumWhenOffTheBottom; + minOffRight = minimumWhenOffTheRight; +} + +void ComponentBoundsConstrainer::setFixedAspectRatio (const double widthOverHeight) throw() +{ + aspectRatio = jmax (0.0, widthOverHeight); +} + +double ComponentBoundsConstrainer::getFixedAspectRatio() const throw() +{ + return aspectRatio; +} + +void ComponentBoundsConstrainer::setBoundsForComponent (Component* const component, + int x, int y, int w, int h, + const bool isStretchingTop, + const bool isStretchingLeft, + const bool isStretchingBottom, + const bool isStretchingRight) +{ + jassert (component != 0); + + Rectangle limits; + Component* const p = component->getParentComponent(); + + if (p == 0) + limits = Desktop::getInstance().getAllMonitorDisplayAreas().getBounds(); + else + limits.setSize (p->getWidth(), p->getHeight()); + + if (component->isOnDesktop()) + { + ComponentPeer* const peer = component->getPeer(); + const BorderSize border (peer->getFrameSize()); + + x -= border.getLeft(); + y -= border.getTop(); + w += border.getLeftAndRight(); + h += border.getTopAndBottom(); + + checkBounds (x, y, w, h, + border.addedTo (component->getBounds()), limits, + isStretchingTop, isStretchingLeft, + isStretchingBottom, isStretchingRight); + + x += border.getLeft(); + y += border.getTop(); + w -= border.getLeftAndRight(); + h -= border.getTopAndBottom(); + } + else + { + checkBounds (x, y, w, h, + component->getBounds(), limits, + isStretchingTop, isStretchingLeft, + isStretchingBottom, isStretchingRight); + } + + applyBoundsToComponent (component, x, y, w, h); +} + +void ComponentBoundsConstrainer::applyBoundsToComponent (Component* component, + int x, int y, int w, int h) +{ + component->setBounds (x, y, w, h); +} + +void ComponentBoundsConstrainer::resizeStart() +{ +} + +void ComponentBoundsConstrainer::resizeEnd() +{ +} + +void ComponentBoundsConstrainer::checkBounds (int& x, int& y, int& w, int& h, + const Rectangle& old, + const Rectangle& limits, + const bool isStretchingTop, + const bool isStretchingLeft, + const bool isStretchingBottom, + const bool isStretchingRight) +{ + // constrain the size if it's being stretched.. + if (isStretchingLeft) + { + x = jlimit (old.getRight() - maxW, old.getRight() - minW, x); + w = old.getRight() - x; + } + + if (isStretchingRight) + { + w = jlimit (minW, maxW, w); + } + + if (isStretchingTop) + { + y = jlimit (old.getBottom() - maxH, old.getBottom() - minH, y); + h = old.getBottom() - y; + } + + if (isStretchingBottom) + { + h = jlimit (minH, maxH, h); + } + + // constrain the aspect ratio if one has been specified.. + if (aspectRatio > 0.0 && w > 0 && h > 0) + { + bool adjustWidth; + + if ((isStretchingTop || isStretchingBottom) && ! (isStretchingLeft || isStretchingRight)) + { + adjustWidth = true; + } + else if ((isStretchingLeft || isStretchingRight) && ! (isStretchingTop || isStretchingBottom)) + { + adjustWidth = false; + } + else + { + const double oldRatio = (old.getHeight() > 0) ? fabs (old.getWidth() / (double) old.getHeight()) : 0.0; + const double newRatio = fabs (w / (double) h); + + adjustWidth = (oldRatio > newRatio); + } + + if (adjustWidth) + { + w = roundDoubleToInt (h * aspectRatio); + + if (w > maxW || w < minW) + { + w = jlimit (minW, maxW, w); + h = roundDoubleToInt (w / aspectRatio); + } + } + else + { + h = roundDoubleToInt (w / aspectRatio); + + if (h > maxH || h < minH) + { + h = jlimit (minH, maxH, h); + w = roundDoubleToInt (h * aspectRatio); + } + } + + if ((isStretchingTop || isStretchingBottom) && ! (isStretchingLeft || isStretchingRight)) + { + x = old.getX() + (old.getWidth() - w) / 2; + } + else if ((isStretchingLeft || isStretchingRight) && ! (isStretchingTop || isStretchingBottom)) + { + y = old.getY() + (old.getHeight() - h) / 2; + } + else + { + if (isStretchingLeft) + x = old.getRight() - w; + + if (isStretchingTop) + y = old.getBottom() - h; + } + } + + // ...and constrain the position if limits have been set for that. + if (minOffTop > 0 || minOffLeft > 0 || minOffBottom > 0 || minOffRight > 0) + { + if (minOffTop > 0) + { + const int limit = limits.getY() + jmin (minOffTop - h, 0); + + if (y < limit) + { + if (isStretchingTop) + h -= (limit - y); + + y = limit; + } + } + + if (minOffLeft > 0) + { + const int limit = limits.getX() + jmin (minOffLeft - w, 0); + + if (x < limit) + { + if (isStretchingLeft) + w -= (limit - x); + + x = limit; + } + } + + if (minOffBottom > 0) + { + const int limit = limits.getBottom() - jmin (minOffBottom, h); + + if (y > limit) + { + if (isStretchingBottom) + h += (limit - y); + else + y = limit; + } + } + + if (minOffRight > 0) + { + const int limit = limits.getRight() - jmin (minOffRight, w); + + if (x > limit) + { + if (isStretchingRight) + w += (limit - x); + else + x = limit; + } + } + } + + jassert (w >= 0 && h >= 0); + +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ComponentBoundsConstrainer.cpp *********/ + +/********* Start of inlined file: juce_ComponentMovementWatcher.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +ComponentMovementWatcher::ComponentMovementWatcher (Component* const component_) + : component (component_), + lastPeer (0), + registeredParentComps (4), + reentrant (false) +{ + jassert (component != 0); // can't use this with a null pointer.. + +#ifdef JUCE_DEBUG + deletionWatcher = new ComponentDeletionWatcher (component_); +#endif + + component->addComponentListener (this); + + registerWithParentComps(); +} + +ComponentMovementWatcher::~ComponentMovementWatcher() +{ + component->removeComponentListener (this); + + unregister(); + +#ifdef JUCE_DEBUG + delete deletionWatcher; +#endif +} + +void ComponentMovementWatcher::componentParentHierarchyChanged (Component&) +{ +#ifdef JUCE_DEBUG + // agh! don't delete the target component without deleting this object first! + jassert (! deletionWatcher->hasBeenDeleted()); +#endif + + if (! reentrant) + { + reentrant = true; + + ComponentPeer* const peer = component->getPeer(); + + if (peer != lastPeer) + { + ComponentDeletionWatcher watcher (component); + + componentPeerChanged(); + + if (watcher.hasBeenDeleted()) + return; + + lastPeer = peer; + } + + unregister(); + registerWithParentComps(); + + reentrant = false; + + componentMovedOrResized (*component, true, true); + } +} + +void ComponentMovementWatcher::componentMovedOrResized (Component&, bool wasMoved, bool wasResized) +{ +#ifdef JUCE_DEBUG + // agh! don't delete the target component without deleting this object first! + jassert (! deletionWatcher->hasBeenDeleted()); +#endif + + if (wasMoved) + { + int x = 0, y = 0; + component->relativePositionToOtherComponent (component->getTopLevelComponent(), x, y); + + wasMoved = (lastX != x || lastY != y); + lastX = x; + lastY = y; + } + + wasResized = (lastWidth != component->getWidth() || lastHeight != component->getHeight()); + lastWidth = component->getWidth(); + lastHeight = component->getHeight(); + + if (wasMoved || wasResized) + componentMovedOrResized (wasMoved, wasResized); +} + +void ComponentMovementWatcher::registerWithParentComps() throw() +{ + Component* p = component->getParentComponent(); + + while (p != 0) + { + p->addComponentListener (this); + registeredParentComps.add (p); + p = p->getParentComponent(); + } +} + +void ComponentMovementWatcher::unregister() throw() +{ + for (int i = registeredParentComps.size(); --i >= 0;) + ((Component*) registeredParentComps.getUnchecked(i))->removeComponentListener (this); + + registeredParentComps.clear(); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ComponentMovementWatcher.cpp *********/ + +/********* Start of inlined file: juce_GroupComponent.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +GroupComponent::GroupComponent (const String& componentName, + const String& labelText) + : Component (componentName), + text (labelText), + justification (Justification::left) +{ + setInterceptsMouseClicks (false, true); +} + +GroupComponent::~GroupComponent() +{ +} + +void GroupComponent::setText (const String& newText) throw() +{ + if (text != newText) + { + text = newText; + repaint(); + } +} + +const String GroupComponent::getText() const throw() +{ + return text; +} + +void GroupComponent::setTextLabelPosition (const Justification& newJustification) +{ + if (justification.getFlags() != newJustification.getFlags()) + { + justification = newJustification; + repaint(); + } +} + +void GroupComponent::paint (Graphics& g) +{ + getLookAndFeel() + .drawGroupComponentOutline (g, getWidth(), getHeight(), + text, justification, + *this); +} + +void GroupComponent::enablementChanged() +{ + repaint(); +} + +void GroupComponent::colourChanged() +{ + repaint(); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_GroupComponent.cpp *********/ + +/********* Start of inlined file: juce_MultiDocumentPanel.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +MultiDocumentPanelWindow::MultiDocumentPanelWindow (const Colour& backgroundColour) + : DocumentWindow (String::empty, backgroundColour, + DocumentWindow::maximiseButton | DocumentWindow::closeButton, false) +{ +} + +MultiDocumentPanelWindow::~MultiDocumentPanelWindow() +{ +} + +void MultiDocumentPanelWindow::maximiseButtonPressed() +{ + MultiDocumentPanel* const owner = getOwner(); + + jassert (owner != 0); // these windows are only designed to be used inside a MultiDocumentPanel! + if (owner != 0) + owner->setLayoutMode (MultiDocumentPanel::MaximisedWindowsWithTabs); +} + +void MultiDocumentPanelWindow::closeButtonPressed() +{ + MultiDocumentPanel* const owner = getOwner(); + + jassert (owner != 0); // these windows are only designed to be used inside a MultiDocumentPanel! + if (owner != 0) + owner->closeDocument (getContentComponent(), true); +} + +void MultiDocumentPanelWindow::activeWindowStatusChanged() +{ + DocumentWindow::activeWindowStatusChanged(); + updateOrder(); +} + +void MultiDocumentPanelWindow::broughtToFront() +{ + DocumentWindow::broughtToFront(); + updateOrder(); +} + +void MultiDocumentPanelWindow::updateOrder() +{ + MultiDocumentPanel* const owner = getOwner(); + + if (owner != 0) + owner->updateOrder(); +} + +MultiDocumentPanel* MultiDocumentPanelWindow::getOwner() const throw() +{ + // (unable to use the syntax findParentComponentOfClass () because of a VC6 compiler bug) + return findParentComponentOfClass ((MultiDocumentPanel*) 0); +} + +class MDITabbedComponentInternal : public TabbedComponent +{ +public: + MDITabbedComponentInternal() + : TabbedComponent (TabbedButtonBar::TabsAtTop) + { + } + + ~MDITabbedComponentInternal() + { + } + + void currentTabChanged (const int, const String&) + { + // (unable to use the syntax findParentComponentOfClass () because of a VC6 compiler bug) + MultiDocumentPanel* const owner = findParentComponentOfClass ((MultiDocumentPanel*) 0); + + if (owner != 0) + owner->updateOrder(); + } +}; + +MultiDocumentPanel::MultiDocumentPanel() + : mode (MaximisedWindowsWithTabs), + tabComponent (0), + backgroundColour (Colours::lightblue), + maximumNumDocuments (0), + numDocsBeforeTabsUsed (0) +{ + setOpaque (true); +} + +MultiDocumentPanel::~MultiDocumentPanel() +{ + closeAllDocuments (false); +} + +static bool shouldDeleteComp (Component* const c) +{ + return c->getComponentPropertyBool (T("mdiDocumentDelete_"), false); +} + +bool MultiDocumentPanel::closeAllDocuments (const bool checkItsOkToCloseFirst) +{ + while (components.size() > 0) + if (! closeDocument (components.getLast(), checkItsOkToCloseFirst)) + return false; + + return true; +} + +MultiDocumentPanelWindow* MultiDocumentPanel::createNewDocumentWindow() +{ + return new MultiDocumentPanelWindow (backgroundColour); +} + +void MultiDocumentPanel::addWindow (Component* component) +{ + MultiDocumentPanelWindow* const dw = createNewDocumentWindow(); + + dw->setResizable (true, false); + dw->setContentComponent (component, false, true); + dw->setName (component->getName()); + dw->setBackgroundColour (component->getComponentPropertyColour (T("mdiDocumentBkg_"), false, backgroundColour)); + + int x = 4; + Component* const topComp = getChildComponent (getNumChildComponents() - 1); + + if (topComp != 0 && topComp->getX() == x && topComp->getY() == x) + x += 16; + + dw->setTopLeftPosition (x, x); + + if (component->getComponentProperty (T("mdiDocumentPos_"), false, String::empty).isNotEmpty()) + dw->restoreWindowStateFromString (component->getComponentProperty (T("mdiDocumentPos_"), false, String::empty)); + + addAndMakeVisible (dw); + dw->toFront (true); +} + +bool MultiDocumentPanel::addDocument (Component* const component, + const Colour& backgroundColour, + const bool deleteWhenRemoved) +{ + // If you try passing a full DocumentWindow or ResizableWindow in here, you'll end up + // with a frame-within-a-frame! Just pass in the bare content component. + jassert (dynamic_cast (component) == 0); + + if (component == 0 || (maximumNumDocuments > 0 && components.size() >= maximumNumDocuments)) + return false; + + components.add (component); + component->setComponentProperty (T("mdiDocumentDelete_"), deleteWhenRemoved); + component->setComponentProperty (T("mdiDocumentBkg_"), backgroundColour); + component->addComponentListener (this); + + if (mode == FloatingWindows) + { + if (isFullscreenWhenOneDocument()) + { + if (components.size() == 1) + { + addAndMakeVisible (component); + } + else + { + if (components.size() == 2) + addWindow (components.getFirst()); + + addWindow (component); + } + } + else + { + addWindow (component); + } + } + else + { + if (tabComponent == 0 && components.size() > numDocsBeforeTabsUsed) + { + addAndMakeVisible (tabComponent = new MDITabbedComponentInternal()); + + Array temp (components); + + for (int i = 0; i < temp.size(); ++i) + tabComponent->addTab (temp[i]->getName(), backgroundColour, temp[i], false); + + resized(); + } + else + { + if (tabComponent != 0) + tabComponent->addTab (component->getName(), backgroundColour, component, false); + else + addAndMakeVisible (component); + } + + setActiveDocument (component); + } + + resized(); + activeDocumentChanged(); + return true; +} + +bool MultiDocumentPanel::closeDocument (Component* component, + const bool checkItsOkToCloseFirst) +{ + if (components.contains (component)) + { + if (checkItsOkToCloseFirst && ! tryToCloseDocument (component)) + return false; + + component->removeComponentListener (this); + + const bool shouldDelete = shouldDeleteComp (component); + component->removeComponentProperty (T("mdiDocumentDelete_")); + component->removeComponentProperty (T("mdiDocumentBkg_")); + + if (mode == FloatingWindows) + { + for (int i = getNumChildComponents(); --i >= 0;) + { + MultiDocumentPanelWindow* const dw = dynamic_cast (getChildComponent (i)); + + if (dw != 0 && dw->getContentComponent() == component) + { + dw->setContentComponent (0, false); + delete dw; + break; + } + } + + if (shouldDelete) + delete component; + + components.removeValue (component); + + if (isFullscreenWhenOneDocument() && components.size() == 1) + { + for (int i = getNumChildComponents(); --i >= 0;) + { + MultiDocumentPanelWindow* const dw = dynamic_cast (getChildComponent (i)); + + if (dw != 0) + { + dw->setContentComponent (0, false); + delete dw; + } + } + + addAndMakeVisible (components.getFirst()); + } + } + else + { + jassert (components.indexOf (component) >= 0); + + if (tabComponent != 0) + { + for (int i = tabComponent->getNumTabs(); --i >= 0;) + if (tabComponent->getTabContentComponent (i) == component) + tabComponent->removeTab (i); + } + else + { + removeChildComponent (component); + } + + if (shouldDelete) + delete component; + + if (tabComponent != 0 && tabComponent->getNumTabs() <= numDocsBeforeTabsUsed) + deleteAndZero (tabComponent); + + components.removeValue (component); + + if (components.size() > 0 && tabComponent == 0) + addAndMakeVisible (components.getFirst()); + } + + resized(); + activeDocumentChanged(); + } + else + { + jassertfalse + } + + return true; +} + +int MultiDocumentPanel::getNumDocuments() const throw() +{ + return components.size(); +} + +Component* MultiDocumentPanel::getDocument (const int index) const throw() +{ + return components [index]; +} + +Component* MultiDocumentPanel::getActiveDocument() const throw() +{ + if (mode == FloatingWindows) + { + for (int i = getNumChildComponents(); --i >= 0;) + { + MultiDocumentPanelWindow* const dw = dynamic_cast (getChildComponent (i)); + + if (dw != 0 && dw->isActiveWindow()) + return dw->getContentComponent(); + } + } + + return components.getLast(); +} + +void MultiDocumentPanel::setActiveDocument (Component* component) +{ + if (mode == FloatingWindows) + { + component = getContainerComp (component); + + if (component != 0) + component->toFront (true); + } + else if (tabComponent != 0) + { + jassert (components.indexOf (component) >= 0); + + for (int i = tabComponent->getNumTabs(); --i >= 0;) + { + if (tabComponent->getTabContentComponent (i) == component) + { + tabComponent->setCurrentTabIndex (i); + break; + } + } + } + else + { + component->grabKeyboardFocus(); + } +} + +void MultiDocumentPanel::activeDocumentChanged() +{ +} + +void MultiDocumentPanel::setMaximumNumDocuments (const int newNumber) +{ + maximumNumDocuments = newNumber; +} + +void MultiDocumentPanel::useFullscreenWhenOneDocument (const bool shouldUseTabs) +{ + numDocsBeforeTabsUsed = shouldUseTabs ? 1 : 0; +} + +bool MultiDocumentPanel::isFullscreenWhenOneDocument() const throw() +{ + return numDocsBeforeTabsUsed != 0; +} + +void MultiDocumentPanel::setLayoutMode (const LayoutMode newLayoutMode) +{ + if (mode != newLayoutMode) + { + mode = newLayoutMode; + + if (mode == FloatingWindows) + { + deleteAndZero (tabComponent); + } + else + { + for (int i = getNumChildComponents(); --i >= 0;) + { + MultiDocumentPanelWindow* const dw = dynamic_cast (getChildComponent (i)); + + if (dw != 0) + { + dw->getContentComponent()->setComponentProperty (T("mdiDocumentPos_"), dw->getWindowStateAsString()); + dw->setContentComponent (0, false); + delete dw; + } + } + } + + resized(); + + const Array tempComps (components); + components.clear(); + + for (int i = 0; i < tempComps.size(); ++i) + { + Component* const c = tempComps.getUnchecked(i); + addDocument (c, + c->getComponentPropertyColour (T("mdiDocumentBkg_"), false, Colours::white), + shouldDeleteComp (c)); + } + } +} + +void MultiDocumentPanel::setBackgroundColour (const Colour& newBackgroundColour) +{ + if (backgroundColour != newBackgroundColour) + { + backgroundColour = newBackgroundColour; + setOpaque (newBackgroundColour.isOpaque()); + repaint(); + } +} + +void MultiDocumentPanel::paint (Graphics& g) +{ + g.fillAll (backgroundColour); +} + +void MultiDocumentPanel::resized() +{ + if (mode == MaximisedWindowsWithTabs || components.size() == numDocsBeforeTabsUsed) + { + for (int i = getNumChildComponents(); --i >= 0;) + getChildComponent (i)->setBounds (0, 0, getWidth(), getHeight()); + } + + setWantsKeyboardFocus (components.size() == 0); +} + +Component* MultiDocumentPanel::getContainerComp (Component* c) const +{ + if (mode == FloatingWindows) + { + for (int i = 0; i < getNumChildComponents(); ++i) + { + MultiDocumentPanelWindow* const dw = dynamic_cast (getChildComponent (i)); + + if (dw != 0 && dw->getContentComponent() == c) + { + c = dw; + break; + } + } + } + + return c; +} + +void MultiDocumentPanel::componentNameChanged (Component&) +{ + if (mode == FloatingWindows) + { + for (int i = 0; i < getNumChildComponents(); ++i) + { + MultiDocumentPanelWindow* const dw = dynamic_cast (getChildComponent (i)); + + if (dw != 0) + dw->setName (dw->getContentComponent()->getName()); + } + } + else if (tabComponent != 0) + { + for (int i = tabComponent->getNumTabs(); --i >= 0;) + tabComponent->setTabName (i, tabComponent->getTabContentComponent (i)->getName()); + } +} + +void MultiDocumentPanel::updateOrder() +{ + const Array oldList (components); + + if (mode == FloatingWindows) + { + components.clear(); + + for (int i = 0; i < getNumChildComponents(); ++i) + { + MultiDocumentPanelWindow* const dw = dynamic_cast (getChildComponent (i)); + + if (dw != 0) + components.add (dw->getContentComponent()); + } + } + else + { + if (tabComponent != 0) + { + Component* const current = tabComponent->getCurrentContentComponent(); + + if (current != 0) + { + components.removeValue (current); + components.add (current); + } + } + } + + if (components != oldList) + activeDocumentChanged(); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_MultiDocumentPanel.cpp *********/ + +/********* Start of inlined file: juce_ResizableBorderComponent.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +const int zoneL = 1; +const int zoneR = 2; +const int zoneT = 4; +const int zoneB = 8; + +ResizableBorderComponent::ResizableBorderComponent (Component* const componentToResize, + ComponentBoundsConstrainer* const constrainer_) + : component (componentToResize), + constrainer (constrainer_), + borderSize (5), + mouseZone (0) +{ +} + +ResizableBorderComponent::~ResizableBorderComponent() +{ +} + +void ResizableBorderComponent::paint (Graphics& g) +{ + getLookAndFeel().drawResizableFrame (g, getWidth(), getHeight(), borderSize); +} + +void ResizableBorderComponent::mouseEnter (const MouseEvent& e) +{ + updateMouseZone (e); +} + +void ResizableBorderComponent::mouseMove (const MouseEvent& e) +{ + updateMouseZone (e); +} + +void ResizableBorderComponent::mouseDown (const MouseEvent& e) +{ + if (component->isValidComponent()) + { + updateMouseZone (e); + + originalX = component->getX(); + originalY = component->getY(); + originalW = component->getWidth(); + originalH = component->getHeight(); + + if (constrainer != 0) + constrainer->resizeStart(); + } + else + { + jassertfalse + } +} + +void ResizableBorderComponent::mouseDrag (const MouseEvent& e) +{ + if (! component->isValidComponent()) + { + jassertfalse + return; + } + + int x = originalX; + int y = originalY; + int w = originalW; + int h = originalH; + + const int dx = e.getDistanceFromDragStartX(); + const int dy = e.getDistanceFromDragStartY(); + + if ((mouseZone & zoneL) != 0) + { + x += dx; + w -= dx; + } + + if ((mouseZone & zoneT) != 0) + { + y += dy; + h -= dy; + } + + if ((mouseZone & zoneR) != 0) + w += dx; + + if ((mouseZone & zoneB) != 0) + h += dy; + + if (constrainer != 0) + constrainer->setBoundsForComponent (component, + x, y, w, h, + (mouseZone & zoneT) != 0, + (mouseZone & zoneL) != 0, + (mouseZone & zoneB) != 0, + (mouseZone & zoneR) != 0); + else + component->setBounds (x, y, w, h); +} + +void ResizableBorderComponent::mouseUp (const MouseEvent&) +{ + if (constrainer != 0) + constrainer->resizeEnd(); +} + +bool ResizableBorderComponent::hitTest (int x, int y) +{ + return x < borderSize.getLeft() + || x >= getWidth() - borderSize.getRight() + || y < borderSize.getTop() + || y >= getHeight() - borderSize.getBottom(); +} + +void ResizableBorderComponent::setBorderThickness (const BorderSize& newBorderSize) throw() +{ + if (borderSize != newBorderSize) + { + borderSize = newBorderSize; + repaint(); + } +} + +const BorderSize ResizableBorderComponent::getBorderThickness() const throw() +{ + return borderSize; +} + +void ResizableBorderComponent::updateMouseZone (const MouseEvent& e) throw() +{ + int newZone = 0; + + if (ResizableBorderComponent::hitTest (e.x, e.y)) + { + if (e.x < jmax (borderSize.getLeft(), proportionOfWidth (0.1f))) + newZone |= zoneL; + else if (e.x >= jmin (getWidth() - borderSize.getRight(), proportionOfWidth (0.9f))) + newZone |= zoneR; + + if (e.y < jmax (borderSize.getTop(), proportionOfHeight (0.1f))) + newZone |= zoneT; + else if (e.y >= jmin (getHeight() - borderSize.getBottom(), proportionOfHeight (0.9f))) + newZone |= zoneB; + } + + if (mouseZone != newZone) + { + mouseZone = newZone; + + MouseCursor::StandardCursorType mc = MouseCursor::NormalCursor; + + switch (newZone) + { + case (zoneL | zoneT): + mc = MouseCursor::TopLeftCornerResizeCursor; + break; + + case zoneT: + mc = MouseCursor::TopEdgeResizeCursor; + break; + + case (zoneR | zoneT): + mc = MouseCursor::TopRightCornerResizeCursor; + break; + + case zoneL: + mc = MouseCursor::LeftEdgeResizeCursor; + break; + + case zoneR: + mc = MouseCursor::RightEdgeResizeCursor; + break; + + case (zoneL | zoneB): + mc = MouseCursor::BottomLeftCornerResizeCursor; + break; + + case zoneB: + mc = MouseCursor::BottomEdgeResizeCursor; + break; + + case (zoneR | zoneB): + mc = MouseCursor::BottomRightCornerResizeCursor; + break; + + default: + break; + } + + setMouseCursor (mc); + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ResizableBorderComponent.cpp *********/ + +/********* Start of inlined file: juce_ResizableCornerComponent.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +ResizableCornerComponent::ResizableCornerComponent (Component* const componentToResize, + ComponentBoundsConstrainer* const constrainer_) + : component (componentToResize), + constrainer (constrainer_) +{ + setRepaintsOnMouseActivity (true); + setMouseCursor (MouseCursor::BottomRightCornerResizeCursor); +} + +ResizableCornerComponent::~ResizableCornerComponent() +{ +} + +void ResizableCornerComponent::paint (Graphics& g) +{ + getLookAndFeel() + .drawCornerResizer (g, getWidth(), getHeight(), + isMouseOverOrDragging(), + isMouseButtonDown()); +} + +void ResizableCornerComponent::mouseDown (const MouseEvent&) +{ + if (component->isValidComponent()) + { + originalX = component->getX(); + originalY = component->getY(); + originalW = component->getWidth(); + originalH = component->getHeight(); + + if (constrainer != 0) + constrainer->resizeStart(); + } + else + { + jassertfalse + } +} + +void ResizableCornerComponent::mouseDrag (const MouseEvent& e) +{ + if (! component->isValidComponent()) + { + jassertfalse + return; + } + + int x = originalX; + int y = originalY; + int w = originalW + e.getDistanceFromDragStartX(); + int h = originalH + e.getDistanceFromDragStartY(); + + if (constrainer != 0) + constrainer->setBoundsForComponent (component, x, y, w, h, + false, false, true, true); + else + component->setBounds (x, y, w, h); +} + +void ResizableCornerComponent::mouseUp (const MouseEvent&) +{ + if (constrainer != 0) + constrainer->resizeStart(); +} + +bool ResizableCornerComponent::hitTest (int x, int y) +{ + if (getWidth() <= 0) + return false; + + const int yAtX = getHeight() - (getHeight() * x / getWidth()); + + return y >= yAtX - getHeight() / 4; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ResizableCornerComponent.cpp *********/ + +/********* Start of inlined file: juce_ScrollBar.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +class ScrollbarButton : public Button +{ +public: + int direction; + + ScrollbarButton (const int direction_, + ScrollBar& owner_) throw() + : Button (String::empty), + direction (direction_), + owner (owner_) + { + setWantsKeyboardFocus (false); + } + + ~ScrollbarButton() + { + } + + void paintButton (Graphics& g, + bool isMouseOver, + bool isMouseDown) + { + getLookAndFeel() + .drawScrollbarButton (g, owner, + getWidth(), getHeight(), + direction, + owner.isVertical(), + isMouseOver, isMouseDown); + } + + void clicked() + { + owner.moveScrollbarInSteps ((direction == 1 || direction == 2) ? 1 : -1); + } + + juce_UseDebuggingNewOperator + +private: + ScrollBar& owner; + + ScrollbarButton (const ScrollbarButton&); + const ScrollbarButton& operator= (const ScrollbarButton&); +}; + +ScrollBar::ScrollBar (const bool vertical_, + const bool buttonsAreVisible) + : minimum (0.0), + maximum (1.0), + rangeStart (0.0), + rangeSize (0.1), + singleStepSize (0.1), + thumbAreaStart (0), + thumbAreaSize (0), + thumbStart (0), + thumbSize (0), + initialDelayInMillisecs (100), + repeatDelayInMillisecs (50), + minimumDelayInMillisecs (10), + vertical (vertical_), + isDraggingThumb (false), + alwaysVisible (false), + upButton (0), + downButton (0), + listeners (2) +{ + setButtonVisibility (buttonsAreVisible); + + setRepaintsOnMouseActivity (true); + setFocusContainer (true); +} + +ScrollBar::~ScrollBar() +{ + deleteAllChildren(); +} + +void ScrollBar::setRangeLimits (const double newMinimum, + const double newMaximum) throw() +{ + minimum = newMinimum; + maximum = newMaximum; + + jassert (maximum >= minimum); // these can't be the wrong way round! + + setCurrentRangeStart (rangeStart); + updateThumbPosition(); +} + +void ScrollBar::setCurrentRange (double newStart, + double newSize) throw() +{ + newSize = jlimit (0.0, maximum - minimum, newSize); + newStart = jlimit (minimum, maximum - newSize, newStart); + + if (rangeStart != newStart + || rangeSize != newSize) + { + rangeStart = newStart; + rangeSize = newSize; + + updateThumbPosition(); + triggerAsyncUpdate(); + } +} + +void ScrollBar::setCurrentRangeStart (double newStart) throw() +{ + setCurrentRange (newStart, rangeSize); +} + +void ScrollBar::setSingleStepSize (const double newSingleStepSize) throw() +{ + singleStepSize = newSingleStepSize; +} + +void ScrollBar::moveScrollbarInSteps (const int howManySteps) throw() +{ + setCurrentRangeStart (rangeStart + howManySteps * singleStepSize); +} + +void ScrollBar::moveScrollbarInPages (const int howManyPages) throw() +{ + setCurrentRangeStart (rangeStart + howManyPages * rangeSize); +} + +void ScrollBar::scrollToTop() throw() +{ + setCurrentRangeStart (minimum); +} + +void ScrollBar::scrollToBottom() throw() +{ + setCurrentRangeStart (maximum - rangeSize); +} + +void ScrollBar::setButtonRepeatSpeed (const int initialDelayInMillisecs_, + const int repeatDelayInMillisecs_, + const int minimumDelayInMillisecs_) throw() +{ + initialDelayInMillisecs = initialDelayInMillisecs_; + repeatDelayInMillisecs = repeatDelayInMillisecs_; + minimumDelayInMillisecs = minimumDelayInMillisecs_; + + if (upButton != 0) + { + upButton->setRepeatSpeed (initialDelayInMillisecs, repeatDelayInMillisecs, minimumDelayInMillisecs); + downButton->setRepeatSpeed (initialDelayInMillisecs, repeatDelayInMillisecs, minimumDelayInMillisecs); + } +} + +void ScrollBar::addListener (ScrollBarListener* const listener) throw() +{ + jassert (listener != 0); + if (listener != 0) + listeners.add (listener); +} + +void ScrollBar::removeListener (ScrollBarListener* const listener) throw() +{ + listeners.removeValue (listener); +} + +void ScrollBar::handleAsyncUpdate() +{ + const double value = getCurrentRangeStart(); + + for (int i = listeners.size(); --i >= 0;) + { + ((ScrollBarListener*) listeners.getUnchecked (i))->scrollBarMoved (this, value); + i = jmin (i, listeners.size()); + } +} + +void ScrollBar::updateThumbPosition() throw() +{ + int newThumbSize = roundDoubleToInt ((maximum > minimum) ? (rangeSize * thumbAreaSize) / (maximum - minimum) + : thumbAreaSize); + + if (newThumbSize < getLookAndFeel().getMinimumScrollbarThumbSize (*this)) + newThumbSize = jmin (getLookAndFeel().getMinimumScrollbarThumbSize (*this), thumbAreaSize - 1); + + if (newThumbSize > thumbAreaSize) + newThumbSize = thumbAreaSize; + + int newThumbStart = thumbAreaStart; + + if (maximum - minimum > rangeSize) + newThumbStart += roundDoubleToInt (((rangeStart - minimum) * (thumbAreaSize - newThumbSize)) + / ((maximum - minimum) - rangeSize)); + + setVisible (alwaysVisible || (maximum - minimum > rangeSize && rangeSize > 0.0)); + + if (thumbStart != newThumbStart || thumbSize != newThumbSize) + { + const int repaintStart = jmin (thumbStart, newThumbStart) - 4; + const int repaintSize = jmax (thumbStart + thumbSize, newThumbStart + newThumbSize) + 8 - repaintStart; + + if (vertical) + repaint (0, repaintStart, getWidth(), repaintSize); + else + repaint (repaintStart, 0, repaintSize, getHeight()); + + thumbStart = newThumbStart; + thumbSize = newThumbSize; + } +} + +void ScrollBar::setOrientation (const bool shouldBeVertical) throw() +{ + if (vertical != shouldBeVertical) + { + vertical = shouldBeVertical; + + if (upButton != 0) + { + ((ScrollbarButton*) upButton)->direction = (vertical) ? 0 : 3; + ((ScrollbarButton*) downButton)->direction = (vertical) ? 2 : 1; + } + + updateThumbPosition(); + } +} + +void ScrollBar::setButtonVisibility (const bool buttonsAreVisible) +{ + deleteAndZero (upButton); + deleteAndZero (downButton); + + if (buttonsAreVisible) + { + addAndMakeVisible (upButton = new ScrollbarButton ((vertical) ? 0 : 3, *this)); + addAndMakeVisible (downButton = new ScrollbarButton ((vertical) ? 2 : 1, *this)); + + setButtonRepeatSpeed (initialDelayInMillisecs, repeatDelayInMillisecs, minimumDelayInMillisecs); + } + + updateThumbPosition(); +} + +void ScrollBar::setAutoHide (const bool shouldHideWhenFullRange) +{ + alwaysVisible = ! shouldHideWhenFullRange; + updateThumbPosition(); +} + +void ScrollBar::paint (Graphics& g) +{ + if (thumbAreaSize > 0) + { + LookAndFeel& lf = getLookAndFeel(); + + const int thumb = (thumbAreaSize > lf.getMinimumScrollbarThumbSize (*this)) + ? thumbSize : 0; + + if (vertical) + { + lf.drawScrollbar (g, *this, + 0, thumbAreaStart, + getWidth(), thumbAreaSize, + vertical, + thumbStart, thumb, + isMouseOver(), isMouseButtonDown()); + } + else + { + lf.drawScrollbar (g, *this, + thumbAreaStart, 0, + thumbAreaSize, getHeight(), + vertical, + thumbStart, thumb, + isMouseOver(), isMouseButtonDown()); + } + } +} + +void ScrollBar::lookAndFeelChanged() +{ + setComponentEffect (getLookAndFeel().getScrollbarEffect()); +} + +void ScrollBar::resized() +{ + const int length = ((vertical) ? getHeight() : getWidth()); + + const int buttonSize = (upButton != 0) ? jmin (getLookAndFeel().getScrollbarButtonSize (*this), (length >> 1)) + : 0; + + if (length < 32 + getLookAndFeel().getMinimumScrollbarThumbSize (*this)) + { + thumbAreaStart = length >> 1; + thumbAreaSize = 0; + } + else + { + thumbAreaStart = buttonSize; + thumbAreaSize = length - (buttonSize << 1); + } + + if (upButton != 0) + { + if (vertical) + { + upButton->setBounds (0, 0, getWidth(), buttonSize); + downButton->setBounds (0, thumbAreaStart + thumbAreaSize, getWidth(), buttonSize); + } + else + { + upButton->setBounds (0, 0, buttonSize, getHeight()); + downButton->setBounds (thumbAreaStart + thumbAreaSize, 0, buttonSize, getHeight()); + } + } + + updateThumbPosition(); +} + +void ScrollBar::mouseDown (const MouseEvent& e) +{ + isDraggingThumb = false; + lastMousePos = vertical ? e.y : e.x; + dragStartMousePos = lastMousePos; + dragStartRange = rangeStart; + + if (dragStartMousePos < thumbStart) + { + moveScrollbarInPages (-1); + startTimer (400); + } + else if (dragStartMousePos >= thumbStart + thumbSize) + { + moveScrollbarInPages (1); + startTimer (400); + } + else + { + isDraggingThumb = (thumbAreaSize > getLookAndFeel().getMinimumScrollbarThumbSize (*this)) + && (thumbAreaSize > thumbSize); + } +} + +void ScrollBar::mouseDrag (const MouseEvent& e) +{ + if (isDraggingThumb) + { + const int deltaPixels = ((vertical) ? e.y : e.x) - dragStartMousePos; + + setCurrentRangeStart (dragStartRange + + deltaPixels * ((maximum - minimum) - rangeSize) + / (thumbAreaSize - thumbSize)); + + // (repainting synchronously makes it feel a bit snappier..) + ComponentPeer* const peer = getPeer(); + + if (peer != 0) + peer->performAnyPendingRepaintsNow(); + } + else + { + lastMousePos = (vertical) ? e.y : e.x; + } +} + +void ScrollBar::mouseUp (const MouseEvent&) +{ + isDraggingThumb = false; + stopTimer(); + repaint(); +} + +void ScrollBar::mouseWheelMove (const MouseEvent&, + float wheelIncrementX, + float wheelIncrementY) +{ + float increment = vertical ? wheelIncrementY : wheelIncrementX; + + if (increment < 0) + increment = jmin (increment * 10.0f, -1.0f); + else if (increment > 0) + increment = jmax (increment * 10.0f, 1.0f); + + setCurrentRangeStart (rangeStart - singleStepSize * increment); +} + +void ScrollBar::timerCallback() +{ + if (isMouseButtonDown()) + { + startTimer (40); + + if (lastMousePos < thumbStart) + setCurrentRangeStart (rangeStart - rangeSize); + else if (lastMousePos > thumbStart + thumbSize) + setCurrentRangeStart (rangeStart + rangeSize); + } + else + { + stopTimer(); + } +} + +bool ScrollBar::keyPressed (const KeyPress& key) +{ + if (! isVisible()) + return false; + + if (key.isKeyCode (KeyPress::upKey) || key.isKeyCode (KeyPress::leftKey)) + moveScrollbarInSteps (-1); + else if (key.isKeyCode (KeyPress::downKey) || key.isKeyCode (KeyPress::rightKey)) + moveScrollbarInSteps (1); + else if (key.isKeyCode (KeyPress::pageUpKey)) + moveScrollbarInPages (-1); + else if (key.isKeyCode (KeyPress::pageDownKey)) + moveScrollbarInPages (1); + else if (key.isKeyCode (KeyPress::homeKey)) + scrollToTop(); + else if (key.isKeyCode (KeyPress::endKey)) + scrollToBottom(); + else + return false; + + return true; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ScrollBar.cpp *********/ + +/********* Start of inlined file: juce_StretchableLayoutManager.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +StretchableLayoutManager::StretchableLayoutManager() + : totalSize (0) +{ +} + +StretchableLayoutManager::~StretchableLayoutManager() +{ +} + +void StretchableLayoutManager::clearAllItems() +{ + items.clear(); + totalSize = 0; +} + +void StretchableLayoutManager::setItemLayout (const int itemIndex, + const double minimumSize, + const double maximumSize, + const double preferredSize) +{ + ItemLayoutProperties* layout = getInfoFor (itemIndex); + + if (layout == 0) + { + layout = new ItemLayoutProperties(); + layout->itemIndex = itemIndex; + + int i; + for (i = 0; i < items.size(); ++i) + if (items.getUnchecked (i)->itemIndex > itemIndex) + break; + + items.insert (i, layout); + } + + layout->minSize = minimumSize; + layout->maxSize = maximumSize; + layout->preferredSize = preferredSize; + layout->currentSize = 0; +} + +bool StretchableLayoutManager::getItemLayout (const int itemIndex, + double& minimumSize, + double& maximumSize, + double& preferredSize) const +{ + const ItemLayoutProperties* const layout = getInfoFor (itemIndex); + + if (layout != 0) + { + minimumSize = layout->minSize; + maximumSize = layout->maxSize; + preferredSize = layout->preferredSize; + return true; + } + + return false; +} + +void StretchableLayoutManager::setTotalSize (const int newTotalSize) +{ + totalSize = newTotalSize; + + fitComponentsIntoSpace (0, items.size(), totalSize, 0); +} + +int StretchableLayoutManager::getItemCurrentPosition (const int itemIndex) const +{ + int pos = 0; + + for (int i = 0; i < itemIndex; ++i) + { + const ItemLayoutProperties* const layout = getInfoFor (i); + + if (layout != 0) + pos += layout->currentSize; + } + + return pos; +} + +int StretchableLayoutManager::getItemCurrentAbsoluteSize (const int itemIndex) const +{ + const ItemLayoutProperties* const layout = getInfoFor (itemIndex); + + if (layout != 0) + return layout->currentSize; + + return 0; +} + +double StretchableLayoutManager::getItemCurrentRelativeSize (const int itemIndex) const +{ + const ItemLayoutProperties* const layout = getInfoFor (itemIndex); + + if (layout != 0) + return -layout->currentSize / (double) totalSize; + + return 0; +} + +void StretchableLayoutManager::setItemPosition (const int itemIndex, + int newPosition) +{ + for (int i = items.size(); --i >= 0;) + { + const ItemLayoutProperties* const layout = items.getUnchecked(i); + + if (layout->itemIndex == itemIndex) + { + int realTotalSize = jmax (totalSize, getMinimumSizeOfItems (0, items.size())); + const int minSizeAfterThisComp = getMinimumSizeOfItems (i, items.size()); + const int maxSizeAfterThisComp = getMaximumSizeOfItems (i + 1, items.size()); + + newPosition = jmax (newPosition, totalSize - maxSizeAfterThisComp - layout->currentSize); + newPosition = jmin (newPosition, realTotalSize - minSizeAfterThisComp); + + int endPos = fitComponentsIntoSpace (0, i, newPosition, 0); + + endPos += layout->currentSize; + + fitComponentsIntoSpace (i + 1, items.size(), totalSize - endPos, endPos); + updatePrefSizesToMatchCurrentPositions(); + break; + } + } +} + +void StretchableLayoutManager::layOutComponents (Component** const components, + int numComponents, + int x, int y, int w, int h, + const bool vertically, + const bool resizeOtherDimension) +{ + setTotalSize (vertically ? h : w); + int pos = vertically ? y : x; + + for (int i = 0; i < numComponents; ++i) + { + const ItemLayoutProperties* const layout = getInfoFor (i); + + if (layout != 0) + { + Component* const c = components[i]; + + if (c != 0) + { + if (i == numComponents - 1) + { + // if it's the last item, crop it to exactly fit the available space.. + if (resizeOtherDimension) + { + if (vertically) + c->setBounds (x, pos, w, jmax (layout->currentSize, h - pos)); + else + c->setBounds (pos, y, jmax (layout->currentSize, w - pos), h); + } + else + { + if (vertically) + c->setBounds (c->getX(), pos, c->getWidth(), jmax (layout->currentSize, h - pos)); + else + c->setBounds (pos, c->getY(), jmax (layout->currentSize, w - pos), c->getHeight()); + } + } + else + { + if (resizeOtherDimension) + { + if (vertically) + c->setBounds (x, pos, w, layout->currentSize); + else + c->setBounds (pos, y, layout->currentSize, h); + } + else + { + if (vertically) + c->setBounds (c->getX(), pos, c->getWidth(), layout->currentSize); + else + c->setBounds (pos, c->getY(), layout->currentSize, c->getHeight()); + } + } + } + + pos += layout->currentSize; + } + } +} + +StretchableLayoutManager::ItemLayoutProperties* StretchableLayoutManager::getInfoFor (const int itemIndex) const +{ + for (int i = items.size(); --i >= 0;) + if (items.getUnchecked(i)->itemIndex == itemIndex) + return items.getUnchecked(i); + + return 0; +} + +int StretchableLayoutManager::fitComponentsIntoSpace (const int startIndex, + const int endIndex, + const int availableSpace, + int startPos) +{ + // calculate the total sizes + int i; + double totalIdealSize = 0.0; + int totalMinimums = 0; + + for (i = startIndex; i < endIndex; ++i) + { + ItemLayoutProperties* const layout = items.getUnchecked (i); + + layout->currentSize = sizeToRealSize (layout->minSize, totalSize); + + totalMinimums += layout->currentSize; + totalIdealSize += sizeToRealSize (layout->preferredSize, availableSpace); + } + + if (totalIdealSize <= 0) + totalIdealSize = 1.0; + + // now calc the best sizes.. + int extraSpace = availableSpace - totalMinimums; + + while (extraSpace > 0) + { + int numWantingMoreSpace = 0; + int numHavingTakenExtraSpace = 0; + + // first figure out how many comps want a slice of the extra space.. + for (i = startIndex; i < endIndex; ++i) + { + ItemLayoutProperties* const layout = items.getUnchecked (i); + + double sizeWanted = sizeToRealSize (layout->preferredSize, availableSpace); + + const int bestSize = jlimit (layout->currentSize, + jmax (layout->currentSize, + sizeToRealSize (layout->maxSize, totalSize)), + roundDoubleToInt (sizeWanted * availableSpace / totalIdealSize)); + + if (bestSize > layout->currentSize) + ++numWantingMoreSpace; + } + + // ..share out the extra space.. + for (i = startIndex; i < endIndex; ++i) + { + ItemLayoutProperties* const layout = items.getUnchecked (i); + + double sizeWanted = sizeToRealSize (layout->preferredSize, availableSpace); + + int bestSize = jlimit (layout->currentSize, + jmax (layout->currentSize, sizeToRealSize (layout->maxSize, totalSize)), + roundDoubleToInt (sizeWanted * availableSpace / totalIdealSize)); + + const int extraWanted = bestSize - layout->currentSize; + + if (extraWanted > 0) + { + const int extraAllowed = jmin (extraWanted, + extraSpace / jmax (1, numWantingMoreSpace)); + + if (extraAllowed > 0) + { + ++numHavingTakenExtraSpace; + --numWantingMoreSpace; + + layout->currentSize += extraAllowed; + extraSpace -= extraAllowed; + } + } + } + + if (numHavingTakenExtraSpace <= 0) + break; + } + + // ..and calculate the end position + for (i = startIndex; i < endIndex; ++i) + { + ItemLayoutProperties* const layout = items.getUnchecked(i); + startPos += layout->currentSize; + } + + return startPos; +} + +int StretchableLayoutManager::getMinimumSizeOfItems (const int startIndex, + const int endIndex) const +{ + int totalMinimums = 0; + + for (int i = startIndex; i < endIndex; ++i) + totalMinimums += sizeToRealSize (items.getUnchecked (i)->minSize, totalSize); + + return totalMinimums; +} + +int StretchableLayoutManager::getMaximumSizeOfItems (const int startIndex, const int endIndex) const +{ + int totalMaximums = 0; + + for (int i = startIndex; i < endIndex; ++i) + totalMaximums += sizeToRealSize (items.getUnchecked (i)->maxSize, totalSize); + + return totalMaximums; +} + +void StretchableLayoutManager::updatePrefSizesToMatchCurrentPositions() +{ + for (int i = 0; i < items.size(); ++i) + { + ItemLayoutProperties* const layout = items.getUnchecked (i); + + layout->preferredSize + = (layout->preferredSize < 0) ? getItemCurrentRelativeSize (i) + : getItemCurrentAbsoluteSize (i); + } +} + +int StretchableLayoutManager::sizeToRealSize (double size, int totalSpace) +{ + if (size < 0) + size *= -totalSpace; + + return roundDoubleToInt (size); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_StretchableLayoutManager.cpp *********/ + +/********* Start of inlined file: juce_StretchableLayoutResizerBar.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +StretchableLayoutResizerBar::StretchableLayoutResizerBar (StretchableLayoutManager* layout_, + const int itemIndex_, + const bool isVertical_) + : layout (layout_), + itemIndex (itemIndex_), + isVertical (isVertical_) +{ + setRepaintsOnMouseActivity (true); + setMouseCursor (MouseCursor (isVertical_ ? MouseCursor::LeftRightResizeCursor + : MouseCursor::UpDownResizeCursor)); +} + +StretchableLayoutResizerBar::~StretchableLayoutResizerBar() +{ +} + +void StretchableLayoutResizerBar::paint (Graphics& g) +{ + getLookAndFeel().drawStretchableLayoutResizerBar (g, + getWidth(), getHeight(), + isVertical, + isMouseOver(), + isMouseButtonDown()); +} + +void StretchableLayoutResizerBar::mouseDown (const MouseEvent&) +{ + mouseDownPos = layout->getItemCurrentPosition (itemIndex); +} + +void StretchableLayoutResizerBar::mouseDrag (const MouseEvent& e) +{ + const int desiredPos = mouseDownPos + (isVertical ? e.getDistanceFromDragStartX() + : e.getDistanceFromDragStartY()); + + layout->setItemPosition (itemIndex, desiredPos); + + hasBeenMoved(); +} + +void StretchableLayoutResizerBar::hasBeenMoved() +{ + if (getParentComponent() != 0) + getParentComponent()->resized(); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_StretchableLayoutResizerBar.cpp *********/ + +/********* Start of inlined file: juce_StretchableObjectResizer.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +StretchableObjectResizer::StretchableObjectResizer() +{ +} + +StretchableObjectResizer::~StretchableObjectResizer() +{ +} + +void StretchableObjectResizer::addItem (const double size, + const double minSize, const double maxSize, + const int order) +{ + jassert (order >= 0 && order < INT_MAX); // the order must be >= 0 and less than INT_MAX + + Item* const item = new Item(); + item->size = size; + item->minSize = minSize; + item->maxSize = maxSize; + item->order = order; + items.add (item); +} + +double StretchableObjectResizer::getItemSize (const int index) const throw() +{ + const Item* const it = items [index]; + return it != 0 ? it->size : 0; +} + +void StretchableObjectResizer::resizeToFit (const double targetSize) +{ + int order = 0; + + for (;;) + { + double currentSize = 0; + double minSize = 0; + double maxSize = 0; + + int nextHighestOrder = INT_MAX; + + for (int i = 0; i < items.size(); ++i) + { + const Item* const it = items.getUnchecked(i); + currentSize += it->size; + + if (it->order <= order) + { + minSize += it->minSize; + maxSize += it->maxSize; + } + else + { + minSize += it->size; + maxSize += it->size; + nextHighestOrder = jmin (nextHighestOrder, it->order); + } + } + + const double thisIterationTarget = jlimit (minSize, maxSize, targetSize); + + if (thisIterationTarget >= currentSize) + { + const double availableExtraSpace = maxSize - currentSize; + const double targetAmountOfExtraSpace = thisIterationTarget - currentSize; + const double scale = targetAmountOfExtraSpace / availableExtraSpace; + + for (int i = 0; i < items.size(); ++i) + { + Item* const it = items.getUnchecked(i); + + if (it->order <= order) + it->size = jmin (it->maxSize, it->size + (it->maxSize - it->size) * scale); + } + } + else + { + const double amountOfSlack = currentSize - minSize; + const double targetAmountOfSlack = thisIterationTarget - minSize; + const double scale = targetAmountOfSlack / amountOfSlack; + + for (int i = 0; i < items.size(); ++i) + { + Item* const it = items.getUnchecked(i); + + if (it->order <= order) + it->size = jmax (it->minSize, it->minSize + (it->size - it->minSize) * scale); + } + } + + if (nextHighestOrder < INT_MAX) + order = nextHighestOrder; + else + break; + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_StretchableObjectResizer.cpp *********/ + +/********* Start of inlined file: juce_TabbedButtonBar.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +TabBarButton::TabBarButton (const String& name, + TabbedButtonBar* const owner_, + const int index) + : Button (name), + owner (owner_), + tabIndex (index), + overlapPixels (0) +{ + shadow.setShadowProperties (2.2f, 0.7f, 0, 0); + setComponentEffect (&shadow); + setWantsKeyboardFocus (false); +} + +TabBarButton::~TabBarButton() +{ +} + +void TabBarButton::paintButton (Graphics& g, + bool isMouseOverButton, + bool isButtonDown) +{ + int x, y, w, h; + getActiveArea (x, y, w, h); + + g.setOrigin (x, y); + + getLookAndFeel() + .drawTabButton (g, w, h, + owner->getTabBackgroundColour (tabIndex), + tabIndex, getButtonText(), *this, + owner->getOrientation(), + isMouseOverButton, isButtonDown, + getToggleState()); +} + +void TabBarButton::clicked (const ModifierKeys& mods) +{ + if (mods.isPopupMenu()) + owner->popupMenuClickOnTab (tabIndex, getButtonText()); + else + owner->setCurrentTabIndex (tabIndex); +} + +bool TabBarButton::hitTest (int mx, int my) +{ + int x, y, w, h; + getActiveArea (x, y, w, h); + + if (owner->getOrientation() == TabbedButtonBar::TabsAtLeft + || owner->getOrientation() == TabbedButtonBar::TabsAtRight) + { + if (((unsigned int) mx) < (unsigned int) getWidth() + && my >= y + overlapPixels + && my < y + h - overlapPixels) + return true; + } + else + { + if (mx >= x + overlapPixels && mx < x + w - overlapPixels + && ((unsigned int) my) < (unsigned int) getHeight()) + return true; + } + + Path p; + getLookAndFeel() + .createTabButtonShape (p, w, h, tabIndex, getButtonText(), *this, + owner->getOrientation(), + false, false, getToggleState()); + + return p.contains ((float) (mx - x), + (float) (my - y)); +} + +int TabBarButton::getBestTabLength (const int depth) +{ + return jlimit (depth * 2, + depth * 7, + getLookAndFeel().getTabButtonBestWidth (tabIndex, getButtonText(), depth, *this)); +} + +void TabBarButton::getActiveArea (int& x, int& y, int& w, int& h) +{ + x = 0; + y = 0; + int r = getWidth(); + int b = getHeight(); + + const int spaceAroundImage = getLookAndFeel().getTabButtonSpaceAroundImage(); + + if (owner->getOrientation() != TabbedButtonBar::TabsAtLeft) + r -= spaceAroundImage; + + if (owner->getOrientation() != TabbedButtonBar::TabsAtRight) + x += spaceAroundImage; + + if (owner->getOrientation() != TabbedButtonBar::TabsAtBottom) + y += spaceAroundImage; + + if (owner->getOrientation() != TabbedButtonBar::TabsAtTop) + b -= spaceAroundImage; + + w = r - x; + h = b - y; +} + +class TabAreaBehindFrontButtonComponent : public Component +{ +public: + TabAreaBehindFrontButtonComponent (TabbedButtonBar* const owner_) + : owner (owner_) + { + setInterceptsMouseClicks (false, false); + } + + ~TabAreaBehindFrontButtonComponent() + { + } + + void paint (Graphics& g) + { + getLookAndFeel() + .drawTabAreaBehindFrontButton (g, getWidth(), getHeight(), + *owner, owner->getOrientation()); + } + + void enablementChanged() + { + repaint(); + } + +private: + TabbedButtonBar* const owner; + + TabAreaBehindFrontButtonComponent (const TabAreaBehindFrontButtonComponent&); + const TabAreaBehindFrontButtonComponent& operator= (const TabAreaBehindFrontButtonComponent&); +}; + +TabbedButtonBar::TabbedButtonBar (const Orientation orientation_) + : orientation (orientation_), + currentTabIndex (-1), + extraTabsButton (0) +{ + setInterceptsMouseClicks (false, true); + addAndMakeVisible (behindFrontTab = new TabAreaBehindFrontButtonComponent (this)); + setFocusContainer (true); +} + +TabbedButtonBar::~TabbedButtonBar() +{ + deleteAllChildren(); +} + +void TabbedButtonBar::setOrientation (const Orientation newOrientation) +{ + orientation = newOrientation; + + for (int i = getNumChildComponents(); --i >= 0;) + getChildComponent (i)->resized(); + + resized(); +} + +TabBarButton* TabbedButtonBar::createTabButton (const String& name, const int index) +{ + return new TabBarButton (name, this, index); +} + +void TabbedButtonBar::clearTabs() +{ + tabs.clear(); + tabColours.clear(); + currentTabIndex = -1; + + deleteAndZero (extraTabsButton); + removeChildComponent (behindFrontTab); + deleteAllChildren(); + addChildComponent (behindFrontTab); + + setCurrentTabIndex (-1); +} + +void TabbedButtonBar::addTab (const String& tabName, + const Colour& tabBackgroundColour, + int insertIndex) +{ + jassert (tabName.isNotEmpty()); // you have to give them all a name.. + + if (tabName.isNotEmpty()) + { + if (((unsigned int) insertIndex) > (unsigned int) tabs.size()) + insertIndex = tabs.size(); + + for (int i = tabs.size(); --i >= insertIndex;) + { + TabBarButton* const tb = getTabButton (i); + + if (tb != 0) + tb->tabIndex++; + } + + tabs.insert (insertIndex, tabName); + tabColours.insert (insertIndex, tabBackgroundColour); + + TabBarButton* const tb = createTabButton (tabName, insertIndex); + jassert (tb != 0); // your createTabButton() mustn't return zero! + + addAndMakeVisible (tb, insertIndex); + + resized(); + + if (currentTabIndex < 0) + setCurrentTabIndex (0); + } +} + +void TabbedButtonBar::setTabName (const int tabIndex, + const String& newName) +{ + if (((unsigned int) tabIndex) < (unsigned int) tabs.size() + && tabs[tabIndex] != newName) + { + tabs.set (tabIndex, newName); + + TabBarButton* const tb = getTabButton (tabIndex); + + if (tb != 0) + tb->setButtonText (newName); + + resized(); + } +} + +void TabbedButtonBar::removeTab (const int tabIndex) +{ + if (((unsigned int) tabIndex) < (unsigned int) tabs.size()) + { + const int oldTabIndex = currentTabIndex; + if (currentTabIndex == tabIndex) + currentTabIndex = -1; + + tabs.remove (tabIndex); + tabColours.remove (tabIndex); + + TabBarButton* const tb = getTabButton (tabIndex); + + if (tb != 0) + delete tb; + + for (int i = tabIndex + 1; i <= tabs.size(); ++i) + { + TabBarButton* const tb = getTabButton (i); + + if (tb != 0) + tb->tabIndex--; + } + + resized(); + + setCurrentTabIndex (jlimit (0, jmax (0, tabs.size() - 1), oldTabIndex)); + } +} + +void TabbedButtonBar::moveTab (const int currentIndex, + const int newIndex) +{ + tabs.move (currentIndex, newIndex); + tabColours.move (currentIndex, newIndex); + resized(); +} + +int TabbedButtonBar::getNumTabs() const +{ + return tabs.size(); +} + +const StringArray TabbedButtonBar::getTabNames() const +{ + return tabs; +} + +void TabbedButtonBar::setCurrentTabIndex (int newIndex) +{ + if (currentTabIndex != newIndex) + { + if (((unsigned int) newIndex) >= (unsigned int) tabs.size()) + newIndex = -1; + + currentTabIndex = newIndex; + + for (int i = 0; i < getNumChildComponents(); ++i) + { + TabBarButton* const tb = dynamic_cast (getChildComponent (i)); + + if (tb != 0) + tb->setToggleState (tb->tabIndex == newIndex, false); + } + + resized(); + sendChangeMessage (this); + + currentTabChanged (newIndex, newIndex >= 0 ? tabs [newIndex] : String::empty); + } +} + +TabBarButton* TabbedButtonBar::getTabButton (const int index) const +{ + for (int i = getNumChildComponents(); --i >= 0;) + { + TabBarButton* const tb = dynamic_cast (getChildComponent (i)); + + if (tb != 0 && tb->tabIndex == index) + return tb; + } + + return 0; +} + +void TabbedButtonBar::lookAndFeelChanged() +{ + deleteAndZero (extraTabsButton); + resized(); +} + +void TabbedButtonBar::resized() +{ + const double minimumScale = 0.7; + int depth = getWidth(); + int length = getHeight(); + + if (orientation == TabsAtTop || orientation == TabsAtBottom) + swapVariables (depth, length); + + const int overlap = getLookAndFeel().getTabButtonOverlap (depth) + + getLookAndFeel().getTabButtonSpaceAroundImage() * 2; + + int i, totalLength = overlap; + int numVisibleButtons = tabs.size(); + + for (i = 0; i < getNumChildComponents(); ++i) + { + TabBarButton* const tb = dynamic_cast (getChildComponent (i)); + + if (tb != 0) + { + totalLength += tb->getBestTabLength (depth) - overlap; + tb->overlapPixels = overlap / 2; + } + } + + double scale = 1.0; + + if (totalLength > length) + scale = jmax (minimumScale, length / (double) totalLength); + + const bool isTooBig = totalLength * scale > length; + int tabsButtonPos = 0; + + if (isTooBig) + { + if (extraTabsButton == 0) + { + addAndMakeVisible (extraTabsButton = getLookAndFeel().createTabBarExtrasButton()); + extraTabsButton->addButtonListener (this); + extraTabsButton->setAlwaysOnTop (true); + extraTabsButton->setTriggeredOnMouseDown (true); + } + + const int buttonSize = jmin (proportionOfWidth (0.7f), proportionOfHeight (0.7f)); + extraTabsButton->setSize (buttonSize, buttonSize); + + if (orientation == TabsAtTop || orientation == TabsAtBottom) + { + tabsButtonPos = getWidth() - buttonSize / 2 - 1; + extraTabsButton->setCentrePosition (tabsButtonPos, getHeight() / 2); + } + else + { + tabsButtonPos = getHeight() - buttonSize / 2 - 1; + extraTabsButton->setCentrePosition (getWidth() / 2, tabsButtonPos); + } + + totalLength = 0; + + for (i = 0; i < tabs.size(); ++i) + { + TabBarButton* const tb = getTabButton (i); + + if (tb != 0) + { + const int newLength = totalLength + tb->getBestTabLength (depth); + + if (i > 0 && newLength * minimumScale > tabsButtonPos) + { + totalLength += overlap; + break; + } + + numVisibleButtons = i + 1; + totalLength = newLength - overlap; + + } + } + + scale = jmax (minimumScale, tabsButtonPos / (double) totalLength); + } + else + { + deleteAndZero (extraTabsButton); + } + + int pos = 0; + + TabBarButton* frontTab = 0; + + for (i = 0; i < tabs.size(); ++i) + { + TabBarButton* const tb = getTabButton (i); + + if (tb != 0) + { + const int bestLength = roundDoubleToInt (scale * tb->getBestTabLength (depth)); + + if (i < numVisibleButtons) + { + if (orientation == TabsAtTop || orientation == TabsAtBottom) + tb->setBounds (pos, 0, bestLength, getHeight()); + else + tb->setBounds (0, pos, getWidth(), bestLength); + + tb->toBack(); + + if (tb->tabIndex == currentTabIndex) + frontTab = tb; + + tb->setVisible (true); + } + else + { + tb->setVisible (false); + } + + pos += bestLength - overlap; + } + } + + behindFrontTab->setBounds (0, 0, getWidth(), getHeight()); + + if (frontTab != 0) + { + frontTab->toFront (false); + behindFrontTab->toBehind (frontTab); + } +} + +const Colour TabbedButtonBar::getTabBackgroundColour (const int tabIndex) +{ + return tabColours [tabIndex]; +} + +void TabbedButtonBar::setTabBackgroundColour (const int tabIndex, const Colour& newColour) +{ + if (((unsigned int) tabIndex) < (unsigned int) tabColours.size() + && tabColours [tabIndex] != newColour) + { + tabColours.set (tabIndex, newColour); + repaint(); + } +} + +void TabbedButtonBar::buttonClicked (Button* button) +{ + if (extraTabsButton == button) + { + PopupMenu m; + + for (int i = 0; i < tabs.size(); ++i) + { + TabBarButton* const tb = getTabButton (i); + + if (tb != 0 && ! tb->isVisible()) + m.addItem (tb->tabIndex + 1, tabs[i], true, i == currentTabIndex); + } + + const int res = m.showAt (extraTabsButton); + + if (res != 0) + setCurrentTabIndex (res - 1); + } +} + +void TabbedButtonBar::currentTabChanged (const int, const String&) +{ +} + +void TabbedButtonBar::popupMenuClickOnTab (const int, const String&) +{ +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_TabbedButtonBar.cpp *********/ + +/********* Start of inlined file: juce_TabbedComponent.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +class TabCompButtonBar : public TabbedButtonBar +{ +public: + TabCompButtonBar (TabbedComponent* const owner_, + const TabbedButtonBar::Orientation orientation) + : TabbedButtonBar (orientation), + owner (owner_) + { + } + + ~TabCompButtonBar() + { + } + + void currentTabChanged (const int newCurrentTabIndex, + const String& newTabName) + { + owner->changeCallback (newCurrentTabIndex, newTabName); + } + + void popupMenuClickOnTab (const int tabIndex, + const String& tabName) + { + owner->popupMenuClickOnTab (tabIndex, tabName); + } + + const Colour getTabBackgroundColour (const int tabIndex) + { + return owner->tabs->getTabBackgroundColour (tabIndex); + } + + TabBarButton* createTabButton (const String& tabName, const int tabIndex) + { + return owner->createTabButton (tabName, tabIndex); + } + + juce_UseDebuggingNewOperator + +private: + TabbedComponent* const owner; + + TabCompButtonBar (const TabCompButtonBar&); + const TabCompButtonBar& operator= (const TabCompButtonBar&); +}; + +TabbedComponent::TabbedComponent (const TabbedButtonBar::Orientation orientation) + : panelComponent (0), + tabDepth (30), + outlineColour (Colours::grey), + outlineThickness (1), + edgeIndent (0) +{ + addAndMakeVisible (tabs = new TabCompButtonBar (this, orientation)); +} + +TabbedComponent::~TabbedComponent() +{ + clearTabs(); + delete tabs; +} + +void TabbedComponent::setOrientation (const TabbedButtonBar::Orientation orientation) +{ + tabs->setOrientation (orientation); + resized(); +} + +TabbedButtonBar::Orientation TabbedComponent::getOrientation() const throw() +{ + return tabs->getOrientation(); +} + +void TabbedComponent::setTabBarDepth (const int newDepth) +{ + if (tabDepth != newDepth) + { + tabDepth = newDepth; + resized(); + } +} + +TabBarButton* TabbedComponent::createTabButton (const String& tabName, const int tabIndex) +{ + return new TabBarButton (tabName, tabs, tabIndex); +} + +void TabbedComponent::clearTabs() +{ + if (panelComponent != 0) + { + panelComponent->setVisible (false); + removeChildComponent (panelComponent); + panelComponent = 0; + } + + tabs->clearTabs(); + + for (int i = contentComponents.size(); --i >= 0;) + { + Component* const c = contentComponents.getUnchecked(i); + + // be careful not to delete these components until they've been removed from the tab component + jassert (c == 0 || c->isValidComponent()); + + if (c != 0 && c->getComponentPropertyBool (T("deleteByTabComp_"), false, false)) + delete c; + } + + contentComponents.clear(); +} + +void TabbedComponent::addTab (const String& tabName, + const Colour& tabBackgroundColour, + Component* const contentComponent, + const bool deleteComponentWhenNotNeeded, + const int insertIndex) +{ + contentComponents.insert (insertIndex, contentComponent); + + if (contentComponent != 0) + contentComponent->setComponentProperty (T("deleteByTabComp_"), deleteComponentWhenNotNeeded); + + tabs->addTab (tabName, tabBackgroundColour, insertIndex); +} + +void TabbedComponent::setTabName (const int tabIndex, + const String& newName) +{ + tabs->setTabName (tabIndex, newName); +} + +void TabbedComponent::removeTab (const int tabIndex) +{ + Component* const c = contentComponents [tabIndex]; + + if (c != 0 && c->getComponentPropertyBool (T("deleteByTabComp_"), false, false)) + { + if (c == panelComponent) + panelComponent = 0; + + delete c; + } + + contentComponents.remove (tabIndex); + + tabs->removeTab (tabIndex); +} + +int TabbedComponent::getNumTabs() const +{ + return tabs->getNumTabs(); +} + +const StringArray TabbedComponent::getTabNames() const +{ + return tabs->getTabNames(); +} + +Component* TabbedComponent::getTabContentComponent (const int tabIndex) const throw() +{ + return contentComponents [tabIndex]; +} + +const Colour TabbedComponent::getTabBackgroundColour (const int tabIndex) const throw() +{ + return tabs->getTabBackgroundColour (tabIndex); +} + +void TabbedComponent::setTabBackgroundColour (const int tabIndex, const Colour& newColour) +{ + tabs->setTabBackgroundColour (tabIndex, newColour); + + if (getCurrentTabIndex() == tabIndex) + repaint(); +} + +void TabbedComponent::setCurrentTabIndex (const int newTabIndex) +{ + tabs->setCurrentTabIndex (newTabIndex); +} + +int TabbedComponent::getCurrentTabIndex() const +{ + return tabs->getCurrentTabIndex(); +} + +const String& TabbedComponent::getCurrentTabName() const +{ + return tabs->getCurrentTabName(); +} + +void TabbedComponent::setOutline (const Colour& colour, int thickness) +{ + outlineColour = colour; + outlineThickness = thickness; + repaint(); +} + +void TabbedComponent::setIndent (const int indentThickness) +{ + edgeIndent = indentThickness; +} + +void TabbedComponent::paint (Graphics& g) +{ + const TabbedButtonBar::Orientation o = getOrientation(); + + int x = 0; + int y = 0; + int r = getWidth(); + int b = getHeight(); + + if (o == TabbedButtonBar::TabsAtTop) + y += tabDepth; + else if (o == TabbedButtonBar::TabsAtBottom) + b -= tabDepth; + else if (o == TabbedButtonBar::TabsAtLeft) + x += tabDepth; + else if (o == TabbedButtonBar::TabsAtRight) + r -= tabDepth; + + g.reduceClipRegion (x, y, r - x, b - y); + g.fillAll (tabs->getTabBackgroundColour (getCurrentTabIndex())); + + if (outlineThickness > 0) + { + if (o == TabbedButtonBar::TabsAtTop) + --y; + else if (o == TabbedButtonBar::TabsAtBottom) + ++b; + else if (o == TabbedButtonBar::TabsAtLeft) + --x; + else if (o == TabbedButtonBar::TabsAtRight) + ++r; + + g.setColour (outlineColour); + g.drawRect (x, y, r - x, b - y, outlineThickness); + } +} + +void TabbedComponent::resized() +{ + const TabbedButtonBar::Orientation o = getOrientation(); + const int indent = edgeIndent + outlineThickness; + BorderSize indents (indent); + + if (o == TabbedButtonBar::TabsAtTop) + { + tabs->setBounds (0, 0, getWidth(), tabDepth); + indents.setTop (tabDepth + edgeIndent); + } + else if (o == TabbedButtonBar::TabsAtBottom) + { + tabs->setBounds (0, getHeight() - tabDepth, getWidth(), tabDepth); + indents.setBottom (tabDepth + edgeIndent); + } + else if (o == TabbedButtonBar::TabsAtLeft) + { + tabs->setBounds (0, 0, tabDepth, getHeight()); + indents.setLeft (tabDepth + edgeIndent); + } + else if (o == TabbedButtonBar::TabsAtRight) + { + tabs->setBounds (getWidth() - tabDepth, 0, tabDepth, getHeight()); + indents.setRight (tabDepth + edgeIndent); + } + + const Rectangle bounds (indents.subtractedFrom (Rectangle (0, 0, getWidth(), getHeight()))); + + for (int i = contentComponents.size(); --i >= 0;) + if (contentComponents.getUnchecked (i) != 0) + contentComponents.getUnchecked (i)->setBounds (bounds); +} + +void TabbedComponent::changeCallback (const int newCurrentTabIndex, + const String& newTabName) +{ + if (panelComponent != 0) + { + panelComponent->setVisible (false); + removeChildComponent (panelComponent); + panelComponent = 0; + } + + if (getCurrentTabIndex() >= 0) + { + panelComponent = contentComponents [getCurrentTabIndex()]; + + if (panelComponent != 0) + { + // do these ops as two stages instead of addAndMakeVisible() so that the + // component has always got a parent when it gets the visibilityChanged() callback + addChildComponent (panelComponent); + panelComponent->setVisible (true); + panelComponent->toFront (true); + } + + repaint(); + } + + resized(); + + currentTabChanged (newCurrentTabIndex, newTabName); +} + +void TabbedComponent::currentTabChanged (const int, const String&) +{ +} + +void TabbedComponent::popupMenuClickOnTab (const int, const String&) +{ +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_TabbedComponent.cpp *********/ + +/********* Start of inlined file: juce_Viewport.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +Viewport::Viewport (const String& componentName) + : Component (componentName), + contentComp (0), + lastVX (0), + lastVY (0), + lastVW (0), + lastVH (0), + scrollBarThickness (0), + singleStepX (16), + singleStepY (16), + showHScrollbar (true), + showVScrollbar (true) +{ + // content holder is used to clip the contents so they don't overlap the scrollbars + addAndMakeVisible (contentHolder = new Component()); + contentHolder->setInterceptsMouseClicks (false, true); + + verticalScrollBar = new ScrollBar (true); + horizontalScrollBar = new ScrollBar (false); + + addChildComponent (verticalScrollBar); + addChildComponent (horizontalScrollBar); + + verticalScrollBar->addListener (this); + horizontalScrollBar->addListener (this); + + setInterceptsMouseClicks (false, true); + setWantsKeyboardFocus (true); +} + +Viewport::~Viewport() +{ + contentHolder->deleteAllChildren(); + deleteAllChildren(); +} + +void Viewport::visibleAreaChanged (int, int, int, int) +{ +} + +void Viewport::setViewedComponent (Component* const newViewedComponent) +{ + if (contentComp != newViewedComponent) + { + if (contentComp->isValidComponent()) + { + Component* const oldComp = contentComp; + contentComp = 0; + delete oldComp; + } + + contentComp = newViewedComponent; + + if (contentComp != 0) + { + contentComp->setTopLeftPosition (0, 0); + contentHolder->addAndMakeVisible (contentComp); + contentComp->addComponentListener (this); + } + + updateVisibleRegion(); + } +} + +int Viewport::getMaximumVisibleWidth() const throw() +{ + return jmax (0, getWidth() - (verticalScrollBar->isVisible() ? getScrollBarThickness() : 0)); +} + +int Viewport::getMaximumVisibleHeight() const throw() +{ + return jmax (0, getHeight() - (horizontalScrollBar->isVisible() ? getScrollBarThickness() : 0)); +} + +void Viewport::setViewPosition (const int xPixelsOffset, + const int yPixelsOffset) +{ + if (contentComp != 0) + contentComp->setTopLeftPosition (-xPixelsOffset, + -yPixelsOffset); +} + +void Viewport::setViewPositionProportionately (const double x, + const double y) +{ + if (contentComp != 0) + setViewPosition (jmax (0, roundDoubleToInt (x * (contentComp->getWidth() - getWidth()))), + jmax (0, roundDoubleToInt (y * (contentComp->getHeight() - getHeight())))); +} + +void Viewport::componentMovedOrResized (Component&, bool, bool) +{ + updateVisibleRegion(); +} + +void Viewport::resized() +{ + updateVisibleRegion(); +} + +void Viewport::updateVisibleRegion() +{ + if (contentComp != 0) + { + const int newVX = -contentComp->getX(); + const int newVY = -contentComp->getY(); + + if (newVX == 0 && newVY == 0 + && contentComp->getWidth() <= getWidth() + && contentComp->getHeight() <= getHeight()) + { + horizontalScrollBar->setVisible (false); + verticalScrollBar->setVisible (false); + } + + if ((contentComp->getWidth() > 0) && showHScrollbar + && getHeight() > getScrollBarThickness()) + { + horizontalScrollBar->setRangeLimits (0.0, contentComp->getWidth()); + horizontalScrollBar->setCurrentRange (newVX, getMaximumVisibleWidth()); + horizontalScrollBar->setSingleStepSize (singleStepX); + } + else + { + horizontalScrollBar->setVisible (false); + } + + if ((contentComp->getHeight() > 0) && showVScrollbar + && getWidth() > getScrollBarThickness()) + { + verticalScrollBar->setRangeLimits (0.0, contentComp->getHeight()); + verticalScrollBar->setCurrentRange (newVY, getMaximumVisibleHeight()); + verticalScrollBar->setSingleStepSize (singleStepY); + } + else + { + verticalScrollBar->setVisible (false); + } + + if (verticalScrollBar->isVisible()) + { + horizontalScrollBar->setCurrentRange (newVX, getMaximumVisibleWidth()); + verticalScrollBar->setCurrentRange (newVY, getMaximumVisibleHeight()); + + verticalScrollBar + ->setBounds (getMaximumVisibleWidth(), 0, + getScrollBarThickness(), getMaximumVisibleHeight()); + } + + if (horizontalScrollBar->isVisible()) + { + horizontalScrollBar->setCurrentRange (newVX, getMaximumVisibleWidth()); + + horizontalScrollBar + ->setBounds (0, getMaximumVisibleHeight(), + getMaximumVisibleWidth(), getScrollBarThickness()); + } + + contentHolder->setSize (getMaximumVisibleWidth(), + getMaximumVisibleHeight()); + + const int newVW = jmin (contentComp->getRight(), getMaximumVisibleWidth()); + const int newVH = jmin (contentComp->getBottom(), getMaximumVisibleHeight()); + + if (newVX != lastVX + || newVY != lastVY + || newVW != lastVW + || newVH != lastVH) + { + lastVX = newVX; + lastVY = newVY; + lastVW = newVW; + lastVH = newVH; + + visibleAreaChanged (newVX, newVY, newVW, newVH); + } + + horizontalScrollBar->handleUpdateNowIfNeeded(); + verticalScrollBar->handleUpdateNowIfNeeded(); + } + else + { + horizontalScrollBar->setVisible (false); + verticalScrollBar->setVisible (false); + } +} + +void Viewport::setSingleStepSizes (const int stepX, + const int stepY) +{ + singleStepX = stepX; + singleStepY = stepY; + updateVisibleRegion(); +} + +void Viewport::setScrollBarsShown (const bool showVerticalScrollbarIfNeeded, + const bool showHorizontalScrollbarIfNeeded) +{ + showVScrollbar = showVerticalScrollbarIfNeeded; + showHScrollbar = showHorizontalScrollbarIfNeeded; + updateVisibleRegion(); +} + +void Viewport::setScrollBarThickness (const int thickness) +{ + scrollBarThickness = thickness; + updateVisibleRegion(); +} + +int Viewport::getScrollBarThickness() const throw() +{ + return (scrollBarThickness > 0) ? scrollBarThickness + : getLookAndFeel().getDefaultScrollbarWidth(); +} + +void Viewport::setScrollBarButtonVisibility (const bool buttonsVisible) +{ + verticalScrollBar->setButtonVisibility (buttonsVisible); + horizontalScrollBar->setButtonVisibility (buttonsVisible); +} + +void Viewport::scrollBarMoved (ScrollBar* scrollBarThatHasMoved, const double newRangeStart) +{ + if (scrollBarThatHasMoved == horizontalScrollBar) + { + setViewPosition (roundDoubleToInt (newRangeStart), getViewPositionY()); + } + else if (scrollBarThatHasMoved == verticalScrollBar) + { + setViewPosition (getViewPositionX(), roundDoubleToInt (newRangeStart)); + } +} + +void Viewport::mouseWheelMove (const MouseEvent& e, float wheelIncrementX, float wheelIncrementY) +{ + if (! useMouseWheelMoveIfNeeded (e, wheelIncrementX, wheelIncrementY)) + Component::mouseWheelMove (e, wheelIncrementX, wheelIncrementY); +} + +bool Viewport::useMouseWheelMoveIfNeeded (const MouseEvent& e, float wheelIncrementX, float wheelIncrementY) +{ + if (! (e.mods.isAltDown() || e.mods.isCtrlDown())) + { + const bool hasVertBar = verticalScrollBar->isVisible(); + const bool hasHorzBar = horizontalScrollBar->isVisible(); + + if (hasHorzBar && (wheelIncrementX != 0 || e.mods.isShiftDown() || ! hasVertBar)) + { + horizontalScrollBar->mouseWheelMove (e.getEventRelativeTo (horizontalScrollBar), + wheelIncrementX, wheelIncrementY); + return true; + } + else if (hasVertBar && wheelIncrementY != 0) + { + verticalScrollBar->mouseWheelMove (e.getEventRelativeTo (verticalScrollBar), + wheelIncrementX, wheelIncrementY); + return true; + } + } + + return false; +} + +bool Viewport::keyPressed (const KeyPress& key) +{ + const bool isUpDownKey = key.isKeyCode (KeyPress::upKey) + || key.isKeyCode (KeyPress::downKey) + || key.isKeyCode (KeyPress::pageUpKey) + || key.isKeyCode (KeyPress::pageDownKey) + || key.isKeyCode (KeyPress::homeKey) + || key.isKeyCode (KeyPress::endKey); + + if (verticalScrollBar->isVisible() && isUpDownKey) + return verticalScrollBar->keyPressed (key); + + const bool isLeftRightKey = key.isKeyCode (KeyPress::leftKey) + || key.isKeyCode (KeyPress::rightKey); + + if (horizontalScrollBar->isVisible() && (isUpDownKey || isLeftRightKey)) + return horizontalScrollBar->keyPressed (key); + + return false; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_Viewport.cpp *********/ + +/********* Start of inlined file: juce_LookAndFeel.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +static const Colour createBaseColour (const Colour& buttonColour, + const bool hasKeyboardFocus, + const bool isMouseOverButton, + const bool isButtonDown) throw() +{ + const float sat = hasKeyboardFocus ? 1.3f : 0.9f; + const Colour baseColour (buttonColour.withMultipliedSaturation (sat)); + + if (isButtonDown) + return baseColour.contrasting (0.2f); + else if (isMouseOverButton) + return baseColour.contrasting (0.1f); + + return baseColour; +} + +LookAndFeel::LookAndFeel() +{ + /* if this fails it means you're trying to create a LookAndFeel object before + the static Colours have been initialised. That ain't gonna work. It probably + means that you're using a static LookAndFeel object and that your compiler has + decided to intialise it before the Colours class. + */ + jassert (Colours::white == Colour (0xffffffff)); + + // set up the standard set of colours.. + #define textButtonColour 0xffbbbbff + #define textHighlightColour 0x401111ee + #define standardOutlineColour 0xb2808080 + + static const int standardColours[] = + { + TextButton::buttonColourId, textButtonColour, + TextButton::buttonOnColourId, 0xff4444ff, + TextButton::textColourId, 0xff000000, + + ComboBox::buttonColourId, 0xffbbbbff, + ComboBox::outlineColourId, standardOutlineColour, + + ToggleButton::textColourId, 0xff000000, + + TextEditor::backgroundColourId, 0xffffffff, + TextEditor::textColourId, 0xff000000, + TextEditor::highlightColourId, textHighlightColour, + TextEditor::highlightedTextColourId, 0xff000000, + TextEditor::caretColourId, 0xff000000, + TextEditor::outlineColourId, 0x00000000, + TextEditor::focusedOutlineColourId, textButtonColour, + TextEditor::shadowColourId, 0x38000000, + + Label::backgroundColourId, 0x00000000, + Label::textColourId, 0xff000000, + Label::outlineColourId, 0x00000000, + + ScrollBar::backgroundColourId, 0x00000000, + ScrollBar::thumbColourId, 0xffffffff, + ScrollBar::trackColourId, 0xffffffff, + + TreeView::linesColourId, 0x4c000000, + TreeView::backgroundColourId, 0x00000000, + + PopupMenu::backgroundColourId, 0xffffffff, + PopupMenu::textColourId, 0xff000000, + PopupMenu::headerTextColourId, 0xff000000, + PopupMenu::highlightedTextColourId, 0xffffffff, + PopupMenu::highlightedBackgroundColourId, 0x991111aa, + + ComboBox::textColourId, 0xff000000, + ComboBox::backgroundColourId, 0xffffffff, + ComboBox::arrowColourId, 0x99000000, + + ListBox::backgroundColourId, 0xffffffff, + ListBox::outlineColourId, standardOutlineColour, + ListBox::textColourId, 0xff000000, + + Slider::backgroundColourId, 0x00000000, + Slider::thumbColourId, textButtonColour, + Slider::trackColourId, 0x7fffffff, + Slider::rotarySliderFillColourId, 0x7f0000ff, + Slider::rotarySliderOutlineColourId, 0x66000000, + Slider::textBoxTextColourId, 0xff000000, + Slider::textBoxBackgroundColourId, 0xffffffff, + Slider::textBoxHighlightColourId, textHighlightColour, + Slider::textBoxOutlineColourId, standardOutlineColour, + + AlertWindow::backgroundColourId, 0xffededed, + AlertWindow::textColourId, 0xff000000, + AlertWindow::outlineColourId, 0xff666666, + + ProgressBar::backgroundColourId, 0xffeeeeee, + ProgressBar::foregroundColourId, 0xffaaaaee, + + TooltipWindow::backgroundColourId, 0xffeeeebb, + TooltipWindow::textColourId, 0xff000000, + TooltipWindow::outlineColourId, 0x4c000000, + + Toolbar::backgroundColourId, 0xfff6f8f9, + Toolbar::separatorColourId, 0x4c000000, + Toolbar::buttonMouseOverBackgroundColourId, 0x4c0000ff, + Toolbar::buttonMouseDownBackgroundColourId, 0x800000ff, + Toolbar::labelTextColourId, 0xff000000, + Toolbar::editingModeOutlineColourId, 0xffff0000, + + HyperlinkButton::textColourId, 0xcc1111ee, + + GroupComponent::outlineColourId, 0x66000000, + GroupComponent::textColourId, 0xff000000, + + DirectoryContentsDisplayComponent::highlightColourId, textHighlightColour, + DirectoryContentsDisplayComponent::textColourId, 0xff000000, + + 0x1000440, /*LassoComponent::lassoFillColourId*/ 0x66dddddd, + 0x1000441, /*LassoComponent::lassoOutlineColourId*/ 0x99111111, + + MidiKeyboardComponent::whiteNoteColourId, 0xffffffff, + MidiKeyboardComponent::blackNoteColourId, 0xff000000, + MidiKeyboardComponent::keySeparatorLineColourId, 0x66000000, + MidiKeyboardComponent::mouseOverKeyOverlayColourId, 0x80ffff00, + MidiKeyboardComponent::keyDownOverlayColourId, 0xffb6b600, + MidiKeyboardComponent::textLabelColourId, 0xff000000, + MidiKeyboardComponent::upDownButtonBackgroundColourId, 0xffd3d3d3, + MidiKeyboardComponent::upDownButtonArrowColourId, 0xff000000, + + ColourSelector::backgroundColourId, 0xffe5e5e5, + ColourSelector::labelTextColourId, 0xff000000, + + FileSearchPathListComponent::backgroundColourId, 0xffffffff, + }; + + for (int i = 0; i < numElementsInArray (standardColours); i += 2) + setColour (standardColours [i], Colour (standardColours [i + 1])); +} + +LookAndFeel::~LookAndFeel() +{ +} + +const Colour LookAndFeel::findColour (const int colourId) const throw() +{ + const int index = colourIds.indexOf (colourId); + + if (index >= 0) + return colours [index]; + + jassertfalse + return Colours::black; +} + +void LookAndFeel::setColour (const int colourId, const Colour& colour) throw() +{ + const int index = colourIds.indexOf (colourId); + + if (index >= 0) + colours.set (index, colour); + + colourIds.add (colourId); + colours.add (colour); +} + +static LookAndFeel* defaultLF = 0; +static LookAndFeel* currentDefaultLF = 0; + +LookAndFeel& LookAndFeel::getDefaultLookAndFeel() throw() +{ + // if this happens, your app hasn't initialised itself properly.. if you're + // trying to hack your own main() function, have a look at + // JUCEApplication::initialiseForGUI() + jassert (currentDefaultLF != 0); + + return *currentDefaultLF; +} + +void LookAndFeel::setDefaultLookAndFeel (LookAndFeel* newDefaultLookAndFeel) throw() +{ + if (newDefaultLookAndFeel == 0) + { + if (defaultLF == 0) + defaultLF = new LookAndFeel(); + + newDefaultLookAndFeel = defaultLF; + } + + currentDefaultLF = newDefaultLookAndFeel; + + for (int i = Desktop::getInstance().getNumComponents(); --i >= 0;) + { + Component* const c = Desktop::getInstance().getComponent (i); + + if (c != 0) + c->sendLookAndFeelChange(); + } +} + +void LookAndFeel::clearDefaultLookAndFeel() throw() +{ + if (currentDefaultLF == defaultLF) + currentDefaultLF = 0; + + deleteAndZero (defaultLF); +} + +void LookAndFeel::drawButtonBackground (Graphics& g, + Button& button, + const Colour& backgroundColour, + bool isMouseOverButton, + bool isButtonDown) +{ + const int width = button.getWidth(); + const int height = button.getHeight(); + + const float outlineThickness = button.isEnabled() ? ((isButtonDown || isMouseOverButton) ? 1.2f : 0.7f) : 0.4f; + const float halfThickness = outlineThickness * 0.5f; + + const float indentL = button.isConnectedOnLeft() ? 0.1f : halfThickness; + const float indentR = button.isConnectedOnRight() ? 0.1f : halfThickness; + const float indentT = button.isConnectedOnTop() ? 0.1f : halfThickness; + const float indentB = button.isConnectedOnBottom() ? 0.1f : halfThickness; + + const Colour baseColour (createBaseColour (backgroundColour, + button.hasKeyboardFocus (true), + isMouseOverButton, isButtonDown) + .withMultipliedAlpha (button.isEnabled() ? 1.0f : 0.5f)); + + drawGlassLozenge (g, + indentL, + indentT, + width - indentL - indentR, + height - indentT - indentB, + baseColour, outlineThickness, -1.0f, + button.isConnectedOnLeft(), + button.isConnectedOnRight(), + button.isConnectedOnTop(), + button.isConnectedOnBottom()); +} + +void LookAndFeel::drawButtonText (Graphics& g, TextButton& button, + bool /*isMouseOverButton*/, bool /*isButtonDown*/) +{ + g.setFont (button.getFont()); + g.setColour (button.findColour (TextButton::textColourId) + .withMultipliedAlpha (button.isEnabled() ? 1.0f : 0.5f)); + + const int yIndent = jmin (4, button.proportionOfHeight (0.3f)); + const int cornerSize = jmin (button.getHeight(), button.getWidth()) / 2; + + const int fontHeight = roundFloatToInt (g.getCurrentFont().getHeight() * 0.6f); + const int leftIndent = jmin (fontHeight, 2 + cornerSize / (button.isConnectedOnLeft() ? 4 : 2)); + const int rightIndent = jmin (fontHeight, 2 + cornerSize / (button.isConnectedOnRight() ? 4 : 2)); + + g.drawFittedText (button.getButtonText(), + leftIndent, + yIndent, + button.getWidth() - leftIndent - rightIndent, + button.getHeight() - yIndent * 2, + Justification::centred, 2); +} + +void LookAndFeel::drawTickBox (Graphics& g, + Component& component, + int x, int y, int w, int h, + const bool ticked, + const bool isEnabled, + const bool isMouseOverButton, + const bool isButtonDown) +{ + const float boxSize = w * 0.7f; + + drawGlassSphere (g, (float) x, y + (h - boxSize) * 0.5f, boxSize, + createBaseColour (component.findColour (TextButton::buttonColourId) + .withMultipliedAlpha (isEnabled ? 1.0f : 0.5f), + true, + isMouseOverButton, + isButtonDown), + isEnabled ? ((isButtonDown || isMouseOverButton) ? 1.1f : 0.5f) : 0.3f); + + if (ticked) + { + Path tick; + tick.startNewSubPath (1.5f, 3.0f); + tick.lineTo (3.0f, 6.0f); + tick.lineTo (6.0f, 0.0f); + + g.setColour (isEnabled ? Colours::black : Colours::grey); + + const AffineTransform trans (AffineTransform::scale (w / 9.0f, h / 9.0f) + .translated ((float) x, (float) y)); + + g.strokePath (tick, PathStrokeType (2.5f), trans); + } +} + +void LookAndFeel::drawToggleButton (Graphics& g, + ToggleButton& button, + bool isMouseOverButton, + bool isButtonDown) +{ + if (button.hasKeyboardFocus (true)) + { + g.setColour (button.findColour (TextEditor::focusedOutlineColourId)); + g.drawRect (0, 0, button.getWidth(), button.getHeight()); + } + + const int tickWidth = jmin (20, button.getHeight() - 4); + + drawTickBox (g, button, 4, (button.getHeight() - tickWidth) / 2, + tickWidth, tickWidth, + button.getToggleState(), + button.isEnabled(), + isMouseOverButton, + isButtonDown); + + g.setColour (button.findColour (ToggleButton::textColourId)); + g.setFont (jmin (15.0f, button.getHeight() * 0.6f)); + + if (! button.isEnabled()) + g.setOpacity (0.5f); + + const int textX = tickWidth + 5; + + g.drawFittedText (button.getButtonText(), + textX, 4, + button.getWidth() - textX - 2, button.getHeight() - 8, + Justification::centredLeft, 10); +} + +void LookAndFeel::changeToggleButtonWidthToFitText (ToggleButton& button) +{ + Font font (jmin (15.0f, button.getHeight() * 0.6f)); + + const int tickWidth = jmin (24, button.getHeight()); + + button.setSize (font.getStringWidth (button.getButtonText()) + tickWidth + 8, + button.getHeight()); +} + +void LookAndFeel::drawAlertBox (Graphics& g, + AlertWindow& alert, + const Rectangle& textArea, + TextLayout& textLayout) +{ + const int iconWidth = 80; + + const Colour background (alert.findColour (AlertWindow::backgroundColourId)); + + g.fillAll (background); + + int iconSpaceUsed = 0; + Justification alignment (Justification::horizontallyCentred); + + int iconSize = jmin (iconWidth + 50, alert.getHeight() + 20); + + if (alert.containsAnyExtraComponents() || alert.getNumButtons() > 2) + iconSize = jmin (iconSize, textArea.getHeight() + 50); + + const Rectangle iconRect (iconSize / -10, + iconSize / -10, + iconSize, + iconSize); + + if (alert.getAlertType() == AlertWindow::QuestionIcon + || alert.getAlertType() == AlertWindow::InfoIcon) + { + if (alert.getAlertType() == AlertWindow::InfoIcon) + g.setColour (background.overlaidWith (Colour (0x280000ff))); + else + g.setColour (background.overlaidWith (Colours::gold.darker().withAlpha (0.25f))); + + g.fillEllipse ((float) iconRect.getX(), + (float) iconRect.getY(), + (float) iconRect.getWidth(), + (float) iconRect.getHeight()); + + g.setColour (background); + g.setFont (iconRect.getHeight() * 0.9f, Font::bold); + g.drawText ((alert.getAlertType() == AlertWindow::InfoIcon) ? "i" + : "?", + iconRect.getX(), + iconRect.getY(), + iconRect.getWidth(), + iconRect.getHeight(), + Justification::centred, false); + + iconSpaceUsed = iconWidth; + alignment = Justification::left; + } + else if (alert.getAlertType() == AlertWindow::WarningIcon) + { + Path p; + p.addTriangle (iconRect.getX() + iconRect.getWidth() * 0.5f, + (float) iconRect.getY(), + (float) iconRect.getRight(), + (float) iconRect.getBottom(), + (float) iconRect.getX(), + (float) iconRect.getBottom()); + + g.setColour (background.overlaidWith (Colour (0x33ff0000))); + g.fillPath (p.createPathWithRoundedCorners (5.0f)); + + g.setColour (background); + g.setFont (iconRect.getHeight() * 0.9f, Font::bold); + + g.drawText (T("!"), + iconRect.getX(), + iconRect.getY(), + iconRect.getWidth(), + iconRect.getHeight() + iconRect.getHeight() / 8, + Justification::centred, false); + + iconSpaceUsed = iconWidth; + alignment = Justification::left; + } + + g.setColour (alert.findColour (AlertWindow::textColourId)); + + textLayout.drawWithin (g, + textArea.getX() + iconSpaceUsed, + textArea.getY(), + textArea.getWidth() - iconSpaceUsed, + textArea.getHeight(), + alignment.getFlags() | Justification::top); + + g.setColour (alert.findColour (AlertWindow::outlineColourId)); + g.drawRect (0, 0, alert.getWidth(), alert.getHeight()); +} + +int LookAndFeel::getAlertBoxWindowFlags() +{ + return ComponentPeer::windowAppearsOnTaskbar + | ComponentPeer::windowHasDropShadow; +} + +void LookAndFeel::drawProgressBar (Graphics& g, ProgressBar& progressBar, + int width, int height, + double progress, const String& textToShow) +{ + const Colour background (progressBar.findColour (ProgressBar::backgroundColourId)); + const Colour foreground (progressBar.findColour (ProgressBar::foregroundColourId)); + + g.fillAll (background); + + if (progress >= 0.0f && progress < 1.0f) + { + drawGlassLozenge (g, 1.0f, 1.0f, + (float) jlimit (0.0, width - 2.0, progress * (width - 2.0)), + (float) (height - 2), + foreground, + 0.5f, 0.0f, + true, true, true, true); + } + else + { + // spinning bar.. + g.setColour (foreground); + + const int stripeWidth = height * 2; + const int position = (Time::getMillisecondCounter() / 15) % stripeWidth; + + Path p; + + for (float x = (float) (stripeWidth - position); x < width + stripeWidth; x += stripeWidth) + p.addQuadrilateral (x, 0.0f, + x + stripeWidth * 0.5f, 0.0f, + x, (float) height, + x - stripeWidth * 0.5f, (float) height); + + Image im (Image::ARGB, width, height, true); + + { + Graphics g (im); + drawGlassLozenge (g, 1.0f, 1.0f, + (float) (width - 2), + (float) (height - 2), + foreground, + 0.5f, 0.0f, + true, true, true, true); + } + + ImageBrush ib (&im, 0, 0, 0.85f); + g.setBrush (&ib); + + g.fillPath (p); + } + + if (textToShow.isNotEmpty()) + { + g.setColour (Colour::contrasting (background, foreground)); + g.setFont (height * 0.6f); + + g.drawText (textToShow, 0, 0, width, height, Justification::centred, false); + } +} + +void LookAndFeel::drawScrollbarButton (Graphics& g, + ScrollBar& scrollbar, + int width, int height, + int buttonDirection, + bool /*isScrollbarVertical*/, + bool /*isMouseOverButton*/, + bool isButtonDown) +{ + Path p; + + if (buttonDirection == 0) + p.addTriangle (width * 0.5f, height * 0.2f, + width * 0.1f, height * 0.7f, + width * 0.9f, height * 0.7f); + else if (buttonDirection == 1) + p.addTriangle (width * 0.8f, height * 0.5f, + width * 0.3f, height * 0.1f, + width * 0.3f, height * 0.9f); + else if (buttonDirection == 2) + p.addTriangle (width * 0.5f, height * 0.8f, + width * 0.1f, height * 0.3f, + width * 0.9f, height * 0.3f); + else if (buttonDirection == 3) + p.addTriangle (width * 0.2f, height * 0.5f, + width * 0.7f, height * 0.1f, + width * 0.7f, height * 0.9f); + + if (isButtonDown) + g.setColour (scrollbar.findColour (ScrollBar::thumbColourId).contrasting (0.2f)); + else + g.setColour (scrollbar.findColour (ScrollBar::thumbColourId)); + + g.fillPath (p); + + g.setColour (Colour (0x80000000)); + g.strokePath (p, PathStrokeType (0.5f)); +} + +void LookAndFeel::drawScrollbar (Graphics& g, + ScrollBar& scrollbar, + int x, int y, + int width, int height, + bool isScrollbarVertical, + int thumbStartPosition, + int thumbSize, + bool /*isMouseOver*/, + bool /*isMouseDown*/) +{ + g.fillAll (scrollbar.findColour (ScrollBar::backgroundColourId)); + + Path slotPath, thumbPath; + + const float slotIndent = jmin (width, height) > 15 ? 1.0f : 0.0f; + const float slotIndentx2 = slotIndent * 2.0f; + const float thumbIndent = slotIndent + 1.0f; + const float thumbIndentx2 = thumbIndent * 2.0f; + + float gx1 = 0.0f, gy1 = 0.0f, gx2 = 0.0f, gy2 = 0.0f; + + if (isScrollbarVertical) + { + slotPath.addRoundedRectangle (x + slotIndent, + y + slotIndent, + width - slotIndentx2, + height - slotIndentx2, + (width - slotIndentx2) * 0.5f); + + if (thumbSize > 0) + thumbPath.addRoundedRectangle (x + thumbIndent, + thumbStartPosition + thumbIndent, + width - thumbIndentx2, + thumbSize - thumbIndentx2, + (width - thumbIndentx2) * 0.5f); + gx1 = (float) x; + gx2 = x + width * 0.7f; + } + else + { + slotPath.addRoundedRectangle (x + slotIndent, + y + slotIndent, + width - slotIndentx2, + height - slotIndentx2, + (height - slotIndentx2) * 0.5f); + + if (thumbSize > 0) + thumbPath.addRoundedRectangle (thumbStartPosition + thumbIndent, + y + thumbIndent, + thumbSize - thumbIndentx2, + height - thumbIndentx2, + (height - thumbIndentx2) * 0.5f); + gy1 = (float) y; + gy2 = y + height * 0.7f; + } + + const Colour thumbColour (scrollbar.findColour (ScrollBar::trackColourId)); + + GradientBrush gb (thumbColour.overlaidWith (Colour (0x44000000)), + gx1, gy1, + thumbColour.overlaidWith (Colour (0x19000000)), + gx2, gy2, false); + + g.setBrush (&gb); + g.fillPath (slotPath); + + if (isScrollbarVertical) + { + gx1 = x + width * 0.6f; + gx2 = (float) x + width; + } + else + { + gy1 = y + height * 0.6f; + gy2 = (float) y + height; + } + + GradientBrush gb2 (Colours::transparentBlack, + gx1, gy1, + Colour (0x19000000), + gx2, gy2, false); + + g.setBrush (&gb2); + g.fillPath (slotPath); + + g.setColour (thumbColour); + g.fillPath (thumbPath); + + GradientBrush gb3 (Colour (0x10000000), + gx1, gy1, + Colours::transparentBlack, + gx2, gy2, false); + + g.saveState(); + g.setBrush (&gb3); + + if (isScrollbarVertical) + g.reduceClipRegion (x + width / 2, y, width, height); + else + g.reduceClipRegion (x, y + height / 2, width, height); + + g.fillPath (thumbPath); + g.restoreState(); + + g.setColour (Colour (0x4c000000)); + g.strokePath (thumbPath, PathStrokeType (0.4f)); +} + +ImageEffectFilter* LookAndFeel::getScrollbarEffect() +{ + return 0; +} + +int LookAndFeel::getMinimumScrollbarThumbSize (ScrollBar& scrollbar) +{ + return jmin (scrollbar.getWidth(), scrollbar.getHeight()) * 2; +} + +int LookAndFeel::getDefaultScrollbarWidth() +{ + return 18; +} + +int LookAndFeel::getScrollbarButtonSize (ScrollBar& scrollbar) +{ + return 2 + (scrollbar.isVertical() ? scrollbar.getWidth() + : scrollbar.getHeight()); +} + +const Path LookAndFeel::getTickShape (const float height) +{ + static const unsigned char tickShapeData[] = + { + 109,0,224,168,68,0,0,119,67,108,0,224,172,68,0,128,146,67,113,0,192,148,68,0,0,219,67,0,96,110,68,0,224,56,68,113,0,64,51,68,0,32,130,68,0,64,20,68,0,224, + 162,68,108,0,128,3,68,0,128,168,68,113,0,128,221,67,0,192,175,68,0,0,207,67,0,32,179,68,113,0,0,201,67,0,224,173,68,0,0,181,67,0,224,161,68,108,0,128,168,67, + 0,128,154,68,113,0,128,141,67,0,192,138,68,0,128,108,67,0,64,131,68,113,0,0,62,67,0,128,119,68,0,0,5,67,0,128,114,68,113,0,0,102,67,0,192,88,68,0,128,155, + 67,0,192,88,68,113,0,0,190,67,0,192,88,68,0,128,232,67,0,224,131,68,108,0,128,246,67,0,192,139,68,113,0,64,33,68,0,128,87,68,0,0,93,68,0,224,26,68,113,0, + 96,140,68,0,128,188,67,0,224,168,68,0,0,119,67,99,101 + }; + + Path p; + p.loadPathFromData (tickShapeData, sizeof (tickShapeData)); + p.scaleToFit (0, 0, height * 2.0f, height, true); + return p; +} + +const Path LookAndFeel::getCrossShape (const float height) +{ + static const unsigned char crossShapeData[] = + { + 109,0,0,17,68,0,96,145,68,108,0,192,13,68,0,192,147,68,113,0,0,213,67,0,64,174,68,0,0,168,67,0,64,174,68,113,0,0,104,67,0,64,174,68,0,0,5,67,0,64, + 153,68,113,0,0,18,67,0,64,153,68,0,0,24,67,0,64,153,68,113,0,0,135,67,0,64,153,68,0,128,207,67,0,224,130,68,108,0,0,220,67,0,0,126,68,108,0,0,204,67, + 0,128,117,68,113,0,0,138,67,0,64,82,68,0,0,138,67,0,192,57,68,113,0,0,138,67,0,192,37,68,0,128,210,67,0,64,10,68,113,0,128,220,67,0,64,45,68,0,0,8, + 68,0,128,78,68,108,0,192,14,68,0,0,87,68,108,0,64,20,68,0,0,80,68,113,0,192,57,68,0,0,32,68,0,128,88,68,0,0,32,68,113,0,64,112,68,0,0,32,68,0, + 128,124,68,0,64,68,68,113,0,0,121,68,0,192,67,68,0,128,119,68,0,192,67,68,113,0,192,108,68,0,192,67,68,0,32,89,68,0,96,82,68,113,0,128,69,68,0,0,97,68, + 0,0,56,68,0,64,115,68,108,0,64,49,68,0,128,124,68,108,0,192,55,68,0,96,129,68,113,0,0,92,68,0,224,146,68,0,192,129,68,0,224,146,68,113,0,64,110,68,0,64, + 168,68,0,64,87,68,0,64,168,68,113,0,128,66,68,0,64,168,68,0,64,27,68,0,32,150,68,99,101 + }; + + Path p; + p.loadPathFromData (crossShapeData, sizeof (crossShapeData)); + p.scaleToFit (0, 0, height * 2.0f, height, true); + return p; +} + +void LookAndFeel::drawTreeviewPlusMinusBox (Graphics& g, int x, int y, int w, int h, bool isPlus) +{ + const int boxSize = ((jmin (16, w, h) << 1) / 3) | 1; + + x += (w - boxSize) >> 1; + y += (h - boxSize) >> 1; + w = boxSize; + h = boxSize; + + g.setColour (Colour (0xe5ffffff)); + g.fillRect (x, y, w, h); + + g.setColour (Colour (0x80000000)); + g.drawRect (x, y, w, h); + + const float size = boxSize / 2 + 1.0f; + const float centre = (float) (boxSize / 2); + + g.fillRect (x + (w - size) * 0.5f, y + centre, size, 1.0f); + + if (isPlus) + g.fillRect (x + centre, y + (h - size) * 0.5f, 1.0f, size); +} + +void LookAndFeel::drawBubble (Graphics& g, + float tipX, float tipY, + float boxX, float boxY, + float boxW, float boxH) +{ + int side = 0; + + if (tipX < boxX) + side = 1; + else if (tipX > boxX + boxW) + side = 3; + else if (tipY > boxY + boxH) + side = 2; + + const float indent = 2.0f; + Path p; + p.addBubble (boxX + indent, + boxY + indent, + boxW - indent * 2.0f, + boxH - indent * 2.0f, + 5.0f, + tipX, tipY, + side, + 0.5f, + jmin (15.0f, boxW * 0.3f, boxH * 0.3f)); + + //xxx need to take comp as param for colour + g.setColour (findColour (TooltipWindow::backgroundColourId).withAlpha (0.9f)); + g.fillPath (p); + + //xxx as above + g.setColour (findColour (TooltipWindow::textColourId).withAlpha (0.4f)); + g.strokePath (p, PathStrokeType (1.33f)); +} + +const Font LookAndFeel::getPopupMenuFont() +{ + return Font (17.0f); +} + +void LookAndFeel::getIdealPopupMenuItemSize (const String& text, + const bool isSeparator, + int standardMenuItemHeight, + int& idealWidth, + int& idealHeight) +{ + if (isSeparator) + { + idealWidth = 50; + idealHeight = standardMenuItemHeight > 0 ? standardMenuItemHeight / 2 : 10; + } + else + { + Font font (getPopupMenuFont()); + + if (standardMenuItemHeight > 0 && font.getHeight() > standardMenuItemHeight / 1.3f) + font.setHeight (standardMenuItemHeight / 1.3f); + + idealHeight = standardMenuItemHeight > 0 ? standardMenuItemHeight : roundFloatToInt (font.getHeight() * 1.3f); + idealWidth = font.getStringWidth (text) + idealHeight * 2; + } +} + +void LookAndFeel::drawPopupMenuBackground (Graphics& g, int width, int height) +{ + const Colour background (findColour (PopupMenu::backgroundColourId)); + + g.fillAll (background); + g.setColour (background.overlaidWith (Colour (0x2badd8e6))); + + for (int i = 0; i < height; i += 3) + g.fillRect (0, i, width, 1); + + g.setColour (findColour (PopupMenu::textColourId).withAlpha (0.6f)); + g.drawRect (0, 0, width, height); +} + +void LookAndFeel::drawPopupMenuUpDownArrow (Graphics& g, + int width, int height, + bool isScrollUpArrow) +{ + const Colour background (findColour (PopupMenu::backgroundColourId)); + + GradientBrush gb (background, + 0.0f, height * 0.5f, + background.withAlpha (0.0f), + 0.0f, isScrollUpArrow ? ((float) height) : 0.0f, + false); + + g.setBrush (&gb); + g.fillRect (1, 1, width - 2, height - 2); + + const float hw = width * 0.5f; + const float arrowW = height * 0.3f; + const float y1 = height * (isScrollUpArrow ? 0.6f : 0.3f); + const float y2 = height * (isScrollUpArrow ? 0.3f : 0.6f); + + Path p; + p.addTriangle (hw - arrowW, y1, + hw + arrowW, y1, + hw, y2); + + g.setColour (findColour (PopupMenu::textColourId).withAlpha (0.5f)); + g.fillPath (p); +} + +void LookAndFeel::drawPopupMenuItem (Graphics& g, + int width, int height, + const bool isSeparator, + const bool isActive, + const bool isHighlighted, + const bool isTicked, + const bool hasSubMenu, + const String& text, + const String& shortcutKeyText, + Image* image, + const Colour* const textColourToUse) +{ + const float halfH = height * 0.5f; + + if (isSeparator) + { + const float separatorIndent = 5.5f; + + g.setColour (Colour (0x33000000)); + g.drawLine (separatorIndent, halfH, width - separatorIndent, halfH); + + g.setColour (Colour (0x66ffffff)); + g.drawLine (separatorIndent, halfH + 1.0f, width - separatorIndent, halfH + 1.0f); + } + else + { + Colour textColour (findColour (PopupMenu::textColourId)); + + if (textColourToUse != 0) + textColour = *textColourToUse; + + if (isHighlighted) + { + g.setColour (findColour (PopupMenu::highlightedBackgroundColourId)); + g.fillRect (1, 1, width - 2, height - 2); + + g.setColour (findColour (PopupMenu::highlightedTextColourId)); + } + else + { + g.setColour (textColour); + } + + if (! isActive) + g.setOpacity (0.3f); + + Font font (getPopupMenuFont()); + + if (font.getHeight() > height / 1.3f) + font.setHeight (height / 1.3f); + + g.setFont (font); + + const int leftBorder = (height * 5) / 4; + const int rightBorder = 4; + + if (image != 0) + { + g.drawImageWithin (image, + 2, 1, leftBorder - 4, height - 2, + RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize, false); + } + else if (isTicked) + { + const Path tick (getTickShape (1.0f)); + const float th = font.getAscent(); + const float ty = halfH - th * 0.5f; + + g.fillPath (tick, tick.getTransformToScaleToFit (2.0f, ty, (float) (leftBorder - 4), + th, true)); + } + + g.drawFittedText (text, + leftBorder, 0, + width - (leftBorder + rightBorder), height, + Justification::centredLeft, 1); + + if (shortcutKeyText.isNotEmpty()) + { + Font f2 (g.getCurrentFont()); + f2.setHeight (f2.getHeight() * 0.75f); + f2.setHorizontalScale (0.95f); + g.setFont (f2); + + g.drawText (shortcutKeyText, + leftBorder, + 0, + width - (leftBorder + rightBorder + 4), + height, + Justification::centredRight, + true); + } + + if (hasSubMenu) + { + const float arrowH = 0.6f * getPopupMenuFont().getAscent(); + const float x = width - height * 0.6f; + + Path p; + p.addTriangle (x, halfH - arrowH * 0.5f, + x, halfH + arrowH * 0.5f, + x + arrowH * 0.6f, halfH); + + g.fillPath (p); + } + } +} + +int LookAndFeel::getMenuWindowFlags() +{ + return ComponentPeer::windowHasDropShadow; +} + +void LookAndFeel::drawMenuBarBackground (Graphics& g, int width, int height, + bool, MenuBarComponent& menuBar) +{ + const Colour baseColour (createBaseColour (menuBar.findColour (PopupMenu::backgroundColourId), false, false, false)); + + if (menuBar.isEnabled()) + { + drawShinyButtonShape (g, + -4.0f, 0.0f, + width + 8.0f, (float) height, + 0.0f, + baseColour, + 0.4f, + true, true, true, true); + } + else + { + g.fillAll (baseColour); + } +} + +const Font LookAndFeel::getMenuBarFont (MenuBarComponent& menuBar, int /*itemIndex*/, const String& /*itemText*/) +{ + return Font (menuBar.getHeight() * 0.7f); +} + +int LookAndFeel::getMenuBarItemWidth (MenuBarComponent& menuBar, int itemIndex, const String& itemText) +{ + return getMenuBarFont (menuBar, itemIndex, itemText) + .getStringWidth (itemText) + menuBar.getHeight(); +} + +void LookAndFeel::drawMenuBarItem (Graphics& g, + int width, int height, + int itemIndex, + const String& itemText, + bool isMouseOverItem, + bool isMenuOpen, + bool /*isMouseOverBar*/, + MenuBarComponent& menuBar) +{ + if (! menuBar.isEnabled()) + { + g.setColour (menuBar.findColour (PopupMenu::textColourId) + .withMultipliedAlpha (0.5f)); + } + else if (isMenuOpen || isMouseOverItem) + { + g.fillAll (menuBar.findColour (PopupMenu::highlightedBackgroundColourId)); + g.setColour (menuBar.findColour (PopupMenu::highlightedTextColourId)); + } + else + { + g.setColour (menuBar.findColour (PopupMenu::textColourId)); + } + + g.setFont (getMenuBarFont (menuBar, itemIndex, itemText)); + g.drawFittedText (itemText, 0, 0, width, height, Justification::centred, 1); +} + +void LookAndFeel::fillTextEditorBackground (Graphics& g, int /*width*/, int /*height*/, + TextEditor& textEditor) +{ + g.fillAll (textEditor.findColour (TextEditor::backgroundColourId)); +} + +void LookAndFeel::drawTextEditorOutline (Graphics& g, int width, int height, TextEditor& textEditor) +{ + if (textEditor.isEnabled()) + { + if (textEditor.hasKeyboardFocus (true) && ! textEditor.isReadOnly()) + { + const int border = 2; + + g.setColour (textEditor.findColour (TextEditor::focusedOutlineColourId)); + g.drawRect (0, 0, width, height, border); + + g.setOpacity (1.0f); + const Colour shadowColour (textEditor.findColour (TextEditor::shadowColourId).withMultipliedAlpha (0.75f)); + g.drawBevel (0, 0, width, height + 2, border + 2, shadowColour, shadowColour); + } + else + { + g.setColour (textEditor.findColour (TextEditor::outlineColourId)); + g.drawRect (0, 0, width, height); + + g.setOpacity (1.0f); + const Colour shadowColour (textEditor.findColour (TextEditor::shadowColourId)); + g.drawBevel (0, 0, width, height + 2, 3, shadowColour, shadowColour); + } + } +} + +void LookAndFeel::drawComboBox (Graphics& g, int width, int height, + const bool isButtonDown, + int buttonX, int buttonY, + int buttonW, int buttonH, + ComboBox& box) +{ + g.fillAll (box.findColour (ComboBox::backgroundColourId)); + + if (box.isEnabled() && box.hasKeyboardFocus (false)) + { + g.setColour (box.findColour (TextButton::buttonColourId)); + g.drawRect (0, 0, width, height, 2); + } + else + { + g.setColour (box.findColour (ComboBox::outlineColourId)); + g.drawRect (0, 0, width, height); + } + + const float outlineThickness = box.isEnabled() ? (isButtonDown ? 1.2f : 0.5f) : 0.3f; + + const Colour baseColour (createBaseColour (box.findColour (ComboBox::buttonColourId), + box.hasKeyboardFocus (true), + false, isButtonDown) + .withMultipliedAlpha (box.isEnabled() ? 1.0f : 0.5f)); + + drawGlassLozenge (g, + buttonX + outlineThickness, buttonY + outlineThickness, + buttonW - outlineThickness * 2.0f, buttonH - outlineThickness * 2.0f, + baseColour, outlineThickness, -1.0f, + true, true, true, true); + + if (box.isEnabled()) + { + const float arrowX = 0.3f; + const float arrowH = 0.2f; + + Path p; + p.addTriangle (buttonX + buttonW * 0.5f, buttonY + buttonH * (0.45f - arrowH), + buttonX + buttonW * (1.0f - arrowX), buttonY + buttonH * 0.45f, + buttonX + buttonW * arrowX, buttonY + buttonH * 0.45f); + + p.addTriangle (buttonX + buttonW * 0.5f, buttonY + buttonH * (0.55f + arrowH), + buttonX + buttonW * (1.0f - arrowX), buttonY + buttonH * 0.55f, + buttonX + buttonW * arrowX, buttonY + buttonH * 0.55f); + + g.setColour (box.findColour (ComboBox::arrowColourId)); + g.fillPath (p); + } +} + +const Font LookAndFeel::getComboBoxFont (ComboBox& box) +{ + return Font (jmin (15.0f, box.getHeight() * 0.85f)); +} + +Label* LookAndFeel::createComboBoxTextBox (ComboBox&) +{ + return new Label (String::empty, String::empty); +} + +void LookAndFeel::drawLinearSliderBackground (Graphics& g, + int x, int y, + int width, int height, + float /*sliderPos*/, + float /*minSliderPos*/, + float /*maxSliderPos*/, + const Slider::SliderStyle /*style*/, + Slider& slider) +{ + const float sliderRadius = (float) (getSliderThumbRadius (slider) - 2); + + const Colour trackColour (slider.findColour (Slider::trackColourId)); + const Colour gradCol1 (trackColour.overlaidWith (Colours::black.withAlpha (slider.isEnabled() ? 0.25f : 0.13f))); + const Colour gradCol2 (trackColour.overlaidWith (Colour (0x14000000))); + Path indent; + + if (slider.isHorizontal()) + { + const float iy = y + height * 0.5f - sliderRadius * 0.5f; + const float ih = sliderRadius; + + GradientBrush gb (gradCol1, 0.0f, iy, + gradCol2, 0.0f, iy + ih, false); + g.setBrush (&gb); + + indent.addRoundedRectangle (x - sliderRadius * 0.5f, iy, + width + sliderRadius, ih, + 5.0f); + g.fillPath (indent); + } + else + { + const float ix = x + width * 0.5f - sliderRadius * 0.5f; + const float iw = sliderRadius; + + GradientBrush gb (gradCol1, ix, 0.0f, + gradCol2, ix + iw, 0.0f, false); + g.setBrush (&gb); + + indent.addRoundedRectangle (ix, y - sliderRadius * 0.5f, + iw, height + sliderRadius, + 5.0f); + g.fillPath (indent); + } + + g.setColour (Colour (0x4c000000)); + g.strokePath (indent, PathStrokeType (0.5f)); +} + +void LookAndFeel::drawLinearSliderThumb (Graphics& g, + int x, int y, + int width, int height, + float sliderPos, + float minSliderPos, + float maxSliderPos, + const Slider::SliderStyle style, + Slider& slider) +{ + const float sliderRadius = (float) (getSliderThumbRadius (slider) - 2); + + Colour knobColour (createBaseColour (slider.findColour (Slider::thumbColourId), + slider.hasKeyboardFocus (false) && slider.isEnabled(), + slider.isMouseOverOrDragging() && slider.isEnabled(), + slider.isMouseButtonDown() && slider.isEnabled())); + + const float outlineThickness = slider.isEnabled() ? 0.8f : 0.3f; + + if (style == Slider::LinearHorizontal || style == Slider::LinearVertical) + { + float kx, ky; + + if (style == Slider::LinearVertical) + { + kx = x + width * 0.5f; + ky = sliderPos; + } + else + { + kx = sliderPos; + ky = y + height * 0.5f; + } + + drawGlassSphere (g, + kx - sliderRadius, + ky - sliderRadius, + sliderRadius * 2.0f, + knobColour, outlineThickness); + } + else + { + if (style == Slider::ThreeValueVertical) + { + drawGlassSphere (g, x + width * 0.5f - sliderRadius, + sliderPos - sliderRadius, + sliderRadius * 2.0f, + knobColour, outlineThickness); + } + else if (style == Slider::ThreeValueHorizontal) + { + drawGlassSphere (g,sliderPos - sliderRadius, + y + height * 0.5f - sliderRadius, + sliderRadius * 2.0f, + knobColour, outlineThickness); + } + + if (style == Slider::TwoValueVertical || style == Slider::ThreeValueVertical) + { + const float sr = jmin (sliderRadius, width * 0.4f); + + drawGlassPointer (g, jmax (0.0f, x + width * 0.5f - sliderRadius * 2.0f), + minSliderPos - sliderRadius, + sliderRadius * 2.0f, knobColour, outlineThickness, 1); + + drawGlassPointer (g, jmin (x + width - sliderRadius * 2.0f, x + width * 0.5f), maxSliderPos - sr, + sliderRadius * 2.0f, knobColour, outlineThickness, 3); + } + else if (style == Slider::TwoValueHorizontal || style == Slider::ThreeValueHorizontal) + { + const float sr = jmin (sliderRadius, height * 0.4f); + + drawGlassPointer (g, minSliderPos - sr, + jmax (0.0f, y + height * 0.5f - sliderRadius * 2.0f), + sliderRadius * 2.0f, knobColour, outlineThickness, 2); + + drawGlassPointer (g, maxSliderPos - sliderRadius, + jmin (y + height - sliderRadius * 2.0f, y + height * 0.5f), + sliderRadius * 2.0f, knobColour, outlineThickness, 4); + } + } +} + +void LookAndFeel::drawLinearSlider (Graphics& g, + int x, int y, + int width, int height, + float sliderPos, + float minSliderPos, + float maxSliderPos, + const Slider::SliderStyle style, + Slider& slider) +{ + g.fillAll (slider.findColour (Slider::backgroundColourId)); + + if (style == Slider::LinearBar) + { + const bool isMouseOver = slider.isMouseOverOrDragging() && slider.isEnabled(); + + Colour baseColour (createBaseColour (slider.findColour (Slider::thumbColourId) + .withMultipliedSaturation (slider.isEnabled() ? 1.0f : 0.5f), + false, + isMouseOver, + isMouseOver || slider.isMouseButtonDown())); + + drawShinyButtonShape (g, + (float) x, (float) y, sliderPos - (float) x, (float) height, 0.0f, + baseColour, + slider.isEnabled() ? 0.9f : 0.3f, + true, true, true, true); + } + else + { + drawLinearSliderBackground (g, x, y, width, height, sliderPos, minSliderPos, maxSliderPos, style, slider); + drawLinearSliderThumb (g, x, y, width, height, sliderPos, minSliderPos, maxSliderPos, style, slider); + } +} + +int LookAndFeel::getSliderThumbRadius (Slider& slider) +{ + return jmin (7, + slider.getHeight() / 2, + slider.getWidth() / 2) + 2; +} + +void LookAndFeel::drawRotarySlider (Graphics& g, + int x, int y, + int width, int height, + float sliderPos, + const float rotaryStartAngle, + const float rotaryEndAngle, + Slider& slider) +{ + const float radius = jmin (width / 2, height / 2) - 2.0f; + const float centreX = x + width * 0.5f; + const float centreY = y + height * 0.5f; + const float rx = centreX - radius; + const float ry = centreY - radius; + const float rw = radius * 2.0f; + const float angle = rotaryStartAngle + sliderPos * (rotaryEndAngle - rotaryStartAngle); + const bool isMouseOver = slider.isMouseOverOrDragging() && slider.isEnabled(); + + if (radius > 12.0f) + { + if (slider.isEnabled()) + g.setColour (slider.findColour (Slider::rotarySliderFillColourId).withAlpha (isMouseOver ? 1.0f : 0.7f)); + else + g.setColour (Colour (0x80808080)); + + const float thickness = 0.7f; + + { + Path filledArc; + filledArc.addPieSegment (rx, ry, rw, rw, + rotaryStartAngle, + angle, + thickness); + + g.fillPath (filledArc); + } + + if (thickness > 0) + { + const float innerRadius = radius * 0.2f; + Path p; + p.addTriangle (-innerRadius, 0.0f, + 0.0f, -radius * thickness * 1.1f, + innerRadius, 0.0f); + + p.addEllipse (-innerRadius, -innerRadius, innerRadius * 2.0f, innerRadius * 2.0f); + + g.fillPath (p, AffineTransform::rotation (angle).translated (centreX, centreY)); + } + + if (slider.isEnabled()) + { + g.setColour (slider.findColour (Slider::rotarySliderOutlineColourId)); + Path outlineArc; + outlineArc.addPieSegment (rx, ry, rw, rw, rotaryStartAngle, rotaryEndAngle, thickness); + outlineArc.closeSubPath(); + + g.strokePath (outlineArc, PathStrokeType (slider.isEnabled() ? (isMouseOver ? 2.0f : 1.2f) : 0.3f)); + } + } + else + { + if (slider.isEnabled()) + g.setColour (slider.findColour (Slider::rotarySliderFillColourId).withAlpha (isMouseOver ? 1.0f : 0.7f)); + else + g.setColour (Colour (0x80808080)); + + Path p; + p.addEllipse (-0.4f * rw, -0.4f * rw, rw * 0.8f, rw * 0.8f); + PathStrokeType (rw * 0.1f).createStrokedPath (p, p); + + p.addLineSegment (0.0f, 0.0f, 0.0f, -radius, rw * 0.2f); + + g.fillPath (p, AffineTransform::rotation (angle).translated (centreX, centreY)); + } +} + +Button* LookAndFeel::createSliderButton (const bool isIncrement) +{ + return new TextButton (isIncrement ? "+" : "-", String::empty); +} + +Label* LookAndFeel::createSliderTextBox (Slider& slider) +{ + Label* const l = new Label (T("n"), String::empty); + + l->setJustificationType (Justification::centred); + + l->setColour (Label::textColourId, slider.findColour (Slider::textBoxTextColourId)); + + l->setColour (Label::backgroundColourId, + (slider.getSliderStyle() == Slider::LinearBar) ? Colours::transparentBlack + : slider.findColour (Slider::textBoxBackgroundColourId)); + l->setColour (Label::outlineColourId, slider.findColour (Slider::textBoxOutlineColourId)); + + l->setColour (TextEditor::textColourId, slider.findColour (Slider::textBoxTextColourId)); + + l->setColour (TextEditor::backgroundColourId, + slider.findColour (Slider::textBoxBackgroundColourId) + .withAlpha (slider.getSliderStyle() == Slider::LinearBar ? 0.7f : 1.0f)); + + l->setColour (TextEditor::outlineColourId, slider.findColour (Slider::textBoxOutlineColourId)); + + return l; +} + +ImageEffectFilter* LookAndFeel::getSliderEffect() +{ + return 0; +} + +static const TextLayout layoutTooltipText (const String& text) throw() +{ + const float tooltipFontSize = 15.0f; + const int maxToolTipWidth = 400; + + const Font f (tooltipFontSize, Font::bold); + TextLayout tl (text, f); + tl.layout (maxToolTipWidth, Justification::left, true); + + return tl; +} + +void LookAndFeel::getTooltipSize (const String& tipText, int& width, int& height) +{ + const TextLayout tl (layoutTooltipText (tipText)); + + width = tl.getWidth() + 14; + height = tl.getHeight() + 10; +} + +void LookAndFeel::drawTooltip (Graphics& g, const String& text, int width, int height) +{ + g.fillAll (findColour (TooltipWindow::backgroundColourId)); + + const Colour textCol (findColour (TooltipWindow::textColourId)); + + g.setColour (findColour (TooltipWindow::outlineColourId)); + g.drawRect (0, 0, width, height); + + const TextLayout tl (layoutTooltipText (text)); + + g.setColour (findColour (TooltipWindow::textColourId)); + tl.drawWithin (g, 0, 0, width, height, Justification::centred); +} + +Button* LookAndFeel::createFilenameComponentBrowseButton (const String& text) +{ + return new TextButton (text, TRANS("click to browse for a different file")); +} + +void LookAndFeel::layoutFilenameComponent (FilenameComponent& filenameComp, + ComboBox* filenameBox, + Button* browseButton) +{ + browseButton->setSize (80, filenameComp.getHeight()); + + TextButton* const tb = dynamic_cast (browseButton); + + if (tb != 0) + tb->changeWidthToFitText(); + + browseButton->setTopRightPosition (filenameComp.getWidth(), 0); + + filenameBox->setBounds (0, 0, browseButton->getX(), filenameComp.getHeight()); +} + +void LookAndFeel::drawCornerResizer (Graphics& g, + int w, int h, + bool /*isMouseOver*/, + bool /*isMouseDragging*/) +{ + const float lineThickness = jmin (w, h) * 0.075f; + + for (float i = 0.0f; i < 1.0f; i += 0.3f) + { + g.setColour (Colours::lightgrey); + + g.drawLine (w * i, + h + 1.0f, + w + 1.0f, + h * i, + lineThickness); + + g.setColour (Colours::darkgrey); + + g.drawLine (w * i + lineThickness, + h + 1.0f, + w + 1.0f, + h * i + lineThickness, + lineThickness); + } +} + +void LookAndFeel::drawResizableFrame (Graphics&, int /*w*/, int /*h*/, + const BorderSize& /*borders*/) +{ +} + +void LookAndFeel::drawResizableWindowBorder (Graphics& g, int w, int h, + const BorderSize& border, ResizableWindow&) +{ + g.setColour (Colour (0x80000000)); + g.drawRect (0, 0, w, h); + + g.setColour (Colour (0x19000000)); + g.drawRect (border.getLeft() - 1, + border.getTop() - 1, + w + 2 - border.getLeftAndRight(), + h + 2 - border.getTopAndBottom()); +} + +void LookAndFeel::drawDocumentWindowTitleBar (DocumentWindow& window, + Graphics& g, int w, int h, + int titleSpaceX, int titleSpaceW, + const Image* icon, + bool drawTitleTextOnLeft) +{ + const bool isActive = window.isActiveWindow(); + + GradientBrush gb (window.getBackgroundColour(), + 0.0f, 0.0f, + window.getBackgroundColour().contrasting (isActive ? 0.15f : 0.05f), + 0.0f, (float) h, false); + g.setBrush (&gb); + g.fillAll(); + + g.setFont (h * 0.65f, Font::bold); + + int textW = g.getCurrentFont().getStringWidth (window.getName()); + int iconW = 0; + int iconH = 0; + + if (icon != 0) + { + iconH = (int) g.getCurrentFont().getHeight(); + iconW = icon->getWidth() * iconH / icon->getHeight() + 4; + } + + textW = jmin (titleSpaceW, textW + iconW); + int textX = drawTitleTextOnLeft ? titleSpaceX + : jmax (titleSpaceX, (w - textW) / 2); + + if (textX + textW > titleSpaceX + titleSpaceW) + textX = titleSpaceX + titleSpaceW - textW; + + if (icon != 0) + { + g.setOpacity (isActive ? 1.0f : 0.6f); + g.drawImageWithin (icon, textX, (h - iconH) / 2, iconW, iconH, + RectanglePlacement::centred, false); + textX += iconW; + textW -= iconW; + } + + g.setColour (window.getBackgroundColour().contrasting (isActive ? 0.7f : 0.4f)); + g.drawText (window.getName(), textX, 0, textW, h, Justification::centredLeft, true); +} + +class GlassWindowButton : public Button +{ +public: + + GlassWindowButton (const String& name, const Colour& col, + const Path& normalShape_, + const Path& toggledShape_) throw() + : Button (name), + colour (col), + normalShape (normalShape_), + toggledShape (toggledShape_) + { + } + + ~GlassWindowButton() + { + } + + void paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown) + { + float alpha = isMouseOverButton ? (isButtonDown ? 1.0f : 0.8f) : 0.55f; + + if (! isEnabled()) + alpha *= 0.5f; + + float x = 0, y = 0, diam; + + if (getWidth() < getHeight()) + { + diam = (float) getWidth(); + y = (getHeight() - getWidth()) * 0.5f; + } + else + { + diam = (float) getHeight(); + y = (getWidth() - getHeight()) * 0.5f; + } + + x += diam * 0.05f; + y += diam * 0.05f; + diam *= 0.9f; + + GradientBrush gb1 (Colour::greyLevel (0.9f).withAlpha (alpha), 0, y + diam, + Colour::greyLevel (0.6f).withAlpha (alpha), 0, y, false); + + g.setBrush (&gb1); + g.fillEllipse (x, y, diam, diam); + + x += 2.0f; + y += 2.0f; + diam -= 4.0f; + + LookAndFeel::drawGlassSphere (g, x, y, diam, colour.withAlpha (alpha), 1.0f); + + Path& p = getToggleState() ? toggledShape : normalShape; + + const AffineTransform t (p.getTransformToScaleToFit (x + diam * 0.3f, y + diam * 0.3f, + diam * 0.4f, diam * 0.4f, true)); + + g.setColour (Colours::black.withAlpha (alpha * 0.6f)); + g.fillPath (p, t); + } + + juce_UseDebuggingNewOperator + +private: + Colour colour; + Path normalShape, toggledShape; + + GlassWindowButton (const GlassWindowButton&); + const GlassWindowButton& operator= (const GlassWindowButton&); +}; + +Button* LookAndFeel::createDocumentWindowButton (int buttonType) +{ + Path shape; + const float crossThickness = 0.25f; + + if (buttonType == DocumentWindow::closeButton) + { + shape.addLineSegment (0.0f, 0.0f, 1.0f, 1.0f, crossThickness * 1.4f); + shape.addLineSegment (1.0f, 0.0f, 0.0f, 1.0f, crossThickness * 1.4f); + + return new GlassWindowButton ("close", Colour (0xffdd1100), shape, shape); + } + else if (buttonType == DocumentWindow::minimiseButton) + { + shape.addLineSegment (0.0f, 0.5f, 1.0f, 0.5f, crossThickness); + + return new GlassWindowButton ("minimise", Colour (0xffaa8811), shape, shape); + } + else if (buttonType == DocumentWindow::maximiseButton) + { + shape.addLineSegment (0.5f, 0.0f, 0.5f, 1.0f, crossThickness); + shape.addLineSegment (0.0f, 0.5f, 1.0f, 0.5f, crossThickness); + + Path fullscreenShape; + fullscreenShape.startNewSubPath (45.0f, 100.0f); + fullscreenShape.lineTo (0.0f, 100.0f); + fullscreenShape.lineTo (0.0f, 0.0f); + fullscreenShape.lineTo (100.0f, 0.0f); + fullscreenShape.lineTo (100.0f, 45.0f); + fullscreenShape.addRectangle (45.0f, 45.0f, 100.0f, 100.0f); + PathStrokeType (30.0f).createStrokedPath (fullscreenShape, fullscreenShape); + + return new GlassWindowButton ("maximise", Colour (0xff119911), shape, fullscreenShape); + } + + jassertfalse + return 0; +} + +void LookAndFeel::positionDocumentWindowButtons (DocumentWindow&, + int titleBarX, + int titleBarY, + int titleBarW, + int titleBarH, + Button* minimiseButton, + Button* maximiseButton, + Button* closeButton, + bool positionTitleBarButtonsOnLeft) +{ + const int buttonW = titleBarH - titleBarH / 8; + + int x = positionTitleBarButtonsOnLeft ? titleBarX + 4 + : titleBarX + titleBarW - buttonW - buttonW / 4; + + if (closeButton != 0) + { + closeButton->setBounds (x, titleBarY, buttonW, titleBarH); + x += positionTitleBarButtonsOnLeft ? buttonW : -(buttonW + buttonW / 4); + } + + if (positionTitleBarButtonsOnLeft) + swapVariables (minimiseButton, maximiseButton); + + if (maximiseButton != 0) + { + maximiseButton->setBounds (x, titleBarY, buttonW, titleBarH); + x += positionTitleBarButtonsOnLeft ? buttonW : -buttonW; + } + + if (minimiseButton != 0) + minimiseButton->setBounds (x, titleBarY, buttonW, titleBarH); +} + +int LookAndFeel::getDefaultMenuBarHeight() +{ + return 24; +} + +DropShadower* LookAndFeel::createDropShadowerForComponent (Component*) +{ + return new DropShadower (0.4f, 1, 5, 10); +} + +void LookAndFeel::drawStretchableLayoutResizerBar (Graphics& g, + int w, int h, + bool /*isVerticalBar*/, + bool isMouseOver, + bool isMouseDragging) +{ + float alpha = 0.5f; + + if (isMouseOver || isMouseDragging) + { + g.fillAll (Colour (0x190000ff)); + alpha = 1.0f; + } + + const float cx = w * 0.5f; + const float cy = h * 0.5f; + const float cr = jmin (w, h) * 0.4f; + + GradientBrush gb (Colours::white.withAlpha (alpha), cx + cr * 0.1f, cy + cr, + Colours::black.withAlpha (alpha), cx, cy - cr * 4.0f, + true); + + g.setBrush (&gb); + g.fillEllipse (cx - cr, cy - cr, cr * 2.0f, cr * 2.0f); +} + +void LookAndFeel::drawGroupComponentOutline (Graphics& g, int width, int height, + const String& text, + const Justification& position, + GroupComponent& group) +{ + const float textH = 15.0f; + const float indent = 3.0f; + const float textEdgeGap = 4.0f; + float cs = 5.0f; + + Font f (textH); + + Path p; + float x = indent; + float y = f.getAscent() - 3.0f; + float w = jmax (0.0f, width - x * 2.0f); + float h = jmax (0.0f, height - y - indent); + cs = jmin (cs, w * 0.5f, h * 0.5f); + const float cs2 = 2.0f * cs; + + float textW = text.isEmpty() ? 0 : jlimit (0.0f, jmax (0.0f, w - cs2 - textEdgeGap * 2), f.getStringWidth (text) + textEdgeGap * 2.0f); + float textX = cs + textEdgeGap; + + if (position.testFlags (Justification::horizontallyCentred)) + textX = cs + (w - cs2 - textW) * 0.5f; + else if (position.testFlags (Justification::right)) + textX = w - cs - textW - textEdgeGap; + + p.startNewSubPath (x + textX + textW, y); + p.lineTo (x + w - cs, y); + + p.addArc (x + w - cs2, y, cs2, cs2, 0, float_Pi * 0.5f); + p.lineTo (x + w, y + h - cs); + + p.addArc (x + w - cs2, y + h - cs2, cs2, cs2, float_Pi * 0.5f, float_Pi); + p.lineTo (x + cs, y + h); + + p.addArc (x, y + h - cs2, cs2, cs2, float_Pi, float_Pi * 1.5f); + p.lineTo (x, y + cs); + + p.addArc (x, y, cs2, cs2, float_Pi * 1.5f, float_Pi * 2.0f); + p.lineTo (x + textX, y); + + const float alpha = group.isEnabled() ? 1.0f : 0.5f; + + g.setColour (group.findColour (GroupComponent::outlineColourId) + .withMultipliedAlpha (alpha)); + + g.strokePath (p, PathStrokeType (2.0f)); + + g.setColour (group.findColour (GroupComponent::textColourId) + .withMultipliedAlpha (alpha)); + g.setFont (f); + g.drawText (text, + roundFloatToInt (x + textX), 0, + roundFloatToInt (textW), + roundFloatToInt (textH), + Justification::centred, true); +} + +int LookAndFeel::getTabButtonOverlap (int tabDepth) +{ + return 1 + tabDepth / 3; +} + +int LookAndFeel::getTabButtonSpaceAroundImage() +{ + return 4; +} + +void LookAndFeel::createTabButtonShape (Path& p, + int width, int height, + int /*tabIndex*/, + const String& /*text*/, + Button& /*button*/, + TabbedButtonBar::Orientation orientation, + const bool /*isMouseOver*/, + const bool /*isMouseDown*/, + const bool /*isFrontTab*/) +{ + const float w = (float) width; + const float h = (float) height; + + float length = w; + float depth = h; + + if (orientation == TabbedButtonBar::TabsAtLeft + || orientation == TabbedButtonBar::TabsAtRight) + { + swapVariables (length, depth); + } + + const float indent = (float) getTabButtonOverlap ((int) depth); + const float overhang = 4.0f; + + if (orientation == TabbedButtonBar::TabsAtLeft) + { + p.startNewSubPath (w, 0.0f); + p.lineTo (0.0f, indent); + p.lineTo (0.0f, h - indent); + p.lineTo (w, h); + p.lineTo (w + overhang, h + overhang); + p.lineTo (w + overhang, -overhang); + } + else if (orientation == TabbedButtonBar::TabsAtRight) + { + p.startNewSubPath (0.0f, 0.0f); + p.lineTo (w, indent); + p.lineTo (w, h - indent); + p.lineTo (0.0f, h); + p.lineTo (-overhang, h + overhang); + p.lineTo (-overhang, -overhang); + } + else if (orientation == TabbedButtonBar::TabsAtBottom) + { + p.startNewSubPath (0.0f, 0.0f); + p.lineTo (indent, h); + p.lineTo (w - indent, h); + p.lineTo (w, 0.0f); + p.lineTo (w + overhang, -overhang); + p.lineTo (-overhang, -overhang); + } + else + { + p.startNewSubPath (0.0f, h); + p.lineTo (indent, 0.0f); + p.lineTo (w - indent, 0.0f); + p.lineTo (w, h); + p.lineTo (w + overhang, h + overhang); + p.lineTo (-overhang, h + overhang); + } + + p.closeSubPath(); + + p = p.createPathWithRoundedCorners (3.0f); +} + +void LookAndFeel::fillTabButtonShape (Graphics& g, + const Path& path, + const Colour& preferredColour, + int /*tabIndex*/, + const String& /*text*/, + Button& button, + TabbedButtonBar::Orientation /*orientation*/, + const bool /*isMouseOver*/, + const bool /*isMouseDown*/, + const bool isFrontTab) +{ + g.setColour (isFrontTab ? preferredColour + : preferredColour.withMultipliedAlpha (0.9f)); + + g.fillPath (path); + + g.setColour (Colours::black.withAlpha (button.isEnabled() ? 0.5f : 0.25f)); + g.strokePath (path, PathStrokeType (isFrontTab ? 1.0f : 0.5f)); +} + +void LookAndFeel::drawTabButtonText (Graphics& g, + int x, int y, int w, int h, + const Colour& preferredBackgroundColour, + int /*tabIndex*/, + const String& text, + Button& button, + TabbedButtonBar::Orientation orientation, + const bool isMouseOver, + const bool isMouseDown, + const bool /*isFrontTab*/) +{ + int length = w; + int depth = h; + + if (orientation == TabbedButtonBar::TabsAtLeft + || orientation == TabbedButtonBar::TabsAtRight) + { + swapVariables (length, depth); + } + + Font font (depth * 0.6f); + font.setUnderline (button.hasKeyboardFocus (false)); + + GlyphArrangement textLayout; + textLayout.addFittedText (font, text.trim(), + 0.0f, 0.0f, (float) length, (float) depth, + Justification::centred, + jmax (1, depth / 12)); + + AffineTransform transform; + + if (orientation == TabbedButtonBar::TabsAtLeft) + { + transform = transform.rotated (float_Pi * -0.5f) + .translated ((float) x, (float) (y + h)); + } + else if (orientation == TabbedButtonBar::TabsAtRight) + { + transform = transform.rotated (float_Pi * 0.5f) + .translated ((float) (x + w), (float) y); + } + else + { + transform = transform.translated ((float) x, (float) y); + } + + g.setColour (preferredBackgroundColour.contrasting()); + + if (! (isMouseOver || isMouseDown)) + g.setOpacity (0.8f); + + if (! button.isEnabled()) + g.setOpacity (0.3f); + + textLayout.draw (g, transform); +} + +int LookAndFeel::getTabButtonBestWidth (int /*tabIndex*/, + const String& text, + int tabDepth, + Button&) +{ + Font f (tabDepth * 0.6f); + return f.getStringWidth (text.trim()) + getTabButtonOverlap (tabDepth) * 2; +} + +void LookAndFeel::drawTabButton (Graphics& g, + int w, int h, + const Colour& preferredColour, + int tabIndex, + const String& text, + Button& button, + TabbedButtonBar::Orientation orientation, + const bool isMouseOver, + const bool isMouseDown, + const bool isFrontTab) +{ + int length = w; + int depth = h; + + if (orientation == TabbedButtonBar::TabsAtLeft + || orientation == TabbedButtonBar::TabsAtRight) + { + swapVariables (length, depth); + } + + Path tabShape; + + createTabButtonShape (tabShape, w, h, + tabIndex, text, button, orientation, + isMouseOver, isMouseDown, isFrontTab); + + fillTabButtonShape (g, tabShape, preferredColour, + tabIndex, text, button, orientation, + isMouseOver, isMouseDown, isFrontTab); + + const int indent = getTabButtonOverlap (depth); + int x = 0, y = 0; + + if (orientation == TabbedButtonBar::TabsAtLeft + || orientation == TabbedButtonBar::TabsAtRight) + { + y += indent; + h -= indent * 2; + } + else + { + x += indent; + w -= indent * 2; + } + + drawTabButtonText (g, x, y, w, h, preferredColour, + tabIndex, text, button, orientation, + isMouseOver, isMouseDown, isFrontTab); +} + +void LookAndFeel::drawTabAreaBehindFrontButton (Graphics& g, + int w, int h, + TabbedButtonBar& tabBar, + TabbedButtonBar::Orientation orientation) +{ + const float shadowSize = 0.2f; + + float x1 = 0.0f, y1 = 0.0f, x2 = 0.0f, y2 = 0.0f; + Rectangle shadowRect; + + if (orientation == TabbedButtonBar::TabsAtLeft) + { + x1 = (float) w; + x2 = w * (1.0f - shadowSize); + shadowRect.setBounds ((int) x2, 0, w - (int) x2, h); + } + else if (orientation == TabbedButtonBar::TabsAtRight) + { + x2 = w * shadowSize; + shadowRect.setBounds (0, 0, (int) x2, h); + } + else if (orientation == TabbedButtonBar::TabsAtBottom) + { + y2 = h * shadowSize; + shadowRect.setBounds (0, 0, w, (int) y2); + } + else + { + y1 = (float) h; + y2 = h * (1.0f - shadowSize); + shadowRect.setBounds (0, (int) y2, w, h - (int) y2); + } + + GradientBrush gb (Colours::black.withAlpha (tabBar.isEnabled() ? 0.3f : 0.15f), x1, y1, + Colours::transparentBlack, x2, y2, + false); + + g.setBrush (&gb); + shadowRect.expand (2, 2); + g.fillRect (shadowRect); + + g.setColour (Colour (0x80000000)); + + if (orientation == TabbedButtonBar::TabsAtLeft) + { + g.fillRect (w - 1, 0, 1, h); + } + else if (orientation == TabbedButtonBar::TabsAtRight) + { + g.fillRect (0, 0, 1, h); + } + else if (orientation == TabbedButtonBar::TabsAtBottom) + { + g.fillRect (0, 0, w, 1); + } + else + { + g.fillRect (0, h - 1, w, 1); + } +} + +Button* LookAndFeel::createTabBarExtrasButton() +{ + const float thickness = 7.0f; + const float indent = 22.0f; + + Path p; + p.addEllipse (-10.0f, -10.0f, 120.0f, 120.0f); + + DrawablePath ellipse; + ellipse.setPath (p); + ellipse.setSolidFill (Colour (0x99ffffff)); + + p.clear(); + p.addEllipse (0.0f, 0.0f, 100.0f, 100.0f); + p.addRectangle (indent, 50.0f - thickness, 100.0f - indent * 2.0f, thickness * 2.0f); + p.addRectangle (50.0f - thickness, indent, thickness * 2.0f, 50.0f - indent - thickness); + p.addRectangle (50.0f - thickness, 50.0f + thickness, thickness * 2.0f, 50.0f - indent - thickness); + p.setUsingNonZeroWinding (false); + + DrawablePath dp; + dp.setPath (p); + dp.setSolidFill (Colour (0x59000000)); + + DrawableComposite normalImage; + normalImage.insertDrawable (ellipse); + normalImage.insertDrawable (dp); + + dp.setSolidFill (Colour (0xcc000000)); + + DrawableComposite overImage; + overImage.insertDrawable (ellipse); + overImage.insertDrawable (dp); + + DrawableButton* db = new DrawableButton (T("tabs"), DrawableButton::ImageFitted); + db->setImages (&normalImage, &overImage, 0); + return db; +} + +void LookAndFeel::drawTableHeaderBackground (Graphics& g, TableHeaderComponent& header) +{ + g.fillAll (Colours::white); + + const int w = header.getWidth(); + const int h = header.getHeight(); + + GradientBrush gb (Colour (0xffe8ebf9), 0.0f, h * 0.5f, + Colour (0xfff6f8f9), 0.0f, h - 1.0f, + false); + + g.setBrush (&gb); + g.fillRect (0, h / 2, w, h); + + g.setColour (Colour (0x33000000)); + g.fillRect (0, h - 1, w, 1); + + for (int i = header.getNumColumns (true); --i >= 0;) + g.fillRect (header.getColumnPosition (i).getRight() - 1, 0, 1, h - 1); +} + +void LookAndFeel::drawTableHeaderColumn (Graphics& g, const String& columnName, int /*columnId*/, + int width, int height, + bool isMouseOver, bool isMouseDown, + int columnFlags) +{ + if (isMouseDown) + g.fillAll (Colour (0x8899aadd)); + else if (isMouseOver) + g.fillAll (Colour (0x5599aadd)); + + int rightOfText = width - 4; + + if ((columnFlags & (TableHeaderComponent::sortedForwards | TableHeaderComponent::sortedBackwards)) != 0) + { + const float top = height * ((columnFlags & TableHeaderComponent::sortedForwards) != 0 ? 0.35f : (1.0f - 0.35f)); + const float bottom = height - top; + + const float w = height * 0.5f; + const float x = rightOfText - (w * 1.25f); + rightOfText = (int) x; + + Path sortArrow; + sortArrow.addTriangle (x, bottom, x + w * 0.5f, top, x + w, bottom); + + g.setColour (Colour (0x99000000)); + g.fillPath (sortArrow); + } + + g.setColour (Colours::black); + g.setFont (height * 0.5f, Font::bold); + const int textX = 4; + g.drawFittedText (columnName, textX, 0, rightOfText - textX, height, Justification::centredLeft, 1); +} + +void LookAndFeel::paintToolbarBackground (Graphics& g, int w, int h, Toolbar& toolbar) +{ + const Colour background (toolbar.findColour (Toolbar::backgroundColourId)); + + GradientBrush gb (background, 0.0f, 0.0f, + background.darker (0.1f), + toolbar.isVertical() ? w - 1.0f : 0.0f, + toolbar.isVertical() ? 0.0f : h - 1.0f, + false); + + g.setBrush (&gb); + g.fillAll(); +} + +Button* LookAndFeel::createToolbarMissingItemsButton (Toolbar& /*toolbar*/) +{ + return createTabBarExtrasButton(); +} + +void LookAndFeel::paintToolbarButtonBackground (Graphics& g, int /*width*/, int /*height*/, + bool isMouseOver, bool isMouseDown, + ToolbarItemComponent& component) +{ + if (isMouseDown) + g.fillAll (component.findColour (Toolbar::buttonMouseDownBackgroundColourId, true)); + else if (isMouseOver) + g.fillAll (component.findColour (Toolbar::buttonMouseOverBackgroundColourId, true)); +} + +void LookAndFeel::paintToolbarButtonLabel (Graphics& g, int x, int y, int width, int height, + const String& text, ToolbarItemComponent& component) +{ + g.setColour (component.findColour (Toolbar::labelTextColourId, true) + .withAlpha (component.isEnabled() ? 1.0f : 0.25f)); + + const float fontHeight = jmin (14.0f, height * 0.85f); + g.setFont (fontHeight); + + g.drawFittedText (text, + x, y, width, height, + Justification::centred, + jmax (1, height / (int) fontHeight)); +} + +void LookAndFeel::drawPropertyPanelSectionHeader (Graphics& g, const String& name, + bool isOpen, int width, int height) +{ + const int buttonSize = (height * 3) / 4; + const int buttonIndent = (height - buttonSize) / 2; + + drawTreeviewPlusMinusBox (g, buttonIndent, buttonIndent, buttonSize, buttonSize, ! isOpen); + + const int textX = buttonIndent * 2 + buttonSize + 2; + + g.setColour (Colours::black); + g.setFont (height * 0.7f, Font::bold); + g.drawText (name, textX, 0, width - textX - 4, height, Justification::centredLeft, true); +} + +void LookAndFeel::drawPropertyComponentBackground (Graphics& g, int width, int height, + PropertyComponent&) +{ + g.setColour (Colour (0x66ffffff)); + g.fillRect (0, 0, width, height - 1); +} + +void LookAndFeel::drawPropertyComponentLabel (Graphics& g, int, int height, + PropertyComponent& component) +{ + g.setColour (Colours::black); + + if (! component.isEnabled()) + g.setOpacity (g.getCurrentColour().getAlpha() * 0.6f); + + g.setFont (jmin (height, 24) * 0.65f); + + const Rectangle r (getPropertyComponentContentPosition (component)); + + g.drawFittedText (component.getName(), + 3, r.getY(), r.getX() - 5, r.getHeight(), + Justification::centredLeft, 2); +} + +const Rectangle LookAndFeel::getPropertyComponentContentPosition (PropertyComponent& component) +{ + return Rectangle (component.getWidth() / 3, 1, + component.getWidth() - component.getWidth() / 3 - 1, component.getHeight() - 3); +} + +void LookAndFeel::createFileChooserHeaderText (const String& title, + const String& instructions, + GlyphArrangement& text, + int width) +{ + text.clear(); + + text.addJustifiedText (Font (17.0f, Font::bold), title, + 8.0f, 22.0f, width - 16.0f, + Justification::centred); + + text.addJustifiedText (Font (14.0f), instructions, + 8.0f, 24.0f + 16.0f, width - 16.0f, + Justification::centred); +} + +void LookAndFeel::drawFileBrowserRow (Graphics& g, int width, int height, + const String& filename, Image* icon, + const String& fileSizeDescription, + const String& fileTimeDescription, + const bool isDirectory, + const bool isItemSelected) +{ + if (isItemSelected) + g.fillAll (findColour (DirectoryContentsDisplayComponent::highlightColourId)); + + g.setColour (findColour (DirectoryContentsDisplayComponent::textColourId)); + g.setFont (height * 0.7f); + + Image* im = icon; + Image* toRelease = 0; + + if (im == 0) + { + toRelease = im = (isDirectory ? getDefaultFolderImage() + : getDefaultDocumentFileImage()); + } + + const int x = 32; + + if (im != 0) + { + g.drawImageWithin (im, 2, 2, x - 4, height - 4, + RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize, + false); + + ImageCache::release (toRelease); + } + + if (width > 450 && ! isDirectory) + { + const int sizeX = roundFloatToInt (width * 0.7f); + const int dateX = roundFloatToInt (width * 0.8f); + + g.drawFittedText (filename, + x, 0, sizeX - x, height, + Justification::centredLeft, 1); + + g.setFont (height * 0.5f); + g.setColour (Colours::darkgrey); + + if (! isDirectory) + { + g.drawFittedText (fileSizeDescription, + sizeX, 0, dateX - sizeX - 8, height, + Justification::centredRight, 1); + + g.drawFittedText (fileTimeDescription, + dateX, 0, width - 8 - dateX, height, + Justification::centredRight, 1); + } + } + else + { + g.drawFittedText (filename, + x, 0, width - x, height, + Justification::centredLeft, 1); + + } +} + +Button* LookAndFeel::createFileBrowserGoUpButton() +{ + DrawableButton* goUpButton = new DrawableButton ("up", DrawableButton::ImageOnButtonBackground); + + Path arrowPath; + arrowPath.addArrow (50.0f, 100.0f, 50.0f, 0.0, 40.0f, 100.0f, 50.0f); + + DrawablePath arrowImage; + arrowImage.setSolidFill (Colours::black.withAlpha (0.4f)); + arrowImage.setPath (arrowPath); + + goUpButton->setImages (&arrowImage); + + return goUpButton; +} + +void LookAndFeel::layoutFileBrowserComponent (FileBrowserComponent& browserComp, + DirectoryContentsDisplayComponent* fileListComponent, + FilePreviewComponent* previewComp, + ComboBox* currentPathBox, + TextEditor* filenameBox, + Button* goUpButton) +{ + const int x = 8; + int w = browserComp.getWidth() - x - x; + + if (previewComp != 0) + { + const int previewWidth = w / 3; + previewComp->setBounds (x + w - previewWidth, 0, previewWidth, browserComp.getHeight()); + + w -= previewWidth + 4; + } + + int y = 4; + + const int controlsHeight = 22; + const int bottomSectionHeight = controlsHeight + 8; + const int upButtonWidth = 50; + + currentPathBox->setBounds (x, y, w - upButtonWidth - 6, controlsHeight); + goUpButton->setBounds (x + w - upButtonWidth, y, upButtonWidth, controlsHeight); + + y += controlsHeight + 4; + + Component* const listAsComp = dynamic_cast (fileListComponent); + listAsComp->setBounds (x, y, w, browserComp.getHeight() - y - bottomSectionHeight); + + y = listAsComp->getBottom() + 4; + filenameBox->setBounds (x + 50, y, w - 50, controlsHeight); +} + +Image* LookAndFeel::getDefaultFolderImage() +{ + static const unsigned char foldericon_png[] = { 137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,32,0,0,0,28,8,6,0,0,0,0,194,189,34,0,0,0,4,103,65,77,65,0,0,175,200,55,5, + 138,233,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114,101,0,65,100,111,98,101,32,73,109,97,103,101,82,101,97,100,121,113,201,101,60,0,0,9,46,73,68,65,84,120,218,98,252,255,255,63,3,50,240,41,95,192, + 197,205,198,32,202,204,202,33,241,254,235,47,133,47,191,24,180,213,164,133,152,69,24,222,44,234,42,77,188,245,31,170,129,145,145,145,1,29,128,164,226,91,86,113,252,248,207,200,171,37,39,204,239,170,43, + 254,206,218,88,231,61,62,61,0,1,196,2,149,96,116,200,158,102,194,202,201,227,197,193,206,166,194,204,193,33,195,202,204,38,42,197,197,42,196,193,202,33,240,241,231,15,134,151,95,127,9,2,149,22,0,241,47, + 152,230,128,134,245,204,63,191,188,103,83,144,16,16,228,229,102,151,76,239,217,32,199,204,198,169,205,254,159,65,245,203,79,6,169,131,151,30,47,1,42,91,10,196,127,208,236,101,76,235,90,43,101,160,40,242, + 19,32,128,64,78,98,52,12,41,149,145,215,52,89,162,38,35,107,39,196,203,203,192,206,194,206,192,197,198,202,192,203,197,198,192,205,193,206,240,252,227,103,134,139,55,175,191,127,243,242,78,219,187,207, + 63,215,255,98,23,48,228,227,96,83,98,102,102,85,225,224,228,80,20,224,230,86,226,225,228,150,103,101,97,101,230,227,228,96,224,0,234,191,243,252,5,195,222,19,199,38,191,127,112,161,83,66,199,86,141,131, + 149,69,146,133,153,69,137,149,133,89,157,141,131,77,83,140,143,243,219,255,31,159,123,0,2,136,69,90,207,129,157,71,68,42,66,71,73,209,210,81,91,27,24,142,140,12,127,255,253,103,0,185,236,31,3,144,6,50, + 148,68,216,25,216,24,117,4,239,11,243,214,49,50,51,84,178,48,114,240,112,177,114,177,240,115,113,49,241,112,112,48,176,179,178,51,176,48,49,3,85,255,99,248,253,247,15,195,247,159,191,25,30,191,126,253, + 71,74,76,200,66,75,197,119,138,168,144,160,150,168,0,183,160,152,32,15,175,188,184,32,199,175,191,127,25,214,31,184,120,247,236,209,253,159,0,2,136,133,95,70,93,74,88,80,196,83,69,66,130,149,9,104,219, + 151,31,191,193,150,194,146,6,136,102,102,98,100,16,227,231,103,16,23,210,230,101,101,102,100,248,255,143,137,225,223,63,6,6,22,102,38,134,239,191,126,49,220,123,241,134,225,227,247,175,64,7,252,101,96, + 97,249,207,192,193,198,200,160,171,34,192,108,165,235,104,42,204,207,101,42,194,199,197,192,199,201,198,192,197,193,202,192,198,202,194,176,247,194,3,134,155,183,110,61,188,127,124,221,19,128,0,92,146, + 49,14,64,64,16,69,63,153,85,16,52,18,74,71,112,6,87,119,0,165,160,86,138,32,172,216,29,49,182,84,253,169,94,94,230,127,17,87,133,34,146,174,3,88,126,240,219,164,147,113,31,145,244,152,112,179,211,130, + 34,31,203,113,162,233,6,36,49,163,174,74,124,140,60,141,144,165,161,220,228,25,3,24,105,255,17,168,101,1,139,245,188,93,104,251,73,239,235,50,90,189,111,175,0,98,249,254,254,249,175,239,223,190,126,6, + 5,27,19,47,90,170,102,0,249,158,129,129,141,133,25,228,20,6,38,38,72,74,7,185,243,243,247,239,12,23,31,60,98,228,231,253,207,144,227,107,206,32,202,199,193,240,249,251,127,134,95,191,255,49,124,249,250, + 159,225,237,239,95,12,63,127,1,35,229,31,194,71,32,71,63,123,251,245,223,197,27,183,159,189,187,178,103,61,80,232,59,64,0,177,48,252,5,134,225,255,191,223,126,254,250,13,182,132,1,41,167,176,3,53,128, + 188,254,226,253,103,96,212,252,96,120,247,249,203,255,79,223,191,254,255,250,235,199,191,239,63,191,255,87,145,17,100,73,116,181,100,252,249,243,63,195,149,123,223,193,14,132,101,55,96,52,3,125,255,15, + 204,254,15,132,160,232,253,13,20,124,248,226,227,223,23,207,30,221,120,119,255,226,109,160,210,31,0,1,196,242,231,219,135,175,140,255,126,190,7,197,37,35,19,34,216,65,248,211,143,111,255,79,223,121,240, + 255,211,183,79,76,220,156,172,12,236,204,140,140,252,124,28,140,250,226,82,140,106,82,34,140,124,156,156,12,175,222,253,1,90,4,137,162,63,127,33,161,6,178,242,215,239,255,224,160,255,15,198,12,64,7,48, + 128,211,200,253,151,111,254,254,248,240,236,44,80,217,71,80,246,4,8,32,160,31,255,255,100,102,248,243,238,199,159,63,16,221,16,19,128,248,31,195,181,199,207,254,255,253,247,133,49,212,78,27,104,8,11,40, + 94,25,184,216,89,129,108,38,70,144,242,183,31,17,105,230,63,148,248,15,97,49,252,248,249,15,20,85,72,105,9,148,187,254,49,220,127,254,242,207,243,75,135,14,128,130,31,84,64,1,4,16,203,247,143,175,127, + 48,253,254,246,234,7,48,206,96,137,13,4,64,65,248,234,195,7,6,7,3,57,70,33,46,97,134,111,63,254,50,252,5,250,244,51,216,103,255,192,185,0,150,91,80,44,135,242,127,253,129,164,23,24,96,102,250,207,112, + 255,213,219,255,247,31,63,188,251,246,201,173,199,176,2,13,32,128,88,62,188,121,241,243,211,231,207,31,126,2,147,236,63,168,6,144,193,223,190,255,254,207,198,198,192,40,35,44,206,240,252,205,79,6,132, + 223,24,224,150,32,251,28,25,128,211,29,19,170,24,51,48,88,111,61,127,206,248,254,245,179,139,192,18,247,219,239,239,95,192,249,9,32,128,88,126,124,249,248,231,203,183,111,159,128,33,240,15,24,68,160,180, + 2,204,223,140,12,111,63,127,102,16,228,229,4,6,53,35,195,31,176,119,25,112,3,70,84,55,0,203,50,112,33,134,108,249,103,160,7,159,189,126,253,235,235,227,203,7,255,255,251,247,13,86,63,0,4,16,168,46,248, + 199,250,231,243,235,159,191,126,254,248,245,251,47,23,11,51,51,48,184,152,24,94,127,250,248,95,68,136,151,241,243,55,96,208,51,160,218,255,31,139,27,144,197,254,98,201,202,79,223,124,96,120,245,232,250, + 185,119,143,174,95,250,243,243,219,119,152,60,64,0,129,2,234,223,183,215,15,95,48,254,255,253,3,146,109,192,229,5,195,135,47,159,25,248,184,121,24,126,0,227,29,88,240,49,252,101,36,14,255,1,90,249,7,156, + 222,17,24,24,164,12,207,223,189,99,248,250,252,230,97,96,229,245,2,104,231,111,152,3,0,2,8,228,128,191,15,239,220,120,255,255,223,159,47,160,116,0,42,44,222,124,250,244,239,207,255,63,12,236,108,236,64, + 67,65,81,0,52,244,63,113,248,47,52,10,96,14,98,2,230,191,119,223,127,48,60,121,254,248,235,151,55,207,46,1,163,252,35,114,128,1,4,16,40,10,254,191,121,249,252,199,175,159,63,191,254,2,230,45,118,22,22, + 134,219,207,94,252,231,224,100,103,250,247,15,148,32,64,85,12,34,14,254,227,72,6,255,225,9,240,63,138,26,46,96,214,189,249,244,37,195,139,167,143,30,124,253,246,253,9,40,245,255,71,202,30,0,1,196,2,226, + 0,243,232,159,239,63,127,124,253,11,202,94,64,169,23,31,62,50,138,137,242,49,50,0,211,195,223,255,80,7,252,199,159,6,224,137,145,9,146,231,153,160,165,218,23,96,29,240,244,237,59,134,111,175,31,95,250, + 252,230,241,83,244,182,1,64,0,177,192,28,14,76,132,31,128,169,19,88,220,126,253,207,206,198,196,32,38,36,0,244,61,11,176,148,251,139,145,3,208,29,0,178,16,82,228,66,42,174,223,192,26,8,152,162,25,222, + 125,248,200,240,242,253,39,134,151,79,238,126,254,242,242,238,177,15,47,30,190,5,215,242,72,0,32,128,224,14,96,254,255,231,61,168,92,123,241,254,253,127,1,62,78,6,78,110,78,134,223,64,195,254,50,98,183, + 24,36,12,202,179,224,202,9,88,228,253,132,90,250,246,211,71,134,55,175,94,254,122,255,250,249,247,15,175,159,126,249,251,237,195,135,95,175,110,31,122,117,251,244,49,160,150,111,255,209,218,128,0,1,152, + 44,183,21,0,65,32,136,110,247,254,255,243,122,9,187,64,105,174,74,22,138,25,173,80,208,194,188,238,156,151,217,217,15,32,182,197,37,83,201,4,31,243,178,169,232,242,214,224,223,252,103,175,35,85,1,41,129, + 228,148,142,8,214,30,32,149,6,161,204,109,182,53,236,184,156,78,142,147,195,153,89,35,198,3,87,166,249,220,227,198,59,218,48,252,223,185,111,30,1,132,228,128,127,31,222,124,248,248,27,24,152,28,60,220, + 220,12,44,172,172,224,224,103,5,102,98,144,133,160,236,244,229,231,47,134,239,223,127,49,188,121,251,158,225,241,179,103,12,31,223,189,254,251,227,221,139,55,191,62,188,120,246,235,205,189,59,207,238, + 94,58,241,228,254,109,144,101,159,128,248,51,40,9,32,97,80,217,255,15,221,1,0,1,4,143,130,207,159,191,126,252,246,234,213,111,94,126,94,118,73,94,9,198,127,64,223,126,252,246,147,225,243,215,239,12,223, + 128,229,198,251,15,239,24,62,189,126,249,227,203,171,135,47,63,189,122,252,228,235,155,199,247,95,63,188,118,227,197,227,123,247,127,255,250,249,30,104,198,7,32,126,11,181,252,7,212,183,160,4,247,7,155, + 197,48,0,16,64,112,7,60,121,241,238,189,16,207,15,134,63,63,216,25,95,125,248,198,112,227,241,27,134,15,239,223,50,124,126,245,228,253,143,55,143,158,191,123,116,237,226,171,135,55,175,126,253,252,225, + 229,183,47,159,95,254,253,245,227,253,175,159,223,223,193,124,7,181,20,84,105,252,70,143,103,124,0,32,128,224,14,224,102,253,251,81,144,253,223,235,167,207,30,254,124,127,231,252,155,143,175,159,188,250, + 246,254,249,125,96,60,62,248,250,233,253,147,119,207,238,221,6,150,214,175,129,106,191,130,18,19,146,133,120,125,72,8,0,4,16,34,27,190,121,112,251,3,211,159,69,143,110,223,229,120,255,232,230,221,215, + 79,239,62,4,102,203,207,72,241,9,11,218,63,72,89,137,20,207,98,100,93,16,0,8,32,70,144,1,64,14,168,209,199,7,196,194,160,166,27,212,135,95,96,65,10,173,95,254,34,219,6,51,128,88,7,96,235,21,129,0,64,0, + 193,28,192,8,174,53,33,152,1,155,133,184,12,196,165,4,151,133,232,0,32,192,0,151,97,210,163,246,134,208,52,0,0,0,0,73,69,78,68,174,66,96,130,0,0}; + + return ImageCache::getFromMemory (foldericon_png, sizeof (foldericon_png)); +} + +Image* LookAndFeel::getDefaultDocumentFileImage() +{ + static const unsigned char fileicon_png[] = { 137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,32,0,0,0,32,8,6,0,0,0,115,122,122,244,0,0,0,4,103,65,77,65,0,0,175,200,55,5, + 138,233,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114,101,0,65,100,111,98,101,32,73,109,97,103,101,82,101,97,100,121,113,201,101,60,0,0,4,99,73,68,65,84,120,218,98,252,255,255,63,3,12,48,50,50,50,1, + 169,127,200,98,148,2,160,153,204,64,243,254,226,146,7,8,32,22,52,203,255,107,233,233,91,76,93,176,184,232,239,239,95,127,24,40,112,8,19,51,203,255,179,23,175,108,1,90,190,28,104,54,43,80,232,207,127,44, + 62,3,8,32,6,144,24,84,156,25,132,189,252,3,146,255,83,9,220,127,254,242,134,162,138,170,10,208,92,144,3,152,97,118,33,99,128,0,98,66,114,11,200,1,92,255,254,252,225,32,215,215,32,127,64,240,127,80,60, + 50,40,72,136,169,47,95,179,118,130,136,148,140,0,40,80,128,33,193,136,174,7,32,128,144,29,192,8,117,41,59,209,22,66,241,191,255,16,12,244,19,195,63,48,134,240,255,0,9,115,125,93,239,252,130,130,108,168, + 249,44,232,102,0,4,16,19,22,62,51,33,11,255,195,44,4,211,255,25,96,16,33,6,117,24,56,226,25,24,202,139,10,75,226,51,115,66,160,105,13,197,17,0,1,196,68,172,79,255,33,91,206,192,192,128,176,22,17,10,200, + 234,32,161,240,31,24,10,255,24,152,153,153,184,39,244,247,117,107,234,234,105,131,66,1,154,224,193,0,32,128,240,58,0,22,180,255,144,18,13,40,136,33,113,140,36,255,15,17,26,48,12,81,15,145,255,254,251, + 31,131,0,59,171,84,81,73,105,33,208,216,191,200,161,12,16,64,44,248,131,251,63,10,31,198,253,143,38,6,83,7,11,33,228,232,2,123,4,202,226,228,96,151,132,166,49,144,35,126,131,196,0,2,136,5,103,60,51,252, + 71,49,12,213,130,255,168,226,232,150,254,255,15,143,6,80,202,3,133,16,200,198,63,127,193,229,17,39,16,127,135,217,7,16,64,88,67,0,28,143,255,25,225,46,135,249,18,155,133,240,178,4,205,145,8,62,52,186, + 32,234,152,160,118,194,179,35,64,0,177,96,11,123,144,236,95,104,92,162,228,113,36,11,81,125,140,112,56,186,131,96,226,176,172,137,148,229,193,0,32,128,88,112,167,248,255,112,223,48,34,165,110,6,124,190, + 253,143,61,106,192,9,19,73,28,25,0,4,16,206,40,248,251,15,45,104,209,130,21,51,222,145,18,238,127,180,68,8,244,250,95,164,16,66,6,0,1,196,130,45,253,195,12,250,135,53,206,255,195,131,18,213,98,236,81, + 243,31,154,11,144,115,8,50,0,8,32,156,81,0,203,227,12,80,223,98,230,4,68,72,96,38,78,84,11,65,9,250,47,146,3,145,1,64,0,97,117,192,95,112,34,68,138,130,255,176,224,251,143,226,51,6,6,68,29,192,136,20, + 77,200,69,54,35,3,36,49,255,69,77,132,112,0,16,64,44,56,139,94,36,7,96,102,59,164,108,249,31,181,82,98,64,203,174,255,144,234,142,127,88,146,33,64,0,97,205,134,240,120,67,75,76,136,224,198,140,22,6,44, + 142,66,201,41,255,177,231,2,128,0,194,25,5,255,254,161,134,192,127,6,28,229,0,129,242,1,150,56,33,81,138,209,28,96,0,8,32,172,81,0,78,3,104,190,68,182,224,31,146,197,224,56,6,146,140,176,202,135,17,169, + 96,130,40,64,56,0,139,93,0,1,132,61,10,64,248,31,106,156,162,199,55,204,65,255,144,178,38,74,84,252,71,51,239,63,246,68,8,16,64,44,216,74,1,88,217,13,203,191,32,1,80,58,7,133,224,127,6,68,114,6,241,65, + 81,197,8,101,255,71,114,33,92,237,127,228,52,128,233,2,128,0,98,193,149,3,64,117,193,255,127,255,81,75,191,127,168,5,18,136,255,31,45,161,49,32,151,134,72,252,127,12,216,203,98,128,0,98,193,210,144,135, + 248,30,201,242,127,208,252,140,145,27,160,113,206,136,148,197,192,121,159,17,53,184,225,149,17,22,23,0,4,16,11,182,150,237,63,168,207,96,142,248,143,163,72,6,203,253,67,13,61,6,104,14,66,46,17,254,65, + 19,40,182,16,0,8,32,22,108,109,235,255,176,234,24,35,79,255,199,222,30,64,81,135,90,35,194,211,4,142,92,0,16,64,88,29,0,107,7,254,251,247,31,53,78,241,54,207,80,29,135,209,96,249,143,189,46,0,8,32,116, + 7,252,101,102,103,103,228,103,99,96,248,193,198,137,53,248,49,125,204,128,225,227,255,88,18,54,47,176,25,202,205,195,205,6,109,11,194,149,0,4,16,35,204,85,208,254,27,159,128,176,176,142,166,182,142,21, + 48,4,248,129,41,143,13,217,16,70,52,95,147,0,254,0,187,69,95,223,188,122,125,235,206,141,107,7,129,252,247,64,123,193,237,66,128,0,66,118,0,168,189,198,3,196,252,32,135,64,105,54,228,230,19,185,29,100, + 168,175,191,0,241,7,32,254,4,196,159,129,246,254,2,73,2,4,16,11,90,72,125,135,210,63,161,138,153,169,212,75,255,15,117,196,15,40,134,119,215,1,2,12,0,187,0,132,247,216,161,197,124,0,0,0,0,73,69,78,68, + 174,66,96,130,0,0}; + + return ImageCache::getFromMemory (fileicon_png, sizeof (fileicon_png)); +} + +void LookAndFeel::playAlertSound() +{ + PlatformUtilities::beep(); +} + +static void createRoundedPath (Path& p, + const float x, const float y, + const float w, const float h, + const float cs, + const bool curveTopLeft, const bool curveTopRight, + const bool curveBottomLeft, const bool curveBottomRight) throw() +{ + const float cs2 = 2.0f * cs; + + if (curveTopLeft) + { + p.startNewSubPath (x, y + cs); + p.addArc (x, y, cs2, cs2, float_Pi * 1.5f, float_Pi * 2.0f); + } + else + { + p.startNewSubPath (x, y); + } + + if (curveTopRight) + { + p.lineTo (x + w - cs, y); + p.addArc (x + w - cs2, y, cs2, cs2, 0.0f, float_Pi * 0.5f); + } + else + { + p.lineTo (x + w, y); + } + + if (curveBottomRight) + { + p.lineTo (x + w, y + h - cs); + p.addArc (x + w - cs2, y + h - cs2, cs2, cs2, float_Pi * 0.5f, float_Pi); + } + else + { + p.lineTo (x + w, y + h); + } + + if (curveBottomLeft) + { + p.lineTo (x + cs, y + h); + p.addArc (x, y + h - cs2, cs2, cs2, float_Pi, float_Pi * 1.5f); + } + else + { + p.lineTo (x, y + h); + } + + p.closeSubPath(); +} + +void LookAndFeel::drawShinyButtonShape (Graphics& g, + float x, float y, float w, float h, + float maxCornerSize, + const Colour& baseColour, + const float strokeWidth, + const bool flatOnLeft, + const bool flatOnRight, + const bool flatOnTop, + const bool flatOnBottom) throw() +{ + if (w <= strokeWidth * 1.1f || h <= strokeWidth * 1.1f) + return; + + const float cs = jmin (maxCornerSize, w * 0.5f, h * 0.5f); + + Path outline; + createRoundedPath (outline, x, y, w, h, cs, + ! (flatOnLeft || flatOnTop), + ! (flatOnRight || flatOnTop), + ! (flatOnLeft || flatOnBottom), + ! (flatOnRight || flatOnBottom)); + + ColourGradient cg (baseColour, 0.0f, y, + baseColour.overlaidWith (Colour (0x070000ff)), 0.0f, y + h, + false); + + cg.addColour (0.5, baseColour.overlaidWith (Colour (0x33ffffff))); + cg.addColour (0.51, baseColour.overlaidWith (Colour (0x110000ff))); + + GradientBrush gb (cg); + g.setBrush (&gb); + g.fillPath (outline); + + g.setColour (Colour (0x80000000)); + g.strokePath (outline, PathStrokeType (strokeWidth)); +} + +void LookAndFeel::drawGlassSphere (Graphics& g, + const float x, const float y, + const float diameter, + const Colour& colour, + const float outlineThickness) throw() +{ + if (diameter <= outlineThickness) + return; + + Path p; + p.addEllipse (x, y, diameter, diameter); + + { + ColourGradient cg (Colours::white.overlaidWith (colour.withMultipliedAlpha (0.3f)), 0, y, + Colours::white.overlaidWith (colour.withMultipliedAlpha (0.3f)), 0, y + diameter, false); + + cg.addColour (0.4, Colours::white.overlaidWith (colour)); + + GradientBrush gb (cg); + g.setBrush (&gb); + g.fillPath (p); + } + + { + GradientBrush gb (Colours::white, 0, y + diameter * 0.06f, + Colours::transparentWhite, 0, y + diameter * 0.3f, false); + + g.setBrush (&gb); + g.fillEllipse (x + diameter * 0.2f, y + diameter * 0.05f, diameter * 0.6f, diameter * 0.4f); + } + + { + ColourGradient cg (Colours::transparentBlack, + x + diameter * 0.5f, y + diameter * 0.5f, + Colours::black.withAlpha (0.5f * outlineThickness * colour.getFloatAlpha()), + x, y + diameter * 0.5f, true); + + cg.addColour (0.7, Colours::transparentBlack); + cg.addColour (0.8, Colours::black.withAlpha (0.1f * outlineThickness)); + + GradientBrush gb (cg); + g.setBrush (&gb); + g.fillPath (p); + } + + g.setColour (Colours::black.withAlpha (0.5f * colour.getFloatAlpha())); + g.drawEllipse (x, y, diameter, diameter, outlineThickness); +} + +void LookAndFeel::drawGlassPointer (Graphics& g, + const float x, const float y, + const float diameter, + const Colour& colour, const float outlineThickness, + const int direction) throw() +{ + if (diameter <= outlineThickness) + return; + + Path p; + p.startNewSubPath (x + diameter * 0.5f, y); + p.lineTo (x + diameter, y + diameter * 0.6f); + p.lineTo (x + diameter, y + diameter); + p.lineTo (x, y + diameter); + p.lineTo (x, y + diameter * 0.6f); + p.closeSubPath(); + + p.applyTransform (AffineTransform::rotation (direction * (float_Pi * 0.5f), x + diameter * 0.5f, y + diameter * 0.5f)); + + { + ColourGradient cg (Colours::white.overlaidWith (colour.withMultipliedAlpha (0.3f)), 0, y, + Colours::white.overlaidWith (colour.withMultipliedAlpha (0.3f)), 0, y + diameter, false); + + cg.addColour (0.4, Colours::white.overlaidWith (colour)); + + GradientBrush gb (cg); + g.setBrush (&gb); + g.fillPath (p); + } + + { + ColourGradient cg (Colours::transparentBlack, + x + diameter * 0.5f, y + diameter * 0.5f, + Colours::black.withAlpha (0.5f * outlineThickness * colour.getFloatAlpha()), + x - diameter * 0.2f, y + diameter * 0.5f, true); + + cg.addColour (0.5, Colours::transparentBlack); + cg.addColour (0.7, Colours::black.withAlpha (0.07f * outlineThickness)); + + GradientBrush gb (cg); + g.setBrush (&gb); + g.fillPath (p); + } + + g.setColour (Colours::black.withAlpha (0.5f * colour.getFloatAlpha())); + g.strokePath (p, PathStrokeType (outlineThickness)); +} + +void LookAndFeel::drawGlassLozenge (Graphics& g, + const float x, const float y, + const float width, const float height, + const Colour& colour, + const float outlineThickness, + const float cornerSize, + const bool flatOnLeft, + const bool flatOnRight, + const bool flatOnTop, + const bool flatOnBottom) throw() +{ + if (width <= outlineThickness || height <= outlineThickness) + return; + + const int intX = (int) x; + const int intY = (int) y; + const int intW = (int) width; + const int intH = (int) height; + + const float cs = cornerSize < 0 ? jmin (width * 0.5f, height * 0.5f) : cornerSize; + const float edgeBlurRadius = height * 0.75f + (height - cs * 2.0f); + const int intEdge = (int) edgeBlurRadius; + + Path outline; + createRoundedPath (outline, x, y, width, height, cs, + ! (flatOnLeft || flatOnTop), + ! (flatOnRight || flatOnTop), + ! (flatOnLeft || flatOnBottom), + ! (flatOnRight || flatOnBottom)); + + { + ColourGradient cg (colour.darker (0.2f), 0, y, + colour.darker (0.2f), 0, y + height, false); + + cg.addColour (0.03, colour.withMultipliedAlpha (0.3f)); + cg.addColour (0.4, colour); + cg.addColour (0.97, colour.withMultipliedAlpha (0.3f)); + + GradientBrush gb (cg); + g.setBrush (&gb); + g.fillPath (outline); + } + + ColourGradient cg (Colours::transparentBlack, x + edgeBlurRadius, y + height * 0.5f, + colour.darker (0.2f), x, y + height * 0.5f, true); + + cg.addColour (jlimit (0.0, 1.0, 1.0 - (cs * 0.5f) / edgeBlurRadius), Colours::transparentBlack); + cg.addColour (jlimit (0.0, 1.0, 1.0 - (cs * 0.25f) / edgeBlurRadius), colour.darker (0.2f).withMultipliedAlpha (0.3f)); + + if (! (flatOnLeft || flatOnTop || flatOnBottom)) + { + GradientBrush gb (cg); + + g.saveState(); + g.setBrush (&gb); + g.reduceClipRegion (intX, intY, intEdge, intH); + g.fillPath (outline); + g.restoreState(); + } + + if (! (flatOnRight || flatOnTop || flatOnBottom)) + { + cg.x1 = x + width - edgeBlurRadius; + cg.x2 = x + width; + GradientBrush gb (cg); + + g.saveState(); + g.setBrush (&gb); + g.reduceClipRegion (intX + intW - intEdge, intY, 2 + intEdge, intH); + g.fillPath (outline); + g.restoreState(); + } + + { + const float leftIndent = flatOnLeft ? 0.0f : cs * 0.4f; + const float rightIndent = flatOnRight ? 0.0f : cs * 0.4f; + + Path highlight; + createRoundedPath (highlight, + x + leftIndent, + y + cs * 0.1f, + width - (leftIndent + rightIndent), + height * 0.4f, cs * 0.4f, + ! (flatOnLeft || flatOnTop), + ! (flatOnRight || flatOnTop), + ! (flatOnLeft || flatOnBottom), + ! (flatOnRight || flatOnBottom)); + + GradientBrush gb (colour.brighter (10.0f), 0, y + height * 0.06f, + Colours::transparentWhite, 0, y + height * 0.4f, false); + + g.setBrush (&gb); + g.fillPath (highlight); + } + + g.setColour (colour.darker().withMultipliedAlpha (1.5f)); + g.strokePath (outline, PathStrokeType (outlineThickness)); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_LookAndFeel.cpp *********/ + +/********* Start of inlined file: juce_OldSchoolLookAndFeel.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +OldSchoolLookAndFeel::OldSchoolLookAndFeel() +{ + setColour (TextButton::buttonColourId, Colour (0xffbbbbff)); + setColour (ListBox::outlineColourId, findColour (ComboBox::outlineColourId)); + setColour (ScrollBar::thumbColourId, Colour (0xffbbbbdd)); + setColour (ScrollBar::backgroundColourId, Colours::transparentBlack); + setColour (Slider::thumbColourId, Colours::white); + setColour (Slider::trackColourId, Colour (0x7f000000)); + setColour (Slider::textBoxOutlineColourId, Colours::grey); + setColour (ProgressBar::backgroundColourId, Colours::white.withAlpha (0.6f)); + setColour (ProgressBar::foregroundColourId, Colours::green.withAlpha (0.7f)); + setColour (PopupMenu::backgroundColourId, Colour (0xffeef5f8)); + setColour (PopupMenu::highlightedBackgroundColourId, Colour (0xbfa4c2ce)); + setColour (PopupMenu::highlightedTextColourId, Colours::black); + setColour (TextEditor::focusedOutlineColourId, findColour (TextButton::buttonColourId)); + + scrollbarShadow.setShadowProperties (2.2f, 0.5f, 0, 0); +} + +OldSchoolLookAndFeel::~OldSchoolLookAndFeel() +{ +} + +void OldSchoolLookAndFeel::drawButtonBackground (Graphics& g, + Button& button, + const Colour& backgroundColour, + bool isMouseOverButton, + bool isButtonDown) +{ + const int width = button.getWidth(); + const int height = button.getHeight(); + + const float indent = 2.0f; + const int cornerSize = jmin (roundFloatToInt (width * 0.4f), + roundFloatToInt (height * 0.4f)); + + Path p; + p.addRoundedRectangle (indent, indent, + width - indent * 2.0f, + height - indent * 2.0f, + (float) cornerSize); + + Colour bc (backgroundColour.withMultipliedSaturation (0.3f)); + + if (isMouseOverButton) + { + if (isButtonDown) + bc = bc.brighter(); + else if (bc.getBrightness() > 0.5f) + bc = bc.darker (0.1f); + else + bc = bc.brighter (0.1f); + } + + g.setColour (bc); + g.fillPath (p); + + g.setColour (bc.contrasting().withAlpha ((isMouseOverButton) ? 0.6f : 0.4f)); + g.strokePath (p, PathStrokeType ((isMouseOverButton) ? 2.0f : 1.4f)); +} + +void OldSchoolLookAndFeel::drawTickBox (Graphics& g, + Component& /*component*/, + int x, int y, int w, int h, + const bool ticked, + const bool isEnabled, + const bool /*isMouseOverButton*/, + const bool isButtonDown) +{ + Path box; + box.addRoundedRectangle (0.0f, 2.0f, 6.0f, 6.0f, 1.0f); + + g.setColour (isEnabled ? Colours::blue.withAlpha (isButtonDown ? 0.3f : 0.1f) + : Colours::lightgrey.withAlpha (0.1f)); + + AffineTransform trans (AffineTransform::scale (w / 9.0f, h / 9.0f) + .translated ((float) x, (float) y)); + + g.fillPath (box, trans); + + g.setColour (Colours::black.withAlpha (0.6f)); + g.strokePath (box, PathStrokeType (0.9f), trans); + + if (ticked) + { + Path tick; + tick.startNewSubPath (1.5f, 3.0f); + tick.lineTo (3.0f, 6.0f); + tick.lineTo (6.0f, 0.0f); + + g.setColour (isEnabled ? Colours::black : Colours::grey); + g.strokePath (tick, PathStrokeType (2.5f), trans); + } +} + +void OldSchoolLookAndFeel::drawToggleButton (Graphics& g, + ToggleButton& button, + bool isMouseOverButton, + bool isButtonDown) +{ + if (button.hasKeyboardFocus (true)) + { + g.setColour (button.findColour (TextEditor::focusedOutlineColourId)); + g.drawRect (0, 0, button.getWidth(), button.getHeight()); + } + + const int tickWidth = jmin (20, button.getHeight() - 4); + + drawTickBox (g, button, 4, (button.getHeight() - tickWidth) / 2, + tickWidth, tickWidth, + button.getToggleState(), + button.isEnabled(), + isMouseOverButton, + isButtonDown); + + g.setColour (button.findColour (ToggleButton::textColourId)); + g.setFont (jmin (15.0f, button.getHeight() * 0.6f)); + + if (! button.isEnabled()) + g.setOpacity (0.5f); + + const int textX = tickWidth + 5; + + g.drawFittedText (button.getButtonText(), + textX, 4, + button.getWidth() - textX - 2, button.getHeight() - 8, + Justification::centredLeft, 10); +} + +void OldSchoolLookAndFeel::drawProgressBar (Graphics& g, ProgressBar& progressBar, + int width, int height, + double progress, const String& textToShow) +{ + if (progress < 0 || progress >= 1.0) + { + LookAndFeel::drawProgressBar (g, progressBar, width, height, progress, textToShow); + } + else + { + const Colour background (progressBar.findColour (ProgressBar::backgroundColourId)); + const Colour foreground (progressBar.findColour (ProgressBar::foregroundColourId)); + + g.fillAll (background); + g.setColour (foreground); + + g.fillRect (1, 1, + jlimit (0, width - 2, roundDoubleToInt (progress * (width - 2))), + height - 2); + + if (textToShow.isNotEmpty()) + { + g.setColour (Colour::contrasting (background, foreground)); + g.setFont (height * 0.6f); + + g.drawText (textToShow, 0, 0, width, height, Justification::centred, false); + } + } +} + +void OldSchoolLookAndFeel::drawScrollbarButton (Graphics& g, + ScrollBar& bar, + int width, int height, + int buttonDirection, + bool isScrollbarVertical, + bool isMouseOverButton, + bool isButtonDown) +{ + if (isScrollbarVertical) + width -= 2; + else + height -= 2; + + Path p; + + if (buttonDirection == 0) + p.addTriangle (width * 0.5f, height * 0.2f, + width * 0.1f, height * 0.7f, + width * 0.9f, height * 0.7f); + else if (buttonDirection == 1) + p.addTriangle (width * 0.8f, height * 0.5f, + width * 0.3f, height * 0.1f, + width * 0.3f, height * 0.9f); + else if (buttonDirection == 2) + p.addTriangle (width * 0.5f, height * 0.8f, + width * 0.1f, height * 0.3f, + width * 0.9f, height * 0.3f); + else if (buttonDirection == 3) + p.addTriangle (width * 0.2f, height * 0.5f, + width * 0.7f, height * 0.1f, + width * 0.7f, height * 0.9f); + + if (isButtonDown) + g.setColour (Colours::white); + else if (isMouseOverButton) + g.setColour (Colours::white.withAlpha (0.7f)); + else + g.setColour (bar.findColour (ScrollBar::thumbColourId).withAlpha (0.5f)); + + g.fillPath (p); + + g.setColour (Colours::black.withAlpha (0.5f)); + g.strokePath (p, PathStrokeType (0.5f)); +} + +void OldSchoolLookAndFeel::drawScrollbar (Graphics& g, + ScrollBar& bar, + int x, int y, + int width, int height, + bool isScrollbarVertical, + int thumbStartPosition, + int thumbSize, + bool isMouseOver, + bool isMouseDown) +{ + g.fillAll (bar.findColour (ScrollBar::backgroundColourId)); + + g.setColour (bar.findColour (ScrollBar::thumbColourId) + .withAlpha ((isMouseOver || isMouseDown) ? 0.4f : 0.15f)); + + if (thumbSize > 0.0f) + { + Rectangle thumb; + + if (isScrollbarVertical) + { + width -= 2; + g.fillRect (x + roundFloatToInt (width * 0.35f), y, + roundFloatToInt (width * 0.3f), height); + + thumb.setBounds (x + 1, thumbStartPosition, + width - 2, thumbSize); + } + else + { + height -= 2; + g.fillRect (x, y + roundFloatToInt (height * 0.35f), + width, roundFloatToInt (height * 0.3f)); + + thumb.setBounds (thumbStartPosition, y + 1, + thumbSize, height - 2); + } + + g.setColour (bar.findColour (ScrollBar::thumbColourId) + .withAlpha ((isMouseOver || isMouseDown) ? 0.95f : 0.7f)); + + g.fillRect (thumb); + + g.setColour (Colours::black.withAlpha ((isMouseOver || isMouseDown) ? 0.4f : 0.25f)); + g.drawRect (thumb.getX(), thumb.getY(), thumb.getWidth(), thumb.getHeight()); + + if (thumbSize > 16) + { + for (int i = 3; --i >= 0;) + { + const float linePos = thumbStartPosition + thumbSize / 2 + (i - 1) * 4.0f; + g.setColour (Colours::black.withAlpha (0.15f)); + + if (isScrollbarVertical) + { + g.drawLine (x + width * 0.2f, linePos, width * 0.8f, linePos); + g.setColour (Colours::white.withAlpha (0.15f)); + g.drawLine (width * 0.2f, linePos - 1, width * 0.8f, linePos - 1); + } + else + { + g.drawLine (linePos, height * 0.2f, linePos, height * 0.8f); + g.setColour (Colours::white.withAlpha (0.15f)); + g.drawLine (linePos - 1, height * 0.2f, linePos - 1, height * 0.8f); + } + } + } + } +} + +ImageEffectFilter* OldSchoolLookAndFeel::getScrollbarEffect() +{ + return &scrollbarShadow; +} + +void OldSchoolLookAndFeel::drawPopupMenuBackground (Graphics& g, int width, int height) +{ + g.fillAll (findColour (PopupMenu::backgroundColourId)); + + g.setColour (Colours::black.withAlpha (0.6f)); + g.drawRect (0, 0, width, height); +} + +void OldSchoolLookAndFeel::drawMenuBarBackground (Graphics& g, int /*width*/, int /*height*/, + bool, MenuBarComponent& menuBar) +{ + g.fillAll (menuBar.findColour (PopupMenu::backgroundColourId)); +} + +void OldSchoolLookAndFeel::drawTextEditorOutline (Graphics& g, int width, int height, TextEditor& textEditor) +{ + if (textEditor.isEnabled()) + { + g.setColour (textEditor.findColour (TextEditor::outlineColourId)); + g.drawRect (0, 0, width, height); + } +} + +void OldSchoolLookAndFeel::drawComboBox (Graphics& g, int width, int height, + const bool isButtonDown, + int buttonX, int buttonY, + int buttonW, int buttonH, + ComboBox& box) +{ + g.fillAll (box.findColour (ComboBox::backgroundColourId)); + + g.setColour (box.findColour ((isButtonDown) ? ComboBox::buttonColourId + : ComboBox::backgroundColourId)); + g.fillRect (buttonX, buttonY, buttonW, buttonH); + + g.setColour (box.findColour (ComboBox::outlineColourId)); + g.drawRect (0, 0, width, height); + + const float arrowX = 0.2f; + const float arrowH = 0.3f; + + if (box.isEnabled()) + { + Path p; + p.addTriangle (buttonX + buttonW * 0.5f, buttonY + buttonH * (0.45f - arrowH), + buttonX + buttonW * (1.0f - arrowX), buttonY + buttonH * 0.45f, + buttonX + buttonW * arrowX, buttonY + buttonH * 0.45f); + + p.addTriangle (buttonX + buttonW * 0.5f, buttonY + buttonH * (0.55f + arrowH), + buttonX + buttonW * (1.0f - arrowX), buttonY + buttonH * 0.55f, + buttonX + buttonW * arrowX, buttonY + buttonH * 0.55f); + + g.setColour (box.findColour ((isButtonDown) ? ComboBox::backgroundColourId + : ComboBox::buttonColourId)); + g.fillPath (p); + } +} + +const Font OldSchoolLookAndFeel::getComboBoxFont (ComboBox& box) +{ + Font f (jmin (15.0f, box.getHeight() * 0.85f)); + f.setHorizontalScale (0.9f); + return f; +} + +static void drawTriangle (Graphics& g, float x1, float y1, float x2, float y2, float x3, float y3, const Colour& fill, const Colour& outline) throw() +{ + Path p; + p.addTriangle (x1, y1, x2, y2, x3, y3); + g.setColour (fill); + g.fillPath (p); + + g.setColour (outline); + g.strokePath (p, PathStrokeType (0.3f)); +} + +void OldSchoolLookAndFeel::drawLinearSlider (Graphics& g, + int x, int y, + int w, int h, + float sliderPos, + float minSliderPos, + float maxSliderPos, + const Slider::SliderStyle style, + Slider& slider) +{ + g.fillAll (slider.findColour (Slider::backgroundColourId)); + + if (style == Slider::LinearBar) + { + g.setColour (slider.findColour (Slider::thumbColourId)); + g.fillRect (x, y, (int) sliderPos - x, h); + + g.setColour (slider.findColour (Slider::textBoxTextColourId).withMultipliedAlpha (0.5f)); + g.drawRect (x, y, (int) sliderPos - x, h); + } + else + { + g.setColour (slider.findColour (Slider::trackColourId) + .withMultipliedAlpha (slider.isEnabled() ? 1.0f : 0.3f)); + + if (slider.isHorizontal()) + { + g.fillRect (x, y + roundFloatToInt (h * 0.6f), + w, roundFloatToInt (h * 0.2f)); + } + else + { + g.fillRect (x + roundFloatToInt (w * 0.5f - jmin (3.0f, w * 0.1f)), y, + jmin (4, roundFloatToInt (w * 0.2f)), h); + } + + float alpha = 0.35f; + + if (slider.isEnabled()) + alpha = slider.isMouseOverOrDragging() ? 1.0f : 0.7f; + + const Colour fill (slider.findColour (Slider::thumbColourId).withAlpha (alpha)); + const Colour outline (Colours::black.withAlpha (slider.isEnabled() ? 0.7f : 0.35f)); + + if (style == Slider::TwoValueVertical || style == Slider::ThreeValueVertical) + { + drawTriangle (g, x + w * 0.5f + jmin (4.0f, w * 0.3f), minSliderPos, + x + w * 0.5f - jmin (8.0f, w * 0.4f), minSliderPos - 7.0f, + x + w * 0.5f - jmin (8.0f, w * 0.4f), minSliderPos, + fill, outline); + + drawTriangle (g, x + w * 0.5f + jmin (4.0f, w * 0.3f), maxSliderPos, + x + w * 0.5f - jmin (8.0f, w * 0.4f), maxSliderPos, + x + w * 0.5f - jmin (8.0f, w * 0.4f), maxSliderPos + 7.0f, + fill, outline); + } + else if (style == Slider::TwoValueHorizontal || style == Slider::ThreeValueHorizontal) + { + drawTriangle (g, minSliderPos, y + h * 0.6f - jmin (4.0f, h * 0.3f), + minSliderPos - 7.0f, y + h * 0.9f , + minSliderPos, y + h * 0.9f, + fill, outline); + + drawTriangle (g, maxSliderPos, y + h * 0.6f - jmin (4.0f, h * 0.3f), + maxSliderPos, y + h * 0.9f, + maxSliderPos + 7.0f, y + h * 0.9f, + fill, outline); + } + + if (style == Slider::LinearHorizontal || style == Slider::ThreeValueHorizontal) + { + drawTriangle (g, sliderPos, y + h * 0.9f, + sliderPos - 7.0f, y + h * 0.2f, + sliderPos + 7.0f, y + h * 0.2f, + fill, outline); + } + else if (style == Slider::LinearVertical || style == Slider::ThreeValueVertical) + { + drawTriangle (g, x + w * 0.5f - jmin (4.0f, w * 0.3f), sliderPos, + x + w * 0.5f + jmin (8.0f, w * 0.4f), sliderPos - 7.0f, + x + w * 0.5f + jmin (8.0f, w * 0.4f), sliderPos + 7.0f, + fill, outline); + } + } +} + +Button* OldSchoolLookAndFeel::createSliderButton (const bool isIncrement) +{ + if (isIncrement) + return new ArrowButton ("u", 0.75f, Colours::white.withAlpha (0.8f)); + else + return new ArrowButton ("d", 0.25f, Colours::white.withAlpha (0.8f)); +} + +ImageEffectFilter* OldSchoolLookAndFeel::getSliderEffect() +{ + return &scrollbarShadow; +} + +int OldSchoolLookAndFeel::getSliderThumbRadius (Slider&) +{ + return 8; +} + +void OldSchoolLookAndFeel::drawCornerResizer (Graphics& g, + int w, int h, + bool isMouseOver, + bool isMouseDragging) +{ + g.setColour ((isMouseOver || isMouseDragging) ? Colours::lightgrey + : Colours::darkgrey); + + const float lineThickness = jmin (w, h) * 0.1f; + + for (float i = 0.0f; i < 1.0f; i += 0.3f) + { + g.drawLine (w * i, + h + 1.0f, + w + 1.0f, + h * i, + lineThickness); + } +} + +Button* OldSchoolLookAndFeel::createDocumentWindowButton (int buttonType) +{ + Path shape; + + if (buttonType == DocumentWindow::closeButton) + { + shape.addLineSegment (0.0f, 0.0f, 1.0f, 1.0f, 0.35f); + shape.addLineSegment (1.0f, 0.0f, 0.0f, 1.0f, 0.35f); + + ShapeButton* const b = new ShapeButton ("close", + Colour (0x7fff3333), + Colour (0xd7ff3333), + Colour (0xf7ff3333)); + + b->setShape (shape, true, true, true); + return b; + } + else if (buttonType == DocumentWindow::minimiseButton) + { + shape.addLineSegment (0.0f, 0.5f, 1.0f, 0.5f, 0.25f); + + DrawableButton* b = new DrawableButton ("minimise", DrawableButton::ImageFitted); + DrawablePath dp; + dp.setPath (shape); + dp.setSolidFill (Colours::black.withAlpha (0.3f)); + b->setImages (&dp); + return b; + } + else if (buttonType == DocumentWindow::maximiseButton) + { + shape.addLineSegment (0.5f, 0.0f, 0.5f, 1.0f, 0.25f); + shape.addLineSegment (0.0f, 0.5f, 1.0f, 0.5f, 0.25f); + + DrawableButton* b = new DrawableButton ("maximise", DrawableButton::ImageFitted); + DrawablePath dp; + dp.setPath (shape); + dp.setSolidFill (Colours::black.withAlpha (0.3f)); + b->setImages (&dp); + return b; + } + + jassertfalse + return 0; +} + +void OldSchoolLookAndFeel::positionDocumentWindowButtons (DocumentWindow&, + int titleBarX, + int titleBarY, + int titleBarW, + int titleBarH, + Button* minimiseButton, + Button* maximiseButton, + Button* closeButton, + bool positionTitleBarButtonsOnLeft) +{ + titleBarY += titleBarH / 8; + titleBarH -= titleBarH / 4; + + const int buttonW = titleBarH; + + int x = positionTitleBarButtonsOnLeft ? titleBarX + 4 + : titleBarX + titleBarW - buttonW - 4; + + if (closeButton != 0) + { + closeButton->setBounds (x, titleBarY, buttonW, titleBarH); + x += positionTitleBarButtonsOnLeft ? buttonW + buttonW / 5 + : -(buttonW + buttonW / 5); + } + + if (positionTitleBarButtonsOnLeft) + swapVariables (minimiseButton, maximiseButton); + + if (maximiseButton != 0) + { + maximiseButton->setBounds (x, titleBarY - 2, buttonW, titleBarH); + x += positionTitleBarButtonsOnLeft ? buttonW : -buttonW; + } + + if (minimiseButton != 0) + minimiseButton->setBounds (x, titleBarY - 2, buttonW, titleBarH); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_OldSchoolLookAndFeel.cpp *********/ + +/********* Start of inlined file: juce_MenuBarComponent.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +class DummyMenuComponent : public Component +{ + DummyMenuComponent (const DummyMenuComponent&); + const DummyMenuComponent& operator= (const DummyMenuComponent&); + +public: + DummyMenuComponent() {} + ~DummyMenuComponent() {} + + void inputAttemptWhenModal() + { + exitModalState (0); + } +}; + +MenuBarComponent::MenuBarComponent (MenuBarModel* model_) + : model (0), + itemUnderMouse (-1), + currentPopupIndex (-1), + indexToShowAgain (-1), + lastMouseX (0), + lastMouseY (0), + inModalState (false), + currentPopup (0) +{ + setRepaintsOnMouseActivity (true); + setWantsKeyboardFocus (false); + setMouseClickGrabsKeyboardFocus (false); + + setModel (model_); +} + +MenuBarComponent::~MenuBarComponent() +{ + setModel (0); + + Desktop::getInstance().removeGlobalMouseListener (this); + deleteAndZero (currentPopup); +} + +void MenuBarComponent::setModel (MenuBarModel* const newModel) +{ + if (model != newModel) + { + if (model != 0) + model->removeListener (this); + + model = newModel; + + if (model != 0) + model->addListener (this); + + repaint(); + menuBarItemsChanged (0); + } +} + +void MenuBarComponent::paint (Graphics& g) +{ + const bool isMouseOverBar = currentPopupIndex >= 0 || itemUnderMouse >= 0 || isMouseOver(); + + getLookAndFeel().drawMenuBarBackground (g, + getWidth(), + getHeight(), + isMouseOverBar, + *this); + + if (model != 0) + { + for (int i = 0; i < menuNames.size(); ++i) + { + g.saveState(); + g.setOrigin (xPositions [i], 0); + g.reduceClipRegion (0, 0, xPositions[i + 1] - xPositions[i], getHeight()); + + getLookAndFeel().drawMenuBarItem (g, + xPositions[i + 1] - xPositions[i], + getHeight(), + i, + menuNames[i], + i == itemUnderMouse, + i == currentPopupIndex, + isMouseOverBar, + *this); + + g.restoreState(); + } + } +} + +void MenuBarComponent::resized() +{ + xPositions.clear(); + int x = 2; + xPositions.add (x); + + for (int i = 0; i < menuNames.size(); ++i) + { + x += getLookAndFeel().getMenuBarItemWidth (*this, i, menuNames[i]); + + xPositions.add (x); + } +} + +int MenuBarComponent::getItemAt (const int x, const int y) +{ + for (int i = 0; i < xPositions.size(); ++i) + if (x >= xPositions[i] && x < xPositions[i + 1]) + return reallyContains (x, y, true) ? i : -1; + + return -1; +} + +void MenuBarComponent::repaintMenuItem (int index) +{ + if (((unsigned int) index) < (unsigned int) xPositions.size()) + { + const int x1 = xPositions [index]; + const int x2 = xPositions [index + 1]; + + repaint (x1 - 2, 0, x2 - x1 + 4, getHeight()); + } +} + +void MenuBarComponent::updateItemUnderMouse (int x, int y) +{ + const int newItem = getItemAt (x, y); + + if (itemUnderMouse != newItem) + { + repaintMenuItem (itemUnderMouse); + itemUnderMouse = newItem; + repaintMenuItem (itemUnderMouse); + } +} + +void MenuBarComponent::hideCurrentMenu() +{ + deleteAndZero (currentPopup); + repaint(); +} + +void MenuBarComponent::showMenu (int index) +{ + if (index != currentPopupIndex) + { + if (inModalState) + { + hideCurrentMenu(); + indexToShowAgain = index; + return; + } + + indexToShowAgain = -1; + currentPopupIndex = -1; + deleteAndZero (currentPopup); + menuBarItemsChanged (0); + + Component* const prevFocused = getCurrentlyFocusedComponent(); + + ComponentDeletionWatcher* prevCompDeletionChecker = 0; + if (prevFocused != 0) + prevCompDeletionChecker = new ComponentDeletionWatcher (prevFocused); + + ComponentDeletionWatcher deletionChecker (this); + + enterModalState (false); + inModalState = true; + int result = 0; + ApplicationCommandManager* managerOfChosenCommand = 0; + + Desktop::getInstance().addGlobalMouseListener (this); + + for (;;) + { + const int x = getScreenX() + xPositions [itemUnderMouse]; + const int w = xPositions [itemUnderMouse + 1] - xPositions [itemUnderMouse]; + + currentPopupIndex = itemUnderMouse; + indexToShowAgain = -1; + repaint(); + + if (((unsigned int) itemUnderMouse) < (unsigned int) menuNames.size()) + { + PopupMenu m (model->getMenuForIndex (itemUnderMouse, + menuNames [itemUnderMouse])); + + currentPopup = m.createMenuComponent (x, getScreenY(), + w, getHeight(), + 0, w, 0, 0, + true, this, + &managerOfChosenCommand, + this); + } + + if (currentPopup == 0) + { + currentPopup = new DummyMenuComponent(); + addAndMakeVisible (currentPopup); + } + + currentPopup->enterModalState (false); + currentPopup->toFront (false); // need to do this after making it modal, or it could + // be stuck behind other comps that are already modal.. + result = currentPopup->runModalLoop(); + + if (deletionChecker.hasBeenDeleted()) + { + delete prevCompDeletionChecker; + return; + } + + const int lastPopupIndex = currentPopupIndex; + deleteAndZero (currentPopup); + currentPopupIndex = -1; + + if (result != 0) + { + topLevelIndexClicked = lastPopupIndex; + break; + } + else if (indexToShowAgain >= 0) + { + menuBarItemsChanged (0); + repaint(); + itemUnderMouse = indexToShowAgain; + + if (((unsigned int) itemUnderMouse) >= (unsigned int) menuNames.size()) + break; + } + else + { + break; + } + } + + Desktop::getInstance().removeGlobalMouseListener (this); + + inModalState = false; + exitModalState (0); + + if (prevCompDeletionChecker != 0) + { + if (! prevCompDeletionChecker->hasBeenDeleted()) + prevFocused->grabKeyboardFocus(); + + delete prevCompDeletionChecker; + } + + int mx, my; + getMouseXYRelative (mx, my); + updateItemUnderMouse (mx, my); + repaint(); + + if (result != 0) + { + if (managerOfChosenCommand != 0) + { + ApplicationCommandTarget::InvocationInfo info (result); + info.invocationMethod = ApplicationCommandTarget::InvocationInfo::fromMenu; + + managerOfChosenCommand->invoke (info, true); + } + + postCommandMessage (result); + } + } +} + +void MenuBarComponent::handleCommandMessage (int commandId) +{ + if (model != 0) + model->menuItemSelected (commandId, topLevelIndexClicked); +} + +void MenuBarComponent::mouseEnter (const MouseEvent& e) +{ + if (e.eventComponent == this) + updateItemUnderMouse (e.x, e.y); +} + +void MenuBarComponent::mouseExit (const MouseEvent& e) +{ + if (e.eventComponent == this) + updateItemUnderMouse (e.x, e.y); +} + +void MenuBarComponent::mouseDown (const MouseEvent& e) +{ + const MouseEvent e2 (e.getEventRelativeTo (this)); + + if (currentPopupIndex < 0) + { + updateItemUnderMouse (e2.x, e2.y); + + currentPopupIndex = -2; + showMenu (itemUnderMouse); + } +} + +void MenuBarComponent::mouseDrag (const MouseEvent& e) +{ + const MouseEvent e2 (e.getEventRelativeTo (this)); + + const int item = getItemAt (e2.x, e2.y); + + if (item >= 0) + showMenu (item); +} + +void MenuBarComponent::mouseUp (const MouseEvent& e) +{ + const MouseEvent e2 (e.getEventRelativeTo (this)); + + updateItemUnderMouse (e2.x, e2.y); + + if (itemUnderMouse < 0 && dynamic_cast (currentPopup) != 0) + hideCurrentMenu(); +} + +void MenuBarComponent::mouseMove (const MouseEvent& e) +{ + const MouseEvent e2 (e.getEventRelativeTo (this)); + + if (lastMouseX != e2.x || lastMouseY != e2.y) + { + if (currentPopupIndex >= 0) + { + const int item = getItemAt (e2.x, e2.y); + + if (item >= 0) + showMenu (item); + } + else + { + updateItemUnderMouse (e2.x, e2.y); + } + + lastMouseX = e2.x; + lastMouseY = e2.y; + } +} + +bool MenuBarComponent::keyPressed (const KeyPress& key) +{ + bool used = false; + const int numMenus = menuNames.size(); + const int currentIndex = jlimit (0, menuNames.size() - 1, currentPopupIndex); + + if (key.isKeyCode (KeyPress::leftKey)) + { + showMenu ((currentIndex + numMenus - 1) % numMenus); + used = true; + } + else if (key.isKeyCode (KeyPress::rightKey)) + { + showMenu ((currentIndex + 1) % numMenus); + used = true; + } + + return used; +} + +void MenuBarComponent::inputAttemptWhenModal() +{ + hideCurrentMenu(); +} + +void MenuBarComponent::menuBarItemsChanged (MenuBarModel* /*menuBarModel*/) +{ + StringArray newNames; + + if (model != 0) + newNames = model->getMenuBarNames(); + + if (newNames != menuNames) + { + menuNames = newNames; + repaint(); + resized(); + } +} + +void MenuBarComponent::menuCommandInvoked (MenuBarModel* /*menuBarModel*/, + const ApplicationCommandTarget::InvocationInfo& info) +{ + if (model == 0 + || (info.commandFlags & ApplicationCommandInfo::dontTriggerVisualFeedback) != 0) + return; + + for (int i = 0; i < menuNames.size(); ++i) + { + const PopupMenu menu (model->getMenuForIndex (i, menuNames [i])); + + if (menu.containsCommandItem (info.commandID)) + { + itemUnderMouse = i; + repaintMenuItem (i); + startTimer (200); + + break; + } + } +} + +void MenuBarComponent::timerCallback() +{ + stopTimer(); + + int mx, my; + getMouseXYRelative (mx, my); + updateItemUnderMouse (mx, my); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_MenuBarComponent.cpp *********/ + +/********* Start of inlined file: juce_MenuBarModel.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +MenuBarModel::MenuBarModel() throw() + : manager (0) +{ +} + +MenuBarModel::~MenuBarModel() +{ + setApplicationCommandManagerToWatch (0); +} + +void MenuBarModel::menuItemsChanged() +{ + triggerAsyncUpdate(); +} + +void MenuBarModel::setApplicationCommandManagerToWatch (ApplicationCommandManager* const newManager) throw() +{ + if (manager != newManager) + { + if (manager != 0) + manager->removeListener (this); + + manager = newManager; + + if (manager != 0) + manager->addListener (this); + } +} + +void MenuBarModel::addListener (MenuBarModelListener* const newListener) throw() +{ + jassert (newListener != 0); + jassert (! listeners.contains (newListener)); // trying to add a listener to the list twice! + + if (newListener != 0) + listeners.add (newListener); +} + +void MenuBarModel::removeListener (MenuBarModelListener* const listenerToRemove) throw() +{ + // Trying to remove a listener that isn't on the list! + // If this assertion happens because this object is a dangling pointer, make sure you've not + // deleted this menu model while it's still being used by something (e.g. by a MenuBarComponent) + jassert (listeners.contains (listenerToRemove)); + + listeners.removeValue (listenerToRemove); +} + +void MenuBarModel::handleAsyncUpdate() +{ + for (int i = listeners.size(); --i >= 0;) + { + ((MenuBarModelListener*) listeners.getUnchecked (i))->menuBarItemsChanged (this); + i = jmin (i, listeners.size()); + } +} + +void MenuBarModel::applicationCommandInvoked (const ApplicationCommandTarget::InvocationInfo& info) +{ + for (int i = listeners.size(); --i >= 0;) + { + ((MenuBarModelListener*) listeners.getUnchecked (i))->menuCommandInvoked (this, info); + i = jmin (i, listeners.size()); + } +} + +void MenuBarModel::applicationCommandListChanged() +{ + menuItemsChanged(); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_MenuBarModel.cpp *********/ + +/********* Start of inlined file: juce_PopupMenu.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +static VoidArray activeMenuWindows; + +class MenuItemInfo +{ +public: + + const int itemId; + String text; + const Colour textColour; + const bool active, isSeparator, isTicked, usesColour; + Image* image; + PopupMenuCustomComponent* const customComp; + PopupMenu* subMenu; + ApplicationCommandManager* const commandManager; + + MenuItemInfo() throw() + : itemId (0), + active (true), + isSeparator (true), + isTicked (false), + usesColour (false), + image (0), + customComp (0), + subMenu (0), + commandManager (0) + { + } + + MenuItemInfo (const int itemId_, + const String& text_, + const bool active_, + const bool isTicked_, + const Image* im, + const Colour& textColour_, + const bool usesColour_, + PopupMenuCustomComponent* const customComp_, + const PopupMenu* const subMenu_, + ApplicationCommandManager* const commandManager_) throw() + : itemId (itemId_), + text (text_), + textColour (textColour_), + active (active_), + isSeparator (false), + isTicked (isTicked_), + usesColour (usesColour_), + image (0), + customComp (customComp_), + commandManager (commandManager_) + { + subMenu = (subMenu_ != 0) ? new PopupMenu (*subMenu_) : 0; + + if (customComp != 0) + customComp->refCount_++; + + if (im != 0) + image = im->createCopy(); + + if (commandManager_ != 0 && itemId_ != 0) + { + String shortcutKey; + + Array keyPresses (commandManager_->getKeyMappings() + ->getKeyPressesAssignedToCommand (itemId_)); + + for (int i = 0; i < keyPresses.size(); ++i) + { + const String key (keyPresses.getReference(i).getTextDescription()); + + if (shortcutKey.isNotEmpty()) + shortcutKey << ", "; + + if (key.length() == 1) + shortcutKey << "shortcut: '" << key << '\''; + else + shortcutKey << key; + } + + shortcutKey = shortcutKey.trim(); + + if (shortcutKey.isNotEmpty()) + text << "" << shortcutKey; + } + } + + MenuItemInfo (const MenuItemInfo& other) throw() + : itemId (other.itemId), + text (other.text), + textColour (other.textColour), + active (other.active), + isSeparator (other.isSeparator), + isTicked (other.isTicked), + usesColour (other.usesColour), + customComp (other.customComp), + commandManager (other.commandManager) + { + if (other.subMenu != 0) + subMenu = new PopupMenu (*(other.subMenu)); + else + subMenu = 0; + + if (other.image != 0) + image = other.image->createCopy(); + else + image = 0; + + if (customComp != 0) + customComp->refCount_++; + } + + ~MenuItemInfo() throw() + { + delete subMenu; + delete image; + + if (customComp != 0 && --(customComp->refCount_) == 0) + delete customComp; + } + + bool canBeTriggered() const throw() + { + return active && ! (isSeparator || (subMenu != 0)); + } + + bool hasActiveSubMenu() const throw() + { + return active && (subMenu != 0); + } + + juce_UseDebuggingNewOperator + +private: + const MenuItemInfo& operator= (const MenuItemInfo&); +}; + +class MenuItemComponent : public Component +{ + bool isHighlighted; + +public: + MenuItemInfo itemInfo; + + MenuItemComponent (const MenuItemInfo& itemInfo_) + : isHighlighted (false), + itemInfo (itemInfo_) + { + if (itemInfo.customComp != 0) + addAndMakeVisible (itemInfo.customComp); + } + + ~MenuItemComponent() + { + if (itemInfo.customComp != 0) + removeChildComponent (itemInfo.customComp); + } + + void getIdealSize (int& idealWidth, + int& idealHeight, + const int standardItemHeight) + { + if (itemInfo.customComp != 0) + { + itemInfo.customComp->getIdealSize (idealWidth, idealHeight); + } + else + { + getLookAndFeel().getIdealPopupMenuItemSize (itemInfo.text, + itemInfo.isSeparator, + standardItemHeight, + idealWidth, + idealHeight); + } + } + + void paint (Graphics& g) + { + if (itemInfo.customComp == 0) + { + String mainText (itemInfo.text); + String endText; + const int endIndex = mainText.indexOf (T("")); + + if (endIndex >= 0) + { + endText = mainText.substring (endIndex + 5).trim(); + mainText = mainText.substring (0, endIndex); + } + + getLookAndFeel() + .drawPopupMenuItem (g, getWidth(), getHeight(), + itemInfo.isSeparator, + itemInfo.active, + isHighlighted, + itemInfo.isTicked, + itemInfo.subMenu != 0, + mainText, endText, + itemInfo.image, + itemInfo.usesColour ? &(itemInfo.textColour) : 0); + } + } + + void resized() + { + if (getNumChildComponents() > 0) + getChildComponent(0)->setBounds (2, 0, getWidth() - 4, getHeight()); + } + + void setHighlighted (bool shouldBeHighlighted) + { + shouldBeHighlighted = shouldBeHighlighted && itemInfo.active; + + if (isHighlighted != shouldBeHighlighted) + { + isHighlighted = shouldBeHighlighted; + + if (itemInfo.customComp != 0) + { + itemInfo.customComp->isHighlighted = shouldBeHighlighted; + itemInfo.customComp->repaint(); + } + + repaint(); + } + } + +private: + MenuItemComponent (const MenuItemComponent&); + const MenuItemComponent& operator= (const MenuItemComponent&); +}; + +static const int scrollZone = 24; +static const int borderSize = 2; +static const int timerInterval = 50; +static const int dismissCommandId = 0x6287345f; + +static bool wasHiddenBecauseOfAppChange = false; + +class PopupMenuWindow : public Component, + private Timer +{ +public: + + PopupMenuWindow() throw() + : Component (T("menu")), + owner (0), + currentChild (0), + activeSubMenu (0), + menuBarComponent (0), + managerOfChosenCommand (0), + componentAttachedTo (0), + attachedCompWatcher (0), + lastMouseX (0), + lastMouseY (0), + minimumWidth (0), + maximumNumColumns (7), + standardItemHeight (0), + isOver (false), + hasBeenOver (false), + isDown (false), + needsToScroll (false), + hideOnExit (false), + disableMouseMoves (false), + hasAnyJuceCompHadFocus (false), + numColumns (0), + contentHeight (0), + childYOffset (0), + timeEnteredCurrentChildComp (0), + scrollAcceleration (1.0) + { + menuCreationTime = lastFocused = lastScroll = Time::getMillisecondCounter(); + setWantsKeyboardFocus (true); + + setOpaque (true); + setAlwaysOnTop (true); + + Desktop::getInstance().addGlobalMouseListener (this); + + activeMenuWindows.add (this); + } + + ~PopupMenuWindow() + { + activeMenuWindows.removeValue (this); + + Desktop::getInstance().removeGlobalMouseListener (this); + + jassert (activeSubMenu == 0 || activeSubMenu->isValidComponent()); + delete activeSubMenu; + + deleteAllChildren(); + delete attachedCompWatcher; + } + + static PopupMenuWindow* create (const PopupMenu& menu, + const bool dismissOnMouseUp, + PopupMenuWindow* const owner_, + const int minX, const int maxX, + const int minY, const int maxY, + const int minimumWidth, + const int maximumNumColumns, + const int standardItemHeight, + const bool alignToRectangle, + const int itemIdThatMustBeVisible, + Component* const menuBarComponent, + ApplicationCommandManager** managerOfChosenCommand, + Component* const componentAttachedTo) throw() + { + if (menu.items.size() > 0) + { + int totalItems = 0; + + PopupMenuWindow* const mw = new PopupMenuWindow(); + mw->setLookAndFeel (menu.lookAndFeel); + mw->setWantsKeyboardFocus (false); + mw->minimumWidth = minimumWidth; + mw->maximumNumColumns = maximumNumColumns; + mw->standardItemHeight = standardItemHeight; + mw->dismissOnMouseUp = dismissOnMouseUp; + + for (int i = 0; i < menu.items.size(); ++i) + { + MenuItemInfo* const item = (MenuItemInfo*) menu.items.getUnchecked(i); + + mw->addItem (*item); + ++totalItems; + } + + if (totalItems == 0) + { + delete mw; + } + else + { + mw->owner = owner_; + mw->menuBarComponent = menuBarComponent; + mw->managerOfChosenCommand = managerOfChosenCommand; + mw->componentAttachedTo = componentAttachedTo; + delete mw->attachedCompWatcher; + mw->attachedCompWatcher = componentAttachedTo != 0 ? new ComponentDeletionWatcher (componentAttachedTo) : 0; + + mw->calculateWindowPos (minX, maxX, minY, maxY, alignToRectangle); + mw->setTopLeftPosition (mw->windowPos.getX(), + mw->windowPos.getY()); + mw->updateYPositions(); + + if (itemIdThatMustBeVisible != 0) + { + const int y = minY - mw->windowPos.getY(); + mw->ensureItemIsVisible (itemIdThatMustBeVisible, + (((unsigned int) y) < (unsigned int) mw->windowPos.getHeight()) ? y : -1); + } + + mw->resizeToBestWindowPos(); + mw->addToDesktop (ComponentPeer::windowIsTemporary + | mw->getLookAndFeel().getMenuWindowFlags()); + + return mw; + } + } + + return 0; + } + + void paint (Graphics& g) + { + getLookAndFeel().drawPopupMenuBackground (g, getWidth(), getHeight()); + } + + void paintOverChildren (Graphics& g) + { + if (isScrolling()) + { + LookAndFeel& lf = getLookAndFeel(); + + if (isScrollZoneActive (false)) + lf.drawPopupMenuUpDownArrow (g, getWidth(), scrollZone, true); + + if (isScrollZoneActive (true)) + { + g.setOrigin (0, getHeight() - scrollZone); + lf.drawPopupMenuUpDownArrow (g, getWidth(), scrollZone, false); + } + } + } + + bool isScrollZoneActive (bool bottomOne) const + { + return isScrolling() + && (bottomOne + ? childYOffset < contentHeight - windowPos.getHeight() + : childYOffset > 0); + } + + void addItem (const MenuItemInfo& item) throw() + { + MenuItemComponent* const mic = new MenuItemComponent (item); + addAndMakeVisible (mic); + + int itemW = 80; + int itemH = 16; + mic->getIdealSize (itemW, itemH, standardItemHeight); + mic->setSize (itemW, jlimit (10, 600, itemH)); + mic->addMouseListener (this, false); + } + + // hide this and all sub-comps + void hide (const MenuItemInfo* const item) throw() + { + if (isVisible()) + { + jassert (activeSubMenu == 0 || activeSubMenu->isValidComponent()); + + deleteAndZero (activeSubMenu); + currentChild = 0; + + exitModalState (item != 0 ? item->itemId : 0); + setVisible (false); + + if (item != 0 + && item->commandManager != 0 + && item->itemId != 0) + { + *managerOfChosenCommand = item->commandManager; + } + } + } + + void dismissMenu (const MenuItemInfo* const item) throw() + { + if (owner != 0) + { + owner->dismissMenu (item); + } + else + { + if (item != 0) + { + // need a copy of this on the stack as the one passed in will get deleted during this call + const MenuItemInfo mi (*item); + hide (&mi); + } + else + { + hide (0); + } + } + } + + void mouseMove (const MouseEvent&) + { + timerCallback(); + } + + void mouseDown (const MouseEvent&) + { + timerCallback(); + } + + void mouseDrag (const MouseEvent&) + { + timerCallback(); + } + + void mouseUp (const MouseEvent&) + { + timerCallback(); + } + + void mouseWheelMove (const MouseEvent&, float /*amountX*/, float amountY) + { + alterChildYPos (roundFloatToInt (-10.0f * amountY * scrollZone)); + lastMouseX = -1; + } + + bool keyPressed (const KeyPress& key) + { + if (key.isKeyCode (KeyPress::downKey)) + { + selectNextItem (1); + } + else if (key.isKeyCode (KeyPress::upKey)) + { + selectNextItem (-1); + } + else if (key.isKeyCode (KeyPress::leftKey)) + { + PopupMenuWindow* parentWindow = owner; + + if (parentWindow != 0) + { + MenuItemComponent* currentChildOfParent + = (parentWindow != 0) ? parentWindow->currentChild : 0; + + hide (0); + + if (parentWindow->isValidComponent()) + parentWindow->setCurrentlyHighlightedChild (currentChildOfParent); + + disableTimerUntilMouseMoves(); + } + else if (menuBarComponent != 0) + { + menuBarComponent->keyPressed (key); + } + } + else if (key.isKeyCode (KeyPress::rightKey)) + { + disableTimerUntilMouseMoves(); + + if (showSubMenuFor (currentChild)) + { + jassert (activeSubMenu == 0 || activeSubMenu->isValidComponent()); + + if (activeSubMenu != 0 && activeSubMenu->isVisible()) + activeSubMenu->selectNextItem (1); + } + else if (menuBarComponent != 0) + { + menuBarComponent->keyPressed (key); + } + } + else if (key.isKeyCode (KeyPress::returnKey)) + { + triggerCurrentlyHighlightedItem(); + } + else if (key.isKeyCode (KeyPress::escapeKey)) + { + dismissMenu (0); + } + else + { + return false; + } + + return true; + } + + void inputAttemptWhenModal() + { + timerCallback(); + + if (! isOverAnyMenu()) + { + if (componentAttachedTo != 0 && ! attachedCompWatcher->hasBeenDeleted()) + { + // we want to dismiss the menu, but if we do it synchronously, then + // the mouse-click will be allowed to pass through. That's good, except + // when the user clicks on the button that orginally popped the menu up, + // as they'll expect the menu to go away, and in fact it'll just + // come back. So only dismiss synchronously if they're not on the original + // comp that we're attached to. + int mx, my; + componentAttachedTo->getMouseXYRelative (mx, my); + + if (componentAttachedTo->reallyContains (mx, my, true)) + { + postCommandMessage (dismissCommandId); // dismiss asynchrounously + return; + } + } + + dismissMenu (0); + } + } + + void handleCommandMessage (int commandId) + { + Component::handleCommandMessage (commandId); + + if (commandId == dismissCommandId) + dismissMenu (0); + } + + void timerCallback() + { + if (! isVisible()) + return; + + if (attachedCompWatcher != 0 && attachedCompWatcher->hasBeenDeleted()) + { + dismissMenu (0); + return; + } + + PopupMenuWindow* currentlyModalWindow = dynamic_cast (Component::getCurrentlyModalComponent()); + + if (currentlyModalWindow != 0 && ! treeContains (currentlyModalWindow)) + return; + + startTimer (timerInterval); // do this in case it was called from a mouse + // move rather than a real timer callback + + int mx, my; + Desktop::getMousePosition (mx, my); + + int x = mx, y = my; + globalPositionToRelative (x, y); + + const uint32 now = Time::getMillisecondCounter(); + + if (now > timeEnteredCurrentChildComp + 100 + && reallyContains (x, y, true) + && currentChild->isValidComponent() + && (! disableMouseMoves) + && ! (activeSubMenu != 0 && activeSubMenu->isVisible())) + { + showSubMenuFor (currentChild); + } + + if (mx != lastMouseX || my != lastMouseY || now > lastMouseMoveTime + 350) + { + highlightItemUnderMouse (mx, my, x, y); + } + + bool overScrollArea = false; + + if (isScrolling() + && (isOver || (isDown && ((unsigned int) x) < (unsigned int) getWidth())) + && ((isScrollZoneActive (false) && y < scrollZone) + || (isScrollZoneActive (true) && y > getHeight() - scrollZone))) + { + if (now > lastScroll + 20) + { + scrollAcceleration = jmin (4.0, scrollAcceleration * 1.04); + int amount = 0; + + for (int i = 0; i < getNumChildComponents() && amount == 0; ++i) + amount = ((int) scrollAcceleration) * getChildComponent (i)->getHeight(); + + alterChildYPos (y < scrollZone ? -amount : amount); + + lastScroll = now; + } + + overScrollArea = true; + lastMouseX = -1; // trigger a mouse-move + } + else + { + scrollAcceleration = 1.0; + } + + const bool wasDown = isDown; + bool isOverAny = isOverAnyMenu(); + + if (hideOnExit && hasBeenOver && (! isOverAny) && activeSubMenu != 0) + { + activeSubMenu->updateMouseOverStatus (mx, my); + isOverAny = isOverAnyMenu(); + } + + if (hideOnExit && hasBeenOver && ! isOverAny) + { + hide (0); + } + else + { + isDown = hasBeenOver + && (ModifierKeys::getCurrentModifiers().isAnyMouseButtonDown() + || ModifierKeys::getCurrentModifiersRealtime().isAnyMouseButtonDown()); + + bool anyFocused = Process::isForegroundProcess(); + + if (anyFocused && Component::getCurrentlyFocusedComponent() == 0) + { + // because no component at all may have focus, our test here will + // only be triggered when something has focus and then loses it. + anyFocused = ! hasAnyJuceCompHadFocus; + + for (int i = ComponentPeer::getNumPeers(); --i >= 0;) + { + if (ComponentPeer::getPeer (i)->isFocused()) + { + anyFocused = true; + hasAnyJuceCompHadFocus = true; + break; + } + } + } + + if (! anyFocused) + { + if (now > lastFocused + 10) + { + wasHiddenBecauseOfAppChange = true; + dismissMenu (0); + + return; // may have been deleted by the previous call.. + } + } + else if (wasDown && now > menuCreationTime + 250 + && ! (isDown || overScrollArea)) + { + isOver = reallyContains (x, y, true); + + if (isOver) + { + triggerCurrentlyHighlightedItem(); + } + else if ((hasBeenOver || ! dismissOnMouseUp) && ! isOverAny) + { + dismissMenu (0); + } + + return; // may have been deleted by the previous calls.. + } + else + { + lastFocused = now; + } + } + } + + juce_UseDebuggingNewOperator + +private: + PopupMenuWindow* owner; + MenuItemComponent* currentChild; + PopupMenuWindow* activeSubMenu; + Component* menuBarComponent; + ApplicationCommandManager** managerOfChosenCommand; + Component* componentAttachedTo; + ComponentDeletionWatcher* attachedCompWatcher; + Rectangle windowPos; + int lastMouseX, lastMouseY; + int minimumWidth, maximumNumColumns, standardItemHeight; + bool isOver, hasBeenOver, isDown, needsToScroll; + bool dismissOnMouseUp, hideOnExit, disableMouseMoves, hasAnyJuceCompHadFocus; + int numColumns, contentHeight, childYOffset; + Array columnWidths; + uint32 menuCreationTime, lastFocused, lastScroll, lastMouseMoveTime, timeEnteredCurrentChildComp; + double scrollAcceleration; + + bool overlaps (const Rectangle& r) const throw() + { + return r.intersects (getBounds()) + || (owner != 0 && owner->overlaps (r)); + } + + bool isOverAnyMenu() const throw() + { + return (owner != 0) ? owner->isOverAnyMenu() + : isOverChildren(); + } + + bool isOverChildren() const throw() + { + jassert (activeSubMenu == 0 || activeSubMenu->isValidComponent()); + + return isVisible() + && (isOver || (activeSubMenu != 0 && activeSubMenu->isOverChildren())); + } + + void updateMouseOverStatus (const int mx, const int my) throw() + { + int rx = mx, ry = my; + globalPositionToRelative (rx, ry); + isOver = reallyContains (rx, ry, true); + + if (activeSubMenu != 0) + activeSubMenu->updateMouseOverStatus (mx, my); + } + + bool treeContains (const PopupMenuWindow* const window) const throw() + { + const PopupMenuWindow* mw = this; + + while (mw->owner != 0) + mw = mw->owner; + + while (mw != 0) + { + if (mw == window) + return true; + + mw = mw->activeSubMenu; + } + + return false; + } + + void calculateWindowPos (const int minX, const int maxX, + const int minY, const int maxY, + const bool alignToRectangle) + { + const Rectangle mon (Desktop::getInstance() + .getMonitorAreaContaining ((minX + maxX) / 2, + (minY + maxY) / 2, + true)); + + int x, y, widthToUse, heightToUse; + layoutMenuItems (mon.getWidth() - 24, widthToUse, heightToUse); + + if (alignToRectangle) + { + x = minX; + + const int spaceUnder = mon.getHeight() - (maxY - mon.getY()); + const int spaceOver = minY - mon.getY(); + + if (heightToUse < spaceUnder - 30 || spaceUnder >= spaceOver) + y = maxY; + else + y = minY - heightToUse; + } + else + { + bool tendTowardsRight = (minX + maxX) / 2 < mon.getCentreX(); + + if (owner != 0) + { + if (owner->owner != 0) + { + const bool ownerGoingRight = (owner->getX() + owner->getWidth() / 2 + > owner->owner->getX() + owner->owner->getWidth() / 2); + + if (ownerGoingRight && maxX + widthToUse < mon.getRight() - 4) + tendTowardsRight = true; + else if ((! ownerGoingRight) && minX > widthToUse + 4) + tendTowardsRight = false; + } + else if (maxX + widthToUse < mon.getRight() - 32) + { + tendTowardsRight = true; + } + } + + const int biggestSpace = jmax (mon.getRight() - maxX, + minX - mon.getX()) - 32; + + if (biggestSpace < widthToUse) + { + layoutMenuItems (biggestSpace + (maxX - minX) / 3, widthToUse, heightToUse); + + if (numColumns > 1) + layoutMenuItems (biggestSpace - 4, widthToUse, heightToUse); + + tendTowardsRight = (mon.getRight() - maxX) >= (minX - mon.getX()); + } + + if (tendTowardsRight) + x = jmin (mon.getRight() - widthToUse - 4, maxX); + else + x = jmax (mon.getX() + 4, minX - widthToUse); + + y = minY; + if ((minY + maxY) / 2 > mon.getCentreY()) + y = jmax (mon.getY(), maxY - heightToUse); + } + + x = jlimit (mon.getX() + 1, mon.getRight() - (widthToUse + 6), x); + y = jlimit (mon.getY() + 1, mon.getBottom() - (heightToUse + 6), y); + + windowPos.setBounds (x, y, widthToUse, heightToUse); + + // sets this flag if it's big enough to obscure any of its parent menus + hideOnExit = (owner != 0) + && owner->windowPos.intersects (windowPos.expanded (-4, -4)); + } + + void layoutMenuItems (const int maxMenuW, int& width, int& height) + { + numColumns = 0; + contentHeight = 0; + const int maxMenuH = getParentHeight() - 24; + int totalW; + + do + { + ++numColumns; + totalW = workOutBestSize (numColumns, maxMenuW); + + if (totalW > maxMenuW) + { + numColumns = jmax (1, numColumns - 1); + totalW = workOutBestSize (numColumns, maxMenuW); // to update col widths + break; + } + else if (totalW > maxMenuW / 2 || contentHeight < maxMenuH) + { + break; + } + + } while (numColumns < maximumNumColumns); + + const int actualH = jmin (contentHeight, maxMenuH); + + needsToScroll = contentHeight > actualH; + + width = updateYPositions(); + height = actualH + borderSize * 2; + } + + int workOutBestSize (const int numColumns, const int maxMenuW) + { + int totalW = 0; + contentHeight = 0; + int childNum = 0; + + for (int col = 0; col < numColumns; ++col) + { + int i, colW = 50, colH = 0; + + const int numChildren = jmin (getNumChildComponents() - childNum, + (getNumChildComponents() + numColumns - 1) / numColumns); + + for (i = numChildren; --i >= 0;) + { + colW = jmax (colW, getChildComponent (childNum + i)->getWidth()); + colH += getChildComponent (childNum + i)->getHeight(); + } + + colW = jmin (maxMenuW / jmax (1, numColumns - 2), colW + borderSize * 2); + + columnWidths.set (col, colW); + totalW += colW; + contentHeight = jmax (contentHeight, colH); + + childNum += numChildren; + } + + if (totalW < minimumWidth) + { + totalW = minimumWidth; + + for (int col = 0; col < numColumns; ++col) + columnWidths.set (0, totalW / numColumns); + } + + return totalW; + } + + void ensureItemIsVisible (const int itemId, int wantedY) + { + jassert (itemId != 0) + + for (int i = getNumChildComponents(); --i >= 0;) + { + MenuItemComponent* const m = (MenuItemComponent*) getChildComponent (i); + + if (m != 0 + && m->itemInfo.itemId == itemId + && windowPos.getHeight() > scrollZone * 4) + { + const int currentY = m->getY(); + + if (wantedY > 0 || currentY < 0 || m->getBottom() > windowPos.getHeight()) + { + if (wantedY < 0) + wantedY = jlimit (scrollZone, + jmax (scrollZone, windowPos.getHeight() - (scrollZone + m->getHeight())), + currentY); + + const Rectangle mon (Desktop::getInstance() + .getMonitorAreaContaining (windowPos.getX(), + windowPos.getY(), + true)); + + int deltaY = wantedY - currentY; + + const int newY = jlimit (mon.getY(), + mon.getBottom() - windowPos.getHeight(), + windowPos.getY() + deltaY); + + deltaY -= newY - windowPos.getY(); + + childYOffset -= deltaY; + windowPos.setPosition (windowPos.getX(), newY); + + updateYPositions(); + } + + break; + } + } + } + + void resizeToBestWindowPos() + { + Rectangle r (windowPos); + + if (childYOffset < 0) + { + r.setBounds (r.getX(), r.getY() - childYOffset, + r.getWidth(), r.getHeight() + childYOffset); + } + else if (childYOffset > 0) + { + const int spaceAtBottom = r.getHeight() - (contentHeight - childYOffset); + + if (spaceAtBottom > 0) + r.setSize (r.getWidth(), r.getHeight() - spaceAtBottom); + } + + setBounds (r); + updateYPositions(); + } + + void alterChildYPos (const int delta) + { + if (isScrolling()) + { + childYOffset += delta; + + if (delta < 0) + { + childYOffset = jmax (childYOffset, 0); + } + else if (delta > 0) + { + childYOffset = jmin (childYOffset, + contentHeight - windowPos.getHeight() + borderSize); + } + + updateYPositions(); + } + else + { + childYOffset = 0; + } + + resizeToBestWindowPos(); + repaint(); + } + + int updateYPositions() + { + int x = 0; + int childNum = 0; + + for (int col = 0; col < numColumns; ++col) + { + const int numChildren = jmin (getNumChildComponents() - childNum, + (getNumChildComponents() + numColumns - 1) / numColumns); + + const int colW = columnWidths [col]; + + int y = borderSize - (childYOffset + (getY() - windowPos.getY())); + + for (int i = 0; i < numChildren; ++i) + { + Component* const c = getChildComponent (childNum + i); + c->setBounds (x, y, colW, c->getHeight()); + y += c->getHeight(); + } + + x += colW; + childNum += numChildren; + } + + return x; + } + + bool isScrolling() const throw() + { + return childYOffset != 0 || needsToScroll; + } + + void setCurrentlyHighlightedChild (MenuItemComponent* const child) throw() + { + if (currentChild->isValidComponent()) + currentChild->setHighlighted (false); + + currentChild = child; + + if (currentChild != 0) + { + currentChild->setHighlighted (true); + timeEnteredCurrentChildComp = Time::getApproximateMillisecondCounter(); + } + } + + bool showSubMenuFor (MenuItemComponent* const childComp) + { + jassert (activeSubMenu == 0 || activeSubMenu->isValidComponent()); + deleteAndZero (activeSubMenu); + + if (childComp->isValidComponent() && childComp->itemInfo.hasActiveSubMenu()) + { + int left = 0, top = 0; + childComp->relativePositionToGlobal (left, top); + int right = childComp->getWidth(), bottom = childComp->getHeight(); + childComp->relativePositionToGlobal (right, bottom); + + activeSubMenu = PopupMenuWindow::create (*(childComp->itemInfo.subMenu), + dismissOnMouseUp, + this, + left, right, top, bottom, + 0, maximumNumColumns, + standardItemHeight, + false, 0, menuBarComponent, + managerOfChosenCommand, + componentAttachedTo); + + if (activeSubMenu != 0) + { + activeSubMenu->setVisible (true); + activeSubMenu->enterModalState (false); + activeSubMenu->toFront (false); + return true; + } + } + + return false; + } + + void highlightItemUnderMouse (const int mx, const int my, const int x, const int y) + { + isOver = reallyContains (x, y, true); + + if (isOver) + hasBeenOver = true; + + if (abs (lastMouseX - mx) > 2 || abs (lastMouseY - my) > 2) + { + lastMouseMoveTime = Time::getApproximateMillisecondCounter(); + + if (disableMouseMoves && isOver) + disableMouseMoves = false; + } + + if (disableMouseMoves) + return; + + bool isMovingTowardsMenu = false; + + jassert (activeSubMenu == 0 || activeSubMenu->isValidComponent()) + + if (isOver && (activeSubMenu != 0) && (mx != lastMouseX || my != lastMouseY)) + { + // try to intelligently guess whether the user is moving the mouse towards a currently-open + // submenu. To do this, look at whether the mouse stays inside a triangular region that + // extends from the last mouse pos to the submenu's rectangle.. + + float subX = (float) activeSubMenu->getScreenX(); + + if (activeSubMenu->getX() > getX()) + { + lastMouseX -= 2; // to enlarge the triangle a bit, in case the mouse only moves a couple of pixels + } + else + { + lastMouseX += 2; + subX += activeSubMenu->getWidth(); + } + + Path areaTowardsSubMenu; + areaTowardsSubMenu.addTriangle ((float) lastMouseX, + (float) lastMouseY, + subX, + (float) activeSubMenu->getScreenY(), + subX, + (float) (activeSubMenu->getScreenY() + activeSubMenu->getHeight())); + + isMovingTowardsMenu = areaTowardsSubMenu.contains ((float) mx, (float) my); + } + + lastMouseX = mx; + lastMouseY = my; + + if (! isMovingTowardsMenu) + { + Component* c = getComponentAt (x, y); + if (c == this) + c = 0; + + MenuItemComponent* mic = dynamic_cast (c); + + if (mic == 0 && c != 0) + mic = c->findParentComponentOfClass ((MenuItemComponent*) 0); + + if (mic != currentChild + && (isOver || (activeSubMenu == 0) || ! activeSubMenu->isVisible())) + { + if (isOver && (c != 0) && (activeSubMenu != 0)) + { + activeSubMenu->hide (0); + } + + if (! isOver) + mic = 0; + + setCurrentlyHighlightedChild (mic); + } + } + } + + void triggerCurrentlyHighlightedItem() + { + if (currentChild->isValidComponent() + && currentChild->itemInfo.canBeTriggered() + && (currentChild->itemInfo.customComp == 0 + || currentChild->itemInfo.customComp->isTriggeredAutomatically)) + { + dismissMenu (¤tChild->itemInfo); + } + } + + void selectNextItem (const int delta) + { + disableTimerUntilMouseMoves(); + MenuItemComponent* mic = 0; + bool wasLastOne = (currentChild == 0); + const int numItems = getNumChildComponents(); + + for (int i = 0; i < numItems + 1; ++i) + { + int index = (delta > 0) ? i : (numItems - 1 - i); + index = (index + numItems) % numItems; + + mic = dynamic_cast (getChildComponent (index)); + + if (mic != 0 && (mic->itemInfo.canBeTriggered() || mic->itemInfo.hasActiveSubMenu()) + && wasLastOne) + break; + + if (mic == currentChild) + wasLastOne = true; + } + + setCurrentlyHighlightedChild (mic); + } + + void disableTimerUntilMouseMoves() throw() + { + disableMouseMoves = true; + + if (owner != 0) + owner->disableTimerUntilMouseMoves(); + } + + PopupMenuWindow (const PopupMenuWindow&); + const PopupMenuWindow& operator= (const PopupMenuWindow&); +}; + +PopupMenu::PopupMenu() throw() + : items (8), + lookAndFeel (0), + separatorPending (false) +{ +} + +PopupMenu::PopupMenu (const PopupMenu& other) throw() + : items (8), + lookAndFeel (other.lookAndFeel), + separatorPending (false) +{ + items.ensureStorageAllocated (other.items.size()); + + for (int i = 0; i < other.items.size(); ++i) + items.add (new MenuItemInfo (*(const MenuItemInfo*) other.items.getUnchecked(i))); +} + +const PopupMenu& PopupMenu::operator= (const PopupMenu& other) throw() +{ + if (this != &other) + { + lookAndFeel = other.lookAndFeel; + + clear(); + items.ensureStorageAllocated (other.items.size()); + + for (int i = 0; i < other.items.size(); ++i) + items.add (new MenuItemInfo (*(const MenuItemInfo*) other.items.getUnchecked(i))); + } + + return *this; +} + +PopupMenu::~PopupMenu() throw() +{ + clear(); +} + +void PopupMenu::clear() throw() +{ + for (int i = items.size(); --i >= 0;) + { + MenuItemInfo* const mi = (MenuItemInfo*) items.getUnchecked(i); + delete mi; + } + + items.clear(); + separatorPending = false; +} + +void PopupMenu::addSeparatorIfPending() +{ + if (separatorPending) + { + separatorPending = false; + + if (items.size() > 0) + items.add (new MenuItemInfo()); + } +} + +void PopupMenu::addItem (const int itemResultId, + const String& itemText, + const bool isActive, + const bool isTicked, + const Image* const iconToUse) throw() +{ + jassert (itemResultId != 0); // 0 is used as a return value to indicate that the user + // didn't pick anything, so you shouldn't use it as the id + // for an item.. + + addSeparatorIfPending(); + + items.add (new MenuItemInfo (itemResultId, + itemText, + isActive, + isTicked, + iconToUse, + Colours::black, + false, + 0, 0, 0)); +} + +void PopupMenu::addCommandItem (ApplicationCommandManager* commandManager, + const int commandID, + const String& displayName) throw() +{ + jassert (commandManager != 0 && commandID != 0); + + const ApplicationCommandInfo* const registeredInfo = commandManager->getCommandForID (commandID); + + if (registeredInfo != 0) + { + ApplicationCommandInfo info (*registeredInfo); + ApplicationCommandTarget* const target = commandManager->getTargetForCommand (commandID, info); + + addSeparatorIfPending(); + + items.add (new MenuItemInfo (commandID, + displayName.isNotEmpty() ? displayName + : info.shortName, + target != 0 && (info.flags & ApplicationCommandInfo::isDisabled) == 0, + (info.flags & ApplicationCommandInfo::isTicked) != 0, + 0, + Colours::black, + false, + 0, 0, + commandManager)); + } +} + +void PopupMenu::addColouredItem (const int itemResultId, + const String& itemText, + const Colour& itemTextColour, + const bool isActive, + const bool isTicked, + const Image* const iconToUse) throw() +{ + jassert (itemResultId != 0); // 0 is used as a return value to indicate that the user + // didn't pick anything, so you shouldn't use it as the id + // for an item.. + + addSeparatorIfPending(); + + items.add (new MenuItemInfo (itemResultId, + itemText, + isActive, + isTicked, + iconToUse, + itemTextColour, + true, + 0, 0, 0)); +} + +void PopupMenu::addCustomItem (const int itemResultId, + PopupMenuCustomComponent* const customComponent) throw() +{ + jassert (itemResultId != 0); // 0 is used as a return value to indicate that the user + // didn't pick anything, so you shouldn't use it as the id + // for an item.. + + addSeparatorIfPending(); + + items.add (new MenuItemInfo (itemResultId, + String::empty, + true, + false, + 0, + Colours::black, + false, + customComponent, + 0, 0)); +} + +class NormalComponentWrapper : public PopupMenuCustomComponent +{ +public: + NormalComponentWrapper (Component* const comp, + const int w, const int h, + const bool triggerMenuItemAutomaticallyWhenClicked) + : PopupMenuCustomComponent (triggerMenuItemAutomaticallyWhenClicked), + width (w), + height (h) + { + addAndMakeVisible (comp); + } + + ~NormalComponentWrapper() {} + + void getIdealSize (int& idealWidth, int& idealHeight) + { + idealWidth = width; + idealHeight = height; + } + + void resized() + { + if (getChildComponent(0) != 0) + getChildComponent(0)->setBounds (0, 0, getWidth(), getHeight()); + } + + juce_UseDebuggingNewOperator + +private: + const int width, height; + + NormalComponentWrapper (const NormalComponentWrapper&); + const NormalComponentWrapper& operator= (const NormalComponentWrapper&); +}; + +void PopupMenu::addCustomItem (const int itemResultId, + Component* customComponent, + int idealWidth, int idealHeight, + const bool triggerMenuItemAutomaticallyWhenClicked) throw() +{ + addCustomItem (itemResultId, + new NormalComponentWrapper (customComponent, + idealWidth, idealHeight, + triggerMenuItemAutomaticallyWhenClicked)); +} + +void PopupMenu::addSubMenu (const String& subMenuName, + const PopupMenu& subMenu, + const bool isActive, + Image* const iconToUse) throw() +{ + addSeparatorIfPending(); + + items.add (new MenuItemInfo (0, + subMenuName, + isActive && (subMenu.getNumItems() > 0), + false, + iconToUse, + Colours::black, + false, + 0, + &subMenu, + 0)); +} + +void PopupMenu::addSeparator() throw() +{ + separatorPending = true; +} + +class HeaderItemComponent : public PopupMenuCustomComponent +{ +public: + HeaderItemComponent (const String& name) + : PopupMenuCustomComponent (false) + { + setName (name); + } + + ~HeaderItemComponent() + { + } + + void paint (Graphics& g) + { + Font f (getLookAndFeel().getPopupMenuFont()); + f.setBold (true); + g.setFont (f); + + g.setColour (findColour (PopupMenu::headerTextColourId)); + + g.drawFittedText (getName(), + 12, 0, getWidth() - 16, proportionOfHeight (0.8f), + Justification::bottomLeft, 1); + } + + void getIdealSize (int& idealWidth, + int& idealHeight) + { + getLookAndFeel().getIdealPopupMenuItemSize (getName(), false, -1, idealWidth, idealHeight); + idealHeight += idealHeight / 2; + idealWidth += idealWidth / 4; + } + + juce_UseDebuggingNewOperator +}; + +void PopupMenu::addSectionHeader (const String& title) throw() +{ + addCustomItem (0X4734a34f, new HeaderItemComponent (title)); +} + +Component* PopupMenu::createMenuComponent (const int x, const int y, const int w, const int h, + const int itemIdThatMustBeVisible, + const int minimumWidth, + const int maximumNumColumns, + const int standardItemHeight, + const bool alignToRectangle, + Component* menuBarComponent, + ApplicationCommandManager** managerOfChosenCommand, + Component* const componentAttachedTo) throw() +{ + PopupMenuWindow* const pw + = PopupMenuWindow::create (*this, + ModifierKeys::getCurrentModifiers().isAnyMouseButtonDown(), + 0, + x, x + w, + y, y + h, + minimumWidth, + maximumNumColumns, + standardItemHeight, + alignToRectangle, + itemIdThatMustBeVisible, + menuBarComponent, + managerOfChosenCommand, + componentAttachedTo); + + if (pw != 0) + pw->setVisible (true); + + return pw; +} + +int PopupMenu::showMenu (const int x, const int y, const int w, const int h, + const int itemIdThatMustBeVisible, + const int minimumWidth, + const int maximumNumColumns, + const int standardItemHeight, + const bool alignToRectangle, + Component* const componentAttachedTo) throw() +{ + Component* const prevFocused = Component::getCurrentlyFocusedComponent(); + + ComponentDeletionWatcher* deletionChecker1 = 0; + if (prevFocused != 0) + deletionChecker1 = new ComponentDeletionWatcher (prevFocused); + + Component* const prevTopLevel = (prevFocused != 0) ? prevFocused->getTopLevelComponent() : 0; + + ComponentDeletionWatcher* deletionChecker2 = 0; + if (prevTopLevel != 0) + deletionChecker2 = new ComponentDeletionWatcher (prevTopLevel); + + wasHiddenBecauseOfAppChange = false; + + int result = 0; + ApplicationCommandManager* managerOfChosenCommand = 0; + + Component* const popupComp = createMenuComponent (x, y, w, h, + itemIdThatMustBeVisible, + minimumWidth, + maximumNumColumns > 0 ? maximumNumColumns : 7, + standardItemHeight, + alignToRectangle, 0, + &managerOfChosenCommand, + componentAttachedTo); + + if (popupComp != 0) + { + popupComp->enterModalState (false); + popupComp->toFront (false); // need to do this after making it modal, or it could + // be stuck behind other comps that are already modal.. + + result = popupComp->runModalLoop(); + delete popupComp; + + if (! wasHiddenBecauseOfAppChange) + { + if (deletionChecker2 != 0 && ! deletionChecker2->hasBeenDeleted()) + prevTopLevel->toFront (true); + + if (deletionChecker1 != 0 && ! deletionChecker1->hasBeenDeleted()) + prevFocused->grabKeyboardFocus(); + } + } + + delete deletionChecker1; + delete deletionChecker2; + + if (managerOfChosenCommand != 0 && result != 0) + { + ApplicationCommandTarget::InvocationInfo info (result); + info.invocationMethod = ApplicationCommandTarget::InvocationInfo::fromMenu; + + managerOfChosenCommand->invoke (info, true); + } + + return result; +} + +int PopupMenu::show (const int itemIdThatMustBeVisible, + const int minimumWidth, + const int maximumNumColumns, + const int standardItemHeight) +{ + int x, y; + Desktop::getMousePosition (x, y); + + return showAt (x, y, + itemIdThatMustBeVisible, + minimumWidth, + maximumNumColumns, + standardItemHeight); +} + +int PopupMenu::showAt (const int screenX, + const int screenY, + const int itemIdThatMustBeVisible, + const int minimumWidth, + const int maximumNumColumns, + const int standardItemHeight) +{ + return showMenu (screenX, screenY, 1, 1, + itemIdThatMustBeVisible, + minimumWidth, maximumNumColumns, + standardItemHeight, + false, 0); +} + +int PopupMenu::showAt (Component* componentToAttachTo, + const int itemIdThatMustBeVisible, + const int minimumWidth, + const int maximumNumColumns, + const int standardItemHeight) +{ + if (componentToAttachTo != 0) + { + return showMenu (componentToAttachTo->getScreenX(), + componentToAttachTo->getScreenY(), + componentToAttachTo->getWidth(), + componentToAttachTo->getHeight(), + itemIdThatMustBeVisible, + minimumWidth, + maximumNumColumns, + standardItemHeight, + true, componentToAttachTo); + } + else + { + return show (itemIdThatMustBeVisible, + minimumWidth, + maximumNumColumns, + standardItemHeight); + } +} + +void JUCE_CALLTYPE PopupMenu::dismissAllActiveMenus() throw() +{ + for (int i = activeMenuWindows.size(); --i >= 0;) + { + PopupMenuWindow* const pmw = (PopupMenuWindow*) activeMenuWindows[i]; + + if (pmw != 0) + pmw->dismissMenu (0); + } +} + +int PopupMenu::getNumItems() const throw() +{ + int num = 0; + + for (int i = items.size(); --i >= 0;) + if (! ((MenuItemInfo*) items.getUnchecked(i))->isSeparator) + ++num; + + return num; +} + +bool PopupMenu::containsCommandItem (const int commandID) const throw() +{ + for (int i = items.size(); --i >= 0;) + { + const MenuItemInfo* mi = (const MenuItemInfo*) items.getUnchecked (i); + + if ((mi->itemId == commandID && mi->commandManager != 0) + || (mi->subMenu != 0 && mi->subMenu->containsCommandItem (commandID))) + { + return true; + } + } + + return false; +} + +bool PopupMenu::containsAnyActiveItems() const throw() +{ + for (int i = items.size(); --i >= 0;) + { + const MenuItemInfo* const mi = (const MenuItemInfo*) items.getUnchecked (i); + + if (mi->subMenu != 0) + { + if (mi->subMenu->containsAnyActiveItems()) + return true; + } + else if (mi->active) + { + return true; + } + } + + return false; +} + +void PopupMenu::setLookAndFeel (LookAndFeel* const newLookAndFeel) throw() +{ + lookAndFeel = newLookAndFeel; +} + +PopupMenuCustomComponent::PopupMenuCustomComponent (const bool isTriggeredAutomatically_) + : refCount_ (0), + isHighlighted (false), + isTriggeredAutomatically (isTriggeredAutomatically_) +{ +} + +PopupMenuCustomComponent::~PopupMenuCustomComponent() +{ + jassert (refCount_ == 0); // should be deleted only by the menu component, as they keep a ref-count. +} + +void PopupMenuCustomComponent::triggerMenuItem() +{ + MenuItemComponent* const mic = dynamic_cast (getParentComponent()); + + if (mic != 0) + { + PopupMenuWindow* const pmw = dynamic_cast (mic->getParentComponent()); + + if (pmw != 0) + { + pmw->dismissMenu (&mic->itemInfo); + } + else + { + // something must have gone wrong with the component hierarchy if this happens.. + jassertfalse + } + } + else + { + // why isn't this component inside a menu? Not much point triggering the item if + // there's no menu. + jassertfalse + } +} + +PopupMenu::MenuItemIterator::MenuItemIterator (const PopupMenu& menu_) throw() + : subMenu (0), + itemId (0), + isSeparator (false), + isTicked (false), + isEnabled (false), + isCustomComponent (false), + isSectionHeader (false), + customColour (0), + customImage (0), + menu (menu_), + index (0) +{ +} + +PopupMenu::MenuItemIterator::~MenuItemIterator() throw() +{ +} + +bool PopupMenu::MenuItemIterator::next() throw() +{ + if (index >= menu.items.size()) + return false; + + const MenuItemInfo* const item = (const MenuItemInfo*) menu.items.getUnchecked (index); + ++index; + + itemName = item->customComp != 0 ? item->customComp->getName() : item->text; + subMenu = item->subMenu; + itemId = item->itemId; + + isSeparator = item->isSeparator; + isTicked = item->isTicked; + isEnabled = item->active; + isSectionHeader = dynamic_cast (item->customComp) != 0; + isCustomComponent = (! isSectionHeader) && item->customComp != 0; + customColour = item->usesColour ? &(item->textColour) : 0; + customImage = item->image; + commandManager = item->commandManager; + + return true; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_PopupMenu.cpp *********/ + +/********* Start of inlined file: juce_ComponentDragger.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +ComponentDragger::ComponentDragger() + : constrainer (0), + originalX (0), + originalY (0) +{ +} + +ComponentDragger::~ComponentDragger() +{ +} + +void ComponentDragger::startDraggingComponent (Component* const componentToDrag, + ComponentBoundsConstrainer* const constrainer_) +{ + jassert (componentToDrag->isValidComponent()); + + if (componentToDrag->isValidComponent()) + { + constrainer = constrainer_; + originalX = 0; + originalY = 0; + componentToDrag->relativePositionToGlobal (originalX, originalY); + } +} + +void ComponentDragger::dragComponent (Component* const componentToDrag, const MouseEvent& e) +{ + jassert (componentToDrag->isValidComponent()); + jassert (e.mods.isAnyMouseButtonDown()); // (the event has to be a drag event..) + + if (componentToDrag->isValidComponent()) + { + int x = originalX + e.getDistanceFromDragStartX(); + int y = originalY + e.getDistanceFromDragStartY(); + int w = componentToDrag->getWidth(); + int h = componentToDrag->getHeight(); + + const Component* const parentComp = componentToDrag->getParentComponent(); + if (parentComp != 0) + parentComp->globalPositionToRelative (x, y); + + if (constrainer != 0) + constrainer->setBoundsForComponent (componentToDrag, x, y, w, h, + false, false, false, false); + else + componentToDrag->setBounds (x, y, w, h); + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ComponentDragger.cpp *********/ + +/********* Start of inlined file: juce_DragAndDropContainer.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +bool juce_performDragDropFiles (const StringArray& files, const bool copyFiles, bool& shouldStop); +bool juce_performDragDropText (const String& text, bool& shouldStop); + +class DragImageComponent : public Component, + public Timer +{ +private: + Image* image; + Component* const source; + DragAndDropContainer* const owner; + + ComponentDeletionWatcher* sourceWatcher; + Component* mouseDragSource; + ComponentDeletionWatcher* mouseDragSourceWatcher; + + DragAndDropTarget* currentlyOver; + String dragDesc; + int xOff, yOff; + bool hasCheckedForExternalDrag, drawImage; + + DragImageComponent (const DragImageComponent&); + const DragImageComponent& operator= (const DragImageComponent&); + +public: + DragImageComponent (Image* const im, + const String& desc, + Component* const s, + DragAndDropContainer* const o) + : image (im), + source (s), + owner (o), + currentlyOver (0), + dragDesc (desc), + hasCheckedForExternalDrag (false), + drawImage (true) + { + setSize (im->getWidth(), im->getHeight()); + + sourceWatcher = new ComponentDeletionWatcher (source); + + mouseDragSource = Component::getComponentUnderMouse(); + + if (mouseDragSource == 0) + mouseDragSource = source; + + mouseDragSourceWatcher = new ComponentDeletionWatcher (mouseDragSource); + mouseDragSource->addMouseListener (this, false); + + int mx, my; + Desktop::getLastMouseDownPosition (mx, my); + source->globalPositionToRelative (mx, my); + + xOff = jlimit (0, im->getWidth(), mx); + yOff = jlimit (0, im->getHeight(), my); + + startTimer (200); + + setInterceptsMouseClicks (false, false); + setAlwaysOnTop (true); + } + + ~DragImageComponent() + { + if (owner->dragImageComponent == this) + owner->dragImageComponent = 0; + + if (((Component*) currentlyOver)->isValidComponent()) + { + Component* const over = dynamic_cast (currentlyOver); + + if (over != 0 + && over->isValidComponent() + && source->isValidComponent() + && currentlyOver->isInterestedInDragSource (dragDesc, source)) + { + currentlyOver->itemDragExit (dragDesc, source); + } + } + + if (! mouseDragSourceWatcher->hasBeenDeleted()) + mouseDragSource->removeMouseListener (this); + + delete mouseDragSourceWatcher; + delete sourceWatcher; + delete image; + } + + void paint (Graphics& g) + { + if (isOpaque()) + g.fillAll (Colours::white); + + if (drawImage) + { + g.setOpacity (1.0f); + g.drawImageAt (image, 0, 0); + } + } + + DragAndDropTarget* findTarget (const int screenX, const int screenY, + int& relX, int& relY) const throw() + { + Component* hit = getParentComponent(); + + if (hit == 0) + { + hit = Desktop::getInstance().findComponentAt (screenX, screenY); + } + else + { + int rx = screenX, ry = screenY; + hit->globalPositionToRelative (rx, ry); + + hit = hit->getComponentAt (rx, ry); + } + + while (hit != 0) + { + DragAndDropTarget* const ddt = dynamic_cast (hit); + + if (ddt != 0 && ddt->isInterestedInDragSource (dragDesc, source)) + { + relX = screenX; + relY = screenY; + hit->globalPositionToRelative (relX, relY); + return ddt; + } + + hit = hit->getParentComponent(); + } + + return 0; + } + + void mouseUp (const MouseEvent& e) + { + if (e.originalComponent != this) + { + if (! mouseDragSourceWatcher->hasBeenDeleted()) + mouseDragSource->removeMouseListener (this); + + bool dropAccepted = false; + DragAndDropTarget* ddt = 0; + int relX = 0, relY = 0; + + if (isVisible()) + { + setVisible (false); + ddt = findTarget (e.getScreenX(), + e.getScreenY(), + relX, relY); + + // fade this component and remove it - it'll be deleted later by the timer callback + + dropAccepted = ddt != 0; + + setVisible (true); + + if (dropAccepted || sourceWatcher->hasBeenDeleted()) + { + fadeOutComponent (120); + } + else + { + int targetX = source->getWidth() / 2; + int targetY = source->getHeight() / 2; + source->relativePositionToGlobal (targetX, targetY); + + int ourCentreX = getWidth() / 2; + int ourCentreY = getHeight() / 2; + relativePositionToGlobal (ourCentreX, ourCentreY); + + fadeOutComponent (120, + targetX - ourCentreX, + targetY - ourCentreY); + } + } + + if (getParentComponent() != 0) + getParentComponent()->removeChildComponent (this); + + if (dropAccepted && ddt != 0) + ddt->itemDropped (dragDesc, source, relX, relY); + + // careful - this object could now be deleted.. + } + } + + void updateLocation (const bool canDoExternalDrag, int x, int y) + { + int newX = x - xOff; + int newY = y - yOff; + + if (getParentComponent() != 0) + getParentComponent()->globalPositionToRelative (newX, newY); + + if (newX != getX() || newY != getY()) + { + setTopLeftPosition (newX, newY); + + int relX = 0, relY = 0; + DragAndDropTarget* const ddt = findTarget (x, y, relX, relY); + + drawImage = (ddt == 0) || ddt->shouldDrawDragImageWhenOver(); + + if (ddt != currentlyOver) + { + Component* const over = dynamic_cast (currentlyOver); + + if (over != 0 + && over->isValidComponent() + && ! (sourceWatcher->hasBeenDeleted()) + && currentlyOver->isInterestedInDragSource (dragDesc, source)) + { + currentlyOver->itemDragExit (dragDesc, source); + } + + currentlyOver = ddt; + + if (currentlyOver != 0 + && currentlyOver->isInterestedInDragSource (dragDesc, source)) + currentlyOver->itemDragEnter (dragDesc, source, relX, relY); + } + + if (currentlyOver != 0 + && currentlyOver->isInterestedInDragSource (dragDesc, source)) + currentlyOver->itemDragMove (dragDesc, source, relX, relY); + + if (currentlyOver == 0 + && canDoExternalDrag + && ! hasCheckedForExternalDrag) + { + if (Desktop::getInstance().findComponentAt (x, y) == 0) + { + hasCheckedForExternalDrag = true; + StringArray files; + bool canMoveFiles = false; + + if (owner->shouldDropFilesWhenDraggedExternally (dragDesc, source, files, canMoveFiles) + && files.size() > 0) + { + ComponentDeletionWatcher cdw (this); + setVisible (false); + + if (ModifierKeys::getCurrentModifiersRealtime().isAnyMouseButtonDown()) + DragAndDropContainer::performExternalDragDropOfFiles (files, canMoveFiles); + + if (! cdw.hasBeenDeleted()) + delete this; + + return; + } + } + } + } + } + + void mouseDrag (const MouseEvent& e) + { + if (e.originalComponent != this) + updateLocation (true, e.getScreenX(), e.getScreenY()); + } + + void timerCallback() + { + if (sourceWatcher->hasBeenDeleted()) + { + delete this; + } + else if (! isMouseButtonDownAnywhere()) + { + if (! mouseDragSourceWatcher->hasBeenDeleted()) + mouseDragSource->removeMouseListener (this); + + delete this; + } + } +}; + +DragAndDropContainer::DragAndDropContainer() + : dragImageComponent (0) +{ +} + +DragAndDropContainer::~DragAndDropContainer() +{ + if (dragImageComponent != 0) + delete dragImageComponent; +} + +void DragAndDropContainer::startDragging (const String& sourceDescription, + Component* sourceComponent, + Image* im, + const bool allowDraggingToExternalWindows) +{ + if (dragImageComponent != 0) + { + if (im != 0) + delete im; + } + else + { + Component* const thisComp = dynamic_cast (this); + + if (thisComp != 0) + { + int mx, my; + Desktop::getLastMouseDownPosition (mx, my); + + if (im == 0) + { + im = sourceComponent->createComponentSnapshot (Rectangle (0, 0, sourceComponent->getWidth(), sourceComponent->getHeight())); + + if (im->getFormat() != Image::ARGB) + { + Image* newIm = new Image (Image::ARGB, im->getWidth(), im->getHeight(), true); + Graphics g2 (*newIm); + g2.drawImageAt (im, 0, 0); + + delete im; + im = newIm; + } + + im->multiplyAllAlphas (0.6f); + + const int lo = 150; + const int hi = 400; + + int rx = mx, ry = my; + sourceComponent->globalPositionToRelative (rx, ry); + const int cx = jlimit (0, im->getWidth(), rx); + const int cy = jlimit (0, im->getHeight(), ry); + + for (int y = im->getHeight(); --y >= 0;) + { + const double dy = (y - cy) * (y - cy); + + for (int x = im->getWidth(); --x >= 0;) + { + const int dx = x - cx; + const int distance = roundDoubleToInt (sqrt (dx * dx + dy)); + + if (distance > lo) + { + const float alpha = (distance > hi) ? 0 + : (hi - distance) / (float) (hi - lo) + + Random::getSystemRandom().nextFloat() * 0.008f; + + im->multiplyAlphaAt (x, y, alpha); + } + } + } + } + + DragImageComponent* const dic + = new DragImageComponent (im, + sourceDescription, + sourceComponent, + this); + + dragImageComponent = dic; + currentDragDesc = sourceDescription; + + if (allowDraggingToExternalWindows) + { + if (! Desktop::canUseSemiTransparentWindows()) + dic->setOpaque (true); + + dic->addToDesktop (ComponentPeer::windowIgnoresMouseClicks + | ComponentPeer::windowIsTemporary); + } + else + thisComp->addChildComponent (dic); + + dic->updateLocation (false, mx, my); + dic->setVisible (true); + } + else + { + // this class must only be implemented by an object that + // is also a Component. + jassertfalse + + if (im != 0) + delete im; + } + } +} + +bool DragAndDropContainer::isDragAndDropActive() const +{ + return dragImageComponent != 0; +} + +const String DragAndDropContainer::getCurrentDragDescription() const +{ + return (dragImageComponent != 0) ? currentDragDesc + : String::empty; +} + +DragAndDropContainer* DragAndDropContainer::findParentDragContainerFor (Component* c) +{ + if (c == 0) + return 0; + + // (unable to use the syntax findParentComponentOfClass () because of a VC6 compiler bug) + return c->findParentComponentOfClass ((DragAndDropContainer*) 0); +} + +bool DragAndDropContainer::shouldDropFilesWhenDraggedExternally (const String&, Component*, StringArray&, bool&) +{ + return false; +} + +void DragAndDropTarget::itemDragEnter (const String&, Component*, int, int) +{ +} + +void DragAndDropTarget::itemDragMove (const String&, Component*, int, int) +{ +} + +void DragAndDropTarget::itemDragExit (const String&, Component*) +{ +} + +bool DragAndDropTarget::shouldDrawDragImageWhenOver() +{ + return true; +} + +void FileDragAndDropTarget::fileDragEnter (const StringArray&, int, int) +{ +} + +void FileDragAndDropTarget::fileDragMove (const StringArray&, int, int) +{ +} + +void FileDragAndDropTarget::fileDragExit (const StringArray&) +{ +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_DragAndDropContainer.cpp *********/ + +/********* Start of inlined file: juce_MouseCursor.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +void* juce_createMouseCursorFromImage (const Image& image, int hotspotX, int hotspotY) throw(); +void* juce_createStandardMouseCursor (MouseCursor::StandardCursorType type) throw(); +// isStandard set depending on which interface was used to create the cursor +void juce_deleteMouseCursor (void* const cursorHandle, const bool isStandard) throw(); + +static CriticalSection mouseCursorLock; +static VoidArray standardCursors (2); + +class RefCountedMouseCursor +{ +public: + RefCountedMouseCursor (const MouseCursor::StandardCursorType t) throw() + : refCount (1), + standardType (t), + isStandard (true) + { + handle = juce_createStandardMouseCursor (standardType); + standardCursors.add (this); + } + + RefCountedMouseCursor (Image& image, + const int hotSpotX, + const int hotSpotY) throw() + : refCount (1), + standardType (MouseCursor::NormalCursor), + isStandard (false) + { + handle = juce_createMouseCursorFromImage (image, hotSpotX, hotSpotY); + } + + ~RefCountedMouseCursor() throw() + { + juce_deleteMouseCursor (handle, isStandard); + standardCursors.removeValue (this); + } + + void decRef() throw() + { + if (--refCount == 0) + delete this; + } + + void incRef() throw() + { + ++refCount; + } + + void* getHandle() const throw() + { + return handle; + } + + static RefCountedMouseCursor* findInstance (MouseCursor::StandardCursorType type) throw() + { + const ScopedLock sl (mouseCursorLock); + + for (int i = 0; i < standardCursors.size(); i++) + { + RefCountedMouseCursor* const r = (RefCountedMouseCursor*) standardCursors.getUnchecked(i); + + if (r->standardType == type) + { + r->incRef(); + return r; + } + } + + return new RefCountedMouseCursor (type); + } + + juce_UseDebuggingNewOperator + +private: + void* handle; + int refCount; + const MouseCursor::StandardCursorType standardType; + const bool isStandard; + + const RefCountedMouseCursor& operator= (const RefCountedMouseCursor&); +}; + +MouseCursor::MouseCursor() throw() +{ + cursorHandle = RefCountedMouseCursor::findInstance (NormalCursor); +} + +MouseCursor::MouseCursor (const StandardCursorType type) throw() +{ + cursorHandle = RefCountedMouseCursor::findInstance (type); +} + +MouseCursor::MouseCursor (Image& image, + const int hotSpotX, + const int hotSpotY) throw() +{ + cursorHandle = new RefCountedMouseCursor (image, hotSpotX, hotSpotY); +} + +MouseCursor::MouseCursor (const MouseCursor& other) throw() + : cursorHandle (other.cursorHandle) +{ + const ScopedLock sl (mouseCursorLock); + cursorHandle->incRef(); +} + +MouseCursor::~MouseCursor() throw() +{ + const ScopedLock sl (mouseCursorLock); + cursorHandle->decRef(); +} + +const MouseCursor& MouseCursor::operator= (const MouseCursor& other) throw() +{ + if (this != &other) + { + const ScopedLock sl (mouseCursorLock); + + cursorHandle->decRef(); + cursorHandle = other.cursorHandle; + cursorHandle->incRef(); + } + + return *this; +} + +bool MouseCursor::operator== (const MouseCursor& other) const throw() +{ + return cursorHandle == other.cursorHandle; +} + +bool MouseCursor::operator!= (const MouseCursor& other) const throw() +{ + return cursorHandle != other.cursorHandle; +} + +void* MouseCursor::getHandle() const throw() +{ + return cursorHandle->getHandle(); +} + +void MouseCursor::showWaitCursor() throw() +{ + const MouseCursor mc (MouseCursor::WaitCursor); + mc.showInAllWindows(); +} + +void MouseCursor::hideWaitCursor() throw() +{ + if (Component::getComponentUnderMouse()->isValidComponent()) + { + Component::getComponentUnderMouse()->getMouseCursor().showInAllWindows(); + } + else + { + const MouseCursor mc (MouseCursor::NormalCursor); + mc.showInAllWindows(); + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_MouseCursor.cpp *********/ + +/********* Start of inlined file: juce_MouseEvent.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +MouseEvent::MouseEvent (const int x_, + const int y_, + const ModifierKeys& mods_, + Component* const originator, + const Time& eventTime_, + const int mouseDownX_, + const int mouseDownY_, + const Time& mouseDownTime_, + const int numberOfClicks_, + const bool mouseWasDragged) throw() + : x (x_), + y (y_), + mods (mods_), + eventComponent (originator), + originalComponent (originator), + eventTime (eventTime_), + mouseDownX (mouseDownX_), + mouseDownY (mouseDownY_), + mouseDownTime (mouseDownTime_), + numberOfClicks (numberOfClicks_), + wasMovedSinceMouseDown (mouseWasDragged) +{ +} + +MouseEvent::~MouseEvent() throw() +{ +} + +bool MouseEvent::mouseWasClicked() const throw() +{ + return ! wasMovedSinceMouseDown; +} + +int MouseEvent::getMouseDownX() const throw() +{ + return mouseDownX; +} + +int MouseEvent::getMouseDownY() const throw() +{ + return mouseDownY; +} + +int MouseEvent::getDistanceFromDragStartX() const throw() +{ + return x - mouseDownX; +} + +int MouseEvent::getDistanceFromDragStartY() const throw() +{ + return y - mouseDownY; +} + +int MouseEvent::getDistanceFromDragStart() const throw() +{ + return roundDoubleToInt (juce_hypot (getDistanceFromDragStartX(), + getDistanceFromDragStartY())); +} + +int MouseEvent::getLengthOfMousePress() const throw() +{ + if (mouseDownTime.toMilliseconds() > 0) + return jmax (0, (int) (eventTime - mouseDownTime).inMilliseconds()); + + return 0; +} + +int MouseEvent::getScreenX() const throw() +{ + int sx = x, sy = y; + eventComponent->relativePositionToGlobal (sx, sy); + return sx; +} + +int MouseEvent::getScreenY() const throw() +{ + int sx = x, sy = y; + eventComponent->relativePositionToGlobal (sx, sy); + return sy; +} + +int MouseEvent::getMouseDownScreenX() const throw() +{ + int sx = mouseDownX, sy = mouseDownY; + eventComponent->relativePositionToGlobal (sx, sy); + return sx; +} + +int MouseEvent::getMouseDownScreenY() const throw() +{ + int sx = mouseDownX, sy = mouseDownY; + eventComponent->relativePositionToGlobal (sx, sy); + return sy; +} + +const MouseEvent MouseEvent::getEventRelativeTo (Component* const otherComponent) const throw() +{ + if (otherComponent == 0) + { + jassertfalse + return *this; + } + + MouseEvent me (*this); + + eventComponent->relativePositionToOtherComponent (otherComponent, me.x, me.y); + eventComponent->relativePositionToOtherComponent (otherComponent, me.mouseDownX, me.mouseDownY); + me.eventComponent = otherComponent; + + return me; +} + +static int doubleClickTimeOutMs = 400; + +void MouseEvent::setDoubleClickTimeout (const int newTime) throw() +{ + doubleClickTimeOutMs = newTime; +} + +int MouseEvent::getDoubleClickTimeout() throw() +{ + return doubleClickTimeOutMs; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_MouseEvent.cpp *********/ + +/********* Start of inlined file: juce_MouseHoverDetector.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +MouseHoverDetector::MouseHoverDetector (const int hoverTimeMillisecs_) + : source (0), + hoverTimeMillisecs (hoverTimeMillisecs_), + hasJustHovered (false) +{ + internalTimer.owner = this; +} + +MouseHoverDetector::~MouseHoverDetector() +{ + setHoverComponent (0); +} + +void MouseHoverDetector::setHoverTimeMillisecs (const int newTimeInMillisecs) +{ + hoverTimeMillisecs = newTimeInMillisecs; +} + +void MouseHoverDetector::setHoverComponent (Component* const newSourceComponent) +{ + if (source != newSourceComponent) + { + internalTimer.stopTimer(); + hasJustHovered = false; + + if (source != 0) + { + // ! you need to delete the hover detector before deleting its component + jassert (source->isValidComponent()); + + source->removeMouseListener (&internalTimer); + } + + source = newSourceComponent; + + if (newSourceComponent != 0) + newSourceComponent->addMouseListener (&internalTimer, false); + } +} + +void MouseHoverDetector::hoverTimerCallback() +{ + internalTimer.stopTimer(); + + if (source != 0) + { + int mx, my; + source->getMouseXYRelative (mx, my); + + if (source->reallyContains (mx, my, false)) + { + hasJustHovered = true; + mouseHovered (mx, my); + } + } +} + +void MouseHoverDetector::checkJustHoveredCallback() +{ + if (hasJustHovered) + { + hasJustHovered = false; + mouseMovedAfterHover(); + } +} + +void MouseHoverDetector::HoverDetectorInternal::timerCallback() +{ + owner->hoverTimerCallback(); +} + +void MouseHoverDetector::HoverDetectorInternal::mouseEnter (const MouseEvent&) +{ + stopTimer(); + owner->checkJustHoveredCallback(); +} + +void MouseHoverDetector::HoverDetectorInternal::mouseExit (const MouseEvent&) +{ + stopTimer(); + owner->checkJustHoveredCallback(); +} + +void MouseHoverDetector::HoverDetectorInternal::mouseDown (const MouseEvent&) +{ + stopTimer(); + owner->checkJustHoveredCallback(); +} + +void MouseHoverDetector::HoverDetectorInternal::mouseUp (const MouseEvent&) +{ + stopTimer(); + owner->checkJustHoveredCallback(); +} + +void MouseHoverDetector::HoverDetectorInternal::mouseMove (const MouseEvent& e) +{ + if (lastX != e.x || lastY != e.y) // to avoid fake mouse-moves setting it off + { + lastX = e.x; + lastY = e.y; + + if (owner->source != 0) + startTimer (owner->hoverTimeMillisecs); + + owner->checkJustHoveredCallback(); + } +} + +void MouseHoverDetector::HoverDetectorInternal::mouseWheelMove (const MouseEvent&, float, float) +{ + stopTimer(); + owner->checkJustHoveredCallback(); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_MouseHoverDetector.cpp *********/ + +/********* Start of inlined file: juce_MouseListener.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +void MouseListener::mouseEnter (const MouseEvent&) +{ +} + +void MouseListener::mouseExit (const MouseEvent&) +{ +} + +void MouseListener::mouseDown (const MouseEvent&) +{ +} + +void MouseListener::mouseUp (const MouseEvent&) +{ +} + +void MouseListener::mouseDrag (const MouseEvent&) +{ +} + +void MouseListener::mouseMove (const MouseEvent&) +{ +} + +void MouseListener::mouseDoubleClick (const MouseEvent&) +{ +} + +void MouseListener::mouseWheelMove (const MouseEvent&, float, float) +{ +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_MouseListener.cpp *********/ + +/********* Start of inlined file: juce_BooleanPropertyComponent.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +BooleanPropertyComponent::BooleanPropertyComponent (const String& name, + const String& buttonTextWhenTrue, + const String& buttonTextWhenFalse) + : PropertyComponent (name), + onText (buttonTextWhenTrue), + offText (buttonTextWhenFalse) +{ + addAndMakeVisible (button = new ToggleButton (String::empty)); + button->setClickingTogglesState (false); + button->addButtonListener (this); +} + +BooleanPropertyComponent::~BooleanPropertyComponent() +{ + deleteAllChildren(); +} + +void BooleanPropertyComponent::paint (Graphics& g) +{ + PropertyComponent::paint (g); + + const Rectangle r (button->getBounds()); + g.setColour (Colours::white); + g.fillRect (r); + + g.setColour (findColour (ComboBox::outlineColourId)); + g.drawRect (r.getX(), r.getY(), r.getWidth(), r.getHeight()); +} + +void BooleanPropertyComponent::refresh() +{ + button->setToggleState (getState(), false); + button->setButtonText (button->getToggleState() ? onText : offText); +} + +void BooleanPropertyComponent::buttonClicked (Button*) +{ + setState (! getState()); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_BooleanPropertyComponent.cpp *********/ + +/********* Start of inlined file: juce_ButtonPropertyComponent.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +ButtonPropertyComponent::ButtonPropertyComponent (const String& name, + const bool triggerOnMouseDown) + : PropertyComponent (name) +{ + addAndMakeVisible (button = new TextButton (String::empty)); + button->setTriggeredOnMouseDown (triggerOnMouseDown); + button->addButtonListener (this); +} + +ButtonPropertyComponent::~ButtonPropertyComponent() +{ + deleteAllChildren(); +} + +void ButtonPropertyComponent::refresh() +{ + button->setButtonText (getButtonText()); +} + +void ButtonPropertyComponent::buttonClicked (Button*) +{ + buttonClicked(); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ButtonPropertyComponent.cpp *********/ + +/********* Start of inlined file: juce_ChoicePropertyComponent.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +ChoicePropertyComponent::ChoicePropertyComponent (const String& name) + : PropertyComponent (name), + comboBox (0) +{ +} + +ChoicePropertyComponent::~ChoicePropertyComponent() +{ + deleteAllChildren(); +} + +const StringArray& ChoicePropertyComponent::getChoices() const throw() +{ + return choices; +} + +void ChoicePropertyComponent::refresh() +{ + if (comboBox == 0) + { + addAndMakeVisible (comboBox = new ComboBox (String::empty)); + + for (int i = 0; i < choices.size(); ++i) + { + if (choices[i].isNotEmpty()) + comboBox->addItem (choices[i], i + 1); + else + comboBox->addSeparator(); + } + + comboBox->setEditableText (false); + comboBox->addListener (this); + } + + comboBox->setSelectedId (getIndex() + 1, true); +} + +void ChoicePropertyComponent::comboBoxChanged (ComboBox*) +{ + const int newIndex = comboBox->getSelectedId() - 1; + + if (newIndex != getIndex()) + setIndex (newIndex); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ChoicePropertyComponent.cpp *********/ + +/********* Start of inlined file: juce_PropertyComponent.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +PropertyComponent::PropertyComponent (const String& name, + const int preferredHeight_) + : Component (name), + preferredHeight (preferredHeight_) +{ + jassert (name.isNotEmpty()); +} + +PropertyComponent::~PropertyComponent() +{ +} + +void PropertyComponent::paint (Graphics& g) +{ + getLookAndFeel().drawPropertyComponentBackground (g, getWidth(), getHeight(), *this); + getLookAndFeel().drawPropertyComponentLabel (g, getWidth(), getHeight(), *this); +} + +void PropertyComponent::resized() +{ + if (getNumChildComponents() > 0) + getChildComponent (0)->setBounds (getLookAndFeel().getPropertyComponentContentPosition (*this)); +} + +void PropertyComponent::enablementChanged() +{ + repaint(); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_PropertyComponent.cpp *********/ + +/********* Start of inlined file: juce_PropertyPanel.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +class PropertyHolderComponent : public Component +{ +public: + PropertyHolderComponent() + { + } + + ~PropertyHolderComponent() + { + deleteAllChildren(); + } + + void paint (Graphics&) + { + } + + void updateLayout (const int width); + + void refreshAll() const; +}; + +class PropertySectionComponent : public Component +{ +public: + PropertySectionComponent (const String& sectionTitle, + const Array & newProperties, + const bool open) + : Component (sectionTitle), + titleHeight (sectionTitle.isNotEmpty() ? 22 : 0), + isOpen_ (open) + { + for (int i = newProperties.size(); --i >= 0;) + { + addAndMakeVisible (newProperties.getUnchecked(i)); + newProperties.getUnchecked(i)->refresh(); + } + } + + ~PropertySectionComponent() + { + deleteAllChildren(); + } + + void paint (Graphics& g) + { + if (titleHeight > 0) + getLookAndFeel().drawPropertyPanelSectionHeader (g, getName(), isOpen(), getWidth(), titleHeight); + } + + void resized() + { + int y = titleHeight; + + for (int i = getNumChildComponents(); --i >= 0;) + { + PropertyComponent* const pec = dynamic_cast (getChildComponent (i)); + + if (pec != 0) + { + const int prefH = pec->getPreferredHeight(); + pec->setBounds (1, y, getWidth() - 2, prefH); + y += prefH; + } + } + } + + int getPreferredHeight() const + { + int y = titleHeight; + + if (isOpen()) + { + for (int i = 0; i < getNumChildComponents(); ++i) + { + PropertyComponent* pec = dynamic_cast (getChildComponent (i)); + + if (pec != 0) + y += pec->getPreferredHeight(); + } + } + + return y; + } + + void setOpen (const bool open) + { + if (isOpen_ != open) + { + isOpen_ = open; + + for (int i = 0; i < getNumChildComponents(); ++i) + { + PropertyComponent* pec = dynamic_cast (getChildComponent (i)); + + if (pec != 0) + pec->setVisible (open); + } + + // (unable to use the syntax findParentComponentOfClass () because of a VC6 compiler bug) + PropertyPanel* const pp = findParentComponentOfClass ((PropertyPanel*) 0); + + if (pp != 0) + pp->resized(); + } + } + + bool isOpen() const throw() + { + return isOpen_; + } + + void refreshAll() const + { + for (int i = 0; i < getNumChildComponents(); ++i) + { + PropertyComponent* pec = dynamic_cast (getChildComponent (i)); + + if (pec != 0) + pec->refresh(); + } + } + + void mouseDown (const MouseEvent&) + { + } + + void mouseUp (const MouseEvent& e) + { + if (e.getMouseDownX() < titleHeight + && e.x < titleHeight + && e.y < titleHeight + && e.getNumberOfClicks() != 2) + { + setOpen (! isOpen()); + } + } + + void mouseDoubleClick (const MouseEvent& e) + { + if (e.y < titleHeight) + setOpen (! isOpen()); + } + +private: + int titleHeight; + bool isOpen_; +}; + +void PropertyHolderComponent::updateLayout (const int width) +{ + int y = 0; + + for (int i = getNumChildComponents(); --i >= 0;) + { + PropertySectionComponent* const section + = dynamic_cast (getChildComponent (i)); + + if (section != 0) + { + const int prefH = section->getPreferredHeight(); + section->setBounds (0, y, width, prefH); + y += prefH; + } + } + + setSize (width, y); + repaint(); +} + +void PropertyHolderComponent::refreshAll() const +{ + for (int i = getNumChildComponents(); --i >= 0;) + { + PropertySectionComponent* const section + = dynamic_cast (getChildComponent (i)); + + if (section != 0) + section->refreshAll(); + } +} + +PropertyPanel::PropertyPanel() +{ + messageWhenEmpty = TRANS("(nothing selected)"); + + addAndMakeVisible (viewport = new Viewport()); + viewport->setViewedComponent (propertyHolderComponent = new PropertyHolderComponent()); + viewport->setFocusContainer (true); +} + +PropertyPanel::~PropertyPanel() +{ + clear(); + deleteAllChildren(); +} + +void PropertyPanel::paint (Graphics& g) +{ + if (propertyHolderComponent->getNumChildComponents() == 0) + { + g.setColour (Colours::black.withAlpha (0.5f)); + g.setFont (14.0f); + g.drawText (messageWhenEmpty, 0, 0, getWidth(), 30, + Justification::centred, true); + } +} + +void PropertyPanel::resized() +{ + viewport->setBounds (0, 0, getWidth(), getHeight()); + updatePropHolderLayout(); +} + +void PropertyPanel::clear() +{ + if (propertyHolderComponent->getNumChildComponents() > 0) + { + propertyHolderComponent->deleteAllChildren(); + repaint(); + } +} + +void PropertyPanel::addProperties (const Array & newProperties) +{ + if (propertyHolderComponent->getNumChildComponents() == 0) + repaint(); + + propertyHolderComponent->addAndMakeVisible (new PropertySectionComponent (String::empty, + newProperties, + true), 0); + updatePropHolderLayout(); +} + +void PropertyPanel::addSection (const String& sectionTitle, + const Array & newProperties, + const bool shouldBeOpen) +{ + jassert (sectionTitle.isNotEmpty()); + + if (propertyHolderComponent->getNumChildComponents() == 0) + repaint(); + + propertyHolderComponent->addAndMakeVisible (new PropertySectionComponent (sectionTitle, + newProperties, + shouldBeOpen), 0); + + updatePropHolderLayout(); +} + +void PropertyPanel::updatePropHolderLayout() const +{ + const int maxWidth = viewport->getMaximumVisibleWidth(); + ((PropertyHolderComponent*) propertyHolderComponent)->updateLayout (maxWidth); + + const int newMaxWidth = viewport->getMaximumVisibleWidth(); + if (maxWidth != newMaxWidth) + { + // need to do this twice because of scrollbars changing the size, etc. + ((PropertyHolderComponent*) propertyHolderComponent)->updateLayout (newMaxWidth); + } +} + +void PropertyPanel::refreshAll() const +{ + ((PropertyHolderComponent*) propertyHolderComponent)->refreshAll(); +} + +const StringArray PropertyPanel::getSectionNames() const +{ + StringArray s; + + for (int i = 0; i < propertyHolderComponent->getNumChildComponents(); ++i) + { + PropertySectionComponent* const section = dynamic_cast (propertyHolderComponent->getChildComponent (i)); + + if (section != 0 && section->getName().isNotEmpty()) + s.add (section->getName()); + } + + return s; +} + +bool PropertyPanel::isSectionOpen (const int sectionIndex) const +{ + int index = 0; + + for (int i = 0; i < propertyHolderComponent->getNumChildComponents(); ++i) + { + PropertySectionComponent* const section = dynamic_cast (propertyHolderComponent->getChildComponent (i)); + + if (section != 0 && section->getName().isNotEmpty()) + { + if (index == sectionIndex) + return section->isOpen(); + + ++index; + } + } + + return false; +} + +void PropertyPanel::setSectionOpen (const int sectionIndex, const bool shouldBeOpen) +{ + int index = 0; + + for (int i = 0; i < propertyHolderComponent->getNumChildComponents(); ++i) + { + PropertySectionComponent* const section = dynamic_cast (propertyHolderComponent->getChildComponent (i)); + + if (section != 0 && section->getName().isNotEmpty()) + { + if (index == sectionIndex) + { + section->setOpen (shouldBeOpen); + break; + } + + ++index; + } + } +} + +XmlElement* PropertyPanel::getOpennessState() const +{ + XmlElement* const xml = new XmlElement (T("PROPERTYPANELSTATE")); + + const StringArray sections (getSectionNames()); + + for (int i = 0; i < sections.size(); ++i) + { + if (sections[i].isNotEmpty()) + { + XmlElement* const e = new XmlElement (T("SECTION")); + e->setAttribute (T("name"), sections[i]); + e->setAttribute (T("open"), isSectionOpen (i) ? 1 : 0); + xml->addChildElement (e); + } + } + + return xml; +} + +void PropertyPanel::restoreOpennessState (const XmlElement& xml) +{ + if (xml.hasTagName (T("PROPERTYPANELSTATE"))) + { + const StringArray sections (getSectionNames()); + + forEachXmlChildElementWithTagName (xml, e, T("SECTION")) + { + setSectionOpen (sections.indexOf (e->getStringAttribute (T("name"))), + e->getBoolAttribute (T("open"))); + } + } +} + +void PropertyPanel::setMessageWhenEmpty (const String& newMessage) +{ + if (messageWhenEmpty != newMessage) + { + messageWhenEmpty = newMessage; + repaint(); + } +} + +const String& PropertyPanel::getMessageWhenEmpty() const throw() +{ + return messageWhenEmpty; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_PropertyPanel.cpp *********/ + +/********* Start of inlined file: juce_SliderPropertyComponent.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +SliderPropertyComponent::SliderPropertyComponent (const String& name, + const double rangeMin, + const double rangeMax, + const double interval, + const double skewFactor) + : PropertyComponent (name) +{ + addAndMakeVisible (slider = new Slider (name)); + + slider->setRange (rangeMin, rangeMax, interval); + slider->setSkewFactor (skewFactor); + slider->setSliderStyle (Slider::LinearBar); + + slider->addListener (this); +} + +SliderPropertyComponent::~SliderPropertyComponent() +{ + deleteAllChildren(); +} + +void SliderPropertyComponent::refresh() +{ + slider->setValue (getValue(), false); +} + +void SliderPropertyComponent::sliderValueChanged (Slider*) +{ + if (getValue() != slider->getValue()) + setValue (slider->getValue()); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_SliderPropertyComponent.cpp *********/ + +/********* Start of inlined file: juce_TextPropertyComponent.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +class TextPropLabel : public Label +{ + TextPropertyComponent& owner; + int maxChars; + bool isMultiline; + +public: + TextPropLabel (TextPropertyComponent& owner_, + const int maxChars_, const bool isMultiline_) + : Label (String::empty, String::empty), + owner (owner_), + maxChars (maxChars_), + isMultiline (isMultiline_) + { + setEditable (true, true, false); + + setColour (backgroundColourId, Colours::white); + setColour (outlineColourId, findColour (ComboBox::outlineColourId)); + } + + ~TextPropLabel() + { + } + + TextEditor* createEditorComponent() + { + TextEditor* const textEditor = Label::createEditorComponent(); + + textEditor->setInputRestrictions (maxChars); + + if (isMultiline) + { + textEditor->setMultiLine (true, true); + textEditor->setReturnKeyStartsNewLine (true); + } + + return textEditor; + } + + void textWasEdited() + { + owner.textWasEdited(); + } +}; + +TextPropertyComponent::TextPropertyComponent (const String& name, + const int maxNumChars, + const bool isMultiLine) + : PropertyComponent (name) +{ + addAndMakeVisible (textEditor = new TextPropLabel (*this, maxNumChars, isMultiLine)); + + if (isMultiLine) + { + textEditor->setJustificationType (Justification::topLeft); + preferredHeight = 120; + } +} + +TextPropertyComponent::~TextPropertyComponent() +{ + deleteAllChildren(); +} + +void TextPropertyComponent::refresh() +{ + textEditor->setText (getText(), false); +} + +void TextPropertyComponent::textWasEdited() +{ + const String newText (textEditor->getText()); + + if (getText() != newText) + setText (newText); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_TextPropertyComponent.cpp *********/ + +/********* Start of inlined file: juce_AudioDeviceSelectorComponent.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +class AudioDeviceSelectorComponentListBox : public ListBox, + public ListBoxModel +{ +public: + enum BoxType + { + midiInputType, + audioInputType, + audioOutputType + }; + + AudioDeviceSelectorComponentListBox (AudioDeviceManager& deviceManager_, + const BoxType type_, + const String& noItemsMessage_, + const int minNumber_, + const int maxNumber_) + : ListBox (String::empty, 0), + deviceManager (deviceManager_), + type (type_), + noItemsMessage (noItemsMessage_), + minNumber (minNumber_), + maxNumber (maxNumber_) + { + AudioIODevice* const currentDevice = deviceManager.getCurrentAudioDevice(); + + if (type_ == midiInputType) + { + items = MidiInput::getDevices(); + } + else if (type_ == audioInputType) + { + items = currentDevice->getInputChannelNames(); + } + else if (type_ == audioOutputType) + { + items = currentDevice->getOutputChannelNames(); + } + else + { + jassertfalse + } + + setModel (this); + setOutlineThickness (1); + } + + ~AudioDeviceSelectorComponentListBox() + { + } + + int getNumRows() + { + return items.size(); + } + + void paintListBoxItem (int row, + Graphics& g, + int width, int height, + bool rowIsSelected) + { + if (((unsigned int) row) < (unsigned int) items.size()) + { + if (rowIsSelected) + g.fillAll (findColour (TextEditor::highlightColourId) + .withMultipliedAlpha (0.3f)); + + const String item (items [row]); + bool enabled = false; + + if (type == midiInputType) + { + enabled = deviceManager.isMidiInputEnabled (item); + } + else if (type == audioInputType) + { + enabled = deviceManager.getInputChannels() [row]; + } + else if (type == audioOutputType) + { + enabled = deviceManager.getOutputChannels() [row]; + } + + const int x = getTickX(); + const int tickW = height - height / 4; + + getLookAndFeel().drawTickBox (g, *this, x - tickW, (height - tickW) / 2, tickW, tickW, + enabled, true, true, false); + + g.setFont (height * 0.6f); + g.setColour (findColour (ListBox::textColourId, true).withMultipliedAlpha (enabled ? 1.0f : 0.6f)); + g.drawText (item, x, 0, width - x - 2, height, Justification::centredLeft, true); + } + } + + void listBoxItemClicked (int row, const MouseEvent& e) + { + selectRow (row); + + if (e.x < getTickX()) + flipEnablement (row); + } + + void listBoxItemDoubleClicked (int row, const MouseEvent&) + { + flipEnablement (row); + } + + void returnKeyPressed (int row) + { + flipEnablement (row); + } + + void paint (Graphics& g) + { + ListBox::paint (g); + + if (items.size() == 0) + { + g.setColour (Colours::grey); + g.setFont (13.0f); + g.drawText (noItemsMessage, + 0, 0, getWidth(), getHeight() / 2, + Justification::centred, true); + } + } + + int getBestHeight (const int preferredHeight) + { + const int extra = getOutlineThickness() * 2; + + return jmax (getRowHeight() * 2 + extra, + jmin (getRowHeight() * getNumRows() + extra, + preferredHeight)); + } + + juce_UseDebuggingNewOperator + +private: + AudioDeviceManager& deviceManager; + const BoxType type; + const String noItemsMessage; + StringArray items; + int minNumber, maxNumber; + + void flipEnablement (const int row) + { + if (((unsigned int) row) < (unsigned int) items.size()) + { + AudioIODevice* const audioDevice = deviceManager.getCurrentAudioDevice(); + + const String item (items [row]); + + if (type == midiInputType) + { + deviceManager.setMidiInputEnabled (item, ! deviceManager.isMidiInputEnabled (item)); + } + else + { + jassert (type == audioInputType || type == audioOutputType); + + if (audioDevice != 0) + { + BitArray chans (type == audioInputType ? deviceManager.getInputChannels() + : deviceManager.getOutputChannels()); + + const BitArray oldChans (chans); + + const bool newVal = ! chans[row]; + const int numActive = chans.countNumberOfSetBits(); + + if (! newVal) + { + if (numActive > minNumber) + chans.setBit (row, false); + } + else + { + if (numActive >= maxNumber) + { + const int firstActiveChan = chans.findNextSetBit(); + + chans.setBit (row > firstActiveChan + ? firstActiveChan : chans.getHighestBit(), + false); + } + + chans.setBit (row, true); + } + + if (type == audioInputType) + deviceManager.setInputChannels (chans, true); + else + deviceManager.setOutputChannels (chans, true); + } + } + } + } + + int getTickX() const throw() + { + return getRowHeight() + 5; + } + + AudioDeviceSelectorComponentListBox (const AudioDeviceSelectorComponentListBox&); + const AudioDeviceSelectorComponentListBox& operator= (const AudioDeviceSelectorComponentListBox&); +}; + +AudioDeviceSelectorComponent::AudioDeviceSelectorComponent (AudioDeviceManager& deviceManager_, + const int minInputChannels_, + const int maxInputChannels_, + const int minOutputChannels_, + const int maxOutputChannels_, + const bool showMidiInputOptions, + const bool showMidiOutputSelector) + : deviceManager (deviceManager_), + minOutputChannels (minOutputChannels_), + maxOutputChannels (maxOutputChannels_), + minInputChannels (minInputChannels_), + maxInputChannels (maxInputChannels_), + sampleRateDropDown (0), + inputChansBox (0), + inputsLabel (0), + outputChansBox (0), + outputsLabel (0), + sampleRateLabel (0), + bufferSizeDropDown (0), + bufferSizeLabel (0), + launchUIButton (0) +{ + jassert (minOutputChannels >= 0 && minOutputChannels <= maxOutputChannels); + jassert (minInputChannels >= 0 && minInputChannels <= maxInputChannels); + + audioDeviceDropDown = new ComboBox ("device"); + deviceManager_.addDeviceNamesToComboBox (*audioDeviceDropDown); + audioDeviceDropDown->setSelectedId (-1, true); + + if (deviceManager_.getCurrentAudioDeviceName().isNotEmpty()) + audioDeviceDropDown->setText (deviceManager_.getCurrentAudioDeviceName(), true); + + audioDeviceDropDown->addListener (this); + addAndMakeVisible (audioDeviceDropDown); + + Label* label = new Label ("l1", TRANS ("audio device:")); + label->attachToComponent (audioDeviceDropDown, true); + + if (showMidiInputOptions) + { + addAndMakeVisible (midiInputsList + = new AudioDeviceSelectorComponentListBox (deviceManager, + AudioDeviceSelectorComponentListBox::midiInputType, + TRANS("(no midi inputs available)"), + 0, 0)); + + midiInputsLabel = new Label ("lm", TRANS ("active midi inputs:")); + midiInputsLabel->setJustificationType (Justification::topRight); + midiInputsLabel->attachToComponent (midiInputsList, true); + } + else + { + midiInputsList = 0; + midiInputsLabel = 0; + } + + if (showMidiOutputSelector) + { + addAndMakeVisible (midiOutputSelector = new ComboBox (String::empty)); + midiOutputSelector->addListener (this); + + midiOutputLabel = new Label ("lm", TRANS("Midi Output:")); + midiOutputLabel->attachToComponent (midiOutputSelector, true); + } + else + { + midiOutputSelector = 0; + midiOutputLabel = 0; + } + + deviceManager_.addChangeListener (this); + changeListenerCallback (0); +} + +AudioDeviceSelectorComponent::~AudioDeviceSelectorComponent() +{ + deviceManager.removeChangeListener (this); + deleteAllChildren(); +} + +void AudioDeviceSelectorComponent::resized() +{ + const int lx = proportionOfWidth (0.35f); + const int w = proportionOfWidth (0.55f); + const int h = 24; + const int space = 6; + const int dh = h + space; + int y = 15; + + audioDeviceDropDown->setBounds (lx, y, w, h); + y += dh; + + if (sampleRateDropDown != 0) + { + sampleRateDropDown->setBounds (lx, y, w, h); + y += dh; + } + + if (bufferSizeDropDown != 0) + { + bufferSizeDropDown->setBounds (lx, y, w, h); + y += dh; + } + + if (launchUIButton != 0) + { + launchUIButton->setBounds (lx, y, 150, h); + ((TextButton*) launchUIButton)->changeWidthToFitText(); + y += dh; + } + + VoidArray boxes; + + if (outputChansBox != 0) + boxes.add (outputChansBox); + + if (inputChansBox != 0) + boxes.add (inputChansBox); + + if (midiInputsList != 0) + boxes.add (midiInputsList); + + const int boxSpace = getHeight() - y; + + for (int i = 0; i < boxes.size(); ++i) + { + AudioDeviceSelectorComponentListBox* const box = (AudioDeviceSelectorComponentListBox*) boxes.getUnchecked (i); + + const int bh = box->getBestHeight (jmin (h * 8, boxSpace / boxes.size()) - space); + box->setBounds (lx, y, w, bh); + y += bh + space; + } + + if (midiOutputSelector != 0) + midiOutputSelector->setBounds (lx, y, w, h); +} + +void AudioDeviceSelectorComponent::buttonClicked (Button*) +{ + AudioIODevice* const device = deviceManager.getCurrentAudioDevice(); + + if (device != 0 && device->hasControlPanel()) + { + const String lastDevice (device->getName()); + + if (device->showControlPanel()) + { + deviceManager.setAudioDevice (String::empty, 0, 0, 0, 0, false); + deviceManager.setAudioDevice (lastDevice, 0, 0, 0, 0, false); + } + + getTopLevelComponent()->toFront (true); + } +} + +void AudioDeviceSelectorComponent::comboBoxChanged (ComboBox* comboBoxThatHasChanged) +{ + AudioIODevice* const audioDevice = deviceManager.getCurrentAudioDevice(); + + if (comboBoxThatHasChanged == audioDeviceDropDown) + { + if (audioDeviceDropDown->getSelectedId() < 0) + { + deviceManager.setAudioDevice (String::empty, 0, 0, 0, 0, true); + } + else + { + String error (deviceManager.setAudioDevice (audioDeviceDropDown->getText(), + 0, 0, 0, 0, true)); + + if (error.isNotEmpty()) + { +#if JUCE_WIN32 + if (deviceManager.getInputChannels().countNumberOfSetBits() > 0 + && deviceManager.getOutputChannels().countNumberOfSetBits() > 0) + { + // in DSound, some machines lose their primary input device when a mic + // is removed, and this also buggers up our attempt at opening an output + // device, so this is a workaround that doesn't fail in that case. + BitArray noInputs; + error = deviceManager.setAudioDevice (audioDeviceDropDown->getText(), + 0, 0, &noInputs, 0, false); + } +#endif + if (error.isNotEmpty()) + AlertWindow::showMessageBox (AlertWindow::WarningIcon, + T("Error while opening \"") + + audioDeviceDropDown->getText() + + T("\""), + error); + } + } + + if (deviceManager.getCurrentAudioDeviceName().isNotEmpty()) + audioDeviceDropDown->setText (deviceManager.getCurrentAudioDeviceName(), true); + else + audioDeviceDropDown->setSelectedId (-1, true); + } + else if (comboBoxThatHasChanged == midiOutputSelector) + { + deviceManager.setDefaultMidiOutput (midiOutputSelector->getText()); + } + else if (audioDevice != 0) + { + if (bufferSizeDropDown != 0 && comboBoxThatHasChanged == bufferSizeDropDown) + { + if (bufferSizeDropDown->getSelectedId() > 0) + deviceManager.setAudioDevice (audioDevice->getName(), + bufferSizeDropDown->getSelectedId(), + audioDevice->getCurrentSampleRate(), + 0, 0, true); + } + else if (sampleRateDropDown != 0 && comboBoxThatHasChanged == sampleRateDropDown) + { + if (sampleRateDropDown->getSelectedId() > 0) + deviceManager.setAudioDevice (audioDevice->getName(), + audioDevice->getCurrentBufferSizeSamples(), + sampleRateDropDown->getSelectedId(), + 0, 0, true); + } + } +} + +void AudioDeviceSelectorComponent::changeListenerCallback (void*) +{ + deleteAndZero (sampleRateDropDown); + deleteAndZero (inputChansBox); + deleteAndZero (inputsLabel); + deleteAndZero (outputChansBox); + deleteAndZero (outputsLabel); + deleteAndZero (sampleRateLabel); + deleteAndZero (bufferSizeDropDown); + deleteAndZero (bufferSizeLabel); + deleteAndZero (launchUIButton); + + AudioIODevice* const currentDevice = deviceManager.getCurrentAudioDevice(); + + if (currentDevice != 0) + { + audioDeviceDropDown->setText (currentDevice->getName(), true); + + // sample rate + addAndMakeVisible (sampleRateDropDown = new ComboBox ("samplerate")); + sampleRateLabel = new Label ("l2", TRANS ("sample rate:")); + sampleRateLabel->attachToComponent (sampleRateDropDown, true); + + const int numRates = currentDevice->getNumSampleRates(); + + int i; + for (i = 0; i < numRates; ++i) + { + const int rate = roundDoubleToInt (currentDevice->getSampleRate (i)); + sampleRateDropDown->addItem (String (rate) + T(" Hz"), rate); + } + + const double currentRate = currentDevice->getCurrentSampleRate(); + sampleRateDropDown->setSelectedId (roundDoubleToInt (currentRate), true); + sampleRateDropDown->addListener (this); + + // buffer size + addAndMakeVisible (bufferSizeDropDown = new ComboBox ("buffersize")); + bufferSizeLabel = new Label ("l2", TRANS ("audio buffer size:")); + bufferSizeLabel->attachToComponent (bufferSizeDropDown, true); + + const int numBufferSizes = currentDevice->getNumBufferSizesAvailable(); + + for (i = 0; i < numBufferSizes; ++i) + { + const int bs = currentDevice->getBufferSizeSamples (i); + bufferSizeDropDown->addItem (String (bs) + + T(" samples (") + + String (bs * 1000.0 / currentRate, 1) + + T(" ms)"), + bs); + } + + bufferSizeDropDown->setSelectedId (currentDevice->getCurrentBufferSizeSamples(), true); + bufferSizeDropDown->addListener (this); + + if (currentDevice->hasControlPanel()) + { + addAndMakeVisible (launchUIButton = new TextButton (TRANS ("show this device's control panel"), + TRANS ("opens the device's own control panel"))); + + launchUIButton->addButtonListener (this); + } + + // output chans + if (maxOutputChannels > 0 && minOutputChannels < currentDevice->getOutputChannelNames().size()) + { + addAndMakeVisible (outputChansBox + = new AudioDeviceSelectorComponentListBox (deviceManager, + AudioDeviceSelectorComponentListBox::audioOutputType, + TRANS ("(no audio output channels found)"), + minOutputChannels, maxOutputChannels)); + + outputsLabel = new Label ("l3", TRANS ("active output channels:")); + outputsLabel->attachToComponent (outputChansBox, true); + } + + // input chans + if (maxInputChannels > 0 && minInputChannels < currentDevice->getInputChannelNames().size()) + { + addAndMakeVisible (inputChansBox + = new AudioDeviceSelectorComponentListBox (deviceManager, + AudioDeviceSelectorComponentListBox::audioInputType, + TRANS ("(no audio input channels found)"), + minInputChannels, maxInputChannels)); + + inputsLabel = new Label ("l4", TRANS ("active input channels:")); + inputsLabel->attachToComponent (inputChansBox, true); + } + } + else + { + audioDeviceDropDown->setSelectedId (-1, true); + } + + if (midiInputsList != 0) + { + midiInputsList->updateContent(); + midiInputsList->repaint(); + } + + if (midiOutputSelector != 0) + { + midiOutputSelector->clear(); + + const StringArray midiOuts (MidiOutput::getDevices()); + + midiOutputSelector->addItem (TRANS("<< no audio device >>"), -1); + midiOutputSelector->addSeparator(); + + for (int i = 0; i < midiOuts.size(); ++i) + midiOutputSelector->addItem (midiOuts[i], i + 1); + + int current = -1; + + if (deviceManager.getDefaultMidiOutput() != 0) + current = 1 + midiOuts.indexOf (deviceManager.getDefaultMidiOutputName()); + + midiOutputSelector->setSelectedId (current, true); + } + + resized(); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_AudioDeviceSelectorComponent.cpp *********/ + +/********* Start of inlined file: juce_BubbleComponent.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +BubbleComponent::BubbleComponent() + : side (0), + allowablePlacements (above | below | left | right), + arrowTipX (0.0f), + arrowTipY (0.0f) +{ + setInterceptsMouseClicks (false, false); + + shadow.setShadowProperties (5.0f, 0.35f, 0, 0); + setComponentEffect (&shadow); +} + +BubbleComponent::~BubbleComponent() +{ +} + +void BubbleComponent::paint (Graphics& g) +{ + int x = content.getX(); + int y = content.getY(); + int w = content.getWidth(); + int h = content.getHeight(); + + int cw, ch; + getContentSize (cw, ch); + + if (side == 3) + x += w - cw; + else if (side != 1) + x += (w - cw) / 2; + + w = cw; + + if (side == 2) + y += h - ch; + else if (side != 0) + y += (h - ch) / 2; + + h = ch; + + getLookAndFeel().drawBubble (g, arrowTipX, arrowTipY, + (float) x, (float) y, + (float) w, (float) h); + + const int cx = x + (w - cw) / 2; + const int cy = y + (h - ch) / 2; + + const int indent = 3; + + g.setOrigin (cx + indent, cy + indent); + g.reduceClipRegion (0, 0, cw - indent * 2, ch - indent * 2); + + paintContent (g, cw - indent * 2, ch - indent * 2); +} + +void BubbleComponent::setAllowedPlacement (const int newPlacement) +{ + allowablePlacements = newPlacement; +} + +void BubbleComponent::setPosition (Component* componentToPointTo) +{ + jassert (componentToPointTo->isValidComponent()); + + int tx = 0; + int ty = 0; + + if (getParentComponent() != 0) + componentToPointTo->relativePositionToOtherComponent (getParentComponent(), tx, ty); + else + componentToPointTo->relativePositionToGlobal (tx, ty); + + setPosition (Rectangle (tx, ty, componentToPointTo->getWidth(), componentToPointTo->getHeight())); +} + +void BubbleComponent::setPosition (const int arrowTipX, + const int arrowTipY) +{ + setPosition (Rectangle (arrowTipX, arrowTipY, 1, 1)); +} + +void BubbleComponent::setPosition (const Rectangle& rectangleToPointTo) +{ + Rectangle availableSpace; + + if (getParentComponent() != 0) + { + availableSpace.setSize (getParentComponent()->getWidth(), + getParentComponent()->getHeight()); + } + else + { + availableSpace = getParentMonitorArea(); + } + + int x = 0; + int y = 0; + int w = 150; + int h = 30; + + getContentSize (w, h); + w += 30; + h += 30; + + const float edgeIndent = 2.0f; + const int arrowLength = jmin (10, h / 3, w / 3); + + int spaceAbove = ((allowablePlacements & above) != 0) ? jmax (0, rectangleToPointTo.getY() - availableSpace.getY()) : -1; + int spaceBelow = ((allowablePlacements & below) != 0) ? jmax (0, availableSpace.getBottom() - rectangleToPointTo.getBottom()) : -1; + int spaceLeft = ((allowablePlacements & left) != 0) ? jmax (0, rectangleToPointTo.getX() - availableSpace.getX()) : -1; + int spaceRight = ((allowablePlacements & right) != 0) ? jmax (0, availableSpace.getRight() - rectangleToPointTo.getRight()) : -1; + + // look at whether the component is elongated, and if so, try to position next to its longer dimension. + if (rectangleToPointTo.getWidth() > rectangleToPointTo.getHeight() * 2 + && (spaceAbove > h + 20 || spaceBelow > h + 20)) + { + spaceLeft = spaceRight = 0; + } + else if (rectangleToPointTo.getWidth() < rectangleToPointTo.getHeight() / 2 + && (spaceLeft > w + 20 || spaceRight > w + 20)) + { + spaceAbove = spaceBelow = 0; + } + + if (jmax (spaceAbove, spaceBelow) >= jmax (spaceLeft, spaceRight)) + { + x = rectangleToPointTo.getX() + (rectangleToPointTo.getWidth() - w) / 2; + arrowTipX = w * 0.5f; + content.setSize (w, h - arrowLength); + + if (spaceAbove >= spaceBelow) + { + // above + y = rectangleToPointTo.getY() - h; + content.setPosition (0, 0); + arrowTipY = h - edgeIndent; + side = 2; + } + else + { + // below + y = rectangleToPointTo.getBottom(); + content.setPosition (0, arrowLength); + arrowTipY = edgeIndent; + side = 0; + } + } + else + { + y = rectangleToPointTo.getY() + (rectangleToPointTo.getHeight() - h) / 2; + arrowTipY = h * 0.5f; + content.setSize (w - arrowLength, h); + + if (spaceLeft > spaceRight) + { + // on the left + x = rectangleToPointTo.getX() - w; + content.setPosition (0, 0); + arrowTipX = w - edgeIndent; + side = 3; + } + else + { + // on the right + x = rectangleToPointTo.getRight(); + content.setPosition (arrowLength, 0); + arrowTipX = edgeIndent; + side = 1; + } + } + + setBounds (x, y, w, h); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_BubbleComponent.cpp *********/ + +/********* Start of inlined file: juce_BubbleMessageComponent.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +BubbleMessageComponent::BubbleMessageComponent (int fadeOutLengthMs) + : fadeOutLength (fadeOutLengthMs), + deleteAfterUse (false) +{ +} + +BubbleMessageComponent::~BubbleMessageComponent() +{ + fadeOutComponent (fadeOutLength); +} + +void BubbleMessageComponent::showAt (int x, int y, + const String& text, + const int numMillisecondsBeforeRemoving, + const bool removeWhenMouseClicked, + const bool deleteSelfAfterUse) +{ + textLayout.clear(); + textLayout.setText (text, Font (14.0f)); + textLayout.layout (256, Justification::centredLeft, true); + + setPosition (x, y); + + init (numMillisecondsBeforeRemoving, removeWhenMouseClicked, deleteSelfAfterUse); +} + +void BubbleMessageComponent::showAt (Component* const component, + const String& text, + const int numMillisecondsBeforeRemoving, + const bool removeWhenMouseClicked, + const bool deleteSelfAfterUse) +{ + textLayout.clear(); + textLayout.setText (text, Font (14.0f)); + textLayout.layout (256, Justification::centredLeft, true); + + setPosition (component); + + init (numMillisecondsBeforeRemoving, removeWhenMouseClicked, deleteSelfAfterUse); +} + +void BubbleMessageComponent::init (const int numMillisecondsBeforeRemoving, + const bool removeWhenMouseClicked, + const bool deleteSelfAfterUse) +{ + setVisible (true); + + deleteAfterUse = deleteSelfAfterUse; + + if (numMillisecondsBeforeRemoving > 0) + expiryTime = Time::getMillisecondCounter() + numMillisecondsBeforeRemoving; + else + expiryTime = 0; + + startTimer (77); + + mouseClickCounter = Desktop::getInstance().getMouseButtonClickCounter(); + + if (! (removeWhenMouseClicked && isShowing())) + mouseClickCounter += 0xfffff; + + repaint(); +} + +void BubbleMessageComponent::getContentSize (int& w, int& h) +{ + w = textLayout.getWidth() + 16; + h = textLayout.getHeight() + 16; +} + +void BubbleMessageComponent::paintContent (Graphics& g, int w, int h) +{ + g.setColour (findColour (TooltipWindow::textColourId)); + + textLayout.drawWithin (g, 0, 0, w, h, Justification::centred); +} + +void BubbleMessageComponent::timerCallback() +{ + if (Desktop::getInstance().getMouseButtonClickCounter() > mouseClickCounter) + { + stopTimer(); + setVisible (false); + + if (deleteAfterUse) + delete this; + } + else if (expiryTime != 0 && Time::getMillisecondCounter() > expiryTime) + { + stopTimer(); + fadeOutComponent (fadeOutLength); + + if (deleteAfterUse) + delete this; + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_BubbleMessageComponent.cpp *********/ + +/********* Start of inlined file: juce_ColourSelector.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +static const int swatchesPerRow = 8; +static const int swatchHeight = 22; + +class ColourComponentSlider : public Slider +{ +public: + ColourComponentSlider (const String& name) + : Slider (name) + { + setRange (0.0, 255.0, 1.0); + } + + ~ColourComponentSlider() + { + } + + const String getTextFromValue (double currentValue) + { + return String::formatted (T("%02X"), (int)currentValue); + } + + double getValueFromText (const String& text) + { + return (double) text.getHexValue32(); + } + +private: + ColourComponentSlider (const ColourComponentSlider&); + const ColourComponentSlider& operator= (const ColourComponentSlider&); +}; + +class ColourSpaceMarker : public Component +{ +public: + ColourSpaceMarker() + { + setInterceptsMouseClicks (false, false); + } + + ~ColourSpaceMarker() + { + } + + void paint (Graphics& g) + { + g.setColour (Colour::greyLevel (0.1f)); + g.drawEllipse (1.0f, 1.0f, getWidth() - 2.0f, getHeight() - 2.0f, 1.0f); + g.setColour (Colour::greyLevel (0.9f)); + g.drawEllipse (2.0f, 2.0f, getWidth() - 4.0f, getHeight() - 4.0f, 1.0f); + } + +private: + ColourSpaceMarker (const ColourSpaceMarker&); + const ColourSpaceMarker& operator= (const ColourSpaceMarker&); +}; + +class ColourSpaceView : public Component +{ + ColourSelector* const owner; + float& h; + float& s; + float& v; + float lastHue; + ColourSpaceMarker* marker; + const int edge; + +public: + ColourSpaceView (ColourSelector* owner_, + float& h_, float& s_, float& v_, + const int edgeSize) + : owner (owner_), + h (h_), s (s_), v (v_), + lastHue (0.0f), + edge (edgeSize) + { + addAndMakeVisible (marker = new ColourSpaceMarker()); + setMouseCursor (MouseCursor::CrosshairCursor); + } + + ~ColourSpaceView() + { + deleteAllChildren(); + } + + void paint (Graphics& g) + { + const float hue = h; + + const float xScale = 1.0f / (getWidth() - edge * 2); + const float yScale = 1.0f / (getHeight() - edge * 2); + + const Rectangle clip (g.getClipBounds()); + const int x1 = jmax (clip.getX(), edge) & ~1; + const int x2 = jmin (clip.getRight(), getWidth() - edge) | 1; + const int y1 = jmax (clip.getY(), edge) & ~1; + const int y2 = jmin (clip.getBottom(), getHeight() - edge) | 1; + + for (int y = y1; y < y2; y += 2) + { + const float v = jlimit (0.0f, 1.0f, 1.0f - (y - edge) * yScale); + + for (int x = x1; x < x2; x += 2) + { + const float s = jlimit (0.0f, 1.0f, (x - edge) * xScale); + g.setColour (Colour (hue, s, v, 1.0f)); + g.fillRect (x, y, 2, 2); + } + } + } + + void mouseDown (const MouseEvent& e) + { + mouseDrag (e); + } + + void mouseDrag (const MouseEvent& e) + { + const float s = (e.x - edge) / (float) (getWidth() - edge * 2); + const float v = 1.0f - (e.y - edge) / (float) (getHeight() - edge * 2); + + owner->setSV (s, v); + } + + void updateIfNeeded() + { + if (lastHue != h) + { + lastHue = h; + repaint(); + } + + resized(); + } + + void resized() + { + marker->setBounds (roundFloatToInt ((getWidth() - edge * 2) * s), + roundFloatToInt ((getHeight() - edge * 2) * (1.0f - v)), + edge * 2, edge * 2); + } + +private: + ColourSpaceView (const ColourSpaceView&); + const ColourSpaceView& operator= (const ColourSpaceView&); +}; + +class HueSelectorMarker : public Component +{ +public: + HueSelectorMarker() + { + setInterceptsMouseClicks (false, false); + } + + ~HueSelectorMarker() + { + } + + void paint (Graphics& g) + { + Path p; + p.addTriangle (1.0f, 1.0f, + getWidth() * 0.3f, getHeight() * 0.5f, + 1.0f, getHeight() - 1.0f); + + p.addTriangle (getWidth() - 1.0f, 1.0f, + getWidth() * 0.7f, getHeight() * 0.5f, + getWidth() - 1.0f, getHeight() - 1.0f); + + g.setColour (Colours::white.withAlpha (0.75f)); + g.fillPath (p); + + g.setColour (Colours::black.withAlpha (0.75f)); + g.strokePath (p, PathStrokeType (1.2f)); + } + +private: + HueSelectorMarker (const HueSelectorMarker&); + const HueSelectorMarker& operator= (const HueSelectorMarker&); +}; + +class HueSelectorComp : public Component +{ +public: + HueSelectorComp (ColourSelector* owner_, + float& h_, float& s_, float& v_, + const int edgeSize) + : owner (owner_), + h (h_), s (s_), v (v_), + lastHue (0.0f), + edge (edgeSize) + { + addAndMakeVisible (marker = new HueSelectorMarker()); + } + + ~HueSelectorComp() + { + deleteAllChildren(); + } + + void paint (Graphics& g) + { + const float yScale = 1.0f / (getHeight() - edge * 2); + + const Rectangle clip (g.getClipBounds()); + + for (int y = jmin (clip.getBottom(), getHeight() - edge); --y >= jmax (edge, clip.getY());) + { + g.setColour (Colour ((y - edge) * yScale, 1.0f, 1.0f, 1.0f)); + g.fillRect (edge, y, getWidth() - edge * 2, 1); + } + } + + void resized() + { + marker->setBounds (0, roundFloatToInt ((getHeight() - edge * 2) * h), + getWidth(), edge * 2); + } + + void mouseDown (const MouseEvent& e) + { + mouseDrag (e); + } + + void mouseDrag (const MouseEvent& e) + { + const float hue = (e.y - edge) / (float) (getHeight() - edge * 2); + + owner->setHue (hue); + } + + void updateIfNeeded() + { + resized(); + } + +private: + ColourSelector* const owner; + float& h; + float& s; + float& v; + float lastHue; + HueSelectorMarker* marker; + const int edge; + + HueSelectorComp (const HueSelectorComp&); + const HueSelectorComp& operator= (const HueSelectorComp&); +}; + +class SwatchComponent : public Component +{ +public: + SwatchComponent (ColourSelector* owner_, int index_) + : owner (owner_), + index (index_) + { + } + + ~SwatchComponent() + { + } + + void paint (Graphics& g) + { + const Colour colour (owner->getSwatchColour (index)); + + g.fillCheckerBoard (0, 0, getWidth(), getHeight(), + 6, 6, + Colour (0xffdddddd).overlaidWith (colour), + Colour (0xffffffff).overlaidWith (colour)); + } + + void mouseDown (const MouseEvent&) + { + PopupMenu m; + m.addItem (1, TRANS("Use this swatch as the current colour")); + m.addSeparator(); + m.addItem (2, TRANS("Set this swatch to the current colour")); + + const int r = m.showAt (this); + + if (r == 1) + { + owner->setCurrentColour (owner->getSwatchColour (index)); + } + else if (r == 2) + { + if (owner->getSwatchColour (index) != owner->getCurrentColour()) + { + owner->setSwatchColour (index, owner->getCurrentColour()); + repaint(); + } + } + } + +private: + ColourSelector* const owner; + const int index; + + SwatchComponent (const SwatchComponent&); + const SwatchComponent& operator= (const SwatchComponent&); +}; + +ColourSelector::ColourSelector (const int flags_, + const int edgeGap_, + const int gapAroundColourSpaceComponent) + : colour (Colours::white), + flags (flags_), + topSpace (0), + edgeGap (edgeGap_) +{ + // not much point having a selector with no components in it! + jassert ((flags_ & (showColourAtTop | showSliders | showColourspace)) != 0); + + updateHSV(); + + if ((flags & showSliders) != 0) + { + addAndMakeVisible (sliders[0] = new ColourComponentSlider (TRANS ("red"))); + addAndMakeVisible (sliders[1] = new ColourComponentSlider (TRANS ("green"))); + addAndMakeVisible (sliders[2] = new ColourComponentSlider (TRANS ("blue"))); + addChildComponent (sliders[3] = new ColourComponentSlider (TRANS ("alpha"))); + + sliders[3]->setVisible ((flags & showAlphaChannel) != 0); + + for (int i = 4; --i >= 0;) + sliders[i]->addListener (this); + } + else + { + zeromem (sliders, sizeof (sliders)); + } + + if ((flags & showColourspace) != 0) + { + addAndMakeVisible (colourSpace = new ColourSpaceView (this, h, s, v, gapAroundColourSpaceComponent)); + addAndMakeVisible (hueSelector = new HueSelectorComp (this, h, s, v, gapAroundColourSpaceComponent)); + } + else + { + colourSpace = 0; + hueSelector = 0; + } + + update(); +} + +ColourSelector::~ColourSelector() +{ + dispatchPendingMessages(); + deleteAllChildren(); +} + +const Colour ColourSelector::getCurrentColour() const +{ + return ((flags & showAlphaChannel) != 0) ? colour + : colour.withAlpha ((uint8) 0xff); +} + +void ColourSelector::setCurrentColour (const Colour& c) +{ + if (c != colour) + { + colour = ((flags & showAlphaChannel) != 0) ? c : c.withAlpha ((uint8) 0xff); + + updateHSV(); + update(); + } +} + +void ColourSelector::setHue (float newH) +{ + newH = jlimit (0.0f, 1.0f, newH); + + if (h != newH) + { + h = newH; + colour = Colour (h, s, v, colour.getFloatAlpha()); + update(); + } +} + +void ColourSelector::setSV (float newS, float newV) +{ + newS = jlimit (0.0f, 1.0f, newS); + newV = jlimit (0.0f, 1.0f, newV); + + if (s != newS || v != newV) + { + s = newS; + v = newV; + colour = Colour (h, s, v, colour.getFloatAlpha()); + update(); + } +} + +void ColourSelector::updateHSV() +{ + colour.getHSB (h, s, v); +} + +void ColourSelector::update() +{ + if (sliders[0] != 0) + { + sliders[0]->setValue ((int) colour.getRed()); + sliders[1]->setValue ((int) colour.getGreen()); + sliders[2]->setValue ((int) colour.getBlue()); + sliders[3]->setValue ((int) colour.getAlpha()); + } + + if (colourSpace != 0) + { + ((ColourSpaceView*) colourSpace)->updateIfNeeded(); + ((HueSelectorComp*) hueSelector)->updateIfNeeded(); + } + + if ((flags & showColourAtTop) != 0) + repaint (0, edgeGap, getWidth(), topSpace - edgeGap); + + sendChangeMessage (this); +} + +void ColourSelector::paint (Graphics& g) +{ + g.fillAll (findColour (backgroundColourId)); + + if ((flags & showColourAtTop) != 0) + { + const Colour colour (getCurrentColour()); + + g.fillCheckerBoard (edgeGap, edgeGap, getWidth() - edgeGap - edgeGap, topSpace - edgeGap - edgeGap, + 10, 10, + Colour (0xffdddddd).overlaidWith (colour), + Colour (0xffffffff).overlaidWith (colour)); + + g.setColour (Colours::white.overlaidWith (colour).contrasting()); + g.setFont (14.0f, true); + g.drawText (((flags & showAlphaChannel) != 0) + ? String::formatted (T("#%02X%02X%02X%02X"), + (int) colour.getAlpha(), + (int) colour.getRed(), + (int) colour.getGreen(), + (int) colour.getBlue()) + : String::formatted (T("#%02X%02X%02X"), + (int) colour.getRed(), + (int) colour.getGreen(), + (int) colour.getBlue()), + 0, edgeGap, getWidth(), topSpace - edgeGap * 2, + Justification::centred, false); + } + + if ((flags & showSliders) != 0) + { + g.setColour (findColour (labelTextColourId)); + g.setFont (11.0f); + + for (int i = 4; --i >= 0;) + { + if (sliders[i]->isVisible()) + g.drawText (sliders[i]->getName() + T(":"), + 0, sliders[i]->getY(), + sliders[i]->getX() - 8, sliders[i]->getHeight(), + Justification::centredRight, false); + } + } +} + +void ColourSelector::resized() +{ + const int numSliders = ((flags & showAlphaChannel) != 0) ? 4 : 3; + const int numSwatches = getNumSwatches(); + + const int swatchSpace = numSwatches > 0 ? edgeGap + swatchHeight * ((numSwatches + 7) / swatchesPerRow) : 0; + const int sliderSpace = ((flags & showSliders) != 0) ? jmin (22 * numSliders + edgeGap, proportionOfHeight (0.3f)) : 0; + topSpace = ((flags & showColourAtTop) != 0) ? jmin (30 + edgeGap * 2, proportionOfHeight (0.2f)) : edgeGap; + + int y = topSpace; + + if ((flags & showColourspace) != 0) + { + const int hueWidth = jmin (50, proportionOfWidth (0.15f)); + + colourSpace->setBounds (edgeGap, y, + getWidth() - hueWidth - edgeGap - 4, + getHeight() - topSpace - sliderSpace - swatchSpace - edgeGap); + + hueSelector->setBounds (colourSpace->getRight() + 4, y, + getWidth() - edgeGap - (colourSpace->getRight() + 4), + colourSpace->getHeight()); + + y = getHeight() - sliderSpace - swatchSpace - edgeGap; + } + + if ((flags & showSliders) != 0) + { + const int sliderHeight = jmax (4, sliderSpace / numSliders); + + for (int i = 0; i < numSliders; ++i) + { + sliders[i]->setBounds (proportionOfWidth (0.2f), y, + proportionOfWidth (0.72f), sliderHeight - 2); + + y += sliderHeight; + } + } + + if (numSwatches > 0) + { + const int startX = 8; + const int xGap = 4; + const int yGap = 4; + const int swatchWidth = (getWidth() - startX * 2) / swatchesPerRow; + y += edgeGap; + + if (swatchComponents.size() != numSwatches) + { + int i; + for (i = swatchComponents.size(); --i >= 0;) + { + SwatchComponent* const sc = (SwatchComponent*) swatchComponents.getUnchecked(i); + delete sc; + } + + for (i = 0; i < numSwatches; ++i) + { + SwatchComponent* const sc = new SwatchComponent (this, i); + swatchComponents.add (sc); + addAndMakeVisible (sc); + } + } + + int x = startX; + + for (int i = 0; i < swatchComponents.size(); ++i) + { + SwatchComponent* const sc = (SwatchComponent*) swatchComponents.getUnchecked(i); + + sc->setBounds (x + xGap / 2, + y + yGap / 2, + swatchWidth - xGap, + swatchHeight - yGap); + + if (((i + 1) % swatchesPerRow) == 0) + { + x = startX; + y += swatchHeight; + } + else + { + x += swatchWidth; + } + } + } +} + +void ColourSelector::sliderValueChanged (Slider*) +{ + if (sliders[0] != 0) + setCurrentColour (Colour ((uint8) sliders[0]->getValue(), + (uint8) sliders[1]->getValue(), + (uint8) sliders[2]->getValue(), + (uint8) sliders[3]->getValue())); +} + +int ColourSelector::getNumSwatches() const +{ + return 0; +} + +const Colour ColourSelector::getSwatchColour (const int) const +{ + jassertfalse // if you've overridden getNumSwatches(), you also need to implement this method + return Colours::black; +} + +void ColourSelector::setSwatchColour (const int, const Colour&) const +{ + jassertfalse // if you've overridden getNumSwatches(), you also need to implement this method +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ColourSelector.cpp *********/ + +/********* Start of inlined file: juce_DropShadower.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +class ShadowWindow : public Component +{ + Component* owner; + Image** shadowImageSections; + const int type; // 0 = left, 1 = right, 2 = top, 3 = bottom. left + right are full-height + +public: + ShadowWindow (Component* const owner_, + const int type_, + Image** const shadowImageSections_) + : owner (owner_), + shadowImageSections (shadowImageSections_), + type (type_) + { + setInterceptsMouseClicks (false, false); + + if (owner_->isOnDesktop()) + { + setSize (1, 1); // to keep the OS happy by not having zero-size windows + addToDesktop (ComponentPeer::windowIgnoresMouseClicks + | ComponentPeer::windowIsTemporary); + } + else if (owner_->getParentComponent() != 0) + { + owner_->getParentComponent()->addChildComponent (this); + } + } + + ~ShadowWindow() + { + } + + void paint (Graphics& g) + { + Image* const topLeft = shadowImageSections [type * 3]; + Image* const bottomRight = shadowImageSections [type * 3 + 1]; + Image* const filler = shadowImageSections [type * 3 + 2]; + ImageBrush fillBrush (filler, 0, 0, 1.0f); + + g.setOpacity (1.0f); + + if (type < 2) + { + int imH = jmin (topLeft->getHeight(), getHeight() / 2); + g.drawImage (topLeft, + 0, 0, topLeft->getWidth(), imH, + 0, 0, topLeft->getWidth(), imH); + + imH = jmin (bottomRight->getHeight(), getHeight() - getHeight() / 2); + g.drawImage (bottomRight, + 0, getHeight() - imH, bottomRight->getWidth(), imH, + 0, bottomRight->getHeight() - imH, bottomRight->getWidth(), imH); + + g.setBrush (&fillBrush); + g.fillRect (0, topLeft->getHeight(), getWidth(), getHeight() - (topLeft->getHeight() + bottomRight->getHeight())); + } + else + { + int imW = jmin (topLeft->getWidth(), getWidth() / 2); + g.drawImage (topLeft, + 0, 0, imW, topLeft->getHeight(), + 0, 0, imW, topLeft->getHeight()); + + imW = jmin (bottomRight->getWidth(), getWidth() - getWidth() / 2); + g.drawImage (bottomRight, + getWidth() - imW, 0, imW, bottomRight->getHeight(), + bottomRight->getWidth() - imW, 0, imW, bottomRight->getHeight()); + + g.setBrush (&fillBrush); + g.fillRect (topLeft->getWidth(), 0, getWidth() - (topLeft->getWidth() + bottomRight->getWidth()), getHeight()); + } + } + + void resized() + { + repaint(); // (needed for correct repainting) + } + +private: + ShadowWindow (const ShadowWindow&); + const ShadowWindow& operator= (const ShadowWindow&); +}; + +DropShadower::DropShadower (const float alpha_, + const int xOffset_, + const int yOffset_, + const float blurRadius_) + : owner (0), + numShadows (0), + shadowEdge (jmax (xOffset_, yOffset_) + (int) blurRadius_), + xOffset (xOffset_), + yOffset (yOffset_), + alpha (alpha_), + blurRadius (blurRadius_), + inDestructor (false), + reentrant (false) +{ +} + +DropShadower::~DropShadower() +{ + if (owner != 0) + owner->removeComponentListener (this); + + inDestructor = true; + + deleteShadowWindows(); +} + +void DropShadower::deleteShadowWindows() +{ + if (numShadows > 0) + { + int i; + for (i = numShadows; --i >= 0;) + delete shadowWindows[i]; + + for (i = 12; --i >= 0;) + delete shadowImageSections[i]; + + numShadows = 0; + } +} + +void DropShadower::setOwner (Component* componentToFollow) +{ + if (componentToFollow != owner) + { + if (owner != 0) + owner->removeComponentListener (this); + + // (the component can't be null) + jassert (componentToFollow != 0); + + owner = componentToFollow; + + jassert (owner != 0); + jassert (owner->isOpaque()); // doesn't work properly for semi-transparent comps! + + owner->addComponentListener (this); + + updateShadows(); + } +} + +void DropShadower::componentMovedOrResized (Component&, bool /*wasMoved*/, bool /*wasResized*/) +{ + updateShadows(); +} + +void DropShadower::componentBroughtToFront (Component&) +{ + bringShadowWindowsToFront(); +} + +void DropShadower::componentChildrenChanged (Component&) +{ +} + +void DropShadower::componentParentHierarchyChanged (Component&) +{ + deleteShadowWindows(); + updateShadows(); +} + +void DropShadower::componentVisibilityChanged (Component&) +{ + updateShadows(); +} + +void DropShadower::updateShadows() +{ + if (reentrant || inDestructor || (owner == 0)) + return; + + reentrant = true; + + ComponentPeer* const nw = owner->getPeer(); + + const bool isOwnerVisible = owner->isVisible() + && (nw == 0 || ! nw->isMinimised()); + + const bool createShadowWindows = numShadows == 0 + && owner->getWidth() > 0 + && owner->getHeight() > 0 + && isOwnerVisible + && (Desktop::canUseSemiTransparentWindows() + || owner->getParentComponent() != 0); + + if (createShadowWindows) + { + // keep a cached version of the image to save doing the gaussian too often + String imageId; + imageId << shadowEdge << T(',') + << xOffset << T(',') + << yOffset << T(',') + << alpha; + + const int hash = imageId.hashCode(); + + Image* bigIm = ImageCache::getFromHashCode (hash); + + if (bigIm == 0) + { + bigIm = new Image (Image::ARGB, shadowEdge * 5, shadowEdge * 5, true); + + Graphics bigG (*bigIm); + bigG.setColour (Colours::black.withAlpha (alpha)); + bigG.fillRect (shadowEdge + xOffset, + shadowEdge + yOffset, + bigIm->getWidth() - (shadowEdge * 2), + bigIm->getHeight() - (shadowEdge * 2)); + + ImageConvolutionKernel blurKernel (roundFloatToInt (blurRadius * 2.0f)); + blurKernel.createGaussianBlur (blurRadius); + + blurKernel.applyToImage (*bigIm, 0, + xOffset, + yOffset, + bigIm->getWidth(), + bigIm->getHeight()); + + ImageCache::addImageToCache (bigIm, hash); + } + + const int iw = bigIm->getWidth(); + const int ih = bigIm->getHeight(); + const int shadowEdge2 = shadowEdge * 2; + + setShadowImage (bigIm, 0, shadowEdge, shadowEdge2, 0, 0); + setShadowImage (bigIm, 1, shadowEdge, shadowEdge2, 0, ih - shadowEdge2); + setShadowImage (bigIm, 2, shadowEdge, shadowEdge, 0, shadowEdge2); + setShadowImage (bigIm, 3, shadowEdge, shadowEdge2, iw - shadowEdge, 0); + setShadowImage (bigIm, 4, shadowEdge, shadowEdge2, iw - shadowEdge, ih - shadowEdge2); + setShadowImage (bigIm, 5, shadowEdge, shadowEdge, iw - shadowEdge, shadowEdge2); + setShadowImage (bigIm, 6, shadowEdge, shadowEdge, shadowEdge, 0); + setShadowImage (bigIm, 7, shadowEdge, shadowEdge, iw - shadowEdge2, 0); + setShadowImage (bigIm, 8, shadowEdge, shadowEdge, shadowEdge2, 0); + setShadowImage (bigIm, 9, shadowEdge, shadowEdge, shadowEdge, ih - shadowEdge); + setShadowImage (bigIm, 10, shadowEdge, shadowEdge, iw - shadowEdge2, ih - shadowEdge); + setShadowImage (bigIm, 11, shadowEdge, shadowEdge, shadowEdge2, ih - shadowEdge); + + ImageCache::release (bigIm); + + for (int i = 0; i < 4; ++i) + { + shadowWindows[numShadows] = new ShadowWindow (owner, i, shadowImageSections); + ++numShadows; + } + } + + if (numShadows > 0) + { + for (int i = numShadows; --i >= 0;) + { + shadowWindows[i]->setAlwaysOnTop (owner->isAlwaysOnTop()); + shadowWindows[i]->setVisible (isOwnerVisible); + } + + const int x = owner->getX(); + const int y = owner->getY() - shadowEdge; + const int w = owner->getWidth(); + const int h = owner->getHeight() + shadowEdge + shadowEdge; + + shadowWindows[0]->setBounds (x - shadowEdge, + y, + shadowEdge, + h); + + shadowWindows[1]->setBounds (x + w, + y, + shadowEdge, + h); + + shadowWindows[2]->setBounds (x, + y, + w, + shadowEdge); + + shadowWindows[3]->setBounds (x, + owner->getBottom(), + w, + shadowEdge); + } + + reentrant = false; + + if (createShadowWindows) + bringShadowWindowsToFront(); +} + +void DropShadower::setShadowImage (Image* const src, + const int num, + const int w, + const int h, + const int sx, + const int sy) throw() +{ + shadowImageSections[num] = new Image (Image::ARGB, w, h, true); + + Graphics g (*shadowImageSections[num]); + g.drawImage (src, 0, 0, w, h, sx, sy, w, h); +} + +void DropShadower::bringShadowWindowsToFront() +{ + if (! (inDestructor || reentrant)) + { + updateShadows(); + + reentrant = true; + + for (int i = numShadows; --i >= 0;) + shadowWindows[i]->toBehind (owner); + + reentrant = false; + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_DropShadower.cpp *********/ + +/********* Start of inlined file: juce_MagnifierComponent.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +class MagnifyingPeer : public ComponentPeer +{ +public: + + MagnifyingPeer (Component* const component, + MagnifierComponent* const magnifierComp_) + : ComponentPeer (component, 0), + magnifierComp (magnifierComp_) + { + } + + ~MagnifyingPeer() + { + } + + void* getNativeHandle() const { return 0; } + void setVisible (bool) {} + void setTitle (const String&) {} + void setPosition (int, int) {} + void setSize (int, int) {} + void setBounds (int, int, int, int, const bool) {} + void setMinimised (bool) {} + bool isMinimised() const { return false; } + void setFullScreen (bool) {} + bool isFullScreen() const { return false; } + const BorderSize getFrameSize() const { return BorderSize (0); } + bool setAlwaysOnTop (bool) { return true; } + void toFront (bool) {} + void toBehind (ComponentPeer*) {} + void setIcon (const Image&) {} + + bool isFocused() const + { + return magnifierComp->hasKeyboardFocus (true); + } + + void grabFocus() + { + ComponentPeer* peer = magnifierComp->getPeer(); + if (peer != 0) + peer->grabFocus(); + } + + void getBounds (int& x, int& y, int& w, int& h) const + { + x = magnifierComp->getScreenX(); + y = magnifierComp->getScreenY(); + w = component->getWidth(); + h = component->getHeight(); + } + + int getScreenX() const { return magnifierComp->getScreenX(); } + int getScreenY() const { return magnifierComp->getScreenY(); } + + void relativePositionToGlobal (int& x, int& y) + { + const double zoom = magnifierComp->getScaleFactor(); + x = roundDoubleToInt (x * zoom); + y = roundDoubleToInt (y * zoom); + + magnifierComp->relativePositionToGlobal (x, y); + } + + void globalPositionToRelative (int& x, int& y) + { + magnifierComp->globalPositionToRelative (x, y); + + const double zoom = magnifierComp->getScaleFactor(); + x = roundDoubleToInt (x / zoom); + y = roundDoubleToInt (y / zoom); + } + + bool contains (int x, int y, bool) const + { + return ((unsigned int) x) < (unsigned int) magnifierComp->getWidth() + && ((unsigned int) y) < (unsigned int) magnifierComp->getHeight(); + } + + void repaint (int x, int y, int w, int h) + { + const double zoom = magnifierComp->getScaleFactor(); + + magnifierComp->repaint ((int) (x * zoom), + (int) (y * zoom), + roundDoubleToInt (w * zoom) + 1, + roundDoubleToInt (h * zoom) + 1); + } + + void performAnyPendingRepaintsNow() + { + } + + juce_UseDebuggingNewOperator + +private: + MagnifierComponent* const magnifierComp; + + MagnifyingPeer (const MagnifyingPeer&); + const MagnifyingPeer& operator= (const MagnifyingPeer&); +}; + +class PeerHolderComp : public Component +{ +public: + PeerHolderComp (MagnifierComponent* const magnifierComp_) + : magnifierComp (magnifierComp_) + { + setVisible (true); + } + + ~PeerHolderComp() + { + } + + ComponentPeer* createNewPeer (int, void*) + { + return new MagnifyingPeer (this, magnifierComp); + } + + void childBoundsChanged (Component* c) + { + if (c != 0) + { + setSize (c->getWidth(), c->getHeight()); + magnifierComp->childBoundsChanged (this); + } + } + + void mouseWheelMove (const MouseEvent& e, float ix, float iy) + { + // unhandled mouse wheel moves can be referred upwards to the parent comp.. + Component* const p = magnifierComp->getParentComponent(); + + if (p != 0) + p->mouseWheelMove (e.getEventRelativeTo (p), ix, iy); + } + +private: + MagnifierComponent* const magnifierComp; + + PeerHolderComp (const PeerHolderComp&); + const PeerHolderComp& operator= (const PeerHolderComp&); +}; + +MagnifierComponent::MagnifierComponent (Component* const content_, + const bool deleteContentCompWhenNoLongerNeeded) + : content (content_), + scaleFactor (0.0), + peer (0), + deleteContent (deleteContentCompWhenNoLongerNeeded) +{ + holderComp = new PeerHolderComp (this); + setScaleFactor (1.0); +} + +MagnifierComponent::~MagnifierComponent() +{ + delete holderComp; + + if (deleteContent) + delete content; +} + +void MagnifierComponent::setScaleFactor (double newScaleFactor) +{ + jassert (newScaleFactor > 0.0); // hmm - unlikely to work well with a negative scale factor + + newScaleFactor = jlimit (1.0 / 8.0, 1000.0, newScaleFactor); + + if (scaleFactor != newScaleFactor) + { + scaleFactor = newScaleFactor; + + if (scaleFactor == 1.0) + { + holderComp->removeFromDesktop(); + peer = 0; + addChildComponent (content); + childBoundsChanged (content); + } + else + { + holderComp->addAndMakeVisible (content); + holderComp->childBoundsChanged (content); + childBoundsChanged (holderComp); + holderComp->addToDesktop (0); + peer = holderComp->getPeer(); + } + + repaint(); + } +} + +void MagnifierComponent::paint (Graphics& g) +{ + const int w = holderComp->getWidth(); + const int h = holderComp->getHeight(); + + if (w == 0 || h == 0) + return; + + const Rectangle r (g.getClipBounds()); + + const int srcX = (int) (r.getX() / scaleFactor); + const int srcY = (int) (r.getY() / scaleFactor); + int srcW = roundDoubleToInt (r.getRight() / scaleFactor) - srcX; + int srcH = roundDoubleToInt (r.getBottom() / scaleFactor) - srcY; + + if (scaleFactor >= 1.0) + { + ++srcW; + ++srcH; + } + + Image temp (Image::ARGB, jmax (w, srcX + srcW), jmax (h, srcY + srcH), false); + temp.clear (srcX, srcY, srcW, srcH); + + Graphics g2 (temp); + g2.reduceClipRegion (srcX, srcY, srcW, srcH); + holderComp->paintEntireComponent (g2); + + g.setImageResamplingQuality (Graphics::lowResamplingQuality); + g.drawImage (&temp, + 0, 0, (int) (w * scaleFactor), (int) (h * scaleFactor), + 0, 0, w, h, + false); +} + +void MagnifierComponent::childBoundsChanged (Component* c) +{ + if (c != 0) + setSize (roundDoubleToInt (c->getWidth() * scaleFactor), + roundDoubleToInt (c->getHeight() * scaleFactor)); +} + +void MagnifierComponent::mouseDown (const MouseEvent& e) +{ + if (peer != 0) + peer->handleMouseDown (scaleInt (e.x), scaleInt (e.y), e.eventTime.toMilliseconds()); +} + +void MagnifierComponent::mouseUp (const MouseEvent& e) +{ + if (peer != 0) + peer->handleMouseUp (e.mods.getRawFlags(), scaleInt (e.x), scaleInt (e.y), e.eventTime.toMilliseconds()); +} + +void MagnifierComponent::mouseDrag (const MouseEvent& e) +{ + if (peer != 0) + peer->handleMouseDrag (scaleInt (e.x), scaleInt (e.y), e.eventTime.toMilliseconds()); +} + +void MagnifierComponent::mouseMove (const MouseEvent& e) +{ + if (peer != 0) + peer->handleMouseMove (scaleInt (e.x), scaleInt (e.y), e.eventTime.toMilliseconds()); +} + +void MagnifierComponent::mouseEnter (const MouseEvent& e) +{ + if (peer != 0) + peer->handleMouseEnter (scaleInt (e.x), scaleInt (e.y), e.eventTime.toMilliseconds()); +} + +void MagnifierComponent::mouseExit (const MouseEvent& e) +{ + if (peer != 0) + peer->handleMouseExit (scaleInt (e.x), scaleInt (e.y), e.eventTime.toMilliseconds()); +} + +void MagnifierComponent::mouseWheelMove (const MouseEvent& e, float ix, float iy) +{ + if (peer != 0) + peer->handleMouseWheel (roundFloatToInt (ix * 256.0f), + roundFloatToInt (iy * 256.0f), + e.eventTime.toMilliseconds()); + else + Component::mouseWheelMove (e, ix, iy); +} + +int MagnifierComponent::scaleInt (const int n) const throw() +{ + return roundDoubleToInt (n / scaleFactor); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_MagnifierComponent.cpp *********/ + +/********* Start of inlined file: juce_MidiKeyboardComponent.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +class MidiKeyboardUpDownButton : public Button +{ +public: + MidiKeyboardUpDownButton (MidiKeyboardComponent* const owner_, + const int delta_) + : Button (String::empty), + owner (owner_), + delta (delta_) + { + setOpaque (true); + } + + ~MidiKeyboardUpDownButton() + { + } + + void clicked() + { + int note = owner->getLowestVisibleKey(); + + if (delta < 0) + note = (note - 1) / 12; + else + note = note / 12 + 1; + + owner->setLowestVisibleKey (note * 12); + } + + void paintButton (Graphics& g, + bool isMouseOverButton, + bool isButtonDown) + { + owner->drawUpDownButton (g, getWidth(), getHeight(), + isMouseOverButton, isButtonDown, + delta > 0); + } + +private: + MidiKeyboardComponent* const owner; + const int delta; + + MidiKeyboardUpDownButton (const MidiKeyboardUpDownButton&); + const MidiKeyboardUpDownButton& operator= (const MidiKeyboardUpDownButton&); +}; + +MidiKeyboardComponent::MidiKeyboardComponent (MidiKeyboardState& state_, + const Orientation orientation_) + : state (state_), + xOffset (0), + blackNoteLength (1), + keyWidth (16.0f), + orientation (orientation_), + midiChannel (1), + midiInChannelMask (0xffff), + velocity (1.0f), + noteUnderMouse (-1), + mouseDownNote (-1), + rangeStart (0), + rangeEnd (127), + firstKey (12 * 4), + canScroll (true), + mouseDragging (false), + keyPresses (4), + keyPressNotes (16), + keyMappingOctave (6), + octaveNumForMiddleC (3) +{ + addChildComponent (scrollDown = new MidiKeyboardUpDownButton (this, -1)); + addChildComponent (scrollUp = new MidiKeyboardUpDownButton (this, 1)); + + // initialise with a default set of querty key-mappings.. + const char* const keymap = "awsedftgyhujkolp;"; + + for (int i = String (keymap).length(); --i >= 0;) + setKeyPressForNote (KeyPress (keymap[i], 0, 0), i); + + setOpaque (true); + setWantsKeyboardFocus (true); + + state.addListener (this); +} + +MidiKeyboardComponent::~MidiKeyboardComponent() +{ + state.removeListener (this); + jassert (mouseDownNote < 0 && keysPressed.countNumberOfSetBits() == 0); // leaving stuck notes! + + deleteAllChildren(); +} + +void MidiKeyboardComponent::setKeyWidth (const float widthInPixels) +{ + keyWidth = widthInPixels; + resized(); +} + +void MidiKeyboardComponent::setOrientation (const Orientation newOrientation) +{ + if (orientation != newOrientation) + { + orientation = newOrientation; + resized(); + } +} + +void MidiKeyboardComponent::setAvailableRange (const int lowestNote, + const int highestNote) +{ + jassert (lowestNote >= 0 && lowestNote <= 127); + jassert (highestNote >= 0 && highestNote <= 127); + jassert (lowestNote <= highestNote); + + if (rangeStart != lowestNote || rangeEnd != highestNote) + { + rangeStart = jlimit (0, 127, lowestNote); + rangeEnd = jlimit (0, 127, highestNote); + firstKey = jlimit (rangeStart, rangeEnd, firstKey); + resized(); + } +} + +void MidiKeyboardComponent::setLowestVisibleKey (int noteNumber) +{ + noteNumber = jlimit (rangeStart, rangeEnd, noteNumber); + + if (noteNumber != firstKey) + { + firstKey = noteNumber; + sendChangeMessage (this); + resized(); + } +} + +void MidiKeyboardComponent::setScrollButtonsVisible (const bool canScroll_) +{ + if (canScroll != canScroll_) + { + canScroll = canScroll_; + resized(); + } +} + +void MidiKeyboardComponent::colourChanged() +{ + repaint(); +} + +void MidiKeyboardComponent::setMidiChannel (const int midiChannelNumber) +{ + jassert (midiChannelNumber > 0 && midiChannelNumber <= 16); + + if (midiChannel != midiChannelNumber) + { + resetAnyKeysInUse(); + midiChannel = jlimit (1, 16, midiChannelNumber); + } +} + +void MidiKeyboardComponent::setMidiChannelsToDisplay (const int midiChannelMask) +{ + midiInChannelMask = midiChannelMask; + triggerAsyncUpdate(); +} + +void MidiKeyboardComponent::setVelocity (const float velocity_) +{ + jassert (velocity > 0 && velocity <= 1.0f); + + velocity = jlimit (0.0f, 1.0f, velocity_); +} + +void MidiKeyboardComponent::getKeyPosition (int midiNoteNumber, const float keyWidth, int& x, int& w) const +{ + jassert (midiNoteNumber >= 0 && midiNoteNumber < 128); + + static const float blackNoteWidth = 0.7f; + + static const float notePos[] = { 0.0f, 1 - blackNoteWidth * 0.6f, + 1.0f, 2 - blackNoteWidth * 0.4f, + 2.0f, 3.0f, 4 - blackNoteWidth * 0.7f, + 4.0f, 5 - blackNoteWidth * 0.5f, + 5.0f, 6 - blackNoteWidth * 0.3f, + 6.0f }; + + static const float widths[] = { 1.0f, blackNoteWidth, + 1.0f, blackNoteWidth, + 1.0f, 1.0f, blackNoteWidth, + 1.0f, blackNoteWidth, + 1.0f, blackNoteWidth, + 1.0f }; + + const int octave = midiNoteNumber / 12; + const int note = midiNoteNumber % 12; + + x = roundFloatToInt (octave * 7.0f * keyWidth + notePos [note] * keyWidth); + w = roundFloatToInt (widths [note] * keyWidth); +} + +void MidiKeyboardComponent::getKeyPos (int midiNoteNumber, int& x, int& w) const +{ + getKeyPosition (midiNoteNumber, keyWidth, x, w); + + int rx, rw; + getKeyPosition (rangeStart, keyWidth, rx, rw); + + x -= xOffset + rx; +} + +int MidiKeyboardComponent::getKeyStartPosition (const int midiNoteNumber) const +{ + int x, y; + getKeyPos (midiNoteNumber, x, y); + return x; +} + +static const uint8 whiteNotes[] = { 0, 2, 4, 5, 7, 9, 11 }; +static const uint8 blackNotes[] = { 1, 3, 6, 8, 10 }; + +int MidiKeyboardComponent::xyToNote (int x, int y) +{ + if (! reallyContains (x, y, false)) + return -1; + + if (orientation != horizontalKeyboard) + { + swapVariables (x, y); + + if (orientation == verticalKeyboardFacingLeft) + y = getWidth() - y; + else + x = getHeight() - x; + } + + return remappedXYToNote (x + xOffset, y); +} + +int MidiKeyboardComponent::remappedXYToNote (int x, int y) const +{ + if (y < blackNoteLength) + { + for (int octaveStart = 12 * (rangeStart / 12); octaveStart < rangeEnd; octaveStart += 12) + { + for (int i = 0; i < 5; ++i) + { + const int note = octaveStart + blackNotes [i]; + + if (note >= rangeStart && note <= rangeEnd) + { + int kx, kw; + getKeyPos (note, kx, kw); + kx += xOffset; + + if (x >= kx && x < kx + kw) + return note; + } + } + } + } + + for (int octaveStart = 12 * (rangeStart / 12); octaveStart < rangeEnd; octaveStart += 12) + { + for (int i = 0; i < 7; ++i) + { + const int note = octaveStart + whiteNotes [i]; + + if (note >= rangeStart && note <= rangeEnd) + { + int kx, kw; + getKeyPos (note, kx, kw); + kx += xOffset; + + if (x >= kx && x < kx + kw) + return note; + } + } + } + + return -1; +} + +void MidiKeyboardComponent::repaintNote (const int noteNum) +{ + if (noteNum >= rangeStart && noteNum <= rangeEnd) + { + int x, w; + getKeyPos (noteNum, x, w); + + if (orientation == horizontalKeyboard) + repaint (x, 0, w, getHeight()); + else if (orientation == verticalKeyboardFacingLeft) + repaint (0, x, getWidth(), w); + else if (orientation == verticalKeyboardFacingRight) + repaint (0, getHeight() - x - w, getWidth(), w); + } +} + +void MidiKeyboardComponent::paint (Graphics& g) +{ + g.fillAll (Colours::white.overlaidWith (findColour (whiteNoteColourId))); + + const Colour lineColour (findColour (keySeparatorLineColourId)); + const Colour textColour (findColour (textLabelColourId)); + + int x, w, octave; + + for (octave = 0; octave < 128; octave += 12) + { + for (int white = 0; white < 7; ++white) + { + const int noteNum = octave + whiteNotes [white]; + + if (noteNum >= rangeStart && noteNum <= rangeEnd) + { + getKeyPos (noteNum, x, w); + + if (orientation == horizontalKeyboard) + drawWhiteNote (noteNum, g, x, 0, w, getHeight(), + state.isNoteOnForChannels (midiInChannelMask, noteNum), + noteUnderMouse == noteNum, + lineColour, textColour); + else if (orientation == verticalKeyboardFacingLeft) + drawWhiteNote (noteNum, g, 0, x, getWidth(), w, + state.isNoteOnForChannels (midiInChannelMask, noteNum), + noteUnderMouse == noteNum, + lineColour, textColour); + else if (orientation == verticalKeyboardFacingRight) + drawWhiteNote (noteNum, g, 0, getHeight() - x - w, getWidth(), w, + state.isNoteOnForChannels (midiInChannelMask, noteNum), + noteUnderMouse == noteNum, + lineColour, textColour); + } + } + } + + float x1 = 0.0f, y1 = 0.0f, x2 = 0.0f, y2 = 0.0f; + + if (orientation == verticalKeyboardFacingLeft) + { + x1 = getWidth() - 1.0f; + x2 = getWidth() - 5.0f; + } + else if (orientation == verticalKeyboardFacingRight) + x2 = 5.0f; + else + y2 = 5.0f; + + GradientBrush gb (Colours::black.withAlpha (0.3f), x1, y1, + Colours::transparentBlack, x2, y2, false); + g.setBrush (&gb); + + getKeyPos (rangeEnd, x, w); + x += w; + + if (orientation == verticalKeyboardFacingLeft) + g.fillRect (getWidth() - 5, 0, 5, x); + else if (orientation == verticalKeyboardFacingRight) + g.fillRect (0, 0, 5, x); + else + g.fillRect (0, 0, x, 5); + + g.setColour (lineColour); + + if (orientation == verticalKeyboardFacingLeft) + g.fillRect (0, 0, 1, x); + else if (orientation == verticalKeyboardFacingRight) + g.fillRect (getWidth() - 1, 0, 1, x); + else + g.fillRect (0, getHeight() - 1, x, 1); + + const Colour blackNoteColour (findColour (blackNoteColourId)); + + for (octave = 0; octave < 128; octave += 12) + { + for (int black = 0; black < 5; ++black) + { + const int noteNum = octave + blackNotes [black]; + + if (noteNum >= rangeStart && noteNum <= rangeEnd) + { + getKeyPos (noteNum, x, w); + + if (orientation == horizontalKeyboard) + drawBlackNote (noteNum, g, x, 0, w, blackNoteLength, + state.isNoteOnForChannels (midiInChannelMask, noteNum), + noteUnderMouse == noteNum, + blackNoteColour); + else if (orientation == verticalKeyboardFacingLeft) + drawBlackNote (noteNum, g, getWidth() - blackNoteLength, x, blackNoteLength, w, + state.isNoteOnForChannels (midiInChannelMask, noteNum), + noteUnderMouse == noteNum, + blackNoteColour); + else if (orientation == verticalKeyboardFacingRight) + drawBlackNote (noteNum, g, 0, getHeight() - x - w, blackNoteLength, w, + state.isNoteOnForChannels (midiInChannelMask, noteNum), + noteUnderMouse == noteNum, + blackNoteColour); + } + } + } +} + +void MidiKeyboardComponent::drawWhiteNote (int midiNoteNumber, + Graphics& g, int x, int y, int w, int h, + bool isDown, bool isOver, + const Colour& lineColour, + const Colour& textColour) +{ + Colour c (Colours::transparentWhite); + + if (isDown) + c = findColour (keyDownOverlayColourId); + + if (isOver) + c = c.overlaidWith (findColour (mouseOverKeyOverlayColourId)); + + g.setColour (c); + g.fillRect (x, y, w, h); + + const String text (getWhiteNoteText (midiNoteNumber)); + + if (! text.isEmpty()) + { + g.setColour (textColour); + + Font f (jmin (12.0f, keyWidth * 0.9f)); + f.setHorizontalScale (0.8f); + g.setFont (f); + Justification justification (Justification::centredBottom); + + if (orientation == verticalKeyboardFacingLeft) + justification = Justification::centredLeft; + else if (orientation == verticalKeyboardFacingRight) + justification = Justification::centredRight; + + g.drawFittedText (text, x + 2, y + 2, w - 4, h - 4, justification, 1); + } + + g.setColour (lineColour); + + if (orientation == horizontalKeyboard) + g.fillRect (x, y, 1, h); + else if (orientation == verticalKeyboardFacingLeft) + g.fillRect (x, y, w, 1); + else if (orientation == verticalKeyboardFacingRight) + g.fillRect (x, y + h - 1, w, 1); + + if (midiNoteNumber == rangeEnd) + { + if (orientation == horizontalKeyboard) + g.fillRect (x + w, y, 1, h); + else if (orientation == verticalKeyboardFacingLeft) + g.fillRect (x, y + h, w, 1); + else if (orientation == verticalKeyboardFacingRight) + g.fillRect (x, y - 1, w, 1); + } +} + +void MidiKeyboardComponent::drawBlackNote (int /*midiNoteNumber*/, + Graphics& g, int x, int y, int w, int h, + bool isDown, bool isOver, + const Colour& noteFillColour) +{ + Colour c (noteFillColour); + + if (isDown) + c = c.overlaidWith (findColour (keyDownOverlayColourId)); + + if (isOver) + c = c.overlaidWith (findColour (mouseOverKeyOverlayColourId)); + + g.setColour (c); + g.fillRect (x, y, w, h); + + if (isDown) + { + g.setColour (noteFillColour); + g.drawRect (x, y, w, h); + } + else + { + const int xIndent = jmax (1, jmin (w, h) / 8); + + g.setColour (c.brighter()); + + if (orientation == horizontalKeyboard) + g.fillRect (x + xIndent, y, w - xIndent * 2, 7 * h / 8); + else if (orientation == verticalKeyboardFacingLeft) + g.fillRect (x + w / 8, y + xIndent, w - w / 8, h - xIndent * 2); + else if (orientation == verticalKeyboardFacingRight) + g.fillRect (x, y + xIndent, 7 * w / 8, h - xIndent * 2); + } +} + +void MidiKeyboardComponent::setOctaveForMiddleC (const int octaveNumForMiddleC_) throw() +{ + octaveNumForMiddleC = octaveNumForMiddleC_; + repaint(); +} + +const String MidiKeyboardComponent::getWhiteNoteText (const int midiNoteNumber) +{ + if (keyWidth > 14.0f && midiNoteNumber % 12 == 0) + return MidiMessage::getMidiNoteName (midiNoteNumber, true, true, octaveNumForMiddleC); + + return String::empty; +} + +void MidiKeyboardComponent::drawUpDownButton (Graphics& g, int w, int h, + const bool isMouseOver, + const bool isButtonDown, + const bool movesOctavesUp) +{ + g.fillAll (findColour (upDownButtonBackgroundColourId)); + + float angle; + + if (orientation == MidiKeyboardComponent::horizontalKeyboard) + angle = movesOctavesUp ? 0.0f : 0.5f; + else if (orientation == MidiKeyboardComponent::verticalKeyboardFacingLeft) + angle = movesOctavesUp ? 0.25f : 0.75f; + else + angle = movesOctavesUp ? 0.75f : 0.25f; + + Path path; + path.lineTo (0.0f, 1.0f); + path.lineTo (1.0f, 0.5f); + path.closeSubPath(); + + path.applyTransform (AffineTransform::rotation (float_Pi * 2.0f * angle, 0.5f, 0.5f)); + + g.setColour (findColour (upDownButtonArrowColourId) + .withAlpha (isButtonDown ? 1.0f : (isMouseOver ? 0.6f : 0.4f))); + + g.fillPath (path, path.getTransformToScaleToFit (1.0f, 1.0f, + w - 2.0f, + h - 2.0f, + true)); +} + +void MidiKeyboardComponent::resized() +{ + int w = getWidth(); + int h = getHeight(); + + if (w > 0 && h > 0) + { + if (orientation != horizontalKeyboard) + swapVariables (w, h); + + blackNoteLength = roundFloatToInt (h * 0.7f); + + int kx2, kw2; + getKeyPos (rangeEnd, kx2, kw2); + + kx2 += kw2; + + if (firstKey != rangeStart) + { + int kx1, kw1; + getKeyPos (rangeStart, kx1, kw1); + + if (kx2 - kx1 <= w) + { + firstKey = rangeStart; + sendChangeMessage (this); + repaint(); + } + } + + const bool showScrollButtons = canScroll && (firstKey > rangeStart || kx2 > w + xOffset * 2); + + scrollDown->setVisible (showScrollButtons); + scrollUp->setVisible (showScrollButtons); + + xOffset = 0; + + if (showScrollButtons) + { + const int scrollButtonW = jmin (12, w / 2); + + if (orientation == horizontalKeyboard) + { + scrollDown->setBounds (0, 0, scrollButtonW, getHeight()); + scrollUp->setBounds (getWidth() - scrollButtonW, 0, scrollButtonW, getHeight()); + } + else if (orientation == verticalKeyboardFacingLeft) + { + scrollDown->setBounds (0, 0, getWidth(), scrollButtonW); + scrollUp->setBounds (0, getHeight() - scrollButtonW, getWidth(), scrollButtonW); + } + else if (orientation == verticalKeyboardFacingRight) + { + scrollDown->setBounds (0, getHeight() - scrollButtonW, getWidth(), scrollButtonW); + scrollUp->setBounds (0, 0, getWidth(), scrollButtonW); + } + + int endOfLastKey, kw; + getKeyPos (rangeEnd, endOfLastKey, kw); + endOfLastKey += kw; + + const int spaceAvailable = w - scrollButtonW * 2; + const int lastStartKey = remappedXYToNote (endOfLastKey - spaceAvailable, 0) + 1; + + if (lastStartKey >= 0 && firstKey > lastStartKey) + { + firstKey = jlimit (rangeStart, rangeEnd, lastStartKey); + sendChangeMessage (this); + } + + int newOffset = 0; + getKeyPos (firstKey, newOffset, kw); + xOffset = newOffset - scrollButtonW; + } + else + { + firstKey = rangeStart; + } + + timerCallback(); + repaint(); + } +} + +void MidiKeyboardComponent::handleNoteOn (MidiKeyboardState*, int /*midiChannel*/, int /*midiNoteNumber*/, float /*velocity*/) +{ + triggerAsyncUpdate(); +} + +void MidiKeyboardComponent::handleNoteOff (MidiKeyboardState*, int /*midiChannel*/, int /*midiNoteNumber*/) +{ + triggerAsyncUpdate(); +} + +void MidiKeyboardComponent::handleAsyncUpdate() +{ + for (int i = rangeStart; i <= rangeEnd; ++i) + { + if (keysCurrentlyDrawnDown[i] != state.isNoteOnForChannels (midiInChannelMask, i)) + { + keysCurrentlyDrawnDown.setBit (i, state.isNoteOnForChannels (midiInChannelMask, i)); + repaintNote (i); + } + } +} + +void MidiKeyboardComponent::resetAnyKeysInUse() +{ + if (keysPressed.countNumberOfSetBits() > 0 || mouseDownNote > 0) + { + state.allNotesOff (midiChannel); + keysPressed.clear(); + mouseDownNote = -1; + } +} + +void MidiKeyboardComponent::updateNoteUnderMouse (int x, int y) +{ + const int newNote = (mouseDragging || isMouseOver()) + ? xyToNote (x, y) : -1; + + if (noteUnderMouse != newNote) + { + if (mouseDownNote >= 0) + { + state.noteOff (midiChannel, mouseDownNote); + mouseDownNote = -1; + } + + if (mouseDragging && newNote >= 0) + { + state.noteOn (midiChannel, newNote, velocity); + mouseDownNote = newNote; + } + + repaintNote (noteUnderMouse); + noteUnderMouse = newNote; + repaintNote (noteUnderMouse); + } + else if (mouseDownNote >= 0 && ! mouseDragging) + { + state.noteOff (midiChannel, mouseDownNote); + mouseDownNote = -1; + } +} + +void MidiKeyboardComponent::mouseMove (const MouseEvent& e) +{ + updateNoteUnderMouse (e.x, e.y); + stopTimer(); +} + +void MidiKeyboardComponent::mouseDrag (const MouseEvent& e) +{ + const int newNote = xyToNote (e.x, e.y); + + if (newNote >= 0) + mouseDraggedToKey (newNote, e); + + updateNoteUnderMouse (e.x, e.y); +} + +bool MidiKeyboardComponent::mouseDownOnKey (int /*midiNoteNumber*/, const MouseEvent&) +{ + return true; +} + +void MidiKeyboardComponent::mouseDraggedToKey (int /*midiNoteNumber*/, const MouseEvent&) +{ +} + +void MidiKeyboardComponent::mouseDown (const MouseEvent& e) +{ + const int newNote = xyToNote (e.x, e.y); + mouseDragging = false; + + if (newNote >= 0 && mouseDownOnKey (newNote, e)) + { + repaintNote (noteUnderMouse); + noteUnderMouse = -1; + mouseDragging = true; + + updateNoteUnderMouse (e.x, e.y); + startTimer (500); + } +} + +void MidiKeyboardComponent::mouseUp (const MouseEvent& e) +{ + mouseDragging = false; + updateNoteUnderMouse (e.x, e.y); + + stopTimer(); +} + +void MidiKeyboardComponent::mouseEnter (const MouseEvent& e) +{ + updateNoteUnderMouse (e.x, e.y); +} + +void MidiKeyboardComponent::mouseExit (const MouseEvent& e) +{ + updateNoteUnderMouse (e.x, e.y); +} + +void MidiKeyboardComponent::mouseWheelMove (const MouseEvent&, float ix, float iy) +{ + setLowestVisibleKey (getLowestVisibleKey() + roundFloatToInt ((ix != 0 ? ix : iy) * 5.0f)); +} + +void MidiKeyboardComponent::timerCallback() +{ + int mx, my; + getMouseXYRelative (mx, my); + + updateNoteUnderMouse (mx, my); +} + +void MidiKeyboardComponent::clearKeyMappings() +{ + resetAnyKeysInUse(); + keyPressNotes.clear(); + keyPresses.clear(); +} + +void MidiKeyboardComponent::setKeyPressForNote (const KeyPress& key, + const int midiNoteOffsetFromC) +{ + removeKeyPressForNote (midiNoteOffsetFromC); + + keyPressNotes.add (midiNoteOffsetFromC); + keyPresses.add (key); +} + +void MidiKeyboardComponent::removeKeyPressForNote (const int midiNoteOffsetFromC) +{ + for (int i = keyPressNotes.size(); --i >= 0;) + { + if (keyPressNotes.getUnchecked (i) == midiNoteOffsetFromC) + { + keyPressNotes.remove (i); + keyPresses.remove (i); + } + } +} + +void MidiKeyboardComponent::setKeyPressBaseOctave (const int newOctaveNumber) +{ + jassert (newOctaveNumber >= 0 && newOctaveNumber <= 10); + + keyMappingOctave = newOctaveNumber; +} + +bool MidiKeyboardComponent::keyStateChanged() +{ + bool keyPressUsed = false; + + for (int i = keyPresses.size(); --i >= 0;) + { + const int note = 12 * keyMappingOctave + keyPressNotes.getUnchecked (i); + + if (keyPresses.getReference(i).isCurrentlyDown()) + { + if (! keysPressed [note]) + { + keysPressed.setBit (note); + state.noteOn (midiChannel, note, velocity); + keyPressUsed = true; + } + } + else + { + if (keysPressed [note]) + { + keysPressed.clearBit (note); + state.noteOff (midiChannel, note); + keyPressUsed = true; + } + } + } + + return keyPressUsed; +} + +void MidiKeyboardComponent::focusLost (FocusChangeType) +{ + resetAnyKeysInUse(); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_MidiKeyboardComponent.cpp *********/ + +/********* Start of inlined file: juce_OpenGLComponent.cpp *********/ + +#if JUCE_OPENGL + +BEGIN_JUCE_NAMESPACE + +extern void juce_glViewport (const int w, const int h); + +OpenGLPixelFormat::OpenGLPixelFormat (const int bitsPerRGBComponent, + const int alphaBits_, + const int depthBufferBits_, + const int stencilBufferBits_) throw() + : redBits (bitsPerRGBComponent), + greenBits (bitsPerRGBComponent), + blueBits (bitsPerRGBComponent), + alphaBits (alphaBits_), + depthBufferBits (depthBufferBits_), + stencilBufferBits (stencilBufferBits_), + accumulationBufferRedBits (0), + accumulationBufferGreenBits (0), + accumulationBufferBlueBits (0), + accumulationBufferAlphaBits (0), + fullSceneAntiAliasingNumSamples (0) +{ +} + +bool OpenGLPixelFormat::operator== (const OpenGLPixelFormat& other) const throw() +{ + return memcmp (this, &other, sizeof (other)) == 0; +} + +static VoidArray knownContexts; + +OpenGLContext::OpenGLContext() throw() +{ + knownContexts.add (this); +} + +OpenGLContext::~OpenGLContext() +{ + knownContexts.removeValue (this); +} + +OpenGLContext* OpenGLContext::getCurrentContext() +{ + for (int i = knownContexts.size(); --i >= 0;) + { + OpenGLContext* const oglc = (OpenGLContext*) knownContexts.getUnchecked(i); + + if (oglc->isActive()) + return oglc; + } + + return 0; +} + +class OpenGLComponentWatcher : public ComponentMovementWatcher +{ +public: + + OpenGLComponentWatcher (OpenGLComponent* const owner_) + : ComponentMovementWatcher (owner_), + owner (owner_), + wasShowing (false) + { + } + + ~OpenGLComponentWatcher() {} + + void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) + { + owner->updateContextPosition(); + } + + void componentPeerChanged() + { + const ScopedLock sl (owner->getContextLock()); + owner->deleteContext(); + } + + void componentVisibilityChanged (Component&) + { + const bool isShowingNow = owner->isShowing(); + + if (wasShowing != isShowingNow) + { + wasShowing = isShowingNow; + owner->updateContextPosition(); + } + } + + juce_UseDebuggingNewOperator + +private: + OpenGLComponent* const owner; + bool wasShowing; +}; + +OpenGLComponent::OpenGLComponent() + : context (0), + contextToShareListsWith (0), + needToUpdateViewport (true) +{ + setOpaque (true); + componentWatcher = new OpenGLComponentWatcher (this); +} + +OpenGLComponent::~OpenGLComponent() +{ + deleteContext(); + delete componentWatcher; +} + +void OpenGLComponent::deleteContext() +{ + const ScopedLock sl (contextLock); + deleteAndZero (context); +} + +void OpenGLComponent::updateContextPosition() +{ + needToUpdateViewport = true; + + if (getWidth() > 0 && getHeight() > 0) + { + Component* const topComp = getTopLevelComponent(); + + if (topComp->getPeer() != 0) + { + const ScopedLock sl (contextLock); + + if (context != 0) + context->updateWindowPosition (getScreenX() - topComp->getScreenX(), + getScreenY() - topComp->getScreenY(), + getWidth(), + getHeight(), + topComp->getHeight()); + } + } +} + +const OpenGLPixelFormat OpenGLComponent::getPixelFormat() const +{ + OpenGLPixelFormat pf; + + const ScopedLock sl (contextLock); + if (context != 0) + pf = context->getPixelFormat(); + + return pf; +} + +void OpenGLComponent::setPixelFormat (const OpenGLPixelFormat& formatToUse) +{ + if (! (preferredPixelFormat == formatToUse)) + { + const ScopedLock sl (contextLock); + deleteContext(); + preferredPixelFormat = formatToUse; + } +} + +void OpenGLComponent::shareWith (OpenGLContext* context) +{ + if (contextToShareListsWith != context) + { + const ScopedLock sl (contextLock); + deleteContext(); + contextToShareListsWith = context; + } +} + +bool OpenGLComponent::makeCurrentContextActive() +{ + if (context == 0) + { + const ScopedLock sl (contextLock); + + if (isShowing() && getTopLevelComponent()->getPeer() != 0) + { + context = OpenGLContext::createContextForWindow (this, + preferredPixelFormat, + contextToShareListsWith); + + if (context != 0) + { + updateContextPosition(); + + if (context->makeActive()) + newOpenGLContextCreated(); + } + } + } + + return context != 0 && context->makeActive(); +} + +void OpenGLComponent::makeCurrentContextInactive() +{ + if (context != 0) + context->makeInactive(); +} + +bool OpenGLComponent::isActiveContext() const throw() +{ + return context != 0 && context->isActive(); +} + +void OpenGLComponent::swapBuffers() +{ + if (context != 0) + context->swapBuffers(); +} + +void OpenGLComponent::paint (Graphics&) +{ + if (renderAndSwapBuffers()) + { + ComponentPeer* const peer = getPeer(); + + if (peer != 0) + { + peer->addMaskedRegion (getScreenX() - peer->getScreenX(), + getScreenY() - peer->getScreenY(), + getWidth(), getHeight()); + } + } +} + +bool OpenGLComponent::renderAndSwapBuffers() +{ + const ScopedLock sl (contextLock); + + if (! makeCurrentContextActive()) + return false; + + if (needToUpdateViewport) + { + needToUpdateViewport = false; + juce_glViewport (getWidth(), getHeight()); + } + + renderOpenGL(); + swapBuffers(); + + return true; +} + +void OpenGLComponent::internalRepaint (int x, int y, int w, int h) +{ + Component::internalRepaint (x, y, w, h); + + if (context != 0) + context->repaint(); +} + +END_JUCE_NAMESPACE + +#endif +/********* End of inlined file: juce_OpenGLComponent.cpp *********/ + +/********* Start of inlined file: juce_PreferencesPanel.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +PreferencesPanel::PreferencesPanel() + : currentPage (0), + buttonSize (70) +{ +} + +PreferencesPanel::~PreferencesPanel() +{ + deleteAllChildren(); +} + +void PreferencesPanel::addSettingsPage (const String& title, + const Drawable* icon, + const Drawable* overIcon, + const Drawable* downIcon) +{ + DrawableButton* button = new DrawableButton (title, DrawableButton::ImageAboveTextLabel); + button->setImages (icon, overIcon, downIcon); + button->setRadioGroupId (1); + button->addButtonListener (this); + button->setClickingTogglesState (true); + button->setWantsKeyboardFocus (false); + addAndMakeVisible (button); + + resized(); +} + +void PreferencesPanel::addSettingsPage (const String& title, + const char* imageData, + const int imageDataSize) +{ + DrawableImage icon, iconOver, iconDown; + icon.setImage (ImageCache::getFromMemory (imageData, imageDataSize), true); + + iconOver.setImage (ImageCache::getFromMemory (imageData, imageDataSize), true); + iconOver.setOverlayColour (Colours::black.withAlpha (0.12f)); + + iconDown.setImage (ImageCache::getFromMemory (imageData, imageDataSize), true); + iconDown.setOverlayColour (Colours::black.withAlpha (0.25f)); + + addSettingsPage (title, &icon, &iconOver, &iconDown); + + if (currentPage == 0) + setCurrentPage (title); +} + +class PrefsDialogWindow : public DialogWindow +{ +public: + PrefsDialogWindow (const String& dialogtitle, + const Colour& backgroundColour) + : DialogWindow (dialogtitle, backgroundColour, true) + { + } + + ~PrefsDialogWindow() + { + } + + void closeButtonPressed() + { + exitModalState (0); + } + +private: + PrefsDialogWindow (const PrefsDialogWindow&); + const PrefsDialogWindow& operator= (const PrefsDialogWindow&); +}; + +void PreferencesPanel::showInDialogBox (const String& dialogtitle, + int dialogWidth, + int dialogHeight, + const Colour& backgroundColour) +{ + setSize (dialogWidth, dialogHeight); + + PrefsDialogWindow dw (dialogtitle, backgroundColour); + + dw.setContentComponent (this, true, true); + dw.centreAroundComponent (0, dw.getWidth(), dw.getHeight()); + dw.runModalLoop(); +} + +void PreferencesPanel::resized() +{ + int x = 0; + + for (int i = 0; i < getNumChildComponents(); ++i) + { + Component* c = getChildComponent (i); + + if (dynamic_cast (c) == 0) + { + c->setBounds (0, buttonSize + 5, getWidth(), getHeight() - buttonSize - 5); + } + else + { + c->setBounds (x, 0, buttonSize, buttonSize); + x += buttonSize; + } + } +} + +void PreferencesPanel::paint (Graphics& g) +{ + g.setColour (Colours::grey); + g.fillRect (0, buttonSize + 2, getWidth(), 1); +} + +void PreferencesPanel::setCurrentPage (const String& pageName) +{ + if (currentPageName != pageName) + { + currentPageName = pageName; + + deleteAndZero (currentPage); + currentPage = createComponentForPage (pageName); + + if (currentPage != 0) + { + addAndMakeVisible (currentPage); + currentPage->toBack(); + resized(); + } + + for (int i = 0; i < getNumChildComponents(); ++i) + { + DrawableButton* db = dynamic_cast (getChildComponent (i)); + + if (db != 0 && db->getName() == pageName) + { + db->setToggleState (true, false); + break; + } + } + } +} + +void PreferencesPanel::buttonClicked (Button*) +{ + for (int i = 0; i < getNumChildComponents(); ++i) + { + DrawableButton* db = dynamic_cast (getChildComponent (i)); + + if (db != 0 && db->getToggleState()) + { + setCurrentPage (db->getName()); + break; + } + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_PreferencesPanel.cpp *********/ + +/********* Start of inlined file: juce_QuickTimeMovieComponent.cpp *********/ + +#if JUCE_QUICKTIME + +#ifdef _MSC_VER + #pragma warning (disable: 4514) +#endif + +#ifdef _WIN32 + #include + + #ifdef _MSC_VER + #pragma warning (push) + #pragma warning (disable : 4100) + #endif + + /* If you've got an include error here, you probably need to install the QuickTime SDK and + add its header directory to your include path. + + Alternatively, if you don't need any QuickTime services, just turn off the JUC_QUICKTIME + flag in juce_Config.h + */ + #include + #include + #include + #include + #include + + #ifdef _MSC_VER + #pragma warning (pop) + #endif + + // If you've got QuickTime 7 installed, then these COM objects should be found in + // the "\Program Files\Quicktime" directory. You'll need to add this directory to + // your include search path to make these import statements work. + #import + #import + + using namespace QTOLibrary; + using namespace QTOControlLib; +#else + #include + #include + #include + #include +#endif + +BEGIN_JUCE_NAMESPACE + +bool juce_OpenQuickTimeMovieFromStream (InputStream* input, Movie& movie, Handle& dataHandle); + +static bool hasLoadedQT = false; +static bool isQTAvailable = false; + +struct QTMovieCompInternal +{ + QTMovieCompInternal() + : dataHandle (0) + { +#if JUCE_MAC + movie = 0; + controller = 0; +#endif + } + + ~QTMovieCompInternal() + { + clearHandle(); + } + +#if JUCE_MAC + Movie movie; + MovieController controller; +#else + IQTControlPtr qtControlInternal; + IQTMoviePtr qtMovieInternal; +#endif + + Handle dataHandle; + + void clearHandle() + { + if (dataHandle != 0) + { + DisposeHandle (dataHandle); + dataHandle = 0; + } + } +}; + +#if JUCE_WIN32 + +#define qtControl (((QTMovieCompInternal*) internal)->qtControlInternal) +#define qtMovie (((QTMovieCompInternal*) internal)->qtMovieInternal) + +QuickTimeMovieComponent::QuickTimeMovieComponent() + : movieLoaded (false), + controllerVisible (true) +{ + internal = new QTMovieCompInternal(); + setMouseEventsAllowed (false); +} + +QuickTimeMovieComponent::~QuickTimeMovieComponent() +{ + closeMovie(); + qtControl = 0; + + deleteControl(); + + delete internal; + internal = 0; +} + +bool QuickTimeMovieComponent::isQuickTimeAvailable() throw() +{ + if (! hasLoadedQT) + { + hasLoadedQT = true; + + isQTAvailable = (InitializeQTML (0) == noErr) + && (EnterMovies() == noErr); + } + + return isQTAvailable; +} + +void QuickTimeMovieComponent::createControlIfNeeded() +{ + if (isShowing() && ! isControlCreated()) + { + const IID qtIID = __uuidof (QTControl); + + if (createControl (&qtIID)) + { + const IID qtInterfaceIID = __uuidof (IQTControl); + qtControl = (IQTControl*) queryInterface (&qtInterfaceIID); + + if (qtControl != 0) + { + qtControl->Release(); // it has one ref too many at this point + + qtControl->QuickTimeInitialize(); + qtControl->PutSizing (qtMovieFitsControl); + + if (movieFile != File::nonexistent) + loadMovie (movieFile, controllerVisible); + } + } + } +} + +bool QuickTimeMovieComponent::isControlCreated() const +{ + return isControlOpen(); +} + +bool QuickTimeMovieComponent::loadMovie (InputStream* movieStream, + const bool isControllerVisible) +{ + movieFile = File::nonexistent; + movieLoaded = false; + qtMovie = 0; + controllerVisible = isControllerVisible; + createControlIfNeeded(); + + if (isControlCreated()) + { + if (qtControl != 0) + { + qtControl->Put_MovieHandle (0); + ((QTMovieCompInternal*) internal)->clearHandle(); + + Movie movie; + if (juce_OpenQuickTimeMovieFromStream (movieStream, movie, ((QTMovieCompInternal*) internal)->dataHandle)) + { + qtControl->Put_MovieHandle ((long) (pointer_sized_int) movie); + + qtMovie = qtControl->GetMovie(); + + if (qtMovie != 0) + qtMovie->PutMovieControllerType (isControllerVisible ? qtMovieControllerTypeStandard + : qtMovieControllerTypeNone); + } + + if (movie == 0) + ((QTMovieCompInternal*) internal)->clearHandle(); + } + + movieLoaded = (qtMovie != 0); + } + else + { + // You're trying to open a movie when the control hasn't yet been created, probably because + // you've not yet added this component to a Window and made the whole component hierarchy visible. + jassertfalse + } + + delete movieStream; + return movieLoaded; +} + +void QuickTimeMovieComponent::closeMovie() +{ + stop(); + movieFile = File::nonexistent; + movieLoaded = false; + qtMovie = 0; + + if (qtControl != 0) + qtControl->Put_MovieHandle (0); + + ((QTMovieCompInternal*) internal)->clearHandle(); +} + +const File QuickTimeMovieComponent::getCurrentMovieFile() const +{ + return movieFile; +} + +bool QuickTimeMovieComponent::isMovieOpen() const +{ + return movieLoaded; +} + +double QuickTimeMovieComponent::getMovieDuration() const +{ + if (qtMovie != 0) + return qtMovie->GetDuration() / (double) qtMovie->GetTimeScale(); + + return 0.0; +} + +void QuickTimeMovieComponent::getMovieNormalSize (int& width, int& height) const +{ + if (qtMovie != 0) + { + struct QTRECT r = qtMovie->GetNaturalRect(); + + width = r.right - r.left; + height = r.bottom - r.top; + } + else + { + width = height = 0; + } +} + +void QuickTimeMovieComponent::play() +{ + if (qtMovie != 0) + qtMovie->Play(); +} + +void QuickTimeMovieComponent::stop() +{ + if (qtMovie != 0) + qtMovie->Stop(); +} + +bool QuickTimeMovieComponent::isPlaying() const +{ + return qtMovie != 0 && qtMovie->GetRate() != 0.0f; +} + +void QuickTimeMovieComponent::setPosition (const double seconds) +{ + if (qtMovie != 0) + qtMovie->PutTime ((long) (seconds * qtMovie->GetTimeScale())); +} + +double QuickTimeMovieComponent::getPosition() const +{ + if (qtMovie != 0) + return qtMovie->GetTime() / (double) qtMovie->GetTimeScale(); + + return 0.0; +} + +void QuickTimeMovieComponent::setSpeed (const float newSpeed) +{ + if (qtMovie != 0) + qtMovie->PutRate (newSpeed); +} + +void QuickTimeMovieComponent::setMovieVolume (const float newVolume) +{ + if (qtMovie != 0) + qtMovie->PutAudioVolume (newVolume); +} + +float QuickTimeMovieComponent::getMovieVolume() const +{ + if (qtMovie != 0) + return qtMovie->GetAudioVolume(); + + return 0.0f; +} + +void QuickTimeMovieComponent::setLooping (const bool shouldLoop) +{ + if (qtMovie != 0) + qtMovie->PutLoop (shouldLoop); +} + +bool QuickTimeMovieComponent::isLooping() const +{ + return qtMovie != 0 && qtMovie->GetLoop(); +} + +bool QuickTimeMovieComponent::isControllerVisible() const +{ + return controllerVisible; +} + +void QuickTimeMovieComponent::parentHierarchyChanged() +{ + createControlIfNeeded(); + QTWinBaseClass::parentHierarchyChanged(); +} + +void QuickTimeMovieComponent::visibilityChanged() +{ + createControlIfNeeded(); + QTWinBaseClass::visibilityChanged(); +} + +void QuickTimeMovieComponent::paint (Graphics& g) +{ + if (! isControlCreated()) + g.fillAll (Colours::black); +} + +#endif + +#if JUCE_MAC + +static VoidArray activeQTWindows (2); + +struct MacClickEventData +{ + ::Point where; + long when; + long modifiers; +}; + +void OfferMouseClickToQuickTime (WindowRef window, + ::Point where, long when, long modifiers, + Component* topLevelComp) +{ + if (hasLoadedQT) + { + for (int i = activeQTWindows.size(); --i >= 0;) + { + QuickTimeMovieComponent* const qtw = (QuickTimeMovieComponent*) activeQTWindows[i]; + + if (qtw->isVisible() && topLevelComp->isParentOf (qtw)) + { + MacClickEventData data; + data.where = where; + data.when = when; + data.modifiers = modifiers; + + qtw->handleMCEvent (&data); + } + } + } +} + +QuickTimeMovieComponent::QuickTimeMovieComponent() + : internal (new QTMovieCompInternal()), + associatedWindow (0), + controllerVisible (false), + controllerAssignedToWindow (false), + reentrant (false) +{ + if (! hasLoadedQT) + { + hasLoadedQT = true; + isQTAvailable = EnterMovies() == noErr; + } + + setOpaque (true); + setVisible (true); + + activeQTWindows.add (this); +} + +QuickTimeMovieComponent::~QuickTimeMovieComponent() +{ + closeMovie(); + + activeQTWindows.removeValue ((void*) this); + + QTMovieCompInternal* const i = (QTMovieCompInternal*) internal; + delete i; + + if (activeQTWindows.size() == 0 && isQTAvailable) + { + isQTAvailable = false; + hasLoadedQT = false; + + ExitMovies(); + } +} + +bool QuickTimeMovieComponent::isQuickTimeAvailable() throw() +{ + if (! hasLoadedQT) + { + hasLoadedQT = true; + isQTAvailable = EnterMovies() == noErr; + } + + return isQTAvailable; +} + +bool QuickTimeMovieComponent::loadMovie (InputStream* movieStream, + const bool controllerVisible_) +{ + if (! isQTAvailable) + return false; + + closeMovie(); + movieFile = File::nonexistent; + + if (getPeer() == 0) + { + // To open a movie, this component must be visible inside a functioning window, so that + // the QT control can be assigned to the window. + jassertfalse + return false; + } + + controllerVisible = controllerVisible_; + + QTMovieCompInternal* const qmci = (QTMovieCompInternal*) internal; + + GrafPtr savedPort; + GetPort (&savedPort); + bool result = false; + + if (juce_OpenQuickTimeMovieFromStream (movieStream, qmci->movie, qmci->dataHandle)) + { + qmci->controller = 0; + + void* window = getWindowHandle(); + + if (window != associatedWindow && window != 0) + associatedWindow = window; + + assignMovieToWindow(); + + SetMovieActive (qmci->movie, true); + SetMovieProgressProc (qmci->movie, (MovieProgressUPP) -1, 0); + + startTimer (1000 / 50); // this needs to be quite a high frequency for smooth playback + result = true; + + repaint(); + } + + MacSetPort (savedPort); + + return result; +} + +void QuickTimeMovieComponent::closeMovie() +{ + stop(); + + QTMovieCompInternal* const qmci = (QTMovieCompInternal*) internal; + + if (qmci->controller != 0) + { + DisposeMovieController (qmci->controller); + qmci->controller = 0; + } + + if (qmci->movie != 0) + { + DisposeMovie (qmci->movie); + qmci->movie = 0; + } + + qmci->clearHandle(); + + stopTimer(); + movieFile = File::nonexistent; +} + +bool QuickTimeMovieComponent::isMovieOpen() const +{ + QTMovieCompInternal* const qmci = (QTMovieCompInternal*) internal; + return qmci->movie != 0 && qmci->controller != 0; +} + +const File QuickTimeMovieComponent::getCurrentMovieFile() const +{ + return movieFile; +} + +static GrafPtr getPortForWindow (void* window) +{ + if (window == 0) + return 0; + + return (GrafPtr) GetWindowPort ((WindowRef) window); +} + +void QuickTimeMovieComponent::assignMovieToWindow() +{ + if (reentrant) + return; + + reentrant = true; + + QTMovieCompInternal* const qmci = (QTMovieCompInternal*) internal; + if (qmci->controller != 0) + { + DisposeMovieController (qmci->controller); + qmci->controller = 0; + } + + controllerAssignedToWindow = false; + + void* window = getWindowHandle(); + GrafPtr port = getPortForWindow (window); + + if (port != 0) + { + GrafPtr savedPort; + GetPort (&savedPort); + + SetMovieGWorld (qmci->movie, (CGrafPtr) port, 0); + MacSetPort (port); + + Rect r; + r.top = 0; + r.left = 0; + r.right = (short) jmax (1, getWidth()); + r.bottom = (short) jmax (1, getHeight()); + SetMovieBox (qmci->movie, &r); + + // create the movie controller + qmci->controller = NewMovieController (qmci->movie, &r, + controllerVisible ? mcTopLeftMovie + : mcNotVisible); + + if (qmci->controller != 0) + { + MCEnableEditing (qmci->controller, true); + + MCDoAction (qmci->controller, mcActionSetUseBadge, (void*) false); + MCDoAction (qmci->controller, mcActionSetLoopIsPalindrome, (void*) false); + setLooping (looping); + + MCDoAction (qmci->controller, mcActionSetFlags, + (void*) (pointer_sized_int) (mcFlagSuppressMovieFrame | (controllerVisible ? 0 : (mcFlagSuppressStepButtons | mcFlagSuppressSpeakerButton)))); + + MCSetControllerBoundsRect (qmci->controller, &r); + + controllerAssignedToWindow = true; + + resized(); + } + + MacSetPort (savedPort); + } + else + { + SetMovieGWorld (qmci->movie, 0, 0); + } + + reentrant = false; +} + +void QuickTimeMovieComponent::play() +{ + QTMovieCompInternal* const qmci = (QTMovieCompInternal*) internal; + + if (qmci->movie != 0) + StartMovie (qmci->movie); +} + +void QuickTimeMovieComponent::stop() +{ + QTMovieCompInternal* const qmci = (QTMovieCompInternal*) internal; + + if (qmci->movie != 0) + StopMovie (qmci->movie); +} + +bool QuickTimeMovieComponent::isPlaying() const +{ + QTMovieCompInternal* const qmci = (QTMovieCompInternal*) internal; + + return qmci->movie != 0 && GetMovieRate (qmci->movie) != 0; +} + +void QuickTimeMovieComponent::setPosition (const double seconds) +{ + QTMovieCompInternal* const qmci = (QTMovieCompInternal*) internal; + + if (qmci->controller != 0) + { + TimeRecord time; + time.base = GetMovieTimeBase (qmci->movie); + time.scale = 100000; + const uint64 t = (uint64) (100000.0 * seconds); + time.value.lo = (UInt32) (t & 0xffffffff); + time.value.hi = (UInt32) (t >> 32); + + SetMovieTime (qmci->movie, &time); + timerCallback(); // to call MCIdle + } + else + { + jassertfalse // no movie is open, so can't set the position. + } +} + +double QuickTimeMovieComponent::getPosition() const +{ + QTMovieCompInternal* const qmci = (QTMovieCompInternal*) internal; + + if (qmci->movie != 0) + { + TimeRecord time; + GetMovieTime (qmci->movie, &time); + + return ((int64) (((uint64) time.value.hi << 32) | (uint64) time.value.lo)) + / (double) time.scale; + } + + return 0.0; +} + +void QuickTimeMovieComponent::setSpeed (const float newSpeed) +{ + QTMovieCompInternal* const qmci = (QTMovieCompInternal*) internal; + + if (qmci->movie != 0) + SetMovieRate (qmci->movie, (Fixed) (newSpeed * (Fixed) 0x00010000L)); +} + +double QuickTimeMovieComponent::getMovieDuration() const +{ + QTMovieCompInternal* const qmci = (QTMovieCompInternal*) internal; + + if (qmci->movie != 0) + return GetMovieDuration (qmci->movie) / (double) GetMovieTimeScale (qmci->movie); + + return 0.0; +} + +void QuickTimeMovieComponent::setLooping (const bool shouldLoop) +{ + QTMovieCompInternal* const qmci = (QTMovieCompInternal*) internal; + looping = shouldLoop; + + if (qmci->controller != 0) + MCDoAction (qmci->controller, mcActionSetLooping, (void*) shouldLoop); +} + +bool QuickTimeMovieComponent::isLooping() const +{ + return looping; +} + +void QuickTimeMovieComponent::setMovieVolume (const float newVolume) +{ + QTMovieCompInternal* const qmci = (QTMovieCompInternal*) internal; + + if (qmci->movie != 0) + SetMovieVolume (qmci->movie, jlimit ((short) 0, (short) 0x100, (short) (newVolume * 0x0100))); +} + +float QuickTimeMovieComponent::getMovieVolume() const +{ + QTMovieCompInternal* const qmci = (QTMovieCompInternal*) internal; + + if (qmci->movie != 0) + return jmax (0.0f, GetMovieVolume (qmci->movie) / (float) 0x0100); + + return 0.0f; +} + +void QuickTimeMovieComponent::getMovieNormalSize (int& width, int& height) const +{ + width = 0; + height = 0; + + QTMovieCompInternal* const qmci = (QTMovieCompInternal*) internal; + + if (qmci->movie != 0) + { + Rect r; + GetMovieNaturalBoundsRect (qmci->movie, &r); + width = r.right - r.left; + height = r.bottom - r.top; + } +} + +void QuickTimeMovieComponent::paint (Graphics& g) +{ + QTMovieCompInternal* const qmci = (QTMovieCompInternal*) internal; + + if (qmci->movie == 0 || qmci->controller == 0) + { + g.fillAll (Colours::black); + return; + } + + GrafPtr savedPort; + GetPort (&savedPort); + + MacSetPort (getPortForWindow (getWindowHandle())); + MCDraw (qmci->controller, (WindowRef) getWindowHandle()); + + MacSetPort (savedPort); + + ComponentPeer* const peer = getPeer(); + + if (peer != 0) + { + peer->addMaskedRegion (getScreenX() - peer->getScreenX(), + getScreenY() - peer->getScreenY(), + getWidth(), getHeight()); + } + + timerCallback(); +} + +static const Rectangle getMoviePos (Component* const c) +{ + return Rectangle (c->getScreenX() - c->getTopLevelComponent()->getScreenX(), + c->getScreenY() - c->getTopLevelComponent()->getScreenY(), + jmax (1, c->getWidth()), + jmax (1, c->getHeight())); +} + +void QuickTimeMovieComponent::moved() +{ + resized(); +} + +void QuickTimeMovieComponent::resized() +{ + QTMovieCompInternal* const qmci = (QTMovieCompInternal*) internal; + + if (qmci->controller != 0 && isShowing()) + { + checkWindowAssociation(); + + GrafPtr port = getPortForWindow (getWindowHandle()); + + if (port != 0) + { + GrafPtr savedPort; + GetPort (&savedPort); + + SetMovieGWorld (qmci->movie, (CGrafPtr) port, 0); + MacSetPort (port); + + lastPositionApplied = getMoviePos (this); + + Rect r; + r.left = (short) lastPositionApplied.getX(); + r.top = (short) lastPositionApplied.getY(); + r.right = (short) lastPositionApplied.getRight(); + r.bottom = (short) lastPositionApplied.getBottom(); + + if (MCGetVisible (qmci->controller)) + MCSetControllerBoundsRect (qmci->controller, &r); + else + SetMovieBox (qmci->movie, &r); + + if (! isPlaying()) + timerCallback(); + + MacSetPort (savedPort); + + repaint(); + } + } +} + +void QuickTimeMovieComponent::visibilityChanged() +{ + checkWindowAssociation(); + QTWinBaseClass::visibilityChanged(); +} + +void QuickTimeMovieComponent::timerCallback() +{ + QTMovieCompInternal* const qmci = (QTMovieCompInternal*) internal; + + if (qmci->controller != 0) + { + if (isTimerRunning()) + startTimer (getTimerInterval()); + + MCIdle (qmci->controller); + + if (lastPositionApplied != getMoviePos (this)) + resized(); + } +} + +void QuickTimeMovieComponent::checkWindowAssociation() +{ + void* const window = getWindowHandle(); + + if (window != associatedWindow + || (window != 0 && ! controllerAssignedToWindow)) + { + associatedWindow = window; + assignMovieToWindow(); + } +} + +void QuickTimeMovieComponent::parentHierarchyChanged() +{ + checkWindowAssociation(); +} + +void QuickTimeMovieComponent::handleMCEvent (void* ev) +{ + QTMovieCompInternal* const qmci = (QTMovieCompInternal*) internal; + + if (qmci->controller != 0 && isShowing()) + { + MacClickEventData* data = (MacClickEventData*) ev; + + data->where.h -= getTopLevelComponent()->getScreenX(); + data->where.v -= getTopLevelComponent()->getScreenY(); + + Boolean b = false; + MCPtInController (qmci->controller, data->where, &b); + + if (b) + { + const int oldTimeBeforeWaitCursor = MessageManager::getInstance()->getTimeBeforeShowingWaitCursor(); + MessageManager::getInstance()->setTimeBeforeShowingWaitCursor (0); + + MCClick (qmci->controller, + (WindowRef) getWindowHandle(), + data->where, + data->when, + data->modifiers); + + MessageManager::getInstance()->setTimeBeforeShowingWaitCursor (oldTimeBeforeWaitCursor); + } + } +} + +#endif + +// (methods common to both platforms..) + +static Handle createHandleDataRef (Handle dataHandle, const char* fileName) +{ + Handle dataRef = 0; + OSStatus err = PtrToHand (&dataHandle, &dataRef, sizeof (Handle)); + if (err == noErr) + { + Str255 suffix; + + CharacterFunctions::copy ((char*) suffix, fileName, 128); + + StringPtr name = suffix; + err = PtrAndHand (name, dataRef, name[0] + 1); + + if (err == noErr) + { + long atoms[3]; + atoms[0] = EndianU32_NtoB (3 * sizeof (long)); + atoms[1] = EndianU32_NtoB (kDataRefExtensionMacOSFileType); + atoms[2] = EndianU32_NtoB (MovieFileType); + + err = PtrAndHand (atoms, dataRef, 3 * sizeof (long)); + + if (err == noErr) + return dataRef; + } + + DisposeHandle (dataRef); + } + + return 0; +} + +static CFStringRef juceStringToCFString (const String& s) +{ + const int len = s.length(); + const juce_wchar* const t = (const juce_wchar*) s; + + UniChar* temp = (UniChar*) juce_malloc (sizeof (UniChar) * len + 4); + + for (int i = 0; i <= len; ++i) + temp[i] = t[i]; + + CFStringRef result = CFStringCreateWithCharacters (kCFAllocatorDefault, temp, len); + juce_free (temp); + + return result; +} + +static bool openMovie (QTNewMoviePropertyElement* props, int prop, Movie& movie) +{ + Boolean trueBool = true; + props[prop].propClass = kQTPropertyClass_MovieInstantiation; + props[prop].propID = kQTMovieInstantiationPropertyID_DontResolveDataRefs; + props[prop].propValueSize = sizeof (trueBool); + props[prop].propValueAddress = &trueBool; + ++prop; + + props[prop].propClass = kQTPropertyClass_MovieInstantiation; + props[prop].propID = kQTMovieInstantiationPropertyID_AsyncOK; + props[prop].propValueSize = sizeof (trueBool); + props[prop].propValueAddress = &trueBool; + ++prop; + + Boolean isActive = true; + props[prop].propClass = kQTPropertyClass_NewMovieProperty; + props[prop].propID = kQTNewMoviePropertyID_Active; + props[prop].propValueSize = sizeof (isActive); + props[prop].propValueAddress = &isActive; + ++prop; + +#if JUCE_MAC + SetPort (0); +#else + MacSetPort (0); +#endif + + jassert (prop <= 5); + OSStatus err = NewMovieFromProperties (prop, props, 0, 0, &movie); + + return err == noErr; +} + +bool juce_OpenQuickTimeMovieFromStream (InputStream* input, Movie& movie, Handle& dataHandle) +{ + if (input == 0) + return false; + + dataHandle = 0; + bool ok = false; + + QTNewMoviePropertyElement props[5]; + zeromem (props, sizeof (props)); + int prop = 0; + + DataReferenceRecord dr; + props[prop].propClass = kQTPropertyClass_DataLocation; + props[prop].propID = kQTDataLocationPropertyID_DataReference; + props[prop].propValueSize = sizeof (dr); + props[prop].propValueAddress = (void*) &dr; + ++prop; + + FileInputStream* const fin = dynamic_cast (input); + + if (fin != 0) + { + CFStringRef filePath = juceStringToCFString (fin->getFile().getFullPathName()); + + QTNewDataReferenceFromFullPathCFString (filePath, (QTPathStyle) kQTNativeDefaultPathStyle, 0, + &dr.dataRef, &dr.dataRefType); + + ok = openMovie (props, prop, movie); + + DisposeHandle (dr.dataRef); + CFRelease (filePath); + } + else + { + // sanity-check because this currently needs to load the whole stream into memory.. + jassert (input->getTotalLength() < 50 * 1024 * 1024); + + dataHandle = NewHandle ((Size) input->getTotalLength()); + HLock (dataHandle); + // read the entire stream into memory - this is a pain, but can't get it to work + // properly using a custom callback to supply the data. + input->read (*dataHandle, (int) input->getTotalLength()); + HUnlock (dataHandle); + + // different types to get QT to try. (We should really be a bit smarter here by + // working out in advance which one the stream contains, rather than just trying + // each one) + const char* const suffixesToTry[] = { "\04.mov", "\04.mp3", + "\04.avi", "\04.m4a" }; + + for (int i = 0; i < numElementsInArray (suffixesToTry) && ! ok; ++i) + { + /* // this fails for some bizarre reason - it can be bodged to work with + // movies, but can't seem to do it for other file types.. + QTNewMovieUserProcRecord procInfo; + procInfo.getMovieUserProc = NewGetMovieUPP (readMovieStreamProc); + procInfo.getMovieUserProcRefcon = this; + procInfo.defaultDataRef.dataRef = dataRef; + procInfo.defaultDataRef.dataRefType = HandleDataHandlerSubType; + + props[prop].propClass = kQTPropertyClass_DataLocation; + props[prop].propID = kQTDataLocationPropertyID_MovieUserProc; + props[prop].propValueSize = sizeof (procInfo); + props[prop].propValueAddress = (void*) &procInfo; + ++prop; */ + + dr.dataRef = createHandleDataRef (dataHandle, suffixesToTry [i]); + dr.dataRefType = HandleDataHandlerSubType; + ok = openMovie (props, prop, movie); + + DisposeHandle (dr.dataRef); + } + } + + return ok; +} + +bool QuickTimeMovieComponent::loadMovie (const File& movieFile_, + const bool isControllerVisible) +{ + const bool ok = loadMovie ((InputStream*) movieFile_.createInputStream(), isControllerVisible); + movieFile = movieFile_; + return ok; +} + +void QuickTimeMovieComponent::goToStart() +{ + setPosition (0.0); +} + +void QuickTimeMovieComponent::setBoundsWithCorrectAspectRatio (const Rectangle& spaceToFitWithin, + const RectanglePlacement& placement) +{ + int normalWidth, normalHeight; + getMovieNormalSize (normalWidth, normalHeight); + + if (normalWidth > 0 && normalHeight > 0 && ! spaceToFitWithin.isEmpty()) + { + double x = 0.0, y = 0.0, w = normalWidth, h = normalHeight; + + placement.applyTo (x, y, w, h, + spaceToFitWithin.getX(), spaceToFitWithin.getY(), + spaceToFitWithin.getWidth(), spaceToFitWithin.getHeight()); + + if (w > 0 && h > 0) + { + setBounds (roundDoubleToInt (x), roundDoubleToInt (y), + roundDoubleToInt (w), roundDoubleToInt (h)); + } + } + else + { + setBounds (spaceToFitWithin); + } +} + +END_JUCE_NAMESPACE + +#endif +/********* End of inlined file: juce_QuickTimeMovieComponent.cpp *********/ + +/********* Start of inlined file: juce_SystemTrayIconComponent.cpp *********/ + +#if JUCE_WIN32 || JUCE_LINUX + +BEGIN_JUCE_NAMESPACE + +SystemTrayIconComponent::SystemTrayIconComponent() +{ + addToDesktop (0); +} + +SystemTrayIconComponent::~SystemTrayIconComponent() +{ +} + +END_JUCE_NAMESPACE + +#endif +/********* End of inlined file: juce_SystemTrayIconComponent.cpp *********/ + +/********* Start of inlined file: juce_AlertWindow.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +static const int titleH = 24; +static const int iconWidth = 80; + +class AlertWindowTextEditor : public TextEditor +{ +public: + #if JUCE_LINUX + #define PASSWORD_CHAR 0x2022 + #else + #define PASSWORD_CHAR 0x25cf + #endif + + AlertWindowTextEditor (const String& name, + const bool isPasswordBox) + : TextEditor (name, + isPasswordBox ? (const tchar) PASSWORD_CHAR + : (const tchar) 0) + { + setSelectAllWhenFocused (true); + } + + ~AlertWindowTextEditor() + { + } + + void returnPressed() + { + // pass these up the component hierarchy to be trigger the buttons + getParentComponent()->keyPressed (KeyPress (KeyPress::returnKey, 0, T('\n'))); + } + + void escapePressed() + { + // pass these up the component hierarchy to be trigger the buttons + getParentComponent()->keyPressed (KeyPress (KeyPress::escapeKey, 0, 0)); + } + +private: + AlertWindowTextEditor (const AlertWindowTextEditor&); + const AlertWindowTextEditor& operator= (const AlertWindowTextEditor&); +}; + +AlertWindow::AlertWindow (const String& title, + const String& message, + AlertIconType iconType) + : TopLevelWindow (title, true), + alertIconType (iconType) +{ + if (message.isEmpty()) + text = T(" "); // to force an update if the message is empty + + setMessage (message); + +#if JUCE_MAC + setAlwaysOnTop (true); +#else + for (int i = Desktop::getInstance().getNumComponents(); --i >= 0;) + { + Component* const c = Desktop::getInstance().getComponent (i); + + if (c != 0 && c->isAlwaysOnTop() && c->isShowing()) + { + setAlwaysOnTop (true); + break; + } + } +#endif + + lookAndFeelChanged(); + + constrainer.setMinimumOnscreenAmounts (0x10000, 0x10000, 0x10000, 0x10000); +} + +AlertWindow::~AlertWindow() +{ + for (int i = customComps.size(); --i >= 0;) + removeChildComponent ((Component*) customComps[i]); + + deleteAllChildren(); +} + +void AlertWindow::setMessage (const String& message) +{ + const String newMessage (message.substring (0, 2048)); + + if (text != newMessage) + { + text = newMessage; + + font.setHeight (15.0f); + + Font titleFont (font.getHeight() * 1.1f, Font::bold); + textLayout.setText (getName() + T("\n\n"), titleFont); + + textLayout.appendText (text, font); + + updateLayout (true); + repaint(); + } +} + +void AlertWindow::buttonClicked (Button* button) +{ + for (int i = 0; i < buttons.size(); i++) + { + TextButton* const c = (TextButton*) buttons[i]; + + if (button->getName() == c->getName()) + { + if (c->getParentComponent() != 0) + c->getParentComponent()->exitModalState (c->getCommandID()); + + break; + } + } +} + +void AlertWindow::addButton (const String& name, + const int returnValue, + const KeyPress& shortcutKey1, + const KeyPress& shortcutKey2) +{ + TextButton* const b = new TextButton (name, String::empty); + + b->setWantsKeyboardFocus (true); + b->setMouseClickGrabsKeyboardFocus (false); + b->setCommandToTrigger (0, returnValue, false); + b->addShortcut (shortcutKey1); + b->addShortcut (shortcutKey2); + b->addButtonListener (this); + b->changeWidthToFitText (28); + + addAndMakeVisible (b, 0); + buttons.add (b); + + updateLayout (false); +} + +int AlertWindow::getNumButtons() const +{ + return buttons.size(); +} + +void AlertWindow::addTextEditor (const String& name, + const String& initialContents, + const String& onScreenLabel, + const bool isPasswordBox) +{ + AlertWindowTextEditor* const tc = new AlertWindowTextEditor (name, isPasswordBox); + + tc->setColour (TextEditor::outlineColourId, findColour (ComboBox::outlineColourId)); + tc->setFont (font); + tc->setText (initialContents); + tc->setCaretPosition (initialContents.length()); + addAndMakeVisible (tc); + textBoxes.add (tc); + allComps.add (tc); + textboxNames.add (onScreenLabel); + + updateLayout (false); +} + +const String AlertWindow::getTextEditorContents (const String& nameOfTextEditor) const +{ + for (int i = textBoxes.size(); --i >= 0;) + if (((TextEditor*)textBoxes[i])->getName() == nameOfTextEditor) + return ((TextEditor*)textBoxes[i])->getText(); + + return String::empty; +} + +void AlertWindow::addComboBox (const String& name, + const StringArray& items, + const String& onScreenLabel) +{ + ComboBox* const cb = new ComboBox (name); + + for (int i = 0; i < items.size(); ++i) + cb->addItem (items[i], i + 1); + + addAndMakeVisible (cb); + cb->setSelectedItemIndex (0); + + comboBoxes.add (cb); + allComps.add (cb); + + comboBoxNames.add (onScreenLabel); + + updateLayout (false); +} + +ComboBox* AlertWindow::getComboBoxComponent (const String& nameOfList) const +{ + for (int i = comboBoxes.size(); --i >= 0;) + if (((ComboBox*) comboBoxes[i])->getName() == nameOfList) + return (ComboBox*) comboBoxes[i]; + + return 0; +} + +class AlertTextComp : public TextEditor +{ + AlertTextComp (const AlertTextComp&); + const AlertTextComp& operator= (const AlertTextComp&); + + int bestWidth; + +public: + AlertTextComp (const String& message, + const Font& font) + { + setReadOnly (true); + setMultiLine (true, true); + setCaretVisible (false); + setScrollbarsShown (true); + lookAndFeelChanged(); + setWantsKeyboardFocus (false); + + setFont (font); + setText (message, false); + + bestWidth = 2 * (int) sqrt (font.getHeight() * font.getStringWidth (message)); + + setColour (TextEditor::backgroundColourId, Colours::transparentBlack); + setColour (TextEditor::outlineColourId, Colours::transparentBlack); + setColour (TextEditor::shadowColourId, Colours::transparentBlack); + } + + ~AlertTextComp() + { + } + + int getPreferredWidth() const throw() { return bestWidth; } + + void updateLayout (const int width) + { + TextLayout text; + text.appendText (getText(), getFont()); + text.layout (width - 8, Justification::topLeft, true); + setSize (width, jmin (width, text.getHeight() + (int) getFont().getHeight())); + } +}; + +void AlertWindow::addTextBlock (const String& text) +{ + AlertTextComp* const c = new AlertTextComp (text, font); + + textBlocks.add (c); + allComps.add (c); + + addAndMakeVisible (c); + + updateLayout (false); +} + +void AlertWindow::addProgressBarComponent (double& progressValue) +{ + ProgressBar* const pb = new ProgressBar (progressValue); + + progressBars.add (pb); + allComps.add (pb); + + addAndMakeVisible (pb); + + updateLayout (false); +} + +void AlertWindow::addCustomComponent (Component* const component) +{ + customComps.add (component); + allComps.add (component); + + addAndMakeVisible (component); + + updateLayout (false); +} + +int AlertWindow::getNumCustomComponents() const +{ + return customComps.size(); +} + +Component* AlertWindow::getCustomComponent (const int index) const +{ + return (Component*) customComps [index]; +} + +Component* AlertWindow::removeCustomComponent (const int index) +{ + Component* const c = getCustomComponent (index); + + if (c != 0) + { + customComps.removeValue (c); + allComps.removeValue (c); + removeChildComponent (c); + + updateLayout (false); + } + + return c; +} + +void AlertWindow::paint (Graphics& g) +{ + getLookAndFeel().drawAlertBox (g, *this, textArea, textLayout); + + g.setColour (Colours::black); + g.setFont (12.0f); + + int i; + for (i = textBoxes.size(); --i >= 0;) + { + if (textboxNames[i].isNotEmpty()) + { + const TextEditor* const te = (TextEditor*) textBoxes[i]; + + g.drawFittedText (textboxNames[i], + te->getX(), te->getY() - 14, + te->getWidth(), 14, + Justification::centredLeft, 1); + } + } + + for (i = comboBoxNames.size(); --i >= 0;) + { + if (comboBoxNames[i].isNotEmpty()) + { + const ComboBox* const cb = (ComboBox*) comboBoxes[i]; + + g.drawFittedText (comboBoxNames[i], + cb->getX(), cb->getY() - 14, + cb->getWidth(), 14, + Justification::centredLeft, 1); + } + } +} + +void AlertWindow::updateLayout (const bool onlyIncreaseSize) +{ + const int wid = jmax (font.getStringWidth (text), + font.getStringWidth (getName())); + + const int sw = (int) sqrt (font.getHeight() * wid); + int w = jmin (300 + sw * 2, (int) (getParentWidth() * 0.7f)); + const int edgeGap = 10; + int iconSpace; + + if (alertIconType == NoIcon) + { + textLayout.layout (w, Justification::horizontallyCentred, true); + iconSpace = 0; + } + else + { + textLayout.layout (w, Justification::left, true); + iconSpace = iconWidth; + } + + w = jmax (350, textLayout.getWidth() + iconSpace + edgeGap * 4); + w = jmin (w, (int) (getParentWidth() * 0.7f)); + + const int textLayoutH = textLayout.getHeight(); + const int textBottom = 16 + titleH + textLayoutH; + int h = textBottom; + + int buttonW = 40; + int i; + for (i = 0; i < buttons.size(); ++i) + buttonW += 16 + ((const TextButton*) buttons[i])->getWidth(); + + w = jmax (buttonW, w); + + h += (textBoxes.size() + comboBoxes.size() + progressBars.size()) * 50; + + if (buttons.size() > 0) + h += 20 + ((TextButton*) buttons[0])->getHeight(); + + for (i = customComps.size(); --i >= 0;) + { + w = jmax (w, ((Component*) customComps[i])->getWidth() + 40); + h += 10 + ((Component*) customComps[i])->getHeight(); + } + + for (i = textBlocks.size(); --i >= 0;) + { + const AlertTextComp* const ac = (AlertTextComp*) textBlocks[i]; + w = jmax (w, ac->getPreferredWidth()); + } + + w = jmin (w, (int) (getParentWidth() * 0.7f)); + + for (i = textBlocks.size(); --i >= 0;) + { + AlertTextComp* const ac = (AlertTextComp*) textBlocks[i]; + ac->updateLayout ((int) (w * 0.8f)); + h += ac->getHeight() + 10; + } + + h = jmin (getParentHeight() - 50, h); + + if (onlyIncreaseSize) + { + w = jmax (w, getWidth()); + h = jmax (h, getHeight()); + } + + if (! isVisible()) + { + centreAroundComponent (0, w, h); + } + else + { + const int cx = getX() + getWidth() / 2; + const int cy = getY() + getHeight() / 2; + + setBounds (cx - w / 2, + cy - h / 2, + w, h); + } + + textArea.setBounds (edgeGap, edgeGap, w - (edgeGap * 2), h - edgeGap); + + const int spacer = 16; + int totalWidth = -spacer; + + for (i = buttons.size(); --i >= 0;) + totalWidth += ((TextButton*) buttons[i])->getWidth() + spacer; + + int x = (w - totalWidth) / 2; + int y = (int) (getHeight() * 0.95f); + + for (i = 0; i < buttons.size(); ++i) + { + TextButton* const c = (TextButton*) buttons[i]; + int ny = proportionOfHeight (0.95f) - c->getHeight(); + c->setTopLeftPosition (x, ny); + if (ny < y) + y = ny; + + x += c->getWidth() + spacer; + + c->toFront (false); + } + + y = textBottom; + + for (i = 0; i < allComps.size(); ++i) + { + Component* const c = (Component*) allComps[i]; + + const int h = 22; + + const int comboIndex = comboBoxes.indexOf (c); + if (comboIndex >= 0 && comboBoxNames [comboIndex].isNotEmpty()) + y += 18; + + const int tbIndex = textBoxes.indexOf (c); + if (tbIndex >= 0 && textboxNames[tbIndex].isNotEmpty()) + y += 18; + + if (customComps.contains (c) || textBlocks.contains (c)) + { + c->setTopLeftPosition ((getWidth() - c->getWidth()) / 2, y); + y += c->getHeight() + 10; + } + else + { + c->setBounds (proportionOfWidth (0.1f), y, proportionOfWidth (0.8f), h); + y += h + 10; + } + } + + setWantsKeyboardFocus (getNumChildComponents() == 0); +} + +bool AlertWindow::containsAnyExtraComponents() const +{ + return textBoxes.size() + + comboBoxes.size() + + progressBars.size() + + customComps.size() > 0; +} + +void AlertWindow::mouseDown (const MouseEvent&) +{ + dragger.startDraggingComponent (this, &constrainer); +} + +void AlertWindow::mouseDrag (const MouseEvent& e) +{ + dragger.dragComponent (this, e); +} + +bool AlertWindow::keyPressed (const KeyPress& key) +{ + for (int i = buttons.size(); --i >= 0;) + { + TextButton* const b = (TextButton*) buttons[i]; + + if (b->isRegisteredForShortcut (key)) + { + b->triggerClick(); + return true; + } + } + + if (key.isKeyCode (KeyPress::escapeKey) && buttons.size() == 0) + { + exitModalState (0); + return true; + } + else if (key.isKeyCode (KeyPress::returnKey) && buttons.size() == 1) + { + ((TextButton*) buttons.getFirst())->triggerClick(); + return true; + } + + return false; +} + +void AlertWindow::lookAndFeelChanged() +{ + const int flags = getLookAndFeel().getAlertBoxWindowFlags(); + + setUsingNativeTitleBar ((flags & ComponentPeer::windowHasTitleBar) != 0); + setDropShadowEnabled ((flags & ComponentPeer::windowHasDropShadow) != 0); +} + +struct AlertWindowInfo +{ + String title, message, button1, button2, button3; + AlertWindow::AlertIconType iconType; + int numButtons; + + int run() const + { + return (int) (pointer_sized_int) + MessageManager::getInstance()->callFunctionOnMessageThread (showCallback, (void*) this); + } + +private: + int show() const + { + AlertWindow aw (title, message, iconType); + + if (numButtons == 1) + { + aw.addButton (button1, 0, + KeyPress (KeyPress::escapeKey, 0, 0), + KeyPress (KeyPress::returnKey, 0, 0)); + } + else + { + const KeyPress button1ShortCut (CharacterFunctions::toLowerCase (button1[0]), 0, 0); + KeyPress button2ShortCut (CharacterFunctions::toLowerCase (button2[0]), 0, 0); + if (button1ShortCut == button2ShortCut) + button2ShortCut = KeyPress(); + + if (numButtons == 2) + { + aw.addButton (button1, 1, KeyPress (KeyPress::returnKey, 0, 0), button1ShortCut); + aw.addButton (button2, 0, KeyPress (KeyPress::escapeKey, 0, 0), button2ShortCut); + } + else + { + jassert (numButtons == 3); + + aw.addButton (button1, 1, button1ShortCut); + aw.addButton (button2, 2, button2ShortCut); + aw.addButton (button3, 0, KeyPress (KeyPress::escapeKey, 0, 0)); + } + } + + return aw.runModalLoop(); + } + + static void* showCallback (void* userData) + { + return (void*) (pointer_sized_int) ((const AlertWindowInfo*) userData)->show(); + } +}; + +void AlertWindow::showMessageBox (AlertIconType iconType, + const String& title, + const String& message, + const String& buttonText) +{ + AlertWindowInfo info; + info.title = title; + info.message = message; + info.button1 = buttonText.isEmpty() ? TRANS("ok") : buttonText; + info.iconType = iconType; + info.numButtons = 1; + + info.run(); +} + +bool AlertWindow::showOkCancelBox (AlertIconType iconType, + const String& title, + const String& message, + const String& button1Text, + const String& button2Text) +{ + AlertWindowInfo info; + info.title = title; + info.message = message; + info.button1 = button1Text.isEmpty() ? TRANS("ok") : button1Text; + info.button2 = button2Text.isEmpty() ? TRANS("cancel") : button2Text; + info.iconType = iconType; + info.numButtons = 2; + + return info.run() != 0; +} + +int AlertWindow::showYesNoCancelBox (AlertIconType iconType, + const String& title, + const String& message, + const String& button1Text, + const String& button2Text, + const String& button3Text) +{ + AlertWindowInfo info; + info.title = title; + info.message = message; + info.button1 = button1Text.isEmpty() ? TRANS("yes") : button1Text; + info.button2 = button2Text.isEmpty() ? TRANS("no") : button2Text; + info.button3 = button3Text.isEmpty() ? TRANS("cancel") : button3Text; + info.iconType = iconType; + info.numButtons = 3; + + return info.run(); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_AlertWindow.cpp *********/ + +/********* Start of inlined file: juce_ComponentPeer.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +//#define JUCE_ENABLE_REPAINT_DEBUGGING 1 + +// these are over in juce_component.cpp +extern int64 juce_recentMouseDownTimes[4]; +extern int juce_recentMouseDownX [4]; +extern int juce_recentMouseDownY [4]; +extern Component* juce_recentMouseDownComponent [4]; +extern int juce_LastMousePosX; +extern int juce_LastMousePosY; +extern int juce_MouseClickCounter; +extern bool juce_MouseHasMovedSignificantlySincePressed; + +static const int fakeMouseMoveMessage = 0x7fff00ff; + +static VoidArray heavyweightPeers (4); + +ComponentPeer::ComponentPeer (Component* const component_, + const int styleFlags_) throw() + : component (component_), + styleFlags (styleFlags_), + lastPaintTime (0), + constrainer (0), + lastFocusedComponent (0), + dragAndDropTargetComponent (0), + lastDragAndDropCompUnderMouse (0), + fakeMouseMessageSent (false), + isWindowMinimised (false) +{ + heavyweightPeers.add (this); +} + +ComponentPeer::~ComponentPeer() +{ + heavyweightPeers.removeValue (this); + delete dragAndDropTargetComponent; +} + +int ComponentPeer::getNumPeers() throw() +{ + return heavyweightPeers.size(); +} + +ComponentPeer* ComponentPeer::getPeer (const int index) throw() +{ + return (ComponentPeer*) heavyweightPeers [index]; +} + +ComponentPeer* ComponentPeer::getPeerFor (const Component* const component) throw() +{ + for (int i = heavyweightPeers.size(); --i >= 0;) + { + ComponentPeer* const peer = (ComponentPeer*) heavyweightPeers.getUnchecked(i); + + if (peer->getComponent() == component) + return peer; + } + + return 0; +} + +bool ComponentPeer::isValidPeer (const ComponentPeer* const peer) throw() +{ + return heavyweightPeers.contains (const_cast (peer)); +} + +void ComponentPeer::updateCurrentModifiers() throw() +{ + ModifierKeys::updateCurrentModifiers(); +} + +void ComponentPeer::handleMouseEnter (int x, int y, const int64 time) +{ + jassert (component->isValidComponent()); + updateCurrentModifiers(); + + Component* c = component->getComponentAt (x, y); + const ComponentDeletionWatcher deletionChecker (component); + + if (c != Component::componentUnderMouse && Component::componentUnderMouse != 0) + { + jassert (Component::componentUnderMouse->isValidComponent()); + + const int oldX = x; + const int oldY = y; + component->relativePositionToOtherComponent (Component::componentUnderMouse, x, y); + Component::componentUnderMouse->internalMouseExit (x, y, time); + Component::componentUnderMouse = 0; + + if (deletionChecker.hasBeenDeleted()) + return; + + c = component->getComponentAt (oldX, oldY); + } + + Component::componentUnderMouse = c; + + if (Component::componentUnderMouse != 0) + { + component->relativePositionToOtherComponent (Component::componentUnderMouse, x, y); + Component::componentUnderMouse->internalMouseEnter (x, y, time); + } +} + +void ComponentPeer::handleMouseMove (int x, int y, const int64 time) +{ + jassert (component->isValidComponent()); + updateCurrentModifiers(); + + fakeMouseMessageSent = false; + + const ComponentDeletionWatcher deletionChecker (component); + Component* c = component->getComponentAt (x, y); + + if (c != Component::componentUnderMouse) + { + const int oldX = x; + const int oldY = y; + + if (Component::componentUnderMouse != 0) + { + component->relativePositionToOtherComponent (Component::componentUnderMouse, x, y); + Component::componentUnderMouse->internalMouseExit (x, y, time); + x = oldX; + y = oldY; + + Component::componentUnderMouse = 0; + + if (deletionChecker.hasBeenDeleted()) + return; // if this window has just been deleted.. + + c = component->getComponentAt (x, y); + } + + Component::componentUnderMouse = c; + + if (c != 0) + { + component->relativePositionToOtherComponent (c, x, y); + c->internalMouseEnter (x, y, time); + x = oldX; + y = oldY; + + if (deletionChecker.hasBeenDeleted()) + return; // if this window has just been deleted.. + } + } + + if (Component::componentUnderMouse != 0) + { + component->relativePositionToOtherComponent (Component::componentUnderMouse, x, y); + Component::componentUnderMouse->internalMouseMove (x, y, time); + } +} + +void ComponentPeer::handleMouseDown (int x, int y, const int64 time) +{ + ++juce_MouseClickCounter; + + updateCurrentModifiers(); + + int numMouseButtonsDown = 0; + + if (ModifierKeys::getCurrentModifiers().isLeftButtonDown()) + ++numMouseButtonsDown; + + if (ModifierKeys::getCurrentModifiers().isRightButtonDown()) + ++numMouseButtonsDown; + + if (ModifierKeys::getCurrentModifiers().isMiddleButtonDown()) + ++numMouseButtonsDown; + + if (numMouseButtonsDown == 1) + { + Component::componentUnderMouse = component->getComponentAt (x, y); + + if (Component::componentUnderMouse != 0) + { + // can't set these in the mouseDownInt() method, because it's re-entrant, so do it here.. + + for (int i = numElementsInArray (juce_recentMouseDownTimes); --i > 0;) + { + juce_recentMouseDownTimes [i] = juce_recentMouseDownTimes [i - 1]; + juce_recentMouseDownX [i] = juce_recentMouseDownX [i - 1]; + juce_recentMouseDownY [i] = juce_recentMouseDownY [i - 1]; + juce_recentMouseDownComponent [i] = juce_recentMouseDownComponent [i - 1]; + } + + juce_recentMouseDownTimes[0] = time; + juce_recentMouseDownX[0] = x; + juce_recentMouseDownY[0] = y; + juce_recentMouseDownComponent[0] = Component::componentUnderMouse; + relativePositionToGlobal (juce_recentMouseDownX[0], juce_recentMouseDownY[0]); + juce_MouseHasMovedSignificantlySincePressed = false; + + component->relativePositionToOtherComponent (Component::componentUnderMouse, x, y); + Component::componentUnderMouse->internalMouseDown (x, y); + } + } +} + +void ComponentPeer::handleMouseDrag (int x, int y, const int64 time) +{ + updateCurrentModifiers(); + + if (Component::componentUnderMouse != 0) + { + component->relativePositionToOtherComponent (Component::componentUnderMouse, x, y); + + Component::componentUnderMouse->internalMouseDrag (x, y, time); + } +} + +void ComponentPeer::handleMouseUp (const int oldModifiers, int x, int y, const int64 time) +{ + updateCurrentModifiers(); + + int numMouseButtonsDown = 0; + + if ((oldModifiers & ModifierKeys::leftButtonModifier) != 0) + ++numMouseButtonsDown; + + if ((oldModifiers & ModifierKeys::rightButtonModifier) != 0) + ++numMouseButtonsDown; + + if ((oldModifiers & ModifierKeys::middleButtonModifier) != 0) + ++numMouseButtonsDown; + + if (numMouseButtonsDown == 1) + { + const ComponentDeletionWatcher deletionChecker (component); + Component* c = component->getComponentAt (x, y); + + if (c != Component::componentUnderMouse) + { + const int oldX = x; + const int oldY = y; + + if (Component::componentUnderMouse != 0) + { + component->relativePositionToOtherComponent (Component::componentUnderMouse, x, y); + Component::componentUnderMouse->internalMouseUp (oldModifiers, x, y, time); + x = oldX; + y = oldY; + + if (Component::componentUnderMouse != 0) + Component::componentUnderMouse->internalMouseExit (x, y, time); + + if (deletionChecker.hasBeenDeleted()) + return; + + c = component->getComponentAt (oldX, oldY); + } + + Component::componentUnderMouse = c; + + if (Component::componentUnderMouse != 0) + { + component->relativePositionToOtherComponent (Component::componentUnderMouse, x, y); + Component::componentUnderMouse->internalMouseEnter (x, y, time); + } + } + else + { + if (Component::componentUnderMouse != 0) + { + component->relativePositionToOtherComponent (Component::componentUnderMouse, x, y); + Component::componentUnderMouse->internalMouseUp (oldModifiers, x, y, time); + } + } + } +} + +void ComponentPeer::handleMouseExit (int x, int y, const int64 time) +{ + jassert (component->isValidComponent()); + updateCurrentModifiers(); + + if (Component::componentUnderMouse != 0) + { + component->relativePositionToOtherComponent (Component::componentUnderMouse, x, y); + + Component::componentUnderMouse->internalMouseExit (x, y, time); + Component::componentUnderMouse = 0; + } +} + +void ComponentPeer::handleMouseWheel (const int amountX, const int amountY, const int64 time) +{ + updateCurrentModifiers(); + + if (Component::componentUnderMouse != 0) + Component::componentUnderMouse->internalMouseWheel (amountX, amountY, time); +} + +void ComponentPeer::sendFakeMouseMove() throw() +{ + if ((! fakeMouseMessageSent) + && component->flags.hasHeavyweightPeerFlag + && ! ModifierKeys::getCurrentModifiers().isAnyMouseButtonDown()) + { + if (! isMinimised()) + { + int realX, realY, realW, realH; + getBounds (realX, realY, realW, realH); + + component->bounds_.setBounds (realX, realY, realW, realH); + } + + int x, y; + component->getMouseXYRelative (x, y); + + if (((unsigned int) x) < (unsigned int) component->getWidth() + && ((unsigned int) y) < (unsigned int) component->getHeight() + && contains (x, y, false)) + { + postMessage (new Message (fakeMouseMoveMessage, x, y, 0)); + } + + fakeMouseMessageSent = true; + } +} + +void ComponentPeer::handleMessage (const Message& message) +{ + if (message.intParameter1 == fakeMouseMoveMessage) + { + handleMouseMove (message.intParameter2, + message.intParameter3, + Time::currentTimeMillis()); + } +} + +void ComponentPeer::handlePaint (LowLevelGraphicsContext& contextToPaintTo) +{ + Graphics g (&contextToPaintTo); + +#if JUCE_ENABLE_REPAINT_DEBUGGING + g.saveState(); +#endif + + JUCE_TRY + { + component->paintEntireComponent (g); + } + JUCE_CATCH_EXCEPTION + +#if JUCE_ENABLE_REPAINT_DEBUGGING + // enabling this code will fill all areas that get repainted with a colour overlay, to show + // clearly when things are being repainted. + { + g.restoreState(); + + g.fillAll (Colour ((uint8) Random::getSystemRandom().nextInt (255), + (uint8) Random::getSystemRandom().nextInt (255), + (uint8) Random::getSystemRandom().nextInt (255), + (uint8) 0x50)); + } +#endif +} + +bool ComponentPeer::handleKeyPress (const int keyCode, + const juce_wchar textCharacter) +{ + updateCurrentModifiers(); + + Component* target = Component::currentlyFocusedComponent->isValidComponent() + ? Component::currentlyFocusedComponent + : component; + + if (target->isCurrentlyBlockedByAnotherModalComponent()) + { + Component* const currentModalComp = Component::getCurrentlyModalComponent(); + + if (currentModalComp != 0) + target = currentModalComp; + } + + const KeyPress keyInfo (keyCode, + ModifierKeys::getCurrentModifiers().getRawFlags() + & ModifierKeys::allKeyboardModifiers, + textCharacter); + + bool keyWasUsed = false; + + while (target != 0) + { + const ComponentDeletionWatcher deletionChecker (target); + + if (target->keyListeners_ != 0) + { + for (int i = target->keyListeners_->size(); --i >= 0;) + { + keyWasUsed = ((KeyListener*) target->keyListeners_->getUnchecked(i))->keyPressed (keyInfo, target); + + if (keyWasUsed || deletionChecker.hasBeenDeleted()) + return keyWasUsed; + + i = jmin (i, target->keyListeners_->size()); + } + } + + keyWasUsed = target->keyPressed (keyInfo); + + if (keyWasUsed || deletionChecker.hasBeenDeleted()) + break; + + if (keyInfo.isKeyCode (KeyPress::tabKey) && Component::getCurrentlyFocusedComponent() != 0) + { + Component::getCurrentlyFocusedComponent() + ->moveKeyboardFocusToSibling (! keyInfo.getModifiers().isShiftDown()); + + keyWasUsed = true; + break; + } + + target = target->parentComponent_; + } + + return keyWasUsed; +} + +bool ComponentPeer::handleKeyUpOrDown() +{ + updateCurrentModifiers(); + + Component* target = Component::currentlyFocusedComponent->isValidComponent() + ? Component::currentlyFocusedComponent + : component; + + if (target->isCurrentlyBlockedByAnotherModalComponent()) + { + Component* const currentModalComp = Component::getCurrentlyModalComponent(); + + if (currentModalComp != 0) + target = currentModalComp; + } + + bool keyWasUsed = false; + + while (target != 0) + { + const ComponentDeletionWatcher deletionChecker (target); + + keyWasUsed = target->keyStateChanged(); + + if (keyWasUsed || deletionChecker.hasBeenDeleted()) + break; + + if (target->keyListeners_ != 0) + { + for (int i = target->keyListeners_->size(); --i >= 0;) + { + keyWasUsed = ((KeyListener*) target->keyListeners_->getUnchecked(i))->keyStateChanged (target); + + if (keyWasUsed || deletionChecker.hasBeenDeleted()) + return keyWasUsed; + + i = jmin (i, target->keyListeners_->size()); + } + } + + target = target->parentComponent_; + } + + return keyWasUsed; +} + +void ComponentPeer::handleModifierKeysChange() +{ + updateCurrentModifiers(); + + Component* target = Component::getComponentUnderMouse(); + + if (target == 0) + target = Component::getCurrentlyFocusedComponent(); + + if (target == 0) + target = component; + + if (target->isValidComponent()) + target->internalModifierKeysChanged(); +} + +void ComponentPeer::handleBroughtToFront() +{ + updateCurrentModifiers(); + + if (component != 0) + component->internalBroughtToFront(); +} + +void ComponentPeer::setConstrainer (ComponentBoundsConstrainer* const newConstrainer) throw() +{ + constrainer = newConstrainer; +} + +void ComponentPeer::handleMovedOrResized() +{ + jassert (component->isValidComponent()); + updateCurrentModifiers(); + + const bool nowMinimised = isMinimised(); + + if (component->flags.hasHeavyweightPeerFlag && ! nowMinimised) + { + const ComponentDeletionWatcher deletionChecker (component); + + int realX, realY, realW, realH; + getBounds (realX, realY, realW, realH); + + const bool wasMoved = (component->getX() != realX || component->getY() != realY); + const bool wasResized = (component->getWidth() != realW || component->getHeight() != realH); + + if (wasMoved || wasResized) + { + component->bounds_.setBounds (realX, realY, realW, realH); + + if (wasResized) + component->repaint(); + + component->sendMovedResizedMessages (wasMoved, wasResized); + + if (deletionChecker.hasBeenDeleted()) + return; + } + } + + if (isWindowMinimised != nowMinimised) + { + isWindowMinimised = nowMinimised; + component->minimisationStateChanged (nowMinimised); + component->sendVisibilityChangeMessage(); + } + + if (! isFullScreen()) + lastNonFullscreenBounds = component->getBounds(); +} + +void ComponentPeer::handleFocusGain() +{ + updateCurrentModifiers(); + + if (component->isParentOf (lastFocusedComponent)) + { + Component::currentlyFocusedComponent = lastFocusedComponent; + Desktop::getInstance().triggerFocusCallback(); + lastFocusedComponent->internalFocusGain (Component::focusChangedDirectly); + } + else + { + if (! component->isCurrentlyBlockedByAnotherModalComponent()) + { + component->grabKeyboardFocus(); + } + else + { + Component* const currentModalComp = Component::getCurrentlyModalComponent(); + + if (currentModalComp != 0) + currentModalComp->toFront (! currentModalComp->hasKeyboardFocus (true)); + } + } +} + +void ComponentPeer::handleFocusLoss() +{ + updateCurrentModifiers(); + + if (component->hasKeyboardFocus (true)) + { + lastFocusedComponent = Component::currentlyFocusedComponent; + + if (lastFocusedComponent != 0) + { + Component::currentlyFocusedComponent = 0; + Desktop::getInstance().triggerFocusCallback(); + lastFocusedComponent->internalFocusLoss (Component::focusChangedByMouseClick); + } + } +} + +Component* ComponentPeer::getLastFocusedSubcomponent() const throw() +{ + return (component->isParentOf (lastFocusedComponent) && lastFocusedComponent->isShowing()) + ? lastFocusedComponent + : component; +} + +void ComponentPeer::handleScreenSizeChange() +{ + updateCurrentModifiers(); + + component->parentSizeChanged(); + handleMovedOrResized(); +} + +void ComponentPeer::setNonFullScreenBounds (const Rectangle& newBounds) throw() +{ + lastNonFullscreenBounds = newBounds; +} + +const Rectangle& ComponentPeer::getNonFullScreenBounds() const throw() +{ + return lastNonFullscreenBounds; +} + +static FileDragAndDropTarget* findDragAndDropTarget (Component* c, + const StringArray& files, + FileDragAndDropTarget* const lastOne) +{ + while (c != 0) + { + FileDragAndDropTarget* const t = dynamic_cast (c); + + if (t != 0 && (t == lastOne || t->isInterestedInFileDrag (files))) + return t; + + c = c->getParentComponent(); + } + + return 0; +} + +void ComponentPeer::handleFileDragMove (const StringArray& files, int x, int y) +{ + updateCurrentModifiers(); + + FileDragAndDropTarget* lastTarget = 0; + + if (dragAndDropTargetComponent != 0 && ! dragAndDropTargetComponent->hasBeenDeleted()) + lastTarget = const_cast (dynamic_cast (dragAndDropTargetComponent->getComponent())); + + FileDragAndDropTarget* newTarget = 0; + + Component* const compUnderMouse = component->getComponentAt (x, y); + + if (compUnderMouse != lastDragAndDropCompUnderMouse) + { + lastDragAndDropCompUnderMouse = compUnderMouse; + newTarget = findDragAndDropTarget (compUnderMouse, files, lastTarget); + + if (newTarget != lastTarget) + { + if (lastTarget != 0) + lastTarget->fileDragExit (files); + + deleteAndZero (dragAndDropTargetComponent); + + if (newTarget != 0) + { + Component* const targetComp = dynamic_cast (newTarget); + int mx = x, my = y; + component->relativePositionToOtherComponent (targetComp, mx, my); + + dragAndDropTargetComponent = new ComponentDeletionWatcher (dynamic_cast (newTarget)); + newTarget->fileDragEnter (files, mx, my); + } + } + } + else + { + newTarget = lastTarget; + } + + if (newTarget != 0) + { + Component* const targetComp = dynamic_cast (newTarget); + component->relativePositionToOtherComponent (targetComp, x, y); + + newTarget->fileDragMove (files, x, y); + } +} + +void ComponentPeer::handleFileDragExit (const StringArray& files) +{ + handleFileDragMove (files, -1, -1); + + jassert (dragAndDropTargetComponent == 0); + lastDragAndDropCompUnderMouse = 0; +} + +void ComponentPeer::handleFileDragDrop (const StringArray& files, int x, int y) +{ + handleFileDragMove (files, x, y); + + if (dragAndDropTargetComponent != 0 && ! dragAndDropTargetComponent->hasBeenDeleted()) + { + FileDragAndDropTarget* const target = const_cast (dynamic_cast (dragAndDropTargetComponent->getComponent())); + + deleteAndZero (dragAndDropTargetComponent); + lastDragAndDropCompUnderMouse = 0; + + if (target != 0) + { + Component* const targetComp = dynamic_cast (target); + + if (targetComp->isCurrentlyBlockedByAnotherModalComponent()) + { + targetComp->internalModalInputAttempt(); + + if (targetComp->isCurrentlyBlockedByAnotherModalComponent()) + return; + } + + component->relativePositionToOtherComponent (targetComp, x, y); + target->filesDropped (files, x, y); + } + } +} + +void ComponentPeer::handleUserClosingWindow() +{ + updateCurrentModifiers(); + + component->userTriedToCloseWindow(); +} + +void ComponentPeer::clearMaskedRegion() throw() +{ + maskedRegion.clear(); +} + +void ComponentPeer::addMaskedRegion (int x, int y, int w, int h) throw() +{ + maskedRegion.add (x, y, w, h); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ComponentPeer.cpp *********/ + +/********* Start of inlined file: juce_DialogWindow.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +DialogWindow::DialogWindow (const String& name, + const Colour& backgroundColour_, + const bool escapeKeyTriggersCloseButton_, + const bool addToDesktop_) + : DocumentWindow (name, backgroundColour_, DocumentWindow::closeButton, addToDesktop_), + escapeKeyTriggersCloseButton (escapeKeyTriggersCloseButton_) +{ +} + +DialogWindow::~DialogWindow() +{ +} + +void DialogWindow::resized() +{ + DocumentWindow::resized(); + + const KeyPress esc (KeyPress::escapeKey, 0, 0); + + if (escapeKeyTriggersCloseButton + && getCloseButton() != 0 + && ! getCloseButton()->isRegisteredForShortcut (esc)) + { + getCloseButton()->addShortcut (esc); + } +} + +class TempDialogWindow : public DialogWindow +{ +public: + TempDialogWindow (const String& title, const Colour& colour, const bool escapeCloses) + : DialogWindow (title, colour, escapeCloses, true) + { + } + + ~TempDialogWindow() + { + } + + void closeButtonPressed() + { + setVisible (false); + } + +private: + TempDialogWindow (const TempDialogWindow&); + const TempDialogWindow& operator= (const TempDialogWindow&); + +}; + +int DialogWindow::showModalDialog (const String& dialogTitle, + Component* contentComponent, + Component* componentToCentreAround, + const Colour& colour, + const bool escapeKeyTriggersCloseButton, + const bool shouldBeResizable, + const bool useBottomRightCornerResizer) +{ + TempDialogWindow dw (dialogTitle, colour, escapeKeyTriggersCloseButton); + + dw.setContentComponent (contentComponent, true, true); + dw.centreAroundComponent (componentToCentreAround, dw.getWidth(), dw.getHeight()); + dw.setResizable (shouldBeResizable, useBottomRightCornerResizer); + const int result = dw.runModalLoop(); + dw.setContentComponent (0, false); + return result; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_DialogWindow.cpp *********/ + +/********* Start of inlined file: juce_DocumentWindow.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +DocumentWindow::DocumentWindow (const String& title, + const Colour& backgroundColour, + const int requiredButtons_, + const bool addToDesktop_) + : ResizableWindow (title, backgroundColour, addToDesktop_), + titleBarHeight (26), + menuBarHeight (24), + requiredButtons (requiredButtons_), +#if JUCE_MAC + positionTitleBarButtonsOnLeft (true), +#else + positionTitleBarButtonsOnLeft (false), +#endif + drawTitleTextCentred (true), + titleBarIcon (0), + menuBar (0), + menuBarModel (0) +{ + zeromem (titleBarButtons, sizeof (titleBarButtons)); + + setResizeLimits (128, 128, 32768, 32768); + + lookAndFeelChanged(); +} + +DocumentWindow::~DocumentWindow() +{ + for (int i = 0; i < 3; ++i) + delete titleBarButtons[i]; + + delete titleBarIcon; + delete menuBar; +} + +void DocumentWindow::repaintTitleBar() +{ + const int border = getBorderSize(); + repaint (border, border, getWidth() - border * 2, getTitleBarHeight()); +} + +void DocumentWindow::setName (const String& newName) +{ + if (newName != getName()) + { + Component::setName (newName); + repaintTitleBar(); + } +} + +void DocumentWindow::setIcon (const Image* imageToUse) +{ + deleteAndZero (titleBarIcon); + + if (imageToUse != 0) + titleBarIcon = imageToUse->createCopy(); + + repaintTitleBar(); +} + +void DocumentWindow::setTitleBarHeight (const int newHeight) +{ + titleBarHeight = newHeight; + resized(); + repaintTitleBar(); +} + +void DocumentWindow::setTitleBarButtonsRequired (const int requiredButtons_, + const bool positionTitleBarButtonsOnLeft_) +{ + requiredButtons = requiredButtons_; + positionTitleBarButtonsOnLeft = positionTitleBarButtonsOnLeft_; + lookAndFeelChanged(); +} + +void DocumentWindow::setTitleBarTextCentred (const bool textShouldBeCentred) +{ + drawTitleTextCentred = textShouldBeCentred; + repaintTitleBar(); +} + +void DocumentWindow::setMenuBar (MenuBarModel* menuBarModel_, + const int menuBarHeight_) +{ + if (menuBarModel != menuBarModel_) + { + delete menuBar; + menuBar = 0; + + menuBarModel = menuBarModel_; + menuBarHeight = (menuBarHeight_ > 0) ? menuBarHeight_ + : getLookAndFeel().getDefaultMenuBarHeight(); + + if (menuBarModel != 0) + { + // (call the Component method directly to avoid the assertion in ResizableWindow) + Component::addAndMakeVisible (menuBar = new MenuBarComponent (menuBarModel)); + menuBar->setEnabled (isActiveWindow()); + } + + resized(); + } +} + +void DocumentWindow::closeButtonPressed() +{ + /* If you've got a close button, you have to override this method to get + rid of your window! + + If the window is just a pop-up, you should override this method and make + it delete the window in whatever way is appropriate for your app. E.g. you + might just want to call "delete this". + + If your app is centred around this window such that the whole app should quit when + the window is closed, then you will probably want to use this method as an opportunity + to call JUCEApplication::quit(), and leave the window to be deleted later by your + JUCEApplication::shutdown() method. (Doing it this way means that your window will + still get cleaned-up if the app is quit by some other means (e.g. a cmd-Q on the mac + or closing it via the taskbar icon on Windows). + */ + jassertfalse +} + +void DocumentWindow::minimiseButtonPressed() +{ + setMinimised (true); +} + +void DocumentWindow::maximiseButtonPressed() +{ + setFullScreen (! isFullScreen()); +} + +void DocumentWindow::paint (Graphics& g) +{ + ResizableWindow::paint (g); + + if (resizableBorder == 0 && getBorderSize() == 1) + { + g.setColour (getBackgroundColour().overlaidWith (Colour (0x80000000))); + g.drawRect (0, 0, getWidth(), getHeight()); + } + + const int border = getBorderSize(); + + g.setOrigin (border, border); + g.reduceClipRegion (0, 0, getWidth() - border * 2, getTitleBarHeight()); + + int titleSpaceX1 = 6; + int titleSpaceX2 = getWidth() - 6; + + for (int i = 0; i < 3; ++i) + { + if (titleBarButtons[i] != 0) + { + if (positionTitleBarButtonsOnLeft) + titleSpaceX1 = jmax (titleSpaceX1, titleBarButtons[i]->getRight() + (getWidth() - titleBarButtons[i]->getRight()) / 8); + else + titleSpaceX2 = jmin (titleSpaceX2, titleBarButtons[i]->getX() - (titleBarButtons[i]->getX() / 8)); + } + } + + getLookAndFeel() + .drawDocumentWindowTitleBar (*this, g, + getWidth() - border * 2, + getTitleBarHeight(), + titleSpaceX1, jmax (1, titleSpaceX2 - titleSpaceX1), + titleBarIcon, ! drawTitleTextCentred); +} + +void DocumentWindow::resized() +{ + ResizableWindow::resized(); + + if (titleBarButtons[1] != 0) + titleBarButtons[1]->setToggleState (isFullScreen(), false); + + const int border = getBorderSize(); + getLookAndFeel() + .positionDocumentWindowButtons (*this, + border, border, + getWidth() - border * 2, getTitleBarHeight(), + titleBarButtons[0], + titleBarButtons[1], + titleBarButtons[2], + positionTitleBarButtonsOnLeft); + + if (menuBar != 0) + menuBar->setBounds (border, border + getTitleBarHeight(), + getWidth() - border * 2, menuBarHeight); +} + +Button* DocumentWindow::getCloseButton() const throw() +{ + return titleBarButtons[2]; +} + +Button* DocumentWindow::getMinimiseButton() const throw() +{ + return titleBarButtons[0]; +} + +Button* DocumentWindow::getMaximiseButton() const throw() +{ + return titleBarButtons[1]; +} + +int DocumentWindow::getDesktopWindowStyleFlags() const +{ + int flags = ResizableWindow::getDesktopWindowStyleFlags(); + + if ((requiredButtons & minimiseButton) != 0) + flags |= ComponentPeer::windowHasMinimiseButton; + + if ((requiredButtons & maximiseButton) != 0) + flags |= ComponentPeer::windowHasMaximiseButton; + + if ((requiredButtons & closeButton) != 0) + flags |= ComponentPeer::windowHasCloseButton; + + return flags; +} + +void DocumentWindow::lookAndFeelChanged() +{ + int i; + for (i = 0; i < 3; ++i) + deleteAndZero (titleBarButtons[i]); + + if (! isUsingNativeTitleBar()) + { + titleBarButtons[0] = ((requiredButtons & minimiseButton) != 0) + ? getLookAndFeel().createDocumentWindowButton (minimiseButton) : 0; + + titleBarButtons[1] = ((requiredButtons & maximiseButton) != 0) + ? getLookAndFeel().createDocumentWindowButton (maximiseButton) : 0; + + titleBarButtons[2] = ((requiredButtons & closeButton) != 0) + ? getLookAndFeel().createDocumentWindowButton (closeButton) : 0; + + for (i = 0; i < 3; ++i) + { + if (titleBarButtons[i] != 0) + { + buttonListener.owner = this; + titleBarButtons[i]->addButtonListener (&buttonListener); + titleBarButtons[i]->setWantsKeyboardFocus (false); + + // (call the Component method directly to avoid the assertion in ResizableWindow) + Component::addAndMakeVisible (titleBarButtons[i]); + } + } + + if (getCloseButton() != 0) + { +#if JUCE_MAC + getCloseButton()->addShortcut (KeyPress (T('w'), ModifierKeys::commandModifier, 0)); +#else + getCloseButton()->addShortcut (KeyPress (KeyPress::F4Key, ModifierKeys::altModifier, 0)); +#endif + } + } + + activeWindowStatusChanged(); + + ResizableWindow::lookAndFeelChanged(); +} + +void DocumentWindow::parentHierarchyChanged() +{ + lookAndFeelChanged(); +} + +void DocumentWindow::activeWindowStatusChanged() +{ + ResizableWindow::activeWindowStatusChanged(); + + for (int i = 0; i < 3; ++i) + if (titleBarButtons[i] != 0) + titleBarButtons[i]->setEnabled (isActiveWindow()); + + if (menuBar != 0) + menuBar->setEnabled (isActiveWindow()); +} + +const BorderSize DocumentWindow::getBorderThickness() +{ + return BorderSize (getBorderSize()); +} + +const BorderSize DocumentWindow::getContentComponentBorder() +{ + const int size = getBorderSize(); + + return BorderSize (size + + (isUsingNativeTitleBar() ? 0 : titleBarHeight) + + (menuBar != 0 ? menuBarHeight : 0), + size, size, size); +} + +void DocumentWindow::mouseDoubleClick (const MouseEvent& e) +{ + const int border = getBorderSize(); + + if (e.x >= border + && e.y >= border + && e.x < getWidth() - border + && e.y < border + getTitleBarHeight() + && getMaximiseButton() != 0) + { + getMaximiseButton()->triggerClick(); + } +} + +void DocumentWindow::userTriedToCloseWindow() +{ + closeButtonPressed(); +} + +int DocumentWindow::getTitleBarHeight() const +{ + return isUsingNativeTitleBar() ? 0 : jmin (titleBarHeight, getHeight() - 4); +} + +int DocumentWindow::getBorderSize() const +{ + return (isFullScreen() || isUsingNativeTitleBar()) ? 0 : (resizableBorder != 0 ? 4 : 1); +} + +DocumentWindow::ButtonListenerProxy::ButtonListenerProxy() +{ +} + +void DocumentWindow::ButtonListenerProxy::buttonClicked (Button* button) +{ + if (button == owner->getMinimiseButton()) + { + owner->minimiseButtonPressed(); + } + else if (button == owner->getMaximiseButton()) + { + owner->maximiseButtonPressed(); + } + else if (button == owner->getCloseButton()) + { + owner->closeButtonPressed(); + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_DocumentWindow.cpp *********/ + +/********* Start of inlined file: juce_ResizableWindow.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +ResizableWindow::ResizableWindow (const String& name, + const Colour& backgroundColour_, + const bool addToDesktop_) + : TopLevelWindow (name, addToDesktop_), + resizableCorner (0), + resizableBorder (0), + contentComponent (0), + resizeToFitContent (false), + fullscreen (false), + constrainer (0) +#ifdef JUCE_DEBUG + , hasBeenResized (false) +#endif +{ + setBackgroundColour (backgroundColour_); + + defaultConstrainer.setMinimumOnscreenAmounts (0x10000, 16, 24, 16); + + lastNonFullScreenPos.setBounds (50, 50, 256, 256); + + if (addToDesktop_) + Component::addToDesktop (getDesktopWindowStyleFlags()); +} + +ResizableWindow::~ResizableWindow() +{ + deleteAndZero (resizableCorner); + deleteAndZero (resizableBorder); + deleteAndZero (contentComponent); + + // have you been adding your own components directly to this window..? tut tut tut. + // Read the instructions for using a ResizableWindow! + jassert (getNumChildComponents() == 0); +} + +int ResizableWindow::getDesktopWindowStyleFlags() const +{ + int flags = TopLevelWindow::getDesktopWindowStyleFlags(); + + if (isResizable() && (flags & ComponentPeer::windowHasTitleBar) != 0) + flags |= ComponentPeer::windowIsResizable; + + return flags; +} + +void ResizableWindow::setContentComponent (Component* const newContentComponent, + const bool deleteOldOne, + const bool resizeToFit) +{ + resizeToFitContent = resizeToFit; + + if (contentComponent != newContentComponent) + { + if (deleteOldOne) + delete contentComponent; + else + removeChildComponent (contentComponent); + + contentComponent = newContentComponent; + + Component::addAndMakeVisible (contentComponent); + } + + if (resizeToFit) + childBoundsChanged (contentComponent); + + resized(); // must always be called to position the new content comp +} + +void ResizableWindow::setContentComponentSize (int width, int height) +{ + jassert (width > 0 && height > 0); // not a great idea to give it a zero size.. + + const BorderSize border (getContentComponentBorder()); + + setSize (width + border.getLeftAndRight(), + height + border.getTopAndBottom()); +} + +const BorderSize ResizableWindow::getBorderThickness() +{ + return BorderSize (isUsingNativeTitleBar() ? 0 : ((resizableBorder != 0 && ! isFullScreen()) ? 5 : 3)); +} + +const BorderSize ResizableWindow::getContentComponentBorder() +{ + return getBorderThickness(); +} + +void ResizableWindow::moved() +{ + updateLastPos(); +} + +void ResizableWindow::visibilityChanged() +{ + TopLevelWindow::visibilityChanged(); + + updateLastPos(); +} + +void ResizableWindow::resized() +{ + if (resizableBorder != 0) + { + resizableBorder->setVisible (! isFullScreen()); + resizableBorder->setBorderThickness (getBorderThickness()); + + resizableBorder->setSize (getWidth(), getHeight()); + resizableBorder->toBack(); + } + + if (resizableCorner != 0) + { + resizableCorner->setVisible (! isFullScreen()); + + const int resizerSize = 18; + resizableCorner->setBounds (getWidth() - resizerSize, + getHeight() - resizerSize, + resizerSize, resizerSize); + } + + if (contentComponent != 0) + contentComponent->setBoundsInset (getContentComponentBorder()); + + updateLastPos(); + +#ifdef JUCE_DEBUG + hasBeenResized = true; +#endif +} + +void ResizableWindow::childBoundsChanged (Component* child) +{ + if ((child == contentComponent) && (child != 0) && resizeToFitContent) + { + // not going to look very good if this component has a zero size.. + jassert (child->getWidth() > 0); + jassert (child->getHeight() > 0); + + const BorderSize borders (getContentComponentBorder()); + + setSize (child->getWidth() + borders.getLeftAndRight(), + child->getHeight() + borders.getTopAndBottom()); + } +} + +void ResizableWindow::activeWindowStatusChanged() +{ + const BorderSize borders (getContentComponentBorder()); + + repaint (0, 0, getWidth(), borders.getTop()); + repaint (0, borders.getTop(), borders.getLeft(), getHeight() - borders.getBottom() - borders.getTop()); + repaint (0, getHeight() - borders.getBottom(), getWidth(), borders.getBottom()); + repaint (getWidth() - borders.getRight(), borders.getTop(), borders.getRight(), getHeight() - borders.getBottom() - borders.getTop()); +} + +void ResizableWindow::setResizable (const bool shouldBeResizable, + const bool useBottomRightCornerResizer) +{ + if (shouldBeResizable) + { + if (useBottomRightCornerResizer) + { + deleteAndZero (resizableBorder); + + if (resizableCorner == 0) + { + Component::addChildComponent (resizableCorner = new ResizableCornerComponent (this, constrainer)); + resizableCorner->setAlwaysOnTop (true); + } + } + else + { + deleteAndZero (resizableCorner); + + if (resizableBorder == 0) + Component::addChildComponent (resizableBorder = new ResizableBorderComponent (this, constrainer)); + } + } + else + { + deleteAndZero (resizableCorner); + deleteAndZero (resizableBorder); + } + + if (isUsingNativeTitleBar()) + recreateDesktopWindow(); + + childBoundsChanged (contentComponent); + resized(); +} + +bool ResizableWindow::isResizable() const throw() +{ + return resizableCorner != 0 + || resizableBorder != 0; +} + +void ResizableWindow::setResizeLimits (const int newMinimumWidth, + const int newMinimumHeight, + const int newMaximumWidth, + const int newMaximumHeight) throw() +{ + // if you've set up a custom constrainer then these settings won't have any effect.. + jassert (constrainer == &defaultConstrainer || constrainer == 0); + + if (constrainer == 0) + setConstrainer (&defaultConstrainer); + + defaultConstrainer.setSizeLimits (newMinimumWidth, newMinimumHeight, + newMaximumWidth, newMaximumHeight); + + setBoundsConstrained (getX(), getY(), getWidth(), getHeight()); +} + +void ResizableWindow::setConstrainer (ComponentBoundsConstrainer* newConstrainer) +{ + if (constrainer != newConstrainer) + { + constrainer = newConstrainer; + + const bool useBottomRightCornerResizer = resizableCorner != 0; + const bool shouldBeResizable = useBottomRightCornerResizer || resizableBorder != 0; + + deleteAndZero (resizableCorner); + deleteAndZero (resizableBorder); + + setResizable (shouldBeResizable, useBottomRightCornerResizer); + + ComponentPeer* const peer = getPeer(); + if (peer != 0) + peer->setConstrainer (newConstrainer); + } +} + +void ResizableWindow::setBoundsConstrained (int x, int y, int w, int h) +{ + if (constrainer != 0) + constrainer->setBoundsForComponent (this, x, y, w, h, false, false, false, false); + else + setBounds (x, y, w, h); +} + +void ResizableWindow::paint (Graphics& g) +{ + g.fillAll (backgroundColour); + + if (! isFullScreen()) + { + getLookAndFeel().drawResizableWindowBorder (g, getWidth(), getHeight(), + getBorderThickness(), *this); + } + +#ifdef JUCE_DEBUG + /* If this fails, then you've probably written a subclass with a resized() + callback but forgotten to make it call its parent class's resized() method. + + It's important when you override methods like resized(), moved(), + etc., that you make sure the base class methods also get called. + + Of course you shouldn't really be overriding ResizableWindow::resized() anyway, + because your content should all be inside the content component - and it's the + content component's resized() method that you should be using to do your + layout. + */ + jassert (hasBeenResized || (getWidth() == 0 && getHeight() == 0)); +#endif +} + +void ResizableWindow::lookAndFeelChanged() +{ + resized(); + + if (isOnDesktop()) + { + Component::addToDesktop (getDesktopWindowStyleFlags()); + + ComponentPeer* const peer = getPeer(); + if (peer != 0) + peer->setConstrainer (constrainer); + } +} + +void ResizableWindow::setBackgroundColour (const Colour& newColour) +{ + if (Desktop::canUseSemiTransparentWindows()) + backgroundColour = newColour; + else + backgroundColour = newColour.withAlpha (1.0f); + + setOpaque (backgroundColour.isOpaque()); + repaint(); +} + +bool ResizableWindow::isFullScreen() const +{ + if (isOnDesktop()) + { + ComponentPeer* const peer = getPeer(); + return peer != 0 && peer->isFullScreen(); + } + + return fullscreen; +} + +void ResizableWindow::setFullScreen (const bool shouldBeFullScreen) +{ + if (shouldBeFullScreen != isFullScreen()) + { + updateLastPos(); + fullscreen = shouldBeFullScreen; + + if (isOnDesktop()) + { + ComponentPeer* const peer = getPeer(); + + if (peer != 0) + { + // keep a copy of this intact in case the real one gets messed-up while we're un-maximising + const Rectangle lastPos (lastNonFullScreenPos); + + peer->setFullScreen (shouldBeFullScreen); + + if (! shouldBeFullScreen) + setBounds (lastPos); + } + else + { + jassertfalse + } + } + else + { + if (shouldBeFullScreen) + setBounds (0, 0, getParentWidth(), getParentHeight()); + else + setBounds (lastNonFullScreenPos); + } + + resized(); + } +} + +bool ResizableWindow::isMinimised() const +{ + ComponentPeer* const peer = getPeer(); + + return (peer != 0) && peer->isMinimised(); +} + +void ResizableWindow::setMinimised (const bool shouldMinimise) +{ + if (shouldMinimise != isMinimised()) + { + ComponentPeer* const peer = getPeer(); + + if (peer != 0) + { + updateLastPos(); + peer->setMinimised (shouldMinimise); + } + else + { + jassertfalse + } + } +} + +void ResizableWindow::updateLastPos() +{ + if (isShowing() && ! (isFullScreen() || isMinimised())) + { + lastNonFullScreenPos = getBounds(); + } +} + +void ResizableWindow::parentSizeChanged() +{ + if (isFullScreen() && getParentComponent() != 0) + { + setBounds (0, 0, getParentWidth(), getParentHeight()); + } +} + +const String ResizableWindow::getWindowStateAsString() +{ + updateLastPos(); + + String s; + + if (isFullScreen()) + s << "fs "; + + s << lastNonFullScreenPos.getX() << T(' ') + << lastNonFullScreenPos.getY() << T(' ') + << lastNonFullScreenPos.getWidth() << T(' ') + << lastNonFullScreenPos.getHeight(); + + return s; +} + +bool ResizableWindow::restoreWindowStateFromString (const String& s) +{ + StringArray tokens; + tokens.addTokens (s, false); + tokens.removeEmptyStrings(); + tokens.trim(); + + const bool fs = tokens[0].startsWithIgnoreCase (T("fs")); + const int n = fs ? 1 : 0; + + if (tokens.size() != 4 + n) + return false; + + Rectangle r (tokens[n].getIntValue(), + tokens[n + 1].getIntValue(), + tokens[n + 2].getIntValue(), + tokens[n + 3].getIntValue()); + + if (r.isEmpty()) + return false; + + const Rectangle screen (Desktop::getInstance().getMonitorAreaContaining (r.getX(), r.getY())); + + r = r.getIntersection (screen); + + lastNonFullScreenPos = r; + + if (isOnDesktop()) + { + ComponentPeer* const peer = getPeer(); + + if (peer != 0) + peer->setNonFullScreenBounds (r); + } + + setFullScreen (fs); + + if (! fs) + setBoundsConstrained (r.getX(), + r.getY(), + r.getWidth(), + r.getHeight()); + + return true; +} + +void ResizableWindow::mouseDown (const MouseEvent&) +{ + if (! isFullScreen()) + dragger.startDraggingComponent (this, constrainer); +} + +void ResizableWindow::mouseDrag (const MouseEvent& e) +{ + if (! isFullScreen()) + dragger.dragComponent (this, e); +} + +#ifdef JUCE_DEBUG +void ResizableWindow::addChildComponent (Component* const child, int zOrder) +{ + /* Agh! You shouldn't add components directly to a ResizableWindow - this class + manages its child components automatically, and if you add your own it'll cause + trouble. Instead, use setContentComponent() to give it a component which + will be automatically resized and kept in the right place - then you can add + subcomponents to the content comp. See the notes for the ResizableWindow class + for more info. + + If you really know what you're doing and want to avoid this assertion, just call + Component::addChildComponent directly. + */ + jassertfalse + + Component::addChildComponent (child, zOrder); +} + +void ResizableWindow::addAndMakeVisible (Component* const child, int zOrder) +{ + /* Agh! You shouldn't add components directly to a ResizableWindow - this class + manages its child components automatically, and if you add your own it'll cause + trouble. Instead, use setContentComponent() to give it a component which + will be automatically resized and kept in the right place - then you can add + subcomponents to the content comp. See the notes for the ResizableWindow class + for more info. + + If you really know what you're doing and want to avoid this assertion, just call + Component::addAndMakeVisible directly. + */ + jassertfalse + + Component::addAndMakeVisible (child, zOrder); +} +#endif + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ResizableWindow.cpp *********/ + +/********* Start of inlined file: juce_SplashScreen.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +SplashScreen::SplashScreen() + : backgroundImage (0), + isImageInCache (false) +{ + setOpaque (true); +} + +SplashScreen::~SplashScreen() +{ + if (isImageInCache) + ImageCache::release (backgroundImage); + else + delete backgroundImage; +} + +void SplashScreen::show (const String& title, + Image* const backgroundImage_, + const int minimumTimeToDisplayFor, + const bool useDropShadow, + const bool removeOnMouseClick) +{ + backgroundImage = backgroundImage_; + + jassert (backgroundImage_ != 0); + + if (backgroundImage_ != 0) + { + isImageInCache = ImageCache::isImageInCache (backgroundImage_); + + setOpaque (! backgroundImage_->hasAlphaChannel()); + + show (title, + backgroundImage_->getWidth(), + backgroundImage_->getHeight(), + minimumTimeToDisplayFor, + useDropShadow, + removeOnMouseClick); + } +} + +void SplashScreen::show (const String& title, + const int width, + const int height, + const int minimumTimeToDisplayFor, + const bool useDropShadow, + const bool removeOnMouseClick) +{ + setName (title); + setAlwaysOnTop (true); + setVisible (true); + centreWithSize (width, height); + + addToDesktop (useDropShadow ? (ComponentPeer::windowAppearsOnTaskbar | ComponentPeer::windowHasDropShadow) + : ComponentPeer::windowAppearsOnTaskbar); + toFront (false); + + MessageManager::getInstance()->dispatchPendingMessages(); + + repaint(); + + originalClickCounter = removeOnMouseClick + ? Desktop::getMouseButtonClickCounter() + : INT_MAX; + + earliestTimeToDelete = Time::getCurrentTime() + RelativeTime::milliseconds (minimumTimeToDisplayFor); + + startTimer (50); +} + +void SplashScreen::paint (Graphics& g) +{ + if (backgroundImage != 0) + { + g.setOpacity (1.0f); + + g.drawImage (backgroundImage, + 0, 0, getWidth(), getHeight(), + 0, 0, backgroundImage->getWidth(), backgroundImage->getHeight()); + } +} + +void SplashScreen::timerCallback() +{ + if (Time::getCurrentTime() > earliestTimeToDelete + || Desktop::getMouseButtonClickCounter() > originalClickCounter) + { + delete this; + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_SplashScreen.cpp *********/ + +/********* Start of inlined file: juce_ThreadWithProgressWindow.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +ThreadWithProgressWindow::ThreadWithProgressWindow (const String& title, + const bool hasProgressBar, + const bool hasCancelButton, + const int timeOutMsWhenCancelling_, + const String& cancelButtonText) + : Thread ("Juce Progress Window"), + progress (0.0), + alertWindow (title, String::empty, AlertWindow::NoIcon), + timeOutMsWhenCancelling (timeOutMsWhenCancelling_) +{ + if (hasProgressBar) + alertWindow.addProgressBarComponent (progress); + + if (hasCancelButton) + alertWindow.addButton (cancelButtonText, 1); +} + +ThreadWithProgressWindow::~ThreadWithProgressWindow() +{ + stopThread (timeOutMsWhenCancelling); +} + +bool ThreadWithProgressWindow::runThread (const int priority) +{ + startThread (priority); + startTimer (100); + + alertWindow.setMessage (message); + + const bool wasCancelled = alertWindow.runModalLoop() != 0; + + stopThread (timeOutMsWhenCancelling); + + alertWindow.setVisible (false); + + return ! wasCancelled; +} + +void ThreadWithProgressWindow::setProgress (const double newProgress) +{ + progress = newProgress; +} + +void ThreadWithProgressWindow::setStatusMessage (const String& newStatusMessage) +{ + message = newStatusMessage; +} + +void ThreadWithProgressWindow::timerCallback() +{ + if (! isThreadRunning()) + { + // thread has finished normally.. + alertWindow.exitModalState (0); + alertWindow.setVisible (false); + } + else + { + alertWindow.setMessage (message); + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ThreadWithProgressWindow.cpp *********/ + +/********* Start of inlined file: juce_TooltipWindow.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +TooltipWindow::TooltipWindow (Component* const parentComponent, + const int millisecondsBeforeTipAppears_) + : Component ("tooltip"), + millisecondsBeforeTipAppears (millisecondsBeforeTipAppears_), + mouseX (0), + mouseY (0), + lastMouseMoveTime (0), + lastHideTime (0), + lastComponentUnderMouse (0), + changedCompsSinceShown (true) +{ + startTimer (123); + + setAlwaysOnTop (true); + setOpaque (true); + + if (parentComponent != 0) + { + parentComponent->addChildComponent (this); + } + else + { + setSize (1, 1); // to keep the OS happy by not having zero-size windows + addToDesktop (ComponentPeer::windowHasDropShadow + | ComponentPeer::windowIsTemporary); + } +} + +TooltipWindow::~TooltipWindow() +{ +} + +void TooltipWindow::paint (Graphics& g) +{ + getLookAndFeel().drawTooltip (g, tip, getWidth(), getHeight()); +} + +void TooltipWindow::mouseEnter (const MouseEvent&) +{ + setVisible (false); +} + +void TooltipWindow::showFor (Component* const c) +{ + TooltipClient* const ttc = dynamic_cast (c); + + if (ttc != 0 && ! c->isCurrentlyBlockedByAnotherModalComponent()) + tip = ttc->getTooltip(); + else + tip = String::empty; + + if (tip.isEmpty()) + { + setVisible (false); + } + else + { + int mx, my; + Desktop::getMousePosition (mx, my); + + if (getParentComponent() != 0) + getParentComponent()->globalPositionToRelative (mx, my); + + int x, y, w, h; + getLookAndFeel().getTooltipSize (tip, w, h); + + if (mx > getParentWidth() / 2) + x = mx - (w + 12); + else + x = mx + 24; + + if (my > getParentHeight() / 2) + y = my - (h + 6); + else + y = my + 6; + + setBounds (x, y, w, h); + setVisible (true); + toFront (false); + } +} + +void TooltipWindow::timerCallback() +{ + int mx, my; + Desktop::getMousePosition (mx, my); + + const unsigned int now = Time::getApproximateMillisecondCounter(); + Component* const underMouse = Component::getComponentUnderMouse(); + const bool changedComp = (underMouse != lastComponentUnderMouse); + lastComponentUnderMouse = underMouse; + + if (changedComp + || abs (mx - mouseX) > 4 + || abs (my - mouseY) > 4 + || Desktop::getInstance().getMouseButtonClickCounter() > mouseClicks) + { + lastMouseMoveTime = now; + + if (isVisible()) + { + lastHideTime = now; + setVisible (false); + } + + changedCompsSinceShown = changedCompsSinceShown || changedComp; + + tip = String::empty; + + mouseX = mx; + mouseY = my; + } + + if (changedCompsSinceShown) + { + if ((now > lastMouseMoveTime + millisecondsBeforeTipAppears + || now < lastHideTime + 500) + && ! isVisible()) + { + if (underMouse->isValidComponent()) + showFor (underMouse); + + changedCompsSinceShown = false; + } + } + + mouseClicks = Desktop::getInstance().getMouseButtonClickCounter(); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_TooltipWindow.cpp *********/ + +/********* Start of inlined file: juce_TopLevelWindow.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +/** Keeps track of the active top level window. +*/ +class TopLevelWindowManager : public Timer, + public DeletedAtShutdown +{ +public: + + TopLevelWindowManager() + : windows (8), + currentActive (0) + { + } + + ~TopLevelWindowManager() + { + clearSingletonInstance(); + } + + juce_DeclareSingleton_SingleThreaded_Minimal (TopLevelWindowManager) + + void timerCallback() + { + startTimer (1731); + TopLevelWindow* active = 0; + + if (Process::isForegroundProcess()) + { + active = currentActive; + + Component* const c = Component::getCurrentlyFocusedComponent(); + + TopLevelWindow* tlw = dynamic_cast (c); + + if (tlw == 0 && c != 0) + // (unable to use the syntax findParentComponentOfClass () because of a VC6 compiler bug) + tlw = c->findParentComponentOfClass ((TopLevelWindow*) 0); + + if (tlw != 0) + active = tlw; + } + + if (active != currentActive) + { + currentActive = active; + + for (int i = windows.size(); --i >= 0;) + { + TopLevelWindow* const tlw = (TopLevelWindow*) windows.getUnchecked (i); + tlw->setWindowActive (isWindowActive (tlw)); + + i = jmin (i, windows.size() - 1); + } + + Desktop::getInstance().triggerFocusCallback(); + } + } + + bool addWindow (TopLevelWindow* const w) throw() + { + windows.add (w); + startTimer (10); + + return isWindowActive (w); + } + + void removeWindow (TopLevelWindow* const w) throw() + { + startTimer (10); + + if (currentActive == w) + currentActive = 0; + + windows.removeValue (w); + + if (windows.size() == 0) + deleteInstance(); + } + + VoidArray windows; + +private: + TopLevelWindow* currentActive; + + bool isWindowActive (TopLevelWindow* const tlw) const throw() + { + return (tlw == currentActive + || tlw->isParentOf (currentActive) + || tlw->hasKeyboardFocus (true)) + && tlw->isShowing(); + } + + TopLevelWindowManager (const TopLevelWindowManager&); + const TopLevelWindowManager& operator= (const TopLevelWindowManager&); +}; + +juce_ImplementSingleton_SingleThreaded (TopLevelWindowManager) + +void juce_CheckCurrentlyFocusedTopLevelWindow() throw() +{ + if (TopLevelWindowManager::getInstanceWithoutCreating() != 0) + TopLevelWindowManager::getInstanceWithoutCreating()->startTimer (20); +} + +TopLevelWindow::TopLevelWindow (const String& name, + const bool addToDesktop_) + : Component (name), + useDropShadow (true), + useNativeTitleBar (false), + windowIsActive_ (false), + shadower (0) +{ + setOpaque (true); + + if (addToDesktop_) + Component::addToDesktop (getDesktopWindowStyleFlags()); + else + setDropShadowEnabled (true); + + setWantsKeyboardFocus (true); + setBroughtToFrontOnMouseClick (true); + windowIsActive_ = TopLevelWindowManager::getInstance()->addWindow (this); +} + +TopLevelWindow::~TopLevelWindow() +{ + deleteAndZero (shadower); + TopLevelWindowManager::getInstance()->removeWindow (this); +} + +void TopLevelWindow::focusOfChildComponentChanged (FocusChangeType) +{ + if (hasKeyboardFocus (true)) + TopLevelWindowManager::getInstance()->timerCallback(); + else + TopLevelWindowManager::getInstance()->startTimer (10); +} + +void TopLevelWindow::setWindowActive (const bool isNowActive) throw() +{ + if (windowIsActive_ != isNowActive) + { + windowIsActive_ = isNowActive; + activeWindowStatusChanged(); + } +} + +void TopLevelWindow::activeWindowStatusChanged() +{ +} + +void TopLevelWindow::parentHierarchyChanged() +{ + setDropShadowEnabled (useDropShadow); +} + +void TopLevelWindow::visibilityChanged() +{ + if (isShowing()) + toFront (true); +} + +int TopLevelWindow::getDesktopWindowStyleFlags() const +{ + int flags = ComponentPeer::windowAppearsOnTaskbar; + + if (useDropShadow) + flags |= ComponentPeer::windowHasDropShadow; + + if (useNativeTitleBar) + flags |= ComponentPeer::windowHasTitleBar; + + return flags; +} + +void TopLevelWindow::setDropShadowEnabled (const bool useShadow) +{ + useDropShadow = useShadow; + + if (isOnDesktop()) + { + deleteAndZero (shadower); + Component::addToDesktop (getDesktopWindowStyleFlags()); + } + else + { + if (useShadow && isOpaque()) + { + if (shadower == 0) + { + shadower = getLookAndFeel().createDropShadowerForComponent (this); + + if (shadower != 0) + shadower->setOwner (this); + } + } + else + { + deleteAndZero (shadower); + } + } +} + +void TopLevelWindow::setUsingNativeTitleBar (const bool useNativeTitleBar_) +{ + if (useNativeTitleBar != useNativeTitleBar_) + { + useNativeTitleBar = useNativeTitleBar_; + recreateDesktopWindow(); + sendLookAndFeelChange(); + } +} + +void TopLevelWindow::recreateDesktopWindow() +{ + if (isOnDesktop()) + { + Component::addToDesktop (getDesktopWindowStyleFlags()); + toFront (true); + } +} + +void TopLevelWindow::addToDesktop (int windowStyleFlags, void* nativeWindowToAttachTo) +{ + /* It's not recommended to change the desktop window flags directly for a TopLevelWindow, + because this class needs to make sure its layout corresponds with settings like whether + it's got a native title bar or not. + + If you need custom flags for your window, you can override the getDesktopWindowStyleFlags() + method. If you do this, it's best to call the base class's getDesktopWindowStyleFlags() + method, then add or remove whatever flags are necessary from this value before returning it. + */ + + jassert ((windowStyleFlags & ~ComponentPeer::windowIsSemiTransparent) + == (getDesktopWindowStyleFlags() & ~ComponentPeer::windowIsSemiTransparent)); + + Component::addToDesktop (windowStyleFlags, nativeWindowToAttachTo); + + if (windowStyleFlags != getDesktopWindowStyleFlags()) + sendLookAndFeelChange(); +} + +void TopLevelWindow::centreAroundComponent (Component* c, const int width, const int height) +{ + if (c == 0) + c = TopLevelWindow::getActiveTopLevelWindow(); + + if (c == 0) + { + centreWithSize (width, height); + } + else + { + int x = (c->getWidth() - width) / 2; + int y = (c->getHeight() - height) / 2; + c->relativePositionToGlobal (x, y); + + Rectangle parentArea (c->getParentMonitorArea()); + + if (getParentComponent() != 0) + { + getParentComponent()->globalPositionToRelative (x, y); + parentArea.setBounds (0, 0, getParentWidth(), getParentHeight()); + } + + parentArea.reduce (12, 12); + + setBounds (jlimit (parentArea.getX(), jmax (parentArea.getX(), parentArea.getRight() - width), x), + jlimit (parentArea.getY(), jmax (parentArea.getY(), parentArea.getBottom() - height), y), + width, height); + } +} + +int TopLevelWindow::getNumTopLevelWindows() throw() +{ + return TopLevelWindowManager::getInstance()->windows.size(); +} + +TopLevelWindow* TopLevelWindow::getTopLevelWindow (const int index) throw() +{ + return (TopLevelWindow*) TopLevelWindowManager::getInstance()->windows [index]; +} + +TopLevelWindow* TopLevelWindow::getActiveTopLevelWindow() throw() +{ + TopLevelWindow* best = 0; + int bestNumTWLParents = -1; + + for (int i = TopLevelWindow::getNumTopLevelWindows(); --i >= 0;) + { + TopLevelWindow* const tlw = TopLevelWindow::getTopLevelWindow (i); + + if (tlw->isActiveWindow()) + { + int numTWLParents = 0; + + const Component* c = tlw->getParentComponent(); + + while (c != 0) + { + if (dynamic_cast (c) != 0) + ++numTWLParents; + + c = c->getParentComponent(); + } + + if (bestNumTWLParents < numTWLParents) + { + best = tlw; + bestNumTWLParents = numTWLParents; + } + } + } + + return best; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_TopLevelWindow.cpp *********/ + +/********* Start of inlined file: juce_Brush.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +Brush::Brush() throw() +{ +} + +Brush::~Brush() throw() +{ +} + +void Brush::paintVerticalLine (LowLevelGraphicsContext& context, + int x, float y1, float y2) throw() +{ + Path p; + p.addRectangle ((float) x, y1, 1.0f, y2 - y1); + paintPath (context, p, AffineTransform::identity); +} + +void Brush::paintHorizontalLine (LowLevelGraphicsContext& context, + int y, float x1, float x2) throw() +{ + Path p; + p.addRectangle (x1, (float) y, x2 - x1, 1.0f); + paintPath (context, p, AffineTransform::identity); +} + +void Brush::paintLine (LowLevelGraphicsContext& context, + float x1, float y1, float x2, float y2) throw() +{ + Path p; + p.addLineSegment (x1, y1, x2, y2, 1.0f); + paintPath (context, p, AffineTransform::identity); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_Brush.cpp *********/ + +/********* Start of inlined file: juce_GradientBrush.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +GradientBrush::GradientBrush (const Colour& colour1, + const float x1, + const float y1, + const Colour& colour2, + const float x2, + const float y2, + const bool isRadial) throw() + : gradient (colour1, x1, y1, + colour2, x2, y2, + isRadial) +{ +} + +GradientBrush::GradientBrush (const ColourGradient& gradient_) throw() + : gradient (gradient_) +{ +} + +GradientBrush::~GradientBrush() throw() +{ +} + +Brush* GradientBrush::createCopy() const throw() +{ + return new GradientBrush (gradient); +} + +void GradientBrush::applyTransform (const AffineTransform& transform) throw() +{ + gradient.transform = gradient.transform.followedBy (transform); +} + +void GradientBrush::multiplyOpacity (const float multiple) throw() +{ + gradient.multiplyOpacity (multiple); +} + +bool GradientBrush::isInvisible() const throw() +{ + return gradient.isInvisible(); +} + +void GradientBrush::paintPath (LowLevelGraphicsContext& context, + const Path& path, const AffineTransform& transform) throw() +{ + context.fillPathWithGradient (path, transform, gradient, EdgeTable::Oversampling_4times); +} + +void GradientBrush::paintRectangle (LowLevelGraphicsContext& context, + int x, int y, int w, int h) throw() +{ + context.fillRectWithGradient (x, y, w, h, gradient); +} + +void GradientBrush::paintAlphaChannel (LowLevelGraphicsContext& context, + const Image& alphaChannelImage, int imageX, int imageY, + int x, int y, int w, int h) throw() +{ + context.saveState(); + + if (context.reduceClipRegion (x, y, w, h)) + context.fillAlphaChannelWithGradient (alphaChannelImage, imageX, imageY, gradient); + + context.restoreState(); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_GradientBrush.cpp *********/ + +/********* Start of inlined file: juce_ImageBrush.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +ImageBrush::ImageBrush (Image* const image_, + const int anchorX_, + const int anchorY_, + const float opacity_) throw() + : image (image_), + anchorX (anchorX_), + anchorY (anchorY_), + opacity (opacity_) +{ + jassert (image != 0); // not much point creating a brush without an image, is there? + + if (image != 0) + { + if (image->getWidth() == 0 || image->getHeight() == 0) + { + jassertfalse // you've passed in an empty image - not exactly brilliant for tiling. + image = 0; + } + } +} + +ImageBrush::~ImageBrush() throw() +{ +} + +Brush* ImageBrush::createCopy() const throw() +{ + return new ImageBrush (image, anchorX, anchorY, opacity); +} + +void ImageBrush::multiplyOpacity (const float multiple) throw() +{ + opacity *= multiple; +} + +bool ImageBrush::isInvisible() const throw() +{ + return opacity == 0.0f; +} + +void ImageBrush::applyTransform (const AffineTransform& /*transform*/) throw() +{ + //xxx should probably be smarter and warp the image +} + +void ImageBrush::getStartXY (int& x, int& y) const throw() +{ + x -= anchorX; + y -= anchorY; + + const int iw = image->getWidth(); + const int ih = image->getHeight(); + + if (x < 0) + x = ((x / iw) - 1) * iw; + else + x = (x / iw) * iw; + + if (y < 0) + y = ((y / ih) - 1) * ih; + else + y = (y / ih) * ih; + + x += anchorX; + y += anchorY; +} + +void ImageBrush::paintRectangle (LowLevelGraphicsContext& context, + int x, int y, int w, int h) throw() +{ + context.saveState(); + + if (image != 0 && context.reduceClipRegion (x, y, w, h)) + { + const int right = x + w; + const int bottom = y + h; + + const int iw = image->getWidth(); + const int ih = image->getHeight(); + + int startX = x; + getStartXY (startX, y); + + while (y < bottom) + { + x = startX; + + while (x < right) + { + context.blendImage (*image, x, y, iw, ih, 0, 0, opacity); + x += iw; + } + + y += ih; + } + } + + context.restoreState(); +} + +void ImageBrush::paintPath (LowLevelGraphicsContext& context, + const Path& path, const AffineTransform& transform) throw() +{ + if (image != 0) + { + Rectangle clip (context.getClipBounds()); + + { + float x, y, w, h; + path.getBoundsTransformed (transform, x, y, w, h); + + clip = clip.getIntersection (Rectangle ((int) floorf (x), + (int) floorf (y), + 2 + (int) floorf (w), + 2 + (int) floorf (h))); + } + + int x = clip.getX(); + int y = clip.getY(); + const int right = clip.getRight(); + const int bottom = clip.getBottom(); + + const int iw = image->getWidth(); + const int ih = image->getHeight(); + + int startX = x; + getStartXY (startX, y); + + while (y < bottom) + { + x = startX; + + while (x < right) + { + context.fillPathWithImage (path, transform, *image, x, y, opacity, EdgeTable::Oversampling_4times); + x += iw; + } + + y += ih; + } + } +} + +void ImageBrush::paintAlphaChannel (LowLevelGraphicsContext& context, + const Image& alphaChannelImage, int imageX, int imageY, + int x, int y, int w, int h) throw() +{ + context.saveState(); + + if (image != 0 && context.reduceClipRegion (x, y, w, h)) + { + const Rectangle clip (context.getClipBounds()); + x = clip.getX(); + y = clip.getY(); + const int right = clip.getRight(); + const int bottom = clip.getBottom(); + + const int iw = image->getWidth(); + const int ih = image->getHeight(); + + int startX = x; + getStartXY (startX, y); + + while (y < bottom) + { + x = startX; + + while (x < right) + { + context.fillAlphaChannelWithImage (alphaChannelImage, + imageX, imageY, *image, + x, y, opacity); + x += iw; + } + + y += ih; + } + } + + context.restoreState(); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ImageBrush.cpp *********/ + +/********* Start of inlined file: juce_SolidColourBrush.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +SolidColourBrush::SolidColourBrush() throw() + : colour (0xff000000) +{ +} + +SolidColourBrush::SolidColourBrush (const Colour& colour_) throw() + : colour (colour_) +{ +} + +SolidColourBrush::~SolidColourBrush() throw() +{ +} + +Brush* SolidColourBrush::createCopy() const throw() +{ + return new SolidColourBrush (colour); +} + +void SolidColourBrush::applyTransform (const AffineTransform& /*transform*/) throw() +{ +} + +void SolidColourBrush::multiplyOpacity (const float multiple) throw() +{ + colour = colour.withMultipliedAlpha (multiple); +} + +bool SolidColourBrush::isInvisible() const throw() +{ + return colour.isTransparent(); +} + +void SolidColourBrush::paintPath (LowLevelGraphicsContext& context, + const Path& path, const AffineTransform& transform) throw() +{ + if (! colour.isTransparent()) + context.fillPathWithColour (path, transform, colour, EdgeTable::Oversampling_4times); +} + +void SolidColourBrush::paintRectangle (LowLevelGraphicsContext& context, + int x, int y, int w, int h) throw() +{ + if (! colour.isTransparent()) + context.fillRectWithColour (x, y, w, h, colour, false); +} + +void SolidColourBrush::paintAlphaChannel (LowLevelGraphicsContext& context, + const Image& alphaChannelImage, int imageX, int imageY, + int x, int y, int w, int h) throw() +{ + if (! colour.isTransparent()) + { + context.saveState(); + + if (context.reduceClipRegion (x, y, w, h)) + context.fillAlphaChannelWithColour (alphaChannelImage, imageX, imageY, colour); + + context.restoreState(); + } +} + +void SolidColourBrush::paintVerticalLine (LowLevelGraphicsContext& context, + int x, float y1, float y2) throw() +{ + context.drawVerticalLine (x, y1, y2, colour); +} + +void SolidColourBrush::paintHorizontalLine (LowLevelGraphicsContext& context, + int y, float x1, float x2) throw() +{ + context.drawHorizontalLine (y, x1, x2, colour); +} + +void SolidColourBrush::paintLine (LowLevelGraphicsContext& context, + float x1, float y1, float x2, float y2) throw() +{ + context.drawLine (x1, y1, x2, y2, colour); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_SolidColourBrush.cpp *********/ + +/********* Start of inlined file: juce_Colour.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +static forcedinline uint8 floatAlphaToInt (const float alpha) +{ + return (uint8) jlimit (0, 0xff, roundFloatToInt (alpha * 255.0f)); +} + +static const float oneOver255 = 1.0f / 255.0f; + +Colour::Colour() throw() + : argb (0) +{ +} + +Colour::Colour (const Colour& other) throw() + : argb (other.argb) +{ +} + +const Colour& Colour::operator= (const Colour& other) throw() +{ + argb = other.argb; + return *this; +} + +bool Colour::operator== (const Colour& other) const throw() +{ + return argb.getARGB() == other.argb.getARGB(); +} + +bool Colour::operator!= (const Colour& other) const throw() +{ + return argb.getARGB() != other.argb.getARGB(); +} + +Colour::Colour (const uint32 argb_) throw() + : argb (argb_) +{ +} + +Colour::Colour (const uint8 red, + const uint8 green, + const uint8 blue) throw() +{ + argb.setARGB (0xff, red, green, blue); +} + +Colour::Colour (const uint8 red, + const uint8 green, + const uint8 blue, + const uint8 alpha) throw() +{ + argb.setARGB (alpha, red, green, blue); +} + +Colour::Colour (const uint8 red, + const uint8 green, + const uint8 blue, + const float alpha) throw() +{ + argb.setARGB (floatAlphaToInt (alpha), red, green, blue); +} + +static void convertHSBtoRGB (float h, const float s, float v, + uint8& r, uint8& g, uint8& b) throw() +{ + v *= 255.0f; + const uint8 intV = (uint8) roundFloatToInt (v); + + if (s == 0) + { + r = intV; + g = intV; + b = intV; + } + else + { + h = (h - floorf (h)) * 6.0f + 0.00001f; // need a small adjustment to compensate for rounding errors + const float f = h - floorf (h); + + const uint8 x = (uint8) roundFloatToInt (v * (1.0f - s)); + const float y = v * (1.0f - s * f); + const float z = v * (1.0f - (s * (1.0f - f))); + + if (h < 1.0f) + { + r = intV; + g = (uint8) roundFloatToInt (z); + b = x; + } + else if (h < 2.0f) + { + r = (uint8) roundFloatToInt (y); + g = intV; + b = x; + } + else if (h < 3.0f) + { + r = x; + g = intV; + b = (uint8) roundFloatToInt (z); + } + else if (h < 4.0f) + { + r = x; + g = (uint8) roundFloatToInt (y); + b = intV; + } + else if (h < 5.0f) + { + r = (uint8) roundFloatToInt (z); + g = x; + b = intV; + } + else if (h < 6.0f) + { + r = intV; + g = x; + b = (uint8) roundFloatToInt (y); + } + else + { + r = 0; + g = 0; + b = 0; + } + } +} + +Colour::Colour (const float hue, + const float saturation, + const float brightness, + const float alpha) throw() +{ + uint8 r = getRed(), g = getGreen(), b = getBlue(); + convertHSBtoRGB (hue, saturation, brightness, r, g, b); + + argb.setARGB (floatAlphaToInt (alpha), r, g, b); +} + +Colour::Colour (const float hue, + const float saturation, + const float brightness, + const uint8 alpha) throw() +{ + uint8 r = getRed(), g = getGreen(), b = getBlue(); + convertHSBtoRGB (hue, saturation, brightness, r, g, b); + + argb.setARGB (alpha, r, g, b); +} + +Colour::~Colour() throw() +{ +} + +const PixelARGB Colour::getPixelARGB() const throw() +{ + PixelARGB p (argb); + p.premultiply(); + return p; +} + +uint32 Colour::getARGB() const throw() +{ + return argb.getARGB(); +} + +bool Colour::isTransparent() const throw() +{ + return getAlpha() == 0; +} + +bool Colour::isOpaque() const throw() +{ + return getAlpha() == 0xff; +} + +const Colour Colour::withAlpha (const uint8 newAlpha) const throw() +{ + PixelARGB newCol (argb); + newCol.setAlpha (newAlpha); + return Colour (newCol.getARGB()); +} + +const Colour Colour::withAlpha (const float newAlpha) const throw() +{ + jassert (newAlpha >= 0 && newAlpha <= 1.0f); + + PixelARGB newCol (argb); + newCol.setAlpha (floatAlphaToInt (newAlpha)); + return Colour (newCol.getARGB()); +} + +const Colour Colour::withMultipliedAlpha (const float alphaMultiplier) const throw() +{ + jassert (alphaMultiplier >= 0); + + PixelARGB newCol (argb); + newCol.setAlpha ((uint8) jmin (0xff, roundFloatToInt (alphaMultiplier * newCol.getAlpha()))); + return Colour (newCol.getARGB()); +} + +const Colour Colour::overlaidWith (const Colour& src) const throw() +{ + const int destAlpha = getAlpha(); + + if (destAlpha > 0) + { + const int invA = 0xff - (int) src.getAlpha(); + const int resA = 0xff - (((0xff - destAlpha) * invA) >> 8); + + if (resA > 0) + { + const int da = (invA * destAlpha) / resA; + + return Colour ((uint8) (src.getRed() + ((((int) getRed() - src.getRed()) * da) >> 8)), + (uint8) (src.getGreen() + ((((int) getGreen() - src.getGreen()) * da) >> 8)), + (uint8) (src.getBlue() + ((((int) getBlue() - src.getBlue()) * da) >> 8)), + (uint8) resA); + } + + return *this; + } + else + { + return src; + } +} + +float Colour::getFloatRed() const throw() +{ + return getRed() * oneOver255; +} + +float Colour::getFloatGreen() const throw() +{ + return getGreen() * oneOver255; +} + +float Colour::getFloatBlue() const throw() +{ + return getBlue() * oneOver255; +} + +float Colour::getFloatAlpha() const throw() +{ + return getAlpha() * oneOver255; +} + +void Colour::getHSB (float& h, float& s, float& v) const throw() +{ + const int r = getRed(); + const int g = getGreen(); + const int b = getBlue(); + + const int hi = jmax (r, g, b); + const int lo = jmin (r, g, b); + + if (hi != 0) + { + s = (hi - lo) / (float) hi; + + if (s != 0) + { + const float invDiff = 1.0f / (hi - lo); + + const float red = (hi - r) * invDiff; + const float green = (hi - g) * invDiff; + const float blue = (hi - b) * invDiff; + + if (r == hi) + h = blue - green; + else if (g == hi) + h = 2.0f + red - blue; + else + h = 4.0f + green - red; + + h *= 1.0f / 6.0f; + + if (h < 0) + ++h; + } + else + { + h = 0; + } + } + else + { + s = 0; + h = 0; + } + + v = hi * oneOver255; +} + +float Colour::getHue() const throw() +{ + float h, s, b; + getHSB (h, s, b); + return h; +} + +const Colour Colour::withHue (const float hue) const throw() +{ + float h, s, b; + getHSB (h, s, b); + + return Colour (hue, s, b, getAlpha()); +} + +const Colour Colour::withRotatedHue (const float amountToRotate) const throw() +{ + float h, s, b; + getHSB (h, s, b); + + h += amountToRotate; + h -= floorf (h); + + return Colour (h, s, b, getAlpha()); +} + +float Colour::getSaturation() const throw() +{ + float h, s, b; + getHSB (h, s, b); + return s; +} + +const Colour Colour::withSaturation (const float saturation) const throw() +{ + float h, s, b; + getHSB (h, s, b); + + return Colour (h, saturation, b, getAlpha()); +} + +const Colour Colour::withMultipliedSaturation (const float amount) const throw() +{ + float h, s, b; + getHSB (h, s, b); + + return Colour (h, jmin (1.0f, s * amount), b, getAlpha()); +} + +float Colour::getBrightness() const throw() +{ + float h, s, b; + getHSB (h, s, b); + return b; +} + +const Colour Colour::withBrightness (const float brightness) const throw() +{ + float h, s, b; + getHSB (h, s, b); + + return Colour (h, s, brightness, getAlpha()); +} + +const Colour Colour::withMultipliedBrightness (const float amount) const throw() +{ + float h, s, b; + getHSB (h, s, b); + + b *= amount; + + if (b > 1.0f) + b = 1.0f; + + return Colour (h, s, b, getAlpha()); +} + +const Colour Colour::brighter (float amount) const throw() +{ + amount = 1.0f / (1.0f + amount); + + return Colour ((uint8) (255 - (amount * (255 - getRed()))), + (uint8) (255 - (amount * (255 - getGreen()))), + (uint8) (255 - (amount * (255 - getBlue()))), + getAlpha()); +} + +const Colour Colour::darker (float amount) const throw() +{ + amount = 1.0f / (1.0f + amount); + + return Colour ((uint8) (amount * getRed()), + (uint8) (amount * getGreen()), + (uint8) (amount * getBlue()), + getAlpha()); +} + +const Colour Colour::greyLevel (const float brightness) throw() +{ + const uint8 level + = (uint8) jlimit (0x00, 0xff, roundFloatToInt (brightness * 255.0f)); + + return Colour (level, level, level); +} + +const Colour Colour::contrasting (const float amount) const throw() +{ + return overlaidWith ((((int) getRed() + (int) getGreen() + (int) getBlue() >= 3 * 128) + ? Colours::black + : Colours::white).withAlpha (amount)); +} + +const Colour Colour::contrasting (const Colour& colour1, + const Colour& colour2) throw() +{ + const float b1 = colour1.getBrightness(); + const float b2 = colour2.getBrightness(); + float best = 0.0f; + float bestDist = 0.0f; + + for (float i = 0.0f; i < 1.0f; i += 0.02f) + { + const float d1 = fabsf (i - b1); + const float d2 = fabsf (i - b2); + const float dist = jmin (d1, d2, 1.0f - d1, 1.0f - d2); + + if (dist > bestDist) + { + best = i; + bestDist = dist; + } + } + + return colour1.overlaidWith (colour2.withMultipliedAlpha (0.5f)) + .withBrightness (best); +} + +const String Colour::toString() const throw() +{ + return String::toHexString ((int) argb.getARGB()); +} + +const Colour Colour::fromString (const String& encodedColourString) +{ + return Colour ((uint32) encodedColourString.getHexValue32()); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_Colour.cpp *********/ + +/********* Start of inlined file: juce_ColourGradient.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +ColourGradient::ColourGradient() throw() + : colours (4) +{ +#ifdef JUCE_DEBUG + x1 = 987654.0f; +#endif +} + +ColourGradient::ColourGradient (const Colour& colour1, + const float x1_, + const float y1_, + const Colour& colour2, + const float x2_, + const float y2_, + const bool isRadial_) throw() + : x1 (x1_), + y1 (y1_), + x2 (x2_), + y2 (y2_), + isRadial (isRadial_), + colours (4) +{ + colours.add (0); + colours.add (colour1.getPixelARGB().getARGB()); + + colours.add (1 << 16); + colours.add (colour2.getPixelARGB().getARGB()); +} + +ColourGradient::~ColourGradient() throw() +{ +} + +void ColourGradient::clearColours() throw() +{ + colours.clear(); +} + +void ColourGradient::addColour (const double proportionAlongGradient, + const Colour& colour) throw() +{ + // must be within the two end-points + jassert (proportionAlongGradient >= 0 && proportionAlongGradient <= 1.0); + + const uint32 pos = jlimit (0, 65535, roundDoubleToInt (proportionAlongGradient * 65536.0)); + + int i; + for (i = 0; i < colours.size(); i += 2) + if (colours.getUnchecked(i) > pos) + break; + + colours.insert (i, pos); + colours.insert (i + 1, colour.getPixelARGB().getARGB()); +} + +void ColourGradient::multiplyOpacity (const float multiplier) throw() +{ + for (int i = 1; i < colours.size(); i += 2) + { + PixelARGB pix (colours.getUnchecked(i)); + pix.multiplyAlpha (multiplier); + colours.set (i, pix.getARGB()); + } +} + +int ColourGradient::getNumColours() const throw() +{ + return colours.size() >> 1; +} + +double ColourGradient::getColourPosition (const int index) const throw() +{ + return colours [index << 1]; +} + +const Colour ColourGradient::getColour (const int index) const throw() +{ + PixelARGB pix (colours [(index << 1) + 1]); + pix.unpremultiply(); + return Colour (pix.getARGB()); +} + +PixelARGB* ColourGradient::createLookupTable (int& numEntries) const throw() +{ +#ifdef JUCE_DEBUG + // trying to use the object without setting its co-ordinates? Have a careful read of + // the comments for the constructors. + jassert (x1 != 987654.0f); +#endif + + const int numColours = colours.size() >> 1; + + float tx1 = x1, ty1 = y1, tx2 = x2, ty2 = y2; + transform.transformPoint (tx1, ty1); + transform.transformPoint (tx2, ty2); + const double distance = juce_hypot (tx1 - tx2, ty1 - ty2); + + numEntries = jlimit (1, (numColours - 1) << 8, 3 * (int) distance); + + PixelARGB* const lookupTable = (PixelARGB*) juce_calloc (numEntries * sizeof (PixelARGB)); + + if (numColours >= 2) + { + jassert (colours.getUnchecked (0) == 0); // the first colour specified has to go at position 0 + + PixelARGB pix1 (colours.getUnchecked (1)); + int index = 0; + + for (int j = 2; j < colours.size(); j += 2) + { + const int numToDo = ((colours.getUnchecked (j) * numEntries) >> 16) - index; + const PixelARGB pix2 (colours.getUnchecked (j + 1)); + + for (int i = 0; i < numToDo; ++i) + { + jassert (index >= 0 && index < numEntries); + + lookupTable[index] = pix1; + lookupTable[index].tween (pix2, (i << 8) / numToDo); + ++index; + } + + pix1 = pix2; + } + + while (index < numEntries) + lookupTable [index++] = pix1; + } + else + { + jassertfalse // no colours specified! + } + + return lookupTable; +} + +bool ColourGradient::isOpaque() const throw() +{ + for (int i = 1; i < colours.size(); i += 2) + if (PixelARGB (colours.getUnchecked(i)).getAlpha() < 0xff) + return false; + + return true; +} + +bool ColourGradient::isInvisible() const throw() +{ + for (int i = 1; i < colours.size(); i += 2) + if (PixelARGB (colours.getUnchecked(i)).getAlpha() > 0) + return false; + + return true; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ColourGradient.cpp *********/ + +/********* Start of inlined file: juce_Colours.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +const Colour Colours::transparentBlack (0); +const Colour Colours::transparentWhite (0x00ffffff); + +const Colour Colours::aliceblue (0xfff0f8ff); +const Colour Colours::antiquewhite (0xfffaebd7); +const Colour Colours::aqua (0xff00ffff); +const Colour Colours::aquamarine (0xff7fffd4); +const Colour Colours::azure (0xfff0ffff); +const Colour Colours::beige (0xfff5f5dc); +const Colour Colours::bisque (0xffffe4c4); +const Colour Colours::black (0xff000000); +const Colour Colours::blanchedalmond (0xffffebcd); +const Colour Colours::blue (0xff0000ff); +const Colour Colours::blueviolet (0xff8a2be2); +const Colour Colours::brown (0xffa52a2a); +const Colour Colours::burlywood (0xffdeb887); +const Colour Colours::cadetblue (0xff5f9ea0); +const Colour Colours::chartreuse (0xff7fff00); +const Colour Colours::chocolate (0xffd2691e); +const Colour Colours::coral (0xffff7f50); +const Colour Colours::cornflowerblue (0xff6495ed); +const Colour Colours::cornsilk (0xfffff8dc); +const Colour Colours::crimson (0xffdc143c); +const Colour Colours::cyan (0xff00ffff); +const Colour Colours::darkblue (0xff00008b); +const Colour Colours::darkcyan (0xff008b8b); +const Colour Colours::darkgoldenrod (0xffb8860b); +const Colour Colours::darkgrey (0xff555555); +const Colour Colours::darkgreen (0xff006400); +const Colour Colours::darkkhaki (0xffbdb76b); +const Colour Colours::darkmagenta (0xff8b008b); +const Colour Colours::darkolivegreen (0xff556b2f); +const Colour Colours::darkorange (0xffff8c00); +const Colour Colours::darkorchid (0xff9932cc); +const Colour Colours::darkred (0xff8b0000); +const Colour Colours::darksalmon (0xffe9967a); +const Colour Colours::darkseagreen (0xff8fbc8f); +const Colour Colours::darkslateblue (0xff483d8b); +const Colour Colours::darkslategrey (0xff2f4f4f); +const Colour Colours::darkturquoise (0xff00ced1); +const Colour Colours::darkviolet (0xff9400d3); +const Colour Colours::deeppink (0xffff1493); +const Colour Colours::deepskyblue (0xff00bfff); +const Colour Colours::dimgrey (0xff696969); +const Colour Colours::dodgerblue (0xff1e90ff); +const Colour Colours::firebrick (0xffb22222); +const Colour Colours::floralwhite (0xfffffaf0); +const Colour Colours::forestgreen (0xff228b22); +const Colour Colours::fuchsia (0xffff00ff); +const Colour Colours::gainsboro (0xffdcdcdc); +const Colour Colours::gold (0xffffd700); +const Colour Colours::goldenrod (0xffdaa520); +const Colour Colours::grey (0xff808080); +const Colour Colours::green (0xff008000); +const Colour Colours::greenyellow (0xffadff2f); +const Colour Colours::honeydew (0xfff0fff0); +const Colour Colours::hotpink (0xffff69b4); +const Colour Colours::indianred (0xffcd5c5c); +const Colour Colours::indigo (0xff4b0082); +const Colour Colours::ivory (0xfffffff0); +const Colour Colours::khaki (0xfff0e68c); +const Colour Colours::lavender (0xffe6e6fa); +const Colour Colours::lavenderblush (0xfffff0f5); +const Colour Colours::lemonchiffon (0xfffffacd); +const Colour Colours::lightblue (0xffadd8e6); +const Colour Colours::lightcoral (0xfff08080); +const Colour Colours::lightcyan (0xffe0ffff); +const Colour Colours::lightgoldenrodyellow (0xfffafad2); +const Colour Colours::lightgreen (0xff90ee90); +const Colour Colours::lightgrey (0xffd3d3d3); +const Colour Colours::lightpink (0xffffb6c1); +const Colour Colours::lightsalmon (0xffffa07a); +const Colour Colours::lightseagreen (0xff20b2aa); +const Colour Colours::lightskyblue (0xff87cefa); +const Colour Colours::lightslategrey (0xff778899); +const Colour Colours::lightsteelblue (0xffb0c4de); +const Colour Colours::lightyellow (0xffffffe0); +const Colour Colours::lime (0xff00ff00); +const Colour Colours::limegreen (0xff32cd32); +const Colour Colours::linen (0xfffaf0e6); +const Colour Colours::magenta (0xffff00ff); +const Colour Colours::maroon (0xff800000); +const Colour Colours::mediumaquamarine (0xff66cdaa); +const Colour Colours::mediumblue (0xff0000cd); +const Colour Colours::mediumorchid (0xffba55d3); +const Colour Colours::mediumpurple (0xff9370db); +const Colour Colours::mediumseagreen (0xff3cb371); +const Colour Colours::mediumslateblue (0xff7b68ee); +const Colour Colours::mediumspringgreen (0xff00fa9a); +const Colour Colours::mediumturquoise (0xff48d1cc); +const Colour Colours::mediumvioletred (0xffc71585); +const Colour Colours::midnightblue (0xff191970); +const Colour Colours::mintcream (0xfff5fffa); +const Colour Colours::mistyrose (0xffffe4e1); +const Colour Colours::navajowhite (0xffffdead); +const Colour Colours::navy (0xff000080); +const Colour Colours::oldlace (0xfffdf5e6); +const Colour Colours::olive (0xff808000); +const Colour Colours::olivedrab (0xff6b8e23); +const Colour Colours::orange (0xffffa500); +const Colour Colours::orangered (0xffff4500); +const Colour Colours::orchid (0xffda70d6); +const Colour Colours::palegoldenrod (0xffeee8aa); +const Colour Colours::palegreen (0xff98fb98); +const Colour Colours::paleturquoise (0xffafeeee); +const Colour Colours::palevioletred (0xffdb7093); +const Colour Colours::papayawhip (0xffffefd5); +const Colour Colours::peachpuff (0xffffdab9); +const Colour Colours::peru (0xffcd853f); +const Colour Colours::pink (0xffffc0cb); +const Colour Colours::plum (0xffdda0dd); +const Colour Colours::powderblue (0xffb0e0e6); +const Colour Colours::purple (0xff800080); +const Colour Colours::red (0xffff0000); +const Colour Colours::rosybrown (0xffbc8f8f); +const Colour Colours::royalblue (0xff4169e1); +const Colour Colours::saddlebrown (0xff8b4513); +const Colour Colours::salmon (0xfffa8072); +const Colour Colours::sandybrown (0xfff4a460); +const Colour Colours::seagreen (0xff2e8b57); +const Colour Colours::seashell (0xfffff5ee); +const Colour Colours::sienna (0xffa0522d); +const Colour Colours::silver (0xffc0c0c0); +const Colour Colours::skyblue (0xff87ceeb); +const Colour Colours::slateblue (0xff6a5acd); +const Colour Colours::slategrey (0xff708090); +const Colour Colours::snow (0xfffffafa); +const Colour Colours::springgreen (0xff00ff7f); +const Colour Colours::steelblue (0xff4682b4); +const Colour Colours::tan (0xffd2b48c); +const Colour Colours::teal (0xff008080); +const Colour Colours::thistle (0xffd8bfd8); +const Colour Colours::tomato (0xffff6347); +const Colour Colours::turquoise (0xff40e0d0); +const Colour Colours::violet (0xffee82ee); +const Colour Colours::wheat (0xfff5deb3); +const Colour Colours::white (0xffffffff); +const Colour Colours::whitesmoke (0xfff5f5f5); +const Colour Colours::yellow (0xffffff00); +const Colour Colours::yellowgreen (0xff9acd32); + +const Colour Colours::findColourForName (const String& colourName, + const Colour& defaultColour) +{ + static const int presets[] = + { + // (first value is the string's hashcode, second is ARGB) + + 0x05978fff, 0xff000000, /* black */ + 0x06bdcc29, 0xffffffff, /* white */ + 0x002e305a, 0xff0000ff, /* blue */ + 0x00308adf, 0xff808080, /* grey */ + 0x05e0cf03, 0xff008000, /* green */ + 0x0001b891, 0xffff0000, /* red */ + 0xd43c6474, 0xffffff00, /* yellow */ + 0x620886da, 0xfff0f8ff, /* aliceblue */ + 0x20a2676a, 0xfffaebd7, /* antiquewhite */ + 0x002dcebc, 0xff00ffff, /* aqua */ + 0x46bb5f7e, 0xff7fffd4, /* aquamarine */ + 0x0590228f, 0xfff0ffff, /* azure */ + 0x05947fe4, 0xfff5f5dc, /* beige */ + 0xad388e35, 0xffffe4c4, /* bisque */ + 0x00674f7e, 0xffffebcd, /* blanchedalmond */ + 0x39129959, 0xff8a2be2, /* blueviolet */ + 0x059a8136, 0xffa52a2a, /* brown */ + 0x89cea8f9, 0xffdeb887, /* burlywood */ + 0x0fa260cf, 0xff5f9ea0, /* cadetblue */ + 0x6b748956, 0xff7fff00, /* chartreuse */ + 0x2903623c, 0xffd2691e, /* chocolate */ + 0x05a74431, 0xffff7f50, /* coral */ + 0x618d42dd, 0xff6495ed, /* cornflowerblue */ + 0xe4b479fd, 0xfffff8dc, /* cornsilk */ + 0x3d8c4edf, 0xffdc143c, /* crimson */ + 0x002ed323, 0xff00ffff, /* cyan */ + 0x67cc74d0, 0xff00008b, /* darkblue */ + 0x67cd1799, 0xff008b8b, /* darkcyan */ + 0x31bbd168, 0xffb8860b, /* darkgoldenrod */ + 0x67cecf55, 0xff555555, /* darkgrey */ + 0x920b194d, 0xff006400, /* darkgreen */ + 0x923edd4c, 0xffbdb76b, /* darkkhaki */ + 0x5c293873, 0xff8b008b, /* darkmagenta */ + 0x6b6671fe, 0xff556b2f, /* darkolivegreen */ + 0xbcfd2524, 0xffff8c00, /* darkorange */ + 0xbcfdf799, 0xff9932cc, /* darkorchid */ + 0x55ee0d5b, 0xff8b0000, /* darkred */ + 0xc2e5f564, 0xffe9967a, /* darksalmon */ + 0x61be858a, 0xff8fbc8f, /* darkseagreen */ + 0xc2b0f2bd, 0xff483d8b, /* darkslateblue */ + 0xc2b34d42, 0xff2f4f4f, /* darkslategrey */ + 0x7cf2b06b, 0xff00ced1, /* darkturquoise */ + 0xc8769375, 0xff9400d3, /* darkviolet */ + 0x25832862, 0xffff1493, /* deeppink */ + 0xfcad568f, 0xff00bfff, /* deepskyblue */ + 0x634c8b67, 0xff696969, /* dimgrey */ + 0x45c1ce55, 0xff1e90ff, /* dodgerblue */ + 0xef19e3cb, 0xffb22222, /* firebrick */ + 0xb852b195, 0xfffffaf0, /* floralwhite */ + 0xd086fd06, 0xff228b22, /* forestgreen */ + 0xe106b6d7, 0xffff00ff, /* fuchsia */ + 0x7880d61e, 0xffdcdcdc, /* gainsboro */ + 0x00308060, 0xffffd700, /* gold */ + 0xb3b3bc1e, 0xffdaa520, /* goldenrod */ + 0xbab8a537, 0xffadff2f, /* greenyellow */ + 0xe4cacafb, 0xfff0fff0, /* honeydew */ + 0x41892743, 0xffff69b4, /* hotpink */ + 0xd5796f1a, 0xffcd5c5c, /* indianred */ + 0xb969fed2, 0xff4b0082, /* indigo */ + 0x05fef6a9, 0xfffffff0, /* ivory */ + 0x06149302, 0xfff0e68c, /* khaki */ + 0xad5a05c7, 0xffe6e6fa, /* lavender */ + 0x7c4d5b99, 0xfffff0f5, /* lavenderblush */ + 0x195756f0, 0xfffffacd, /* lemonchiffon */ + 0x28e4ea70, 0xffadd8e6, /* lightblue */ + 0xf3c7ccdb, 0xfff08080, /* lightcoral */ + 0x28e58d39, 0xffe0ffff, /* lightcyan */ + 0x21234e3c, 0xfffafad2, /* lightgoldenrodyellow */ + 0xf40157ad, 0xff90ee90, /* lightgreen */ + 0x28e744f5, 0xffd3d3d3, /* lightgrey */ + 0x28eb3b8c, 0xffffb6c1, /* lightpink */ + 0x9fb78304, 0xffffa07a, /* lightsalmon */ + 0x50632b2a, 0xff20b2aa, /* lightseagreen */ + 0x68fb7b25, 0xff87cefa, /* lightskyblue */ + 0xa8a35ba2, 0xff778899, /* lightslategrey */ + 0xa20d484f, 0xffb0c4de, /* lightsteelblue */ + 0xaa2cf10a, 0xffffffe0, /* lightyellow */ + 0x0032afd5, 0xff00ff00, /* lime */ + 0x607bbc4e, 0xff32cd32, /* limegreen */ + 0x06234efa, 0xfffaf0e6, /* linen */ + 0x316858a9, 0xffff00ff, /* magenta */ + 0xbf8ca470, 0xff800000, /* maroon */ + 0xbd58e0b3, 0xff66cdaa, /* mediumaquamarine */ + 0x967dfd4f, 0xff0000cd, /* mediumblue */ + 0x056f5c58, 0xffba55d3, /* mediumorchid */ + 0x07556b71, 0xff9370db, /* mediumpurple */ + 0x5369b689, 0xff3cb371, /* mediumseagreen */ + 0x066be19e, 0xff7b68ee, /* mediumslateblue */ + 0x3256b281, 0xff00fa9a, /* mediumspringgreen */ + 0xc0ad9f4c, 0xff48d1cc, /* mediumturquoise */ + 0x628e63dd, 0xffc71585, /* mediumvioletred */ + 0x168eb32a, 0xff191970, /* midnightblue */ + 0x4306b960, 0xfff5fffa, /* mintcream */ + 0x4cbc0e6b, 0xffffe4e1, /* mistyrose */ + 0xe97218a6, 0xffffdead, /* navajowhite */ + 0x00337bb6, 0xff000080, /* navy */ + 0xadd2d33e, 0xfffdf5e6, /* oldlace */ + 0x064ee1db, 0xff808000, /* olive */ + 0x9e33a98a, 0xff6b8e23, /* olivedrab */ + 0xc3de262e, 0xffffa500, /* orange */ + 0x58bebba3, 0xffff4500, /* orangered */ + 0xc3def8a3, 0xffda70d6, /* orchid */ + 0x28cb4834, 0xffeee8aa, /* palegoldenrod */ + 0x3d9dd619, 0xff98fb98, /* palegreen */ + 0x74022737, 0xffafeeee, /* paleturquoise */ + 0x15e2ebc8, 0xffdb7093, /* palevioletred */ + 0x5fd898e2, 0xffffefd5, /* papayawhip */ + 0x93e1b776, 0xffffdab9, /* peachpuff */ + 0x003472f8, 0xffcd853f, /* peru */ + 0x00348176, 0xffffc0cb, /* pink */ + 0x00348d94, 0xffdda0dd, /* plum */ + 0xd036be93, 0xffb0e0e6, /* powderblue */ + 0xc5c507bc, 0xff800080, /* purple */ + 0xa89d65b3, 0xffbc8f8f, /* rosybrown */ + 0xbd9413e1, 0xff4169e1, /* royalblue */ + 0xf456044f, 0xff8b4513, /* saddlebrown */ + 0xc9c6f66e, 0xfffa8072, /* salmon */ + 0x0bb131e1, 0xfff4a460, /* sandybrown */ + 0x34636c14, 0xff2e8b57, /* seagreen */ + 0x3507fb41, 0xfffff5ee, /* seashell */ + 0xca348772, 0xffa0522d, /* sienna */ + 0xca37d30d, 0xffc0c0c0, /* silver */ + 0x80da74fb, 0xff87ceeb, /* skyblue */ + 0x44a8dd73, 0xff6a5acd, /* slateblue */ + 0x44ab37f8, 0xff708090, /* slategrey */ + 0x0035f183, 0xfffffafa, /* snow */ + 0xd5440d16, 0xff00ff7f, /* springgreen */ + 0x3e1524a5, 0xff4682b4, /* steelblue */ + 0x0001bfa1, 0xffd2b48c, /* tan */ + 0x0036425c, 0xff008080, /* teal */ + 0xafc8858f, 0xffd8bfd8, /* thistle */ + 0xcc41600a, 0xffff6347, /* tomato */ + 0xfeea9b21, 0xff40e0d0, /* turquoise */ + 0xcf57947f, 0xffee82ee, /* violet */ + 0x06bdbae7, 0xfff5deb3, /* wheat */ + 0x10802ee6, 0xfff5f5f5, /* whitesmoke */ + 0xe1b5130f, 0xff9acd32 /* yellowgreen */ + }; + + const int hash = colourName.trim().toLowerCase().hashCode(); + + for (int i = 0; i < numElementsInArray (presets); i += 2) + if (presets [i] == hash) + return Colour (presets [i + 1]); + + return defaultColour; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_Colours.cpp *********/ + +/********* Start of inlined file: juce_EdgeTable.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +EdgeTable::EdgeTable (const int top_, + const int height_, + const OversamplingLevel oversampling_, + const int expectedEdgesPerLine) throw() + : top (top_), + height (height_), + maxEdgesPerLine (expectedEdgesPerLine), + lineStrideElements ((expectedEdgesPerLine << 1) + 1), + oversampling (oversampling_) +{ + table = (int*) juce_calloc ((height << (int)oversampling_) + * lineStrideElements * sizeof (int)); +} + +EdgeTable::EdgeTable (const EdgeTable& other) throw() + : table (0) +{ + operator= (other); +} + +const EdgeTable& EdgeTable::operator= (const EdgeTable& other) throw() +{ + juce_free (table); + + top = other.top; + height = other.height; + maxEdgesPerLine = other.maxEdgesPerLine; + lineStrideElements = other.lineStrideElements; + oversampling = other.oversampling; + + const int tableSize = (height << (int)oversampling) + * lineStrideElements * sizeof (int); + + table = (int*) juce_malloc (tableSize); + memcpy (table, other.table, tableSize); + + return *this; +} + +EdgeTable::~EdgeTable() throw() +{ + juce_free (table); +} + +void EdgeTable::remapTableForNumEdges (const int newNumEdgesPerLine) throw() +{ + if (newNumEdgesPerLine != maxEdgesPerLine) + { + maxEdgesPerLine = newNumEdgesPerLine; + + const int newLineStrideElements = maxEdgesPerLine * 2 + 1; + int* const newTable = (int*) juce_malloc ((height << (int) oversampling) + * newLineStrideElements * sizeof (int)); + + for (int i = 0; i < (height << (int) oversampling); ++i) + { + const int* srcLine = table + lineStrideElements * i; + int* dstLine = newTable + newLineStrideElements * i; + + int num = *srcLine++; + *dstLine++ = num; + + num <<= 1; + while (--num >= 0) + *dstLine++ = *srcLine++; + } + + juce_free (table); + table = newTable; + lineStrideElements = newLineStrideElements; + } +} + +void EdgeTable::optimiseTable() throw() +{ + int maxLineElements = 0; + + for (int i = height; --i >= 0;) + maxLineElements = jmax (maxLineElements, + table [i * lineStrideElements]); + + remapTableForNumEdges (maxLineElements); +} + +void EdgeTable::addEdgePoint (const int x, const int y, const int winding) throw() +{ + jassert (y >= 0 && y < (height << oversampling)) + + int* lineStart = table + lineStrideElements * y; + int n = lineStart[0]; + + if (n >= maxEdgesPerLine) + { + remapTableForNumEdges (maxEdgesPerLine + juce_edgeTableDefaultEdgesPerLine); + lineStart = table + lineStrideElements * y; + } + + n <<= 1; + + int* const line = lineStart + 1; + + while (n > 0) + { + const int cx = line [n - 2]; + + if (cx <= x) + break; + + line [n] = cx; + line [n + 1] = line [n - 1]; + n -= 2; + } + + line [n] = x; + line [n + 1] = winding; + + lineStart[0]++; +} + +void EdgeTable::addPath (const Path& path, + const AffineTransform& transform) throw() +{ + const int windingAmount = 256 / (1 << (int) oversampling); + const float timesOversampling = (float) (1 << (int) oversampling); + + const int bottomLimit = (height << (int) oversampling); + + PathFlatteningIterator iter (path, transform); + + while (iter.next()) + { + int y1 = roundFloatToInt (iter.y1 * timesOversampling) - (top << (int) oversampling); + int y2 = roundFloatToInt (iter.y2 * timesOversampling) - (top << (int) oversampling); + + if (y1 != y2) + { + const double x1 = 256.0 * iter.x1; + const double x2 = 256.0 * iter.x2; + + const double multiplier = (x2 - x1) / (y2 - y1); + + const int oldY1 = y1; + int winding; + + if (y1 > y2) + { + swapVariables (y1, y2); + winding = windingAmount; + } + else + { + winding = -windingAmount; + } + + jassert (y1 < y2); + + if (y1 < 0) + y1 = 0; + + if (y2 > bottomLimit) + y2 = bottomLimit; + + while (y1 < y2) + { + addEdgePoint (roundDoubleToInt (x1 + multiplier * (y1 - oldY1)), + y1, + winding); + + ++y1; + } + } + } + + if (! path.isUsingNonZeroWinding()) + { + // if it's an alternate-winding path, we need to go through and + // make sure all the windings are alternating. + + int* lineStart = table; + + for (int i = height << (int) oversampling; --i >= 0;) + { + int* line = lineStart; + lineStart += lineStrideElements; + + int num = *line; + + while (--num >= 0) + { + line += 2; + *line = abs (*line); + + if (--num >= 0) + { + line += 2; + *line = -abs (*line); + } + } + } + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_EdgeTable.cpp *********/ + +/********* Start of inlined file: juce_Graphics.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +static const Graphics::ResamplingQuality defaultQuality = Graphics::mediumResamplingQuality; + +#define MINIMUM_COORD -0x3fffffff +#define MAXIMUM_COORD 0x3fffffff + +#undef ASSERT_COORDS_ARE_SENSIBLE_NUMBERS +#define ASSERT_COORDS_ARE_SENSIBLE_NUMBERS(x, y, w, h) \ + jassert ((int) x >= MINIMUM_COORD \ + && (int) x <= MAXIMUM_COORD \ + && (int) y >= MINIMUM_COORD \ + && (int) y <= MAXIMUM_COORD \ + && (int) w >= MINIMUM_COORD \ + && (int) w <= MAXIMUM_COORD \ + && (int) h >= MINIMUM_COORD \ + && (int) h <= MAXIMUM_COORD); + +LowLevelGraphicsContext::LowLevelGraphicsContext() +{ +} + +LowLevelGraphicsContext::~LowLevelGraphicsContext() +{ +} + +Graphics::Graphics (Image& imageToDrawOnto) throw() + : context (imageToDrawOnto.createLowLevelContext()), + ownsContext (true), + state (new GraphicsState()), + saveStatePending (false) +{ +} + +Graphics::Graphics (LowLevelGraphicsContext* const internalContext) throw() + : context (internalContext), + ownsContext (false), + state (new GraphicsState()), + saveStatePending (false) +{ +} + +Graphics::~Graphics() throw() +{ + delete state; + + if (ownsContext) + delete context; +} + +void Graphics::resetToDefaultState() throw() +{ + setColour (Colours::black); + state->font.resetToDefaultState(); + state->quality = defaultQuality; +} + +bool Graphics::isVectorDevice() const throw() +{ + return context->isVectorDevice(); +} + +bool Graphics::reduceClipRegion (const int x, const int y, + const int w, const int h) throw() +{ + saveStateIfPending(); + return context->reduceClipRegion (x, y, w, h); +} + +bool Graphics::reduceClipRegion (const RectangleList& clipRegion) throw() +{ + saveStateIfPending(); + return context->reduceClipRegion (clipRegion); +} + +void Graphics::excludeClipRegion (const int x, const int y, + const int w, const int h) throw() +{ + saveStateIfPending(); + context->excludeClipRegion (x, y, w, h); +} + +bool Graphics::isClipEmpty() const throw() +{ + return context->isClipEmpty(); +} + +const Rectangle Graphics::getClipBounds() const throw() +{ + return context->getClipBounds(); +} + +void Graphics::saveState() throw() +{ + saveStateIfPending(); + saveStatePending = true; +} + +void Graphics::restoreState() throw() +{ + if (saveStatePending) + { + saveStatePending = false; + } + else + { + const int stackSize = stateStack.size(); + + if (stackSize > 0) + { + context->restoreState(); + + delete state; + state = stateStack.getUnchecked (stackSize - 1); + + stateStack.removeLast (1, false); + } + else + { + // Trying to call restoreState() more times than you've called saveState() ! + // Be careful to correctly match each saveState() with exactly one call to restoreState(). + jassertfalse + } + } +} + +void Graphics::saveStateIfPending() throw() +{ + if (saveStatePending) + { + saveStatePending = false; + + context->saveState(); + stateStack.add (new GraphicsState (*state)); + } +} + +void Graphics::setOrigin (const int newOriginX, + const int newOriginY) throw() +{ + saveStateIfPending(); + context->setOrigin (newOriginX, newOriginY); +} + +bool Graphics::clipRegionIntersects (const int x, const int y, + const int w, const int h) const throw() +{ + return context->clipRegionIntersects (x, y, w, h); +} + +void Graphics::setColour (const Colour& newColour) throw() +{ + saveStateIfPending(); + state->colour = newColour; + deleteAndZero (state->brush); +} + +const Colour& Graphics::getCurrentColour() const throw() +{ + return state->colour; +} + +void Graphics::setOpacity (const float newOpacity) throw() +{ + saveStateIfPending(); + state->colour = state->colour.withAlpha (newOpacity); +} + +void Graphics::setBrush (const Brush* const newBrush) throw() +{ + saveStateIfPending(); + delete state->brush; + + if (newBrush != 0) + state->brush = newBrush->createCopy(); + else + state->brush = 0; +} + +Graphics::GraphicsState::GraphicsState() throw() + : colour (Colours::black), + brush (0), + quality (defaultQuality) +{ +} + +Graphics::GraphicsState::GraphicsState (const GraphicsState& other) throw() + : colour (other.colour), + brush (other.brush != 0 ? other.brush->createCopy() : 0), + font (other.font), + quality (other.quality) +{ +} + +Graphics::GraphicsState::~GraphicsState() throw() +{ + delete brush; +} + +void Graphics::setFont (const Font& newFont) throw() +{ + saveStateIfPending(); + state->font = newFont; +} + +void Graphics::setFont (const float newFontHeight, + const int newFontStyleFlags) throw() +{ + saveStateIfPending(); + state->font.setSizeAndStyle (newFontHeight, newFontStyleFlags, 1.0f, 0.0f); +} + +const Font& Graphics::getCurrentFont() const throw() +{ + return state->font; +} + +void Graphics::drawSingleLineText (const String& text, + const int startX, + const int baselineY) const throw() +{ + if (text.isNotEmpty() + && startX < context->getClipBounds().getRight()) + { + GlyphArrangement arr; + arr.addLineOfText (state->font, text, (float) startX, (float) baselineY); + arr.draw (*this); + } +} + +void Graphics::drawTextAsPath (const String& text, + const AffineTransform& transform) const throw() +{ + if (text.isNotEmpty()) + { + GlyphArrangement arr; + arr.addLineOfText (state->font, text, 0.0f, 0.0f); + arr.draw (*this, transform); + } +} + +void Graphics::drawMultiLineText (const String& text, + const int startX, + const int baselineY, + const int maximumLineWidth) const throw() +{ + if (text.isNotEmpty() + && startX < context->getClipBounds().getRight()) + { + GlyphArrangement arr; + arr.addJustifiedText (state->font, text, + (float) startX, (float) baselineY, (float) maximumLineWidth, + Justification::left); + arr.draw (*this); + } +} + +void Graphics::drawText (const String& text, + const int x, + const int y, + const int width, + const int height, + const Justification& justificationType, + const bool useEllipsesIfTooBig) const throw() +{ + if (text.isNotEmpty() && context->clipRegionIntersects (x, y, width, height)) + { + GlyphArrangement arr; + + arr.addCurtailedLineOfText (state->font, text, + 0.0f, 0.0f, (float)width, + useEllipsesIfTooBig); + + arr.justifyGlyphs (0, arr.getNumGlyphs(), + (float) x, (float) y, + (float) width, (float) height, + justificationType); + arr.draw (*this); + } +} + +void Graphics::drawFittedText (const String& text, + const int x, + const int y, + const int width, + const int height, + const Justification& justification, + const int maximumNumberOfLines, + const float minimumHorizontalScale) const throw() +{ + if (text.isNotEmpty() + && width > 0 && height > 0 + && context->clipRegionIntersects (x, y, width, height)) + { + GlyphArrangement arr; + + arr.addFittedText (state->font, text, + (float) x, (float) y, + (float) width, (float) height, + justification, + maximumNumberOfLines, + minimumHorizontalScale); + + arr.draw (*this); + } +} + +void Graphics::fillRect (int x, + int y, + int width, + int height) const throw() +{ + // passing in a silly number can cause maths problems in rendering! + ASSERT_COORDS_ARE_SENSIBLE_NUMBERS (x, y, width, height); + + SolidColourBrush colourBrush (state->colour); + (state->brush != 0 ? *(state->brush) : (Brush&) colourBrush).paintRectangle (*context, x, y, width, height); +} + +void Graphics::fillRect (const Rectangle& r) const throw() +{ + fillRect (r.getX(), + r.getY(), + r.getWidth(), + r.getHeight()); +} + +void Graphics::fillRect (const float x, + const float y, + const float width, + const float height) const throw() +{ + // passing in a silly number can cause maths problems in rendering! + ASSERT_COORDS_ARE_SENSIBLE_NUMBERS (x, y, width, height); + + Path p; + p.addRectangle (x, y, width, height); + fillPath (p); +} + +void Graphics::setPixel (int x, int y) const throw() +{ + if (context->clipRegionIntersects (x, y, 1, 1)) + { + SolidColourBrush colourBrush (state->colour); + (state->brush != 0 ? *(state->brush) : (Brush&) colourBrush).paintRectangle (*context, x, y, 1, 1); + } +} + +void Graphics::fillAll() const throw() +{ + fillRect (context->getClipBounds()); +} + +void Graphics::fillAll (const Colour& colourToUse) const throw() +{ + if (! colourToUse.isTransparent()) + { + const Rectangle clip (context->getClipBounds()); + + context->fillRectWithColour (clip.getX(), clip.getY(), clip.getWidth(), clip.getHeight(), + colourToUse, false); + } +} + +void Graphics::fillPath (const Path& path, + const AffineTransform& transform) const throw() +{ + if ((! context->isClipEmpty()) && ! path.isEmpty()) + { + SolidColourBrush colourBrush (state->colour); + (state->brush != 0 ? *(state->brush) : (Brush&) colourBrush).paintPath (*context, path, transform); + } +} + +void Graphics::strokePath (const Path& path, + const PathStrokeType& strokeType, + const AffineTransform& transform) const throw() +{ + if (! state->colour.isTransparent()) + { + Path stroke; + strokeType.createStrokedPath (stroke, path, transform); + fillPath (stroke); + } +} + +void Graphics::drawRect (const int x, + const int y, + const int width, + const int height, + const int lineThickness) const throw() +{ + // passing in a silly number can cause maths problems in rendering! + ASSERT_COORDS_ARE_SENSIBLE_NUMBERS (x, y, width, height); + + SolidColourBrush colourBrush (state->colour); + Brush& b = (state->brush != 0 ? *(state->brush) : (Brush&) colourBrush); + + b.paintRectangle (*context, x, y, width, lineThickness); + b.paintRectangle (*context, x, y + lineThickness, lineThickness, height - lineThickness * 2); + b.paintRectangle (*context, x + width - lineThickness, y + lineThickness, lineThickness, height - lineThickness * 2); + b.paintRectangle (*context, x, y + height - lineThickness, width, lineThickness); +} + +void Graphics::drawRect (const float x, + const float y, + const float width, + const float height, + const float lineThickness) const throw() +{ + // passing in a silly number can cause maths problems in rendering! + ASSERT_COORDS_ARE_SENSIBLE_NUMBERS (x, y, width, height); + + Path p; + p.addRectangle (x, y, width, lineThickness); + p.addRectangle (x, y + lineThickness, lineThickness, height - lineThickness * 2.0f); + p.addRectangle (x + width - lineThickness, y + lineThickness, lineThickness, height - lineThickness * 2.0f); + p.addRectangle (x, y + height - lineThickness, width, lineThickness); + fillPath (p); +} + +void Graphics::drawRect (const Rectangle& r, + const int lineThickness) const throw() +{ + drawRect (r.getX(), r.getY(), + r.getWidth(), r.getHeight(), + lineThickness); +} + +void Graphics::drawBevel (const int x, + const int y, + const int width, + const int height, + const int bevelThickness, + const Colour& topLeftColour, + const Colour& bottomRightColour, + const bool useGradient) const throw() +{ + // passing in a silly number can cause maths problems in rendering! + ASSERT_COORDS_ARE_SENSIBLE_NUMBERS (x, y, width, height); + + if (clipRegionIntersects (x, y, width, height)) + { + const float oldOpacity = state->colour.getFloatAlpha(); + const float ramp = oldOpacity / bevelThickness; + + for (int i = bevelThickness; --i >= 0;) + { + const float op = useGradient ? ramp * (bevelThickness - i) + : oldOpacity; + + context->fillRectWithColour (x + i, y + i, width - i * 2, 1, topLeftColour.withMultipliedAlpha (op), false); + context->fillRectWithColour (x + i, y + i + 1, 1, height - i * 2 - 2, topLeftColour.withMultipliedAlpha (op * 0.75f), false); + context->fillRectWithColour (x + i, y + height - i - 1, width - i * 2, 1, bottomRightColour.withMultipliedAlpha (op), false); + context->fillRectWithColour (x + width - i - 1, y + i + 1, 1, height - i * 2 - 2, bottomRightColour.withMultipliedAlpha (op * 0.75f), false); + } + } +} + +void Graphics::fillEllipse (const float x, + const float y, + const float width, + const float height) const throw() +{ + // passing in a silly number can cause maths problems in rendering! + ASSERT_COORDS_ARE_SENSIBLE_NUMBERS (x, y, width, height); + + Path p; + p.addEllipse (x, y, width, height); + fillPath (p); +} + +void Graphics::drawEllipse (const float x, + const float y, + const float width, + const float height, + const float lineThickness) const throw() +{ + // passing in a silly number can cause maths problems in rendering! + ASSERT_COORDS_ARE_SENSIBLE_NUMBERS (x, y, width, height); + + Path p; + p.addEllipse (x, y, width, height); + strokePath (p, PathStrokeType (lineThickness)); +} + +void Graphics::fillRoundedRectangle (const float x, + const float y, + const float width, + const float height, + const float cornerSize) const throw() +{ + // passing in a silly number can cause maths problems in rendering! + ASSERT_COORDS_ARE_SENSIBLE_NUMBERS (x, y, width, height); + + Path p; + p.addRoundedRectangle (x, y, width, height, cornerSize); + fillPath (p); +} + +void Graphics::fillRoundedRectangle (const Rectangle& r, + const float cornerSize) const throw() +{ + fillRoundedRectangle ((float) r.getX(), + (float) r.getY(), + (float) r.getWidth(), + (float) r.getHeight(), + cornerSize); +} + +void Graphics::drawRoundedRectangle (const float x, + const float y, + const float width, + const float height, + const float cornerSize, + const float lineThickness) const throw() +{ + // passing in a silly number can cause maths problems in rendering! + ASSERT_COORDS_ARE_SENSIBLE_NUMBERS (x, y, width, height); + + Path p; + p.addRoundedRectangle (x, y, width, height, cornerSize); + strokePath (p, PathStrokeType (lineThickness)); +} + +void Graphics::drawRoundedRectangle (const Rectangle& r, + const float cornerSize, + const float lineThickness) const throw() +{ + drawRoundedRectangle ((float) r.getX(), + (float) r.getY(), + (float) r.getWidth(), + (float) r.getHeight(), + cornerSize, lineThickness); +} + +void Graphics::drawArrow (const float startX, + const float startY, + const float endX, + const float endY, + const float lineThickness, + const float arrowheadWidth, + const float arrowheadLength) const throw() +{ + Path p; + p.addArrow (startX, startY, endX, endY, + lineThickness, arrowheadWidth, arrowheadLength); + fillPath (p); +} + +void Graphics::fillCheckerBoard (int x, int y, + int width, int height, + const int checkWidth, + const int checkHeight, + const Colour& colour1, + const Colour& colour2) const throw() +{ + jassert (checkWidth > 0 && checkHeight > 0); // can't be zero or less! + + if (checkWidth > 0 && checkHeight > 0) + { + if (colour1 == colour2) + { + context->fillRectWithColour (x, y, width, height, colour1, false); + } + else + { + const Rectangle clip (context->getClipBounds()); + + const int right = jmin (x + width, clip.getRight()); + const int bottom = jmin (y + height, clip.getBottom()); + + int cy = 0; + while (y < bottom) + { + int cx = cy; + + for (int xx = x; xx < right; xx += checkWidth) + context->fillRectWithColour (xx, y, + jmin (checkWidth, right - xx), + jmin (checkHeight, bottom - y), + ((cx++ & 1) == 0) ? colour1 : colour2, + false); + + ++cy; + y += checkHeight; + } + } + } +} + +void Graphics::drawVerticalLine (const int x, float top, float bottom) const throw() +{ + SolidColourBrush colourBrush (state->colour); + (state->brush != 0 ? *(state->brush) : (Brush&) colourBrush).paintVerticalLine (*context, x, top, bottom); +} + +void Graphics::drawHorizontalLine (const int y, float left, float right) const throw() +{ + SolidColourBrush colourBrush (state->colour); + (state->brush != 0 ? *(state->brush) : (Brush&) colourBrush).paintHorizontalLine (*context, y, left, right); +} + +void Graphics::drawLine (float x1, float y1, + float x2, float y2) const throw() +{ + if (! context->isClipEmpty()) + { + SolidColourBrush colourBrush (state->colour); + (state->brush != 0 ? *(state->brush) : (Brush&) colourBrush).paintLine (*context, x1, y1, x2, y2); + } +} + +void Graphics::drawLine (const float startX, + const float startY, + const float endX, + const float endY, + const float lineThickness) const throw() +{ + Path p; + p.addLineSegment (startX, startY, endX, endY, lineThickness); + fillPath (p); +} + +void Graphics::drawLine (const Line& line) const throw() +{ + drawLine (line.getStartX(), line.getStartY(), line.getEndX(), line.getEndY()); +} + +void Graphics::drawLine (const Line& line, + const float lineThickness) const throw() +{ + drawLine (line.getStartX(), line.getStartY(), line.getEndX(), line.getEndY(), lineThickness); +} + +void Graphics::drawDashedLine (const float startX, + const float startY, + const float endX, + const float endY, + const float* const dashLengths, + const int numDashLengths, + const float lineThickness) const throw() +{ + const double dx = endX - startX; + const double dy = endY - startY; + const double totalLen = juce_hypot (dx, dy); + + if (totalLen >= 0.5) + { + const double onePixAlpha = 1.0 / totalLen; + + double alpha = 0.0; + float x = startX; + float y = startY; + int n = 0; + + while (alpha < 1.0f) + { + alpha = jmin (1.0, alpha + dashLengths[n++] * onePixAlpha); + n = n % numDashLengths; + + const float oldX = x; + const float oldY = y; + + x = (float) (startX + dx * alpha); + y = (float) (startY + dy * alpha); + + if ((n & 1) != 0) + { + if (lineThickness != 1.0f) + drawLine (oldX, oldY, x, y, lineThickness); + else + drawLine (oldX, oldY, x, y); + } + } + } +} + +void Graphics::setImageResamplingQuality (const Graphics::ResamplingQuality newQuality) throw() +{ + saveStateIfPending(); + state->quality = newQuality; +} + +void Graphics::drawImageAt (const Image* const imageToDraw, + const int topLeftX, + const int topLeftY, + const bool fillAlphaChannelWithCurrentBrush) const throw() +{ + if (imageToDraw != 0) + { + const int imageW = imageToDraw->getWidth(); + const int imageH = imageToDraw->getHeight(); + + drawImage (imageToDraw, + topLeftX, topLeftY, imageW, imageH, + 0, 0, imageW, imageH, + fillAlphaChannelWithCurrentBrush); + } +} + +void Graphics::drawImageWithin (const Image* const imageToDraw, + const int destX, + const int destY, + const int destW, + const int destH, + const RectanglePlacement& placementWithinTarget, + const bool fillAlphaChannelWithCurrentBrush) const throw() +{ + // passing in a silly number can cause maths problems in rendering! + ASSERT_COORDS_ARE_SENSIBLE_NUMBERS (destX, destY, destW, destH); + + if (imageToDraw != 0) + { + const int imageW = imageToDraw->getWidth(); + const int imageH = imageToDraw->getHeight(); + + if (imageW > 0 && imageH > 0) + { + double newX = 0.0, newY = 0.0; + double newW = imageW; + double newH = imageH; + + placementWithinTarget.applyTo (newX, newY, newW, newH, + destX, destY, destW, destH); + + if (newW > 0 && newH > 0) + { + drawImage (imageToDraw, + roundDoubleToInt (newX), roundDoubleToInt (newY), + roundDoubleToInt (newW), roundDoubleToInt (newH), + 0, 0, imageW, imageH, + fillAlphaChannelWithCurrentBrush); + } + } + } +} + +void Graphics::drawImage (const Image* const imageToDraw, + int dx, int dy, int dw, int dh, + int sx, int sy, int sw, int sh, + const bool fillAlphaChannelWithCurrentBrush) const throw() +{ + // passing in a silly number can cause maths problems in rendering! + ASSERT_COORDS_ARE_SENSIBLE_NUMBERS (dx, dy, dw, dh); + ASSERT_COORDS_ARE_SENSIBLE_NUMBERS (sx, sy, sw, sh); + + if (imageToDraw == 0 || ! context->clipRegionIntersects (dx, dy, dw, dh)) + return; + + if (sw == dw && sh == dh) + { + if (sx < 0) + { + dx -= sx; + dw += sx; + sw += sx; + sx = 0; + } + + if (sx + sw > imageToDraw->getWidth()) + { + const int amount = sx + sw - imageToDraw->getWidth(); + dw -= amount; + sw -= amount; + } + + if (sy < 0) + { + dy -= sy; + dh += sy; + sh += sy; + sy = 0; + } + + if (sy + sh > imageToDraw->getHeight()) + { + const int amount = sy + sh - imageToDraw->getHeight(); + dh -= amount; + sh -= amount; + } + + if (dw <= 0 || dh <= 0 || sw <= 0 || sh <= 0) + return; + + if (fillAlphaChannelWithCurrentBrush) + { + SolidColourBrush colourBrush (state->colour); + (state->brush != 0 ? *(state->brush) : (Brush&) colourBrush) + .paintAlphaChannel (*context, *imageToDraw, + dx - sx, dy - sy, + dx, dy, + dw, dh); + } + else + { + context->blendImage (*imageToDraw, + dx, dy, dw, dh, sx, sy, + state->colour.getFloatAlpha()); + } + } + else + { + if (dw <= 0 || dh <= 0 || sw <= 0 || sh <= 0) + return; + + if (fillAlphaChannelWithCurrentBrush) + { + if (imageToDraw->isRGB()) + { + fillRect (dx, dy, dw, dh); + } + else + { + int tx = dx; + int ty = dy; + int tw = dw; + int th = dh; + + if (context->getClipBounds().intersectRectangle (tx, ty, tw, th)) + { + Image temp (imageToDraw->getFormat(), tw, th, true); + Graphics g (temp); + g.setImageResamplingQuality (state->quality); + g.setOrigin (dx - tx, dy - ty); + + g.drawImage (imageToDraw, + 0, 0, dw, dh, + sx, sy, sw, sh, + false); + + SolidColourBrush colourBrush (state->colour); + (state->brush != 0 ? *(state->brush) : (Brush&) colourBrush) + .paintAlphaChannel (*context, temp, tx, ty, tx, ty, tw, th); + } + } + } + else + { + context->blendImageRescaling (*imageToDraw, + dx, dy, dw, dh, + sx, sy, sw, sh, + state->colour.getFloatAlpha(), + state->quality); + } + } +} + +void Graphics::drawImageTransformed (const Image* const imageToDraw, + int sourceClipX, + int sourceClipY, + int sourceClipWidth, + int sourceClipHeight, + const AffineTransform& transform, + const bool fillAlphaChannelWithCurrentBrush) const throw() +{ + if (imageToDraw != 0 + && (! context->isClipEmpty()) + && ! transform.isSingularity()) + { + if (fillAlphaChannelWithCurrentBrush) + { + Path p; + p.addRectangle ((float) sourceClipX, (float) sourceClipY, + (float) sourceClipWidth, (float) sourceClipHeight); + + p.applyTransform (transform); + + float dx, dy, dw, dh; + p.getBounds (dx, dy, dw, dh); + int tx = (int) dx; + int ty = (int) dy; + int tw = roundFloatToInt (dw) + 2; + int th = roundFloatToInt (dh) + 2; + + if (context->getClipBounds().intersectRectangle (tx, ty, tw, th)) + { + Image temp (imageToDraw->getFormat(), tw, th, true); + Graphics g (temp); + g.setImageResamplingQuality (state->quality); + + g.drawImageTransformed (imageToDraw, + sourceClipX, + sourceClipY, + sourceClipWidth, + sourceClipHeight, + transform.translated ((float) -tx, (float) -ty), + false); + + SolidColourBrush colourBrush (state->colour); + (state->brush != 0 ? *(state->brush) : (Brush&) colourBrush).paintAlphaChannel (*context, temp, tx, ty, tx, ty, tw, th); + } + } + else + { + context->blendImageWarping (*imageToDraw, + sourceClipX, + sourceClipY, + sourceClipWidth, + sourceClipHeight, + transform, + state->colour.getFloatAlpha(), + state->quality); + } + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_Graphics.cpp *********/ + +/********* Start of inlined file: juce_Justification.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +Justification::Justification (const Justification& other) throw() + : flags (other.flags) +{ +} + +const Justification& Justification::operator= (const Justification& other) throw() +{ + flags = other.flags; + return *this; +} + +int Justification::getOnlyVerticalFlags() const throw() +{ + return flags & (top | bottom | verticallyCentred); +} + +int Justification::getOnlyHorizontalFlags() const throw() +{ + return flags & (left | right | horizontallyCentred | horizontallyJustified); +} + +void Justification::applyToRectangle (int& x, int& y, + const int w, const int h, + const int spaceX, const int spaceY, + const int spaceW, const int spaceH) const throw() +{ + if ((flags & horizontallyCentred) != 0) + { + x = spaceX + ((spaceW - w) >> 1); + } + else if ((flags & right) != 0) + { + x = spaceX + spaceW - w; + } + else + { + x = spaceX; + } + + if ((flags & verticallyCentred) != 0) + { + y = spaceY + ((spaceH - h) >> 1); + } + else if ((flags & bottom) != 0) + { + y = spaceY + spaceH - h; + } + else + { + y = spaceY; + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_Justification.cpp *********/ + +/********* Start of inlined file: juce_LowLevelGraphicsPostScriptRenderer.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +#if JUCE_MSVC + #pragma warning (disable: 4996) // deprecated sprintf warning +#endif + +// this will throw an assertion if you try to draw something that's not +// possible in postscript +#define WARN_ABOUT_NON_POSTSCRIPT_OPERATIONS 0 + +#if defined (JUCE_DEBUG) && WARN_ABOUT_NON_POSTSCRIPT_OPERATIONS + #define notPossibleInPostscriptAssert jassertfalse +#else + #define notPossibleInPostscriptAssert +#endif + +LowLevelGraphicsPostScriptRenderer::LowLevelGraphicsPostScriptRenderer (OutputStream& resultingPostScript, + const String& documentTitle, + const int totalWidth_, + const int totalHeight_) + : out (resultingPostScript), + totalWidth (totalWidth_), + totalHeight (totalHeight_), + xOffset (0), + yOffset (0), + needToClip (true) +{ + clip = new RectangleList (Rectangle (0, 0, totalWidth_, totalHeight_)); + + const float scale = jmin ((520.0f / totalWidth_), (750.0f / totalHeight)); + + out << "%!PS-Adobe-3.0 EPSF-3.0" + "\n%%BoundingBox: 0 0 600 824" + "\n%%Pages: 0" + "\n%%Creator: Raw Material Software JUCE" + "\n%%Title: " << documentTitle << + "\n%%CreationDate: none" + "\n%%LanguageLevel: 2" + "\n%%EndComments" + "\n%%BeginProlog" + "\n%%BeginResource: JRes" + "\n/bd {bind def} bind def" + "\n/c {setrgbcolor} bd" + "\n/m {moveto} bd" + "\n/l {lineto} bd" + "\n/rl {rlineto} bd" + "\n/ct {curveto} bd" + "\n/cp {closepath} bd" + "\n/pr {3 index 3 index moveto 1 index 0 rlineto 0 1 index rlineto pop neg 0 rlineto pop pop closepath} bd" + "\n/doclip {initclip newpath} bd" + "\n/endclip {clip newpath} bd" + "\n%%EndResource" + "\n%%EndProlog" + "\n%%BeginSetup" + "\n%%EndSetup" + "\n%%Page: 1 1" + "\n%%BeginPageSetup" + "\n%%EndPageSetup\n\n" + << "40 800 translate\n" + << scale << ' ' << scale << " scale\n\n"; +} + +LowLevelGraphicsPostScriptRenderer::~LowLevelGraphicsPostScriptRenderer() +{ + delete clip; +} + +bool LowLevelGraphicsPostScriptRenderer::isVectorDevice() const +{ + return true; +} + +void LowLevelGraphicsPostScriptRenderer::setOrigin (int x, int y) +{ + if (x != 0 || y != 0) + { + xOffset += x; + yOffset += y; + needToClip = true; + } +} + +bool LowLevelGraphicsPostScriptRenderer::reduceClipRegion (int x, int y, int w, int h) +{ + needToClip = true; + return clip->clipTo (Rectangle (x + xOffset, y + yOffset, w, h)); +} + +bool LowLevelGraphicsPostScriptRenderer::reduceClipRegion (const RectangleList& clipRegion) +{ + needToClip = true; + return clip->clipTo (clipRegion); +} + +void LowLevelGraphicsPostScriptRenderer::excludeClipRegion (int x, int y, int w, int h) +{ + needToClip = true; + clip->subtract (Rectangle (x + xOffset, y + yOffset, w, h)); +} + +bool LowLevelGraphicsPostScriptRenderer::clipRegionIntersects (int x, int y, int w, int h) +{ + return clip->intersectsRectangle (Rectangle (x + xOffset, y + yOffset, w, h)); +} + +const Rectangle LowLevelGraphicsPostScriptRenderer::getClipBounds() const +{ + return clip->getBounds().translated (-xOffset, -yOffset); +} + +bool LowLevelGraphicsPostScriptRenderer::isClipEmpty() const +{ + return clip->isEmpty(); +} + +LowLevelGraphicsPostScriptRenderer::SavedState::SavedState (RectangleList* const clip_, + const int xOffset_, const int yOffset_) + : clip (clip_), + xOffset (xOffset_), + yOffset (yOffset_) +{ +} + +LowLevelGraphicsPostScriptRenderer::SavedState::~SavedState() +{ + delete clip; +} + +void LowLevelGraphicsPostScriptRenderer::saveState() +{ + stateStack.add (new SavedState (new RectangleList (*clip), xOffset, yOffset)); +} + +void LowLevelGraphicsPostScriptRenderer::restoreState() +{ + SavedState* const top = stateStack.getLast(); + + if (top != 0) + { + clip->swapWith (*top->clip); + + xOffset = top->xOffset; + yOffset = top->yOffset; + + stateStack.removeLast(); + + needToClip = true; + } + else + { + jassertfalse // trying to pop with an empty stack! + } +} + +void LowLevelGraphicsPostScriptRenderer::writeClip() +{ + if (needToClip) + { + needToClip = false; + + out << "doclip "; + + int itemsOnLine = 0; + + for (RectangleList::Iterator i (*clip); i.next();) + { + if (++itemsOnLine == 6) + { + itemsOnLine = 0; + out << '\n'; + } + + const Rectangle& r = *i.getRectangle(); + + out << r.getX() << ' ' << -r.getY() << ' ' + << r.getWidth() << ' ' << -r.getHeight() << " pr "; + } + + out << "endclip\n"; + } +} + +void LowLevelGraphicsPostScriptRenderer::writeColour (const Colour& colour) +{ + Colour c (Colours::white.overlaidWith (colour)); + + if (lastColour != c) + { + lastColour = c; + + out << String (c.getFloatRed(), 3) << ' ' + << String (c.getFloatGreen(), 3) << ' ' + << String (c.getFloatBlue(), 3) << " c\n"; + } +} + +void LowLevelGraphicsPostScriptRenderer::writeXY (const float x, const float y) const +{ + out << String (x, 2) << ' ' + << String (-y, 2) << ' '; +} + +void LowLevelGraphicsPostScriptRenderer::writePath (const Path& path) const +{ + out << "newpath "; + + float lastX = 0.0f; + float lastY = 0.0f; + int itemsOnLine = 0; + + Path::Iterator i (path); + + while (i.next()) + { + if (++itemsOnLine == 4) + { + itemsOnLine = 0; + out << '\n'; + } + + switch (i.elementType) + { + case Path::Iterator::startNewSubPath: + writeXY (i.x1, i.y1); + lastX = i.x1; + lastY = i.y1; + out << "m "; + break; + + case Path::Iterator::lineTo: + writeXY (i.x1, i.y1); + lastX = i.x1; + lastY = i.y1; + out << "l "; + break; + + case Path::Iterator::quadraticTo: + { + const float cp1x = lastX + (i.x1 - lastX) * 2.0f / 3.0f; + const float cp1y = lastY + (i.y1 - lastY) * 2.0f / 3.0f; + const float cp2x = cp1x + (i.x2 - lastX) / 3.0f; + const float cp2y = cp1y + (i.y2 - lastY) / 3.0f; + + writeXY (cp1x, cp1y); + writeXY (cp2x, cp2y); + writeXY (i.x2, i.y2); + out << "ct "; + lastX = i.x2; + lastY = i.y2; + } + break; + + case Path::Iterator::cubicTo: + writeXY (i.x1, i.y1); + writeXY (i.x2, i.y2); + writeXY (i.x3, i.y3); + out << "ct "; + lastX = i.x3; + lastY = i.y3; + break; + + case Path::Iterator::closePath: + out << "cp "; + break; + + default: + jassertfalse + break; + } + } + + out << '\n'; +} + +void LowLevelGraphicsPostScriptRenderer::writeTransform (const AffineTransform& trans) const +{ + out << "[ " + << trans.mat00 << ' ' + << trans.mat10 << ' ' + << trans.mat01 << ' ' + << trans.mat11 << ' ' + << trans.mat02 << ' ' + << trans.mat12 << " ] concat "; +} + +void LowLevelGraphicsPostScriptRenderer::fillRectWithColour (int x, int y, int w, int h, const Colour& colour, const bool /*replaceExistingContents*/) +{ + writeClip(); + writeColour (colour); + + x += xOffset; + y += yOffset; + + out << x << ' ' << -(y + h) << ' ' << w << ' ' << h << " rectfill\n"; +} + +void LowLevelGraphicsPostScriptRenderer::fillRectWithGradient (int x, int y, int w, int h, const ColourGradient& gradient) +{ + Path p; + p.addRectangle ((float) x, (float) y, (float) w, (float) h); + + fillPathWithGradient (p, AffineTransform::identity, gradient, EdgeTable::Oversampling_256times); +} + +void LowLevelGraphicsPostScriptRenderer::fillPathWithColour (const Path& path, const AffineTransform& t, + const Colour& colour, EdgeTable::OversamplingLevel /*quality*/) +{ + writeClip(); + + Path p (path); + p.applyTransform (t.translated ((float) xOffset, (float) yOffset)); + writePath (p); + + writeColour (colour); + + out << "fill\n"; +} + +void LowLevelGraphicsPostScriptRenderer::fillPathWithGradient (const Path& path, const AffineTransform& t, const ColourGradient& gradient, EdgeTable::OversamplingLevel /*quality*/) +{ + // this doesn't work correctly yet - it could be improved to handle solid gradients, but + // postscript can't do semi-transparent ones. + notPossibleInPostscriptAssert // you can disable this warning by setting the WARN_ABOUT_NON_POSTSCRIPT_OPERATIONS flag at the top of this file + + writeClip(); + out << "gsave "; + + { + Path p (path); + p.applyTransform (t.translated ((float) xOffset, (float) yOffset)); + writePath (p); + out << "clip\n"; + } + + int numColours = 256; + PixelARGB* const colours = gradient.createLookupTable (numColours); + + for (int i = numColours; --i >= 0;) + colours[i].unpremultiply(); + + const Rectangle bounds (clip->getBounds()); + + // ideally this would draw lots of lines or ellipses to approximate the gradient, but for the + // time-being, this just fills it with the average colour.. + writeColour (Colour (colours [numColours / 2].getARGB())); + out << bounds.getX() << ' ' << -bounds.getBottom() << ' ' << bounds.getWidth() << ' ' << bounds.getHeight() << " rectfill\n"; + + juce_free (colours); + out << "grestore\n"; +} + +void LowLevelGraphicsPostScriptRenderer::fillPathWithImage (const Path& path, const AffineTransform& transform, + const Image& sourceImage, + int imageX, int imageY, + float opacity, EdgeTable::OversamplingLevel /*quality*/) +{ + writeClip(); + + out << "gsave "; + Path p (path); + p.applyTransform (transform.translated ((float) xOffset, (float) yOffset)); + writePath (p); + out << "clip\n"; + + blendImage (sourceImage, imageX, imageY, sourceImage.getWidth(), sourceImage.getHeight(), 0, 0, opacity); + + out << "grestore\n"; +} + +void LowLevelGraphicsPostScriptRenderer::fillAlphaChannelWithColour (const Image& /*clipImage*/, int x, int y, const Colour& colour) +{ + x += xOffset; + y += yOffset; + + writeClip(); + writeColour (colour); + + notPossibleInPostscriptAssert // you can disable this warning by setting the WARN_ABOUT_NON_POSTSCRIPT_OPERATIONS flag at the top of this file +} + +void LowLevelGraphicsPostScriptRenderer::fillAlphaChannelWithGradient (const Image& /*alphaChannelImage*/, int imageX, int imageY, const ColourGradient& /*gradient*/) +{ + imageX += xOffset; + imageY += yOffset; + + writeClip(); + + notPossibleInPostscriptAssert // you can disable this warning by setting the WARN_ABOUT_NON_POSTSCRIPT_OPERATIONS flag at the top of this file +} + +void LowLevelGraphicsPostScriptRenderer::fillAlphaChannelWithImage (const Image& /*alphaImage*/, int alphaImageX, int alphaImageY, + const Image& /*fillerImage*/, int fillerImageX, int fillerImageY, float /*opacity*/) +{ + alphaImageX += xOffset; + alphaImageY += yOffset; + + fillerImageX += xOffset; + fillerImageY += yOffset; + + writeClip(); + + notPossibleInPostscriptAssert // you can disable this warning by setting the WARN_ABOUT_NON_POSTSCRIPT_OPERATIONS flag at the top of this file +} + +void LowLevelGraphicsPostScriptRenderer::blendImageRescaling (const Image& sourceImage, + int dx, int dy, int dw, int dh, + int sx, int sy, int sw, int sh, + float alpha, + const Graphics::ResamplingQuality quality) +{ + if (sw > 0 && sh > 0) + { + jassert (sx >= 0 && sx + sw <= sourceImage.getWidth()); + jassert (sy >= 0 && sy + sh <= sourceImage.getHeight()); + + if (sw == dw && sh == dh) + { + blendImage (sourceImage, + dx, dy, dw, dh, + sx, sy, alpha); + } + else + { + blendImageWarping (sourceImage, + sx, sy, sw, sh, + AffineTransform::scale (dw / (float) sw, + dh / (float) sh) + .translated ((float) (dx - sx), + (float) (dy - sy)), + alpha, + quality); + } + } +} + +void LowLevelGraphicsPostScriptRenderer::blendImage (const Image& sourceImage, int dx, int dy, int dw, int dh, int sx, int sy, float opacity) +{ + blendImageWarping (sourceImage, + sx, sy, dw, dh, + AffineTransform::translation ((float) dx, (float) dy), + opacity, Graphics::highResamplingQuality); +} + +void LowLevelGraphicsPostScriptRenderer::writeImage (const Image& im, + const int sx, const int sy, + const int maxW, const int maxH) const +{ + out << "{<\n"; + + const int w = jmin (maxW, im.getWidth()); + const int h = jmin (maxH, im.getHeight()); + + int charsOnLine = 0; + int lineStride, pixelStride; + const uint8* data = im.lockPixelDataReadOnly (0, 0, w, h, lineStride, pixelStride); + + Colour pixel; + + for (int y = h; --y >= 0;) + { + for (int x = 0; x < w; ++x) + { + const uint8* pixelData = data + lineStride * y + pixelStride * x; + + if (x >= sx && y >= sy) + { + if (im.isARGB()) + { + PixelARGB p (*(const PixelARGB*) pixelData); + p.unpremultiply(); + pixel = Colours::white.overlaidWith (Colour (p.getARGB())); + } + else if (im.isRGB()) + { + pixel = Colour (((const PixelRGB*) pixelData)->getARGB()); + } + else + { + pixel = Colour ((uint8) 0, (uint8) 0, (uint8) 0, *pixelData); + } + } + else + { + pixel = Colours::transparentWhite; + } + + char colourString [16]; + sprintf (colourString, "%x%x%x", pixel.getRed(), pixel.getGreen(), pixel.getBlue()); + + out << (const char*) colourString; + charsOnLine += 3; + + if (charsOnLine > 100) + { + out << '\n'; + charsOnLine = 0; + } + } + } + + im.releasePixelDataReadOnly (data); + + out << "\n>}\n"; +} + +void LowLevelGraphicsPostScriptRenderer::blendImageWarping (const Image& sourceImage, + int srcClipX, int srcClipY, + int srcClipW, int srcClipH, + const AffineTransform& t, + float /*opacity*/, + const Graphics::ResamplingQuality /*quality*/) +{ + const int w = jmin (sourceImage.getWidth(), srcClipX + srcClipW); + const int h = jmin (sourceImage.getHeight(), srcClipY + srcClipH); + + writeClip(); + + out << "gsave "; + writeTransform (t.translated ((float) xOffset, (float) yOffset) + .scaled (1.0f, -1.0f)); + + RectangleList imageClip; + sourceImage.createSolidAreaMask (imageClip, 0.5f); + imageClip.clipTo (Rectangle (srcClipX, srcClipY, srcClipW, srcClipH)); + + out << "newpath "; + int itemsOnLine = 0; + + for (RectangleList::Iterator i (imageClip); i.next();) + { + if (++itemsOnLine == 6) + { + out << '\n'; + itemsOnLine = 0; + } + + const Rectangle& r = *i.getRectangle(); + + out << r.getX() << ' ' << r.getY() << ' ' << r.getWidth() << ' ' << r.getHeight() << " pr "; + } + + out << " clip newpath\n"; + + out << w << ' ' << h << " scale\n"; + out << w << ' ' << h << " 8 [" << w << " 0 0 -" << h << ' ' << (int) 0 << ' ' << h << " ]\n"; + + writeImage (sourceImage, srcClipX, srcClipY, srcClipW, srcClipH); + + out << "false 3 colorimage grestore\n"; + needToClip = true; +} + +void LowLevelGraphicsPostScriptRenderer::drawLine (double x1, double y1, double x2, double y2, const Colour& colour) +{ + Path p; + p.addLineSegment ((float) x1, (float) y1, (float) x2, (float) y2, 1.0f); + + fillPathWithColour (p, AffineTransform::identity, colour, EdgeTable::Oversampling_256times); +} + +void LowLevelGraphicsPostScriptRenderer::drawVerticalLine (const int x, double top, double bottom, const Colour& col) +{ + drawLine (x, top, x, bottom, col); +} + +void LowLevelGraphicsPostScriptRenderer::drawHorizontalLine (const int y, double left, double right, const Colour& col) +{ + drawLine (left, y, right, y, col); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_LowLevelGraphicsPostScriptRenderer.cpp *********/ + +/********* Start of inlined file: juce_LowLevelGraphicsSoftwareRenderer.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +#if ! (defined (JUCE_MAC) || (defined (JUCE_WIN32) && defined (JUCE_64BIT))) + #define JUCE_USE_SSE_INSTRUCTIONS 1 +#endif + +#if defined (JUCE_DEBUG) && JUCE_MSVC + #pragma warning (disable: 4714) +#endif + +#define MINIMUM_COORD -0x3fffffff +#define MAXIMUM_COORD 0x3fffffff + +#undef ASSERT_COORDS_ARE_SENSIBLE_NUMBERS +#define ASSERT_COORDS_ARE_SENSIBLE_NUMBERS(x, y, w, h) \ + jassert ((int) x >= MINIMUM_COORD \ + && (int) x <= MAXIMUM_COORD \ + && (int) y >= MINIMUM_COORD \ + && (int) y <= MAXIMUM_COORD \ + && (int) w >= 0 \ + && (int) w < MAXIMUM_COORD \ + && (int) h >= 0 \ + && (int) h < MAXIMUM_COORD); + +static void replaceRectRGB (uint8* pixels, const int w, int h, const int stride, const Colour& colour) throw() +{ + const PixelARGB blendColour (colour.getPixelARGB()); + + if (w < 32) + { + while (--h >= 0) + { + PixelRGB* dest = (PixelRGB*) pixels; + + for (int i = w; --i >= 0;) + (dest++)->set (blendColour); + + pixels += stride; + } + } + else + { + // for wider fills, it's worth using some optimisations.. + + const uint8 r = blendColour.getRed(); + const uint8 g = blendColour.getGreen(); + const uint8 b = blendColour.getBlue(); + + if (r == g && r == b) // if all the component values are the same, we can cheat.. + { + while (--h >= 0) + { + memset (pixels, r, w * 3); + pixels += stride; + } + } + else + { + PixelRGB filler [4]; + filler[0].set (blendColour); + filler[1].set (blendColour); + filler[2].set (blendColour); + filler[3].set (blendColour); + const int* const intFiller = (const int*) filler; + + while (--h >= 0) + { + uint8* dest = (uint8*) pixels; + + int i = w; + + while ((i > 8) && (((pointer_sized_int) dest & 7) != 0)) + { + ((PixelRGB*) dest)->set (blendColour); + dest += 3; + --i; + } + + while (i >= 4) + { + ((int*) dest) [0] = intFiller[0]; + ((int*) dest) [1] = intFiller[1]; + ((int*) dest) [2] = intFiller[2]; + + dest += 12; + i -= 4; + } + + while (--i >= 0) + { + ((PixelRGB*) dest)->set (blendColour); + dest += 3; + } + + pixels += stride; + } + } + } +} + +static void replaceRectARGB (uint8* pixels, const int w, int h, const int stride, const Colour& colour) throw() +{ + const PixelARGB blendColour (colour.getPixelARGB()); + + while (--h >= 0) + { + PixelARGB* const dest = (PixelARGB*) pixels; + + for (int i = 0; i < w; ++i) + dest[i] = blendColour; + + pixels += stride; + } +} + +static void blendRectRGB (uint8* pixels, const int w, int h, const int stride, const Colour& colour) throw() +{ + if (colour.isOpaque()) + { + replaceRectRGB (pixels, w, h, stride, colour); + } + else + { + const PixelARGB blendColour (colour.getPixelARGB()); + const int alpha = blendColour.getAlpha(); + + if (alpha <= 0) + return; + +#if defined (JUCE_USE_SSE_INSTRUCTIONS) && ! JUCE_64BIT + if (SystemStats::hasSSE()) + { + int64 rgb0 = (((int64) blendColour.getRed()) << 32) + | (int64) ((blendColour.getGreen() << 16) + | blendColour.getBlue()); + + const int invAlpha = 0xff - alpha; + int64 aaaa = (invAlpha << 16) | invAlpha; + aaaa = (aaaa << 16) | aaaa; + +#ifndef JUCE_GCC + __asm + { + movq mm1, aaaa + movq mm2, rgb0 + pxor mm7, mm7 + } + + while (--h >= 0) + { + __asm + { + mov edx, pixels + mov ebx, w + + pixloop: + prefetchnta [edx] + mov ax, [edx + 1] + shl eax, 8 + mov al, [edx] + movd mm0, eax + + punpcklbw mm0, mm7 + pmullw mm0, mm1 + psrlw mm0, 8 + paddw mm0, mm2 + packuswb mm0, mm7 + + movd eax, mm0 + mov [edx], al + inc edx + shr eax, 8 + mov [edx], ax + add edx, 2 + + dec ebx + jg pixloop + } + + pixels += stride; + } + + __asm emms +#else + __asm__ __volatile__ ( + "movq %[aaaa], %%mm1 \n" + "\tmovq %[rgb0], %%mm2 \n" + "\tpxor %%mm7, %%mm7 \n" + ".lineLoop2: \n" + "\tmovl %%esi,%%edx \n" + "\tmovl %[w], %%ebx \n" + ".pixLoop2: \n" + "\tprefetchnta (%%edx) \n" + "\tmov (%%edx), %%ax \n" + "\tshl $8, %%eax \n" + "\tmov 2(%%edx), %%al \n" + "\tmovd %%eax, %%mm0 \n" + "\tpunpcklbw %%mm7, %%mm0 \n" + "\tpmullw %%mm1, %%mm0 \n" + "\tpsrlw $8, %%mm0 \n" + "\tpaddw %%mm2, %%mm0 \n" + "\tpackuswb %%mm7, %%mm0 \n" + "\tmovd %%mm0, %%eax \n" + "\tmovb %%al, (%%edx) \n" + "\tinc %%edx \n" + "\tshr $8, %%eax \n" + "\tmovw %%ax, (%%edx) \n" + "\tadd $2, %%edx \n" + "\tdec %%ebx \n" + "\tjg .pixLoop2 \n" + "\tadd %%edi, %%esi \n" + "\tdec %%ecx \n" + "\tjg .lineLoop2 \n" + "\temms \n" + : /* No output registers */ + : [aaaa] "m" (aaaa), /* Input registers */ + [rgb0] "m" (rgb0), + [w] "m" (w), + "c" (h), + [stride] "D" (stride), + [pixels] "S" (pixels) + : "cc", "eax", "edx", "memory" /* Clobber list */ + ); +#endif + } + else +#endif + { + while (--h >= 0) + { + PixelRGB* dest = (PixelRGB*) pixels; + + for (int i = w; --i >= 0;) + (dest++)->blend (blendColour); + + pixels += stride; + } + } + } +} + +static void blendRectARGB (uint8* pixels, const int w, int h, const int stride, const Colour& colour) throw() +{ + if (colour.isOpaque()) + { + replaceRectARGB (pixels, w, h, stride, colour); + } + else + { + const PixelARGB blendColour (colour.getPixelARGB()); + const int alpha = blendColour.getAlpha(); + + if (alpha <= 0) + return; + + while (--h >= 0) + { + PixelARGB* dest = (PixelARGB*) pixels; + + for (int i = w; --i >= 0;) + (dest++)->blend (blendColour); + + pixels += stride; + } + } +} + +static void blendAlphaMapARGB (uint8* destPixel, const int imageStride, + const uint8* alphaValues, const int w, int h, + const int pixelStride, const int lineStride, + const Colour& colour) throw() +{ + const PixelARGB srcPix (colour.getPixelARGB()); + + while (--h >= 0) + { + PixelARGB* dest = (PixelARGB*) destPixel; + const uint8* src = alphaValues; + + int i = w; + while (--i >= 0) + { + unsigned int srcAlpha = *src; + src += pixelStride; + + if (srcAlpha > 0) + dest->blend (srcPix, srcAlpha); + + ++dest; + } + + alphaValues += lineStride; + destPixel += imageStride; + } +} + +static void blendAlphaMapRGB (uint8* destPixel, const int imageStride, + const uint8* alphaValues, int const width, int height, + const int pixelStride, const int lineStride, + const Colour& colour) throw() +{ + const PixelARGB srcPix (colour.getPixelARGB()); + + while (--height >= 0) + { + PixelRGB* dest = (PixelRGB*) destPixel; + const uint8* src = alphaValues; + + int i = width; + while (--i >= 0) + { + unsigned int srcAlpha = *src; + src += pixelStride; + + if (srcAlpha > 0) + dest->blend (srcPix, srcAlpha); + + ++dest; + } + + alphaValues += lineStride; + destPixel += imageStride; + } +} + +template +class SolidColourEdgeTableRenderer +{ + uint8* const data; + const int stride; + PixelType* linePixels; + PixelARGB sourceColour; + + SolidColourEdgeTableRenderer (const SolidColourEdgeTableRenderer&); + const SolidColourEdgeTableRenderer& operator= (const SolidColourEdgeTableRenderer&); + +public: + SolidColourEdgeTableRenderer (uint8* const data_, + const int stride_, + const Colour& colour) throw() + : data (data_), + stride (stride_), + sourceColour (colour.getPixelARGB()) + { + } + + forcedinline void setEdgeTableYPos (const int y) throw() + { + linePixels = (PixelType*) (data + stride * y); + } + + forcedinline void handleEdgeTablePixel (const int x, const int alphaLevel) const throw() + { + linePixels[x].blend (sourceColour, alphaLevel); + } + + forcedinline void handleEdgeTableLine (const int x, int width, const int alphaLevel) const throw() + { + PixelARGB p (sourceColour); + p.multiplyAlpha (alphaLevel); + + PixelType* dest = linePixels + x; + + if (p.getAlpha() < 0xff) + { + do + { + dest->blend (p); + ++dest; + + } while (--width > 0); + } + else + { + do + { + dest->set (p); + ++dest; + + } while (--width > 0); + } + } +}; + +class AlphaBitmapRenderer +{ + uint8* data; + int stride; + uint8* lineStart; + + AlphaBitmapRenderer (const AlphaBitmapRenderer&); + const AlphaBitmapRenderer& operator= (const AlphaBitmapRenderer&); + +public: + AlphaBitmapRenderer (uint8* const data_, + const int stride_) throw() + : data (data_), + stride (stride_) + { + } + + forcedinline void setEdgeTableYPos (const int y) throw() + { + lineStart = data + (stride * y); + } + + forcedinline void handleEdgeTablePixel (const int x, const int alphaLevel) const throw() + { + lineStart [x] = (uint8) alphaLevel; + } + + forcedinline void handleEdgeTableLine (const int x, int width, const int alphaLevel) const throw() + { + uint8* d = lineStart + x; + + while (--width >= 0) + *d++ = (uint8) alphaLevel; + } +}; + +static const int numScaleBits = 12; + +class LinearGradientPixelGenerator +{ + const PixelARGB* const lookupTable; + const int numEntries; + PixelARGB linePix; + int start, scale; + double grad, yTerm; + bool vertical, horizontal; + + LinearGradientPixelGenerator (const LinearGradientPixelGenerator&); + const LinearGradientPixelGenerator& operator= (const LinearGradientPixelGenerator&); + +public: + LinearGradientPixelGenerator (const ColourGradient& gradient, + const PixelARGB* const lookupTable_, const int numEntries_) + : lookupTable (lookupTable_), + numEntries (numEntries_) + { + jassert (numEntries_ >= 0); + float x1 = gradient.x1; + float y1 = gradient.y1; + float x2 = gradient.x2; + float y2 = gradient.y2; + + if (! gradient.transform.isIdentity()) + { + Line l (x2, y2, x1, y1); + const Point p3 = l.getPointAlongLine (0.0, 100.0f); + float x3 = p3.getX(); + float y3 = p3.getY(); + + gradient.transform.transformPoint (x1, y1); + gradient.transform.transformPoint (x2, y2); + gradient.transform.transformPoint (x3, y3); + + Line l2 (x2, y2, x3, y3); + float prop = l2.findNearestPointTo (x1, y1); + const Point newP2 (l2.getPointAlongLineProportionally (prop)); + + x2 = newP2.getX(); + y2 = newP2.getY(); + } + + vertical = fabs (x1 - x2) < 0.001f; + horizontal = fabs (y1 - y2) < 0.001f; + + if (vertical) + { + scale = roundDoubleToInt ((numEntries << numScaleBits) / (double) (y2 - y1)); + start = roundDoubleToInt (y1 * scale); + } + else if (horizontal) + { + scale = roundDoubleToInt ((numEntries << numScaleBits) / (double) (x2 - x1)); + start = roundDoubleToInt (x1 * scale); + } + else + { + grad = (y2 - y1) / (double) (x1 - x2); + yTerm = y1 - x1 / grad; + scale = roundDoubleToInt ((numEntries << numScaleBits) / (yTerm * grad - (y2 * grad - x2))); + grad *= scale; + } + } + + forcedinline void setY (const int y) throw() + { + if (vertical) + linePix = lookupTable [jlimit (0, numEntries, (y * scale - start) >> numScaleBits)]; + else if (! horizontal) + start = roundDoubleToInt ((y - yTerm) * grad); + } + + forcedinline const PixelARGB getPixel (const int x) const throw() + { + if (vertical) + return linePix; + + return lookupTable [jlimit (0, numEntries, (x * scale - start) >> numScaleBits)]; + } +}; + +class RadialGradientPixelGenerator +{ +protected: + const PixelARGB* const lookupTable; + const int numEntries; + const double gx1, gy1; + double maxDist, invScale; + double dy; + + RadialGradientPixelGenerator (const RadialGradientPixelGenerator&); + const RadialGradientPixelGenerator& operator= (const RadialGradientPixelGenerator&); + +public: + RadialGradientPixelGenerator (const ColourGradient& gradient, + const PixelARGB* const lookupTable_, const int numEntries_) throw() + : lookupTable (lookupTable_), + numEntries (numEntries_), + gx1 (gradient.x1), + gy1 (gradient.y1) + { + jassert (numEntries_ >= 0); + const float dx = gradient.x1 - gradient.x2; + const float dy = gradient.y1 - gradient.y2; + maxDist = dx * dx + dy * dy; + invScale = (numEntries + 1) / sqrt (maxDist); + } + + forcedinline void setY (const int y) throw() + { + dy = y - gy1; + dy *= dy; + } + + forcedinline const PixelARGB getPixel (const int px) const throw() + { + double x = px - gx1; + x *= x; + x += dy; + + if (x >= maxDist) + return lookupTable [numEntries]; + else + return lookupTable [jmin (numEntries, roundDoubleToInt (sqrt (x) * invScale))]; + } +}; + +class TransformedRadialGradientPixelGenerator : public RadialGradientPixelGenerator +{ + double tM10, tM00, lineYM01, lineYM11; + AffineTransform inverseTransform; + + TransformedRadialGradientPixelGenerator (const TransformedRadialGradientPixelGenerator&); + const TransformedRadialGradientPixelGenerator& operator= (const TransformedRadialGradientPixelGenerator&); + +public: + TransformedRadialGradientPixelGenerator (const ColourGradient& gradient, + const PixelARGB* const lookupTable_, const int numEntries_) throw() + : RadialGradientPixelGenerator (gradient, lookupTable_, numEntries_), + inverseTransform (gradient.transform.inverted()) + { + tM10 = inverseTransform.mat10; + tM00 = inverseTransform.mat00; + } + + forcedinline void setY (const int y) throw() + { + lineYM01 = inverseTransform.mat01 * y + inverseTransform.mat02 - gx1; + lineYM11 = inverseTransform.mat11 * y + inverseTransform.mat12 - gy1; + } + + forcedinline const PixelARGB getPixel (const int px) const throw() + { + double x = px; + const double y = tM10 * x + lineYM11; + x = tM00 * x + lineYM01; + x *= x; + x += y * y; + + if (x >= maxDist) + return lookupTable [numEntries]; + else + return lookupTable [jmin (numEntries, roundDoubleToInt (sqrt (x) * invScale))]; + } +}; + +template +class GradientEdgeTableRenderer : public GradientType +{ + uint8* const data; + const int stride; + PixelType* linePixels; + + GradientEdgeTableRenderer (const GradientEdgeTableRenderer&); + const GradientEdgeTableRenderer& operator= (const GradientEdgeTableRenderer&); + +public: + GradientEdgeTableRenderer (uint8* const data_, + const int stride_, + const ColourGradient& gradient, + const PixelARGB* const lookupTable, const int numEntries) throw() + : GradientType (gradient, lookupTable, numEntries - 1), + data (data_), + stride (stride_) + { + } + + forcedinline void setEdgeTableYPos (const int y) throw() + { + linePixels = (PixelType*) (data + stride * y); + GradientType::setY (y); + } + + forcedinline void handleEdgeTablePixel (const int x, const int alphaLevel) const throw() + { + linePixels[x].blend (GradientType::getPixel (x), alphaLevel); + } + + forcedinline void handleEdgeTableLine (int x, int width, const int alphaLevel) const throw() + { + PixelType* dest = linePixels + x; + + if (alphaLevel < 0xff) + { + do + { + (dest++)->blend (GradientType::getPixel (x++), alphaLevel); + + } while (--width > 0); + } + else + { + do + { + (dest++)->blend (GradientType::getPixel (x++)); + + } while (--width > 0); + } + } +}; + +template +class ImageFillEdgeTableRenderer +{ + uint8* const destImageData; + const uint8* srcImageData; + int stride, srcStride, extraAlpha; + + DestPixelType* linePixels; + SrcPixelType* sourceLineStart; + + ImageFillEdgeTableRenderer (const ImageFillEdgeTableRenderer&); + const ImageFillEdgeTableRenderer& operator= (const ImageFillEdgeTableRenderer&); + +public: + ImageFillEdgeTableRenderer (uint8* const destImageData_, + const int stride_, + const uint8* srcImageData_, + const int srcStride_, + int extraAlpha_, + SrcPixelType*) throw() // dummy param to avoid compiler error + : destImageData (destImageData_), + srcImageData (srcImageData_), + stride (stride_), + srcStride (srcStride_), + extraAlpha (extraAlpha_) + { + } + + forcedinline void setEdgeTableYPos (int y) throw() + { + linePixels = (DestPixelType*) (destImageData + stride * y); + sourceLineStart = (SrcPixelType*) (srcImageData + srcStride * y); + } + + forcedinline void handleEdgeTablePixel (const int x, int alphaLevel) const throw() + { + alphaLevel = (alphaLevel * extraAlpha) >> 8; + + linePixels[x].blend (sourceLineStart [x], alphaLevel); + } + + forcedinline void handleEdgeTableLine (int x, int width, int alphaLevel) const throw() + { + DestPixelType* dest = linePixels + x; + alphaLevel = (alphaLevel * extraAlpha) >> 8; + + if (alphaLevel < 0xfe) + { + do + { + dest++ ->blend (sourceLineStart [x++], alphaLevel); + + } while (--width > 0); + } + else + { + do + { + dest++ ->blend (sourceLineStart [x++]); + + } while (--width > 0); + } + } +}; + +static void blendRowOfPixels (PixelARGB* dst, + const PixelRGB* src, + int width) throw() +{ + while (--width >= 0) + (dst++)->set (*src++); +} + +static void blendRowOfPixels (PixelRGB* dst, + const PixelRGB* src, + int width) throw() +{ + memcpy (dst, src, 3 * width); +} + +static void blendRowOfPixels (PixelRGB* dst, + const PixelARGB* src, + int width) throw() +{ + while (--width >= 0) + (dst++)->blend (*src++); +} + +static void blendRowOfPixels (PixelARGB* dst, + const PixelARGB* src, + int width) throw() +{ + while (--width >= 0) + (dst++)->blend (*src++); +} + +static void blendRowOfPixels (PixelARGB* dst, + const PixelRGB* src, + int width, + const uint8 alpha) throw() +{ + while (--width >= 0) + (dst++)->blend (*src++, alpha); +} + +static void blendRowOfPixels (PixelRGB* dst, + const PixelRGB* src, + int width, + const uint8 alpha) throw() +{ + uint8* d = (uint8*) dst; + const uint8* s = (const uint8*) src; + const int inverseAlpha = 0xff - alpha; + + while (--width >= 0) + { + d[0] = (uint8) (s[0] + (((d[0] - s[0]) * inverseAlpha) >> 8)); + d[1] = (uint8) (s[1] + (((d[1] - s[1]) * inverseAlpha) >> 8)); + d[2] = (uint8) (s[2] + (((d[2] - s[2]) * inverseAlpha) >> 8)); + + d += 3; + s += 3; + } +} + +static void blendRowOfPixels (PixelRGB* dst, + const PixelARGB* src, + int width, + const uint8 alpha) throw() +{ + while (--width >= 0) + (dst++)->blend (*src++, alpha); +} + +static void blendRowOfPixels (PixelARGB* dst, + const PixelARGB* src, + int width, + const uint8 alpha) throw() +{ + while (--width >= 0) + (dst++)->blend (*src++, alpha); +} + +template +static void overlayImage (DestPixelType* dest, + const int destStride, + const SrcPixelType* src, + const int srcStride, + const int width, + int height, + const uint8 alpha) throw() +{ + if (alpha < 0xff) + { + while (--height >= 0) + { + blendRowOfPixels (dest, src, width, alpha); + + dest = (DestPixelType*) (((uint8*) dest) + destStride); + src = (const SrcPixelType*) (((const uint8*) src) + srcStride); + } + } + else + { + while (--height >= 0) + { + blendRowOfPixels (dest, src, width); + + dest = (DestPixelType*) (((uint8*) dest) + destStride); + src = (const SrcPixelType*) (((const uint8*) src) + srcStride); + } + } +} + +template +static void transformedImageRender (Image& destImage, + const Image& sourceImage, + const int destClipX, const int destClipY, + const int destClipW, const int destClipH, + const int srcClipX, const int srcClipY, + const int srcClipWidth, const int srcClipHeight, + double srcX, double srcY, + const double lineDX, const double lineDY, + const double pixelDX, const double pixelDY, + const uint8 alpha, + const Graphics::ResamplingQuality quality, + DestPixelType*, + SrcPixelType*) throw() // forced by a compiler bug to include dummy + // parameters of the templated classes to + // make it use the correct instance of this function.. +{ + int destStride, destPixelStride; + uint8* const destPixels = destImage.lockPixelDataReadWrite (destClipX, destClipY, destClipW, destClipH, destStride, destPixelStride); + + int srcStride, srcPixelStride; + const uint8* const srcPixels = sourceImage.lockPixelDataReadOnly (srcClipX, srcClipY, srcClipWidth, srcClipHeight, srcStride, srcPixelStride); + + if (quality == Graphics::lowResamplingQuality) // nearest-neighbour.. + { + for (int y = 0; y < destClipH; ++y) + { + double sx = srcX; + double sy = srcY; + + DestPixelType* dest = (DestPixelType*) (destPixels + destStride * y); + + for (int x = 0; x < destClipW; ++x) + { + const int ix = roundDoubleToInt (floor (sx)) - srcClipX; + + if (((unsigned int) ix) < (unsigned int) srcClipWidth) + { + const int iy = roundDoubleToInt (floor (sy)) - srcClipY; + + if (((unsigned int) iy) < (unsigned int) srcClipHeight) + { + const SrcPixelType* const src = (const SrcPixelType*) (srcPixels + srcStride * iy + srcPixelStride * ix); + + dest->blend (*src, alpha); + } + } + + ++dest; + sx += pixelDX; + sy += pixelDY; + } + + srcX += lineDX; + srcY += lineDY; + } + } + else + { + jassert (quality == Graphics::mediumResamplingQuality); // (only bilinear is implemented, so that's what you'll get here..) + + for (int y = 0; y < destClipH; ++y) + { + double sx = srcX; + double sy = srcY; + DestPixelType* dest = (DestPixelType*) (destPixels + destStride * y); + + for (int x = 0; x < destClipW; ++x) + { + const double fx = floor (sx); + const double fy = floor (sy); + const int ix = roundDoubleToInt (fx) - srcClipX; + const int iy = roundDoubleToInt (fy) - srcClipY; + + if (ix < srcClipWidth && iy < srcClipHeight) + { + PixelARGB p1 (0), p2 (0), p3 (0), p4 (0); + + const SrcPixelType* src = (const SrcPixelType*) (srcPixels + srcStride * iy + srcPixelStride * ix); + + if (iy >= 0) + { + if (ix >= 0) + p1.set (src[0]); + + if (((unsigned int) (ix + 1)) < (unsigned int) srcClipWidth) + p2.set (src[1]); + } + + if (((unsigned int) (iy + 1)) < (unsigned int) srcClipHeight) + { + src = (const SrcPixelType*) (((const uint8*) src) + srcStride); + + if (ix >= 0) + p3.set (src[0]); + + if (((unsigned int) (ix + 1)) < (unsigned int) srcClipWidth) + p4.set (src[1]); + } + + const int dx = roundDoubleToInt ((sx - fx) * 255.0); + p1.tween (p2, dx); + p3.tween (p4, dx); + p1.tween (p3, roundDoubleToInt ((sy - fy) * 255.0)); + + if (p1.getAlpha() > 0) + dest->blend (p1, alpha); + } + + ++dest; + sx += pixelDX; + sy += pixelDY; + } + + srcX += lineDX; + srcY += lineDY; + } + } + + destImage.releasePixelDataReadWrite (destPixels); + sourceImage.releasePixelDataReadOnly (srcPixels); +} + +template +static void renderAlphaMap (DestPixelType* destPixels, + int destStride, + SrcPixelType* srcPixels, + int srcStride, + const uint8* alphaValues, + const int lineStride, const int pixelStride, + int width, int height, + const int extraAlpha) throw() +{ + while (--height >= 0) + { + SrcPixelType* srcPix = srcPixels; + srcPixels = (SrcPixelType*) (((const uint8*) srcPixels) + srcStride); + + DestPixelType* destPix = destPixels; + destPixels = (DestPixelType*) (((uint8*) destPixels) + destStride); + + const uint8* alpha = alphaValues; + alphaValues += lineStride; + + if (extraAlpha < 0x100) + { + for (int i = width; --i >= 0;) + { + destPix++ ->blend (*srcPix++, (extraAlpha * *alpha) >> 8); + alpha += pixelStride; + } + } + else + { + for (int i = width; --i >= 0;) + { + destPix++ ->blend (*srcPix++, *alpha); + alpha += pixelStride; + } + } + } +} + +LowLevelGraphicsSoftwareRenderer::LowLevelGraphicsSoftwareRenderer (Image& image_) + : image (image_), + xOffset (0), + yOffset (0), + stateStack (20) +{ + clip = new RectangleList (Rectangle (0, 0, image_.getWidth(), image_.getHeight())); +} + +LowLevelGraphicsSoftwareRenderer::~LowLevelGraphicsSoftwareRenderer() +{ + delete clip; +} + +bool LowLevelGraphicsSoftwareRenderer::isVectorDevice() const +{ + return false; +} + +void LowLevelGraphicsSoftwareRenderer::setOrigin (int x, int y) +{ + xOffset += x; + yOffset += y; +} + +bool LowLevelGraphicsSoftwareRenderer::reduceClipRegion (int x, int y, int w, int h) +{ + return clip->clipTo (Rectangle (x + xOffset, y + yOffset, w, h)); +} + +bool LowLevelGraphicsSoftwareRenderer::reduceClipRegion (const RectangleList& clipRegion) +{ + RectangleList temp (clipRegion); + temp.offsetAll (xOffset, yOffset); + + return clip->clipTo (temp); +} + +void LowLevelGraphicsSoftwareRenderer::excludeClipRegion (int x, int y, int w, int h) +{ + clip->subtract (Rectangle (x + xOffset, y + yOffset, w, h)); +} + +bool LowLevelGraphicsSoftwareRenderer::clipRegionIntersects (int x, int y, int w, int h) +{ + return clip->intersectsRectangle (Rectangle (x + xOffset, y + yOffset, w, h)); +} + +const Rectangle LowLevelGraphicsSoftwareRenderer::getClipBounds() const +{ + return clip->getBounds().translated (-xOffset, -yOffset); +} + +bool LowLevelGraphicsSoftwareRenderer::isClipEmpty() const +{ + return clip->isEmpty(); +} + +LowLevelGraphicsSoftwareRenderer::SavedState::SavedState (RectangleList* const clip_, + const int xOffset_, const int yOffset_) + : clip (clip_), + xOffset (xOffset_), + yOffset (yOffset_) +{ +} + +LowLevelGraphicsSoftwareRenderer::SavedState::~SavedState() +{ + delete clip; +} + +void LowLevelGraphicsSoftwareRenderer::saveState() +{ + stateStack.add (new SavedState (new RectangleList (*clip), xOffset, yOffset)); +} + +void LowLevelGraphicsSoftwareRenderer::restoreState() +{ + SavedState* const top = stateStack.getLast(); + + if (top != 0) + { + clip->swapWith (*top->clip); + + xOffset = top->xOffset; + yOffset = top->yOffset; + + stateStack.removeLast(); + } + else + { + jassertfalse // trying to pop with an empty stack! + } +} + +void LowLevelGraphicsSoftwareRenderer::fillRectWithColour (int x, int y, int w, int h, const Colour& colour, const bool replaceExistingContents) +{ + x += xOffset; + y += yOffset; + + for (RectangleList::Iterator i (*clip); i.next();) + { + clippedFillRectWithColour (*i.getRectangle(), x, y, w, h, colour, replaceExistingContents); + } +} + +void LowLevelGraphicsSoftwareRenderer::clippedFillRectWithColour (const Rectangle& clipRect, + int x, int y, int w, int h, const Colour& colour, const bool replaceExistingContents) +{ + if (clipRect.intersectRectangle (x, y, w, h)) + { + int stride, pixelStride; + uint8* const pixels = (uint8*) image.lockPixelDataReadWrite (x, y, w, h, stride, pixelStride); + + if (image.getFormat() == Image::RGB) + { + if (replaceExistingContents) + replaceRectRGB (pixels, w, h, stride, colour); + else + blendRectRGB (pixels, w, h, stride, colour); + } + else if (image.getFormat() == Image::ARGB) + { + if (replaceExistingContents) + replaceRectARGB (pixels, w, h, stride, colour); + else + blendRectARGB (pixels, w, h, stride, colour); + } + else + { + jassertfalse // not done! + } + + image.releasePixelDataReadWrite (pixels); + } +} + +void LowLevelGraphicsSoftwareRenderer::fillRectWithGradient (int x, int y, int w, int h, const ColourGradient& gradient) +{ + Path p; + p.addRectangle ((float) x, (float) y, (float) w, (float) h); + fillPathWithGradient (p, AffineTransform::identity, gradient, EdgeTable::Oversampling_none); +} + +bool LowLevelGraphicsSoftwareRenderer::getPathBounds (int clipX, int clipY, int clipW, int clipH, + const Path& path, const AffineTransform& transform, + int& x, int& y, int& w, int& h) const +{ + float tx, ty, tw, th; + path.getBoundsTransformed (transform, tx, ty, tw, th); + + x = roundDoubleToInt (tx) - 1; + y = roundDoubleToInt (ty) - 1; + w = roundDoubleToInt (tw) + 2; + h = roundDoubleToInt (th) + 2; + + // seems like this operation is using some crazy out-of-range numbers.. + ASSERT_COORDS_ARE_SENSIBLE_NUMBERS (x, y, w, h); + + return Rectangle::intersectRectangles (x, y, w, h, clipX, clipY, clipW, clipH); +} + +void LowLevelGraphicsSoftwareRenderer::fillPathWithColour (const Path& path, const AffineTransform& t, + const Colour& colour, EdgeTable::OversamplingLevel quality) +{ + for (RectangleList::Iterator i (*clip); i.next();) + { + const Rectangle& r = *i.getRectangle(); + + clippedFillPathWithColour (r.getX(), r.getY(), r.getWidth(), r.getHeight(), path, t, colour, quality); + } +} + +void LowLevelGraphicsSoftwareRenderer::clippedFillPathWithColour (int clipX, int clipY, int clipW, int clipH, const Path& path, const AffineTransform& t, + const Colour& colour, EdgeTable::OversamplingLevel quality) +{ + const AffineTransform transform (t.translated ((float) xOffset, (float) yOffset)); + int cx, cy, cw, ch; + + if (getPathBounds (clipX, clipY, clipW, clipH, path, transform, cx, cy, cw, ch)) + { + EdgeTable edgeTable (0, ch, quality); + + edgeTable.addPath (path, transform.translated ((float) -cx, (float) -cy)); + + int stride, pixelStride; + uint8* const pixels = (uint8*) image.lockPixelDataReadWrite (cx, cy, cw, ch, stride, pixelStride); + + if (image.getFormat() == Image::RGB) + { + jassert (pixelStride == 3); + SolidColourEdgeTableRenderer renderer (pixels, stride, colour); + edgeTable.iterate (renderer, 0, 0, cw, ch, 0); + } + else if (image.getFormat() == Image::ARGB) + { + jassert (pixelStride == 4); + SolidColourEdgeTableRenderer renderer (pixels, stride, colour); + edgeTable.iterate (renderer, 0, 0, cw, ch, 0); + } + else if (image.getFormat() == Image::SingleChannel) + { + jassert (pixelStride == 1); + AlphaBitmapRenderer renderer (pixels, stride); + edgeTable.iterate (renderer, 0, 0, cw, ch, 0); + } + + image.releasePixelDataReadWrite (pixels); + } +} + +void LowLevelGraphicsSoftwareRenderer::fillPathWithGradient (const Path& path, const AffineTransform& t, const ColourGradient& gradient, EdgeTable::OversamplingLevel quality) +{ + for (RectangleList::Iterator i (*clip); i.next();) + { + const Rectangle& r = *i.getRectangle(); + + clippedFillPathWithGradient (r.getX(), r.getY(), r.getWidth(), r.getHeight(), + path, t, gradient, quality); + } +} + +void LowLevelGraphicsSoftwareRenderer::clippedFillPathWithGradient (int clipX, int clipY, int clipW, int clipH, const Path& path, const AffineTransform& t, + const ColourGradient& gradient, EdgeTable::OversamplingLevel quality) +{ + const AffineTransform transform (t.translated ((float) xOffset, (float) yOffset)); + int cx, cy, cw, ch; + + if (getPathBounds (clipX, clipY, clipW, clipH, path, transform, cx, cy, cw, ch)) + { + int stride, pixelStride; + uint8* const pixels = (uint8*) image.lockPixelDataReadWrite (cx, cy, cw, ch, stride, pixelStride); + + ColourGradient g2 (gradient); + + const bool isIdentity = g2.transform.isIdentity(); + if (isIdentity) + { + g2.x1 += xOffset - cx; + g2.x2 += xOffset - cx; + g2.y1 += yOffset - cy; + g2.y2 += yOffset - cy; + } + else + { + g2.transform = g2.transform.translated ((float) (xOffset - cx), + (float) (yOffset - cy)); + } + + int numLookupEntries; + PixelARGB* const lookupTable = g2.createLookupTable (numLookupEntries); + jassert (numLookupEntries > 0); + + EdgeTable edgeTable (0, ch, quality); + + edgeTable.addPath (path, transform.translated ((float) -cx, (float) -cy)); + + if (image.getFormat() == Image::RGB) + { + jassert (pixelStride == 3); + + if (g2.isRadial) + { + if (isIdentity) + { + GradientEdgeTableRenderer renderer (pixels, stride, g2, lookupTable, numLookupEntries); + edgeTable.iterate (renderer, 0, 0, cw, ch, 0); + } + else + { + GradientEdgeTableRenderer renderer (pixels, stride, g2, lookupTable, numLookupEntries); + edgeTable.iterate (renderer, 0, 0, cw, ch, 0); + } + } + else + { + GradientEdgeTableRenderer renderer (pixels, stride, g2, lookupTable, numLookupEntries); + edgeTable.iterate (renderer, 0, 0, cw, ch, 0); + } + } + else if (image.getFormat() == Image::ARGB) + { + jassert (pixelStride == 4); + + if (g2.isRadial) + { + if (isIdentity) + { + GradientEdgeTableRenderer renderer (pixels, stride, g2, lookupTable, numLookupEntries); + edgeTable.iterate (renderer, 0, 0, cw, ch, 0); + } + else + { + GradientEdgeTableRenderer renderer (pixels, stride, g2, lookupTable, numLookupEntries); + edgeTable.iterate (renderer, 0, 0, cw, ch, 0); + } + } + else + { + GradientEdgeTableRenderer renderer (pixels, stride, g2, lookupTable, numLookupEntries); + edgeTable.iterate (renderer, 0, 0, cw, ch, 0); + } + } + else if (image.getFormat() == Image::SingleChannel) + { + jassertfalse // not done! + } + + juce_free (lookupTable); + image.releasePixelDataReadWrite (pixels); + } +} + +void LowLevelGraphicsSoftwareRenderer::fillPathWithImage (const Path& path, const AffineTransform& transform, + const Image& sourceImage, int imageX, int imageY, float opacity, EdgeTable::OversamplingLevel quality) +{ + imageX += xOffset; + imageY += yOffset; + + for (RectangleList::Iterator i (*clip); i.next();) + { + const Rectangle& r = *i.getRectangle(); + + clippedFillPathWithImage (r.getX(), r.getY(), r.getWidth(), r.getHeight(), + path, transform, sourceImage, imageX, imageY, opacity, quality); + } +} + +void LowLevelGraphicsSoftwareRenderer::clippedFillPathWithImage (int x, int y, int w, int h, const Path& path, const AffineTransform& transform, + const Image& sourceImage, int imageX, int imageY, float opacity, EdgeTable::OversamplingLevel quality) +{ + if (Rectangle::intersectRectangles (x, y, w, h, imageX, imageY, sourceImage.getWidth(), sourceImage.getHeight())) + { + EdgeTable edgeTable (0, h, quality); + edgeTable.addPath (path, transform.translated ((float) (xOffset - x), (float) (yOffset - y))); + + int stride, pixelStride; + uint8* const pixels = (uint8*) image.lockPixelDataReadWrite (x, y, w, h, stride, pixelStride); + + int srcStride, srcPixelStride; + const uint8* const srcPix = (const uint8*) sourceImage.lockPixelDataReadOnly (x - imageX, y - imageY, w, h, srcStride, srcPixelStride); + + const int alpha = jlimit (0, 255, roundDoubleToInt (opacity * 255.0f)); + + if (image.getFormat() == Image::RGB) + { + if (sourceImage.getFormat() == Image::RGB) + { + ImageFillEdgeTableRenderer renderer (pixels, stride, + srcPix, srcStride, + alpha, (PixelRGB*) 0); + edgeTable.iterate (renderer, 0, 0, w, h, 0); + } + else if (sourceImage.getFormat() == Image::ARGB) + { + ImageFillEdgeTableRenderer renderer (pixels, stride, + srcPix, srcStride, + alpha, (PixelARGB*) 0); + edgeTable.iterate (renderer, 0, 0, w, h, 0); + } + else + { + jassertfalse // not done! + } + } + else if (image.getFormat() == Image::ARGB) + { + if (sourceImage.getFormat() == Image::RGB) + { + ImageFillEdgeTableRenderer renderer (pixels, stride, + srcPix, srcStride, + alpha, (PixelRGB*) 0); + edgeTable.iterate (renderer, 0, 0, w, h, 0); + } + else if (sourceImage.getFormat() == Image::ARGB) + { + ImageFillEdgeTableRenderer renderer (pixels, stride, + srcPix, srcStride, + alpha, (PixelARGB*) 0); + edgeTable.iterate (renderer, 0, 0, w, h, 0); + } + else + { + jassertfalse // not done! + } + } + else + { + jassertfalse // not done! + } + + sourceImage.releasePixelDataReadOnly (srcPix); + image.releasePixelDataReadWrite (pixels); + } +} + +void LowLevelGraphicsSoftwareRenderer::fillAlphaChannelWithColour (const Image& clipImage, int x, int y, const Colour& colour) +{ + x += xOffset; + y += yOffset; + + for (RectangleList::Iterator i (*clip); i.next();) + { + const Rectangle& r = *i.getRectangle(); + + clippedFillAlphaChannelWithColour (r.getX(), r.getY(), r.getWidth(), r.getHeight(), + clipImage, x, y, colour); + } +} + +void LowLevelGraphicsSoftwareRenderer::clippedFillAlphaChannelWithColour (int clipX, int clipY, int clipW, int clipH, const Image& clipImage, int x, int y, const Colour& colour) +{ + int w = clipImage.getWidth(); + int h = clipImage.getHeight(); + int sx = 0; + int sy = 0; + + if (x < clipX) + { + sx = clipX - x; + w -= clipX - x; + x = clipX; + } + + if (y < clipY) + { + sy = clipY - y; + h -= clipY - y; + y = clipY; + } + + if (x + w > clipX + clipW) + w = clipX + clipW - x; + + if (y + h > clipY + clipH) + h = clipY + clipH - y; + + if (w > 0 && h > 0) + { + int stride, alphaStride, pixelStride; + uint8* const pixels = (uint8*) image.lockPixelDataReadWrite (x, y, w, h, stride, pixelStride); + + const uint8* const alphaValues + = clipImage.lockPixelDataReadOnly (sx, sy, w, h, alphaStride, pixelStride); + +#if JUCE_MAC + const uint8* const alphas = alphaValues; +#else + const uint8* const alphas = alphaValues + (clipImage.getFormat() == Image::ARGB ? 3 : 0); +#endif + + if (image.getFormat() == Image::RGB) + { + blendAlphaMapRGB (pixels, stride, + alphas, w, h, + pixelStride, alphaStride, + colour); + } + else if (image.getFormat() == Image::ARGB) + { + blendAlphaMapARGB (pixels, stride, + alphas, w, h, + pixelStride, alphaStride, + colour); + } + else + { + jassertfalse // not done! + } + + clipImage.releasePixelDataReadOnly (alphaValues); + image.releasePixelDataReadWrite (pixels); + } +} + +void LowLevelGraphicsSoftwareRenderer::fillAlphaChannelWithGradient (const Image& alphaChannelImage, int imageX, int imageY, const ColourGradient& gradient) +{ + imageX += xOffset; + imageY += yOffset; + + for (RectangleList::Iterator i (*clip); i.next();) + { + const Rectangle& r = *i.getRectangle(); + + clippedFillAlphaChannelWithGradient (r.getX(), r.getY(), r.getWidth(), r.getHeight(), + alphaChannelImage, imageX, imageY, gradient); + } +} + +void LowLevelGraphicsSoftwareRenderer::clippedFillAlphaChannelWithGradient (int x, int y, int w, int h, + const Image& alphaChannelImage, + int imageX, int imageY, const ColourGradient& gradient) +{ + if (Rectangle::intersectRectangles (x, y, w, h, imageX, imageY, alphaChannelImage.getWidth(), alphaChannelImage.getHeight())) + { + ColourGradient g2 (gradient); + g2.x1 += xOffset - x; + g2.x2 += xOffset - x; + g2.y1 += yOffset - y; + g2.y2 += yOffset - y; + + Image temp (g2.isOpaque() ? Image::RGB : Image::ARGB, w, h, true); + LowLevelGraphicsSoftwareRenderer tempG (temp); + tempG.fillRectWithGradient (0, 0, w, h, g2); + + clippedFillAlphaChannelWithImage (x, y, w, h, + alphaChannelImage, imageX, imageY, + temp, x, y, 1.0f); + } +} + +void LowLevelGraphicsSoftwareRenderer::fillAlphaChannelWithImage (const Image& alphaImage, int alphaImageX, int alphaImageY, + const Image& fillerImage, int fillerImageX, int fillerImageY, float opacity) +{ + alphaImageX += xOffset; + alphaImageY += yOffset; + + fillerImageX += xOffset; + fillerImageY += yOffset; + + for (RectangleList::Iterator i (*clip); i.next();) + { + const Rectangle& r = *i.getRectangle(); + + clippedFillAlphaChannelWithImage (r.getX(), r.getY(), r.getWidth(), r.getHeight(), + alphaImage, alphaImageX, alphaImageY, + fillerImage, fillerImageX, fillerImageY, opacity); + } +} + +void LowLevelGraphicsSoftwareRenderer::clippedFillAlphaChannelWithImage (int x, int y, int w, int h, const Image& alphaImage, int alphaImageX, int alphaImageY, + const Image& fillerImage, int fillerImageX, int fillerImageY, float opacity) +{ + if (Rectangle::intersectRectangles (x, y, w, h, alphaImageX, alphaImageY, alphaImage.getWidth(), alphaImage.getHeight()) + && Rectangle::intersectRectangles (x, y, w, h, fillerImageX, fillerImageY, fillerImage.getWidth(), fillerImage.getHeight())) + { + int dstStride, dstPixStride; + uint8* const dstPix = image.lockPixelDataReadWrite (x, y, w, h, dstStride, dstPixStride); + + int srcStride, srcPixStride; + const uint8* const srcPix = fillerImage.lockPixelDataReadOnly (x - fillerImageX, y - fillerImageY, w, h, srcStride, srcPixStride); + + int maskStride, maskPixStride; + const uint8* const alpha + = alphaImage.lockPixelDataReadOnly (x - alphaImageX, y - alphaImageY, w, h, maskStride, maskPixStride); + +#if JUCE_MAC + const uint8* const alphaValues = alpha; +#else + const uint8* const alphaValues = alpha + (alphaImage.getFormat() == Image::ARGB ? 3 : 0); +#endif + + const int extraAlpha = jlimit (0, 0x100, roundDoubleToInt (opacity * 256.0f)); + + if (image.getFormat() == Image::RGB) + { + if (fillerImage.getFormat() == Image::RGB) + { + renderAlphaMap ((PixelRGB*) dstPix, dstStride, (const PixelRGB*) srcPix, srcStride, alphaValues, maskStride, maskPixStride, w, h, extraAlpha); + } + else if (fillerImage.getFormat() == Image::ARGB) + { + renderAlphaMap ((PixelRGB*) dstPix, dstStride, (const PixelARGB*) srcPix, srcStride, alphaValues, maskStride, maskPixStride, w, h, extraAlpha); + } + else + { + jassertfalse // not done! + } + } + else if (image.getFormat() == Image::ARGB) + { + if (fillerImage.getFormat() == Image::RGB) + { + renderAlphaMap ((PixelARGB*) dstPix, dstStride, (const PixelRGB*) srcPix, srcStride, alphaValues, maskStride, maskPixStride, w, h, extraAlpha); + } + else if (fillerImage.getFormat() == Image::ARGB) + { + renderAlphaMap ((PixelARGB*) dstPix, dstStride, (const PixelARGB*) srcPix, srcStride, alphaValues, maskStride, maskPixStride, w, h, extraAlpha); + } + else + { + jassertfalse // not done! + } + } + else + { + jassertfalse // not done! + } + + alphaImage.releasePixelDataReadOnly (alphaValues); + fillerImage.releasePixelDataReadOnly (srcPix); + image.releasePixelDataReadWrite (dstPix); + } +} + +void LowLevelGraphicsSoftwareRenderer::blendImage (const Image& sourceImage, int dx, int dy, int dw, int dh, int sx, int sy, float opacity) +{ + dx += xOffset; + dy += yOffset; + + for (RectangleList::Iterator i (*clip); i.next();) + { + const Rectangle& r = *i.getRectangle(); + + clippedBlendImage (r.getX(), r.getY(), r.getWidth(), r.getHeight(), + sourceImage, dx, dy, dw, dh, sx, sy, opacity); + } +} + +void LowLevelGraphicsSoftwareRenderer::clippedBlendImage (int clipX, int clipY, int clipW, int clipH, + const Image& sourceImage, int dx, int dy, int dw, int dh, int sx, int sy, float opacity) +{ + if (dx < clipX) + { + sx += clipX - dx; + dw -= clipX - dx; + dx = clipX; + } + + if (dy < clipY) + { + sy += clipY - dy; + dh -= clipY - dy; + dy = clipY; + } + + if (dx + dw > clipX + clipW) + dw = clipX + clipW - dx; + + if (dy + dh > clipY + clipH) + dh = clipY + clipH - dy; + + if (dw <= 0 || dh <= 0) + return; + + const uint8 alpha = (uint8) jlimit (0, 0xff, roundDoubleToInt (opacity * 256.0f)); + + if (alpha == 0) + return; + + int dstStride, dstPixelStride; + uint8* const dstPixels = image.lockPixelDataReadWrite (dx, dy, dw, dh, dstStride, dstPixelStride); + + int srcStride, srcPixelStride; + const uint8* const srcPixels = sourceImage.lockPixelDataReadOnly (sx, sy, dw, dh, srcStride, srcPixelStride); + + if (image.getFormat() == Image::ARGB) + { + if (sourceImage.getFormat() == Image::ARGB) + { + overlayImage ((PixelARGB*) dstPixels, dstStride, + (PixelARGB*) srcPixels, srcStride, + dw, dh, alpha); + } + else if (sourceImage.getFormat() == Image::RGB) + { + overlayImage ((PixelARGB*) dstPixels, dstStride, + (PixelRGB*) srcPixels, srcStride, + dw, dh, alpha); + } + else + { + jassertfalse + } + } + else if (image.getFormat() == Image::RGB) + { + if (sourceImage.getFormat() == Image::ARGB) + { + overlayImage ((PixelRGB*) dstPixels, dstStride, + (PixelARGB*) srcPixels, srcStride, + dw, dh, alpha); + } + else if (sourceImage.getFormat() == Image::RGB) + { + overlayImage ((PixelRGB*) dstPixels, dstStride, + (PixelRGB*) srcPixels, srcStride, + dw, dh, alpha); + } + else + { + jassertfalse + } + } + else + { + jassertfalse + } + + image.releasePixelDataReadWrite (dstPixels); + sourceImage.releasePixelDataReadOnly (srcPixels); +} + +void LowLevelGraphicsSoftwareRenderer::blendImageRescaling (const Image& sourceImage, + int dx, int dy, int dw, int dh, + int sx, int sy, int sw, int sh, + float alpha, + const Graphics::ResamplingQuality quality) +{ + if (sw > 0 && sh > 0) + { + if (sw == dw && sh == dh) + { + blendImage (sourceImage, + dx, dy, dw, dh, + sx, sy, alpha); + } + else + { + blendImageWarping (sourceImage, + sx, sy, sw, sh, + AffineTransform::translation ((float) -sx, + (float) -sy) + .scaled (dw / (float) sw, + dh / (float) sh) + .translated ((float) dx, + (float) dy), + alpha, + quality); + } + } +} + +void LowLevelGraphicsSoftwareRenderer::blendImageWarping (const Image& sourceImage, + int srcClipX, int srcClipY, int srcClipW, int srcClipH, + const AffineTransform& t, + float opacity, + const Graphics::ResamplingQuality quality) +{ + const AffineTransform transform (t.translated ((float) xOffset, (float) yOffset)); + + for (RectangleList::Iterator i (*clip); i.next();) + { + const Rectangle& r = *i.getRectangle(); + + clippedBlendImageWarping (r.getX(), r.getY(), r.getWidth(), r.getHeight(), + sourceImage, srcClipX, srcClipY, srcClipW, srcClipH, + transform, opacity, quality); + } +} + +void LowLevelGraphicsSoftwareRenderer::clippedBlendImageWarping (int destClipX, int destClipY, int destClipW, int destClipH, + const Image& sourceImage, + int srcClipX, int srcClipY, int srcClipW, int srcClipH, + const AffineTransform& transform, + float opacity, + const Graphics::ResamplingQuality quality) +{ + if (opacity > 0 && destClipW > 0 && destClipH > 0 && ! transform.isSingularity()) + { + Rectangle::intersectRectangles (srcClipX, srcClipY, srcClipW, srcClipH, + 0, 0, sourceImage.getWidth(), sourceImage.getHeight()); + + if (srcClipW <= 0 || srcClipH <= 0) + return; + + jassert (srcClipX >= 0 && srcClipY >= 0); + + Path imageBounds; + imageBounds.addRectangle ((float) srcClipX, (float) srcClipY, (float) srcClipW, (float) srcClipH); + imageBounds.applyTransform (transform); + float imX, imY, imW, imH; + imageBounds.getBounds (imX, imY, imW, imH); + + if (Rectangle::intersectRectangles (destClipX, destClipY, destClipW, destClipH, + (int) floorf (imX), + (int) floorf (imY), + 1 + roundDoubleToInt (imW), + 1 + roundDoubleToInt (imH))) + { + const uint8 alpha = (uint8) jlimit (0, 0xff, roundDoubleToInt (opacity * 256.0f)); + + float srcX1 = (float) destClipX; + float srcY1 = (float) destClipY; + float srcX2 = (float) (destClipX + destClipW); + float srcY2 = srcY1; + float srcX3 = srcX1; + float srcY3 = (float) (destClipY + destClipH); + + AffineTransform inverse (transform.inverted()); + inverse.transformPoint (srcX1, srcY1); + inverse.transformPoint (srcX2, srcY2); + inverse.transformPoint (srcX3, srcY3); + + const double lineDX = (double) (srcX3 - srcX1) / destClipH; + const double lineDY = (double) (srcY3 - srcY1) / destClipH; + const double pixelDX = (double) (srcX2 - srcX1) / destClipW; + const double pixelDY = (double) (srcY2 - srcY1) / destClipW; + + if (image.getFormat() == Image::ARGB) + { + if (sourceImage.getFormat() == Image::ARGB) + { + transformedImageRender (image, sourceImage, + destClipX, destClipY, destClipW, destClipH, + srcClipX, srcClipY, srcClipW, srcClipH, + srcX1, srcY1, lineDX, lineDY, pixelDX, pixelDY, + alpha, quality, (PixelARGB*)0, (PixelARGB*)0); + } + else if (sourceImage.getFormat() == Image::RGB) + { + transformedImageRender (image, sourceImage, + destClipX, destClipY, destClipW, destClipH, + srcClipX, srcClipY, srcClipW, srcClipH, + srcX1, srcY1, lineDX, lineDY, pixelDX, pixelDY, + alpha, quality, (PixelARGB*)0, (PixelRGB*)0); + } + else + { + jassertfalse + } + } + else if (image.getFormat() == Image::RGB) + { + if (sourceImage.getFormat() == Image::ARGB) + { + transformedImageRender (image, sourceImage, + destClipX, destClipY, destClipW, destClipH, + srcClipX, srcClipY, srcClipW, srcClipH, + srcX1, srcY1, lineDX, lineDY, pixelDX, pixelDY, + alpha, quality, (PixelRGB*)0, (PixelARGB*)0); + } + else if (sourceImage.getFormat() == Image::RGB) + { + transformedImageRender (image, sourceImage, + destClipX, destClipY, destClipW, destClipH, + srcClipX, srcClipY, srcClipW, srcClipH, + srcX1, srcY1, lineDX, lineDY, pixelDX, pixelDY, + alpha, quality, (PixelRGB*)0, (PixelRGB*)0); + } + else + { + jassertfalse + } + } + else + { + jassertfalse + } + } + } +} + +void LowLevelGraphicsSoftwareRenderer::drawLine (double x1, double y1, double x2, double y2, const Colour& colour) +{ + x1 += xOffset; + y1 += yOffset; + x2 += xOffset; + y2 += yOffset; + + for (RectangleList::Iterator i (*clip); i.next();) + { + const Rectangle& r = *i.getRectangle(); + + clippedDrawLine (r.getX(), r.getY(), r.getWidth(), r.getHeight(), + x1, y1, x2, y2, colour); + } +} + +void LowLevelGraphicsSoftwareRenderer::clippedDrawLine (int clipX, int clipY, int clipW, int clipH, double x1, double y1, double x2, double y2, const Colour& colour) +{ + if (clipW > 0 && clipH > 0) + { + if (x1 == x2) + { + if (y2 < y1) + swapVariables (y1, y2); + + clippedDrawVerticalLine (clipX, clipY, clipW, clipH, roundDoubleToInt (x1), y1, y2, colour); + } + else if (y1 == y2) + { + if (x2 < x1) + swapVariables (x1, x2); + + clippedDrawHorizontalLine (clipX, clipY, clipW, clipH, roundDoubleToInt (y1), x1, x2, colour); + } + else + { + double gradient = (y2 - y1) / (x2 - x1); + + if (fabs (gradient) > 1.0) + { + gradient = 1.0 / gradient; + + int y = roundDoubleToInt (y1); + const int startY = y; + int endY = roundDoubleToInt (y2); + + if (y > endY) + swapVariables (y, endY); + + while (y < endY) + { + const double x = x1 + gradient * (y - startY); + clippedDrawHorizontalLine (clipX, clipY, clipW, clipH, y, x, x + 1.0, colour); + ++y; + } + } + else + { + int x = roundDoubleToInt (x1); + const int startX = x; + int endX = roundDoubleToInt (x2); + + if (x > endX) + swapVariables (x, endX); + + while (x < endX) + { + const double y = y1 + gradient * (x - startX); + clippedDrawVerticalLine (clipX, clipY, clipW, clipH, x, y, y + 1.0, colour); + ++x; + } + } + } + } +} + +void LowLevelGraphicsSoftwareRenderer::drawVerticalLine (const int x, double top, double bottom, const Colour& col) +{ + for (RectangleList::Iterator i (*clip); i.next();) + { + const Rectangle& r = *i.getRectangle(); + + clippedDrawVerticalLine (r.getX(), r.getY(), r.getWidth(), r.getHeight(), + x + xOffset, top + yOffset, bottom + yOffset, col); + } +} + +void LowLevelGraphicsSoftwareRenderer::clippedDrawVerticalLine (int clipX, int clipY, int clipW, int clipH, + const int x, double top, double bottom, const Colour& col) +{ + jassert (top <= bottom); + + if (((unsigned int) (x - clipX)) < (unsigned int) clipW + && top < clipY + clipH + && bottom > clipY + && clipW > 0) + { + if (top < clipY) + top = clipY; + + if (bottom > clipY + clipH) + bottom = clipY + clipH; + + if (bottom > top) + drawVertical (x, top, bottom, col); + } +} + +void LowLevelGraphicsSoftwareRenderer::drawHorizontalLine (const int y, double left, double right, const Colour& col) +{ + for (RectangleList::Iterator i (*clip); i.next();) + { + const Rectangle& r = *i.getRectangle(); + + clippedDrawHorizontalLine (r.getX(), r.getY(), r.getWidth(), r.getHeight(), + y + yOffset, left + xOffset, right + xOffset, col); + } +} + +void LowLevelGraphicsSoftwareRenderer::clippedDrawHorizontalLine (int clipX, int clipY, int clipW, int clipH, + const int y, double left, double right, const Colour& col) +{ + jassert (left <= right); + + if (((unsigned int) (y - clipY)) < (unsigned int) clipH + && left < clipX + clipW + && right > clipX + && clipW > 0) + { + if (left < clipX) + left = clipX; + + if (right > clipX + clipW) + right = clipX + clipW; + + if (right > left) + drawHorizontal (y, left, right, col); + } +} + +void LowLevelGraphicsSoftwareRenderer::drawVertical (const int x, + const double top, + const double bottom, + const Colour& col) +{ + int wholeStart = (int) top; + const int wholeEnd = (int) bottom; + + const int lastAlpha = roundDoubleToInt (255.0 * (bottom - wholeEnd)); + const int totalPixels = (wholeEnd - wholeStart) + (lastAlpha > 0 ? 1 : 0); + + if (totalPixels <= 0) + return; + + int lineStride, dstPixelStride; + uint8* const dstPixels = image.lockPixelDataReadWrite (x, wholeStart, 1, totalPixels, lineStride, dstPixelStride); + uint8* dest = dstPixels; + + PixelARGB colour (col.getPixelARGB()); + + if (wholeEnd == wholeStart) + { + if (image.getFormat() == Image::ARGB) + ((PixelARGB*) dest)->blend (colour, roundDoubleToInt (255.0 * (bottom - top))); + else if (image.getFormat() == Image::RGB) + ((PixelRGB*) dest)->blend (colour, roundDoubleToInt (255.0 * (bottom - top))); + else + { + jassertfalse + } + } + else + { + if (image.getFormat() == Image::ARGB) + { + ((PixelARGB*) dest)->blend (colour, roundDoubleToInt (255.0 * (1.0 - (top - wholeStart)))); + ++wholeStart; + dest += lineStride; + + if (colour.getAlpha() == 0xff) + { + while (wholeEnd > wholeStart) + { + ((PixelARGB*) dest)->set (colour); + ++wholeStart; + dest += lineStride; + } + } + else + { + while (wholeEnd > wholeStart) + { + ((PixelARGB*) dest)->blend (colour); + ++wholeStart; + dest += lineStride; + } + } + + if (lastAlpha > 0) + { + ((PixelARGB*) dest)->blend (colour, lastAlpha); + } + } + else if (image.getFormat() == Image::RGB) + { + ((PixelRGB*) dest)->blend (colour, roundDoubleToInt (255.0 * (1.0 - (top - wholeStart)))); + ++wholeStart; + dest += lineStride; + + if (colour.getAlpha() == 0xff) + { + while (wholeEnd > wholeStart) + { + ((PixelRGB*) dest)->set (colour); + ++wholeStart; + dest += lineStride; + } + } + else + { + while (wholeEnd > wholeStart) + { + ((PixelRGB*) dest)->blend (colour); + ++wholeStart; + dest += lineStride; + } + } + + if (lastAlpha > 0) + { + ((PixelRGB*) dest)->blend (colour, lastAlpha); + } + } + else + { + jassertfalse + } + } + + image.releasePixelDataReadWrite (dstPixels); +} + +void LowLevelGraphicsSoftwareRenderer::drawHorizontal (const int y, + const double top, + const double bottom, + const Colour& col) +{ + int wholeStart = (int) top; + const int wholeEnd = (int) bottom; + + const int lastAlpha = roundDoubleToInt (255.0 * (bottom - wholeEnd)); + const int totalPixels = (wholeEnd - wholeStart) + (lastAlpha > 0 ? 1 : 0); + + if (totalPixels <= 0) + return; + + int lineStride, dstPixelStride; + uint8* const dstPixels = image.lockPixelDataReadWrite (wholeStart, y, totalPixels, 1, lineStride, dstPixelStride); + uint8* dest = dstPixels; + + PixelARGB colour (col.getPixelARGB()); + + if (wholeEnd == wholeStart) + { + if (image.getFormat() == Image::ARGB) + ((PixelARGB*) dest)->blend (colour, roundDoubleToInt (255.0 * (bottom - top))); + else if (image.getFormat() == Image::RGB) + ((PixelRGB*) dest)->blend (colour, roundDoubleToInt (255.0 * (bottom - top))); + else + { + jassertfalse + } + } + else + { + if (image.getFormat() == Image::ARGB) + { + ((PixelARGB*) dest)->blend (colour, roundDoubleToInt (255.0 * (1.0 - (top - wholeStart)))); + dest += dstPixelStride; + ++wholeStart; + + if (colour.getAlpha() == 0xff) + { + while (wholeEnd > wholeStart) + { + ((PixelARGB*) dest)->set (colour); + dest += dstPixelStride; + ++wholeStart; + } + } + else + { + while (wholeEnd > wholeStart) + { + ((PixelARGB*) dest)->blend (colour); + dest += dstPixelStride; + ++wholeStart; + } + } + + if (lastAlpha > 0) + { + ((PixelARGB*) dest)->blend (colour, lastAlpha); + } + } + else if (image.getFormat() == Image::RGB) + { + ((PixelRGB*) dest)->blend (colour, roundDoubleToInt (255.0 * (1.0 - (top - wholeStart)))); + dest += dstPixelStride; + ++wholeStart; + + if (colour.getAlpha() == 0xff) + { + while (wholeEnd > wholeStart) + { + ((PixelRGB*) dest)->set (colour); + dest += dstPixelStride; + ++wholeStart; + } + } + else + { + while (wholeEnd > wholeStart) + { + ((PixelRGB*) dest)->blend (colour); + dest += dstPixelStride; + ++wholeStart; + } + } + + if (lastAlpha > 0) + { + ((PixelRGB*) dest)->blend (colour, lastAlpha); + } + } + else + { + jassertfalse + } + } + + image.releasePixelDataReadWrite (dstPixels); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_LowLevelGraphicsSoftwareRenderer.cpp *********/ + +/********* Start of inlined file: juce_RectanglePlacement.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +RectanglePlacement::RectanglePlacement (const RectanglePlacement& other) throw() + : flags (other.flags) +{ +} + +const RectanglePlacement& RectanglePlacement::operator= (const RectanglePlacement& other) throw() +{ + flags = other.flags; + return *this; +} + +void RectanglePlacement::applyTo (double& x, double& y, + double& w, double& h, + const double dx, const double dy, + const double dw, const double dh) const throw() +{ + if (w == 0 || h == 0) + return; + + if ((flags & stretchToFit) != 0) + { + x = dx; + y = dy; + w = dw; + h = dh; + } + else + { + double scale = (flags & fillDestination) != 0 ? jmax (dw / w, dh / h) + : jmin (dw / w, dh / h); + + if ((flags & onlyReduceInSize) != 0) + scale = jmin (scale, 1.0); + + if ((flags & onlyIncreaseInSize) != 0) + scale = jmax (scale, 1.0); + + w *= scale; + h *= scale; + + if ((flags & xLeft) != 0) + x = dx; + else if ((flags & xRight) != 0) + x = dx + dw - w; + else + x = dx + (dw - w) * 0.5; + + if ((flags & yTop) != 0) + y = dy; + else if ((flags & yBottom) != 0) + y = dy + dh - h; + else + y = dy + (dh - h) * 0.5; + } +} + +const AffineTransform RectanglePlacement::getTransformToFit (float x, float y, + float w, float h, + const float dx, const float dy, + const float dw, const float dh) const throw() +{ + if (w == 0 || h == 0) + return AffineTransform::identity; + + const float scaleX = dw / w; + const float scaleY = dh / h; + + if ((flags & stretchToFit) != 0) + { + return AffineTransform::translation (-x, -y) + .scaled (scaleX, scaleY) + .translated (dx - x, dy - y); + } + + float scale = (flags & fillDestination) != 0 ? jmax (scaleX, scaleY) + : jmin (scaleX, scaleY); + + if ((flags & onlyReduceInSize) != 0) + scale = jmin (scale, 1.0f); + + if ((flags & onlyIncreaseInSize) != 0) + scale = jmax (scale, 1.0f); + + w *= scale; + h *= scale; + + float newX = dx; + + if ((flags & xRight) != 0) + newX += dw - w; // right + else if ((flags & xLeft) == 0) + newX += (dw - w) / 2.0f; // centre + + float newY = dy; + + if ((flags & yBottom) != 0) + newY += dh - h; // bottom + else if ((flags & yTop) == 0) + newY += (dh - h) / 2.0f; // centre + + return AffineTransform::translation (-x, -y) + .scaled (scale, scale) + .translated (newX, newY); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_RectanglePlacement.cpp *********/ + +/********* Start of inlined file: juce_Drawable.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +Drawable::Drawable() +{ +} + +Drawable::~Drawable() +{ +} + +void Drawable::drawAt (Graphics& g, const float x, const float y) const +{ + draw (g, AffineTransform::translation (x, y)); +} + +void Drawable::drawWithin (Graphics& g, + const int destX, + const int destY, + const int destW, + const int destH, + const RectanglePlacement& placement) const +{ + if (destW > 0 && destH > 0) + { + float x, y, w, h; + getBounds (x, y, w, h); + + draw (g, placement.getTransformToFit (x, y, w, h, + (float) destX, (float) destY, + (float) destW, (float) destH)); + } +} + +Drawable* Drawable::createFromImageData (const void* data, const int numBytes) +{ + Drawable* result = 0; + + Image* const image = ImageFileFormat::loadFrom (data, numBytes); + + if (image != 0) + { + DrawableImage* const di = new DrawableImage(); + di->setImage (image, true); + result = di; + } + else + { + const String asString (String::createStringFromData (data, numBytes)); + + XmlDocument doc (asString); + XmlElement* const outer = doc.getDocumentElement (true); + + if (outer != 0 && outer->hasTagName (T("svg"))) + { + XmlElement* const svg = doc.getDocumentElement(); + + if (svg != 0) + { + result = Drawable::createFromSVG (*svg); + delete svg; + } + } + + delete outer; + } + + return result; +} + +Drawable* Drawable::createFromImageDataStream (InputStream& dataSource) +{ + MemoryBlock mb; + dataSource.readIntoMemoryBlock (mb); + + return createFromImageData (mb.getData(), mb.getSize()); +} + +Drawable* Drawable::createFromImageFile (const File& file) +{ + FileInputStream* fin = file.createInputStream(); + + if (fin == 0) + return 0; + + Drawable* d = createFromImageDataStream (*fin); + delete fin; + + return d; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_Drawable.cpp *********/ + +/********* Start of inlined file: juce_DrawableComposite.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +DrawableComposite::DrawableComposite() +{ +} + +DrawableComposite::~DrawableComposite() +{ +} + +void DrawableComposite::insertDrawable (Drawable* drawable, + const AffineTransform& transform, + const int index) +{ + if (drawable != 0) + { + if (! drawables.contains (drawable)) + { + drawables.insert (index, drawable); + + if (transform.isIdentity()) + transforms.insert (index, 0); + else + transforms.insert (index, new AffineTransform (transform)); + } + else + { + jassertfalse // trying to add a drawable that's already in here! + } + } +} + +void DrawableComposite::insertDrawable (const Drawable& drawable, + const AffineTransform& transform, + const int index) +{ + insertDrawable (drawable.createCopy(), transform, index); +} + +void DrawableComposite::removeDrawable (const int index) +{ + drawables.remove (index); + transforms.remove (index); +} + +void DrawableComposite::bringToFront (const int index) +{ + if (index >= 0 && index < drawables.size() - 1) + { + drawables.move (index, -1); + transforms.move (index, -1); + } +} + +void DrawableComposite::draw (Graphics& g, const AffineTransform& transform) const +{ + for (int i = 0; i < drawables.size(); ++i) + { + const AffineTransform* const t = transforms.getUnchecked(i); + + drawables.getUnchecked(i)->draw (g, t == 0 ? transform + : t->followedBy (transform)); + } +} + +void DrawableComposite::getBounds (float& x, float& y, float& width, float& height) const +{ + Path totalPath; + + for (int i = 0; i < drawables.size(); ++i) + { + drawables.getUnchecked(i)->getBounds (x, y, width, height); + + if (width > 0.0f && height > 0.0f) + { + Path outline; + outline.addRectangle (x, y, width, height); + + const AffineTransform* const t = transforms.getUnchecked(i); + + if (t == 0) + totalPath.addPath (outline); + else + totalPath.addPath (outline, *t); + } + } + + totalPath.getBounds (x, y, width, height); +} + +bool DrawableComposite::hitTest (float x, float y) const +{ + for (int i = 0; i < drawables.size(); ++i) + { + float tx = x; + float ty = y; + + const AffineTransform* const t = transforms.getUnchecked(i); + + if (t != 0) + t->inverted().transformPoint (tx, ty); + + if (drawables.getUnchecked(i)->hitTest (tx, ty)) + return true; + } + + return false; +} + +Drawable* DrawableComposite::createCopy() const +{ + DrawableComposite* const dc = new DrawableComposite(); + + for (int i = 0; i < drawables.size(); ++i) + { + dc->drawables.add (drawables.getUnchecked(i)->createCopy()); + + const AffineTransform* const t = transforms.getUnchecked(i); + dc->transforms.add (t != 0 ? new AffineTransform (*t) : 0); + } + + return dc; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_DrawableComposite.cpp *********/ + +/********* Start of inlined file: juce_DrawableImage.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +DrawableImage::DrawableImage() + : image (0), + canDeleteImage (false), + opacity (1.0f), + overlayColour (0x00000000) +{ +} + +DrawableImage::~DrawableImage() +{ + clearImage(); +} + +void DrawableImage::clearImage() +{ + if (canDeleteImage && image != 0) + { + if (ImageCache::isImageInCache (image)) + ImageCache::release (image); + else + delete image; + } + + image = 0; +} + +void DrawableImage::setImage (const Image& imageToCopy) +{ + clearImage(); + image = new Image (imageToCopy); + canDeleteImage = true; +} + +void DrawableImage::setImage (Image* imageToUse, + const bool releaseWhenNotNeeded) +{ + clearImage(); + image = imageToUse; + canDeleteImage = releaseWhenNotNeeded; +} + +void DrawableImage::setOpacity (const float newOpacity) +{ + opacity = newOpacity; +} + +void DrawableImage::setOverlayColour (const Colour& newOverlayColour) +{ + overlayColour = newOverlayColour; +} + +void DrawableImage::draw (Graphics& g, const AffineTransform& transform) const +{ + if (image != 0) + { + const Colour oldColour (g.getCurrentColour()); // save this so we can restore it later + + if (opacity > 0.0f && ! overlayColour.isOpaque()) + { + g.setColour (oldColour.withMultipliedAlpha (opacity)); + + g.drawImageTransformed (image, + 0, 0, image->getWidth(), image->getHeight(), + transform, false); + } + + if (! overlayColour.isTransparent()) + { + g.setColour (overlayColour.withMultipliedAlpha (oldColour.getFloatAlpha())); + + g.drawImageTransformed (image, + 0, 0, image->getWidth(), image->getHeight(), + transform, true); + } + + g.setColour (oldColour); + } +} + +void DrawableImage::getBounds (float& x, float& y, float& width, float& height) const +{ + x = 0.0f; + y = 0.0f; + width = 0.0f; + height = 0.0f; + + if (image != 0) + { + width = (float) image->getWidth(); + height = (float) image->getHeight(); + } +} + +bool DrawableImage::hitTest (float x, float y) const +{ + return image != 0 + && x >= 0.0f + && y >= 0.0f + && x < image->getWidth() + && y < image->getHeight() + && image->getPixelAt (roundFloatToInt (x), roundFloatToInt (y)).getAlpha() >= 127; +} + +Drawable* DrawableImage::createCopy() const +{ + DrawableImage* const di = new DrawableImage(); + + di->opacity = opacity; + di->overlayColour = overlayColour; + + if (image != 0) + { + if ((! canDeleteImage) || ! ImageCache::isImageInCache (image)) + { + di->setImage (*image); + } + else + { + ImageCache::incReferenceCount (image); + di->setImage (image, true); + } + } + + return di; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_DrawableImage.cpp *********/ + +/********* Start of inlined file: juce_DrawablePath.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +DrawablePath::DrawablePath() + : fillBrush (new SolidColourBrush (Colours::black)), + strokeBrush (0), + strokeType (0.0f) +{ +} + +DrawablePath::~DrawablePath() +{ + delete fillBrush; + delete strokeBrush; +} + +void DrawablePath::setPath (const Path& newPath) +{ + path = newPath; + updateOutline(); +} + +void DrawablePath::setSolidFill (const Colour& newColour) +{ + delete fillBrush; + fillBrush = new SolidColourBrush (newColour); +} + +void DrawablePath::setFillBrush (const Brush& newBrush) +{ + delete fillBrush; + fillBrush = newBrush.createCopy(); +} + +void DrawablePath::setOutline (const float thickness, const Colour& colour) +{ + strokeType = PathStrokeType (thickness); + delete strokeBrush; + strokeBrush = new SolidColourBrush (colour); + updateOutline(); +} + +void DrawablePath::setOutline (const PathStrokeType& strokeType_, const Brush& newStrokeBrush) +{ + strokeType = strokeType_; + delete strokeBrush; + strokeBrush = newStrokeBrush.createCopy(); + updateOutline(); +} + +void DrawablePath::draw (Graphics& g, const AffineTransform& transform) const +{ + const Colour oldColour (g.getCurrentColour()); // save this so we can restore it later + const float currentOpacity = oldColour.getFloatAlpha(); + + { + Brush* const tempBrush = fillBrush->createCopy(); + tempBrush->applyTransform (transform); + tempBrush->multiplyOpacity (currentOpacity); + + g.setBrush (tempBrush); + g.fillPath (path, transform); + + delete tempBrush; + } + + if (strokeBrush != 0 && strokeType.getStrokeThickness() > 0.0f) + { + Brush* const tempBrush = strokeBrush->createCopy(); + tempBrush->applyTransform (transform); + tempBrush->multiplyOpacity (currentOpacity); + + g.setBrush (tempBrush); + g.fillPath (outline, transform); + + delete tempBrush; + } + + g.setColour (oldColour); +} + +void DrawablePath::updateOutline() +{ + outline.clear(); + strokeType.createStrokedPath (outline, path, AffineTransform::identity, 4.0f); +} + +void DrawablePath::getBounds (float& x, float& y, float& width, float& height) const +{ + if (strokeType.getStrokeThickness() > 0.0f) + outline.getBounds (x, y, width, height); + else + path.getBounds (x, y, width, height); +} + +bool DrawablePath::hitTest (float x, float y) const +{ + return path.contains (x, y) + || outline.contains (x, y); +} + +Drawable* DrawablePath::createCopy() const +{ + DrawablePath* const dp = new DrawablePath(); + + dp->path = path; + dp->setFillBrush (*fillBrush); + + if (strokeBrush != 0) + dp->setOutline (strokeType, *strokeBrush); + + return dp; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_DrawablePath.cpp *********/ + +/********* Start of inlined file: juce_DrawableText.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +DrawableText::DrawableText() + : colour (Colours::white) +{ +} + +DrawableText::~DrawableText() +{ +} + +void DrawableText::setText (const GlyphArrangement& newText) +{ + text = newText; +} + +void DrawableText::setText (const String& newText, const Font& fontToUse) +{ + text.clear(); + text.addLineOfText (fontToUse, newText, 0.0f, 0.0f); +} + +void DrawableText::setColour (const Colour& newColour) +{ + colour = newColour; +} + +void DrawableText::draw (Graphics& g, const AffineTransform& transform) const +{ + const Colour oldColour (g.getCurrentColour()); // save this so we can restore it later + + g.setColour (colour.withMultipliedAlpha (oldColour.getFloatAlpha())); + text.draw (g, transform); + + g.setColour (oldColour); +} + +void DrawableText::getBounds (float& x, float& y, float& width, float& height) const +{ + text.getBoundingBox (0, -1, x, y, width, height, false); // (really returns top, left, bottom, right) + width -= x; + height -= y; +} + +bool DrawableText::hitTest (float x, float y) const +{ + return text.findGlyphIndexAt (x, y) >= 0; +} + +Drawable* DrawableText::createCopy() const +{ + DrawableText* const dt = new DrawableText(); + + dt->text = text; + dt->colour = colour; + + return dt; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_DrawableText.cpp *********/ + +/********* Start of inlined file: juce_SVGParser.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +class SVGState +{ +public: + + SVGState (const XmlElement* const topLevel) + : topLevelXml (topLevel), + x (0), y (0), + width (512), height (512), + viewBoxW (0), viewBoxH (0) + { + } + + ~SVGState() + { + } + + Drawable* parseSVGElement (const XmlElement& xml) + { + if (! xml.hasTagName (T("svg"))) + return 0; + + DrawableComposite* const drawable = new DrawableComposite(); + + drawable->setName (xml.getStringAttribute (T("id"))); + + SVGState newState (*this); + + if (xml.hasAttribute (T("transform"))) + newState.addTransform (xml); + + newState.x = getCoordLength (xml.getStringAttribute (T("x"), String (newState.x)), viewBoxW); + newState.y = getCoordLength (xml.getStringAttribute (T("y"), String (newState.y)), viewBoxH); + newState.width = getCoordLength (xml.getStringAttribute (T("width"), String (newState.width)), viewBoxW); + newState.height = getCoordLength (xml.getStringAttribute (T("height"), String (newState.height)), viewBoxH); + + if (xml.hasAttribute (T("viewBox"))) + { + const String viewParams (xml.getStringAttribute (T("viewBox"))); + int i = 0; + float vx, vy, vw, vh; + + if (parseCoords (viewParams, vx, vy, i, true) + && parseCoords (viewParams, vw, vh, i, true) + && vw > 0 + && vh > 0) + { + newState.viewBoxW = vw; + newState.viewBoxH = vh; + + int placementFlags = 0; + + const String aspect (xml.getStringAttribute (T("preserveAspectRatio"))); + + if (aspect.containsIgnoreCase (T("none"))) + { + placementFlags = RectanglePlacement::stretchToFit; + } + else + { + if (aspect.containsIgnoreCase (T("slice"))) + placementFlags |= RectanglePlacement::fillDestination; + + if (aspect.containsIgnoreCase (T("xMin"))) + placementFlags |= RectanglePlacement::xLeft; + else if (aspect.containsIgnoreCase (T("xMax"))) + placementFlags |= RectanglePlacement::xRight; + else + placementFlags |= RectanglePlacement::xMid; + + if (aspect.containsIgnoreCase (T("yMin"))) + placementFlags |= RectanglePlacement::yTop; + else if (aspect.containsIgnoreCase (T("yMax"))) + placementFlags |= RectanglePlacement::yBottom; + else + placementFlags |= RectanglePlacement::yMid; + } + + const RectanglePlacement placement (placementFlags); + + newState.transform + = placement.getTransformToFit (vx, vy, vw, vh, + 0.0f, 0.0f, newState.width, newState.height) + .followedBy (newState.transform); + } + } + else + { + if (viewBoxW == 0) + newState.viewBoxW = newState.width; + + if (viewBoxH == 0) + newState.viewBoxH = newState.height; + } + + newState.parseSubElements (xml, drawable); + + return drawable; + } + +private: + + const XmlElement* const topLevelXml; + float x, y, width, height, viewBoxW, viewBoxH; + AffineTransform transform; + String cssStyleText; + + void parseSubElements (const XmlElement& xml, DrawableComposite* const parentDrawable) + { + forEachXmlChildElement (xml, e) + { + Drawable* d = 0; + + if (e->hasTagName (T("g"))) + d = parseGroupElement (*e); + else if (e->hasTagName (T("svg"))) + d = parseSVGElement (*e); + else if (e->hasTagName (T("path"))) + d = parsePath (*e); + else if (e->hasTagName (T("rect"))) + d = parseRect (*e); + else if (e->hasTagName (T("circle"))) + d = parseCircle (*e); + else if (e->hasTagName (T("ellipse"))) + d = parseEllipse (*e); + else if (e->hasTagName (T("line"))) + d = parseLine (*e); + else if (e->hasTagName (T("polyline"))) + d = parsePolygon (*e, true); + else if (e->hasTagName (T("polygon"))) + d = parsePolygon (*e, false); + else if (e->hasTagName (T("text"))) + d = parseText (*e); + else if (e->hasTagName (T("switch"))) + d = parseSwitch (*e); + else if (e->hasTagName (T("style"))) + parseCSSStyle (*e); + + parentDrawable->insertDrawable (d); + } + } + + DrawableComposite* parseSwitch (const XmlElement& xml) + { + const XmlElement* const group = xml.getChildByName (T("g")); + + if (group != 0) + return parseGroupElement (*group); + + return 0; + } + + DrawableComposite* parseGroupElement (const XmlElement& xml) + { + DrawableComposite* const drawable = new DrawableComposite(); + + drawable->setName (xml.getStringAttribute (T("id"))); + + if (xml.hasAttribute (T("transform"))) + { + SVGState newState (*this); + newState.addTransform (xml); + + newState.parseSubElements (xml, drawable); + } + else + { + parseSubElements (xml, drawable); + } + + return drawable; + } + + Drawable* parsePath (const XmlElement& xml) const + { + const String d (xml.getStringAttribute (T("d")).trimStart()); + Path path; + + if (getStyleAttribute (&xml, T("fill-rule")).trim().equalsIgnoreCase (T("evenodd"))) + path.setUsingNonZeroWinding (false); + + int index = 0; + float lastX = 0, lastY = 0; + float lastX2 = 0, lastY2 = 0; + tchar lastCommandChar = 0; + bool carryOn = true; + + const String validCommandChars (T("MmLlHhVvCcSsQqTtAaZz")); + + for (;;) + { + float x, y, x2, y2, x3, y3; + const bool isRelative = (d[index] >= 'a' && d[index] <= 'z'); + + if (validCommandChars.containsChar (d[index])) + lastCommandChar = d [index++]; + + switch (lastCommandChar) + { + case T('M'): + case T('m'): + case T('L'): + case T('l'): + if (parseCoords (d, x, y, index, false)) + { + if (isRelative) + { + x += lastX; + y += lastY; + } + + if (lastCommandChar == T('M') || lastCommandChar == T('m')) + path.startNewSubPath (x, y); + else + path.lineTo (x, y); + + lastX2 = lastX; + lastY2 = lastY; + lastX = x; + lastY = y; + } + else + { + ++index; + } + + break; + + case T('H'): + case T('h'): + if (parseCoord (d, x, index, false, true)) + { + if (isRelative) + x += lastX; + + path.lineTo (x, lastY); + + lastX2 = lastX; + lastX = x; + } + else + { + ++index; + } + break; + + case T('V'): + case T('v'): + if (parseCoord (d, y, index, false, false)) + { + if (isRelative) + y += lastY; + + path.lineTo (lastX, y); + + lastY2 = lastY; + lastY = y; + } + else + { + ++index; + } + break; + + case T('C'): + case T('c'): + if (parseCoords (d, x, y, index, false) + && parseCoords (d, x2, y2, index, false) + && parseCoords (d, x3, y3, index, false)) + { + if (isRelative) + { + x += lastX; + y += lastY; + x2 += lastX; + y2 += lastY; + x3 += lastX; + y3 += lastY; + } + + path.cubicTo (x, y, x2, y2, x3, y3); + + lastX2 = x2; + lastY2 = y2; + lastX = x3; + lastY = y3; + } + else + { + ++index; + } + break; + + case T('S'): + case T('s'): + if (parseCoords (d, x, y, index, false) + && parseCoords (d, x3, y3, index, false)) + { + if (isRelative) + { + x += lastX; + y += lastY; + x3 += lastX; + y3 += lastY; + } + + x2 = lastX + (lastX - lastX2); + y2 = lastY + (lastY - lastY2); + path.cubicTo (x2, y2, x, y, x3, y3); + + lastX2 = x2; + lastY2 = y2; + lastX = x3; + lastY = y3; + } + else + { + ++index; + } + break; + + case T('Q'): + case T('q'): + if (parseCoords (d, x, y, index, false) + && parseCoords (d, x2, y2, index, false)) + { + if (isRelative) + { + x += lastX; + y += lastY; + x2 += lastX; + y2 += lastY; + } + + path.quadraticTo (x, y, x2, y2); + + lastX2 = x; + lastY2 = y; + lastX = x2; + lastY = y2; + } + else + { + ++index; + } + break; + + case T('T'): + case T('t'): + if (parseCoords (d, x, y, index, false)) + { + if (isRelative) + { + x += lastX; + y += lastY; + } + + x2 = lastX + (lastX - lastX2); + y2 = lastY + (lastY - lastY2); + path.quadraticTo (x2, y2, x, y); + + lastX2 = x2; + lastY2 = y2; + lastX = x; + lastY = y; + } + else + { + ++index; + } + break; + + case T('A'): + case T('a'): + if (parseCoords (d, x, y, index, false)) + { + String num; + + if (parseNextNumber (d, num, index, false)) + { + const float angle = num.getFloatValue() * (180.0f / float_Pi); + + if (parseNextNumber (d, num, index, false)) + { + const bool largeArc = num.getIntValue() != 0; + + if (parseNextNumber (d, num, index, false)) + { + const bool sweep = num.getIntValue() != 0; + + if (parseCoords (d, x2, y2, index, false)) + { + if (isRelative) + { + x2 += lastX; + y2 += lastY; + } + + if (lastX != x2 || lastY != y2) + { + double centreX, centreY, startAngle, deltaAngle; + double rx = x, ry = y; + + endpointToCentreParameters (lastX, lastY, x2, y2, + angle, largeArc, sweep, + rx, ry, centreX, centreY, + startAngle, deltaAngle); + + path.addCentredArc ((float) centreX, (float) centreY, + (float) rx, (float) ry, + angle, (float) startAngle, (float) (startAngle + deltaAngle), + false); + + path.lineTo (x2, y2); + } + + lastX2 = lastX; + lastY2 = lastY; + lastX = x2; + lastY = y2; + } + } + } + } + } + else + { + ++index; + } + + break; + + case T('Z'): + case T('z'): + path.closeSubPath(); + while (CharacterFunctions::isWhitespace (d [index])) + ++index; + + break; + + default: + carryOn = false; + break; + } + + if (! carryOn) + break; + } + + return parseShape (xml, path); + } + + Drawable* parseRect (const XmlElement& xml) const + { + Path rect; + + const bool hasRX = xml.hasAttribute (T("rx")); + const bool hasRY = xml.hasAttribute (T("ry")); + + if (hasRX || hasRY) + { + float rx = getCoordLength (xml.getStringAttribute (T("rx")), viewBoxW); + float ry = getCoordLength (xml.getStringAttribute (T("ry")), viewBoxH); + + if (! hasRX) + rx = ry; + else if (! hasRY) + ry = rx; + + rect.addRoundedRectangle (getCoordLength (xml.getStringAttribute (T("x")), viewBoxW), + getCoordLength (xml.getStringAttribute (T("y")), viewBoxH), + getCoordLength (xml.getStringAttribute (T("width")), viewBoxW), + getCoordLength (xml.getStringAttribute (T("height")), viewBoxH), + rx, ry); + } + else + { + rect.addRectangle (getCoordLength (xml.getStringAttribute (T("x")), viewBoxW), + getCoordLength (xml.getStringAttribute (T("y")), viewBoxH), + getCoordLength (xml.getStringAttribute (T("width")), viewBoxW), + getCoordLength (xml.getStringAttribute (T("height")), viewBoxH)); + } + + return parseShape (xml, rect); + } + + Drawable* parseCircle (const XmlElement& xml) const + { + Path circle; + + const float cx = getCoordLength (xml.getStringAttribute (T("cx")), viewBoxW); + const float cy = getCoordLength (xml.getStringAttribute (T("cy")), viewBoxH); + const float radius = getCoordLength (xml.getStringAttribute (T("r")), viewBoxW); + + circle.addEllipse (cx - radius, cy - radius, radius * 2.0f, radius * 2.0f); + + return parseShape (xml, circle); + } + + Drawable* parseEllipse (const XmlElement& xml) const + { + Path ellipse; + + const float cx = getCoordLength (xml.getStringAttribute (T("cx")), viewBoxW); + const float cy = getCoordLength (xml.getStringAttribute (T("cy")), viewBoxH); + const float radiusX = getCoordLength (xml.getStringAttribute (T("rx")), viewBoxW); + const float radiusY = getCoordLength (xml.getStringAttribute (T("ry")), viewBoxH); + + ellipse.addEllipse (cx - radiusX, cy - radiusY, radiusX * 2.0f, radiusY * 2.0f); + + return parseShape (xml, ellipse); + } + + Drawable* parseLine (const XmlElement& xml) const + { + Path line; + + const float x1 = getCoordLength (xml.getStringAttribute (T("x1")), viewBoxW); + const float y1 = getCoordLength (xml.getStringAttribute (T("y1")), viewBoxH); + const float x2 = getCoordLength (xml.getStringAttribute (T("x2")), viewBoxW); + const float y2 = getCoordLength (xml.getStringAttribute (T("y2")), viewBoxH); + + line.startNewSubPath (x1, y1); + line.lineTo (x2, y2); + + return parseShape (xml, line); + } + + Drawable* parsePolygon (const XmlElement& xml, const bool isPolyline) const + { + const String points (xml.getStringAttribute (T("points"))); + Path path; + + int index = 0; + float x, y; + + if (parseCoords (points, x, y, index, true)) + { + float firstX = x; + float firstY = y; + float lastX = 0, lastY = 0; + + path.startNewSubPath (x, y); + + while (parseCoords (points, x, y, index, true)) + { + lastX = x; + lastY = y; + path.lineTo (x, y); + } + + if ((! isPolyline) || (firstX == lastX && firstY == lastY)) + path.closeSubPath(); + } + + return parseShape (xml, path); + } + + Drawable* parseShape (const XmlElement& xml, Path& path, + const bool parseTransform = true) const + { + if (parseTransform && xml.hasAttribute (T("transform"))) + { + SVGState newState (*this); + newState.addTransform (xml); + + return newState.parseShape (xml, path, false); + } + + DrawablePath* dp = new DrawablePath(); + dp->setSolidFill (Colours::transparentBlack); + + path.applyTransform (transform); + dp->setPath (path); + + Path::Iterator iter (path); + + bool containsClosedSubPath = false; + while (iter.next()) + { + if (iter.elementType == Path::Iterator::closePath) + { + containsClosedSubPath = true; + break; + } + } + + Brush* const fillBrush + = getBrushForFill (path, + getStyleAttribute (&xml, T("fill")), + getStyleAttribute (&xml, T("fill-opacity")), + getStyleAttribute (&xml, T("opacity")), + containsClosedSubPath ? Colours::black + : Colours::transparentBlack); + + if (fillBrush != 0) + { + if (! fillBrush->isInvisible()) + { + fillBrush->applyTransform (transform); + dp->setFillBrush (*fillBrush); + } + + delete fillBrush; + } + + const String strokeType (getStyleAttribute (&xml, T("stroke"))); + + if (strokeType.isNotEmpty() && ! strokeType.equalsIgnoreCase (T("none"))) + { + Brush* const strokeBrush + = getBrushForFill (path, strokeType, + getStyleAttribute (&xml, T("stroke-opacity")), + getStyleAttribute (&xml, T("opacity")), + Colours::transparentBlack); + + if (strokeBrush != 0) + { + const PathStrokeType stroke (getStrokeFor (&xml)); + + if (! strokeBrush->isInvisible()) + { + strokeBrush->applyTransform (transform); + dp->setOutline (stroke, *strokeBrush); + } + + delete strokeBrush; + } + } + + return dp; + } + + const XmlElement* findLinkedElement (const XmlElement* e) const + { + const String id (e->getStringAttribute (T("xlink:href"))); + + if (! id.startsWithChar (T('#'))) + return 0; + + return findElementForId (topLevelXml, id.substring (1)); + } + + void addGradientStopsIn (ColourGradient& cg, const XmlElement* const fillXml) const + { + if (fillXml == 0) + return; + + forEachXmlChildElementWithTagName (*fillXml, e, T("stop")) + { + int index = 0; + Colour col (parseColour (getStyleAttribute (e, T("stop-color")), index, Colours::black)); + + const String opacity (getStyleAttribute (e, T("stop-opacity"), T("1"))); + col = col.withMultipliedAlpha (jlimit (0.0f, 1.0f, opacity.getFloatValue())); + + double offset = e->getDoubleAttribute (T("offset")); + + if (e->getStringAttribute (T("offset")).containsChar (T('%'))) + offset *= 0.01; + + cg.addColour (jlimit (0.0, 1.0, offset), col); + } + } + + Brush* getBrushForFill (const Path& path, + const String& fill, + const String& fillOpacity, + const String& overallOpacity, + const Colour& defaultColour) const + { + float opacity = 1.0f; + + if (overallOpacity.isNotEmpty()) + opacity = jlimit (0.0f, 1.0f, overallOpacity.getFloatValue()); + + if (fillOpacity.isNotEmpty()) + opacity *= (jlimit (0.0f, 1.0f, fillOpacity.getFloatValue())); + + if (fill.startsWithIgnoreCase (T("url"))) + { + const String id (fill.fromFirstOccurrenceOf (T("#"), false, false) + .upToLastOccurrenceOf (T(")"), false, false).trim()); + + const XmlElement* const fillXml = findElementForId (topLevelXml, id); + + if (fillXml != 0 + && (fillXml->hasTagName (T("linearGradient")) + || fillXml->hasTagName (T("radialGradient")))) + { + const XmlElement* inheritedFrom = findLinkedElement (fillXml); + + ColourGradient cg; + + addGradientStopsIn (cg, inheritedFrom); + addGradientStopsIn (cg, fillXml); + + if (cg.getNumColours() > 0) + { + cg.addColour (0.0, cg.getColour (0)); + cg.addColour (1.0, cg.getColour (cg.getNumColours() - 1)); + } + else + { + cg.addColour (0.0, Colours::black); + cg.addColour (1.0, Colours::black); + } + + if (overallOpacity.isNotEmpty()) + cg.multiplyOpacity (overallOpacity.getFloatValue()); + + jassert (cg.getNumColours() > 0); + + cg.isRadial = fillXml->hasTagName (T("radialGradient")); + cg.transform = parseTransform (fillXml->getStringAttribute (T("gradientTransform"))); + + float width = viewBoxW; + float height = viewBoxH; + float dx = 0.0; + float dy = 0.0; + + const bool userSpace = fillXml->getStringAttribute (T("gradientUnits")).equalsIgnoreCase (T("userSpaceOnUse")); + + if (! userSpace) + path.getBounds (dx, dy, width, height); + + if (cg.isRadial) + { + cg.x1 = dx + getCoordLength (fillXml->getStringAttribute (T("cx"), T("50%")), width); + cg.y1 = dy + getCoordLength (fillXml->getStringAttribute (T("cy"), T("50%")), height); + + const float radius = getCoordLength (fillXml->getStringAttribute (T("r"), T("50%")), width); + + cg.x2 = cg.x1 + radius; + cg.y2 = cg.y1; + + //xxx (the fx, fy focal point isn't handled properly here..) + } + else + { + cg.x1 = dx + getCoordLength (fillXml->getStringAttribute (T("x1"), T("0%")), width); + cg.y1 = dy + getCoordLength (fillXml->getStringAttribute (T("y1"), T("0%")), height); + + cg.x2 = dx + getCoordLength (fillXml->getStringAttribute (T("x2"), T("100%")), width); + cg.y2 = dy + getCoordLength (fillXml->getStringAttribute (T("y2"), T("0%")), height); + + if (cg.x1 == cg.x2 && cg.y1 == cg.y2) + return new SolidColourBrush (cg.getColour (cg.getNumColours() - 1)); + } + + return new GradientBrush (cg); + } + } + + if (fill.equalsIgnoreCase (T("none"))) + return new SolidColourBrush (Colours::transparentBlack); + + int i = 0; + Colour colour (parseColour (fill, i, defaultColour)); + colour = colour.withMultipliedAlpha (opacity); + + return new SolidColourBrush (colour); + } + + const PathStrokeType getStrokeFor (const XmlElement* const xml) const + { + const String width (getStyleAttribute (xml, T("stroke-width"))); + const String cap (getStyleAttribute (xml, T("stroke-linecap"))); + const String join (getStyleAttribute (xml, T("stroke-linejoin"))); + + //const String mitreLimit (getStyleAttribute (xml, T("stroke-miterlimit"))); + //const String dashArray (getStyleAttribute (xml, T("stroke-dasharray"))); + //const String dashOffset (getStyleAttribute (xml, T("stroke-dashoffset"))); + + PathStrokeType::JointStyle joinStyle = PathStrokeType::mitered; + PathStrokeType::EndCapStyle capStyle = PathStrokeType::butt; + + if (join.equalsIgnoreCase (T("round"))) + joinStyle = PathStrokeType::curved; + else if (join.equalsIgnoreCase (T("bevel"))) + joinStyle = PathStrokeType::beveled; + + if (cap.equalsIgnoreCase (T("round"))) + capStyle = PathStrokeType::rounded; + else if (cap.equalsIgnoreCase (T("square"))) + capStyle = PathStrokeType::square; + + float ox = 0.0f, oy = 0.0f; + transform.transformPoint (ox, oy); + float x = getCoordLength (width, viewBoxW), y = 0.0f; + transform.transformPoint (x, y); + + return PathStrokeType (width.isNotEmpty() ? juce_hypotf (x - ox, y - oy) : 1.0f, + joinStyle, capStyle); + } + + Drawable* parseText (const XmlElement& xml) + { + Array xCoords, yCoords, dxCoords, dyCoords; + + getCoordList (xCoords, getInheritedAttribute (&xml, T("x")), true, true); + getCoordList (yCoords, getInheritedAttribute (&xml, T("y")), true, false); + getCoordList (dxCoords, getInheritedAttribute (&xml, T("dx")), true, true); + getCoordList (dyCoords, getInheritedAttribute (&xml, T("dy")), true, false); + + //xxx not done text yet! + + forEachXmlChildElement (xml, e) + { + if (e->isTextElement()) + { + const String text (e->getText()); + + Path path; + parseShape (*e, path); + } + else if (e->hasTagName (T("tspan"))) + { + parseText (*e); + } + } + + return 0; + } + + void addTransform (const XmlElement& xml) + { + transform = parseTransform (xml.getStringAttribute (T("transform"))) + .followedBy (transform); + } + + bool parseCoord (const String& s, float& value, int& index, + const bool allowUnits, const bool isX) const + { + String number; + + if (! parseNextNumber (s, number, index, allowUnits)) + { + value = 0; + return false; + } + + value = getCoordLength (number, isX ? viewBoxW : viewBoxH); + return true; + } + + bool parseCoords (const String& s, float& x, float& y, + int& index, const bool allowUnits) const + { + return parseCoord (s, x, index, allowUnits, true) + && parseCoord (s, y, index, allowUnits, false); + } + + float getCoordLength (const String& s, const float sizeForProportions) const + { + float n = s.getFloatValue(); + const int len = s.length(); + + if (len > 2) + { + const float dpi = 96.0f; + + const tchar n1 = s [len - 2]; + const tchar n2 = s [len - 1]; + + if (n1 == T('i') && n2 == T('n')) + n *= dpi; + else if (n1 == T('m') && n2 == T('m')) + n *= dpi / 25.4f; + else if (n1 == T('c') && n2 == T('m')) + n *= dpi / 2.54f; + else if (n1 == T('p') && n2 == T('c')) + n *= 15.0f; + else if (n2 == T('%')) + n *= 0.01f * sizeForProportions; + } + + return n; + } + + void getCoordList (Array & coords, const String& list, + const bool allowUnits, const bool isX) const + { + int index = 0; + float value; + + while (parseCoord (list, value, index, allowUnits, isX)) + coords.add (value); + } + + void parseCSSStyle (const XmlElement& xml) + { + cssStyleText = xml.getAllSubText() + T("\n") + cssStyleText; + } + + const String getStyleAttribute (const XmlElement* xml, const String& attributeName, + const String& defaultValue = String::empty) const + { + if (xml->hasAttribute (attributeName)) + return xml->getStringAttribute (attributeName, defaultValue); + + const String styleAtt (xml->getStringAttribute (T("style"))); + + if (styleAtt.isNotEmpty()) + { + const String value (getAttributeFromStyleList (styleAtt, attributeName, String::empty)); + + if (value.isNotEmpty()) + return value; + } + else if (xml->hasAttribute (T("class"))) + { + const String className (T(".") + xml->getStringAttribute (T("class"))); + + int index = cssStyleText.indexOfIgnoreCase (className + T(" ")); + + if (index < 0) + index = cssStyleText.indexOfIgnoreCase (className + T("{")); + + if (index >= 0) + { + const int openBracket = cssStyleText.indexOfChar (index, T('{')); + + if (openBracket > index) + { + const int closeBracket = cssStyleText.indexOfChar (openBracket, T('}')); + + if (closeBracket > openBracket) + { + const String value (getAttributeFromStyleList (cssStyleText.substring (openBracket + 1, closeBracket), attributeName, defaultValue)); + + if (value.isNotEmpty()) + return value; + } + } + } + } + + xml = const_cast (topLevelXml)->findParentElementOf (xml); + + if (xml != 0) + return getStyleAttribute (xml, attributeName, defaultValue); + + return defaultValue; + } + + const String getInheritedAttribute (const XmlElement* xml, const String& attributeName) const + { + if (xml->hasAttribute (attributeName)) + return xml->getStringAttribute (attributeName); + + xml = const_cast (topLevelXml)->findParentElementOf (xml); + + if (xml != 0) + return getInheritedAttribute (xml, attributeName); + + return String::empty; + } + + static bool isIdentifierChar (const tchar c) + { + return CharacterFunctions::isLetter (c) || c == T('-'); + } + + static const String getAttributeFromStyleList (const String& list, const String& attributeName, const String& defaultValue) + { + int i = 0; + + for (;;) + { + i = list.indexOf (i, attributeName); + + if (i < 0) + break; + + if ((i == 0 || (i > 0 && ! isIdentifierChar (list [i - 1]))) + && ! isIdentifierChar (list [i + attributeName.length()])) + { + i = list.indexOfChar (i, T(':')); + + if (i < 0) + break; + + int end = list.indexOfChar (i, T(';')); + + if (end < 0) + end = 0x7ffff; + + return list.substring (i + 1, end).trim(); + } + + ++i; + } + + return defaultValue; + } + + static bool parseNextNumber (const String& source, String& value, int& index, const bool allowUnits) + { + const tchar* const s = (const tchar*) source; + + while (CharacterFunctions::isWhitespace (s[index]) || s[index] == T(',')) + ++index; + + int start = index; + + if (CharacterFunctions::isDigit (s[index]) || s[index] == T('.') || s[index] == T('-')) + ++index; + + while (CharacterFunctions::isDigit (s[index]) || s[index] == T('.')) + ++index; + + if ((s[index] == T('e') || s[index] == T('E')) + && (CharacterFunctions::isDigit (s[index + 1]) + || s[index + 1] == T('-') + || s[index + 1] == T('+'))) + { + index += 2; + + while (CharacterFunctions::isDigit (s[index])) + ++index; + } + + if (allowUnits) + { + while (CharacterFunctions::isLetter (s[index])) + ++index; + } + + if (index == start) + return false; + + value = String (s + start, index - start); + + while (CharacterFunctions::isWhitespace (s[index]) || s[index] == T(',')) + ++index; + + return true; + } + + static const Colour parseColour (const String& s, int& index, const Colour& defaultColour) + { + if (s [index] == T('#')) + { + uint32 hex [6]; + zeromem (hex, sizeof (hex)); + int numChars = 0; + + for (int i = 6; --i >= 0;) + { + const int hexValue = CharacterFunctions::getHexDigitValue (s [++index]); + + if (hexValue >= 0) + hex [numChars++] = hexValue; + else + break; + } + + if (numChars <= 3) + return Colour ((uint8) (hex [0] * 0x11), + (uint8) (hex [1] * 0x11), + (uint8) (hex [2] * 0x11)); + else + return Colour ((uint8) ((hex [0] << 4) + hex [1]), + (uint8) ((hex [2] << 4) + hex [3]), + (uint8) ((hex [4] << 4) + hex [5])); + } + else if (s [index] == T('r') + && s [index + 1] == T('g') + && s [index + 2] == T('b')) + { + const int openBracket = s.indexOfChar (index, T('(')); + const int closeBracket = s.indexOfChar (openBracket, T(')')); + + if (openBracket >= 3 && closeBracket > openBracket) + { + index = closeBracket; + + StringArray tokens; + tokens.addTokens (s.substring (openBracket + 1, closeBracket), T(","), T("")); + tokens.trim(); + tokens.removeEmptyStrings(); + + if (tokens[0].containsChar T('%')) + return Colour ((uint8) roundDoubleToInt (2.55 * tokens[0].getDoubleValue()), + (uint8) roundDoubleToInt (2.55 * tokens[1].getDoubleValue()), + (uint8) roundDoubleToInt (2.55 * tokens[2].getDoubleValue())); + else + return Colour ((uint8) tokens[0].getIntValue(), + (uint8) tokens[1].getIntValue(), + (uint8) tokens[2].getIntValue()); + } + } + + return Colours::findColourForName (s, defaultColour); + } + + static const AffineTransform parseTransform (String t) + { + AffineTransform result; + + while (t.isNotEmpty()) + { + StringArray tokens; + tokens.addTokens (t.fromFirstOccurrenceOf (T("("), false, false) + .upToFirstOccurrenceOf (T(")"), false, false), + T(", "), 0); + + tokens.removeEmptyStrings (true); + + float numbers [6]; + + for (int i = 0; i < 6; ++i) + numbers[i] = tokens[i].getFloatValue(); + + AffineTransform trans; + + if (t.startsWithIgnoreCase (T("matrix"))) + { + trans = AffineTransform (numbers[0], numbers[2], numbers[4], + numbers[1], numbers[3], numbers[5]); + } + else if (t.startsWithIgnoreCase (T("translate"))) + { + trans = trans.translated (numbers[0], numbers[1]); + } + else if (t.startsWithIgnoreCase (T("scale"))) + { + if (tokens.size() == 1) + trans = trans.scaled (numbers[0], numbers[0]); + else + trans = trans.scaled (numbers[0], numbers[1]); + } + else if (t.startsWithIgnoreCase (T("rotate"))) + { + if (tokens.size() != 3) + trans = trans.rotated (numbers[0] / (180.0f / float_Pi)); + else + trans = trans.rotated (numbers[0] / (180.0f / float_Pi), + numbers[1], numbers[2]); + } + else if (t.startsWithIgnoreCase (T("skewX"))) + { + trans = AffineTransform (1.0f, tanf (numbers[0] * (float_Pi / 180.0f)), 0.0f, + 0.0f, 1.0f, 0.0f); + } + else if (t.startsWithIgnoreCase (T("skewY"))) + { + trans = AffineTransform (1.0f, 0.0f, 0.0f, + tanf (numbers[0] * (float_Pi / 180.0f)), 1.0f, 0.0f); + } + + result = trans.followedBy (result); + t = t.fromFirstOccurrenceOf (T(")"), false, false).trimStart(); + } + + return result; + } + + static void endpointToCentreParameters (const double x1, const double y1, + const double x2, const double y2, + const double angle, + const bool largeArc, const bool sweep, + double& rx, double& ry, + double& centreX, double& centreY, + double& startAngle, double& deltaAngle) + { + const double midX = (x1 - x2) * 0.5; + const double midY = (y1 - y2) * 0.5; + + const double cosAngle = cos (angle); + const double sinAngle = sin (angle); + const double xp = cosAngle * midX + sinAngle * midY; + const double yp = cosAngle * midY - sinAngle * midX; + const double xp2 = xp * xp; + const double yp2 = yp * yp; + + double rx2 = rx * rx; + double ry2 = ry * ry; + + const double s = (xp2 / rx2) + (yp2 / ry2); + double c; + + if (s <= 1.0) + { + c = sqrt (jmax (0.0, ((rx2 * ry2) - (rx2 * yp2) - (ry2 * xp2)) + / (( rx2 * yp2) + (ry2 * xp2)))); + + if (largeArc == sweep) + c = -c; + } + else + { + const double s2 = sqrt (s); + rx *= s2; + ry *= s2; + rx2 = rx * rx; + ry2 = ry * ry; + c = 0; + } + + const double cpx = ((rx * yp) / ry) * c; + const double cpy = ((-ry * xp) / rx) * c; + + centreX = ((x1 + x2) * 0.5) + (cosAngle * cpx) - (sinAngle * cpy); + centreY = ((y1 + y2) * 0.5) + (sinAngle * cpx) + (cosAngle * cpy); + + const double ux = (xp - cpx) / rx; + const double uy = (yp - cpy) / ry; + const double vx = (-xp - cpx) / rx; + const double vy = (-yp - cpy) / ry; + + const double length = juce_hypot (ux, uy); + + startAngle = acos (jlimit (-1.0, 1.0, ux / length)); + + if (uy < 0) + startAngle = -startAngle; + + startAngle += double_Pi * 0.5; + + deltaAngle = acos (jlimit (-1.0, 1.0, ((ux * vx) + (uy * vy)) + / (length * juce_hypot (vx, vy)))); + + if ((ux * vy) - (uy * vx) < 0) + deltaAngle = -deltaAngle; + + if (sweep) + { + if (deltaAngle < 0) + deltaAngle += double_Pi * 2.0; + } + else + { + if (deltaAngle > 0) + deltaAngle -= double_Pi * 2.0; + } + + deltaAngle = fmod (deltaAngle, double_Pi * 2.0); + } + + static const XmlElement* findElementForId (const XmlElement* const parent, const String& id) + { + forEachXmlChildElement (*parent, e) + { + if (e->compareAttribute (T("id"), id)) + return e; + + const XmlElement* const found = findElementForId (e, id); + + if (found != 0) + return found; + } + + return 0; + } + + const SVGState& operator= (const SVGState&); +}; + +Drawable* Drawable::createFromSVG (const XmlElement& svgDocument) +{ + SVGState state (&svgDocument); + return state.parseSVGElement (svgDocument); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_SVGParser.cpp *********/ + +/********* Start of inlined file: juce_DropShadowEffect.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +#if JUCE_MSVC + #pragma optimize ("t", on) // try to avoid slowing everything down in debug builds +#endif + +DropShadowEffect::DropShadowEffect() + : offsetX (0), + offsetY (0), + radius (4), + opacity (0.6f) +{ +} + +DropShadowEffect::~DropShadowEffect() +{ +} + +void DropShadowEffect::setShadowProperties (const float newRadius, + const float newOpacity, + const int newShadowOffsetX, + const int newShadowOffsetY) +{ + radius = jmax (1.1f, newRadius); + offsetX = newShadowOffsetX; + offsetY = newShadowOffsetY; + opacity = newOpacity; +} + +void DropShadowEffect::applyEffect (Image& image, Graphics& g) +{ + const int w = image.getWidth(); + const int h = image.getHeight(); + + int lineStride, pixelStride; + const PixelARGB* srcPixels = (const PixelARGB*) image.lockPixelDataReadOnly (0, 0, image.getWidth(), image.getHeight(), lineStride, pixelStride); + + Image shadowImage (Image::SingleChannel, w, h, false); + int destStride, destPixelStride; + uint8* const shadowChannel = (uint8*) shadowImage.lockPixelDataReadWrite (0, 0, w, h, destStride, destPixelStride); + + const int filter = roundFloatToInt (63.0f / radius); + const int radiusMinus1 = roundFloatToInt ((radius - 1.0f) * 63.0f); + + for (int x = w; --x >= 0;) + { + int shadowAlpha = 0; + + const PixelARGB* src = srcPixels + x; + uint8* shadowPix = shadowChannel + x; + + for (int y = h; --y >= 0;) + { + shadowAlpha = ((shadowAlpha * radiusMinus1 + (src->getAlpha() << 6)) * filter) >> 12; + + *shadowPix = (uint8) shadowAlpha; + src = (const PixelARGB*) (((const uint8*) src) + lineStride); + shadowPix += destStride; + } + } + + for (int y = h; --y >= 0;) + { + int shadowAlpha = 0; + uint8* shadowPix = shadowChannel + y * destStride; + + for (int x = w; --x >= 0;) + { + shadowAlpha = ((shadowAlpha * radiusMinus1 + (*shadowPix << 6)) * filter) >> 12; + *shadowPix++ = (uint8) shadowAlpha; + } + } + + image.releasePixelDataReadOnly (srcPixels); + shadowImage.releasePixelDataReadWrite (shadowChannel); + + g.setColour (Colours::black.withAlpha (opacity)); + g.drawImageAt (&shadowImage, offsetX, offsetY, true); + + g.setOpacity (1.0f); + g.drawImageAt (&image, 0, 0); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_DropShadowEffect.cpp *********/ + +/********* Start of inlined file: juce_GlowEffect.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +GlowEffect::GlowEffect() + : radius (2.0f), + colour (Colours::white) +{ +} + +GlowEffect::~GlowEffect() +{ +} + +void GlowEffect::setGlowProperties (const float newRadius, + const Colour& newColour) +{ + radius = newRadius; + colour = newColour; +} + +void GlowEffect::applyEffect (Image& image, Graphics& g) +{ + const int w = image.getWidth(); + const int h = image.getHeight(); + + Image temp (image.getFormat(), w, h, true); + + ImageConvolutionKernel blurKernel (roundFloatToInt (radius * 2.0f)); + + blurKernel.createGaussianBlur (radius); + blurKernel.rescaleAllValues (radius); + + blurKernel.applyToImage (temp, &image, 0, 0, w, h); + + g.setColour (colour); + g.drawImageAt (&temp, 0, 0, true); + + g.setOpacity (1.0f); + g.drawImageAt (&image, 0, 0, false); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_GlowEffect.cpp *********/ + +/********* Start of inlined file: juce_ReduceOpacityEffect.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +ReduceOpacityEffect::ReduceOpacityEffect (const float opacity_) + : opacity (opacity_) +{ +} + +ReduceOpacityEffect::~ReduceOpacityEffect() +{ +} + +void ReduceOpacityEffect::setOpacity (const float newOpacity) +{ + opacity = jlimit (0.0f, 1.0f, newOpacity); +} + +void ReduceOpacityEffect::applyEffect (Image& image, Graphics& g) +{ + g.setOpacity (opacity); + g.drawImageAt (&image, 0, 0); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ReduceOpacityEffect.cpp *********/ + +/********* Start of inlined file: juce_Font.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +static const float minFontHeight = 0.1f; +static const float maxFontHeight = 10000.0f; +static const float defaultFontHeight = 14.0f; + +static String defaultSans, defaultSerif, defaultFixed, fallbackFont; + +Font::Font() throw() + : typefaceName (defaultSans), + height (defaultFontHeight), + horizontalScale (1.0f), + kerning (0), + ascent (0), + styleFlags (Font::plain) +{ +} + +void Font::resetToDefaultState() throw() +{ + typefaceName = defaultSans; + height = defaultFontHeight; + horizontalScale = 1.0f; + kerning = 0; + ascent = 0; + styleFlags = Font::plain; + typeface = 0; +} + +Font::Font (const float fontHeight, + const int styleFlags_) throw() + : typefaceName (defaultSans), + height (jlimit (minFontHeight, maxFontHeight, fontHeight)), + horizontalScale (1.0f), + kerning (0), + ascent (0), + styleFlags (styleFlags_) +{ +} + +Font::Font (const String& typefaceName_, + const float fontHeight, + const int styleFlags_) throw() + : typefaceName (typefaceName_), + height (jlimit (minFontHeight, maxFontHeight, fontHeight)), + horizontalScale (1.0f), + kerning (0), + ascent (0), + styleFlags (styleFlags_) +{ +} + +Font::Font (const Font& other) throw() + : typefaceName (other.typefaceName), + height (other.height), + horizontalScale (other.horizontalScale), + kerning (other.kerning), + ascent (other.ascent), + styleFlags (other.styleFlags), + typeface (other.typeface) +{ +} + +const Font& Font::operator= (const Font& other) throw() +{ + if (this != &other) + { + typefaceName = other.typefaceName; + height = other.height; + styleFlags = other.styleFlags; + horizontalScale = other.horizontalScale; + kerning = other.kerning; + ascent = other.ascent; + typeface = other.typeface; + } + + return *this; +} + +Font::~Font() throw() +{ +} + +Font::Font (const Typeface& face) throw() + : height (11.0f), + horizontalScale (1.0f), + kerning (0), + ascent (0), + styleFlags (plain) +{ + typefaceName = face.getName(); + setBold (face.isBold()); + setItalic (face.isItalic()); + typeface = new Typeface (face); +} + +bool Font::operator== (const Font& other) const throw() +{ + return height == other.height + && horizontalScale == other.horizontalScale + && kerning == other.kerning + && styleFlags == other.styleFlags + && typefaceName == other.typefaceName; +} + +bool Font::operator!= (const Font& other) const throw() +{ + return ! operator== (other); +} + +void Font::setTypefaceName (const String& faceName) throw() +{ + typefaceName = faceName; + typeface = 0; + ascent = 0; +} + +void Font::initialiseDefaultFontNames() throw() +{ + Font::getDefaultFontNames (defaultSans, + defaultSerif, + defaultFixed); +} + +void clearUpDefaultFontNames() throw() // called at shutdown by code in Typface +{ + defaultSans = String::empty; + defaultSerif = String::empty; + defaultFixed = String::empty; + fallbackFont = String::empty; +} + +const String Font::getDefaultSansSerifFontName() throw() +{ + return defaultSans; +} + +const String Font::getDefaultSerifFontName() throw() +{ + return defaultSerif; +} + +const String Font::getDefaultMonospacedFontName() throw() +{ + return defaultFixed; +} + +void Font::setDefaultSansSerifFontName (const String& name) throw() +{ + defaultSans = name; +} + +const String Font::getFallbackFontName() throw() +{ + return fallbackFont; +} + +void Font::setFallbackFontName (const String& name) throw() +{ + fallbackFont = name; +} + +void Font::setHeight (float newHeight) throw() +{ + height = jlimit (minFontHeight, maxFontHeight, newHeight); +} + +void Font::setHeightWithoutChangingWidth (float newHeight) throw() +{ + newHeight = jlimit (minFontHeight, maxFontHeight, newHeight); + horizontalScale *= (height / newHeight); + height = newHeight; +} + +void Font::setStyleFlags (const int newFlags) throw() +{ + if (styleFlags != newFlags) + { + styleFlags = newFlags; + typeface = 0; + ascent = 0; + } +} + +void Font::setSizeAndStyle (const float newHeight, + const int newStyleFlags, + const float newHorizontalScale, + const float newKerningAmount) throw() +{ + height = jlimit (minFontHeight, maxFontHeight, newHeight); + horizontalScale = newHorizontalScale; + kerning = newKerningAmount; + + setStyleFlags (newStyleFlags); +} + +void Font::setHorizontalScale (const float scaleFactor) throw() +{ + horizontalScale = scaleFactor; +} + +void Font::setExtraKerningFactor (const float extraKerning) throw() +{ + kerning = extraKerning; +} + +void Font::setBold (const bool shouldBeBold) throw() +{ + setStyleFlags (shouldBeBold ? (styleFlags | bold) + : (styleFlags & ~bold)); +} + +bool Font::isBold() const throw() +{ + return (styleFlags & bold) != 0; +} + +void Font::setItalic (const bool shouldBeItalic) throw() +{ + setStyleFlags (shouldBeItalic ? (styleFlags | italic) + : (styleFlags & ~italic)); +} + +bool Font::isItalic() const throw() +{ + return (styleFlags & italic) != 0; +} + +void Font::setUnderline (const bool shouldBeUnderlined) throw() +{ + setStyleFlags (shouldBeUnderlined ? (styleFlags | underlined) + : (styleFlags & ~underlined)); +} + +bool Font::isUnderlined() const throw() +{ + return (styleFlags & underlined) != 0; +} + +float Font::getAscent() const throw() +{ + if (ascent == 0) + ascent = getTypeface()->getAscent(); + + return height * ascent; +} + +float Font::getDescent() const throw() +{ + return height - getAscent(); +} + +int Font::getStringWidth (const String& text) const throw() +{ + return roundFloatToInt (getStringWidthFloat (text)); +} + +float Font::getStringWidthFloat (const String& text) const throw() +{ + float x = 0.0f; + + if (text.isNotEmpty()) + { + Typeface* const typeface = getTypeface(); + const juce_wchar* t = (const juce_wchar*) text; + + do + { + const TypefaceGlyphInfo* const glyph = typeface->getGlyph (*t++); + + if (glyph != 0) + x += kerning + glyph->getHorizontalSpacing (*t); + } + while (*t != 0); + + x *= height; + x *= horizontalScale; + } + + return x; +} + +Typeface* Font::getTypeface() const throw() +{ + if (typeface == 0) + typeface = Typeface::getTypefaceFor (*this); + + return typeface; +} + +void Font::findFonts (OwnedArray& destArray) throw() +{ + const StringArray names (findAllTypefaceNames()); + + for (int i = 0; i < names.size(); ++i) + destArray.add (new Font (names[i], defaultFontHeight, Font::plain)); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_Font.cpp *********/ + +/********* Start of inlined file: juce_GlyphArrangement.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +#define SHOULD_WRAP(x, wrapwidth) (((x) - 0.0001f) >= (wrapwidth)) + +class FontGlyphAlphaMap +{ +public: + + bool draw (const Graphics& g, float x, const float y) const throw() + { + if (bitmap1 == 0) + return false; + + x += xOrigin; + const float xFloor = floorf (x); + const int intX = (int) xFloor; + + g.drawImageAt (((x - xFloor) >= 0.5f && bitmap2 != 0) ? bitmap2 : bitmap1, + intX, (int) floorf (y + yOrigin), true); + + return true; + } + + juce_UseDebuggingNewOperator + +private: + + Image* bitmap1; + Image* bitmap2; + float xOrigin, yOrigin; + int lastAccessCount; + Typeface::Ptr typeface; + float height, horizontalScale; + juce_wchar character; + + friend class GlyphCache; + + FontGlyphAlphaMap() throw() + : bitmap1 (0), + bitmap2 (0), + lastAccessCount (0), + height (0), + horizontalScale (0), + character (0) + { + } + + ~FontGlyphAlphaMap() throw() + { + delete bitmap1; + delete bitmap2; + } + + class AlphaBitmapRenderer + { + uint8* const data; + const int stride; + uint8* lineStart; + + AlphaBitmapRenderer (const AlphaBitmapRenderer&); + const AlphaBitmapRenderer& operator= (const AlphaBitmapRenderer&); + + public: + AlphaBitmapRenderer (uint8* const data_, + const int stride_) throw() + : data (data_), + stride (stride_) + { + } + + forcedinline void setEdgeTableYPos (const int y) throw() + { + lineStart = data + (stride * y); + } + + forcedinline void handleEdgeTablePixel (const int x, const int alphaLevel) const throw() + { + lineStart [x] = (uint8) alphaLevel; + } + + forcedinline void handleEdgeTableLine (const int x, int width, const int alphaLevel) const throw() + { + uint8* d = lineStart + x; + + while (--width >= 0) + *d++ = (uint8) alphaLevel; + } + }; + + Image* createAlphaMapFromPath (const Path& path, + float& topLeftX, float& topLeftY, + float xScale, float yScale, + const float subPixelOffsetX) throw() + { + Image* im = 0; + + float px, py, pw, ph; + path.getBounds (px, py, pw, ph); + + topLeftX = floorf (px * xScale); + topLeftY = floorf (py * yScale); + + int bitmapWidth = roundFloatToInt (pw * xScale) + 2; + int bitmapHeight = roundFloatToInt (ph * yScale) + 2; + + im = new Image (Image::SingleChannel, bitmapWidth, bitmapHeight, true); + + EdgeTable edgeTable (0, bitmapHeight, EdgeTable::Oversampling_16times); + + edgeTable.addPath (path, AffineTransform::scale (xScale, yScale) + .translated (subPixelOffsetX - topLeftX, -topLeftY)); + + int stride, pixelStride; + uint8* const pixels = (uint8*) im->lockPixelDataReadWrite (0, 0, bitmapWidth, bitmapHeight, stride, pixelStride); + + jassert (pixelStride == 1); + AlphaBitmapRenderer renderer (pixels, stride); + edgeTable.iterate (renderer, 0, 0, bitmapWidth, bitmapHeight, 0); + + im->releasePixelDataReadWrite (pixels); + + return im; + } + + void generate (Typeface* const face, + const juce_wchar character_, + const float fontHeight, + const float fontHorizontalScale) throw() + { + character = character_; + typeface = face; + height = fontHeight; + horizontalScale = fontHorizontalScale; + + const Path* const glyphPath = face->getOutlineForGlyph (character_); + + deleteAndZero (bitmap1); + deleteAndZero (bitmap2); + + const float fontHScale = fontHeight * fontHorizontalScale; + + if (glyphPath != 0 && ! glyphPath->isEmpty()) + { + bitmap1 = createAlphaMapFromPath (*glyphPath, xOrigin, yOrigin, fontHScale, fontHeight, 0.0f); + + if (fontHScale < 24.0f) + bitmap2 = createAlphaMapFromPath (*glyphPath, xOrigin, yOrigin, fontHScale, fontHeight, 0.5f); + } + else + { + xOrigin = yOrigin = 0; + } + } +}; + +static const int defaultNumGlyphsToCache = 120; +class GlyphCache; +static GlyphCache* cacheInstance = 0; + +class GlyphCache : private DeletedAtShutdown +{ +public: + + static GlyphCache* getInstance() throw() + { + if (cacheInstance == 0) + cacheInstance = new GlyphCache(); + + return cacheInstance; + } + + const FontGlyphAlphaMap& getGlyphFor (Typeface* const typeface, + const float fontHeight, + const float fontHorizontalScale, + const juce_wchar character) throw() + { + ++accessCounter; + + int oldestCounter = INT_MAX; + int oldestIndex = 0; + + for (int i = numGlyphs; --i >= 0;) + { + FontGlyphAlphaMap& g = glyphs[i]; + + if (g.character == character + && g.height == fontHeight + && g.typeface->hashCode() == typeface->hashCode() + && g.horizontalScale == fontHorizontalScale) + { + g.lastAccessCount = accessCounter; + ++hits; + return g; + } + + if (oldestCounter > g.lastAccessCount) + { + oldestCounter = g.lastAccessCount; + oldestIndex = i; + } + } + + ++misses; + + if (hits + misses > (numGlyphs << 4)) + { + if (misses * 2 > hits) + setCacheSize (numGlyphs + 32); + + hits = 0; + misses = 0; + oldestIndex = 0; + } + + FontGlyphAlphaMap& oldest = glyphs [oldestIndex]; + oldest.lastAccessCount = accessCounter; + + oldest.generate (typeface, + character, + fontHeight, + fontHorizontalScale); + + return oldest; + } + + void setCacheSize (const int num) throw() + { + if (numGlyphs != num) + { + numGlyphs = num; + + if (glyphs != 0) + delete[] glyphs; + + glyphs = new FontGlyphAlphaMap [numGlyphs]; + + hits = 0; + misses = 0; + } + } + + juce_UseDebuggingNewOperator + +private: + FontGlyphAlphaMap* glyphs; + int numGlyphs, accessCounter; + int hits, misses; + + GlyphCache() throw() + : glyphs (0), + numGlyphs (0), + accessCounter (0) + { + setCacheSize (defaultNumGlyphsToCache); + } + + ~GlyphCache() throw() + { + delete[] glyphs; + + jassert (cacheInstance == this); + cacheInstance = 0; + } + + GlyphCache (const GlyphCache&); + const GlyphCache& operator= (const GlyphCache&); +}; + +PositionedGlyph::PositionedGlyph() throw() +{ +} + +void PositionedGlyph::draw (const Graphics& g) const throw() +{ + if (! glyphInfo->isWhitespace()) + { + if (fontHeight < 100.0f && fontHeight > 0.1f && ! g.isVectorDevice()) + { + const FontGlyphAlphaMap& alphaMap + = GlyphCache::getInstance()->getGlyphFor (glyphInfo->getTypeface(), + fontHeight, + fontHorizontalScale, + getCharacter()); + + alphaMap.draw (g, x, y); + } + else + { + // that's a bit of a dodgy size, isn't it?? + jassert (fontHeight > 0.0f && fontHeight < 4000.0f); + + draw (g, AffineTransform::identity); + } + } +} + +void PositionedGlyph::draw (const Graphics& g, + const AffineTransform& transform) const throw() +{ + if (! glyphInfo->isWhitespace()) + { + g.fillPath (glyphInfo->getPath(), + AffineTransform::scale (fontHeight * fontHorizontalScale, fontHeight) + .translated (x, y) + .followedBy (transform)); + } +} + +void PositionedGlyph::createPath (Path& path) const throw() +{ + if (! glyphInfo->isWhitespace()) + { + path.addPath (glyphInfo->getPath(), + AffineTransform::scale (fontHeight * fontHorizontalScale, fontHeight) + .translated (x, y)); + } +} + +bool PositionedGlyph::hitTest (float px, float py) const throw() +{ + if (px >= getLeft() && px < getRight() + && py >= getTop() && py < getBottom() + && fontHeight > 0.0f + && ! glyphInfo->isWhitespace()) + { + AffineTransform::translation (-x, -y) + .scaled (1.0f / (fontHeight * fontHorizontalScale), 1.0f / fontHeight) + .transformPoint (px, py); + + return glyphInfo->getPath().contains (px, py); + } + + return false; +} + +void PositionedGlyph::moveBy (const float deltaX, + const float deltaY) throw() +{ + x += deltaX; + y += deltaY; +} + +GlyphArrangement::GlyphArrangement() throw() + : numGlyphs (0), + numAllocated (0), + glyphs (0) +{ +} + +GlyphArrangement::GlyphArrangement (const GlyphArrangement& other) throw() + : numGlyphs (0), + numAllocated (0), + glyphs (0) +{ + addGlyphArrangement (other); +} + +const GlyphArrangement& GlyphArrangement::operator= (const GlyphArrangement& other) throw() +{ + if (this != &other) + { + clear(); + addGlyphArrangement (other); + } + + return *this; +} + +GlyphArrangement::~GlyphArrangement() throw() +{ + clear(); + juce_free (glyphs); +} + +void GlyphArrangement::ensureNumGlyphsAllocated (const int minGlyphs) throw() +{ + if (numAllocated <= minGlyphs) + { + numAllocated = minGlyphs + 2; + + if (glyphs == 0) + glyphs = (PositionedGlyph*) juce_malloc (numAllocated * sizeof (PositionedGlyph)); + else + glyphs = (PositionedGlyph*) juce_realloc (glyphs, numAllocated * sizeof (PositionedGlyph)); + } +} + +void GlyphArrangement::incGlyphRefCount (const int i) const throw() +{ + jassert (((unsigned int) i) < (unsigned int) numGlyphs); + + if (glyphs[i].glyphInfo != 0 && glyphs[i].glyphInfo->getTypeface() != 0) + glyphs[i].glyphInfo->getTypeface()->incReferenceCount(); +} + +void GlyphArrangement::decGlyphRefCount (const int i) const throw() +{ + if (glyphs[i].glyphInfo != 0 && glyphs[i].glyphInfo->getTypeface() != 0) + glyphs[i].glyphInfo->getTypeface()->decReferenceCount(); +} + +void GlyphArrangement::clear() throw() +{ + for (int i = numGlyphs; --i >= 0;) + decGlyphRefCount (i); + + numGlyphs = 0; +} + +PositionedGlyph& GlyphArrangement::getGlyph (const int index) const throw() +{ + jassert (((unsigned int) index) < (unsigned int) numGlyphs); + + return glyphs [index]; +} + +void GlyphArrangement::addGlyphArrangement (const GlyphArrangement& other) throw() +{ + ensureNumGlyphsAllocated (numGlyphs + other.numGlyphs); + + memcpy (glyphs + numGlyphs, other.glyphs, + other.numGlyphs * sizeof (PositionedGlyph)); + + for (int i = other.numGlyphs; --i >= 0;) + incGlyphRefCount (numGlyphs++); +} + +void GlyphArrangement::removeLast() throw() +{ + if (numGlyphs > 0) + decGlyphRefCount (--numGlyphs); +} + +void GlyphArrangement::removeRangeOfGlyphs (int startIndex, const int num) throw() +{ + jassert (startIndex >= 0); + + if (startIndex < 0) + startIndex = 0; + + if (num < 0 || startIndex + num >= numGlyphs) + { + while (numGlyphs > startIndex) + removeLast(); + } + else if (num > 0) + { + int i; + for (i = startIndex; i < startIndex + num; ++i) + decGlyphRefCount (i); + + for (i = numGlyphs - (startIndex + num); --i >= 0;) + { + glyphs [startIndex] = glyphs [startIndex + num]; + ++startIndex; + } + + numGlyphs -= num; + } +} + +void GlyphArrangement::addLineOfText (const Font& font, + const String& text, + const float xOffset, + const float yOffset) throw() +{ + addCurtailedLineOfText (font, text, + xOffset, yOffset, + 1.0e10f, false); +} + +void GlyphArrangement::addCurtailedLineOfText (const Font& font, + const String& text, + float xOffset, + const float yOffset, + const float maxWidthPixels, + const bool useEllipsis) throw() +{ + const int textLen = text.length(); + + if (textLen > 0) + { + ensureNumGlyphsAllocated (numGlyphs + textLen + 3); // extra chars for ellipsis + + Typeface* const typeface = font.getTypeface(); + const float fontHeight = font.getHeight(); + const float ascent = font.getAscent(); + const float fontHorizontalScale = font.getHorizontalScale(); + const float heightTimesScale = fontHorizontalScale * fontHeight; + const float kerningFactor = font.getExtraKerningFactor(); + const float startX = xOffset; + + const juce_wchar* const unicodeText = (const juce_wchar*) text; + + for (int i = 0; i < textLen; ++i) + { + const TypefaceGlyphInfo* const glyph = typeface->getGlyph (unicodeText[i]); + + if (glyph != 0) + { + jassert (numAllocated > numGlyphs); + + ensureNumGlyphsAllocated (numGlyphs); + PositionedGlyph& pg = glyphs [numGlyphs]; + pg.glyphInfo = glyph; + pg.x = xOffset; + pg.y = yOffset; + pg.w = heightTimesScale * glyph->getHorizontalSpacing (0); + pg.fontHeight = fontHeight; + pg.fontAscent = ascent; + pg.fontHorizontalScale = fontHorizontalScale; + pg.isUnderlined = font.isUnderlined(); + + xOffset += heightTimesScale * (kerningFactor + glyph->getHorizontalSpacing (unicodeText [i + 1])); + + if (xOffset - startX > maxWidthPixels + 1.0f) + { + // curtail the string if it's too wide.. + + if (useEllipsis && textLen > 3 && numGlyphs >= 3) + appendEllipsis (font, startX + maxWidthPixels); + + break; + } + else + { + if (glyph->getTypeface() != 0) + glyph->getTypeface()->incReferenceCount(); + + ++numGlyphs; + } + } + } + } +} + +void GlyphArrangement::appendEllipsis (const Font& font, const float maxXPixels) throw() +{ + const TypefaceGlyphInfo* const dotGlyph = font.getTypeface()->getGlyph (T('.')); + + if (dotGlyph != 0) + { + if (numGlyphs > 0) + { + PositionedGlyph& glyph = glyphs [numGlyphs - 1]; + const float fontHeight = glyph.fontHeight; + const float fontHorizontalScale = glyph.fontHorizontalScale; + const float fontAscent = glyph.fontAscent; + + const float dx = fontHeight * fontHorizontalScale + * (font.getExtraKerningFactor() + dotGlyph->getHorizontalSpacing (T('.'))); + + float xOffset = 0.0f, yOffset = 0.0f; + + for (int dotPos = 3; --dotPos >= 0 && numGlyphs > 0;) + { + removeLast(); + + jassert (numAllocated > numGlyphs); + PositionedGlyph& pg = glyphs [numGlyphs]; + xOffset = pg.x; + yOffset = pg.y; + + if (numGlyphs == 0 || xOffset + dx * 3 <= maxXPixels) + break; + } + + for (int i = 3; --i >= 0;) + { + jassert (numAllocated > numGlyphs); + + ensureNumGlyphsAllocated (numGlyphs); + PositionedGlyph& pg = glyphs [numGlyphs]; + pg.glyphInfo = dotGlyph; + pg.x = xOffset; + pg.y = yOffset; + pg.w = dx; + pg.fontHeight = fontHeight; + pg.fontAscent = fontAscent; + pg.fontHorizontalScale = fontHorizontalScale; + pg.isUnderlined = font.isUnderlined(); + + xOffset += dx; + + if (dotGlyph->getTypeface() != 0) + dotGlyph->getTypeface()->incReferenceCount(); + + ++numGlyphs; + } + } + } +} + +void GlyphArrangement::addJustifiedText (const Font& font, + const String& text, + float x, float y, + const float maxLineWidth, + const Justification& horizontalLayout) throw() +{ + int lineStartIndex = numGlyphs; + addLineOfText (font, text, x, y); + + const float originalY = y; + + while (lineStartIndex < numGlyphs) + { + int i = lineStartIndex; + + if (glyphs[i].getCharacter() != T('\n') && glyphs[i].getCharacter() != T('\r')) + ++i; + + const float lineMaxX = glyphs [lineStartIndex].getLeft() + maxLineWidth; + int lastWordBreakIndex = -1; + + while (i < numGlyphs) + { + PositionedGlyph& pg = glyphs[i]; + const juce_wchar c = pg.getCharacter(); + + if (c == T('\r') || c == T('\n')) + { + ++i; + + if (c == T('\r') && i < numGlyphs && glyphs [i].getCharacter() == T('\n')) + ++i; + + break; + } + else if (pg.isWhitespace()) + { + lastWordBreakIndex = i + 1; + } + else if (SHOULD_WRAP (pg.getRight(), lineMaxX)) + { + if (lastWordBreakIndex >= 0) + i = lastWordBreakIndex; + + break; + } + + ++i; + } + + const float currentLineStartX = glyphs [lineStartIndex].getLeft(); + float currentLineEndX = currentLineStartX; + + for (int j = i; --j >= lineStartIndex;) + { + if (! glyphs[j].isWhitespace()) + { + currentLineEndX = glyphs[j].getRight(); + break; + } + } + + float deltaX = 0.0f; + + if (horizontalLayout.testFlags (Justification::horizontallyJustified)) + spreadOutLine (lineStartIndex, i - lineStartIndex, maxLineWidth); + else if (horizontalLayout.testFlags (Justification::horizontallyCentred)) + deltaX = (maxLineWidth - (currentLineEndX - currentLineStartX)) * 0.5f; + else if (horizontalLayout.testFlags (Justification::right)) + deltaX = maxLineWidth - (currentLineEndX - currentLineStartX); + + moveRangeOfGlyphs (lineStartIndex, i - lineStartIndex, + x + deltaX - currentLineStartX, y - originalY); + + lineStartIndex = i; + + y += font.getHeight(); + } +} + +void GlyphArrangement::addFittedText (const Font& f, + const String& text, + float x, float y, + float width, float height, + const Justification& layout, + int maximumLines, + const float minimumHorizontalScale) throw() +{ + // doesn't make much sense if this is outside a sensible range of 0.5 to 1.0 + jassert (minimumHorizontalScale > 0 && minimumHorizontalScale <= 1.0f); + + if (text.containsAnyOf (T("\r\n"))) + { + GlyphArrangement ga; + ga.addJustifiedText (f, text, x, y, width, layout); + + float l, t, r, b; + ga.getBoundingBox (0, -1, l, t, r, b, false); + + float dy = y - t; + + if (layout.testFlags (Justification::verticallyCentred)) + dy += (height - (b - t)) * 0.5f; + else if (layout.testFlags (Justification::bottom)) + dy += height - (b - t); + + ga.moveRangeOfGlyphs (0, -1, 0.0f, dy); + + addGlyphArrangement (ga); + + return; + } + + int startIndex = numGlyphs; + addLineOfText (f, text.trim(), x, y); + + if (numGlyphs > startIndex) + { + float lineWidth = glyphs[numGlyphs - 1].getRight() - glyphs[startIndex].getLeft(); + + if (lineWidth <= 0) + return; + + if (lineWidth * minimumHorizontalScale < width) + { + if (lineWidth > width) + { + stretchRangeOfGlyphs (startIndex, numGlyphs - startIndex, + width / lineWidth); + + } + + justifyGlyphs (startIndex, numGlyphs - startIndex, + x, y, width, height, layout); + } + else if (maximumLines <= 1) + { + const float ratio = jmax (minimumHorizontalScale, width / lineWidth); + + stretchRangeOfGlyphs (startIndex, numGlyphs - startIndex, ratio); + + while (numGlyphs > 0 && glyphs [numGlyphs - 1].x + glyphs [numGlyphs - 1].w >= x + width) + removeLast(); + + appendEllipsis (f, x + width); + + justifyGlyphs (startIndex, numGlyphs - startIndex, + x, y, width, height, layout); + } + else + { + Font font (f); + + String txt (text.trim()); + const int length = txt.length(); + int numLines = 1; + const int originalStartIndex = startIndex; + + if (length <= 12 && ! txt.containsAnyOf (T(" -\t\r\n"))) + maximumLines = 1; + + maximumLines = jmin (maximumLines, length); + + while (numLines < maximumLines) + { + ++numLines; + + const float newFontHeight = height / (float)numLines; + + if (newFontHeight < 8.0f) + break; + + if (newFontHeight < font.getHeight()) + { + font.setHeight (newFontHeight); + + while (numGlyphs > startIndex) + removeLast(); + + addLineOfText (font, txt, x, y); + + lineWidth = glyphs[numGlyphs - 1].getRight() - glyphs[startIndex].getLeft(); + } + + if (numLines > lineWidth / width) + break; + } + + if (numLines < 1) + numLines = 1; + + float lineY = y; + float widthPerLine = lineWidth / numLines; + int lastLineStartIndex = 0; + + for (int line = 0; line < numLines; ++line) + { + int i = startIndex; + lastLineStartIndex = i; + float lineStartX = glyphs[startIndex].getLeft(); + + while (i < numGlyphs) + { + lineWidth = (glyphs[i].getRight() - lineStartX); + + if (lineWidth > widthPerLine) + { + // got to a point where the line's too long, so skip forward to find a + // good place to break it.. + const int searchStartIndex = i; + + while (i < numGlyphs) + { + if ((glyphs[i].getRight() - lineStartX) * minimumHorizontalScale < width) + { + if (glyphs[i].isWhitespace() + || glyphs[i].getCharacter() == T('-')) + { + ++i; + break; + } + } + else + { + // can't find a suitable break, so try looking backwards.. + i = searchStartIndex; + + for (int back = 1; back < jmin (5, i - startIndex - 1); ++back) + { + if (glyphs[i - back].isWhitespace() + || glyphs[i - back].getCharacter() == T('-')) + { + i -= back - 1; + break; + } + } + + break; + } + + ++i; + } + + break; + } + + ++i; + } + + int wsStart = i; + while (wsStart > 0 && glyphs[wsStart - 1].isWhitespace()) + --wsStart; + + int wsEnd = i; + + while (wsEnd < numGlyphs && glyphs[wsEnd].isWhitespace()) + ++wsEnd; + + removeRangeOfGlyphs (wsStart, wsEnd - wsStart); + i = jmax (wsStart, startIndex + 1); + + lineWidth = glyphs[i - 1].getRight() - lineStartX; + + if (lineWidth > width) + { + stretchRangeOfGlyphs (startIndex, i - startIndex, + width / lineWidth); + } + + justifyGlyphs (startIndex, i - startIndex, + x, lineY, width, font.getHeight(), + layout.getOnlyHorizontalFlags() | Justification::verticallyCentred); + + startIndex = i; + lineY += font.getHeight(); + + if (startIndex >= numGlyphs) + break; + } + + if (startIndex < numGlyphs) + { + while (numGlyphs > startIndex) + removeLast(); + + if (startIndex - originalStartIndex > 4) + { + const float lineStartX = glyphs[lastLineStartIndex].getLeft(); + appendEllipsis (font, lineStartX + width); + + lineWidth = glyphs[startIndex - 1].getRight() - lineStartX; + + if (lineWidth > width) + { + stretchRangeOfGlyphs (lastLineStartIndex, startIndex - lastLineStartIndex, + width / lineWidth); + } + + justifyGlyphs (lastLineStartIndex, startIndex - lastLineStartIndex, + x, lineY - font.getHeight(), width, font.getHeight(), + layout.getOnlyHorizontalFlags() | Justification::verticallyCentred); + } + + startIndex = numGlyphs; + } + + justifyGlyphs (originalStartIndex, startIndex - originalStartIndex, + x, y, width, height, layout.getFlags() & ~Justification::horizontallyJustified); + } + } +} + +void GlyphArrangement::moveRangeOfGlyphs (int startIndex, int num, + const float dx, const float dy) throw() +{ + jassert (startIndex >= 0); + + if (dx != 0.0f || dy != 0.0f) + { + if (num < 0 || startIndex + num > numGlyphs) + num = numGlyphs - startIndex; + + while (--num >= 0) + { + jassert (((unsigned int) startIndex) <= (unsigned int) numGlyphs); + glyphs [startIndex++].moveBy (dx, dy); + } + } +} + +void GlyphArrangement::stretchRangeOfGlyphs (int startIndex, int num, + const float horizontalScaleFactor) throw() +{ + jassert (startIndex >= 0); + + if (num < 0 || startIndex + num > numGlyphs) + num = numGlyphs - startIndex; + + if (num > 0) + { + const float xAnchor = glyphs[startIndex].getLeft(); + + while (--num >= 0) + { + jassert (((unsigned int) startIndex) <= (unsigned int) numGlyphs); + PositionedGlyph& pg = glyphs[startIndex++]; + + pg.x = xAnchor + (pg.x - xAnchor) * horizontalScaleFactor; + pg.fontHorizontalScale *= horizontalScaleFactor; + pg.w *= horizontalScaleFactor; + } + } +} + +void GlyphArrangement::getBoundingBox (int startIndex, int num, + float& left, + float& top, + float& right, + float& bottom, + const bool includeWhitespace) const throw() +{ + jassert (startIndex >= 0); + + if (num < 0 || startIndex + num > numGlyphs) + num = numGlyphs - startIndex; + + left = 0.0f; + top = 0.0f; + right = 0.0f; + bottom = 0.0f; + bool isFirst = true; + + while (--num >= 0) + { + const PositionedGlyph& pg = glyphs [startIndex++]; + + if (includeWhitespace || ! pg.isWhitespace()) + { + if (isFirst) + { + isFirst = false; + left = pg.getLeft(); + top = pg.getTop(); + right = pg.getRight(); + bottom = pg.getBottom(); + } + else + { + left = jmin (left, pg.getLeft()); + top = jmin (top, pg.getTop()); + right = jmax (right, pg.getRight()); + bottom = jmax (bottom, pg.getBottom()); + } + } + } +} + +void GlyphArrangement::justifyGlyphs (const int startIndex, + const int num, + const float x, const float y, + const float width, const float height, + const Justification& justification) throw() +{ + jassert (num >= 0 && startIndex >= 0); + + if (numGlyphs > 0 && num > 0) + { + float left, top, right, bottom; + getBoundingBox (startIndex, num, left, top, right, bottom, + ! justification.testFlags (Justification::horizontallyJustified + | Justification::horizontallyCentred)); + + float deltaX = 0.0f; + + if (justification.testFlags (Justification::horizontallyJustified)) + deltaX = x - left; + else if (justification.testFlags (Justification::horizontallyCentred)) + deltaX = x + (width - (right - left)) * 0.5f - left; + else if (justification.testFlags (Justification::right)) + deltaX = (x + width) - right; + else + deltaX = x - left; + + float deltaY = 0.0f; + + if (justification.testFlags (Justification::top)) + deltaY = y - top; + else if (justification.testFlags (Justification::bottom)) + deltaY = (y + height) - bottom; + else + deltaY = y + (height - (bottom - top)) * 0.5f - top; + + moveRangeOfGlyphs (startIndex, num, deltaX, deltaY); + + if (justification.testFlags (Justification::horizontallyJustified)) + { + int lineStart = 0; + float baseY = glyphs [startIndex].getBaselineY(); + + int i; + for (i = 0; i < num; ++i) + { + const float glyphY = glyphs [startIndex + i].getBaselineY(); + + if (glyphY != baseY) + { + spreadOutLine (startIndex + lineStart, i - lineStart, width); + + lineStart = i; + baseY = glyphY; + } + } + + if (i > lineStart) + spreadOutLine (startIndex + lineStart, i - lineStart, width); + } + } +} + +void GlyphArrangement::spreadOutLine (const int start, const int num, const float targetWidth) throw() +{ + if (start + num < numGlyphs + && glyphs [start + num - 1].getCharacter() != T('\r') + && glyphs [start + num - 1].getCharacter() != T('\n')) + { + int numSpaces = 0; + int spacesAtEnd = 0; + + for (int i = 0; i < num; ++i) + { + if (glyphs [start + i].isWhitespace()) + { + ++spacesAtEnd; + ++numSpaces; + } + else + { + spacesAtEnd = 0; + } + } + + numSpaces -= spacesAtEnd; + + if (numSpaces > 0) + { + const float startX = glyphs [start].getLeft(); + const float endX = glyphs [start + num - 1 - spacesAtEnd].getRight(); + + const float extraPaddingBetweenWords + = (targetWidth - (endX - startX)) / (float) numSpaces; + + float deltaX = 0.0f; + + for (int i = 0; i < num; ++i) + { + glyphs [start + i].moveBy (deltaX, 0.0); + + if (glyphs [start + i].isWhitespace()) + deltaX += extraPaddingBetweenWords; + } + } + } +} + +void GlyphArrangement::draw (const Graphics& g) const throw() +{ + for (int i = 0; i < numGlyphs; ++i) + { + glyphs[i].draw (g); + + if (glyphs[i].isUnderlined) + { + const float lineThickness = (glyphs[i].fontHeight - glyphs[i].fontAscent) * 0.3f; + + juce_wchar nextChar = 0; + + if (i < numGlyphs - 1 + && glyphs[i + 1].y == glyphs[i].y) + { + nextChar = glyphs[i + 1].glyphInfo->getCharacter(); + } + + g.fillRect (glyphs[i].x, + glyphs[i].y + lineThickness * 2.0f, + glyphs[i].fontHeight + * glyphs[i].fontHorizontalScale + * glyphs[i].glyphInfo->getHorizontalSpacing (nextChar), + lineThickness); + } + } +} + +void GlyphArrangement::draw (const Graphics& g, const AffineTransform& transform) const throw() +{ + for (int i = 0; i < numGlyphs; ++i) + { + glyphs[i].draw (g, transform); + + if (glyphs[i].isUnderlined) + { + const float lineThickness = (glyphs[i].fontHeight - glyphs[i].fontAscent) * 0.3f; + + juce_wchar nextChar = 0; + + if (i < numGlyphs - 1 + && glyphs[i + 1].y == glyphs[i].y) + { + nextChar = glyphs[i + 1].glyphInfo->getCharacter(); + } + + Path p; + p.addLineSegment (glyphs[i].x, + glyphs[i].y + lineThickness * 2.5f, + glyphs[i].x + glyphs[i].fontHeight + * glyphs[i].fontHorizontalScale + * glyphs[i].glyphInfo->getHorizontalSpacing (nextChar), + glyphs[i].y + lineThickness * 2.5f, + lineThickness); + + g.fillPath (p, transform); + } + } +} + +void GlyphArrangement::createPath (Path& path) const throw() +{ + for (int i = 0; i < numGlyphs; ++i) + glyphs[i].createPath (path); +} + +int GlyphArrangement::findGlyphIndexAt (float x, float y) const throw() +{ + for (int i = 0; i < numGlyphs; ++i) + if (glyphs[i].hitTest (x, y)) + return i; + + return -1; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_GlyphArrangement.cpp *********/ + +/********* Start of inlined file: juce_TextLayout.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +class TextLayoutToken +{ +public: + String text; + Font font; + int x, y, w, h; + int line, lineHeight; + bool isWhitespace, isNewLine; + + TextLayoutToken (const String& t, + const Font& f, + const bool isWhitespace_) throw() + : text (t), + font (f), + x(0), + y(0), + isWhitespace (isWhitespace_) + { + w = font.getStringWidth (t); + h = roundFloatToInt (f.getHeight()); + isNewLine = t.containsAnyOf (T("\r\n")); + } + + TextLayoutToken (const TextLayoutToken& other) throw() + : text (other.text), + font (other.font), + x (other.x), + y (other.y), + w (other.w), + h (other.h), + line (other.line), + lineHeight (other.lineHeight), + isWhitespace (other.isWhitespace), + isNewLine (other.isNewLine) + { + } + + ~TextLayoutToken() throw() + { + } + + void draw (Graphics& g, + const int xOffset, + const int yOffset) throw() + { + if (! isWhitespace) + { + g.setFont (font); + g.drawSingleLineText (text.trimEnd(), + xOffset + x, + yOffset + y + (lineHeight - h) + + roundFloatToInt (font.getAscent())); + } + } + + juce_UseDebuggingNewOperator +}; + +TextLayout::TextLayout() throw() + : tokens (64), + totalLines (0) +{ +} + +TextLayout::TextLayout (const String& text, + const Font& font) throw() + : tokens (64), + totalLines (0) +{ + appendText (text, font); +} + +TextLayout::TextLayout (const TextLayout& other) throw() + : tokens (64), + totalLines (0) +{ + *this = other; +} + +const TextLayout& TextLayout::operator= (const TextLayout& other) throw() +{ + if (this != &other) + { + clear(); + + totalLines = other.totalLines; + + for (int i = 0; i < other.tokens.size(); ++i) + tokens.add (new TextLayoutToken (*(const TextLayoutToken*)(other.tokens.getUnchecked(i)))); + } + + return *this; +} + +TextLayout::~TextLayout() throw() +{ + clear(); +} + +void TextLayout::clear() throw() +{ + for (int i = tokens.size(); --i >= 0;) + { + TextLayoutToken* const t = (TextLayoutToken*)tokens.getUnchecked(i); + delete t; + } + + tokens.clear(); + totalLines = 0; +} + +void TextLayout::appendText (const String& text, + const Font& font) throw() +{ + const tchar* t = text; + String currentString; + int lastCharType = 0; + + for (;;) + { + const tchar c = *t++; + if (c == 0) + break; + + int charType; + if (c == T('\r') || c == T('\n')) + { + charType = 0; + } + else if (CharacterFunctions::isWhitespace (c)) + { + charType = 2; + } + else + { + charType = 1; + } + + if (charType == 0 || charType != lastCharType) + { + if (currentString.isNotEmpty()) + { + tokens.add (new TextLayoutToken (currentString, font, + lastCharType == 2 || lastCharType == 0)); + } + + currentString = String::charToString (c); + + if (c == T('\r') && *t == T('\n')) + currentString += *t++; + } + else + { + currentString += c; + } + + lastCharType = charType; + } + + if (currentString.isNotEmpty()) + tokens.add (new TextLayoutToken (currentString, + font, + lastCharType == 2)); +} + +void TextLayout::setText (const String& text, const Font& font) throw() +{ + clear(); + appendText (text, font); +} + +void TextLayout::layout (int maxWidth, + const Justification& justification, + const bool attemptToBalanceLineLengths) throw() +{ + if (attemptToBalanceLineLengths) + { + const int originalW = maxWidth; + int bestWidth = maxWidth; + float bestLineProportion = 0.0f; + + while (maxWidth > originalW / 2) + { + layout (maxWidth, justification, false); + + if (getNumLines() <= 1) + return; + + const int lastLineW = getLineWidth (getNumLines() - 1); + const int lastButOneLineW = getLineWidth (getNumLines() - 2); + + const float prop = lastLineW / (float) lastButOneLineW; + + if (prop > 0.9f) + return; + + if (prop > bestLineProportion) + { + bestLineProportion = prop; + bestWidth = maxWidth; + } + + maxWidth -= 10; + } + + layout (bestWidth, justification, false); + } + else + { + int x = 0; + int y = 0; + int h = 0; + totalLines = 0; + int i; + + for (i = 0; i < tokens.size(); ++i) + { + TextLayoutToken* const t = (TextLayoutToken*)tokens.getUnchecked(i); + t->x = x; + t->y = y; + t->line = totalLines; + x += t->w; + h = jmax (h, t->h); + + const TextLayoutToken* nextTok = (TextLayoutToken*) tokens [i + 1]; + + if (nextTok == 0) + break; + + if (t->isNewLine || ((! nextTok->isWhitespace) && x + nextTok->w > maxWidth)) + { + // finished a line, so go back and update the heights of the things on it + for (int j = i; j >= 0; --j) + { + TextLayoutToken* const tok = (TextLayoutToken*)tokens.getUnchecked(j); + + if (tok->line == totalLines) + tok->lineHeight = h; + else + break; + } + + x = 0; + y += h; + h = 0; + ++totalLines; + } + } + + // finished a line, so go back and update the heights of the things on it + for (int j = jmin (i, tokens.size() - 1); j >= 0; --j) + { + TextLayoutToken* const t = (TextLayoutToken*) tokens.getUnchecked(j); + + if (t->line == totalLines) + t->lineHeight = h; + else + break; + } + + ++totalLines; + + if (! justification.testFlags (Justification::left)) + { + int totalW = getWidth(); + + for (i = totalLines; --i >= 0;) + { + const int lineW = getLineWidth (i); + + int dx = 0; + if (justification.testFlags (Justification::horizontallyCentred)) + dx = (totalW - lineW) / 2; + else if (justification.testFlags (Justification::right)) + dx = totalW - lineW; + + for (int j = tokens.size(); --j >= 0;) + { + TextLayoutToken* const t = (TextLayoutToken*)tokens.getUnchecked(j); + + if (t->line == i) + t->x += dx; + } + } + } + } +} + +int TextLayout::getLineWidth (const int lineNumber) const throw() +{ + int maxW = 0; + + for (int i = tokens.size(); --i >= 0;) + { + const TextLayoutToken* const t = (TextLayoutToken*) tokens.getUnchecked(i); + + if (t->line == lineNumber && ! t->isWhitespace) + maxW = jmax (maxW, t->x + t->w); + } + + return maxW; +} + +int TextLayout::getWidth() const throw() +{ + int maxW = 0; + + for (int i = tokens.size(); --i >= 0;) + { + const TextLayoutToken* const t = (TextLayoutToken*) tokens.getUnchecked(i); + if (! t->isWhitespace) + maxW = jmax (maxW, t->x + t->w); + } + + return maxW; +} + +int TextLayout::getHeight() const throw() +{ + int maxH = 0; + + for (int i = tokens.size(); --i >= 0;) + { + const TextLayoutToken* const t = (TextLayoutToken*) tokens.getUnchecked(i); + + if (! t->isWhitespace) + maxH = jmax (maxH, t->y + t->h); + } + + return maxH; +} + +void TextLayout::draw (Graphics& g, + const int xOffset, + const int yOffset) const throw() +{ + for (int i = tokens.size(); --i >= 0;) + ((TextLayoutToken*) tokens.getUnchecked(i))->draw (g, xOffset, yOffset); +} + +void TextLayout::drawWithin (Graphics& g, + int x, int y, int w, int h, + const Justification& justification) const throw() +{ + justification.applyToRectangle (x, y, getWidth(), getHeight(), + x, y, w, h); + + draw (g, x, y); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_TextLayout.cpp *********/ + +/********* Start of inlined file: juce_Typeface.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +TypefaceGlyphInfo::TypefaceGlyphInfo (const juce_wchar character_, + const Path& shape, + const float horizontalSeparation, + Typeface* const typeface_) throw() + : character (character_), + path (shape), + width (horizontalSeparation), + typeface (typeface_) +{ +} + +TypefaceGlyphInfo::~TypefaceGlyphInfo() throw() +{ +} + +float TypefaceGlyphInfo::getHorizontalSpacing (const juce_wchar subsequentCharacter) const throw() +{ + if (subsequentCharacter != 0) + { + const KerningPair* const pairs = (const KerningPair*) kerningPairs.getData(); + const int numPairs = getNumKerningPairs(); + + for (int i = 0; i < numPairs; ++i) + if (pairs [i].character2 == subsequentCharacter) + return width + pairs [i].kerningAmount; + } + + return width; +} + +void TypefaceGlyphInfo::addKerningPair (const juce_wchar subsequentCharacter, + const float extraKerningAmount) throw() +{ + const int numPairs = getNumKerningPairs(); + kerningPairs.setSize ((numPairs + 1) * sizeof (KerningPair)); + + KerningPair& p = getKerningPair (numPairs); + p.character2 = subsequentCharacter; + p.kerningAmount = extraKerningAmount; +} + +TypefaceGlyphInfo::KerningPair& TypefaceGlyphInfo::getKerningPair (const int index) const throw() +{ + return ((KerningPair*) kerningPairs.getData()) [index]; +} + +int TypefaceGlyphInfo::getNumKerningPairs() const throw() +{ + return kerningPairs.getSize() / sizeof (KerningPair); +} + +Typeface::Typeface() throw() + : hash (0), + isFullyPopulated (false) +{ + zeromem (lookupTable, sizeof (lookupTable)); +} + +Typeface::Typeface (const Typeface& other) + : typefaceName (other.typefaceName), + ascent (other.ascent), + bold (other.bold), + italic (other.italic), + isFullyPopulated (other.isFullyPopulated), + defaultCharacter (other.defaultCharacter) +{ + zeromem (lookupTable, sizeof (lookupTable)); + + for (int i = 0; i < other.glyphs.size(); ++i) + addGlyphCopy ((const TypefaceGlyphInfo*) other.glyphs.getUnchecked(i)); + + updateHashCode(); +} + +Typeface::Typeface (const String& faceName, + const bool bold, + const bool italic) + : isFullyPopulated (false) +{ + zeromem (lookupTable, sizeof (lookupTable)); + + initialiseTypefaceCharacteristics (faceName, bold, italic, false); + + updateHashCode(); +} + +Typeface::~Typeface() +{ + clear(); +} + +const Typeface& Typeface::operator= (const Typeface& other) throw() +{ + if (this != &other) + { + clear(); + + typefaceName = other.typefaceName; + ascent = other.ascent; + bold = other.bold; + italic = other.italic; + isFullyPopulated = other.isFullyPopulated; + defaultCharacter = other.defaultCharacter; + + for (int i = 0; i < other.glyphs.size(); ++i) + addGlyphCopy ((const TypefaceGlyphInfo*) other.glyphs.getUnchecked(i)); + + updateHashCode(); + } + + return *this; +} + +void Typeface::updateHashCode() throw() +{ + hash = typefaceName.hashCode(); + + if (bold) + hash ^= 0xffff; + + if (italic) + hash ^= 0xffff0000; +} + +void Typeface::clear() throw() +{ + zeromem (lookupTable, sizeof (lookupTable)); + typefaceName = String::empty; + bold = false; + italic = false; + + for (int i = glyphs.size(); --i >= 0;) + { + TypefaceGlyphInfo* const g = (TypefaceGlyphInfo*) (glyphs.getUnchecked(i)); + delete g; + } + + glyphs.clear(); + updateHashCode(); +} + +Typeface::Typeface (InputStream& serialisedTypefaceStream) +{ + zeromem (lookupTable, sizeof (lookupTable)); + isFullyPopulated = true; + + GZIPDecompressorInputStream gzin (&serialisedTypefaceStream, false); + BufferedInputStream in (&gzin, 32768, false); + + typefaceName = in.readString(); + bold = in.readBool(); + italic = in.readBool(); + ascent = in.readFloat(); + defaultCharacter = (juce_wchar) in.readShort(); + + int i, numChars = in.readInt(); + + for (i = 0; i < numChars; ++i) + { + const juce_wchar c = (juce_wchar) in.readShort(); + const float width = in.readFloat(); + + Path p; + p.loadPathFromStream (in); + addGlyph (c, p, width); + } + + const int numKerningPairs = in.readInt(); + + for (i = 0; i < numKerningPairs; ++i) + { + const juce_wchar char1 = (juce_wchar) in.readShort(); + const juce_wchar char2 = (juce_wchar) in.readShort(); + + addKerningPair (char1, char2, in.readFloat()); + } + + updateHashCode(); +} + +void Typeface::serialise (OutputStream& outputStream) +{ + GZIPCompressorOutputStream out (&outputStream); + + out.writeString (typefaceName); + out.writeBool (bold); + out.writeBool (italic); + out.writeFloat (ascent); + out.writeShort ((short) (unsigned short) defaultCharacter); + out.writeInt (glyphs.size()); + + int i, numKerningPairs = 0; + + for (i = 0; i < glyphs.size(); ++i) + { + const TypefaceGlyphInfo& g = *(const TypefaceGlyphInfo*)(glyphs.getUnchecked (i)); + out.writeShort ((short) (unsigned short) g.character); + out.writeFloat (g.width); + g.path.writePathToStream (out); + + numKerningPairs += g.getNumKerningPairs(); + } + + out.writeInt (numKerningPairs); + + for (i = 0; i < glyphs.size(); ++i) + { + const TypefaceGlyphInfo& g = *(const TypefaceGlyphInfo*)(glyphs.getUnchecked (i)); + + for (int j = 0; j < g.getNumKerningPairs(); ++j) + { + const TypefaceGlyphInfo::KerningPair& p = g.getKerningPair (j); + out.writeShort ((short) (unsigned short) g.character); + out.writeShort ((short) (unsigned short) p.character2); + out.writeFloat (p.kerningAmount); + } + } +} + +const Path* Typeface::getOutlineForGlyph (const juce_wchar character) throw() +{ + const TypefaceGlyphInfo* const g = (const TypefaceGlyphInfo*) getGlyph (character); + + if (g != 0) + return &(g->path); + else + return 0; +} + +const TypefaceGlyphInfo* Typeface::getGlyph (const juce_wchar character) throw() +{ + if (((unsigned int) character) < 128 && lookupTable [character] > 0) + return (const TypefaceGlyphInfo*) glyphs [(int) lookupTable [character]]; + + for (int i = 0; i < glyphs.size(); ++i) + { + const TypefaceGlyphInfo* const g = (const TypefaceGlyphInfo*) glyphs.getUnchecked(i); + + if (g->character == character) + return g; + } + + if ((! isFullyPopulated) + && findAndAddSystemGlyph (character)) + { + for (int i = 0; i < glyphs.size(); ++i) + { + const TypefaceGlyphInfo* const g = (const TypefaceGlyphInfo*) glyphs.getUnchecked(i); + + if (g->character == character) + return g; + } + } + + if (CharacterFunctions::isWhitespace (character) && character != L' ') + { + const TypefaceGlyphInfo* spaceGlyph = getGlyph (L' '); + + if (spaceGlyph != 0) + { + // Add a copy of the empty glyph, mapped onto this character + addGlyph (character, spaceGlyph->getPath(), spaceGlyph->getHorizontalSpacing (0)); + spaceGlyph = (const TypefaceGlyphInfo*) glyphs [(int) lookupTable [character]]; + } + + return spaceGlyph; + } + else if (character != defaultCharacter) + { + const Font fallbackFont (Font::getFallbackFontName(), 10, 0); + Typeface* const fallbackTypeface = fallbackFont.getTypeface(); + + if (fallbackTypeface != 0 && fallbackTypeface != this) + return fallbackTypeface->getGlyph (character); + + return getGlyph (defaultCharacter); + } + + return 0; +} + +void Typeface::addGlyph (const juce_wchar character, + const Path& path, + const float horizontalSpacing) throw() +{ +#ifdef JUCE_DEBUG + for (int i = 0; i < glyphs.size(); ++i) + { + const TypefaceGlyphInfo* const g = (const TypefaceGlyphInfo*) glyphs.getUnchecked(i); + + if (g->character == character) + jassertfalse; + } +#endif + + if (((unsigned int) character) < 128) + lookupTable [character] = (short) glyphs.size(); + + glyphs.add (new TypefaceGlyphInfo (character, + path, + horizontalSpacing, + this)); +} + +void Typeface::addGlyphCopy (const TypefaceGlyphInfo* const glyphInfoToCopy) throw() +{ + if (glyphInfoToCopy != 0) + { + if (glyphInfoToCopy->character > 0 && glyphInfoToCopy->character < 128) + lookupTable [glyphInfoToCopy->character] = (short) glyphs.size(); + + TypefaceGlyphInfo* const newOne + = new TypefaceGlyphInfo (glyphInfoToCopy->character, + glyphInfoToCopy->path, + glyphInfoToCopy->width, + this); + + newOne->kerningPairs = glyphInfoToCopy->kerningPairs; + glyphs.add (newOne); + } +} + +void Typeface::addKerningPair (const juce_wchar char1, + const juce_wchar char2, + const float extraAmount) throw() +{ + TypefaceGlyphInfo* const g = (TypefaceGlyphInfo*) getGlyph (char1); + + if (g != 0) + g->addKerningPair (char2, extraAmount); +} + +void Typeface::setName (const String& name) throw() +{ + typefaceName = name; + updateHashCode(); +} + +void Typeface::setAscent (const float newAscent) throw() +{ + ascent = newAscent; +} + +void Typeface::setDefaultCharacter (const juce_wchar newDefaultCharacter) throw() +{ + defaultCharacter = newDefaultCharacter; +} + +void Typeface::setBold (const bool shouldBeBold) throw() +{ + bold = shouldBeBold; + updateHashCode(); +} + +void Typeface::setItalic (const bool shouldBeItalic) throw() +{ + italic = shouldBeItalic; + updateHashCode(); +} + +class TypefaceCache; +static TypefaceCache* typefaceCacheInstance = 0; + +void clearUpDefaultFontNames() throw(); // in juce_Font.cpp + +class TypefaceCache : private DeletedAtShutdown +{ +private: + + struct CachedFace + { + CachedFace() throw() + : lastUsageCount (0), + flags (0) + { + } + + String typefaceName; + int lastUsageCount; + int flags; + Typeface::Ptr typeFace; + }; + + int counter; + OwnedArray faces; + + TypefaceCache (const TypefaceCache&); + const TypefaceCache& operator= (const TypefaceCache&); + +public: + + TypefaceCache (int numToCache = 10) + : counter (1), + faces (2) + { + while (--numToCache >= 0) + { + CachedFace* const face = new CachedFace(); + face->typeFace = new Typeface(); + faces.add (face); + } + } + + ~TypefaceCache() + { + faces.clear(); + jassert (typefaceCacheInstance == this); + typefaceCacheInstance = 0; + + // just a courtesy call to get avoid leaking these strings at shutdown + clearUpDefaultFontNames(); + } + + static TypefaceCache* getInstance() throw() + { + if (typefaceCacheInstance == 0) + typefaceCacheInstance = new TypefaceCache(); + + return typefaceCacheInstance; + } + + const Typeface::Ptr findTypefaceFor (const Font& font) throw() + { + const int flags = font.getStyleFlags() & (Font::bold | Font::italic); + + int i; + for (i = faces.size(); --i >= 0;) + { + CachedFace* const face = faces.getUnchecked(i); + + if (face->flags == flags + && face->typefaceName == font.getTypefaceName()) + { + face->lastUsageCount = ++counter; + return face->typeFace; + } + } + + int replaceIndex = 0; + int bestLastUsageCount = INT_MAX; + + for (i = faces.size(); --i >= 0;) + { + const int lu = faces.getUnchecked(i)->lastUsageCount; + + if (bestLastUsageCount > lu) + { + bestLastUsageCount = lu; + replaceIndex = i; + } + } + + CachedFace* const face = faces.getUnchecked (replaceIndex); + + face->typefaceName = font.getTypefaceName(); + face->flags = flags; + face->lastUsageCount = ++counter; + face->typeFace = new Typeface (font.getTypefaceName(), + font.isBold(), + font.isItalic()); + + return face->typeFace; + } +}; + +const Typeface::Ptr Typeface::getTypefaceFor (const Font& font) throw() +{ + return TypefaceCache::getInstance()->findTypefaceFor (font); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_Typeface.cpp *********/ + +/********* Start of inlined file: juce_AffineTransform.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +AffineTransform::AffineTransform() throw() + : mat00 (1.0f), + mat01 (0), + mat02 (0), + mat10 (0), + mat11 (1.0f), + mat12 (0) +{ +} + +AffineTransform::AffineTransform (const AffineTransform& other) throw() + : mat00 (other.mat00), + mat01 (other.mat01), + mat02 (other.mat02), + mat10 (other.mat10), + mat11 (other.mat11), + mat12 (other.mat12) +{ +} + +AffineTransform::AffineTransform (const float mat00_, + const float mat01_, + const float mat02_, + const float mat10_, + const float mat11_, + const float mat12_) throw() + : mat00 (mat00_), + mat01 (mat01_), + mat02 (mat02_), + mat10 (mat10_), + mat11 (mat11_), + mat12 (mat12_) +{ +} + +const AffineTransform& AffineTransform::operator= (const AffineTransform& other) throw() +{ + mat00 = other.mat00; + mat01 = other.mat01; + mat02 = other.mat02; + mat10 = other.mat10; + mat11 = other.mat11; + mat12 = other.mat12; + + return *this; +} + +bool AffineTransform::operator== (const AffineTransform& other) const throw() +{ + return mat00 == other.mat00 + && mat01 == other.mat01 + && mat02 == other.mat02 + && mat10 == other.mat10 + && mat11 == other.mat11 + && mat12 == other.mat12; +} + +bool AffineTransform::operator!= (const AffineTransform& other) const throw() +{ + return ! operator== (other); +} + +bool AffineTransform::isIdentity() const throw() +{ + return (mat01 == 0) + && (mat02 == 0) + && (mat10 == 0) + && (mat12 == 0) + && (mat00 == 1.0f) + && (mat11 == 1.0f); +} + +const AffineTransform AffineTransform::identity; + +const AffineTransform AffineTransform::followedBy (const AffineTransform& other) const throw() +{ + return AffineTransform (other.mat00 * mat00 + other.mat01 * mat10, + other.mat00 * mat01 + other.mat01 * mat11, + other.mat00 * mat02 + other.mat01 * mat12 + other.mat02, + other.mat10 * mat00 + other.mat11 * mat10, + other.mat10 * mat01 + other.mat11 * mat11, + other.mat10 * mat02 + other.mat11 * mat12 + other.mat12); +} + +const AffineTransform AffineTransform::followedBy (const float omat00, + const float omat01, + const float omat02, + const float omat10, + const float omat11, + const float omat12) const throw() +{ + return AffineTransform (omat00 * mat00 + omat01 * mat10, + omat00 * mat01 + omat01 * mat11, + omat00 * mat02 + omat01 * mat12 + omat02, + omat10 * mat00 + omat11 * mat10, + omat10 * mat01 + omat11 * mat11, + omat10 * mat02 + omat11 * mat12 + omat12); +} + +const AffineTransform AffineTransform::translated (const float dx, + const float dy) const throw() +{ + return followedBy (1.0f, 0, dx, + 0, 1.0f, dy); +} + +const AffineTransform AffineTransform::translation (const float dx, + const float dy) throw() +{ + return AffineTransform (1.0f, 0, dx, + 0, 1.0f, dy); +} + +const AffineTransform AffineTransform::rotated (const float rad) const throw() +{ + const float cosRad = cosf (rad); + const float sinRad = sinf (rad); + + return followedBy (cosRad, -sinRad, 0, + sinRad, cosRad, 0); +} + +const AffineTransform AffineTransform::rotation (const float rad) throw() +{ + const float cosRad = cosf (rad); + const float sinRad = sinf (rad); + + return AffineTransform (cosRad, -sinRad, 0, + sinRad, cosRad, 0); +} + +const AffineTransform AffineTransform::rotated (const float angle, + const float pivotX, + const float pivotY) const throw() +{ + return translated (-pivotX, -pivotY) + .rotated (angle) + .translated (pivotX, pivotY); +} + +const AffineTransform AffineTransform::rotation (const float angle, + const float pivotX, + const float pivotY) throw() +{ + return translation (-pivotX, -pivotY) + .rotated (angle) + .translated (pivotX, pivotY); +} + +const AffineTransform AffineTransform::scaled (const float factorX, + const float factorY) const throw() +{ + return followedBy (factorX, 0, 0, + 0, factorY, 0); +} + +const AffineTransform AffineTransform::scale (const float factorX, + const float factorY) throw() +{ + return AffineTransform (factorX, 0, 0, + 0, factorY, 0); +} + +const AffineTransform AffineTransform::sheared (const float shearX, + const float shearY) const throw() +{ + return followedBy (1.0f, shearX, 0, + shearY, 1.0f, 0); +} + +const AffineTransform AffineTransform::inverted() const throw() +{ + double determinant = (mat00 * mat11 - mat10 * mat01); + + if (determinant != 0.0) + { + determinant = 1.0 / determinant; + + const float dst00 = (float) (mat11 * determinant); + const float dst10 = (float) (-mat10 * determinant); + const float dst01 = (float) (-mat01 * determinant); + const float dst11 = (float) (mat00 * determinant); + + return AffineTransform (dst00, dst01, -mat02 * dst00 - mat12 * dst01, + dst10, dst11, -mat02 * dst10 - mat12 * dst11); + } + else + { + // singularity.. + return *this; + } +} + +bool AffineTransform::isSingularity() const throw() +{ + return (mat00 * mat11 - mat10 * mat01) == 0.0; +} + +void AffineTransform::transformPoint (float& x, + float& y) const throw() +{ + const float oldX = x; + x = mat00 * oldX + mat01 * y + mat02; + y = mat10 * oldX + mat11 * y + mat12; +} + +void AffineTransform::transformPoint (double& x, + double& y) const throw() +{ + const double oldX = x; + x = mat00 * oldX + mat01 * y + mat02; + y = mat10 * oldX + mat11 * y + mat12; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_AffineTransform.cpp *********/ + +/********* Start of inlined file: juce_BorderSize.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +BorderSize::BorderSize() throw() + : top (0), + left (0), + bottom (0), + right (0) +{ +} + +BorderSize::BorderSize (const BorderSize& other) throw() + : top (other.top), + left (other.left), + bottom (other.bottom), + right (other.right) +{ +} + +BorderSize::BorderSize (const int topGap, + const int leftGap, + const int bottomGap, + const int rightGap) throw() + : top (topGap), + left (leftGap), + bottom (bottomGap), + right (rightGap) +{ +} + +BorderSize::BorderSize (const int allGaps) throw() + : top (allGaps), + left (allGaps), + bottom (allGaps), + right (allGaps) +{ +} + +BorderSize::~BorderSize() throw() +{ +} + +void BorderSize::setTop (const int newTopGap) throw() +{ + top = newTopGap; +} + +void BorderSize::setLeft (const int newLeftGap) throw() +{ + left = newLeftGap; +} + +void BorderSize::setBottom (const int newBottomGap) throw() +{ + bottom = newBottomGap; +} + +void BorderSize::setRight (const int newRightGap) throw() +{ + right = newRightGap; +} + +const Rectangle BorderSize::subtractedFrom (const Rectangle& r) const throw() +{ + return Rectangle (r.getX() + left, + r.getY() + top, + r.getWidth() - (left + right), + r.getHeight() - (top + bottom)); +} + +void BorderSize::subtractFrom (Rectangle& r) const throw() +{ + r.setBounds (r.getX() + left, + r.getY() + top, + r.getWidth() - (left + right), + r.getHeight() - (top + bottom)); +} + +const Rectangle BorderSize::addedTo (const Rectangle& r) const throw() +{ + return Rectangle (r.getX() - left, + r.getY() - top, + r.getWidth() + (left + right), + r.getHeight() + (top + bottom)); +} + +void BorderSize::addTo (Rectangle& r) const throw() +{ + r.setBounds (r.getX() - left, + r.getY() - top, + r.getWidth() + (left + right), + r.getHeight() + (top + bottom)); +} + +bool BorderSize::operator== (const BorderSize& other) const throw() +{ + return top == other.top + && left == other.left + && bottom == other.bottom + && right == other.right; +} + +bool BorderSize::operator!= (const BorderSize& other) const throw() +{ + return ! operator== (other); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_BorderSize.cpp *********/ + +/********* Start of inlined file: juce_Line.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +static bool juce_lineIntersection (const float x1, const float y1, + const float x2, const float y2, + const float x3, const float y3, + const float x4, const float y4, + float& intersectionX, + float& intersectionY) throw() +{ + if (x2 != x3 || y2 != y3) + { + const float dx1 = x2 - x1; + const float dy1 = y2 - y1; + const float dx2 = x4 - x3; + const float dy2 = y4 - y3; + const float divisor = dx1 * dy2 - dx2 * dy1; + + if (divisor == 0) + { + if (! ((dx1 == 0 && dy1 == 0) || (dx2 == 0 && dy2 == 0))) + { + if (dy1 == 0 && dy2 != 0) + { + const float along = (y1 - y3) / dy2; + intersectionX = x3 + along * dx2; + intersectionY = y1; + + return along >= 0 && along <= 1.0f; + } + else if (dy2 == 0 && dy1 != 0) + { + const float along = (y3 - y1) / dy1; + intersectionX = x1 + along * dx1; + intersectionY = y3; + + return along >= 0 && along <= 1.0f; + } + else if (dx1 == 0 && dx2 != 0) + { + const float along = (x1 - x3) / dx2; + intersectionX = x1; + intersectionY = y3 + along * dy2; + + return along >= 0 && along <= 1.0f; + } + else if (dx2 == 0 && dx1 != 0) + { + const float along = (x3 - x1) / dx1; + intersectionX = x3; + intersectionY = y1 + along * dy1; + + return along >= 0 && along <= 1.0f; + } + } + + intersectionX = 0.5f * (x2 + x3); + intersectionY = 0.5f * (y2 + y3); + + return false; + } + + const float along1 = ((y1 - y3) * dx2 - (x1 - x3) * dy2) / divisor; + + intersectionX = x1 + along1 * dx1; + intersectionY = y1 + along1 * dy1; + + if (along1 < 0 || along1 > 1.0f) + return false; + + const float along2 = ((y1 - y3) * dx1 - (x1 - x3) * dy1) / divisor; + + return along2 >= 0 && along2 <= 1.0f; + } + + intersectionX = x2; + intersectionY = y2; + return true; +} + +Line::Line() throw() + : startX (0.0f), + startY (0.0f), + endX (0.0f), + endY (0.0f) +{ +} + +Line::Line (const Line& other) throw() + : startX (other.startX), + startY (other.startY), + endX (other.endX), + endY (other.endY) +{ +} + +Line::Line (const float startX_, const float startY_, + const float endX_, const float endY_) throw() + : startX (startX_), + startY (startY_), + endX (endX_), + endY (endY_) +{ +} + +Line::Line (const Point& start, + const Point& end) throw() + : startX (start.getX()), + startY (start.getY()), + endX (end.getX()), + endY (end.getY()) +{ +} + +const Line& Line::operator= (const Line& other) throw() +{ + startX = other.startX; + startY = other.startY; + endX = other.endX; + endY = other.endY; + + return *this; +} + +Line::~Line() throw() +{ +} + +const Point Line::getStart() const throw() +{ + return Point (startX, startY); +} + +const Point Line::getEnd() const throw() +{ + return Point (endX, endY); +} + +void Line::setStart (const float newStartX, + const float newStartY) throw() +{ + startX = newStartX; + startY = newStartY; +} + +void Line::setStart (const Point& newStart) throw() +{ + startX = newStart.getX(); + startY = newStart.getY(); +} + +void Line::setEnd (const float newEndX, + const float newEndY) throw() +{ + endX = newEndX; + endY = newEndY; +} + +void Line::setEnd (const Point& newEnd) throw() +{ + endX = newEnd.getX(); + endY = newEnd.getY(); +} + +bool Line::operator== (const Line& other) const throw() +{ + return startX == other.startX + && startY == other.startY + && endX == other.endX + && endY == other.endY; +} + +bool Line::operator!= (const Line& other) const throw() +{ + return startX != other.startX + || startY != other.startY + || endX != other.endX + || endY != other.endY; +} + +void Line::applyTransform (const AffineTransform& transform) throw() +{ + transform.transformPoint (startX, startY); + transform.transformPoint (endX, endY); +} + +float Line::getLength() const throw() +{ + return (float) juce_hypot (startX - endX, + startY - endY); +} + +float Line::getAngle() const throw() +{ + return atan2f (endX - startX, + endY - startY); +} + +const Point Line::getPointAlongLine (const float distanceFromStart) const throw() +{ + const float alpha = distanceFromStart / getLength(); + + return Point (startX + (endX - startX) * alpha, + startY + (endY - startY) * alpha); +} + +const Point Line::getPointAlongLine (const float offsetX, + const float offsetY) const throw() +{ + const float dx = endX - startX; + const float dy = endY - startY; + const double length = juce_hypot (dx, dy); + + if (length == 0) + return Point (startX, startY); + else + return Point (startX + (float) (((dx * offsetX) - (dy * offsetY)) / length), + startY + (float) (((dy * offsetX) + (dx * offsetY)) / length)); +} + +const Point Line::getPointAlongLineProportionally (const float alpha) const throw() +{ + return Point (startX + (endX - startX) * alpha, + startY + (endY - startY) * alpha); +} + +float Line::getDistanceFromLine (const float x, + const float y) const throw() +{ + const double dx = endX - startX; + const double dy = endY - startY; + const double length = dx * dx + dy * dy; + + if (length > 0) + { + const double prop = ((x - startX) * dx + (y - startY) * dy) / length; + + if (prop >= 0.0f && prop < 1.0f) + { + return (float) juce_hypot (x - (startX + prop * dx), + y - (startY + prop * dy)); + } + } + + return (float) jmin (juce_hypot (x - startX, y - startY), + juce_hypot (x - endX, y - endY)); +} + +float Line::findNearestPointTo (const float x, + const float y) const throw() +{ + const double dx = endX - startX; + const double dy = endY - startY; + const double length = dx * dx + dy * dy; + + if (length <= 0.0) + return 0.0f; + + return jlimit (0.0f, 1.0f, + (float) (((x - startX) * dx + (y - startY) * dy) / length)); +} + +const Line Line::withShortenedStart (const float distanceToShortenBy) const throw() +{ + const float length = getLength(); + + return Line (getPointAlongLine (jmin (distanceToShortenBy, length)), + getEnd()); +} + +const Line Line::withShortenedEnd (const float distanceToShortenBy) const throw() +{ + const float length = getLength(); + + return Line (getStart(), + getPointAlongLine (length - jmin (distanceToShortenBy, length))); +} + +bool Line::clipToPath (const Path& path, + const bool keepSectionOutsidePath) throw() +{ + const bool startInside = path.contains (startX, startY); + const bool endInside = path.contains (endX, endY); + + if (startInside == endInside) + { + if (keepSectionOutsidePath != startInside) + { + // entirely outside the path + return false; + } + else + { + // entirely inside the path + startX = 0.0f; + startY = 0.0f; + endX = 0.0f; + endY = 0.0f; + + return true; + } + } + else + { + bool changed = false; + PathFlatteningIterator iter (path, AffineTransform::identity); + + while (iter.next()) + { + float ix, iy; + + if (intersects (Line (iter.x1, iter.y1, + iter.x2, iter.y2), + ix, iy)) + { + if ((startInside && keepSectionOutsidePath) + || (endInside && ! keepSectionOutsidePath)) + { + setStart (ix, iy); + } + else + { + setEnd (ix, iy); + } + + changed = true; + } + } + + return changed; + } +} + +bool Line::intersects (const Line& line, + float& intersectionX, + float& intersectionY) const throw() +{ + return juce_lineIntersection (startX, startY, + endX, endY, + line.startX, line.startY, + line.endX, line.endY, + intersectionX, + intersectionY); +} + +bool Line::isVertical() const throw() +{ + return startX == endX; +} + +bool Line::isHorizontal() const throw() +{ + return startY == endY; +} + +bool Line::isPointAbove (const float x, const float y) const throw() +{ + return startX != endX + && y < ((endY - startY) * (x - startX)) / (endX - startX) + startY; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_Line.cpp *********/ + +/********* Start of inlined file: juce_Path.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +// tests that some co-ords aren't NaNs +#define CHECK_COORDS_ARE_VALID(x, y) \ + jassert (x == x && y == y); + +const float Path::lineMarker = 100001.0f; +const float Path::moveMarker = 100002.0f; +const float Path::quadMarker = 100003.0f; +const float Path::cubicMarker = 100004.0f; +const float Path::closeSubPathMarker = 100005.0f; + +static const int defaultGranularity = 32; + +Path::Path() throw() + : ArrayAllocationBase (defaultGranularity), + numElements (0), + pathXMin (0), + pathXMax (0), + pathYMin (0), + pathYMax (0), + useNonZeroWinding (true) +{ +} + +Path::~Path() throw() +{ +} + +Path::Path (const Path& other) throw() + : ArrayAllocationBase (defaultGranularity), + numElements (other.numElements), + pathXMin (other.pathXMin), + pathXMax (other.pathXMax), + pathYMin (other.pathYMin), + pathYMax (other.pathYMax), + useNonZeroWinding (other.useNonZeroWinding) +{ + if (numElements > 0) + { + setAllocatedSize (numElements); + memcpy (elements, other.elements, numElements * sizeof (float)); + } +} + +const Path& Path::operator= (const Path& other) throw() +{ + if (this != &other) + { + ensureAllocatedSize (other.numElements); + + numElements = other.numElements; + pathXMin = other.pathXMin; + pathXMax = other.pathXMax; + pathYMin = other.pathYMin; + pathYMax = other.pathYMax; + useNonZeroWinding = other.useNonZeroWinding; + + if (numElements > 0) + memcpy (elements, other.elements, numElements * sizeof (float)); + } + + return *this; +} + +void Path::clear() throw() +{ + numElements = 0; + pathXMin = 0; + pathYMin = 0; + pathYMax = 0; + pathXMax = 0; +} + +void Path::swapWithPath (Path& other) +{ + swapVariables (this->numAllocated, other.numAllocated); + swapVariables (this->elements, other.elements); + swapVariables (this->numElements, other.numElements); + swapVariables (this->pathXMin, other.pathXMin); + swapVariables (this->pathXMax, other.pathXMax); + swapVariables (this->pathYMin, other.pathYMin); + swapVariables (this->pathYMax, other.pathYMax); + swapVariables (this->useNonZeroWinding, other.useNonZeroWinding); +} + +void Path::setUsingNonZeroWinding (const bool isNonZero) throw() +{ + useNonZeroWinding = isNonZero; +} + +void Path::scaleToFit (const float x, const float y, const float w, const float h, + const bool preserveProportions) throw() +{ + applyTransform (getTransformToScaleToFit (x, y, w, h, preserveProportions)); +} + +bool Path::isEmpty() const throw() +{ + int i = 0; + + while (i < numElements) + { + const float type = elements [i++]; + + if (type == moveMarker) + { + i += 2; + } + else if (type == lineMarker + || type == quadMarker + || type == cubicMarker) + { + return false; + } + } + + return true; +} + +void Path::getBounds (float& x, float& y, + float& w, float& h) const throw() +{ + x = pathXMin; + y = pathYMin; + w = pathXMax - pathXMin; + h = pathYMax - pathYMin; +} + +void Path::getBoundsTransformed (const AffineTransform& transform, + float& x, float& y, + float& w, float& h) const throw() +{ + float x1 = pathXMin; + float y1 = pathYMin; + transform.transformPoint (x1, y1); + + float x2 = pathXMax; + float y2 = pathYMin; + transform.transformPoint (x2, y2); + + float x3 = pathXMin; + float y3 = pathYMax; + transform.transformPoint (x3, y3); + + float x4 = pathXMax; + float y4 = pathYMax; + transform.transformPoint (x4, y4); + + x = jmin (x1, x2, x3, x4); + y = jmin (y1, y2, y3, y4); + w = jmax (x1, x2, x3, x4) - x; + h = jmax (y1, y2, y3, y4) - y; +} + +void Path::startNewSubPath (const float x, + const float y) throw() +{ + CHECK_COORDS_ARE_VALID (x, y); + + if (numElements == 0) + { + pathXMin = pathXMax = x; + pathYMin = pathYMax = y; + } + else + { + pathXMin = jmin (pathXMin, x); + pathXMax = jmax (pathXMax, x); + pathYMin = jmin (pathYMin, y); + pathYMax = jmax (pathYMax, y); + } + + ensureAllocatedSize (numElements + 3); + + elements [numElements++] = moveMarker; + elements [numElements++] = x; + elements [numElements++] = y; +} + +void Path::lineTo (const float x, const float y) throw() +{ + CHECK_COORDS_ARE_VALID (x, y); + + if (numElements == 0) + startNewSubPath (0, 0); + + ensureAllocatedSize (numElements + 3); + + elements [numElements++] = lineMarker; + elements [numElements++] = x; + elements [numElements++] = y; + + pathXMin = jmin (pathXMin, x); + pathXMax = jmax (pathXMax, x); + pathYMin = jmin (pathYMin, y); + pathYMax = jmax (pathYMax, y); +} + +void Path::quadraticTo (const float x1, const float y1, + const float x2, const float y2) throw() +{ + CHECK_COORDS_ARE_VALID (x1, y1); + CHECK_COORDS_ARE_VALID (x2, y2); + + if (numElements == 0) + startNewSubPath (0, 0); + + ensureAllocatedSize (numElements + 5); + + elements [numElements++] = quadMarker; + elements [numElements++] = x1; + elements [numElements++] = y1; + elements [numElements++] = x2; + elements [numElements++] = y2; + + pathXMin = jmin (pathXMin, x1, x2); + pathXMax = jmax (pathXMax, x1, x2); + pathYMin = jmin (pathYMin, y1, y2); + pathYMax = jmax (pathYMax, y1, y2); +} + +void Path::cubicTo (const float x1, const float y1, + const float x2, const float y2, + const float x3, const float y3) throw() +{ + CHECK_COORDS_ARE_VALID (x1, y1); + CHECK_COORDS_ARE_VALID (x2, y2); + CHECK_COORDS_ARE_VALID (x3, y3); + + if (numElements == 0) + startNewSubPath (0, 0); + + ensureAllocatedSize (numElements + 7); + + elements [numElements++] = cubicMarker; + elements [numElements++] = x1; + elements [numElements++] = y1; + elements [numElements++] = x2; + elements [numElements++] = y2; + elements [numElements++] = x3; + elements [numElements++] = y3; + + pathXMin = jmin (pathXMin, x1, x2, x3); + pathXMax = jmax (pathXMax, x1, x2, x3); + pathYMin = jmin (pathYMin, y1, y2, y3); + pathYMax = jmax (pathYMax, y1, y2, y3); +} + +void Path::closeSubPath() throw() +{ + if (numElements > 0 + && elements [numElements - 1] != closeSubPathMarker) + { + ensureAllocatedSize (numElements + 1); + elements [numElements++] = closeSubPathMarker; + } +} + +const Point Path::getCurrentPosition() const +{ + int i = numElements - 1; + + if (i > 0 && elements[i] == closeSubPathMarker) + { + while (i >= 0) + { + if (elements[i] == moveMarker) + { + i += 2; + break; + } + + --i; + } + } + + if (i > 0) + return Point (elements [i - 1], elements [i]); + + return Point (0.0f, 0.0f); +} + +void Path::addRectangle (const float x, const float y, + const float w, const float h) throw() +{ + startNewSubPath (x, y + h); + lineTo (x, y); + lineTo (x + w, y); + lineTo (x + w, y + h); + closeSubPath(); +} + +void Path::addRoundedRectangle (const float x, const float y, + const float w, const float h, + float csx, + float csy) throw() +{ + csx = jmin (csx, w * 0.5f); + csy = jmin (csy, h * 0.5f); + const float cs45x = csx * 0.45f; + const float cs45y = csy * 0.45f; + const float x2 = x + w; + const float y2 = y + h; + + startNewSubPath (x + csx, y); + lineTo (x2 - csx, y); + cubicTo (x2 - cs45x, y, x2, y + cs45y, x2, y + csy); + lineTo (x2, y2 - csy); + cubicTo (x2, y2 - cs45y, x2 - cs45x, y2, x2 - csx, y2); + lineTo (x + csx, y2); + cubicTo (x + cs45x, y2, x, y2 - cs45y, x, y2 - csy); + lineTo (x, y + csy); + cubicTo (x, y + cs45y, x + cs45x, y, x + csx, y); + closeSubPath(); +} + +void Path::addRoundedRectangle (const float x, const float y, + const float w, const float h, + float cs) throw() +{ + addRoundedRectangle (x, y, w, h, cs, cs); +} + +void Path::addTriangle (const float x1, const float y1, + const float x2, const float y2, + const float x3, const float y3) throw() +{ + startNewSubPath (x1, y1); + lineTo (x2, y2); + lineTo (x3, y3); + closeSubPath(); +} + +void Path::addQuadrilateral (const float x1, const float y1, + const float x2, const float y2, + const float x3, const float y3, + const float x4, const float y4) throw() +{ + startNewSubPath (x1, y1); + lineTo (x2, y2); + lineTo (x3, y3); + lineTo (x4, y4); + closeSubPath(); +} + +void Path::addEllipse (const float x, const float y, + const float w, const float h) throw() +{ + const float hw = w * 0.5f; + const float hw55 = hw * 0.55f; + const float hh = h * 0.5f; + const float hh45 = hh * 0.55f; + const float cx = x + hw; + const float cy = y + hh; + + startNewSubPath (cx, cy - hh); + cubicTo (cx + hw55, cy - hh, cx + hw, cy - hh45, cx + hw, cy); + cubicTo (cx + hw, cy + hh45, cx + hw55, cy + hh, cx, cy + hh); + cubicTo (cx - hw55, cy + hh, cx - hw, cy + hh45, cx - hw, cy); + cubicTo (cx - hw, cy - hh45, cx - hw55, cy - hh, cx, cy - hh); + closeSubPath(); +} + +void Path::addArc (const float x, const float y, + const float w, const float h, + const float fromRadians, + const float toRadians, + const bool startAsNewSubPath) throw() +{ + const float radiusX = w / 2.0f; + const float radiusY = h / 2.0f; + + addCentredArc (x + radiusX, + y + radiusY, + radiusX, radiusY, + 0.0f, + fromRadians, toRadians, + startAsNewSubPath); +} + +static const float ellipseAngularIncrement = 0.05f; + +void Path::addCentredArc (const float centreX, const float centreY, + const float radiusX, const float radiusY, + const float rotationOfEllipse, + const float fromRadians, + const float toRadians, + const bool startAsNewSubPath) throw() +{ + if (radiusX > 0.0f && radiusY > 0.0f) + { + const AffineTransform rotation (AffineTransform::rotation (rotationOfEllipse, centreX, centreY)); + float angle = fromRadians; + + if (startAsNewSubPath) + { + float x = centreX + radiusX * sinf (angle); + float y = centreY - radiusY * cosf (angle); + + if (rotationOfEllipse != 0) + rotation.transformPoint (x, y); + + startNewSubPath (x, y); + } + + if (fromRadians < toRadians) + { + if (startAsNewSubPath) + angle += ellipseAngularIncrement; + + while (angle < toRadians) + { + float x = centreX + radiusX * sinf (angle); + float y = centreY - radiusY * cosf (angle); + + if (rotationOfEllipse != 0) + rotation.transformPoint (x, y); + + lineTo (x, y); + + angle += ellipseAngularIncrement; + } + } + else + { + if (startAsNewSubPath) + angle -= ellipseAngularIncrement; + + while (angle > toRadians) + { + float x = centreX + radiusX * sinf (angle); + float y = centreY - radiusY * cosf (angle); + + if (rotationOfEllipse != 0) + rotation.transformPoint (x, y); + + lineTo (x, y); + + angle -= ellipseAngularIncrement; + } + } + + float x = centreX + radiusX * sinf (toRadians); + float y = centreY - radiusY * cosf (toRadians); + + if (rotationOfEllipse != 0) + rotation.transformPoint (x, y); + + lineTo (x, y); + } +} + +void Path::addPieSegment (const float x, const float y, + const float width, const float height, + const float fromRadians, + const float toRadians, + const float innerCircleProportionalSize) +{ + float hw = width * 0.5f; + float hh = height * 0.5f; + const float centreX = x + hw; + const float centreY = y + hh; + + startNewSubPath (centreX + hw * sinf (fromRadians), + centreY - hh * cosf (fromRadians)); + + addArc (x, y, width, height, fromRadians, toRadians); + + if (fabs (fromRadians - toRadians) > float_Pi * 1.999f) + { + closeSubPath(); + + if (innerCircleProportionalSize > 0) + { + hw *= innerCircleProportionalSize; + hh *= innerCircleProportionalSize; + + startNewSubPath (centreX + hw * sinf (toRadians), + centreY - hh * cosf (toRadians)); + + addArc (centreX - hw, centreY - hh, hw * 2.0f, hh * 2.0f, + toRadians, fromRadians); + } + } + else + { + if (innerCircleProportionalSize > 0) + { + hw *= innerCircleProportionalSize; + hh *= innerCircleProportionalSize; + + addArc (centreX - hw, centreY - hh, hw * 2.0f, hh * 2.0f, + toRadians, fromRadians); + } + else + { + lineTo (centreX, centreY); + } + } + + closeSubPath(); +} + +static void perpendicularOffset (const float x1, const float y1, + const float x2, const float y2, + const float offsetX, const float offsetY, + float& resultX, float& resultY) throw() +{ + const float dx = x2 - x1; + const float dy = y2 - y1; + const float len = juce_hypotf (dx, dy); + + if (len == 0) + { + resultX = x1; + resultY = y1; + } + else + { + resultX = x1 + ((dx * offsetX) - (dy * offsetY)) / len; + resultY = y1 + ((dy * offsetX) + (dx * offsetY)) / len; + } +} + +void Path::addLineSegment (const float startX, const float startY, + const float endX, const float endY, + float lineThickness) throw() +{ + lineThickness *= 0.5f; + + float x, y; + + perpendicularOffset (startX, startY, endX, endY, + 0, lineThickness, x, y); + startNewSubPath (x, y); + + perpendicularOffset (startX, startY, endX, endY, + 0, -lineThickness, x, y); + lineTo (x, y); + + perpendicularOffset (endX, endY, startX, startY, + 0, lineThickness, x, y); + lineTo (x, y); + + perpendicularOffset (endX, endY, startX, startY, + 0, -lineThickness, x, y); + lineTo (x, y); + + closeSubPath(); +} + +void Path::addArrow (const float startX, const float startY, + const float endX, const float endY, + float lineThickness, + float arrowheadWidth, + float arrowheadLength) throw() +{ + lineThickness *= 0.5f; + arrowheadWidth *= 0.5f; + arrowheadLength = jmin (arrowheadLength, 0.8f * juce_hypotf (startX - endX, + startY - endY)); + + float x, y; + + perpendicularOffset (startX, startY, endX, endY, + 0, lineThickness, x, y); + startNewSubPath (x, y); + + perpendicularOffset (startX, startY, endX, endY, + 0, -lineThickness, x, y); + lineTo (x, y); + + perpendicularOffset (endX, endY, startX, startY, + arrowheadLength, lineThickness, x, y); + lineTo (x, y); + + perpendicularOffset (endX, endY, startX, startY, + arrowheadLength, arrowheadWidth, x, y); + lineTo (x, y); + + perpendicularOffset (endX, endY, startX, startY, + 0, 0, x, y); + lineTo (x, y); + + perpendicularOffset (endX, endY, startX, startY, + arrowheadLength, -arrowheadWidth, x, y); + lineTo (x, y); + + perpendicularOffset (endX, endY, startX, startY, + arrowheadLength, -lineThickness, x, y); + lineTo (x, y); + + closeSubPath(); +} + +void Path::addStar (const float centreX, + const float centreY, + const int numberOfPoints, + const float innerRadius, + const float outerRadius, + const float startAngle) +{ + jassert (numberOfPoints > 1); // this would be silly. + + if (numberOfPoints > 1) + { + const float angleBetweenPoints = float_Pi * 2.0f / numberOfPoints; + + for (int i = 0; i < numberOfPoints; ++i) + { + float angle = startAngle + i * angleBetweenPoints; + + const float x = centreX + outerRadius * sinf (angle); + const float y = centreY - outerRadius * cosf (angle); + + if (i == 0) + startNewSubPath (x, y); + else + lineTo (x, y); + + angle += angleBetweenPoints * 0.5f; + + lineTo (centreX + innerRadius * sinf (angle), + centreY - innerRadius * cosf (angle)); + } + + closeSubPath(); + } +} + +void Path::addBubble (float x, float y, + float w, float h, + float cs, + float tipX, + float tipY, + int whichSide, + float arrowPos, + float arrowWidth) +{ + if (w > 1.0f && h > 1.0f) + { + cs = jmin (cs, w * 0.5f, h * 0.5f); + const float cs2 = 2.0f * cs; + + startNewSubPath (x + cs, y); + + if (whichSide == 0) + { + const float halfArrowW = jmin (arrowWidth, w - cs2) * 0.5f; + const float arrowX1 = x + cs + jmax (0.0f, (w - cs2) * arrowPos - halfArrowW); + lineTo (arrowX1, y); + lineTo (tipX, tipY); + lineTo (arrowX1 + halfArrowW * 2.0f, y); + } + + lineTo (x + w - cs, y); + + if (cs > 0.0f) + addArc (x + w - cs2, y, cs2, cs2, 0, float_Pi * 0.5f); + + if (whichSide == 3) + { + const float halfArrowH = jmin (arrowWidth, h - cs2) * 0.5f; + const float arrowY1 = y + cs + jmax (0.0f, (h - cs2) * arrowPos - halfArrowH); + lineTo (x + w, arrowY1); + lineTo (tipX, tipY); + lineTo (x + w, arrowY1 + halfArrowH * 2.0f); + } + + lineTo (x + w, y + h - cs); + + if (cs > 0.0f) + addArc (x + w - cs2, y + h - cs2, cs2, cs2, float_Pi * 0.5f, float_Pi); + + if (whichSide == 2) + { + const float halfArrowW = jmin (arrowWidth, w - cs2) * 0.5f; + const float arrowX1 = x + cs + jmax (0.0f, (w - cs2) * arrowPos - halfArrowW); + lineTo (arrowX1 + halfArrowW * 2.0f, y + h); + lineTo (tipX, tipY); + lineTo (arrowX1, y + h); + } + + lineTo (x + cs, y + h); + + if (cs > 0.0f) + addArc (x, y + h - cs2, cs2, cs2, float_Pi, float_Pi * 1.5f); + + if (whichSide == 1) + { + const float halfArrowH = jmin (arrowWidth, h - cs2) * 0.5f; + const float arrowY1 = y + cs + jmax (0.0f, (h - cs2) * arrowPos - halfArrowH); + lineTo (x, arrowY1 + halfArrowH * 2.0f); + lineTo (tipX, tipY); + lineTo (x, arrowY1); + } + + lineTo (x, y + cs); + + if (cs > 0.0f) + addArc (x, y, cs2, cs2, float_Pi * 1.5f, float_Pi * 2.0f - ellipseAngularIncrement); + + closeSubPath(); + } +} + +void Path::addPath (const Path& other) throw() +{ + int i = 0; + + while (i < other.numElements) + { + const float type = other.elements [i++]; + + if (type == moveMarker) + { + startNewSubPath (other.elements [i], + other.elements [i + 1]); + + i += 2; + } + else if (type == lineMarker) + { + lineTo (other.elements [i], + other.elements [i + 1]); + + i += 2; + } + else if (type == quadMarker) + { + quadraticTo (other.elements [i], + other.elements [i + 1], + other.elements [i + 2], + other.elements [i + 3]); + i += 4; + } + else if (type == cubicMarker) + { + cubicTo (other.elements [i], + other.elements [i + 1], + other.elements [i + 2], + other.elements [i + 3], + other.elements [i + 4], + other.elements [i + 5]); + + i += 6; + } + else if (type == closeSubPathMarker) + { + closeSubPath(); + } + else + { + // something's gone wrong with the element list! + jassertfalse + } + } +} + +void Path::addPath (const Path& other, + const AffineTransform& transformToApply) throw() +{ + int i = 0; + + while (i < other.numElements) + { + const float type = other.elements [i++]; + + if (type == closeSubPathMarker) + { + closeSubPath(); + } + else + { + float x = other.elements [i++]; + float y = other.elements [i++]; + transformToApply.transformPoint (x, y); + + if (type == moveMarker) + { + startNewSubPath (x, y); + } + else if (type == lineMarker) + { + lineTo (x, y); + } + else if (type == quadMarker) + { + float x2 = other.elements [i++]; + float y2 = other.elements [i++]; + transformToApply.transformPoint (x2, y2); + + quadraticTo (x, y, x2, y2); + } + else if (type == cubicMarker) + { + float x2 = other.elements [i++]; + float y2 = other.elements [i++]; + float x3 = other.elements [i++]; + float y3 = other.elements [i++]; + transformToApply.transformPoint (x2, y2); + transformToApply.transformPoint (x3, y3); + + cubicTo (x, y, x2, y2, x3, y3); + } + else + { + // something's gone wrong with the element list! + jassertfalse + } + } + } +} + +void Path::applyTransform (const AffineTransform& transform) throw() +{ + int i = 0; + pathYMin = pathXMin = 0; + pathYMax = pathXMax = 0; + bool setMaxMin = false; + + while (i < numElements) + { + const float type = elements [i++]; + + if (type == moveMarker) + { + transform.transformPoint (elements [i], + elements [i + 1]); + + if (setMaxMin) + { + pathXMin = jmin (pathXMin, elements [i]); + pathXMax = jmax (pathXMax, elements [i]); + pathYMin = jmin (pathYMin, elements [i + 1]); + pathYMax = jmax (pathYMax, elements [i + 1]); + } + else + { + pathXMin = pathXMax = elements [i]; + pathYMin = pathYMax = elements [i + 1]; + setMaxMin = true; + } + + i += 2; + } + else if (type == lineMarker) + { + transform.transformPoint (elements [i], + elements [i + 1]); + + pathXMin = jmin (pathXMin, elements [i]); + pathXMax = jmax (pathXMax, elements [i]); + pathYMin = jmin (pathYMin, elements [i + 1]); + pathYMax = jmax (pathYMax, elements [i + 1]); + + i += 2; + } + else if (type == quadMarker) + { + transform.transformPoint (elements [i], + elements [i + 1]); + + transform.transformPoint (elements [i + 2], + elements [i + 3]); + + pathXMin = jmin (pathXMin, elements [i], elements [i + 2]); + pathXMax = jmax (pathXMax, elements [i], elements [i + 2]); + pathYMin = jmin (pathYMin, elements [i + 1], elements [i + 3]); + pathYMax = jmax (pathYMax, elements [i + 1], elements [i + 3]); + + i += 4; + } + else if (type == cubicMarker) + { + transform.transformPoint (elements [i], + elements [i + 1]); + + transform.transformPoint (elements [i + 2], + elements [i + 3]); + + transform.transformPoint (elements [i + 4], + elements [i + 5]); + + pathXMin = jmin (pathXMin, elements [i], elements [i + 2], elements [i + 4]); + pathXMax = jmax (pathXMax, elements [i], elements [i + 2], elements [i + 4]); + pathYMin = jmin (pathYMin, elements [i + 1], elements [i + 3], elements [i + 5]); + pathYMax = jmax (pathYMax, elements [i + 1], elements [i + 3], elements [i + 5]); + + i += 6; + } + } +} + +const AffineTransform Path::getTransformToScaleToFit (const float x, const float y, + const float w, const float h, + const bool preserveProportions, + const Justification& justification) const throw() +{ + float sx, sy, sw, sh; + getBounds (sx, sy, sw, sh); + + if (preserveProportions) + { + if (w <= 0 || h <= 0 || sw <= 0 || sh <= 0) + return AffineTransform::identity; + + float newW, newH; + const float srcRatio = sh / sw; + + if (srcRatio > h / w) + { + newW = h / srcRatio; + newH = h; + } + else + { + newW = w; + newH = w * srcRatio; + } + + float newXCentre = x; + float newYCentre = y; + + if (justification.testFlags (Justification::left)) + newXCentre += newW * 0.5f; + else if (justification.testFlags (Justification::right)) + newXCentre += w - newW * 0.5f; + else + newXCentre += w * 0.5f; + + if (justification.testFlags (Justification::top)) + newYCentre += newH * 0.5f; + else if (justification.testFlags (Justification::bottom)) + newYCentre += h - newH * 0.5f; + else + newYCentre += h * 0.5f; + + return AffineTransform::translation (sw * -0.5f - sx, sh * -0.5f - sy) + .scaled (newW / sw, newH / sh) + .translated (newXCentre, newYCentre); + } + else + { + return AffineTransform::translation (-sx, -sy) + .scaled (w / sw, h / sh) + .translated (x, y); + } +} + +static const float collisionDetectionTolerence = 20.0f; + +bool Path::contains (const float x, const float y) const throw() +{ + if (x <= pathXMin || x >= pathXMax + || y <= pathYMin || y >= pathYMax) + return false; + + PathFlatteningIterator i (*this, AffineTransform::identity, collisionDetectionTolerence); + + int positiveCrossings = 0; + int negativeCrossings = 0; + + while (i.next()) + { + if ((i.y1 <= y && i.y2 > y) + || (i.y2 <= y && i.y1 > y)) + { + const float intersectX = i.x1 + (i.x2 - i.x1) * (y - i.y1) / (i.y2 - i.y1); + + if (intersectX <= x) + { + if (i.y1 < i.y2) + ++positiveCrossings; + else + ++negativeCrossings; + } + } + } + + return (useNonZeroWinding) ? (negativeCrossings != positiveCrossings) + : ((negativeCrossings + positiveCrossings) & 1) != 0; +} + +bool Path::intersectsLine (const float x1, const float y1, + const float x2, const float y2) throw() +{ + PathFlatteningIterator i (*this, AffineTransform::identity, collisionDetectionTolerence); + + const Line line1 (x1, y1, x2, y2); + + while (i.next()) + { + const Line line2 (i.x1, i.y1, i.x2, i.y2); + + float ix, iy; + if (line1.intersects (line2, ix, iy)) + return true; + } + + return false; +} + +const Path Path::createPathWithRoundedCorners (const float cornerRadius) const throw() +{ + if (cornerRadius <= 0.01f) + return *this; + + int indexOfPathStart = 0, indexOfPathStartThis = 0; + int n = 0; + bool lastWasLine = false, firstWasLine = false; + Path p; + + while (n < numElements) + { + const float type = elements [n++]; + + if (type == moveMarker) + { + indexOfPathStart = p.numElements; + indexOfPathStartThis = n - 1; + const float x = elements [n++]; + const float y = elements [n++]; + p.startNewSubPath (x, y); + lastWasLine = false; + firstWasLine = (elements [n] == lineMarker); + } + else if (type == lineMarker || type == closeSubPathMarker) + { + float startX = 0, startY = 0, joinX = 0, joinY = 0, endX, endY; + + if (type == lineMarker) + { + endX = elements [n++]; + endY = elements [n++]; + + if (n > 8) + { + startX = elements [n - 8]; + startY = elements [n - 7]; + joinX = elements [n - 5]; + joinY = elements [n - 4]; + } + } + else + { + endX = elements [indexOfPathStartThis + 1]; + endY = elements [indexOfPathStartThis + 2]; + + if (n > 6) + { + startX = elements [n - 6]; + startY = elements [n - 5]; + joinX = elements [n - 3]; + joinY = elements [n - 2]; + } + } + + if (lastWasLine) + { + const double len1 = juce_hypot (startX - joinX, + startY - joinY); + + if (len1 > 0) + { + const double propNeeded = jmin (0.5, cornerRadius / len1); + + p.elements [p.numElements - 2] = (float) (joinX - (joinX - startX) * propNeeded); + p.elements [p.numElements - 1] = (float) (joinY - (joinY - startY) * propNeeded); + } + + const double len2 = juce_hypot (endX - joinX, + endY - joinY); + + if (len2 > 0) + { + const double propNeeded = jmin (0.5, cornerRadius / len2); + + p.quadraticTo (joinX, joinY, + (float) (joinX + (endX - joinX) * propNeeded), + (float) (joinY + (endY - joinY) * propNeeded)); + } + + p.lineTo (endX, endY); + } + else if (type == lineMarker) + { + p.lineTo (endX, endY); + lastWasLine = true; + } + + if (type == closeSubPathMarker) + { + if (firstWasLine) + { + startX = elements [n - 3]; + startY = elements [n - 2]; + joinX = endX; + joinY = endY; + endX = elements [indexOfPathStartThis + 4]; + endY = elements [indexOfPathStartThis + 5]; + + const double len1 = juce_hypot (startX - joinX, + startY - joinY); + + if (len1 > 0) + { + const double propNeeded = jmin (0.5, cornerRadius / len1); + + p.elements [p.numElements - 2] = (float) (joinX - (joinX - startX) * propNeeded); + p.elements [p.numElements - 1] = (float) (joinY - (joinY - startY) * propNeeded); + } + + const double len2 = juce_hypot (endX - joinX, + endY - joinY); + + if (len2 > 0) + { + const double propNeeded = jmin (0.5, cornerRadius / len2); + + endX = (float) (joinX + (endX - joinX) * propNeeded); + endY = (float) (joinY + (endY - joinY) * propNeeded); + + p.quadraticTo (joinX, joinY, endX, endY); + + p.elements [indexOfPathStart + 1] = endX; + p.elements [indexOfPathStart + 2] = endY; + } + } + + p.closeSubPath(); + } + } + else if (type == quadMarker) + { + lastWasLine = false; + const float x1 = elements [n++]; + const float y1 = elements [n++]; + const float x2 = elements [n++]; + const float y2 = elements [n++]; + p.quadraticTo (x1, y1, x2, y2); + } + else if (type == cubicMarker) + { + lastWasLine = false; + const float x1 = elements [n++]; + const float y1 = elements [n++]; + const float x2 = elements [n++]; + const float y2 = elements [n++]; + const float x3 = elements [n++]; + const float y3 = elements [n++]; + p.cubicTo (x1, y1, x2, y2, x3, y3); + } + } + + return p; +} + +void Path::loadPathFromStream (InputStream& source) +{ + while (! source.isExhausted()) + { + switch (source.readByte()) + { + case 'm': + { + const float x = source.readFloat(); + const float y = source.readFloat(); + startNewSubPath (x, y); + break; + } + + case 'l': + { + const float x = source.readFloat(); + const float y = source.readFloat(); + lineTo (x, y); + break; + } + + case 'q': + { + const float x1 = source.readFloat(); + const float y1 = source.readFloat(); + const float x2 = source.readFloat(); + const float y2 = source.readFloat(); + quadraticTo (x1, y1, x2, y2); + break; + } + + case 'b': + { + const float x1 = source.readFloat(); + const float y1 = source.readFloat(); + const float x2 = source.readFloat(); + const float y2 = source.readFloat(); + const float x3 = source.readFloat(); + const float y3 = source.readFloat(); + cubicTo (x1, y1, x2, y2, x3, y3); + break; + } + + case 'c': + closeSubPath(); + break; + + case 'n': + useNonZeroWinding = true; + break; + + case 'z': + useNonZeroWinding = false; + break; + + case 'e': + return; // end of path marker + + default: + jassertfalse // illegal char in the stream + break; + } + } +} + +void Path::loadPathFromData (const unsigned char* const data, + const int numberOfBytes) throw() +{ + MemoryInputStream in ((const char*) data, numberOfBytes, false); + loadPathFromStream (in); +} + +void Path::writePathToStream (OutputStream& dest) const +{ + dest.writeByte ((useNonZeroWinding) ? 'n' : 'z'); + + int i = 0; + while (i < numElements) + { + const float type = elements [i++]; + + if (type == moveMarker) + { + dest.writeByte ('m'); + dest.writeFloat (elements [i++]); + dest.writeFloat (elements [i++]); + } + else if (type == lineMarker) + { + dest.writeByte ('l'); + dest.writeFloat (elements [i++]); + dest.writeFloat (elements [i++]); + } + else if (type == quadMarker) + { + dest.writeByte ('q'); + dest.writeFloat (elements [i++]); + dest.writeFloat (elements [i++]); + dest.writeFloat (elements [i++]); + dest.writeFloat (elements [i++]); + } + else if (type == cubicMarker) + { + dest.writeByte ('b'); + dest.writeFloat (elements [i++]); + dest.writeFloat (elements [i++]); + dest.writeFloat (elements [i++]); + dest.writeFloat (elements [i++]); + dest.writeFloat (elements [i++]); + dest.writeFloat (elements [i++]); + } + else if (type == closeSubPathMarker) + { + dest.writeByte ('c'); + } + } + + dest.writeByte ('e'); // marks the end-of-path +} + +const String Path::toString() const +{ + String s; + s.preallocateStorage (numElements * 4); + if (! useNonZeroWinding) + s << T("a "); + + int i = 0; + float lastMarker = 0.0f; + + while (i < numElements) + { + const float marker = elements [i++]; + tchar markerChar = 0; + int numCoords = 0; + + if (marker == moveMarker) + { + markerChar = T('m'); + numCoords = 2; + } + else if (marker == lineMarker) + { + markerChar = T('l'); + numCoords = 2; + } + else if (marker == quadMarker) + { + markerChar = T('q'); + numCoords = 4; + } + else if (marker == cubicMarker) + { + markerChar = T('c'); + numCoords = 6; + } + else + { + jassert (marker == closeSubPathMarker); + markerChar = T('z'); + } + + if (marker != lastMarker) + { + s << markerChar << T(' '); + lastMarker = marker; + } + + while (--numCoords >= 0 && i < numElements) + { + String n (elements [i++], 3); + + while (n.endsWithChar (T('0'))) + n = n.dropLastCharacters (1); + + if (n.endsWithChar (T('.'))) + n = n.dropLastCharacters (1); + + s << n << T(' '); + } + } + + return s.trimEnd(); +} + +static const String nextToken (const tchar*& t) +{ + while (*t == T(' ')) + ++t; + + const tchar* const start = t; + + while (*t != 0 && *t != T(' ')) + ++t; + + const int length = (int) (t - start); + + while (*t == T(' ')) + ++t; + + return String (start, length); +} + +void Path::restoreFromString (const String& stringVersion) +{ + clear(); + setUsingNonZeroWinding (true); + + const tchar* t = stringVersion; + tchar marker = T('m'); + int numValues = 2; + float values [6]; + + while (*t != 0) + { + const String token (nextToken (t)); + const tchar firstChar = token[0]; + int startNum = 0; + + if (firstChar == T('m') || firstChar == T('l')) + { + marker = firstChar; + numValues = 2; + } + else if (firstChar == T('q')) + { + marker = firstChar; + numValues = 4; + } + else if (firstChar == T('c')) + { + marker = firstChar; + numValues = 6; + } + else if (firstChar == T('z')) + { + marker = firstChar; + numValues = 0; + } + else if (firstChar == T('a')) + { + setUsingNonZeroWinding (false); + continue; + } + else + { + ++startNum; + values [0] = token.getFloatValue(); + } + + for (int i = startNum; i < numValues; ++i) + values [i] = nextToken (t).getFloatValue(); + + switch (marker) + { + case T('m'): + startNewSubPath (values[0], values[1]); + break; + + case T('l'): + lineTo (values[0], values[1]); + break; + + case T('q'): + quadraticTo (values[0], values[1], + values[2], values[3]); + break; + + case T('c'): + cubicTo (values[0], values[1], + values[2], values[3], + values[4], values[5]); + break; + + case T('z'): + closeSubPath(); + break; + + default: + jassertfalse // illegal string format? + break; + } + } +} + +Path::Iterator::Iterator (const Path& path_) + : path (path_), + index (0) +{ +} + +Path::Iterator::~Iterator() +{ +} + +bool Path::Iterator::next() +{ + const float* const elements = path.elements; + + if (index < path.numElements) + { + const float type = elements [index++]; + + if (type == moveMarker) + { + elementType = startNewSubPath; + x1 = elements [index++]; + y1 = elements [index++]; + } + else if (type == lineMarker) + { + elementType = lineTo; + x1 = elements [index++]; + y1 = elements [index++]; + } + else if (type == quadMarker) + { + elementType = quadraticTo; + x1 = elements [index++]; + y1 = elements [index++]; + x2 = elements [index++]; + y2 = elements [index++]; + } + else if (type == cubicMarker) + { + elementType = cubicTo; + x1 = elements [index++]; + y1 = elements [index++]; + x2 = elements [index++]; + y2 = elements [index++]; + x3 = elements [index++]; + y3 = elements [index++]; + } + else if (type == closeSubPathMarker) + { + elementType = closePath; + } + + return true; + } + + return false; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_Path.cpp *********/ + +/********* Start of inlined file: juce_PathIterator.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +#if JUCE_MSVC + #pragma optimize ("t", on) +#endif + +PathFlatteningIterator::PathFlatteningIterator (const Path& path_, + const AffineTransform& transform_, + float tolerence_) throw() + : x2 (0), + y2 (0), + closesSubPath (false), + subPathIndex (-1), + path (path_), + transform (transform_), + points (path_.elements), + tolerence (tolerence_ * tolerence_), + subPathCloseX (0), + subPathCloseY (0), + index (0), + stackSize (32) +{ + stackBase = (float*) juce_malloc (stackSize * sizeof (float)); + isIdentityTransform = transform.isIdentity(); + stackPos = stackBase; +} + +PathFlatteningIterator::~PathFlatteningIterator() throw() +{ + juce_free (stackBase); +} + +bool PathFlatteningIterator::next() throw() +{ + x1 = x2; + y1 = y2; + + float x3 = 0; + float y3 = 0; + float x4 = 0; + float y4 = 0; + float type; + + for (;;) + { + if (stackPos == stackBase) + { + if (index >= path.numElements) + { + return false; + } + else + { + type = points [index++]; + + if (type != Path::closeSubPathMarker) + { + x2 = points [index++]; + y2 = points [index++]; + + if (! isIdentityTransform) + transform.transformPoint (x2, y2); + + if (type == Path::quadMarker) + { + x3 = points [index++]; + y3 = points [index++]; + + if (! isIdentityTransform) + transform.transformPoint (x3, y3); + } + else if (type == Path::cubicMarker) + { + x3 = points [index++]; + y3 = points [index++]; + x4 = points [index++]; + y4 = points [index++]; + + if (! isIdentityTransform) + { + transform.transformPoint (x3, y3); + transform.transformPoint (x4, y4); + } + } + } + } + } + else + { + type = *--stackPos; + + if (type != Path::closeSubPathMarker) + { + x2 = *--stackPos; + y2 = *--stackPos; + + if (type == Path::quadMarker) + { + x3 = *--stackPos; + y3 = *--stackPos; + } + else if (type == Path::cubicMarker) + { + x3 = *--stackPos; + y3 = *--stackPos; + x4 = *--stackPos; + y4 = *--stackPos; + } + } + } + + if (type == Path::lineMarker) + { + ++subPathIndex; + + closesSubPath = (stackPos == stackBase) + && (index < path.numElements) + && (points [index] == Path::closeSubPathMarker) + && x2 == subPathCloseX + && y2 == subPathCloseY; + + return true; + } + else if (type == Path::quadMarker) + { + const int offset = (int) (stackPos - stackBase); + + if (offset >= stackSize - 10) + { + stackSize <<= 1; + stackBase = (float*) juce_realloc (stackBase, stackSize * sizeof (float)); + stackPos = stackBase + offset; + } + + const float dx1 = x1 - x2; + const float dy1 = y1 - y2; + const float dx2 = x2 - x3; + const float dy2 = y2 - y3; + + const float m1x = (x1 + x2) * 0.5f; + const float m1y = (y1 + y2) * 0.5f; + const float m2x = (x2 + x3) * 0.5f; + const float m2y = (y2 + y3) * 0.5f; + const float m3x = (m1x + m2x) * 0.5f; + const float m3y = (m1y + m2y) * 0.5f; + + if (dx1*dx1 + dy1*dy1 + dx2*dx2 + dy2*dy2 > tolerence) + { + *stackPos++ = y3; + *stackPos++ = x3; + *stackPos++ = m2y; + *stackPos++ = m2x; + *stackPos++ = Path::quadMarker; + + *stackPos++ = m3y; + *stackPos++ = m3x; + *stackPos++ = m1y; + *stackPos++ = m1x; + *stackPos++ = Path::quadMarker; + } + else + { + *stackPos++ = y3; + *stackPos++ = x3; + *stackPos++ = Path::lineMarker; + + *stackPos++ = m3y; + *stackPos++ = m3x; + *stackPos++ = Path::lineMarker; + } + + jassert (stackPos < stackBase + stackSize); + } + else if (type == Path::cubicMarker) + { + const int offset = (int) (stackPos - stackBase); + + if (offset >= stackSize - 16) + { + stackSize <<= 1; + stackBase = (float*) juce_realloc (stackBase, stackSize * sizeof (float)); + stackPos = stackBase + offset; + } + + const float dx1 = x1 - x2; + const float dy1 = y1 - y2; + const float dx2 = x2 - x3; + const float dy2 = y2 - y3; + const float dx3 = x3 - x4; + const float dy3 = y3 - y4; + + const float m1x = (x1 + x2) * 0.5f; + const float m1y = (y1 + y2) * 0.5f; + const float m2x = (x3 + x2) * 0.5f; + const float m2y = (y3 + y2) * 0.5f; + const float m3x = (x3 + x4) * 0.5f; + const float m3y = (y3 + y4) * 0.5f; + const float m4x = (m1x + m2x) * 0.5f; + const float m4y = (m1y + m2y) * 0.5f; + const float m5x = (m3x + m2x) * 0.5f; + const float m5y = (m3y + m2y) * 0.5f; + + if (dx1*dx1 + dy1*dy1 + dx2*dx2 + + dy2*dy2 + dx3*dx3 + dy3*dy3 > tolerence) + { + *stackPos++ = y4; + *stackPos++ = x4; + *stackPos++ = m3y; + *stackPos++ = m3x; + *stackPos++ = m5y; + *stackPos++ = m5x; + *stackPos++ = Path::cubicMarker; + + *stackPos++ = (m4y + m5y) * 0.5f; + *stackPos++ = (m4x + m5x) * 0.5f; + *stackPos++ = m4y; + *stackPos++ = m4x; + *stackPos++ = m1y; + *stackPos++ = m1x; + *stackPos++ = Path::cubicMarker; + } + else + { + *stackPos++ = y4; + *stackPos++ = x4; + *stackPos++ = Path::lineMarker; + + *stackPos++ = m5y; + *stackPos++ = m5x; + *stackPos++ = Path::lineMarker; + + *stackPos++ = m4y; + *stackPos++ = m4x; + *stackPos++ = Path::lineMarker; + } + } + else if (type == Path::closeSubPathMarker) + { + if (x2 != subPathCloseX || y2 != subPathCloseY) + { + x1 = x2; + y1 = y2; + x2 = subPathCloseX; + y2 = subPathCloseY; + closesSubPath = true; + + return true; + } + } + else + { + subPathIndex = -1; + subPathCloseX = x1 = x2; + subPathCloseY = y1 = y2; + } + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_PathIterator.cpp *********/ + +/********* Start of inlined file: juce_PathStrokeType.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +PathStrokeType::PathStrokeType (const float strokeThickness, + const JointStyle jointStyle_, + const EndCapStyle endStyle_) throw() + : thickness (strokeThickness), + jointStyle (jointStyle_), + endStyle (endStyle_) +{ +} + +PathStrokeType::PathStrokeType (const PathStrokeType& other) throw() + : thickness (other.thickness), + jointStyle (other.jointStyle), + endStyle (other.endStyle) +{ +} + +const PathStrokeType& PathStrokeType::operator= (const PathStrokeType& other) throw() +{ + thickness = other.thickness; + jointStyle = other.jointStyle; + endStyle = other.endStyle; + return *this; +} + +PathStrokeType::~PathStrokeType() throw() +{ +} + +bool PathStrokeType::operator== (const PathStrokeType& other) const throw() +{ + return thickness == other.thickness + && jointStyle == other.jointStyle + && endStyle == other.endStyle; +} + +bool PathStrokeType::operator!= (const PathStrokeType& other) const throw() +{ + return ! operator== (other); +} + +static bool lineIntersection (const float x1, const float y1, + const float x2, const float y2, + const float x3, const float y3, + const float x4, const float y4, + float& intersectionX, + float& intersectionY, + float& distanceBeyondLine1EndSquared) throw() +{ + if (x2 != x3 || y2 != y3) + { + const float dx1 = x2 - x1; + const float dy1 = y2 - y1; + const float dx2 = x4 - x3; + const float dy2 = y4 - y3; + const float divisor = dx1 * dy2 - dx2 * dy1; + + if (divisor == 0) + { + if (! ((dx1 == 0 && dy1 == 0) || (dx2 == 0 && dy2 == 0))) + { + if (dy1 == 0 && dy2 != 0) + { + const float along = (y1 - y3) / dy2; + intersectionX = x3 + along * dx2; + intersectionY = y1; + + distanceBeyondLine1EndSquared = intersectionX - x2; + distanceBeyondLine1EndSquared *= distanceBeyondLine1EndSquared; + if ((x2 > x1) == (intersectionX < x2)) + distanceBeyondLine1EndSquared = -distanceBeyondLine1EndSquared; + + return along >= 0 && along <= 1.0f; + } + else if (dy2 == 0 && dy1 != 0) + { + const float along = (y3 - y1) / dy1; + intersectionX = x1 + along * dx1; + intersectionY = y3; + + distanceBeyondLine1EndSquared = (along - 1.0f) * dx1; + distanceBeyondLine1EndSquared *= distanceBeyondLine1EndSquared; + if (along < 1.0f) + distanceBeyondLine1EndSquared = -distanceBeyondLine1EndSquared; + + return along >= 0 && along <= 1.0f; + } + else if (dx1 == 0 && dx2 != 0) + { + const float along = (x1 - x3) / dx2; + intersectionX = x1; + intersectionY = y3 + along * dy2; + + distanceBeyondLine1EndSquared = intersectionY - y2; + distanceBeyondLine1EndSquared *= distanceBeyondLine1EndSquared; + + if ((y2 > y1) == (intersectionY < y2)) + distanceBeyondLine1EndSquared = -distanceBeyondLine1EndSquared; + + return along >= 0 && along <= 1.0f; + } + else if (dx2 == 0 && dx1 != 0) + { + const float along = (x3 - x1) / dx1; + intersectionX = x3; + intersectionY = y1 + along * dy1; + + distanceBeyondLine1EndSquared = (along - 1.0f) * dy1; + distanceBeyondLine1EndSquared *= distanceBeyondLine1EndSquared; + if (along < 1.0f) + distanceBeyondLine1EndSquared = -distanceBeyondLine1EndSquared; + + return along >= 0 && along <= 1.0f; + } + } + + intersectionX = 0.5f * (x2 + x3); + intersectionY = 0.5f * (y2 + y3); + + distanceBeyondLine1EndSquared = 0.0f; + return false; + } + else + { + const float along1 = ((y1 - y3) * dx2 - (x1 - x3) * dy2) / divisor; + + intersectionX = x1 + along1 * dx1; + intersectionY = y1 + along1 * dy1; + + if (along1 >= 0 && along1 <= 1.0f) + { + const float along2 = ((y1 - y3) * dx1 - (x1 - x3) * dy1); + + if (along2 >= 0 && along2 <= divisor) + { + distanceBeyondLine1EndSquared = 0.0f; + return true; + } + } + + distanceBeyondLine1EndSquared = along1 - 1.0f; + distanceBeyondLine1EndSquared *= distanceBeyondLine1EndSquared; + distanceBeyondLine1EndSquared *= (dx1 * dx1 + dy1 * dy1); + + if (along1 < 1.0f) + distanceBeyondLine1EndSquared = -distanceBeyondLine1EndSquared; + + return false; + } + } + + intersectionX = x2; + intersectionY = y2; + + distanceBeyondLine1EndSquared = 0.0f; + return true; +} + +// part of stroke drawing stuff +static void addEdgeAndJoint (Path& destPath, + const PathStrokeType::JointStyle style, + const float maxMiterExtensionSquared, const float width, + const float x1, const float y1, + const float x2, const float y2, + const float x3, const float y3, + const float x4, const float y4, + const float midX, const float midY) throw() +{ + if (style == PathStrokeType::beveled + || (x3 == x4 && y3 == y4) + || (x1 == x2 && y1 == y2)) + { + destPath.lineTo (x2, y2); + destPath.lineTo (x3, y3); + } + else + { + float jx, jy, distanceBeyondLine1EndSquared; + + // if they intersect, use this point.. + if (lineIntersection (x1, y1, x2, y2, + x3, y3, x4, y4, + jx, jy, distanceBeyondLine1EndSquared)) + { + destPath.lineTo (jx, jy); + } + else + { + if (style == PathStrokeType::mitered) + { + if (distanceBeyondLine1EndSquared < maxMiterExtensionSquared + && distanceBeyondLine1EndSquared > 0.0f) + { + destPath.lineTo (jx, jy); + } + else + { + // the end sticks out too far, so just use a blunt joint + destPath.lineTo (x2, y2); + destPath.lineTo (x3, y3); + } + } + else + { + // curved joints + float angle = atan2f (x2 - midX, y2 - midY); + float angle2 = atan2f (x3 - midX, y3 - midY); + + while (angle < angle2 - 0.01f) + angle2 -= float_Pi * 2.0f; + + destPath.lineTo (x2, y2); + + while (angle > angle2) + { + destPath.lineTo (midX + width * sinf (angle), + midY + width * cosf (angle)); + + angle -= 0.1f; + } + + destPath.lineTo (x3, y3); + } + } + } +} + +static inline void addLineEnd (Path& destPath, + const PathStrokeType::EndCapStyle style, + const float x1, const float y1, + const float x2, const float y2, + const float width) throw() +{ + if (style == PathStrokeType::butt) + { + destPath.lineTo (x2, y2); + } + else + { + float offx1, offy1, offx2, offy2; + + float dx = x2 - x1; + float dy = y2 - y1; + const float len = juce_hypotf (dx, dy); + + if (len == 0) + { + offx1 = offx2 = x1; + offy1 = offy2 = y1; + } + else + { + const float offset = width / len; + dx *= offset; + dy *= offset; + + offx1 = x1 + dy; + offy1 = y1 - dx; + offx2 = x2 + dy; + offy2 = y2 - dx; + } + + if (style == PathStrokeType::square) + { + // sqaure ends + destPath.lineTo (offx1, offy1); + destPath.lineTo (offx2, offy2); + destPath.lineTo (x2, y2); + } + else + { + // rounded ends + const float midx = (offx1 + offx2) * 0.5f; + const float midy = (offy1 + offy2) * 0.5f; + + destPath.cubicTo (x1 + (offx1 - x1) * 0.55f, y1 + (offy1 - y1) * 0.55f, + offx1 + (midx - offx1) * 0.45f, offy1 + (midy - offy1) * 0.45f, + midx, midy); + + destPath.cubicTo (midx + (offx2 - midx) * 0.55f, midy + (offy2 - midy) * 0.55f, + offx2 + (x2 - offx2) * 0.45f, offy2 + (y2 - offy2) * 0.45f, + x2, y2); + } + } +} + +struct LineSection +{ + LineSection() throw() {} + LineSection (int) throw() {} + + float x1, y1, x2, y2; // original line + float lx1, ly1, lx2, ly2; // the left-hand stroke + float rx1, ry1, rx2, ry2; // the right-hand stroke +}; + +static void addSubPath (Path& destPath, const Array & subPath, + const bool isClosed, + const float width, const float maxMiterExtensionSquared, + const PathStrokeType::JointStyle jointStyle, const PathStrokeType::EndCapStyle endStyle) throw() +{ + jassert (subPath.size() > 0); + + const LineSection& firstLine = subPath.getReference (0); + + float lastX1 = firstLine.lx1; + float lastY1 = firstLine.ly1; + float lastX2 = firstLine.lx2; + float lastY2 = firstLine.ly2; + + if (isClosed) + { + destPath.startNewSubPath (lastX1, lastY1); + } + else + { + destPath.startNewSubPath (firstLine.rx2, firstLine.ry2); + + addLineEnd (destPath, endStyle, + firstLine.rx2, firstLine.ry2, + lastX1, lastY1, + width); + } + + int i; + for (i = 1; i < subPath.size(); ++i) + { + const LineSection& l = subPath.getReference (i); + + addEdgeAndJoint (destPath, jointStyle, + maxMiterExtensionSquared, width, + lastX1, lastY1, lastX2, lastY2, + l.lx1, l.ly1, l.lx2, l.ly2, + l.x1, l.y1); + + lastX1 = l.lx1; + lastY1 = l.ly1; + lastX2 = l.lx2; + lastY2 = l.ly2; + } + + const LineSection& lastLine = subPath.getReference (subPath.size() - 1); + + if (isClosed) + { + const LineSection& l = subPath.getReference (0); + + addEdgeAndJoint (destPath, jointStyle, + maxMiterExtensionSquared, width, + lastX1, lastY1, lastX2, lastY2, + l.lx1, l.ly1, l.lx2, l.ly2, + l.x1, l.y1); + + destPath.closeSubPath(); + destPath.startNewSubPath (lastLine.rx1, lastLine.ry1); + } + else + { + destPath.lineTo (lastX2, lastY2); + + addLineEnd (destPath, endStyle, + lastX2, lastY2, + lastLine.rx1, lastLine.ry1, + width); + } + + lastX1 = lastLine.rx1; + lastY1 = lastLine.ry1; + lastX2 = lastLine.rx2; + lastY2 = lastLine.ry2; + + for (i = subPath.size() - 1; --i >= 0;) + { + const LineSection& l = subPath.getReference (i); + + addEdgeAndJoint (destPath, jointStyle, + maxMiterExtensionSquared, width, + lastX1, lastY1, lastX2, lastY2, + l.rx1, l.ry1, l.rx2, l.ry2, + l.x2, l.y2); + + lastX1 = l.rx1; + lastY1 = l.ry1; + lastX2 = l.rx2; + lastY2 = l.ry2; + } + + if (isClosed) + { + addEdgeAndJoint (destPath, jointStyle, + maxMiterExtensionSquared, width, + lastX1, lastY1, lastX2, lastY2, + lastLine.rx1, lastLine.ry1, lastLine.rx2, lastLine.ry2, + lastLine.x2, lastLine.y2); + } + else + { + // do the last line + destPath.lineTo (lastX2, lastY2); + } + + destPath.closeSubPath(); +} + +void PathStrokeType::createStrokedPath (Path& destPath, + const Path& source, + const AffineTransform& transform, + const float extraAccuracy) const throw() +{ + if (thickness <= 0) + { + destPath.clear(); + return; + } + + const Path* sourcePath = &source; + Path temp; + + if (sourcePath == &destPath) + { + destPath.swapWithPath (temp); + sourcePath = &temp; + } + else + { + destPath.clear(); + } + + destPath.setUsingNonZeroWinding (true); + + const float maxMiterExtensionSquared = 9.0f * thickness * thickness; + const float width = 0.5f * thickness; + + // Iterate the path, creating a list of the + // left/right-hand lines along either side of it... + PathFlatteningIterator it (*sourcePath, transform, 9.0f / extraAccuracy); + + Array subPath; + LineSection l; + l.x1 = 0; + l.y1 = 0; + + const float minSegmentLength = 2.0f / (extraAccuracy * extraAccuracy); + + while (it.next()) + { + if (it.subPathIndex == 0) + { + if (subPath.size() > 0) + { + addSubPath (destPath, subPath, false, width, maxMiterExtensionSquared, jointStyle, endStyle); + subPath.clearQuick(); + } + + l.x1 = it.x1; + l.y1 = it.y1; + } + + l.x2 = it.x2; + l.y2 = it.y2; + + float dx = l.x2 - l.x1; + float dy = l.y2 - l.y1; + + const float hypotSquared = dx*dx + dy*dy; + + if (it.closesSubPath || hypotSquared > minSegmentLength || it.isLastInSubpath()) + { + const float len = sqrtf (hypotSquared); + + if (len == 0) + { + l.rx1 = l.rx2 = l.lx1 = l.lx2 = l.x1; + l.ry1 = l.ry2 = l.ly1 = l.ly2 = l.y1; + } + else + { + const float offset = width / len; + dx *= offset; + dy *= offset; + + l.rx2 = l.x1 - dy; + l.ry2 = l.y1 + dx; + l.lx1 = l.x1 + dy; + l.ly1 = l.y1 - dx; + + l.lx2 = l.x2 + dy; + l.ly2 = l.y2 - dx; + l.rx1 = l.x2 - dy; + l.ry1 = l.y2 + dx; + } + + subPath.add (l); + + if (it.closesSubPath) + { + addSubPath (destPath, subPath, true, width, maxMiterExtensionSquared, jointStyle, endStyle); + subPath.clearQuick(); + } + else + { + l.x1 = it.x2; + l.y1 = it.y2; + } + } + } + + if (subPath.size() > 0) + addSubPath (destPath, subPath, false, width, maxMiterExtensionSquared, jointStyle, endStyle); +} + +void PathStrokeType::createDashedStroke (Path& destPath, + const Path& sourcePath, + const float* dashLengths, + int numDashLengths, + const AffineTransform& transform, + const float extraAccuracy) const throw() +{ + if (thickness <= 0) + return; + + // this should really be an even number.. + jassert ((numDashLengths & 1) == 0); + + Path newDestPath; + PathFlatteningIterator it (sourcePath, transform, 9.0f / extraAccuracy); + + bool first = true; + int dashNum = 0; + float pos = 0.0f, lineLen = 0.0f, lineEndPos = 0.0f; + float dx = 0.0f, dy = 0.0f; + + for (;;) + { + const bool isSolid = ((dashNum & 1) == 0); + + const float dashLen = dashLengths [dashNum++ % numDashLengths]; + jassert (dashLen > 0); // must be a positive increment! + if (dashLen <= 0) + break; + + pos += dashLen; + + while (pos > lineEndPos) + { + if (! it.next()) + { + if (isSolid && ! first) + newDestPath.lineTo (it.x2, it.y2); + + createStrokedPath (destPath, newDestPath, AffineTransform::identity, extraAccuracy); + return; + } + + if (isSolid && ! first) + { + newDestPath.lineTo (it.x1, it.y1); + } + else + { + newDestPath.startNewSubPath (it.x1, it.y1); + first = false; + } + + dx = it.x2 - it.x1; + dy = it.y2 - it.y1; + lineLen = juce_hypotf (dx, dy); + lineEndPos += lineLen; + } + + const float alpha = (pos - (lineEndPos - lineLen)) / lineLen; + + if (isSolid) + newDestPath.lineTo (it.x1 + dx * alpha, + it.y1 + dy * alpha); + else + newDestPath.startNewSubPath (it.x1 + dx * alpha, + it.y1 + dy * alpha); + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_PathStrokeType.cpp *********/ + +/********* Start of inlined file: juce_Point.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +Point::Point() throw() + : x (0.0f), + y (0.0f) +{ +} + +Point::Point (const Point& other) throw() + : x (other.x), + y (other.y) +{ +} + +const Point& Point::operator= (const Point& other) throw() +{ + x = other.x; + y = other.y; + + return *this; +} + +Point::Point (const float x_, + const float y_) throw() + : x (x_), + y (y_) +{ +} + +Point::~Point() throw() +{ +} + +void Point::setXY (const float x_, + const float y_) throw() +{ + x = x_; + y = y_; +} + +void Point::applyTransform (const AffineTransform& transform) throw() +{ + transform.transformPoint (x, y); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_Point.cpp *********/ + +/********* Start of inlined file: juce_PositionedRectangle.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +PositionedRectangle::PositionedRectangle() throw() + : x (0.0), + y (0.0), + w (0.0), + h (0.0), + xMode (anchorAtLeftOrTop | absoluteFromParentTopLeft), + yMode (anchorAtLeftOrTop | absoluteFromParentTopLeft), + wMode (absoluteSize), + hMode (absoluteSize) +{ +} + +PositionedRectangle::PositionedRectangle (const PositionedRectangle& other) throw() + : x (other.x), + y (other.y), + w (other.w), + h (other.h), + xMode (other.xMode), + yMode (other.yMode), + wMode (other.wMode), + hMode (other.hMode) +{ +} + +const PositionedRectangle& PositionedRectangle::operator= (const PositionedRectangle& other) throw() +{ + if (this != &other) + { + x = other.x; + y = other.y; + w = other.w; + h = other.h; + xMode = other.xMode; + yMode = other.yMode; + wMode = other.wMode; + hMode = other.hMode; + } + + return *this; +} + +PositionedRectangle::~PositionedRectangle() throw() +{ +} + +const bool PositionedRectangle::operator== (const PositionedRectangle& other) const throw() +{ + return x == other.x + && y == other.y + && w == other.w + && h == other.h + && xMode == other.xMode + && yMode == other.yMode + && wMode == other.wMode + && hMode == other.hMode; +} + +const bool PositionedRectangle::operator!= (const PositionedRectangle& other) const throw() +{ + return ! operator== (other); +} + +PositionedRectangle::PositionedRectangle (const String& stringVersion) throw() +{ + StringArray tokens; + tokens.addTokens (stringVersion, false); + + decodePosString (tokens [0], xMode, x); + decodePosString (tokens [1], yMode, y); + decodeSizeString (tokens [2], wMode, w); + decodeSizeString (tokens [3], hMode, h); +} + +const String PositionedRectangle::toString() const throw() +{ + String s; + s.preallocateStorage (12); + + addPosDescription (s, xMode, x); + s << T(' '); + addPosDescription (s, yMode, y); + s << T(' '); + addSizeDescription (s, wMode, w); + s << T(' '); + addSizeDescription (s, hMode, h); + + return s; +} + +const Rectangle PositionedRectangle::getRectangle (const Rectangle& target) const throw() +{ + jassert (! target.isEmpty()); + + double x_, y_, w_, h_; + applyPosAndSize (x_, w_, x, w, xMode, wMode, target.getX(), target.getWidth()); + applyPosAndSize (y_, h_, y, h, yMode, hMode, target.getY(), target.getHeight()); + + return Rectangle (roundDoubleToInt (x_), roundDoubleToInt (y_), + roundDoubleToInt (w_), roundDoubleToInt (h_)); +} + +void PositionedRectangle::getRectangleDouble (const Rectangle& target, + double& x_, double& y_, + double& w_, double& h_) const throw() +{ + jassert (! target.isEmpty()); + + applyPosAndSize (x_, w_, x, w, xMode, wMode, target.getX(), target.getWidth()); + applyPosAndSize (y_, h_, y, h, yMode, hMode, target.getY(), target.getHeight()); +} + +void PositionedRectangle::applyToComponent (Component& comp) const throw() +{ + comp.setBounds (getRectangle (Rectangle (0, 0, comp.getParentWidth(), comp.getParentHeight()))); +} + +void PositionedRectangle::updateFrom (const Rectangle& rectangle, + const Rectangle& target) throw() +{ + updatePosAndSize (x, w, rectangle.getX(), rectangle.getWidth(), xMode, wMode, target.getX(), target.getWidth()); + updatePosAndSize (y, h, rectangle.getY(), rectangle.getHeight(), yMode, hMode, target.getY(), target.getHeight()); +} + +void PositionedRectangle::updateFromDouble (const double newX, const double newY, + const double newW, const double newH, + const Rectangle& target) throw() +{ + updatePosAndSize (x, w, newX, newW, xMode, wMode, target.getX(), target.getWidth()); + updatePosAndSize (y, h, newY, newH, yMode, hMode, target.getY(), target.getHeight()); +} + +void PositionedRectangle::updateFromComponent (const Component& comp) throw() +{ + if (comp.getParentComponent() == 0 && ! comp.isOnDesktop()) + updateFrom (comp.getBounds(), Rectangle()); + else + updateFrom (comp.getBounds(), Rectangle (0, 0, comp.getParentWidth(), comp.getParentHeight())); +} + +PositionedRectangle::AnchorPoint PositionedRectangle::getAnchorPointX() const throw() +{ + return (AnchorPoint) (xMode & (anchorAtLeftOrTop | anchorAtRightOrBottom | anchorAtCentre)); +} + +PositionedRectangle::PositionMode PositionedRectangle::getPositionModeX() const throw() +{ + return (PositionMode) (xMode & (absoluteFromParentTopLeft + | absoluteFromParentBottomRight + | absoluteFromParentCentre + | proportionOfParentSize)); +} + +PositionedRectangle::AnchorPoint PositionedRectangle::getAnchorPointY() const throw() +{ + return (AnchorPoint) (yMode & (anchorAtLeftOrTop | anchorAtRightOrBottom | anchorAtCentre)); +} + +PositionedRectangle::PositionMode PositionedRectangle::getPositionModeY() const throw() +{ + return (PositionMode) (yMode & (absoluteFromParentTopLeft + | absoluteFromParentBottomRight + | absoluteFromParentCentre + | proportionOfParentSize)); +} + +PositionedRectangle::SizeMode PositionedRectangle::getWidthMode() const throw() +{ + return (SizeMode) wMode; +} + +PositionedRectangle::SizeMode PositionedRectangle::getHeightMode() const throw() +{ + return (SizeMode) hMode; +} + +void PositionedRectangle::setModes (const AnchorPoint xAnchor, + const PositionMode xMode_, + const AnchorPoint yAnchor, + const PositionMode yMode_, + const SizeMode widthMode, + const SizeMode heightMode, + const Rectangle& target) throw() +{ + if (xMode != (xAnchor | xMode_) || wMode != widthMode) + { + double tx, tw; + applyPosAndSize (tx, tw, x, w, xMode, wMode, target.getX(), target.getWidth()); + + xMode = (uint8) (xAnchor | xMode_); + wMode = (uint8) widthMode; + + updatePosAndSize (x, w, tx, tw, xMode, wMode, target.getX(), target.getWidth()); + } + + if (yMode != (yAnchor | yMode_) || hMode != heightMode) + { + double ty, th; + applyPosAndSize (ty, th, y, h, yMode, hMode, target.getY(), target.getHeight()); + + yMode = (uint8) (yAnchor | yMode_); + hMode = (uint8) heightMode; + + updatePosAndSize (y, h, ty, th, yMode, hMode, target.getY(), target.getHeight()); + } +} + +bool PositionedRectangle::isPositionAbsolute() const throw() +{ + return xMode == absoluteFromParentTopLeft + && yMode == absoluteFromParentTopLeft + && wMode == absoluteSize + && hMode == absoluteSize; +} + +void PositionedRectangle::addPosDescription (String& s, const uint8 mode, const double value) const throw() +{ + if ((mode & proportionOfParentSize) != 0) + { + s << (roundDoubleToInt (value * 100000.0) / 1000.0) << T('%'); + } + else + { + s << (roundDoubleToInt (value * 100.0) / 100.0); + + if ((mode & absoluteFromParentBottomRight) != 0) + s << T('R'); + else if ((mode & absoluteFromParentCentre) != 0) + s << T('C'); + } + + if ((mode & anchorAtRightOrBottom) != 0) + s << T('r'); + else if ((mode & anchorAtCentre) != 0) + s << T('c'); +} + +void PositionedRectangle::addSizeDescription (String& s, const uint8 mode, const double value) const throw() +{ + if (mode == proportionalSize) + s << (roundDoubleToInt (value * 100000.0) / 1000.0) << T('%'); + else if (mode == parentSizeMinusAbsolute) + s << (roundDoubleToInt (value * 100.0) / 100.0) << T('M'); + else + s << (roundDoubleToInt (value * 100.0) / 100.0); +} + +void PositionedRectangle::decodePosString (const String& s, uint8& mode, double& value) throw() +{ + if (s.containsChar (T('r'))) + mode = anchorAtRightOrBottom; + else if (s.containsChar (T('c'))) + mode = anchorAtCentre; + else + mode = anchorAtLeftOrTop; + + if (s.containsChar (T('%'))) + { + mode |= proportionOfParentSize; + value = s.removeCharacters (T("%rcRC")).getDoubleValue() / 100.0; + } + else + { + if (s.containsChar (T('R'))) + mode |= absoluteFromParentBottomRight; + else if (s.containsChar (T('C'))) + mode |= absoluteFromParentCentre; + else + mode |= absoluteFromParentTopLeft; + + value = s.removeCharacters (T("rcRC")).getDoubleValue(); + } +} + +void PositionedRectangle::decodeSizeString (const String& s, uint8& mode, double& value) throw() +{ + if (s.containsChar (T('%'))) + { + mode = proportionalSize; + value = s.upToFirstOccurrenceOf (T("%"), false, false).getDoubleValue() / 100.0; + } + else if (s.containsChar (T('M'))) + { + mode = parentSizeMinusAbsolute; + value = s.getDoubleValue(); + } + else + { + mode = absoluteSize; + value = s.getDoubleValue(); + } +} + +void PositionedRectangle::applyPosAndSize (double& xOut, double& wOut, + const double x, const double w, + const uint8 xMode, const uint8 wMode, + const int parentPos, + const int parentSize) const throw() +{ + if (wMode == proportionalSize) + wOut = roundDoubleToInt (w * parentSize); + else if (wMode == parentSizeMinusAbsolute) + wOut = jmax (0, parentSize - roundDoubleToInt (w)); + else + wOut = roundDoubleToInt (w); + + if ((xMode & proportionOfParentSize) != 0) + xOut = parentPos + x * parentSize; + else if ((xMode & absoluteFromParentBottomRight) != 0) + xOut = (parentPos + parentSize) - x; + else if ((xMode & absoluteFromParentCentre) != 0) + xOut = x + (parentPos + parentSize / 2); + else + xOut = x + parentPos; + + if ((xMode & anchorAtRightOrBottom) != 0) + xOut -= wOut; + else if ((xMode & anchorAtCentre) != 0) + xOut -= wOut / 2; +} + +void PositionedRectangle::updatePosAndSize (double& xOut, double& wOut, + double x, const double w, + const uint8 xMode, const uint8 wMode, + const int parentPos, + const int parentSize) const throw() +{ + if (wMode == proportionalSize) + { + if (parentSize > 0) + wOut = w / parentSize; + } + else if (wMode == parentSizeMinusAbsolute) + wOut = parentSize - w; + else + wOut = w; + + if ((xMode & anchorAtRightOrBottom) != 0) + x += w; + else if ((xMode & anchorAtCentre) != 0) + x += w / 2; + + if ((xMode & proportionOfParentSize) != 0) + { + if (parentSize > 0) + xOut = (x - parentPos) / parentSize; + } + else if ((xMode & absoluteFromParentBottomRight) != 0) + xOut = (parentPos + parentSize) - x; + else if ((xMode & absoluteFromParentCentre) != 0) + xOut = x - (parentPos + parentSize / 2); + else + xOut = x - parentPos; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_PositionedRectangle.cpp *********/ + +/********* Start of inlined file: juce_Rectangle.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +Rectangle::Rectangle() throw() + : x (0), + y (0), + w (0), + h (0) +{ +} + +Rectangle::Rectangle (const int x_, const int y_, + const int w_, const int h_) throw() + : x (x_), + y (y_), + w (w_), + h (h_) +{ +} + +Rectangle::Rectangle (const int w_, const int h_) throw() + : x (0), + y (0), + w (w_), + h (h_) +{ +} + +Rectangle::Rectangle (const Rectangle& other) throw() + : x (other.x), + y (other.y), + w (other.w), + h (other.h) +{ +} + +Rectangle::~Rectangle() throw() +{ +} + +bool Rectangle::isEmpty() const throw() +{ + return w <= 0 || h <= 0; +} + +void Rectangle::setBounds (const int x_, + const int y_, + const int w_, + const int h_) throw() +{ + x = x_; + y = y_; + w = w_; + h = h_; +} + +void Rectangle::setPosition (const int x_, + const int y_) throw() +{ + x = x_; + y = y_; +} + +void Rectangle::setSize (const int w_, + const int h_) throw() +{ + w = w_; + h = h_; +} + +void Rectangle::translate (const int dx, + const int dy) throw() +{ + x += dx; + y += dy; +} + +const Rectangle Rectangle::translated (const int dx, + const int dy) const throw() +{ + return Rectangle (x + dx, y + dy, w, h); +} + +void Rectangle::expand (const int deltaX, + const int deltaY) throw() +{ + const int nw = jmax (0, w + deltaX + deltaX); + const int nh = jmax (0, h + deltaY + deltaY); + + setBounds (x - deltaX, + y - deltaY, + nw, nh); +} + +const Rectangle Rectangle::expanded (const int deltaX, + const int deltaY) const throw() +{ + const int nw = jmax (0, w + deltaX + deltaX); + const int nh = jmax (0, h + deltaY + deltaY); + + return Rectangle (x - deltaX, + y - deltaY, + nw, nh); +} + +void Rectangle::reduce (const int deltaX, + const int deltaY) throw() +{ + expand (-deltaX, -deltaY); +} + +const Rectangle Rectangle::reduced (const int deltaX, + const int deltaY) const throw() +{ + return expanded (-deltaX, -deltaY); +} + +bool Rectangle::operator== (const Rectangle& other) const throw() +{ + return x == other.x + && y == other.y + && w == other.w + && h == other.h; +} + +bool Rectangle::operator!= (const Rectangle& other) const throw() +{ + return x != other.x + || y != other.y + || w != other.w + || h != other.h; +} + +bool Rectangle::contains (const int px, + const int py) const throw() +{ + return px >= x + && py >= y + && px < x + w + && py < y + h; +} + +bool Rectangle::contains (const Rectangle& other) const throw() +{ + return x <= other.x + && y <= other.y + && x + w >= other.x + other.w + && y + h >= other.y + other.h; +} + +bool Rectangle::intersects (const Rectangle& other) const throw() +{ + return x + w > other.x + && y + h > other.y + && x < other.x + other.w + && y < other.y + other.h + && w > 0 + && h > 0; +} + +const Rectangle Rectangle::getIntersection (const Rectangle& other) const throw() +{ + const int nx = jmax (x, other.x); + const int ny = jmax (y, other.y); + const int nw = jmin (x + w, other.x + other.w) - nx; + const int nh = jmin (y + h, other.y + other.h) - ny; + + if (nw >= 0 && nh >= 0) + return Rectangle (nx, ny, nw, nh); + else + return Rectangle(); +} + +bool Rectangle::intersectRectangle (int& x1, int& y1, int& w1, int& h1) const throw() +{ + const int maxX = jmax (x1, x); + w1 = jmin (x1 + w1, x + w) - maxX; + + if (w1 > 0) + { + const int maxY = jmax (y1, y); + h1 = jmin (y1 + h1, y + h) - maxY; + + if (h1 > 0) + { + x1 = maxX; + y1 = maxY; + + return true; + } + } + + return false; +} + +bool Rectangle::intersectRectangles (int& x1, int& y1, int& w1, int& h1, + int x2, int y2, int w2, int h2) throw() +{ + const int x = jmax (x1, x2); + w1 = jmin (x1 + w1, x2 + w2) - x; + + if (w1 > 0) + { + const int y = jmax (y1, y2); + h1 = jmin (y1 + h1, y2 + h2) - y; + + if (h1 > 0) + { + x1 = x; + y1 = y; + + return true; + } + } + + return false; +} + +const Rectangle Rectangle::getUnion (const Rectangle& other) const throw() +{ + const int newX = jmin (x, other.x); + const int newY = jmin (y, other.y); + + return Rectangle (newX, newY, + jmax (x + w, other.x + other.w) - newX, + jmax (y + h, other.y + other.h) - newY); +} + +bool Rectangle::enlargeIfAdjacent (const Rectangle& other) throw() +{ + if (x == other.x && getRight() == other.getRight() + && (other.getBottom() >= y && other.y <= getBottom())) + { + const int newY = jmin (y, other.y); + h = jmax (getBottom(), other.getBottom()) - newY; + y = newY; + return true; + } + else if (y == other.y && getBottom() == other.getBottom() + && (other.getRight() >= x && other.x <= getRight())) + { + const int newX = jmin (x, other.x); + w = jmax (getRight(), other.getRight()) - newX; + x = newX; + return true; + } + + return false; +} + +bool Rectangle::reduceIfPartlyContainedIn (const Rectangle& other) throw() +{ + int inside = 0; + const int otherR = other.getRight(); + + if (x >= other.x && x < otherR) + inside = 1; + + const int otherB = other.getBottom(); + + if (y >= other.y && y < otherB) + inside |= 2; + + const int r = x + w; + + if (r >= other.x && r < otherR) + inside |= 4; + + const int b = y + h; + + if (b >= other.y && b < otherB) + inside |= 8; + + switch (inside) + { + case 1 + 2 + 8: + w = r - otherR; + x = otherR; + return true; + + case 1 + 2 + 4: + h = b - otherB; + y = otherB; + return true; + + case 2 + 4 + 8: + w = other.x - x; + return true; + + case 1 + 4 + 8: + h = other.y - y; + return true; + } + + return false; +} + +const String Rectangle::toString() const throw() +{ + String s; + s.preallocateStorage (16); + + s << x << T(' ') + << y << T(' ') + << w << T(' ') + << h; + + return s; +} + +const Rectangle Rectangle::fromString (const String& stringVersion) +{ + StringArray toks; + toks.addTokens (stringVersion.trim(), T(",; \t\r\n"), 0); + + return Rectangle (toks[0].trim().getIntValue(), + toks[1].trim().getIntValue(), + toks[2].trim().getIntValue(), + toks[3].trim().getIntValue()); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_Rectangle.cpp *********/ + +/********* Start of inlined file: juce_RectangleList.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +RectangleList::RectangleList() throw() +{ +} + +RectangleList::RectangleList (const Rectangle& rect) throw() +{ + if (! rect.isEmpty()) + rects.add (rect); +} + +RectangleList::RectangleList (const RectangleList& other) throw() + : rects (other.rects) +{ +} + +const RectangleList& RectangleList::operator= (const RectangleList& other) throw() +{ + if (this != &other) + rects = other.rects; + + return *this; +} + +RectangleList::~RectangleList() throw() +{ +} + +void RectangleList::clear() throw() +{ + rects.clearQuick(); +} + +const Rectangle RectangleList::getRectangle (const int index) const throw() +{ + if (((unsigned int) index) < (unsigned int) rects.size()) + return rects.getReference (index); + + return Rectangle(); +} + +bool RectangleList::isEmpty() const throw() +{ + return rects.size() == 0; +} + +RectangleList::Iterator::Iterator (const RectangleList& list) throw() + : current (0), + owner (list), + index (list.rects.size()) +{ +} + +RectangleList::Iterator::~Iterator() throw() +{ +} + +bool RectangleList::Iterator::next() throw() +{ + if (--index >= 0) + { + current = & (owner.rects.getReference (index)); + return true; + } + + return false; +} + +void RectangleList::add (const Rectangle& rect) throw() +{ + if (! rect.isEmpty()) + { + if (rects.size() == 0) + { + rects.add (rect); + } + else + { + bool anyOverlaps = false; + + int i; + for (i = rects.size(); --i >= 0;) + { + Rectangle& ourRect = rects.getReference (i); + + if (rect.intersects (ourRect)) + { + if (rect.contains (ourRect)) + rects.remove (i); + else if (! ourRect.reduceIfPartlyContainedIn (rect)) + anyOverlaps = true; + } + } + + if (anyOverlaps && rects.size() > 0) + { + RectangleList r (rect); + + for (i = rects.size(); --i >= 0;) + { + const Rectangle& ourRect = rects.getReference (i); + + if (rect.intersects (ourRect)) + { + r.subtract (ourRect); + + if (r.rects.size() == 0) + return; + } + } + + for (i = r.getNumRectangles(); --i >= 0;) + rects.add (r.rects.getReference (i)); + } + else + { + rects.add (rect); + } + } + } +} + +void RectangleList::addWithoutMerging (const Rectangle& rect) throw() +{ + rects.add (rect); +} + +void RectangleList::add (const int x, const int y, const int w, const int h) throw() +{ + if (rects.size() == 0) + { + if (w > 0 && h > 0) + rects.add (Rectangle (x, y, w, h)); + } + else + { + add (Rectangle (x, y, w, h)); + } +} + +void RectangleList::add (const RectangleList& other) throw() +{ + for (int i = 0; i < other.rects.size(); ++i) + add (other.rects.getReference (i)); +} + +void RectangleList::subtract (const Rectangle& rect) throw() +{ + const int originalNumRects = rects.size(); + + if (originalNumRects > 0) + { + const int x1 = rect.x; + const int y1 = rect.y; + const int x2 = x1 + rect.w; + const int y2 = y1 + rect.h; + + for (int i = getNumRectangles(); --i >= 0;) + { + Rectangle& r = rects.getReference (i); + + const int rx1 = r.x; + const int ry1 = r.y; + const int rx2 = rx1 + r.w; + const int ry2 = ry1 + r.h; + + if (! (x2 <= rx1 || x1 >= rx2 || y2 <= ry1 || y1 >= ry2)) + { + if (x1 > rx1 && x1 < rx2) + { + if (y1 <= ry1 && y2 >= ry2 && x2 >= rx2) + { + r.w = x1 - rx1; + } + else + { + r.x = x1; + r.w = rx2 - x1; + + rects.insert (i + 1, Rectangle (rx1, ry1, x1 - rx1, ry2 - ry1)); + i += 2; + } + } + else if (x2 > rx1 && x2 < rx2) + { + r.x = x2; + r.w = rx2 - x2; + + if (y1 > ry1 || y2 < ry2 || x1 > rx1) + { + rects.insert (i + 1, Rectangle (rx1, ry1, x2 - rx1, ry2 - ry1)); + i += 2; + } + } + else if (y1 > ry1 && y1 < ry2) + { + if (x1 <= rx1 && x2 >= rx2 && y2 >= ry2) + { + r.h = y1 - ry1; + } + else + { + r.y = y1; + r.h = ry2 - y1; + + rects.insert (i + 1, Rectangle (rx1, ry1, rx2 - rx1, y1 - ry1)); + i += 2; + } + } + else if (y2 > ry1 && y2 < ry2) + { + r.y = y2; + r.h = ry2 - y2; + + if (x1 > rx1 || x2 < rx2 || y1 > ry1) + { + rects.insert (i + 1, Rectangle (rx1, ry1, rx2 - rx1, y2 - ry1)); + i += 2; + } + } + else + { + rects.remove (i); + } + } + } + + if (rects.size() > originalNumRects + 10) + consolidate(); + } +} + +void RectangleList::subtract (const RectangleList& otherList) throw() +{ + for (int i = otherList.rects.size(); --i >= 0;) + subtract (otherList.rects.getReference (i)); +} + +bool RectangleList::clipTo (const Rectangle& rect) throw() +{ + bool notEmpty = false; + + if (rect.isEmpty()) + { + clear(); + } + else + { + for (int i = rects.size(); --i >= 0;) + { + Rectangle& r = rects.getReference (i); + + if (! rect.intersectRectangle (r.x, r.y, r.w, r.h)) + rects.remove (i); + else + notEmpty = true; + } + } + + return notEmpty; +} + +bool RectangleList::clipTo (const RectangleList& other) throw() +{ + if (rects.size() == 0) + return false; + + RectangleList result; + + for (int j = 0; j < rects.size(); ++j) + { + const Rectangle& rect = rects.getReference (j); + + for (int i = other.rects.size(); --i >= 0;) + { + Rectangle r (other.rects.getReference (i)); + + if (rect.intersectRectangle (r.x, r.y, r.w, r.h)) + result.rects.add (r); + } + } + + swapWith (result); + + return ! isEmpty(); +} + +bool RectangleList::getIntersectionWith (const Rectangle& rect, RectangleList& destRegion) const throw() +{ + destRegion.clear(); + + if (! rect.isEmpty()) + { + for (int i = rects.size(); --i >= 0;) + { + Rectangle r (rects.getReference (i)); + + if (rect.intersectRectangle (r.x, r.y, r.w, r.h)) + destRegion.rects.add (r); + } + } + + return destRegion.rects.size() > 0; +} + +void RectangleList::swapWith (RectangleList& otherList) throw() +{ + rects.swapWithArray (otherList.rects); +} + +void RectangleList::consolidate() throw() +{ + int i; + for (i = 0; i < getNumRectangles() - 1; ++i) + { + Rectangle& r = rects.getReference (i); + const int rx1 = r.x; + const int ry1 = r.y; + const int rx2 = rx1 + r.w; + const int ry2 = ry1 + r.h; + + for (int j = rects.size(); --j > i;) + { + Rectangle& r2 = rects.getReference (j); + const int jrx1 = r2.x; + const int jry1 = r2.y; + const int jrx2 = jrx1 + r2.w; + const int jry2 = jry1 + r2.h; + + // if the vertical edges of any blocks are touching and their horizontals don't + // line up, split them horizontally.. + if (jrx1 == rx2 || jrx2 == rx1) + { + if (jry1 > ry1 && jry1 < ry2) + { + r.h = jry1 - ry1; + rects.add (Rectangle (rx1, jry1, rx2 - rx1, ry2 - jry1)); + i = -1; + break; + } + + if (jry2 > ry1 && jry2 < ry2) + { + r.h = jry2 - ry1; + rects.add (Rectangle (rx1, jry2, rx2 - rx1, ry2 - jry2)); + i = -1; + break; + } + else if (ry1 > jry1 && ry1 < jry2) + { + r2.h = ry1 - jry1; + rects.add (Rectangle (jrx1, ry1, jrx2 - jrx1, jry2 - ry1)); + i = -1; + break; + } + else if (ry2 > jry1 && ry2 < jry2) + { + r2.h = ry2 - jry1; + rects.add (Rectangle (jrx1, ry2, jrx2 - jrx1, jry2 - ry2)); + i = -1; + break; + } + } + } + } + + for (i = 0; i < rects.size() - 1; ++i) + { + Rectangle& r = rects.getReference (i); + + for (int j = rects.size(); --j > i;) + { + if (r.enlargeIfAdjacent (rects.getReference (j))) + { + rects.remove (j); + i = -1; + break; + } + } + } +} + +bool RectangleList::containsPoint (const int x, const int y) const throw() +{ + for (int i = getNumRectangles(); --i >= 0;) + if (rects.getReference (i).contains (x, y)) + return true; + + return false; +} + +bool RectangleList::containsRectangle (const Rectangle& rectangleToCheck) const throw() +{ + if (rects.size() > 1) + { + RectangleList r (rectangleToCheck); + + for (int i = rects.size(); --i >= 0;) + { + r.subtract (rects.getReference (i)); + + if (r.rects.size() == 0) + return true; + } + } + else if (rects.size() > 0) + { + return rects.getReference (0).contains (rectangleToCheck); + } + + return false; +} + +bool RectangleList::intersectsRectangle (const Rectangle& rectangleToCheck) const throw() +{ + for (int i = rects.size(); --i >= 0;) + if (rects.getReference (i).intersects (rectangleToCheck)) + return true; + + return false; +} + +bool RectangleList::intersects (const RectangleList& other) const throw() +{ + for (int i = rects.size(); --i >= 0;) + if (other.intersectsRectangle (rects.getReference (i))) + return true; + + return false; +} + +const Rectangle RectangleList::getBounds() const throw() +{ + if (rects.size() <= 1) + { + if (rects.size() == 0) + return Rectangle(); + else + return rects.getReference (0); + } + else + { + const Rectangle& r = rects.getReference (0); + + int minX = r.x; + int minY = r.y; + int maxX = minX + r.w; + int maxY = minY + r.h; + + for (int i = rects.size(); --i > 0;) + { + const Rectangle& r2 = rects.getReference (i); + + minX = jmin (minX, r2.x); + minY = jmin (minY, r2.y); + maxX = jmax (maxX, r2.getRight()); + maxY = jmax (maxY, r2.getBottom()); + } + + return Rectangle (minX, minY, maxX - minX, maxY - minY); + } +} + +void RectangleList::offsetAll (const int dx, const int dy) throw() +{ + for (int i = rects.size(); --i >= 0;) + { + Rectangle& r = rects.getReference (i); + + r.x += dx; + r.y += dy; + } +} + +const Path RectangleList::toPath() const throw() +{ + Path p; + + for (int i = rects.size(); --i >= 0;) + { + const Rectangle& r = rects.getReference (i); + + p.addRectangle ((float) r.x, + (float) r.y, + (float) r.w, + (float) r.h); + } + + return p; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_RectangleList.cpp *********/ + +/********* Start of inlined file: juce_Image.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +static const int fullAlphaThreshold = 253; + +Image::Image (const PixelFormat format_, + const int imageWidth_, + const int imageHeight_) + : format (format_), + imageWidth (imageWidth_), + imageHeight (imageHeight_), + imageData (0) +{ + jassert (format_ == RGB || format_ == ARGB || format_ == SingleChannel); + jassert (imageWidth_ > 0 && imageHeight_ > 0); // it's illegal to create a zero-sized image - the + // actual image will be at least 1x1. +} + +Image::Image (const PixelFormat format_, + const int imageWidth_, + const int imageHeight_, + const bool clearImage) + : format (format_), + imageWidth (imageWidth_), + imageHeight (imageHeight_) +{ + jassert (format_ == RGB || format_ == ARGB || format_ == SingleChannel); + jassert (imageWidth_ > 0 && imageHeight_ > 0); // it's illegal to create a zero-sized image - the + // actual image will be at least 1x1. + + pixelStride = (format == RGB) ? 3 : ((format == ARGB) ? 4 : 1); + lineStride = (pixelStride * jmax (1, imageWidth_) + 3) & ~3; + const int dataSize = lineStride * jmax (1, imageHeight_); + + imageData = (uint8*) (clearImage ? juce_calloc (dataSize) + : juce_malloc (dataSize)); +} + +Image::Image (const Image& other) + : format (other.format), + imageWidth (other.imageWidth), + imageHeight (other.imageHeight) +{ + pixelStride = (format == RGB) ? 3 : ((format == ARGB) ? 4 : 1); + lineStride = (pixelStride * jmax (1, imageWidth) + 3) & ~3; + const int dataSize = lineStride * jmax (1, imageHeight); + + imageData = (uint8*) juce_malloc (dataSize); + + int ls, ps; + const uint8* srcData = other.lockPixelDataReadOnly (0, 0, imageWidth, imageHeight, ls, ps); + setPixelData (0, 0, imageWidth, imageHeight, srcData, ls); + other.releasePixelDataReadOnly (srcData); +} + +Image::~Image() +{ + juce_free (imageData); +} + +LowLevelGraphicsContext* Image::createLowLevelContext() +{ + return new LowLevelGraphicsSoftwareRenderer (*this); +} + +uint8* Image::lockPixelDataReadWrite (int x, int y, int w, int h, int& ls, int& ps) +{ + jassert (x >= 0 && y >= 0 && w > 0 && h > 0 && x + w <= imageWidth && y + h <= imageHeight); + w = w; + h = h; + + ls = lineStride; + ps = pixelStride; + return imageData + x * pixelStride + y * lineStride; +} + +void Image::releasePixelDataReadWrite (void*) +{ +} + +const uint8* Image::lockPixelDataReadOnly (int x, int y, int w, int h, int& ls, int& ps) const +{ + jassert (x >= 0 && y >= 0 && w > 0 && h > 0 && x + w <= imageWidth && y + h <= imageHeight); + w = w; + h = h; + + ls = lineStride; + ps = pixelStride; + return imageData + x * pixelStride + y * lineStride; +} + +void Image::releasePixelDataReadOnly (const void*) const +{ +} + +void Image::setPixelData (int x, int y, int w, int h, + const uint8* sourcePixelData, int sourceLineStride) +{ + jassert (x >= 0 && y >= 0 && w > 0 && h > 0 && x + w <= imageWidth && y + h <= imageHeight); + + if (Rectangle::intersectRectangles (x, y, w, h, 0, 0, imageWidth, imageHeight)) + { + int ls, ps; + uint8* dest = lockPixelDataReadWrite (x, y, w, h, ls, ps); + + for (int i = 0; i < h; ++i) + { + memcpy (dest + ls * i, + sourcePixelData + sourceLineStride * i, + w * pixelStride); + } + + releasePixelDataReadWrite (dest); + } +} + +void Image::clear (int dx, int dy, int dw, int dh, + const Colour& colourToClearTo) +{ + const PixelARGB col (colourToClearTo.getPixelARGB()); + + int ls, ps; + uint8* dstData = lockPixelDataReadWrite (dx, dy, dw, dh, ls, ps); + uint8* dest = dstData; + + while (--dh >= 0) + { + uint8* line = dest; + dest += ls; + + if (isARGB()) + { + for (int x = dw; --x >= 0;) + { + ((PixelARGB*) line)->set (col); + line += ps; + } + } + else if (isRGB()) + { + for (int x = dw; --x >= 0;) + { + ((PixelRGB*) line)->set (col); + line += ps; + } + } + else + { + for (int x = dw; --x >= 0;) + { + *line = col.getAlpha(); + line += ps; + } + } + } + + releasePixelDataReadWrite (dstData); +} + +Image* Image::createCopy (int newWidth, int newHeight, + const Graphics::ResamplingQuality quality) const +{ + if (newWidth < 0) + newWidth = imageWidth; + + if (newHeight < 0) + newHeight = imageHeight; + + Image* const newImage = new Image (format, newWidth, newHeight, true); + + Graphics g (*newImage); + g.setImageResamplingQuality (quality); + + g.drawImage (this, + 0, 0, newWidth, newHeight, + 0, 0, imageWidth, imageHeight, + false); + + return newImage; +} + +const Colour Image::getPixelAt (const int x, const int y) const +{ + Colour c; + + if (((unsigned int) x) < (unsigned int) imageWidth + && ((unsigned int) y) < (unsigned int) imageHeight) + { + int ls, ps; + const uint8* const pixels = lockPixelDataReadOnly (x, y, 1, 1, ls, ps); + + if (isARGB()) + { + PixelARGB p (*(const PixelARGB*) pixels); + p.unpremultiply(); + c = Colour (p.getARGB()); + } + else if (isRGB()) + c = Colour (((const PixelRGB*) pixels)->getARGB()); + else + c = Colour ((uint8) 0, (uint8) 0, (uint8) 0, *pixels); + + releasePixelDataReadOnly (pixels); + } + + return c; +} + +void Image::setPixelAt (const int x, const int y, + const Colour& colour) +{ + if (((unsigned int) x) < (unsigned int) imageWidth + && ((unsigned int) y) < (unsigned int) imageHeight) + { + int ls, ps; + uint8* const pixels = lockPixelDataReadWrite (x, y, 1, 1, ls, ps); + const PixelARGB col (colour.getPixelARGB()); + + if (isARGB()) + ((PixelARGB*) pixels)->set (col); + else if (isRGB()) + ((PixelRGB*) pixels)->set (col); + else + *pixels = col.getAlpha(); + + releasePixelDataReadWrite (pixels); + } +} + +void Image::multiplyAlphaAt (const int x, const int y, + const float multiplier) +{ + if (((unsigned int) x) < (unsigned int) imageWidth + && ((unsigned int) y) < (unsigned int) imageHeight + && hasAlphaChannel()) + { + int ls, ps; + uint8* const pixels = lockPixelDataReadWrite (x, y, 1, 1, ls, ps); + + if (isARGB()) + ((PixelARGB*) pixels)->multiplyAlpha (multiplier); + else + *pixels = (uint8) (*pixels * multiplier); + + releasePixelDataReadWrite (pixels); + } +} + +void Image::multiplyAllAlphas (const float amountToMultiplyBy) +{ + if (hasAlphaChannel()) + { + int ls, ps; + uint8* const pixels = lockPixelDataReadWrite (0, 0, getWidth(), getHeight(), ls, ps); + + if (isARGB()) + { + for (int y = 0; y < imageHeight; ++y) + { + uint8* p = pixels + y * ls; + + for (int x = 0; x < imageWidth; ++x) + { + ((PixelARGB*) p)->multiplyAlpha (amountToMultiplyBy); + p += ps; + } + } + } + else + { + for (int y = 0; y < imageHeight; ++y) + { + uint8* p = pixels + y * ls; + + for (int x = 0; x < imageWidth; ++x) + { + *p = (uint8) (*p * amountToMultiplyBy); + p += ps; + } + } + } + + releasePixelDataReadWrite (pixels); + } + else + { + jassertfalse // can't do this without an alpha-channel! + } +} + +void Image::desaturate() +{ + if (isARGB() || isRGB()) + { + int ls, ps; + uint8* const pixels = lockPixelDataReadWrite (0, 0, getWidth(), getHeight(), ls, ps); + + if (isARGB()) + { + for (int y = 0; y < imageHeight; ++y) + { + uint8* p = pixels + y * ls; + + for (int x = 0; x < imageWidth; ++x) + { + ((PixelARGB*) p)->desaturate(); + p += ps; + } + } + } + else + { + for (int y = 0; y < imageHeight; ++y) + { + uint8* p = pixels + y * ls; + + for (int x = 0; x < imageWidth; ++x) + { + ((PixelRGB*) p)->desaturate(); + p += ps; + } + } + } + + releasePixelDataReadWrite (pixels); + } +} + +void Image::createSolidAreaMask (RectangleList& result, const float alphaThreshold) const +{ + if (hasAlphaChannel()) + { + const uint8 threshold = (uint8) jlimit (0, 255, roundFloatToInt (alphaThreshold * 255.0f)); + SparseSet pixelsOnRow; + + int ls, ps; + const uint8* const pixels = lockPixelDataReadOnly (0, 0, imageWidth, imageHeight, ls, ps); + + for (int y = 0; y < imageHeight; ++y) + { + pixelsOnRow.clear(); + const uint8* lineData = pixels + ls * y; + + if (isARGB()) + { + for (int x = 0; x < imageWidth; ++x) + { + if (((const PixelARGB*) lineData)->getAlpha() >= threshold) + pixelsOnRow.addRange (x, 1); + + lineData += ps; + } + } + else + { + for (int x = 0; x < imageWidth; ++x) + { + if (*lineData >= threshold) + pixelsOnRow.addRange (x, 1); + + lineData += ps; + } + } + + for (int i = 0; i < pixelsOnRow.getNumRanges(); ++i) + { + int x, w; + + if (pixelsOnRow.getRange (i, x, w)) + result.add (Rectangle (x, y, w, 1)); + } + + result.consolidate(); + } + + releasePixelDataReadOnly (pixels); + } + else + { + result.add (0, 0, imageWidth, imageHeight); + } +} + +void Image::moveImageSection (int dx, int dy, + int sx, int sy, + int w, int h) +{ + if (dx < 0) + { + w += dx; + sx -= dx; + dx = 0; + } + + if (dy < 0) + { + h += dy; + sy -= dy; + dy = 0; + } + + if (sx < 0) + { + w += sx; + dx -= sx; + sx = 0; + } + + if (sy < 0) + { + h += sy; + dy -= sy; + sy = 0; + } + + const int minX = jmin (dx, sx); + const int minY = jmin (dy, sy); + + w = jmin (w, getWidth() - jmax (sx, dx)); + h = jmin (h, getHeight() - jmax (sy, dy)); + + if (w > 0 && h > 0) + { + const int maxX = jmax (dx, sx) + w; + const int maxY = jmax (dy, sy) + h; + + int ls, ps; + uint8* const pixels = lockPixelDataReadWrite (minX, minY, maxX - minX, maxY - minY, ls, ps); + + uint8* dst = pixels + ls * (dy - minY) + ps * (dx - minX); + const uint8* src = pixels + ls * (sy - minY) + ps * (sx - minX); + + const int lineSize = ps * w; + + if (dy > sy) + { + while (--h >= 0) + { + const int offset = h * ls; + memmove (dst + offset, src + offset, lineSize); + } + } + else if (dst != src) + { + while (--h >= 0) + { + memmove (dst, src, lineSize); + dst += ls; + src += ls; + } + } + + releasePixelDataReadWrite (pixels); + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_Image.cpp *********/ + +/********* Start of inlined file: juce_ImageCache.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +struct CachedImageInfo +{ + Image* image; + int64 hashCode; + int refCount; + unsigned int releaseTime; + + juce_UseDebuggingNewOperator +}; + +static ImageCache* instance = 0; +static int cacheTimeout = 5000; + +ImageCache::ImageCache() throw() + : images (4) +{ +} + +ImageCache::~ImageCache() +{ + const ScopedLock sl (lock); + + for (int i = images.size(); --i >= 0;) + { + CachedImageInfo* const ci = (CachedImageInfo*)(images.getUnchecked(i)); + delete ci->image; + delete ci; + } + + images.clear(); + + jassert (instance == this); + instance = 0; +} + +Image* ImageCache::getFromHashCode (const int64 hashCode) +{ + if (instance != 0) + { + const ScopedLock sl (instance->lock); + + for (int i = instance->images.size(); --i >= 0;) + { + CachedImageInfo* const ci = (CachedImageInfo*) instance->images.getUnchecked(i); + + if (ci->hashCode == hashCode) + { + atomicIncrement (ci->refCount); + return ci->image; + } + } + } + + return 0; +} + +void ImageCache::addImageToCache (Image* const image, + const int64 hashCode) +{ + if (image != 0) + { + if (instance == 0) + instance = new ImageCache(); + + CachedImageInfo* const newC = new CachedImageInfo(); + newC->hashCode = hashCode; + newC->image = image; + newC->refCount = 1; + newC->releaseTime = 0; + + const ScopedLock sl (instance->lock); + instance->images.add (newC); + } +} + +void ImageCache::release (Image* const imageToRelease) +{ + if (imageToRelease != 0 && instance != 0) + { + const ScopedLock sl (instance->lock); + + for (int i = instance->images.size(); --i >= 0;) + { + CachedImageInfo* const ci = (CachedImageInfo*) instance->images.getUnchecked(i); + + if (ci->image == imageToRelease) + { + if (--(ci->refCount) == 0) + ci->releaseTime = Time::getApproximateMillisecondCounter(); + + if (! instance->isTimerRunning()) + instance->startTimer (999); + + break; + } + } + } +} + +bool ImageCache::isImageInCache (Image* const imageToLookFor) +{ + if (instance != 0) + { + const ScopedLock sl (instance->lock); + + for (int i = instance->images.size(); --i >= 0;) + if (((const CachedImageInfo*) instance->images.getUnchecked(i))->image == imageToLookFor) + return true; + } + + return false; +} + +void ImageCache::incReferenceCount (Image* const image) +{ + if (instance != 0) + { + const ScopedLock sl (instance->lock); + + for (int i = instance->images.size(); --i >= 0;) + { + CachedImageInfo* const ci = (CachedImageInfo*) instance->images.getUnchecked(i); + + if (ci->image == image) + { + ci->refCount++; + return; + } + } + } + + jassertfalse // (trying to inc the ref count of an image that's not in the cache) +} + +void ImageCache::timerCallback() +{ + int numberStillNeedingReleasing = 0; + const unsigned int now = Time::getApproximateMillisecondCounter(); + + const ScopedLock sl (lock); + + for (int i = images.size(); --i >= 0;) + { + CachedImageInfo* const ci = (CachedImageInfo*) images.getUnchecked(i); + + if (ci->refCount <= 0) + { + if (now > ci->releaseTime + cacheTimeout + || now < ci->releaseTime - 1000) + { + images.remove (i); + delete ci->image; + delete ci; + } + else + { + ++numberStillNeedingReleasing; + } + } + } + + if (numberStillNeedingReleasing == 0) + stopTimer(); +} + +Image* ImageCache::getFromFile (const File& file) +{ + const int64 hashCode = file.getFullPathName().hashCode64(); + + Image* image = getFromHashCode (hashCode); + + if (image == 0) + { + image = ImageFileFormat::loadFrom (file); + addImageToCache (image, hashCode); + } + + return image; +} + +Image* ImageCache::getFromMemory (const void* imageData, + const int dataSize) +{ + const int64 hashCode = (int64) (pointer_sized_int) imageData; + + Image* image = getFromHashCode (hashCode); + + if (image == 0) + { + image = ImageFileFormat::loadFrom (imageData, dataSize); + addImageToCache (image, hashCode); + } + + return image; +} + +void ImageCache::setCacheTimeout (const int millisecs) +{ + cacheTimeout = millisecs; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ImageCache.cpp *********/ + +/********* Start of inlined file: juce_ImageConvolutionKernel.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +ImageConvolutionKernel::ImageConvolutionKernel (const int size_) throw() + : size (size_) +{ + values = new float* [size]; + + for (int i = size; --i >= 0;) + values[i] = new float [size]; + + clear(); +} + +ImageConvolutionKernel::~ImageConvolutionKernel() throw() +{ + for (int i = size; --i >= 0;) + delete[] values[i]; + + delete[] values; +} + +void ImageConvolutionKernel::setKernelValue (const int x, + const int y, + const float value) throw() +{ + if (((unsigned int) x) < (unsigned int) size + && ((unsigned int) y) < (unsigned int) size) + { + values[x][y] = value; + } + else + { + jassertfalse + } +} + +void ImageConvolutionKernel::clear() throw() +{ + for (int y = size; --y >= 0;) + for (int x = size; --x >= 0;) + values[x][y] = 0; +} + +void ImageConvolutionKernel::setOverallSum (const float desiredTotalSum) throw() +{ + double currentTotal = 0.0; + + for (int y = size; --y >= 0;) + for (int x = size; --x >= 0;) + currentTotal += values[x][y]; + + rescaleAllValues ((float) (desiredTotalSum / currentTotal)); +} + +void ImageConvolutionKernel::rescaleAllValues (const float multiplier) throw() +{ + for (int y = size; --y >= 0;) + for (int x = size; --x >= 0;) + values[x][y] *= multiplier; +} + +void ImageConvolutionKernel::createGaussianBlur (const float radius) throw() +{ + const double radiusFactor = -1.0 / (radius * radius * 2); + const int centre = size >> 1; + + for (int y = size; --y >= 0;) + { + for (int x = size; --x >= 0;) + { + const int cx = x - centre; + const int cy = y - centre; + + values[x][y] = (float) exp (radiusFactor * (cx * cx + cy * cy)); + } + } + + setOverallSum (1.0f); +} + +void ImageConvolutionKernel::applyToImage (Image& destImage, + const Image* sourceImage, + int dx, + int dy, + int dw, + int dh) const +{ + Image* imageCreated = 0; + + if (sourceImage == 0) + { + sourceImage = imageCreated = destImage.createCopy(); + } + else + { + jassert (sourceImage->getWidth() == destImage.getWidth() + && sourceImage->getHeight() == destImage.getHeight() + && sourceImage->getFormat() == destImage.getFormat()); + + if (sourceImage->getWidth() != destImage.getWidth() + || sourceImage->getHeight() != destImage.getHeight() + || sourceImage->getFormat() != destImage.getFormat()) + return; + } + + const int imageWidth = destImage.getWidth(); + const int imageHeight = destImage.getHeight(); + + if (dx >= imageWidth || dy >= imageHeight) + return; + + if (dx + dw > imageWidth) + dw = imageWidth - dx; + + if (dy + dh > imageHeight) + dh = imageHeight - dy; + + const int dx2 = dx + dw; + const int dy2 = dy + dh; + + int lineStride, pixelStride; + uint8* pixels = destImage.lockPixelDataReadWrite (dx, dy, dw, dh, lineStride, pixelStride); + uint8* line = pixels; + + int srcLineStride, srcPixelStride; + const uint8* srcPixels = sourceImage->lockPixelDataReadOnly (0, 0, sourceImage->getWidth(), sourceImage->getHeight(), srcLineStride, srcPixelStride); + + if (pixelStride == 4) + { + for (int y = dy; y < dy2; ++y) + { + uint8* dest = line; + line += lineStride; + + for (int x = dx; x < dx2; ++x) + { + float c1 = 0; + float c2 = 0; + float c3 = 0; + float c4 = 0; + + for (int yy = 0; yy < size; ++yy) + { + const int sy = y + yy - (size >> 1); + + if (sy >= imageHeight) + break; + + if (sy >= 0) + { + int sx = x - (size >> 1); + const uint8* src = srcPixels + srcLineStride * sy + srcPixelStride * sx; + + for (int xx = 0; xx < size; ++xx) + { + if (sx >= imageWidth) + break; + + if (sx >= 0) + { + const float kernelMult = values[xx][yy]; + c1 += kernelMult * *src++; + c2 += kernelMult * *src++; + c3 += kernelMult * *src++; + c4 += kernelMult * *src++; + } + else + { + src += 4; + } + + ++sx; + } + } + } + + *dest++ = (uint8) jmin (0xff, roundFloatToInt (c1)); + *dest++ = (uint8) jmin (0xff, roundFloatToInt (c2)); + *dest++ = (uint8) jmin (0xff, roundFloatToInt (c3)); + *dest++ = (uint8) jmin (0xff, roundFloatToInt (c4)); + } + } + } + else if (pixelStride == 3) + { + for (int y = dy; y < dy2; ++y) + { + uint8* dest = line; + line += lineStride; + + for (int x = dx; x < dx2; ++x) + { + float c1 = 0; + float c2 = 0; + float c3 = 0; + + for (int yy = 0; yy < size; ++yy) + { + const int sy = y + yy - (size >> 1); + + if (sy >= imageHeight) + break; + + if (sy >= 0) + { + int sx = x - (size >> 1); + const uint8* src = srcPixels + srcLineStride * sy + srcPixelStride * sx; + + for (int xx = 0; xx < size; ++xx) + { + if (sx >= imageWidth) + break; + + if (sx >= 0) + { + const float kernelMult = values[xx][yy]; + c1 += kernelMult * *src++; + c2 += kernelMult * *src++; + c3 += kernelMult * *src++; + } + else + { + src += 3; + } + + ++sx; + } + } + } + + *dest++ = (uint8) roundFloatToInt (c1); + *dest++ = (uint8) roundFloatToInt (c2); + *dest++ = (uint8) roundFloatToInt (c3); + } + } + } + + sourceImage->releasePixelDataReadOnly (srcPixels); + destImage.releasePixelDataReadWrite (pixels); + + if (imageCreated != 0) + delete imageCreated; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ImageConvolutionKernel.cpp *********/ + +/********* Start of inlined file: juce_ImageFileFormat.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +/********* Start of inlined file: juce_GIFLoader.h *********/ +#ifndef __JUCE_GIFLOADER_JUCEHEADER__ +#define __JUCE_GIFLOADER_JUCEHEADER__ + +#ifndef DOXYGEN + +static const int maxGifCode = 1 << 12; + +/** + Used internally by ImageFileFormat - don't use this class directly in your + application. + + @see ImageFileFormat +*/ +class GIFLoader +{ +public: + GIFLoader (InputStream& in); + ~GIFLoader() throw(); + + Image* getImage() const throw() { return image; } + +private: + Image* image; + InputStream& input; + uint8 buffer [300]; + uint8 palette [256][4]; + bool dataBlockIsZero, fresh, finished; + int currentBit, lastBit, lastByteIndex; + int codeSize, setCodeSize; + int maxCode, maxCodeSize; + int firstcode, oldcode; + int clearCode, end_code; + int table [2] [maxGifCode]; + int stack [2 * maxGifCode]; + int *sp; + + bool getSizeFromHeader (int& width, int& height); + bool readPalette (const int numCols); + int readDataBlock (unsigned char* dest); + int processExtension (int type, int& transparent); + int readLZWByte (bool initialise, int input_code_size); + int getCode (int code_size, bool initialise); + bool readImage (int width, int height, + int interlace, int transparent); + + GIFLoader (const GIFLoader&); + const GIFLoader& operator= (const GIFLoader&); +}; + +#endif // DOXYGEN + +#endif // __JUCE_GIFLOADER_JUCEHEADER__ +/********* End of inlined file: juce_GIFLoader.h *********/ + +Image* juce_loadPNGImageFromStream (InputStream& inputStream) throw(); +bool juce_writePNGImageToStream (const Image& image, OutputStream& out) throw(); + +PNGImageFormat::PNGImageFormat() throw() {} +PNGImageFormat::~PNGImageFormat() throw() {} + +const String PNGImageFormat::getFormatName() +{ + return T("PNG"); +} + +bool PNGImageFormat::canUnderstand (InputStream& in) +{ + const int bytesNeeded = 4; + char header [bytesNeeded]; + + return in.read (header, bytesNeeded) == bytesNeeded + && header[1] == 'P' + && header[2] == 'N' + && header[3] == 'G'; +} + +Image* PNGImageFormat::decodeImage (InputStream& in) +{ + return juce_loadPNGImageFromStream (in); +} + +bool PNGImageFormat::writeImageToStream (const Image& sourceImage, + OutputStream& destStream) +{ + return juce_writePNGImageToStream (sourceImage, destStream); +} + +Image* juce_loadJPEGImageFromStream (InputStream& inputStream) throw(); +bool juce_writeJPEGImageToStream (const Image& image, OutputStream& out, float quality) throw(); + +JPEGImageFormat::JPEGImageFormat() throw() + : quality (-1.0f) +{ +} + +JPEGImageFormat::~JPEGImageFormat() throw() {} + +void JPEGImageFormat::setQuality (const float newQuality) +{ + quality = newQuality; +} + +const String JPEGImageFormat::getFormatName() +{ + return T("JPEG"); +} + +bool JPEGImageFormat::canUnderstand (InputStream& in) +{ + const int bytesNeeded = 10; + uint8 header [bytesNeeded]; + + if (in.read (header, bytesNeeded) == bytesNeeded) + { + return header[0] == 0xff + && header[1] == 0xd8 + && header[2] == 0xff + && (header[3] == 0xe0 || header[3] == 0xe1); + } + + return false; +} + +Image* JPEGImageFormat::decodeImage (InputStream& in) +{ + return juce_loadJPEGImageFromStream (in); +} + +bool JPEGImageFormat::writeImageToStream (const Image& sourceImage, + OutputStream& destStream) +{ + return juce_writeJPEGImageToStream (sourceImage, destStream, quality); +} + +class GIFImageFormat : public ImageFileFormat +{ +public: + GIFImageFormat() throw() {} + ~GIFImageFormat() throw() {} + + const String getFormatName() + { + return T("GIF"); + } + + bool canUnderstand (InputStream& in) + { + const int bytesNeeded = 4; + char header [bytesNeeded]; + + return (in.read (header, bytesNeeded) == bytesNeeded) + && header[0] == 'G' + && header[1] == 'I' + && header[2] == 'F'; + } + + Image* decodeImage (InputStream& in) + { + GIFLoader* const loader = new GIFLoader (in); + Image* const im = loader->getImage(); + delete loader; + return im; + } + + bool writeImageToStream (const Image& /*sourceImage*/, OutputStream& /*destStream*/) + { + return false; + } +}; + +ImageFileFormat* ImageFileFormat::findImageFormatForStream (InputStream& input) +{ + static PNGImageFormat png; + static JPEGImageFormat jpg; + static GIFImageFormat gif; + + ImageFileFormat* formats[4]; + int numFormats = 0; + + formats [numFormats++] = &png; + formats [numFormats++] = &jpg; + formats [numFormats++] = &gif; + + const int64 streamPos = input.getPosition(); + + for (int i = 0; i < numFormats; ++i) + { + const bool found = formats[i]->canUnderstand (input); + input.setPosition (streamPos); + + if (found) + return formats[i]; + } + + return 0; +} + +Image* ImageFileFormat::loadFrom (InputStream& input) +{ + ImageFileFormat* const format = findImageFormatForStream (input); + + if (format != 0) + return format->decodeImage (input); + + return 0; +} + +Image* ImageFileFormat::loadFrom (const File& file) +{ + InputStream* const in = file.createInputStream(); + + if (in != 0) + { + BufferedInputStream b (in, 8192, true); + return loadFrom (b); + } + + return 0; +} + +Image* ImageFileFormat::loadFrom (const void* rawData, const int numBytes) +{ + if (rawData != 0 && numBytes > 4) + { + MemoryInputStream stream (rawData, numBytes, false); + return loadFrom (stream); + } + + return 0; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_ImageFileFormat.cpp *********/ + +/********* Start of inlined file: juce_GIFLoader.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +static inline int makeWord (const unsigned char a, const unsigned char b) throw() +{ + return (b << 8) | a; +} + +GIFLoader::GIFLoader (InputStream& in) + : image (0), + input (in), + dataBlockIsZero (false), + fresh (false), + finished (false) +{ + currentBit = lastBit = lastByteIndex = 0; + maxCode = maxCodeSize = codeSize = setCodeSize = 0; + firstcode = oldcode = 0; + clearCode = end_code = 0; + + int imageWidth, imageHeight; + int transparent = -1; + + if (! getSizeFromHeader (imageWidth, imageHeight)) + return; + + if ((imageWidth <= 0) || (imageHeight <= 0)) + return; + + unsigned char buf [16]; + if (in.read (buf, 3) != 3) + return; + + int numColours = 2 << (buf[0] & 7); + + if ((buf[0] & 0x80) != 0) + readPalette (numColours); + + for (;;) + { + if (input.read (buf, 1) != 1) + break; + + if (buf[0] == ';') + break; + + if (buf[0] == '!') + { + if (input.read (buf, 1) != 1) + break; + + if (processExtension (buf[0], transparent) < 0) + break; + + continue; + } + + if (buf[0] != ',') + continue; + + if (input.read (buf, 9) != 9) + break; + + imageWidth = makeWord (buf[4], buf[5]); + imageHeight = makeWord (buf[6], buf[7]); + + numColours = 2 << (buf[8] & 7); + + if ((buf[8] & 0x80) != 0) + if (! readPalette (numColours)) + break; + + image = new Image ((transparent >= 0) ? Image::ARGB : Image::RGB, + imageWidth, imageHeight, (transparent >= 0)); + + readImage (imageWidth, imageHeight, + (buf[8] & 0x40) != 0, + transparent); + + break; + } +} + +GIFLoader::~GIFLoader() throw() +{ +} + +bool GIFLoader::getSizeFromHeader (int& w, int& h) +{ + unsigned char b [8]; + + if (input.read (b, 6) == 6) + { + if ((strncmp ("GIF87a", (char*) b, 6) == 0) + || (strncmp ("GIF89a", (char*) b, 6) == 0)) + { + if (input.read (b, 4) == 4) + { + w = makeWord (b[0], b[1]); + h = makeWord (b[2], b[3]); + return true; + } + } + } + + return false; +} + +bool GIFLoader::readPalette (const int numCols) +{ + unsigned char rgb[4]; + + for (int i = 0; i < numCols; ++i) + { + input.read (rgb, 3); + + palette [i][0] = rgb[0]; + palette [i][1] = rgb[1]; + palette [i][2] = rgb[2]; + palette [i][3] = 0xff; + } + + return true; +} + +int GIFLoader::readDataBlock (unsigned char* const dest) +{ + unsigned char n; + + if (input.read (&n, 1) == 1) + { + dataBlockIsZero = (n == 0); + + if (dataBlockIsZero || (input.read (dest, n) == n)) + return n; + } + + return -1; +} + +int GIFLoader::processExtension (const int type, int& transparent) +{ + unsigned char b [300]; + int n = 0; + + if (type == 0xf9) + { + n = readDataBlock (b); + if (n < 0) + return 1; + + if ((b[0] & 0x1) != 0) + transparent = b[3]; + } + + do + { + n = readDataBlock (b); + } + while (n > 0); + + return n; +} + +int GIFLoader::getCode (const int codeSize, const bool initialise) +{ + if (initialise) + { + currentBit = 0; + lastBit = 0; + finished = false; + return 0; + } + + if ((currentBit + codeSize) >= lastBit) + { + if (finished) + return -1; + + buffer[0] = buffer [lastByteIndex - 2]; + buffer[1] = buffer [lastByteIndex - 1]; + + const int n = readDataBlock (&buffer[2]); + + if (n == 0) + finished = true; + + lastByteIndex = 2 + n; + currentBit = (currentBit - lastBit) + 16; + lastBit = (2 + n) * 8 ; + } + + int result = 0; + int i = currentBit; + + for (int j = 0; j < codeSize; ++j) + { + result |= ((buffer[i >> 3] & (1 << (i & 7))) != 0) << j; + ++i; + } + + currentBit += codeSize; + + return result; +} + +int GIFLoader::readLZWByte (const bool initialise, const int inputCodeSize) +{ + int code, incode, i; + + if (initialise) + { + setCodeSize = inputCodeSize; + codeSize = setCodeSize + 1; + clearCode = 1 << setCodeSize; + end_code = clearCode + 1; + maxCodeSize = 2 * clearCode; + maxCode = clearCode + 2; + + getCode (0, true); + + fresh = true; + + for (i = 0; i < clearCode; ++i) + { + table[0][i] = 0; + table[1][i] = i; + } + + for (; i < maxGifCode; ++i) + { + table[0][i] = 0; + table[1][i] = 0; + } + + sp = stack; + + return 0; + } + else if (fresh) + { + fresh = false; + + do + { + firstcode = oldcode + = getCode (codeSize, false); + } + while (firstcode == clearCode); + + return firstcode; + } + + if (sp > stack) + return *--sp; + + while ((code = getCode (codeSize, false)) >= 0) + { + if (code == clearCode) + { + for (i = 0; i < clearCode; ++i) + { + table[0][i] = 0; + table[1][i] = i; + } + + for (; i < maxGifCode; ++i) + { + table[0][i] = 0; + table[1][i] = 0; + } + + codeSize = setCodeSize + 1; + maxCodeSize = 2 * clearCode; + maxCode = clearCode + 2; + sp = stack; + firstcode = oldcode = getCode (codeSize, false); + return firstcode; + + } + else if (code == end_code) + { + if (dataBlockIsZero) + return -2; + + unsigned char buf [260]; + + int n; + while ((n = readDataBlock (buf)) > 0) + {} + + if (n != 0) + return -2; + } + + incode = code; + + if (code >= maxCode) + { + *sp++ = firstcode; + code = oldcode; + } + + while (code >= clearCode) + { + *sp++ = table[1][code]; + if (code == table[0][code]) + return -2; + + code = table[0][code]; + } + + *sp++ = firstcode = table[1][code]; + + if ((code = maxCode) < maxGifCode) + { + table[0][code] = oldcode; + table[1][code] = firstcode; + ++maxCode; + + if ((maxCode >= maxCodeSize) + && (maxCodeSize < maxGifCode)) + { + maxCodeSize <<= 1; + ++codeSize; + } + } + + oldcode = incode; + + if (sp > stack) + return *--sp; + } + + return code; +} + +bool GIFLoader::readImage (const int width, const int height, + const int interlace, const int transparent) +{ + unsigned char c; + + if (input.read (&c, 1) != 1 + || readLZWByte (true, c) < 0) + return false; + + if (transparent >= 0) + { + palette [transparent][0] = 0; + palette [transparent][1] = 0; + palette [transparent][2] = 0; + palette [transparent][3] = 0; + } + + int index; + int xpos = 0, ypos = 0, pass = 0; + + int stride, pixelStride; + uint8* const pixels = image->lockPixelDataReadWrite (0, 0, width, height, stride, pixelStride); + uint8* p = pixels; + const bool hasAlpha = image->hasAlphaChannel(); + + while ((index = readLZWByte (false, c)) >= 0) + { + const uint8* const paletteEntry = palette [index]; + + if (hasAlpha) + { + ((PixelARGB*) p)->setARGB (paletteEntry[3], + paletteEntry[0], + paletteEntry[1], + paletteEntry[2]); + + ((PixelARGB*) p)->premultiply(); + + p += pixelStride; + } + else + { + ((PixelRGB*) p)->setARGB (0, + paletteEntry[0], + paletteEntry[1], + paletteEntry[2]); + + p += pixelStride; + } + + ++xpos; + + if (xpos == width) + { + xpos = 0; + + if (interlace) + { + switch (pass) + { + case 0: + case 1: + ypos += 8; + break; + case 2: + ypos += 4; + break; + case 3: + ypos += 2; + break; + } + + while (ypos >= height) + { + ++pass; + + switch (pass) + { + case 1: + ypos = 4; + break; + case 2: + ypos = 2; + break; + case 3: + ypos = 1; + break; + default: + return true; + } + } + } + else + { + ++ypos; + } + + p = pixels + xpos * pixelStride + ypos * stride; + } + + if (ypos >= height) + break; + } + + image->releasePixelDataReadWrite (pixels); + return true; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_GIFLoader.cpp *********/ + +#endif + +//============================================================================== +// some files include lots of library code, so leave them to the end to avoid cluttering +// up the build for the clean files. + +/********* Start of inlined file: juce_GZIPCompressorOutputStream.cpp *********/ + +namespace zlibNamespace +{ + #undef OS_CODE + #undef fdopen + +/********* Start of inlined file: zlib.h *********/ +#ifndef ZLIB_H +#define ZLIB_H + +/********* Start of inlined file: zconf.h *********/ +/* @(#) $Id: zconf.h,v 1.1 2007/06/07 17:54:37 jules_rms Exp $ */ + +#ifndef ZCONF_H +#define ZCONF_H + +// *** Just a few hacks here to make it compile nicely with Juce.. +#define Z_PREFIX 1 +#undef __MACTYPES__ + +#ifdef _MSC_VER + #pragma warning (disable : 4131 4127 4244 4267) +#endif + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + */ +#ifdef Z_PREFIX +# define deflateInit_ z_deflateInit_ +# define deflate z_deflate +# define deflateEnd z_deflateEnd +# define inflateInit_ z_inflateInit_ +# define inflate z_inflate +# define inflateEnd z_inflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateSetDictionary z_deflateSetDictionary +# define deflateCopy z_deflateCopy +# define deflateReset z_deflateReset +# define deflateParams z_deflateParams +# define deflateBound z_deflateBound +# define deflatePrime z_deflatePrime +# define inflateInit2_ z_inflateInit2_ +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateCopy z_inflateCopy +# define inflateReset z_inflateReset +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# define uncompress z_uncompress +# define adler32 z_adler32 +# define crc32 z_crc32 +# define get_crc_table z_get_crc_table +# define zError z_zError + +# define alloc_func z_alloc_func +# define free_func z_free_func +# define in_func z_in_func +# define out_func z_out_func +# define Byte z_Byte +# define uInt z_uInt +# define uLong z_uLong +# define Bytef z_Bytef +# define charf z_charf +# define intf z_intf +# define uIntf z_uIntf +# define uLongf z_uLongf +# define voidpf z_voidpf +# define voidp z_voidp +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if 0 /* HAVE_UNISTD_H -- this line is updated by ./configure */ +# include /* for off_t */ +# include /* for SEEK_* and off_t */ +# ifdef VMS +# include /* for off_t */ +# endif +# define z_off_t off_t +#endif +#ifndef SEEK_SET +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif +#ifndef z_off_t +# define z_off_t long +#endif + +#if defined(__OS400__) +# define NO_vsnprintf +#endif + +#if defined(__MVS__) +# define NO_vsnprintf +# ifdef FAR +# undef FAR +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) +# pragma map(deflateInit_,"DEIN") +# pragma map(deflateInit2_,"DEIN2") +# pragma map(deflateEnd,"DEEND") +# pragma map(deflateBound,"DEBND") +# pragma map(inflateInit_,"ININ") +# pragma map(inflateInit2_,"ININ2") +# pragma map(inflateEnd,"INEND") +# pragma map(inflateSync,"INSY") +# pragma map(inflateSetDictionary,"INSEDI") +# pragma map(compressBound,"CMBND") +# pragma map(inflate_table,"INTABL") +# pragma map(inflate_fast,"INFA") +# pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ +/********* End of inlined file: zconf.h *********/ + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.2.3" +#define ZLIB_VERNUM 0x1230 + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed + data. This version of the library supports only one compression method + (deflation) but other algorithms will be added later and will have the same + stream interface. + + Compression can be done in a single step if the buffers are large + enough (for example if an input file is mmap'ed), or can be done by + repeated calls of the compression function. In the latter case, the + application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by default by the in-memory functions is + the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped + around a deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio using the functions that start + with "gz". The gzip format is different from the zlib format. gzip is a + gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + + This library can optionally read and write gzip streams in memory as well. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never + crash even in case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total nb of input bytes read so far */ + + Bytef *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total nb of bytes output so far */ + + char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: binary or text */ + uLong adler; /* adler32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + gzip header information passed to and from zlib routines. See RFC 1952 + for more details on the meanings of these fields. +*/ +typedef struct gz_header_s { + int text; /* true if compressed data believed to be text */ + uLong time; /* modification time */ + int xflags; /* extra flags (not used when writing a gzip file) */ + int os; /* operating system */ + Bytef *extra; /* pointer to extra field or Z_NULL if none */ + uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ + uInt extra_max; /* space at extra (only when reading header) */ + Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ + uInt name_max; /* space at name (only when reading header) */ + Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ + uInt comm_max; /* space at comment (only when reading header) */ + int hcrc; /* true if there was or will be a header crc */ + int done; /* true when done reading gzip header (not used + when writing a gzip file) */ +} gz_header; + +typedef gz_header FAR *gz_headerp; + +/* + The application must update next_in and avail_in when avail_in has + dropped to zero. It must update next_out and avail_out when avail_out + has dropped to zero. The application must initialize zalloc, zfree and + opaque before calling the init function. All other fields are set by the + compression library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this + if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, + pointers returned by zalloc for objects of exactly 65536 bytes *must* + have their offset normalized to zero. The default allocation function + provided by this library ensures this (see zutil.c). To reduce memory + requirements and avoid any allocation of 64K objects, at the expense of + compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or + progress reports. After compression, total_in holds the total size of + the uncompressed data and may be saved for use in the decompressor + (particularly if the decompressor wants to decompress everything in + a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */ +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +#define Z_BLOCK 5 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative + * values are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_FIXED 4 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_TEXT 1 +#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ +#define Z_UNKNOWN 2 +/* Possible values of the data_type field (though see inflate()) */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + /* basic functions */ + +//ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is + not compatible with the zlib.h header file used by the application. + This check is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. + If zalloc and zfree are set to Z_NULL, deflateInit updates them to + use default allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at + all (the input data is simply copied a block at a time). + Z_DEFAULT_COMPRESSION requests a default compromise between speed and + compression (currently equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if level is not a valid compression level, + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). + msg is set to null if there is no error message. deflateInit does not + perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce some + output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary (in interactive applications). + Some output may be provided even if flush is not set. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating avail_in or avail_out accordingly; avail_out + should never be zero before the call. The application can consume the + compressed output when it wants, for example when the output buffer is full + (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK + and with zero avail_out, it must be called again after making room in the + output buffer because there might be more output pending. + + Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to + decide how much data to accumualte before producing output, in order to + maximize compression. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In particular + avail_in is zero after the call if enough output space has been provided + before the call.) Flushing may degrade compression for some compression + algorithms and so it should be used only when necessary. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out is greater than six to avoid repeated flush markers due to + avail_out == 0 on return. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there + was enough output space; if deflate returns with Z_OK, this function must be + called again with Z_FINISH and more output space (updated avail_out) but no + more input data, until it returns with Z_STREAM_END or an error. After + deflate has returned Z_STREAM_END, the only possible operations on the + stream are deflateReset or deflateEnd. + + Z_FINISH can be used immediately after deflateInit if all the compression + is to be done in a single step. In this case, avail_out must be at least + the value returned by deflateBound (see below). If deflate does not return + Z_STREAM_END, then it must be called again as described above. + + deflate() sets strm->adler to the adler32 checksum of all input read + so far (that is, total_in bytes). + + deflate() may update strm->data_type if it can make a good guess about + the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered + binary. This field is only for information purposes and does not affect + the compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible + (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not + fatal, and deflate() can be called again with more input and more output + space to continue compressing. +*/ + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, + msg may be set but then points to a static string (which must not be + deallocated). +*/ + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. If next_in is not Z_NULL and avail_in is large enough (the exact + value depends on the compression method), inflateInit determines the + compression method from the zlib header and allocates all data structures + accordingly; otherwise the allocation will be deferred to the first call of + inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to + use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller. msg is set to null if there is no error + message. inflateInit does not perform any decompression apart from reading + the zlib header if present: this will be done by inflate(). (So next_in and + avail_in may be modified, but next_out and avail_out are unchanged.) +*/ + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing + will resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there + is no more input data or no more space in the output buffer (see below + about the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating the next_* and avail_* values accordingly. + The application can consume the uncompressed output when it wants, for + example when the output buffer is full (avail_out == 0), or after each + call of inflate(). If inflate returns Z_OK and with zero avail_out, it + must be called again after making room in the output buffer because there + might be more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, + Z_FINISH, or Z_BLOCK. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() stop + if and when it gets to the next deflate block boundary. When decoding the + zlib or gzip format, this will cause inflate() to return immediately after + the header and before the first block. When doing a raw inflate, inflate() + will go ahead and process the first block, and will return when it gets to + the end of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + Also to assist in this, on return inflate() will set strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 + if inflate() is currently decoding the last block in the deflate stream, + plus 128 if inflate() returned immediately after decoding an end-of-block + code or decoding the complete header up to just before the first byte of the + deflate stream. The end-of-block will not be indicated until all of the + uncompressed data from that block has been written to strm->next_out. The + number of unused bits may in general be greater than seven, except when + bit 7 of data_type is set, in which case the number of unused bits will be + less than eight. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step + (a single call of inflate), the parameter flush should be set to + Z_FINISH. In this case all pending input is processed and all pending + output is flushed; avail_out must be large enough to hold all the + uncompressed data. (The size of the uncompressed data may have been saved + by the compressor for this purpose.) The next operation on this stream must + be inflateEnd to deallocate the decompression state. The use of Z_FINISH + is never required, but can be used to inform inflate that a faster approach + may be used for the single inflate() call. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the only effect of the flush parameter in this implementation + is on the return value of inflate(), as noted below, or when it returns early + because Z_BLOCK is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm->adler to the adler32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the adler32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed adler32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() will decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically. Any information + contained in the gzip header is not retained, so applications that need that + information should instead use raw inflate, see inflateInit2() below, or + inflateBack() and perform their own processing of the gzip header and + trailer. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value), Z_STREAM_ERROR if the stream structure was inconsistent (for example + if next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory, + Z_BUF_ERROR if no progress is possible or if there was not enough room in the + output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may then + call inflateSync() to look for a good compression block if a partial recovery + of the data is desired. +*/ + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by + the caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute an adler32 check value. + + windowBits can also be greater than 15 for optional gzip encoding. Add + 16 to windowBits to write a simple gzip header and trailer around the + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), + no header crc, and the operating system will be set to 255 (unknown). If a + gzip stream is being written, strm->adler is a crc32 instead of an adler32. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but + is slow and reduces compression ratio; memLevel=9 uses maximum memory + for optimal speed. The default value is 8. See zconf.h for total memory + usage as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match), or Z_RLE to limit match distances to one (run-length + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman + coding and less string matching; it is somewhat intermediate between + Z_DEFAULT and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as fast as + Z_HUFFMAN_ONLY, but give better compression for PNG image data. The strategy + parameter only affects the compression ratio but not the correctness of the + compressed output even if it is not set appropriately. Z_FIXED prevents the + use of dynamic Huffman codes, allowing for a simpler decoder for special + applications. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid + method). msg is set to null if there is no error message. deflateInit2 does + not perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. This function must be called + immediately after deflateInit, deflateInit2 or deflateReset, before any + call of deflate. The compressor and decompressor must use exactly the same + dictionary (see inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size in + deflate or deflate2. Thus the strings most likely to be useful should be + put at the end of the dictionary, not at the front. In addition, the + current implementation of deflate will use at most the window size minus + 262 bytes of the provided dictionary. + + Upon return of this function, strm->adler is set to the adler32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The adler32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) If a raw deflate was requested, then the + adler32 value is not computed and strm->adler is not set. + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if the compression method is bsort). deflateSetDictionary does not + perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and + can consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, + but does not free and reallocate all the internal compression state. + The stream will keep the same compression level and any other attributes + that may have been set by deflateInit2. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2. This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different + strategy. If the compression level is changed, the input available so far + is compressed with the old level (and may be flushed); the new level will + take effect only at the next call of deflate(). + + Before the call of deflateParams, the stream state must be set as for + a call of deflate(), since the currently available input may have to + be compressed and flushed. In particular, strm->avail_out must be non-zero. + + deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source + stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR + if strm->avail_out was zero. +*/ + +ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain)); +/* + Fine tune deflate's internal compression parameters. This should only be + used by someone who understands the algorithm used by zlib's deflate for + searching for the best matching string, and even then only by the most + fanatic optimizer trying to squeeze out the last compressed bit for their + specific input data. Read the deflate.c source code for the meaning of the + max_lazy, good_length, nice_length, and max_chain parameters. + + deflateTune() can be called after deflateInit() or deflateInit2(), and + returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. + */ + +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, + uLong sourceLen)); +/* + deflateBound() returns an upper bound on the compressed size after + deflation of sourceLen bytes. It must be called after deflateInit() + or deflateInit2(). This would be used to allocate an output buffer + for deflation in a single pass, and so would be called before deflate(). +*/ + +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + deflatePrime() inserts bits in the deflate output stream. The intent + is that this function is used to start off the deflate output with the + bits leftover from a previous deflate stream when appending to it. As such, + this function can only be used for raw deflate, and must be used before the + first deflate() call after a deflateInit2() or deflateReset(). bits must be + less than or equal to 16, and that many of the least significant bits of + value will be inserted in the output. + + deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, + gz_headerp head)); +/* + deflateSetHeader() provides gzip header information for when a gzip + stream is requested by deflateInit2(). deflateSetHeader() may be called + after deflateInit2() or deflateReset() and before the first call of + deflate(). The text, time, os, extra field, name, and comment information + in the provided gz_header structure are written to the gzip header (xflag is + ignored -- the extra flags are set according to the compression level). The + caller must assure that, if not Z_NULL, name and comment are terminated with + a zero byte, and that if extra is not Z_NULL, that extra_len bytes are + available there. If hcrc is true, a gzip header crc is included. Note that + the current versions of the command-line version of gzip (up through version + 1.3.x) do not support header crc's, and will report that it is a "multi-part + gzip file" and give up. + + If deflateSetHeader is not used, the default gzip header has text false, + the time set to zero, and os set to 255, with no extra, name, or comment + fields. The gzip header is returned to the default state by deflateReset(). + + deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an adler32 or a crc32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is + a crc32 instead of an adler32. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as a null strm). msg + is set to null if there is no error message. inflateInit2 does not perform + any decompression apart from reading the zlib header if present: this will + be done by inflate(). (So next_in and avail_in may be modified, but next_out + and avail_out are unchanged.) +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the adler32 value returned by that call of inflate. + The compressor and decompressor must use exactly the same dictionary (see + deflateSetDictionary). For raw inflate, this function can be called + immediately after inflateInit2() or inflateReset() and before any call of + inflate() to set the dictionary. The application must insure that the + dictionary that was used for compression is provided. + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect adler32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a full flush point (see above the + description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR + if no more input was provided, Z_DATA_ERROR if no flush point has been found, + or Z_STREAM_ERROR if the stream structure was inconsistent. In the success + case, the application may save the current current value of total_in which + indicates where valid compressed data was found. In the error case, the + application may repeatedly call inflateSync, providing more input each time, + until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when randomly accessing a large stream. The + first pass through the stream can periodically record the inflate state, + allowing restarting inflate at those points when randomly accessing the + stream. + + inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. + The stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + This function inserts bits in the inflate input stream. The intent is + that this function is used to start inflating at a bit position in the + middle of a byte. The provided bits will be used before any bytes are used + from next_in. This function should only be used with raw inflate, and + should be used before the first inflate() call after inflateInit2() or + inflateReset(). bits must be less than or equal to 16, and that many of the + least significant bits of value will be inserted in the input. + + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, + gz_headerp head)); +/* + inflateGetHeader() requests that gzip header information be stored in the + provided gz_header structure. inflateGetHeader() may be called after + inflateInit2() or inflateReset(), and before the first call of inflate(). + As inflate() processes the gzip stream, head->done is zero until the header + is completed, at which time head->done is set to one. If a zlib stream is + being decoded, then head->done is set to -1 to indicate that there will be + no gzip header information forthcoming. Note that Z_BLOCK can be used to + force inflate() to return immediately after header processing is complete + and before any actual data is decompressed. + + The text, time, xflags, and os fields are filled in with the gzip header + contents. hcrc is set to true if there is a header CRC. (The header CRC + was valid if done is set to one.) If extra is not Z_NULL, then extra_max + contains the maximum number of bytes to write to extra. Once done is true, + extra_len contains the actual extra field length, and extra contains the + extra field, or that field truncated if extra_max is less than extra_len. + If name is not Z_NULL, then up to name_max characters are written there, + terminated with a zero unless the length is greater than name_max. If + comment is not Z_NULL, then up to comm_max characters are written there, + terminated with a zero unless the length is greater than comm_max. When + any of extra, name, or comment are not Z_NULL and the respective field is + not present in the header, then that field is set to Z_NULL to signal its + absence. This allows the use of deflateSetHeader() with the returned + structure to duplicate the header. However if those fields are set to + allocated memory, then the application will need to save those pointers + elsewhere so that they can be eventually freed. + + If inflateGetHeader is not used, then the header information is simply + discarded. The header is always checked for validity, including the header + CRC if present. inflateReset() will reset the process to discard the header + information. The application would need to call inflateGetHeader() again to + retrieve the header from the next gzip stream. + + inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, + unsigned char FAR *window)); + + Initialize the internal stream state for decompression using inflateBack() + calls. The fields zalloc, zfree and opaque in strm must be initialized + before the call. If zalloc and zfree are Z_NULL, then the default library- + derived memory allocation routines are used. windowBits is the base two + logarithm of the window size, in the range 8..15. window is a caller + supplied buffer of that size. Except for special applications where it is + assured that deflate was used with small window sizes, windowBits must be 15 + and a 32K byte window must be supplied to be able to decompress general + deflate streams. + + See inflateBack() for the usage of these routines. + + inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of + the paramaters are invalid, Z_MEM_ERROR if the internal state could not + be allocated, or Z_VERSION_ERROR if the version of the library does not + match the version of the header file. +*/ + +typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *)); +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); + +ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc)); +/* + inflateBack() does a raw inflate with a single call using a call-back + interface for input and output. This is more efficient than inflate() for + file i/o applications in that it avoids copying between the output and the + sliding window by simply making the window itself the output buffer. This + function trusts the application to not change the output buffer passed by + the output function, at least until inflateBack() returns. + + inflateBackInit() must be called first to allocate the internal state + and to initialize the state with the user-provided window buffer. + inflateBack() may then be used multiple times to inflate a complete, raw + deflate stream with each call. inflateBackEnd() is then called to free + the allocated state. + + A raw deflate stream is one with no zlib or gzip header or trailer. + This routine would normally be used in a utility that reads zip or gzip + files and writes out uncompressed files. The utility would decode the + header and process the trailer on its own, hence this routine expects + only the raw deflate stream to decompress. This is different from the + normal behavior of inflate(), which expects either a zlib or gzip header and + trailer around the deflate stream. + + inflateBack() uses two subroutines supplied by the caller that are then + called by inflateBack() for input and output. inflateBack() calls those + routines until it reads a complete deflate stream and writes out all of the + uncompressed data, or until it encounters an error. The function's + parameters and return types are defined above in the in_func and out_func + typedefs. inflateBack() will call in(in_desc, &buf) which should return the + number of bytes of provided input, and a pointer to that input in buf. If + there is no input available, in() must return zero--buf is ignored in that + case--and inflateBack() will return a buffer error. inflateBack() will call + out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() + should return zero on success, or non-zero on failure. If out() returns + non-zero, inflateBack() will return with an error. Neither in() nor out() + are permitted to change the contents of the window provided to + inflateBackInit(), which is also the buffer that out() uses to write from. + The length written by out() will be at most the window size. Any non-zero + amount of input may be provided by in(). + + For convenience, inflateBack() can be provided input on the first call by + setting strm->next_in and strm->avail_in. If that input is exhausted, then + in() will be called. Therefore strm->next_in must be initialized before + calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called + immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in + must also be initialized, and then if strm->avail_in is not zero, input will + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + + The in_desc and out_desc parameters of inflateBack() is passed as the + first parameter of in() and out() respectively when they are called. These + descriptors can be optionally used to pass any information that the caller- + supplied in() and out() functions need to do their job. + + On return, inflateBack() will set strm->next_in and strm->avail_in to + pass back any unused input that was provided by the last in() call. The + return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR + if in() or out() returned an error, Z_DATA_ERROR if there was a format + error in the deflate stream (in which case strm->msg is set to indicate the + nature of the error), or Z_STREAM_ERROR if the stream was not properly + initialized. In the case of Z_BUF_ERROR, an input or output error can be + distinguished using strm->next_in which will be Z_NULL only if in() returned + an error. If strm->next is not Z_NULL, then the Z_BUF_ERROR was due to + out() returning non-zero. (in() will always be called before out(), so + strm->next_in is assured to be defined if out() returns non-zero.) Note + that inflateBack() cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +/* + All memory allocated by inflateBackInit() is freed. + + inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream + state was inconsistent. +*/ + +//ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +/* Return flags indicating compile-time options. + + Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: + 1.0: size of uInt + 3.2: size of uLong + 5.4: size of voidpf (pointer) + 7.6: size of z_off_t + + Compiler, assembler, and debug options: + 8: DEBUG + 9: ASMV or ASMINF -- use ASM code + 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention + 11: 0 (reserved) + + One-time table building (smaller code, but not thread-safe if true): + 12: BUILDFIXED -- build static block decoding tables when needed + 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed + 14,15: 0 (reserved) + + Library content (indicates missing functionality): + 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking + deflate code when not needed) + 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect + and decode gzip streams (to avoid linking crc code) + 18-19: 0 (reserved) + + Operation variations (changes in library functionality): + 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate + 21: FASTEST -- deflate algorithm with only one, lowest compression level + 22,23: 0 (reserved) + + The sprintf variant used by gzprintf (zero is best): + 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! + 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + + Remainder: + 27-31: 0 (reserved) + */ + + /* utility functions */ + +/* + The following utility functions are implemented on top of the + basic stream-oriented functions. To simplify the interface, some + default options are assumed (compression level and memory usage, + standard memory allocation functions). The source code of these + utility functions can easily be modified if you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be at least the value returned + by compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + This function can be used to compress a whole file at once if the + input file is mmap'ed. + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +/* + compressBound() returns an upper bound on the compressed size after + compress() or compress2() on sourceLen bytes. It would be used before + a compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + This function can be used to decompress a whole file at once if the + input file is mmap'ed. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. +*/ + +typedef voidp gzFile; + +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); +/* + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb") but can also include a compression level + ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for + Huffman only compression as in "wb1h", or 'R' for run-length encoding + as in "wb1R". (See the description of deflateInit2 for more information + about the strategy parameter.) + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. + + gzopen returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). */ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen() associates a gzFile with the file descriptor fd. File + descriptors are obtained from calls like open, dup, creat, pipe or + fileno (in the file has been previously opened with fopen). + The mode parameter is as in gzopen. + The next call of gzclose on the returned gzFile will also close the + file descriptor fd, just like fclose(fdopen(fd), mode) closes the file + descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode). + gzdopen returns NULL if there was insufficient memory to allocate + the (de)compression state. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. + gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not + opened for writing. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. + If the input file was not in gzip format, gzread copies the given number + of bytes into the buffer. + gzread returns the number of uncompressed bytes actually read (0 for + end of file, -1 for error). */ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + voidpc buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes actually written + (0 in case of error). +*/ + +ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). The number of + uncompressed bytes written is limited to 4095. The caller should assure that + this limit is not exceeded. If it is exceeded, then gzprintf() will return + return an error (0) with nothing written. In this case, there may also be a + buffer overflow with unpredictable consequences, which is possible only if + zlib was compiled with the insecure functions sprintf() or vsprintf() + because the secure snprintf() or vsnprintf() functions were not available. +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or + a newline character is read and transferred to buf, or an end-of-file + condition is encountered. The string is then terminated with a null + character. + gzgets returns buf, or Z_NULL in case of error. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ + +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +/* + Push one character back onto the stream to be read again later. + Only one character of push-back is allowed. gzungetc() returns the + character pushed, or -1 on failure. gzungetc() will fail if a + character has been pushed but not read yet, or if c is -1. The pushed + character will be discarded if the stream is repositioned with gzseek() + or gzrewind(). +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. The return value is the zlib + error number (see function gzerror below). gzflush returns Z_OK if + the flush parameter is Z_FINISH and all output could be flushed. + gzflush should be called only when strictly necessary because it can + degrade compression. +*/ + +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); +/* + Sets the starting position for the next gzread or gzwrite on the + given compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); +/* + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ + +ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +/* + Returns 1 if file is being read directly without decompression, otherwise + zero. +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. The return value is the zlib + error number (see function gzerror below). +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the + given compressed file. errnum is set to zlib error number. If an + error occurred in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ + +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +/* + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip + file that is being written concurrently. +*/ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the + compression library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is NULL, this function returns + the required initial value for the checksum. + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, + z_off_t len2)); +/* + Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 + and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for + each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running CRC-32 with the bytes buf[0..len-1] and return the + updated CRC-32. If buf is NULL, this function returns the required initial + value for the for the crc. Pre- and post-conditioning (one's complement) is + performed within this function so it shouldn't be done by the application. + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + +ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); + +/* + Combine two CRC-32 check values into one. For two sequences of bytes, + seq1 and seq2 with lengths len1 and len2, CRC-32 check values were + calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 + check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and + len2. +*/ + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size)); +#define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) +#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) +#define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, sizeof(z_stream)) + +#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) + struct internal_state {int dummy;}; /* hack for buggy compilers */ +#endif + +ZEXTERN const char * ZEXPORT zError OF((int)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp z)); +ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void)); + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ +/********* End of inlined file: zlib.h *********/ + + #undef OS_CODE +} + +BEGIN_JUCE_NAMESPACE + +using namespace zlibNamespace; + +// internal helper object that holds the zlib structures so they don't have to be +// included publicly. +class GZIPCompressorHelper +{ +private: + z_stream* stream; + uint8* data; + int dataSize, compLevel, strategy; + bool setParams; + +public: + bool finished, shouldFinish; + + GZIPCompressorHelper (const int compressionLevel, const bool nowrap) + : data (0), + dataSize (0), + compLevel (compressionLevel), + strategy (0), + setParams (true), + finished (false), + shouldFinish (false) + { + stream = (z_stream*) juce_calloc (sizeof (z_stream)); + + if (deflateInit2 (stream, + compLevel, + Z_DEFLATED, + nowrap ? -MAX_WBITS : MAX_WBITS, + 8, + strategy) != Z_OK) + { + juce_free (stream); + stream = 0; + } + } + + ~GZIPCompressorHelper() + { + if (stream != 0) + { + deflateEnd (stream); + juce_free (stream); + } + } + + bool needsInput() const throw() + { + return dataSize <= 0; + } + + void setInput (uint8* const newData, const int size) throw() + { + data = newData; + dataSize = size; + } + + int doNextBlock (uint8* const dest, const int destSize) throw() + { + if (stream != 0) + { + stream->next_in = data; + stream->next_out = dest; + stream->avail_in = dataSize; + stream->avail_out = destSize; + + const int result = setParams ? deflateParams (stream, compLevel, strategy) + : deflate (stream, shouldFinish ? Z_FINISH : Z_NO_FLUSH); + + setParams = false; + + switch (result) + { + case Z_STREAM_END: + finished = true; + + case Z_OK: + data += dataSize - stream->avail_in; + dataSize = stream->avail_in; + + return destSize - stream->avail_out; + + default: + break; + } + } + + return 0; + } +}; + +const int gzipCompBufferSize = 32768; + +GZIPCompressorOutputStream::GZIPCompressorOutputStream (OutputStream* const destStream_, + int compressionLevel, + const bool deleteDestStream_, + const bool noWrap) + : destStream (destStream_), + deleteDestStream (deleteDestStream_) +{ + if (compressionLevel < 1 || compressionLevel > 9) + compressionLevel = -1; + + helper = new GZIPCompressorHelper (compressionLevel, noWrap); + + buffer = (uint8*) juce_malloc (gzipCompBufferSize); +} + +GZIPCompressorOutputStream::~GZIPCompressorOutputStream() +{ + flush(); + + GZIPCompressorHelper* const h = (GZIPCompressorHelper*) helper; + delete h; + + juce_free (buffer); + + if (deleteDestStream) + delete destStream; +} + +void GZIPCompressorOutputStream::flush() +{ + GZIPCompressorHelper* const h = (GZIPCompressorHelper*) helper; + + if (! h->finished) + { + h->shouldFinish = true; + + while (! h->finished) + doNextBlock(); + } + + destStream->flush(); +} + +bool GZIPCompressorOutputStream::write (const void* destBuffer, int howMany) +{ + GZIPCompressorHelper* const h = (GZIPCompressorHelper*) helper; + + if (! h->finished) + { + h->setInput ((uint8*) destBuffer, howMany); + + while (! h->needsInput()) + { + if (! doNextBlock()) + return false; + } + } + + return true; +} + +bool GZIPCompressorOutputStream::doNextBlock() +{ + GZIPCompressorHelper* const h = (GZIPCompressorHelper*) helper; + const int len = h->doNextBlock (buffer, gzipCompBufferSize); + + if (len > 0) + return destStream->write (buffer, len); + else + return true; +} + +int64 GZIPCompressorOutputStream::getPosition() +{ + return destStream->getPosition(); +} + +bool GZIPCompressorOutputStream::setPosition (int64 /*newPosition*/) +{ + jassertfalse // can't do it! + return false; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_GZIPCompressorOutputStream.cpp *********/ + +/********* Start of inlined file: juce_GZIPDecompressorInputStream.cpp *********/ + +#if JUCE_MSVC + #pragma warning (push) + #pragma warning (disable: 4309 4305) +#endif + +namespace zlibNamespace +{ + extern "C" + { + #undef OS_CODE + #undef fdopen + #define ZLIB_INTERNAL + #define NO_DUMMY_DECL + +/********* Start of inlined file: adler32.c *********/ +/* @(#) $Id: adler32.c,v 1.1 2007/06/07 17:54:37 jules_rms Exp $ */ + +#define ZLIB_INTERNAL + +#define BASE 65521UL /* largest prime smaller than 65536 */ +#define NMAX 5552 +/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + +#define DO1(buf,i) {adler += (buf)[i]; sum2 += adler;} +#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); +#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); +#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); +#define DO16(buf) DO8(buf,0); DO8(buf,8); + +/* use NO_DIVIDE if your processor does not do division in hardware */ +#ifdef NO_DIVIDE +# define MOD(a) \ + do { \ + if (a >= (BASE << 16)) a -= (BASE << 16); \ + if (a >= (BASE << 15)) a -= (BASE << 15); \ + if (a >= (BASE << 14)) a -= (BASE << 14); \ + if (a >= (BASE << 13)) a -= (BASE << 13); \ + if (a >= (BASE << 12)) a -= (BASE << 12); \ + if (a >= (BASE << 11)) a -= (BASE << 11); \ + if (a >= (BASE << 10)) a -= (BASE << 10); \ + if (a >= (BASE << 9)) a -= (BASE << 9); \ + if (a >= (BASE << 8)) a -= (BASE << 8); \ + if (a >= (BASE << 7)) a -= (BASE << 7); \ + if (a >= (BASE << 6)) a -= (BASE << 6); \ + if (a >= (BASE << 5)) a -= (BASE << 5); \ + if (a >= (BASE << 4)) a -= (BASE << 4); \ + if (a >= (BASE << 3)) a -= (BASE << 3); \ + if (a >= (BASE << 2)) a -= (BASE << 2); \ + if (a >= (BASE << 1)) a -= (BASE << 1); \ + if (a >= BASE) a -= BASE; \ + } while (0) +# define MOD4(a) \ + do { \ + if (a >= (BASE << 4)) a -= (BASE << 4); \ + if (a >= (BASE << 3)) a -= (BASE << 3); \ + if (a >= (BASE << 2)) a -= (BASE << 2); \ + if (a >= (BASE << 1)) a -= (BASE << 1); \ + if (a >= BASE) a -= BASE; \ + } while (0) +#else +# define MOD(a) a %= BASE +# define MOD4(a) a %= BASE +#endif + +/* ========================================================================= */ +uLong ZEXPORT adler32(uLong adler, const Bytef *buf, uInt len) +{ + unsigned long sum2; + unsigned n; + + /* split Adler-32 into component sums */ + sum2 = (adler >> 16) & 0xffff; + adler &= 0xffff; + + /* in case user likes doing a byte at a time, keep it fast */ + if (len == 1) { + adler += buf[0]; + if (adler >= BASE) + adler -= BASE; + sum2 += adler; + if (sum2 >= BASE) + sum2 -= BASE; + return adler | (sum2 << 16); + } + + /* initial Adler-32 value (deferred check for len == 1 speed) */ + if (buf == Z_NULL) + return 1L; + + /* in case short lengths are provided, keep it somewhat fast */ + if (len < 16) { + while (len--) { + adler += *buf++; + sum2 += adler; + } + if (adler >= BASE) + adler -= BASE; + MOD4(sum2); /* only added so many BASE's */ + return adler | (sum2 << 16); + } + + /* do length NMAX blocks -- requires just one modulo operation */ + while (len >= NMAX) { + len -= NMAX; + n = NMAX / 16; /* NMAX is divisible by 16 */ + do { + DO16(buf); /* 16 sums unrolled */ + buf += 16; + } while (--n); + MOD(adler); + MOD(sum2); + } + + /* do remaining bytes (less than NMAX, still just one modulo) */ + if (len) { /* avoid modulos if none remaining */ + while (len >= 16) { + len -= 16; + DO16(buf); + buf += 16; + } + while (len--) { + adler += *buf++; + sum2 += adler; + } + MOD(adler); + MOD(sum2); + } + + /* return recombined sums */ + return adler | (sum2 << 16); +} + +/* ========================================================================= */ +uLong ZEXPORT adler32_combine(uLong adler1, uLong adler2, z_off_t len2) +{ + unsigned long sum1; + unsigned long sum2; + unsigned rem; + + /* the derivation of this formula is left as an exercise for the reader */ + rem = (unsigned)(len2 % BASE); + sum1 = adler1 & 0xffff; + sum2 = rem * sum1; + MOD(sum2); + sum1 += (adler2 & 0xffff) + BASE - 1; + sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem; + if (sum1 > BASE) sum1 -= BASE; + if (sum1 > BASE) sum1 -= BASE; + if (sum2 > (BASE << 1)) sum2 -= (BASE << 1); + if (sum2 > BASE) sum2 -= BASE; + return sum1 | (sum2 << 16); +} +/********* End of inlined file: adler32.c *********/ + +/********* Start of inlined file: compress.c *********/ +/* @(#) $Id: compress.c,v 1.1 2007/06/07 17:54:37 jules_rms Exp $ */ + +#define ZLIB_INTERNAL + +/* =========================================================================== + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least 0.1% larger than sourceLen plus + 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ +int ZEXPORT compress2 (Bytef *dest, uLongf *destLen, const Bytef *source, + uLong sourceLen, int level) +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; +#ifdef MAXSEG_64K + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; +#endif + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + stream.opaque = (voidpf)0; + + err = deflateInit(&stream, level); + if (err != Z_OK) return err; + + err = deflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + deflateEnd(&stream); + return err == Z_OK ? Z_BUF_ERROR : err; + } + *destLen = stream.total_out; + + err = deflateEnd(&stream); + return err; +} + +/* =========================================================================== + */ +int ZEXPORT compress (Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen) +{ + return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION); +} + +/* =========================================================================== + If the default memLevel or windowBits for deflateInit() is changed, then + this function needs to be updated. + */ +uLong ZEXPORT compressBound (uLong sourceLen) +{ + return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + 11; +} +/********* End of inlined file: compress.c *********/ + + #undef DO1 + #undef DO8 + +/********* Start of inlined file: crc32.c *********/ +/* @(#) $Id: crc32.c,v 1.1 2007/06/07 17:54:37 jules_rms Exp $ */ + +/* + Note on the use of DYNAMIC_CRC_TABLE: there is no mutex or semaphore + protection on the static variables used to control the first-use generation + of the crc tables. Therefore, if you #define DYNAMIC_CRC_TABLE, you should + first call get_crc_table() to initialize the tables before allowing more than + one thread to use crc32(). + */ + +#ifdef MAKECRCH +# include +# ifndef DYNAMIC_CRC_TABLE +# define DYNAMIC_CRC_TABLE +# endif /* !DYNAMIC_CRC_TABLE */ +#endif /* MAKECRCH */ + +/********* Start of inlined file: zutil.h *********/ +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id: zutil.h,v 1.1 2007/06/07 17:54:37 jules_rms Exp $ */ + +#ifndef ZUTIL_H +#define ZUTIL_H + +#define ZLIB_INTERNAL + +#ifdef STDC +# ifndef _WIN32_WCE +# include +# endif +# include +# include +#endif +#ifdef NO_ERRNO_H +# ifdef _WIN32_WCE + /* The Microsoft C Run-Time Library for Windows CE doesn't have + * errno. We define it as a global variable to simplify porting. + * Its value is always 0 and should not be used. We rename it to + * avoid conflict with other libraries that use the same workaround. + */ +# define errno z_errno +# endif + extern int errno; +#else +# ifndef _WIN32_WCE +# include +# endif +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +typedef unsigned char uch; +typedef uch FAR uchf; +typedef unsigned short ush; +typedef ush FAR ushf; +typedef unsigned long ulg; + +extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ +/* (size given to avoid silly warnings with Visual C++) */ + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = (char*)ERR_MSG(err), (err)) +/* To be used only when the state is known to be valid */ + + /* common constants */ + +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ + + /* target dependencies */ + +#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32)) +# define OS_CODE 0x00 +# if defined(__TURBOC__) || defined(__BORLANDC__) +# if(__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) + /* Allow compilation with ANSI keywords only enabled */ + void _Cdecl farfree( void *block ); + void *_Cdecl farmalloc( unsigned long nbytes ); +# else +# include +# endif +# else /* MSC or DJGPP */ +# include +# endif +#endif + +#ifdef AMIGA +# define OS_CODE 0x01 +#endif + +#if defined(VAXC) || defined(VMS) +# define OS_CODE 0x02 +# define F_OPEN(name, mode) \ + fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") +#endif + +#if defined(ATARI) || defined(atarist) +# define OS_CODE 0x05 +#endif + +#ifdef OS2 +# define OS_CODE 0x06 +# ifdef M_I86 + #include +# endif +#endif + +#if defined(MACOS) || TARGET_OS_MAC +# define OS_CODE 0x07 +# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os +# include /* for fdopen */ +# else +# ifndef fdopen +# define fdopen(fd,mode) NULL /* No fdopen() */ +# endif +# endif +#endif + +#ifdef TOPS20 +# define OS_CODE 0x0a +#endif + +#ifdef WIN32 +# ifndef __CYGWIN__ /* Cygwin is Unix, not Win32 */ +# define OS_CODE 0x0b +# endif +#endif + +#ifdef __50SERIES /* Prime/PRIMOS */ +# define OS_CODE 0x0f +#endif + +#if defined(_BEOS_) || defined(RISCOS) +# define fdopen(fd,mode) NULL /* No fdopen() */ +#endif + +#if (defined(_MSC_VER) && (_MSC_VER > 600)) +# if defined(_WIN32_WCE) +# define fdopen(fd,mode) NULL /* No fdopen() */ +# ifndef _PTRDIFF_T_DEFINED + typedef int ptrdiff_t; +# define _PTRDIFF_T_DEFINED +# endif +# else +# define fdopen(fd,type) _fdopen(fd,type) +# endif +#endif + + /* common defaults */ + +#ifndef OS_CODE +# define OS_CODE 0x03 /* assume Unix */ +#endif + +#ifndef F_OPEN +# define F_OPEN(name, mode) fopen((name), (mode)) +#endif + + /* functions */ + +#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#if defined(__CYGWIN__) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#ifndef HAVE_VSNPRINTF +# ifdef MSDOS + /* vsnprintf may exist on some MS-DOS compilers (DJGPP?), + but for now we just assume it doesn't. */ +# define NO_vsnprintf +# endif +# ifdef __TURBOC__ +# define NO_vsnprintf +# endif +# ifdef WIN32 + /* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */ +# if !defined(vsnprintf) && !defined(NO_vsnprintf) +# define vsnprintf _vsnprintf +# endif +# endif +# ifdef __SASC +# define NO_vsnprintf +# endif +#endif +#ifdef VMS +# define NO_vsnprintf +#endif + +#if defined(pyr) +# define NO_MEMCPY +#endif +#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) + /* Use our own functions for small and medium model with MSC <= 5.0. + * You may have to use the same strategy for Borland C (untested). + * The __SC__ check is for Symantec. + */ +# define NO_MEMCPY +#endif +#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) +# define HAVE_MEMCPY +#endif +#ifdef HAVE_MEMCPY +# ifdef SMALL_MEDIUM /* MSDOS small or medium model */ +# define zmemcpy _fmemcpy +# define zmemcmp _fmemcmp +# define zmemzero(dest, len) _fmemset(dest, 0, len) +# else +# define zmemcpy memcpy +# define zmemcmp memcmp +# define zmemzero(dest, len) memset(dest, 0, len) +# endif +#else + extern void zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); + extern int zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); + extern void zmemzero OF((Bytef* dest, uInt len)); +#endif + +/* Diagnostic functions */ +#ifdef DEBUG +# include + extern int z_verbose; + extern void z_error OF((char *m)); +# define Assert(cond,msg) {if(!(cond)) z_error(msg);} +# define Trace(x) {if (z_verbose>=0) fprintf x ;} +# define Tracev(x) {if (z_verbose>0) fprintf x ;} +# define Tracevv(x) {if (z_verbose>1) fprintf x ;} +# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + +voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size)); +void zcfree OF((voidpf opaque, voidpf ptr)); + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + +#endif /* ZUTIL_H */ +/********* End of inlined file: zutil.h *********/ + + /* for STDC and FAR definitions */ + +#define local static + +/* Find a four-byte integer type for crc32_little() and crc32_big(). */ +#ifndef NOBYFOUR +# ifdef STDC /* need ANSI C limits.h to determine sizes */ +# include +# define BYFOUR +# if (UINT_MAX == 0xffffffffUL) + typedef unsigned int u4; +# else +# if (ULONG_MAX == 0xffffffffUL) + typedef unsigned long u4; +# else +# if (USHRT_MAX == 0xffffffffUL) + typedef unsigned short u4; +# else +# undef BYFOUR /* can't find a four-byte integer type! */ +# endif +# endif +# endif +# endif /* STDC */ +#endif /* !NOBYFOUR */ + +/* Definitions for doing the crc four data bytes at a time. */ +#ifdef BYFOUR +# define REV(w) (((w)>>24)+(((w)>>8)&0xff00)+ \ + (((w)&0xff00)<<8)+(((w)&0xff)<<24)) + local unsigned long crc32_little OF((unsigned long, + const unsigned char FAR *, unsigned)); + local unsigned long crc32_big OF((unsigned long, + const unsigned char FAR *, unsigned)); +# define TBLS 8 +#else +# define TBLS 1 +#endif /* BYFOUR */ + +/* Local functions for crc concatenation */ +local unsigned long gf2_matrix_times OF((unsigned long *mat, + unsigned long vec)); +local void gf2_matrix_square OF((unsigned long *square, unsigned long *mat)); + +#ifdef DYNAMIC_CRC_TABLE + +local volatile int crc_table_empty = 1; +local unsigned long FAR crc_table[TBLS][256]; +local void make_crc_table OF((void)); +#ifdef MAKECRCH + local void write_table OF((FILE *, const unsigned long FAR *)); +#endif /* MAKECRCH */ +/* + Generate tables for a byte-wise 32-bit CRC calculation on the polynomial: + x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. + + Polynomials over GF(2) are represented in binary, one bit per coefficient, + with the lowest powers in the most significant bit. Then adding polynomials + is just exclusive-or, and multiplying a polynomial by x is a right shift by + one. If we call the above polynomial p, and represent a byte as the + polynomial q, also with the lowest power in the most significant bit (so the + byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, + where a mod b means the remainder after dividing a by b. + + This calculation is done using the shift-register method of multiplying and + taking the remainder. The register is initialized to zero, and for each + incoming bit, x^32 is added mod p to the register if the bit is a one (where + x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by + x (which is shifting right by one and adding x^32 mod p if the bit shifted + out is a one). We start with the highest power (least significant bit) of + q and repeat for all eight bits of q. + + The first table is simply the CRC of all possible eight bit values. This is + all the information needed to generate CRCs on data a byte at a time for all + combinations of CRC register values and incoming bytes. The remaining tables + allow for word-at-a-time CRC calculation for both big-endian and little- + endian machines, where a word is four bytes. +*/ +local void make_crc_table() +{ + unsigned long c; + int n, k; + unsigned long poly; /* polynomial exclusive-or pattern */ + /* terms of polynomial defining this crc (except x^32): */ + static volatile int first = 1; /* flag to limit concurrent making */ + static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; + + /* See if another task is already doing this (not thread-safe, but better + than nothing -- significantly reduces duration of vulnerability in + case the advice about DYNAMIC_CRC_TABLE is ignored) */ + if (first) { + first = 0; + + /* make exclusive-or pattern from polynomial (0xedb88320UL) */ + poly = 0UL; + for (n = 0; n < sizeof(p)/sizeof(unsigned char); n++) + poly |= 1UL << (31 - p[n]); + + /* generate a crc for every 8-bit value */ + for (n = 0; n < 256; n++) { + c = (unsigned long)n; + for (k = 0; k < 8; k++) + c = c & 1 ? poly ^ (c >> 1) : c >> 1; + crc_table[0][n] = c; + } + +#ifdef BYFOUR + /* generate crc for each value followed by one, two, and three zeros, + and then the byte reversal of those as well as the first table */ + for (n = 0; n < 256; n++) { + c = crc_table[0][n]; + crc_table[4][n] = REV(c); + for (k = 1; k < 4; k++) { + c = crc_table[0][c & 0xff] ^ (c >> 8); + crc_table[k][n] = c; + crc_table[k + 4][n] = REV(c); + } + } +#endif /* BYFOUR */ + + crc_table_empty = 0; + } + else { /* not first */ + /* wait for the other guy to finish (not efficient, but rare) */ + while (crc_table_empty) + ; + } + +#ifdef MAKECRCH + /* write out CRC tables to crc32.h */ + { + FILE *out; + + out = fopen("crc32.h", "w"); + if (out == NULL) return; + fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n"); + fprintf(out, " * Generated automatically by crc32.c\n */\n\n"); + fprintf(out, "local const unsigned long FAR "); + fprintf(out, "crc_table[TBLS][256] =\n{\n {\n"); + write_table(out, crc_table[0]); +# ifdef BYFOUR + fprintf(out, "#ifdef BYFOUR\n"); + for (k = 1; k < 8; k++) { + fprintf(out, " },\n {\n"); + write_table(out, crc_table[k]); + } + fprintf(out, "#endif\n"); +# endif /* BYFOUR */ + fprintf(out, " }\n};\n"); + fclose(out); + } +#endif /* MAKECRCH */ +} + +#ifdef MAKECRCH +local void write_table(out, table) + FILE *out; + const unsigned long FAR *table; +{ + int n; + + for (n = 0; n < 256; n++) + fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ", table[n], + n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", ")); +} +#endif /* MAKECRCH */ + +#else /* !DYNAMIC_CRC_TABLE */ +/* ======================================================================== + * Tables of CRC-32s of all single-byte values, made by make_crc_table(). + */ + +/********* Start of inlined file: crc32.h *********/ +local const unsigned long FAR crc_table[TBLS][256] = +{ + { + 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL, + 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL, + 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL, + 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL, + 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL, + 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL, + 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL, + 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL, + 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL, + 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL, + 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL, + 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL, + 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL, + 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL, + 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL, + 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL, + 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL, + 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL, + 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL, + 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL, + 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL, + 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL, + 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL, + 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL, + 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL, + 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL, + 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL, + 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL, + 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL, + 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL, + 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL, + 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL, + 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL, + 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL, + 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL, + 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL, + 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL, + 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL, + 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL, + 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL, + 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL, + 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL, + 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL, + 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL, + 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL, + 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL, + 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL, + 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL, + 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL, + 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL, + 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL, + 0x2d02ef8dUL +#ifdef BYFOUR + }, + { + 0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL, + 0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL, + 0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL, + 0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL, + 0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL, + 0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL, + 0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL, + 0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL, + 0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL, + 0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL, + 0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL, + 0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL, + 0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL, + 0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL, + 0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL, + 0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL, + 0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL, + 0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL, + 0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL, + 0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL, + 0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL, + 0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL, + 0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL, + 0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL, + 0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL, + 0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL, + 0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL, + 0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL, + 0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL, + 0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL, + 0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL, + 0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL, + 0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL, + 0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL, + 0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL, + 0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL, + 0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL, + 0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL, + 0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL, + 0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL, + 0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL, + 0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL, + 0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL, + 0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL, + 0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL, + 0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL, + 0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL, + 0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL, + 0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL, + 0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL, + 0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL, + 0x9324fd72UL + }, + { + 0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL, + 0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL, + 0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL, + 0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL, + 0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL, + 0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL, + 0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL, + 0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL, + 0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL, + 0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL, + 0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL, + 0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL, + 0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL, + 0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL, + 0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL, + 0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL, + 0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL, + 0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL, + 0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL, + 0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL, + 0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL, + 0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL, + 0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL, + 0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL, + 0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL, + 0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL, + 0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL, + 0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL, + 0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL, + 0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL, + 0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL, + 0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL, + 0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL, + 0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL, + 0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL, + 0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL, + 0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL, + 0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL, + 0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL, + 0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL, + 0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL, + 0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL, + 0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL, + 0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL, + 0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL, + 0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL, + 0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL, + 0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL, + 0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL, + 0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL, + 0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL, + 0xbe9834edUL + }, + { + 0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL, + 0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL, + 0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL, + 0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL, + 0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL, + 0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL, + 0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL, + 0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL, + 0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL, + 0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL, + 0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL, + 0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL, + 0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL, + 0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL, + 0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL, + 0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL, + 0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL, + 0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL, + 0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL, + 0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL, + 0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL, + 0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL, + 0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL, + 0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL, + 0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL, + 0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL, + 0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL, + 0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL, + 0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL, + 0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL, + 0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL, + 0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL, + 0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL, + 0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL, + 0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL, + 0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL, + 0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL, + 0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL, + 0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL, + 0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL, + 0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL, + 0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL, + 0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL, + 0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL, + 0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL, + 0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL, + 0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL, + 0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL, + 0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL, + 0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL, + 0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL, + 0xde0506f1UL + }, + { + 0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL, + 0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL, + 0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL, + 0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL, + 0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL, + 0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL, + 0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL, + 0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL, + 0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL, + 0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL, + 0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL, + 0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL, + 0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL, + 0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL, + 0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL, + 0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL, + 0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL, + 0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL, + 0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL, + 0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL, + 0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL, + 0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL, + 0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL, + 0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL, + 0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL, + 0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL, + 0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL, + 0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL, + 0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL, + 0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL, + 0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL, + 0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL, + 0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL, + 0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL, + 0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL, + 0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL, + 0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL, + 0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL, + 0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL, + 0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL, + 0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL, + 0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL, + 0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL, + 0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL, + 0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL, + 0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL, + 0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL, + 0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL, + 0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL, + 0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL, + 0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL, + 0x8def022dUL + }, + { + 0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL, + 0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL, + 0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL, + 0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL, + 0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL, + 0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL, + 0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL, + 0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL, + 0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL, + 0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL, + 0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL, + 0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL, + 0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL, + 0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL, + 0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL, + 0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL, + 0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL, + 0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL, + 0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL, + 0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL, + 0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL, + 0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL, + 0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL, + 0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL, + 0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL, + 0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL, + 0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL, + 0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL, + 0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL, + 0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL, + 0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL, + 0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL, + 0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL, + 0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL, + 0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL, + 0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL, + 0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL, + 0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL, + 0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL, + 0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL, + 0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL, + 0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL, + 0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL, + 0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL, + 0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL, + 0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL, + 0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL, + 0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL, + 0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL, + 0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL, + 0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL, + 0x72fd2493UL + }, + { + 0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL, + 0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL, + 0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL, + 0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL, + 0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL, + 0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL, + 0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL, + 0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL, + 0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL, + 0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL, + 0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL, + 0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL, + 0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL, + 0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL, + 0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL, + 0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL, + 0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL, + 0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL, + 0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL, + 0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL, + 0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL, + 0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL, + 0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL, + 0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL, + 0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL, + 0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL, + 0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL, + 0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL, + 0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL, + 0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL, + 0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL, + 0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL, + 0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL, + 0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL, + 0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL, + 0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL, + 0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL, + 0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL, + 0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL, + 0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL, + 0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL, + 0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL, + 0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL, + 0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL, + 0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL, + 0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL, + 0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL, + 0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL, + 0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL, + 0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL, + 0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL, + 0xed3498beUL + }, + { + 0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL, + 0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL, + 0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL, + 0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL, + 0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL, + 0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL, + 0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL, + 0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL, + 0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL, + 0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL, + 0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL, + 0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL, + 0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL, + 0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL, + 0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL, + 0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL, + 0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL, + 0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL, + 0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL, + 0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL, + 0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL, + 0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL, + 0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL, + 0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL, + 0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL, + 0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL, + 0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL, + 0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL, + 0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL, + 0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL, + 0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL, + 0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL, + 0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL, + 0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL, + 0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL, + 0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL, + 0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL, + 0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL, + 0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL, + 0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL, + 0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL, + 0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL, + 0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL, + 0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL, + 0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL, + 0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL, + 0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL, + 0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL, + 0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL, + 0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL, + 0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL, + 0xf10605deUL +#endif + } +}; +/********* End of inlined file: crc32.h *********/ + +#endif /* DYNAMIC_CRC_TABLE */ + +/* ========================================================================= + * This function can be used by asm versions of crc32() + */ +const unsigned long FAR * ZEXPORT get_crc_table() +{ +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + return (const unsigned long FAR *)crc_table; +} + +/* ========================================================================= */ +#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8) +#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1 + +/* ========================================================================= */ +unsigned long ZEXPORT crc32 (unsigned long crc, const unsigned char FAR *buf, unsigned len) +{ + if (buf == Z_NULL) return 0UL; + +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + +#ifdef BYFOUR + if (sizeof(void *) == sizeof(ptrdiff_t)) { + u4 endian; + + endian = 1; + if (*((unsigned char *)(&endian))) + return crc32_little(crc, buf, len); + else + return crc32_big(crc, buf, len); + } +#endif /* BYFOUR */ + crc = crc ^ 0xffffffffUL; + while (len >= 8) { + DO8; + len -= 8; + } + if (len) do { + DO1; + } while (--len); + return crc ^ 0xffffffffUL; +} + +#ifdef BYFOUR + +/* ========================================================================= */ +#define DOLIT4 c ^= *buf4++; \ + c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \ + crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24] +#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4 + +/* ========================================================================= */ +local unsigned long crc32_little(unsigned long crc, const unsigned char FAR *buf, unsigned len) +{ + register u4 c; + register const u4 FAR *buf4; + + c = (u4)crc; + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + len--; + } + + buf4 = (const u4 FAR *)(const void FAR *)buf; + while (len >= 32) { + DOLIT32; + len -= 32; + } + while (len >= 4) { + DOLIT4; + len -= 4; + } + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + } while (--len); + c = ~c; + return (unsigned long)c; +} + +/* ========================================================================= */ +#define DOBIG4 c ^= *++buf4; \ + c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \ + crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24] +#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4 + +/* ========================================================================= */ +local unsigned long crc32_big (unsigned long crc, const unsigned char FAR *buf, unsigned len) +{ + register u4 c; + register const u4 FAR *buf4; + + c = REV((u4)crc); + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + len--; + } + + buf4 = (const u4 FAR *)(const void FAR *)buf; + buf4--; + while (len >= 32) { + DOBIG32; + len -= 32; + } + while (len >= 4) { + DOBIG4; + len -= 4; + } + buf4++; + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + } while (--len); + c = ~c; + return (unsigned long)(REV(c)); +} + +#endif /* BYFOUR */ + +#define GF2_DIM 32 /* dimension of GF(2) vectors (length of CRC) */ + +/* ========================================================================= */ +local unsigned long gf2_matrix_times (unsigned long *mat, unsigned long vec) +{ + unsigned long sum; + + sum = 0; + while (vec) { + if (vec & 1) + sum ^= *mat; + vec >>= 1; + mat++; + } + return sum; +} + +/* ========================================================================= */ +local void gf2_matrix_square (unsigned long *square, unsigned long *mat) +{ + int n; + + for (n = 0; n < GF2_DIM; n++) + square[n] = gf2_matrix_times(mat, mat[n]); +} + +/* ========================================================================= */ +uLong ZEXPORT crc32_combine (uLong crc1, uLong crc2, z_off_t len2) +{ + int n; + unsigned long row; + unsigned long even[GF2_DIM]; /* even-power-of-two zeros operator */ + unsigned long odd[GF2_DIM]; /* odd-power-of-two zeros operator */ + + /* degenerate case */ + if (len2 == 0) + return crc1; + + /* put operator for one zero bit in odd */ + odd[0] = 0xedb88320L; /* CRC-32 polynomial */ + row = 1; + for (n = 1; n < GF2_DIM; n++) { + odd[n] = row; + row <<= 1; + } + + /* put operator for two zero bits in even */ + gf2_matrix_square(even, odd); + + /* put operator for four zero bits in odd */ + gf2_matrix_square(odd, even); + + /* apply len2 zeros to crc1 (first square will put the operator for one + zero byte, eight zero bits, in even) */ + do { + /* apply zeros operator for this bit of len2 */ + gf2_matrix_square(even, odd); + if (len2 & 1) + crc1 = gf2_matrix_times(even, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + if (len2 == 0) + break; + + /* another iteration of the loop with odd and even swapped */ + gf2_matrix_square(odd, even); + if (len2 & 1) + crc1 = gf2_matrix_times(odd, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + } while (len2 != 0); + + /* return combined crc */ + crc1 ^= crc2; + return crc1; +} +/********* End of inlined file: crc32.c *********/ + +/********* Start of inlined file: deflate.c *********/ +/* + * ALGORITHM + * + * The "deflation" process depends on being able to identify portions + * of the input text which are identical to earlier input (within a + * sliding window trailing behind the input currently being processed). + * + * The most straightforward technique turns out to be the fastest for + * most input files: try all possible matches and select the longest. + * The key feature of this algorithm is that insertions into the string + * dictionary are very simple and thus fast, and deletions are avoided + * completely. Insertions are performed at each input character, whereas + * string matches are performed only when the previous match ends. So it + * is preferable to spend more time in matches to allow very fast string + * insertions and avoid deletions. The matching algorithm for small + * strings is inspired from that of Rabin & Karp. A brute force approach + * is used to find longer strings when a small match has been found. + * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze + * (by Leonid Broukhis). + * A previous version of this file used a more sophisticated algorithm + * (by Fiala and Greene) which is guaranteed to run in linear amortized + * time, but has a larger average cost, uses more memory and is patented. + * However the F&G algorithm may be faster for some highly redundant + * files if the parameter max_chain_length (described below) is too large. + * + * ACKNOWLEDGEMENTS + * + * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and + * I found it in 'freeze' written by Leonid Broukhis. + * Thanks to many people for bug reports and testing. + * + * REFERENCES + * + * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". + * Available in http://www.ietf.org/rfc/rfc1951.txt + * + * A description of the Rabin and Karp algorithm is given in the book + * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. + * + * Fiala,E.R., and Greene,D.H. + * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 + * + */ + +/* @(#) $Id: deflate.c,v 1.1 2007/06/07 17:54:37 jules_rms Exp $ */ + +/********* Start of inlined file: deflate.h *********/ +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id: deflate.h,v 1.1 2007/06/07 17:54:37 jules_rms Exp $ */ + +#ifndef DEFLATE_H +#define DEFLATE_H + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer creation by deflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip encoding + should be left enabled. */ +#ifndef NO_GZIP +# define GZIP +#endif + +#define NO_DUMMY_DECL + +/* =========================================================================== + * Internal compression state. + */ + +#define LENGTH_CODES 29 +/* number of length codes, not counting the special END_BLOCK code */ + +#define LITERALS 256 +/* number of literal bytes 0..255 */ + +#define L_CODES (LITERALS+1+LENGTH_CODES) +/* number of Literal or Length codes, including the END_BLOCK code */ + +#define D_CODES 30 +/* number of distance codes */ + +#define BL_CODES 19 +/* number of codes used to transfer the bit lengths */ + +#define HEAP_SIZE (2*L_CODES+1) +/* maximum heap size */ + +#define MAX_BITS 15 +/* All codes must not exceed MAX_BITS bits */ + +#define INIT_STATE 42 +#define EXTRA_STATE 69 +#define NAME_STATE 73 +#define COMMENT_STATE 91 +#define HCRC_STATE 103 +#define BUSY_STATE 113 +#define FINISH_STATE 666 +/* Stream status */ + +/* Data structure describing a single value and its code string. */ +typedef struct ct_data_s { + union { + ush freq; /* frequency count */ + ush code; /* bit string */ + } fc; + union { + ush dad; /* father node in Huffman tree */ + ush len; /* length of bit string */ + } dl; +} FAR ct_data; + +#define Freq fc.freq +#define Code fc.code +#define Dad dl.dad +#define Len dl.len + +typedef struct static_tree_desc_s static_tree_desc; + +typedef struct tree_desc_s { + ct_data *dyn_tree; /* the dynamic tree */ + int max_code; /* largest code with non zero frequency */ + static_tree_desc *stat_desc; /* the corresponding static tree */ +} FAR tree_desc; + +typedef ush Pos; +typedef Pos FAR Posf; +typedef unsigned IPos; + +/* A Pos is an index in the character window. We use short instead of int to + * save space in the various tables. IPos is used only for parameter passing. + */ + +typedef struct internal_state { + z_streamp strm; /* pointer back to this zlib stream */ + int status; /* as the name implies */ + Bytef *pending_buf; /* output still pending */ + ulg pending_buf_size; /* size of pending_buf */ + Bytef *pending_out; /* next pending byte to output to the stream */ + uInt pending; /* nb of bytes in the pending buffer */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + gz_headerp gzhead; /* gzip header information to write */ + uInt gzindex; /* where in extra, name, or comment */ + Byte method; /* STORED (for zip only) or DEFLATED */ + int last_flush; /* value of flush param for previous deflate call */ + + /* used by deflate.c: */ + + uInt w_size; /* LZ77 window size (32K by default) */ + uInt w_bits; /* log2(w_size) (8..16) */ + uInt w_mask; /* w_size - 1 */ + + Bytef *window; + /* Sliding window. Input bytes are read into the second half of the window, + * and move to the first half later to keep a dictionary of at least wSize + * bytes. With this organization, matches are limited to a distance of + * wSize-MAX_MATCH bytes, but this ensures that IO is always + * performed with a length multiple of the block size. Also, it limits + * the window size to 64K, which is quite useful on MSDOS. + * To do: use the user input buffer as sliding window. + */ + + ulg window_size; + /* Actual size of window: 2*wSize, except when the user input buffer + * is directly used as sliding window. + */ + + Posf *prev; + /* Link to older string with same hash index. To limit the size of this + * array to 64K, this link is maintained only for the last 32K strings. + * An index in this array is thus a window index modulo 32K. + */ + + Posf *head; /* Heads of the hash chains or NIL. */ + + uInt ins_h; /* hash index of string to be inserted */ + uInt hash_size; /* number of elements in hash table */ + uInt hash_bits; /* log2(hash_size) */ + uInt hash_mask; /* hash_size-1 */ + + uInt hash_shift; + /* Number of bits by which ins_h must be shifted at each input + * step. It must be such that after MIN_MATCH steps, the oldest + * byte no longer takes part in the hash key, that is: + * hash_shift * MIN_MATCH >= hash_bits + */ + + long block_start; + /* Window position at the beginning of the current output block. Gets + * negative when the window is moved backwards. + */ + + uInt match_length; /* length of best match */ + IPos prev_match; /* previous match */ + int match_available; /* set if previous match exists */ + uInt strstart; /* start of string to insert */ + uInt match_start; /* start of matching string */ + uInt lookahead; /* number of valid bytes ahead in window */ + + uInt prev_length; + /* Length of the best match at previous step. Matches not greater than this + * are discarded. This is used in the lazy match evaluation. + */ + + uInt max_chain_length; + /* To speed up deflation, hash chains are never searched beyond this + * length. A higher limit improves compression ratio but degrades the + * speed. + */ + + uInt max_lazy_match; + /* Attempt to find a better match only when the current match is strictly + * smaller than this value. This mechanism is used only for compression + * levels >= 4. + */ +# define max_insert_length max_lazy_match + /* Insert new strings in the hash table only if the match length is not + * greater than this length. This saves time but degrades compression. + * max_insert_length is used only for compression levels <= 3. + */ + + int level; /* compression level (1..9) */ + int strategy; /* favor or force Huffman coding*/ + + uInt good_match; + /* Use a faster search when the previous match is longer than this */ + + int nice_match; /* Stop searching when current match exceeds this */ + + /* used by trees.c: */ + /* Didn't use ct_data typedef below to supress compiler warning */ + struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ + struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ + struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ + + struct tree_desc_s l_desc; /* desc. for literal tree */ + struct tree_desc_s d_desc; /* desc. for distance tree */ + struct tree_desc_s bl_desc; /* desc. for bit length tree */ + + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ + int heap_len; /* number of elements in the heap */ + int heap_max; /* element of largest frequency */ + /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ + + uch depth[2*L_CODES+1]; + /* Depth of each subtree used as tie breaker for trees of equal frequency + */ + + uchf *l_buf; /* buffer for literals or lengths */ + + uInt lit_bufsize; + /* Size of match buffer for literals/lengths. There are 4 reasons for + * limiting lit_bufsize to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input + * data is still in the window so we can still emit a stored block even + * when input comes from standard input. (This can also be done for + * all blocks if lit_bufsize is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * This is applicable only for zip (not gzip or zlib). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting + * trees more frequently. + * - I can't count above 4 + */ + + uInt last_lit; /* running index in l_buf */ + + ushf *d_buf; + /* Buffer for distances. To simplify the code, d_buf and l_buf have + * the same number of elements. To use different lengths, an extra flag + * array would be necessary. + */ + + ulg opt_len; /* bit length of current block with optimal trees */ + ulg static_len; /* bit length of current block with static trees */ + uInt matches; /* number of string matches in current block */ + int last_eob_len; /* bit length of EOB code for last block */ + +#ifdef DEBUG + ulg compressed_len; /* total bit length of compressed file mod 2^32 */ + ulg bits_sent; /* bit length of compressed data sent mod 2^32 */ +#endif + + ush bi_buf; + /* Output buffer. bits are inserted starting at the bottom (least + * significant bits). + */ + int bi_valid; + /* Number of valid bits in bi_buf. All bits above the last valid bit + * are always zero. + */ + +} FAR deflate_state; + +/* Output a byte on the stream. + * IN assertion: there is enough room in pending_buf. + */ +#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);} + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) +/* In order to simplify the code, particularly on 16 bit machines, match + * distances are limited to MAX_DIST instead of WSIZE. + */ + + /* in trees.c */ +void _tr_init OF((deflate_state *s)); +int _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); +void _tr_flush_block OF((deflate_state *s, charf *buf, ulg stored_len, + int eof)); +void _tr_align OF((deflate_state *s)); +void _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len, + int eof)); + +#define d_code(dist) \ + ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) +/* Mapping from a distance to a distance code. dist is the distance - 1 and + * must not have side effects. _dist_code[256] and _dist_code[257] are never + * used. + */ + +#ifndef DEBUG +/* Inline versions of _tr_tally for speed: */ + +#if defined(GEN_TREES_H) || !defined(STDC) + extern uch _length_code[]; + extern uch _dist_code[]; +#else + extern const uch _length_code[]; + extern const uch _dist_code[]; +#endif + +# define _tr_tally_lit(s, c, flush) \ + { uch cc = (c); \ + s->d_buf[s->last_lit] = 0; \ + s->l_buf[s->last_lit++] = cc; \ + s->dyn_ltree[cc].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +# define _tr_tally_dist(s, distance, length, flush) \ + { uch len = (length); \ + ush dist = (distance); \ + s->d_buf[s->last_lit] = dist; \ + s->l_buf[s->last_lit++] = len; \ + dist--; \ + s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ + s->dyn_dtree[d_code(dist)].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +#else +# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c) +# define _tr_tally_dist(s, distance, length, flush) \ + flush = _tr_tally(s, distance, length) +#endif + +#endif /* DEFLATE_H */ +/********* End of inlined file: deflate.h *********/ + +const char deflate_copyright[] = + " deflate 1.2.3 Copyright 1995-2005 Jean-loup Gailly "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* =========================================================================== + * Function prototypes. + */ +typedef enum { + need_more, /* block not completed, need more input or more output */ + block_done, /* block flush performed */ + finish_started, /* finish started, need only more output at next deflate */ + finish_done /* finish done, accept no more input or output */ +} block_state; + +typedef block_state (*compress_func) OF((deflate_state *s, int flush)); +/* Compression function. Returns the block state after the call. */ + +local void fill_window OF((deflate_state *s)); +local block_state deflate_stored OF((deflate_state *s, int flush)); +local block_state deflate_fast OF((deflate_state *s, int flush)); +#ifndef FASTEST +local block_state deflate_slow OF((deflate_state *s, int flush)); +#endif +local void lm_init OF((deflate_state *s)); +local void putShortMSB OF((deflate_state *s, uInt b)); +local void flush_pending OF((z_streamp strm)); +local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); +#ifndef FASTEST +#ifdef ASMV + void match_init OF((void)); /* asm code initialization */ + uInt longest_match OF((deflate_state *s, IPos cur_match)); +#else +local uInt longest_match OF((deflate_state *s, IPos cur_match)); +#endif +#endif +local uInt longest_match_fast OF((deflate_state *s, IPos cur_match)); + +#ifdef DEBUG +local void check_match OF((deflate_state *s, IPos start, IPos match, + int length)); +#endif + +/* =========================================================================== + * Local data + */ + +#define NIL 0 +/* Tail of hash chains */ + +#ifndef TOO_FAR +# define TOO_FAR 4096 +#endif +/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +/* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ +typedef struct config_s { + ush good_length; /* reduce lazy search above this match length */ + ush max_lazy; /* do not perform lazy search above this match length */ + ush nice_length; /* quit search above this match length */ + ush max_chain; + compress_func func; +} config; + +#ifdef FASTEST +local const config configuration_table[2] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}}; /* max speed, no lazy matches */ +#else +local const config configuration_table[10] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}, /* max speed, no lazy matches */ +/* 2 */ {4, 5, 16, 8, deflate_fast}, +/* 3 */ {4, 6, 32, 32, deflate_fast}, + +/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */ +/* 5 */ {8, 16, 32, 32, deflate_slow}, +/* 6 */ {8, 16, 128, 128, deflate_slow}, +/* 7 */ {8, 32, 128, 256, deflate_slow}, +/* 8 */ {32, 128, 258, 1024, deflate_slow}, +/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */ +#endif + +/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 + * For deflate_fast() (levels <= 3) good is ignored and lazy has a different + * meaning. + */ + +#define EQUAL 0 +/* result of memcmp for equal strings */ + +#ifndef NO_DUMMY_DECL +struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ +#endif + +/* =========================================================================== + * Update a hash value with the given input byte + * IN assertion: all calls to to UPDATE_HASH are made with consecutive + * input characters, so that a running hash key can be computed from the + * previous key instead of complete recalculation each time. + */ +#define UPDATE_HASH(s,h,c) (h = (((h)<hash_shift) ^ (c)) & s->hash_mask) + +/* =========================================================================== + * Insert string str in the dictionary and set match_head to the previous head + * of the hash chain (the most recent string with same hash key). Return + * the previous length of the hash chain. + * If this file is compiled with -DFASTEST, the compression level is forced + * to 1, and no hash chains are maintained. + * IN assertion: all calls to to INSERT_STRING are made with consecutive + * input characters and the first MIN_MATCH bytes of str are valid + * (except for the last MIN_MATCH-1 bytes of the input file). + */ +#ifdef FASTEST +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#else +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#endif + +/* =========================================================================== + * Initialize the hash table (avoiding 64K overflow for 16 bit systems). + * prev[] will be initialized on the fly. + */ +#define CLEAR_HASH(s) \ + s->head[s->hash_size-1] = NIL; \ + zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head)); + +/* ========================================================================= */ +int ZEXPORT deflateInit_(z_streamp strm, int level, const char *version, int stream_size) +{ + return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, version, stream_size); + /* To do: ignore strm->next_in if we use it as window */ +} + +/* ========================================================================= */ +int ZEXPORT deflateInit2_ (z_streamp strm, int level, int method, int windowBits, int memLevel, int strategy, const char *version, int stream_size) +{ + deflate_state *s; + int wrap = 1; + static const char my_version[] = ZLIB_VERSION; + + ushf *overlay; + /* We overlay pending_buf and d_buf+l_buf. This works since the average + * output size for (length,distance) codes is <= 24 bits. + */ + + if (version == Z_NULL || version[0] != my_version[0] || + stream_size != sizeof(z_stream)) { + return Z_VERSION_ERROR; + } + if (strm == Z_NULL) return Z_STREAM_ERROR; + + strm->msg = Z_NULL; + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + + if (windowBits < 0) { /* suppress zlib wrapper */ + wrap = 0; + windowBits = -windowBits; + } +#ifdef GZIP + else if (windowBits > 15) { + wrap = 2; /* write gzip wrapper instead */ + windowBits -= 16; + } +#endif + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || + windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || + strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */ + s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state)); + if (s == Z_NULL) return Z_MEM_ERROR; + strm->state = (struct internal_state FAR *)s; + s->strm = strm; + + s->wrap = wrap; + s->gzhead = Z_NULL; + s->w_bits = windowBits; + s->w_size = 1 << s->w_bits; + s->w_mask = s->w_size - 1; + + s->hash_bits = memLevel + 7; + s->hash_size = 1 << s->hash_bits; + s->hash_mask = s->hash_size - 1; + s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH); + + s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); + s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); + s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); + + s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ + + overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); + s->pending_buf = (uchf *) overlay; + s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L); + + if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || + s->pending_buf == Z_NULL) { + s->status = FINISH_STATE; + strm->msg = (char*)ERR_MSG(Z_MEM_ERROR); + deflateEnd (strm); + return Z_MEM_ERROR; + } + s->d_buf = overlay + s->lit_bufsize/sizeof(ush); + s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; + + s->level = level; + s->strategy = strategy; + s->method = (Byte)method; + + return deflateReset(strm); +} + +/* ========================================================================= */ +int ZEXPORT deflateSetDictionary (z_streamp strm, const Bytef *dictionary, uInt dictLength) +{ + deflate_state *s; + uInt length = dictLength; + uInt n; + IPos hash_head = 0; + + if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL || + strm->state->wrap == 2 || + (strm->state->wrap == 1 && strm->state->status != INIT_STATE)) + return Z_STREAM_ERROR; + + s = strm->state; + if (s->wrap) + strm->adler = adler32(strm->adler, dictionary, dictLength); + + if (length < MIN_MATCH) return Z_OK; + if (length > MAX_DIST(s)) { + length = MAX_DIST(s); + dictionary += dictLength - length; /* use the tail of the dictionary */ + } + zmemcpy(s->window, dictionary, length); + s->strstart = length; + s->block_start = (long)length; + + /* Insert all strings in the hash table (except for the last two bytes). + * s->lookahead stays null, so s->ins_h will be recomputed at the next + * call of fill_window. + */ + s->ins_h = s->window[0]; + UPDATE_HASH(s, s->ins_h, s->window[1]); + for (n = 0; n <= length - MIN_MATCH; n++) { + INSERT_STRING(s, n, hash_head); + } + if (hash_head) hash_head = 0; /* to make compiler happy */ + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateReset (z_streamp strm) +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) { + return Z_STREAM_ERROR; + } + + strm->total_in = strm->total_out = 0; + strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */ + strm->data_type = Z_UNKNOWN; + + s = (deflate_state *)strm->state; + s->pending = 0; + s->pending_out = s->pending_buf; + + if (s->wrap < 0) { + s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */ + } + s->status = s->wrap ? INIT_STATE : BUSY_STATE; + strm->adler = +#ifdef GZIP + s->wrap == 2 ? crc32(0L, Z_NULL, 0) : +#endif + adler32(0L, Z_NULL, 0); + s->last_flush = Z_NO_FLUSH; + + _tr_init(s); + lm_init(s); + + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateSetHeader (z_streamp strm, gz_headerp head) +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (strm->state->wrap != 2) return Z_STREAM_ERROR; + strm->state->gzhead = head; + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflatePrime (z_streamp strm, int bits, int value) +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + strm->state->bi_valid = bits; + strm->state->bi_buf = (ush)(value & ((1 << bits) - 1)); + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateParams (z_streamp strm, int level, int strategy) +{ + deflate_state *s; + compress_func func; + int err = Z_OK; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + if (level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + func = configuration_table[s->level].func; + + if (func != configuration_table[level].func && strm->total_in != 0) { + /* Flush the last buffer: */ + err = deflate(strm, Z_PARTIAL_FLUSH); + } + if (s->level != level) { + s->level = level; + s->max_lazy_match = configuration_table[level].max_lazy; + s->good_match = configuration_table[level].good_length; + s->nice_match = configuration_table[level].nice_length; + s->max_chain_length = configuration_table[level].max_chain; + } + s->strategy = strategy; + return err; +} + +/* ========================================================================= */ +int ZEXPORT deflateTune (z_streamp strm, int good_length, int max_lazy, int nice_length, int max_chain) +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + s->good_match = good_length; + s->max_lazy_match = max_lazy; + s->nice_match = nice_length; + s->max_chain_length = max_chain; + return Z_OK; +} + +/* ========================================================================= + * For the default windowBits of 15 and memLevel of 8, this function returns + * a close to exact, as well as small, upper bound on the compressed size. + * They are coded as constants here for a reason--if the #define's are + * changed, then this function needs to be changed as well. The return + * value for 15 and 8 only works for those exact settings. + * + * For any setting other than those defaults for windowBits and memLevel, + * the value returned is a conservative worst case for the maximum expansion + * resulting from using fixed blocks instead of stored blocks, which deflate + * can emit on compressed data for some combinations of the parameters. + * + * This function could be more sophisticated to provide closer upper bounds + * for every combination of windowBits and memLevel, as well as wrap. + * But even the conservative upper bound of about 14% expansion does not + * seem onerous for output buffer allocation. + */ +uLong ZEXPORT deflateBound (z_streamp strm, uLong sourceLen) +{ + deflate_state *s; + uLong destLen; + + /* conservative upper bound */ + destLen = sourceLen + + ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 11; + + /* if can't get parameters, return conservative bound */ + if (strm == Z_NULL || strm->state == Z_NULL) + return destLen; + + /* if not default parameters, return conservative bound */ + s = strm->state; + if (s->w_bits != 15 || s->hash_bits != 8 + 7) + return destLen; + + /* default settings: return tight bound for that case */ + return compressBound(sourceLen); +} + +/* ========================================================================= + * Put a short in the pending buffer. The 16-bit value is put in MSB order. + * IN assertion: the stream state is correct and there is enough room in + * pending_buf. + */ +local void putShortMSB (deflate_state *s, uInt b) +{ + put_byte(s, (Byte)(b >> 8)); + put_byte(s, (Byte)(b & 0xff)); +} + +/* ========================================================================= + * Flush as much pending output as possible. All deflate() output goes + * through this function so some applications may wish to modify it + * to avoid allocating a large strm->next_out buffer and copying into it. + * (See also read_buf()). + */ +local void flush_pending (z_streamp strm) +{ + unsigned len = strm->state->pending; + + if (len > strm->avail_out) len = strm->avail_out; + if (len == 0) return; + + zmemcpy(strm->next_out, strm->state->pending_out, len); + strm->next_out += len; + strm->state->pending_out += len; + strm->total_out += len; + strm->avail_out -= len; + strm->state->pending -= len; + if (strm->state->pending == 0) { + strm->state->pending_out = strm->state->pending_buf; + } +} + +/* ========================================================================= */ +int ZEXPORT deflate (z_streamp strm, int flush) +{ + int old_flush; /* value of flush param for previous deflate call */ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + flush > Z_FINISH || flush < 0) { + return Z_STREAM_ERROR; + } + s = strm->state; + + if (strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0) || + (s->status == FINISH_STATE && flush != Z_FINISH)) { + ERR_RETURN(strm, Z_STREAM_ERROR); + } + if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); + + s->strm = strm; /* just in case */ + old_flush = s->last_flush; + s->last_flush = flush; + + /* Write the header */ + if (s->status == INIT_STATE) { +#ifdef GZIP + if (s->wrap == 2) { + strm->adler = crc32(0L, Z_NULL, 0); + put_byte(s, 31); + put_byte(s, 139); + put_byte(s, 8); + if (s->gzhead == NULL) { + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, OS_CODE); + s->status = BUSY_STATE; + } + else { + put_byte(s, (s->gzhead->text ? 1 : 0) + + (s->gzhead->hcrc ? 2 : 0) + + (s->gzhead->extra == Z_NULL ? 0 : 4) + + (s->gzhead->name == Z_NULL ? 0 : 8) + + (s->gzhead->comment == Z_NULL ? 0 : 16) + ); + put_byte(s, (Byte)(s->gzhead->time & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 24) & 0xff)); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, s->gzhead->os & 0xff); + if (s->gzhead->extra != NULL) { + put_byte(s, s->gzhead->extra_len & 0xff); + put_byte(s, (s->gzhead->extra_len >> 8) & 0xff); + } + if (s->gzhead->hcrc) + strm->adler = crc32(strm->adler, s->pending_buf, + s->pending); + s->gzindex = 0; + s->status = EXTRA_STATE; + } + } + else +#endif + { + uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; + uInt level_flags; + + if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2) + level_flags = 0; + else if (s->level < 6) + level_flags = 1; + else if (s->level == 6) + level_flags = 2; + else + level_flags = 3; + header |= (level_flags << 6); + if (s->strstart != 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + s->status = BUSY_STATE; + putShortMSB(s, header); + + /* Save the adler32 of the preset dictionary: */ + if (s->strstart != 0) { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + strm->adler = adler32(0L, Z_NULL, 0); + } + } +#ifdef GZIP + if (s->status == EXTRA_STATE) { + if (s->gzhead->extra != NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + + while (s->gzindex < (s->gzhead->extra_len & 0xffff)) { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) + break; + } + put_byte(s, s->gzhead->extra[s->gzindex]); + s->gzindex++; + } + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (s->gzindex == s->gzhead->extra_len) { + s->gzindex = 0; + s->status = NAME_STATE; + } + } + else + s->status = NAME_STATE; + } + if (s->status == NAME_STATE) { + if (s->gzhead->name != NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->name[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) { + s->gzindex = 0; + s->status = COMMENT_STATE; + } + } + else + s->status = COMMENT_STATE; + } + if (s->status == COMMENT_STATE) { + if (s->gzhead->comment != NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->comment[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) + s->status = HCRC_STATE; + } + else + s->status = HCRC_STATE; + } + if (s->status == HCRC_STATE) { + if (s->gzhead->hcrc) { + if (s->pending + 2 > s->pending_buf_size) + flush_pending(strm); + if (s->pending + 2 <= s->pending_buf_size) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + strm->adler = crc32(0L, Z_NULL, 0); + s->status = BUSY_STATE; + } + } + else + s->status = BUSY_STATE; + } +#endif + + /* Flush as much pending output as possible */ + if (s->pending != 0) { + flush_pending(strm); + if (strm->avail_out == 0) { + /* Since avail_out is 0, deflate will be called again with + * more output space, but possibly with both pending and + * avail_in equal to zero. There won't be anything to do, + * but this is not an error situation so make sure we + * return OK instead of BUF_ERROR at next call of deflate: + */ + s->last_flush = -1; + return Z_OK; + } + + /* Make sure there is something to do and avoid duplicate consecutive + * flushes. For repeated and useless calls with Z_FINISH, we keep + * returning Z_STREAM_END instead of Z_BUF_ERROR. + */ + } else if (strm->avail_in == 0 && flush <= old_flush && + flush != Z_FINISH) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* User must not provide more input after the first FINISH: */ + if (s->status == FINISH_STATE && strm->avail_in != 0) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* Start a new block or continue the current one. + */ + if (strm->avail_in != 0 || s->lookahead != 0 || + (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { + block_state bstate; + + bstate = (*(configuration_table[s->level].func))(s, flush); + + if (bstate == finish_started || bstate == finish_done) { + s->status = FINISH_STATE; + } + if (bstate == need_more || bstate == finish_started) { + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR next call, see above */ + } + return Z_OK; + /* If flush != Z_NO_FLUSH && avail_out == 0, the next call + * of deflate should use the same flush parameter to make sure + * that the flush is complete. So we don't have to output an + * empty block here, this will be done at next call. This also + * ensures that for a very small output buffer, we emit at most + * one empty block. + */ + } + if (bstate == block_done) { + if (flush == Z_PARTIAL_FLUSH) { + _tr_align(s); + } else { /* FULL_FLUSH or SYNC_FLUSH */ + _tr_stored_block(s, (char*)0, 0L, 0); + /* For a full flush, this empty block will be recognized + * as a special marker by inflate_sync(). + */ + if (flush == Z_FULL_FLUSH) { + CLEAR_HASH(s); /* forget history */ + } + } + flush_pending(strm); + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */ + return Z_OK; + } + } + } + Assert(strm->avail_out > 0, "bug2"); + + if (flush != Z_FINISH) return Z_OK; + if (s->wrap <= 0) return Z_STREAM_END; + + /* Write the trailer */ +#ifdef GZIP + if (s->wrap == 2) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 16) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 24) & 0xff)); + put_byte(s, (Byte)(strm->total_in & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 8) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 16) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 24) & 0xff)); + } + else +#endif + { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + flush_pending(strm); + /* If avail_out is zero, the application will call deflate again + * to flush the rest. + */ + if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */ + return s->pending != 0 ? Z_OK : Z_STREAM_END; +} + +/* ========================================================================= */ +int ZEXPORT deflateEnd (z_streamp strm) +{ + int status; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + + status = strm->state->status; + if (status != INIT_STATE && + status != EXTRA_STATE && + status != NAME_STATE && + status != COMMENT_STATE && + status != HCRC_STATE && + status != BUSY_STATE && + status != FINISH_STATE) { + return Z_STREAM_ERROR; + } + + /* Deallocate in reverse order of allocations: */ + TRY_FREE(strm, strm->state->pending_buf); + TRY_FREE(strm, strm->state->head); + TRY_FREE(strm, strm->state->prev); + TRY_FREE(strm, strm->state->window); + + ZFREE(strm, strm->state); + strm->state = Z_NULL; + + return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; +} + +/* ========================================================================= + * Copy the source state to the destination state. + * To simplify the source, this is not supported for 16-bit MSDOS (which + * doesn't have enough memory anyway to duplicate compression states). + */ +int ZEXPORT deflateCopy (z_streamp dest, z_streamp source) +{ +#ifdef MAXSEG_64K + return Z_STREAM_ERROR; +#else + deflate_state *ds; + deflate_state *ss; + ushf *overlay; + + if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) { + return Z_STREAM_ERROR; + } + + ss = source->state; + + zmemcpy(dest, source, sizeof(z_stream)); + + ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state)); + if (ds == Z_NULL) return Z_MEM_ERROR; + dest->state = (struct internal_state FAR *) ds; + zmemcpy(ds, ss, sizeof(deflate_state)); + ds->strm = dest; + + ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); + ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos)); + ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos)); + overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2); + ds->pending_buf = (uchf *) overlay; + + if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || + ds->pending_buf == Z_NULL) { + deflateEnd (dest); + return Z_MEM_ERROR; + } + /* following zmemcpy do not work for 16-bit MSDOS */ + zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); + zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos)); + zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos)); + zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size); + + ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); + ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush); + ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize; + + ds->l_desc.dyn_tree = ds->dyn_ltree; + ds->d_desc.dyn_tree = ds->dyn_dtree; + ds->bl_desc.dyn_tree = ds->bl_tree; + + return Z_OK; +#endif /* MAXSEG_64K */ +} + +/* =========================================================================== + * Read a new buffer from the current input stream, update the adler32 + * and total number of bytes read. All deflate() input goes through + * this function so some applications may wish to modify it to avoid + * allocating a large strm->next_in buffer and copying from it. + * (See also flush_pending()). + */ +local int read_buf (z_streamp strm, Bytef *buf, unsigned size) +{ + unsigned len = strm->avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + strm->avail_in -= len; + + if (strm->state->wrap == 1) { + strm->adler = adler32(strm->adler, strm->next_in, len); + } +#ifdef GZIP + else if (strm->state->wrap == 2) { + strm->adler = crc32(strm->adler, strm->next_in, len); + } +#endif + zmemcpy(buf, strm->next_in, len); + strm->next_in += len; + strm->total_in += len; + + return (int)len; +} + +/* =========================================================================== + * Initialize the "longest match" routines for a new zlib stream + */ +local void lm_init (deflate_state *s) +{ + s->window_size = (ulg)2L*s->w_size; + + CLEAR_HASH(s); + + /* Set the default configuration parameters: + */ + s->max_lazy_match = configuration_table[s->level].max_lazy; + s->good_match = configuration_table[s->level].good_length; + s->nice_match = configuration_table[s->level].nice_length; + s->max_chain_length = configuration_table[s->level].max_chain; + + s->strstart = 0; + s->block_start = 0L; + s->lookahead = 0; + s->match_length = s->prev_length = MIN_MATCH-1; + s->match_available = 0; + s->ins_h = 0; +#ifndef FASTEST +#ifdef ASMV + match_init(); /* initialize the asm code */ +#endif +#endif +} + +#ifndef FASTEST +/* =========================================================================== + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + * OUT assertion: the match length is not greater than s->lookahead. + */ +#ifndef ASMV +/* For 80x86 and 680x0, an optimized version will be provided in match.asm or + * match.S. The code will be functionally equivalent. + */ +local uInt longest_match(deflate_state *s, IPos cur_match) +{ + unsigned chain_length = s->max_chain_length;/* max hash chain length */ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + int best_len = s->prev_length; /* best match length so far */ + int nice_match = s->nice_match; /* stop if match long enough */ + IPos limit = s->strstart > (IPos)MAX_DIST(s) ? + s->strstart - (IPos)MAX_DIST(s) : NIL; + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + Posf *prev = s->prev; + uInt wmask = s->w_mask; + +#ifdef UNALIGNED_OK + /* Compare two bytes at a time. Note: this is not always beneficial. + * Try with and without -DUNALIGNED_OK to check. + */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1; + register ush scan_start = *(ushf*)scan; + register ush scan_end = *(ushf*)(scan+best_len-1); +#else + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + register Byte scan_end1 = scan[best_len-1]; + register Byte scan_end = scan[best_len]; +#endif + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + /* Do not waste too much time if we already have a good match: */ + if (s->prev_length >= s->good_match) { + chain_length >>= 2; + } + /* Do not look for matches beyond the end of the input. This is necessary + * to make deflate deterministic. + */ + if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + do { + Assert(cur_match < s->strstart, "no future"); + match = s->window + cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2. Note that the checks below + * for insufficient lookahead only occur occasionally for performance + * reasons. Therefore uninitialized memory will be accessed, and + * conditional jumps will be made that depend on those values. + * However the length of the match is limited to the lookahead, so + * the output of deflate is not affected by the uninitialized values. + */ +#if (defined(UNALIGNED_OK) && MAX_MATCH == 258) + /* This code assumes sizeof(unsigned short) == 2. Do not use + * UNALIGNED_OK if your compiler uses a different size. + */ + if (*(ushf*)(match+best_len-1) != scan_end || + *(ushf*)match != scan_start) continue; + + /* It is not necessary to compare scan[2] and match[2] since they are + * always equal when the other bytes match, given that the hash keys + * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at + * strstart+3, +5, ... up to strstart+257. We check for insufficient + * lookahead only every 4th comparison; the 128th check will be made + * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is + * necessary to put more guard bytes at the end of the window, or + * to check more often for insufficient lookahead. + */ + Assert(scan[2] == match[2], "scan[2]?"); + scan++, match++; + do { + } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + scan < strend); + /* The funny "do {}" generates better code on most compilers */ + + /* Here, scan <= window+strstart+257 */ + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + if (*scan == *match) scan++; + + len = (MAX_MATCH - 1) - (int)(strend-scan); + scan = strend - (MAX_MATCH-1); + +#else /* UNALIGNED_OK */ + + if (match[best_len] != scan_end || + match[best_len-1] != scan_end1 || + *match != *scan || + *++match != scan[1]) continue; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match++; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + +#endif /* UNALIGNED_OK */ + + if (len > best_len) { + s->match_start = cur_match; + best_len = len; + if (len >= nice_match) break; +#ifdef UNALIGNED_OK + scan_end = *(ushf*)(scan+best_len-1); +#else + scan_end1 = scan[best_len-1]; + scan_end = scan[best_len]; +#endif + } + } while ((cur_match = prev[cur_match & wmask]) > limit + && --chain_length != 0); + + if ((uInt)best_len <= s->lookahead) return (uInt)best_len; + return s->lookahead; +} +#endif /* ASMV */ +#endif /* FASTEST */ + +/* --------------------------------------------------------------------------- + * Optimized version for level == 1 or strategy == Z_RLE only + */ +local uInt longest_match_fast (deflate_state *s, IPos cur_match) +{ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + Assert(cur_match < s->strstart, "no future"); + + match = s->window + cur_match; + + /* Return failure if the match length is less than 2: + */ + if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match += 2; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + + if (len < MIN_MATCH) return MIN_MATCH - 1; + + s->match_start = cur_match; + return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead; +} + +#ifdef DEBUG +/* =========================================================================== + * Check that the match at match_start is indeed a match. + */ +local void check_match(deflate_state *s, IPos start, IPos match, int length) +{ + /* check that the match is indeed a match */ + if (zmemcmp(s->window + match, + s->window + start, length) != EQUAL) { + fprintf(stderr, " start %u, match %u, length %d\n", + start, match, length); + do { + fprintf(stderr, "%c%c", s->window[match++], s->window[start++]); + } while (--length != 0); + z_error("invalid match"); + } + if (z_verbose > 1) { + fprintf(stderr,"\\[%d,%d]", start-match, length); + do { putc(s->window[start++], stderr); } while (--length != 0); + } +} +#else +# define check_match(s, start, match, length) +#endif /* DEBUG */ + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead. + * + * IN assertion: lookahead < MIN_LOOKAHEAD + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + * At least one byte has been read, or avail_in == 0; reads are + * performed for at least two bytes (required for the zip translate_eol + * option -- not supported here). + */ +local void fill_window (deflate_state *s) +{ + register unsigned n, m; + register Posf *p; + unsigned more; /* Amount of free space at the end of the window. */ + uInt wsize = s->w_size; + + do { + more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); + + /* Deal with !@#$% 64K limit: */ + if (sizeof(int) <= 2) { + if (more == 0 && s->strstart == 0 && s->lookahead == 0) { + more = wsize; + + } else if (more == (unsigned)(-1)) { + /* Very unlikely, but possible on 16 bit machine if + * strstart == 0 && lookahead == 1 (input done a byte at time) + */ + more--; + } + } + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (s->strstart >= wsize+MAX_DIST(s)) { + + zmemcpy(s->window, s->window+wsize, (unsigned)wsize); + s->match_start -= wsize; + s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ + s->block_start -= (long) wsize; + + /* Slide the hash table (could be avoided with 32 bit values + at the expense of memory usage). We slide even when level == 0 + to keep the hash table consistent if we switch back to level > 0 + later. (Using level 0 permanently is not an optimal usage of + zlib, so we don't care about this pathological case.) + */ + /* %%% avoid this when Z_RLE */ + n = s->hash_size; + p = &s->head[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + } while (--n); + + n = wsize; +#ifndef FASTEST + p = &s->prev[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } while (--n); +#endif + more += wsize; + } + if (s->strm->avail_in == 0) return; + + /* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the BIG_MEM or MMAP case (not yet supported), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + Assert(more >= 2, "more < 2"); + + n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); + s->lookahead += n; + + /* Initialize the hash value now that we have some input: */ + if (s->lookahead >= MIN_MATCH) { + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + } + /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + * but this is not important since only literal bytes will be emitted. + */ + + } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); +} + +/* =========================================================================== + * Flush the current block, with given end-of-file flag. + * IN assertion: strstart is set to the end of the current match. + */ +#define FLUSH_BLOCK_ONLY(s, eof) { \ + _tr_flush_block(s, (s->block_start >= 0L ? \ + (charf *)&s->window[(unsigned)s->block_start] : \ + (charf *)Z_NULL), \ + (ulg)((long)s->strstart - s->block_start), \ + (eof)); \ + s->block_start = s->strstart; \ + flush_pending(s->strm); \ + Tracev((stderr,"[FLUSH]")); \ +} + +/* Same but force premature exit if necessary. */ +#define FLUSH_BLOCK(s, eof) { \ + FLUSH_BLOCK_ONLY(s, eof); \ + if (s->strm->avail_out == 0) return (eof) ? finish_started : need_more; \ +} + +/* =========================================================================== + * Copy without compression as much as possible from the input stream, return + * the current block state. + * This function does not insert new strings in the dictionary since + * uncompressible data is probably not useful. This function is used + * only for the level=0 compression option. + * NOTE: this function should be optimized to avoid extra copying from + * window to pending_buf. + */ +local block_state deflate_stored(deflate_state *s, int flush) +{ + /* Stored blocks are limited to 0xffff bytes, pending_buf is limited + * to pending_buf_size, and each stored block has a 5 byte header: + */ + ulg max_block_size = 0xffff; + ulg max_start; + + if (max_block_size > s->pending_buf_size - 5) { + max_block_size = s->pending_buf_size - 5; + } + + /* Copy as much as possible from input to output: */ + for (;;) { + /* Fill the window as much as possible: */ + if (s->lookahead <= 1) { + + Assert(s->strstart < s->w_size+MAX_DIST(s) || + s->block_start >= (long)s->w_size, "slide too late"); + + fill_window(s); + if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more; + + if (s->lookahead == 0) break; /* flush the current block */ + } + Assert(s->block_start >= 0L, "block gone"); + + s->strstart += s->lookahead; + s->lookahead = 0; + + /* Emit a stored block if pending_buf will be full: */ + max_start = s->block_start + max_block_size; + if (s->strstart == 0 || (ulg)s->strstart >= max_start) { + /* strstart == 0 is possible when wraparound on 16-bit machine */ + s->lookahead = (uInt)(s->strstart - max_start); + s->strstart = (uInt)max_start; + FLUSH_BLOCK(s, 0); + } + /* Flush if we may have to slide, otherwise block_start may become + * negative and the data will be gone: + */ + if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) { + FLUSH_BLOCK(s, 0); + } + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +/* =========================================================================== + * Compress as much as possible from the input stream, return the current + * block state. + * This function does not perform lazy evaluation of matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ +local block_state deflate_fast(deflate_state *s, int flush) +{ + IPos hash_head = NIL; /* head of the hash chain */ + int bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ +#ifdef FASTEST + if ((s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) || + (s->strategy == Z_RLE && s->strstart - hash_head == 1)) { + s->match_length = longest_match_fast (s, hash_head); + } +#else + if (s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) { + s->match_length = longest_match (s, hash_head); + } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) { + s->match_length = longest_match_fast (s, hash_head); + } +#endif + /* longest_match() or longest_match_fast() sets match_start */ + } + if (s->match_length >= MIN_MATCH) { + check_match(s, s->strstart, s->match_start, s->match_length); + + _tr_tally_dist(s, s->strstart - s->match_start, + s->match_length - MIN_MATCH, bflush); + + s->lookahead -= s->match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ +#ifndef FASTEST + if (s->match_length <= s->max_insert_length && + s->lookahead >= MIN_MATCH) { + s->match_length--; /* string at strstart already in table */ + do { + s->strstart++; + INSERT_STRING(s, s->strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } while (--s->match_length != 0); + s->strstart++; + } else +#endif + { + s->strstart += s->match_length; + s->match_length = 0; + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not + * matter since it will be recomputed at next deflate call. + */ + } + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +#ifndef FASTEST +/* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +local block_state deflate_slow(deflate_state *s, int flush) +{ + IPos hash_head = NIL; /* head of hash chain */ + int bflush; /* set if current block must be flushed */ + + /* Process the input block. */ + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + */ + s->prev_length = s->match_length, s->prev_match = s->match_start; + s->match_length = MIN_MATCH-1; + + if (hash_head != NIL && s->prev_length < s->max_lazy_match && + s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + if (s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) { + s->match_length = longest_match (s, hash_head); + } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) { + s->match_length = longest_match_fast (s, hash_head); + } + /* longest_match() or longest_match_fast() sets match_start */ + + if (s->match_length <= 5 && (s->strategy == Z_FILTERED +#if TOO_FAR <= 32767 + || (s->match_length == MIN_MATCH && + s->strstart - s->match_start > TOO_FAR) +#endif + )) { + + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + s->match_length = MIN_MATCH-1; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { + uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; + /* Do not insert strings in hash table beyond this. */ + + check_match(s, s->strstart-1, s->prev_match, s->prev_length); + + _tr_tally_dist(s, s->strstart -1 - s->prev_match, + s->prev_length - MIN_MATCH, bflush); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. If there is not + * enough lookahead, the last two strings are not inserted in + * the hash table. + */ + s->lookahead -= s->prev_length-1; + s->prev_length -= 2; + do { + if (++s->strstart <= max_insert) { + INSERT_STRING(s, s->strstart, hash_head); + } + } while (--s->prev_length != 0); + s->match_available = 0; + s->match_length = MIN_MATCH-1; + s->strstart++; + + if (bflush) FLUSH_BLOCK(s, 0); + + } else if (s->match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + if (bflush) { + FLUSH_BLOCK_ONLY(s, 0); + } + s->strstart++; + s->lookahead--; + if (s->strm->avail_out == 0) return need_more; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + s->match_available = 1; + s->strstart++; + s->lookahead--; + } + } + Assert (flush != Z_NO_FLUSH, "no flush?"); + if (s->match_available) { + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + s->match_available = 0; + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} +#endif /* FASTEST */ + +#if 0 +/* =========================================================================== + * For Z_RLE, simply look for runs of bytes, generate matches only of distance + * one. Do not maintain a hash table. (It will be regenerated if this run of + * deflate switches away from Z_RLE.) + */ +local block_state deflate_rle(s, flush) + deflate_state *s; + int flush; +{ + int bflush; /* set if current block must be flushed */ + uInt run; /* length of run */ + uInt max; /* maximum length of run */ + uInt prev; /* byte at distance one to match */ + Bytef *scan; /* scan for end of run */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the longest encodable run. + */ + if (s->lookahead < MAX_MATCH) { + fill_window(s); + if (s->lookahead < MAX_MATCH && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* See how many times the previous byte repeats */ + run = 0; + if (s->strstart > 0) { /* if there is a previous byte, that is */ + max = s->lookahead < MAX_MATCH ? s->lookahead : MAX_MATCH; + scan = s->window + s->strstart - 1; + prev = *scan++; + do { + if (*scan++ != prev) + break; + } while (++run < max); + } + + /* Emit match if have run of MIN_MATCH or longer, else emit literal */ + if (run >= MIN_MATCH) { + check_match(s, s->strstart, s->strstart - 1, run); + _tr_tally_dist(s, 1, run - MIN_MATCH, bflush); + s->lookahead -= run; + s->strstart += run; + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} +#endif +/********* End of inlined file: deflate.c *********/ + +/********* Start of inlined file: infback.c *********/ +/* + This code is largely copied from inflate.c. Normally either infback.o or + inflate.o would be linked into an application--not both. The interface + with inffast.c is retained so that optimized assembler-coded versions of + inflate_fast() can be used with either inflate.c or infback.c. + */ + +/********* Start of inlined file: inftrees.h *********/ +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +#ifndef _INFTREES_H_ +#define _INFTREES_H_ + +/* Structure for decoding tables. Each entry provides either the + information needed to do the operation requested by the code that + indexed that table entry, or it provides a pointer to another + table that indexes more bits of the code. op indicates whether + the entry is a pointer to another table, a literal, a length or + distance, an end-of-block, or an invalid code. For a table + pointer, the low four bits of op is the number of index bits of + that table. For a length or distance, the low four bits of op + is the number of extra bits to get after the code. bits is + the number of bits in this code or part of the code to drop off + of the bit buffer. val is the actual byte to output in the case + of a literal, the base length or distance, or the offset from + the current table to the next table. Each entry is four bytes. */ +typedef struct { + unsigned char op; /* operation, extra bits, table bits */ + unsigned char bits; /* bits in this part of the code */ + unsigned short val; /* offset in table or code value */ +} code; + +/* op values as set by inflate_table(): + 00000000 - literal + 0000tttt - table link, tttt != 0 is the number of table index bits + 0001eeee - length or distance, eeee is the number of extra bits + 01100000 - end of block + 01000000 - invalid code + */ + +/* Maximum size of dynamic tree. The maximum found in a long but non- + exhaustive search was 1444 code structures (852 for length/literals + and 592 for distances, the latter actually the result of an + exhaustive search). The true maximum is not known, but the value + below is more than safe. */ +#define ENOUGH 2048 +#define MAXD 592 + +/* Type of code to build for inftable() */ +typedef enum { + CODES, + LENS, + DISTS +} codetype; + +extern int inflate_table OF((codetype type, unsigned short FAR *lens, + unsigned codes, code FAR * FAR *table, + unsigned FAR *bits, unsigned short FAR *work)); + +#endif +/********* End of inlined file: inftrees.h *********/ + +/********* Start of inlined file: inflate.h *********/ +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +#ifndef _INFLATE_H_ +#define _INFLATE_H_ + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer decoding by inflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip decoding + should be left enabled. */ +#ifndef NO_GZIP +# define GUNZIP +#endif + +/* Possible inflate modes between inflate() calls */ +typedef enum { + HEAD, /* i: waiting for magic header */ + FLAGS, /* i: waiting for method and flags (gzip) */ + TIME, /* i: waiting for modification time (gzip) */ + OS, /* i: waiting for extra flags and operating system (gzip) */ + EXLEN, /* i: waiting for extra length (gzip) */ + EXTRA, /* i: waiting for extra bytes (gzip) */ + NAME, /* i: waiting for end of file name (gzip) */ + COMMENT, /* i: waiting for end of comment (gzip) */ + HCRC, /* i: waiting for header crc (gzip) */ + DICTID, /* i: waiting for dictionary check value */ + DICT, /* waiting for inflateSetDictionary() call */ + TYPE, /* i: waiting for type bits, including last-flag bit */ + TYPEDO, /* i: same, but skip check to exit inflate on new block */ + STORED, /* i: waiting for stored size (length and complement) */ + COPY, /* i/o: waiting for input or output to copy stored block */ + TABLE, /* i: waiting for dynamic block table lengths */ + LENLENS, /* i: waiting for code length code lengths */ + CODELENS, /* i: waiting for length/lit and distance code lengths */ + LEN, /* i: waiting for length/lit code */ + LENEXT, /* i: waiting for length extra bits */ + DIST, /* i: waiting for distance code */ + DISTEXT, /* i: waiting for distance extra bits */ + MATCH, /* o: waiting for output space to copy string */ + LIT, /* o: waiting for output space to write literal */ + CHECK, /* i: waiting for 32-bit check value */ + LENGTH, /* i: waiting for 32-bit length (gzip) */ + DONE, /* finished check, done -- remain here until reset */ + BAD, /* got a data error -- remain here until reset */ + MEM, /* got an inflate() memory error -- remain here until reset */ + SYNC /* looking for synchronization bytes to restart inflate() */ +} inflate_mode; + +/* + State transitions between above modes - + + (most modes can go to the BAD or MEM mode -- not shown for clarity) + + Process header: + HEAD -> (gzip) or (zlib) + (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME + NAME -> COMMENT -> HCRC -> TYPE + (zlib) -> DICTID or TYPE + DICTID -> DICT -> TYPE + Read deflate blocks: + TYPE -> STORED or TABLE or LEN or CHECK + STORED -> COPY -> TYPE + TABLE -> LENLENS -> CODELENS -> LEN + Read deflate codes: + LEN -> LENEXT or LIT or TYPE + LENEXT -> DIST -> DISTEXT -> MATCH -> LEN + LIT -> LEN + Process trailer: + CHECK -> LENGTH -> DONE + */ + +/* state maintained between inflate() calls. Approximately 7K bytes. */ +struct inflate_state { + inflate_mode mode; /* current inflate mode */ + int last; /* true if processing last block */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + int havedict; /* true if dictionary provided */ + int flags; /* gzip header method and flags (0 if zlib) */ + unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */ + unsigned long check; /* protected copy of check value */ + unsigned long total; /* protected copy of output count */ + gz_headerp head; /* where to save gzip header information */ + /* sliding window */ + unsigned wbits; /* log base 2 of requested window size */ + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned write; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if needed */ + /* bit accumulator */ + unsigned long hold; /* input bit accumulator */ + unsigned bits; /* number of bits in "in" */ + /* for string and stored block copying */ + unsigned length; /* literal or length of data to copy */ + unsigned offset; /* distance back to copy string from */ + /* for table and code decoding */ + unsigned extra; /* extra bits needed */ + /* fixed and dynamic code tables */ + code const FAR *lencode; /* starting table for length/literal codes */ + code const FAR *distcode; /* starting table for distance codes */ + unsigned lenbits; /* index bits for lencode */ + unsigned distbits; /* index bits for distcode */ + /* dynamic table building */ + unsigned ncode; /* number of code length code lengths */ + unsigned nlen; /* number of length code lengths */ + unsigned ndist; /* number of distance code lengths */ + unsigned have; /* number of code lengths in lens[] */ + code FAR *next; /* next available space in codes[] */ + unsigned short lens[320]; /* temporary storage for code lengths */ + unsigned short work[288]; /* work area for code table building */ + code codes[ENOUGH]; /* space for code tables */ +}; + +#endif +/********* End of inlined file: inflate.h *********/ + +/********* Start of inlined file: inffast.h *********/ +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +void inflate_fast OF((z_streamp strm, unsigned start)); +/********* End of inlined file: inffast.h *********/ + +/* function prototypes */ +local void fixedtables1 OF((struct inflate_state FAR *state)); + +/* + strm provides memory allocation functions in zalloc and zfree, or + Z_NULL to use the library memory allocation functions. + + windowBits is in the range 8..15, and window is a user-supplied + window and output buffer that is 2**windowBits bytes. + */ +int ZEXPORT inflateBackInit_(z_streamp strm, int windowBits, unsigned char FAR *window, const char *version, int stream_size) +{ + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL || window == Z_NULL || + windowBits < 8 || windowBits > 15) + return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + state = (struct inflate_state FAR *)ZALLOC(strm, 1, + sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + state->dmax = 32768U; + state->wbits = windowBits; + state->wsize = 1U << windowBits; + state->window = window; + state->write = 0; + state->whave = 0; + return Z_OK; +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables1 (struct inflate_state FAR *state) +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ + +/********* Start of inlined file: inffixed.h *********/ + /* inffixed.h -- table for decoding fixed codes + * Generated automatically by makefixed(). + */ + + /* WARNING: this file should *not* be used by applications. It + is part of the implementation of the compression library and + is subject to change. Applications should only use zlib.h. + */ + + static const code lenfix[512] = { + {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48}, + {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128}, + {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59}, + {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176}, + {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20}, + {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100}, + {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8}, + {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216}, + {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76}, + {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114}, + {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2}, + {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148}, + {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42}, + {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86}, + {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15}, + {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236}, + {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62}, + {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142}, + {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31}, + {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162}, + {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25}, + {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105}, + {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4}, + {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202}, + {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69}, + {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125}, + {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13}, + {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195}, + {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35}, + {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91}, + {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19}, + {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246}, + {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55}, + {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135}, + {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99}, + {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190}, + {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16}, + {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96}, + {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6}, + {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209}, + {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72}, + {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116}, + {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4}, + {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153}, + {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44}, + {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82}, + {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11}, + {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229}, + {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58}, + {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138}, + {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51}, + {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173}, + {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30}, + {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110}, + {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0}, + {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195}, + {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65}, + {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121}, + {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9}, + {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258}, + {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37}, + {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93}, + {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23}, + {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251}, + {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51}, + {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131}, + {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67}, + {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183}, + {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23}, + {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103}, + {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9}, + {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223}, + {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79}, + {0,9,255} + }; + + static const code distfix[32] = { + {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025}, + {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193}, + {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385}, + {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577}, + {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073}, + {22,5,193},{64,5,0} + }; +/********* End of inlined file: inffixed.h *********/ + +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +/* Macros for inflateBack(): */ + +/* Load returned state from inflate_fast() */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Set state from registers for inflate_fast() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Assure that some input is available. If input is requested, but denied, + then return a Z_BUF_ERROR from inflateBack(). */ +#define PULL() \ + do { \ + if (have == 0) { \ + have = in(in_desc, &next); \ + if (have == 0) { \ + next = Z_NULL; \ + ret = Z_BUF_ERROR; \ + goto inf_leave; \ + } \ + } \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflateBack() + with an error if there is no input available. */ +#define PULLBYTE() \ + do { \ + PULL(); \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflateBack() with + an error. */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* Assure that some output space is available, by writing out the window + if it's full. If the write fails, return from inflateBack() with a + Z_BUF_ERROR. */ +#define ROOM() \ + do { \ + if (left == 0) { \ + put = state->window; \ + left = state->wsize; \ + state->whave = left; \ + if (out(out_desc, put, left)) { \ + ret = Z_BUF_ERROR; \ + goto inf_leave; \ + } \ + } \ + } while (0) + +/* + strm provides the memory allocation functions and window buffer on input, + and provides information on the unused input on return. For Z_DATA_ERROR + returns, strm will also provide an error message. + + in() and out() are the call-back input and output functions. When + inflateBack() needs more input, it calls in(). When inflateBack() has + filled the window with output, or when it completes with data in the + window, it calls out() to write out the data. The application must not + change the provided input until in() is called again or inflateBack() + returns. The application must not change the window/output buffer until + inflateBack() returns. + + in() and out() are called with a descriptor parameter provided in the + inflateBack() call. This parameter can be a structure that provides the + information required to do the read or write, as well as accumulated + information on the input and output such as totals and check values. + + in() should return zero on failure. out() should return non-zero on + failure. If either in() or out() fails, than inflateBack() returns a + Z_BUF_ERROR. strm->next_in can be checked for Z_NULL to see whether it + was in() or out() that caused in the error. Otherwise, inflateBack() + returns Z_STREAM_END on success, Z_DATA_ERROR for an deflate format + error, or Z_MEM_ERROR if it could not allocate memory for the state. + inflateBack() can also return Z_STREAM_ERROR if the input parameters + are not correct, i.e. strm is Z_NULL or the state was not initialized. + */ +int ZEXPORT inflateBack(z_streamp strm, in_func in, void FAR *in_desc, out_func out, void FAR *out_desc) +{ + struct inflate_state FAR *state; + unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code thisx; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + /* Check that the strm exists and that the state was initialized */ + if (strm == Z_NULL || strm->state == Z_NULL) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + + /* Reset the state */ + strm->msg = Z_NULL; + state->mode = TYPE; + state->last = 0; + state->whave = 0; + next = strm->next_in; + have = next != Z_NULL ? strm->avail_in : 0; + hold = 0; + bits = 0; + put = state->window; + left = state->wsize; + + /* Inflate until end of block marked as last */ + for (;;) + switch (state->mode) { + case TYPE: + /* determine and dispatch block type */ + if (state->last) { + BYTEBITS(); + state->mode = DONE; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables1(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN; /* decode codes */ + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + + case STORED: + /* get and verify stored block length */ + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + + /* copy stored block from input to output */ + while (state->length != 0) { + copy = state->length; + PULL(); + ROOM(); + if (copy > have) copy = have; + if (copy > left) copy = left; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + + case TABLE: + /* get dynamic table entries descriptor */ + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + + /* get code length code lengths (not a typo) */ + state->have = 0; + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + + /* get length and distance code code lengths */ + state->have = 0; + while (state->have < state->nlen + state->ndist) { + for (;;) { + thisx = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(thisx.bits) <= bits) break; + PULLBYTE(); + } + if (thisx.val < 16) { + NEEDBITS(thisx.bits); + DROPBITS(thisx.bits); + state->lens[state->have++] = thisx.val; + } + else { + if (thisx.val == 16) { + NEEDBITS(thisx.bits + 2); + DROPBITS(thisx.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = (unsigned)(state->lens[state->have - 1]); + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (thisx.val == 17) { + NEEDBITS(thisx.bits + 3); + DROPBITS(thisx.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(thisx.bits + 7); + DROPBITS(thisx.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* build code tables */ + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (code const FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN; + + case LEN: + /* use inflate_fast() if we have enough input and output */ + if (have >= 6 && left >= 258) { + RESTORE(); + if (state->whave < state->wsize) + state->whave = state->wsize - left; + inflate_fast(strm, state->wsize); + LOAD(); + break; + } + + /* get a literal, length, or end-of-block code */ + for (;;) { + thisx = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(thisx.bits) <= bits) break; + PULLBYTE(); + } + if (thisx.op && (thisx.op & 0xf0) == 0) { + last = thisx; + for (;;) { + thisx = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + thisx.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(thisx.bits); + state->length = (unsigned)thisx.val; + + /* process literal */ + if (thisx.op == 0) { + Tracevv((stderr, thisx.val >= 0x20 && thisx.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", thisx.val)); + ROOM(); + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + } + + /* process end of block */ + if (thisx.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + + /* invalid code */ + if (thisx.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + + /* length code -- get extra bits, if any */ + state->extra = (unsigned)(thisx.op) & 15; + if (state->extra != 0) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + + /* get distance code */ + for (;;) { + thisx = state->distcode[BITS(state->distbits)]; + if ((unsigned)(thisx.bits) <= bits) break; + PULLBYTE(); + } + if ((thisx.op & 0xf0) == 0) { + last = thisx; + for (;;) { + thisx = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + thisx.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(thisx.bits); + if (thisx.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)thisx.val; + + /* get distance extra bits, if any */ + state->extra = (unsigned)(thisx.op) & 15; + if (state->extra != 0) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + } + if (state->offset > state->wsize - (state->whave < state->wsize ? + left : 0)) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + + /* copy match from window to output */ + do { + ROOM(); + copy = state->wsize - state->offset; + if (copy < left) { + from = put + copy; + copy = left - copy; + } + else { + from = put - state->offset; + copy = left; + } + if (copy > state->length) copy = state->length; + state->length -= copy; + left -= copy; + do { + *put++ = *from++; + } while (--copy); + } while (state->length != 0); + break; + + case DONE: + /* inflate stream terminated properly -- write leftover output */ + ret = Z_STREAM_END; + if (left < state->wsize) { + if (out(out_desc, state->window, state->wsize - left)) + ret = Z_BUF_ERROR; + } + goto inf_leave; + + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + + default: /* can't happen, but makes compilers happy */ + ret = Z_STREAM_ERROR; + goto inf_leave; + } + + /* Return unused input */ + inf_leave: + strm->next_in = next; + strm->avail_in = have; + return ret; +} + +int ZEXPORT inflateBackEnd (z_streamp strm) +{ + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} +/********* End of inlined file: infback.c *********/ + +/********* Start of inlined file: inffast.c *********/ + +/********* Start of inlined file: inffast.h *********/ +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +void inflate_fast OF((z_streamp strm, unsigned start)); +/********* End of inlined file: inffast.h *********/ + +#ifndef ASMINF + +/* Allow machine dependent optimization for post-increment or pre-increment. + Based on testing to date, + Pre-increment preferred for: + - PowerPC G3 (Adler) + - MIPS R5000 (Randers-Pehrson) + Post-increment preferred for: + - none + No measurable difference: + - Pentium III (Anderson) + - M68060 (Nikl) + */ +#ifdef POSTINC +# define OFF 0 +# define PUP(a) *(a)++ +#else +# define OFF 1 +# define PUP(a) *++(a) +#endif + +/* + Decode literal, length, and distance codes and write out the resulting + literal and match bytes until either not enough input or output is + available, an end-of-block is encountered, or a data error is encountered. + When large enough input and output buffers are supplied to inflate(), for + example, a 16K input buffer and a 64K output buffer, more than 95% of the + inflate execution time is spent in this routine. + + Entry assumptions: + + state->mode == LEN + strm->avail_in >= 6 + strm->avail_out >= 258 + start >= strm->avail_out + state->bits < 8 + + On return, state->mode is one of: + + LEN -- ran out of enough output space or enough available input + TYPE -- reached end of block code, inflate() to interpret next block + BAD -- error in block data + + Notes: + + - The maximum input bits used by a length/distance pair is 15 bits for the + length code, 5 bits for the length extra, 15 bits for the distance code, + and 13 bits for the distance extra. This totals 48 bits, or six bytes. + Therefore if strm->avail_in >= 6, then there is enough input to avoid + checking for available input while decoding. + + - The maximum bytes that a single length/distance pair can output is 258 + bytes, which is the maximum length that can be coded. inflate_fast() + requires strm->avail_out >= 258 for each loop to avoid checking for + output space. + */ +void inflate_fast (z_streamp strm, unsigned start) +{ + struct inflate_state FAR *state; + unsigned char FAR *in; /* local strm->next_in */ + unsigned char FAR *last; /* while in < last, enough input available */ + unsigned char FAR *out; /* local strm->next_out */ + unsigned char FAR *beg; /* inflate()'s initial strm->next_out */ + unsigned char FAR *end; /* while out < end, enough space available */ +#ifdef INFLATE_STRICT + unsigned dmax; /* maximum distance from zlib header */ +#endif + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned write; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if wsize != 0 */ + unsigned long hold; /* local strm->hold */ + unsigned bits; /* local strm->bits */ + code const FAR *lcode; /* local strm->lencode */ + code const FAR *dcode; /* local strm->distcode */ + unsigned lmask; /* mask for first level of length codes */ + unsigned dmask; /* mask for first level of distance codes */ + code thisx; /* retrieved table entry */ + unsigned op; /* code bits, operation, extra bits, or */ + /* window position, window bytes to copy */ + unsigned len; /* match length, unused bytes */ + unsigned dist; /* match distance */ + unsigned char FAR *from; /* where to copy match from */ + + /* copy state to local variables */ + state = (struct inflate_state FAR *)strm->state; + in = strm->next_in - OFF; + last = in + (strm->avail_in - 5); + out = strm->next_out - OFF; + beg = out - (start - strm->avail_out); + end = out + (strm->avail_out - 257); +#ifdef INFLATE_STRICT + dmax = state->dmax; +#endif + wsize = state->wsize; + whave = state->whave; + write = state->write; + window = state->window; + hold = state->hold; + bits = state->bits; + lcode = state->lencode; + dcode = state->distcode; + lmask = (1U << state->lenbits) - 1; + dmask = (1U << state->distbits) - 1; + + /* decode literals and length/distances until end-of-block or not enough + input data or output space */ + do { + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + thisx = lcode[hold & lmask]; + dolen: + op = (unsigned)(thisx.bits); + hold >>= op; + bits -= op; + op = (unsigned)(thisx.op); + if (op == 0) { /* literal */ + Tracevv((stderr, thisx.val >= 0x20 && thisx.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", thisx.val)); + PUP(out) = (unsigned char)(thisx.val); + } + else if (op & 16) { /* length base */ + len = (unsigned)(thisx.val); + op &= 15; /* number of extra bits */ + if (op) { + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + len += (unsigned)hold & ((1U << op) - 1); + hold >>= op; + bits -= op; + } + Tracevv((stderr, "inflate: length %u\n", len)); + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + thisx = dcode[hold & dmask]; + dodist: + op = (unsigned)(thisx.bits); + hold >>= op; + bits -= op; + op = (unsigned)(thisx.op); + if (op & 16) { /* distance base */ + dist = (unsigned)(thisx.val); + op &= 15; /* number of extra bits */ + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + } + dist += (unsigned)hold & ((1U << op) - 1); +#ifdef INFLATE_STRICT + if (dist > dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + hold >>= op; + bits -= op; + Tracevv((stderr, "inflate: distance %u\n", dist)); + op = (unsigned)(out - beg); /* max distance in output */ + if (dist > op) { /* see if copy from window */ + op = dist - op; /* distance back in window */ + if (op > whave) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + from = window - OFF; + if (write == 0) { /* very common case */ + from += wsize - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + else if (write < op) { /* wrap around window */ + from += wsize + write - op; + op -= write; + if (op < len) { /* some from end of window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = window - OFF; + if (write < len) { /* some from start of window */ + op = write; + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + } + else { /* contiguous in window */ + from += write - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + while (len > 2) { + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + else { + from = out - dist; /* copy direct from output */ + do { /* minimum length is three */ + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } while (len > 2); + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + } + else if ((op & 64) == 0) { /* 2nd level distance code */ + thisx = dcode[thisx.val + (hold & ((1U << op) - 1))]; + goto dodist; + } + else { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + } + else if ((op & 64) == 0) { /* 2nd level length code */ + thisx = lcode[thisx.val + (hold & ((1U << op) - 1))]; + goto dolen; + } + else if (op & 32) { /* end-of-block */ + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + else { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + } while (in < last && out < end); + + /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ + len = bits >> 3; + in -= len; + bits -= len << 3; + hold &= (1U << bits) - 1; + + /* update state and return */ + strm->next_in = in + OFF; + strm->next_out = out + OFF; + strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last)); + strm->avail_out = (unsigned)(out < end ? + 257 + (end - out) : 257 - (out - end)); + state->hold = hold; + state->bits = bits; + return; +} + +/* + inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe): + - Using bit fields for code structure + - Different op definition to avoid & for extra bits (do & for table bits) + - Three separate decoding do-loops for direct, window, and write == 0 + - Special case for distance > 1 copies to do overlapped load and store copy + - Explicit branch predictions (based on measured branch probabilities) + - Deferring match copy and interspersed it with decoding subsequent codes + - Swapping literal/length else + - Swapping window/direct else + - Larger unrolled copy loops (three is about right) + - Moving len -= 3 statement into middle of loop + */ + +#endif /* !ASMINF */ +/********* End of inlined file: inffast.c *********/ + + #undef PULLBYTE + #undef LOAD + #undef RESTORE + #undef INITBITS + #undef NEEDBITS + #undef DROPBITS + #undef BYTEBITS + +/********* Start of inlined file: inflate.c *********/ +/* + * Change history: + * + * 1.2.beta0 24 Nov 2002 + * - First version -- complete rewrite of inflate to simplify code, avoid + * creation of window when not needed, minimize use of window when it is + * needed, make inffast.c even faster, implement gzip decoding, and to + * improve code readability and style over the previous zlib inflate code + * + * 1.2.beta1 25 Nov 2002 + * - Use pointers for available input and output checking in inffast.c + * - Remove input and output counters in inffast.c + * - Change inffast.c entry and loop from avail_in >= 7 to >= 6 + * - Remove unnecessary second byte pull from length extra in inffast.c + * - Unroll direct copy to three copies per loop in inffast.c + * + * 1.2.beta2 4 Dec 2002 + * - Change external routine names to reduce potential conflicts + * - Correct filename to inffixed.h for fixed tables in inflate.c + * - Make hbuf[] unsigned char to match parameter type in inflate.c + * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset) + * to avoid negation problem on Alphas (64 bit) in inflate.c + * + * 1.2.beta3 22 Dec 2002 + * - Add comments on state->bits assertion in inffast.c + * - Add comments on op field in inftrees.h + * - Fix bug in reuse of allocated window after inflateReset() + * - Remove bit fields--back to byte structure for speed + * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths + * - Change post-increments to pre-increments in inflate_fast(), PPC biased? + * - Add compile time option, POSTINC, to use post-increments instead (Intel?) + * - Make MATCH copy in inflate() much faster for when inflate_fast() not used + * - Use local copies of stream next and avail values, as well as local bit + * buffer and bit count in inflate()--for speed when inflate_fast() not used + * + * 1.2.beta4 1 Jan 2003 + * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings + * - Move a comment on output buffer sizes from inffast.c to inflate.c + * - Add comments in inffast.c to introduce the inflate_fast() routine + * - Rearrange window copies in inflate_fast() for speed and simplification + * - Unroll last copy for window match in inflate_fast() + * - Use local copies of window variables in inflate_fast() for speed + * - Pull out common write == 0 case for speed in inflate_fast() + * - Make op and len in inflate_fast() unsigned for consistency + * - Add FAR to lcode and dcode declarations in inflate_fast() + * - Simplified bad distance check in inflate_fast() + * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new + * source file infback.c to provide a call-back interface to inflate for + * programs like gzip and unzip -- uses window as output buffer to avoid + * window copying + * + * 1.2.beta5 1 Jan 2003 + * - Improved inflateBack() interface to allow the caller to provide initial + * input in strm. + * - Fixed stored blocks bug in inflateBack() + * + * 1.2.beta6 4 Jan 2003 + * - Added comments in inffast.c on effectiveness of POSTINC + * - Typecasting all around to reduce compiler warnings + * - Changed loops from while (1) or do {} while (1) to for (;;), again to + * make compilers happy + * - Changed type of window in inflateBackInit() to unsigned char * + * + * 1.2.beta7 27 Jan 2003 + * - Changed many types to unsigned or unsigned short to avoid warnings + * - Added inflateCopy() function + * + * 1.2.0 9 Mar 2003 + * - Changed inflateBack() interface to provide separate opaque descriptors + * for the in() and out() functions + * - Changed inflateBack() argument and in_func typedef to swap the length + * and buffer address return values for the input function + * - Check next_in and next_out for Z_NULL on entry to inflate() + * + * The history for versions after 1.2.0 are in ChangeLog in zlib distribution. + */ + +/********* Start of inlined file: inffast.h *********/ +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +void inflate_fast OF((z_streamp strm, unsigned start)); +/********* End of inlined file: inffast.h *********/ + +#ifdef MAKEFIXED +# ifndef BUILDFIXED +# define BUILDFIXED +# endif +#endif + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); +local int updatewindow OF((z_streamp strm, unsigned out)); +#ifdef BUILDFIXED + void makefixed OF((void)); +#endif +local unsigned syncsearch OF((unsigned FAR *have, unsigned char FAR *buf, + unsigned len)); + +int ZEXPORT inflateReset (z_streamp strm) +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + strm->total_in = strm->total_out = state->total = 0; + strm->msg = Z_NULL; + strm->adler = 1; /* to support ill-conceived Java test suite */ + state->mode = HEAD; + state->last = 0; + state->havedict = 0; + state->dmax = 32768U; + state->head = Z_NULL; + state->wsize = 0; + state->whave = 0; + state->write = 0; + state->hold = 0; + state->bits = 0; + state->lencode = state->distcode = state->next = state->codes; + Tracev((stderr, "inflate: reset\n")); + return Z_OK; +} + +int ZEXPORT inflatePrime (z_streamp strm, int bits, int value) +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (bits > 16 || state->bits + bits > 32) return Z_STREAM_ERROR; + value &= (1L << bits) - 1; + state->hold += value << state->bits; + state->bits += bits; + return Z_OK; +} + +int ZEXPORT inflateInit2_(z_streamp strm, int windowBits, const char *version, int stream_size) +{ + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL) return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + state = (struct inflate_state FAR *) + ZALLOC(strm, 1, sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + if (windowBits < 0) { + state->wrap = 0; + windowBits = -windowBits; + } + else { + state->wrap = (windowBits >> 4) + 1; +#ifdef GUNZIP + if (windowBits < 48) windowBits &= 15; +#endif + } + if (windowBits < 8 || windowBits > 15) { + ZFREE(strm, state); + strm->state = Z_NULL; + return Z_STREAM_ERROR; + } + state->wbits = (unsigned)windowBits; + state->window = Z_NULL; + return inflateReset(strm); +} + +int ZEXPORT inflateInit_ (z_streamp strm, const char *version, int stream_size) +{ + return inflateInit2_(strm, DEF_WBITS, version, stream_size); +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables (struct inflate_state FAR *state) +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ + +/********* Start of inlined file: inffixed.h *********/ + /* inffixed.h -- table for decoding fixed codes + * Generated automatically by makefixed(). + */ + + /* WARNING: this file should *not* be used by applications. It + is part of the implementation of the compression library and + is subject to change. Applications should only use zlib.h. + */ + + static const code lenfix[512] = { + {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48}, + {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128}, + {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59}, + {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176}, + {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20}, + {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100}, + {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8}, + {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216}, + {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76}, + {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114}, + {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2}, + {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148}, + {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42}, + {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86}, + {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15}, + {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236}, + {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62}, + {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142}, + {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31}, + {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162}, + {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25}, + {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105}, + {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4}, + {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202}, + {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69}, + {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125}, + {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13}, + {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195}, + {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35}, + {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91}, + {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19}, + {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246}, + {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55}, + {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135}, + {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99}, + {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190}, + {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16}, + {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96}, + {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6}, + {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209}, + {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72}, + {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116}, + {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4}, + {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153}, + {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44}, + {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82}, + {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11}, + {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229}, + {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58}, + {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138}, + {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51}, + {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173}, + {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30}, + {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110}, + {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0}, + {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195}, + {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65}, + {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121}, + {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9}, + {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258}, + {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37}, + {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93}, + {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23}, + {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251}, + {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51}, + {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131}, + {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67}, + {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183}, + {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23}, + {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103}, + {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9}, + {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223}, + {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79}, + {0,9,255} + }; + + static const code distfix[32] = { + {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025}, + {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193}, + {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385}, + {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577}, + {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073}, + {22,5,193},{64,5,0} + }; +/********* End of inlined file: inffixed.h *********/ + +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +#ifdef MAKEFIXED +#include + +/* + Write out the inffixed.h that is #include'd above. Defining MAKEFIXED also + defines BUILDFIXED, so the tables are built on the fly. makefixed() writes + those tables to stdout, which would be piped to inffixed.h. A small program + can simply call makefixed to do this: + + void makefixed(void); + + int main(void) + { + makefixed(); + return 0; + } + + Then that can be linked with zlib built with MAKEFIXED defined and run: + + a.out > inffixed.h + */ +void makefixed() +{ + unsigned low, size; + struct inflate_state state; + + fixedtables(&state); + puts(" /* inffixed.h -- table for decoding fixed codes"); + puts(" * Generated automatically by makefixed()."); + puts(" */"); + puts(""); + puts(" /* WARNING: this file should *not* be used by applications."); + puts(" It is part of the implementation of this library and is"); + puts(" subject to change. Applications should only use zlib.h."); + puts(" */"); + puts(""); + size = 1U << 9; + printf(" static const code lenfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 7) == 0) printf("\n "); + printf("{%u,%u,%d}", state.lencode[low].op, state.lencode[low].bits, + state.lencode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); + size = 1U << 5; + printf("\n static const code distfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 6) == 0) printf("\n "); + printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits, + state.distcode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); +} +#endif /* MAKEFIXED */ + +/* + Update the window with the last wsize (normally 32K) bytes written before + returning. If window does not exist yet, create it. This is only called + when a window is already in use, or when output has been written during this + inflate call, but the end of the deflate stream has not been reached yet. + It is also called to create a window for dictionary data when a dictionary + is loaded. + + Providing output buffers larger than 32K to inflate() should provide a speed + advantage, since only the last 32K of output is copied to the sliding window + upon return from inflate(), and since all distances after the first 32K of + output will fall in the output data, making match copies simpler and faster. + The advantage may be dependent on the size of the processor's data caches. + */ +local int updatewindow (z_streamp strm, unsigned out) +{ + struct inflate_state FAR *state; + unsigned copy, dist; + + state = (struct inflate_state FAR *)strm->state; + + /* if it hasn't been done already, allocate space for the window */ + if (state->window == Z_NULL) { + state->window = (unsigned char FAR *) + ZALLOC(strm, 1U << state->wbits, + sizeof(unsigned char)); + if (state->window == Z_NULL) return 1; + } + + /* if window not in use yet, initialize */ + if (state->wsize == 0) { + state->wsize = 1U << state->wbits; + state->write = 0; + state->whave = 0; + } + + /* copy state->wsize or less output bytes into the circular window */ + copy = out - strm->avail_out; + if (copy >= state->wsize) { + zmemcpy(state->window, strm->next_out - state->wsize, state->wsize); + state->write = 0; + state->whave = state->wsize; + } + else { + dist = state->wsize - state->write; + if (dist > copy) dist = copy; + zmemcpy(state->window + state->write, strm->next_out - copy, dist); + copy -= dist; + if (copy) { + zmemcpy(state->window, strm->next_out - copy, copy); + state->write = copy; + state->whave = state->wsize; + } + else { + state->write += dist; + if (state->write == state->wsize) state->write = 0; + if (state->whave < state->wsize) state->whave += dist; + } + } + return 0; +} + +/* Macros for inflate(): */ + +/* check function to use adler32() for zlib or crc32() for gzip */ +#ifdef GUNZIP +# define UPDATE(check, buf, len) \ + (state->flags ? crc32(check, buf, len) : adler32(check, buf, len)) +#else +# define UPDATE(check, buf, len) adler32(check, buf, len) +#endif + +/* check macros for header crc */ +#ifdef GUNZIP +# define CRC2(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + check = crc32(check, hbuf, 2); \ + } while (0) + +# define CRC4(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + hbuf[2] = (unsigned char)((word) >> 16); \ + hbuf[3] = (unsigned char)((word) >> 24); \ + check = crc32(check, hbuf, 4); \ + } while (0) +#endif + +/* Load registers with state in inflate() for speed */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Restore state from registers in inflate() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflate() + if there is no input available. */ +#define PULLBYTE() \ + do { \ + if (have == 0) goto inf_leave; \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflate(). */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* Reverse the bytes in a 32-bit value */ +#define REVERSE(q) \ + ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \ + (((q) & 0xff00) << 8) + (((q) & 0xff) << 24)) + +/* + inflate() uses a state machine to process as much input data and generate as + much output data as possible before returning. The state machine is + structured roughly as follows: + + for (;;) switch (state) { + ... + case STATEn: + if (not enough input data or output space to make progress) + return; + ... make progress ... + state = STATEm; + break; + ... + } + + so when inflate() is called again, the same case is attempted again, and + if the appropriate resources are provided, the machine proceeds to the + next state. The NEEDBITS() macro is usually the way the state evaluates + whether it can proceed or should return. NEEDBITS() does the return if + the requested bits are not available. The typical use of the BITS macros + is: + + NEEDBITS(n); + ... do something with BITS(n) ... + DROPBITS(n); + + where NEEDBITS(n) either returns from inflate() if there isn't enough + input left to load n bits into the accumulator, or it continues. BITS(n) + gives the low n bits in the accumulator. When done, DROPBITS(n) drops + the low n bits off the accumulator. INITBITS() clears the accumulator + and sets the number of available bits to zero. BYTEBITS() discards just + enough bits to put the accumulator on a byte boundary. After BYTEBITS() + and a NEEDBITS(8), then BITS(8) would return the next byte in the stream. + + NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return + if there is no input available. The decoding of variable length codes uses + PULLBYTE() directly in order to pull just enough bytes to decode the next + code, and no more. + + Some states loop until they get enough input, making sure that enough + state information is maintained to continue the loop where it left off + if NEEDBITS() returns in the loop. For example, want, need, and keep + would all have to actually be part of the saved state in case NEEDBITS() + returns: + + case STATEw: + while (want < need) { + NEEDBITS(n); + keep[want++] = BITS(n); + DROPBITS(n); + } + state = STATEx; + case STATEx: + + As shown above, if the next state is also the next case, then the break + is omitted. + + A state may also return if there is not enough output space available to + complete that state. Those states are copying stored data, writing a + literal byte, and copying a matching string. + + When returning, a "goto inf_leave" is used to update the total counters, + update the check value, and determine whether any progress has been made + during that inflate() call in order to return the proper return code. + Progress is defined as a change in either strm->avail_in or strm->avail_out. + When there is a window, goto inf_leave will update the window with the last + output written. If a goto inf_leave occurs in the middle of decompression + and there is no window currently, goto inf_leave will create one and copy + output to the window for the next call of inflate(). + + In this implementation, the flush parameter of inflate() only affects the + return code (per zlib.h). inflate() always writes as much as possible to + strm->next_out, given the space available and the provided input--the effect + documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers + the allocation of and copying into a sliding window until necessary, which + provides the effect documented in zlib.h for Z_FINISH when the entire input + stream available. So the only thing the flush parameter actually does is: + when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it + will return Z_BUF_ERROR if it has not reached the end of the stream. + */ + +int ZEXPORT inflate (z_streamp strm, int flush) +{ + struct inflate_state FAR *state; + unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned in, out; /* save starting available input and output */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code thisx; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ +#ifdef GUNZIP + unsigned char hbuf[4]; /* buffer for gzip header crc calculation */ +#endif + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + if (strm == Z_NULL || strm->state == Z_NULL || strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0)) + return Z_STREAM_ERROR; + + state = (struct inflate_state FAR *)strm->state; + if (state->mode == TYPE) state->mode = TYPEDO; /* skip check */ + LOAD(); + in = have; + out = left; + ret = Z_OK; + for (;;) + switch (state->mode) { + case HEAD: + if (state->wrap == 0) { + state->mode = TYPEDO; + break; + } + NEEDBITS(16); +#ifdef GUNZIP + if ((state->wrap & 2) && hold == 0x8b1f) { /* gzip header */ + state->check = crc32(0L, Z_NULL, 0); + CRC2(state->check, hold); + INITBITS(); + state->mode = FLAGS; + break; + } + state->flags = 0; /* expect zlib header */ + if (state->head != Z_NULL) + state->head->done = -1; + if (!(state->wrap & 1) || /* check if zlib header allowed */ +#else + if ( +#endif + ((BITS(8) << 8) + (hold >> 8)) % 31) { + strm->msg = (char *)"incorrect header check"; + state->mode = BAD; + break; + } + if (BITS(4) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + DROPBITS(4); + len = BITS(4) + 8; + if (len > state->wbits) { + strm->msg = (char *)"invalid window size"; + state->mode = BAD; + break; + } + state->dmax = 1U << len; + Tracev((stderr, "inflate: zlib header ok\n")); + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = hold & 0x200 ? DICTID : TYPE; + INITBITS(); + break; +#ifdef GUNZIP + case FLAGS: + NEEDBITS(16); + state->flags = (int)(hold); + if ((state->flags & 0xff) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + if (state->flags & 0xe000) { + strm->msg = (char *)"unknown header flags set"; + state->mode = BAD; + break; + } + if (state->head != Z_NULL) + state->head->text = (int)((hold >> 8) & 1); + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = TIME; + case TIME: + NEEDBITS(32); + if (state->head != Z_NULL) + state->head->time = hold; + if (state->flags & 0x0200) CRC4(state->check, hold); + INITBITS(); + state->mode = OS; + case OS: + NEEDBITS(16); + if (state->head != Z_NULL) { + state->head->xflags = (int)(hold & 0xff); + state->head->os = (int)(hold >> 8); + } + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = EXLEN; + case EXLEN: + if (state->flags & 0x0400) { + NEEDBITS(16); + state->length = (unsigned)(hold); + if (state->head != Z_NULL) + state->head->extra_len = (unsigned)hold; + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + } + else if (state->head != Z_NULL) + state->head->extra = Z_NULL; + state->mode = EXTRA; + case EXTRA: + if (state->flags & 0x0400) { + copy = state->length; + if (copy > have) copy = have; + if (copy) { + if (state->head != Z_NULL && + state->head->extra != Z_NULL) { + len = state->head->extra_len - state->length; + zmemcpy(state->head->extra + len, next, + len + copy > state->head->extra_max ? + state->head->extra_max - len : copy); + } + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + state->length -= copy; + } + if (state->length) goto inf_leave; + } + state->length = 0; + state->mode = NAME; + case NAME: + if (state->flags & 0x0800) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->name != Z_NULL && + state->length < state->head->name_max) + state->head->name[state->length++] = len; + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->name = Z_NULL; + state->length = 0; + state->mode = COMMENT; + case COMMENT: + if (state->flags & 0x1000) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->comment != Z_NULL && + state->length < state->head->comm_max) + state->head->comment[state->length++] = len; + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->comment = Z_NULL; + state->mode = HCRC; + case HCRC: + if (state->flags & 0x0200) { + NEEDBITS(16); + if (hold != (state->check & 0xffff)) { + strm->msg = (char *)"header crc mismatch"; + state->mode = BAD; + break; + } + INITBITS(); + } + if (state->head != Z_NULL) { + state->head->hcrc = (int)((state->flags >> 9) & 1); + state->head->done = 1; + } + strm->adler = state->check = crc32(0L, Z_NULL, 0); + state->mode = TYPE; + break; +#endif + case DICTID: + NEEDBITS(32); + strm->adler = state->check = REVERSE(hold); + INITBITS(); + state->mode = DICT; + case DICT: + if (state->havedict == 0) { + RESTORE(); + return Z_NEED_DICT; + } + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = TYPE; + case TYPE: + if (flush == Z_BLOCK) goto inf_leave; + case TYPEDO: + if (state->last) { + BYTEBITS(); + state->mode = CHECK; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN; /* decode codes */ + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + case STORED: + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + state->mode = COPY; + case COPY: + copy = state->length; + if (copy) { + if (copy > have) copy = have; + if (copy > left) copy = left; + if (copy == 0) goto inf_leave; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + break; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + case TABLE: + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + state->have = 0; + state->mode = LENLENS; + case LENLENS: + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + state->have = 0; + state->mode = CODELENS; + case CODELENS: + while (state->have < state->nlen + state->ndist) { + for (;;) { + thisx = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(thisx.bits) <= bits) break; + PULLBYTE(); + } + if (thisx.val < 16) { + NEEDBITS(thisx.bits); + DROPBITS(thisx.bits); + state->lens[state->have++] = thisx.val; + } + else { + if (thisx.val == 16) { + NEEDBITS(thisx.bits + 2); + DROPBITS(thisx.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = state->lens[state->have - 1]; + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (thisx.val == 17) { + NEEDBITS(thisx.bits + 3); + DROPBITS(thisx.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(thisx.bits + 7); + DROPBITS(thisx.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* build code tables */ + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (code const FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN; + case LEN: + if (have >= 6 && left >= 258) { + RESTORE(); + inflate_fast(strm, out); + LOAD(); + break; + } + for (;;) { + thisx = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(thisx.bits) <= bits) break; + PULLBYTE(); + } + if (thisx.op && (thisx.op & 0xf0) == 0) { + last = thisx; + for (;;) { + thisx = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + thisx.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(thisx.bits); + state->length = (unsigned)thisx.val; + if ((int)(thisx.op) == 0) { + Tracevv((stderr, thisx.val >= 0x20 && thisx.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", thisx.val)); + state->mode = LIT; + break; + } + if (thisx.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + if (thisx.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + state->extra = (unsigned)(thisx.op) & 15; + state->mode = LENEXT; + case LENEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + state->mode = DIST; + case DIST: + for (;;) { + thisx = state->distcode[BITS(state->distbits)]; + if ((unsigned)(thisx.bits) <= bits) break; + PULLBYTE(); + } + if ((thisx.op & 0xf0) == 0) { + last = thisx; + for (;;) { + thisx = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + thisx.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(thisx.bits); + if (thisx.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)thisx.val; + state->extra = (unsigned)(thisx.op) & 15; + state->mode = DISTEXT; + case DISTEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + } +#ifdef INFLATE_STRICT + if (state->offset > state->dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + if (state->offset > state->whave + out - left) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + state->mode = MATCH; + case MATCH: + if (left == 0) goto inf_leave; + copy = out - left; + if (state->offset > copy) { /* copy from window */ + copy = state->offset - copy; + if (copy > state->write) { + copy -= state->write; + from = state->window + (state->wsize - copy); + } + else + from = state->window + (state->write - copy); + if (copy > state->length) copy = state->length; + } + else { /* copy from output */ + from = put - state->offset; + copy = state->length; + } + if (copy > left) copy = left; + left -= copy; + state->length -= copy; + do { + *put++ = *from++; + } while (--copy); + if (state->length == 0) state->mode = LEN; + break; + case LIT: + if (left == 0) goto inf_leave; + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + case CHECK: + if (state->wrap) { + NEEDBITS(32); + out -= left; + strm->total_out += out; + state->total += out; + if (out) + strm->adler = state->check = + UPDATE(state->check, put - out, out); + out = left; + if (( +#ifdef GUNZIP + state->flags ? hold : +#endif + REVERSE(hold)) != state->check) { + strm->msg = (char *)"incorrect data check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: check matches trailer\n")); + } +#ifdef GUNZIP + state->mode = LENGTH; + case LENGTH: + if (state->wrap && state->flags) { + NEEDBITS(32); + if (hold != (state->total & 0xffffffffUL)) { + strm->msg = (char *)"incorrect length check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: length matches trailer\n")); + } +#endif + state->mode = DONE; + case DONE: + ret = Z_STREAM_END; + goto inf_leave; + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + case MEM: + return Z_MEM_ERROR; + case SYNC: + default: + return Z_STREAM_ERROR; + } + + /* + Return from inflate(), updating the total counts and the check value. + If there was no progress during the inflate() call, return a buffer + error. Call updatewindow() to create and/or update the window state. + Note: a memory error from inflate() is non-recoverable. + */ + inf_leave: + RESTORE(); + if (state->wsize || (state->mode < CHECK && out != strm->avail_out)) + if (updatewindow(strm, out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + in -= strm->avail_in; + out -= strm->avail_out; + strm->total_in += in; + strm->total_out += out; + state->total += out; + if (state->wrap && out) + strm->adler = state->check = + UPDATE(state->check, strm->next_out - out, out); + strm->data_type = state->bits + (state->last ? 64 : 0) + + (state->mode == TYPE ? 128 : 0); + if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK) + ret = Z_BUF_ERROR; + return ret; +} + +int ZEXPORT inflateEnd (z_streamp strm) +{ + struct inflate_state FAR *state; + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->window != Z_NULL) ZFREE(strm, state->window); + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} + +int ZEXPORT inflateSetDictionary (z_streamp strm, const Bytef *dictionary, uInt dictLength) +{ + struct inflate_state FAR *state; + unsigned long id_; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->wrap != 0 && state->mode != DICT) + return Z_STREAM_ERROR; + + /* check for correct dictionary id */ + if (state->mode == DICT) { + id_ = adler32(0L, Z_NULL, 0); + id_ = adler32(id_, dictionary, dictLength); + if (id_ != state->check) + return Z_DATA_ERROR; + } + + /* copy dictionary to window */ + if (updatewindow(strm, strm->avail_out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + if (dictLength > state->wsize) { + zmemcpy(state->window, dictionary + dictLength - state->wsize, + state->wsize); + state->whave = state->wsize; + } + else { + zmemcpy(state->window + state->wsize - dictLength, dictionary, + dictLength); + state->whave = dictLength; + } + state->havedict = 1; + Tracev((stderr, "inflate: dictionary set\n")); + return Z_OK; +} + +int ZEXPORT inflateGetHeader (z_streamp strm, gz_headerp head) +{ + struct inflate_state FAR *state; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if ((state->wrap & 2) == 0) return Z_STREAM_ERROR; + + /* save header structure */ + state->head = head; + head->done = 0; + return Z_OK; +} + +/* + Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff. Return when found + or when out of input. When called, *have is the number of pattern bytes + found in order so far, in 0..3. On return *have is updated to the new + state. If on return *have equals four, then the pattern was found and the + return value is how many bytes were read including the last byte of the + pattern. If *have is less than four, then the pattern has not been found + yet and the return value is len. In the latter case, syncsearch() can be + called again with more data and the *have state. *have is initialized to + zero for the first call. + */ +local unsigned syncsearch (unsigned FAR *have, unsigned char FAR *buf, unsigned len) +{ + unsigned got; + unsigned next; + + got = *have; + next = 0; + while (next < len && got < 4) { + if ((int)(buf[next]) == (got < 2 ? 0 : 0xff)) + got++; + else if (buf[next]) + got = 0; + else + got = 4 - got; + next++; + } + *have = got; + return next; +} + +int ZEXPORT inflateSync (z_streamp strm) +{ + unsigned len; /* number of bytes to look at or looked at */ + unsigned long in, out; /* temporary to save total_in and total_out */ + unsigned char buf[4]; /* to restore bit buffer to byte string */ + struct inflate_state FAR *state; + + /* check parameters */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR; + + /* if first time, start search in bit buffer */ + if (state->mode != SYNC) { + state->mode = SYNC; + state->hold <<= state->bits & 7; + state->bits -= state->bits & 7; + len = 0; + while (state->bits >= 8) { + buf[len++] = (unsigned char)(state->hold); + state->hold >>= 8; + state->bits -= 8; + } + state->have = 0; + syncsearch(&(state->have), buf, len); + } + + /* search available input */ + len = syncsearch(&(state->have), strm->next_in, strm->avail_in); + strm->avail_in -= len; + strm->next_in += len; + strm->total_in += len; + + /* return no joy or set up to restart inflate() on a new block */ + if (state->have != 4) return Z_DATA_ERROR; + in = strm->total_in; out = strm->total_out; + inflateReset(strm); + strm->total_in = in; strm->total_out = out; + state->mode = TYPE; + return Z_OK; +} + +/* + Returns true if inflate is currently at the end of a block generated by + Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP + implementation to provide an additional safety check. PPP uses + Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored + block. When decompressing, PPP checks that at the end of input packet, + inflate is waiting for these length bytes. + */ +int ZEXPORT inflateSyncPoint (z_streamp strm) +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + return state->mode == STORED && state->bits == 0; +} + +int ZEXPORT inflateCopy(z_streamp dest, z_streamp source) +{ + struct inflate_state FAR *state; + struct inflate_state FAR *copy; + unsigned char FAR *window; + unsigned wsize; + + /* check input */ + if (dest == Z_NULL || source == Z_NULL || source->state == Z_NULL || + source->zalloc == (alloc_func)0 || source->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)source->state; + + /* allocate space */ + copy = (struct inflate_state FAR *) + ZALLOC(source, 1, sizeof(struct inflate_state)); + if (copy == Z_NULL) return Z_MEM_ERROR; + window = Z_NULL; + if (state->window != Z_NULL) { + window = (unsigned char FAR *) + ZALLOC(source, 1U << state->wbits, sizeof(unsigned char)); + if (window == Z_NULL) { + ZFREE(source, copy); + return Z_MEM_ERROR; + } + } + + /* copy state */ + zmemcpy(dest, source, sizeof(z_stream)); + zmemcpy(copy, state, sizeof(struct inflate_state)); + if (state->lencode >= state->codes && + state->lencode <= state->codes + ENOUGH - 1) { + copy->lencode = copy->codes + (state->lencode - state->codes); + copy->distcode = copy->codes + (state->distcode - state->codes); + } + copy->next = copy->codes + (state->next - state->codes); + if (window != Z_NULL) { + wsize = 1U << state->wbits; + zmemcpy(window, state->window, wsize); + } + copy->window = window; + dest->state = (struct internal_state FAR *)copy; + return Z_OK; +} +/********* End of inlined file: inflate.c *********/ + +/********* Start of inlined file: inftrees.c *********/ + +#define MAXBITS 15 + +const char inflate_copyright[] = + " inflate 1.2.3 Copyright 1995-2005 Mark Adler "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* + Build a set of tables to decode the provided canonical Huffman code. + The code lengths are lens[0..codes-1]. The result starts at *table, + whose indices are 0..2^bits-1. work is a writable array of at least + lens shorts, which is used as a work area. type is the type of code + to be generated, CODES, LENS, or DISTS. On return, zero is success, + -1 is an invalid code, and +1 means that ENOUGH isn't enough. table + on return points to the next available entry's address. bits is the + requested root table index bits, and on return it is the actual root + table index bits. It will differ if the request is greater than the + longest code or if it is less than the shortest code. + */ +int inflate_table (codetype type, + unsigned short FAR *lens, + unsigned codes, + code FAR * FAR *table, + unsigned FAR *bits, + unsigned short FAR *work) +{ + unsigned len; /* a code's length in bits */ + unsigned sym; /* index of code symbols */ + unsigned min, max; /* minimum and maximum code lengths */ + unsigned root; /* number of index bits for root table */ + unsigned curr; /* number of index bits for current table */ + unsigned drop; /* code bits to drop for sub-table */ + int left; /* number of prefix codes available */ + unsigned used; /* code entries in table used */ + unsigned huff; /* Huffman code */ + unsigned incr; /* for incrementing code, index */ + unsigned fill; /* index for replicating entries */ + unsigned low; /* low bits for current root entry */ + unsigned mask; /* mask for low root bits */ + code thisx; /* table entry for duplication */ + code FAR *next; /* next available space in table */ + const unsigned short FAR *base; /* base value table to use */ + const unsigned short FAR *extra; /* extra bits table to use */ + int end; /* use base and extra for symbol > end */ + unsigned short count[MAXBITS+1]; /* number of codes of each length */ + unsigned short offs[MAXBITS+1]; /* offsets in table for each length */ + static const unsigned short lbase[31] = { /* Length codes 257..285 base */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + static const unsigned short lext[31] = { /* Length codes 257..285 extra */ + 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 201, 196}; + static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577, 0, 0}; + static const unsigned short dext[32] = { /* Distance codes 0..29 extra */ + 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, + 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, + 28, 28, 29, 29, 64, 64}; + + /* + Process a set of code lengths to create a canonical Huffman code. The + code lengths are lens[0..codes-1]. Each length corresponds to the + symbols 0..codes-1. The Huffman code is generated by first sorting the + symbols by length from short to long, and retaining the symbol order + for codes with equal lengths. Then the code starts with all zero bits + for the first code of the shortest length, and the codes are integer + increments for the same length, and zeros are appended as the length + increases. For the deflate format, these bits are stored backwards + from their more natural integer increment ordering, and so when the + decoding tables are built in the large loop below, the integer codes + are incremented backwards. + + This routine assumes, but does not check, that all of the entries in + lens[] are in the range 0..MAXBITS. The caller must assure this. + 1..MAXBITS is interpreted as that code length. zero means that that + symbol does not occur in this code. + + The codes are sorted by computing a count of codes for each length, + creating from that a table of starting indices for each length in the + sorted table, and then entering the symbols in order in the sorted + table. The sorted table is work[], with that space being provided by + the caller. + + The length counts are used for other purposes as well, i.e. finding + the minimum and maximum length codes, determining if there are any + codes at all, checking for a valid set of lengths, and looking ahead + at length counts to determine sub-table sizes when building the + decoding tables. + */ + + /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ + for (len = 0; len <= MAXBITS; len++) + count[len] = 0; + for (sym = 0; sym < codes; sym++) + count[lens[sym]]++; + + /* bound code lengths, force root to be within code lengths */ + root = *bits; + for (max = MAXBITS; max >= 1; max--) + if (count[max] != 0) break; + if (root > max) root = max; + if (max == 0) { /* no symbols to code at all */ + thisx.op = (unsigned char)64; /* invalid code marker */ + thisx.bits = (unsigned char)1; + thisx.val = (unsigned short)0; + *(*table)++ = thisx; /* make a table to force an error */ + *(*table)++ = thisx; + *bits = 1; + return 0; /* no symbols, but wait for decoding to report error */ + } + for (min = 1; min <= MAXBITS; min++) + if (count[min] != 0) break; + if (root < min) root = min; + + /* check for an over-subscribed or incomplete set of lengths */ + left = 1; + for (len = 1; len <= MAXBITS; len++) { + left <<= 1; + left -= count[len]; + if (left < 0) return -1; /* over-subscribed */ + } + if (left > 0 && (type == CODES || max != 1)) + return -1; /* incomplete set */ + + /* generate offsets into symbol table for each length for sorting */ + offs[1] = 0; + for (len = 1; len < MAXBITS; len++) + offs[len + 1] = offs[len] + count[len]; + + /* sort symbols by length, by symbol order within each length */ + for (sym = 0; sym < codes; sym++) + if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym; + + /* + Create and fill in decoding tables. In this loop, the table being + filled is at next and has curr index bits. The code being used is huff + with length len. That code is converted to an index by dropping drop + bits off of the bottom. For codes where len is less than drop + curr, + those top drop + curr - len bits are incremented through all values to + fill the table with replicated entries. + + root is the number of index bits for the root table. When len exceeds + root, sub-tables are created pointed to by the root entry with an index + of the low root bits of huff. This is saved in low to check for when a + new sub-table should be started. drop is zero when the root table is + being filled, and drop is root when sub-tables are being filled. + + When a new sub-table is needed, it is necessary to look ahead in the + code lengths to determine what size sub-table is needed. The length + counts are used for this, and so count[] is decremented as codes are + entered in the tables. + + used keeps track of how many table entries have been allocated from the + provided *table space. It is checked when a LENS table is being made + against the space in *table, ENOUGH, minus the maximum space needed by + the worst case distance code, MAXD. This should never happen, but the + sufficiency of ENOUGH has not been proven exhaustively, hence the check. + This assumes that when type == LENS, bits == 9. + + sym increments through all symbols, and the loop terminates when + all codes of length max, i.e. all codes, have been processed. This + routine permits incomplete codes, so another loop after this one fills + in the rest of the decoding tables with invalid code markers. + */ + + /* set up for code type */ + switch (type) { + case CODES: + base = extra = work; /* dummy value--not used */ + end = 19; + break; + case LENS: + base = lbase; + base -= 257; + extra = lext; + extra -= 257; + end = 256; + break; + default: /* DISTS */ + base = dbase; + extra = dext; + end = -1; + } + + /* initialize state for loop */ + huff = 0; /* starting code */ + sym = 0; /* starting code symbol */ + len = min; /* starting code length */ + next = *table; /* current table to fill in */ + curr = root; /* current table index bits */ + drop = 0; /* current bits to drop from code for index */ + low = (unsigned)(-1); /* trigger new sub-table when len > root */ + used = 1U << root; /* use root table entries */ + mask = used - 1; /* mask for comparing low */ + + /* check available table space */ + if (type == LENS && used >= ENOUGH - MAXD) + return 1; + + /* process all codes and make table entries */ + for (;;) { + /* create table entry */ + thisx.bits = (unsigned char)(len - drop); + if ((int)(work[sym]) < end) { + thisx.op = (unsigned char)0; + thisx.val = work[sym]; + } + else if ((int)(work[sym]) > end) { + thisx.op = (unsigned char)(extra[work[sym]]); + thisx.val = base[work[sym]]; + } + else { + thisx.op = (unsigned char)(32 + 64); /* end of block */ + thisx.val = 0; + } + + /* replicate for those indices with low len bits equal to huff */ + incr = 1U << (len - drop); + fill = 1U << curr; + min = fill; /* save offset to next table */ + do { + fill -= incr; + next[(huff >> drop) + fill] = thisx; + } while (fill != 0); + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + + /* go to next symbol, update count, len */ + sym++; + if (--(count[len]) == 0) { + if (len == max) break; + len = lens[work[sym]]; + } + + /* create new sub-table if needed */ + if (len > root && (huff & mask) != low) { + /* if first time, transition to sub-tables */ + if (drop == 0) + drop = root; + + /* increment past last table */ + next += min; /* here min is 1 << curr */ + + /* determine length of next table */ + curr = len - drop; + left = (int)(1 << curr); + while (curr + drop < max) { + left -= count[curr + drop]; + if (left <= 0) break; + curr++; + left <<= 1; + } + + /* check for enough space */ + used += 1U << curr; + if (type == LENS && used >= ENOUGH - MAXD) + return 1; + + /* point entry in root table to sub-table */ + low = huff & mask; + (*table)[low].op = (unsigned char)curr; + (*table)[low].bits = (unsigned char)root; + (*table)[low].val = (unsigned short)(next - *table); + } + } + + /* + Fill in rest of table for incomplete codes. This loop is similar to the + loop above in incrementing huff for table indices. It is assumed that + len is equal to curr + drop, so there is no loop needed to increment + through high index bits. When the current sub-table is filled, the loop + drops back to the root table to fill in any remaining entries there. + */ + thisx.op = (unsigned char)64; /* invalid code marker */ + thisx.bits = (unsigned char)(len - drop); + thisx.val = (unsigned short)0; + while (huff != 0) { + /* when done with sub-table, drop back to root table */ + if (drop != 0 && (huff & mask) != low) { + drop = 0; + len = root; + next = *table; + thisx.bits = (unsigned char)len; + } + + /* put invalid code marker in table */ + next[huff >> drop] = thisx; + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + } + + /* set return parameters */ + *table += used; + *bits = root; + return 0; +} +/********* End of inlined file: inftrees.c *********/ + +/********* Start of inlined file: trees.c *********/ +/* + * ALGORITHM + * + * The "deflation" process uses several Huffman trees. The more + * common source values are represented by shorter bit sequences. + * + * Each code tree is stored in a compressed form which is itself + * a Huffman encoding of the lengths of all the code strings (in + * ascending order by source values). The actual code strings are + * reconstructed from the lengths in the inflate process, as described + * in the deflate specification. + * + * REFERENCES + * + * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". + * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc + * + * Storer, James A. + * Data Compression: Methods and Theory, pp. 49-50. + * Computer Science Press, 1988. ISBN 0-7167-8156-5. + * + * Sedgewick, R. + * Algorithms, p290. + * Addison-Wesley, 1983. ISBN 0-201-06672-6. + */ + +/* @(#) $Id: trees.c,v 1.1 2007/06/07 17:54:37 jules_rms Exp $ */ + +/* #define GEN_TREES_H */ + +#ifdef DEBUG +# include +#endif + +/* =========================================================================== + * Constants + */ + +#define MAX_BL_BITS 7 +/* Bit length codes must not exceed MAX_BL_BITS bits */ + +#define END_BLOCK 256 +/* end of block literal code */ + +#define REP_3_6 16 +/* repeat previous bit length 3-6 times (2 bits of repeat count) */ + +#define REPZ_3_10 17 +/* repeat a zero length 3-10 times (3 bits of repeat count) */ + +#define REPZ_11_138 18 +/* repeat a zero length 11-138 times (7 bits of repeat count) */ + +local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ + = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; + +local const int extra_dbits[D_CODES] /* extra bits for each distance code */ + = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */ + = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + +local const uch bl_order[BL_CODES] + = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; +/* The lengths of the bit length codes are sent in order of decreasing + * probability, to avoid transmitting the lengths for unused bit length codes. + */ + +#define Buf_size (8 * 2*sizeof(char)) +/* Number of bits used within bi_buf. (bi_buf might be implemented on + * more than 16 bits on some systems.) + */ + +/* =========================================================================== + * Local data. These are initialized only once. + */ + +#define DIST_CODE_LEN 512 /* see definition of array dist_code below */ + +#if defined(GEN_TREES_H) || !defined(STDC) +/* non ANSI compilers may not accept trees.h */ + +local ct_data static_ltree[L_CODES+2]; +/* The static literal tree. Since the bit lengths are imposed, there is no + * need for the L_CODES extra codes used during heap construction. However + * The codes 286 and 287 are needed to build a canonical tree (see _tr_init + * below). + */ + +local ct_data static_dtree[D_CODES]; +/* The static distance tree. (Actually a trivial tree since all codes use + * 5 bits.) + */ + +uch _dist_code[DIST_CODE_LEN]; +/* Distance codes. The first 256 values correspond to the distances + * 3 .. 258, the last 256 values correspond to the top 8 bits of + * the 15 bit distances. + */ + +uch _length_code[MAX_MATCH-MIN_MATCH+1]; +/* length code for each normalized match length (0 == MIN_MATCH) */ + +local int base_length[LENGTH_CODES]; +/* First normalized length for each code (0 = MIN_MATCH) */ + +local int base_dist[D_CODES]; +/* First normalized distance for each code (0 = distance of 1) */ + +#else + +/********* Start of inlined file: trees.h *********/ +local const ct_data static_ltree[L_CODES+2] = { +{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}}, +{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}}, +{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}}, +{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}}, +{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}}, +{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}}, +{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}}, +{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}}, +{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}}, +{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}}, +{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}}, +{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}}, +{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}}, +{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}}, +{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}}, +{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}}, +{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}}, +{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}}, +{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}}, +{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}}, +{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}}, +{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}}, +{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}}, +{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}}, +{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}}, +{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}}, +{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}}, +{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}}, +{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}}, +{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}}, +{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}}, +{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}}, +{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}}, +{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}}, +{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}}, +{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}}, +{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}}, +{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}}, +{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}}, +{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}}, +{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}}, +{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}}, +{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}}, +{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}}, +{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}}, +{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}}, +{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}}, +{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}}, +{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}}, +{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}}, +{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}}, +{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}}, +{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}}, +{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}}, +{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}}, +{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}}, +{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}}, +{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}} +}; + +local const ct_data static_dtree[D_CODES] = { +{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}}, +{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}}, +{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}}, +{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}}, +{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}}, +{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}} +}; + +const uch _dist_code[DIST_CODE_LEN] = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, +10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, +11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, +12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, +18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 +}; + +const uch _length_code[MAX_MATCH-MIN_MATCH+1]= { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, +13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, +17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, +19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, +22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 +}; + +local const int base_length[LENGTH_CODES] = { +0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, +64, 80, 96, 112, 128, 160, 192, 224, 0 +}; + +local const int base_dist[D_CODES] = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, + 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 +}; +/********* End of inlined file: trees.h *********/ + +#endif /* GEN_TREES_H */ + +struct static_tree_desc_s { + const ct_data *static_tree; /* static tree or NULL */ + const intf *extra_bits; /* extra bits for each code or NULL */ + int extra_base; /* base index for extra_bits */ + int elems; /* max number of elements in the tree */ + int max_length; /* max bit length for the codes */ +}; + +local static_tree_desc static_l_desc = +{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; + +local static_tree_desc static_d_desc = +{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; + +local static_tree_desc static_bl_desc = +{(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; + +/* =========================================================================== + * Local (static) routines in this file. + */ + +local void tr_static_init OF((void)); +local void init_block OF((deflate_state *s)); +local void pqdownheap OF((deflate_state *s, ct_data *tree, int k)); +local void gen_bitlen OF((deflate_state *s, tree_desc *desc)); +local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count)); +local void build_tree OF((deflate_state *s, tree_desc *desc)); +local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local void send_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local int build_bl_tree OF((deflate_state *s)); +local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, + int blcodes)); +local void compress_block OF((deflate_state *s, ct_data *ltree, + ct_data *dtree)); +local void set_data_type OF((deflate_state *s)); +local unsigned bi_reverse OF((unsigned value, int length)); +local void bi_windup OF((deflate_state *s)); +local void bi_flush OF((deflate_state *s)); +local void copy_block OF((deflate_state *s, charf *buf, unsigned len, + int header)); + +#ifdef GEN_TREES_H +local void gen_trees_header OF((void)); +#endif + +#ifndef DEBUG +# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) + /* Send a code of the given tree. c and tree must not have side effects */ + +#else /* DEBUG */ +# define send_code(s, c, tree) \ + { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \ + send_bits(s, tree[c].Code, tree[c].Len); } +#endif + +/* =========================================================================== + * Output a short LSB first on the stream. + * IN assertion: there is enough room in pendingBuf. + */ +#define put_short(s, w) { \ + put_byte(s, (uch)((w) & 0xff)); \ + put_byte(s, (uch)((ush)(w) >> 8)); \ +} + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +#ifdef DEBUG +local void send_bits OF((deflate_state *s, int value, int length)); + +local void send_bits (deflate_state *s, int value, int length) +{ + Tracevv((stderr," l %2d v %4x ", length, value)); + Assert(length > 0 && length <= 15, "invalid length"); + s->bits_sent += (ulg)length; + + /* If not enough room in bi_buf, use (valid) bits from bi_buf and + * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) + * unused bits in value. + */ + if (s->bi_valid > (int)Buf_size - length) { + s->bi_buf |= (value << s->bi_valid); + put_short(s, s->bi_buf); + s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); + s->bi_valid += length - Buf_size; + } else { + s->bi_buf |= value << s->bi_valid; + s->bi_valid += length; + } +} +#else /* !DEBUG */ + +#define send_bits(s, value, length) \ +{ int len = length;\ + if (s->bi_valid > (int)Buf_size - len) {\ + int val = value;\ + s->bi_buf |= (val << s->bi_valid);\ + put_short(s, s->bi_buf);\ + s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ + s->bi_valid += len - Buf_size;\ + } else {\ + s->bi_buf |= (value) << s->bi_valid;\ + s->bi_valid += len;\ + }\ +} +#endif /* DEBUG */ + +/* the arguments must not have side effects */ + +/* =========================================================================== + * Initialize the various 'constant' tables. + */ +local void tr_static_init() +{ +#if defined(GEN_TREES_H) || !defined(STDC) + static int static_init_done = 0; + int n; /* iterates over tree elements */ + int bits; /* bit counter */ + int length; /* length value */ + int code; /* code value */ + int dist; /* distance index */ + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + if (static_init_done) return; + + /* For some embedded targets, global variables are not initialized: */ + static_l_desc.static_tree = static_ltree; + static_l_desc.extra_bits = extra_lbits; + static_d_desc.static_tree = static_dtree; + static_d_desc.extra_bits = extra_dbits; + static_bl_desc.extra_bits = extra_blbits; + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES-1; code++) { + base_length[code] = length; + for (n = 0; n < (1< dist code (0..29) */ + dist = 0; + for (code = 0 ; code < 16; code++) { + base_dist[code] = dist; + for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */ + for ( ; code < D_CODES; code++) { + base_dist[code] = dist << 7; + for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { + _dist_code[256 + dist++] = (uch)code; + } + } + Assert (dist == 256, "tr_static_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; + n = 0; + while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; + while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; + while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; + while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + static_dtree[n].Len = 5; + static_dtree[n].Code = bi_reverse((unsigned)n, 5); + } + static_init_done = 1; + +# ifdef GEN_TREES_H + gen_trees_header(); +# endif +#endif /* defined(GEN_TREES_H) || !defined(STDC) */ +} + +/* =========================================================================== + * Genererate the file trees.h describing the static trees. + */ +#ifdef GEN_TREES_H +# ifndef DEBUG +# include +# endif + +# define SEPARATOR(i, last, width) \ + ((i) == (last)? "\n};\n\n" : \ + ((i) % (width) == (width)-1 ? ",\n" : ", ")) + +void gen_trees_header() +{ + FILE *header = fopen("trees.h", "w"); + int i; + + Assert (header != NULL, "Can't open trees.h"); + fprintf(header, + "/* header created automatically with -DGEN_TREES_H */\n\n"); + + fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n"); + for (i = 0; i < L_CODES+2; i++) { + fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code, + static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5)); + } + + fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code, + static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5)); + } + + fprintf(header, "const uch _dist_code[DIST_CODE_LEN] = {\n"); + for (i = 0; i < DIST_CODE_LEN; i++) { + fprintf(header, "%2u%s", _dist_code[i], + SEPARATOR(i, DIST_CODE_LEN-1, 20)); + } + + fprintf(header, "const uch _length_code[MAX_MATCH-MIN_MATCH+1]= {\n"); + for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) { + fprintf(header, "%2u%s", _length_code[i], + SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20)); + } + + fprintf(header, "local const int base_length[LENGTH_CODES] = {\n"); + for (i = 0; i < LENGTH_CODES; i++) { + fprintf(header, "%1u%s", base_length[i], + SEPARATOR(i, LENGTH_CODES-1, 20)); + } + + fprintf(header, "local const int base_dist[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "%5u%s", base_dist[i], + SEPARATOR(i, D_CODES-1, 10)); + } + + fclose(header); +} +#endif /* GEN_TREES_H */ + +/* =========================================================================== + * Initialize the tree data structures for a new zlib stream. + */ +void _tr_init(deflate_state *s) +{ + tr_static_init(); + + s->l_desc.dyn_tree = s->dyn_ltree; + s->l_desc.stat_desc = &static_l_desc; + + s->d_desc.dyn_tree = s->dyn_dtree; + s->d_desc.stat_desc = &static_d_desc; + + s->bl_desc.dyn_tree = s->bl_tree; + s->bl_desc.stat_desc = &static_bl_desc; + + s->bi_buf = 0; + s->bi_valid = 0; + s->last_eob_len = 8; /* enough lookahead for inflate */ +#ifdef DEBUG + s->compressed_len = 0L; + s->bits_sent = 0L; +#endif + + /* Initialize the first block of the first file: */ + init_block(s); +} + +/* =========================================================================== + * Initialize a new block. + */ +local void init_block (deflate_state *s) +{ + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; + for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; + for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; + + s->dyn_ltree[END_BLOCK].Freq = 1; + s->opt_len = s->static_len = 0L; + s->last_lit = s->matches = 0; +} + +#define SMALLEST 1 +/* Index within the heap array of least frequent node in the Huffman tree */ + +/* =========================================================================== + * Remove the smallest element from the heap and recreate the heap with + * one less element. Updates heap and heap_len. + */ +#define pqremove(s, tree, top) \ +{\ + top = s->heap[SMALLEST]; \ + s->heap[SMALLEST] = s->heap[s->heap_len--]; \ + pqdownheap(s, tree, SMALLEST); \ +} + +/* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +#define smaller(tree, n, m, depth) \ + (tree[n].Freq < tree[m].Freq || \ + (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) + +/* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +local void pqdownheap (deflate_state *s, + ct_data *tree, /* the tree to restore */ + int k) /* node to move down */ +{ + int v = s->heap[k]; + int j = k << 1; /* left son of k */ + while (j <= s->heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < s->heap_len && + smaller(tree, s->heap[j+1], s->heap[j], s->depth)) { + j++; + } + /* Exit if v is smaller than both sons */ + if (smaller(tree, v, s->heap[j], s->depth)) break; + + /* Exchange v with the smallest son */ + s->heap[k] = s->heap[j]; k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + s->heap[k] = v; +} + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +local void gen_bitlen (deflate_state *s, tree_desc *desc) +{ + ct_data *tree = desc->dyn_tree; + int max_code = desc->max_code; + const ct_data *stree = desc->stat_desc->static_tree; + const intf *extra = desc->stat_desc->extra_bits; + int base = desc->stat_desc->extra_base; + int max_length = desc->stat_desc->max_length; + int h; /* heap index */ + int n, m; /* iterate over the tree elements */ + int bits; /* bit length */ + int xbits; /* extra bits */ + ush f; /* frequency */ + int overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ + + for (h = s->heap_max+1; h < HEAP_SIZE; h++) { + n = s->heap[h]; + bits = tree[tree[n].Dad].Len + 1; + if (bits > max_length) bits = max_length, overflow++; + tree[n].Len = (ush)bits; + /* We overwrite tree[n].Dad which is no longer needed */ + + if (n > max_code) continue; /* not a leaf node */ + + s->bl_count[bits]++; + xbits = 0; + if (n >= base) xbits = extra[n-base]; + f = tree[n].Freq; + s->opt_len += (ulg)f * (bits + xbits); + if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits); + } + if (overflow == 0) return; + + Trace((stderr,"\nbit length overflow\n")); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length-1; + while (s->bl_count[bits] == 0) bits--; + s->bl_count[bits]--; /* move one leaf down the tree */ + s->bl_count[bits+1] += 2; /* move one overflow item as its brother */ + s->bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = s->bl_count[bits]; + while (n != 0) { + m = s->heap[--h]; + if (m > max_code) continue; + if ((unsigned) tree[m].Len != (unsigned) bits) { + Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); + s->opt_len += ((long)bits - (long)tree[m].Len) + *(long)tree[m].Freq; + tree[m].Len = (ush)bits; + } + n--; + } + } +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +local void gen_codes (ct_data *tree, /* the tree to decorate */ + int max_code, /* largest code with non zero frequency */ + ushf *bl_count) /* number of codes at each bit length */ +{ + ush next_code[MAX_BITS+1]; /* next code value for each bit length */ + ush code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (code + bl_count[bits-1]) << 1; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert (code + bl_count[MAX_BITS]-1 == (1<dyn_tree; + const ct_data *stree = desc->stat_desc->static_tree; + int elems = desc->stat_desc->elems; + int n, m; /* iterate over heap elements */ + int max_code = -1; /* largest code with non zero frequency */ + int node; /* new node being created */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + s->heap_len = 0, s->heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].Freq != 0) { + s->heap[++(s->heap_len)] = max_code = n; + s->depth[n] = 0; + } else { + tree[n].Len = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (s->heap_len < 2) { + node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0); + tree[node].Freq = 1; + s->depth[node] = 0; + s->opt_len--; if (stree) s->static_len -= stree[node].Len; + /* node is 0 or 1 so it does not have extra bits */ + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + node = elems; /* next internal node of the tree */ + do { + pqremove(s, tree, n); /* n = node of least frequency */ + m = s->heap[SMALLEST]; /* m = node of next least frequency */ + + s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */ + s->heap[--(s->heap_max)] = m; + + /* Create a new node father of n and m */ + tree[node].Freq = tree[n].Freq + tree[m].Freq; + s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ? + s->depth[n] : s->depth[m]) + 1); + tree[n].Dad = tree[m].Dad = (ush)node; +#ifdef DUMP_BL_TREE + if (tree == s->bl_tree) { + fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", + node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); + } +#endif + /* and insert the new node in the heap */ + s->heap[SMALLEST] = node++; + pqdownheap(s, tree, SMALLEST); + + } while (s->heap_len >= 2); + + s->heap[--(s->heap_max)] = s->heap[SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen(s, (tree_desc *)desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes ((ct_data *)tree, max_code, s->bl_count); +} + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. + */ +local void scan_tree (deflate_state *s, + ct_data *tree, /* the tree to be scanned */ + int max_code) /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + if (nextlen == 0) max_count = 138, min_count = 3; + tree[max_code+1].Len = (ush)0xffff; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + s->bl_tree[curlen].Freq += count; + } else if (curlen != 0) { + if (curlen != prevlen) s->bl_tree[curlen].Freq++; + s->bl_tree[REP_3_6].Freq++; + } else if (count <= 10) { + s->bl_tree[REPZ_3_10].Freq++; + } else { + s->bl_tree[REPZ_11_138].Freq++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +local void send_tree (deflate_state *s, + ct_data *tree, /* the tree to be scanned */ + int max_code) /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + /* tree[max_code+1].Len = -1; */ /* guard already set */ + if (nextlen == 0) max_count = 138, min_count = 3; + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { send_code(s, curlen, s->bl_tree); } while (--count != 0); + + } else if (curlen != 0) { + if (curlen != prevlen) { + send_code(s, curlen, s->bl_tree); count--; + } + Assert(count >= 3 && count <= 6, " 3_6?"); + send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2); + + } else if (count <= 10) { + send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3); + + } else { + send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +local int build_bl_tree (deflate_state *s) +{ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code); + scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code); + + /* Build the bit length tree: */ + build_tree(s, (tree_desc *)(&(s->bl_desc))); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { + if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; + } + /* Update opt_len to include the bit length tree and counts */ + s->opt_len += 3*(max_blindex+1) + 5+5+4; + Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", + s->opt_len, s->static_len)); + + return max_blindex; +} + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +local void send_all_trees (deflate_state *s, + int lcodes, int dcodes, int blcodes) /* number of codes for each tree */ +{ + int rank; /* index in bl_order */ + + Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + "too many codes"); + Tracev((stderr, "\nbl counts: ")); + send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ + send_bits(s, dcodes-1, 5); + send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + Tracev((stderr, "\nbl code %2d ", bl_order[rank])); + send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); + } + Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */ + Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */ + Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); +} + +/* =========================================================================== + * Send a stored block + */ +void _tr_stored_block (deflate_state *s, charf *buf, ulg stored_len, int eof) +{ + send_bits(s, (STORED_BLOCK<<1)+eof, 3); /* send block type */ +#ifdef DEBUG + s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; + s->compressed_len += (stored_len + 4) << 3; +#endif + copy_block(s, buf, (unsigned)stored_len, 1); /* with header */ +} + +/* =========================================================================== + * Send one empty static block to give enough lookahead for inflate. + * This takes 10 bits, of which 7 may remain in the bit buffer. + * The current inflate code requires 9 bits of lookahead. If the + * last two codes for the previous block (real code plus EOB) were coded + * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + * the last real code. In this case we send two empty static blocks instead + * of one. (There are no problems if the previous block is stored or fixed.) + * To simplify the code, we assume the worst case of last real code encoded + * on one bit only. + */ +void _tr_align (deflate_state *s) +{ + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ +#endif + bi_flush(s); + /* Of the 10 bits for the empty block, we have already sent + * (10 - bi_valid) bits. The lookahead for the last real code (before + * the EOB of the previous block) was thus at least one plus the length + * of the EOB plus what we have just sent of the empty static block. + */ + if (1 + s->last_eob_len + 10 - s->bi_valid < 9) { + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; +#endif + bi_flush(s); + } + s->last_eob_len = 7; +} + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. + */ +void _tr_flush_block (deflate_state *s, + charf *buf, /* input block, or NULL if too old */ + ulg stored_len, /* length of input block */ + int eof) /* true if this is the last block for a file */ +{ + ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + int max_blindex = 0; /* index of last bit length code of non zero freq */ + + /* Build the Huffman trees unless a stored block is forced */ + if (s->level > 0) { + + /* Check if the file is binary or text */ + if (stored_len > 0 && s->strm->data_type == Z_UNKNOWN) + set_data_type(s); + + /* Construct the literal and distance trees */ + build_tree(s, (tree_desc *)(&(s->l_desc))); + Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + + build_tree(s, (tree_desc *)(&(s->d_desc))); + Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(s); + + /* Determine the best encoding. Compute the block lengths in bytes. */ + opt_lenb = (s->opt_len+3+7)>>3; + static_lenb = (s->static_len+3+7)>>3; + + Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", + opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, + s->last_lit)); + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + + } else { + Assert(buf != (char*)0, "lost buf"); + opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ + } + +#ifdef FORCE_STORED + if (buf != (char*)0) { /* force stored block */ +#else + if (stored_len+4 <= opt_lenb && buf != (char*)0) { + /* 4: two words for the lengths */ +#endif + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + _tr_stored_block(s, buf, stored_len, eof); + +#ifdef FORCE_STATIC + } else if (static_lenb >= 0) { /* force static trees */ +#else + } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) { +#endif + send_bits(s, (STATIC_TREES<<1)+eof, 3); + compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->static_len; +#endif + } else { + send_bits(s, (DYN_TREES<<1)+eof, 3); + send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, + max_blindex+1); + compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->opt_len; +#endif + } + Assert (s->compressed_len == s->bits_sent, "bad compressed size"); + /* The above check is made mod 2^32, for files larger than 512 MB + * and uLong implemented on 32 bits. + */ + init_block(s); + + if (eof) { + bi_windup(s); +#ifdef DEBUG + s->compressed_len += 7; /* align on byte boundary */ +#endif + } + Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, + s->compressed_len-7*eof)); +} + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +int _tr_tally (deflate_state *s, + unsigned dist, /* distance of matched string */ + unsigned lc) /* match length-MIN_MATCH or unmatched char (if dist==0) */ +{ + s->d_buf[s->last_lit] = (ush)dist; + s->l_buf[s->last_lit++] = (uch)lc; + if (dist == 0) { + /* lc is the unmatched char */ + s->dyn_ltree[lc].Freq++; + } else { + s->matches++; + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + Assert((ush)dist < (ush)MAX_DIST(s) && + (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && + (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); + + s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++; + s->dyn_dtree[d_code(dist)].Freq++; + } + +#ifdef TRUNCATE_BLOCK + /* Try to guess if it is profitable to stop the current block here */ + if ((s->last_lit & 0x1fff) == 0 && s->level > 2) { + /* Compute an upper bound for the compressed length */ + ulg out_length = (ulg)s->last_lit*8L; + ulg in_length = (ulg)((long)s->strstart - s->block_start); + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (ulg)s->dyn_dtree[dcode].Freq * + (5L+extra_dbits[dcode]); + } + out_length >>= 3; + Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", + s->last_lit, in_length, out_length, + 100L - out_length*100L/in_length)); + if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1; + } +#endif + return (s->last_lit == s->lit_bufsize-1); + /* We avoid equality with lit_bufsize because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +local void compress_block (deflate_state *s, + ct_data *ltree, /* literal tree */ + ct_data *dtree) /* distance tree */ +{ + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned lx = 0; /* running index in l_buf */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (s->last_lit != 0) do { + dist = s->d_buf[lx]; + lc = s->l_buf[lx++]; + if (dist == 0) { + send_code(s, lc, ltree); /* send a literal byte */ + Tracecv(isgraph(lc), (stderr," '%c' ", lc)); + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = _length_code[lc]; + send_code(s, code+LITERALS+1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= base_length[code]; + send_bits(s, lc, extra); /* send the extra length bits */ + } + dist--; /* dist is now the match distance - 1 */ + code = d_code(dist); + Assert (code < D_CODES, "bad d_code"); + + send_code(s, code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= base_dist[code]; + send_bits(s, dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + + /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ + Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx, + "pendingBuf overflow"); + + } while (lx < s->last_lit); + + send_code(s, END_BLOCK, ltree); + s->last_eob_len = ltree[END_BLOCK].Len; +} + +/* =========================================================================== + * Set the data type to BINARY or TEXT, using a crude approximation: + * set it to Z_TEXT if all symbols are either printable characters (33 to 255) + * or white spaces (9 to 13, or 32); or set it to Z_BINARY otherwise. + * IN assertion: the fields Freq of dyn_ltree are set. + */ +local void set_data_type (deflate_state *s) +{ + int n; + + for (n = 0; n < 9; n++) + if (s->dyn_ltree[n].Freq != 0) + break; + if (n == 9) + for (n = 14; n < 32; n++) + if (s->dyn_ltree[n].Freq != 0) + break; + s->strm->data_type = (n == 32) ? Z_TEXT : Z_BINARY; +} + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +local unsigned bi_reverse (unsigned code, int len) +{ + register unsigned res = 0; + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return res >> 1; +} + +/* =========================================================================== + * Flush the bit buffer, keeping at most 7 bits in it. + */ +local void bi_flush (deflate_state *s) +{ + if (s->bi_valid == 16) { + put_short(s, s->bi_buf); + s->bi_buf = 0; + s->bi_valid = 0; + } else if (s->bi_valid >= 8) { + put_byte(s, (Byte)s->bi_buf); + s->bi_buf >>= 8; + s->bi_valid -= 8; + } +} + +/* =========================================================================== + * Flush the bit buffer and align the output on a byte boundary + */ +local void bi_windup (deflate_state *s) +{ + if (s->bi_valid > 8) { + put_short(s, s->bi_buf); + } else if (s->bi_valid > 0) { + put_byte(s, (Byte)s->bi_buf); + } + s->bi_buf = 0; + s->bi_valid = 0; +#ifdef DEBUG + s->bits_sent = (s->bits_sent+7) & ~7; +#endif +} + +/* =========================================================================== + * Copy a stored block, storing first the length and its + * one's complement if requested. + */ +local void copy_block(deflate_state *s, + charf *buf, /* the input data */ + unsigned len, /* its length */ + int header) /* true if block header must be written */ +{ + bi_windup(s); /* align on byte boundary */ + s->last_eob_len = 8; /* enough lookahead for inflate */ + + if (header) { + put_short(s, (ush)len); + put_short(s, (ush)~len); +#ifdef DEBUG + s->bits_sent += 2*16; +#endif + } +#ifdef DEBUG + s->bits_sent += (ulg)len<<3; +#endif + while (len--) { + put_byte(s, *buf++); + } +} +/********* End of inlined file: trees.c *********/ + +/********* Start of inlined file: uncompr.c *********/ +/* @(#) $Id: uncompr.c,v 1.1 2007/06/07 17:54:37 jules_rms Exp $ */ + +#define ZLIB_INTERNAL + +/* =========================================================================== + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + This function can be used to decompress a whole file at once if the + input file is mmap'ed. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted. +*/ +int ZEXPORT uncompress (Bytef *dest, + uLongf *destLen, + const Bytef *source, + uLong sourceLen) +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; + + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + + err = inflateInit(&stream); + if (err != Z_OK) return err; + + err = inflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + inflateEnd(&stream); + if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0)) + return Z_DATA_ERROR; + return err; + } + *destLen = stream.total_out; + + err = inflateEnd(&stream); + return err; +} +/********* End of inlined file: uncompr.c *********/ + +/********* Start of inlined file: zutil.c *********/ +/* @(#) $Id: zutil.c,v 1.1 2007/06/07 17:54:37 jules_rms Exp $ */ + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +const char * const z_errmsg[10] = { +"need dictionary", /* Z_NEED_DICT 2 */ +"stream end", /* Z_STREAM_END 1 */ +"", /* Z_OK 0 */ +"file error", /* Z_ERRNO (-1) */ +"stream error", /* Z_STREAM_ERROR (-2) */ +"data error", /* Z_DATA_ERROR (-3) */ +"insufficient memory", /* Z_MEM_ERROR (-4) */ +"buffer error", /* Z_BUF_ERROR (-5) */ +"incompatible version",/* Z_VERSION_ERROR (-6) */ +""}; + +/*const char * ZEXPORT zlibVersion() +{ + return ZLIB_VERSION; +} + +uLong ZEXPORT zlibCompileFlags() +{ + uLong flags; + + flags = 0; + switch (sizeof(uInt)) { + case 2: break; + case 4: flags += 1; break; + case 8: flags += 2; break; + default: flags += 3; + } + switch (sizeof(uLong)) { + case 2: break; + case 4: flags += 1 << 2; break; + case 8: flags += 2 << 2; break; + default: flags += 3 << 2; + } + switch (sizeof(voidpf)) { + case 2: break; + case 4: flags += 1 << 4; break; + case 8: flags += 2 << 4; break; + default: flags += 3 << 4; + } + switch (sizeof(z_off_t)) { + case 2: break; + case 4: flags += 1 << 6; break; + case 8: flags += 2 << 6; break; + default: flags += 3 << 6; + } +#ifdef DEBUG + flags += 1 << 8; +#endif +#if defined(ASMV) || defined(ASMINF) + flags += 1 << 9; +#endif +#ifdef ZLIB_WINAPI + flags += 1 << 10; +#endif +#ifdef BUILDFIXED + flags += 1 << 12; +#endif +#ifdef DYNAMIC_CRC_TABLE + flags += 1 << 13; +#endif +#ifdef NO_GZCOMPRESS + flags += 1L << 16; +#endif +#ifdef NO_GZIP + flags += 1L << 17; +#endif +#ifdef PKZIP_BUG_WORKAROUND + flags += 1L << 20; +#endif +#ifdef FASTEST + flags += 1L << 21; +#endif +#ifdef STDC +# ifdef NO_vsnprintf + flags += 1L << 25; +# ifdef HAS_vsprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_vsnprintf_void + flags += 1L << 26; +# endif +# endif +#else + flags += 1L << 24; +# ifdef NO_snprintf + flags += 1L << 25; +# ifdef HAS_sprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_snprintf_void + flags += 1L << 26; +# endif +# endif +#endif + return flags; +}*/ + +#ifdef DEBUG + +# ifndef verbose +# define verbose 0 +# endif +int z_verbose = verbose; + +void z_error (char *m) +{ + fprintf(stderr, "%s\n", m); + exit(1); +} +#endif + +/* exported to allow conversion of error code to string for compress() and + * uncompress() + */ +const char * ZEXPORT zError(int err) +{ + return ERR_MSG(err); +} + +#if defined(_WIN32_WCE) + /* The Microsoft C Run-Time Library for Windows CE doesn't have + * errno. We define it as a global variable to simplify porting. + * Its value is always 0 and should not be used. + */ + int errno = 0; +#endif + +#ifndef HAVE_MEMCPY + +void zmemcpy(dest, source, len) + Bytef* dest; + const Bytef* source; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = *source++; /* ??? to be unrolled */ + } while (--len != 0); +} + +int zmemcmp(s1, s2, len) + const Bytef* s1; + const Bytef* s2; + uInt len; +{ + uInt j; + + for (j = 0; j < len; j++) { + if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1; + } + return 0; +} + +void zmemzero(dest, len) + Bytef* dest; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = 0; /* ??? to be unrolled */ + } while (--len != 0); +} +#endif + +#ifdef SYS16BIT + +#ifdef __TURBOC__ +/* Turbo C in 16-bit mode */ + +# define MY_ZCALLOC + +/* Turbo C malloc() does not allow dynamic allocation of 64K bytes + * and farmalloc(64K) returns a pointer with an offset of 8, so we + * must fix the pointer. Warning: the pointer must be put back to its + * original form in order to free it, use zcfree(). + */ + +#define MAX_PTR 10 +/* 10*64K = 640K */ + +local int next_ptr = 0; + +typedef struct ptr_table_s { + voidpf org_ptr; + voidpf new_ptr; +} ptr_table; + +local ptr_table table[MAX_PTR]; +/* This table is used to remember the original form of pointers + * to large buffers (64K). Such pointers are normalized with a zero offset. + * Since MSDOS is not a preemptive multitasking OS, this table is not + * protected from concurrent access. This hack doesn't work anyway on + * a protected system like OS/2. Use Microsoft C instead. + */ + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + voidpf buf = opaque; /* just to make some compilers happy */ + ulg bsize = (ulg)items*size; + + /* If we allocate less than 65520 bytes, we assume that farmalloc + * will return a usable pointer which doesn't have to be normalized. + */ + if (bsize < 65520L) { + buf = farmalloc(bsize); + if (*(ush*)&buf != 0) return buf; + } else { + buf = farmalloc(bsize + 16L); + } + if (buf == NULL || next_ptr >= MAX_PTR) return NULL; + table[next_ptr].org_ptr = buf; + + /* Normalize the pointer to seg:0 */ + *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4; + *(ush*)&buf = 0; + table[next_ptr++].new_ptr = buf; + return buf; +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + int n; + if (*(ush*)&ptr != 0) { /* object < 64K */ + farfree(ptr); + return; + } + /* Find the original pointer */ + for (n = 0; n < next_ptr; n++) { + if (ptr != table[n].new_ptr) continue; + + farfree(table[n].org_ptr); + while (++n < next_ptr) { + table[n-1] = table[n]; + } + next_ptr--; + return; + } + ptr = opaque; /* just to make some compilers happy */ + Assert(0, "zcfree: ptr not found"); +} + +#endif /* __TURBOC__ */ + +#ifdef M_I86 +/* Microsoft C in 16-bit mode */ + +# define MY_ZCALLOC + +#if (!defined(_MSC_VER) || (_MSC_VER <= 600)) +# define _halloc halloc +# define _hfree hfree +#endif + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + return _halloc((long)items, size); +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + _hfree(ptr); +} + +#endif /* M_I86 */ + +#endif /* SYS16BIT */ + +#ifndef MY_ZCALLOC /* Any system without a special alloc function */ + +#ifndef STDC +extern voidp malloc OF((uInt size)); +extern voidp calloc OF((uInt items, uInt size)); +extern void free OF((voidpf ptr)); +#endif + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + if (opaque) items += size - size; /* make compiler happy */ + return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) : + (voidpf)calloc(items, size); +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + free(ptr); + if (opaque) return; /* make compiler happy */ +} + +#endif /* MY_ZCALLOC */ +/********* End of inlined file: zutil.c *********/ + + } +} + +#if JUCE_MSVC + #pragma warning (pop) +#endif + +BEGIN_JUCE_NAMESPACE + +using namespace zlibNamespace; + +// internal helper object that holds the zlib structures so they don't have to be +// included publicly. +class GZIPDecompressHelper +{ +private: + z_stream* stream; + uint8* data; + int dataSize; + +public: + bool finished, needsDictionary, error; + + GZIPDecompressHelper (const bool noWrap) throw() + : data (0), + dataSize (0), + finished (false), + needsDictionary (false), + error (false) + { + stream = (z_stream*) juce_calloc (sizeof (z_stream)); + + if (inflateInit2 (stream, (noWrap) ? -MAX_WBITS + : MAX_WBITS) != Z_OK) + { + juce_free (stream); + stream = 0; + error = true; + finished = true; + } + } + + ~GZIPDecompressHelper() throw() + { + if (stream != 0) + { + inflateEnd (stream); + juce_free (stream); + } + } + + bool needsInput() const throw() { return dataSize <= 0; } + int getTotalOut() const throw() { return (stream != 0) ? stream->total_out : 0; } + + void setInput (uint8* const data_, const int size) throw() + { + data = data_; + dataSize = size; + } + + int doNextBlock (uint8* const dest, const int destSize) throw() + { + if (stream != 0 && data != 0 && ! finished) + { + stream->next_in = data; + stream->next_out = dest; + stream->avail_in = dataSize; + stream->avail_out = destSize; + + switch (inflate (stream, Z_PARTIAL_FLUSH)) + { + case Z_STREAM_END: + finished = true; + // deliberate fall-through + + case Z_OK: + data += dataSize - stream->avail_in; + dataSize = stream->avail_in; + return destSize - stream->avail_out; + + case Z_NEED_DICT: + needsDictionary = true; + data += dataSize - stream->avail_in; + dataSize = stream->avail_in; + break; + + case Z_DATA_ERROR: + case Z_MEM_ERROR: + error = true; + + default: + break; + } + } + + return 0; + } +}; + +const int gzipDecompBufferSize = 32768; + +GZIPDecompressorInputStream::GZIPDecompressorInputStream (InputStream* const sourceStream_, + const bool deleteSourceWhenDestroyed_, + const bool noWrap_, + const int64 uncompressedStreamLength_) + : sourceStream (sourceStream_), + uncompressedStreamLength (uncompressedStreamLength_), + deleteSourceWhenDestroyed (deleteSourceWhenDestroyed_), + noWrap (noWrap_), + isEof (false), + activeBufferSize (0), + originalSourcePos (sourceStream_->getPosition()) +{ + buffer = (uint8*) juce_malloc (gzipDecompBufferSize); + helper = new GZIPDecompressHelper (noWrap_); +} + +GZIPDecompressorInputStream::~GZIPDecompressorInputStream() +{ + juce_free (buffer); + + if (deleteSourceWhenDestroyed) + delete sourceStream; + + GZIPDecompressHelper* const h = (GZIPDecompressHelper*) helper; + delete h; +} + +int64 GZIPDecompressorInputStream::getTotalLength() +{ + return uncompressedStreamLength; +} + +int GZIPDecompressorInputStream::read (void* destBuffer, int howMany) +{ + GZIPDecompressHelper* const h = (GZIPDecompressHelper*) helper; + + if ((howMany > 0) && ! isEof) + { + jassert (destBuffer != 0); + + if (destBuffer != 0) + { + int numRead = 0; + uint8* d = (uint8*) destBuffer; + + while (! h->error) + { + const int n = h->doNextBlock (d, howMany); + + if (n == 0) + { + if (h->finished || h->needsDictionary) + { + isEof = true; + return numRead; + } + + if (h->needsInput()) + { + activeBufferSize = sourceStream->read (buffer, gzipDecompBufferSize); + + if (activeBufferSize > 0) + { + h->setInput ((uint8*) buffer, activeBufferSize); + } + else + { + isEof = true; + return numRead; + } + } + } + else + { + numRead += n; + howMany -= n; + d += n; + + if (howMany <= 0) + return numRead; + } + } + } + } + + return 0; +} + +bool GZIPDecompressorInputStream::isExhausted() +{ + const GZIPDecompressHelper* const h = (GZIPDecompressHelper*) helper; + + return h->error || isEof; +} + +int64 GZIPDecompressorInputStream::getPosition() +{ + const GZIPDecompressHelper* const h = (GZIPDecompressHelper*) helper; + + return h->getTotalOut() + activeBufferSize; +} + +bool GZIPDecompressorInputStream::setPosition (int64 newPos) +{ + const int64 currentPos = getPosition(); + + if (newPos != currentPos) + { + // reset the stream and start again.. + GZIPDecompressHelper* const h = (GZIPDecompressHelper*) helper; + delete h; + + isEof = false; + activeBufferSize = 0; + helper = new GZIPDecompressHelper (noWrap); + + sourceStream->setPosition (originalSourcePos); + skipNextBytes (newPos); + } + + return true; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_GZIPDecompressorInputStream.cpp *********/ + +#if ! JUCE_ONLY_BUILD_CORE_LIBRARY + +/********* Start of inlined file: juce_FlacAudioFormat.cpp *********/ + +#ifdef _MSC_VER + #include +#endif + +#if JUCE_USE_FLAC + +#ifdef _MSC_VER + #pragma warning (disable : 4505) + #pragma warning (push) +#endif + +namespace FlacNamespace +{ + extern "C" + { + #define FLAC__NO_DLL 1 + + #if ! defined (SIZE_MAX) + #define SIZE_MAX 0xffffffff + #endif + + #define __STDC_LIMIT_MACROS 1 + +/********* Start of inlined file: all.h *********/ +#ifndef FLAC__ALL_H +#define FLAC__ALL_H + +/********* Start of inlined file: export.h *********/ +#ifndef FLAC__EXPORT_H +#define FLAC__EXPORT_H + +/** \file include/FLAC/export.h + * + * \brief + * This module contains #defines and symbols for exporting function + * calls, and providing version information and compiled-in features. + * + * See the \link flac_export export \endlink module. + */ + +/** \defgroup flac_export FLAC/export.h: export symbols + * \ingroup flac + * + * \brief + * This module contains #defines and symbols for exporting function + * calls, and providing version information and compiled-in features. + * + * If you are compiling with MSVC and will link to the static library + * (libFLAC.lib) you should define FLAC__NO_DLL in your project to + * make sure the symbols are exported properly. + * + * \{ + */ + +#if defined(FLAC__NO_DLL) || !defined(_MSC_VER) +#define FLAC_API + +#else + +#ifdef FLAC_API_EXPORTS +#define FLAC_API _declspec(dllexport) +#else +#define FLAC_API _declspec(dllimport) + +#endif +#endif + +/** These #defines will mirror the libtool-based library version number, see + * http://www.gnu.org/software/libtool/manual.html#Libtool-versioning + */ +#define FLAC_API_VERSION_CURRENT 10 +#define FLAC_API_VERSION_REVISION 0 /**< see above */ +#define FLAC_API_VERSION_AGE 2 /**< see above */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** \c 1 if the library has been compiled with support for Ogg FLAC, else \c 0. */ +extern FLAC_API int FLAC_API_SUPPORTS_OGG_FLAC; + +#ifdef __cplusplus +} +#endif + +/* \} */ + +#endif +/********* End of inlined file: export.h *********/ + +/********* Start of inlined file: assert.h *********/ +#ifndef FLAC__ASSERT_H +#define FLAC__ASSERT_H + +/* we need this since some compilers (like MSVC) leave assert()s on release code (and we don't want to use their ASSERT) */ +#ifdef DEBUG +#include +#define FLAC__ASSERT(x) assert(x) +#define FLAC__ASSERT_DECLARATION(x) x +#else +#define FLAC__ASSERT(x) +#define FLAC__ASSERT_DECLARATION(x) +#endif + +#endif +/********* End of inlined file: assert.h *********/ + +/********* Start of inlined file: callback.h *********/ +#ifndef FLAC__CALLBACK_H +#define FLAC__CALLBACK_H + +/********* Start of inlined file: ordinals.h *********/ +#ifndef FLAC__ORDINALS_H +#define FLAC__ORDINALS_H + +#if !(defined(_MSC_VER) || defined(__BORLANDC__) || defined(__EMX__)) +#include +#endif + +typedef signed char FLAC__int8; +typedef unsigned char FLAC__uint8; + +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef __int16 FLAC__int16; +typedef __int32 FLAC__int32; +typedef __int64 FLAC__int64; +typedef unsigned __int16 FLAC__uint16; +typedef unsigned __int32 FLAC__uint32; +typedef unsigned __int64 FLAC__uint64; +#elif defined(__EMX__) +typedef short FLAC__int16; +typedef long FLAC__int32; +typedef long long FLAC__int64; +typedef unsigned short FLAC__uint16; +typedef unsigned long FLAC__uint32; +typedef unsigned long long FLAC__uint64; +#else +typedef int16_t FLAC__int16; +typedef int32_t FLAC__int32; +typedef int64_t FLAC__int64; +typedef uint16_t FLAC__uint16; +typedef uint32_t FLAC__uint32; +typedef uint64_t FLAC__uint64; +#endif + +typedef int FLAC__bool; + +typedef FLAC__uint8 FLAC__byte; + +#ifdef true +#undef true +#endif +#ifdef false +#undef false +#endif +#ifndef __cplusplus +#define true 1 +#define false 0 +#endif + +#endif +/********* End of inlined file: ordinals.h *********/ + +#include /* for size_t */ + +/** \file include/FLAC/callback.h + * + * \brief + * This module defines the structures for describing I/O callbacks + * to the other FLAC interfaces. + * + * See the detailed documentation for callbacks in the + * \link flac_callbacks callbacks \endlink module. + */ + +/** \defgroup flac_callbacks FLAC/callback.h: I/O callback structures + * \ingroup flac + * + * \brief + * This module defines the structures for describing I/O callbacks + * to the other FLAC interfaces. + * + * The purpose of the I/O callback functions is to create a common way + * for the metadata interfaces to handle I/O. + * + * Originally the metadata interfaces required filenames as the way of + * specifying FLAC files to operate on. This is problematic in some + * environments so there is an additional option to specify a set of + * callbacks for doing I/O on the FLAC file, instead of the filename. + * + * In addition to the callbacks, a FLAC__IOHandle type is defined as an + * opaque structure for a data source. + * + * The callback function prototypes are similar (but not identical) to the + * stdio functions fread, fwrite, fseek, ftell, feof, and fclose. If you use + * stdio streams to implement the callbacks, you can pass fread, fwrite, and + * fclose anywhere a FLAC__IOCallback_Read, FLAC__IOCallback_Write, or + * FLAC__IOCallback_Close is required, and a FILE* anywhere a FLAC__IOHandle + * is required. \warning You generally CANNOT directly use fseek or ftell + * for FLAC__IOCallback_Seek or FLAC__IOCallback_Tell since on most systems + * these use 32-bit offsets and FLAC requires 64-bit offsets to deal with + * large files. You will have to find an equivalent function (e.g. ftello), + * or write a wrapper. The same is true for feof() since this is usually + * implemented as a macro, not as a function whose address can be taken. + * + * \{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** This is the opaque handle type used by the callbacks. Typically + * this is a \c FILE* or address of a file descriptor. + */ +typedef void* FLAC__IOHandle; + +/** Signature for the read callback. + * The signature and semantics match POSIX fread() implementations + * and can generally be used interchangeably. + * + * \param ptr The address of the read buffer. + * \param size The size of the records to be read. + * \param nmemb The number of records to be read. + * \param handle The handle to the data source. + * \retval size_t + * The number of records read. + */ +typedef size_t (*FLAC__IOCallback_Read) (void *ptr, size_t size, size_t nmemb, FLAC__IOHandle handle); + +/** Signature for the write callback. + * The signature and semantics match POSIX fwrite() implementations + * and can generally be used interchangeably. + * + * \param ptr The address of the write buffer. + * \param size The size of the records to be written. + * \param nmemb The number of records to be written. + * \param handle The handle to the data source. + * \retval size_t + * The number of records written. + */ +typedef size_t (*FLAC__IOCallback_Write) (const void *ptr, size_t size, size_t nmemb, FLAC__IOHandle handle); + +/** Signature for the seek callback. + * The signature and semantics mostly match POSIX fseek() WITH ONE IMPORTANT + * EXCEPTION: the offset is a 64-bit type whereas fseek() is generally 'long' + * and 32-bits wide. + * + * \param handle The handle to the data source. + * \param offset The new position, relative to \a whence + * \param whence \c SEEK_SET, \c SEEK_CUR, or \c SEEK_END + * \retval int + * \c 0 on success, \c -1 on error. + */ +typedef int (*FLAC__IOCallback_Seek) (FLAC__IOHandle handle, FLAC__int64 offset, int whence); + +/** Signature for the tell callback. + * The signature and semantics mostly match POSIX ftell() WITH ONE IMPORTANT + * EXCEPTION: the offset is a 64-bit type whereas ftell() is generally 'long' + * and 32-bits wide. + * + * \param handle The handle to the data source. + * \retval FLAC__int64 + * The current position on success, \c -1 on error. + */ +typedef FLAC__int64 (*FLAC__IOCallback_Tell) (FLAC__IOHandle handle); + +/** Signature for the EOF callback. + * The signature and semantics mostly match POSIX feof() but WATCHOUT: + * on many systems, feof() is a macro, so in this case a wrapper function + * must be provided instead. + * + * \param handle The handle to the data source. + * \retval int + * \c 0 if not at end of file, nonzero if at end of file. + */ +typedef int (*FLAC__IOCallback_Eof) (FLAC__IOHandle handle); + +/** Signature for the close callback. + * The signature and semantics match POSIX fclose() implementations + * and can generally be used interchangeably. + * + * \param handle The handle to the data source. + * \retval int + * \c 0 on success, \c EOF on error. + */ +typedef int (*FLAC__IOCallback_Close) (FLAC__IOHandle handle); + +/** A structure for holding a set of callbacks. + * Each FLAC interface that requires a FLAC__IOCallbacks structure will + * describe which of the callbacks are required. The ones that are not + * required may be set to NULL. + * + * If the seek requirement for an interface is optional, you can signify that + * a data sorce is not seekable by setting the \a seek field to \c NULL. + */ +typedef struct { + FLAC__IOCallback_Read read; + FLAC__IOCallback_Write write; + FLAC__IOCallback_Seek seek; + FLAC__IOCallback_Tell tell; + FLAC__IOCallback_Eof eof; + FLAC__IOCallback_Close close; +} FLAC__IOCallbacks; + +/* \} */ + +#ifdef __cplusplus +} +#endif + +#endif +/********* End of inlined file: callback.h *********/ + +/********* Start of inlined file: format.h *********/ +#ifndef FLAC__FORMAT_H +#define FLAC__FORMAT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file include/FLAC/format.h + * + * \brief + * This module contains structure definitions for the representation + * of FLAC format components in memory. These are the basic + * structures used by the rest of the interfaces. + * + * See the detailed documentation in the + * \link flac_format format \endlink module. + */ + +/** \defgroup flac_format FLAC/format.h: format components + * \ingroup flac + * + * \brief + * This module contains structure definitions for the representation + * of FLAC format components in memory. These are the basic + * structures used by the rest of the interfaces. + * + * First, you should be familiar with the + * FLAC format. Many of the values here + * follow directly from the specification. As a user of libFLAC, the + * interesting parts really are the structures that describe the frame + * header and metadata blocks. + * + * The format structures here are very primitive, designed to store + * information in an efficient way. Reading information from the + * structures is easy but creating or modifying them directly is + * more complex. For the most part, as a user of a library, editing + * is not necessary; however, for metadata blocks it is, so there are + * convenience functions provided in the \link flac_metadata metadata + * module \endlink to simplify the manipulation of metadata blocks. + * + * \note + * It's not the best convention, but symbols ending in _LEN are in bits + * and _LENGTH are in bytes. _LENGTH symbols are \#defines instead of + * global variables because they are usually used when declaring byte + * arrays and some compilers require compile-time knowledge of array + * sizes when declared on the stack. + * + * \{ + */ + +/* + Most of the values described in this file are defined by the FLAC + format specification. There is nothing to tune here. +*/ + +/** The largest legal metadata type code. */ +#define FLAC__MAX_METADATA_TYPE_CODE (126u) + +/** The minimum block size, in samples, permitted by the format. */ +#define FLAC__MIN_BLOCK_SIZE (16u) + +/** The maximum block size, in samples, permitted by the format. */ +#define FLAC__MAX_BLOCK_SIZE (65535u) + +/** The maximum block size, in samples, permitted by the FLAC subset for + * sample rates up to 48kHz. */ +#define FLAC__SUBSET_MAX_BLOCK_SIZE_48000HZ (4608u) + +/** The maximum number of channels permitted by the format. */ +#define FLAC__MAX_CHANNELS (8u) + +/** The minimum sample resolution permitted by the format. */ +#define FLAC__MIN_BITS_PER_SAMPLE (4u) + +/** The maximum sample resolution permitted by the format. */ +#define FLAC__MAX_BITS_PER_SAMPLE (32u) + +/** The maximum sample resolution permitted by libFLAC. + * + * \warning + * FLAC__MAX_BITS_PER_SAMPLE is the limit of the FLAC format. However, + * the reference encoder/decoder is currently limited to 24 bits because + * of prevalent 32-bit math, so make sure and use this value when + * appropriate. + */ +#define FLAC__REFERENCE_CODEC_MAX_BITS_PER_SAMPLE (24u) + +/** The maximum sample rate permitted by the format. The value is + * ((2 ^ 16) - 1) * 10; see FLAC format + * as to why. + */ +#define FLAC__MAX_SAMPLE_RATE (655350u) + +/** The maximum LPC order permitted by the format. */ +#define FLAC__MAX_LPC_ORDER (32u) + +/** The maximum LPC order permitted by the FLAC subset for sample rates + * up to 48kHz. */ +#define FLAC__SUBSET_MAX_LPC_ORDER_48000HZ (12u) + +/** The minimum quantized linear predictor coefficient precision + * permitted by the format. + */ +#define FLAC__MIN_QLP_COEFF_PRECISION (5u) + +/** The maximum quantized linear predictor coefficient precision + * permitted by the format. + */ +#define FLAC__MAX_QLP_COEFF_PRECISION (15u) + +/** The maximum order of the fixed predictors permitted by the format. */ +#define FLAC__MAX_FIXED_ORDER (4u) + +/** The maximum Rice partition order permitted by the format. */ +#define FLAC__MAX_RICE_PARTITION_ORDER (15u) + +/** The maximum Rice partition order permitted by the FLAC Subset. */ +#define FLAC__SUBSET_MAX_RICE_PARTITION_ORDER (8u) + +/** The version string of the release, stamped onto the libraries and binaries. + * + * \note + * This does not correspond to the shared library version number, which + * is used to determine binary compatibility. + */ +extern FLAC_API const char *FLAC__VERSION_STRING; + +/** The vendor string inserted by the encoder into the VORBIS_COMMENT block. + * This is a NUL-terminated ASCII string; when inserted into the + * VORBIS_COMMENT the trailing null is stripped. + */ +extern FLAC_API const char *FLAC__VENDOR_STRING; + +/** The byte string representation of the beginning of a FLAC stream. */ +extern FLAC_API const FLAC__byte FLAC__STREAM_SYNC_STRING[4]; /* = "fLaC" */ + +/** The 32-bit integer big-endian representation of the beginning of + * a FLAC stream. + */ +extern FLAC_API const unsigned FLAC__STREAM_SYNC; /* = 0x664C6143 */ + +/** The length of the FLAC signature in bits. */ +extern FLAC_API const unsigned FLAC__STREAM_SYNC_LEN; /* = 32 bits */ + +/** The length of the FLAC signature in bytes. */ +#define FLAC__STREAM_SYNC_LENGTH (4u) + +/***************************************************************************** + * + * Subframe structures + * + *****************************************************************************/ + +/*****************************************************************************/ + +/** An enumeration of the available entropy coding methods. */ +typedef enum { + FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE = 0, + /**< Residual is coded by partitioning into contexts, each with it's own + * 4-bit Rice parameter. */ + + FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2 = 1 + /**< Residual is coded by partitioning into contexts, each with it's own + * 5-bit Rice parameter. */ +} FLAC__EntropyCodingMethodType; + +/** Maps a FLAC__EntropyCodingMethodType to a C string. + * + * Using a FLAC__EntropyCodingMethodType as the index to this array will + * give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__EntropyCodingMethodTypeString[]; + +/** Contents of a Rice partitioned residual + */ +typedef struct { + + unsigned *parameters; + /**< The Rice parameters for each context. */ + + unsigned *raw_bits; + /**< Widths for escape-coded partitions. Will be non-zero for escaped + * partitions and zero for unescaped partitions. + */ + + unsigned capacity_by_order; + /**< The capacity of the \a parameters and \a raw_bits arrays + * specified as an order, i.e. the number of array elements + * allocated is 2 ^ \a capacity_by_order. + */ +} FLAC__EntropyCodingMethod_PartitionedRiceContents; + +/** Header for a Rice partitioned residual. (c.f. format specification) + */ +typedef struct { + + unsigned order; + /**< The partition order, i.e. # of contexts = 2 ^ \a order. */ + + const FLAC__EntropyCodingMethod_PartitionedRiceContents *contents; + /**< The context's Rice parameters and/or raw bits. */ + +} FLAC__EntropyCodingMethod_PartitionedRice; + +extern FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN; /**< == 4 (bits) */ +extern FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN; /**< == 4 (bits) */ +extern FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN; /**< == 5 (bits) */ +extern FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_RAW_LEN; /**< == 5 (bits) */ + +extern FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER; +/**< == (1<format specification) + */ +typedef struct { + FLAC__EntropyCodingMethodType type; + union { + FLAC__EntropyCodingMethod_PartitionedRice partitioned_rice; + } data; +} FLAC__EntropyCodingMethod; + +extern FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_TYPE_LEN; /**< == 2 (bits) */ + +/*****************************************************************************/ + +/** An enumeration of the available subframe types. */ +typedef enum { + FLAC__SUBFRAME_TYPE_CONSTANT = 0, /**< constant signal */ + FLAC__SUBFRAME_TYPE_VERBATIM = 1, /**< uncompressed signal */ + FLAC__SUBFRAME_TYPE_FIXED = 2, /**< fixed polynomial prediction */ + FLAC__SUBFRAME_TYPE_LPC = 3 /**< linear prediction */ +} FLAC__SubframeType; + +/** Maps a FLAC__SubframeType to a C string. + * + * Using a FLAC__SubframeType as the index to this array will + * give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__SubframeTypeString[]; + +/** CONSTANT subframe. (c.f. format specification) + */ +typedef struct { + FLAC__int32 value; /**< The constant signal value. */ +} FLAC__Subframe_Constant; + +/** VERBATIM subframe. (c.f. format specification) + */ +typedef struct { + const FLAC__int32 *data; /**< A pointer to verbatim signal. */ +} FLAC__Subframe_Verbatim; + +/** FIXED subframe. (c.f. format specification) + */ +typedef struct { + FLAC__EntropyCodingMethod entropy_coding_method; + /**< The residual coding method. */ + + unsigned order; + /**< The polynomial order. */ + + FLAC__int32 warmup[FLAC__MAX_FIXED_ORDER]; + /**< Warmup samples to prime the predictor, length == order. */ + + const FLAC__int32 *residual; + /**< The residual signal, length == (blocksize minus order) samples. */ +} FLAC__Subframe_Fixed; + +/** LPC subframe. (c.f. format specification) + */ +typedef struct { + FLAC__EntropyCodingMethod entropy_coding_method; + /**< The residual coding method. */ + + unsigned order; + /**< The FIR order. */ + + unsigned qlp_coeff_precision; + /**< Quantized FIR filter coefficient precision in bits. */ + + int quantization_level; + /**< The qlp coeff shift needed. */ + + FLAC__int32 qlp_coeff[FLAC__MAX_LPC_ORDER]; + /**< FIR filter coefficients. */ + + FLAC__int32 warmup[FLAC__MAX_LPC_ORDER]; + /**< Warmup samples to prime the predictor, length == order. */ + + const FLAC__int32 *residual; + /**< The residual signal, length == (blocksize minus order) samples. */ +} FLAC__Subframe_LPC; + +extern FLAC_API const unsigned FLAC__SUBFRAME_LPC_QLP_COEFF_PRECISION_LEN; /**< == 4 (bits) */ +extern FLAC_API const unsigned FLAC__SUBFRAME_LPC_QLP_SHIFT_LEN; /**< == 5 (bits) */ + +/** FLAC subframe structure. (c.f. format specification) + */ +typedef struct { + FLAC__SubframeType type; + union { + FLAC__Subframe_Constant constant; + FLAC__Subframe_Fixed fixed; + FLAC__Subframe_LPC lpc; + FLAC__Subframe_Verbatim verbatim; + } data; + unsigned wasted_bits; +} FLAC__Subframe; + +/** == 1 (bit) + * + * This used to be a zero-padding bit (hence the name + * FLAC__SUBFRAME_ZERO_PAD_LEN) but is now a reserved bit. It still has a + * mandatory value of \c 0 but in the future may take on the value \c 0 or \c 1 + * to mean something else. + */ +extern FLAC_API const unsigned FLAC__SUBFRAME_ZERO_PAD_LEN; +extern FLAC_API const unsigned FLAC__SUBFRAME_TYPE_LEN; /**< == 6 (bits) */ +extern FLAC_API const unsigned FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN; /**< == 1 (bit) */ + +extern FLAC_API const unsigned FLAC__SUBFRAME_TYPE_CONSTANT_BYTE_ALIGNED_MASK; /**< = 0x00 */ +extern FLAC_API const unsigned FLAC__SUBFRAME_TYPE_VERBATIM_BYTE_ALIGNED_MASK; /**< = 0x02 */ +extern FLAC_API const unsigned FLAC__SUBFRAME_TYPE_FIXED_BYTE_ALIGNED_MASK; /**< = 0x10 */ +extern FLAC_API const unsigned FLAC__SUBFRAME_TYPE_LPC_BYTE_ALIGNED_MASK; /**< = 0x40 */ + +/*****************************************************************************/ + +/***************************************************************************** + * + * Frame structures + * + *****************************************************************************/ + +/** An enumeration of the available channel assignments. */ +typedef enum { + FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT = 0, /**< independent channels */ + FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE = 1, /**< left+side stereo */ + FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE = 2, /**< right+side stereo */ + FLAC__CHANNEL_ASSIGNMENT_MID_SIDE = 3 /**< mid+side stereo */ +} FLAC__ChannelAssignment; + +/** Maps a FLAC__ChannelAssignment to a C string. + * + * Using a FLAC__ChannelAssignment as the index to this array will + * give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__ChannelAssignmentString[]; + +/** An enumeration of the possible frame numbering methods. */ +typedef enum { + FLAC__FRAME_NUMBER_TYPE_FRAME_NUMBER, /**< number contains the frame number */ + FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER /**< number contains the sample number of first sample in frame */ +} FLAC__FrameNumberType; + +/** Maps a FLAC__FrameNumberType to a C string. + * + * Using a FLAC__FrameNumberType as the index to this array will + * give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__FrameNumberTypeString[]; + +/** FLAC frame header structure. (c.f. format specification) + */ +typedef struct { + unsigned blocksize; + /**< The number of samples per subframe. */ + + unsigned sample_rate; + /**< The sample rate in Hz. */ + + unsigned channels; + /**< The number of channels (== number of subframes). */ + + FLAC__ChannelAssignment channel_assignment; + /**< The channel assignment for the frame. */ + + unsigned bits_per_sample; + /**< The sample resolution. */ + + FLAC__FrameNumberType number_type; + /**< The numbering scheme used for the frame. As a convenience, the + * decoder will always convert a frame number to a sample number because + * the rules are complex. */ + + union { + FLAC__uint32 frame_number; + FLAC__uint64 sample_number; + } number; + /**< The frame number or sample number of first sample in frame; + * use the \a number_type value to determine which to use. */ + + FLAC__uint8 crc; + /**< CRC-8 (polynomial = x^8 + x^2 + x^1 + x^0, initialized with 0) + * of the raw frame header bytes, meaning everything before the CRC byte + * including the sync code. + */ +} FLAC__FrameHeader; + +extern FLAC_API const unsigned FLAC__FRAME_HEADER_SYNC; /**< == 0x3ffe; the frame header sync code */ +extern FLAC_API const unsigned FLAC__FRAME_HEADER_SYNC_LEN; /**< == 14 (bits) */ +extern FLAC_API const unsigned FLAC__FRAME_HEADER_RESERVED_LEN; /**< == 1 (bits) */ +extern FLAC_API const unsigned FLAC__FRAME_HEADER_BLOCKING_STRATEGY_LEN; /**< == 1 (bits) */ +extern FLAC_API const unsigned FLAC__FRAME_HEADER_BLOCK_SIZE_LEN; /**< == 4 (bits) */ +extern FLAC_API const unsigned FLAC__FRAME_HEADER_SAMPLE_RATE_LEN; /**< == 4 (bits) */ +extern FLAC_API const unsigned FLAC__FRAME_HEADER_CHANNEL_ASSIGNMENT_LEN; /**< == 4 (bits) */ +extern FLAC_API const unsigned FLAC__FRAME_HEADER_BITS_PER_SAMPLE_LEN; /**< == 3 (bits) */ +extern FLAC_API const unsigned FLAC__FRAME_HEADER_ZERO_PAD_LEN; /**< == 1 (bit) */ +extern FLAC_API const unsigned FLAC__FRAME_HEADER_CRC_LEN; /**< == 8 (bits) */ + +/** FLAC frame footer structure. (c.f. format specification) + */ +typedef struct { + FLAC__uint16 crc; + /**< CRC-16 (polynomial = x^16 + x^15 + x^2 + x^0, initialized with + * 0) of the bytes before the crc, back to and including the frame header + * sync code. + */ +} FLAC__FrameFooter; + +extern FLAC_API const unsigned FLAC__FRAME_FOOTER_CRC_LEN; /**< == 16 (bits) */ + +/** FLAC frame structure. (c.f. format specification) + */ +typedef struct { + FLAC__FrameHeader header; + FLAC__Subframe subframes[FLAC__MAX_CHANNELS]; + FLAC__FrameFooter footer; +} FLAC__Frame; + +/*****************************************************************************/ + +/***************************************************************************** + * + * Meta-data structures + * + *****************************************************************************/ + +/** An enumeration of the available metadata block types. */ +typedef enum { + + FLAC__METADATA_TYPE_STREAMINFO = 0, + /**< STREAMINFO block */ + + FLAC__METADATA_TYPE_PADDING = 1, + /**< PADDING block */ + + FLAC__METADATA_TYPE_APPLICATION = 2, + /**< APPLICATION block */ + + FLAC__METADATA_TYPE_SEEKTABLE = 3, + /**< SEEKTABLE block */ + + FLAC__METADATA_TYPE_VORBIS_COMMENT = 4, + /**< VORBISCOMMENT block (a.k.a. FLAC tags) */ + + FLAC__METADATA_TYPE_CUESHEET = 5, + /**< CUESHEET block */ + + FLAC__METADATA_TYPE_PICTURE = 6, + /**< PICTURE block */ + + FLAC__METADATA_TYPE_UNDEFINED = 7 + /**< marker to denote beginning of undefined type range; this number will increase as new metadata types are added */ + +} FLAC__MetadataType; + +/** Maps a FLAC__MetadataType to a C string. + * + * Using a FLAC__MetadataType as the index to this array will + * give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__MetadataTypeString[]; + +/** FLAC STREAMINFO structure. (c.f. format specification) + */ +typedef struct { + unsigned min_blocksize, max_blocksize; + unsigned min_framesize, max_framesize; + unsigned sample_rate; + unsigned channels; + unsigned bits_per_sample; + FLAC__uint64 total_samples; + FLAC__byte md5sum[16]; +} FLAC__StreamMetadata_StreamInfo; + +extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN; /**< == 16 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN; /**< == 16 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN; /**< == 24 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN; /**< == 24 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN; /**< == 20 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN; /**< == 3 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN; /**< == 5 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN; /**< == 36 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MD5SUM_LEN; /**< == 128 (bits) */ + +/** The total stream length of the STREAMINFO block in bytes. */ +#define FLAC__STREAM_METADATA_STREAMINFO_LENGTH (34u) + +/** FLAC PADDING structure. (c.f. format specification) + */ +typedef struct { + int dummy; + /**< Conceptually this is an empty struct since we don't store the + * padding bytes. Empty structs are not allowed by some C compilers, + * hence the dummy. + */ +} FLAC__StreamMetadata_Padding; + +/** FLAC APPLICATION structure. (c.f. format specification) + */ +typedef struct { + FLAC__byte id[4]; + FLAC__byte *data; +} FLAC__StreamMetadata_Application; + +extern FLAC_API const unsigned FLAC__STREAM_METADATA_APPLICATION_ID_LEN; /**< == 32 (bits) */ + +/** SeekPoint structure used in SEEKTABLE blocks. (c.f. format specification) + */ +typedef struct { + FLAC__uint64 sample_number; + /**< The sample number of the target frame. */ + + FLAC__uint64 stream_offset; + /**< The offset, in bytes, of the target frame with respect to + * beginning of the first frame. */ + + unsigned frame_samples; + /**< The number of samples in the target frame. */ +} FLAC__StreamMetadata_SeekPoint; + +extern FLAC_API const unsigned FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN; /**< == 64 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN; /**< == 64 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN; /**< == 16 (bits) */ + +/** The total stream length of a seek point in bytes. */ +#define FLAC__STREAM_METADATA_SEEKPOINT_LENGTH (18u) + +/** The value used in the \a sample_number field of + * FLAC__StreamMetadataSeekPoint used to indicate a placeholder + * point (== 0xffffffffffffffff). + */ +extern FLAC_API const FLAC__uint64 FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER; + +/** FLAC SEEKTABLE structure. (c.f. format specification) + * + * \note From the format specification: + * - The seek points must be sorted by ascending sample number. + * - Each seek point's sample number must be the first sample of the + * target frame. + * - Each seek point's sample number must be unique within the table. + * - Existence of a SEEKTABLE block implies a correct setting of + * total_samples in the stream_info block. + * - Behavior is undefined when more than one SEEKTABLE block is + * present in a stream. + */ +typedef struct { + unsigned num_points; + FLAC__StreamMetadata_SeekPoint *points; +} FLAC__StreamMetadata_SeekTable; + +/** Vorbis comment entry structure used in VORBIS_COMMENT blocks. (c.f. format specification) + * + * For convenience, the APIs maintain a trailing NUL character at the end of + * \a entry which is not counted toward \a length, i.e. + * \code strlen(entry) == length \endcode + */ +typedef struct { + FLAC__uint32 length; + FLAC__byte *entry; +} FLAC__StreamMetadata_VorbisComment_Entry; + +extern FLAC_API const unsigned FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN; /**< == 32 (bits) */ + +/** FLAC VORBIS_COMMENT structure. (c.f. format specification) + */ +typedef struct { + FLAC__StreamMetadata_VorbisComment_Entry vendor_string; + FLAC__uint32 num_comments; + FLAC__StreamMetadata_VorbisComment_Entry *comments; +} FLAC__StreamMetadata_VorbisComment; + +extern FLAC_API const unsigned FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN; /**< == 32 (bits) */ + +/** FLAC CUESHEET track index structure. (See the + * format specification for + * the full description of each field.) + */ +typedef struct { + FLAC__uint64 offset; + /**< Offset in samples, relative to the track offset, of the index + * point. + */ + + FLAC__byte number; + /**< The index point number. */ +} FLAC__StreamMetadata_CueSheet_Index; + +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN; /**< == 64 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN; /**< == 8 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN; /**< == 3*8 (bits) */ + +/** FLAC CUESHEET track structure. (See the + * format specification for + * the full description of each field.) + */ +typedef struct { + FLAC__uint64 offset; + /**< Track offset in samples, relative to the beginning of the FLAC audio stream. */ + + FLAC__byte number; + /**< The track number. */ + + char isrc[13]; + /**< Track ISRC. This is a 12-digit alphanumeric code plus a trailing \c NUL byte */ + + unsigned type:1; + /**< The track type: 0 for audio, 1 for non-audio. */ + + unsigned pre_emphasis:1; + /**< The pre-emphasis flag: 0 for no pre-emphasis, 1 for pre-emphasis. */ + + FLAC__byte num_indices; + /**< The number of track index points. */ + + FLAC__StreamMetadata_CueSheet_Index *indices; + /**< NULL if num_indices == 0, else pointer to array of index points. */ + +} FLAC__StreamMetadata_CueSheet_Track; + +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN; /**< == 64 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN; /**< == 8 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN; /**< == 12*8 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN; /**< == 1 (bit) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN; /**< == 1 (bit) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN; /**< == 6+13*8 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN; /**< == 8 (bits) */ + +/** FLAC CUESHEET structure. (See the + * format specification + * for the full description of each field.) + */ +typedef struct { + char media_catalog_number[129]; + /**< Media catalog number, in ASCII printable characters 0x20-0x7e. In + * general, the media catalog number may be 0 to 128 bytes long; any + * unused characters should be right-padded with NUL characters. + */ + + FLAC__uint64 lead_in; + /**< The number of lead-in samples. */ + + FLAC__bool is_cd; + /**< \c true if CUESHEET corresponds to a Compact Disc, else \c false. */ + + unsigned num_tracks; + /**< The number of tracks. */ + + FLAC__StreamMetadata_CueSheet_Track *tracks; + /**< NULL if num_tracks == 0, else pointer to array of tracks. */ + +} FLAC__StreamMetadata_CueSheet; + +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN; /**< == 128*8 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN; /**< == 64 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN; /**< == 1 (bit) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN; /**< == 7+258*8 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN; /**< == 8 (bits) */ + +/** An enumeration of the PICTURE types (see FLAC__StreamMetadataPicture and id3 v2.4 APIC tag). */ +typedef enum { + FLAC__STREAM_METADATA_PICTURE_TYPE_OTHER = 0, /**< Other */ + FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON_STANDARD = 1, /**< 32x32 pixels 'file icon' (PNG only) */ + FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON = 2, /**< Other file icon */ + FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER = 3, /**< Cover (front) */ + FLAC__STREAM_METADATA_PICTURE_TYPE_BACK_COVER = 4, /**< Cover (back) */ + FLAC__STREAM_METADATA_PICTURE_TYPE_LEAFLET_PAGE = 5, /**< Leaflet page */ + FLAC__STREAM_METADATA_PICTURE_TYPE_MEDIA = 6, /**< Media (e.g. label side of CD) */ + FLAC__STREAM_METADATA_PICTURE_TYPE_LEAD_ARTIST = 7, /**< Lead artist/lead performer/soloist */ + FLAC__STREAM_METADATA_PICTURE_TYPE_ARTIST = 8, /**< Artist/performer */ + FLAC__STREAM_METADATA_PICTURE_TYPE_CONDUCTOR = 9, /**< Conductor */ + FLAC__STREAM_METADATA_PICTURE_TYPE_BAND = 10, /**< Band/Orchestra */ + FLAC__STREAM_METADATA_PICTURE_TYPE_COMPOSER = 11, /**< Composer */ + FLAC__STREAM_METADATA_PICTURE_TYPE_LYRICIST = 12, /**< Lyricist/text writer */ + FLAC__STREAM_METADATA_PICTURE_TYPE_RECORDING_LOCATION = 13, /**< Recording Location */ + FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_RECORDING = 14, /**< During recording */ + FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_PERFORMANCE = 15, /**< During performance */ + FLAC__STREAM_METADATA_PICTURE_TYPE_VIDEO_SCREEN_CAPTURE = 16, /**< Movie/video screen capture */ + FLAC__STREAM_METADATA_PICTURE_TYPE_FISH = 17, /**< A bright coloured fish */ + FLAC__STREAM_METADATA_PICTURE_TYPE_ILLUSTRATION = 18, /**< Illustration */ + FLAC__STREAM_METADATA_PICTURE_TYPE_BAND_LOGOTYPE = 19, /**< Band/artist logotype */ + FLAC__STREAM_METADATA_PICTURE_TYPE_PUBLISHER_LOGOTYPE = 20, /**< Publisher/Studio logotype */ + FLAC__STREAM_METADATA_PICTURE_TYPE_UNDEFINED +} FLAC__StreamMetadata_Picture_Type; + +/** Maps a FLAC__StreamMetadata_Picture_Type to a C string. + * + * Using a FLAC__StreamMetadata_Picture_Type as the index to this array + * will give the string equivalent. The contents should not be + * modified. + */ +extern FLAC_API const char * const FLAC__StreamMetadata_Picture_TypeString[]; + +/** FLAC PICTURE structure. (See the + * format specification + * for the full description of each field.) + */ +typedef struct { + FLAC__StreamMetadata_Picture_Type type; + /**< The kind of picture stored. */ + + char *mime_type; + /**< Picture data's MIME type, in ASCII printable characters + * 0x20-0x7e, NUL terminated. For best compatibility with players, + * use picture data of MIME type \c image/jpeg or \c image/png. A + * MIME type of '-->' is also allowed, in which case the picture + * data should be a complete URL. In file storage, the MIME type is + * stored as a 32-bit length followed by the ASCII string with no NUL + * terminator, but is converted to a plain C string in this structure + * for convenience. + */ + + FLAC__byte *description; + /**< Picture's description in UTF-8, NUL terminated. In file storage, + * the description is stored as a 32-bit length followed by the UTF-8 + * string with no NUL terminator, but is converted to a plain C string + * in this structure for convenience. + */ + + FLAC__uint32 width; + /**< Picture's width in pixels. */ + + FLAC__uint32 height; + /**< Picture's height in pixels. */ + + FLAC__uint32 depth; + /**< Picture's color depth in bits-per-pixel. */ + + FLAC__uint32 colors; + /**< For indexed palettes (like GIF), picture's number of colors (the + * number of palette entries), or \c 0 for non-indexed (i.e. 2^depth). + */ + + FLAC__uint32 data_length; + /**< Length of binary picture data in bytes. */ + + FLAC__byte *data; + /**< Binary picture data. */ + +} FLAC__StreamMetadata_Picture; + +extern FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_TYPE_LEN; /**< == 32 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN; /**< == 32 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN; /**< == 32 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN; /**< == 32 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN; /**< == 32 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN; /**< == 32 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_COLORS_LEN; /**< == 32 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN; /**< == 32 (bits) */ + +/** Structure that is used when a metadata block of unknown type is loaded. + * The contents are opaque. The structure is used only internally to + * correctly handle unknown metadata. + */ +typedef struct { + FLAC__byte *data; +} FLAC__StreamMetadata_Unknown; + +/** FLAC metadata block structure. (c.f. format specification) + */ +typedef struct { + FLAC__MetadataType type; + /**< The type of the metadata block; used determine which member of the + * \a data union to dereference. If type >= FLAC__METADATA_TYPE_UNDEFINED + * then \a data.unknown must be used. */ + + FLAC__bool is_last; + /**< \c true if this metadata block is the last, else \a false */ + + unsigned length; + /**< Length, in bytes, of the block data as it appears in the stream. */ + + union { + FLAC__StreamMetadata_StreamInfo stream_info; + FLAC__StreamMetadata_Padding padding; + FLAC__StreamMetadata_Application application; + FLAC__StreamMetadata_SeekTable seek_table; + FLAC__StreamMetadata_VorbisComment vorbis_comment; + FLAC__StreamMetadata_CueSheet cue_sheet; + FLAC__StreamMetadata_Picture picture; + FLAC__StreamMetadata_Unknown unknown; + } data; + /**< Polymorphic block data; use the \a type value to determine which + * to use. */ +} FLAC__StreamMetadata; + +extern FLAC_API const unsigned FLAC__STREAM_METADATA_IS_LAST_LEN; /**< == 1 (bit) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_TYPE_LEN; /**< == 7 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_LENGTH_LEN; /**< == 24 (bits) */ + +/** The total stream length of a metadata block header in bytes. */ +#define FLAC__STREAM_METADATA_HEADER_LENGTH (4u) + +/*****************************************************************************/ + +/***************************************************************************** + * + * Utility functions + * + *****************************************************************************/ + +/** Tests that a sample rate is valid for FLAC. + * + * \param sample_rate The sample rate to test for compliance. + * \retval FLAC__bool + * \c true if the given sample rate conforms to the specification, else + * \c false. + */ +FLAC_API FLAC__bool FLAC__format_sample_rate_is_valid(unsigned sample_rate); + +/** Tests that a sample rate is valid for the FLAC subset. The subset rules + * for valid sample rates are slightly more complex since the rate has to + * be expressible completely in the frame header. + * + * \param sample_rate The sample rate to test for compliance. + * \retval FLAC__bool + * \c true if the given sample rate conforms to the specification for the + * subset, else \c false. + */ +FLAC_API FLAC__bool FLAC__format_sample_rate_is_subset(unsigned sample_rate); + +/** Check a Vorbis comment entry name to see if it conforms to the Vorbis + * comment specification. + * + * Vorbis comment names must be composed only of characters from + * [0x20-0x3C,0x3E-0x7D]. + * + * \param name A NUL-terminated string to be checked. + * \assert + * \code name != NULL \endcode + * \retval FLAC__bool + * \c false if entry name is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_name_is_legal(const char *name); + +/** Check a Vorbis comment entry value to see if it conforms to the Vorbis + * comment specification. + * + * Vorbis comment values must be valid UTF-8 sequences. + * + * \param value A string to be checked. + * \param length A the length of \a value in bytes. May be + * \c (unsigned)(-1) to indicate that \a value is a plain + * UTF-8 NUL-terminated string. + * \assert + * \code value != NULL \endcode + * \retval FLAC__bool + * \c false if entry name is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_value_is_legal(const FLAC__byte *value, unsigned length); + +/** Check a Vorbis comment entry to see if it conforms to the Vorbis + * comment specification. + * + * Vorbis comment entries must be of the form 'name=value', and 'name' and + * 'value' must be legal according to + * FLAC__format_vorbiscomment_entry_name_is_legal() and + * FLAC__format_vorbiscomment_entry_value_is_legal() respectively. + * + * \param entry An entry to be checked. + * \param length The length of \a entry in bytes. + * \assert + * \code value != NULL \endcode + * \retval FLAC__bool + * \c false if entry name is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_is_legal(const FLAC__byte *entry, unsigned length); + +/** Check a seek table to see if it conforms to the FLAC specification. + * See the format specification for limits on the contents of the + * seek table. + * + * \param seek_table A pointer to a seek table to be checked. + * \assert + * \code seek_table != NULL \endcode + * \retval FLAC__bool + * \c false if seek table is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__format_seektable_is_legal(const FLAC__StreamMetadata_SeekTable *seek_table); + +/** Sort a seek table's seek points according to the format specification. + * This includes a "unique-ification" step to remove duplicates, i.e. + * seek points with identical \a sample_number values. Duplicate seek + * points are converted into placeholder points and sorted to the end of + * the table. + * + * \param seek_table A pointer to a seek table to be sorted. + * \assert + * \code seek_table != NULL \endcode + * \retval unsigned + * The number of duplicate seek points converted into placeholders. + */ +FLAC_API unsigned FLAC__format_seektable_sort(FLAC__StreamMetadata_SeekTable *seek_table); + +/** Check a cue sheet to see if it conforms to the FLAC specification. + * See the format specification for limits on the contents of the + * cue sheet. + * + * \param cue_sheet A pointer to an existing cue sheet to be checked. + * \param check_cd_da_subset If \c true, check CUESHEET against more + * stringent requirements for a CD-DA (audio) disc. + * \param violation Address of a pointer to a string. If there is a + * violation, a pointer to a string explanation of the + * violation will be returned here. \a violation may be + * \c NULL if you don't need the returned string. Do not + * free the returned string; it will always point to static + * data. + * \assert + * \code cue_sheet != NULL \endcode + * \retval FLAC__bool + * \c false if cue sheet is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__format_cuesheet_is_legal(const FLAC__StreamMetadata_CueSheet *cue_sheet, FLAC__bool check_cd_da_subset, const char **violation); + +/** Check picture data to see if it conforms to the FLAC specification. + * See the format specification for limits on the contents of the + * PICTURE block. + * + * \param picture A pointer to existing picture data to be checked. + * \param violation Address of a pointer to a string. If there is a + * violation, a pointer to a string explanation of the + * violation will be returned here. \a violation may be + * \c NULL if you don't need the returned string. Do not + * free the returned string; it will always point to static + * data. + * \assert + * \code picture != NULL \endcode + * \retval FLAC__bool + * \c false if picture data is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__format_picture_is_legal(const FLAC__StreamMetadata_Picture *picture, const char **violation); + +/* \} */ + +#ifdef __cplusplus +} +#endif + +#endif +/********* End of inlined file: format.h *********/ + +/********* Start of inlined file: metadata.h *********/ +#ifndef FLAC__METADATA_H +#define FLAC__METADATA_H + +#include /* for off_t */ + +/* -------------------------------------------------------------------- + (For an example of how all these routines are used, see the source + code for the unit tests in src/test_libFLAC/metadata_*.c, or + metaflac in src/metaflac/) + ------------------------------------------------------------------*/ + +/** \file include/FLAC/metadata.h + * + * \brief + * This module provides functions for creating and manipulating FLAC + * metadata blocks in memory, and three progressively more powerful + * interfaces for traversing and editing metadata in FLAC files. + * + * See the detailed documentation for each interface in the + * \link flac_metadata metadata \endlink module. + */ + +/** \defgroup flac_metadata FLAC/metadata.h: metadata interfaces + * \ingroup flac + * + * \brief + * This module provides functions for creating and manipulating FLAC + * metadata blocks in memory, and three progressively more powerful + * interfaces for traversing and editing metadata in native FLAC files. + * Note that currently only the Chain interface (level 2) supports Ogg + * FLAC files, and it is read-only i.e. no writing back changed + * metadata to file. + * + * There are three metadata interfaces of increasing complexity: + * + * Level 0: + * Read-only access to the STREAMINFO, VORBIS_COMMENT, CUESHEET, and + * PICTURE blocks. + * + * Level 1: + * Read-write access to all metadata blocks. This level is write- + * efficient in most cases (more on this below), and uses less memory + * than level 2. + * + * Level 2: + * Read-write access to all metadata blocks. This level is write- + * efficient in all cases, but uses more memory since all metadata for + * the whole file is read into memory and manipulated before writing + * out again. + * + * What do we mean by efficient? Since FLAC metadata appears at the + * beginning of the file, when writing metadata back to a FLAC file + * it is possible to grow or shrink the metadata such that the entire + * file must be rewritten. However, if the size remains the same during + * changes or PADDING blocks are utilized, only the metadata needs to be + * overwritten, which is much faster. + * + * Efficient means the whole file is rewritten at most one time, and only + * when necessary. Level 1 is not efficient only in the case that you + * cause more than one metadata block to grow or shrink beyond what can + * be accomodated by padding. In this case you should probably use level + * 2, which allows you to edit all the metadata for a file in memory and + * write it out all at once. + * + * All levels know how to skip over and not disturb an ID3v2 tag at the + * front of the file. + * + * All levels access files via their filenames. In addition, level 2 + * has additional alternative read and write functions that take an I/O + * handle and callbacks, for situations where access by filename is not + * possible. + * + * In addition to the three interfaces, this module defines functions for + * creating and manipulating various metadata objects in memory. As we see + * from the Format module, FLAC metadata blocks in memory are very primitive + * structures for storing information in an efficient way. Reading + * information from the structures is easy but creating or modifying them + * directly is more complex. The metadata object routines here facilitate + * this by taking care of the consistency and memory management drudgery. + * + * Unless you will be using the level 1 or 2 interfaces to modify existing + * metadata however, you will not probably not need these. + * + * From a dependency standpoint, none of the encoders or decoders require + * the metadata module. This is so that embedded users can strip out the + * metadata module from libFLAC to reduce the size and complexity. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** \defgroup flac_metadata_level0 FLAC/metadata.h: metadata level 0 interface + * \ingroup flac_metadata + * + * \brief + * The level 0 interface consists of individual routines to read the + * STREAMINFO, VORBIS_COMMENT, CUESHEET, and PICTURE blocks, requiring + * only a filename. + * + * They try to skip any ID3v2 tag at the head of the file. + * + * \{ + */ + +/** Read the STREAMINFO metadata block of the given FLAC file. This function + * will try to skip any ID3v2 tag at the head of the file. + * + * \param filename The path to the FLAC file to read. + * \param streaminfo A pointer to space for the STREAMINFO block. Since + * FLAC__StreamMetadata is a simple structure with no + * memory allocation involved, you pass the address of + * an existing structure. It need not be initialized. + * \assert + * \code filename != NULL \endcode + * \code streaminfo != NULL \endcode + * \retval FLAC__bool + * \c true if a valid STREAMINFO block was read from \a filename. Returns + * \c false if there was a memory allocation error, a file decoder error, + * or the file contained no STREAMINFO block. (A memory allocation error + * is possible because this function must set up a file decoder.) + */ +FLAC_API FLAC__bool FLAC__metadata_get_streaminfo(const char *filename, FLAC__StreamMetadata *streaminfo); + +/** Read the VORBIS_COMMENT metadata block of the given FLAC file. This + * function will try to skip any ID3v2 tag at the head of the file. + * + * \param filename The path to the FLAC file to read. + * \param tags The address where the returned pointer will be + * stored. The \a tags object must be deleted by + * the caller using FLAC__metadata_object_delete(). + * \assert + * \code filename != NULL \endcode + * \code tags != NULL \endcode + * \retval FLAC__bool + * \c true if a valid VORBIS_COMMENT block was read from \a filename, + * and \a *tags will be set to the address of the metadata structure. + * Returns \c false if there was a memory allocation error, a file + * decoder error, or the file contained no VORBIS_COMMENT block, and + * \a *tags will be set to \c NULL. + */ +FLAC_API FLAC__bool FLAC__metadata_get_tags(const char *filename, FLAC__StreamMetadata **tags); + +/** Read the CUESHEET metadata block of the given FLAC file. This + * function will try to skip any ID3v2 tag at the head of the file. + * + * \param filename The path to the FLAC file to read. + * \param cuesheet The address where the returned pointer will be + * stored. The \a cuesheet object must be deleted by + * the caller using FLAC__metadata_object_delete(). + * \assert + * \code filename != NULL \endcode + * \code cuesheet != NULL \endcode + * \retval FLAC__bool + * \c true if a valid CUESHEET block was read from \a filename, + * and \a *cuesheet will be set to the address of the metadata + * structure. Returns \c false if there was a memory allocation + * error, a file decoder error, or the file contained no CUESHEET + * block, and \a *cuesheet will be set to \c NULL. + */ +FLAC_API FLAC__bool FLAC__metadata_get_cuesheet(const char *filename, FLAC__StreamMetadata **cuesheet); + +/** Read a PICTURE metadata block of the given FLAC file. This + * function will try to skip any ID3v2 tag at the head of the file. + * Since there can be more than one PICTURE block in a file, this + * function takes a number of parameters that act as constraints to + * the search. The PICTURE block with the largest area matching all + * the constraints will be returned, or \a *picture will be set to + * \c NULL if there was no such block. + * + * \param filename The path to the FLAC file to read. + * \param picture The address where the returned pointer will be + * stored. The \a picture object must be deleted by + * the caller using FLAC__metadata_object_delete(). + * \param type The desired picture type. Use \c -1 to mean + * "any type". + * \param mime_type The desired MIME type, e.g. "image/jpeg". The + * string will be matched exactly. Use \c NULL to + * mean "any MIME type". + * \param description The desired description. The string will be + * matched exactly. Use \c NULL to mean "any + * description". + * \param max_width The maximum width in pixels desired. Use + * \c (unsigned)(-1) to mean "any width". + * \param max_height The maximum height in pixels desired. Use + * \c (unsigned)(-1) to mean "any height". + * \param max_depth The maximum color depth in bits-per-pixel desired. + * Use \c (unsigned)(-1) to mean "any depth". + * \param max_colors The maximum number of colors desired. Use + * \c (unsigned)(-1) to mean "any number of colors". + * \assert + * \code filename != NULL \endcode + * \code picture != NULL \endcode + * \retval FLAC__bool + * \c true if a valid PICTURE block was read from \a filename, + * and \a *picture will be set to the address of the metadata + * structure. Returns \c false if there was a memory allocation + * error, a file decoder error, or the file contained no PICTURE + * block, and \a *picture will be set to \c NULL. + */ +FLAC_API FLAC__bool FLAC__metadata_get_picture(const char *filename, FLAC__StreamMetadata **picture, FLAC__StreamMetadata_Picture_Type type, const char *mime_type, const FLAC__byte *description, unsigned max_width, unsigned max_height, unsigned max_depth, unsigned max_colors); + +/* \} */ + +/** \defgroup flac_metadata_level1 FLAC/metadata.h: metadata level 1 interface + * \ingroup flac_metadata + * + * \brief + * The level 1 interface provides read-write access to FLAC file metadata and + * operates directly on the FLAC file. + * + * The general usage of this interface is: + * + * - Create an iterator using FLAC__metadata_simple_iterator_new() + * - Attach it to a file using FLAC__metadata_simple_iterator_init() and check + * the exit code. Call FLAC__metadata_simple_iterator_is_writable() to + * see if the file is writable, or only read access is allowed. + * - Use FLAC__metadata_simple_iterator_next() and + * FLAC__metadata_simple_iterator_prev() to traverse the blocks. + * This is does not read the actual blocks themselves. + * FLAC__metadata_simple_iterator_next() is relatively fast. + * FLAC__metadata_simple_iterator_prev() is slower since it needs to search + * forward from the front of the file. + * - Use FLAC__metadata_simple_iterator_get_block_type() or + * FLAC__metadata_simple_iterator_get_block() to access the actual data at + * the current iterator position. The returned object is yours to modify + * and free. + * - Use FLAC__metadata_simple_iterator_set_block() to write a modified block + * back. You must have write permission to the original file. Make sure to + * read the whole comment to FLAC__metadata_simple_iterator_set_block() + * below. + * - Use FLAC__metadata_simple_iterator_insert_block_after() to add new blocks. + * Use the object creation functions from + * \link flac_metadata_object here \endlink to generate new objects. + * - Use FLAC__metadata_simple_iterator_delete_block() to remove the block + * currently referred to by the iterator, or replace it with padding. + * - Destroy the iterator with FLAC__metadata_simple_iterator_delete() when + * finished. + * + * \note + * The FLAC file remains open the whole time between + * FLAC__metadata_simple_iterator_init() and + * FLAC__metadata_simple_iterator_delete(), so make sure you are not altering + * the file during this time. + * + * \note + * Do not modify the \a is_last, \a length, or \a type fields of returned + * FLAC__StreamMetadata objects. These are managed automatically. + * + * \note + * If any of the modification functions + * (FLAC__metadata_simple_iterator_set_block(), + * FLAC__metadata_simple_iterator_delete_block(), + * FLAC__metadata_simple_iterator_insert_block_after(), etc.) return \c false, + * you should delete the iterator as it may no longer be valid. + * + * \{ + */ + +struct FLAC__Metadata_SimpleIterator; +/** The opaque structure definition for the level 1 iterator type. + * See the + * \link flac_metadata_level1 metadata level 1 module \endlink + * for a detailed description. + */ +typedef struct FLAC__Metadata_SimpleIterator FLAC__Metadata_SimpleIterator; + +/** Status type for FLAC__Metadata_SimpleIterator. + * + * The iterator's current status can be obtained by calling FLAC__metadata_simple_iterator_status(). + */ +typedef enum { + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK = 0, + /**< The iterator is in the normal OK state */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT, + /**< The data passed into a function violated the function's usage criteria */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE, + /**< The iterator could not open the target file */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_A_FLAC_FILE, + /**< The iterator could not find the FLAC signature at the start of the file */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_WRITABLE, + /**< The iterator tried to write to a file that was not writable */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_BAD_METADATA, + /**< The iterator encountered input that does not conform to the FLAC metadata specification */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR, + /**< The iterator encountered an error while reading the FLAC file */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR, + /**< The iterator encountered an error while seeking in the FLAC file */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR, + /**< The iterator encountered an error while writing the FLAC file */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_RENAME_ERROR, + /**< The iterator encountered an error renaming the FLAC file */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_UNLINK_ERROR, + /**< The iterator encountered an error removing the temporary file */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR, + /**< Memory allocation failed */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_INTERNAL_ERROR + /**< The caller violated an assertion or an unexpected error occurred */ + +} FLAC__Metadata_SimpleIteratorStatus; + +/** Maps a FLAC__Metadata_SimpleIteratorStatus to a C string. + * + * Using a FLAC__Metadata_SimpleIteratorStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__Metadata_SimpleIteratorStatusString[]; + +/** Create a new iterator instance. + * + * \retval FLAC__Metadata_SimpleIterator* + * \c NULL if there was an error allocating memory, else the new instance. + */ +FLAC_API FLAC__Metadata_SimpleIterator *FLAC__metadata_simple_iterator_new(void); + +/** Free an iterator instance. Deletes the object pointed to by \a iterator. + * + * \param iterator A pointer to an existing iterator. + * \assert + * \code iterator != NULL \endcode + */ +FLAC_API void FLAC__metadata_simple_iterator_delete(FLAC__Metadata_SimpleIterator *iterator); + +/** Get the current status of the iterator. Call this after a function + * returns \c false to get the reason for the error. Also resets the status + * to FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK. + * + * \param iterator A pointer to an existing iterator. + * \assert + * \code iterator != NULL \endcode + * \retval FLAC__Metadata_SimpleIteratorStatus + * The current status of the iterator. + */ +FLAC_API FLAC__Metadata_SimpleIteratorStatus FLAC__metadata_simple_iterator_status(FLAC__Metadata_SimpleIterator *iterator); + +/** Initialize the iterator to point to the first metadata block in the + * given FLAC file. + * + * \param iterator A pointer to an existing iterator. + * \param filename The path to the FLAC file. + * \param read_only If \c true, the FLAC file will be opened + * in read-only mode; if \c false, the FLAC + * file will be opened for edit even if no + * edits are performed. + * \param preserve_file_stats If \c true, the owner and modification + * time will be preserved even if the FLAC + * file is written to. + * \assert + * \code iterator != NULL \endcode + * \code filename != NULL \endcode + * \retval FLAC__bool + * \c false if a memory allocation error occurs, the file can't be + * opened, or another error occurs, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_init(FLAC__Metadata_SimpleIterator *iterator, const char *filename, FLAC__bool read_only, FLAC__bool preserve_file_stats); + +/** Returns \c true if the FLAC file is writable. If \c false, calls to + * FLAC__metadata_simple_iterator_set_block() and + * FLAC__metadata_simple_iterator_insert_block_after() will fail. + * + * \param iterator A pointer to an existing iterator. + * \assert + * \code iterator != NULL \endcode + * \retval FLAC__bool + * See above. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_is_writable(const FLAC__Metadata_SimpleIterator *iterator); + +/** Moves the iterator forward one metadata block, returning \c false if + * already at the end. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \retval FLAC__bool + * \c false if already at the last metadata block of the chain, else + * \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_next(FLAC__Metadata_SimpleIterator *iterator); + +/** Moves the iterator backward one metadata block, returning \c false if + * already at the beginning. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \retval FLAC__bool + * \c false if already at the first metadata block of the chain, else + * \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_prev(FLAC__Metadata_SimpleIterator *iterator); + +/** Returns a flag telling if the current metadata block is the last. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \retval FLAC__bool + * \c true if the current metadata block is the last in the file, + * else \c false. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_is_last(const FLAC__Metadata_SimpleIterator *iterator); + +/** Get the offset of the metadata block at the current position. This + * avoids reading the actual block data which can save time for large + * blocks. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \retval off_t + * The offset of the metadata block at the current iterator position. + * This is the byte offset relative to the beginning of the file of + * the current metadata block's header. + */ +FLAC_API off_t FLAC__metadata_simple_iterator_get_block_offset(const FLAC__Metadata_SimpleIterator *iterator); + +/** Get the type of the metadata block at the current position. This + * avoids reading the actual block data which can save time for large + * blocks. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \retval FLAC__MetadataType + * The type of the metadata block at the current iterator position. + */ +FLAC_API FLAC__MetadataType FLAC__metadata_simple_iterator_get_block_type(const FLAC__Metadata_SimpleIterator *iterator); + +/** Get the length of the metadata block at the current position. This + * avoids reading the actual block data which can save time for large + * blocks. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \retval unsigned + * The length of the metadata block at the current iterator position. + * The is same length as that in the + * metadata block header, + * i.e. the length of the metadata body that follows the header. + */ +FLAC_API unsigned FLAC__metadata_simple_iterator_get_block_length(const FLAC__Metadata_SimpleIterator *iterator); + +/** Get the application ID of the \c APPLICATION block at the current + * position. This avoids reading the actual block data which can save + * time for large blocks. + * + * \param iterator A pointer to an existing initialized iterator. + * \param id A pointer to a buffer of at least \c 4 bytes where + * the ID will be stored. + * \assert + * \code iterator != NULL \endcode + * \code id != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \retval FLAC__bool + * \c true if the ID was successfully read, else \c false, in which + * case you should check FLAC__metadata_simple_iterator_status() to + * find out why. If the status is + * \c FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT, then the + * current metadata block is not an \c APPLICATION block. Otherwise + * if the status is + * \c FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR or + * \c FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR, an I/O error + * occurred and the iterator can no longer be used. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_get_application_id(FLAC__Metadata_SimpleIterator *iterator, FLAC__byte *id); + +/** Get the metadata block at the current position. You can modify the + * block but must use FLAC__metadata_simple_iterator_set_block() to + * write it back to the FLAC file. + * + * You must call FLAC__metadata_object_delete() on the returned object + * when you are finished with it. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \retval FLAC__StreamMetadata* + * The current metadata block, or \c NULL if there was a memory + * allocation error. + */ +FLAC_API FLAC__StreamMetadata *FLAC__metadata_simple_iterator_get_block(FLAC__Metadata_SimpleIterator *iterator); + +/** Write a block back to the FLAC file. This function tries to be + * as efficient as possible; how the block is actually written is + * shown by the following: + * + * Existing block is a STREAMINFO block and the new block is a + * STREAMINFO block: the new block is written in place. Make sure + * you know what you're doing when changing the values of a + * STREAMINFO block. + * + * Existing block is a STREAMINFO block and the new block is a + * not a STREAMINFO block: this is an error since the first block + * must be a STREAMINFO block. Returns \c false without altering the + * file. + * + * Existing block is not a STREAMINFO block and the new block is a + * STREAMINFO block: this is an error since there may be only one + * STREAMINFO block. Returns \c false without altering the file. + * + * Existing block and new block are the same length: the existing + * block will be replaced by the new block, written in place. + * + * Existing block is longer than new block: if use_padding is \c true, + * the existing block will be overwritten in place with the new + * block followed by a PADDING block, if possible, to make the total + * size the same as the existing block. Remember that a padding + * block requires at least four bytes so if the difference in size + * between the new block and existing block is less than that, the + * entire file will have to be rewritten, using the new block's + * exact size. If use_padding is \c false, the entire file will be + * rewritten, replacing the existing block by the new block. + * + * Existing block is shorter than new block: if use_padding is \c true, + * the function will try and expand the new block into the following + * PADDING block, if it exists and doing so won't shrink the PADDING + * block to less than 4 bytes. If there is no following PADDING + * block, or it will shrink to less than 4 bytes, or use_padding is + * \c false, the entire file is rewritten, replacing the existing block + * with the new block. Note that in this case any following PADDING + * block is preserved as is. + * + * After writing the block, the iterator will remain in the same + * place, i.e. pointing to the new block. + * + * \param iterator A pointer to an existing initialized iterator. + * \param block The block to set. + * \param use_padding See above. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \code block != NULL \endcode + * \retval FLAC__bool + * \c true if successful, else \c false. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_set_block(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, FLAC__bool use_padding); + +/** This is similar to FLAC__metadata_simple_iterator_set_block() + * except that instead of writing over an existing block, it appends + * a block after the existing block. \a use_padding is again used to + * tell the function to try an expand into following padding in an + * attempt to avoid rewriting the entire file. + * + * This function will fail and return \c false if given a STREAMINFO + * block. + * + * After writing the block, the iterator will be pointing to the + * new block. + * + * \param iterator A pointer to an existing initialized iterator. + * \param block The block to set. + * \param use_padding See above. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \code block != NULL \endcode + * \retval FLAC__bool + * \c true if successful, else \c false. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_insert_block_after(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, FLAC__bool use_padding); + +/** Deletes the block at the current position. This will cause the + * entire FLAC file to be rewritten, unless \a use_padding is \c true, + * in which case the block will be replaced by an equal-sized PADDING + * block. The iterator will be left pointing to the block before the + * one just deleted. + * + * You may not delete the STREAMINFO block. + * + * \param iterator A pointer to an existing initialized iterator. + * \param use_padding See above. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \retval FLAC__bool + * \c true if successful, else \c false. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_delete_block(FLAC__Metadata_SimpleIterator *iterator, FLAC__bool use_padding); + +/* \} */ + +/** \defgroup flac_metadata_level2 FLAC/metadata.h: metadata level 2 interface + * \ingroup flac_metadata + * + * \brief + * The level 2 interface provides read-write access to FLAC file metadata; + * all metadata is read into memory, operated on in memory, and then written + * to file, which is more efficient than level 1 when editing multiple blocks. + * + * Currently Ogg FLAC is supported for read only, via + * FLAC__metadata_chain_read_ogg() but a subsequent + * FLAC__metadata_chain_write() will fail. + * + * The general usage of this interface is: + * + * - Create a new chain using FLAC__metadata_chain_new(). A chain is a + * linked list of FLAC metadata blocks. + * - Read all metadata into the the chain from a FLAC file using + * FLAC__metadata_chain_read() or FLAC__metadata_chain_read_ogg() and + * check the status. + * - Optionally, consolidate the padding using + * FLAC__metadata_chain_merge_padding() or + * FLAC__metadata_chain_sort_padding(). + * - Create a new iterator using FLAC__metadata_iterator_new() + * - Initialize the iterator to point to the first element in the chain + * using FLAC__metadata_iterator_init() + * - Traverse the chain using FLAC__metadata_iterator_next and + * FLAC__metadata_iterator_prev(). + * - Get a block for reading or modification using + * FLAC__metadata_iterator_get_block(). The pointer to the object + * inside the chain is returned, so the block is yours to modify. + * Changes will be reflected in the FLAC file when you write the + * chain. You can also add and delete blocks (see functions below). + * - When done, write out the chain using FLAC__metadata_chain_write(). + * Make sure to read the whole comment to the function below. + * - Delete the chain using FLAC__metadata_chain_delete(). + * + * \note + * Even though the FLAC file is not open while the chain is being + * manipulated, you must not alter the file externally during + * this time. The chain assumes the FLAC file will not change + * between the time of FLAC__metadata_chain_read()/FLAC__metadata_chain_read_ogg() + * and FLAC__metadata_chain_write(). + * + * \note + * Do not modify the is_last, length, or type fields of returned + * FLAC__StreamMetadata objects. These are managed automatically. + * + * \note + * The metadata objects returned by FLAC__metadata_iterator_get_block() + * are owned by the chain; do not FLAC__metadata_object_delete() them. + * In the same way, blocks passed to FLAC__metadata_iterator_set_block() + * become owned by the chain and they will be deleted when the chain is + * deleted. + * + * \{ + */ + +struct FLAC__Metadata_Chain; +/** The opaque structure definition for the level 2 chain type. + */ +typedef struct FLAC__Metadata_Chain FLAC__Metadata_Chain; + +struct FLAC__Metadata_Iterator; +/** The opaque structure definition for the level 2 iterator type. + */ +typedef struct FLAC__Metadata_Iterator FLAC__Metadata_Iterator; + +typedef enum { + FLAC__METADATA_CHAIN_STATUS_OK = 0, + /**< The chain is in the normal OK state */ + + FLAC__METADATA_CHAIN_STATUS_ILLEGAL_INPUT, + /**< The data passed into a function violated the function's usage criteria */ + + FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE, + /**< The chain could not open the target file */ + + FLAC__METADATA_CHAIN_STATUS_NOT_A_FLAC_FILE, + /**< The chain could not find the FLAC signature at the start of the file */ + + FLAC__METADATA_CHAIN_STATUS_NOT_WRITABLE, + /**< The chain tried to write to a file that was not writable */ + + FLAC__METADATA_CHAIN_STATUS_BAD_METADATA, + /**< The chain encountered input that does not conform to the FLAC metadata specification */ + + FLAC__METADATA_CHAIN_STATUS_READ_ERROR, + /**< The chain encountered an error while reading the FLAC file */ + + FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR, + /**< The chain encountered an error while seeking in the FLAC file */ + + FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR, + /**< The chain encountered an error while writing the FLAC file */ + + FLAC__METADATA_CHAIN_STATUS_RENAME_ERROR, + /**< The chain encountered an error renaming the FLAC file */ + + FLAC__METADATA_CHAIN_STATUS_UNLINK_ERROR, + /**< The chain encountered an error removing the temporary file */ + + FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR, + /**< Memory allocation failed */ + + FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR, + /**< The caller violated an assertion or an unexpected error occurred */ + + FLAC__METADATA_CHAIN_STATUS_INVALID_CALLBACKS, + /**< One or more of the required callbacks was NULL */ + + FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH, + /**< FLAC__metadata_chain_write() was called on a chain read by + * FLAC__metadata_chain_read_with_callbacks()/FLAC__metadata_chain_read_ogg_with_callbacks(), + * or + * FLAC__metadata_chain_write_with_callbacks()/FLAC__metadata_chain_write_with_callbacks_and_tempfile() + * was called on a chain read by + * FLAC__metadata_chain_read()/FLAC__metadata_chain_read_ogg(). + * Matching read/write methods must always be used. */ + + FLAC__METADATA_CHAIN_STATUS_WRONG_WRITE_CALL + /**< FLAC__metadata_chain_write_with_callbacks() was called when the + * chain write requires a tempfile; use + * FLAC__metadata_chain_write_with_callbacks_and_tempfile() instead. + * Or, FLAC__metadata_chain_write_with_callbacks_and_tempfile() was + * called when the chain write does not require a tempfile; use + * FLAC__metadata_chain_write_with_callbacks() instead. + * Always check FLAC__metadata_chain_check_if_tempfile_needed() + * before writing via callbacks. */ + +} FLAC__Metadata_ChainStatus; + +/** Maps a FLAC__Metadata_ChainStatus to a C string. + * + * Using a FLAC__Metadata_ChainStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__Metadata_ChainStatusString[]; + +/*********** FLAC__Metadata_Chain ***********/ + +/** Create a new chain instance. + * + * \retval FLAC__Metadata_Chain* + * \c NULL if there was an error allocating memory, else the new instance. + */ +FLAC_API FLAC__Metadata_Chain *FLAC__metadata_chain_new(void); + +/** Free a chain instance. Deletes the object pointed to by \a chain. + * + * \param chain A pointer to an existing chain. + * \assert + * \code chain != NULL \endcode + */ +FLAC_API void FLAC__metadata_chain_delete(FLAC__Metadata_Chain *chain); + +/** Get the current status of the chain. Call this after a function + * returns \c false to get the reason for the error. Also resets the + * status to FLAC__METADATA_CHAIN_STATUS_OK. + * + * \param chain A pointer to an existing chain. + * \assert + * \code chain != NULL \endcode + * \retval FLAC__Metadata_ChainStatus + * The current status of the chain. + */ +FLAC_API FLAC__Metadata_ChainStatus FLAC__metadata_chain_status(FLAC__Metadata_Chain *chain); + +/** Read all metadata from a FLAC file into the chain. + * + * \param chain A pointer to an existing chain. + * \param filename The path to the FLAC file to read. + * \assert + * \code chain != NULL \endcode + * \code filename != NULL \endcode + * \retval FLAC__bool + * \c true if a valid list of metadata blocks was read from + * \a filename, else \c false. On failure, check the status with + * FLAC__metadata_chain_status(). + */ +FLAC_API FLAC__bool FLAC__metadata_chain_read(FLAC__Metadata_Chain *chain, const char *filename); + +/** Read all metadata from an Ogg FLAC file into the chain. + * + * \note Ogg FLAC metadata data writing is not supported yet and + * FLAC__metadata_chain_write() will fail. + * + * \param chain A pointer to an existing chain. + * \param filename The path to the Ogg FLAC file to read. + * \assert + * \code chain != NULL \endcode + * \code filename != NULL \endcode + * \retval FLAC__bool + * \c true if a valid list of metadata blocks was read from + * \a filename, else \c false. On failure, check the status with + * FLAC__metadata_chain_status(). + */ +FLAC_API FLAC__bool FLAC__metadata_chain_read_ogg(FLAC__Metadata_Chain *chain, const char *filename); + +/** Read all metadata from a FLAC stream into the chain via I/O callbacks. + * + * The \a handle need only be open for reading, but must be seekable. + * The equivalent minimum stdio fopen() file mode is \c "r" (or \c "rb" + * for Windows). + * + * \param chain A pointer to an existing chain. + * \param handle The I/O handle of the FLAC stream to read. The + * handle will NOT be closed after the metadata is read; + * that is the duty of the caller. + * \param callbacks + * A set of callbacks to use for I/O. The mandatory + * callbacks are \a read, \a seek, and \a tell. + * \assert + * \code chain != NULL \endcode + * \retval FLAC__bool + * \c true if a valid list of metadata blocks was read from + * \a handle, else \c false. On failure, check the status with + * FLAC__metadata_chain_status(). + */ +FLAC_API FLAC__bool FLAC__metadata_chain_read_with_callbacks(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks); + +/** Read all metadata from an Ogg FLAC stream into the chain via I/O callbacks. + * + * The \a handle need only be open for reading, but must be seekable. + * The equivalent minimum stdio fopen() file mode is \c "r" (or \c "rb" + * for Windows). + * + * \note Ogg FLAC metadata data writing is not supported yet and + * FLAC__metadata_chain_write() will fail. + * + * \param chain A pointer to an existing chain. + * \param handle The I/O handle of the Ogg FLAC stream to read. The + * handle will NOT be closed after the metadata is read; + * that is the duty of the caller. + * \param callbacks + * A set of callbacks to use for I/O. The mandatory + * callbacks are \a read, \a seek, and \a tell. + * \assert + * \code chain != NULL \endcode + * \retval FLAC__bool + * \c true if a valid list of metadata blocks was read from + * \a handle, else \c false. On failure, check the status with + * FLAC__metadata_chain_status(). + */ +FLAC_API FLAC__bool FLAC__metadata_chain_read_ogg_with_callbacks(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks); + +/** Checks if writing the given chain would require the use of a + * temporary file, or if it could be written in place. + * + * Under certain conditions, padding can be utilized so that writing + * edited metadata back to the FLAC file does not require rewriting the + * entire file. If rewriting is required, then a temporary workfile is + * required. When writing metadata using callbacks, you must check + * this function to know whether to call + * FLAC__metadata_chain_write_with_callbacks() or + * FLAC__metadata_chain_write_with_callbacks_and_tempfile(). When + * writing with FLAC__metadata_chain_write(), the temporary file is + * handled internally. + * + * \param chain A pointer to an existing chain. + * \param use_padding + * Whether or not padding will be allowed to be used + * during the write. The value of \a use_padding given + * here must match the value later passed to + * FLAC__metadata_chain_write_with_callbacks() or + * FLAC__metadata_chain_write_with_callbacks_with_tempfile(). + * \assert + * \code chain != NULL \endcode + * \retval FLAC__bool + * \c true if writing the current chain would require a tempfile, or + * \c false if metadata can be written in place. + */ +FLAC_API FLAC__bool FLAC__metadata_chain_check_if_tempfile_needed(FLAC__Metadata_Chain *chain, FLAC__bool use_padding); + +/** Write all metadata out to the FLAC file. This function tries to be as + * efficient as possible; how the metadata is actually written is shown by + * the following: + * + * If the current chain is the same size as the existing metadata, the new + * data is written in place. + * + * If the current chain is longer than the existing metadata, and + * \a use_padding is \c true, and the last block is a PADDING block of + * sufficient length, the function will truncate the final padding block + * so that the overall size of the metadata is the same as the existing + * metadata, and then just rewrite the metadata. Otherwise, if not all of + * the above conditions are met, the entire FLAC file must be rewritten. + * If you want to use padding this way it is a good idea to call + * FLAC__metadata_chain_sort_padding() first so that you have the maximum + * amount of padding to work with, unless you need to preserve ordering + * of the PADDING blocks for some reason. + * + * If the current chain is shorter than the existing metadata, and + * \a use_padding is \c true, and the final block is a PADDING block, the padding + * is extended to make the overall size the same as the existing data. If + * \a use_padding is \c true and the last block is not a PADDING block, a new + * PADDING block is added to the end of the new data to make it the same + * size as the existing data (if possible, see the note to + * FLAC__metadata_simple_iterator_set_block() about the four byte limit) + * and the new data is written in place. If none of the above apply or + * \a use_padding is \c false, the entire FLAC file is rewritten. + * + * If \a preserve_file_stats is \c true, the owner and modification time will + * be preserved even if the FLAC file is written. + * + * For this write function to be used, the chain must have been read with + * FLAC__metadata_chain_read()/FLAC__metadata_chain_read_ogg(), not + * FLAC__metadata_chain_read_with_callbacks()/FLAC__metadata_chain_read_ogg_with_callbacks(). + * + * \param chain A pointer to an existing chain. + * \param use_padding See above. + * \param preserve_file_stats See above. + * \assert + * \code chain != NULL \endcode + * \retval FLAC__bool + * \c true if the write succeeded, else \c false. On failure, + * check the status with FLAC__metadata_chain_status(). + */ +FLAC_API FLAC__bool FLAC__metadata_chain_write(FLAC__Metadata_Chain *chain, FLAC__bool use_padding, FLAC__bool preserve_file_stats); + +/** Write all metadata out to a FLAC stream via callbacks. + * + * (See FLAC__metadata_chain_write() for the details on how padding is + * used to write metadata in place if possible.) + * + * The \a handle must be open for updating and be seekable. The + * equivalent minimum stdio fopen() file mode is \c "r+" (or \c "r+b" + * for Windows). + * + * For this write function to be used, the chain must have been read with + * FLAC__metadata_chain_read_with_callbacks()/FLAC__metadata_chain_read_ogg_with_callbacks(), + * not FLAC__metadata_chain_read()/FLAC__metadata_chain_read_ogg(). + * Also, FLAC__metadata_chain_check_if_tempfile_needed() must have returned + * \c false. + * + * \param chain A pointer to an existing chain. + * \param use_padding See FLAC__metadata_chain_write() + * \param handle The I/O handle of the FLAC stream to write. The + * handle will NOT be closed after the metadata is + * written; that is the duty of the caller. + * \param callbacks A set of callbacks to use for I/O. The mandatory + * callbacks are \a write and \a seek. + * \assert + * \code chain != NULL \endcode + * \retval FLAC__bool + * \c true if the write succeeded, else \c false. On failure, + * check the status with FLAC__metadata_chain_status(). + */ +FLAC_API FLAC__bool FLAC__metadata_chain_write_with_callbacks(FLAC__Metadata_Chain *chain, FLAC__bool use_padding, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks); + +/** Write all metadata out to a FLAC stream via callbacks. + * + * (See FLAC__metadata_chain_write() for the details on how padding is + * used to write metadata in place if possible.) + * + * This version of the write-with-callbacks function must be used when + * FLAC__metadata_chain_check_if_tempfile_needed() returns true. In + * this function, you must supply an I/O handle corresponding to the + * FLAC file to edit, and a temporary handle to which the new FLAC + * file will be written. It is the caller's job to move this temporary + * FLAC file on top of the original FLAC file to complete the metadata + * edit. + * + * The \a handle must be open for reading and be seekable. The + * equivalent minimum stdio fopen() file mode is \c "r" (or \c "rb" + * for Windows). + * + * The \a temp_handle must be open for writing. The + * equivalent minimum stdio fopen() file mode is \c "w" (or \c "wb" + * for Windows). It should be an empty stream, or at least positioned + * at the start-of-file (in which case it is the caller's duty to + * truncate it on return). + * + * For this write function to be used, the chain must have been read with + * FLAC__metadata_chain_read_with_callbacks()/FLAC__metadata_chain_read_ogg_with_callbacks(), + * not FLAC__metadata_chain_read()/FLAC__metadata_chain_read_ogg(). + * Also, FLAC__metadata_chain_check_if_tempfile_needed() must have returned + * \c true. + * + * \param chain A pointer to an existing chain. + * \param use_padding See FLAC__metadata_chain_write() + * \param handle The I/O handle of the original FLAC stream to read. + * The handle will NOT be closed after the metadata is + * written; that is the duty of the caller. + * \param callbacks A set of callbacks to use for I/O on \a handle. + * The mandatory callbacks are \a read, \a seek, and + * \a eof. + * \param temp_handle The I/O handle of the FLAC stream to write. The + * handle will NOT be closed after the metadata is + * written; that is the duty of the caller. + * \param temp_callbacks + * A set of callbacks to use for I/O on temp_handle. + * The only mandatory callback is \a write. + * \assert + * \code chain != NULL \endcode + * \retval FLAC__bool + * \c true if the write succeeded, else \c false. On failure, + * check the status with FLAC__metadata_chain_status(). + */ +FLAC_API FLAC__bool FLAC__metadata_chain_write_with_callbacks_and_tempfile(FLAC__Metadata_Chain *chain, FLAC__bool use_padding, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks, FLAC__IOHandle temp_handle, FLAC__IOCallbacks temp_callbacks); + +/** Merge adjacent PADDING blocks into a single block. + * + * \note This function does not write to the FLAC file, it only + * modifies the chain. + * + * \warning Any iterator on the current chain will become invalid after this + * call. You should delete the iterator and get a new one. + * + * \param chain A pointer to an existing chain. + * \assert + * \code chain != NULL \endcode + */ +FLAC_API void FLAC__metadata_chain_merge_padding(FLAC__Metadata_Chain *chain); + +/** This function will move all PADDING blocks to the end on the metadata, + * then merge them into a single block. + * + * \note This function does not write to the FLAC file, it only + * modifies the chain. + * + * \warning Any iterator on the current chain will become invalid after this + * call. You should delete the iterator and get a new one. + * + * \param chain A pointer to an existing chain. + * \assert + * \code chain != NULL \endcode + */ +FLAC_API void FLAC__metadata_chain_sort_padding(FLAC__Metadata_Chain *chain); + +/*********** FLAC__Metadata_Iterator ***********/ + +/** Create a new iterator instance. + * + * \retval FLAC__Metadata_Iterator* + * \c NULL if there was an error allocating memory, else the new instance. + */ +FLAC_API FLAC__Metadata_Iterator *FLAC__metadata_iterator_new(void); + +/** Free an iterator instance. Deletes the object pointed to by \a iterator. + * + * \param iterator A pointer to an existing iterator. + * \assert + * \code iterator != NULL \endcode + */ +FLAC_API void FLAC__metadata_iterator_delete(FLAC__Metadata_Iterator *iterator); + +/** Initialize the iterator to point to the first metadata block in the + * given chain. + * + * \param iterator A pointer to an existing iterator. + * \param chain A pointer to an existing and initialized (read) chain. + * \assert + * \code iterator != NULL \endcode + * \code chain != NULL \endcode + */ +FLAC_API void FLAC__metadata_iterator_init(FLAC__Metadata_Iterator *iterator, FLAC__Metadata_Chain *chain); + +/** Moves the iterator forward one metadata block, returning \c false if + * already at the end. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_iterator_init() + * \retval FLAC__bool + * \c false if already at the last metadata block of the chain, else + * \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_iterator_next(FLAC__Metadata_Iterator *iterator); + +/** Moves the iterator backward one metadata block, returning \c false if + * already at the beginning. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_iterator_init() + * \retval FLAC__bool + * \c false if already at the first metadata block of the chain, else + * \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_iterator_prev(FLAC__Metadata_Iterator *iterator); + +/** Get the type of the metadata block at the current position. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_iterator_init() + * \retval FLAC__MetadataType + * The type of the metadata block at the current iterator position. + */ +FLAC_API FLAC__MetadataType FLAC__metadata_iterator_get_block_type(const FLAC__Metadata_Iterator *iterator); + +/** Get the metadata block at the current position. You can modify + * the block in place but must write the chain before the changes + * are reflected to the FLAC file. You do not need to call + * FLAC__metadata_iterator_set_block() to reflect the changes; + * the pointer returned by FLAC__metadata_iterator_get_block() + * points directly into the chain. + * + * \warning + * Do not call FLAC__metadata_object_delete() on the returned object; + * to delete a block use FLAC__metadata_iterator_delete_block(). + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_iterator_init() + * \retval FLAC__StreamMetadata* + * The current metadata block. + */ +FLAC_API FLAC__StreamMetadata *FLAC__metadata_iterator_get_block(FLAC__Metadata_Iterator *iterator); + +/** Set the metadata block at the current position, replacing the existing + * block. The new block passed in becomes owned by the chain and it will be + * deleted when the chain is deleted. + * + * \param iterator A pointer to an existing initialized iterator. + * \param block A pointer to a metadata block. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_iterator_init() + * \code block != NULL \endcode + * \retval FLAC__bool + * \c false if the conditions in the above description are not met, or + * a memory allocation error occurs, otherwise \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_iterator_set_block(FLAC__Metadata_Iterator *iterator, FLAC__StreamMetadata *block); + +/** Removes the current block from the chain. If \a replace_with_padding is + * \c true, the block will instead be replaced with a padding block of equal + * size. You can not delete the STREAMINFO block. The iterator will be + * left pointing to the block before the one just "deleted", even if + * \a replace_with_padding is \c true. + * + * \param iterator A pointer to an existing initialized iterator. + * \param replace_with_padding See above. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_iterator_init() + * \retval FLAC__bool + * \c false if the conditions in the above description are not met, + * otherwise \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_iterator_delete_block(FLAC__Metadata_Iterator *iterator, FLAC__bool replace_with_padding); + +/** Insert a new block before the current block. You cannot insert a block + * before the first STREAMINFO block. You cannot insert a STREAMINFO block + * as there can be only one, the one that already exists at the head when you + * read in a chain. The chain takes ownership of the new block and it will be + * deleted when the chain is deleted. The iterator will be left pointing to + * the new block. + * + * \param iterator A pointer to an existing initialized iterator. + * \param block A pointer to a metadata block to insert. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_iterator_init() + * \retval FLAC__bool + * \c false if the conditions in the above description are not met, or + * a memory allocation error occurs, otherwise \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_iterator_insert_block_before(FLAC__Metadata_Iterator *iterator, FLAC__StreamMetadata *block); + +/** Insert a new block after the current block. You cannot insert a STREAMINFO + * block as there can be only one, the one that already exists at the head when + * you read in a chain. The chain takes ownership of the new block and it will + * be deleted when the chain is deleted. The iterator will be left pointing to + * the new block. + * + * \param iterator A pointer to an existing initialized iterator. + * \param block A pointer to a metadata block to insert. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_iterator_init() + * \retval FLAC__bool + * \c false if the conditions in the above description are not met, or + * a memory allocation error occurs, otherwise \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_iterator_insert_block_after(FLAC__Metadata_Iterator *iterator, FLAC__StreamMetadata *block); + +/* \} */ + +/** \defgroup flac_metadata_object FLAC/metadata.h: metadata object methods + * \ingroup flac_metadata + * + * \brief + * This module contains methods for manipulating FLAC metadata objects. + * + * Since many are variable length we have to be careful about the memory + * management. We decree that all pointers to data in the object are + * owned by the object and memory-managed by the object. + * + * Use the FLAC__metadata_object_new() and FLAC__metadata_object_delete() + * functions to create all instances. When using the + * FLAC__metadata_object_set_*() functions to set pointers to data, set + * \a copy to \c true to have the function make it's own copy of the data, or + * to \c false to give the object ownership of your data. In the latter case + * your pointer must be freeable by free() and will be free()d when the object + * is FLAC__metadata_object_delete()d. It is legal to pass a null pointer as + * the data pointer to a FLAC__metadata_object_set_*() function as long as + * the length argument is 0 and the \a copy argument is \c false. + * + * The FLAC__metadata_object_new() and FLAC__metadata_object_clone() function + * will return \c NULL in the case of a memory allocation error, otherwise a new + * object. The FLAC__metadata_object_set_*() functions return \c false in the + * case of a memory allocation error. + * + * We don't have the convenience of C++ here, so note that the library relies + * on you to keep the types straight. In other words, if you pass, for + * example, a FLAC__StreamMetadata* that represents a STREAMINFO block to + * FLAC__metadata_object_application_set_data(), you will get an assertion + * failure. + * + * For convenience the FLAC__metadata_object_vorbiscomment_*() functions + * maintain a trailing NUL on each Vorbis comment entry. This is not counted + * toward the length or stored in the stream, but it can make working with plain + * comments (those that don't contain embedded-NULs in the value) easier. + * Entries passed into these functions have trailing NULs added if missing, and + * returned entries are guaranteed to have a trailing NUL. + * + * The FLAC__metadata_object_vorbiscomment_*() functions that take a Vorbis + * comment entry/name/value will first validate that it complies with the Vorbis + * comment specification and return false if it does not. + * + * There is no need to recalculate the length field on metadata blocks you + * have modified. They will be calculated automatically before they are + * written back to a file. + * + * \{ + */ + +/** Create a new metadata object instance of the given type. + * + * The object will be "empty"; i.e. values and data pointers will be \c 0, + * with the exception of FLAC__METADATA_TYPE_VORBIS_COMMENT, which will have + * the vendor string set (but zero comments). + * + * Do not pass in a value greater than or equal to + * \a FLAC__METADATA_TYPE_UNDEFINED unless you really know what you're + * doing. + * + * \param type Type of object to create + * \retval FLAC__StreamMetadata* + * \c NULL if there was an error allocating memory or the type code is + * greater than FLAC__MAX_METADATA_TYPE_CODE, else the new instance. + */ +FLAC_API FLAC__StreamMetadata *FLAC__metadata_object_new(FLAC__MetadataType type); + +/** Create a copy of an existing metadata object. + * + * The copy is a "deep" copy, i.e. dynamically allocated data within the + * object is also copied. The caller takes ownership of the new block and + * is responsible for freeing it with FLAC__metadata_object_delete(). + * + * \param object Pointer to object to copy. + * \assert + * \code object != NULL \endcode + * \retval FLAC__StreamMetadata* + * \c NULL if there was an error allocating memory, else the new instance. + */ +FLAC_API FLAC__StreamMetadata *FLAC__metadata_object_clone(const FLAC__StreamMetadata *object); + +/** Free a metadata object. Deletes the object pointed to by \a object. + * + * The delete is a "deep" delete, i.e. dynamically allocated data within the + * object is also deleted. + * + * \param object A pointer to an existing object. + * \assert + * \code object != NULL \endcode + */ +FLAC_API void FLAC__metadata_object_delete(FLAC__StreamMetadata *object); + +/** Compares two metadata objects. + * + * The compare is "deep", i.e. dynamically allocated data within the + * object is also compared. + * + * \param block1 A pointer to an existing object. + * \param block2 A pointer to an existing object. + * \assert + * \code block1 != NULL \endcode + * \code block2 != NULL \endcode + * \retval FLAC__bool + * \c true if objects are identical, else \c false. + */ +FLAC_API FLAC__bool FLAC__metadata_object_is_equal(const FLAC__StreamMetadata *block1, const FLAC__StreamMetadata *block2); + +/** Sets the application data of an APPLICATION block. + * + * If \a copy is \c true, a copy of the data is stored; otherwise, the object + * takes ownership of the pointer. The existing data will be freed if this + * function is successful, otherwise the original data will remain if \a copy + * is \c true and malloc() fails. + * + * \note It is safe to pass a const pointer to \a data if \a copy is \c true. + * + * \param object A pointer to an existing APPLICATION object. + * \param data A pointer to the data to set. + * \param length The length of \a data in bytes. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_APPLICATION \endcode + * \code (data != NULL && length > 0) || + * (data == NULL && length == 0 && copy == false) \endcode + * \retval FLAC__bool + * \c false if \a copy is \c true and malloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_application_set_data(FLAC__StreamMetadata *object, FLAC__byte *data, unsigned length, FLAC__bool copy); + +/** Resize the seekpoint array. + * + * If the size shrinks, elements will truncated; if it grows, new placeholder + * points will be added to the end. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param new_num_points The desired length of the array; may be \c 0. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \code (object->data.seek_table.points == NULL && object->data.seek_table.num_points == 0) || + * (object->data.seek_table.points != NULL && object->data.seek_table.num_points > 0) \endcode + * \retval FLAC__bool + * \c false if memory allocation error, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_resize_points(FLAC__StreamMetadata *object, unsigned new_num_points); + +/** Set a seekpoint in a seektable. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param point_num Index into seekpoint array to set. + * \param point The point to set. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \code object->data.seek_table.num_points > point_num \endcode + */ +FLAC_API void FLAC__metadata_object_seektable_set_point(FLAC__StreamMetadata *object, unsigned point_num, FLAC__StreamMetadata_SeekPoint point); + +/** Insert a seekpoint into a seektable. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param point_num Index into seekpoint array to set. + * \param point The point to set. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \code object->data.seek_table.num_points >= point_num \endcode + * \retval FLAC__bool + * \c false if memory allocation error, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_insert_point(FLAC__StreamMetadata *object, unsigned point_num, FLAC__StreamMetadata_SeekPoint point); + +/** Delete a seekpoint from a seektable. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param point_num Index into seekpoint array to set. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \code object->data.seek_table.num_points > point_num \endcode + * \retval FLAC__bool + * \c false if memory allocation error, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_delete_point(FLAC__StreamMetadata *object, unsigned point_num); + +/** Check a seektable to see if it conforms to the FLAC specification. + * See the format specification for limits on the contents of the + * seektable. + * + * \param object A pointer to an existing SEEKTABLE object. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \retval FLAC__bool + * \c false if seek table is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_is_legal(const FLAC__StreamMetadata *object); + +/** Append a number of placeholder points to the end of a seek table. + * + * \note + * As with the other ..._seektable_template_... functions, you should + * call FLAC__metadata_object_seektable_template_sort() when finished + * to make the seek table legal. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param num The number of placeholder points to append. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \retval FLAC__bool + * \c false if memory allocation fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_placeholders(FLAC__StreamMetadata *object, unsigned num); + +/** Append a specific seek point template to the end of a seek table. + * + * \note + * As with the other ..._seektable_template_... functions, you should + * call FLAC__metadata_object_seektable_template_sort() when finished + * to make the seek table legal. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param sample_number The sample number of the seek point template. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \retval FLAC__bool + * \c false if memory allocation fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_point(FLAC__StreamMetadata *object, FLAC__uint64 sample_number); + +/** Append specific seek point templates to the end of a seek table. + * + * \note + * As with the other ..._seektable_template_... functions, you should + * call FLAC__metadata_object_seektable_template_sort() when finished + * to make the seek table legal. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param sample_numbers An array of sample numbers for the seek points. + * \param num The number of seek point templates to append. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \retval FLAC__bool + * \c false if memory allocation fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_points(FLAC__StreamMetadata *object, FLAC__uint64 sample_numbers[], unsigned num); + +/** Append a set of evenly-spaced seek point templates to the end of a + * seek table. + * + * \note + * As with the other ..._seektable_template_... functions, you should + * call FLAC__metadata_object_seektable_template_sort() when finished + * to make the seek table legal. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param num The number of placeholder points to append. + * \param total_samples The total number of samples to be encoded; + * the seekpoints will be spaced approximately + * \a total_samples / \a num samples apart. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \code total_samples > 0 \endcode + * \retval FLAC__bool + * \c false if memory allocation fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_spaced_points(FLAC__StreamMetadata *object, unsigned num, FLAC__uint64 total_samples); + +/** Append a set of evenly-spaced seek point templates to the end of a + * seek table. + * + * \note + * As with the other ..._seektable_template_... functions, you should + * call FLAC__metadata_object_seektable_template_sort() when finished + * to make the seek table legal. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param samples The number of samples apart to space the placeholder + * points. The first point will be at sample \c 0, the + * second at sample \a samples, then 2*\a samples, and + * so on. As long as \a samples and \a total_samples + * are greater than \c 0, there will always be at least + * one seekpoint at sample \c 0. + * \param total_samples The total number of samples to be encoded; + * the seekpoints will be spaced + * \a samples samples apart. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \code samples > 0 \endcode + * \code total_samples > 0 \endcode + * \retval FLAC__bool + * \c false if memory allocation fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_spaced_points_by_samples(FLAC__StreamMetadata *object, unsigned samples, FLAC__uint64 total_samples); + +/** Sort a seek table's seek points according to the format specification, + * removing duplicates. + * + * \param object A pointer to a seek table to be sorted. + * \param compact If \c false, behaves like FLAC__format_seektable_sort(). + * If \c true, duplicates are deleted and the seek table is + * shrunk appropriately; the number of placeholder points + * present in the seek table will be the same after the call + * as before. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \retval FLAC__bool + * \c false if realloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_sort(FLAC__StreamMetadata *object, FLAC__bool compact); + +/** Sets the vendor string in a VORBIS_COMMENT block. + * + * For convenience, a trailing NUL is added to the entry if it doesn't have + * one already. + * + * If \a copy is \c true, a copy of the entry is stored; otherwise, the object + * takes ownership of the \c entry.entry pointer. + * + * \note If this function returns \c false, the caller still owns the + * pointer. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param entry The entry to set the vendor string to. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code (entry.entry != NULL && entry.length > 0) || + * (entry.entry == NULL && entry.length == 0) \endcode + * \retval FLAC__bool + * \c false if memory allocation fails or \a entry does not comply with the + * Vorbis comment specification, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_vendor_string(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy); + +/** Resize the comment array. + * + * If the size shrinks, elements will truncated; if it grows, new empty + * fields will be added to the end. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param new_num_comments The desired length of the array; may be \c 0. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code (object->data.vorbis_comment.comments == NULL && object->data.vorbis_comment.num_comments == 0) || + * (object->data.vorbis_comment.comments != NULL && object->data.vorbis_comment.num_comments > 0) \endcode + * \retval FLAC__bool + * \c false if memory allocation fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_resize_comments(FLAC__StreamMetadata *object, unsigned new_num_comments); + +/** Sets a comment in a VORBIS_COMMENT block. + * + * For convenience, a trailing NUL is added to the entry if it doesn't have + * one already. + * + * If \a copy is \c true, a copy of the entry is stored; otherwise, the object + * takes ownership of the \c entry.entry pointer. + * + * \note If this function returns \c false, the caller still owns the + * pointer. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param comment_num Index into comment array to set. + * \param entry The entry to set the comment to. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code comment_num < object->data.vorbis_comment.num_comments \endcode + * \code (entry.entry != NULL && entry.length > 0) || + * (entry.entry == NULL && entry.length == 0) \endcode + * \retval FLAC__bool + * \c false if memory allocation fails or \a entry does not comply with the + * Vorbis comment specification, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_comment(FLAC__StreamMetadata *object, unsigned comment_num, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy); + +/** Insert a comment in a VORBIS_COMMENT block at the given index. + * + * For convenience, a trailing NUL is added to the entry if it doesn't have + * one already. + * + * If \a copy is \c true, a copy of the entry is stored; otherwise, the object + * takes ownership of the \c entry.entry pointer. + * + * \note If this function returns \c false, the caller still owns the + * pointer. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param comment_num The index at which to insert the comment. The comments + * at and after \a comment_num move right one position. + * To append a comment to the end, set \a comment_num to + * \c object->data.vorbis_comment.num_comments . + * \param entry The comment to insert. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code object->data.vorbis_comment.num_comments >= comment_num \endcode + * \code (entry.entry != NULL && entry.length > 0) || + * (entry.entry == NULL && entry.length == 0 && copy == false) \endcode + * \retval FLAC__bool + * \c false if memory allocation fails or \a entry does not comply with the + * Vorbis comment specification, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_insert_comment(FLAC__StreamMetadata *object, unsigned comment_num, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy); + +/** Appends a comment to a VORBIS_COMMENT block. + * + * For convenience, a trailing NUL is added to the entry if it doesn't have + * one already. + * + * If \a copy is \c true, a copy of the entry is stored; otherwise, the object + * takes ownership of the \c entry.entry pointer. + * + * \note If this function returns \c false, the caller still owns the + * pointer. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param entry The comment to insert. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code (entry.entry != NULL && entry.length > 0) || + * (entry.entry == NULL && entry.length == 0 && copy == false) \endcode + * \retval FLAC__bool + * \c false if memory allocation fails or \a entry does not comply with the + * Vorbis comment specification, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_append_comment(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy); + +/** Replaces comments in a VORBIS_COMMENT block with a new one. + * + * For convenience, a trailing NUL is added to the entry if it doesn't have + * one already. + * + * Depending on the the value of \a all, either all or just the first comment + * whose field name(s) match the given entry's name will be replaced by the + * given entry. If no comments match, \a entry will simply be appended. + * + * If \a copy is \c true, a copy of the entry is stored; otherwise, the object + * takes ownership of the \c entry.entry pointer. + * + * \note If this function returns \c false, the caller still owns the + * pointer. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param entry The comment to insert. + * \param all If \c true, all comments whose field name matches + * \a entry's field name will be removed, and \a entry will + * be inserted at the position of the first matching + * comment. If \c false, only the first comment whose + * field name matches \a entry's field name will be + * replaced with \a entry. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code (entry.entry != NULL && entry.length > 0) || + * (entry.entry == NULL && entry.length == 0 && copy == false) \endcode + * \retval FLAC__bool + * \c false if memory allocation fails or \a entry does not comply with the + * Vorbis comment specification, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_replace_comment(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool all, FLAC__bool copy); + +/** Delete a comment in a VORBIS_COMMENT block at the given index. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param comment_num The index of the comment to delete. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code object->data.vorbis_comment.num_comments > comment_num \endcode + * \retval FLAC__bool + * \c false if realloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_delete_comment(FLAC__StreamMetadata *object, unsigned comment_num); + +/** Creates a Vorbis comment entry from NUL-terminated name and value strings. + * + * On return, the filled-in \a entry->entry pointer will point to malloc()ed + * memory and shall be owned by the caller. For convenience the entry will + * have a terminating NUL. + * + * \param entry A pointer to a Vorbis comment entry. The entry's + * \c entry pointer should not point to allocated + * memory as it will be overwritten. + * \param field_name The field name in ASCII, \c NUL terminated. + * \param field_value The field value in UTF-8, \c NUL terminated. + * \assert + * \code entry != NULL \endcode + * \code field_name != NULL \endcode + * \code field_value != NULL \endcode + * \retval FLAC__bool + * \c false if malloc() fails, or if \a field_name or \a field_value does + * not comply with the Vorbis comment specification, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(FLAC__StreamMetadata_VorbisComment_Entry *entry, const char *field_name, const char *field_value); + +/** Splits a Vorbis comment entry into NUL-terminated name and value strings. + * + * The returned pointers to name and value will be allocated by malloc() + * and shall be owned by the caller. + * + * \param entry An existing Vorbis comment entry. + * \param field_name The address of where the returned pointer to the + * field name will be stored. + * \param field_value The address of where the returned pointer to the + * field value will be stored. + * \assert + * \code (entry.entry != NULL && entry.length > 0) \endcode + * \code memchr(entry.entry, '=', entry.length) != NULL \endcode + * \code field_name != NULL \endcode + * \code field_value != NULL \endcode + * \retval FLAC__bool + * \c false if memory allocation fails or \a entry does not comply with the + * Vorbis comment specification, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair(const FLAC__StreamMetadata_VorbisComment_Entry entry, char **field_name, char **field_value); + +/** Check if the given Vorbis comment entry's field name matches the given + * field name. + * + * \param entry An existing Vorbis comment entry. + * \param field_name The field name to check. + * \param field_name_length The length of \a field_name, not including the + * terminating \c NUL. + * \assert + * \code (entry.entry != NULL && entry.length > 0) \endcode + * \retval FLAC__bool + * \c true if the field names match, else \c false + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_matches(const FLAC__StreamMetadata_VorbisComment_Entry entry, const char *field_name, unsigned field_name_length); + +/** Find a Vorbis comment with the given field name. + * + * The search begins at entry number \a offset; use an offset of 0 to + * search from the beginning of the comment array. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param offset The offset into the comment array from where to start + * the search. + * \param field_name The field name of the comment to find. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code field_name != NULL \endcode + * \retval int + * The offset in the comment array of the first comment whose field + * name matches \a field_name, or \c -1 if no match was found. + */ +FLAC_API int FLAC__metadata_object_vorbiscomment_find_entry_from(const FLAC__StreamMetadata *object, unsigned offset, const char *field_name); + +/** Remove first Vorbis comment matching the given field name. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param field_name The field name of comment to delete. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \retval int + * \c -1 for memory allocation error, \c 0 for no matching entries, + * \c 1 for one matching entry deleted. + */ +FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entry_matching(FLAC__StreamMetadata *object, const char *field_name); + +/** Remove all Vorbis comments matching the given field name. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param field_name The field name of comments to delete. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \retval int + * \c -1 for memory allocation error, \c 0 for no matching entries, + * else the number of matching entries deleted. + */ +FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entries_matching(FLAC__StreamMetadata *object, const char *field_name); + +/** Create a new CUESHEET track instance. + * + * The object will be "empty"; i.e. values and data pointers will be \c 0. + * + * \retval FLAC__StreamMetadata_CueSheet_Track* + * \c NULL if there was an error allocating memory, else the new instance. + */ +FLAC_API FLAC__StreamMetadata_CueSheet_Track *FLAC__metadata_object_cuesheet_track_new(void); + +/** Create a copy of an existing CUESHEET track object. + * + * The copy is a "deep" copy, i.e. dynamically allocated data within the + * object is also copied. The caller takes ownership of the new object and + * is responsible for freeing it with + * FLAC__metadata_object_cuesheet_track_delete(). + * + * \param object Pointer to object to copy. + * \assert + * \code object != NULL \endcode + * \retval FLAC__StreamMetadata_CueSheet_Track* + * \c NULL if there was an error allocating memory, else the new instance. + */ +FLAC_API FLAC__StreamMetadata_CueSheet_Track *FLAC__metadata_object_cuesheet_track_clone(const FLAC__StreamMetadata_CueSheet_Track *object); + +/** Delete a CUESHEET track object + * + * \param object A pointer to an existing CUESHEET track object. + * \assert + * \code object != NULL \endcode + */ +FLAC_API void FLAC__metadata_object_cuesheet_track_delete(FLAC__StreamMetadata_CueSheet_Track *object); + +/** Resize a track's index point array. + * + * If the size shrinks, elements will truncated; if it grows, new blank + * indices will be added to the end. + * + * \param object A pointer to an existing CUESHEET object. + * \param track_num The index of the track to modify. NOTE: this is not + * necessarily the same as the track's \a number field. + * \param new_num_indices The desired length of the array; may be \c 0. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code object->data.cue_sheet.num_tracks > track_num \endcode + * \code (object->data.cue_sheet.tracks[track_num].indices == NULL && object->data.cue_sheet.tracks[track_num].num_indices == 0) || + * (object->data.cue_sheet.tracks[track_num].indices != NULL && object->data.cue_sheet.tracks[track_num].num_indices > 0) \endcode + * \retval FLAC__bool + * \c false if memory allocation error, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_resize_indices(FLAC__StreamMetadata *object, unsigned track_num, unsigned new_num_indices); + +/** Insert an index point in a CUESHEET track at the given index. + * + * \param object A pointer to an existing CUESHEET object. + * \param track_num The index of the track to modify. NOTE: this is not + * necessarily the same as the track's \a number field. + * \param index_num The index into the track's index array at which to + * insert the index point. NOTE: this is not necessarily + * the same as the index point's \a number field. The + * indices at and after \a index_num move right one + * position. To append an index point to the end, set + * \a index_num to + * \c object->data.cue_sheet.tracks[track_num].num_indices . + * \param index The index point to insert. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code object->data.cue_sheet.num_tracks > track_num \endcode + * \code object->data.cue_sheet.tracks[track_num].num_indices >= index_num \endcode + * \retval FLAC__bool + * \c false if realloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_insert_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num, FLAC__StreamMetadata_CueSheet_Index index); + +/** Insert a blank index point in a CUESHEET track at the given index. + * + * A blank index point is one in which all field values are zero. + * + * \param object A pointer to an existing CUESHEET object. + * \param track_num The index of the track to modify. NOTE: this is not + * necessarily the same as the track's \a number field. + * \param index_num The index into the track's index array at which to + * insert the index point. NOTE: this is not necessarily + * the same as the index point's \a number field. The + * indices at and after \a index_num move right one + * position. To append an index point to the end, set + * \a index_num to + * \c object->data.cue_sheet.tracks[track_num].num_indices . + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code object->data.cue_sheet.num_tracks > track_num \endcode + * \code object->data.cue_sheet.tracks[track_num].num_indices >= index_num \endcode + * \retval FLAC__bool + * \c false if realloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_insert_blank_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num); + +/** Delete an index point in a CUESHEET track at the given index. + * + * \param object A pointer to an existing CUESHEET object. + * \param track_num The index into the track array of the track to + * modify. NOTE: this is not necessarily the same + * as the track's \a number field. + * \param index_num The index into the track's index array of the index + * to delete. NOTE: this is not necessarily the same + * as the index's \a number field. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code object->data.cue_sheet.num_tracks > track_num \endcode + * \code object->data.cue_sheet.tracks[track_num].num_indices > index_num \endcode + * \retval FLAC__bool + * \c false if realloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_delete_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num); + +/** Resize the track array. + * + * If the size shrinks, elements will truncated; if it grows, new blank + * tracks will be added to the end. + * + * \param object A pointer to an existing CUESHEET object. + * \param new_num_tracks The desired length of the array; may be \c 0. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code (object->data.cue_sheet.tracks == NULL && object->data.cue_sheet.num_tracks == 0) || + * (object->data.cue_sheet.tracks != NULL && object->data.cue_sheet.num_tracks > 0) \endcode + * \retval FLAC__bool + * \c false if memory allocation error, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_resize_tracks(FLAC__StreamMetadata *object, unsigned new_num_tracks); + +/** Sets a track in a CUESHEET block. + * + * If \a copy is \c true, a copy of the track is stored; otherwise, the object + * takes ownership of the \a track pointer. + * + * \param object A pointer to an existing CUESHEET object. + * \param track_num Index into track array to set. NOTE: this is not + * necessarily the same as the track's \a number field. + * \param track The track to set the track to. You may safely pass in + * a const pointer if \a copy is \c true. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code track_num < object->data.cue_sheet.num_tracks \endcode + * \code (track->indices != NULL && track->num_indices > 0) || + * (track->indices == NULL && track->num_indices == 0) + * \retval FLAC__bool + * \c false if \a copy is \c true and malloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_set_track(FLAC__StreamMetadata *object, unsigned track_num, FLAC__StreamMetadata_CueSheet_Track *track, FLAC__bool copy); + +/** Insert a track in a CUESHEET block at the given index. + * + * If \a copy is \c true, a copy of the track is stored; otherwise, the object + * takes ownership of the \a track pointer. + * + * \param object A pointer to an existing CUESHEET object. + * \param track_num The index at which to insert the track. NOTE: this + * is not necessarily the same as the track's \a number + * field. The tracks at and after \a track_num move right + * one position. To append a track to the end, set + * \a track_num to \c object->data.cue_sheet.num_tracks . + * \param track The track to insert. You may safely pass in a const + * pointer if \a copy is \c true. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code object->data.cue_sheet.num_tracks >= track_num \endcode + * \retval FLAC__bool + * \c false if \a copy is \c true and malloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_track(FLAC__StreamMetadata *object, unsigned track_num, FLAC__StreamMetadata_CueSheet_Track *track, FLAC__bool copy); + +/** Insert a blank track in a CUESHEET block at the given index. + * + * A blank track is one in which all field values are zero. + * + * \param object A pointer to an existing CUESHEET object. + * \param track_num The index at which to insert the track. NOTE: this + * is not necessarily the same as the track's \a number + * field. The tracks at and after \a track_num move right + * one position. To append a track to the end, set + * \a track_num to \c object->data.cue_sheet.num_tracks . + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code object->data.cue_sheet.num_tracks >= track_num \endcode + * \retval FLAC__bool + * \c false if \a copy is \c true and malloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_blank_track(FLAC__StreamMetadata *object, unsigned track_num); + +/** Delete a track in a CUESHEET block at the given index. + * + * \param object A pointer to an existing CUESHEET object. + * \param track_num The index into the track array of the track to + * delete. NOTE: this is not necessarily the same + * as the track's \a number field. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code object->data.cue_sheet.num_tracks > track_num \endcode + * \retval FLAC__bool + * \c false if realloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_delete_track(FLAC__StreamMetadata *object, unsigned track_num); + +/** Check a cue sheet to see if it conforms to the FLAC specification. + * See the format specification for limits on the contents of the + * cue sheet. + * + * \param object A pointer to an existing CUESHEET object. + * \param check_cd_da_subset If \c true, check CUESHEET against more + * stringent requirements for a CD-DA (audio) disc. + * \param violation Address of a pointer to a string. If there is a + * violation, a pointer to a string explanation of the + * violation will be returned here. \a violation may be + * \c NULL if you don't need the returned string. Do not + * free the returned string; it will always point to static + * data. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \retval FLAC__bool + * \c false if cue sheet is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_is_legal(const FLAC__StreamMetadata *object, FLAC__bool check_cd_da_subset, const char **violation); + +/** Calculate and return the CDDB/freedb ID for a cue sheet. The function + * assumes the cue sheet corresponds to a CD; the result is undefined + * if the cuesheet's is_cd bit is not set. + * + * \param object A pointer to an existing CUESHEET object. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \retval FLAC__uint32 + * The unsigned integer representation of the CDDB/freedb ID + */ +FLAC_API FLAC__uint32 FLAC__metadata_object_cuesheet_calculate_cddb_id(const FLAC__StreamMetadata *object); + +/** Sets the MIME type of a PICTURE block. + * + * If \a copy is \c true, a copy of the string is stored; otherwise, the object + * takes ownership of the pointer. The existing string will be freed if this + * function is successful, otherwise the original string will remain if \a copy + * is \c true and malloc() fails. + * + * \note It is safe to pass a const pointer to \a mime_type if \a copy is \c true. + * + * \param object A pointer to an existing PICTURE object. + * \param mime_type A pointer to the MIME type string. The string must be + * ASCII characters 0x20-0x7e, NUL-terminated. No validation + * is done. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_PICTURE \endcode + * \code (mime_type != NULL) \endcode + * \retval FLAC__bool + * \c false if \a copy is \c true and malloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_picture_set_mime_type(FLAC__StreamMetadata *object, char *mime_type, FLAC__bool copy); + +/** Sets the description of a PICTURE block. + * + * If \a copy is \c true, a copy of the string is stored; otherwise, the object + * takes ownership of the pointer. The existing string will be freed if this + * function is successful, otherwise the original string will remain if \a copy + * is \c true and malloc() fails. + * + * \note It is safe to pass a const pointer to \a description if \a copy is \c true. + * + * \param object A pointer to an existing PICTURE object. + * \param description A pointer to the description string. The string must be + * valid UTF-8, NUL-terminated. No validation is done. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_PICTURE \endcode + * \code (description != NULL) \endcode + * \retval FLAC__bool + * \c false if \a copy is \c true and malloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_picture_set_description(FLAC__StreamMetadata *object, FLAC__byte *description, FLAC__bool copy); + +/** Sets the picture data of a PICTURE block. + * + * If \a copy is \c true, a copy of the data is stored; otherwise, the object + * takes ownership of the pointer. Also sets the \a data_length field of the + * metadata object to what is passed in as the \a length parameter. The + * existing data will be freed if this function is successful, otherwise the + * original data and data_length will remain if \a copy is \c true and + * malloc() fails. + * + * \note It is safe to pass a const pointer to \a data if \a copy is \c true. + * + * \param object A pointer to an existing PICTURE object. + * \param data A pointer to the data to set. + * \param length The length of \a data in bytes. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_PICTURE \endcode + * \code (data != NULL && length > 0) || + * (data == NULL && length == 0 && copy == false) \endcode + * \retval FLAC__bool + * \c false if \a copy is \c true and malloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_picture_set_data(FLAC__StreamMetadata *object, FLAC__byte *data, FLAC__uint32 length, FLAC__bool copy); + +/** Check a PICTURE block to see if it conforms to the FLAC specification. + * See the format specification for limits on the contents of the + * PICTURE block. + * + * \param object A pointer to existing PICTURE block to be checked. + * \param violation Address of a pointer to a string. If there is a + * violation, a pointer to a string explanation of the + * violation will be returned here. \a violation may be + * \c NULL if you don't need the returned string. Do not + * free the returned string; it will always point to static + * data. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_PICTURE \endcode + * \retval FLAC__bool + * \c false if PICTURE block is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_picture_is_legal(const FLAC__StreamMetadata *object, const char **violation); + +/* \} */ + +#ifdef __cplusplus +} +#endif + +#endif +/********* End of inlined file: metadata.h *********/ + +/********* Start of inlined file: stream_decoder.h *********/ +#ifndef FLAC__STREAM_DECODER_H +#define FLAC__STREAM_DECODER_H + +#include /* for FILE */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file include/FLAC/stream_decoder.h + * + * \brief + * This module contains the functions which implement the stream + * decoder. + * + * See the detailed documentation in the + * \link flac_stream_decoder stream decoder \endlink module. + */ + +/** \defgroup flac_decoder FLAC/ \*_decoder.h: decoder interfaces + * \ingroup flac + * + * \brief + * This module describes the decoder layers provided by libFLAC. + * + * The stream decoder can be used to decode complete streams either from + * the client via callbacks, or directly from a file, depending on how + * it is initialized. When decoding via callbacks, the client provides + * callbacks for reading FLAC data and writing decoded samples, and + * handling metadata and errors. If the client also supplies seek-related + * callback, the decoder function for sample-accurate seeking within the + * FLAC input is also available. When decoding from a file, the client + * needs only supply a filename or open \c FILE* and write/metadata/error + * callbacks; the rest of the callbacks are supplied internally. For more + * info see the \link flac_stream_decoder stream decoder \endlink module. + */ + +/** \defgroup flac_stream_decoder FLAC/stream_decoder.h: stream decoder interface + * \ingroup flac_decoder + * + * \brief + * This module contains the functions which implement the stream + * decoder. + * + * The stream decoder can decode native FLAC, and optionally Ogg FLAC + * (check FLAC_API_SUPPORTS_OGG_FLAC) streams and files. + * + * The basic usage of this decoder is as follows: + * - The program creates an instance of a decoder using + * FLAC__stream_decoder_new(). + * - The program overrides the default settings using + * FLAC__stream_decoder_set_*() functions. + * - The program initializes the instance to validate the settings and + * prepare for decoding using + * - FLAC__stream_decoder_init_stream() or FLAC__stream_decoder_init_FILE() + * or FLAC__stream_decoder_init_file() for native FLAC, + * - FLAC__stream_decoder_init_ogg_stream() or FLAC__stream_decoder_init_ogg_FILE() + * or FLAC__stream_decoder_init_ogg_file() for Ogg FLAC + * - The program calls the FLAC__stream_decoder_process_*() functions + * to decode data, which subsequently calls the callbacks. + * - The program finishes the decoding with FLAC__stream_decoder_finish(), + * which flushes the input and output and resets the decoder to the + * uninitialized state. + * - The instance may be used again or deleted with + * FLAC__stream_decoder_delete(). + * + * In more detail, the program will create a new instance by calling + * FLAC__stream_decoder_new(), then call FLAC__stream_decoder_set_*() + * functions to override the default decoder options, and call + * one of the FLAC__stream_decoder_init_*() functions. + * + * There are three initialization functions for native FLAC, one for + * setting up the decoder to decode FLAC data from the client via + * callbacks, and two for decoding directly from a FLAC file. + * + * For decoding via callbacks, use FLAC__stream_decoder_init_stream(). + * You must also supply several callbacks for handling I/O. Some (like + * seeking) are optional, depending on the capabilities of the input. + * + * For decoding directly from a file, use FLAC__stream_decoder_init_FILE() + * or FLAC__stream_decoder_init_file(). Then you must only supply an open + * \c FILE* or filename and fewer callbacks; the decoder will handle + * the other callbacks internally. + * + * There are three similarly-named init functions for decoding from Ogg + * FLAC streams. Check \c FLAC_API_SUPPORTS_OGG_FLAC to find out if the + * library has been built with Ogg support. + * + * Once the decoder is initialized, your program will call one of several + * functions to start the decoding process: + * + * - FLAC__stream_decoder_process_single() - Tells the decoder to process at + * most one metadata block or audio frame and return, calling either the + * metadata callback or write callback, respectively, once. If the decoder + * loses sync it will return with only the error callback being called. + * - FLAC__stream_decoder_process_until_end_of_metadata() - Tells the decoder + * to process the stream from the current location and stop upon reaching + * the first audio frame. The client will get one metadata, write, or error + * callback per metadata block, audio frame, or sync error, respectively. + * - FLAC__stream_decoder_process_until_end_of_stream() - Tells the decoder + * to process the stream from the current location until the read callback + * returns FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM or + * FLAC__STREAM_DECODER_READ_STATUS_ABORT. The client will get one metadata, + * write, or error callback per metadata block, audio frame, or sync error, + * respectively. + * + * When the decoder has finished decoding (normally or through an abort), + * the instance is finished by calling FLAC__stream_decoder_finish(), which + * ensures the decoder is in the correct state and frees memory. Then the + * instance may be deleted with FLAC__stream_decoder_delete() or initialized + * again to decode another stream. + * + * Seeking is exposed through the FLAC__stream_decoder_seek_absolute() method. + * At any point after the stream decoder has been initialized, the client can + * call this function to seek to an exact sample within the stream. + * Subsequently, the first time the write callback is called it will be + * passed a (possibly partial) block starting at that sample. + * + * If the client cannot seek via the callback interface provided, but still + * has another way of seeking, it can flush the decoder using + * FLAC__stream_decoder_flush() and start feeding data from the new position + * through the read callback. + * + * The stream decoder also provides MD5 signature checking. If this is + * turned on before initialization, FLAC__stream_decoder_finish() will + * report when the decoded MD5 signature does not match the one stored + * in the STREAMINFO block. MD5 checking is automatically turned off + * (until the next FLAC__stream_decoder_reset()) if there is no signature + * in the STREAMINFO block or when a seek is attempted. + * + * The FLAC__stream_decoder_set_metadata_*() functions deserve special + * attention. By default, the decoder only calls the metadata_callback for + * the STREAMINFO block. These functions allow you to tell the decoder + * explicitly which blocks to parse and return via the metadata_callback + * and/or which to skip. Use a FLAC__stream_decoder_set_metadata_respond_all(), + * FLAC__stream_decoder_set_metadata_ignore() ... or FLAC__stream_decoder_set_metadata_ignore_all(), + * FLAC__stream_decoder_set_metadata_respond() ... sequence to exactly specify + * which blocks to return. Remember that metadata blocks can potentially + * be big (for example, cover art) so filtering out the ones you don't + * use can reduce the memory requirements of the decoder. Also note the + * special forms FLAC__stream_decoder_set_metadata_respond_application(id) + * and FLAC__stream_decoder_set_metadata_ignore_application(id) for + * filtering APPLICATION blocks based on the application ID. + * + * STREAMINFO and SEEKTABLE blocks are always parsed and used internally, but + * they still can legally be filtered from the metadata_callback. + * + * \note + * The "set" functions may only be called when the decoder is in the + * state FLAC__STREAM_DECODER_UNINITIALIZED, i.e. after + * FLAC__stream_decoder_new() or FLAC__stream_decoder_finish(), but + * before FLAC__stream_decoder_init_*(). If this is the case they will + * return \c true, otherwise \c false. + * + * \note + * FLAC__stream_decoder_finish() resets all settings to the constructor + * defaults, including the callbacks. + * + * \{ + */ + +/** State values for a FLAC__StreamDecoder + * + * The decoder's state can be obtained by calling FLAC__stream_decoder_get_state(). + */ +typedef enum { + + FLAC__STREAM_DECODER_SEARCH_FOR_METADATA = 0, + /**< The decoder is ready to search for metadata. */ + + FLAC__STREAM_DECODER_READ_METADATA, + /**< The decoder is ready to or is in the process of reading metadata. */ + + FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC, + /**< The decoder is ready to or is in the process of searching for the + * frame sync code. + */ + + FLAC__STREAM_DECODER_READ_FRAME, + /**< The decoder is ready to or is in the process of reading a frame. */ + + FLAC__STREAM_DECODER_END_OF_STREAM, + /**< The decoder has reached the end of the stream. */ + + FLAC__STREAM_DECODER_OGG_ERROR, + /**< An error occurred in the underlying Ogg layer. */ + + FLAC__STREAM_DECODER_SEEK_ERROR, + /**< An error occurred while seeking. The decoder must be flushed + * with FLAC__stream_decoder_flush() or reset with + * FLAC__stream_decoder_reset() before decoding can continue. + */ + + FLAC__STREAM_DECODER_ABORTED, + /**< The decoder was aborted by the read callback. */ + + FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR, + /**< An error occurred allocating memory. The decoder is in an invalid + * state and can no longer be used. + */ + + FLAC__STREAM_DECODER_UNINITIALIZED + /**< The decoder is in the uninitialized state; one of the + * FLAC__stream_decoder_init_*() functions must be called before samples + * can be processed. + */ + +} FLAC__StreamDecoderState; + +/** Maps a FLAC__StreamDecoderState to a C string. + * + * Using a FLAC__StreamDecoderState as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderStateString[]; + +/** Possible return values for the FLAC__stream_decoder_init_*() functions. + */ +typedef enum { + + FLAC__STREAM_DECODER_INIT_STATUS_OK = 0, + /**< Initialization was successful. */ + + FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER, + /**< The library was not compiled with support for the given container + * format. + */ + + FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS, + /**< A required callback was not supplied. */ + + FLAC__STREAM_DECODER_INIT_STATUS_MEMORY_ALLOCATION_ERROR, + /**< An error occurred allocating memory. */ + + FLAC__STREAM_DECODER_INIT_STATUS_ERROR_OPENING_FILE, + /**< fopen() failed in FLAC__stream_decoder_init_file() or + * FLAC__stream_decoder_init_ogg_file(). */ + + FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED + /**< FLAC__stream_decoder_init_*() was called when the decoder was + * already initialized, usually because + * FLAC__stream_decoder_finish() was not called. + */ + +} FLAC__StreamDecoderInitStatus; + +/** Maps a FLAC__StreamDecoderInitStatus to a C string. + * + * Using a FLAC__StreamDecoderInitStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderInitStatusString[]; + +/** Return values for the FLAC__StreamDecoder read callback. + */ +typedef enum { + + FLAC__STREAM_DECODER_READ_STATUS_CONTINUE, + /**< The read was OK and decoding can continue. */ + + FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM, + /**< The read was attempted while at the end of the stream. Note that + * the client must only return this value when the read callback was + * called when already at the end of the stream. Otherwise, if the read + * itself moves to the end of the stream, the client should still return + * the data and \c FLAC__STREAM_DECODER_READ_STATUS_CONTINUE, and then on + * the next read callback it should return + * \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM with a byte count + * of \c 0. + */ + + FLAC__STREAM_DECODER_READ_STATUS_ABORT + /**< An unrecoverable error occurred. The decoder will return from the process call. */ + +} FLAC__StreamDecoderReadStatus; + +/** Maps a FLAC__StreamDecoderReadStatus to a C string. + * + * Using a FLAC__StreamDecoderReadStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderReadStatusString[]; + +/** Return values for the FLAC__StreamDecoder seek callback. + */ +typedef enum { + + FLAC__STREAM_DECODER_SEEK_STATUS_OK, + /**< The seek was OK and decoding can continue. */ + + FLAC__STREAM_DECODER_SEEK_STATUS_ERROR, + /**< An unrecoverable error occurred. The decoder will return from the process call. */ + + FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED + /**< Client does not support seeking. */ + +} FLAC__StreamDecoderSeekStatus; + +/** Maps a FLAC__StreamDecoderSeekStatus to a C string. + * + * Using a FLAC__StreamDecoderSeekStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderSeekStatusString[]; + +/** Return values for the FLAC__StreamDecoder tell callback. + */ +typedef enum { + + FLAC__STREAM_DECODER_TELL_STATUS_OK, + /**< The tell was OK and decoding can continue. */ + + FLAC__STREAM_DECODER_TELL_STATUS_ERROR, + /**< An unrecoverable error occurred. The decoder will return from the process call. */ + + FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED + /**< Client does not support telling the position. */ + +} FLAC__StreamDecoderTellStatus; + +/** Maps a FLAC__StreamDecoderTellStatus to a C string. + * + * Using a FLAC__StreamDecoderTellStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderTellStatusString[]; + +/** Return values for the FLAC__StreamDecoder length callback. + */ +typedef enum { + + FLAC__STREAM_DECODER_LENGTH_STATUS_OK, + /**< The length call was OK and decoding can continue. */ + + FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR, + /**< An unrecoverable error occurred. The decoder will return from the process call. */ + + FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED + /**< Client does not support reporting the length. */ + +} FLAC__StreamDecoderLengthStatus; + +/** Maps a FLAC__StreamDecoderLengthStatus to a C string. + * + * Using a FLAC__StreamDecoderLengthStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderLengthStatusString[]; + +/** Return values for the FLAC__StreamDecoder write callback. + */ +typedef enum { + + FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE, + /**< The write was OK and decoding can continue. */ + + FLAC__STREAM_DECODER_WRITE_STATUS_ABORT + /**< An unrecoverable error occurred. The decoder will return from the process call. */ + +} FLAC__StreamDecoderWriteStatus; + +/** Maps a FLAC__StreamDecoderWriteStatus to a C string. + * + * Using a FLAC__StreamDecoderWriteStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderWriteStatusString[]; + +/** Possible values passed back to the FLAC__StreamDecoder error callback. + * \c FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC is the generic catch- + * all. The rest could be caused by bad sync (false synchronization on + * data that is not the start of a frame) or corrupted data. The error + * itself is the decoder's best guess at what happened assuming a correct + * sync. For example \c FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER + * could be caused by a correct sync on the start of a frame, but some + * data in the frame header was corrupted. Or it could be the result of + * syncing on a point the stream that looked like the starting of a frame + * but was not. \c FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM + * could be because the decoder encountered a valid frame made by a future + * version of the encoder which it cannot parse, or because of a false + * sync making it appear as though an encountered frame was generated by + * a future encoder. + */ +typedef enum { + + FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC, + /**< An error in the stream caused the decoder to lose synchronization. */ + + FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER, + /**< The decoder encountered a corrupted frame header. */ + + FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH, + /**< The frame's data did not match the CRC in the footer. */ + + FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM + /**< The decoder encountered reserved fields in use in the stream. */ + +} FLAC__StreamDecoderErrorStatus; + +/** Maps a FLAC__StreamDecoderErrorStatus to a C string. + * + * Using a FLAC__StreamDecoderErrorStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderErrorStatusString[]; + +/*********************************************************************** + * + * class FLAC__StreamDecoder + * + ***********************************************************************/ + +struct FLAC__StreamDecoderProtected; +struct FLAC__StreamDecoderPrivate; +/** The opaque structure definition for the stream decoder type. + * See the \link flac_stream_decoder stream decoder module \endlink + * for a detailed description. + */ +typedef struct { + struct FLAC__StreamDecoderProtected *protected_; /* avoid the C++ keyword 'protected' */ + struct FLAC__StreamDecoderPrivate *private_; /* avoid the C++ keyword 'private' */ +} FLAC__StreamDecoder; + +/** Signature for the read callback. + * + * A function pointer matching this signature must be passed to + * FLAC__stream_decoder_init*_stream(). The supplied function will be + * called when the decoder needs more input data. The address of the + * buffer to be filled is supplied, along with the number of bytes the + * buffer can hold. The callback may choose to supply less data and + * modify the byte count but must be careful not to overflow the buffer. + * The callback then returns a status code chosen from + * FLAC__StreamDecoderReadStatus. + * + * Here is an example of a read callback for stdio streams: + * \code + * FLAC__StreamDecoderReadStatus read_cb(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data) + * { + * FILE *file = ((MyClientData*)client_data)->file; + * if(*bytes > 0) { + * *bytes = fread(buffer, sizeof(FLAC__byte), *bytes, file); + * if(ferror(file)) + * return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + * else if(*bytes == 0) + * return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; + * else + * return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; + * } + * else + * return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + * } + * \endcode + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param buffer A pointer to a location for the callee to store + * data to be decoded. + * \param bytes A pointer to the size of the buffer. On entry + * to the callback, it contains the maximum number + * of bytes that may be stored in \a buffer. The + * callee must set it to the actual number of bytes + * stored (0 in case of error or end-of-stream) before + * returning. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + * \retval FLAC__StreamDecoderReadStatus + * The callee's return status. Note that the callback should return + * \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM if and only if + * zero bytes were read and there is no more data to be read. + */ +typedef FLAC__StreamDecoderReadStatus (*FLAC__StreamDecoderReadCallback)(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data); + +/** Signature for the seek callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_decoder_init*_stream(). The supplied function will be + * called when the decoder needs to seek the input stream. The decoder + * will pass the absolute byte offset to seek to, 0 meaning the + * beginning of the stream. + * + * Here is an example of a seek callback for stdio streams: + * \code + * FLAC__StreamDecoderSeekStatus seek_cb(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data) + * { + * FILE *file = ((MyClientData*)client_data)->file; + * if(file == stdin) + * return FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED; + * else if(fseeko(file, (off_t)absolute_byte_offset, SEEK_SET) < 0) + * return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR; + * else + * return FLAC__STREAM_DECODER_SEEK_STATUS_OK; + * } + * \endcode + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param absolute_byte_offset The offset from the beginning of the stream + * to seek to. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + * \retval FLAC__StreamDecoderSeekStatus + * The callee's return status. + */ +typedef FLAC__StreamDecoderSeekStatus (*FLAC__StreamDecoderSeekCallback)(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data); + +/** Signature for the tell callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_decoder_init*_stream(). The supplied function will be + * called when the decoder wants to know the current position of the + * stream. The callback should return the byte offset from the + * beginning of the stream. + * + * Here is an example of a tell callback for stdio streams: + * \code + * FLAC__StreamDecoderTellStatus tell_cb(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data) + * { + * FILE *file = ((MyClientData*)client_data)->file; + * off_t pos; + * if(file == stdin) + * return FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED; + * else if((pos = ftello(file)) < 0) + * return FLAC__STREAM_DECODER_TELL_STATUS_ERROR; + * else { + * *absolute_byte_offset = (FLAC__uint64)pos; + * return FLAC__STREAM_DECODER_TELL_STATUS_OK; + * } + * } + * \endcode + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param absolute_byte_offset A pointer to storage for the current offset + * from the beginning of the stream. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + * \retval FLAC__StreamDecoderTellStatus + * The callee's return status. + */ +typedef FLAC__StreamDecoderTellStatus (*FLAC__StreamDecoderTellCallback)(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data); + +/** Signature for the length callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_decoder_init*_stream(). The supplied function will be + * called when the decoder wants to know the total length of the stream + * in bytes. + * + * Here is an example of a length callback for stdio streams: + * \code + * FLAC__StreamDecoderLengthStatus length_cb(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data) + * { + * FILE *file = ((MyClientData*)client_data)->file; + * struct stat filestats; + * + * if(file == stdin) + * return FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED; + * else if(fstat(fileno(file), &filestats) != 0) + * return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR; + * else { + * *stream_length = (FLAC__uint64)filestats.st_size; + * return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; + * } + * } + * \endcode + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param stream_length A pointer to storage for the length of the stream + * in bytes. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + * \retval FLAC__StreamDecoderLengthStatus + * The callee's return status. + */ +typedef FLAC__StreamDecoderLengthStatus (*FLAC__StreamDecoderLengthCallback)(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data); + +/** Signature for the EOF callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_decoder_init*_stream(). The supplied function will be + * called when the decoder needs to know if the end of the stream has + * been reached. + * + * Here is an example of a EOF callback for stdio streams: + * FLAC__bool eof_cb(const FLAC__StreamDecoder *decoder, void *client_data) + * \code + * { + * FILE *file = ((MyClientData*)client_data)->file; + * return feof(file)? true : false; + * } + * \endcode + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + * \retval FLAC__bool + * \c true if the currently at the end of the stream, else \c false. + */ +typedef FLAC__bool (*FLAC__StreamDecoderEofCallback)(const FLAC__StreamDecoder *decoder, void *client_data); + +/** Signature for the write callback. + * + * A function pointer matching this signature must be passed to one of + * the FLAC__stream_decoder_init_*() functions. + * The supplied function will be called when the decoder has decoded a + * single audio frame. The decoder will pass the frame metadata as well + * as an array of pointers (one for each channel) pointing to the + * decoded audio. + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param frame The description of the decoded frame. See + * FLAC__Frame. + * \param buffer An array of pointers to decoded channels of data. + * Each pointer will point to an array of signed + * samples of length \a frame->header.blocksize. + * Channels will be ordered according to the FLAC + * specification; see the documentation for the + * frame header. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + * \retval FLAC__StreamDecoderWriteStatus + * The callee's return status. + */ +typedef FLAC__StreamDecoderWriteStatus (*FLAC__StreamDecoderWriteCallback)(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data); + +/** Signature for the metadata callback. + * + * A function pointer matching this signature must be passed to one of + * the FLAC__stream_decoder_init_*() functions. + * The supplied function will be called when the decoder has decoded a + * metadata block. In a valid FLAC file there will always be one + * \c STREAMINFO block, followed by zero or more other metadata blocks. + * These will be supplied by the decoder in the same order as they + * appear in the stream and always before the first audio frame (i.e. + * write callback). The metadata block that is passed in must not be + * modified, and it doesn't live beyond the callback, so you should make + * a copy of it with FLAC__metadata_object_clone() if you will need it + * elsewhere. Since metadata blocks can potentially be large, by + * default the decoder only calls the metadata callback for the + * \c STREAMINFO block; you can instruct the decoder to pass or filter + * other blocks with FLAC__stream_decoder_set_metadata_*() calls. + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param metadata The decoded metadata block. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + */ +typedef void (*FLAC__StreamDecoderMetadataCallback)(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data); + +/** Signature for the error callback. + * + * A function pointer matching this signature must be passed to one of + * the FLAC__stream_decoder_init_*() functions. + * The supplied function will be called whenever an error occurs during + * decoding. + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param status The error encountered by the decoder. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + */ +typedef void (*FLAC__StreamDecoderErrorCallback)(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data); + +/*********************************************************************** + * + * Class constructor/destructor + * + ***********************************************************************/ + +/** Create a new stream decoder instance. The instance is created with + * default settings; see the individual FLAC__stream_decoder_set_*() + * functions for each setting's default. + * + * \retval FLAC__StreamDecoder* + * \c NULL if there was an error allocating memory, else the new instance. + */ +FLAC_API FLAC__StreamDecoder *FLAC__stream_decoder_new(void); + +/** Free a decoder instance. Deletes the object pointed to by \a decoder. + * + * \param decoder A pointer to an existing decoder. + * \assert + * \code decoder != NULL \endcode + */ +FLAC_API void FLAC__stream_decoder_delete(FLAC__StreamDecoder *decoder); + +/*********************************************************************** + * + * Public class method prototypes + * + ***********************************************************************/ + +/** Set the serial number for the FLAC stream within the Ogg container. + * The default behavior is to use the serial number of the first Ogg + * page. Setting a serial number here will explicitly specify which + * stream is to be decoded. + * + * \note + * This does not need to be set for native FLAC decoding. + * + * \default \c use serial number of first page + * \param decoder A decoder instance to set. + * \param serial_number See above. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_ogg_serial_number(FLAC__StreamDecoder *decoder, long serial_number); + +/** Set the "MD5 signature checking" flag. If \c true, the decoder will + * compute the MD5 signature of the unencoded audio data while decoding + * and compare it to the signature from the STREAMINFO block, if it + * exists, during FLAC__stream_decoder_finish(). + * + * MD5 signature checking will be turned off (until the next + * FLAC__stream_decoder_reset()) if there is no signature in the + * STREAMINFO block or when a seek is attempted. + * + * Clients that do not use the MD5 check should leave this off to speed + * up decoding. + * + * \default \c false + * \param decoder A decoder instance to set. + * \param value Flag value (see above). + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_md5_checking(FLAC__StreamDecoder *decoder, FLAC__bool value); + +/** Direct the decoder to pass on all metadata blocks of type \a type. + * + * \default By default, only the \c STREAMINFO block is returned via the + * metadata callback. + * \param decoder A decoder instance to set. + * \param type See above. + * \assert + * \code decoder != NULL \endcode + * \a type is valid + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond(FLAC__StreamDecoder *decoder, FLAC__MetadataType type); + +/** Direct the decoder to pass on all APPLICATION metadata blocks of the + * given \a id. + * + * \default By default, only the \c STREAMINFO block is returned via the + * metadata callback. + * \param decoder A decoder instance to set. + * \param id See above. + * \assert + * \code decoder != NULL \endcode + * \code id != NULL \endcode + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond_application(FLAC__StreamDecoder *decoder, const FLAC__byte id[4]); + +/** Direct the decoder to pass on all metadata blocks of any type. + * + * \default By default, only the \c STREAMINFO block is returned via the + * metadata callback. + * \param decoder A decoder instance to set. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond_all(FLAC__StreamDecoder *decoder); + +/** Direct the decoder to filter out all metadata blocks of type \a type. + * + * \default By default, only the \c STREAMINFO block is returned via the + * metadata callback. + * \param decoder A decoder instance to set. + * \param type See above. + * \assert + * \code decoder != NULL \endcode + * \a type is valid + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore(FLAC__StreamDecoder *decoder, FLAC__MetadataType type); + +/** Direct the decoder to filter out all APPLICATION metadata blocks of + * the given \a id. + * + * \default By default, only the \c STREAMINFO block is returned via the + * metadata callback. + * \param decoder A decoder instance to set. + * \param id See above. + * \assert + * \code decoder != NULL \endcode + * \code id != NULL \endcode + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore_application(FLAC__StreamDecoder *decoder, const FLAC__byte id[4]); + +/** Direct the decoder to filter out all metadata blocks of any type. + * + * \default By default, only the \c STREAMINFO block is returned via the + * metadata callback. + * \param decoder A decoder instance to set. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore_all(FLAC__StreamDecoder *decoder); + +/** Get the current decoder state. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__StreamDecoderState + * The current decoder state. + */ +FLAC_API FLAC__StreamDecoderState FLAC__stream_decoder_get_state(const FLAC__StreamDecoder *decoder); + +/** Get the current decoder state as a C string. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval const char * + * The decoder state as a C string. Do not modify the contents. + */ +FLAC_API const char *FLAC__stream_decoder_get_resolved_state_string(const FLAC__StreamDecoder *decoder); + +/** Get the "MD5 signature checking" flag. + * This is the value of the setting, not whether or not the decoder is + * currently checking the MD5 (remember, it can be turned off automatically + * by a seek). When the decoder is reset the flag will be restored to the + * value returned by this function. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * See above. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_get_md5_checking(const FLAC__StreamDecoder *decoder); + +/** Get the total number of samples in the stream being decoded. + * Will only be valid after decoding has started and will contain the + * value from the \c STREAMINFO block. A value of \c 0 means "unknown". + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval unsigned + * See above. + */ +FLAC_API FLAC__uint64 FLAC__stream_decoder_get_total_samples(const FLAC__StreamDecoder *decoder); + +/** Get the current number of channels in the stream being decoded. + * Will only be valid after decoding has started and will contain the + * value from the most recently decoded frame header. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval unsigned + * See above. + */ +FLAC_API unsigned FLAC__stream_decoder_get_channels(const FLAC__StreamDecoder *decoder); + +/** Get the current channel assignment in the stream being decoded. + * Will only be valid after decoding has started and will contain the + * value from the most recently decoded frame header. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__ChannelAssignment + * See above. + */ +FLAC_API FLAC__ChannelAssignment FLAC__stream_decoder_get_channel_assignment(const FLAC__StreamDecoder *decoder); + +/** Get the current sample resolution in the stream being decoded. + * Will only be valid after decoding has started and will contain the + * value from the most recently decoded frame header. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval unsigned + * See above. + */ +FLAC_API unsigned FLAC__stream_decoder_get_bits_per_sample(const FLAC__StreamDecoder *decoder); + +/** Get the current sample rate in Hz of the stream being decoded. + * Will only be valid after decoding has started and will contain the + * value from the most recently decoded frame header. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval unsigned + * See above. + */ +FLAC_API unsigned FLAC__stream_decoder_get_sample_rate(const FLAC__StreamDecoder *decoder); + +/** Get the current blocksize of the stream being decoded. + * Will only be valid after decoding has started and will contain the + * value from the most recently decoded frame header. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval unsigned + * See above. + */ +FLAC_API unsigned FLAC__stream_decoder_get_blocksize(const FLAC__StreamDecoder *decoder); + +/** Returns the decoder's current read position within the stream. + * The position is the byte offset from the start of the stream. + * Bytes before this position have been fully decoded. Note that + * there may still be undecoded bytes in the decoder's read FIFO. + * The returned position is correct even after a seek. + * + * \warning This function currently only works for native FLAC, + * not Ogg FLAC streams. + * + * \param decoder A decoder instance to query. + * \param position Address at which to return the desired position. + * \assert + * \code decoder != NULL \endcode + * \code position != NULL \endcode + * \retval FLAC__bool + * \c true if successful, \c false if the stream is not native FLAC, + * or there was an error from the 'tell' callback or it returned + * \c FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_get_decode_position(const FLAC__StreamDecoder *decoder, FLAC__uint64 *position); + +/** Initialize the decoder instance to decode native FLAC streams. + * + * This flavor of initialization sets up the decoder to decode from a + * native FLAC stream. I/O is performed via callbacks to the client. + * For decoding from a plain file via filename or open FILE*, + * FLAC__stream_decoder_init_file() and FLAC__stream_decoder_init_FILE() + * provide a simpler interface. + * + * This function should be called after FLAC__stream_decoder_new() and + * FLAC__stream_decoder_set_*() but before any of the + * FLAC__stream_decoder_process_*() functions. Will set and return the + * decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA + * if initialization succeeded. + * + * \param decoder An uninitialized decoder instance. + * \param read_callback See FLAC__StreamDecoderReadCallback. This + * pointer must not be \c NULL. + * \param seek_callback See FLAC__StreamDecoderSeekCallback. This + * pointer may be \c NULL if seeking is not + * supported. If \a seek_callback is not \c NULL then a + * \a tell_callback, \a length_callback, and \a eof_callback must also be supplied. + * Alternatively, a dummy seek callback that just + * returns \c FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param tell_callback See FLAC__StreamDecoderTellCallback. This + * pointer may be \c NULL if not supported by the client. If + * \a seek_callback is not \c NULL then a + * \a tell_callback must also be supplied. + * Alternatively, a dummy tell callback that just + * returns \c FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param length_callback See FLAC__StreamDecoderLengthCallback. This + * pointer may be \c NULL if not supported by the client. If + * \a seek_callback is not \c NULL then a + * \a length_callback must also be supplied. + * Alternatively, a dummy length callback that just + * returns \c FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param eof_callback See FLAC__StreamDecoderEofCallback. This + * pointer may be \c NULL if not supported by the client. If + * \a seek_callback is not \c NULL then a + * \a eof_callback must also be supplied. + * Alternatively, a dummy length callback that just + * returns \c false + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param write_callback See FLAC__StreamDecoderWriteCallback. This + * pointer must not be \c NULL. + * \param metadata_callback See FLAC__StreamDecoderMetadataCallback. This + * pointer may be \c NULL if the callback is not + * desired. + * \param error_callback See FLAC__StreamDecoderErrorCallback. This + * pointer must not be \c NULL. + * \param client_data This value will be supplied to callbacks in their + * \a client_data argument. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__StreamDecoderInitStatus + * \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful; + * see FLAC__StreamDecoderInitStatus for the meanings of other return values. + */ +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_stream( + FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderReadCallback read_callback, + FLAC__StreamDecoderSeekCallback seek_callback, + FLAC__StreamDecoderTellCallback tell_callback, + FLAC__StreamDecoderLengthCallback length_callback, + FLAC__StreamDecoderEofCallback eof_callback, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +); + +/** Initialize the decoder instance to decode Ogg FLAC streams. + * + * This flavor of initialization sets up the decoder to decode from a + * FLAC stream in an Ogg container. I/O is performed via callbacks to the + * client. For decoding from a plain file via filename or open FILE*, + * FLAC__stream_decoder_init_ogg_file() and FLAC__stream_decoder_init_ogg_FILE() + * provide a simpler interface. + * + * This function should be called after FLAC__stream_decoder_new() and + * FLAC__stream_decoder_set_*() but before any of the + * FLAC__stream_decoder_process_*() functions. Will set and return the + * decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA + * if initialization succeeded. + * + * \note Support for Ogg FLAC in the library is optional. If this + * library has been built without support for Ogg FLAC, this function + * will return \c FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER. + * + * \param decoder An uninitialized decoder instance. + * \param read_callback See FLAC__StreamDecoderReadCallback. This + * pointer must not be \c NULL. + * \param seek_callback See FLAC__StreamDecoderSeekCallback. This + * pointer may be \c NULL if seeking is not + * supported. If \a seek_callback is not \c NULL then a + * \a tell_callback, \a length_callback, and \a eof_callback must also be supplied. + * Alternatively, a dummy seek callback that just + * returns \c FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param tell_callback See FLAC__StreamDecoderTellCallback. This + * pointer may be \c NULL if not supported by the client. If + * \a seek_callback is not \c NULL then a + * \a tell_callback must also be supplied. + * Alternatively, a dummy tell callback that just + * returns \c FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param length_callback See FLAC__StreamDecoderLengthCallback. This + * pointer may be \c NULL if not supported by the client. If + * \a seek_callback is not \c NULL then a + * \a length_callback must also be supplied. + * Alternatively, a dummy length callback that just + * returns \c FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param eof_callback See FLAC__StreamDecoderEofCallback. This + * pointer may be \c NULL if not supported by the client. If + * \a seek_callback is not \c NULL then a + * \a eof_callback must also be supplied. + * Alternatively, a dummy length callback that just + * returns \c false + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param write_callback See FLAC__StreamDecoderWriteCallback. This + * pointer must not be \c NULL. + * \param metadata_callback See FLAC__StreamDecoderMetadataCallback. This + * pointer may be \c NULL if the callback is not + * desired. + * \param error_callback See FLAC__StreamDecoderErrorCallback. This + * pointer must not be \c NULL. + * \param client_data This value will be supplied to callbacks in their + * \a client_data argument. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__StreamDecoderInitStatus + * \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful; + * see FLAC__StreamDecoderInitStatus for the meanings of other return values. + */ +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_stream( + FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderReadCallback read_callback, + FLAC__StreamDecoderSeekCallback seek_callback, + FLAC__StreamDecoderTellCallback tell_callback, + FLAC__StreamDecoderLengthCallback length_callback, + FLAC__StreamDecoderEofCallback eof_callback, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +); + +/** Initialize the decoder instance to decode native FLAC files. + * + * This flavor of initialization sets up the decoder to decode from a + * plain native FLAC file. For non-stdio streams, you must use + * FLAC__stream_decoder_init_stream() and provide callbacks for the I/O. + * + * This function should be called after FLAC__stream_decoder_new() and + * FLAC__stream_decoder_set_*() but before any of the + * FLAC__stream_decoder_process_*() functions. Will set and return the + * decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA + * if initialization succeeded. + * + * \param decoder An uninitialized decoder instance. + * \param file An open FLAC file. The file should have been + * opened with mode \c "rb" and rewound. The file + * becomes owned by the decoder and should not be + * manipulated by the client while decoding. + * Unless \a file is \c stdin, it will be closed + * when FLAC__stream_decoder_finish() is called. + * Note however that seeking will not work when + * decoding from \c stdout since it is not seekable. + * \param write_callback See FLAC__StreamDecoderWriteCallback. This + * pointer must not be \c NULL. + * \param metadata_callback See FLAC__StreamDecoderMetadataCallback. This + * pointer may be \c NULL if the callback is not + * desired. + * \param error_callback See FLAC__StreamDecoderErrorCallback. This + * pointer must not be \c NULL. + * \param client_data This value will be supplied to callbacks in their + * \a client_data argument. + * \assert + * \code decoder != NULL \endcode + * \code file != NULL \endcode + * \retval FLAC__StreamDecoderInitStatus + * \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful; + * see FLAC__StreamDecoderInitStatus for the meanings of other return values. + */ +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_FILE( + FLAC__StreamDecoder *decoder, + FILE *file, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +); + +/** Initialize the decoder instance to decode Ogg FLAC files. + * + * This flavor of initialization sets up the decoder to decode from a + * plain Ogg FLAC file. For non-stdio streams, you must use + * FLAC__stream_decoder_init_ogg_stream() and provide callbacks for the I/O. + * + * This function should be called after FLAC__stream_decoder_new() and + * FLAC__stream_decoder_set_*() but before any of the + * FLAC__stream_decoder_process_*() functions. Will set and return the + * decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA + * if initialization succeeded. + * + * \note Support for Ogg FLAC in the library is optional. If this + * library has been built without support for Ogg FLAC, this function + * will return \c FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER. + * + * \param decoder An uninitialized decoder instance. + * \param file An open FLAC file. The file should have been + * opened with mode \c "rb" and rewound. The file + * becomes owned by the decoder and should not be + * manipulated by the client while decoding. + * Unless \a file is \c stdin, it will be closed + * when FLAC__stream_decoder_finish() is called. + * Note however that seeking will not work when + * decoding from \c stdout since it is not seekable. + * \param write_callback See FLAC__StreamDecoderWriteCallback. This + * pointer must not be \c NULL. + * \param metadata_callback See FLAC__StreamDecoderMetadataCallback. This + * pointer may be \c NULL if the callback is not + * desired. + * \param error_callback See FLAC__StreamDecoderErrorCallback. This + * pointer must not be \c NULL. + * \param client_data This value will be supplied to callbacks in their + * \a client_data argument. + * \assert + * \code decoder != NULL \endcode + * \code file != NULL \endcode + * \retval FLAC__StreamDecoderInitStatus + * \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful; + * see FLAC__StreamDecoderInitStatus for the meanings of other return values. + */ +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_FILE( + FLAC__StreamDecoder *decoder, + FILE *file, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +); + +/** Initialize the decoder instance to decode native FLAC files. + * + * This flavor of initialization sets up the decoder to decode from a plain + * native FLAC file. If POSIX fopen() semantics are not sufficient, (for + * example, with Unicode filenames on Windows), you must use + * FLAC__stream_decoder_init_FILE(), or FLAC__stream_decoder_init_stream() + * and provide callbacks for the I/O. + * + * This function should be called after FLAC__stream_decoder_new() and + * FLAC__stream_decoder_set_*() but before any of the + * FLAC__stream_decoder_process_*() functions. Will set and return the + * decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA + * if initialization succeeded. + * + * \param decoder An uninitialized decoder instance. + * \param filename The name of the file to decode from. The file will + * be opened with fopen(). Use \c NULL to decode from + * \c stdin. Note that \c stdin is not seekable. + * \param write_callback See FLAC__StreamDecoderWriteCallback. This + * pointer must not be \c NULL. + * \param metadata_callback See FLAC__StreamDecoderMetadataCallback. This + * pointer may be \c NULL if the callback is not + * desired. + * \param error_callback See FLAC__StreamDecoderErrorCallback. This + * pointer must not be \c NULL. + * \param client_data This value will be supplied to callbacks in their + * \a client_data argument. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__StreamDecoderInitStatus + * \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful; + * see FLAC__StreamDecoderInitStatus for the meanings of other return values. + */ +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_file( + FLAC__StreamDecoder *decoder, + const char *filename, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +); + +/** Initialize the decoder instance to decode Ogg FLAC files. + * + * This flavor of initialization sets up the decoder to decode from a plain + * Ogg FLAC file. If POSIX fopen() semantics are not sufficient, (for + * example, with Unicode filenames on Windows), you must use + * FLAC__stream_decoder_init_ogg_FILE(), or FLAC__stream_decoder_init_ogg_stream() + * and provide callbacks for the I/O. + * + * This function should be called after FLAC__stream_decoder_new() and + * FLAC__stream_decoder_set_*() but before any of the + * FLAC__stream_decoder_process_*() functions. Will set and return the + * decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA + * if initialization succeeded. + * + * \note Support for Ogg FLAC in the library is optional. If this + * library has been built without support for Ogg FLAC, this function + * will return \c FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER. + * + * \param decoder An uninitialized decoder instance. + * \param filename The name of the file to decode from. The file will + * be opened with fopen(). Use \c NULL to decode from + * \c stdin. Note that \c stdin is not seekable. + * \param write_callback See FLAC__StreamDecoderWriteCallback. This + * pointer must not be \c NULL. + * \param metadata_callback See FLAC__StreamDecoderMetadataCallback. This + * pointer may be \c NULL if the callback is not + * desired. + * \param error_callback See FLAC__StreamDecoderErrorCallback. This + * pointer must not be \c NULL. + * \param client_data This value will be supplied to callbacks in their + * \a client_data argument. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__StreamDecoderInitStatus + * \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful; + * see FLAC__StreamDecoderInitStatus for the meanings of other return values. + */ +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_file( + FLAC__StreamDecoder *decoder, + const char *filename, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +); + +/** Finish the decoding process. + * Flushes the decoding buffer, releases resources, resets the decoder + * settings to their defaults, and returns the decoder state to + * FLAC__STREAM_DECODER_UNINITIALIZED. + * + * In the event of a prematurely-terminated decode, it is not strictly + * necessary to call this immediately before FLAC__stream_decoder_delete() + * but it is good practice to match every FLAC__stream_decoder_init_*() + * with a FLAC__stream_decoder_finish(). + * + * \param decoder An uninitialized decoder instance. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if MD5 checking is on AND a STREAMINFO block was available + * AND the MD5 signature in the STREAMINFO block was non-zero AND the + * signature does not match the one computed by the decoder; else + * \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_finish(FLAC__StreamDecoder *decoder); + +/** Flush the stream input. + * The decoder's input buffer will be cleared and the state set to + * \c FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC. This will also turn + * off MD5 checking. + * + * \param decoder A decoder instance. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c true if successful, else \c false if a memory allocation + * error occurs (in which case the state will be set to + * \c FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR). + */ +FLAC_API FLAC__bool FLAC__stream_decoder_flush(FLAC__StreamDecoder *decoder); + +/** Reset the decoding process. + * The decoder's input buffer will be cleared and the state set to + * \c FLAC__STREAM_DECODER_SEARCH_FOR_METADATA. This is similar to + * FLAC__stream_decoder_finish() except that the settings are + * preserved; there is no need to call FLAC__stream_decoder_init_*() + * before decoding again. MD5 checking will be restored to its original + * setting. + * + * If the decoder is seekable, or was initialized with + * FLAC__stream_decoder_init*_FILE() or FLAC__stream_decoder_init*_file(), + * the decoder will also attempt to seek to the beginning of the file. + * If this rewind fails, this function will return \c false. It follows + * that FLAC__stream_decoder_reset() cannot be used when decoding from + * \c stdin. + * + * If the decoder was initialized with FLAC__stream_encoder_init*_stream() + * and is not seekable (i.e. no seek callback was provided or the seek + * callback returns \c FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED), it + * is the duty of the client to start feeding data from the beginning of + * the stream on the next FLAC__stream_decoder_process() or + * FLAC__stream_decoder_process_interleaved() call. + * + * \param decoder A decoder instance. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c true if successful, else \c false if a memory allocation occurs + * (in which case the state will be set to + * \c FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR) or a seek error + * occurs (the state will be unchanged). + */ +FLAC_API FLAC__bool FLAC__stream_decoder_reset(FLAC__StreamDecoder *decoder); + +/** Decode one metadata block or audio frame. + * This version instructs the decoder to decode a either a single metadata + * block or a single frame and stop, unless the callbacks return a fatal + * error or the read callback returns + * \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM. + * + * As the decoder needs more input it will call the read callback. + * Depending on what was decoded, the metadata or write callback will be + * called with the decoded metadata block or audio frame. + * + * Unless there is a fatal read error or end of stream, this function + * will return once one whole frame is decoded. In other words, if the + * stream is not synchronized or points to a corrupt frame header, the + * decoder will continue to try and resync until it gets to a valid + * frame, then decode one frame, then return. If the decoder points to + * a frame whose frame CRC in the frame footer does not match the + * computed frame CRC, this function will issue a + * FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH error to the + * error callback, and return, having decoded one complete, although + * corrupt, frame. (Such corrupted frames are sent as silence of the + * correct length to the write callback.) + * + * \param decoder An initialized decoder instance. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if any fatal read, write, or memory allocation error + * occurred (meaning decoding must stop), else \c true; for more + * information about the decoder, check the decoder state with + * FLAC__stream_decoder_get_state(). + */ +FLAC_API FLAC__bool FLAC__stream_decoder_process_single(FLAC__StreamDecoder *decoder); + +/** Decode until the end of the metadata. + * This version instructs the decoder to decode from the current position + * and continue until all the metadata has been read, or until the + * callbacks return a fatal error or the read callback returns + * \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM. + * + * As the decoder needs more input it will call the read callback. + * As each metadata block is decoded, the metadata callback will be called + * with the decoded metadata. + * + * \param decoder An initialized decoder instance. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if any fatal read, write, or memory allocation error + * occurred (meaning decoding must stop), else \c true; for more + * information about the decoder, check the decoder state with + * FLAC__stream_decoder_get_state(). + */ +FLAC_API FLAC__bool FLAC__stream_decoder_process_until_end_of_metadata(FLAC__StreamDecoder *decoder); + +/** Decode until the end of the stream. + * This version instructs the decoder to decode from the current position + * and continue until the end of stream (the read callback returns + * \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM), or until the + * callbacks return a fatal error. + * + * As the decoder needs more input it will call the read callback. + * As each metadata block and frame is decoded, the metadata or write + * callback will be called with the decoded metadata or frame. + * + * \param decoder An initialized decoder instance. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if any fatal read, write, or memory allocation error + * occurred (meaning decoding must stop), else \c true; for more + * information about the decoder, check the decoder state with + * FLAC__stream_decoder_get_state(). + */ +FLAC_API FLAC__bool FLAC__stream_decoder_process_until_end_of_stream(FLAC__StreamDecoder *decoder); + +/** Skip one audio frame. + * This version instructs the decoder to 'skip' a single frame and stop, + * unless the callbacks return a fatal error or the read callback returns + * \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM. + * + * The decoding flow is the same as what occurs when + * FLAC__stream_decoder_process_single() is called to process an audio + * frame, except that this function does not decode the parsed data into + * PCM or call the write callback. The integrity of the frame is still + * checked the same way as in the other process functions. + * + * This function will return once one whole frame is skipped, in the + * same way that FLAC__stream_decoder_process_single() will return once + * one whole frame is decoded. + * + * This function can be used in more quickly determining FLAC frame + * boundaries when decoding of the actual data is not needed, for + * example when an application is separating a FLAC stream into frames + * for editing or storing in a container. To do this, the application + * can use FLAC__stream_decoder_skip_single_frame() to quickly advance + * to the next frame, then use + * FLAC__stream_decoder_get_decode_position() to find the new frame + * boundary. + * + * This function should only be called when the stream has advanced + * past all the metadata, otherwise it will return \c false. + * + * \param decoder An initialized decoder instance not in a metadata + * state. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if any fatal read, write, or memory allocation error + * occurred (meaning decoding must stop), or if the decoder + * is in the FLAC__STREAM_DECODER_SEARCH_FOR_METADATA or + * FLAC__STREAM_DECODER_READ_METADATA state, else \c true; for more + * information about the decoder, check the decoder state with + * FLAC__stream_decoder_get_state(). + */ +FLAC_API FLAC__bool FLAC__stream_decoder_skip_single_frame(FLAC__StreamDecoder *decoder); + +/** Flush the input and seek to an absolute sample. + * Decoding will resume at the given sample. Note that because of + * this, the next write callback may contain a partial block. The + * client must support seeking the input or this function will fail + * and return \c false. Furthermore, if the decoder state is + * \c FLAC__STREAM_DECODER_SEEK_ERROR, then the decoder must be flushed + * with FLAC__stream_decoder_flush() or reset with + * FLAC__stream_decoder_reset() before decoding can continue. + * + * \param decoder A decoder instance. + * \param sample The target sample number to seek to. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c true if successful, else \c false. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_seek_absolute(FLAC__StreamDecoder *decoder, FLAC__uint64 sample); + +/* \} */ + +#ifdef __cplusplus +} +#endif + +#endif +/********* End of inlined file: stream_decoder.h *********/ + +/********* Start of inlined file: stream_encoder.h *********/ +#ifndef FLAC__STREAM_ENCODER_H +#define FLAC__STREAM_ENCODER_H + +#include /* for FILE */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file include/FLAC/stream_encoder.h + * + * \brief + * This module contains the functions which implement the stream + * encoder. + * + * See the detailed documentation in the + * \link flac_stream_encoder stream encoder \endlink module. + */ + +/** \defgroup flac_encoder FLAC/ \*_encoder.h: encoder interfaces + * \ingroup flac + * + * \brief + * This module describes the encoder layers provided by libFLAC. + * + * The stream encoder can be used to encode complete streams either to the + * client via callbacks, or directly to a file, depending on how it is + * initialized. When encoding via callbacks, the client provides a write + * callback which will be called whenever FLAC data is ready to be written. + * If the client also supplies a seek callback, the encoder will also + * automatically handle the writing back of metadata discovered while + * encoding, like stream info, seek points offsets, etc. When encoding to + * a file, the client needs only supply a filename or open \c FILE* and an + * optional progress callback for periodic notification of progress; the + * write and seek callbacks are supplied internally. For more info see the + * \link flac_stream_encoder stream encoder \endlink module. + */ + +/** \defgroup flac_stream_encoder FLAC/stream_encoder.h: stream encoder interface + * \ingroup flac_encoder + * + * \brief + * This module contains the functions which implement the stream + * encoder. + * + * The stream encoder can encode to native FLAC, and optionally Ogg FLAC + * (check FLAC_API_SUPPORTS_OGG_FLAC) streams and files. + * + * The basic usage of this encoder is as follows: + * - The program creates an instance of an encoder using + * FLAC__stream_encoder_new(). + * - The program overrides the default settings using + * FLAC__stream_encoder_set_*() functions. At a minimum, the following + * functions should be called: + * - FLAC__stream_encoder_set_channels() + * - FLAC__stream_encoder_set_bits_per_sample() + * - FLAC__stream_encoder_set_sample_rate() + * - FLAC__stream_encoder_set_ogg_serial_number() (if encoding to Ogg FLAC) + * - FLAC__stream_encoder_set_total_samples_estimate() (if known) + * - If the application wants to control the compression level or set its own + * metadata, then the following should also be called: + * - FLAC__stream_encoder_set_compression_level() + * - FLAC__stream_encoder_set_verify() + * - FLAC__stream_encoder_set_metadata() + * - The rest of the set functions should only be called if the client needs + * exact control over how the audio is compressed; thorough understanding + * of the FLAC format is necessary to achieve good results. + * - The program initializes the instance to validate the settings and + * prepare for encoding using + * - FLAC__stream_encoder_init_stream() or FLAC__stream_encoder_init_FILE() + * or FLAC__stream_encoder_init_file() for native FLAC + * - FLAC__stream_encoder_init_ogg_stream() or FLAC__stream_encoder_init_ogg_FILE() + * or FLAC__stream_encoder_init_ogg_file() for Ogg FLAC + * - The program calls FLAC__stream_encoder_process() or + * FLAC__stream_encoder_process_interleaved() to encode data, which + * subsequently calls the callbacks when there is encoder data ready + * to be written. + * - The program finishes the encoding with FLAC__stream_encoder_finish(), + * which causes the encoder to encode any data still in its input pipe, + * update the metadata with the final encoding statistics if output + * seeking is possible, and finally reset the encoder to the + * uninitialized state. + * - The instance may be used again or deleted with + * FLAC__stream_encoder_delete(). + * + * In more detail, the stream encoder functions similarly to the + * \link flac_stream_decoder stream decoder \endlink, but has fewer + * callbacks and more options. Typically the client will create a new + * instance by calling FLAC__stream_encoder_new(), then set the necessary + * parameters with FLAC__stream_encoder_set_*(), and initialize it by + * calling one of the FLAC__stream_encoder_init_*() functions. + * + * Unlike the decoders, the stream encoder has many options that can + * affect the speed and compression ratio. When setting these parameters + * you should have some basic knowledge of the format (see the + * user-level documentation + * or the formal description). The + * FLAC__stream_encoder_set_*() functions themselves do not validate the + * values as many are interdependent. The FLAC__stream_encoder_init_*() + * functions will do this, so make sure to pay attention to the state + * returned by FLAC__stream_encoder_init_*() to make sure that it is + * FLAC__STREAM_ENCODER_INIT_STATUS_OK. Any parameters that are not set + * before FLAC__stream_encoder_init_*() will take on the defaults from + * the constructor. + * + * There are three initialization functions for native FLAC, one for + * setting up the encoder to encode FLAC data to the client via + * callbacks, and two for encoding directly to a file. + * + * For encoding via callbacks, use FLAC__stream_encoder_init_stream(). + * You must also supply a write callback which will be called anytime + * there is raw encoded data to write. If the client can seek the output + * it is best to also supply seek and tell callbacks, as this allows the + * encoder to go back after encoding is finished to write back + * information that was collected while encoding, like seek point offsets, + * frame sizes, etc. + * + * For encoding directly to a file, use FLAC__stream_encoder_init_FILE() + * or FLAC__stream_encoder_init_file(). Then you must only supply a + * filename or open \c FILE*; the encoder will handle all the callbacks + * internally. You may also supply a progress callback for periodic + * notification of the encoding progress. + * + * There are three similarly-named init functions for encoding to Ogg + * FLAC streams. Check \c FLAC_API_SUPPORTS_OGG_FLAC to find out if the + * library has been built with Ogg support. + * + * The call to FLAC__stream_encoder_init_*() currently will also immediately + * call the write callback several times, once with the \c fLaC signature, + * and once for each encoded metadata block. Note that for Ogg FLAC + * encoding you will usually get at least twice the number of callbacks than + * with native FLAC, one for the Ogg page header and one for the page body. + * + * After initializing the instance, the client may feed audio data to the + * encoder in one of two ways: + * + * - Channel separate, through FLAC__stream_encoder_process() - The client + * will pass an array of pointers to buffers, one for each channel, to + * the encoder, each of the same length. The samples need not be + * block-aligned, but each channel should have the same number of samples. + * - Channel interleaved, through + * FLAC__stream_encoder_process_interleaved() - The client will pass a single + * pointer to data that is channel-interleaved (i.e. channel0_sample0, + * channel1_sample0, ... , channelN_sample0, channel0_sample1, ...). + * Again, the samples need not be block-aligned but they must be + * sample-aligned, i.e. the first value should be channel0_sample0 and + * the last value channelN_sampleM. + * + * Note that for either process call, each sample in the buffers should be a + * signed integer, right-justified to the resolution set by + * FLAC__stream_encoder_set_bits_per_sample(). For example, if the resolution + * is 16 bits per sample, the samples should all be in the range [-32768,32767]. + * + * When the client is finished encoding data, it calls + * FLAC__stream_encoder_finish(), which causes the encoder to encode any + * data still in its input pipe, and call the metadata callback with the + * final encoding statistics. Then the instance may be deleted with + * FLAC__stream_encoder_delete() or initialized again to encode another + * stream. + * + * For programs that write their own metadata, but that do not know the + * actual metadata until after encoding, it is advantageous to instruct + * the encoder to write a PADDING block of the correct size, so that + * instead of rewriting the whole stream after encoding, the program can + * just overwrite the PADDING block. If only the maximum size of the + * metadata is known, the program can write a slightly larger padding + * block, then split it after encoding. + * + * Make sure you understand how lengths are calculated. All FLAC metadata + * blocks have a 4 byte header which contains the type and length. This + * length does not include the 4 bytes of the header. See the format page + * for the specification of metadata blocks and their lengths. + * + * \note + * If you are writing the FLAC data to a file via callbacks, make sure it + * is open for update (e.g. mode "w+" for stdio streams). This is because + * after the first encoding pass, the encoder will try to seek back to the + * beginning of the stream, to the STREAMINFO block, to write some data + * there. (If using FLAC__stream_encoder_init*_file() or + * FLAC__stream_encoder_init*_FILE(), the file is managed internally.) + * + * \note + * The "set" functions may only be called when the encoder is in the + * state FLAC__STREAM_ENCODER_UNINITIALIZED, i.e. after + * FLAC__stream_encoder_new() or FLAC__stream_encoder_finish(), but + * before FLAC__stream_encoder_init_*(). If this is the case they will + * return \c true, otherwise \c false. + * + * \note + * FLAC__stream_encoder_finish() resets all settings to the constructor + * defaults. + * + * \{ + */ + +/** State values for a FLAC__StreamEncoder. + * + * The encoder's state can be obtained by calling FLAC__stream_encoder_get_state(). + * + * If the encoder gets into any other state besides \c FLAC__STREAM_ENCODER_OK + * or \c FLAC__STREAM_ENCODER_UNINITIALIZED, it becomes invalid for encoding and + * must be deleted with FLAC__stream_encoder_delete(). + */ +typedef enum { + + FLAC__STREAM_ENCODER_OK = 0, + /**< The encoder is in the normal OK state and samples can be processed. */ + + FLAC__STREAM_ENCODER_UNINITIALIZED, + /**< The encoder is in the uninitialized state; one of the + * FLAC__stream_encoder_init_*() functions must be called before samples + * can be processed. + */ + + FLAC__STREAM_ENCODER_OGG_ERROR, + /**< An error occurred in the underlying Ogg layer. */ + + FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR, + /**< An error occurred in the underlying verify stream decoder; + * check FLAC__stream_encoder_get_verify_decoder_state(). + */ + + FLAC__STREAM_ENCODER_VERIFY_MISMATCH_IN_AUDIO_DATA, + /**< The verify decoder detected a mismatch between the original + * audio signal and the decoded audio signal. + */ + + FLAC__STREAM_ENCODER_CLIENT_ERROR, + /**< One of the callbacks returned a fatal error. */ + + FLAC__STREAM_ENCODER_IO_ERROR, + /**< An I/O error occurred while opening/reading/writing a file. + * Check \c errno. + */ + + FLAC__STREAM_ENCODER_FRAMING_ERROR, + /**< An error occurred while writing the stream; usually, the + * write_callback returned an error. + */ + + FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR + /**< Memory allocation failed. */ + +} FLAC__StreamEncoderState; + +/** Maps a FLAC__StreamEncoderState to a C string. + * + * Using a FLAC__StreamEncoderState as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamEncoderStateString[]; + +/** Possible return values for the FLAC__stream_encoder_init_*() functions. + */ +typedef enum { + + FLAC__STREAM_ENCODER_INIT_STATUS_OK = 0, + /**< Initialization was successful. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR, + /**< General failure to set up encoder; call FLAC__stream_encoder_get_state() for cause. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_UNSUPPORTED_CONTAINER, + /**< The library was not compiled with support for the given container + * format. + */ + + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_CALLBACKS, + /**< A required callback was not supplied. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_NUMBER_OF_CHANNELS, + /**< The encoder has an invalid setting for number of channels. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_BITS_PER_SAMPLE, + /**< The encoder has an invalid setting for bits-per-sample. + * FLAC supports 4-32 bps but the reference encoder currently supports + * only up to 24 bps. + */ + + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_SAMPLE_RATE, + /**< The encoder has an invalid setting for the input sample rate. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_BLOCK_SIZE, + /**< The encoder has an invalid setting for the block size. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_MAX_LPC_ORDER, + /**< The encoder has an invalid setting for the maximum LPC order. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_QLP_COEFF_PRECISION, + /**< The encoder has an invalid setting for the precision of the quantized linear predictor coefficients. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_BLOCK_SIZE_TOO_SMALL_FOR_LPC_ORDER, + /**< The specified block size is less than the maximum LPC order. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_NOT_STREAMABLE, + /**< The encoder is bound to the Subset but other settings violate it. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_METADATA, + /**< The metadata input to the encoder is invalid, in one of the following ways: + * - FLAC__stream_encoder_set_metadata() was called with a null pointer but a block count > 0 + * - One of the metadata blocks contains an undefined type + * - It contains an illegal CUESHEET as checked by FLAC__format_cuesheet_is_legal() + * - It contains an illegal SEEKTABLE as checked by FLAC__format_seektable_is_legal() + * - It contains more than one SEEKTABLE block or more than one VORBIS_COMMENT block + */ + + FLAC__STREAM_ENCODER_INIT_STATUS_ALREADY_INITIALIZED + /**< FLAC__stream_encoder_init_*() was called when the encoder was + * already initialized, usually because + * FLAC__stream_encoder_finish() was not called. + */ + +} FLAC__StreamEncoderInitStatus; + +/** Maps a FLAC__StreamEncoderInitStatus to a C string. + * + * Using a FLAC__StreamEncoderInitStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamEncoderInitStatusString[]; + +/** Return values for the FLAC__StreamEncoder read callback. + */ +typedef enum { + + FLAC__STREAM_ENCODER_READ_STATUS_CONTINUE, + /**< The read was OK and decoding can continue. */ + + FLAC__STREAM_ENCODER_READ_STATUS_END_OF_STREAM, + /**< The read was attempted at the end of the stream. */ + + FLAC__STREAM_ENCODER_READ_STATUS_ABORT, + /**< An unrecoverable error occurred. */ + + FLAC__STREAM_ENCODER_READ_STATUS_UNSUPPORTED + /**< Client does not support reading back from the output. */ + +} FLAC__StreamEncoderReadStatus; + +/** Maps a FLAC__StreamEncoderReadStatus to a C string. + * + * Using a FLAC__StreamEncoderReadStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamEncoderReadStatusString[]; + +/** Return values for the FLAC__StreamEncoder write callback. + */ +typedef enum { + + FLAC__STREAM_ENCODER_WRITE_STATUS_OK = 0, + /**< The write was OK and encoding can continue. */ + + FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR + /**< An unrecoverable error occurred. The encoder will return from the process call. */ + +} FLAC__StreamEncoderWriteStatus; + +/** Maps a FLAC__StreamEncoderWriteStatus to a C string. + * + * Using a FLAC__StreamEncoderWriteStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamEncoderWriteStatusString[]; + +/** Return values for the FLAC__StreamEncoder seek callback. + */ +typedef enum { + + FLAC__STREAM_ENCODER_SEEK_STATUS_OK, + /**< The seek was OK and encoding can continue. */ + + FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR, + /**< An unrecoverable error occurred. */ + + FLAC__STREAM_ENCODER_SEEK_STATUS_UNSUPPORTED + /**< Client does not support seeking. */ + +} FLAC__StreamEncoderSeekStatus; + +/** Maps a FLAC__StreamEncoderSeekStatus to a C string. + * + * Using a FLAC__StreamEncoderSeekStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamEncoderSeekStatusString[]; + +/** Return values for the FLAC__StreamEncoder tell callback. + */ +typedef enum { + + FLAC__STREAM_ENCODER_TELL_STATUS_OK, + /**< The tell was OK and encoding can continue. */ + + FLAC__STREAM_ENCODER_TELL_STATUS_ERROR, + /**< An unrecoverable error occurred. */ + + FLAC__STREAM_ENCODER_TELL_STATUS_UNSUPPORTED + /**< Client does not support seeking. */ + +} FLAC__StreamEncoderTellStatus; + +/** Maps a FLAC__StreamEncoderTellStatus to a C string. + * + * Using a FLAC__StreamEncoderTellStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamEncoderTellStatusString[]; + +/*********************************************************************** + * + * class FLAC__StreamEncoder + * + ***********************************************************************/ + +struct FLAC__StreamEncoderProtected; +struct FLAC__StreamEncoderPrivate; +/** The opaque structure definition for the stream encoder type. + * See the \link flac_stream_encoder stream encoder module \endlink + * for a detailed description. + */ +typedef struct { + struct FLAC__StreamEncoderProtected *protected_; /* avoid the C++ keyword 'protected' */ + struct FLAC__StreamEncoderPrivate *private_; /* avoid the C++ keyword 'private' */ +} FLAC__StreamEncoder; + +/** Signature for the read callback. + * + * A function pointer matching this signature must be passed to + * FLAC__stream_encoder_init_ogg_stream() if seeking is supported. + * The supplied function will be called when the encoder needs to read back + * encoded data. This happens during the metadata callback, when the encoder + * has to read, modify, and rewrite the metadata (e.g. seekpoints) gathered + * while encoding. The address of the buffer to be filled is supplied, along + * with the number of bytes the buffer can hold. The callback may choose to + * supply less data and modify the byte count but must be careful not to + * overflow the buffer. The callback then returns a status code chosen from + * FLAC__StreamEncoderReadStatus. + * + * Here is an example of a read callback for stdio streams: + * \code + * FLAC__StreamEncoderReadStatus read_cb(const FLAC__StreamEncoder *encoder, FLAC__byte buffer[], size_t *bytes, void *client_data) + * { + * FILE *file = ((MyClientData*)client_data)->file; + * if(*bytes > 0) { + * *bytes = fread(buffer, sizeof(FLAC__byte), *bytes, file); + * if(ferror(file)) + * return FLAC__STREAM_ENCODER_READ_STATUS_ABORT; + * else if(*bytes == 0) + * return FLAC__STREAM_ENCODER_READ_STATUS_END_OF_STREAM; + * else + * return FLAC__STREAM_ENCODER_READ_STATUS_CONTINUE; + * } + * else + * return FLAC__STREAM_ENCODER_READ_STATUS_ABORT; + * } + * \endcode + * + * \note In general, FLAC__StreamEncoder functions which change the + * state should not be called on the \a encoder while in the callback. + * + * \param encoder The encoder instance calling the callback. + * \param buffer A pointer to a location for the callee to store + * data to be encoded. + * \param bytes A pointer to the size of the buffer. On entry + * to the callback, it contains the maximum number + * of bytes that may be stored in \a buffer. The + * callee must set it to the actual number of bytes + * stored (0 in case of error or end-of-stream) before + * returning. + * \param client_data The callee's client data set through + * FLAC__stream_encoder_set_client_data(). + * \retval FLAC__StreamEncoderReadStatus + * The callee's return status. + */ +typedef FLAC__StreamEncoderReadStatus (*FLAC__StreamEncoderReadCallback)(const FLAC__StreamEncoder *encoder, FLAC__byte buffer[], size_t *bytes, void *client_data); + +/** Signature for the write callback. + * + * A function pointer matching this signature must be passed to + * FLAC__stream_encoder_init*_stream(). The supplied function will be called + * by the encoder anytime there is raw encoded data ready to write. It may + * include metadata mixed with encoded audio frames and the data is not + * guaranteed to be aligned on frame or metadata block boundaries. + * + * The only duty of the callback is to write out the \a bytes worth of data + * in \a buffer to the current position in the output stream. The arguments + * \a samples and \a current_frame are purely informational. If \a samples + * is greater than \c 0, then \a current_frame will hold the current frame + * number that is being written; otherwise it indicates that the write + * callback is being called to write metadata. + * + * \note + * Unlike when writing to native FLAC, when writing to Ogg FLAC the + * write callback will be called twice when writing each audio + * frame; once for the page header, and once for the page body. + * When writing the page header, the \a samples argument to the + * write callback will be \c 0. + * + * \note In general, FLAC__StreamEncoder functions which change the + * state should not be called on the \a encoder while in the callback. + * + * \param encoder The encoder instance calling the callback. + * \param buffer An array of encoded data of length \a bytes. + * \param bytes The byte length of \a buffer. + * \param samples The number of samples encoded by \a buffer. + * \c 0 has a special meaning; see above. + * \param current_frame The number of the current frame being encoded. + * \param client_data The callee's client data set through + * FLAC__stream_encoder_init_*(). + * \retval FLAC__StreamEncoderWriteStatus + * The callee's return status. + */ +typedef FLAC__StreamEncoderWriteStatus (*FLAC__StreamEncoderWriteCallback)(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame, void *client_data); + +/** Signature for the seek callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_encoder_init*_stream(). The supplied function will be called + * when the encoder needs to seek the output stream. The encoder will pass + * the absolute byte offset to seek to, 0 meaning the beginning of the stream. + * + * Here is an example of a seek callback for stdio streams: + * \code + * FLAC__StreamEncoderSeekStatus seek_cb(const FLAC__StreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data) + * { + * FILE *file = ((MyClientData*)client_data)->file; + * if(file == stdin) + * return FLAC__STREAM_ENCODER_SEEK_STATUS_UNSUPPORTED; + * else if(fseeko(file, (off_t)absolute_byte_offset, SEEK_SET) < 0) + * return FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR; + * else + * return FLAC__STREAM_ENCODER_SEEK_STATUS_OK; + * } + * \endcode + * + * \note In general, FLAC__StreamEncoder functions which change the + * state should not be called on the \a encoder while in the callback. + * + * \param encoder The encoder instance calling the callback. + * \param absolute_byte_offset The offset from the beginning of the stream + * to seek to. + * \param client_data The callee's client data set through + * FLAC__stream_encoder_init_*(). + * \retval FLAC__StreamEncoderSeekStatus + * The callee's return status. + */ +typedef FLAC__StreamEncoderSeekStatus (*FLAC__StreamEncoderSeekCallback)(const FLAC__StreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data); + +/** Signature for the tell callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_encoder_init*_stream(). The supplied function will be called + * when the encoder needs to know the current position of the output stream. + * + * \warning + * The callback must return the true current byte offset of the output to + * which the encoder is writing. If you are buffering the output, make + * sure and take this into account. If you are writing directly to a + * FILE* from your write callback, ftell() is sufficient. If you are + * writing directly to a file descriptor from your write callback, you + * can use lseek(fd, SEEK_CUR, 0). The encoder may later seek back to + * these points to rewrite metadata after encoding. + * + * Here is an example of a tell callback for stdio streams: + * \code + * FLAC__StreamEncoderTellStatus tell_cb(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_byte_offset, void *client_data) + * { + * FILE *file = ((MyClientData*)client_data)->file; + * off_t pos; + * if(file == stdin) + * return FLAC__STREAM_ENCODER_TELL_STATUS_UNSUPPORTED; + * else if((pos = ftello(file)) < 0) + * return FLAC__STREAM_ENCODER_TELL_STATUS_ERROR; + * else { + * *absolute_byte_offset = (FLAC__uint64)pos; + * return FLAC__STREAM_ENCODER_TELL_STATUS_OK; + * } + * } + * \endcode + * + * \note In general, FLAC__StreamEncoder functions which change the + * state should not be called on the \a encoder while in the callback. + * + * \param encoder The encoder instance calling the callback. + * \param absolute_byte_offset The address at which to store the current + * position of the output. + * \param client_data The callee's client data set through + * FLAC__stream_encoder_init_*(). + * \retval FLAC__StreamEncoderTellStatus + * The callee's return status. + */ +typedef FLAC__StreamEncoderTellStatus (*FLAC__StreamEncoderTellCallback)(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_byte_offset, void *client_data); + +/** Signature for the metadata callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_encoder_init*_stream(). The supplied function will be called + * once at the end of encoding with the populated STREAMINFO structure. This + * is so the client can seek back to the beginning of the file and write the + * STREAMINFO block with the correct statistics after encoding (like + * minimum/maximum frame size and total samples). + * + * \note In general, FLAC__StreamEncoder functions which change the + * state should not be called on the \a encoder while in the callback. + * + * \param encoder The encoder instance calling the callback. + * \param metadata The final populated STREAMINFO block. + * \param client_data The callee's client data set through + * FLAC__stream_encoder_init_*(). + */ +typedef void (*FLAC__StreamEncoderMetadataCallback)(const FLAC__StreamEncoder *encoder, const FLAC__StreamMetadata *metadata, void *client_data); + +/** Signature for the progress callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_encoder_init*_file() or FLAC__stream_encoder_init*_FILE(). + * The supplied function will be called when the encoder has finished + * writing a frame. The \c total_frames_estimate argument to the + * callback will be based on the value from + * FLAC__stream_encoder_set_total_samples_estimate(). + * + * \note In general, FLAC__StreamEncoder functions which change the + * state should not be called on the \a encoder while in the callback. + * + * \param encoder The encoder instance calling the callback. + * \param bytes_written Bytes written so far. + * \param samples_written Samples written so far. + * \param frames_written Frames written so far. + * \param total_frames_estimate The estimate of the total number of + * frames to be written. + * \param client_data The callee's client data set through + * FLAC__stream_encoder_init_*(). + */ +typedef void (*FLAC__StreamEncoderProgressCallback)(const FLAC__StreamEncoder *encoder, FLAC__uint64 bytes_written, FLAC__uint64 samples_written, unsigned frames_written, unsigned total_frames_estimate, void *client_data); + +/*********************************************************************** + * + * Class constructor/destructor + * + ***********************************************************************/ + +/** Create a new stream encoder instance. The instance is created with + * default settings; see the individual FLAC__stream_encoder_set_*() + * functions for each setting's default. + * + * \retval FLAC__StreamEncoder* + * \c NULL if there was an error allocating memory, else the new instance. + */ +FLAC_API FLAC__StreamEncoder *FLAC__stream_encoder_new(void); + +/** Free an encoder instance. Deletes the object pointed to by \a encoder. + * + * \param encoder A pointer to an existing encoder. + * \assert + * \code encoder != NULL \endcode + */ +FLAC_API void FLAC__stream_encoder_delete(FLAC__StreamEncoder *encoder); + +/*********************************************************************** + * + * Public class method prototypes + * + ***********************************************************************/ + +/** Set the serial number for the FLAC stream to use in the Ogg container. + * + * \note + * This does not need to be set for native FLAC encoding. + * + * \note + * It is recommended to set a serial number explicitly as the default of '0' + * may collide with other streams. + * + * \default \c 0 + * \param encoder An encoder instance to set. + * \param serial_number See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_ogg_serial_number(FLAC__StreamEncoder *encoder, long serial_number); + +/** Set the "verify" flag. If \c true, the encoder will verify it's own + * encoded output by feeding it through an internal decoder and comparing + * the original signal against the decoded signal. If a mismatch occurs, + * the process call will return \c false. Note that this will slow the + * encoding process by the extra time required for decoding and comparison. + * + * \default \c false + * \param encoder An encoder instance to set. + * \param value Flag value (see above). + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_verify(FLAC__StreamEncoder *encoder, FLAC__bool value); + +/** Set the Subset flag. If \c true, + * the encoder will comply with the Subset and will check the + * settings during FLAC__stream_encoder_init_*() to see if all settings + * comply. If \c false, the settings may take advantage of the full + * range that the format allows. + * + * Make sure you know what it entails before setting this to \c false. + * + * \default \c true + * \param encoder An encoder instance to set. + * \param value Flag value (see above). + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_streamable_subset(FLAC__StreamEncoder *encoder, FLAC__bool value); + +/** Set the number of channels to be encoded. + * + * \default \c 2 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_channels(FLAC__StreamEncoder *encoder, unsigned value); + +/** Set the sample resolution of the input to be encoded. + * + * \warning + * Do not feed the encoder data that is wider than the value you + * set here or you will generate an invalid stream. + * + * \default \c 16 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_bits_per_sample(FLAC__StreamEncoder *encoder, unsigned value); + +/** Set the sample rate (in Hz) of the input to be encoded. + * + * \default \c 44100 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_sample_rate(FLAC__StreamEncoder *encoder, unsigned value); + +/** Set the compression level + * + * The compression level is roughly proportional to the amount of effort + * the encoder expends to compress the file. A higher level usually + * means more computation but higher compression. The default level is + * suitable for most applications. + * + * Currently the levels range from \c 0 (fastest, least compression) to + * \c 8 (slowest, most compression). A value larger than \c 8 will be + * treated as \c 8. + * + * This function automatically calls the following other \c _set_ + * functions with appropriate values, so the client does not need to + * unless it specifically wants to override them: + * - FLAC__stream_encoder_set_do_mid_side_stereo() + * - FLAC__stream_encoder_set_loose_mid_side_stereo() + * - FLAC__stream_encoder_set_apodization() + * - FLAC__stream_encoder_set_max_lpc_order() + * - FLAC__stream_encoder_set_qlp_coeff_precision() + * - FLAC__stream_encoder_set_do_qlp_coeff_prec_search() + * - FLAC__stream_encoder_set_do_escape_coding() + * - FLAC__stream_encoder_set_do_exhaustive_model_search() + * - FLAC__stream_encoder_set_min_residual_partition_order() + * - FLAC__stream_encoder_set_max_residual_partition_order() + * - FLAC__stream_encoder_set_rice_parameter_search_dist() + * + * The actual values set for each level are: + * + * + * + * + * + * + * + * + * + * + * + * + *
level + * do mid-side stereo + * loose mid-side stereo + * apodization + * max lpc order + * qlp coeff precision + * qlp coeff prec search + * escape coding + * exhaustive model search + * min residual partition order + * max residual partition order + * rice parameter search dist + *
0 false false tukey(0.5) 0 0 false false false 0 3 0
1 true true tukey(0.5) 0 0 false false false 0 3 0
2 true false tukey(0.5) 0 0 false false false 0 3 0
3 false false tukey(0.5) 6 0 false false false 0 4 0
4 true true tukey(0.5) 8 0 false false false 0 4 0
5 true false tukey(0.5) 8 0 false false false 0 5 0
6 true false tukey(0.5) 8 0 false false false 0 6 0
7 true false tukey(0.5) 8 0 false false true 0 6 0
8 true false tukey(0.5) 12 0 false false true 0 6 0
+ * + * \default \c 5 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_compression_level(FLAC__StreamEncoder *encoder, unsigned value); + +/** Set the blocksize to use while encoding. + * + * The number of samples to use per frame. Use \c 0 to let the encoder + * estimate a blocksize; this is usually best. + * + * \default \c 0 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_blocksize(FLAC__StreamEncoder *encoder, unsigned value); + +/** Set to \c true to enable mid-side encoding on stereo input. The + * number of channels must be 2 for this to have any effect. Set to + * \c false to use only independent channel coding. + * + * \default \c false + * \param encoder An encoder instance to set. + * \param value Flag value (see above). + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_do_mid_side_stereo(FLAC__StreamEncoder *encoder, FLAC__bool value); + +/** Set to \c true to enable adaptive switching between mid-side and + * left-right encoding on stereo input. Set to \c false to use + * exhaustive searching. Setting this to \c true requires + * FLAC__stream_encoder_set_do_mid_side_stereo() to also be set to + * \c true in order to have any effect. + * + * \default \c false + * \param encoder An encoder instance to set. + * \param value Flag value (see above). + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_loose_mid_side_stereo(FLAC__StreamEncoder *encoder, FLAC__bool value); + +/** Sets the apodization function(s) the encoder will use when windowing + * audio data for LPC analysis. + * + * The \a specification is a plain ASCII string which specifies exactly + * which functions to use. There may be more than one (up to 32), + * separated by \c ';' characters. Some functions take one or more + * comma-separated arguments in parentheses. + * + * The available functions are \c bartlett, \c bartlett_hann, + * \c blackman, \c blackman_harris_4term_92db, \c connes, \c flattop, + * \c gauss(STDDEV), \c hamming, \c hann, \c kaiser_bessel, \c nuttall, + * \c rectangle, \c triangle, \c tukey(P), \c welch. + * + * For \c gauss(STDDEV), STDDEV specifies the standard deviation + * (0blocksize / (2 ^ order). + * + * Set both min and max values to \c 0 to force a single context, + * whose Rice parameter is based on the residual signal variance. + * Otherwise, set a min and max order, and the encoder will search + * all orders, using the mean of each context for its Rice parameter, + * and use the best. + * + * \default \c 0 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_min_residual_partition_order(FLAC__StreamEncoder *encoder, unsigned value); + +/** Set the maximum partition order to search when coding the residual. + * This is used in tandem with + * FLAC__stream_encoder_set_min_residual_partition_order(). + * + * The partition order determines the context size in the residual. + * The context size will be approximately blocksize / (2 ^ order). + * + * Set both min and max values to \c 0 to force a single context, + * whose Rice parameter is based on the residual signal variance. + * Otherwise, set a min and max order, and the encoder will search + * all orders, using the mean of each context for its Rice parameter, + * and use the best. + * + * \default \c 0 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_max_residual_partition_order(FLAC__StreamEncoder *encoder, unsigned value); + +/** Deprecated. Setting this value has no effect. + * + * \default \c 0 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_rice_parameter_search_dist(FLAC__StreamEncoder *encoder, unsigned value); + +/** Set an estimate of the total samples that will be encoded. + * This is merely an estimate and may be set to \c 0 if unknown. + * This value will be written to the STREAMINFO block before encoding, + * and can remove the need for the caller to rewrite the value later + * if the value is known before encoding. + * + * \default \c 0 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_total_samples_estimate(FLAC__StreamEncoder *encoder, FLAC__uint64 value); + +/** Set the metadata blocks to be emitted to the stream before encoding. + * A value of \c NULL, \c 0 implies no metadata; otherwise, supply an + * array of pointers to metadata blocks. The array is non-const since + * the encoder may need to change the \a is_last flag inside them, and + * in some cases update seek point offsets. Otherwise, the encoder will + * not modify or free the blocks. It is up to the caller to free the + * metadata blocks after encoding finishes. + * + * \note + * The encoder stores only copies of the pointers in the \a metadata array; + * the metadata blocks themselves must survive at least until after + * FLAC__stream_encoder_finish() returns. Do not free the blocks until then. + * + * \note + * The STREAMINFO block is always written and no STREAMINFO block may + * occur in the supplied array. + * + * \note + * By default the encoder does not create a SEEKTABLE. If one is supplied + * in the \a metadata array, but the client has specified that it does not + * support seeking, then the SEEKTABLE will be written verbatim. However + * by itself this is not very useful as the client will not know the stream + * offsets for the seekpoints ahead of time. In order to get a proper + * seektable the client must support seeking. See next note. + * + * \note + * SEEKTABLE blocks are handled specially. Since you will not know + * the values for the seek point stream offsets, you should pass in + * a SEEKTABLE 'template', that is, a SEEKTABLE object with the + * required sample numbers (or placeholder points), with \c 0 for the + * \a frame_samples and \a stream_offset fields for each point. If the + * client has specified that it supports seeking by providing a seek + * callback to FLAC__stream_encoder_init_stream() or both seek AND read + * callback to FLAC__stream_encoder_init_ogg_stream() (or by using + * FLAC__stream_encoder_init*_file() or FLAC__stream_encoder_init*_FILE()), + * then while it is encoding the encoder will fill the stream offsets in + * for you and when encoding is finished, it will seek back and write the + * real values into the SEEKTABLE block in the stream. There are helper + * routines for manipulating seektable template blocks; see metadata.h: + * FLAC__metadata_object_seektable_template_*(). If the client does + * not support seeking, the SEEKTABLE will have inaccurate offsets which + * will slow down or remove the ability to seek in the FLAC stream. + * + * \note + * The encoder instance \b will modify the first \c SEEKTABLE block + * as it transforms the template to a valid seektable while encoding, + * but it is still up to the caller to free all metadata blocks after + * encoding. + * + * \note + * A VORBIS_COMMENT block may be supplied. The vendor string in it + * will be ignored. libFLAC will use it's own vendor string. libFLAC + * will not modify the passed-in VORBIS_COMMENT's vendor string, it + * will simply write it's own into the stream. If no VORBIS_COMMENT + * block is present in the \a metadata array, libFLAC will write an + * empty one, containing only the vendor string. + * + * \note The Ogg FLAC mapping requires that the VORBIS_COMMENT block be + * the second metadata block of the stream. The encoder already supplies + * the STREAMINFO block automatically. If \a metadata does not contain a + * VORBIS_COMMENT block, the encoder will supply that too. Otherwise, if + * \a metadata does contain a VORBIS_COMMENT block and it is not the + * first, the init function will reorder \a metadata by moving the + * VORBIS_COMMENT block to the front; the relative ordering of the other + * blocks will remain as they were. + * + * \note The Ogg FLAC mapping limits the number of metadata blocks per + * stream to \c 65535. If \a num_blocks exceeds this the function will + * return \c false. + * + * \default \c NULL, 0 + * \param encoder An encoder instance to set. + * \param metadata See above. + * \param num_blocks See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + * \c false if the encoder is already initialized, or if + * \a num_blocks > 65535 if encoding to Ogg FLAC, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_metadata(FLAC__StreamEncoder *encoder, FLAC__StreamMetadata **metadata, unsigned num_blocks); + +/** Get the current encoder state. + * + * \param encoder An encoder instance to query. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__StreamEncoderState + * The current encoder state. + */ +FLAC_API FLAC__StreamEncoderState FLAC__stream_encoder_get_state(const FLAC__StreamEncoder *encoder); + +/** Get the state of the verify stream decoder. + * Useful when the stream encoder state is + * \c FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR. + * + * \param encoder An encoder instance to query. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__StreamDecoderState + * The verify stream decoder state. + */ +FLAC_API FLAC__StreamDecoderState FLAC__stream_encoder_get_verify_decoder_state(const FLAC__StreamEncoder *encoder); + +/** Get the current encoder state as a C string. + * This version automatically resolves + * \c FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR by getting the + * verify decoder's state. + * + * \param encoder A encoder instance to query. + * \assert + * \code encoder != NULL \endcode + * \retval const char * + * The encoder state as a C string. Do not modify the contents. + */ +FLAC_API const char *FLAC__stream_encoder_get_resolved_state_string(const FLAC__StreamEncoder *encoder); + +/** Get relevant values about the nature of a verify decoder error. + * Useful when the stream encoder state is + * \c FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR. The arguments should + * be addresses in which the stats will be returned, or NULL if value + * is not desired. + * + * \param encoder An encoder instance to query. + * \param absolute_sample The absolute sample number of the mismatch. + * \param frame_number The number of the frame in which the mismatch occurred. + * \param channel The channel in which the mismatch occurred. + * \param sample The number of the sample (relative to the frame) in + * which the mismatch occurred. + * \param expected The expected value for the sample in question. + * \param got The actual value returned by the decoder. + * \assert + * \code encoder != NULL \endcode + */ +FLAC_API void FLAC__stream_encoder_get_verify_decoder_error_stats(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_sample, unsigned *frame_number, unsigned *channel, unsigned *sample, FLAC__int32 *expected, FLAC__int32 *got); + +/** Get the "verify" flag. + * + * \param encoder An encoder instance to query. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * See FLAC__stream_encoder_set_verify(). + */ +FLAC_API FLAC__bool FLAC__stream_encoder_get_verify(const FLAC__StreamEncoder *encoder); + +/** Get the frame header. + * + * \param encoder An initialized encoder instance in the OK state. + * \param buffer An array of pointers to each channel's signal. + * \param samples The number of samples in one channel. + * \assert + * \code encoder != NULL \endcode + * \code FLAC__stream_encoder_get_state(encoder) == FLAC__STREAM_ENCODER_OK \endcode + * \retval FLAC__bool + * \c true if successful, else \c false; in this case, check the + * encoder state with FLAC__stream_encoder_get_state() to see what + * went wrong. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_process(FLAC__StreamEncoder *encoder, const FLAC__int32 * const buffer[], unsigned samples); + +/** Submit data for encoding. + * This version allows you to supply the input data where the channels + * are interleaved into a single array (i.e. channel0_sample0, + * channel1_sample0, ... , channelN_sample0, channel0_sample1, ...). + * The samples need not be block-aligned but they must be + * sample-aligned, i.e. the first value should be channel0_sample0 + * and the last value channelN_sampleM. Each sample should be a signed + * integer, right-justified to the resolution set by + * FLAC__stream_encoder_set_bits_per_sample(). For example, if the + * resolution is 16 bits per sample, the samples should all be in the + * range [-32768,32767]. + * + * For applications where channel order is important, channels must + * follow the order as described in the + * frame header. + * + * \param encoder An initialized encoder instance in the OK state. + * \param buffer An array of channel-interleaved data (see above). + * \param samples The number of samples in one channel, the same as for + * FLAC__stream_encoder_process(). For example, if + * encoding two channels, \c 1000 \a samples corresponds + * to a \a buffer of 2000 values. + * \assert + * \code encoder != NULL \endcode + * \code FLAC__stream_encoder_get_state(encoder) == FLAC__STREAM_ENCODER_OK \endcode + * \retval FLAC__bool + * \c true if successful, else \c false; in this case, check the + * encoder state with FLAC__stream_encoder_get_state() to see what + * went wrong. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_process_interleaved(FLAC__StreamEncoder *encoder, const FLAC__int32 buffer[], unsigned samples); + +/* \} */ + +#ifdef __cplusplus +} +#endif + +#endif +/********* End of inlined file: stream_encoder.h *********/ + +#ifdef _MSC_VER +/* OPT: an MSVC built-in would be better */ +static _inline FLAC__uint32 local_swap32_(FLAC__uint32 x) +{ + x = ((x<<8)&0xFF00FF00) | ((x>>8)&0x00FF00FF); + return (x>>16) | (x<<16); +} +#endif + +#if defined(_MSC_VER) && defined(_X86_) +/* OPT: an MSVC built-in would be better */ +static void local_swap32_block_(FLAC__uint32 *start, FLAC__uint32 len) +{ + __asm { + mov edx, start + mov ecx, len + test ecx, ecx +loop1: + jz done1 + mov eax, [edx] + bswap eax + mov [edx], eax + add edx, 4 + dec ecx + jmp short loop1 +done1: + } +} +#endif + +/** \mainpage + * + * \section intro Introduction + * + * This is the documentation for the FLAC C and C++ APIs. It is + * highly interconnected; this introduction should give you a top + * level idea of the structure and how to find the information you + * need. As a prerequisite you should have at least a basic + * knowledge of the FLAC format, documented + * here. + * + * \section c_api FLAC C API + * + * The FLAC C API is the interface to libFLAC, a set of structures + * describing the components of FLAC streams, and functions for + * encoding and decoding streams, as well as manipulating FLAC + * metadata in files. The public include files will be installed + * in your include area (for example /usr/include/FLAC/...). + * + * By writing a little code and linking against libFLAC, it is + * relatively easy to add FLAC support to another program. The + * library is licensed under Xiph's BSD license. + * Complete source code of libFLAC as well as the command-line + * encoder and plugins is available and is a useful source of + * examples. + * + * Aside from encoders and decoders, libFLAC provides a powerful + * metadata interface for manipulating metadata in FLAC files. It + * allows the user to add, delete, and modify FLAC metadata blocks + * and it can automatically take advantage of PADDING blocks to avoid + * rewriting the entire FLAC file when changing the size of the + * metadata. + * + * libFLAC usually only requires the standard C library and C math + * library. In particular, threading is not used so there is no + * dependency on a thread library. However, libFLAC does not use + * global variables and should be thread-safe. + * + * libFLAC also supports encoding to and decoding from Ogg FLAC. + * However the metadata editing interfaces currently have limited + * read-only support for Ogg FLAC files. + * + * \section cpp_api FLAC C++ API + * + * The FLAC C++ API is a set of classes that encapsulate the + * structures and functions in libFLAC. They provide slightly more + * functionality with respect to metadata but are otherwise + * equivalent. For the most part, they share the same usage as + * their counterparts in libFLAC, and the FLAC C API documentation + * can be used as a supplement. The public include files + * for the C++ API will be installed in your include area (for + * example /usr/include/FLAC++/...). + * + * libFLAC++ is also licensed under + * Xiph's BSD license. + * + * \section getting_started Getting Started + * + * A good starting point for learning the API is to browse through + * the modules. Modules are logical + * groupings of related functions or classes, which correspond roughly + * to header files or sections of header files. Each module includes a + * detailed description of the general usage of its functions or + * classes. + * + * From there you can go on to look at the documentation of + * individual functions. You can see different views of the individual + * functions through the links in top bar across this page. + * + * If you prefer a more hands-on approach, you can jump right to some + * example code. + * + * \section porting_guide Porting Guide + * + * Starting with FLAC 1.1.3 a \link porting Porting Guide \endlink + * has been introduced which gives detailed instructions on how to + * port your code to newer versions of FLAC. + * + * \section embedded_developers Embedded Developers + * + * libFLAC has grown larger over time as more functionality has been + * included, but much of it may be unnecessary for a particular embedded + * implementation. Unused parts may be pruned by some simple editing of + * src/libFLAC/Makefile.am. In general, the decoders, encoders, and + * metadata interface are all independent from each other. + * + * It is easiest to just describe the dependencies: + * + * - All modules depend on the \link flac_format Format \endlink module. + * - The decoders and encoders depend on the bitbuffer. + * - The decoder is independent of the encoder. The encoder uses the + * decoder because of the verify feature, but this can be removed if + * not needed. + * - Parts of the metadata interface require the stream decoder (but not + * the encoder). + * - Ogg support is selectable through the compile time macro + * \c FLAC__HAS_OGG. + * + * For example, if your application only requires the stream decoder, no + * encoder, and no metadata interface, you can remove the stream encoder + * and the metadata interface, which will greatly reduce the size of the + * library. + * + * Also, there are several places in the libFLAC code with comments marked + * with "OPT:" where a #define can be changed to enable code that might be + * faster on a specific platform. Experimenting with these can yield faster + * binaries. + */ + +/** \defgroup porting Porting Guide for New Versions + * + * This module describes differences in the library interfaces from + * version to version. It assists in the porting of code that uses + * the libraries to newer versions of FLAC. + * + * One simple facility for making porting easier that has been added + * in FLAC 1.1.3 is a set of \c #defines in \c export.h of each + * library's includes (e.g. \c include/FLAC/export.h). The + * \c #defines mirror the libraries' + * libtool version numbers, + * e.g. in libFLAC there are \c FLAC_API_VERSION_CURRENT, + * \c FLAC_API_VERSION_REVISION, and \c FLAC_API_VERSION_AGE. + * These can be used to support multiple versions of an API during the + * transition phase, e.g. + * + * \code + * #if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7 + * legacy code + * #else + * new code + * #endif + * \endcode + * + * The the source will work for multiple versions and the legacy code can + * easily be removed when the transition is complete. + * + * Another available symbol is FLAC_API_SUPPORTS_OGG_FLAC (defined in + * include/FLAC/export.h), which can be used to determine whether or not + * the library has been compiled with support for Ogg FLAC. This is + * simpler than trying to call an Ogg init function and catching the + * error. + */ + +/** \defgroup porting_1_1_2_to_1_1_3 Porting from FLAC 1.1.2 to 1.1.3 + * \ingroup porting + * + * \brief + * This module describes porting from FLAC 1.1.2 to FLAC 1.1.3. + * + * The main change between the APIs in 1.1.2 and 1.1.3 is that they have + * been simplified. First, libOggFLAC has been merged into libFLAC and + * libOggFLAC++ has been merged into libFLAC++. Second, both the three + * decoding layers and three encoding layers have been merged into a + * single stream decoder and stream encoder. That is, the functionality + * of FLAC__SeekableStreamDecoder and FLAC__FileDecoder has been merged + * into FLAC__StreamDecoder, and FLAC__SeekableStreamEncoder and + * FLAC__FileEncoder into FLAC__StreamEncoder. Only the + * FLAC__StreamDecoder and FLAC__StreamEncoder remain. What this means + * is there is now a single API that can be used to encode or decode + * streams to/from native FLAC or Ogg FLAC and the single API can work + * on both seekable and non-seekable streams. + * + * Instead of creating an encoder or decoder of a certain layer, now the + * client will always create a FLAC__StreamEncoder or + * FLAC__StreamDecoder. The old layers are now differentiated by the + * initialization function. For example, for the decoder, + * FLAC__stream_decoder_init() has been replaced by + * FLAC__stream_decoder_init_stream(). This init function takes + * callbacks for the I/O, and the seeking callbacks are optional. This + * allows the client to use the same object for seekable and + * non-seekable streams. For decoding a FLAC file directly, the client + * can use FLAC__stream_decoder_init_file() and pass just a filename + * and fewer callbacks; most of the other callbacks are supplied + * internally. For situations where fopen()ing by filename is not + * possible (e.g. Unicode filenames on Windows) the client can instead + * open the file itself and supply the FILE* to + * FLAC__stream_decoder_init_FILE(). The init functions now returns a + * FLAC__StreamDecoderInitStatus instead of FLAC__StreamDecoderState. + * Since the callbacks and client data are now passed to the init + * function, the FLAC__stream_decoder_set_*_callback() functions and + * FLAC__stream_decoder_set_client_data() are no longer needed. The + * rest of the calls to the decoder are the same as before. + * + * There are counterpart init functions for Ogg FLAC, e.g. + * FLAC__stream_decoder_init_ogg_stream(). All the rest of the calls + * and callbacks are the same as for native FLAC. + * + * As an example, in FLAC 1.1.2 a seekable stream decoder would have + * been set up like so: + * + * \code + * FLAC__SeekableStreamDecoder *decoder = FLAC__seekable_stream_decoder_new(); + * if(decoder == NULL) do_something; + * FLAC__seekable_stream_decoder_set_md5_checking(decoder, true); + * [... other settings ...] + * FLAC__seekable_stream_decoder_set_read_callback(decoder, my_read_callback); + * FLAC__seekable_stream_decoder_set_seek_callback(decoder, my_seek_callback); + * FLAC__seekable_stream_decoder_set_tell_callback(decoder, my_tell_callback); + * FLAC__seekable_stream_decoder_set_length_callback(decoder, my_length_callback); + * FLAC__seekable_stream_decoder_set_eof_callback(decoder, my_eof_callback); + * FLAC__seekable_stream_decoder_set_write_callback(decoder, my_write_callback); + * FLAC__seekable_stream_decoder_set_metadata_callback(decoder, my_metadata_callback); + * FLAC__seekable_stream_decoder_set_error_callback(decoder, my_error_callback); + * FLAC__seekable_stream_decoder_set_client_data(decoder, my_client_data); + * if(FLAC__seekable_stream_decoder_init(decoder) != FLAC__SEEKABLE_STREAM_DECODER_OK) do_something; + * \endcode + * + * In FLAC 1.1.3 it is like this: + * + * \code + * FLAC__StreamDecoder *decoder = FLAC__stream_decoder_new(); + * if(decoder == NULL) do_something; + * FLAC__stream_decoder_set_md5_checking(decoder, true); + * [... other settings ...] + * if(FLAC__stream_decoder_init_stream( + * decoder, + * my_read_callback, + * my_seek_callback, // or NULL + * my_tell_callback, // or NULL + * my_length_callback, // or NULL + * my_eof_callback, // or NULL + * my_write_callback, + * my_metadata_callback, // or NULL + * my_error_callback, + * my_client_data + * ) != FLAC__STREAM_DECODER_INIT_STATUS_OK) do_something; + * \endcode + * + * or you could do; + * + * \code + * [...] + * FILE *file = fopen("somefile.flac","rb"); + * if(file == NULL) do_somthing; + * if(FLAC__stream_decoder_init_FILE( + * decoder, + * file, + * my_write_callback, + * my_metadata_callback, // or NULL + * my_error_callback, + * my_client_data + * ) != FLAC__STREAM_DECODER_INIT_STATUS_OK) do_something; + * \endcode + * + * or just: + * + * \code + * [...] + * if(FLAC__stream_decoder_init_file( + * decoder, + * "somefile.flac", + * my_write_callback, + * my_metadata_callback, // or NULL + * my_error_callback, + * my_client_data + * ) != FLAC__STREAM_DECODER_INIT_STATUS_OK) do_something; + * \endcode + * + * Another small change to the decoder is in how it handles unparseable + * streams. Before, when the decoder found an unparseable stream + * (reserved for when the decoder encounters a stream from a future + * encoder that it can't parse), it changed the state to + * \c FLAC__STREAM_DECODER_UNPARSEABLE_STREAM. Now the decoder instead + * drops sync and calls the error callback with a new error code + * \c FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM. This is + * more robust. If your error callback does not discriminate on the the + * error state, your code does not need to be changed. + * + * The encoder now has a new setting: + * FLAC__stream_encoder_set_apodization(). This is for setting the + * method used to window the data before LPC analysis. You only need to + * add a call to this function if the default is not suitable. There + * are also two new convenience functions that may be useful: + * FLAC__metadata_object_cuesheet_calculate_cddb_id() and + * FLAC__metadata_get_cuesheet(). + * + * The \a bytes parameter to FLAC__StreamDecoderReadCallback, + * FLAC__StreamEncoderReadCallback, and FLAC__StreamEncoderWriteCallback + * is now \c size_t instead of \c unsigned. + */ + +/** \defgroup porting_1_1_3_to_1_1_4 Porting from FLAC 1.1.3 to 1.1.4 + * \ingroup porting + * + * \brief + * This module describes porting from FLAC 1.1.3 to FLAC 1.1.4. + * + * There were no changes to any of the interfaces from 1.1.3 to 1.1.4. + * There was a slight change in the implementation of + * FLAC__stream_encoder_set_metadata(); the function now makes a copy + * of the \a metadata array of pointers so the client no longer needs + * to maintain it after the call. The objects themselves that are + * pointed to by the array are still not copied though and must be + * maintained until the call to FLAC__stream_encoder_finish(). + */ + +/** \defgroup porting_1_1_4_to_1_2_0 Porting from FLAC 1.1.4 to 1.2.0 + * \ingroup porting + * + * \brief + * This module describes porting from FLAC 1.1.4 to FLAC 1.2.0. + * + * There were only very minor changes to the interfaces from 1.1.4 to 1.2.0. + * In libFLAC, \c FLAC__format_sample_rate_is_subset() was added. + * In libFLAC++, \c FLAC::Decoder::Stream::get_decode_position() was added. + * + * Finally, value of the constant \c FLAC__FRAME_HEADER_RESERVED_LEN + * has changed to reflect the conversion of one of the reserved bits + * into active use. It used to be \c 2 and now is \c 1. However the + * FLAC frame header length has not changed, so to skip the proper + * number of bits, use \c FLAC__FRAME_HEADER_RESERVED_LEN + + * \c FLAC__FRAME_HEADER_BLOCKING_STRATEGY_LEN + */ + +/** \defgroup flac FLAC C API + * + * The FLAC C API is the interface to libFLAC, a set of structures + * describing the components of FLAC streams, and functions for + * encoding and decoding streams, as well as manipulating FLAC + * metadata in files. + * + * You should start with the format components as all other modules + * are dependent on it. + */ + +#endif +/********* End of inlined file: all.h *********/ + +/********* Start of inlined file: bitmath.c *********/ + +/********* Start of inlined file: juce_FlacHeader.h *********/ +// This file is included at the start of each FLAC .c file, just to do a few housekeeping +// tasks.. + +#define VERSION "1.2.1" + +#define FLAC__NO_DLL 1 + +#ifdef _MSC_VER + #pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4312) +#endif + +#if ! (defined (_WIN32) || defined (_WIN64) || defined (LINUX)) + #define FLAC__SYS_DARWIN 1 +#endif +/********* End of inlined file: juce_FlacHeader.h *********/ + +#if JUCE_USE_FLAC + +#if HAVE_CONFIG_H +# include +#endif + +/********* Start of inlined file: bitmath.h *********/ +#ifndef FLAC__PRIVATE__BITMATH_H +#define FLAC__PRIVATE__BITMATH_H + +unsigned FLAC__bitmath_ilog2(FLAC__uint32 v); +unsigned FLAC__bitmath_ilog2_wide(FLAC__uint64 v); +unsigned FLAC__bitmath_silog2(int v); +unsigned FLAC__bitmath_silog2_wide(FLAC__int64 v); + +#endif +/********* End of inlined file: bitmath.h *********/ + +/* An example of what FLAC__bitmath_ilog2() computes: + * + * ilog2( 0) = assertion failure + * ilog2( 1) = 0 + * ilog2( 2) = 1 + * ilog2( 3) = 1 + * ilog2( 4) = 2 + * ilog2( 5) = 2 + * ilog2( 6) = 2 + * ilog2( 7) = 2 + * ilog2( 8) = 3 + * ilog2( 9) = 3 + * ilog2(10) = 3 + * ilog2(11) = 3 + * ilog2(12) = 3 + * ilog2(13) = 3 + * ilog2(14) = 3 + * ilog2(15) = 3 + * ilog2(16) = 4 + * ilog2(17) = 4 + * ilog2(18) = 4 + */ +unsigned FLAC__bitmath_ilog2(FLAC__uint32 v) +{ + unsigned l = 0; + FLAC__ASSERT(v > 0); + while(v >>= 1) + l++; + return l; +} + +unsigned FLAC__bitmath_ilog2_wide(FLAC__uint64 v) +{ + unsigned l = 0; + FLAC__ASSERT(v > 0); + while(v >>= 1) + l++; + return l; +} + +/* An example of what FLAC__bitmath_silog2() computes: + * + * silog2(-10) = 5 + * silog2(- 9) = 5 + * silog2(- 8) = 4 + * silog2(- 7) = 4 + * silog2(- 6) = 4 + * silog2(- 5) = 4 + * silog2(- 4) = 3 + * silog2(- 3) = 3 + * silog2(- 2) = 2 + * silog2(- 1) = 2 + * silog2( 0) = 0 + * silog2( 1) = 2 + * silog2( 2) = 3 + * silog2( 3) = 3 + * silog2( 4) = 4 + * silog2( 5) = 4 + * silog2( 6) = 4 + * silog2( 7) = 4 + * silog2( 8) = 5 + * silog2( 9) = 5 + * silog2( 10) = 5 + */ +unsigned FLAC__bitmath_silog2(int v) +{ + while(1) { + if(v == 0) { + return 0; + } + else if(v > 0) { + unsigned l = 0; + while(v) { + l++; + v >>= 1; + } + return l+1; + } + else if(v == -1) { + return 2; + } + else { + v++; + v = -v; + } + } +} + +unsigned FLAC__bitmath_silog2_wide(FLAC__int64 v) +{ + while(1) { + if(v == 0) { + return 0; + } + else if(v > 0) { + unsigned l = 0; + while(v) { + l++; + v >>= 1; + } + return l+1; + } + else if(v == -1) { + return 2; + } + else { + v++; + v = -v; + } + } +} + +#endif +/********* End of inlined file: bitmath.c *********/ + +/********* Start of inlined file: bitreader.c *********/ + +/********* Start of inlined file: juce_FlacHeader.h *********/ +// This file is included at the start of each FLAC .c file, just to do a few housekeeping +// tasks.. + +#define VERSION "1.2.1" + +#define FLAC__NO_DLL 1 + +#ifdef _MSC_VER + #pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4312) +#endif + +#if ! (defined (_WIN32) || defined (_WIN64) || defined (LINUX)) + #define FLAC__SYS_DARWIN 1 +#endif +/********* End of inlined file: juce_FlacHeader.h *********/ + +#if JUCE_USE_FLAC + +#if HAVE_CONFIG_H +# include +#endif + +#include /* for malloc() */ +#include /* for memcpy(), memset() */ +#ifdef _MSC_VER +#include /* for ntohl() */ +#elif defined FLAC__SYS_DARWIN +#include /* for ntohl() */ +#elif defined __MINGW32__ +#include /* for ntohl() */ +#else +#include /* for ntohl() */ +#endif + +/********* Start of inlined file: bitreader.h *********/ +#ifndef FLAC__PRIVATE__BITREADER_H +#define FLAC__PRIVATE__BITREADER_H + +#include /* for FILE */ + +/********* Start of inlined file: cpu.h *********/ +#ifndef FLAC__PRIVATE__CPU_H +#define FLAC__PRIVATE__CPU_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +typedef enum { + FLAC__CPUINFO_TYPE_IA32, + FLAC__CPUINFO_TYPE_PPC, + FLAC__CPUINFO_TYPE_UNKNOWN +} FLAC__CPUInfo_Type; + +typedef struct { + FLAC__bool cpuid; + FLAC__bool bswap; + FLAC__bool cmov; + FLAC__bool mmx; + FLAC__bool fxsr; + FLAC__bool sse; + FLAC__bool sse2; + FLAC__bool sse3; + FLAC__bool ssse3; + FLAC__bool _3dnow; + FLAC__bool ext3dnow; + FLAC__bool extmmx; +} FLAC__CPUInfo_IA32; + +typedef struct { + FLAC__bool altivec; + FLAC__bool ppc64; +} FLAC__CPUInfo_PPC; + +typedef struct { + FLAC__bool use_asm; + FLAC__CPUInfo_Type type; + union { + FLAC__CPUInfo_IA32 ia32; + FLAC__CPUInfo_PPC ppc; + } data; +} FLAC__CPUInfo; + +void FLAC__cpu_info(FLAC__CPUInfo *info); + +#ifndef FLAC__NO_ASM +#ifdef FLAC__CPU_IA32 +#ifdef FLAC__HAS_NASM +FLAC__uint32 FLAC__cpu_have_cpuid_asm_ia32(void); +void FLAC__cpu_info_asm_ia32(FLAC__uint32 *flags_edx, FLAC__uint32 *flags_ecx); +FLAC__uint32 FLAC__cpu_info_extended_amd_asm_ia32(void); +#endif +#endif +#endif + +#endif +/********* End of inlined file: cpu.h *********/ + +/* + * opaque structure definition + */ +struct FLAC__BitReader; +typedef struct FLAC__BitReader FLAC__BitReader; + +typedef FLAC__bool (*FLAC__BitReaderReadCallback)(FLAC__byte buffer[], size_t *bytes, void *client_data); + +/* + * construction, deletion, initialization, etc functions + */ +FLAC__BitReader *FLAC__bitreader_new(void); +void FLAC__bitreader_delete(FLAC__BitReader *br); +FLAC__bool FLAC__bitreader_init(FLAC__BitReader *br, FLAC__CPUInfo cpu, FLAC__BitReaderReadCallback rcb, void *cd); +void FLAC__bitreader_free(FLAC__BitReader *br); /* does not 'free(br)' */ +FLAC__bool FLAC__bitreader_clear(FLAC__BitReader *br); +void FLAC__bitreader_dump(const FLAC__BitReader *br, FILE *out); + +/* + * CRC functions + */ +void FLAC__bitreader_reset_read_crc16(FLAC__BitReader *br, FLAC__uint16 seed); +FLAC__uint16 FLAC__bitreader_get_read_crc16(FLAC__BitReader *br); + +/* + * info functions + */ +FLAC__bool FLAC__bitreader_is_consumed_byte_aligned(const FLAC__BitReader *br); +unsigned FLAC__bitreader_bits_left_for_byte_alignment(const FLAC__BitReader *br); +unsigned FLAC__bitreader_get_input_bits_unconsumed(const FLAC__BitReader *br); + +/* + * read functions + */ + +FLAC__bool FLAC__bitreader_read_raw_uint32(FLAC__BitReader *br, FLAC__uint32 *val, unsigned bits); +FLAC__bool FLAC__bitreader_read_raw_int32(FLAC__BitReader *br, FLAC__int32 *val, unsigned bits); +FLAC__bool FLAC__bitreader_read_raw_uint64(FLAC__BitReader *br, FLAC__uint64 *val, unsigned bits); +FLAC__bool FLAC__bitreader_read_uint32_little_endian(FLAC__BitReader *br, FLAC__uint32 *val); /*only for bits=32*/ +FLAC__bool FLAC__bitreader_skip_bits_no_crc(FLAC__BitReader *br, unsigned bits); /* WATCHOUT: does not CRC the skipped data! */ /*@@@@ add to unit tests */ +FLAC__bool FLAC__bitreader_skip_byte_block_aligned_no_crc(FLAC__BitReader *br, unsigned nvals); /* WATCHOUT: does not CRC the read data! */ +FLAC__bool FLAC__bitreader_read_byte_block_aligned_no_crc(FLAC__BitReader *br, FLAC__byte *val, unsigned nvals); /* WATCHOUT: does not CRC the read data! */ +FLAC__bool FLAC__bitreader_read_unary_unsigned(FLAC__BitReader *br, unsigned *val); +FLAC__bool FLAC__bitreader_read_rice_signed(FLAC__BitReader *br, int *val, unsigned parameter); +FLAC__bool FLAC__bitreader_read_rice_signed_block(FLAC__BitReader *br, int vals[], unsigned nvals, unsigned parameter); +#ifndef FLAC__NO_ASM +# ifdef FLAC__CPU_IA32 +# ifdef FLAC__HAS_NASM +FLAC__bool FLAC__bitreader_read_rice_signed_block_asm_ia32_bswap(FLAC__BitReader *br, int vals[], unsigned nvals, unsigned parameter); +# endif +# endif +#endif +#if 0 /* UNUSED */ +FLAC__bool FLAC__bitreader_read_golomb_signed(FLAC__BitReader *br, int *val, unsigned parameter); +FLAC__bool FLAC__bitreader_read_golomb_unsigned(FLAC__BitReader *br, unsigned *val, unsigned parameter); +#endif +FLAC__bool FLAC__bitreader_read_utf8_uint32(FLAC__BitReader *br, FLAC__uint32 *val, FLAC__byte *raw, unsigned *rawlen); +FLAC__bool FLAC__bitreader_read_utf8_uint64(FLAC__BitReader *br, FLAC__uint64 *val, FLAC__byte *raw, unsigned *rawlen); + +FLAC__bool bitreader_read_from_client_(FLAC__BitReader *br); +#endif +/********* End of inlined file: bitreader.h *********/ + +/********* Start of inlined file: crc.h *********/ +#ifndef FLAC__PRIVATE__CRC_H +#define FLAC__PRIVATE__CRC_H + +/* 8 bit CRC generator, MSB shifted first +** polynomial = x^8 + x^2 + x^1 + x^0 +** init = 0 +*/ +extern FLAC__byte const FLAC__crc8_table[256]; +#define FLAC__CRC8_UPDATE(data, crc) (crc) = FLAC__crc8_table[(crc) ^ (data)]; +void FLAC__crc8_update(const FLAC__byte data, FLAC__uint8 *crc); +void FLAC__crc8_update_block(const FLAC__byte *data, unsigned len, FLAC__uint8 *crc); +FLAC__uint8 FLAC__crc8(const FLAC__byte *data, unsigned len); + +/* 16 bit CRC generator, MSB shifted first +** polynomial = x^16 + x^15 + x^2 + x^0 +** init = 0 +*/ +extern unsigned FLAC__crc16_table[256]; + +#define FLAC__CRC16_UPDATE(data, crc) (((((crc)<<8) & 0xffff) ^ FLAC__crc16_table[((crc)>>8) ^ (data)])) +/* this alternate may be faster on some systems/compilers */ +#if 0 +#define FLAC__CRC16_UPDATE(data, crc) ((((crc)<<8) ^ FLAC__crc16_table[((crc)>>8) ^ (data)]) & 0xffff) +#endif + +unsigned FLAC__crc16(const FLAC__byte *data, unsigned len); + +#endif +/********* End of inlined file: crc.h *********/ + +/* Things should be fastest when this matches the machine word size */ +/* WATCHOUT: if you change this you must also change the following #defines down to COUNT_ZERO_MSBS below to match */ +/* WATCHOUT: there are a few places where the code will not work unless brword is >= 32 bits wide */ +/* also, some sections currently only have fast versions for 4 or 8 bytes per word */ +typedef FLAC__uint32 brword; +#define FLAC__BYTES_PER_WORD 4 +#define FLAC__BITS_PER_WORD 32 +#define FLAC__WORD_ALL_ONES ((FLAC__uint32)0xffffffff) +/* SWAP_BE_WORD_TO_HOST swaps bytes in a brword (which is always big-endian) if necessary to match host byte order */ +#if WORDS_BIGENDIAN +#define SWAP_BE_WORD_TO_HOST(x) (x) +#else +#if defined (_MSC_VER) && defined (_X86_) +#define SWAP_BE_WORD_TO_HOST(x) local_swap32_(x) +#else +#define SWAP_BE_WORD_TO_HOST(x) ntohl(x) +#endif +#endif +/* counts the # of zero MSBs in a word */ +#define COUNT_ZERO_MSBS(word) ( \ + (word) <= 0xffff ? \ + ( (word) <= 0xff? byte_to_unary_table[word] + 24 : byte_to_unary_table[(word) >> 8] + 16 ) : \ + ( (word) <= 0xffffff? byte_to_unary_table[word >> 16] + 8 : byte_to_unary_table[(word) >> 24] ) \ +) +/* this alternate might be slightly faster on some systems/compilers: */ +#define COUNT_ZERO_MSBS2(word) ( (word) <= 0xff ? byte_to_unary_table[word] + 24 : ((word) <= 0xffff ? byte_to_unary_table[(word) >> 8] + 16 : ((word) <= 0xffffff ? byte_to_unary_table[(word) >> 16] + 8 : byte_to_unary_table[(word) >> 24])) ) + +/* + * This should be at least twice as large as the largest number of words + * required to represent any 'number' (in any encoding) you are going to + * read. With FLAC this is on the order of maybe a few hundred bits. + * If the buffer is smaller than that, the decoder won't be able to read + * in a whole number that is in a variable length encoding (e.g. Rice). + * But to be practical it should be at least 1K bytes. + * + * Increase this number to decrease the number of read callbacks, at the + * expense of using more memory. Or decrease for the reverse effect, + * keeping in mind the limit from the first paragraph. The optimal size + * also depends on the CPU cache size and other factors; some twiddling + * may be necessary to squeeze out the best performance. + */ +static const unsigned FLAC__BITREADER_DEFAULT_CAPACITY = 65536u / FLAC__BITS_PER_WORD; /* in words */ + +static const unsigned char byte_to_unary_table[] = { + 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +#ifdef min +#undef min +#endif +#define min(x,y) ((x)<(y)?(x):(y)) +#ifdef max +#undef max +#endif +#define max(x,y) ((x)>(y)?(x):(y)) + +/* adjust for compilers that can't understand using LLU suffix for uint64_t literals */ +#ifdef _MSC_VER +#define FLAC__U64L(x) x +#else +#define FLAC__U64L(x) x##LLU +#endif + +#ifndef FLaC__INLINE +#define FLaC__INLINE +#endif + +/* WATCHOUT: assembly routines rely on the order in which these fields are declared */ +struct FLAC__BitReader { + /* any partially-consumed word at the head will stay right-justified as bits are consumed from the left */ + /* any incomplete word at the tail will be left-justified, and bytes from the read callback are added on the right */ + brword *buffer; + unsigned capacity; /* in words */ + unsigned words; /* # of completed words in buffer */ + unsigned bytes; /* # of bytes in incomplete word at buffer[words] */ + unsigned consumed_words; /* #words ... */ + unsigned consumed_bits; /* ... + (#bits of head word) already consumed from the front of buffer */ + unsigned read_crc16; /* the running frame CRC */ + unsigned crc16_align; /* the number of bits in the current consumed word that should not be CRC'd */ + FLAC__BitReaderReadCallback read_callback; + void *client_data; + FLAC__CPUInfo cpu_info; +}; + +static FLaC__INLINE void crc16_update_word_(FLAC__BitReader *br, brword word) +{ + register unsigned crc = br->read_crc16; +#if FLAC__BYTES_PER_WORD == 4 + switch(br->crc16_align) { + case 0: crc = FLAC__CRC16_UPDATE((unsigned)(word >> 24), crc); + case 8: crc = FLAC__CRC16_UPDATE((unsigned)((word >> 16) & 0xff), crc); + case 16: crc = FLAC__CRC16_UPDATE((unsigned)((word >> 8) & 0xff), crc); + case 24: br->read_crc16 = FLAC__CRC16_UPDATE((unsigned)(word & 0xff), crc); + } +#elif FLAC__BYTES_PER_WORD == 8 + switch(br->crc16_align) { + case 0: crc = FLAC__CRC16_UPDATE((unsigned)(word >> 56), crc); + case 8: crc = FLAC__CRC16_UPDATE((unsigned)((word >> 48) & 0xff), crc); + case 16: crc = FLAC__CRC16_UPDATE((unsigned)((word >> 40) & 0xff), crc); + case 24: crc = FLAC__CRC16_UPDATE((unsigned)((word >> 32) & 0xff), crc); + case 32: crc = FLAC__CRC16_UPDATE((unsigned)((word >> 24) & 0xff), crc); + case 40: crc = FLAC__CRC16_UPDATE((unsigned)((word >> 16) & 0xff), crc); + case 48: crc = FLAC__CRC16_UPDATE((unsigned)((word >> 8) & 0xff), crc); + case 56: br->read_crc16 = FLAC__CRC16_UPDATE((unsigned)(word & 0xff), crc); + } +#else + for( ; br->crc16_align < FLAC__BITS_PER_WORD; br->crc16_align += 8) + crc = FLAC__CRC16_UPDATE((unsigned)((word >> (FLAC__BITS_PER_WORD-8-br->crc16_align)) & 0xff), crc); + br->read_crc16 = crc; +#endif + br->crc16_align = 0; +} + +/* would be static except it needs to be called by asm routines */ +FLAC__bool bitreader_read_from_client_(FLAC__BitReader *br) +{ + unsigned start, end; + size_t bytes; + FLAC__byte *target; + + /* first shift the unconsumed buffer data toward the front as much as possible */ + if(br->consumed_words > 0) { + start = br->consumed_words; + end = br->words + (br->bytes? 1:0); + memmove(br->buffer, br->buffer+start, FLAC__BYTES_PER_WORD * (end - start)); + + br->words -= start; + br->consumed_words = 0; + } + + /* + * set the target for reading, taking into account word alignment and endianness + */ + bytes = (br->capacity - br->words) * FLAC__BYTES_PER_WORD - br->bytes; + if(bytes == 0) + return false; /* no space left, buffer is too small; see note for FLAC__BITREADER_DEFAULT_CAPACITY */ + target = ((FLAC__byte*)(br->buffer+br->words)) + br->bytes; + + /* before reading, if the existing reader looks like this (say brword is 32 bits wide) + * bitstream : 11 22 33 44 55 br->words=1 br->bytes=1 (partial tail word is left-justified) + * buffer[BE]: 11 22 33 44 55 ?? ?? ?? (shown layed out as bytes sequentially in memory) + * buffer[LE]: 44 33 22 11 ?? ?? ?? 55 (?? being don't-care) + * ^^-------target, bytes=3 + * on LE machines, have to byteswap the odd tail word so nothing is + * overwritten: + */ +#if WORDS_BIGENDIAN +#else + if(br->bytes) + br->buffer[br->words] = SWAP_BE_WORD_TO_HOST(br->buffer[br->words]); +#endif + + /* now it looks like: + * bitstream : 11 22 33 44 55 br->words=1 br->bytes=1 + * buffer[BE]: 11 22 33 44 55 ?? ?? ?? + * buffer[LE]: 44 33 22 11 55 ?? ?? ?? + * ^^-------target, bytes=3 + */ + + /* read in the data; note that the callback may return a smaller number of bytes */ + if(!br->read_callback(target, &bytes, br->client_data)) + return false; + + /* after reading bytes 66 77 88 99 AA BB CC DD EE FF from the client: + * bitstream : 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF + * buffer[BE]: 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF ?? + * buffer[LE]: 44 33 22 11 55 66 77 88 99 AA BB CC DD EE FF ?? + * now have to byteswap on LE machines: + */ +#if WORDS_BIGENDIAN +#else + end = (br->words*FLAC__BYTES_PER_WORD + br->bytes + bytes + (FLAC__BYTES_PER_WORD-1)) / FLAC__BYTES_PER_WORD; +# if defined(_MSC_VER) && defined (_X86_) && (FLAC__BYTES_PER_WORD == 4) + if(br->cpu_info.type == FLAC__CPUINFO_TYPE_IA32 && br->cpu_info.data.ia32.bswap) { + start = br->words; + local_swap32_block_(br->buffer + start, end - start); + } + else +# endif + for(start = br->words; start < end; start++) + br->buffer[start] = SWAP_BE_WORD_TO_HOST(br->buffer[start]); +#endif + + /* now it looks like: + * bitstream : 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF + * buffer[BE]: 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF ?? + * buffer[LE]: 44 33 22 11 88 77 66 55 CC BB AA 99 ?? FF EE DD + * finally we'll update the reader values: + */ + end = br->words*FLAC__BYTES_PER_WORD + br->bytes + bytes; + br->words = end / FLAC__BYTES_PER_WORD; + br->bytes = end % FLAC__BYTES_PER_WORD; + + return true; +} + +/*********************************************************************** + * + * Class constructor/destructor + * + ***********************************************************************/ + +FLAC__BitReader *FLAC__bitreader_new(void) +{ + FLAC__BitReader *br = (FLAC__BitReader*)calloc(1, sizeof(FLAC__BitReader)); + + /* calloc() implies: + memset(br, 0, sizeof(FLAC__BitReader)); + br->buffer = 0; + br->capacity = 0; + br->words = br->bytes = 0; + br->consumed_words = br->consumed_bits = 0; + br->read_callback = 0; + br->client_data = 0; + */ + return br; +} + +void FLAC__bitreader_delete(FLAC__BitReader *br) +{ + FLAC__ASSERT(0 != br); + + FLAC__bitreader_free(br); + free(br); +} + +/*********************************************************************** + * + * Public class methods + * + ***********************************************************************/ + +FLAC__bool FLAC__bitreader_init(FLAC__BitReader *br, FLAC__CPUInfo cpu, FLAC__BitReaderReadCallback rcb, void *cd) +{ + FLAC__ASSERT(0 != br); + + br->words = br->bytes = 0; + br->consumed_words = br->consumed_bits = 0; + br->capacity = FLAC__BITREADER_DEFAULT_CAPACITY; + br->buffer = (brword*)malloc(sizeof(brword) * br->capacity); + if(br->buffer == 0) + return false; + br->read_callback = rcb; + br->client_data = cd; + br->cpu_info = cpu; + + return true; +} + +void FLAC__bitreader_free(FLAC__BitReader *br) +{ + FLAC__ASSERT(0 != br); + + if(0 != br->buffer) + free(br->buffer); + br->buffer = 0; + br->capacity = 0; + br->words = br->bytes = 0; + br->consumed_words = br->consumed_bits = 0; + br->read_callback = 0; + br->client_data = 0; +} + +FLAC__bool FLAC__bitreader_clear(FLAC__BitReader *br) +{ + br->words = br->bytes = 0; + br->consumed_words = br->consumed_bits = 0; + return true; +} + +void FLAC__bitreader_dump(const FLAC__BitReader *br, FILE *out) +{ + unsigned i, j; + if(br == 0) { + fprintf(out, "bitreader is NULL\n"); + } + else { + fprintf(out, "bitreader: capacity=%u words=%u bytes=%u consumed: words=%u, bits=%u\n", br->capacity, br->words, br->bytes, br->consumed_words, br->consumed_bits); + + for(i = 0; i < br->words; i++) { + fprintf(out, "%08X: ", i); + for(j = 0; j < FLAC__BITS_PER_WORD; j++) + if(i < br->consumed_words || (i == br->consumed_words && j < br->consumed_bits)) + fprintf(out, "."); + else + fprintf(out, "%01u", br->buffer[i] & (1 << (FLAC__BITS_PER_WORD-j-1)) ? 1:0); + fprintf(out, "\n"); + } + if(br->bytes > 0) { + fprintf(out, "%08X: ", i); + for(j = 0; j < br->bytes*8; j++) + if(i < br->consumed_words || (i == br->consumed_words && j < br->consumed_bits)) + fprintf(out, "."); + else + fprintf(out, "%01u", br->buffer[i] & (1 << (br->bytes*8-j-1)) ? 1:0); + fprintf(out, "\n"); + } + } +} + +void FLAC__bitreader_reset_read_crc16(FLAC__BitReader *br, FLAC__uint16 seed) +{ + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + FLAC__ASSERT((br->consumed_bits & 7) == 0); + + br->read_crc16 = (unsigned)seed; + br->crc16_align = br->consumed_bits; +} + +FLAC__uint16 FLAC__bitreader_get_read_crc16(FLAC__BitReader *br) +{ + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + FLAC__ASSERT((br->consumed_bits & 7) == 0); + FLAC__ASSERT(br->crc16_align <= br->consumed_bits); + + /* CRC any tail bytes in a partially-consumed word */ + if(br->consumed_bits) { + const brword tail = br->buffer[br->consumed_words]; + for( ; br->crc16_align < br->consumed_bits; br->crc16_align += 8) + br->read_crc16 = FLAC__CRC16_UPDATE((unsigned)((tail >> (FLAC__BITS_PER_WORD-8-br->crc16_align)) & 0xff), br->read_crc16); + } + return br->read_crc16; +} + +FLaC__INLINE FLAC__bool FLAC__bitreader_is_consumed_byte_aligned(const FLAC__BitReader *br) +{ + return ((br->consumed_bits & 7) == 0); +} + +FLaC__INLINE unsigned FLAC__bitreader_bits_left_for_byte_alignment(const FLAC__BitReader *br) +{ + return 8 - (br->consumed_bits & 7); +} + +FLaC__INLINE unsigned FLAC__bitreader_get_input_bits_unconsumed(const FLAC__BitReader *br) +{ + return (br->words-br->consumed_words)*FLAC__BITS_PER_WORD + br->bytes*8 - br->consumed_bits; +} + +FLaC__INLINE FLAC__bool FLAC__bitreader_read_raw_uint32(FLAC__BitReader *br, FLAC__uint32 *val, unsigned bits) +{ + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + + FLAC__ASSERT(bits <= 32); + FLAC__ASSERT((br->capacity*FLAC__BITS_PER_WORD) * 2 >= bits); + FLAC__ASSERT(br->consumed_words <= br->words); + + /* WATCHOUT: code does not work with <32bit words; we can make things much faster with this assertion */ + FLAC__ASSERT(FLAC__BITS_PER_WORD >= 32); + + if(bits == 0) { /* OPT: investigate if this can ever happen, maybe change to assertion */ + *val = 0; + return true; + } + + while((br->words-br->consumed_words)*FLAC__BITS_PER_WORD + br->bytes*8 - br->consumed_bits < bits) { + if(!bitreader_read_from_client_(br)) + return false; + } + if(br->consumed_words < br->words) { /* if we've not consumed up to a partial tail word... */ + /* OPT: taking out the consumed_bits==0 "else" case below might make things faster if less code allows the compiler to inline this function */ + if(br->consumed_bits) { + /* this also works when consumed_bits==0, it's just a little slower than necessary for that case */ + const unsigned n = FLAC__BITS_PER_WORD - br->consumed_bits; + const brword word = br->buffer[br->consumed_words]; + if(bits < n) { + *val = (word & (FLAC__WORD_ALL_ONES >> br->consumed_bits)) >> (n-bits); + br->consumed_bits += bits; + return true; + } + *val = word & (FLAC__WORD_ALL_ONES >> br->consumed_bits); + bits -= n; + crc16_update_word_(br, word); + br->consumed_words++; + br->consumed_bits = 0; + if(bits) { /* if there are still bits left to read, there have to be less than 32 so they will all be in the next word */ + *val <<= bits; + *val |= (br->buffer[br->consumed_words] >> (FLAC__BITS_PER_WORD-bits)); + br->consumed_bits = bits; + } + return true; + } + else { + const brword word = br->buffer[br->consumed_words]; + if(bits < FLAC__BITS_PER_WORD) { + *val = word >> (FLAC__BITS_PER_WORD-bits); + br->consumed_bits = bits; + return true; + } + /* at this point 'bits' must be == FLAC__BITS_PER_WORD; because of previous assertions, it can't be larger */ + *val = word; + crc16_update_word_(br, word); + br->consumed_words++; + return true; + } + } + else { + /* in this case we're starting our read at a partial tail word; + * the reader has guaranteed that we have at least 'bits' bits + * available to read, which makes this case simpler. + */ + /* OPT: taking out the consumed_bits==0 "else" case below might make things faster if less code allows the compiler to inline this function */ + if(br->consumed_bits) { + /* this also works when consumed_bits==0, it's just a little slower than necessary for that case */ + FLAC__ASSERT(br->consumed_bits + bits <= br->bytes*8); + *val = (br->buffer[br->consumed_words] & (FLAC__WORD_ALL_ONES >> br->consumed_bits)) >> (FLAC__BITS_PER_WORD-br->consumed_bits-bits); + br->consumed_bits += bits; + return true; + } + else { + *val = br->buffer[br->consumed_words] >> (FLAC__BITS_PER_WORD-bits); + br->consumed_bits += bits; + return true; + } + } +} + +FLAC__bool FLAC__bitreader_read_raw_int32(FLAC__BitReader *br, FLAC__int32 *val, unsigned bits) +{ + /* OPT: inline raw uint32 code here, or make into a macro if possible in the .h file */ + if(!FLAC__bitreader_read_raw_uint32(br, (FLAC__uint32*)val, bits)) + return false; + /* sign-extend: */ + *val <<= (32-bits); + *val >>= (32-bits); + return true; +} + +FLAC__bool FLAC__bitreader_read_raw_uint64(FLAC__BitReader *br, FLAC__uint64 *val, unsigned bits) +{ + FLAC__uint32 hi, lo; + + if(bits > 32) { + if(!FLAC__bitreader_read_raw_uint32(br, &hi, bits-32)) + return false; + if(!FLAC__bitreader_read_raw_uint32(br, &lo, 32)) + return false; + *val = hi; + *val <<= 32; + *val |= lo; + } + else { + if(!FLAC__bitreader_read_raw_uint32(br, &lo, bits)) + return false; + *val = lo; + } + return true; +} + +FLaC__INLINE FLAC__bool FLAC__bitreader_read_uint32_little_endian(FLAC__BitReader *br, FLAC__uint32 *val) +{ + FLAC__uint32 x8, x32 = 0; + + /* this doesn't need to be that fast as currently it is only used for vorbis comments */ + + if(!FLAC__bitreader_read_raw_uint32(br, &x32, 8)) + return false; + + if(!FLAC__bitreader_read_raw_uint32(br, &x8, 8)) + return false; + x32 |= (x8 << 8); + + if(!FLAC__bitreader_read_raw_uint32(br, &x8, 8)) + return false; + x32 |= (x8 << 16); + + if(!FLAC__bitreader_read_raw_uint32(br, &x8, 8)) + return false; + x32 |= (x8 << 24); + + *val = x32; + return true; +} + +FLAC__bool FLAC__bitreader_skip_bits_no_crc(FLAC__BitReader *br, unsigned bits) +{ + /* + * OPT: a faster implementation is possible but probably not that useful + * since this is only called a couple of times in the metadata readers. + */ + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + + if(bits > 0) { + const unsigned n = br->consumed_bits & 7; + unsigned m; + FLAC__uint32 x; + + if(n != 0) { + m = min(8-n, bits); + if(!FLAC__bitreader_read_raw_uint32(br, &x, m)) + return false; + bits -= m; + } + m = bits / 8; + if(m > 0) { + if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(br, m)) + return false; + bits %= 8; + } + if(bits > 0) { + if(!FLAC__bitreader_read_raw_uint32(br, &x, bits)) + return false; + } + } + + return true; +} + +FLAC__bool FLAC__bitreader_skip_byte_block_aligned_no_crc(FLAC__BitReader *br, unsigned nvals) +{ + FLAC__uint32 x; + + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(br)); + + /* step 1: skip over partial head word to get word aligned */ + while(nvals && br->consumed_bits) { /* i.e. run until we read 'nvals' bytes or we hit the end of the head word */ + if(!FLAC__bitreader_read_raw_uint32(br, &x, 8)) + return false; + nvals--; + } + if(0 == nvals) + return true; + /* step 2: skip whole words in chunks */ + while(nvals >= FLAC__BYTES_PER_WORD) { + if(br->consumed_words < br->words) { + br->consumed_words++; + nvals -= FLAC__BYTES_PER_WORD; + } + else if(!bitreader_read_from_client_(br)) + return false; + } + /* step 3: skip any remainder from partial tail bytes */ + while(nvals) { + if(!FLAC__bitreader_read_raw_uint32(br, &x, 8)) + return false; + nvals--; + } + + return true; +} + +FLAC__bool FLAC__bitreader_read_byte_block_aligned_no_crc(FLAC__BitReader *br, FLAC__byte *val, unsigned nvals) +{ + FLAC__uint32 x; + + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(br)); + + /* step 1: read from partial head word to get word aligned */ + while(nvals && br->consumed_bits) { /* i.e. run until we read 'nvals' bytes or we hit the end of the head word */ + if(!FLAC__bitreader_read_raw_uint32(br, &x, 8)) + return false; + *val++ = (FLAC__byte)x; + nvals--; + } + if(0 == nvals) + return true; + /* step 2: read whole words in chunks */ + while(nvals >= FLAC__BYTES_PER_WORD) { + if(br->consumed_words < br->words) { + const brword word = br->buffer[br->consumed_words++]; +#if FLAC__BYTES_PER_WORD == 4 + val[0] = (FLAC__byte)(word >> 24); + val[1] = (FLAC__byte)(word >> 16); + val[2] = (FLAC__byte)(word >> 8); + val[3] = (FLAC__byte)word; +#elif FLAC__BYTES_PER_WORD == 8 + val[0] = (FLAC__byte)(word >> 56); + val[1] = (FLAC__byte)(word >> 48); + val[2] = (FLAC__byte)(word >> 40); + val[3] = (FLAC__byte)(word >> 32); + val[4] = (FLAC__byte)(word >> 24); + val[5] = (FLAC__byte)(word >> 16); + val[6] = (FLAC__byte)(word >> 8); + val[7] = (FLAC__byte)word; +#else + for(x = 0; x < FLAC__BYTES_PER_WORD; x++) + val[x] = (FLAC__byte)(word >> (8*(FLAC__BYTES_PER_WORD-x-1))); +#endif + val += FLAC__BYTES_PER_WORD; + nvals -= FLAC__BYTES_PER_WORD; + } + else if(!bitreader_read_from_client_(br)) + return false; + } + /* step 3: read any remainder from partial tail bytes */ + while(nvals) { + if(!FLAC__bitreader_read_raw_uint32(br, &x, 8)) + return false; + *val++ = (FLAC__byte)x; + nvals--; + } + + return true; +} + +FLaC__INLINE FLAC__bool FLAC__bitreader_read_unary_unsigned(FLAC__BitReader *br, unsigned *val) +#if 0 /* slow but readable version */ +{ + unsigned bit; + + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + + *val = 0; + while(1) { + if(!FLAC__bitreader_read_bit(br, &bit)) + return false; + if(bit) + break; + else + *val++; + } + return true; +} +#else +{ + unsigned i; + + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + + *val = 0; + while(1) { + while(br->consumed_words < br->words) { /* if we've not consumed up to a partial tail word... */ + brword b = br->buffer[br->consumed_words] << br->consumed_bits; + if(b) { + i = COUNT_ZERO_MSBS(b); + *val += i; + i++; + br->consumed_bits += i; + if(br->consumed_bits >= FLAC__BITS_PER_WORD) { /* faster way of testing if(br->consumed_bits == FLAC__BITS_PER_WORD) */ + crc16_update_word_(br, br->buffer[br->consumed_words]); + br->consumed_words++; + br->consumed_bits = 0; + } + return true; + } + else { + *val += FLAC__BITS_PER_WORD - br->consumed_bits; + crc16_update_word_(br, br->buffer[br->consumed_words]); + br->consumed_words++; + br->consumed_bits = 0; + /* didn't find stop bit yet, have to keep going... */ + } + } + /* at this point we've eaten up all the whole words; have to try + * reading through any tail bytes before calling the read callback. + * this is a repeat of the above logic adjusted for the fact we + * don't have a whole word. note though if the client is feeding + * us data a byte at a time (unlikely), br->consumed_bits may not + * be zero. + */ + if(br->bytes) { + const unsigned end = br->bytes * 8; + brword b = (br->buffer[br->consumed_words] & (FLAC__WORD_ALL_ONES << (FLAC__BITS_PER_WORD-end))) << br->consumed_bits; + if(b) { + i = COUNT_ZERO_MSBS(b); + *val += i; + i++; + br->consumed_bits += i; + FLAC__ASSERT(br->consumed_bits < FLAC__BITS_PER_WORD); + return true; + } + else { + *val += end - br->consumed_bits; + br->consumed_bits += end; + FLAC__ASSERT(br->consumed_bits < FLAC__BITS_PER_WORD); + /* didn't find stop bit yet, have to keep going... */ + } + } + if(!bitreader_read_from_client_(br)) + return false; + } +} +#endif + +FLAC__bool FLAC__bitreader_read_rice_signed(FLAC__BitReader *br, int *val, unsigned parameter) +{ + FLAC__uint32 lsbs = 0, msbs = 0; + unsigned uval; + + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + FLAC__ASSERT(parameter <= 31); + + /* read the unary MSBs and end bit */ + if(!FLAC__bitreader_read_unary_unsigned(br, (unsigned int*) &msbs)) + return false; + + /* read the binary LSBs */ + if(!FLAC__bitreader_read_raw_uint32(br, &lsbs, parameter)) + return false; + + /* compose the value */ + uval = (msbs << parameter) | lsbs; + if(uval & 1) + *val = -((int)(uval >> 1)) - 1; + else + *val = (int)(uval >> 1); + + return true; +} + +/* this is by far the most heavily used reader call. it ain't pretty but it's fast */ +/* a lot of the logic is copied, then adapted, from FLAC__bitreader_read_unary_unsigned() and FLAC__bitreader_read_raw_uint32() */ +FLAC__bool FLAC__bitreader_read_rice_signed_block(FLAC__BitReader *br, int vals[], unsigned nvals, unsigned parameter) +/* OPT: possibly faster version for use with MSVC */ +#ifdef _MSC_VER +{ + unsigned i; + unsigned uval = 0; + unsigned bits; /* the # of binary LSBs left to read to finish a rice codeword */ + + /* try and get br->consumed_words and br->consumed_bits into register; + * must remember to flush them back to *br before calling other + * bitwriter functions that use them, and before returning */ + register unsigned cwords; + register unsigned cbits; + + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + /* WATCHOUT: code does not work with <32bit words; we can make things much faster with this assertion */ + FLAC__ASSERT(FLAC__BITS_PER_WORD >= 32); + FLAC__ASSERT(parameter < 32); + /* the above two asserts also guarantee that the binary part never straddles more that 2 words, so we don't have to loop to read it */ + + if(nvals == 0) + return true; + + cbits = br->consumed_bits; + cwords = br->consumed_words; + + while(1) { + + /* read unary part */ + while(1) { + while(cwords < br->words) { /* if we've not consumed up to a partial tail word... */ + brword b = br->buffer[cwords] << cbits; + if(b) { +#if 0 /* slower, probably due to bad register allocation... */ && defined FLAC__CPU_IA32 && !defined FLAC__NO_ASM && FLAC__BITS_PER_WORD == 32 + __asm { + bsr eax, b + not eax + and eax, 31 + mov i, eax + } +#else + i = COUNT_ZERO_MSBS(b); +#endif + uval += i; + bits = parameter; + i++; + cbits += i; + if(cbits == FLAC__BITS_PER_WORD) { + crc16_update_word_(br, br->buffer[cwords]); + cwords++; + cbits = 0; + } + goto break1; + } + else { + uval += FLAC__BITS_PER_WORD - cbits; + crc16_update_word_(br, br->buffer[cwords]); + cwords++; + cbits = 0; + /* didn't find stop bit yet, have to keep going... */ + } + } + /* at this point we've eaten up all the whole words; have to try + * reading through any tail bytes before calling the read callback. + * this is a repeat of the above logic adjusted for the fact we + * don't have a whole word. note though if the client is feeding + * us data a byte at a time (unlikely), br->consumed_bits may not + * be zero. + */ + if(br->bytes) { + const unsigned end = br->bytes * 8; + brword b = (br->buffer[cwords] & (FLAC__WORD_ALL_ONES << (FLAC__BITS_PER_WORD-end))) << cbits; + if(b) { + i = COUNT_ZERO_MSBS(b); + uval += i; + bits = parameter; + i++; + cbits += i; + FLAC__ASSERT(cbits < FLAC__BITS_PER_WORD); + goto break1; + } + else { + uval += end - cbits; + cbits += end; + FLAC__ASSERT(cbits < FLAC__BITS_PER_WORD); + /* didn't find stop bit yet, have to keep going... */ + } + } + /* flush registers and read; bitreader_read_from_client_() does + * not touch br->consumed_bits at all but we still need to set + * it in case it fails and we have to return false. + */ + br->consumed_bits = cbits; + br->consumed_words = cwords; + if(!bitreader_read_from_client_(br)) + return false; + cwords = br->consumed_words; + } +break1: + /* read binary part */ + FLAC__ASSERT(cwords <= br->words); + + if(bits) { + while((br->words-cwords)*FLAC__BITS_PER_WORD + br->bytes*8 - cbits < bits) { + /* flush registers and read; bitreader_read_from_client_() does + * not touch br->consumed_bits at all but we still need to set + * it in case it fails and we have to return false. + */ + br->consumed_bits = cbits; + br->consumed_words = cwords; + if(!bitreader_read_from_client_(br)) + return false; + cwords = br->consumed_words; + } + if(cwords < br->words) { /* if we've not consumed up to a partial tail word... */ + if(cbits) { + /* this also works when consumed_bits==0, it's just a little slower than necessary for that case */ + const unsigned n = FLAC__BITS_PER_WORD - cbits; + const brword word = br->buffer[cwords]; + if(bits < n) { + uval <<= bits; + uval |= (word & (FLAC__WORD_ALL_ONES >> cbits)) >> (n-bits); + cbits += bits; + goto break2; + } + uval <<= n; + uval |= word & (FLAC__WORD_ALL_ONES >> cbits); + bits -= n; + crc16_update_word_(br, word); + cwords++; + cbits = 0; + if(bits) { /* if there are still bits left to read, there have to be less than 32 so they will all be in the next word */ + uval <<= bits; + uval |= (br->buffer[cwords] >> (FLAC__BITS_PER_WORD-bits)); + cbits = bits; + } + goto break2; + } + else { + FLAC__ASSERT(bits < FLAC__BITS_PER_WORD); + uval <<= bits; + uval |= br->buffer[cwords] >> (FLAC__BITS_PER_WORD-bits); + cbits = bits; + goto break2; + } + } + else { + /* in this case we're starting our read at a partial tail word; + * the reader has guaranteed that we have at least 'bits' bits + * available to read, which makes this case simpler. + */ + uval <<= bits; + if(cbits) { + /* this also works when consumed_bits==0, it's just a little slower than necessary for that case */ + FLAC__ASSERT(cbits + bits <= br->bytes*8); + uval |= (br->buffer[cwords] & (FLAC__WORD_ALL_ONES >> cbits)) >> (FLAC__BITS_PER_WORD-cbits-bits); + cbits += bits; + goto break2; + } + else { + uval |= br->buffer[cwords] >> (FLAC__BITS_PER_WORD-bits); + cbits += bits; + goto break2; + } + } + } +break2: + /* compose the value */ + *vals = (int)(uval >> 1 ^ -(int)(uval & 1)); + + /* are we done? */ + --nvals; + if(nvals == 0) { + br->consumed_bits = cbits; + br->consumed_words = cwords; + return true; + } + + uval = 0; + ++vals; + + } +} +#else +{ + unsigned i; + unsigned uval = 0; + + /* try and get br->consumed_words and br->consumed_bits into register; + * must remember to flush them back to *br before calling other + * bitwriter functions that use them, and before returning */ + register unsigned cwords; + register unsigned cbits; + unsigned ucbits; /* keep track of the number of unconsumed bits in the buffer */ + + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + /* WATCHOUT: code does not work with <32bit words; we can make things much faster with this assertion */ + FLAC__ASSERT(FLAC__BITS_PER_WORD >= 32); + FLAC__ASSERT(parameter < 32); + /* the above two asserts also guarantee that the binary part never straddles more than 2 words, so we don't have to loop to read it */ + + if(nvals == 0) + return true; + + cbits = br->consumed_bits; + cwords = br->consumed_words; + ucbits = (br->words-cwords)*FLAC__BITS_PER_WORD + br->bytes*8 - cbits; + + while(1) { + + /* read unary part */ + while(1) { + while(cwords < br->words) { /* if we've not consumed up to a partial tail word... */ + brword b = br->buffer[cwords] << cbits; + if(b) { +#if 0 /* is not discernably faster... */ && defined FLAC__CPU_IA32 && !defined FLAC__NO_ASM && FLAC__BITS_PER_WORD == 32 && defined __GNUC__ + asm volatile ( + "bsrl %1, %0;" + "notl %0;" + "andl $31, %0;" + : "=r"(i) + : "r"(b) + ); +#else + i = COUNT_ZERO_MSBS(b); +#endif + uval += i; + cbits += i; + cbits++; /* skip over stop bit */ + if(cbits >= FLAC__BITS_PER_WORD) { /* faster way of testing if(cbits == FLAC__BITS_PER_WORD) */ + crc16_update_word_(br, br->buffer[cwords]); + cwords++; + cbits = 0; + } + goto break1; + } + else { + uval += FLAC__BITS_PER_WORD - cbits; + crc16_update_word_(br, br->buffer[cwords]); + cwords++; + cbits = 0; + /* didn't find stop bit yet, have to keep going... */ + } + } + /* at this point we've eaten up all the whole words; have to try + * reading through any tail bytes before calling the read callback. + * this is a repeat of the above logic adjusted for the fact we + * don't have a whole word. note though if the client is feeding + * us data a byte at a time (unlikely), br->consumed_bits may not + * be zero. + */ + if(br->bytes) { + const unsigned end = br->bytes * 8; + brword b = (br->buffer[cwords] & ~(FLAC__WORD_ALL_ONES >> end)) << cbits; + if(b) { + i = COUNT_ZERO_MSBS(b); + uval += i; + cbits += i; + cbits++; /* skip over stop bit */ + FLAC__ASSERT(cbits < FLAC__BITS_PER_WORD); + goto break1; + } + else { + uval += end - cbits; + cbits += end; + FLAC__ASSERT(cbits < FLAC__BITS_PER_WORD); + /* didn't find stop bit yet, have to keep going... */ + } + } + /* flush registers and read; bitreader_read_from_client_() does + * not touch br->consumed_bits at all but we still need to set + * it in case it fails and we have to return false. + */ + br->consumed_bits = cbits; + br->consumed_words = cwords; + if(!bitreader_read_from_client_(br)) + return false; + cwords = br->consumed_words; + ucbits = (br->words-cwords)*FLAC__BITS_PER_WORD + br->bytes*8 - cbits + uval; + /* + uval to offset our count by the # of unary bits already + * consumed before the read, because we will add these back + * in all at once at break1 + */ + } +break1: + ucbits -= uval; + ucbits--; /* account for stop bit */ + + /* read binary part */ + FLAC__ASSERT(cwords <= br->words); + + if(parameter) { + while(ucbits < parameter) { + /* flush registers and read; bitreader_read_from_client_() does + * not touch br->consumed_bits at all but we still need to set + * it in case it fails and we have to return false. + */ + br->consumed_bits = cbits; + br->consumed_words = cwords; + if(!bitreader_read_from_client_(br)) + return false; + cwords = br->consumed_words; + ucbits = (br->words-cwords)*FLAC__BITS_PER_WORD + br->bytes*8 - cbits; + } + if(cwords < br->words) { /* if we've not consumed up to a partial tail word... */ + if(cbits) { + /* this also works when consumed_bits==0, it's just slower than necessary for that case */ + const unsigned n = FLAC__BITS_PER_WORD - cbits; + const brword word = br->buffer[cwords]; + if(parameter < n) { + uval <<= parameter; + uval |= (word & (FLAC__WORD_ALL_ONES >> cbits)) >> (n-parameter); + cbits += parameter; + } + else { + uval <<= n; + uval |= word & (FLAC__WORD_ALL_ONES >> cbits); + crc16_update_word_(br, word); + cwords++; + cbits = parameter - n; + if(cbits) { /* parameter > n, i.e. if there are still bits left to read, there have to be less than 32 so they will all be in the next word */ + uval <<= cbits; + uval |= (br->buffer[cwords] >> (FLAC__BITS_PER_WORD-cbits)); + } + } + } + else { + cbits = parameter; + uval <<= parameter; + uval |= br->buffer[cwords] >> (FLAC__BITS_PER_WORD-cbits); + } + } + else { + /* in this case we're starting our read at a partial tail word; + * the reader has guaranteed that we have at least 'parameter' + * bits available to read, which makes this case simpler. + */ + uval <<= parameter; + if(cbits) { + /* this also works when consumed_bits==0, it's just a little slower than necessary for that case */ + FLAC__ASSERT(cbits + parameter <= br->bytes*8); + uval |= (br->buffer[cwords] & (FLAC__WORD_ALL_ONES >> cbits)) >> (FLAC__BITS_PER_WORD-cbits-parameter); + cbits += parameter; + } + else { + cbits = parameter; + uval |= br->buffer[cwords] >> (FLAC__BITS_PER_WORD-cbits); + } + } + } + + ucbits -= parameter; + + /* compose the value */ + *vals = (int)(uval >> 1 ^ -(int)(uval & 1)); + + /* are we done? */ + --nvals; + if(nvals == 0) { + br->consumed_bits = cbits; + br->consumed_words = cwords; + return true; + } + + uval = 0; + ++vals; + + } +} +#endif + +#if 0 /* UNUSED */ +FLAC__bool FLAC__bitreader_read_golomb_signed(FLAC__BitReader *br, int *val, unsigned parameter) +{ + FLAC__uint32 lsbs = 0, msbs = 0; + unsigned bit, uval, k; + + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + + k = FLAC__bitmath_ilog2(parameter); + + /* read the unary MSBs and end bit */ + if(!FLAC__bitreader_read_unary_unsigned(br, &msbs)) + return false; + + /* read the binary LSBs */ + if(!FLAC__bitreader_read_raw_uint32(br, &lsbs, k)) + return false; + + if(parameter == 1u<= d) { + if(!FLAC__bitreader_read_bit(br, &bit)) + return false; + lsbs <<= 1; + lsbs |= bit; + lsbs -= d; + } + /* compose the value */ + uval = msbs * parameter + lsbs; + } + + /* unfold unsigned to signed */ + if(uval & 1) + *val = -((int)(uval >> 1)) - 1; + else + *val = (int)(uval >> 1); + + return true; +} + +FLAC__bool FLAC__bitreader_read_golomb_unsigned(FLAC__BitReader *br, unsigned *val, unsigned parameter) +{ + FLAC__uint32 lsbs, msbs = 0; + unsigned bit, k; + + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + + k = FLAC__bitmath_ilog2(parameter); + + /* read the unary MSBs and end bit */ + if(!FLAC__bitreader_read_unary_unsigned(br, &msbs)) + return false; + + /* read the binary LSBs */ + if(!FLAC__bitreader_read_raw_uint32(br, &lsbs, k)) + return false; + + if(parameter == 1u<= d) { + if(!FLAC__bitreader_read_bit(br, &bit)) + return false; + lsbs <<= 1; + lsbs |= bit; + lsbs -= d; + } + /* compose the value */ + *val = msbs * parameter + lsbs; + } + + return true; +} +#endif /* UNUSED */ + +/* on return, if *val == 0xffffffff then the utf-8 sequence was invalid, but the return value will be true */ +FLAC__bool FLAC__bitreader_read_utf8_uint32(FLAC__BitReader *br, FLAC__uint32 *val, FLAC__byte *raw, unsigned *rawlen) +{ + FLAC__uint32 v = 0; + FLAC__uint32 x; + unsigned i; + + if(!FLAC__bitreader_read_raw_uint32(br, &x, 8)) + return false; + if(raw) + raw[(*rawlen)++] = (FLAC__byte)x; + if(!(x & 0x80)) { /* 0xxxxxxx */ + v = x; + i = 0; + } + else if(x & 0xC0 && !(x & 0x20)) { /* 110xxxxx */ + v = x & 0x1F; + i = 1; + } + else if(x & 0xE0 && !(x & 0x10)) { /* 1110xxxx */ + v = x & 0x0F; + i = 2; + } + else if(x & 0xF0 && !(x & 0x08)) { /* 11110xxx */ + v = x & 0x07; + i = 3; + } + else if(x & 0xF8 && !(x & 0x04)) { /* 111110xx */ + v = x & 0x03; + i = 4; + } + else if(x & 0xFC && !(x & 0x02)) { /* 1111110x */ + v = x & 0x01; + i = 5; + } + else { + *val = 0xffffffff; + return true; + } + for( ; i; i--) { + if(!FLAC__bitreader_read_raw_uint32(br, &x, 8)) + return false; + if(raw) + raw[(*rawlen)++] = (FLAC__byte)x; + if(!(x & 0x80) || (x & 0x40)) { /* 10xxxxxx */ + *val = 0xffffffff; + return true; + } + v <<= 6; + v |= (x & 0x3F); + } + *val = v; + return true; +} + +/* on return, if *val == 0xffffffffffffffff then the utf-8 sequence was invalid, but the return value will be true */ +FLAC__bool FLAC__bitreader_read_utf8_uint64(FLAC__BitReader *br, FLAC__uint64 *val, FLAC__byte *raw, unsigned *rawlen) +{ + FLAC__uint64 v = 0; + FLAC__uint32 x; + unsigned i; + + if(!FLAC__bitreader_read_raw_uint32(br, &x, 8)) + return false; + if(raw) + raw[(*rawlen)++] = (FLAC__byte)x; + if(!(x & 0x80)) { /* 0xxxxxxx */ + v = x; + i = 0; + } + else if(x & 0xC0 && !(x & 0x20)) { /* 110xxxxx */ + v = x & 0x1F; + i = 1; + } + else if(x & 0xE0 && !(x & 0x10)) { /* 1110xxxx */ + v = x & 0x0F; + i = 2; + } + else if(x & 0xF0 && !(x & 0x08)) { /* 11110xxx */ + v = x & 0x07; + i = 3; + } + else if(x & 0xF8 && !(x & 0x04)) { /* 111110xx */ + v = x & 0x03; + i = 4; + } + else if(x & 0xFC && !(x & 0x02)) { /* 1111110x */ + v = x & 0x01; + i = 5; + } + else if(x & 0xFE && !(x & 0x01)) { /* 11111110 */ + v = 0; + i = 6; + } + else { + *val = FLAC__U64L(0xffffffffffffffff); + return true; + } + for( ; i; i--) { + if(!FLAC__bitreader_read_raw_uint32(br, &x, 8)) + return false; + if(raw) + raw[(*rawlen)++] = (FLAC__byte)x; + if(!(x & 0x80) || (x & 0x40)) { /* 10xxxxxx */ + *val = FLAC__U64L(0xffffffffffffffff); + return true; + } + v <<= 6; + v |= (x & 0x3F); + } + *val = v; + return true; +} + +#endif +/********* End of inlined file: bitreader.c *********/ + +/********* Start of inlined file: bitwriter.c *********/ + +/********* Start of inlined file: juce_FlacHeader.h *********/ +// This file is included at the start of each FLAC .c file, just to do a few housekeeping +// tasks.. + +#define VERSION "1.2.1" + +#define FLAC__NO_DLL 1 + +#ifdef _MSC_VER + #pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4312) +#endif + +#if ! (defined (_WIN32) || defined (_WIN64) || defined (LINUX)) + #define FLAC__SYS_DARWIN 1 +#endif +/********* End of inlined file: juce_FlacHeader.h *********/ + +#if JUCE_USE_FLAC + +#if HAVE_CONFIG_H +# include +#endif + +#include /* for malloc() */ +#include /* for memcpy(), memset() */ +#ifdef _MSC_VER +#include /* for ntohl() */ +#elif defined FLAC__SYS_DARWIN +#include /* for ntohl() */ +#elif defined __MINGW32__ +#include /* for ntohl() */ +#else +#include /* for ntohl() */ +#endif +#if 0 /* UNUSED */ + +#endif + +/********* Start of inlined file: bitwriter.h *********/ +#ifndef FLAC__PRIVATE__BITWRITER_H +#define FLAC__PRIVATE__BITWRITER_H + +#include /* for FILE */ + +/* + * opaque structure definition + */ +struct FLAC__BitWriter; +typedef struct FLAC__BitWriter FLAC__BitWriter; + +/* + * construction, deletion, initialization, etc functions + */ +FLAC__BitWriter *FLAC__bitwriter_new(void); +void FLAC__bitwriter_delete(FLAC__BitWriter *bw); +FLAC__bool FLAC__bitwriter_init(FLAC__BitWriter *bw); +void FLAC__bitwriter_free(FLAC__BitWriter *bw); /* does not 'free(buffer)' */ +void FLAC__bitwriter_clear(FLAC__BitWriter *bw); +void FLAC__bitwriter_dump(const FLAC__BitWriter *bw, FILE *out); + +/* + * CRC functions + * + * non-const *bw because they have to cal FLAC__bitwriter_get_buffer() + */ +FLAC__bool FLAC__bitwriter_get_write_crc16(FLAC__BitWriter *bw, FLAC__uint16 *crc); +FLAC__bool FLAC__bitwriter_get_write_crc8(FLAC__BitWriter *bw, FLAC__byte *crc); + +/* + * info functions + */ +FLAC__bool FLAC__bitwriter_is_byte_aligned(const FLAC__BitWriter *bw); +unsigned FLAC__bitwriter_get_input_bits_unconsumed(const FLAC__BitWriter *bw); /* can be called anytime, returns total # of bits unconsumed */ + +/* + * direct buffer access + * + * there may be no calls on the bitwriter between get and release. + * the bitwriter continues to own the returned buffer. + * before get, bitwriter MUST be byte aligned: check with FLAC__bitwriter_is_byte_aligned() + */ +FLAC__bool FLAC__bitwriter_get_buffer(FLAC__BitWriter *bw, const FLAC__byte **buffer, size_t *bytes); +void FLAC__bitwriter_release_buffer(FLAC__BitWriter *bw); + +/* + * write functions + */ +FLAC__bool FLAC__bitwriter_write_zeroes(FLAC__BitWriter *bw, unsigned bits); +FLAC__bool FLAC__bitwriter_write_raw_uint32(FLAC__BitWriter *bw, FLAC__uint32 val, unsigned bits); +FLAC__bool FLAC__bitwriter_write_raw_int32(FLAC__BitWriter *bw, FLAC__int32 val, unsigned bits); +FLAC__bool FLAC__bitwriter_write_raw_uint64(FLAC__BitWriter *bw, FLAC__uint64 val, unsigned bits); +FLAC__bool FLAC__bitwriter_write_raw_uint32_little_endian(FLAC__BitWriter *bw, FLAC__uint32 val); /*only for bits=32*/ +FLAC__bool FLAC__bitwriter_write_byte_block(FLAC__BitWriter *bw, const FLAC__byte vals[], unsigned nvals); +FLAC__bool FLAC__bitwriter_write_unary_unsigned(FLAC__BitWriter *bw, unsigned val); +unsigned FLAC__bitwriter_rice_bits(FLAC__int32 val, unsigned parameter); +#if 0 /* UNUSED */ +unsigned FLAC__bitwriter_golomb_bits_signed(int val, unsigned parameter); +unsigned FLAC__bitwriter_golomb_bits_unsigned(unsigned val, unsigned parameter); +#endif +FLAC__bool FLAC__bitwriter_write_rice_signed(FLAC__BitWriter *bw, FLAC__int32 val, unsigned parameter); +FLAC__bool FLAC__bitwriter_write_rice_signed_block(FLAC__BitWriter *bw, const FLAC__int32 *vals, unsigned nvals, unsigned parameter); +#if 0 /* UNUSED */ +FLAC__bool FLAC__bitwriter_write_golomb_signed(FLAC__BitWriter *bw, int val, unsigned parameter); +FLAC__bool FLAC__bitwriter_write_golomb_unsigned(FLAC__BitWriter *bw, unsigned val, unsigned parameter); +#endif +FLAC__bool FLAC__bitwriter_write_utf8_uint32(FLAC__BitWriter *bw, FLAC__uint32 val); +FLAC__bool FLAC__bitwriter_write_utf8_uint64(FLAC__BitWriter *bw, FLAC__uint64 val); +FLAC__bool FLAC__bitwriter_zero_pad_to_byte_boundary(FLAC__BitWriter *bw); + +#endif +/********* End of inlined file: bitwriter.h *********/ + +/********* Start of inlined file: alloc.h *********/ +#ifndef FLAC__SHARE__ALLOC_H +#define FLAC__SHARE__ALLOC_H + +#if HAVE_CONFIG_H +# include +#endif + +/* WATCHOUT: for c++ you may have to #define __STDC_LIMIT_MACROS 1 real early + * before #including this file, otherwise SIZE_MAX might not be defined + */ + +#include /* for SIZE_MAX */ +#if !defined _MSC_VER && !defined __MINGW32__ && !defined __EMX__ +#include /* for SIZE_MAX in case limits.h didn't get it */ +#endif +#include /* for size_t, malloc(), etc */ + +#ifndef SIZE_MAX +# ifndef SIZE_T_MAX +# ifdef _MSC_VER +# define SIZE_T_MAX UINT_MAX +# else +# error +# endif +# endif +# define SIZE_MAX SIZE_T_MAX +#endif + +#ifndef FLaC__INLINE +#define FLaC__INLINE +#endif + +/* avoid malloc()ing 0 bytes, see: + * https://www.securecoding.cert.org/confluence/display/seccode/MEM04-A.+Do+not+make+assumptions+about+the+result+of+allocating+0+bytes?focusedCommentId=5407003 +*/ +static FLaC__INLINE void *safe_malloc_(size_t size) +{ + /* malloc(0) is undefined; FLAC src convention is to always allocate */ + if(!size) + size++; + return malloc(size); +} + +static FLaC__INLINE void *safe_calloc_(size_t nmemb, size_t size) +{ + if(!nmemb || !size) + return malloc(1); /* malloc(0) is undefined; FLAC src convention is to always allocate */ + return calloc(nmemb, size); +} + +/*@@@@ there's probably a better way to prevent overflows when allocating untrusted sums but this works for now */ + +static FLaC__INLINE void *safe_malloc_add_2op_(size_t size1, size_t size2) +{ + size2 += size1; + if(size2 < size1) + return 0; + return safe_malloc_(size2); +} + +static FLaC__INLINE void *safe_malloc_add_3op_(size_t size1, size_t size2, size_t size3) +{ + size2 += size1; + if(size2 < size1) + return 0; + size3 += size2; + if(size3 < size2) + return 0; + return safe_malloc_(size3); +} + +static FLaC__INLINE void *safe_malloc_add_4op_(size_t size1, size_t size2, size_t size3, size_t size4) +{ + size2 += size1; + if(size2 < size1) + return 0; + size3 += size2; + if(size3 < size2) + return 0; + size4 += size3; + if(size4 < size3) + return 0; + return safe_malloc_(size4); +} + +static FLaC__INLINE void *safe_malloc_mul_2op_(size_t size1, size_t size2) +#if 0 +needs support for cases where sizeof(size_t) != 4 +{ + /* could be faster #ifdef'ing off SIZEOF_SIZE_T */ + if(sizeof(size_t) == 4) { + if ((double)size1 * (double)size2 < 4294967296.0) + return malloc(size1*size2); + } + return 0; +} +#else +/* better? */ +{ + if(!size1 || !size2) + return malloc(1); /* malloc(0) is undefined; FLAC src convention is to always allocate */ + if(size1 > SIZE_MAX / size2) + return 0; + return malloc(size1*size2); +} +#endif + +static FLaC__INLINE void *safe_malloc_mul_3op_(size_t size1, size_t size2, size_t size3) +{ + if(!size1 || !size2 || !size3) + return malloc(1); /* malloc(0) is undefined; FLAC src convention is to always allocate */ + if(size1 > SIZE_MAX / size2) + return 0; + size1 *= size2; + if(size1 > SIZE_MAX / size3) + return 0; + return malloc(size1*size3); +} + +/* size1*size2 + size3 */ +static FLaC__INLINE void *safe_malloc_mul2add_(size_t size1, size_t size2, size_t size3) +{ + if(!size1 || !size2) + return safe_malloc_(size3); + if(size1 > SIZE_MAX / size2) + return 0; + return safe_malloc_add_2op_(size1*size2, size3); +} + +/* size1 * (size2 + size3) */ +static FLaC__INLINE void *safe_malloc_muladd2_(size_t size1, size_t size2, size_t size3) +{ + if(!size1 || (!size2 && !size3)) + return malloc(1); /* malloc(0) is undefined; FLAC src convention is to always allocate */ + size2 += size3; + if(size2 < size3) + return 0; + return safe_malloc_mul_2op_(size1, size2); +} + +static FLaC__INLINE void *safe_realloc_add_2op_(void *ptr, size_t size1, size_t size2) +{ + size2 += size1; + if(size2 < size1) + return 0; + return realloc(ptr, size2); +} + +static FLaC__INLINE void *safe_realloc_add_3op_(void *ptr, size_t size1, size_t size2, size_t size3) +{ + size2 += size1; + if(size2 < size1) + return 0; + size3 += size2; + if(size3 < size2) + return 0; + return realloc(ptr, size3); +} + +static FLaC__INLINE void *safe_realloc_add_4op_(void *ptr, size_t size1, size_t size2, size_t size3, size_t size4) +{ + size2 += size1; + if(size2 < size1) + return 0; + size3 += size2; + if(size3 < size2) + return 0; + size4 += size3; + if(size4 < size3) + return 0; + return realloc(ptr, size4); +} + +static FLaC__INLINE void *safe_realloc_mul_2op_(void *ptr, size_t size1, size_t size2) +{ + if(!size1 || !size2) + return realloc(ptr, 0); /* preserve POSIX realloc(ptr, 0) semantics */ + if(size1 > SIZE_MAX / size2) + return 0; + return realloc(ptr, size1*size2); +} + +/* size1 * (size2 + size3) */ +static FLaC__INLINE void *safe_realloc_muladd2_(void *ptr, size_t size1, size_t size2, size_t size3) +{ + if(!size1 || (!size2 && !size3)) + return realloc(ptr, 0); /* preserve POSIX realloc(ptr, 0) semantics */ + size2 += size3; + if(size2 < size3) + return 0; + return safe_realloc_mul_2op_(ptr, size1, size2); +} + +#endif +/********* End of inlined file: alloc.h *********/ + +/* Things should be fastest when this matches the machine word size */ +/* WATCHOUT: if you change this you must also change the following #defines down to SWAP_BE_WORD_TO_HOST below to match */ +/* WATCHOUT: there are a few places where the code will not work unless bwword is >= 32 bits wide */ +typedef FLAC__uint32 bwword; +#define FLAC__BYTES_PER_WORD 4 +#define FLAC__BITS_PER_WORD 32 +#define FLAC__WORD_ALL_ONES ((FLAC__uint32)0xffffffff) +/* SWAP_BE_WORD_TO_HOST swaps bytes in a bwword (which is always big-endian) if necessary to match host byte order */ +#if WORDS_BIGENDIAN +#define SWAP_BE_WORD_TO_HOST(x) (x) +#else +#ifdef _MSC_VER +#define SWAP_BE_WORD_TO_HOST(x) local_swap32_(x) +#else +#define SWAP_BE_WORD_TO_HOST(x) ntohl(x) +#endif +#endif + +/* + * The default capacity here doesn't matter too much. The buffer always grows + * to hold whatever is written to it. Usually the encoder will stop adding at + * a frame or metadata block, then write that out and clear the buffer for the + * next one. + */ +static const unsigned FLAC__BITWRITER_DEFAULT_CAPACITY = 32768u / sizeof(bwword); /* size in words */ +/* When growing, increment 4K at a time */ +static const unsigned FLAC__BITWRITER_DEFAULT_INCREMENT = 4096u / sizeof(bwword); /* size in words */ + +#define FLAC__WORDS_TO_BITS(words) ((words) * FLAC__BITS_PER_WORD) +#define FLAC__TOTAL_BITS(bw) (FLAC__WORDS_TO_BITS((bw)->words) + (bw)->bits) + +#ifdef min +#undef min +#endif +#define min(x,y) ((x)<(y)?(x):(y)) + +/* adjust for compilers that can't understand using LLU suffix for uint64_t literals */ +#ifdef _MSC_VER +#define FLAC__U64L(x) x +#else +#define FLAC__U64L(x) x##LLU +#endif + +#ifndef FLaC__INLINE +#define FLaC__INLINE +#endif + +struct FLAC__BitWriter { + bwword *buffer; + bwword accum; /* accumulator; bits are right-justified; when full, accum is appended to buffer */ + unsigned capacity; /* capacity of buffer in words */ + unsigned words; /* # of complete words in buffer */ + unsigned bits; /* # of used bits in accum */ +}; + +/* * WATCHOUT: The current implementation only grows the buffer. */ +static FLAC__bool bitwriter_grow_(FLAC__BitWriter *bw, unsigned bits_to_add) +{ + unsigned new_capacity; + bwword *new_buffer; + + FLAC__ASSERT(0 != bw); + FLAC__ASSERT(0 != bw->buffer); + + /* calculate total words needed to store 'bits_to_add' additional bits */ + new_capacity = bw->words + ((bw->bits + bits_to_add + FLAC__BITS_PER_WORD - 1) / FLAC__BITS_PER_WORD); + + /* it's possible (due to pessimism in the growth estimation that + * leads to this call) that we don't actually need to grow + */ + if(bw->capacity >= new_capacity) + return true; + + /* round up capacity increase to the nearest FLAC__BITWRITER_DEFAULT_INCREMENT */ + if((new_capacity - bw->capacity) % FLAC__BITWRITER_DEFAULT_INCREMENT) + new_capacity += FLAC__BITWRITER_DEFAULT_INCREMENT - ((new_capacity - bw->capacity) % FLAC__BITWRITER_DEFAULT_INCREMENT); + /* make sure we got everything right */ + FLAC__ASSERT(0 == (new_capacity - bw->capacity) % FLAC__BITWRITER_DEFAULT_INCREMENT); + FLAC__ASSERT(new_capacity > bw->capacity); + FLAC__ASSERT(new_capacity >= bw->words + ((bw->bits + bits_to_add + FLAC__BITS_PER_WORD - 1) / FLAC__BITS_PER_WORD)); + + new_buffer = (bwword*)safe_realloc_mul_2op_(bw->buffer, sizeof(bwword), /*times*/new_capacity); + if(new_buffer == 0) + return false; + bw->buffer = new_buffer; + bw->capacity = new_capacity; + return true; +} + +/*********************************************************************** + * + * Class constructor/destructor + * + ***********************************************************************/ + +FLAC__BitWriter *FLAC__bitwriter_new(void) +{ + FLAC__BitWriter *bw = (FLAC__BitWriter*)calloc(1, sizeof(FLAC__BitWriter)); + /* note that calloc() sets all members to 0 for us */ + return bw; +} + +void FLAC__bitwriter_delete(FLAC__BitWriter *bw) +{ + FLAC__ASSERT(0 != bw); + + FLAC__bitwriter_free(bw); + free(bw); +} + +/*********************************************************************** + * + * Public class methods + * + ***********************************************************************/ + +FLAC__bool FLAC__bitwriter_init(FLAC__BitWriter *bw) +{ + FLAC__ASSERT(0 != bw); + + bw->words = bw->bits = 0; + bw->capacity = FLAC__BITWRITER_DEFAULT_CAPACITY; + bw->buffer = (bwword*)malloc(sizeof(bwword) * bw->capacity); + if(bw->buffer == 0) + return false; + + return true; +} + +void FLAC__bitwriter_free(FLAC__BitWriter *bw) +{ + FLAC__ASSERT(0 != bw); + + if(0 != bw->buffer) + free(bw->buffer); + bw->buffer = 0; + bw->capacity = 0; + bw->words = bw->bits = 0; +} + +void FLAC__bitwriter_clear(FLAC__BitWriter *bw) +{ + bw->words = bw->bits = 0; +} + +void FLAC__bitwriter_dump(const FLAC__BitWriter *bw, FILE *out) +{ + unsigned i, j; + if(bw == 0) { + fprintf(out, "bitwriter is NULL\n"); + } + else { + fprintf(out, "bitwriter: capacity=%u words=%u bits=%u total_bits=%u\n", bw->capacity, bw->words, bw->bits, FLAC__TOTAL_BITS(bw)); + + for(i = 0; i < bw->words; i++) { + fprintf(out, "%08X: ", i); + for(j = 0; j < FLAC__BITS_PER_WORD; j++) + fprintf(out, "%01u", bw->buffer[i] & (1 << (FLAC__BITS_PER_WORD-j-1)) ? 1:0); + fprintf(out, "\n"); + } + if(bw->bits > 0) { + fprintf(out, "%08X: ", i); + for(j = 0; j < bw->bits; j++) + fprintf(out, "%01u", bw->accum & (1 << (bw->bits-j-1)) ? 1:0); + fprintf(out, "\n"); + } + } +} + +FLAC__bool FLAC__bitwriter_get_write_crc16(FLAC__BitWriter *bw, FLAC__uint16 *crc) +{ + const FLAC__byte *buffer; + size_t bytes; + + FLAC__ASSERT((bw->bits & 7) == 0); /* assert that we're byte-aligned */ + + if(!FLAC__bitwriter_get_buffer(bw, &buffer, &bytes)) + return false; + + *crc = (FLAC__uint16)FLAC__crc16(buffer, bytes); + FLAC__bitwriter_release_buffer(bw); + return true; +} + +FLAC__bool FLAC__bitwriter_get_write_crc8(FLAC__BitWriter *bw, FLAC__byte *crc) +{ + const FLAC__byte *buffer; + size_t bytes; + + FLAC__ASSERT((bw->bits & 7) == 0); /* assert that we're byte-aligned */ + + if(!FLAC__bitwriter_get_buffer(bw, &buffer, &bytes)) + return false; + + *crc = FLAC__crc8(buffer, bytes); + FLAC__bitwriter_release_buffer(bw); + return true; +} + +FLAC__bool FLAC__bitwriter_is_byte_aligned(const FLAC__BitWriter *bw) +{ + return ((bw->bits & 7) == 0); +} + +unsigned FLAC__bitwriter_get_input_bits_unconsumed(const FLAC__BitWriter *bw) +{ + return FLAC__TOTAL_BITS(bw); +} + +FLAC__bool FLAC__bitwriter_get_buffer(FLAC__BitWriter *bw, const FLAC__byte **buffer, size_t *bytes) +{ + FLAC__ASSERT((bw->bits & 7) == 0); + /* double protection */ + if(bw->bits & 7) + return false; + /* if we have bits in the accumulator we have to flush those to the buffer first */ + if(bw->bits) { + FLAC__ASSERT(bw->words <= bw->capacity); + if(bw->words == bw->capacity && !bitwriter_grow_(bw, FLAC__BITS_PER_WORD)) + return false; + /* append bits as complete word to buffer, but don't change bw->accum or bw->bits */ + bw->buffer[bw->words] = SWAP_BE_WORD_TO_HOST(bw->accum << (FLAC__BITS_PER_WORD-bw->bits)); + } + /* now we can just return what we have */ + *buffer = (FLAC__byte*)bw->buffer; + *bytes = (FLAC__BYTES_PER_WORD * bw->words) + (bw->bits >> 3); + return true; +} + +void FLAC__bitwriter_release_buffer(FLAC__BitWriter *bw) +{ + /* nothing to do. in the future, strict checking of a 'writer-is-in- + * get-mode' flag could be added everywhere and then cleared here + */ + (void)bw; +} + +FLaC__INLINE FLAC__bool FLAC__bitwriter_write_zeroes(FLAC__BitWriter *bw, unsigned bits) +{ + unsigned n; + + FLAC__ASSERT(0 != bw); + FLAC__ASSERT(0 != bw->buffer); + + if(bits == 0) + return true; + /* slightly pessimistic size check but faster than "<= bw->words + (bw->bits+bits+FLAC__BITS_PER_WORD-1)/FLAC__BITS_PER_WORD" */ + if(bw->capacity <= bw->words + bits && !bitwriter_grow_(bw, bits)) + return false; + /* first part gets to word alignment */ + if(bw->bits) { + n = min(FLAC__BITS_PER_WORD - bw->bits, bits); + bw->accum <<= n; + bits -= n; + bw->bits += n; + if(bw->bits == FLAC__BITS_PER_WORD) { + bw->buffer[bw->words++] = SWAP_BE_WORD_TO_HOST(bw->accum); + bw->bits = 0; + } + else + return true; + } + /* do whole words */ + while(bits >= FLAC__BITS_PER_WORD) { + bw->buffer[bw->words++] = 0; + bits -= FLAC__BITS_PER_WORD; + } + /* do any leftovers */ + if(bits > 0) { + bw->accum = 0; + bw->bits = bits; + } + return true; +} + +FLaC__INLINE FLAC__bool FLAC__bitwriter_write_raw_uint32(FLAC__BitWriter *bw, FLAC__uint32 val, unsigned bits) +{ + register unsigned left; + + /* WATCHOUT: code does not work with <32bit words; we can make things much faster with this assertion */ + FLAC__ASSERT(FLAC__BITS_PER_WORD >= 32); + + FLAC__ASSERT(0 != bw); + FLAC__ASSERT(0 != bw->buffer); + + FLAC__ASSERT(bits <= 32); + if(bits == 0) + return true; + + /* slightly pessimistic size check but faster than "<= bw->words + (bw->bits+bits+FLAC__BITS_PER_WORD-1)/FLAC__BITS_PER_WORD" */ + if(bw->capacity <= bw->words + bits && !bitwriter_grow_(bw, bits)) + return false; + + left = FLAC__BITS_PER_WORD - bw->bits; + if(bits < left) { + bw->accum <<= bits; + bw->accum |= val; + bw->bits += bits; + } + else if(bw->bits) { /* WATCHOUT: if bw->bits == 0, left==FLAC__BITS_PER_WORD and bw->accum<<=left is a NOP instead of setting to 0 */ + bw->accum <<= left; + bw->accum |= val >> (bw->bits = bits - left); + bw->buffer[bw->words++] = SWAP_BE_WORD_TO_HOST(bw->accum); + bw->accum = val; + } + else { + bw->accum = val; + bw->bits = 0; + bw->buffer[bw->words++] = SWAP_BE_WORD_TO_HOST(val); + } + + return true; +} + +FLaC__INLINE FLAC__bool FLAC__bitwriter_write_raw_int32(FLAC__BitWriter *bw, FLAC__int32 val, unsigned bits) +{ + /* zero-out unused bits */ + if(bits < 32) + val &= (~(0xffffffff << bits)); + + return FLAC__bitwriter_write_raw_uint32(bw, (FLAC__uint32)val, bits); +} + +FLaC__INLINE FLAC__bool FLAC__bitwriter_write_raw_uint64(FLAC__BitWriter *bw, FLAC__uint64 val, unsigned bits) +{ + /* this could be a little faster but it's not used for much */ + if(bits > 32) { + return + FLAC__bitwriter_write_raw_uint32(bw, (FLAC__uint32)(val>>32), bits-32) && + FLAC__bitwriter_write_raw_uint32(bw, (FLAC__uint32)val, 32); + } + else + return FLAC__bitwriter_write_raw_uint32(bw, (FLAC__uint32)val, bits); +} + +FLaC__INLINE FLAC__bool FLAC__bitwriter_write_raw_uint32_little_endian(FLAC__BitWriter *bw, FLAC__uint32 val) +{ + /* this doesn't need to be that fast as currently it is only used for vorbis comments */ + + if(!FLAC__bitwriter_write_raw_uint32(bw, val & 0xff, 8)) + return false; + if(!FLAC__bitwriter_write_raw_uint32(bw, (val>>8) & 0xff, 8)) + return false; + if(!FLAC__bitwriter_write_raw_uint32(bw, (val>>16) & 0xff, 8)) + return false; + if(!FLAC__bitwriter_write_raw_uint32(bw, val>>24, 8)) + return false; + + return true; +} + +FLaC__INLINE FLAC__bool FLAC__bitwriter_write_byte_block(FLAC__BitWriter *bw, const FLAC__byte vals[], unsigned nvals) +{ + unsigned i; + + /* this could be faster but currently we don't need it to be since it's only used for writing metadata */ + for(i = 0; i < nvals; i++) { + if(!FLAC__bitwriter_write_raw_uint32(bw, (FLAC__uint32)(vals[i]), 8)) + return false; + } + + return true; +} + +FLAC__bool FLAC__bitwriter_write_unary_unsigned(FLAC__BitWriter *bw, unsigned val) +{ + if(val < 32) + return FLAC__bitwriter_write_raw_uint32(bw, 1, ++val); + else + return + FLAC__bitwriter_write_zeroes(bw, val) && + FLAC__bitwriter_write_raw_uint32(bw, 1, 1); +} + +unsigned FLAC__bitwriter_rice_bits(FLAC__int32 val, unsigned parameter) +{ + FLAC__uint32 uval; + + FLAC__ASSERT(parameter < sizeof(unsigned)*8); + + /* fold signed to unsigned; actual formula is: negative(v)? -2v-1 : 2v */ + uval = (val<<1) ^ (val>>31); + + return 1 + parameter + (uval >> parameter); +} + +#if 0 /* UNUSED */ +unsigned FLAC__bitwriter_golomb_bits_signed(int val, unsigned parameter) +{ + unsigned bits, msbs, uval; + unsigned k; + + FLAC__ASSERT(parameter > 0); + + /* fold signed to unsigned */ + if(val < 0) + uval = (unsigned)(((-(++val)) << 1) + 1); + else + uval = (unsigned)(val << 1); + + k = FLAC__bitmath_ilog2(parameter); + if(parameter == 1u<> k; + bits = 1 + k + msbs; + } + else { + unsigned q, r, d; + + d = (1 << (k+1)) - parameter; + q = uval / parameter; + r = uval - (q * parameter); + + bits = 1 + q + k; + if(r >= d) + bits++; + } + return bits; +} + +unsigned FLAC__bitwriter_golomb_bits_unsigned(unsigned uval, unsigned parameter) +{ + unsigned bits, msbs; + unsigned k; + + FLAC__ASSERT(parameter > 0); + + k = FLAC__bitmath_ilog2(parameter); + if(parameter == 1u<> k; + bits = 1 + k + msbs; + } + else { + unsigned q, r, d; + + d = (1 << (k+1)) - parameter; + q = uval / parameter; + r = uval - (q * parameter); + + bits = 1 + q + k; + if(r >= d) + bits++; + } + return bits; +} +#endif /* UNUSED */ + +FLAC__bool FLAC__bitwriter_write_rice_signed(FLAC__BitWriter *bw, FLAC__int32 val, unsigned parameter) +{ + unsigned total_bits, interesting_bits, msbs; + FLAC__uint32 uval, pattern; + + FLAC__ASSERT(0 != bw); + FLAC__ASSERT(0 != bw->buffer); + FLAC__ASSERT(parameter < 8*sizeof(uval)); + + /* fold signed to unsigned; actual formula is: negative(v)? -2v-1 : 2v */ + uval = (val<<1) ^ (val>>31); + + msbs = uval >> parameter; + interesting_bits = 1 + parameter; + total_bits = interesting_bits + msbs; + pattern = 1 << parameter; /* the unary end bit */ + pattern |= (uval & ((1<> (31-parameter); /* ...then mask off the bits above the stop bit with val&=mask2*/ + FLAC__uint32 uval; + unsigned left; + const unsigned lsbits = 1 + parameter; + unsigned msbits; + + FLAC__ASSERT(0 != bw); + FLAC__ASSERT(0 != bw->buffer); + FLAC__ASSERT(parameter < 8*sizeof(bwword)-1); + /* WATCHOUT: code does not work with <32bit words; we can make things much faster with this assertion */ + FLAC__ASSERT(FLAC__BITS_PER_WORD >= 32); + + while(nvals) { + /* fold signed to unsigned; actual formula is: negative(v)? -2v-1 : 2v */ + uval = (*vals<<1) ^ (*vals>>31); + + msbits = uval >> parameter; + +#if 0 /* OPT: can remove this special case if it doesn't make up for the extra compare (doesn't make a statistically significant difference with msvc or gcc/x86) */ + if(bw->bits && bw->bits + msbits + lsbits <= FLAC__BITS_PER_WORD) { /* i.e. if the whole thing fits in the current bwword */ + /* ^^^ if bw->bits is 0 then we may have filled the buffer and have no free bwword to work in */ + bw->bits = bw->bits + msbits + lsbits; + uval |= mask1; /* set stop bit */ + uval &= mask2; /* mask off unused top bits */ + /* NOT: bw->accum <<= msbits + lsbits because msbits+lsbits could be 32, then the shift would be a NOP */ + bw->accum <<= msbits; + bw->accum <<= lsbits; + bw->accum |= uval; + if(bw->bits == FLAC__BITS_PER_WORD) { + bw->buffer[bw->words++] = SWAP_BE_WORD_TO_HOST(bw->accum); + bw->bits = 0; + /* burying the capacity check down here means we have to grow the buffer a little if there are more vals to do */ + if(bw->capacity <= bw->words && nvals > 1 && !bitwriter_grow_(bw, 1)) { + FLAC__ASSERT(bw->capacity == bw->words); + return false; + } + } + } + else { +#elif 1 /*@@@@@@ OPT: try this version with MSVC6 to see if better, not much difference for gcc-4 */ + if(bw->bits && bw->bits + msbits + lsbits < FLAC__BITS_PER_WORD) { /* i.e. if the whole thing fits in the current bwword */ + /* ^^^ if bw->bits is 0 then we may have filled the buffer and have no free bwword to work in */ + bw->bits = bw->bits + msbits + lsbits; + uval |= mask1; /* set stop bit */ + uval &= mask2; /* mask off unused top bits */ + bw->accum <<= msbits + lsbits; + bw->accum |= uval; + } + else { +#endif + /* slightly pessimistic size check but faster than "<= bw->words + (bw->bits+msbits+lsbits+FLAC__BITS_PER_WORD-1)/FLAC__BITS_PER_WORD" */ + /* OPT: pessimism may cause flurry of false calls to grow_ which eat up all savings before it */ + if(bw->capacity <= bw->words + bw->bits + msbits + 1/*lsbits always fit in 1 bwword*/ && !bitwriter_grow_(bw, msbits+lsbits)) + return false; + + if(msbits) { + /* first part gets to word alignment */ + if(bw->bits) { + left = FLAC__BITS_PER_WORD - bw->bits; + if(msbits < left) { + bw->accum <<= msbits; + bw->bits += msbits; + goto break1; + } + else { + bw->accum <<= left; + msbits -= left; + bw->buffer[bw->words++] = SWAP_BE_WORD_TO_HOST(bw->accum); + bw->bits = 0; + } + } + /* do whole words */ + while(msbits >= FLAC__BITS_PER_WORD) { + bw->buffer[bw->words++] = 0; + msbits -= FLAC__BITS_PER_WORD; + } + /* do any leftovers */ + if(msbits > 0) { + bw->accum = 0; + bw->bits = msbits; + } + } +break1: + uval |= mask1; /* set stop bit */ + uval &= mask2; /* mask off unused top bits */ + + left = FLAC__BITS_PER_WORD - bw->bits; + if(lsbits < left) { + bw->accum <<= lsbits; + bw->accum |= uval; + bw->bits += lsbits; + } + else { + /* if bw->bits == 0, left==FLAC__BITS_PER_WORD which will always + * be > lsbits (because of previous assertions) so it would have + * triggered the (lsbitsbits); + FLAC__ASSERT(left < FLAC__BITS_PER_WORD); + bw->accum <<= left; + bw->accum |= uval >> (bw->bits = lsbits - left); + bw->buffer[bw->words++] = SWAP_BE_WORD_TO_HOST(bw->accum); + bw->accum = uval; + } +#if 1 + } +#endif + vals++; + nvals--; + } + return true; +} + +#if 0 /* UNUSED */ +FLAC__bool FLAC__bitwriter_write_golomb_signed(FLAC__BitWriter *bw, int val, unsigned parameter) +{ + unsigned total_bits, msbs, uval; + unsigned k; + + FLAC__ASSERT(0 != bw); + FLAC__ASSERT(0 != bw->buffer); + FLAC__ASSERT(parameter > 0); + + /* fold signed to unsigned */ + if(val < 0) + uval = (unsigned)(((-(++val)) << 1) + 1); + else + uval = (unsigned)(val << 1); + + k = FLAC__bitmath_ilog2(parameter); + if(parameter == 1u<> k; + total_bits = 1 + k + msbs; + pattern = 1 << k; /* the unary end bit */ + pattern |= (uval & ((1u<= d) { + if(!FLAC__bitwriter_write_raw_uint32(bw, r+d, k+1)) + return false; + } + else { + if(!FLAC__bitwriter_write_raw_uint32(bw, r, k)) + return false; + } + } + return true; +} + +FLAC__bool FLAC__bitwriter_write_golomb_unsigned(FLAC__BitWriter *bw, unsigned uval, unsigned parameter) +{ + unsigned total_bits, msbs; + unsigned k; + + FLAC__ASSERT(0 != bw); + FLAC__ASSERT(0 != bw->buffer); + FLAC__ASSERT(parameter > 0); + + k = FLAC__bitmath_ilog2(parameter); + if(parameter == 1u<> k; + total_bits = 1 + k + msbs; + pattern = 1 << k; /* the unary end bit */ + pattern |= (uval & ((1u<= d) { + if(!FLAC__bitwriter_write_raw_uint32(bw, r+d, k+1)) + return false; + } + else { + if(!FLAC__bitwriter_write_raw_uint32(bw, r, k)) + return false; + } + } + return true; +} +#endif /* UNUSED */ + +FLAC__bool FLAC__bitwriter_write_utf8_uint32(FLAC__BitWriter *bw, FLAC__uint32 val) +{ + FLAC__bool ok = 1; + + FLAC__ASSERT(0 != bw); + FLAC__ASSERT(0 != bw->buffer); + + FLAC__ASSERT(!(val & 0x80000000)); /* this version only handles 31 bits */ + + if(val < 0x80) { + return FLAC__bitwriter_write_raw_uint32(bw, val, 8); + } + else if(val < 0x800) { + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xC0 | (val>>6), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (val&0x3F), 8); + } + else if(val < 0x10000) { + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xE0 | (val>>12), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | ((val>>6)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (val&0x3F), 8); + } + else if(val < 0x200000) { + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xF0 | (val>>18), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | ((val>>12)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | ((val>>6)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (val&0x3F), 8); + } + else if(val < 0x4000000) { + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xF8 | (val>>24), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | ((val>>18)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | ((val>>12)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | ((val>>6)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (val&0x3F), 8); + } + else { + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xFC | (val>>30), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | ((val>>24)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | ((val>>18)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | ((val>>12)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | ((val>>6)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (val&0x3F), 8); + } + + return ok; +} + +FLAC__bool FLAC__bitwriter_write_utf8_uint64(FLAC__BitWriter *bw, FLAC__uint64 val) +{ + FLAC__bool ok = 1; + + FLAC__ASSERT(0 != bw); + FLAC__ASSERT(0 != bw->buffer); + + FLAC__ASSERT(!(val & FLAC__U64L(0xFFFFFFF000000000))); /* this version only handles 36 bits */ + + if(val < 0x80) { + return FLAC__bitwriter_write_raw_uint32(bw, (FLAC__uint32)val, 8); + } + else if(val < 0x800) { + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xC0 | (FLAC__uint32)(val>>6), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)(val&0x3F), 8); + } + else if(val < 0x10000) { + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xE0 | (FLAC__uint32)(val>>12), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>6)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)(val&0x3F), 8); + } + else if(val < 0x200000) { + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xF0 | (FLAC__uint32)(val>>18), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>12)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>6)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)(val&0x3F), 8); + } + else if(val < 0x4000000) { + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xF8 | (FLAC__uint32)(val>>24), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>18)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>12)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>6)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)(val&0x3F), 8); + } + else if(val < 0x80000000) { + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xFC | (FLAC__uint32)(val>>30), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>24)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>18)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>12)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>6)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)(val&0x3F), 8); + } + else { + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xFE, 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>30)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>24)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>18)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>12)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>6)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)(val&0x3F), 8); + } + + return ok; +} + +FLAC__bool FLAC__bitwriter_zero_pad_to_byte_boundary(FLAC__BitWriter *bw) +{ + /* 0-pad to byte boundary */ + if(bw->bits & 7u) + return FLAC__bitwriter_write_zeroes(bw, 8 - (bw->bits & 7u)); + else + return true; +} + +#endif +/********* End of inlined file: bitwriter.c *********/ + +/********* Start of inlined file: cpu.c *********/ + +/********* Start of inlined file: juce_FlacHeader.h *********/ +// This file is included at the start of each FLAC .c file, just to do a few housekeeping +// tasks.. + +#define VERSION "1.2.1" + +#define FLAC__NO_DLL 1 + +#ifdef _MSC_VER + #pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4312) +#endif + +#if ! (defined (_WIN32) || defined (_WIN64) || defined (LINUX)) + #define FLAC__SYS_DARWIN 1 +#endif +/********* End of inlined file: juce_FlacHeader.h *********/ + +#if JUCE_USE_FLAC + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include + +#if defined FLAC__CPU_IA32 +# include +#elif defined FLAC__CPU_PPC +# if !defined FLAC__NO_ASM +# if defined FLAC__SYS_DARWIN +# include +# include +# include +# include +# include +# ifndef CPU_SUBTYPE_POWERPC_970 +# define CPU_SUBTYPE_POWERPC_970 ((cpu_subtype_t) 100) +# endif +# else /* FLAC__SYS_DARWIN */ + +# include +# include + +static sigjmp_buf jmpbuf; +static volatile sig_atomic_t canjump = 0; + +static void sigill_handler (int sig) +{ + if (!canjump) { + signal (sig, SIG_DFL); + raise (sig); + } + canjump = 0; + siglongjmp (jmpbuf, 1); +} +# endif /* FLAC__SYS_DARWIN */ +# endif /* FLAC__NO_ASM */ +#endif /* FLAC__CPU_PPC */ + +#if defined (__NetBSD__) || defined(__OpenBSD__) +#include +#include +#include +#endif + +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) +#include +#include +#endif + +#if defined(__APPLE__) +/* how to get sysctlbyname()? */ +#endif + +/* these are flags in EDX of CPUID AX=00000001 */ +static const unsigned FLAC__CPUINFO_IA32_CPUID_CMOV = 0x00008000; +static const unsigned FLAC__CPUINFO_IA32_CPUID_MMX = 0x00800000; +static const unsigned FLAC__CPUINFO_IA32_CPUID_FXSR = 0x01000000; +static const unsigned FLAC__CPUINFO_IA32_CPUID_SSE = 0x02000000; +static const unsigned FLAC__CPUINFO_IA32_CPUID_SSE2 = 0x04000000; +/* these are flags in ECX of CPUID AX=00000001 */ +static const unsigned FLAC__CPUINFO_IA32_CPUID_SSE3 = 0x00000001; +static const unsigned FLAC__CPUINFO_IA32_CPUID_SSSE3 = 0x00000200; +/* these are flags in EDX of CPUID AX=80000001 */ +static const unsigned FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_3DNOW = 0x80000000; +static const unsigned FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_EXT3DNOW = 0x40000000; +static const unsigned FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_EXTMMX = 0x00400000; + +/* + * Extra stuff needed for detection of OS support for SSE on IA-32 + */ +#if defined(FLAC__CPU_IA32) && !defined FLAC__NO_ASM && defined FLAC__HAS_NASM && !defined FLAC__NO_SSE_OS && !defined FLAC__SSE_OS +# if defined(__linux__) +/* + * If the OS doesn't support SSE, we will get here with a SIGILL. We + * modify the return address to jump over the offending SSE instruction + * and also the operation following it that indicates the instruction + * executed successfully. In this way we use no global variables and + * stay thread-safe. + * + * 3 + 3 + 6: + * 3 bytes for "xorps xmm0,xmm0" + * 3 bytes for estimate of how long the follwing "inc var" instruction is + * 6 bytes extra in case our estimate is wrong + * 12 bytes puts us in the NOP "landing zone" + */ +# undef USE_OBSOLETE_SIGCONTEXT_FLAVOR /* #define this to use the older signal handler method */ +# ifdef USE_OBSOLETE_SIGCONTEXT_FLAVOR + static void sigill_handler_sse_os(int signal, struct sigcontext sc) + { + (void)signal; + sc.eip += 3 + 3 + 6; + } +# else +# include + static void sigill_handler_sse_os(int signal, siginfo_t *si, void *uc) + { + (void)signal, (void)si; + ((ucontext_t*)uc)->uc_mcontext.gregs[14/*REG_EIP*/] += 3 + 3 + 6; + } +# endif +# elif defined(_MSC_VER) +# include +# undef USE_TRY_CATCH_FLAVOR /* #define this to use the try/catch method for catching illegal opcode exception */ +# ifdef USE_TRY_CATCH_FLAVOR +# else + LONG CALLBACK sigill_handler_sse_os(EXCEPTION_POINTERS *ep) + { + if(ep->ExceptionRecord->ExceptionCode == EXCEPTION_ILLEGAL_INSTRUCTION) { + ep->ContextRecord->Eip += 3 + 3 + 6; + return EXCEPTION_CONTINUE_EXECUTION; + } + return EXCEPTION_CONTINUE_SEARCH; + } +# endif +# endif +#endif + +void FLAC__cpu_info(FLAC__CPUInfo *info) +{ +/* + * IA32-specific + */ +#ifdef FLAC__CPU_IA32 + info->type = FLAC__CPUINFO_TYPE_IA32; +#if !defined FLAC__NO_ASM && defined FLAC__HAS_NASM + info->use_asm = true; /* we assume a minimum of 80386 with FLAC__CPU_IA32 */ + info->data.ia32.cpuid = FLAC__cpu_have_cpuid_asm_ia32()? true : false; + info->data.ia32.bswap = info->data.ia32.cpuid; /* CPUID => BSWAP since it came after */ + info->data.ia32.cmov = false; + info->data.ia32.mmx = false; + info->data.ia32.fxsr = false; + info->data.ia32.sse = false; + info->data.ia32.sse2 = false; + info->data.ia32.sse3 = false; + info->data.ia32.ssse3 = false; + info->data.ia32._3dnow = false; + info->data.ia32.ext3dnow = false; + info->data.ia32.extmmx = false; + if(info->data.ia32.cpuid) { + /* http://www.sandpile.org/ia32/cpuid.htm */ + FLAC__uint32 flags_edx, flags_ecx; + FLAC__cpu_info_asm_ia32(&flags_edx, &flags_ecx); + info->data.ia32.cmov = (flags_edx & FLAC__CPUINFO_IA32_CPUID_CMOV )? true : false; + info->data.ia32.mmx = (flags_edx & FLAC__CPUINFO_IA32_CPUID_MMX )? true : false; + info->data.ia32.fxsr = (flags_edx & FLAC__CPUINFO_IA32_CPUID_FXSR )? true : false; + info->data.ia32.sse = (flags_edx & FLAC__CPUINFO_IA32_CPUID_SSE )? true : false; + info->data.ia32.sse2 = (flags_edx & FLAC__CPUINFO_IA32_CPUID_SSE2 )? true : false; + info->data.ia32.sse3 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE3 )? true : false; + info->data.ia32.ssse3 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSSE3)? true : false; + +#ifdef FLAC__USE_3DNOW + flags_edx = FLAC__cpu_info_extended_amd_asm_ia32(); + info->data.ia32._3dnow = (flags_edx & FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_3DNOW )? true : false; + info->data.ia32.ext3dnow = (flags_edx & FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_EXT3DNOW)? true : false; + info->data.ia32.extmmx = (flags_edx & FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_EXTMMX )? true : false; +#else + info->data.ia32._3dnow = info->data.ia32.ext3dnow = info->data.ia32.extmmx = false; +#endif + +#ifdef DEBUG + fprintf(stderr, "CPU info (IA-32):\n"); + fprintf(stderr, " CPUID ...... %c\n", info->data.ia32.cpuid ? 'Y' : 'n'); + fprintf(stderr, " BSWAP ...... %c\n", info->data.ia32.bswap ? 'Y' : 'n'); + fprintf(stderr, " CMOV ....... %c\n", info->data.ia32.cmov ? 'Y' : 'n'); + fprintf(stderr, " MMX ........ %c\n", info->data.ia32.mmx ? 'Y' : 'n'); + fprintf(stderr, " FXSR ....... %c\n", info->data.ia32.fxsr ? 'Y' : 'n'); + fprintf(stderr, " SSE ........ %c\n", info->data.ia32.sse ? 'Y' : 'n'); + fprintf(stderr, " SSE2 ....... %c\n", info->data.ia32.sse2 ? 'Y' : 'n'); + fprintf(stderr, " SSE3 ....... %c\n", info->data.ia32.sse3 ? 'Y' : 'n'); + fprintf(stderr, " SSSE3 ...... %c\n", info->data.ia32.ssse3 ? 'Y' : 'n'); + fprintf(stderr, " 3DNow! ..... %c\n", info->data.ia32._3dnow ? 'Y' : 'n'); + fprintf(stderr, " 3DNow!-ext . %c\n", info->data.ia32.ext3dnow? 'Y' : 'n'); + fprintf(stderr, " 3DNow!-MMX . %c\n", info->data.ia32.extmmx ? 'Y' : 'n'); +#endif + + /* + * now have to check for OS support of SSE/SSE2 + */ + if(info->data.ia32.fxsr || info->data.ia32.sse || info->data.ia32.sse2) { +#if defined FLAC__NO_SSE_OS + /* assume user knows better than us; turn it off */ + info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; +#elif defined FLAC__SSE_OS + /* assume user knows better than us; leave as detected above */ +#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) || defined(__APPLE__) + int sse = 0; + size_t len; + /* at least one of these must work: */ + len = sizeof(sse); sse = sse || (sysctlbyname("hw.instruction_sse", &sse, &len, NULL, 0) == 0 && sse); + len = sizeof(sse); sse = sse || (sysctlbyname("hw.optional.sse" , &sse, &len, NULL, 0) == 0 && sse); /* __APPLE__ ? */ + if(!sse) + info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; +#elif defined(__NetBSD__) || defined (__OpenBSD__) +# if __NetBSD_Version__ >= 105250000 || (defined __OpenBSD__) + int val = 0, mib[2] = { CTL_MACHDEP, CPU_SSE }; + size_t len = sizeof(val); + if(sysctl(mib, 2, &val, &len, NULL, 0) < 0 || !val) + info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; + else { /* double-check SSE2 */ + mib[1] = CPU_SSE2; + len = sizeof(val); + if(sysctl(mib, 2, &val, &len, NULL, 0) < 0 || !val) + info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; + } +# else + info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; +# endif +#elif defined(__linux__) + int sse = 0; + struct sigaction sigill_save; +#ifdef USE_OBSOLETE_SIGCONTEXT_FLAVOR + if(0 == sigaction(SIGILL, NULL, &sigill_save) && signal(SIGILL, (void (*)(int))sigill_handler_sse_os) != SIG_ERR) +#else + struct sigaction sigill_sse; + sigill_sse.sa_sigaction = sigill_handler_sse_os; + __sigemptyset(&sigill_sse.sa_mask); + sigill_sse.sa_flags = SA_SIGINFO | SA_RESETHAND; /* SA_RESETHAND just in case our SIGILL return jump breaks, so we don't get stuck in a loop */ + if(0 == sigaction(SIGILL, &sigill_sse, &sigill_save)) +#endif + { + /* http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html */ + /* see sigill_handler_sse_os() for an explanation of the following: */ + asm volatile ( + "xorl %0,%0\n\t" /* for some reason, still need to do this to clear 'sse' var */ + "xorps %%xmm0,%%xmm0\n\t" /* will cause SIGILL if unsupported by OS */ + "incl %0\n\t" /* SIGILL handler will jump over this */ + /* landing zone */ + "nop\n\t" /* SIGILL jump lands here if "inc" is 9 bytes */ + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" /* SIGILL jump lands here if "inc" is 3 bytes (expected) */ + "nop\n\t" + "nop" /* SIGILL jump lands here if "inc" is 1 byte */ + : "=r"(sse) + : "r"(sse) + ); + + sigaction(SIGILL, &sigill_save, NULL); + } + + if(!sse) + info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; +#elif defined(_MSC_VER) +# ifdef USE_TRY_CATCH_FLAVOR + _try { + __asm { +# if _MSC_VER <= 1200 + /* VC6 assembler doesn't know SSE, have to emit bytecode instead */ + _emit 0x0F + _emit 0x57 + _emit 0xC0 +# else + xorps xmm0,xmm0 +# endif + } + } + _except(EXCEPTION_EXECUTE_HANDLER) { + if (_exception_code() == STATUS_ILLEGAL_INSTRUCTION) + info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; + } +# else + int sse = 0; + LPTOP_LEVEL_EXCEPTION_FILTER save = SetUnhandledExceptionFilter(sigill_handler_sse_os); + /* see GCC version above for explanation */ + /* http://msdn2.microsoft.com/en-us/library/4ks26t93.aspx */ + /* http://www.codeproject.com/cpp/gccasm.asp */ + /* http://www.hick.org/~mmiller/msvc_inline_asm.html */ + __asm { +# if _MSC_VER <= 1200 + /* VC6 assembler doesn't know SSE, have to emit bytecode instead */ + _emit 0x0F + _emit 0x57 + _emit 0xC0 +# else + xorps xmm0,xmm0 +# endif + inc sse + nop + nop + nop + nop + nop + nop + nop + nop + nop + } + SetUnhandledExceptionFilter(save); + if(!sse) + info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; +# endif +#else + /* no way to test, disable to be safe */ + info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; +#endif +#ifdef DEBUG + fprintf(stderr, " SSE OS sup . %c\n", info->data.ia32.sse ? 'Y' : 'n'); +#endif + + } + } +#else + info->use_asm = false; +#endif + +/* + * PPC-specific + */ +#elif defined FLAC__CPU_PPC + info->type = FLAC__CPUINFO_TYPE_PPC; +# if !defined FLAC__NO_ASM + info->use_asm = true; +# ifdef FLAC__USE_ALTIVEC +# if defined FLAC__SYS_DARWIN + { + int val = 0, mib[2] = { CTL_HW, HW_VECTORUNIT }; + size_t len = sizeof(val); + info->data.ppc.altivec = !(sysctl(mib, 2, &val, &len, NULL, 0) || !val); + } + { + host_basic_info_data_t hostInfo; + mach_msg_type_number_t infoCount; + + infoCount = HOST_BASIC_INFO_COUNT; + host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)&hostInfo, &infoCount); + + info->data.ppc.ppc64 = (hostInfo.cpu_type == CPU_TYPE_POWERPC) && (hostInfo.cpu_subtype == CPU_SUBTYPE_POWERPC_970); + } +# else /* FLAC__USE_ALTIVEC && !FLAC__SYS_DARWIN */ + { + /* no Darwin, do it the brute-force way */ + /* @@@@@@ this is not thread-safe; replace with SSE OS method above or remove */ + info->data.ppc.altivec = 0; + info->data.ppc.ppc64 = 0; + + signal (SIGILL, sigill_handler); + canjump = 0; + if (!sigsetjmp (jmpbuf, 1)) { + canjump = 1; + + asm volatile ( + "mtspr 256, %0\n\t" + "vand %%v0, %%v0, %%v0" + : + : "r" (-1) + ); + + info->data.ppc.altivec = 1; + } + canjump = 0; + if (!sigsetjmp (jmpbuf, 1)) { + int x = 0; + canjump = 1; + + /* PPC64 hardware implements the cntlzd instruction */ + asm volatile ("cntlzd %0, %1" : "=r" (x) : "r" (x) ); + + info->data.ppc.ppc64 = 1; + } + signal (SIGILL, SIG_DFL); /*@@@@@@ should save and restore old signal */ + } +# endif +# else /* !FLAC__USE_ALTIVEC */ + info->data.ppc.altivec = 0; + info->data.ppc.ppc64 = 0; +# endif +# else + info->use_asm = false; +# endif + +/* + * unknown CPI + */ +#else + info->type = FLAC__CPUINFO_TYPE_UNKNOWN; + info->use_asm = false; +#endif +} + +#endif +/********* End of inlined file: cpu.c *********/ + +/********* Start of inlined file: crc.c *********/ + +/********* Start of inlined file: juce_FlacHeader.h *********/ +// This file is included at the start of each FLAC .c file, just to do a few housekeeping +// tasks.. + +#define VERSION "1.2.1" + +#define FLAC__NO_DLL 1 + +#ifdef _MSC_VER + #pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4312) +#endif + +#if ! (defined (_WIN32) || defined (_WIN64) || defined (LINUX)) + #define FLAC__SYS_DARWIN 1 +#endif +/********* End of inlined file: juce_FlacHeader.h *********/ + +#if JUCE_USE_FLAC + +#if HAVE_CONFIG_H +# include +#endif + +/* CRC-8, poly = x^8 + x^2 + x^1 + x^0, init = 0 */ + +FLAC__byte const FLAC__crc8_table[256] = { + 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, + 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, + 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, + 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, + 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, + 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, + 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, + 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, + 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, + 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, + 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, + 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, + 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, + 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, + 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, + 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, + 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, + 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, + 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, + 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, + 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, + 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, + 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, + 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, + 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, + 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, + 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, + 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, + 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, + 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, + 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, + 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 +}; + +/* CRC-16, poly = x^16 + x^15 + x^2 + x^0, init = 0 */ + +unsigned FLAC__crc16_table[256] = { + 0x0000, 0x8005, 0x800f, 0x000a, 0x801b, 0x001e, 0x0014, 0x8011, + 0x8033, 0x0036, 0x003c, 0x8039, 0x0028, 0x802d, 0x8027, 0x0022, + 0x8063, 0x0066, 0x006c, 0x8069, 0x0078, 0x807d, 0x8077, 0x0072, + 0x0050, 0x8055, 0x805f, 0x005a, 0x804b, 0x004e, 0x0044, 0x8041, + 0x80c3, 0x00c6, 0x00cc, 0x80c9, 0x00d8, 0x80dd, 0x80d7, 0x00d2, + 0x00f0, 0x80f5, 0x80ff, 0x00fa, 0x80eb, 0x00ee, 0x00e4, 0x80e1, + 0x00a0, 0x80a5, 0x80af, 0x00aa, 0x80bb, 0x00be, 0x00b4, 0x80b1, + 0x8093, 0x0096, 0x009c, 0x8099, 0x0088, 0x808d, 0x8087, 0x0082, + 0x8183, 0x0186, 0x018c, 0x8189, 0x0198, 0x819d, 0x8197, 0x0192, + 0x01b0, 0x81b5, 0x81bf, 0x01ba, 0x81ab, 0x01ae, 0x01a4, 0x81a1, + 0x01e0, 0x81e5, 0x81ef, 0x01ea, 0x81fb, 0x01fe, 0x01f4, 0x81f1, + 0x81d3, 0x01d6, 0x01dc, 0x81d9, 0x01c8, 0x81cd, 0x81c7, 0x01c2, + 0x0140, 0x8145, 0x814f, 0x014a, 0x815b, 0x015e, 0x0154, 0x8151, + 0x8173, 0x0176, 0x017c, 0x8179, 0x0168, 0x816d, 0x8167, 0x0162, + 0x8123, 0x0126, 0x012c, 0x8129, 0x0138, 0x813d, 0x8137, 0x0132, + 0x0110, 0x8115, 0x811f, 0x011a, 0x810b, 0x010e, 0x0104, 0x8101, + 0x8303, 0x0306, 0x030c, 0x8309, 0x0318, 0x831d, 0x8317, 0x0312, + 0x0330, 0x8335, 0x833f, 0x033a, 0x832b, 0x032e, 0x0324, 0x8321, + 0x0360, 0x8365, 0x836f, 0x036a, 0x837b, 0x037e, 0x0374, 0x8371, + 0x8353, 0x0356, 0x035c, 0x8359, 0x0348, 0x834d, 0x8347, 0x0342, + 0x03c0, 0x83c5, 0x83cf, 0x03ca, 0x83db, 0x03de, 0x03d4, 0x83d1, + 0x83f3, 0x03f6, 0x03fc, 0x83f9, 0x03e8, 0x83ed, 0x83e7, 0x03e2, + 0x83a3, 0x03a6, 0x03ac, 0x83a9, 0x03b8, 0x83bd, 0x83b7, 0x03b2, + 0x0390, 0x8395, 0x839f, 0x039a, 0x838b, 0x038e, 0x0384, 0x8381, + 0x0280, 0x8285, 0x828f, 0x028a, 0x829b, 0x029e, 0x0294, 0x8291, + 0x82b3, 0x02b6, 0x02bc, 0x82b9, 0x02a8, 0x82ad, 0x82a7, 0x02a2, + 0x82e3, 0x02e6, 0x02ec, 0x82e9, 0x02f8, 0x82fd, 0x82f7, 0x02f2, + 0x02d0, 0x82d5, 0x82df, 0x02da, 0x82cb, 0x02ce, 0x02c4, 0x82c1, + 0x8243, 0x0246, 0x024c, 0x8249, 0x0258, 0x825d, 0x8257, 0x0252, + 0x0270, 0x8275, 0x827f, 0x027a, 0x826b, 0x026e, 0x0264, 0x8261, + 0x0220, 0x8225, 0x822f, 0x022a, 0x823b, 0x023e, 0x0234, 0x8231, + 0x8213, 0x0216, 0x021c, 0x8219, 0x0208, 0x820d, 0x8207, 0x0202 +}; + +void FLAC__crc8_update(const FLAC__byte data, FLAC__uint8 *crc) +{ + *crc = FLAC__crc8_table[*crc ^ data]; +} + +void FLAC__crc8_update_block(const FLAC__byte *data, unsigned len, FLAC__uint8 *crc) +{ + while(len--) + *crc = FLAC__crc8_table[*crc ^ *data++]; +} + +FLAC__uint8 FLAC__crc8(const FLAC__byte *data, unsigned len) +{ + FLAC__uint8 crc = 0; + + while(len--) + crc = FLAC__crc8_table[crc ^ *data++]; + + return crc; +} + +unsigned FLAC__crc16(const FLAC__byte *data, unsigned len) +{ + unsigned crc = 0; + + while(len--) + crc = ((crc<<8) ^ FLAC__crc16_table[(crc>>8) ^ *data++]) & 0xffff; + + return crc; +} + +#endif +/********* End of inlined file: crc.c *********/ + +/********* Start of inlined file: fixed.c *********/ + +/********* Start of inlined file: juce_FlacHeader.h *********/ +// This file is included at the start of each FLAC .c file, just to do a few housekeeping +// tasks.. + +#define VERSION "1.2.1" + +#define FLAC__NO_DLL 1 + +#ifdef _MSC_VER + #pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4312) +#endif + +#if ! (defined (_WIN32) || defined (_WIN64) || defined (LINUX)) + #define FLAC__SYS_DARWIN 1 +#endif +/********* End of inlined file: juce_FlacHeader.h *********/ + +#if JUCE_USE_FLAC + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include + +/********* Start of inlined file: fixed.h *********/ +#ifndef FLAC__PRIVATE__FIXED_H +#define FLAC__PRIVATE__FIXED_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +/********* Start of inlined file: float.h *********/ +#ifndef FLAC__PRIVATE__FLOAT_H +#define FLAC__PRIVATE__FLOAT_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +/* + * These typedefs make it easier to ensure that integer versions of + * the library really only contain integer operations. All the code + * in libFLAC should use FLAC__float and FLAC__double in place of + * float and double, and be protected by checks of the macro + * FLAC__INTEGER_ONLY_LIBRARY. + * + * FLAC__real is the basic floating point type used in LPC analysis. + */ +#ifndef FLAC__INTEGER_ONLY_LIBRARY +typedef double FLAC__double; +typedef float FLAC__float; +/* + * WATCHOUT: changing FLAC__real will change the signatures of many + * functions that have assembly language equivalents and break them. + */ +typedef float FLAC__real; +#else +/* + * The convention for FLAC__fixedpoint is to use the upper 16 bits + * for the integer part and lower 16 bits for the fractional part. + */ +typedef FLAC__int32 FLAC__fixedpoint; +extern const FLAC__fixedpoint FLAC__FP_ZERO; +extern const FLAC__fixedpoint FLAC__FP_ONE_HALF; +extern const FLAC__fixedpoint FLAC__FP_ONE; +extern const FLAC__fixedpoint FLAC__FP_LN2; +extern const FLAC__fixedpoint FLAC__FP_E; + +#define FLAC__fixedpoint_trunc(x) ((x)>>16) + +#define FLAC__fixedpoint_mul(x, y) ( (FLAC__fixedpoint) ( ((FLAC__int64)(x)*(FLAC__int64)(y)) >> 16 ) ) + +#define FLAC__fixedpoint_div(x, y) ( (FLAC__fixedpoint) ( ( ((FLAC__int64)(x)<<32) / (FLAC__int64)(y) ) >> 16 ) ) + +/* + * FLAC__fixedpoint_log2() + * -------------------------------------------------------------------- + * Returns the base-2 logarithm of the fixed-point number 'x' using an + * algorithm by Knuth for x >= 1.0 + * + * 'fracbits' is the number of fractional bits of 'x'. 'fracbits' must + * be < 32 and evenly divisible by 4 (0 is OK but not very precise). + * + * 'precision' roughly limits the number of iterations that are done; + * use (unsigned)(-1) for maximum precision. + * + * If 'x' is less than one -- that is, x < (1< 30 + * + * IN data[0,data_len-1] + * IN data_len + * OUT residual_bits_per_sample[0,FLAC__MAX_FIXED_ORDER] + */ +#ifndef FLAC__INTEGER_ONLY_LIBRARY +unsigned FLAC__fixed_compute_best_predictor(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); +# ifndef FLAC__NO_ASM +# ifdef FLAC__CPU_IA32 +# ifdef FLAC__HAS_NASM +unsigned FLAC__fixed_compute_best_predictor_asm_ia32_mmx_cmov(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); +# endif +# endif +# endif +unsigned FLAC__fixed_compute_best_predictor_wide(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); +#else +unsigned FLAC__fixed_compute_best_predictor(const FLAC__int32 data[], unsigned data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); +unsigned FLAC__fixed_compute_best_predictor_wide(const FLAC__int32 data[], unsigned data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); +#endif + +/* + * FLAC__fixed_compute_residual() + * -------------------------------------------------------------------- + * Compute the residual signal obtained from sutracting the predicted + * signal from the original. + * + * IN data[-order,data_len-1] original signal (NOTE THE INDICES!) + * IN data_len length of original signal + * IN order <= FLAC__MAX_FIXED_ORDER fixed-predictor order + * OUT residual[0,data_len-1] residual signal + */ +void FLAC__fixed_compute_residual(const FLAC__int32 data[], unsigned data_len, unsigned order, FLAC__int32 residual[]); + +/* + * FLAC__fixed_restore_signal() + * -------------------------------------------------------------------- + * Restore the original signal by summing the residual and the + * predictor. + * + * IN residual[0,data_len-1] residual signal + * IN data_len length of original signal + * IN order <= FLAC__MAX_FIXED_ORDER fixed-predictor order + * *** IMPORTANT: the caller must pass in the historical samples: + * IN data[-order,-1] previously-reconstructed historical samples + * OUT data[0,data_len-1] original signal + */ +void FLAC__fixed_restore_signal(const FLAC__int32 residual[], unsigned data_len, unsigned order, FLAC__int32 data[]); + +#endif +/********* End of inlined file: fixed.h *********/ + +#ifndef M_LN2 +/* math.h in VC++ doesn't seem to have this (how Microsoft is that?) */ +#define M_LN2 0.69314718055994530942 +#endif + +#ifdef min +#undef min +#endif +#define min(x,y) ((x) < (y)? (x) : (y)) + +#ifdef local_abs +#undef local_abs +#endif +#define local_abs(x) ((unsigned)((x)<0? -(x) : (x))) + +#ifdef FLAC__INTEGER_ONLY_LIBRARY +/* rbps stands for residual bits per sample + * + * (ln(2) * err) + * rbps = log (-----------) + * 2 ( n ) + */ +static FLAC__fixedpoint local__compute_rbps_integerized(FLAC__uint32 err, FLAC__uint32 n) +{ + FLAC__uint32 rbps; + unsigned bits; /* the number of bits required to represent a number */ + int fracbits; /* the number of bits of rbps that comprise the fractional part */ + + FLAC__ASSERT(sizeof(rbps) == sizeof(FLAC__fixedpoint)); + FLAC__ASSERT(err > 0); + FLAC__ASSERT(n > 0); + + FLAC__ASSERT(n <= FLAC__MAX_BLOCK_SIZE); + if(err <= n) + return 0; + /* + * The above two things tell us 1) n fits in 16 bits; 2) err/n > 1. + * These allow us later to know we won't lose too much precision in the + * fixed-point division (err< 0); + bits = FLAC__bitmath_ilog2(err)+1; + if(bits > 16) { + err >>= (bits-16); + fracbits -= (bits-16); + } + rbps = (FLAC__uint32)err; + + /* Multiply by fixed-point version of ln(2), with 16 fractional bits */ + rbps *= FLAC__FP_LN2; + fracbits += 16; + FLAC__ASSERT(fracbits >= 0); + + /* FLAC__fixedpoint_log2 requires fracbits%4 to be 0 */ + { + const int f = fracbits & 3; + if(f) { + rbps >>= f; + fracbits -= f; + } + } + + rbps = FLAC__fixedpoint_log2(rbps, fracbits, (unsigned)(-1)); + + if(rbps == 0) + return 0; + + /* + * The return value must have 16 fractional bits. Since the whole part + * of the base-2 log of a 32 bit number must fit in 5 bits, and fracbits + * must be >= -3, these assertion allows us to be able to shift rbps + * left if necessary to get 16 fracbits without losing any bits of the + * whole part of rbps. + * + * There is a slight chance due to accumulated error that the whole part + * will require 6 bits, so we use 6 in the assertion. Really though as + * long as it fits in 13 bits (32 - (16 - (-3))) we are fine. + */ + FLAC__ASSERT((int)FLAC__bitmath_ilog2(rbps)+1 <= fracbits + 6); + FLAC__ASSERT(fracbits >= -3); + + /* now shift the decimal point into place */ + if(fracbits < 16) + return rbps << (16-fracbits); + else if(fracbits > 16) + return rbps >> (fracbits-16); + else + return rbps; +} + +static FLAC__fixedpoint local__compute_rbps_wide_integerized(FLAC__uint64 err, FLAC__uint32 n) +{ + FLAC__uint32 rbps; + unsigned bits; /* the number of bits required to represent a number */ + int fracbits; /* the number of bits of rbps that comprise the fractional part */ + + FLAC__ASSERT(sizeof(rbps) == sizeof(FLAC__fixedpoint)); + FLAC__ASSERT(err > 0); + FLAC__ASSERT(n > 0); + + FLAC__ASSERT(n <= FLAC__MAX_BLOCK_SIZE); + if(err <= n) + return 0; + /* + * The above two things tell us 1) n fits in 16 bits; 2) err/n > 1. + * These allow us later to know we won't lose too much precision in the + * fixed-point division (err< 0); + bits = FLAC__bitmath_ilog2_wide(err)+1; + if(bits > 16) { + err >>= (bits-16); + fracbits -= (bits-16); + } + rbps = (FLAC__uint32)err; + + /* Multiply by fixed-point version of ln(2), with 16 fractional bits */ + rbps *= FLAC__FP_LN2; + fracbits += 16; + FLAC__ASSERT(fracbits >= 0); + + /* FLAC__fixedpoint_log2 requires fracbits%4 to be 0 */ + { + const int f = fracbits & 3; + if(f) { + rbps >>= f; + fracbits -= f; + } + } + + rbps = FLAC__fixedpoint_log2(rbps, fracbits, (unsigned)(-1)); + + if(rbps == 0) + return 0; + + /* + * The return value must have 16 fractional bits. Since the whole part + * of the base-2 log of a 32 bit number must fit in 5 bits, and fracbits + * must be >= -3, these assertion allows us to be able to shift rbps + * left if necessary to get 16 fracbits without losing any bits of the + * whole part of rbps. + * + * There is a slight chance due to accumulated error that the whole part + * will require 6 bits, so we use 6 in the assertion. Really though as + * long as it fits in 13 bits (32 - (16 - (-3))) we are fine. + */ + FLAC__ASSERT((int)FLAC__bitmath_ilog2(rbps)+1 <= fracbits + 6); + FLAC__ASSERT(fracbits >= -3); + + /* now shift the decimal point into place */ + if(fracbits < 16) + return rbps << (16-fracbits); + else if(fracbits > 16) + return rbps >> (fracbits-16); + else + return rbps; +} +#endif + +#ifndef FLAC__INTEGER_ONLY_LIBRARY +unsigned FLAC__fixed_compute_best_predictor(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]) +#else +unsigned FLAC__fixed_compute_best_predictor(const FLAC__int32 data[], unsigned data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]) +#endif +{ + FLAC__int32 last_error_0 = data[-1]; + FLAC__int32 last_error_1 = data[-1] - data[-2]; + FLAC__int32 last_error_2 = last_error_1 - (data[-2] - data[-3]); + FLAC__int32 last_error_3 = last_error_2 - (data[-2] - 2*data[-3] + data[-4]); + FLAC__int32 error, save; + FLAC__uint32 total_error_0 = 0, total_error_1 = 0, total_error_2 = 0, total_error_3 = 0, total_error_4 = 0; + unsigned i, order; + + for(i = 0; i < data_len; i++) { + error = data[i] ; total_error_0 += local_abs(error); save = error; + error -= last_error_0; total_error_1 += local_abs(error); last_error_0 = save; save = error; + error -= last_error_1; total_error_2 += local_abs(error); last_error_1 = save; save = error; + error -= last_error_2; total_error_3 += local_abs(error); last_error_2 = save; save = error; + error -= last_error_3; total_error_4 += local_abs(error); last_error_3 = save; + } + + if(total_error_0 < min(min(min(total_error_1, total_error_2), total_error_3), total_error_4)) + order = 0; + else if(total_error_1 < min(min(total_error_2, total_error_3), total_error_4)) + order = 1; + else if(total_error_2 < min(total_error_3, total_error_4)) + order = 2; + else if(total_error_3 < total_error_4) + order = 3; + else + order = 4; + + /* Estimate the expected number of bits per residual signal sample. */ + /* 'total_error*' is linearly related to the variance of the residual */ + /* signal, so we use it directly to compute E(|x|) */ + FLAC__ASSERT(data_len > 0 || total_error_0 == 0); + FLAC__ASSERT(data_len > 0 || total_error_1 == 0); + FLAC__ASSERT(data_len > 0 || total_error_2 == 0); + FLAC__ASSERT(data_len > 0 || total_error_3 == 0); + FLAC__ASSERT(data_len > 0 || total_error_4 == 0); +#ifndef FLAC__INTEGER_ONLY_LIBRARY + residual_bits_per_sample[0] = (FLAC__float)((total_error_0 > 0) ? log(M_LN2 * (FLAC__double)total_error_0 / (FLAC__double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[1] = (FLAC__float)((total_error_1 > 0) ? log(M_LN2 * (FLAC__double)total_error_1 / (FLAC__double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[2] = (FLAC__float)((total_error_2 > 0) ? log(M_LN2 * (FLAC__double)total_error_2 / (FLAC__double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[3] = (FLAC__float)((total_error_3 > 0) ? log(M_LN2 * (FLAC__double)total_error_3 / (FLAC__double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[4] = (FLAC__float)((total_error_4 > 0) ? log(M_LN2 * (FLAC__double)total_error_4 / (FLAC__double)data_len) / M_LN2 : 0.0); +#else + residual_bits_per_sample[0] = (total_error_0 > 0) ? local__compute_rbps_integerized(total_error_0, data_len) : 0; + residual_bits_per_sample[1] = (total_error_1 > 0) ? local__compute_rbps_integerized(total_error_1, data_len) : 0; + residual_bits_per_sample[2] = (total_error_2 > 0) ? local__compute_rbps_integerized(total_error_2, data_len) : 0; + residual_bits_per_sample[3] = (total_error_3 > 0) ? local__compute_rbps_integerized(total_error_3, data_len) : 0; + residual_bits_per_sample[4] = (total_error_4 > 0) ? local__compute_rbps_integerized(total_error_4, data_len) : 0; +#endif + + return order; +} + +#ifndef FLAC__INTEGER_ONLY_LIBRARY +unsigned FLAC__fixed_compute_best_predictor_wide(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]) +#else +unsigned FLAC__fixed_compute_best_predictor_wide(const FLAC__int32 data[], unsigned data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]) +#endif +{ + FLAC__int32 last_error_0 = data[-1]; + FLAC__int32 last_error_1 = data[-1] - data[-2]; + FLAC__int32 last_error_2 = last_error_1 - (data[-2] - data[-3]); + FLAC__int32 last_error_3 = last_error_2 - (data[-2] - 2*data[-3] + data[-4]); + FLAC__int32 error, save; + /* total_error_* are 64-bits to avoid overflow when encoding + * erratic signals when the bits-per-sample and blocksize are + * large. + */ + FLAC__uint64 total_error_0 = 0, total_error_1 = 0, total_error_2 = 0, total_error_3 = 0, total_error_4 = 0; + unsigned i, order; + + for(i = 0; i < data_len; i++) { + error = data[i] ; total_error_0 += local_abs(error); save = error; + error -= last_error_0; total_error_1 += local_abs(error); last_error_0 = save; save = error; + error -= last_error_1; total_error_2 += local_abs(error); last_error_1 = save; save = error; + error -= last_error_2; total_error_3 += local_abs(error); last_error_2 = save; save = error; + error -= last_error_3; total_error_4 += local_abs(error); last_error_3 = save; + } + + if(total_error_0 < min(min(min(total_error_1, total_error_2), total_error_3), total_error_4)) + order = 0; + else if(total_error_1 < min(min(total_error_2, total_error_3), total_error_4)) + order = 1; + else if(total_error_2 < min(total_error_3, total_error_4)) + order = 2; + else if(total_error_3 < total_error_4) + order = 3; + else + order = 4; + + /* Estimate the expected number of bits per residual signal sample. */ + /* 'total_error*' is linearly related to the variance of the residual */ + /* signal, so we use it directly to compute E(|x|) */ + FLAC__ASSERT(data_len > 0 || total_error_0 == 0); + FLAC__ASSERT(data_len > 0 || total_error_1 == 0); + FLAC__ASSERT(data_len > 0 || total_error_2 == 0); + FLAC__ASSERT(data_len > 0 || total_error_3 == 0); + FLAC__ASSERT(data_len > 0 || total_error_4 == 0); +#ifndef FLAC__INTEGER_ONLY_LIBRARY +#if defined _MSC_VER || defined __MINGW32__ + /* with MSVC you have to spoon feed it the casting */ + residual_bits_per_sample[0] = (FLAC__float)((total_error_0 > 0) ? log(M_LN2 * (FLAC__double)(FLAC__int64)total_error_0 / (FLAC__double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[1] = (FLAC__float)((total_error_1 > 0) ? log(M_LN2 * (FLAC__double)(FLAC__int64)total_error_1 / (FLAC__double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[2] = (FLAC__float)((total_error_2 > 0) ? log(M_LN2 * (FLAC__double)(FLAC__int64)total_error_2 / (FLAC__double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[3] = (FLAC__float)((total_error_3 > 0) ? log(M_LN2 * (FLAC__double)(FLAC__int64)total_error_3 / (FLAC__double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[4] = (FLAC__float)((total_error_4 > 0) ? log(M_LN2 * (FLAC__double)(FLAC__int64)total_error_4 / (FLAC__double)data_len) / M_LN2 : 0.0); +#else + residual_bits_per_sample[0] = (FLAC__float)((total_error_0 > 0) ? log(M_LN2 * (FLAC__double)total_error_0 / (FLAC__double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[1] = (FLAC__float)((total_error_1 > 0) ? log(M_LN2 * (FLAC__double)total_error_1 / (FLAC__double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[2] = (FLAC__float)((total_error_2 > 0) ? log(M_LN2 * (FLAC__double)total_error_2 / (FLAC__double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[3] = (FLAC__float)((total_error_3 > 0) ? log(M_LN2 * (FLAC__double)total_error_3 / (FLAC__double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[4] = (FLAC__float)((total_error_4 > 0) ? log(M_LN2 * (FLAC__double)total_error_4 / (FLAC__double)data_len) / M_LN2 : 0.0); +#endif +#else + residual_bits_per_sample[0] = (total_error_0 > 0) ? local__compute_rbps_wide_integerized(total_error_0, data_len) : 0; + residual_bits_per_sample[1] = (total_error_1 > 0) ? local__compute_rbps_wide_integerized(total_error_1, data_len) : 0; + residual_bits_per_sample[2] = (total_error_2 > 0) ? local__compute_rbps_wide_integerized(total_error_2, data_len) : 0; + residual_bits_per_sample[3] = (total_error_3 > 0) ? local__compute_rbps_wide_integerized(total_error_3, data_len) : 0; + residual_bits_per_sample[4] = (total_error_4 > 0) ? local__compute_rbps_wide_integerized(total_error_4, data_len) : 0; +#endif + + return order; +} + +void FLAC__fixed_compute_residual(const FLAC__int32 data[], unsigned data_len, unsigned order, FLAC__int32 residual[]) +{ + const int idata_len = (int)data_len; + int i; + + switch(order) { + case 0: + FLAC__ASSERT(sizeof(residual[0]) == sizeof(data[0])); + memcpy(residual, data, sizeof(residual[0])*data_len); + break; + case 1: + for(i = 0; i < idata_len; i++) + residual[i] = data[i] - data[i-1]; + break; + case 2: + for(i = 0; i < idata_len; i++) +#if 1 /* OPT: may be faster with some compilers on some systems */ + residual[i] = data[i] - (data[i-1] << 1) + data[i-2]; +#else + residual[i] = data[i] - 2*data[i-1] + data[i-2]; +#endif + break; + case 3: + for(i = 0; i < idata_len; i++) +#if 1 /* OPT: may be faster with some compilers on some systems */ + residual[i] = data[i] - (((data[i-1]-data[i-2])<<1) + (data[i-1]-data[i-2])) - data[i-3]; +#else + residual[i] = data[i] - 3*data[i-1] + 3*data[i-2] - data[i-3]; +#endif + break; + case 4: + for(i = 0; i < idata_len; i++) +#if 1 /* OPT: may be faster with some compilers on some systems */ + residual[i] = data[i] - ((data[i-1]+data[i-3])<<2) + ((data[i-2]<<2) + (data[i-2]<<1)) + data[i-4]; +#else + residual[i] = data[i] - 4*data[i-1] + 6*data[i-2] - 4*data[i-3] + data[i-4]; +#endif + break; + default: + FLAC__ASSERT(0); + } +} + +void FLAC__fixed_restore_signal(const FLAC__int32 residual[], unsigned data_len, unsigned order, FLAC__int32 data[]) +{ + int i, idata_len = (int)data_len; + + switch(order) { + case 0: + FLAC__ASSERT(sizeof(residual[0]) == sizeof(data[0])); + memcpy(data, residual, sizeof(residual[0])*data_len); + break; + case 1: + for(i = 0; i < idata_len; i++) + data[i] = residual[i] + data[i-1]; + break; + case 2: + for(i = 0; i < idata_len; i++) +#if 1 /* OPT: may be faster with some compilers on some systems */ + data[i] = residual[i] + (data[i-1]<<1) - data[i-2]; +#else + data[i] = residual[i] + 2*data[i-1] - data[i-2]; +#endif + break; + case 3: + for(i = 0; i < idata_len; i++) +#if 1 /* OPT: may be faster with some compilers on some systems */ + data[i] = residual[i] + (((data[i-1]-data[i-2])<<1) + (data[i-1]-data[i-2])) + data[i-3]; +#else + data[i] = residual[i] + 3*data[i-1] - 3*data[i-2] + data[i-3]; +#endif + break; + case 4: + for(i = 0; i < idata_len; i++) +#if 1 /* OPT: may be faster with some compilers on some systems */ + data[i] = residual[i] + ((data[i-1]+data[i-3])<<2) - ((data[i-2]<<2) + (data[i-2]<<1)) - data[i-4]; +#else + data[i] = residual[i] + 4*data[i-1] - 6*data[i-2] + 4*data[i-3] - data[i-4]; +#endif + break; + default: + FLAC__ASSERT(0); + } +} + +#endif +/********* End of inlined file: fixed.c *********/ + +/********* Start of inlined file: float.c *********/ + +/********* Start of inlined file: juce_FlacHeader.h *********/ +// This file is included at the start of each FLAC .c file, just to do a few housekeeping +// tasks.. + +#define VERSION "1.2.1" + +#define FLAC__NO_DLL 1 + +#ifdef _MSC_VER + #pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4312) +#endif + +#if ! (defined (_WIN32) || defined (_WIN64) || defined (LINUX)) + #define FLAC__SYS_DARWIN 1 +#endif +/********* End of inlined file: juce_FlacHeader.h *********/ + +#if JUCE_USE_FLAC + +#if HAVE_CONFIG_H +# include +#endif + +#ifdef FLAC__INTEGER_ONLY_LIBRARY + +/* adjust for compilers that can't understand using LLU suffix for uint64_t literals */ +#ifdef _MSC_VER +#define FLAC__U64L(x) x +#else +#define FLAC__U64L(x) x##LLU +#endif + +const FLAC__fixedpoint FLAC__FP_ZERO = 0; +const FLAC__fixedpoint FLAC__FP_ONE_HALF = 0x00008000; +const FLAC__fixedpoint FLAC__FP_ONE = 0x00010000; +const FLAC__fixedpoint FLAC__FP_LN2 = 45426; +const FLAC__fixedpoint FLAC__FP_E = 178145; + +/* Lookup tables for Knuth's logarithm algorithm */ +#define LOG2_LOOKUP_PRECISION 16 +static const FLAC__uint32 log2_lookup[][LOG2_LOOKUP_PRECISION] = { + { + /* + * 0 fraction bits + */ + /* undefined */ 0x00000000, + /* lg(2/1) = */ 0x00000001, + /* lg(4/3) = */ 0x00000000, + /* lg(8/7) = */ 0x00000000, + /* lg(16/15) = */ 0x00000000, + /* lg(32/31) = */ 0x00000000, + /* lg(64/63) = */ 0x00000000, + /* lg(128/127) = */ 0x00000000, + /* lg(256/255) = */ 0x00000000, + /* lg(512/511) = */ 0x00000000, + /* lg(1024/1023) = */ 0x00000000, + /* lg(2048/2047) = */ 0x00000000, + /* lg(4096/4095) = */ 0x00000000, + /* lg(8192/8191) = */ 0x00000000, + /* lg(16384/16383) = */ 0x00000000, + /* lg(32768/32767) = */ 0x00000000 + }, + { + /* + * 4 fraction bits + */ + /* undefined */ 0x00000000, + /* lg(2/1) = */ 0x00000010, + /* lg(4/3) = */ 0x00000007, + /* lg(8/7) = */ 0x00000003, + /* lg(16/15) = */ 0x00000001, + /* lg(32/31) = */ 0x00000001, + /* lg(64/63) = */ 0x00000000, + /* lg(128/127) = */ 0x00000000, + /* lg(256/255) = */ 0x00000000, + /* lg(512/511) = */ 0x00000000, + /* lg(1024/1023) = */ 0x00000000, + /* lg(2048/2047) = */ 0x00000000, + /* lg(4096/4095) = */ 0x00000000, + /* lg(8192/8191) = */ 0x00000000, + /* lg(16384/16383) = */ 0x00000000, + /* lg(32768/32767) = */ 0x00000000 + }, + { + /* + * 8 fraction bits + */ + /* undefined */ 0x00000000, + /* lg(2/1) = */ 0x00000100, + /* lg(4/3) = */ 0x0000006a, + /* lg(8/7) = */ 0x00000031, + /* lg(16/15) = */ 0x00000018, + /* lg(32/31) = */ 0x0000000c, + /* lg(64/63) = */ 0x00000006, + /* lg(128/127) = */ 0x00000003, + /* lg(256/255) = */ 0x00000001, + /* lg(512/511) = */ 0x00000001, + /* lg(1024/1023) = */ 0x00000000, + /* lg(2048/2047) = */ 0x00000000, + /* lg(4096/4095) = */ 0x00000000, + /* lg(8192/8191) = */ 0x00000000, + /* lg(16384/16383) = */ 0x00000000, + /* lg(32768/32767) = */ 0x00000000 + }, + { + /* + * 12 fraction bits + */ + /* undefined */ 0x00000000, + /* lg(2/1) = */ 0x00001000, + /* lg(4/3) = */ 0x000006a4, + /* lg(8/7) = */ 0x00000315, + /* lg(16/15) = */ 0x0000017d, + /* lg(32/31) = */ 0x000000bc, + /* lg(64/63) = */ 0x0000005d, + /* lg(128/127) = */ 0x0000002e, + /* lg(256/255) = */ 0x00000017, + /* lg(512/511) = */ 0x0000000c, + /* lg(1024/1023) = */ 0x00000006, + /* lg(2048/2047) = */ 0x00000003, + /* lg(4096/4095) = */ 0x00000001, + /* lg(8192/8191) = */ 0x00000001, + /* lg(16384/16383) = */ 0x00000000, + /* lg(32768/32767) = */ 0x00000000 + }, + { + /* + * 16 fraction bits + */ + /* undefined */ 0x00000000, + /* lg(2/1) = */ 0x00010000, + /* lg(4/3) = */ 0x00006a40, + /* lg(8/7) = */ 0x00003151, + /* lg(16/15) = */ 0x000017d6, + /* lg(32/31) = */ 0x00000bba, + /* lg(64/63) = */ 0x000005d1, + /* lg(128/127) = */ 0x000002e6, + /* lg(256/255) = */ 0x00000172, + /* lg(512/511) = */ 0x000000b9, + /* lg(1024/1023) = */ 0x0000005c, + /* lg(2048/2047) = */ 0x0000002e, + /* lg(4096/4095) = */ 0x00000017, + /* lg(8192/8191) = */ 0x0000000c, + /* lg(16384/16383) = */ 0x00000006, + /* lg(32768/32767) = */ 0x00000003 + }, + { + /* + * 20 fraction bits + */ + /* undefined */ 0x00000000, + /* lg(2/1) = */ 0x00100000, + /* lg(4/3) = */ 0x0006a3fe, + /* lg(8/7) = */ 0x00031513, + /* lg(16/15) = */ 0x00017d60, + /* lg(32/31) = */ 0x0000bb9d, + /* lg(64/63) = */ 0x00005d10, + /* lg(128/127) = */ 0x00002e59, + /* lg(256/255) = */ 0x00001721, + /* lg(512/511) = */ 0x00000b8e, + /* lg(1024/1023) = */ 0x000005c6, + /* lg(2048/2047) = */ 0x000002e3, + /* lg(4096/4095) = */ 0x00000171, + /* lg(8192/8191) = */ 0x000000b9, + /* lg(16384/16383) = */ 0x0000005c, + /* lg(32768/32767) = */ 0x0000002e + }, + { + /* + * 24 fraction bits + */ + /* undefined */ 0x00000000, + /* lg(2/1) = */ 0x01000000, + /* lg(4/3) = */ 0x006a3fe6, + /* lg(8/7) = */ 0x00315130, + /* lg(16/15) = */ 0x0017d605, + /* lg(32/31) = */ 0x000bb9ca, + /* lg(64/63) = */ 0x0005d0fc, + /* lg(128/127) = */ 0x0002e58f, + /* lg(256/255) = */ 0x0001720e, + /* lg(512/511) = */ 0x0000b8d8, + /* lg(1024/1023) = */ 0x00005c61, + /* lg(2048/2047) = */ 0x00002e2d, + /* lg(4096/4095) = */ 0x00001716, + /* lg(8192/8191) = */ 0x00000b8b, + /* lg(16384/16383) = */ 0x000005c5, + /* lg(32768/32767) = */ 0x000002e3 + }, + { + /* + * 28 fraction bits + */ + /* undefined */ 0x00000000, + /* lg(2/1) = */ 0x10000000, + /* lg(4/3) = */ 0x06a3fe5c, + /* lg(8/7) = */ 0x03151301, + /* lg(16/15) = */ 0x017d6049, + /* lg(32/31) = */ 0x00bb9ca6, + /* lg(64/63) = */ 0x005d0fba, + /* lg(128/127) = */ 0x002e58f7, + /* lg(256/255) = */ 0x001720da, + /* lg(512/511) = */ 0x000b8d87, + /* lg(1024/1023) = */ 0x0005c60b, + /* lg(2048/2047) = */ 0x0002e2d7, + /* lg(4096/4095) = */ 0x00017160, + /* lg(8192/8191) = */ 0x0000b8ad, + /* lg(16384/16383) = */ 0x00005c56, + /* lg(32768/32767) = */ 0x00002e2b + } +}; + +#if 0 +static const FLAC__uint64 log2_lookup_wide[] = { + { + /* + * 32 fraction bits + */ + /* undefined */ 0x00000000, + /* lg(2/1) = */ FLAC__U64L(0x100000000), + /* lg(4/3) = */ FLAC__U64L(0x6a3fe5c6), + /* lg(8/7) = */ FLAC__U64L(0x31513015), + /* lg(16/15) = */ FLAC__U64L(0x17d60497), + /* lg(32/31) = */ FLAC__U64L(0x0bb9ca65), + /* lg(64/63) = */ FLAC__U64L(0x05d0fba2), + /* lg(128/127) = */ FLAC__U64L(0x02e58f74), + /* lg(256/255) = */ FLAC__U64L(0x01720d9c), + /* lg(512/511) = */ FLAC__U64L(0x00b8d875), + /* lg(1024/1023) = */ FLAC__U64L(0x005c60aa), + /* lg(2048/2047) = */ FLAC__U64L(0x002e2d72), + /* lg(4096/4095) = */ FLAC__U64L(0x00171600), + /* lg(8192/8191) = */ FLAC__U64L(0x000b8ad2), + /* lg(16384/16383) = */ FLAC__U64L(0x0005c55d), + /* lg(32768/32767) = */ FLAC__U64L(0x0002e2ac) + }, + { + /* + * 48 fraction bits + */ + /* undefined */ 0x00000000, + /* lg(2/1) = */ FLAC__U64L(0x1000000000000), + /* lg(4/3) = */ FLAC__U64L(0x6a3fe5c60429), + /* lg(8/7) = */ FLAC__U64L(0x315130157f7a), + /* lg(16/15) = */ FLAC__U64L(0x17d60496cfbb), + /* lg(32/31) = */ FLAC__U64L(0xbb9ca64ecac), + /* lg(64/63) = */ FLAC__U64L(0x5d0fba187cd), + /* lg(128/127) = */ FLAC__U64L(0x2e58f7441ee), + /* lg(256/255) = */ FLAC__U64L(0x1720d9c06a8), + /* lg(512/511) = */ FLAC__U64L(0xb8d8752173), + /* lg(1024/1023) = */ FLAC__U64L(0x5c60aa252e), + /* lg(2048/2047) = */ FLAC__U64L(0x2e2d71b0d8), + /* lg(4096/4095) = */ FLAC__U64L(0x1716001719), + /* lg(8192/8191) = */ FLAC__U64L(0xb8ad1de1b), + /* lg(16384/16383) = */ FLAC__U64L(0x5c55d640d), + /* lg(32768/32767) = */ FLAC__U64L(0x2e2abcf52) + } +}; +#endif + +FLAC__uint32 FLAC__fixedpoint_log2(FLAC__uint32 x, unsigned fracbits, unsigned precision) +{ + const FLAC__uint32 ONE = (1u << fracbits); + const FLAC__uint32 *table = log2_lookup[fracbits >> 2]; + + FLAC__ASSERT(fracbits < 32); + FLAC__ASSERT((fracbits & 0x3) == 0); + + if(x < ONE) + return 0; + + if(precision > LOG2_LOOKUP_PRECISION) + precision = LOG2_LOOKUP_PRECISION; + + /* Knuth's algorithm for computing logarithms, optimized for base-2 with lookup tables */ + { + FLAC__uint32 y = 0; + FLAC__uint32 z = x >> 1, k = 1; + while (x > ONE && k < precision) { + if (x - z >= ONE) { + x -= z; + z = x >> k; + y += table[k]; + } + else { + z >>= 1; + k++; + } + } + return y; + } +} + +#endif /* defined FLAC__INTEGER_ONLY_LIBRARY */ + +#endif +/********* End of inlined file: float.c *********/ + +/********* Start of inlined file: format.c *********/ + +/********* Start of inlined file: juce_FlacHeader.h *********/ +// This file is included at the start of each FLAC .c file, just to do a few housekeeping +// tasks.. + +#define VERSION "1.2.1" + +#define FLAC__NO_DLL 1 + +#ifdef _MSC_VER + #pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4312) +#endif + +#if ! (defined (_WIN32) || defined (_WIN64) || defined (LINUX)) + #define FLAC__SYS_DARWIN 1 +#endif +/********* End of inlined file: juce_FlacHeader.h *********/ + +#if JUCE_USE_FLAC + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include /* for qsort() */ +#include /* for memset() */ + +#ifndef FLaC__INLINE +#define FLaC__INLINE +#endif + +#ifdef min +#undef min +#endif +#define min(a,b) ((a)<(b)?(a):(b)) + +/* adjust for compilers that can't understand using LLU suffix for uint64_t literals */ +#ifdef _MSC_VER +#define FLAC__U64L(x) x +#else +#define FLAC__U64L(x) x##LLU +#endif + +/* VERSION should come from configure */ +FLAC_API const char *FLAC__VERSION_STRING = VERSION + +; + +#if defined _MSC_VER || defined __BORLANDC__ || defined __MINW32__ +/* yet one more hack because of MSVC6: */ +FLAC_API const char *FLAC__VENDOR_STRING = "reference libFLAC 1.2.1 20070917"; +#else +FLAC_API const char *FLAC__VENDOR_STRING = "reference libFLAC " VERSION " 20070917"; +#endif + +FLAC_API const FLAC__byte FLAC__STREAM_SYNC_STRING[4] = { 'f','L','a','C' }; +FLAC_API const unsigned FLAC__STREAM_SYNC = 0x664C6143; +FLAC_API const unsigned FLAC__STREAM_SYNC_LEN = 32; /* bits */ + +FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN = 16; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN = 16; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN = 24; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN = 24; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN = 20; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN = 3; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN = 5; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN = 36; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MD5SUM_LEN = 128; /* bits */ + +FLAC_API const unsigned FLAC__STREAM_METADATA_APPLICATION_ID_LEN = 32; /* bits */ + +FLAC_API const unsigned FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN = 64; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN = 64; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN = 16; /* bits */ + +FLAC_API const FLAC__uint64 FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER = FLAC__U64L(0xffffffffffffffff); + +FLAC_API const unsigned FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN = 32; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN = 32; /* bits */ + +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN = 64; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN = 8; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN = 3*8; /* bits */ + +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN = 64; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN = 8; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN = 12*8; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN = 1; /* bit */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN = 1; /* bit */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN = 6+13*8; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN = 8; /* bits */ + +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN = 128*8; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN = 64; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN = 1; /* bit */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN = 7+258*8; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN = 8; /* bits */ + +FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_TYPE_LEN = 32; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN = 32; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN = 32; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN = 32; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN = 32; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN = 32; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_COLORS_LEN = 32; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN = 32; /* bits */ + +FLAC_API const unsigned FLAC__STREAM_METADATA_IS_LAST_LEN = 1; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_TYPE_LEN = 7; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_LENGTH_LEN = 24; /* bits */ + +FLAC_API const unsigned FLAC__FRAME_HEADER_SYNC = 0x3ffe; +FLAC_API const unsigned FLAC__FRAME_HEADER_SYNC_LEN = 14; /* bits */ +FLAC_API const unsigned FLAC__FRAME_HEADER_RESERVED_LEN = 1; /* bits */ +FLAC_API const unsigned FLAC__FRAME_HEADER_BLOCKING_STRATEGY_LEN = 1; /* bits */ +FLAC_API const unsigned FLAC__FRAME_HEADER_BLOCK_SIZE_LEN = 4; /* bits */ +FLAC_API const unsigned FLAC__FRAME_HEADER_SAMPLE_RATE_LEN = 4; /* bits */ +FLAC_API const unsigned FLAC__FRAME_HEADER_CHANNEL_ASSIGNMENT_LEN = 4; /* bits */ +FLAC_API const unsigned FLAC__FRAME_HEADER_BITS_PER_SAMPLE_LEN = 3; /* bits */ +FLAC_API const unsigned FLAC__FRAME_HEADER_ZERO_PAD_LEN = 1; /* bits */ +FLAC_API const unsigned FLAC__FRAME_HEADER_CRC_LEN = 8; /* bits */ + +FLAC_API const unsigned FLAC__FRAME_FOOTER_CRC_LEN = 16; /* bits */ + +FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_TYPE_LEN = 2; /* bits */ +FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN = 4; /* bits */ +FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN = 4; /* bits */ +FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN = 5; /* bits */ +FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_RAW_LEN = 5; /* bits */ + +FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER = 15; /* == (1< FLAC__MAX_SAMPLE_RATE) { + return false; + } + else + return true; +} + +FLAC_API FLAC__bool FLAC__format_sample_rate_is_subset(unsigned sample_rate) +{ + if( + !FLAC__format_sample_rate_is_valid(sample_rate) || + ( + sample_rate >= (1u << 16) && + !(sample_rate % 1000 == 0 || sample_rate % 10 == 0) + ) + ) { + return false; + } + else + return true; +} + +/* @@@@ add to unit tests; it is already indirectly tested by the metadata_object tests */ +FLAC_API FLAC__bool FLAC__format_seektable_is_legal(const FLAC__StreamMetadata_SeekTable *seek_table) +{ + unsigned i; + FLAC__uint64 prev_sample_number = 0; + FLAC__bool got_prev = false; + + FLAC__ASSERT(0 != seek_table); + + for(i = 0; i < seek_table->num_points; i++) { + if(got_prev) { + if( + seek_table->points[i].sample_number != FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER && + seek_table->points[i].sample_number <= prev_sample_number + ) + return false; + } + prev_sample_number = seek_table->points[i].sample_number; + got_prev = true; + } + + return true; +} + +/* used as the sort predicate for qsort() */ +static int seekpoint_compare_(const FLAC__StreamMetadata_SeekPoint *l, const FLAC__StreamMetadata_SeekPoint *r) +{ + /* we don't just 'return l->sample_number - r->sample_number' since the result (FLAC__int64) might overflow an 'int' */ + if(l->sample_number == r->sample_number) + return 0; + else if(l->sample_number < r->sample_number) + return -1; + else + return 1; +} + +/* @@@@ add to unit tests; it is already indirectly tested by the metadata_object tests */ +FLAC_API unsigned FLAC__format_seektable_sort(FLAC__StreamMetadata_SeekTable *seek_table) +{ + unsigned i, j; + FLAC__bool first; + + FLAC__ASSERT(0 != seek_table); + + /* sort the seekpoints */ + qsort(seek_table->points, seek_table->num_points, sizeof(FLAC__StreamMetadata_SeekPoint), (int (*)(const void *, const void *))seekpoint_compare_); + + /* uniquify the seekpoints */ + first = true; + for(i = j = 0; i < seek_table->num_points; i++) { + if(seek_table->points[i].sample_number != FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER) { + if(!first) { + if(seek_table->points[i].sample_number == seek_table->points[j-1].sample_number) + continue; + } + } + first = false; + seek_table->points[j++] = seek_table->points[i]; + } + + for(i = j; i < seek_table->num_points; i++) { + seek_table->points[i].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER; + seek_table->points[i].stream_offset = 0; + seek_table->points[i].frame_samples = 0; + } + + return j; +} + +/* + * also disallows non-shortest-form encodings, c.f. + * http://www.unicode.org/versions/corrigendum1.html + * and a more clear explanation at the end of this section: + * http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + */ +static FLaC__INLINE unsigned utf8len_(const FLAC__byte *utf8) +{ + FLAC__ASSERT(0 != utf8); + if ((utf8[0] & 0x80) == 0) { + return 1; + } + else if ((utf8[0] & 0xE0) == 0xC0 && (utf8[1] & 0xC0) == 0x80) { + if ((utf8[0] & 0xFE) == 0xC0) /* overlong sequence check */ + return 0; + return 2; + } + else if ((utf8[0] & 0xF0) == 0xE0 && (utf8[1] & 0xC0) == 0x80 && (utf8[2] & 0xC0) == 0x80) { + if (utf8[0] == 0xE0 && (utf8[1] & 0xE0) == 0x80) /* overlong sequence check */ + return 0; + /* illegal surrogates check (U+D800...U+DFFF and U+FFFE...U+FFFF) */ + if (utf8[0] == 0xED && (utf8[1] & 0xE0) == 0xA0) /* D800-DFFF */ + return 0; + if (utf8[0] == 0xEF && utf8[1] == 0xBF && (utf8[2] & 0xFE) == 0xBE) /* FFFE-FFFF */ + return 0; + return 3; + } + else if ((utf8[0] & 0xF8) == 0xF0 && (utf8[1] & 0xC0) == 0x80 && (utf8[2] & 0xC0) == 0x80 && (utf8[3] & 0xC0) == 0x80) { + if (utf8[0] == 0xF0 && (utf8[1] & 0xF0) == 0x80) /* overlong sequence check */ + return 0; + return 4; + } + else if ((utf8[0] & 0xFC) == 0xF8 && (utf8[1] & 0xC0) == 0x80 && (utf8[2] & 0xC0) == 0x80 && (utf8[3] & 0xC0) == 0x80 && (utf8[4] & 0xC0) == 0x80) { + if (utf8[0] == 0xF8 && (utf8[1] & 0xF8) == 0x80) /* overlong sequence check */ + return 0; + return 5; + } + else if ((utf8[0] & 0xFE) == 0xFC && (utf8[1] & 0xC0) == 0x80 && (utf8[2] & 0xC0) == 0x80 && (utf8[3] & 0xC0) == 0x80 && (utf8[4] & 0xC0) == 0x80 && (utf8[5] & 0xC0) == 0x80) { + if (utf8[0] == 0xFC && (utf8[1] & 0xFC) == 0x80) /* overlong sequence check */ + return 0; + return 6; + } + else { + return 0; + } +} + +FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_name_is_legal(const char *name) +{ + char c; + for(c = *name; c; c = *(++name)) + if(c < 0x20 || c == 0x3d || c > 0x7d) + return false; + return true; +} + +FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_value_is_legal(const FLAC__byte *value, unsigned length) +{ + if(length == (unsigned)(-1)) { + while(*value) { + unsigned n = utf8len_(value); + if(n == 0) + return false; + value += n; + } + } + else { + const FLAC__byte *end = value + length; + while(value < end) { + unsigned n = utf8len_(value); + if(n == 0) + return false; + value += n; + } + if(value != end) + return false; + } + return true; +} + +FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_is_legal(const FLAC__byte *entry, unsigned length) +{ + const FLAC__byte *s, *end; + + for(s = entry, end = s + length; s < end && *s != '='; s++) { + if(*s < 0x20 || *s > 0x7D) + return false; + } + if(s == end) + return false; + + s++; /* skip '=' */ + + while(s < end) { + unsigned n = utf8len_(s); + if(n == 0) + return false; + s += n; + } + if(s != end) + return false; + + return true; +} + +/* @@@@ add to unit tests; it is already indirectly tested by the metadata_object tests */ +FLAC_API FLAC__bool FLAC__format_cuesheet_is_legal(const FLAC__StreamMetadata_CueSheet *cue_sheet, FLAC__bool check_cd_da_subset, const char **violation) +{ + unsigned i, j; + + if(check_cd_da_subset) { + if(cue_sheet->lead_in < 2 * 44100) { + if(violation) *violation = "CD-DA cue sheet must have a lead-in length of at least 2 seconds"; + return false; + } + if(cue_sheet->lead_in % 588 != 0) { + if(violation) *violation = "CD-DA cue sheet lead-in length must be evenly divisible by 588 samples"; + return false; + } + } + + if(cue_sheet->num_tracks == 0) { + if(violation) *violation = "cue sheet must have at least one track (the lead-out)"; + return false; + } + + if(check_cd_da_subset && cue_sheet->tracks[cue_sheet->num_tracks-1].number != 170) { + if(violation) *violation = "CD-DA cue sheet must have a lead-out track number 170 (0xAA)"; + return false; + } + + for(i = 0; i < cue_sheet->num_tracks; i++) { + if(cue_sheet->tracks[i].number == 0) { + if(violation) *violation = "cue sheet may not have a track number 0"; + return false; + } + + if(check_cd_da_subset) { + if(!((cue_sheet->tracks[i].number >= 1 && cue_sheet->tracks[i].number <= 99) || cue_sheet->tracks[i].number == 170)) { + if(violation) *violation = "CD-DA cue sheet track number must be 1-99 or 170"; + return false; + } + } + + if(check_cd_da_subset && cue_sheet->tracks[i].offset % 588 != 0) { + if(violation) { + if(i == cue_sheet->num_tracks-1) /* the lead-out track... */ + *violation = "CD-DA cue sheet lead-out offset must be evenly divisible by 588 samples"; + else + *violation = "CD-DA cue sheet track offset must be evenly divisible by 588 samples"; + } + return false; + } + + if(i < cue_sheet->num_tracks - 1) { + if(cue_sheet->tracks[i].num_indices == 0) { + if(violation) *violation = "cue sheet track must have at least one index point"; + return false; + } + + if(cue_sheet->tracks[i].indices[0].number > 1) { + if(violation) *violation = "cue sheet track's first index number must be 0 or 1"; + return false; + } + } + + for(j = 0; j < cue_sheet->tracks[i].num_indices; j++) { + if(check_cd_da_subset && cue_sheet->tracks[i].indices[j].offset % 588 != 0) { + if(violation) *violation = "CD-DA cue sheet track index offset must be evenly divisible by 588 samples"; + return false; + } + + if(j > 0) { + if(cue_sheet->tracks[i].indices[j].number != cue_sheet->tracks[i].indices[j-1].number + 1) { + if(violation) *violation = "cue sheet track index numbers must increase by 1"; + return false; + } + } + } + } + + return true; +} + +/* @@@@ add to unit tests; it is already indirectly tested by the metadata_object tests */ +FLAC_API FLAC__bool FLAC__format_picture_is_legal(const FLAC__StreamMetadata_Picture *picture, const char **violation) +{ + char *p; + FLAC__byte *b; + + for(p = picture->mime_type; *p; p++) { + if(*p < 0x20 || *p > 0x7e) { + if(violation) *violation = "MIME type string must contain only printable ASCII characters (0x20-0x7e)"; + return false; + } + } + + for(b = picture->description; *b; ) { + unsigned n = utf8len_(b); + if(n == 0) { + if(violation) *violation = "description string must be valid UTF-8"; + return false; + } + b += n; + } + + return true; +} + +/* + * These routines are private to libFLAC + */ +unsigned FLAC__format_get_max_rice_partition_order(unsigned blocksize, unsigned predictor_order) +{ + return + FLAC__format_get_max_rice_partition_order_from_blocksize_limited_max_and_predictor_order( + FLAC__format_get_max_rice_partition_order_from_blocksize(blocksize), + blocksize, + predictor_order + ); +} + +unsigned FLAC__format_get_max_rice_partition_order_from_blocksize(unsigned blocksize) +{ + unsigned max_rice_partition_order = 0; + while(!(blocksize & 1)) { + max_rice_partition_order++; + blocksize >>= 1; + } + return min(FLAC__MAX_RICE_PARTITION_ORDER, max_rice_partition_order); +} + +unsigned FLAC__format_get_max_rice_partition_order_from_blocksize_limited_max_and_predictor_order(unsigned limit, unsigned blocksize, unsigned predictor_order) +{ + unsigned max_rice_partition_order = limit; + + while(max_rice_partition_order > 0 && (blocksize >> max_rice_partition_order) <= predictor_order) + max_rice_partition_order--; + + FLAC__ASSERT( + (max_rice_partition_order == 0 && blocksize >= predictor_order) || + (max_rice_partition_order > 0 && blocksize >> max_rice_partition_order > predictor_order) + ); + + return max_rice_partition_order; +} + +void FLAC__format_entropy_coding_method_partitioned_rice_contents_init(FLAC__EntropyCodingMethod_PartitionedRiceContents *object) +{ + FLAC__ASSERT(0 != object); + + object->parameters = 0; + object->raw_bits = 0; + object->capacity_by_order = 0; +} + +void FLAC__format_entropy_coding_method_partitioned_rice_contents_clear(FLAC__EntropyCodingMethod_PartitionedRiceContents *object) +{ + FLAC__ASSERT(0 != object); + + if(0 != object->parameters) + free(object->parameters); + if(0 != object->raw_bits) + free(object->raw_bits); + FLAC__format_entropy_coding_method_partitioned_rice_contents_init(object); +} + +FLAC__bool FLAC__format_entropy_coding_method_partitioned_rice_contents_ensure_size(FLAC__EntropyCodingMethod_PartitionedRiceContents *object, unsigned max_partition_order) +{ + FLAC__ASSERT(0 != object); + + FLAC__ASSERT(object->capacity_by_order > 0 || (0 == object->parameters && 0 == object->raw_bits)); + + if(object->capacity_by_order < max_partition_order) { + if(0 == (object->parameters = (unsigned*)realloc(object->parameters, sizeof(unsigned)*(1 << max_partition_order)))) + return false; + if(0 == (object->raw_bits = (unsigned*)realloc(object->raw_bits, sizeof(unsigned)*(1 << max_partition_order)))) + return false; + memset(object->raw_bits, 0, sizeof(unsigned)*(1 << max_partition_order)); + object->capacity_by_order = max_partition_order; + } + + return true; +} + +#endif +/********* End of inlined file: format.c *********/ + +/********* Start of inlined file: lpc_flac.c *********/ + +/********* Start of inlined file: juce_FlacHeader.h *********/ +// This file is included at the start of each FLAC .c file, just to do a few housekeeping +// tasks.. + +#define VERSION "1.2.1" + +#define FLAC__NO_DLL 1 + +#ifdef _MSC_VER + #pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4312) +#endif + +#if ! (defined (_WIN32) || defined (_WIN64) || defined (LINUX)) + #define FLAC__SYS_DARWIN 1 +#endif +/********* End of inlined file: juce_FlacHeader.h *********/ + +#if JUCE_USE_FLAC + +#if HAVE_CONFIG_H +# include +#endif + +#include + +/********* Start of inlined file: lpc.h *********/ +#ifndef FLAC__PRIVATE__LPC_H +#define FLAC__PRIVATE__LPC_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef FLAC__INTEGER_ONLY_LIBRARY + +/* + * FLAC__lpc_window_data() + * -------------------------------------------------------------------- + * Applies the given window to the data. + * OPT: asm implementation + * + * IN in[0,data_len-1] + * IN window[0,data_len-1] + * OUT out[0,lag-1] + * IN data_len + */ +void FLAC__lpc_window_data(const FLAC__int32 in[], const FLAC__real window[], FLAC__real out[], unsigned data_len); + +/* + * FLAC__lpc_compute_autocorrelation() + * -------------------------------------------------------------------- + * Compute the autocorrelation for lags between 0 and lag-1. + * Assumes data[] outside of [0,data_len-1] == 0. + * Asserts that lag > 0. + * + * IN data[0,data_len-1] + * IN data_len + * IN 0 < lag <= data_len + * OUT autoc[0,lag-1] + */ +void FLAC__lpc_compute_autocorrelation(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); +#ifndef FLAC__NO_ASM +# ifdef FLAC__CPU_IA32 +# ifdef FLAC__HAS_NASM +void FLAC__lpc_compute_autocorrelation_asm_ia32(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_4(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_8(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_12(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_asm_ia32_3dnow(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); +# endif +# endif +#endif + +/* + * FLAC__lpc_compute_lp_coefficients() + * -------------------------------------------------------------------- + * Computes LP coefficients for orders 1..max_order. + * Do not call if autoc[0] == 0.0. This means the signal is zero + * and there is no point in calculating a predictor. + * + * IN autoc[0,max_order] autocorrelation values + * IN 0 < max_order <= FLAC__MAX_LPC_ORDER max LP order to compute + * OUT lp_coeff[0,max_order-1][0,max_order-1] LP coefficients for each order + * *** IMPORTANT: + * *** lp_coeff[0,max_order-1][max_order,FLAC__MAX_LPC_ORDER-1] are untouched + * OUT error[0,max_order-1] error for each order (more + * specifically, the variance of + * the error signal times # of + * samples in the signal) + * + * Example: if max_order is 9, the LP coefficients for order 9 will be + * in lp_coeff[8][0,8], the LP coefficients for order 8 will be + * in lp_coeff[7][0,7], etc. + */ +void FLAC__lpc_compute_lp_coefficients(const FLAC__real autoc[], unsigned *max_order, FLAC__real lp_coeff[][FLAC__MAX_LPC_ORDER], FLAC__double error[]); + +/* + * FLAC__lpc_quantize_coefficients() + * -------------------------------------------------------------------- + * Quantizes the LP coefficients. NOTE: precision + bits_per_sample + * must be less than 32 (sizeof(FLAC__int32)*8). + * + * IN lp_coeff[0,order-1] LP coefficients + * IN order LP order + * IN FLAC__MIN_QLP_COEFF_PRECISION < precision + * desired precision (in bits, including sign + * bit) of largest coefficient + * OUT qlp_coeff[0,order-1] quantized coefficients + * OUT shift # of bits to shift right to get approximated + * LP coefficients. NOTE: could be negative. + * RETURN 0 => quantization OK + * 1 => coefficients require too much shifting for *shift to + * fit in the LPC subframe header. 'shift' is unset. + * 2 => coefficients are all zero, which is bad. 'shift' is + * unset. + */ +int FLAC__lpc_quantize_coefficients(const FLAC__real lp_coeff[], unsigned order, unsigned precision, FLAC__int32 qlp_coeff[], int *shift); + +/* + * FLAC__lpc_compute_residual_from_qlp_coefficients() + * -------------------------------------------------------------------- + * Compute the residual signal obtained from sutracting the predicted + * signal from the original. + * + * IN data[-order,data_len-1] original signal (NOTE THE INDICES!) + * IN data_len length of original signal + * IN qlp_coeff[0,order-1] quantized LP coefficients + * IN order > 0 LP order + * IN lp_quantization quantization of LP coefficients in bits + * OUT residual[0,data_len-1] residual signal + */ +void FLAC__lpc_compute_residual_from_qlp_coefficients(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); +void FLAC__lpc_compute_residual_from_qlp_coefficients_wide(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); +#ifndef FLAC__NO_ASM +# ifdef FLAC__CPU_IA32 +# ifdef FLAC__HAS_NASM +void FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); +void FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32_mmx(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); +# endif +# endif +#endif + +#endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */ + +/* + * FLAC__lpc_restore_signal() + * -------------------------------------------------------------------- + * Restore the original signal by summing the residual and the + * predictor. + * + * IN residual[0,data_len-1] residual signal + * IN data_len length of original signal + * IN qlp_coeff[0,order-1] quantized LP coefficients + * IN order > 0 LP order + * IN lp_quantization quantization of LP coefficients in bits + * *** IMPORTANT: the caller must pass in the historical samples: + * IN data[-order,-1] previously-reconstructed historical samples + * OUT data[0,data_len-1] original signal + */ +void FLAC__lpc_restore_signal(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); +void FLAC__lpc_restore_signal_wide(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); +#ifndef FLAC__NO_ASM +# ifdef FLAC__CPU_IA32 +# ifdef FLAC__HAS_NASM +void FLAC__lpc_restore_signal_asm_ia32(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); +void FLAC__lpc_restore_signal_asm_ia32_mmx(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); +# endif /* FLAC__HAS_NASM */ +# elif defined FLAC__CPU_PPC +void FLAC__lpc_restore_signal_asm_ppc_altivec_16(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); +void FLAC__lpc_restore_signal_asm_ppc_altivec_16_order8(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); +# endif/* FLAC__CPU_IA32 || FLAC__CPU_PPC */ +#endif /* FLAC__NO_ASM */ + +#ifndef FLAC__INTEGER_ONLY_LIBRARY + +/* + * FLAC__lpc_compute_expected_bits_per_residual_sample() + * -------------------------------------------------------------------- + * Compute the expected number of bits per residual signal sample + * based on the LP error (which is related to the residual variance). + * + * IN lpc_error >= 0.0 error returned from calculating LP coefficients + * IN total_samples > 0 # of samples in residual signal + * RETURN expected bits per sample + */ +FLAC__double FLAC__lpc_compute_expected_bits_per_residual_sample(FLAC__double lpc_error, unsigned total_samples); +FLAC__double FLAC__lpc_compute_expected_bits_per_residual_sample_with_error_scale(FLAC__double lpc_error, FLAC__double error_scale); + +/* + * FLAC__lpc_compute_best_order() + * -------------------------------------------------------------------- + * Compute the best order from the array of signal errors returned + * during coefficient computation. + * + * IN lpc_error[0,max_order-1] >= 0.0 error returned from calculating LP coefficients + * IN max_order > 0 max LP order + * IN total_samples > 0 # of samples in residual signal + * IN overhead_bits_per_order # of bits overhead for each increased LP order + * (includes warmup sample size and quantized LP coefficient) + * RETURN [1,max_order] best order + */ +unsigned FLAC__lpc_compute_best_order(const FLAC__double lpc_error[], unsigned max_order, unsigned total_samples, unsigned overhead_bits_per_order); + +#endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */ + +#endif +/********* End of inlined file: lpc.h *********/ + +#if defined DEBUG || defined FLAC__OVERFLOW_DETECT || defined FLAC__OVERFLOW_DETECT_VERBOSE +#include +#endif + +#ifndef FLAC__INTEGER_ONLY_LIBRARY + +#ifndef M_LN2 +/* math.h in VC++ doesn't seem to have this (how Microsoft is that?) */ +#define M_LN2 0.69314718055994530942 +#endif + +/* OPT: #undef'ing this may improve the speed on some architectures */ +#define FLAC__LPC_UNROLLED_FILTER_LOOPS + +void FLAC__lpc_window_data(const FLAC__int32 in[], const FLAC__real window[], FLAC__real out[], unsigned data_len) +{ + unsigned i; + for(i = 0; i < data_len; i++) + out[i] = in[i] * window[i]; +} + +void FLAC__lpc_compute_autocorrelation(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]) +{ + /* a readable, but slower, version */ +#if 0 + FLAC__real d; + unsigned i; + + FLAC__ASSERT(lag > 0); + FLAC__ASSERT(lag <= data_len); + + /* + * Technically we should subtract the mean first like so: + * for(i = 0; i < data_len; i++) + * data[i] -= mean; + * but it appears not to make enough of a difference to matter, and + * most signals are already closely centered around zero + */ + while(lag--) { + for(i = lag, d = 0.0; i < data_len; i++) + d += data[i] * data[i - lag]; + autoc[lag] = d; + } +#endif + + /* + * this version tends to run faster because of better data locality + * ('data_len' is usually much larger than 'lag') + */ + FLAC__real d; + unsigned sample, coeff; + const unsigned limit = data_len - lag; + + FLAC__ASSERT(lag > 0); + FLAC__ASSERT(lag <= data_len); + + for(coeff = 0; coeff < lag; coeff++) + autoc[coeff] = 0.0; + for(sample = 0; sample <= limit; sample++) { + d = data[sample]; + for(coeff = 0; coeff < lag; coeff++) + autoc[coeff] += d * data[sample+coeff]; + } + for(; sample < data_len; sample++) { + d = data[sample]; + for(coeff = 0; coeff < data_len - sample; coeff++) + autoc[coeff] += d * data[sample+coeff]; + } +} + +void FLAC__lpc_compute_lp_coefficients(const FLAC__real autoc[], unsigned *max_order, FLAC__real lp_coeff[][FLAC__MAX_LPC_ORDER], FLAC__double error[]) +{ + unsigned i, j; + FLAC__double r, err, ref[FLAC__MAX_LPC_ORDER], lpc[FLAC__MAX_LPC_ORDER]; + + FLAC__ASSERT(0 != max_order); + FLAC__ASSERT(0 < *max_order); + FLAC__ASSERT(*max_order <= FLAC__MAX_LPC_ORDER); + FLAC__ASSERT(autoc[0] != 0.0); + + err = autoc[0]; + + for(i = 0; i < *max_order; i++) { + /* Sum up this iteration's reflection coefficient. */ + r = -autoc[i+1]; + for(j = 0; j < i; j++) + r -= lpc[j] * autoc[i-j]; + ref[i] = (r/=err); + + /* Update LPC coefficients and total error. */ + lpc[i]=r; + for(j = 0; j < (i>>1); j++) { + FLAC__double tmp = lpc[j]; + lpc[j] += r * lpc[i-1-j]; + lpc[i-1-j] += r * tmp; + } + if(i & 1) + lpc[j] += lpc[j] * r; + + err *= (1.0 - r * r); + + /* save this order */ + for(j = 0; j <= i; j++) + lp_coeff[i][j] = (FLAC__real)(-lpc[j]); /* negate FIR filter coeff to get predictor coeff */ + error[i] = err; + + /* see SF bug #1601812 http://sourceforge.net/tracker/index.php?func=detail&aid=1601812&group_id=13478&atid=113478 */ + if(err == 0.0) { + *max_order = i+1; + return; + } + } +} + +int FLAC__lpc_quantize_coefficients(const FLAC__real lp_coeff[], unsigned order, unsigned precision, FLAC__int32 qlp_coeff[], int *shift) +{ + unsigned i; + FLAC__double cmax; + FLAC__int32 qmax, qmin; + + FLAC__ASSERT(precision > 0); + FLAC__ASSERT(precision >= FLAC__MIN_QLP_COEFF_PRECISION); + + /* drop one bit for the sign; from here on out we consider only |lp_coeff[i]| */ + precision--; + qmax = 1 << precision; + qmin = -qmax; + qmax--; + + /* calc cmax = max( |lp_coeff[i]| ) */ + cmax = 0.0; + for(i = 0; i < order; i++) { + const FLAC__double d = fabs(lp_coeff[i]); + if(d > cmax) + cmax = d; + } + + if(cmax <= 0.0) { + /* => coefficients are all 0, which means our constant-detect didn't work */ + return 2; + } + else { + const int max_shiftlimit = (1 << (FLAC__SUBFRAME_LPC_QLP_SHIFT_LEN-1)) - 1; + const int min_shiftlimit = -max_shiftlimit - 1; + int log2cmax; + + (void)frexp(cmax, &log2cmax); + log2cmax--; + *shift = (int)precision - log2cmax - 1; + + if(*shift > max_shiftlimit) + *shift = max_shiftlimit; + else if(*shift < min_shiftlimit) + return 1; + } + + if(*shift >= 0) { + FLAC__double error = 0.0; + FLAC__int32 q; + for(i = 0; i < order; i++) { + error += lp_coeff[i] * (1 << *shift); +#if 1 /* unfortunately lround() is C99 */ + if(error >= 0.0) + q = (FLAC__int32)(error + 0.5); + else + q = (FLAC__int32)(error - 0.5); +#else + q = lround(error); +#endif +#ifdef FLAC__OVERFLOW_DETECT + if(q > qmax+1) /* we expect q==qmax+1 occasionally due to rounding */ + fprintf(stderr,"FLAC__lpc_quantize_coefficients: quantizer overflow: q>qmax %d>%d shift=%d cmax=%f precision=%u lpc[%u]=%f\n",q,qmax,*shift,cmax,precision+1,i,lp_coeff[i]); + else if(q < qmin) + fprintf(stderr,"FLAC__lpc_quantize_coefficients: quantizer overflow: q qmax) + q = qmax; + else if(q < qmin) + q = qmin; + error -= q; + qlp_coeff[i] = q; + } + } + /* negative shift is very rare but due to design flaw, negative shift is + * a NOP in the decoder, so it must be handled specially by scaling down + * coeffs + */ + else { + const int nshift = -(*shift); + FLAC__double error = 0.0; + FLAC__int32 q; +#ifdef DEBUG + fprintf(stderr,"FLAC__lpc_quantize_coefficients: negative shift=%d order=%u cmax=%f\n", *shift, order, cmax); +#endif + for(i = 0; i < order; i++) { + error += lp_coeff[i] / (1 << nshift); +#if 1 /* unfortunately lround() is C99 */ + if(error >= 0.0) + q = (FLAC__int32)(error + 0.5); + else + q = (FLAC__int32)(error - 0.5); +#else + q = lround(error); +#endif +#ifdef FLAC__OVERFLOW_DETECT + if(q > qmax+1) /* we expect q==qmax+1 occasionally due to rounding */ + fprintf(stderr,"FLAC__lpc_quantize_coefficients: quantizer overflow: q>qmax %d>%d shift=%d cmax=%f precision=%u lpc[%u]=%f\n",q,qmax,*shift,cmax,precision+1,i,lp_coeff[i]); + else if(q < qmin) + fprintf(stderr,"FLAC__lpc_quantize_coefficients: quantizer overflow: q qmax) + q = qmax; + else if(q < qmin) + q = qmin; + error -= q; + qlp_coeff[i] = q; + } + *shift = 0; + } + + return 0; +} + +void FLAC__lpc_compute_residual_from_qlp_coefficients(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]) +#if defined(FLAC__OVERFLOW_DETECT) || !defined(FLAC__LPC_UNROLLED_FILTER_LOOPS) +{ + FLAC__int64 sumo; + unsigned i, j; + FLAC__int32 sum; + const FLAC__int32 *history; + +#ifdef FLAC__OVERFLOW_DETECT_VERBOSE + fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients: data_len=%d, order=%u, lpq=%d",data_len,order,lp_quantization); + for(i=0;i 0); + + for(i = 0; i < data_len; i++) { + sumo = 0; + sum = 0; + history = data; + for(j = 0; j < order; j++) { + sum += qlp_coeff[j] * (*(--history)); + sumo += (FLAC__int64)qlp_coeff[j] * (FLAC__int64)(*history); +#if defined _MSC_VER + if(sumo > 2147483647I64 || sumo < -2147483648I64) + fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients: OVERFLOW, i=%u, j=%u, c=%d, d=%d, sumo=%I64d\n",i,j,qlp_coeff[j],*history,sumo); +#else + if(sumo > 2147483647ll || sumo < -2147483648ll) + fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients: OVERFLOW, i=%u, j=%u, c=%d, d=%d, sumo=%lld\n",i,j,qlp_coeff[j],*history,(long long)sumo); +#endif + } + *(residual++) = *(data++) - (sum >> lp_quantization); + } + + /* Here's a slower but clearer version: + for(i = 0; i < data_len; i++) { + sum = 0; + for(j = 0; j < order; j++) + sum += qlp_coeff[j] * data[i-j-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + */ +} +#else /* fully unrolled version for normal use */ +{ + int i; + FLAC__int32 sum; + + FLAC__ASSERT(order > 0); + FLAC__ASSERT(order <= 32); + + /* + * We do unique versions up to 12th order since that's the subset limit. + * Also they are roughly ordered to match frequency of occurrence to + * minimize branching. + */ + if(order <= 12) { + if(order > 8) { + if(order > 10) { + if(order == 12) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[11] * data[i-12]; + sum += qlp_coeff[10] * data[i-11]; + sum += qlp_coeff[9] * data[i-10]; + sum += qlp_coeff[8] * data[i-9]; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + else { /* order == 11 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[10] * data[i-11]; + sum += qlp_coeff[9] * data[i-10]; + sum += qlp_coeff[8] * data[i-9]; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + } + else { + if(order == 10) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[9] * data[i-10]; + sum += qlp_coeff[8] * data[i-9]; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + else { /* order == 9 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[8] * data[i-9]; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + } + } + else if(order > 4) { + if(order > 6) { + if(order == 8) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + else { /* order == 7 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + } + else { + if(order == 6) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + else { /* order == 5 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + } + } + else { + if(order > 2) { + if(order == 4) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + else { /* order == 3 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + } + else { + if(order == 2) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + else { /* order == 1 */ + for(i = 0; i < (int)data_len; i++) + residual[i] = data[i] - ((qlp_coeff[0] * data[i-1]) >> lp_quantization); + } + } + } + } + else { /* order > 12 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + switch(order) { + case 32: sum += qlp_coeff[31] * data[i-32]; + case 31: sum += qlp_coeff[30] * data[i-31]; + case 30: sum += qlp_coeff[29] * data[i-30]; + case 29: sum += qlp_coeff[28] * data[i-29]; + case 28: sum += qlp_coeff[27] * data[i-28]; + case 27: sum += qlp_coeff[26] * data[i-27]; + case 26: sum += qlp_coeff[25] * data[i-26]; + case 25: sum += qlp_coeff[24] * data[i-25]; + case 24: sum += qlp_coeff[23] * data[i-24]; + case 23: sum += qlp_coeff[22] * data[i-23]; + case 22: sum += qlp_coeff[21] * data[i-22]; + case 21: sum += qlp_coeff[20] * data[i-21]; + case 20: sum += qlp_coeff[19] * data[i-20]; + case 19: sum += qlp_coeff[18] * data[i-19]; + case 18: sum += qlp_coeff[17] * data[i-18]; + case 17: sum += qlp_coeff[16] * data[i-17]; + case 16: sum += qlp_coeff[15] * data[i-16]; + case 15: sum += qlp_coeff[14] * data[i-15]; + case 14: sum += qlp_coeff[13] * data[i-14]; + case 13: sum += qlp_coeff[12] * data[i-13]; + sum += qlp_coeff[11] * data[i-12]; + sum += qlp_coeff[10] * data[i-11]; + sum += qlp_coeff[ 9] * data[i-10]; + sum += qlp_coeff[ 8] * data[i- 9]; + sum += qlp_coeff[ 7] * data[i- 8]; + sum += qlp_coeff[ 6] * data[i- 7]; + sum += qlp_coeff[ 5] * data[i- 6]; + sum += qlp_coeff[ 4] * data[i- 5]; + sum += qlp_coeff[ 3] * data[i- 4]; + sum += qlp_coeff[ 2] * data[i- 3]; + sum += qlp_coeff[ 1] * data[i- 2]; + sum += qlp_coeff[ 0] * data[i- 1]; + } + residual[i] = data[i] - (sum >> lp_quantization); + } + } +} +#endif + +void FLAC__lpc_compute_residual_from_qlp_coefficients_wide(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]) +#if defined(FLAC__OVERFLOW_DETECT) || !defined(FLAC__LPC_UNROLLED_FILTER_LOOPS) +{ + unsigned i, j; + FLAC__int64 sum; + const FLAC__int32 *history; + +#ifdef FLAC__OVERFLOW_DETECT_VERBOSE + fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients_wide: data_len=%d, order=%u, lpq=%d",data_len,order,lp_quantization); + for(i=0;i 0); + + for(i = 0; i < data_len; i++) { + sum = 0; + history = data; + for(j = 0; j < order; j++) + sum += (FLAC__int64)qlp_coeff[j] * (FLAC__int64)(*(--history)); + if(FLAC__bitmath_silog2_wide(sum >> lp_quantization) > 32) { +#if defined _MSC_VER + fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients_wide: OVERFLOW, i=%u, sum=%I64d\n", i, sum >> lp_quantization); +#else + fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients_wide: OVERFLOW, i=%u, sum=%lld\n", i, (long long)(sum >> lp_quantization)); +#endif + break; + } + if(FLAC__bitmath_silog2_wide((FLAC__int64)(*data) - (sum >> lp_quantization)) > 32) { +#if defined _MSC_VER + fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients_wide: OVERFLOW, i=%u, data=%d, sum=%I64d, residual=%I64d\n", i, *data, sum >> lp_quantization, (FLAC__int64)(*data) - (sum >> lp_quantization)); +#else + fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients_wide: OVERFLOW, i=%u, data=%d, sum=%lld, residual=%lld\n", i, *data, (long long)(sum >> lp_quantization), (long long)((FLAC__int64)(*data) - (sum >> lp_quantization))); +#endif + break; + } + *(residual++) = *(data++) - (FLAC__int32)(sum >> lp_quantization); + } +} +#else /* fully unrolled version for normal use */ +{ + int i; + FLAC__int64 sum; + + FLAC__ASSERT(order > 0); + FLAC__ASSERT(order <= 32); + + /* + * We do unique versions up to 12th order since that's the subset limit. + * Also they are roughly ordered to match frequency of occurrence to + * minimize branching. + */ + if(order <= 12) { + if(order > 8) { + if(order > 10) { + if(order == 12) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[11] * (FLAC__int64)data[i-12]; + sum += qlp_coeff[10] * (FLAC__int64)data[i-11]; + sum += qlp_coeff[9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 11 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[10] * (FLAC__int64)data[i-11]; + sum += qlp_coeff[9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + } + else { + if(order == 10) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 9 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + } + } + else if(order > 4) { + if(order > 6) { + if(order == 8) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 7 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + } + else { + if(order == 6) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 5 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + } + } + else { + if(order > 2) { + if(order == 4) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 3 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + } + else { + if(order == 2) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 1 */ + for(i = 0; i < (int)data_len; i++) + residual[i] = data[i] - (FLAC__int32)((qlp_coeff[0] * (FLAC__int64)data[i-1]) >> lp_quantization); + } + } + } + } + else { /* order > 12 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + switch(order) { + case 32: sum += qlp_coeff[31] * (FLAC__int64)data[i-32]; + case 31: sum += qlp_coeff[30] * (FLAC__int64)data[i-31]; + case 30: sum += qlp_coeff[29] * (FLAC__int64)data[i-30]; + case 29: sum += qlp_coeff[28] * (FLAC__int64)data[i-29]; + case 28: sum += qlp_coeff[27] * (FLAC__int64)data[i-28]; + case 27: sum += qlp_coeff[26] * (FLAC__int64)data[i-27]; + case 26: sum += qlp_coeff[25] * (FLAC__int64)data[i-26]; + case 25: sum += qlp_coeff[24] * (FLAC__int64)data[i-25]; + case 24: sum += qlp_coeff[23] * (FLAC__int64)data[i-24]; + case 23: sum += qlp_coeff[22] * (FLAC__int64)data[i-23]; + case 22: sum += qlp_coeff[21] * (FLAC__int64)data[i-22]; + case 21: sum += qlp_coeff[20] * (FLAC__int64)data[i-21]; + case 20: sum += qlp_coeff[19] * (FLAC__int64)data[i-20]; + case 19: sum += qlp_coeff[18] * (FLAC__int64)data[i-19]; + case 18: sum += qlp_coeff[17] * (FLAC__int64)data[i-18]; + case 17: sum += qlp_coeff[16] * (FLAC__int64)data[i-17]; + case 16: sum += qlp_coeff[15] * (FLAC__int64)data[i-16]; + case 15: sum += qlp_coeff[14] * (FLAC__int64)data[i-15]; + case 14: sum += qlp_coeff[13] * (FLAC__int64)data[i-14]; + case 13: sum += qlp_coeff[12] * (FLAC__int64)data[i-13]; + sum += qlp_coeff[11] * (FLAC__int64)data[i-12]; + sum += qlp_coeff[10] * (FLAC__int64)data[i-11]; + sum += qlp_coeff[ 9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[ 8] * (FLAC__int64)data[i- 9]; + sum += qlp_coeff[ 7] * (FLAC__int64)data[i- 8]; + sum += qlp_coeff[ 6] * (FLAC__int64)data[i- 7]; + sum += qlp_coeff[ 5] * (FLAC__int64)data[i- 6]; + sum += qlp_coeff[ 4] * (FLAC__int64)data[i- 5]; + sum += qlp_coeff[ 3] * (FLAC__int64)data[i- 4]; + sum += qlp_coeff[ 2] * (FLAC__int64)data[i- 3]; + sum += qlp_coeff[ 1] * (FLAC__int64)data[i- 2]; + sum += qlp_coeff[ 0] * (FLAC__int64)data[i- 1]; + } + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } +} +#endif + +#endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */ + +void FLAC__lpc_restore_signal(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]) +#if defined(FLAC__OVERFLOW_DETECT) || !defined(FLAC__LPC_UNROLLED_FILTER_LOOPS) +{ + FLAC__int64 sumo; + unsigned i, j; + FLAC__int32 sum; + const FLAC__int32 *r = residual, *history; + +#ifdef FLAC__OVERFLOW_DETECT_VERBOSE + fprintf(stderr,"FLAC__lpc_restore_signal: data_len=%d, order=%u, lpq=%d",data_len,order,lp_quantization); + for(i=0;i 0); + + for(i = 0; i < data_len; i++) { + sumo = 0; + sum = 0; + history = data; + for(j = 0; j < order; j++) { + sum += qlp_coeff[j] * (*(--history)); + sumo += (FLAC__int64)qlp_coeff[j] * (FLAC__int64)(*history); +#if defined _MSC_VER + if(sumo > 2147483647I64 || sumo < -2147483648I64) + fprintf(stderr,"FLAC__lpc_restore_signal: OVERFLOW, i=%u, j=%u, c=%d, d=%d, sumo=%I64d\n",i,j,qlp_coeff[j],*history,sumo); +#else + if(sumo > 2147483647ll || sumo < -2147483648ll) + fprintf(stderr,"FLAC__lpc_restore_signal: OVERFLOW, i=%u, j=%u, c=%d, d=%d, sumo=%lld\n",i,j,qlp_coeff[j],*history,(long long)sumo); +#endif + } + *(data++) = *(r++) + (sum >> lp_quantization); + } + + /* Here's a slower but clearer version: + for(i = 0; i < data_len; i++) { + sum = 0; + for(j = 0; j < order; j++) + sum += qlp_coeff[j] * data[i-j-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + */ +} +#else /* fully unrolled version for normal use */ +{ + int i; + FLAC__int32 sum; + + FLAC__ASSERT(order > 0); + FLAC__ASSERT(order <= 32); + + /* + * We do unique versions up to 12th order since that's the subset limit. + * Also they are roughly ordered to match frequency of occurrence to + * minimize branching. + */ + if(order <= 12) { + if(order > 8) { + if(order > 10) { + if(order == 12) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[11] * data[i-12]; + sum += qlp_coeff[10] * data[i-11]; + sum += qlp_coeff[9] * data[i-10]; + sum += qlp_coeff[8] * data[i-9]; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + else { /* order == 11 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[10] * data[i-11]; + sum += qlp_coeff[9] * data[i-10]; + sum += qlp_coeff[8] * data[i-9]; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + } + else { + if(order == 10) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[9] * data[i-10]; + sum += qlp_coeff[8] * data[i-9]; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + else { /* order == 9 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[8] * data[i-9]; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + } + } + else if(order > 4) { + if(order > 6) { + if(order == 8) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + else { /* order == 7 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + } + else { + if(order == 6) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + else { /* order == 5 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + } + } + else { + if(order > 2) { + if(order == 4) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + else { /* order == 3 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + } + else { + if(order == 2) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + else { /* order == 1 */ + for(i = 0; i < (int)data_len; i++) + data[i] = residual[i] + ((qlp_coeff[0] * data[i-1]) >> lp_quantization); + } + } + } + } + else { /* order > 12 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + switch(order) { + case 32: sum += qlp_coeff[31] * data[i-32]; + case 31: sum += qlp_coeff[30] * data[i-31]; + case 30: sum += qlp_coeff[29] * data[i-30]; + case 29: sum += qlp_coeff[28] * data[i-29]; + case 28: sum += qlp_coeff[27] * data[i-28]; + case 27: sum += qlp_coeff[26] * data[i-27]; + case 26: sum += qlp_coeff[25] * data[i-26]; + case 25: sum += qlp_coeff[24] * data[i-25]; + case 24: sum += qlp_coeff[23] * data[i-24]; + case 23: sum += qlp_coeff[22] * data[i-23]; + case 22: sum += qlp_coeff[21] * data[i-22]; + case 21: sum += qlp_coeff[20] * data[i-21]; + case 20: sum += qlp_coeff[19] * data[i-20]; + case 19: sum += qlp_coeff[18] * data[i-19]; + case 18: sum += qlp_coeff[17] * data[i-18]; + case 17: sum += qlp_coeff[16] * data[i-17]; + case 16: sum += qlp_coeff[15] * data[i-16]; + case 15: sum += qlp_coeff[14] * data[i-15]; + case 14: sum += qlp_coeff[13] * data[i-14]; + case 13: sum += qlp_coeff[12] * data[i-13]; + sum += qlp_coeff[11] * data[i-12]; + sum += qlp_coeff[10] * data[i-11]; + sum += qlp_coeff[ 9] * data[i-10]; + sum += qlp_coeff[ 8] * data[i- 9]; + sum += qlp_coeff[ 7] * data[i- 8]; + sum += qlp_coeff[ 6] * data[i- 7]; + sum += qlp_coeff[ 5] * data[i- 6]; + sum += qlp_coeff[ 4] * data[i- 5]; + sum += qlp_coeff[ 3] * data[i- 4]; + sum += qlp_coeff[ 2] * data[i- 3]; + sum += qlp_coeff[ 1] * data[i- 2]; + sum += qlp_coeff[ 0] * data[i- 1]; + } + data[i] = residual[i] + (sum >> lp_quantization); + } + } +} +#endif + +void FLAC__lpc_restore_signal_wide(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]) +#if defined(FLAC__OVERFLOW_DETECT) || !defined(FLAC__LPC_UNROLLED_FILTER_LOOPS) +{ + unsigned i, j; + FLAC__int64 sum; + const FLAC__int32 *r = residual, *history; + +#ifdef FLAC__OVERFLOW_DETECT_VERBOSE + fprintf(stderr,"FLAC__lpc_restore_signal_wide: data_len=%d, order=%u, lpq=%d",data_len,order,lp_quantization); + for(i=0;i 0); + + for(i = 0; i < data_len; i++) { + sum = 0; + history = data; + for(j = 0; j < order; j++) + sum += (FLAC__int64)qlp_coeff[j] * (FLAC__int64)(*(--history)); + if(FLAC__bitmath_silog2_wide(sum >> lp_quantization) > 32) { +#ifdef _MSC_VER + fprintf(stderr,"FLAC__lpc_restore_signal_wide: OVERFLOW, i=%u, sum=%I64d\n", i, sum >> lp_quantization); +#else + fprintf(stderr,"FLAC__lpc_restore_signal_wide: OVERFLOW, i=%u, sum=%lld\n", i, (long long)(sum >> lp_quantization)); +#endif + break; + } + if(FLAC__bitmath_silog2_wide((FLAC__int64)(*r) + (sum >> lp_quantization)) > 32) { +#ifdef _MSC_VER + fprintf(stderr,"FLAC__lpc_restore_signal_wide: OVERFLOW, i=%u, residual=%d, sum=%I64d, data=%I64d\n", i, *r, sum >> lp_quantization, (FLAC__int64)(*r) + (sum >> lp_quantization)); +#else + fprintf(stderr,"FLAC__lpc_restore_signal_wide: OVERFLOW, i=%u, residual=%d, sum=%lld, data=%lld\n", i, *r, (long long)(sum >> lp_quantization), (long long)((FLAC__int64)(*r) + (sum >> lp_quantization))); +#endif + break; + } + *(data++) = *(r++) + (FLAC__int32)(sum >> lp_quantization); + } +} +#else /* fully unrolled version for normal use */ +{ + int i; + FLAC__int64 sum; + + FLAC__ASSERT(order > 0); + FLAC__ASSERT(order <= 32); + + /* + * We do unique versions up to 12th order since that's the subset limit. + * Also they are roughly ordered to match frequency of occurrence to + * minimize branching. + */ + if(order <= 12) { + if(order > 8) { + if(order > 10) { + if(order == 12) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[11] * (FLAC__int64)data[i-12]; + sum += qlp_coeff[10] * (FLAC__int64)data[i-11]; + sum += qlp_coeff[9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 11 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[10] * (FLAC__int64)data[i-11]; + sum += qlp_coeff[9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + } + else { + if(order == 10) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 9 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + } + } + else if(order > 4) { + if(order > 6) { + if(order == 8) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 7 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + } + else { + if(order == 6) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 5 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + } + } + else { + if(order > 2) { + if(order == 4) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 3 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + } + else { + if(order == 2) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 1 */ + for(i = 0; i < (int)data_len; i++) + data[i] = residual[i] + (FLAC__int32)((qlp_coeff[0] * (FLAC__int64)data[i-1]) >> lp_quantization); + } + } + } + } + else { /* order > 12 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + switch(order) { + case 32: sum += qlp_coeff[31] * (FLAC__int64)data[i-32]; + case 31: sum += qlp_coeff[30] * (FLAC__int64)data[i-31]; + case 30: sum += qlp_coeff[29] * (FLAC__int64)data[i-30]; + case 29: sum += qlp_coeff[28] * (FLAC__int64)data[i-29]; + case 28: sum += qlp_coeff[27] * (FLAC__int64)data[i-28]; + case 27: sum += qlp_coeff[26] * (FLAC__int64)data[i-27]; + case 26: sum += qlp_coeff[25] * (FLAC__int64)data[i-26]; + case 25: sum += qlp_coeff[24] * (FLAC__int64)data[i-25]; + case 24: sum += qlp_coeff[23] * (FLAC__int64)data[i-24]; + case 23: sum += qlp_coeff[22] * (FLAC__int64)data[i-23]; + case 22: sum += qlp_coeff[21] * (FLAC__int64)data[i-22]; + case 21: sum += qlp_coeff[20] * (FLAC__int64)data[i-21]; + case 20: sum += qlp_coeff[19] * (FLAC__int64)data[i-20]; + case 19: sum += qlp_coeff[18] * (FLAC__int64)data[i-19]; + case 18: sum += qlp_coeff[17] * (FLAC__int64)data[i-18]; + case 17: sum += qlp_coeff[16] * (FLAC__int64)data[i-17]; + case 16: sum += qlp_coeff[15] * (FLAC__int64)data[i-16]; + case 15: sum += qlp_coeff[14] * (FLAC__int64)data[i-15]; + case 14: sum += qlp_coeff[13] * (FLAC__int64)data[i-14]; + case 13: sum += qlp_coeff[12] * (FLAC__int64)data[i-13]; + sum += qlp_coeff[11] * (FLAC__int64)data[i-12]; + sum += qlp_coeff[10] * (FLAC__int64)data[i-11]; + sum += qlp_coeff[ 9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[ 8] * (FLAC__int64)data[i- 9]; + sum += qlp_coeff[ 7] * (FLAC__int64)data[i- 8]; + sum += qlp_coeff[ 6] * (FLAC__int64)data[i- 7]; + sum += qlp_coeff[ 5] * (FLAC__int64)data[i- 6]; + sum += qlp_coeff[ 4] * (FLAC__int64)data[i- 5]; + sum += qlp_coeff[ 3] * (FLAC__int64)data[i- 4]; + sum += qlp_coeff[ 2] * (FLAC__int64)data[i- 3]; + sum += qlp_coeff[ 1] * (FLAC__int64)data[i- 2]; + sum += qlp_coeff[ 0] * (FLAC__int64)data[i- 1]; + } + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } +} +#endif + +#ifndef FLAC__INTEGER_ONLY_LIBRARY + +FLAC__double FLAC__lpc_compute_expected_bits_per_residual_sample(FLAC__double lpc_error, unsigned total_samples) +{ + FLAC__double error_scale; + + FLAC__ASSERT(total_samples > 0); + + error_scale = 0.5 * M_LN2 * M_LN2 / (FLAC__double)total_samples; + + return FLAC__lpc_compute_expected_bits_per_residual_sample_with_error_scale(lpc_error, error_scale); +} + +FLAC__double FLAC__lpc_compute_expected_bits_per_residual_sample_with_error_scale(FLAC__double lpc_error, FLAC__double error_scale) +{ + if(lpc_error > 0.0) { + FLAC__double bps = (FLAC__double)0.5 * log(error_scale * lpc_error) / M_LN2; + if(bps >= 0.0) + return bps; + else + return 0.0; + } + else if(lpc_error < 0.0) { /* error should not be negative but can happen due to inadequate floating-point resolution */ + return 1e32; + } + else { + return 0.0; + } +} + +unsigned FLAC__lpc_compute_best_order(const FLAC__double lpc_error[], unsigned max_order, unsigned total_samples, unsigned overhead_bits_per_order) +{ + unsigned order, index, best_index; /* 'index' the index into lpc_error; index==order-1 since lpc_error[0] is for order==1, lpc_error[1] is for order==2, etc */ + FLAC__double bits, best_bits, error_scale; + + FLAC__ASSERT(max_order > 0); + FLAC__ASSERT(total_samples > 0); + + error_scale = 0.5 * M_LN2 * M_LN2 / (FLAC__double)total_samples; + + best_index = 0; + best_bits = (unsigned)(-1); + + for(index = 0, order = 1; index < max_order; index++, order++) { + bits = FLAC__lpc_compute_expected_bits_per_residual_sample_with_error_scale(lpc_error[index], error_scale) * (FLAC__double)(total_samples - order) + (FLAC__double)(order * overhead_bits_per_order); + if(bits < best_bits) { + best_index = index; + best_bits = bits; + } + } + + return best_index+1; /* +1 since index of lpc_error[] is order-1 */ +} + +#endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */ + +#endif +/********* End of inlined file: lpc_flac.c *********/ + +/********* Start of inlined file: md5.c *********/ + +/********* Start of inlined file: juce_FlacHeader.h *********/ +// This file is included at the start of each FLAC .c file, just to do a few housekeeping +// tasks.. + +#define VERSION "1.2.1" + +#define FLAC__NO_DLL 1 + +#ifdef _MSC_VER + #pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4312) +#endif + +#if ! (defined (_WIN32) || defined (_WIN64) || defined (LINUX)) + #define FLAC__SYS_DARWIN 1 +#endif +/********* End of inlined file: juce_FlacHeader.h *********/ + +#if JUCE_USE_FLAC + +#if HAVE_CONFIG_H +# include +#endif + +#include /* for malloc() */ +#include /* for memcpy() */ + +/********* Start of inlined file: md5.h *********/ +#ifndef FLAC__PRIVATE__MD5_H +#define FLAC__PRIVATE__MD5_H + +/* + * This is the header file for the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + * + * Changed so as no longer to depend on Colin Plumb's `usual.h' + * header definitions; now uses stuff from dpkg's config.h + * - Ian Jackson . + * Still in the public domain. + * + * Josh Coalson: made some changes to integrate with libFLAC. + * Still in the public domain, with no warranty. + */ + +typedef struct { + FLAC__uint32 in[16]; + FLAC__uint32 buf[4]; + FLAC__uint32 bytes[2]; + FLAC__byte *internal_buf; + size_t capacity; +} FLAC__MD5Context; + +void FLAC__MD5Init(FLAC__MD5Context *context); +void FLAC__MD5Final(FLAC__byte digest[16], FLAC__MD5Context *context); + +FLAC__bool FLAC__MD5Accumulate(FLAC__MD5Context *ctx, const FLAC__int32 * const signal[], unsigned channels, unsigned samples, unsigned bytes_per_sample); + +#endif +/********* End of inlined file: md5.h *********/ + +#ifndef FLaC__INLINE +#define FLaC__INLINE +#endif + +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + * + * Changed so as no longer to depend on Colin Plumb's `usual.h' header + * definitions; now uses stuff from dpkg's config.h. + * - Ian Jackson . + * Still in the public domain. + * + * Josh Coalson: made some changes to integrate with libFLAC. + * Still in the public domain. + */ + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f,w,x,y,z,in,s) \ + (w += f(x,y,z) + in, w = (w<>(32-s)) + x) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +static void FLAC__MD5Transform(FLAC__uint32 buf[4], FLAC__uint32 const in[16]) +{ + register FLAC__uint32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +#if WORDS_BIGENDIAN +//@@@@@@ OPT: use bswap/intrinsics +static void byteSwap(FLAC__uint32 *buf, unsigned words) +{ + register FLAC__uint32 x; + do { + x = *buf; + x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); + *buf++ = (x >> 16) | (x << 16); + } while (--words); +} +static void byteSwapX16(FLAC__uint32 *buf) +{ + register FLAC__uint32 x; + + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf = (x >> 16) | (x << 16); +} +#else +#define byteSwap(buf, words) +#define byteSwapX16(buf) +#endif + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +static void FLAC__MD5Update(FLAC__MD5Context *ctx, FLAC__byte const *buf, unsigned len) +{ + FLAC__uint32 t; + + /* Update byte count */ + + t = ctx->bytes[0]; + if ((ctx->bytes[0] = t + len) < t) + ctx->bytes[1]++; /* Carry from low to high */ + + t = 64 - (t & 0x3f); /* Space available in ctx->in (at least 1) */ + if (t > len) { + memcpy((FLAC__byte *)ctx->in + 64 - t, buf, len); + return; + } + /* First chunk is an odd size */ + memcpy((FLAC__byte *)ctx->in + 64 - t, buf, t); + byteSwapX16(ctx->in); + FLAC__MD5Transform(ctx->buf, ctx->in); + buf += t; + len -= t; + + /* Process data in 64-byte chunks */ + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteSwapX16(ctx->in); + FLAC__MD5Transform(ctx->buf, ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + memcpy(ctx->in, buf, len); +} + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void FLAC__MD5Init(FLAC__MD5Context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bytes[0] = 0; + ctx->bytes[1] = 0; + + ctx->internal_buf = 0; + ctx->capacity = 0; +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void FLAC__MD5Final(FLAC__byte digest[16], FLAC__MD5Context *ctx) +{ + int count = ctx->bytes[0] & 0x3f; /* Number of bytes in ctx->in */ + FLAC__byte *p = (FLAC__byte *)ctx->in + count; + + /* Set the first char of padding to 0x80. There is always room. */ + *p++ = 0x80; + + /* Bytes of padding needed to make 56 bytes (-8..55) */ + count = 56 - 1 - count; + + if (count < 0) { /* Padding forces an extra block */ + memset(p, 0, count + 8); + byteSwapX16(ctx->in); + FLAC__MD5Transform(ctx->buf, ctx->in); + p = (FLAC__byte *)ctx->in; + count = 56; + } + memset(p, 0, count); + byteSwap(ctx->in, 14); + + /* Append length in bits and transform */ + ctx->in[14] = ctx->bytes[0] << 3; + ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29; + FLAC__MD5Transform(ctx->buf, ctx->in); + + byteSwap(ctx->buf, 4); + memcpy(digest, ctx->buf, 16); + memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ + if(0 != ctx->internal_buf) { + free(ctx->internal_buf); + ctx->internal_buf = 0; + ctx->capacity = 0; + } +} + +/* + * Convert the incoming audio signal to a byte stream + */ +static void format_input_(FLAC__byte *buf, const FLAC__int32 * const signal[], unsigned channels, unsigned samples, unsigned bytes_per_sample) +{ + unsigned channel, sample; + register FLAC__int32 a_word; + register FLAC__byte *buf_ = buf; + +#if WORDS_BIGENDIAN +#else + if(channels == 2 && bytes_per_sample == 2) { + FLAC__int16 *buf1_ = ((FLAC__int16*)buf_) + 1; + memcpy(buf_, signal[0], sizeof(FLAC__int32) * samples); + for(sample = 0; sample < samples; sample++, buf1_+=2) + *buf1_ = (FLAC__int16)signal[1][sample]; + } + else if(channels == 1 && bytes_per_sample == 2) { + FLAC__int16 *buf1_ = (FLAC__int16*)buf_; + for(sample = 0; sample < samples; sample++) + *buf1_++ = (FLAC__int16)signal[0][sample]; + } + else +#endif + if(bytes_per_sample == 2) { + if(channels == 2) { + for(sample = 0; sample < samples; sample++) { + a_word = signal[0][sample]; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; + a_word = signal[1][sample]; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; + } + } + else if(channels == 1) { + for(sample = 0; sample < samples; sample++) { + a_word = signal[0][sample]; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; + } + } + else { + for(sample = 0; sample < samples; sample++) { + for(channel = 0; channel < channels; channel++) { + a_word = signal[channel][sample]; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; + } + } + } + } + else if(bytes_per_sample == 3) { + if(channels == 2) { + for(sample = 0; sample < samples; sample++) { + a_word = signal[0][sample]; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; + a_word = signal[1][sample]; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; + } + } + else if(channels == 1) { + for(sample = 0; sample < samples; sample++) { + a_word = signal[0][sample]; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; + } + } + else { + for(sample = 0; sample < samples; sample++) { + for(channel = 0; channel < channels; channel++) { + a_word = signal[channel][sample]; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; + } + } + } + } + else if(bytes_per_sample == 1) { + if(channels == 2) { + for(sample = 0; sample < samples; sample++) { + a_word = signal[0][sample]; + *buf_++ = (FLAC__byte)a_word; + a_word = signal[1][sample]; + *buf_++ = (FLAC__byte)a_word; + } + } + else if(channels == 1) { + for(sample = 0; sample < samples; sample++) { + a_word = signal[0][sample]; + *buf_++ = (FLAC__byte)a_word; + } + } + else { + for(sample = 0; sample < samples; sample++) { + for(channel = 0; channel < channels; channel++) { + a_word = signal[channel][sample]; + *buf_++ = (FLAC__byte)a_word; + } + } + } + } + else { /* bytes_per_sample == 4, maybe optimize more later */ + for(sample = 0; sample < samples; sample++) { + for(channel = 0; channel < channels; channel++) { + a_word = signal[channel][sample]; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; + } + } + } +} + +/* + * Convert the incoming audio signal to a byte stream and FLAC__MD5Update it. + */ +FLAC__bool FLAC__MD5Accumulate(FLAC__MD5Context *ctx, const FLAC__int32 * const signal[], unsigned channels, unsigned samples, unsigned bytes_per_sample) +{ + const size_t bytes_needed = (size_t)channels * (size_t)samples * (size_t)bytes_per_sample; + + /* overflow check */ + if((size_t)channels > SIZE_MAX / (size_t)bytes_per_sample) + return false; + if((size_t)channels * (size_t)bytes_per_sample > SIZE_MAX / (size_t)samples) + return false; + + if(ctx->capacity < bytes_needed) { + FLAC__byte *tmp = (FLAC__byte*)realloc(ctx->internal_buf, bytes_needed); + if(0 == tmp) { + free(ctx->internal_buf); + if(0 == (ctx->internal_buf = (FLAC__byte*)safe_malloc_(bytes_needed))) + return false; + } + ctx->internal_buf = tmp; + ctx->capacity = bytes_needed; + } + + format_input_(ctx->internal_buf, signal, channels, samples, bytes_per_sample); + + FLAC__MD5Update(ctx, ctx->internal_buf, bytes_needed); + + return true; +} + +#endif +/********* End of inlined file: md5.c *********/ + +/********* Start of inlined file: memory.c *********/ + +/********* Start of inlined file: juce_FlacHeader.h *********/ +// This file is included at the start of each FLAC .c file, just to do a few housekeeping +// tasks.. + +#define VERSION "1.2.1" + +#define FLAC__NO_DLL 1 + +#ifdef _MSC_VER + #pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4312) +#endif + +#if ! (defined (_WIN32) || defined (_WIN64) || defined (LINUX)) + #define FLAC__SYS_DARWIN 1 +#endif +/********* End of inlined file: juce_FlacHeader.h *********/ + +#if JUCE_USE_FLAC + +#if HAVE_CONFIG_H +# include +#endif + +/********* Start of inlined file: memory.h *********/ +#ifndef FLAC__PRIVATE__MEMORY_H +#define FLAC__PRIVATE__MEMORY_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include /* for size_t */ + +/* Returns the unaligned address returned by malloc. + * Use free() on this address to deallocate. + */ +void *FLAC__memory_alloc_aligned(size_t bytes, void **aligned_address); +FLAC__bool FLAC__memory_alloc_aligned_int32_array(unsigned elements, FLAC__int32 **unaligned_pointer, FLAC__int32 **aligned_pointer); +FLAC__bool FLAC__memory_alloc_aligned_uint32_array(unsigned elements, FLAC__uint32 **unaligned_pointer, FLAC__uint32 **aligned_pointer); +FLAC__bool FLAC__memory_alloc_aligned_uint64_array(unsigned elements, FLAC__uint64 **unaligned_pointer, FLAC__uint64 **aligned_pointer); +FLAC__bool FLAC__memory_alloc_aligned_unsigned_array(unsigned elements, unsigned **unaligned_pointer, unsigned **aligned_pointer); +#ifndef FLAC__INTEGER_ONLY_LIBRARY +FLAC__bool FLAC__memory_alloc_aligned_real_array(unsigned elements, FLAC__real **unaligned_pointer, FLAC__real **aligned_pointer); +#endif + +#endif +/********* End of inlined file: memory.h *********/ + +void *FLAC__memory_alloc_aligned(size_t bytes, void **aligned_address) +{ + void *x; + + FLAC__ASSERT(0 != aligned_address); + +#ifdef FLAC__ALIGN_MALLOC_DATA + /* align on 32-byte (256-bit) boundary */ + x = safe_malloc_add_2op_(bytes, /*+*/31); +#ifdef SIZEOF_VOIDP +#if SIZEOF_VOIDP == 4 + /* could do *aligned_address = x + ((unsigned) (32 - (((unsigned)x) & 31))) & 31; */ + *aligned_address = (void*)(((unsigned)x + 31) & -32); +#elif SIZEOF_VOIDP == 8 + *aligned_address = (void*)(((FLAC__uint64)x + 31) & (FLAC__uint64)(-((FLAC__int64)32))); +#else +# error Unsupported sizeof(void*) +#endif +#else + /* there's got to be a better way to do this right for all archs */ + if(sizeof(void*) == sizeof(unsigned)) + *aligned_address = (void*)(((unsigned)x + 31) & -32); + else if(sizeof(void*) == sizeof(FLAC__uint64)) + *aligned_address = (void*)(((FLAC__uint64)x + 31) & (FLAC__uint64)(-((FLAC__int64)32))); + else + return 0; +#endif +#else + x = safe_malloc_(bytes); + *aligned_address = x; +#endif + return x; +} + +FLAC__bool FLAC__memory_alloc_aligned_int32_array(unsigned elements, FLAC__int32 **unaligned_pointer, FLAC__int32 **aligned_pointer) +{ + FLAC__int32 *pu; /* unaligned pointer */ + union { /* union needed to comply with C99 pointer aliasing rules */ + FLAC__int32 *pa; /* aligned pointer */ + void *pv; /* aligned pointer alias */ + } u; + + FLAC__ASSERT(elements > 0); + FLAC__ASSERT(0 != unaligned_pointer); + FLAC__ASSERT(0 != aligned_pointer); + FLAC__ASSERT(unaligned_pointer != aligned_pointer); + + if((size_t)elements > SIZE_MAX / sizeof(*pu)) /* overflow check */ + return false; + + pu = (FLAC__int32*)FLAC__memory_alloc_aligned(sizeof(*pu) * (size_t)elements, &u.pv); + if(0 == pu) { + return false; + } + else { + if(*unaligned_pointer != 0) + free(*unaligned_pointer); + *unaligned_pointer = pu; + *aligned_pointer = u.pa; + return true; + } +} + +FLAC__bool FLAC__memory_alloc_aligned_uint32_array(unsigned elements, FLAC__uint32 **unaligned_pointer, FLAC__uint32 **aligned_pointer) +{ + FLAC__uint32 *pu; /* unaligned pointer */ + union { /* union needed to comply with C99 pointer aliasing rules */ + FLAC__uint32 *pa; /* aligned pointer */ + void *pv; /* aligned pointer alias */ + } u; + + FLAC__ASSERT(elements > 0); + FLAC__ASSERT(0 != unaligned_pointer); + FLAC__ASSERT(0 != aligned_pointer); + FLAC__ASSERT(unaligned_pointer != aligned_pointer); + + if((size_t)elements > SIZE_MAX / sizeof(*pu)) /* overflow check */ + return false; + + pu = (FLAC__uint32*)FLAC__memory_alloc_aligned(sizeof(*pu) * elements, &u.pv); + if(0 == pu) { + return false; + } + else { + if(*unaligned_pointer != 0) + free(*unaligned_pointer); + *unaligned_pointer = pu; + *aligned_pointer = u.pa; + return true; + } +} + +FLAC__bool FLAC__memory_alloc_aligned_uint64_array(unsigned elements, FLAC__uint64 **unaligned_pointer, FLAC__uint64 **aligned_pointer) +{ + FLAC__uint64 *pu; /* unaligned pointer */ + union { /* union needed to comply with C99 pointer aliasing rules */ + FLAC__uint64 *pa; /* aligned pointer */ + void *pv; /* aligned pointer alias */ + } u; + + FLAC__ASSERT(elements > 0); + FLAC__ASSERT(0 != unaligned_pointer); + FLAC__ASSERT(0 != aligned_pointer); + FLAC__ASSERT(unaligned_pointer != aligned_pointer); + + if((size_t)elements > SIZE_MAX / sizeof(*pu)) /* overflow check */ + return false; + + pu = (FLAC__uint64*)FLAC__memory_alloc_aligned(sizeof(*pu) * elements, &u.pv); + if(0 == pu) { + return false; + } + else { + if(*unaligned_pointer != 0) + free(*unaligned_pointer); + *unaligned_pointer = pu; + *aligned_pointer = u.pa; + return true; + } +} + +FLAC__bool FLAC__memory_alloc_aligned_unsigned_array(unsigned elements, unsigned **unaligned_pointer, unsigned **aligned_pointer) +{ + unsigned *pu; /* unaligned pointer */ + union { /* union needed to comply with C99 pointer aliasing rules */ + unsigned *pa; /* aligned pointer */ + void *pv; /* aligned pointer alias */ + } u; + + FLAC__ASSERT(elements > 0); + FLAC__ASSERT(0 != unaligned_pointer); + FLAC__ASSERT(0 != aligned_pointer); + FLAC__ASSERT(unaligned_pointer != aligned_pointer); + + if((size_t)elements > SIZE_MAX / sizeof(*pu)) /* overflow check */ + return false; + + pu = (unsigned*)FLAC__memory_alloc_aligned(sizeof(*pu) * elements, &u.pv); + if(0 == pu) { + return false; + } + else { + if(*unaligned_pointer != 0) + free(*unaligned_pointer); + *unaligned_pointer = pu; + *aligned_pointer = u.pa; + return true; + } +} + +#ifndef FLAC__INTEGER_ONLY_LIBRARY + +FLAC__bool FLAC__memory_alloc_aligned_real_array(unsigned elements, FLAC__real **unaligned_pointer, FLAC__real **aligned_pointer) +{ + FLAC__real *pu; /* unaligned pointer */ + union { /* union needed to comply with C99 pointer aliasing rules */ + FLAC__real *pa; /* aligned pointer */ + void *pv; /* aligned pointer alias */ + } u; + + FLAC__ASSERT(elements > 0); + FLAC__ASSERT(0 != unaligned_pointer); + FLAC__ASSERT(0 != aligned_pointer); + FLAC__ASSERT(unaligned_pointer != aligned_pointer); + + if((size_t)elements > SIZE_MAX / sizeof(*pu)) /* overflow check */ + return false; + + pu = (FLAC__real*)FLAC__memory_alloc_aligned(sizeof(*pu) * elements, &u.pv); + if(0 == pu) { + return false; + } + else { + if(*unaligned_pointer != 0) + free(*unaligned_pointer); + *unaligned_pointer = pu; + *aligned_pointer = u.pa; + return true; + } +} + +#endif + +#endif +/********* End of inlined file: memory.c *********/ + +/********* Start of inlined file: stream_decoder.c *********/ + +/********* Start of inlined file: juce_FlacHeader.h *********/ +// This file is included at the start of each FLAC .c file, just to do a few housekeeping +// tasks.. + +#define VERSION "1.2.1" + +#define FLAC__NO_DLL 1 + +#ifdef _MSC_VER + #pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4312) +#endif + +#if ! (defined (_WIN32) || defined (_WIN64) || defined (LINUX)) + #define FLAC__SYS_DARWIN 1 +#endif +/********* End of inlined file: juce_FlacHeader.h *********/ + +#if JUCE_USE_FLAC + +#if HAVE_CONFIG_H +# include +#endif + +#if defined _MSC_VER || defined __MINGW32__ +#include /* for _setmode() */ +#include /* for _O_BINARY */ +#endif +#if defined __CYGWIN__ || defined __EMX__ +#include /* for setmode(), O_BINARY */ +#include /* for _O_BINARY */ +#endif +#include +#include /* for malloc() */ +#include /* for memset/memcpy() */ +#include /* for stat() */ +#include /* for off_t */ +#if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__ +#if _MSC_VER <= 1600 || defined __BORLANDC__ /* @@@ [2G limit] */ +#define fseeko fseek +#define ftello ftell +#endif +#endif + +/********* Start of inlined file: stream_decoder.h *********/ +#ifndef FLAC__PROTECTED__STREAM_DECODER_H +#define FLAC__PROTECTED__STREAM_DECODER_H + +#if FLAC__HAS_OGG +#include "include/private/ogg_decoder_aspect.h" +#endif + +typedef struct FLAC__StreamDecoderProtected { + FLAC__StreamDecoderState state; + unsigned channels; + FLAC__ChannelAssignment channel_assignment; + unsigned bits_per_sample; + unsigned sample_rate; /* in Hz */ + unsigned blocksize; /* in samples (per channel) */ + FLAC__bool md5_checking; /* if true, generate MD5 signature of decoded data and compare against signature in the STREAMINFO metadata block */ +#if FLAC__HAS_OGG + FLAC__OggDecoderAspect ogg_decoder_aspect; +#endif +} FLAC__StreamDecoderProtected; + +/* + * return the number of input bytes consumed + */ +unsigned FLAC__stream_decoder_get_input_bytes_unconsumed(const FLAC__StreamDecoder *decoder); + +#endif +/********* End of inlined file: stream_decoder.h *********/ + +#ifdef max +#undef max +#endif +#define max(a,b) ((a)>(b)?(a):(b)) + +/* adjust for compilers that can't understand using LLU suffix for uint64_t literals */ +#ifdef _MSC_VER +#define FLAC__U64L(x) x +#else +#define FLAC__U64L(x) x##LLU +#endif + +/* technically this should be in an "export.c" but this is convenient enough */ +FLAC_API int FLAC_API_SUPPORTS_OGG_FLAC = +#if FLAC__HAS_OGG + 1 +#else + 0 +#endif +; + +/*********************************************************************** + * + * Private static data + * + ***********************************************************************/ + +static FLAC__byte ID3V2_TAG_[3] = { 'I', 'D', '3' }; + +/*********************************************************************** + * + * Private class method prototypes + * + ***********************************************************************/ + +static void set_defaults_dec(FLAC__StreamDecoder *decoder); +static FILE *get_binary_stdin_(void); +static FLAC__bool allocate_output_(FLAC__StreamDecoder *decoder, unsigned size, unsigned channels); +static FLAC__bool has_id_filtered_(FLAC__StreamDecoder *decoder, FLAC__byte *id); +static FLAC__bool find_metadata_(FLAC__StreamDecoder *decoder); +static FLAC__bool read_metadata_(FLAC__StreamDecoder *decoder); +static FLAC__bool read_metadata_streaminfo_(FLAC__StreamDecoder *decoder, FLAC__bool is_last, unsigned length); +static FLAC__bool read_metadata_seektable_(FLAC__StreamDecoder *decoder, FLAC__bool is_last, unsigned length); +static FLAC__bool read_metadata_vorbiscomment_(FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_VorbisComment *obj); +static FLAC__bool read_metadata_cuesheet_(FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_CueSheet *obj); +static FLAC__bool read_metadata_picture_(FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_Picture *obj); +static FLAC__bool skip_id3v2_tag_(FLAC__StreamDecoder *decoder); +static FLAC__bool frame_sync_(FLAC__StreamDecoder *decoder); +static FLAC__bool read_frame_(FLAC__StreamDecoder *decoder, FLAC__bool *got_a_frame, FLAC__bool do_full_decode); +static FLAC__bool read_frame_header_(FLAC__StreamDecoder *decoder); +static FLAC__bool read_subframe_(FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, FLAC__bool do_full_decode); +static FLAC__bool read_subframe_constant_(FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, FLAC__bool do_full_decode); +static FLAC__bool read_subframe_fixed_(FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, const unsigned order, FLAC__bool do_full_decode); +static FLAC__bool read_subframe_lpc_(FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, const unsigned order, FLAC__bool do_full_decode); +static FLAC__bool read_subframe_verbatim_(FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, FLAC__bool do_full_decode); +static FLAC__bool read_residual_partitioned_rice_(FLAC__StreamDecoder *decoder, unsigned predictor_order, unsigned partition_order, FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents, FLAC__int32 *residual, FLAC__bool is_extended); +static FLAC__bool read_zero_padding_(FLAC__StreamDecoder *decoder); +static FLAC__bool read_callback_(FLAC__byte buffer[], size_t *bytes, void *client_data); +#if FLAC__HAS_OGG +static FLAC__StreamDecoderReadStatus read_callback_ogg_aspect_(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes); +static FLAC__OggDecoderAspectReadStatus read_callback_proxy_(const void *void_decoder, FLAC__byte buffer[], size_t *bytes, void *client_data); +#endif +static FLAC__StreamDecoderWriteStatus write_audio_frame_to_client_(FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[]); +static void send_error_to_client_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status); +static FLAC__bool seek_to_absolute_sample_(FLAC__StreamDecoder *decoder, FLAC__uint64 stream_length, FLAC__uint64 target_sample); +#if FLAC__HAS_OGG +static FLAC__bool seek_to_absolute_sample_ogg_(FLAC__StreamDecoder *decoder, FLAC__uint64 stream_length, FLAC__uint64 target_sample); +#endif +static FLAC__StreamDecoderReadStatus file_read_callback_dec (const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data); +static FLAC__StreamDecoderSeekStatus file_seek_callback_dec (const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data); +static FLAC__StreamDecoderTellStatus file_tell_callback_dec (const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data); +static FLAC__StreamDecoderLengthStatus file_length_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data); +static FLAC__bool file_eof_callback_(const FLAC__StreamDecoder *decoder, void *client_data); + +/*********************************************************************** + * + * Private class data + * + ***********************************************************************/ + +typedef struct FLAC__StreamDecoderPrivate { +#if FLAC__HAS_OGG + FLAC__bool is_ogg; +#endif + FLAC__StreamDecoderReadCallback read_callback; + FLAC__StreamDecoderSeekCallback seek_callback; + FLAC__StreamDecoderTellCallback tell_callback; + FLAC__StreamDecoderLengthCallback length_callback; + FLAC__StreamDecoderEofCallback eof_callback; + FLAC__StreamDecoderWriteCallback write_callback; + FLAC__StreamDecoderMetadataCallback metadata_callback; + FLAC__StreamDecoderErrorCallback error_callback; + /* generic 32-bit datapath: */ + void (*local_lpc_restore_signal)(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); + /* generic 64-bit datapath: */ + void (*local_lpc_restore_signal_64bit)(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); + /* for use when the signal is <= 16 bits-per-sample, or <= 15 bits-per-sample on a side channel (which requires 1 extra bit): */ + void (*local_lpc_restore_signal_16bit)(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); + /* for use when the signal is <= 16 bits-per-sample, or <= 15 bits-per-sample on a side channel (which requires 1 extra bit), AND order <= 8: */ + void (*local_lpc_restore_signal_16bit_order8)(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); + FLAC__bool (*local_bitreader_read_rice_signed_block)(FLAC__BitReader *br, int* vals, unsigned nvals, unsigned parameter); + void *client_data; + FILE *file; /* only used if FLAC__stream_decoder_init_file()/FLAC__stream_decoder_init_file() called, else NULL */ + FLAC__BitReader *input; + FLAC__int32 *output[FLAC__MAX_CHANNELS]; + FLAC__int32 *residual[FLAC__MAX_CHANNELS]; /* WATCHOUT: these are the aligned pointers; the real pointers that should be free()'d are residual_unaligned[] below */ + FLAC__EntropyCodingMethod_PartitionedRiceContents partitioned_rice_contents[FLAC__MAX_CHANNELS]; + unsigned output_capacity, output_channels; + FLAC__uint32 fixed_block_size, next_fixed_block_size; + FLAC__uint64 samples_decoded; + FLAC__bool has_stream_info, has_seek_table; + FLAC__StreamMetadata stream_info; + FLAC__StreamMetadata seek_table; + FLAC__bool metadata_filter[128]; /* MAGIC number 128 == total number of metadata block types == 1 << 7 */ + FLAC__byte *metadata_filter_ids; + size_t metadata_filter_ids_count, metadata_filter_ids_capacity; /* units for both are IDs, not bytes */ + FLAC__Frame frame; + FLAC__bool cached; /* true if there is a byte in lookahead */ + FLAC__CPUInfo cpuinfo; + FLAC__byte header_warmup[2]; /* contains the sync code and reserved bits */ + FLAC__byte lookahead; /* temp storage when we need to look ahead one byte in the stream */ + /* unaligned (original) pointers to allocated data */ + FLAC__int32 *residual_unaligned[FLAC__MAX_CHANNELS]; + FLAC__bool do_md5_checking; /* initially gets protected_->md5_checking but is turned off after a seek or if the metadata has a zero MD5 */ + FLAC__bool internal_reset_hack; /* used only during init() so we can call reset to set up the decoder without rewinding the input */ + FLAC__bool is_seeking; + FLAC__MD5Context md5context; + FLAC__byte computed_md5sum[16]; /* this is the sum we computed from the decoded data */ + /* (the rest of these are only used for seeking) */ + FLAC__Frame last_frame; /* holds the info of the last frame we seeked to */ + FLAC__uint64 first_frame_offset; /* hint to the seek routine of where in the stream the first audio frame starts */ + FLAC__uint64 target_sample; + unsigned unparseable_frame_count; /* used to tell whether we're decoding a future version of FLAC or just got a bad sync */ +#if FLAC__HAS_OGG + FLAC__bool got_a_frame; /* hack needed in Ogg FLAC seek routine to check when process_single() actually writes a frame */ +#endif +} FLAC__StreamDecoderPrivate; + +/*********************************************************************** + * + * Public static class data + * + ***********************************************************************/ + +FLAC_API const char * const FLAC__StreamDecoderStateString[] = { + "FLAC__STREAM_DECODER_SEARCH_FOR_METADATA", + "FLAC__STREAM_DECODER_READ_METADATA", + "FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC", + "FLAC__STREAM_DECODER_READ_FRAME", + "FLAC__STREAM_DECODER_END_OF_STREAM", + "FLAC__STREAM_DECODER_OGG_ERROR", + "FLAC__STREAM_DECODER_SEEK_ERROR", + "FLAC__STREAM_DECODER_ABORTED", + "FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR", + "FLAC__STREAM_DECODER_UNINITIALIZED" +}; + +FLAC_API const char * const FLAC__StreamDecoderInitStatusString[] = { + "FLAC__STREAM_DECODER_INIT_STATUS_OK", + "FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER", + "FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS", + "FLAC__STREAM_DECODER_INIT_STATUS_MEMORY_ALLOCATION_ERROR", + "FLAC__STREAM_DECODER_INIT_STATUS_ERROR_OPENING_FILE", + "FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED" +}; + +FLAC_API const char * const FLAC__StreamDecoderReadStatusString[] = { + "FLAC__STREAM_DECODER_READ_STATUS_CONTINUE", + "FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM", + "FLAC__STREAM_DECODER_READ_STATUS_ABORT" +}; + +FLAC_API const char * const FLAC__StreamDecoderSeekStatusString[] = { + "FLAC__STREAM_DECODER_SEEK_STATUS_OK", + "FLAC__STREAM_DECODER_SEEK_STATUS_ERROR", + "FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED" +}; + +FLAC_API const char * const FLAC__StreamDecoderTellStatusString[] = { + "FLAC__STREAM_DECODER_TELL_STATUS_OK", + "FLAC__STREAM_DECODER_TELL_STATUS_ERROR", + "FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED" +}; + +FLAC_API const char * const FLAC__StreamDecoderLengthStatusString[] = { + "FLAC__STREAM_DECODER_LENGTH_STATUS_OK", + "FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR", + "FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED" +}; + +FLAC_API const char * const FLAC__StreamDecoderWriteStatusString[] = { + "FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE", + "FLAC__STREAM_DECODER_WRITE_STATUS_ABORT" +}; + +FLAC_API const char * const FLAC__StreamDecoderErrorStatusString[] = { + "FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC", + "FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER", + "FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH", + "FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM" +}; + +/*********************************************************************** + * + * Class constructor/destructor + * + ***********************************************************************/ +FLAC_API FLAC__StreamDecoder *FLAC__stream_decoder_new(void) +{ + FLAC__StreamDecoder *decoder; + unsigned i; + + FLAC__ASSERT(sizeof(int) >= 4); /* we want to die right away if this is not true */ + + decoder = (FLAC__StreamDecoder*)calloc(1, sizeof(FLAC__StreamDecoder)); + if(decoder == 0) { + return 0; + } + + decoder->protected_ = (FLAC__StreamDecoderProtected*)calloc(1, sizeof(FLAC__StreamDecoderProtected)); + if(decoder->protected_ == 0) { + free(decoder); + return 0; + } + + decoder->private_ = (FLAC__StreamDecoderPrivate*)calloc(1, sizeof(FLAC__StreamDecoderPrivate)); + if(decoder->private_ == 0) { + free(decoder->protected_); + free(decoder); + return 0; + } + + decoder->private_->input = FLAC__bitreader_new(); + if(decoder->private_->input == 0) { + free(decoder->private_); + free(decoder->protected_); + free(decoder); + return 0; + } + + decoder->private_->metadata_filter_ids_capacity = 16; + if(0 == (decoder->private_->metadata_filter_ids = (FLAC__byte*)malloc((FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8) * decoder->private_->metadata_filter_ids_capacity))) { + FLAC__bitreader_delete(decoder->private_->input); + free(decoder->private_); + free(decoder->protected_); + free(decoder); + return 0; + } + + for(i = 0; i < FLAC__MAX_CHANNELS; i++) { + decoder->private_->output[i] = 0; + decoder->private_->residual_unaligned[i] = decoder->private_->residual[i] = 0; + } + + decoder->private_->output_capacity = 0; + decoder->private_->output_channels = 0; + decoder->private_->has_seek_table = false; + + for(i = 0; i < FLAC__MAX_CHANNELS; i++) + FLAC__format_entropy_coding_method_partitioned_rice_contents_init(&decoder->private_->partitioned_rice_contents[i]); + + decoder->private_->file = 0; + + set_defaults_dec(decoder); + + decoder->protected_->state = FLAC__STREAM_DECODER_UNINITIALIZED; + + return decoder; +} + +FLAC_API void FLAC__stream_decoder_delete(FLAC__StreamDecoder *decoder) +{ + unsigned i; + + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->private_->input); + + (void)FLAC__stream_decoder_finish(decoder); + + if(0 != decoder->private_->metadata_filter_ids) + free(decoder->private_->metadata_filter_ids); + + FLAC__bitreader_delete(decoder->private_->input); + + for(i = 0; i < FLAC__MAX_CHANNELS; i++) + FLAC__format_entropy_coding_method_partitioned_rice_contents_clear(&decoder->private_->partitioned_rice_contents[i]); + + free(decoder->private_); + free(decoder->protected_); + free(decoder); +} + +/*********************************************************************** + * + * Public class methods + * + ***********************************************************************/ + +static FLAC__StreamDecoderInitStatus init_stream_internal_dec( + FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderReadCallback read_callback, + FLAC__StreamDecoderSeekCallback seek_callback, + FLAC__StreamDecoderTellCallback tell_callback, + FLAC__StreamDecoderLengthCallback length_callback, + FLAC__StreamDecoderEofCallback eof_callback, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data, + FLAC__bool is_ogg +) +{ + FLAC__ASSERT(0 != decoder); + + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED; + +#if !FLAC__HAS_OGG + if(is_ogg) + return FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER; +#endif + + if( + 0 == read_callback || + 0 == write_callback || + 0 == error_callback || + (seek_callback && (0 == tell_callback || 0 == length_callback || 0 == eof_callback)) + ) + return FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS; + +#if FLAC__HAS_OGG + decoder->private_->is_ogg = is_ogg; + if(is_ogg && !FLAC__ogg_decoder_aspect_init(&decoder->protected_->ogg_decoder_aspect)) + return decoder->protected_->state = FLAC__STREAM_DECODER_OGG_ERROR; +#endif + + /* + * get the CPU info and set the function pointers + */ + FLAC__cpu_info(&decoder->private_->cpuinfo); + /* first default to the non-asm routines */ + decoder->private_->local_lpc_restore_signal = FLAC__lpc_restore_signal; + decoder->private_->local_lpc_restore_signal_64bit = FLAC__lpc_restore_signal_wide; + decoder->private_->local_lpc_restore_signal_16bit = FLAC__lpc_restore_signal; + decoder->private_->local_lpc_restore_signal_16bit_order8 = FLAC__lpc_restore_signal; + decoder->private_->local_bitreader_read_rice_signed_block = FLAC__bitreader_read_rice_signed_block; + /* now override with asm where appropriate */ +#ifndef FLAC__NO_ASM + if(decoder->private_->cpuinfo.use_asm) { +#ifdef FLAC__CPU_IA32 + FLAC__ASSERT(decoder->private_->cpuinfo.type == FLAC__CPUINFO_TYPE_IA32); +#ifdef FLAC__HAS_NASM +#if 1 /*@@@@@@ OPT: not clearly faster, needs more testing */ + if(decoder->private_->cpuinfo.data.ia32.bswap) + decoder->private_->local_bitreader_read_rice_signed_block = FLAC__bitreader_read_rice_signed_block_asm_ia32_bswap; +#endif + if(decoder->private_->cpuinfo.data.ia32.mmx) { + decoder->private_->local_lpc_restore_signal = FLAC__lpc_restore_signal_asm_ia32; + decoder->private_->local_lpc_restore_signal_16bit = FLAC__lpc_restore_signal_asm_ia32_mmx; + decoder->private_->local_lpc_restore_signal_16bit_order8 = FLAC__lpc_restore_signal_asm_ia32_mmx; + } + else { + decoder->private_->local_lpc_restore_signal = FLAC__lpc_restore_signal_asm_ia32; + decoder->private_->local_lpc_restore_signal_16bit = FLAC__lpc_restore_signal_asm_ia32; + decoder->private_->local_lpc_restore_signal_16bit_order8 = FLAC__lpc_restore_signal_asm_ia32; + } +#endif +#elif defined FLAC__CPU_PPC + FLAC__ASSERT(decoder->private_->cpuinfo.type == FLAC__CPUINFO_TYPE_PPC); + if(decoder->private_->cpuinfo.data.ppc.altivec) { + decoder->private_->local_lpc_restore_signal_16bit = FLAC__lpc_restore_signal_asm_ppc_altivec_16; + decoder->private_->local_lpc_restore_signal_16bit_order8 = FLAC__lpc_restore_signal_asm_ppc_altivec_16_order8; + } +#endif + } +#endif + + /* from here on, errors are fatal */ + + if(!FLAC__bitreader_init(decoder->private_->input, decoder->private_->cpuinfo, read_callback_, decoder)) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return FLAC__STREAM_DECODER_INIT_STATUS_MEMORY_ALLOCATION_ERROR; + } + + decoder->private_->read_callback = read_callback; + decoder->private_->seek_callback = seek_callback; + decoder->private_->tell_callback = tell_callback; + decoder->private_->length_callback = length_callback; + decoder->private_->eof_callback = eof_callback; + decoder->private_->write_callback = write_callback; + decoder->private_->metadata_callback = metadata_callback; + decoder->private_->error_callback = error_callback; + decoder->private_->client_data = client_data; + decoder->private_->fixed_block_size = decoder->private_->next_fixed_block_size = 0; + decoder->private_->samples_decoded = 0; + decoder->private_->has_stream_info = false; + decoder->private_->cached = false; + + decoder->private_->do_md5_checking = decoder->protected_->md5_checking; + decoder->private_->is_seeking = false; + + decoder->private_->internal_reset_hack = true; /* so the following reset does not try to rewind the input */ + if(!FLAC__stream_decoder_reset(decoder)) { + /* above call sets the state for us */ + return FLAC__STREAM_DECODER_INIT_STATUS_MEMORY_ALLOCATION_ERROR; + } + + return FLAC__STREAM_DECODER_INIT_STATUS_OK; +} + +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_stream( + FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderReadCallback read_callback, + FLAC__StreamDecoderSeekCallback seek_callback, + FLAC__StreamDecoderTellCallback tell_callback, + FLAC__StreamDecoderLengthCallback length_callback, + FLAC__StreamDecoderEofCallback eof_callback, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +) +{ + return init_stream_internal_dec( + decoder, + read_callback, + seek_callback, + tell_callback, + length_callback, + eof_callback, + write_callback, + metadata_callback, + error_callback, + client_data, + /*is_ogg=*/false + ); +} + +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_stream( + FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderReadCallback read_callback, + FLAC__StreamDecoderSeekCallback seek_callback, + FLAC__StreamDecoderTellCallback tell_callback, + FLAC__StreamDecoderLengthCallback length_callback, + FLAC__StreamDecoderEofCallback eof_callback, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +) +{ + return init_stream_internal_dec( + decoder, + read_callback, + seek_callback, + tell_callback, + length_callback, + eof_callback, + write_callback, + metadata_callback, + error_callback, + client_data, + /*is_ogg=*/true + ); +} + +static FLAC__StreamDecoderInitStatus init_FILE_internal_( + FLAC__StreamDecoder *decoder, + FILE *file, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data, + FLAC__bool is_ogg +) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != file); + + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return (FLAC__StreamDecoderInitStatus) (decoder->protected_->state = (FLAC__StreamDecoderState) FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED); + + if(0 == write_callback || 0 == error_callback) + return (FLAC__StreamDecoderInitStatus) (decoder->protected_->state = (FLAC__StreamDecoderState) FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS); + + /* + * To make sure that our file does not go unclosed after an error, we + * must assign the FILE pointer before any further error can occur in + * this routine. + */ + if(file == stdin) + file = get_binary_stdin_(); /* just to be safe */ + + decoder->private_->file = file; + + return init_stream_internal_dec( + decoder, + file_read_callback_dec, + decoder->private_->file == stdin? 0: file_seek_callback_dec, + decoder->private_->file == stdin? 0: file_tell_callback_dec, + decoder->private_->file == stdin? 0: file_length_callback_, + file_eof_callback_, + write_callback, + metadata_callback, + error_callback, + client_data, + is_ogg + ); +} + +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_FILE( + FLAC__StreamDecoder *decoder, + FILE *file, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +) +{ + return init_FILE_internal_(decoder, file, write_callback, metadata_callback, error_callback, client_data, /*is_ogg=*/false); +} + +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_FILE( + FLAC__StreamDecoder *decoder, + FILE *file, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +) +{ + return init_FILE_internal_(decoder, file, write_callback, metadata_callback, error_callback, client_data, /*is_ogg=*/true); +} + +static FLAC__StreamDecoderInitStatus init_file_internal_( + FLAC__StreamDecoder *decoder, + const char *filename, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data, + FLAC__bool is_ogg +) +{ + FILE *file; + + FLAC__ASSERT(0 != decoder); + + /* + * To make sure that our file does not go unclosed after an error, we + * have to do the same entrance checks here that are later performed + * in FLAC__stream_decoder_init_FILE() before the FILE* is assigned. + */ + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return (FLAC__StreamDecoderInitStatus) (decoder->protected_->state = (FLAC__StreamDecoderState) FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED); + + if(0 == write_callback || 0 == error_callback) + return (FLAC__StreamDecoderInitStatus) (decoder->protected_->state = (FLAC__StreamDecoderState) FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS); + + file = filename? fopen(filename, "rb") : stdin; + + if(0 == file) + return FLAC__STREAM_DECODER_INIT_STATUS_ERROR_OPENING_FILE; + + return init_FILE_internal_(decoder, file, write_callback, metadata_callback, error_callback, client_data, is_ogg); +} + +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_file( + FLAC__StreamDecoder *decoder, + const char *filename, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +) +{ + return init_file_internal_(decoder, filename, write_callback, metadata_callback, error_callback, client_data, /*is_ogg=*/false); +} + +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_file( + FLAC__StreamDecoder *decoder, + const char *filename, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +) +{ + return init_file_internal_(decoder, filename, write_callback, metadata_callback, error_callback, client_data, /*is_ogg=*/true); +} + +FLAC_API FLAC__bool FLAC__stream_decoder_finish(FLAC__StreamDecoder *decoder) +{ + FLAC__bool md5_failed = false; + unsigned i; + + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->protected_); + + if(decoder->protected_->state == FLAC__STREAM_DECODER_UNINITIALIZED) + return true; + + /* see the comment in FLAC__seekable_stream_decoder_reset() as to why we + * always call FLAC__MD5Final() + */ + FLAC__MD5Final(decoder->private_->computed_md5sum, &decoder->private_->md5context); + + if(decoder->private_->has_seek_table && 0 != decoder->private_->seek_table.data.seek_table.points) { + free(decoder->private_->seek_table.data.seek_table.points); + decoder->private_->seek_table.data.seek_table.points = 0; + decoder->private_->has_seek_table = false; + } + FLAC__bitreader_free(decoder->private_->input); + for(i = 0; i < FLAC__MAX_CHANNELS; i++) { + /* WATCHOUT: + * FLAC__lpc_restore_signal_asm_ia32_mmx() requires that the + * output arrays have a buffer of up to 3 zeroes in front + * (at negative indices) for alignment purposes; we use 4 + * to keep the data well-aligned. + */ + if(0 != decoder->private_->output[i]) { + free(decoder->private_->output[i]-4); + decoder->private_->output[i] = 0; + } + if(0 != decoder->private_->residual_unaligned[i]) { + free(decoder->private_->residual_unaligned[i]); + decoder->private_->residual_unaligned[i] = decoder->private_->residual[i] = 0; + } + } + decoder->private_->output_capacity = 0; + decoder->private_->output_channels = 0; + +#if FLAC__HAS_OGG + if(decoder->private_->is_ogg) + FLAC__ogg_decoder_aspect_finish(&decoder->protected_->ogg_decoder_aspect); +#endif + + if(0 != decoder->private_->file) { + if(decoder->private_->file != stdin) + fclose(decoder->private_->file); + decoder->private_->file = 0; + } + + if(decoder->private_->do_md5_checking) { + if(memcmp(decoder->private_->stream_info.data.stream_info.md5sum, decoder->private_->computed_md5sum, 16)) + md5_failed = true; + } + decoder->private_->is_seeking = false; + + set_defaults_dec(decoder); + + decoder->protected_->state = FLAC__STREAM_DECODER_UNINITIALIZED; + + return !md5_failed; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_set_ogg_serial_number(FLAC__StreamDecoder *decoder, long value) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->protected_); + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return false; +#if FLAC__HAS_OGG + /* can't check decoder->private_->is_ogg since that's not set until init time */ + FLAC__ogg_decoder_aspect_set_serial_number(&decoder->protected_->ogg_decoder_aspect, value); + return true; +#else + (void)value; + return false; +#endif +} + +FLAC_API FLAC__bool FLAC__stream_decoder_set_md5_checking(FLAC__StreamDecoder *decoder, FLAC__bool value) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return false; + decoder->protected_->md5_checking = value; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond(FLAC__StreamDecoder *decoder, FLAC__MetadataType type) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->protected_); + FLAC__ASSERT((unsigned)type <= FLAC__MAX_METADATA_TYPE_CODE); + /* double protection */ + if((unsigned)type > FLAC__MAX_METADATA_TYPE_CODE) + return false; + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return false; + decoder->private_->metadata_filter[type] = true; + if(type == FLAC__METADATA_TYPE_APPLICATION) + decoder->private_->metadata_filter_ids_count = 0; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond_application(FLAC__StreamDecoder *decoder, const FLAC__byte id[4]) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->protected_); + FLAC__ASSERT(0 != id); + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return false; + + if(decoder->private_->metadata_filter[FLAC__METADATA_TYPE_APPLICATION]) + return true; + + FLAC__ASSERT(0 != decoder->private_->metadata_filter_ids); + + if(decoder->private_->metadata_filter_ids_count == decoder->private_->metadata_filter_ids_capacity) { + if(0 == (decoder->private_->metadata_filter_ids = (FLAC__byte*)safe_realloc_mul_2op_(decoder->private_->metadata_filter_ids, decoder->private_->metadata_filter_ids_capacity, /*times*/2))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + decoder->private_->metadata_filter_ids_capacity *= 2; + } + + memcpy(decoder->private_->metadata_filter_ids + decoder->private_->metadata_filter_ids_count * (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8), id, (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8)); + decoder->private_->metadata_filter_ids_count++; + + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond_all(FLAC__StreamDecoder *decoder) +{ + unsigned i; + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->protected_); + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return false; + for(i = 0; i < sizeof(decoder->private_->metadata_filter) / sizeof(decoder->private_->metadata_filter[0]); i++) + decoder->private_->metadata_filter[i] = true; + decoder->private_->metadata_filter_ids_count = 0; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore(FLAC__StreamDecoder *decoder, FLAC__MetadataType type) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->protected_); + FLAC__ASSERT((unsigned)type <= FLAC__MAX_METADATA_TYPE_CODE); + /* double protection */ + if((unsigned)type > FLAC__MAX_METADATA_TYPE_CODE) + return false; + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return false; + decoder->private_->metadata_filter[type] = false; + if(type == FLAC__METADATA_TYPE_APPLICATION) + decoder->private_->metadata_filter_ids_count = 0; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore_application(FLAC__StreamDecoder *decoder, const FLAC__byte id[4]) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->protected_); + FLAC__ASSERT(0 != id); + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return false; + + if(!decoder->private_->metadata_filter[FLAC__METADATA_TYPE_APPLICATION]) + return true; + + FLAC__ASSERT(0 != decoder->private_->metadata_filter_ids); + + if(decoder->private_->metadata_filter_ids_count == decoder->private_->metadata_filter_ids_capacity) { + if(0 == (decoder->private_->metadata_filter_ids = (FLAC__byte*)safe_realloc_mul_2op_(decoder->private_->metadata_filter_ids, decoder->private_->metadata_filter_ids_capacity, /*times*/2))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + decoder->private_->metadata_filter_ids_capacity *= 2; + } + + memcpy(decoder->private_->metadata_filter_ids + decoder->private_->metadata_filter_ids_count * (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8), id, (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8)); + decoder->private_->metadata_filter_ids_count++; + + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore_all(FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->protected_); + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return false; + memset(decoder->private_->metadata_filter, 0, sizeof(decoder->private_->metadata_filter)); + decoder->private_->metadata_filter_ids_count = 0; + return true; +} + +FLAC_API FLAC__StreamDecoderState FLAC__stream_decoder_get_state(const FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + return decoder->protected_->state; +} + +FLAC_API const char *FLAC__stream_decoder_get_resolved_state_string(const FLAC__StreamDecoder *decoder) +{ + return FLAC__StreamDecoderStateString[decoder->protected_->state]; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_get_md5_checking(const FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + return decoder->protected_->md5_checking; +} + +FLAC_API FLAC__uint64 FLAC__stream_decoder_get_total_samples(const FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + return decoder->private_->has_stream_info? decoder->private_->stream_info.data.stream_info.total_samples : 0; +} + +FLAC_API unsigned FLAC__stream_decoder_get_channels(const FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + return decoder->protected_->channels; +} + +FLAC_API FLAC__ChannelAssignment FLAC__stream_decoder_get_channel_assignment(const FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + return decoder->protected_->channel_assignment; +} + +FLAC_API unsigned FLAC__stream_decoder_get_bits_per_sample(const FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + return decoder->protected_->bits_per_sample; +} + +FLAC_API unsigned FLAC__stream_decoder_get_sample_rate(const FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + return decoder->protected_->sample_rate; +} + +FLAC_API unsigned FLAC__stream_decoder_get_blocksize(const FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + return decoder->protected_->blocksize; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_get_decode_position(const FLAC__StreamDecoder *decoder, FLAC__uint64 *position) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != position); + +#if FLAC__HAS_OGG + if(decoder->private_->is_ogg) + return false; +#endif + if(0 == decoder->private_->tell_callback) + return false; + if(decoder->private_->tell_callback(decoder, position, decoder->private_->client_data) != FLAC__STREAM_DECODER_TELL_STATUS_OK) + return false; + /* should never happen since all FLAC frames and metadata blocks are byte aligned, but check just in case */ + if(!FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)) + return false; + FLAC__ASSERT(*position >= FLAC__stream_decoder_get_input_bytes_unconsumed(decoder)); + *position -= FLAC__stream_decoder_get_input_bytes_unconsumed(decoder); + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_flush(FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->protected_); + + decoder->private_->samples_decoded = 0; + decoder->private_->do_md5_checking = false; + +#if FLAC__HAS_OGG + if(decoder->private_->is_ogg) + FLAC__ogg_decoder_aspect_flush(&decoder->protected_->ogg_decoder_aspect); +#endif + + if(!FLAC__bitreader_clear(decoder->private_->input)) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_reset(FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->protected_); + + if(!FLAC__stream_decoder_flush(decoder)) { + /* above call sets the state for us */ + return false; + } + +#if FLAC__HAS_OGG + /*@@@ could go in !internal_reset_hack block below */ + if(decoder->private_->is_ogg) + FLAC__ogg_decoder_aspect_reset(&decoder->protected_->ogg_decoder_aspect); +#endif + + /* Rewind if necessary. If FLAC__stream_decoder_init() is calling us, + * (internal_reset_hack) don't try to rewind since we are already at + * the beginning of the stream and don't want to fail if the input is + * not seekable. + */ + if(!decoder->private_->internal_reset_hack) { + if(decoder->private_->file == stdin) + return false; /* can't rewind stdin, reset fails */ + if(decoder->private_->seek_callback && decoder->private_->seek_callback(decoder, 0, decoder->private_->client_data) == FLAC__STREAM_DECODER_SEEK_STATUS_ERROR) + return false; /* seekable and seek fails, reset fails */ + } + else + decoder->private_->internal_reset_hack = false; + + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_METADATA; + + decoder->private_->has_stream_info = false; + if(decoder->private_->has_seek_table && 0 != decoder->private_->seek_table.data.seek_table.points) { + free(decoder->private_->seek_table.data.seek_table.points); + decoder->private_->seek_table.data.seek_table.points = 0; + decoder->private_->has_seek_table = false; + } + decoder->private_->do_md5_checking = decoder->protected_->md5_checking; + /* + * This goes in reset() and not flush() because according to the spec, a + * fixed-blocksize stream must stay that way through the whole stream. + */ + decoder->private_->fixed_block_size = decoder->private_->next_fixed_block_size = 0; + + /* We initialize the FLAC__MD5Context even though we may never use it. This + * is because md5 checking may be turned on to start and then turned off if + * a seek occurs. So we init the context here and finalize it in + * FLAC__stream_decoder_finish() to make sure things are always cleaned up + * properly. + */ + FLAC__MD5Init(&decoder->private_->md5context); + + decoder->private_->first_frame_offset = 0; + decoder->private_->unparseable_frame_count = 0; + + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_process_single(FLAC__StreamDecoder *decoder) +{ + FLAC__bool got_a_frame; + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + + while(1) { + switch(decoder->protected_->state) { + case FLAC__STREAM_DECODER_SEARCH_FOR_METADATA: + if(!find_metadata_(decoder)) + return false; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_READ_METADATA: + if(!read_metadata_(decoder)) + return false; /* above function sets the status for us */ + else + return true; + case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC: + if(!frame_sync_(decoder)) + return true; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_READ_FRAME: + if(!read_frame_(decoder, &got_a_frame, /*do_full_decode=*/true)) + return false; /* above function sets the status for us */ + if(got_a_frame) + return true; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_END_OF_STREAM: + case FLAC__STREAM_DECODER_ABORTED: + return true; + default: + FLAC__ASSERT(0); + return false; + } + } +} + +FLAC_API FLAC__bool FLAC__stream_decoder_process_until_end_of_metadata(FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + + while(1) { + switch(decoder->protected_->state) { + case FLAC__STREAM_DECODER_SEARCH_FOR_METADATA: + if(!find_metadata_(decoder)) + return false; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_READ_METADATA: + if(!read_metadata_(decoder)) + return false; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC: + case FLAC__STREAM_DECODER_READ_FRAME: + case FLAC__STREAM_DECODER_END_OF_STREAM: + case FLAC__STREAM_DECODER_ABORTED: + return true; + default: + FLAC__ASSERT(0); + return false; + } + } +} + +FLAC_API FLAC__bool FLAC__stream_decoder_process_until_end_of_stream(FLAC__StreamDecoder *decoder) +{ + FLAC__bool dummy; + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + + while(1) { + switch(decoder->protected_->state) { + case FLAC__STREAM_DECODER_SEARCH_FOR_METADATA: + if(!find_metadata_(decoder)) + return false; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_READ_METADATA: + if(!read_metadata_(decoder)) + return false; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC: + if(!frame_sync_(decoder)) + return true; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_READ_FRAME: + if(!read_frame_(decoder, &dummy, /*do_full_decode=*/true)) + return false; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_END_OF_STREAM: + case FLAC__STREAM_DECODER_ABORTED: + return true; + default: + FLAC__ASSERT(0); + return false; + } + } +} + +FLAC_API FLAC__bool FLAC__stream_decoder_skip_single_frame(FLAC__StreamDecoder *decoder) +{ + FLAC__bool got_a_frame; + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + + while(1) { + switch(decoder->protected_->state) { + case FLAC__STREAM_DECODER_SEARCH_FOR_METADATA: + case FLAC__STREAM_DECODER_READ_METADATA: + return false; /* above function sets the status for us */ + case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC: + if(!frame_sync_(decoder)) + return true; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_READ_FRAME: + if(!read_frame_(decoder, &got_a_frame, /*do_full_decode=*/false)) + return false; /* above function sets the status for us */ + if(got_a_frame) + return true; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_END_OF_STREAM: + case FLAC__STREAM_DECODER_ABORTED: + return true; + default: + FLAC__ASSERT(0); + return false; + } + } +} + +FLAC_API FLAC__bool FLAC__stream_decoder_seek_absolute(FLAC__StreamDecoder *decoder, FLAC__uint64 sample) +{ + FLAC__uint64 length; + + FLAC__ASSERT(0 != decoder); + + if( + decoder->protected_->state != FLAC__STREAM_DECODER_SEARCH_FOR_METADATA && + decoder->protected_->state != FLAC__STREAM_DECODER_READ_METADATA && + decoder->protected_->state != FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC && + decoder->protected_->state != FLAC__STREAM_DECODER_READ_FRAME && + decoder->protected_->state != FLAC__STREAM_DECODER_END_OF_STREAM + ) + return false; + + if(0 == decoder->private_->seek_callback) + return false; + + FLAC__ASSERT(decoder->private_->seek_callback); + FLAC__ASSERT(decoder->private_->tell_callback); + FLAC__ASSERT(decoder->private_->length_callback); + FLAC__ASSERT(decoder->private_->eof_callback); + + if(FLAC__stream_decoder_get_total_samples(decoder) > 0 && sample >= FLAC__stream_decoder_get_total_samples(decoder)) + return false; + + decoder->private_->is_seeking = true; + + /* turn off md5 checking if a seek is attempted */ + decoder->private_->do_md5_checking = false; + + /* get the file length (currently our algorithm needs to know the length so it's also an error to get FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED) */ + if(decoder->private_->length_callback(decoder, &length, decoder->private_->client_data) != FLAC__STREAM_DECODER_LENGTH_STATUS_OK) { + decoder->private_->is_seeking = false; + return false; + } + + /* if we haven't finished processing the metadata yet, do that so we have the STREAMINFO, SEEK_TABLE, and first_frame_offset */ + if( + decoder->protected_->state == FLAC__STREAM_DECODER_SEARCH_FOR_METADATA || + decoder->protected_->state == FLAC__STREAM_DECODER_READ_METADATA + ) { + if(!FLAC__stream_decoder_process_until_end_of_metadata(decoder)) { + /* above call sets the state for us */ + decoder->private_->is_seeking = false; + return false; + } + /* check this again in case we didn't know total_samples the first time */ + if(FLAC__stream_decoder_get_total_samples(decoder) > 0 && sample >= FLAC__stream_decoder_get_total_samples(decoder)) { + decoder->private_->is_seeking = false; + return false; + } + } + + { + const FLAC__bool ok = +#if FLAC__HAS_OGG + decoder->private_->is_ogg? + seek_to_absolute_sample_ogg_(decoder, length, sample) : +#endif + seek_to_absolute_sample_(decoder, length, sample) + ; + decoder->private_->is_seeking = false; + return ok; + } +} + +/*********************************************************************** + * + * Protected class methods + * + ***********************************************************************/ + +unsigned FLAC__stream_decoder_get_input_bytes_unconsumed(const FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)); + FLAC__ASSERT(!(FLAC__bitreader_get_input_bits_unconsumed(decoder->private_->input) & 7)); + return FLAC__bitreader_get_input_bits_unconsumed(decoder->private_->input) / 8; +} + +/*********************************************************************** + * + * Private class methods + * + ***********************************************************************/ + +void set_defaults_dec(FLAC__StreamDecoder *decoder) +{ +#if FLAC__HAS_OGG + decoder->private_->is_ogg = false; +#endif + decoder->private_->read_callback = 0; + decoder->private_->seek_callback = 0; + decoder->private_->tell_callback = 0; + decoder->private_->length_callback = 0; + decoder->private_->eof_callback = 0; + decoder->private_->write_callback = 0; + decoder->private_->metadata_callback = 0; + decoder->private_->error_callback = 0; + decoder->private_->client_data = 0; + + memset(decoder->private_->metadata_filter, 0, sizeof(decoder->private_->metadata_filter)); + decoder->private_->metadata_filter[FLAC__METADATA_TYPE_STREAMINFO] = true; + decoder->private_->metadata_filter_ids_count = 0; + + decoder->protected_->md5_checking = false; + +#if FLAC__HAS_OGG + FLAC__ogg_decoder_aspect_set_defaults(&decoder->protected_->ogg_decoder_aspect); +#endif +} + +/* + * This will forcibly set stdin to binary mode (for OSes that require it) + */ +FILE *get_binary_stdin_(void) +{ + /* if something breaks here it is probably due to the presence or + * absence of an underscore before the identifiers 'setmode', + * 'fileno', and/or 'O_BINARY'; check your system header files. + */ +#if defined _MSC_VER || defined __MINGW32__ + _setmode(_fileno(stdin), _O_BINARY); +#elif defined __CYGWIN__ + /* almost certainly not needed for any modern Cygwin, but let's be safe... */ + setmode(_fileno(stdin), _O_BINARY); +#elif defined __EMX__ + setmode(fileno(stdin), O_BINARY); +#endif + + return stdin; +} + +FLAC__bool allocate_output_(FLAC__StreamDecoder *decoder, unsigned size, unsigned channels) +{ + unsigned i; + FLAC__int32 *tmp; + + if(size <= decoder->private_->output_capacity && channels <= decoder->private_->output_channels) + return true; + + /* simply using realloc() is not practical because the number of channels may change mid-stream */ + + for(i = 0; i < FLAC__MAX_CHANNELS; i++) { + if(0 != decoder->private_->output[i]) { + free(decoder->private_->output[i]-4); + decoder->private_->output[i] = 0; + } + if(0 != decoder->private_->residual_unaligned[i]) { + free(decoder->private_->residual_unaligned[i]); + decoder->private_->residual_unaligned[i] = decoder->private_->residual[i] = 0; + } + } + + for(i = 0; i < channels; i++) { + /* WATCHOUT: + * FLAC__lpc_restore_signal_asm_ia32_mmx() requires that the + * output arrays have a buffer of up to 3 zeroes in front + * (at negative indices) for alignment purposes; we use 4 + * to keep the data well-aligned. + */ + tmp = (FLAC__int32*)safe_malloc_muladd2_(sizeof(FLAC__int32), /*times (*/size, /*+*/4/*)*/); + if(tmp == 0) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + memset(tmp, 0, sizeof(FLAC__int32)*4); + decoder->private_->output[i] = tmp + 4; + + /* WATCHOUT: + * minimum of quadword alignment for PPC vector optimizations is REQUIRED: + */ + if(!FLAC__memory_alloc_aligned_int32_array(size, &decoder->private_->residual_unaligned[i], &decoder->private_->residual[i])) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + } + + decoder->private_->output_capacity = size; + decoder->private_->output_channels = channels; + + return true; +} + +FLAC__bool has_id_filtered_(FLAC__StreamDecoder *decoder, FLAC__byte *id) +{ + size_t i; + + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + + for(i = 0; i < decoder->private_->metadata_filter_ids_count; i++) + if(0 == memcmp(decoder->private_->metadata_filter_ids + i * (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8), id, (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8))) + return true; + + return false; +} + +FLAC__bool find_metadata_(FLAC__StreamDecoder *decoder) +{ + FLAC__uint32 x; + unsigned i, id_; + FLAC__bool first = true; + + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)); + + for(i = id_ = 0; i < 4; ) { + if(decoder->private_->cached) { + x = (FLAC__uint32)decoder->private_->lookahead; + decoder->private_->cached = false; + } + else { + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + } + if(x == FLAC__STREAM_SYNC_STRING[i]) { + first = true; + i++; + id_ = 0; + continue; + } + if(x == ID3V2_TAG_[id_]) { + id_++; + i = 0; + if(id_ == 3) { + if(!skip_id3v2_tag_(decoder)) + return false; /* skip_id3v2_tag_ sets the state for us */ + } + continue; + } + id_ = 0; + if(x == 0xff) { /* MAGIC NUMBER for the first 8 frame sync bits */ + decoder->private_->header_warmup[0] = (FLAC__byte)x; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + + /* we have to check if we just read two 0xff's in a row; the second may actually be the beginning of the sync code */ + /* else we have to check if the second byte is the end of a sync code */ + if(x == 0xff) { /* MAGIC NUMBER for the first 8 frame sync bits */ + decoder->private_->lookahead = (FLAC__byte)x; + decoder->private_->cached = true; + } + else if(x >> 2 == 0x3e) { /* MAGIC NUMBER for the last 6 sync bits */ + decoder->private_->header_warmup[1] = (FLAC__byte)x; + decoder->protected_->state = FLAC__STREAM_DECODER_READ_FRAME; + return true; + } + } + i = 0; + if(first) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + first = false; + } + } + + decoder->protected_->state = FLAC__STREAM_DECODER_READ_METADATA; + return true; +} + +FLAC__bool read_metadata_(FLAC__StreamDecoder *decoder) +{ + FLAC__bool is_last; + FLAC__uint32 i, x, type, length; + + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)); + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_IS_LAST_LEN)) + return false; /* read_callback_ sets the state for us */ + is_last = x? true : false; + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &type, FLAC__STREAM_METADATA_TYPE_LEN)) + return false; /* read_callback_ sets the state for us */ + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &length, FLAC__STREAM_METADATA_LENGTH_LEN)) + return false; /* read_callback_ sets the state for us */ + + if(type == FLAC__METADATA_TYPE_STREAMINFO) { + if(!read_metadata_streaminfo_(decoder, is_last, length)) + return false; + + decoder->private_->has_stream_info = true; + if(0 == memcmp(decoder->private_->stream_info.data.stream_info.md5sum, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16)) + decoder->private_->do_md5_checking = false; + if(!decoder->private_->is_seeking && decoder->private_->metadata_filter[FLAC__METADATA_TYPE_STREAMINFO] && decoder->private_->metadata_callback) + decoder->private_->metadata_callback(decoder, &decoder->private_->stream_info, decoder->private_->client_data); + } + else if(type == FLAC__METADATA_TYPE_SEEKTABLE) { + if(!read_metadata_seektable_(decoder, is_last, length)) + return false; + + decoder->private_->has_seek_table = true; + if(!decoder->private_->is_seeking && decoder->private_->metadata_filter[FLAC__METADATA_TYPE_SEEKTABLE] && decoder->private_->metadata_callback) + decoder->private_->metadata_callback(decoder, &decoder->private_->seek_table, decoder->private_->client_data); + } + else { + FLAC__bool skip_it = !decoder->private_->metadata_filter[type]; + unsigned real_length = length; + FLAC__StreamMetadata block; + + block.is_last = is_last; + block.type = (FLAC__MetadataType)type; + block.length = length; + + if(type == FLAC__METADATA_TYPE_APPLICATION) { + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, block.data.application.id, FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8)) + return false; /* read_callback_ sets the state for us */ + + if(real_length < FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8) { /* underflow check */ + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR;/*@@@@@@ maybe wrong error? need to resync?*/ + return false; + } + + real_length -= FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8; + + if(decoder->private_->metadata_filter_ids_count > 0 && has_id_filtered_(decoder, block.data.application.id)) + skip_it = !skip_it; + } + + if(skip_it) { + if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(decoder->private_->input, real_length)) + return false; /* read_callback_ sets the state for us */ + } + else { + switch(type) { + case FLAC__METADATA_TYPE_PADDING: + /* skip the padding bytes */ + if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(decoder->private_->input, real_length)) + return false; /* read_callback_ sets the state for us */ + break; + case FLAC__METADATA_TYPE_APPLICATION: + /* remember, we read the ID already */ + if(real_length > 0) { + if(0 == (block.data.application.data = (FLAC__byte*)malloc(real_length))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, block.data.application.data, real_length)) + return false; /* read_callback_ sets the state for us */ + } + else + block.data.application.data = 0; + break; + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + if(!read_metadata_vorbiscomment_(decoder, &block.data.vorbis_comment)) + return false; + break; + case FLAC__METADATA_TYPE_CUESHEET: + if(!read_metadata_cuesheet_(decoder, &block.data.cue_sheet)) + return false; + break; + case FLAC__METADATA_TYPE_PICTURE: + if(!read_metadata_picture_(decoder, &block.data.picture)) + return false; + break; + case FLAC__METADATA_TYPE_STREAMINFO: + case FLAC__METADATA_TYPE_SEEKTABLE: + FLAC__ASSERT(0); + break; + default: + if(real_length > 0) { + if(0 == (block.data.unknown.data = (FLAC__byte*)malloc(real_length))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, block.data.unknown.data, real_length)) + return false; /* read_callback_ sets the state for us */ + } + else + block.data.unknown.data = 0; + break; + } + if(!decoder->private_->is_seeking && decoder->private_->metadata_callback) + decoder->private_->metadata_callback(decoder, &block, decoder->private_->client_data); + + /* now we have to free any malloc()ed data in the block */ + switch(type) { + case FLAC__METADATA_TYPE_PADDING: + break; + case FLAC__METADATA_TYPE_APPLICATION: + if(0 != block.data.application.data) + free(block.data.application.data); + break; + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + if(0 != block.data.vorbis_comment.vendor_string.entry) + free(block.data.vorbis_comment.vendor_string.entry); + if(block.data.vorbis_comment.num_comments > 0) + for(i = 0; i < block.data.vorbis_comment.num_comments; i++) + if(0 != block.data.vorbis_comment.comments[i].entry) + free(block.data.vorbis_comment.comments[i].entry); + if(0 != block.data.vorbis_comment.comments) + free(block.data.vorbis_comment.comments); + break; + case FLAC__METADATA_TYPE_CUESHEET: + if(block.data.cue_sheet.num_tracks > 0) + for(i = 0; i < block.data.cue_sheet.num_tracks; i++) + if(0 != block.data.cue_sheet.tracks[i].indices) + free(block.data.cue_sheet.tracks[i].indices); + if(0 != block.data.cue_sheet.tracks) + free(block.data.cue_sheet.tracks); + break; + case FLAC__METADATA_TYPE_PICTURE: + if(0 != block.data.picture.mime_type) + free(block.data.picture.mime_type); + if(0 != block.data.picture.description) + free(block.data.picture.description); + if(0 != block.data.picture.data) + free(block.data.picture.data); + break; + case FLAC__METADATA_TYPE_STREAMINFO: + case FLAC__METADATA_TYPE_SEEKTABLE: + FLAC__ASSERT(0); + default: + if(0 != block.data.unknown.data) + free(block.data.unknown.data); + break; + } + } + } + + if(is_last) { + /* if this fails, it's OK, it's just a hint for the seek routine */ + if(!FLAC__stream_decoder_get_decode_position(decoder, &decoder->private_->first_frame_offset)) + decoder->private_->first_frame_offset = 0; + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + } + + return true; +} + +FLAC__bool read_metadata_streaminfo_(FLAC__StreamDecoder *decoder, FLAC__bool is_last, unsigned length) +{ + FLAC__uint32 x; + unsigned bits, used_bits = 0; + + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)); + + decoder->private_->stream_info.type = FLAC__METADATA_TYPE_STREAMINFO; + decoder->private_->stream_info.is_last = is_last; + decoder->private_->stream_info.length = length; + + bits = FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, bits)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->stream_info.data.stream_info.min_blocksize = x; + used_bits += bits; + + bits = FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->stream_info.data.stream_info.max_blocksize = x; + used_bits += bits; + + bits = FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->stream_info.data.stream_info.min_framesize = x; + used_bits += bits; + + bits = FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->stream_info.data.stream_info.max_framesize = x; + used_bits += bits; + + bits = FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->stream_info.data.stream_info.sample_rate = x; + used_bits += bits; + + bits = FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->stream_info.data.stream_info.channels = x+1; + used_bits += bits; + + bits = FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->stream_info.data.stream_info.bits_per_sample = x+1; + used_bits += bits; + + bits = FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN; + if(!FLAC__bitreader_read_raw_uint64(decoder->private_->input, &decoder->private_->stream_info.data.stream_info.total_samples, FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN)) + return false; /* read_callback_ sets the state for us */ + used_bits += bits; + + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, decoder->private_->stream_info.data.stream_info.md5sum, 16)) + return false; /* read_callback_ sets the state for us */ + used_bits += 16*8; + + /* skip the rest of the block */ + FLAC__ASSERT(used_bits % 8 == 0); + length -= (used_bits / 8); + if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(decoder->private_->input, length)) + return false; /* read_callback_ sets the state for us */ + + return true; +} + +FLAC__bool read_metadata_seektable_(FLAC__StreamDecoder *decoder, FLAC__bool is_last, unsigned length) +{ + FLAC__uint32 i, x; + FLAC__uint64 xx; + + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)); + + decoder->private_->seek_table.type = FLAC__METADATA_TYPE_SEEKTABLE; + decoder->private_->seek_table.is_last = is_last; + decoder->private_->seek_table.length = length; + + decoder->private_->seek_table.data.seek_table.num_points = length / FLAC__STREAM_METADATA_SEEKPOINT_LENGTH; + + /* use realloc since we may pass through here several times (e.g. after seeking) */ + if(0 == (decoder->private_->seek_table.data.seek_table.points = (FLAC__StreamMetadata_SeekPoint*)safe_realloc_mul_2op_(decoder->private_->seek_table.data.seek_table.points, decoder->private_->seek_table.data.seek_table.num_points, /*times*/sizeof(FLAC__StreamMetadata_SeekPoint)))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + for(i = 0; i < decoder->private_->seek_table.data.seek_table.num_points; i++) { + if(!FLAC__bitreader_read_raw_uint64(decoder->private_->input, &xx, FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->seek_table.data.seek_table.points[i].sample_number = xx; + + if(!FLAC__bitreader_read_raw_uint64(decoder->private_->input, &xx, FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->seek_table.data.seek_table.points[i].stream_offset = xx; + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->seek_table.data.seek_table.points[i].frame_samples = x; + } + length -= (decoder->private_->seek_table.data.seek_table.num_points * FLAC__STREAM_METADATA_SEEKPOINT_LENGTH); + /* if there is a partial point left, skip over it */ + if(length > 0) { + /*@@@ do a send_error_to_client_() here? there's an argument for either way */ + if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(decoder->private_->input, length)) + return false; /* read_callback_ sets the state for us */ + } + + return true; +} + +FLAC__bool read_metadata_vorbiscomment_(FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_VorbisComment *obj) +{ + FLAC__uint32 i; + + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)); + + /* read vendor string */ + FLAC__ASSERT(FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN == 32); + if(!FLAC__bitreader_read_uint32_little_endian(decoder->private_->input, &obj->vendor_string.length)) + return false; /* read_callback_ sets the state for us */ + if(obj->vendor_string.length > 0) { + if(0 == (obj->vendor_string.entry = (FLAC__byte*)safe_malloc_add_2op_(obj->vendor_string.length, /*+*/1))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, obj->vendor_string.entry, obj->vendor_string.length)) + return false; /* read_callback_ sets the state for us */ + obj->vendor_string.entry[obj->vendor_string.length] = '\0'; + } + else + obj->vendor_string.entry = 0; + + /* read num comments */ + FLAC__ASSERT(FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN == 32); + if(!FLAC__bitreader_read_uint32_little_endian(decoder->private_->input, &obj->num_comments)) + return false; /* read_callback_ sets the state for us */ + + /* read comments */ + if(obj->num_comments > 0) { + if(0 == (obj->comments = (FLAC__StreamMetadata_VorbisComment_Entry*)safe_malloc_mul_2op_(obj->num_comments, /*times*/sizeof(FLAC__StreamMetadata_VorbisComment_Entry)))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + for(i = 0; i < obj->num_comments; i++) { + FLAC__ASSERT(FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN == 32); + if(!FLAC__bitreader_read_uint32_little_endian(decoder->private_->input, &obj->comments[i].length)) + return false; /* read_callback_ sets the state for us */ + if(obj->comments[i].length > 0) { + if(0 == (obj->comments[i].entry = (FLAC__byte*)safe_malloc_add_2op_(obj->comments[i].length, /*+*/1))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, obj->comments[i].entry, obj->comments[i].length)) + return false; /* read_callback_ sets the state for us */ + obj->comments[i].entry[obj->comments[i].length] = '\0'; + } + else + obj->comments[i].entry = 0; + } + } + else { + obj->comments = 0; + } + + return true; +} + +FLAC__bool read_metadata_cuesheet_(FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_CueSheet *obj) +{ + FLAC__uint32 i, j, x; + + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)); + + memset(obj, 0, sizeof(FLAC__StreamMetadata_CueSheet)); + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN % 8 == 0); + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, (FLAC__byte*)obj->media_catalog_number, FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN/8)) + return false; /* read_callback_ sets the state for us */ + + if(!FLAC__bitreader_read_raw_uint64(decoder->private_->input, &obj->lead_in, FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN)) + return false; /* read_callback_ sets the state for us */ + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN)) + return false; /* read_callback_ sets the state for us */ + obj->is_cd = x? true : false; + + if(!FLAC__bitreader_skip_bits_no_crc(decoder->private_->input, FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN)) + return false; /* read_callback_ sets the state for us */ + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN)) + return false; /* read_callback_ sets the state for us */ + obj->num_tracks = x; + + if(obj->num_tracks > 0) { + if(0 == (obj->tracks = (FLAC__StreamMetadata_CueSheet_Track*)safe_calloc_(obj->num_tracks, sizeof(FLAC__StreamMetadata_CueSheet_Track)))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + for(i = 0; i < obj->num_tracks; i++) { + FLAC__StreamMetadata_CueSheet_Track *track = &obj->tracks[i]; + if(!FLAC__bitreader_read_raw_uint64(decoder->private_->input, &track->offset, FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN)) + return false; /* read_callback_ sets the state for us */ + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN)) + return false; /* read_callback_ sets the state for us */ + track->number = (FLAC__byte)x; + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN % 8 == 0); + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, (FLAC__byte*)track->isrc, FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN/8)) + return false; /* read_callback_ sets the state for us */ + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN)) + return false; /* read_callback_ sets the state for us */ + track->type = x; + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN)) + return false; /* read_callback_ sets the state for us */ + track->pre_emphasis = x; + + if(!FLAC__bitreader_skip_bits_no_crc(decoder->private_->input, FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN)) + return false; /* read_callback_ sets the state for us */ + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN)) + return false; /* read_callback_ sets the state for us */ + track->num_indices = (FLAC__byte)x; + + if(track->num_indices > 0) { + if(0 == (track->indices = (FLAC__StreamMetadata_CueSheet_Index*)safe_calloc_(track->num_indices, sizeof(FLAC__StreamMetadata_CueSheet_Index)))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + for(j = 0; j < track->num_indices; j++) { + FLAC__StreamMetadata_CueSheet_Index *index = &track->indices[j]; + if(!FLAC__bitreader_read_raw_uint64(decoder->private_->input, &index->offset, FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN)) + return false; /* read_callback_ sets the state for us */ + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN)) + return false; /* read_callback_ sets the state for us */ + index->number = (FLAC__byte)x; + + if(!FLAC__bitreader_skip_bits_no_crc(decoder->private_->input, FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN)) + return false; /* read_callback_ sets the state for us */ + } + } + } + } + + return true; +} + +FLAC__bool read_metadata_picture_(FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_Picture *obj) +{ + FLAC__uint32 x; + + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)); + + /* read type */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_PICTURE_TYPE_LEN)) + return false; /* read_callback_ sets the state for us */ + obj->type = (FLAC__StreamMetadata_Picture_Type) x; + + /* read MIME type */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN)) + return false; /* read_callback_ sets the state for us */ + if(0 == (obj->mime_type = (char*)safe_malloc_add_2op_(x, /*+*/1))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + if(x > 0) { + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, (FLAC__byte*)obj->mime_type, x)) + return false; /* read_callback_ sets the state for us */ + } + obj->mime_type[x] = '\0'; + + /* read description */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN)) + return false; /* read_callback_ sets the state for us */ + if(0 == (obj->description = (FLAC__byte*)safe_malloc_add_2op_(x, /*+*/1))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + if(x > 0) { + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, obj->description, x)) + return false; /* read_callback_ sets the state for us */ + } + obj->description[x] = '\0'; + + /* read width */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &obj->width, FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN)) + return false; /* read_callback_ sets the state for us */ + + /* read height */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &obj->height, FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN)) + return false; /* read_callback_ sets the state for us */ + + /* read depth */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &obj->depth, FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN)) + return false; /* read_callback_ sets the state for us */ + + /* read colors */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &obj->colors, FLAC__STREAM_METADATA_PICTURE_COLORS_LEN)) + return false; /* read_callback_ sets the state for us */ + + /* read data */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &(obj->data_length), FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN)) + return false; /* read_callback_ sets the state for us */ + if(0 == (obj->data = (FLAC__byte*)safe_malloc_(obj->data_length))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + if(obj->data_length > 0) { + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, obj->data, obj->data_length)) + return false; /* read_callback_ sets the state for us */ + } + + return true; +} + +FLAC__bool skip_id3v2_tag_(FLAC__StreamDecoder *decoder) +{ + FLAC__uint32 x; + unsigned i, skip; + + /* skip the version and flags bytes */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 24)) + return false; /* read_callback_ sets the state for us */ + /* get the size (in bytes) to skip */ + skip = 0; + for(i = 0; i < 4; i++) { + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + skip <<= 7; + skip |= (x & 0x7f); + } + /* skip the rest of the tag */ + if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(decoder->private_->input, skip)) + return false; /* read_callback_ sets the state for us */ + return true; +} + +FLAC__bool frame_sync_(FLAC__StreamDecoder *decoder) +{ + FLAC__uint32 x; + FLAC__bool first = true; + + /* If we know the total number of samples in the stream, stop if we've read that many. */ + /* This will stop us, for example, from wasting time trying to sync on an ID3V1 tag. */ + if(FLAC__stream_decoder_get_total_samples(decoder) > 0) { + if(decoder->private_->samples_decoded >= FLAC__stream_decoder_get_total_samples(decoder)) { + decoder->protected_->state = FLAC__STREAM_DECODER_END_OF_STREAM; + return true; + } + } + + /* make sure we're byte aligned */ + if(!FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)) { + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__bitreader_bits_left_for_byte_alignment(decoder->private_->input))) + return false; /* read_callback_ sets the state for us */ + } + + while(1) { + if(decoder->private_->cached) { + x = (FLAC__uint32)decoder->private_->lookahead; + decoder->private_->cached = false; + } + else { + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + } + if(x == 0xff) { /* MAGIC NUMBER for the first 8 frame sync bits */ + decoder->private_->header_warmup[0] = (FLAC__byte)x; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + + /* we have to check if we just read two 0xff's in a row; the second may actually be the beginning of the sync code */ + /* else we have to check if the second byte is the end of a sync code */ + if(x == 0xff) { /* MAGIC NUMBER for the first 8 frame sync bits */ + decoder->private_->lookahead = (FLAC__byte)x; + decoder->private_->cached = true; + } + else if(x >> 2 == 0x3e) { /* MAGIC NUMBER for the last 6 sync bits */ + decoder->private_->header_warmup[1] = (FLAC__byte)x; + decoder->protected_->state = FLAC__STREAM_DECODER_READ_FRAME; + return true; + } + } + if(first) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + first = false; + } + } + + return true; +} + +FLAC__bool read_frame_(FLAC__StreamDecoder *decoder, FLAC__bool *got_a_frame, FLAC__bool do_full_decode) +{ + unsigned channel; + unsigned i; + FLAC__int32 mid, side; + unsigned frame_crc; /* the one we calculate from the input stream */ + FLAC__uint32 x; + + *got_a_frame = false; + + /* init the CRC */ + frame_crc = 0; + frame_crc = FLAC__CRC16_UPDATE(decoder->private_->header_warmup[0], frame_crc); + frame_crc = FLAC__CRC16_UPDATE(decoder->private_->header_warmup[1], frame_crc); + FLAC__bitreader_reset_read_crc16(decoder->private_->input, (FLAC__uint16)frame_crc); + + if(!read_frame_header_(decoder)) + return false; + if(decoder->protected_->state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC) /* means we didn't sync on a valid header */ + return true; + if(!allocate_output_(decoder, decoder->private_->frame.header.blocksize, decoder->private_->frame.header.channels)) + return false; + for(channel = 0; channel < decoder->private_->frame.header.channels; channel++) { + /* + * first figure the correct bits-per-sample of the subframe + */ + unsigned bps = decoder->private_->frame.header.bits_per_sample; + switch(decoder->private_->frame.header.channel_assignment) { + case FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT: + /* no adjustment needed */ + break; + case FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE: + FLAC__ASSERT(decoder->private_->frame.header.channels == 2); + if(channel == 1) + bps++; + break; + case FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE: + FLAC__ASSERT(decoder->private_->frame.header.channels == 2); + if(channel == 0) + bps++; + break; + case FLAC__CHANNEL_ASSIGNMENT_MID_SIDE: + FLAC__ASSERT(decoder->private_->frame.header.channels == 2); + if(channel == 1) + bps++; + break; + default: + FLAC__ASSERT(0); + } + /* + * now read it + */ + if(!read_subframe_(decoder, channel, bps, do_full_decode)) + return false; + if(decoder->protected_->state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC) /* means bad sync or got corruption */ + return true; + } + if(!read_zero_padding_(decoder)) + return false; + if(decoder->protected_->state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC) /* means bad sync or got corruption (i.e. "zero bits" were not all zeroes) */ + return true; + + /* + * Read the frame CRC-16 from the footer and check + */ + frame_crc = FLAC__bitreader_get_read_crc16(decoder->private_->input); + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__FRAME_FOOTER_CRC_LEN)) + return false; /* read_callback_ sets the state for us */ + if(frame_crc == x) { + if(do_full_decode) { + /* Undo any special channel coding */ + switch(decoder->private_->frame.header.channel_assignment) { + case FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT: + /* do nothing */ + break; + case FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE: + FLAC__ASSERT(decoder->private_->frame.header.channels == 2); + for(i = 0; i < decoder->private_->frame.header.blocksize; i++) + decoder->private_->output[1][i] = decoder->private_->output[0][i] - decoder->private_->output[1][i]; + break; + case FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE: + FLAC__ASSERT(decoder->private_->frame.header.channels == 2); + for(i = 0; i < decoder->private_->frame.header.blocksize; i++) + decoder->private_->output[0][i] += decoder->private_->output[1][i]; + break; + case FLAC__CHANNEL_ASSIGNMENT_MID_SIDE: + FLAC__ASSERT(decoder->private_->frame.header.channels == 2); + for(i = 0; i < decoder->private_->frame.header.blocksize; i++) { +#if 1 + mid = decoder->private_->output[0][i]; + side = decoder->private_->output[1][i]; + mid <<= 1; + mid |= (side & 1); /* i.e. if 'side' is odd... */ + decoder->private_->output[0][i] = (mid + side) >> 1; + decoder->private_->output[1][i] = (mid - side) >> 1; +#else + /* OPT: without 'side' temp variable */ + mid = (decoder->private_->output[0][i] << 1) | (decoder->private_->output[1][i] & 1); /* i.e. if 'side' is odd... */ + decoder->private_->output[0][i] = (mid + decoder->private_->output[1][i]) >> 1; + decoder->private_->output[1][i] = (mid - decoder->private_->output[1][i]) >> 1; +#endif + } + break; + default: + FLAC__ASSERT(0); + break; + } + } + } + else { + /* Bad frame, emit error and zero the output signal */ + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH); + if(do_full_decode) { + for(channel = 0; channel < decoder->private_->frame.header.channels; channel++) { + memset(decoder->private_->output[channel], 0, sizeof(FLAC__int32) * decoder->private_->frame.header.blocksize); + } + } + } + + *got_a_frame = true; + + /* we wait to update fixed_block_size until here, when we're sure we've got a proper frame and hence a correct blocksize */ + if(decoder->private_->next_fixed_block_size) + decoder->private_->fixed_block_size = decoder->private_->next_fixed_block_size; + + /* put the latest values into the public section of the decoder instance */ + decoder->protected_->channels = decoder->private_->frame.header.channels; + decoder->protected_->channel_assignment = decoder->private_->frame.header.channel_assignment; + decoder->protected_->bits_per_sample = decoder->private_->frame.header.bits_per_sample; + decoder->protected_->sample_rate = decoder->private_->frame.header.sample_rate; + decoder->protected_->blocksize = decoder->private_->frame.header.blocksize; + + FLAC__ASSERT(decoder->private_->frame.header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER); + decoder->private_->samples_decoded = decoder->private_->frame.header.number.sample_number + decoder->private_->frame.header.blocksize; + + /* write it */ + if(do_full_decode) { + if(write_audio_frame_to_client_(decoder, &decoder->private_->frame, (const FLAC__int32 * const *)decoder->private_->output) != FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE) + return false; + } + + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; +} + +FLAC__bool read_frame_header_(FLAC__StreamDecoder *decoder) +{ + FLAC__uint32 x; + FLAC__uint64 xx; + unsigned i, blocksize_hint = 0, sample_rate_hint = 0; + FLAC__byte crc8, raw_header[16]; /* MAGIC NUMBER based on the maximum frame header size, including CRC */ + unsigned raw_header_len; + FLAC__bool is_unparseable = false; + + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)); + + /* init the raw header with the saved bits from synchronization */ + raw_header[0] = decoder->private_->header_warmup[0]; + raw_header[1] = decoder->private_->header_warmup[1]; + raw_header_len = 2; + + /* check to make sure that reserved bit is 0 */ + if(raw_header[1] & 0x02) /* MAGIC NUMBER */ + is_unparseable = true; + + /* + * Note that along the way as we read the header, we look for a sync + * code inside. If we find one it would indicate that our original + * sync was bad since there cannot be a sync code in a valid header. + * + * Three kinds of things can go wrong when reading the frame header: + * 1) We may have sync'ed incorrectly and not landed on a frame header. + * If we don't find a sync code, it can end up looking like we read + * a valid but unparseable header, until getting to the frame header + * CRC. Even then we could get a false positive on the CRC. + * 2) We may have sync'ed correctly but on an unparseable frame (from a + * future encoder). + * 3) We may be on a damaged frame which appears valid but unparseable. + * + * For all these reasons, we try and read a complete frame header as + * long as it seems valid, even if unparseable, up until the frame + * header CRC. + */ + + /* + * read in the raw header as bytes so we can CRC it, and parse it on the way + */ + for(i = 0; i < 2; i++) { + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + if(x == 0xff) { /* MAGIC NUMBER for the first 8 frame sync bits */ + /* if we get here it means our original sync was erroneous since the sync code cannot appear in the header */ + decoder->private_->lookahead = (FLAC__byte)x; + decoder->private_->cached = true; + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + raw_header[raw_header_len++] = (FLAC__byte)x; + } + + switch(x = raw_header[2] >> 4) { + case 0: + is_unparseable = true; + break; + case 1: + decoder->private_->frame.header.blocksize = 192; + break; + case 2: + case 3: + case 4: + case 5: + decoder->private_->frame.header.blocksize = 576 << (x-2); + break; + case 6: + case 7: + blocksize_hint = x; + break; + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + decoder->private_->frame.header.blocksize = 256 << (x-8); + break; + default: + FLAC__ASSERT(0); + break; + } + + switch(x = raw_header[2] & 0x0f) { + case 0: + if(decoder->private_->has_stream_info) + decoder->private_->frame.header.sample_rate = decoder->private_->stream_info.data.stream_info.sample_rate; + else + is_unparseable = true; + break; + case 1: + decoder->private_->frame.header.sample_rate = 88200; + break; + case 2: + decoder->private_->frame.header.sample_rate = 176400; + break; + case 3: + decoder->private_->frame.header.sample_rate = 192000; + break; + case 4: + decoder->private_->frame.header.sample_rate = 8000; + break; + case 5: + decoder->private_->frame.header.sample_rate = 16000; + break; + case 6: + decoder->private_->frame.header.sample_rate = 22050; + break; + case 7: + decoder->private_->frame.header.sample_rate = 24000; + break; + case 8: + decoder->private_->frame.header.sample_rate = 32000; + break; + case 9: + decoder->private_->frame.header.sample_rate = 44100; + break; + case 10: + decoder->private_->frame.header.sample_rate = 48000; + break; + case 11: + decoder->private_->frame.header.sample_rate = 96000; + break; + case 12: + case 13: + case 14: + sample_rate_hint = x; + break; + case 15: + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + default: + FLAC__ASSERT(0); + } + + x = (unsigned)(raw_header[3] >> 4); + if(x & 8) { + decoder->private_->frame.header.channels = 2; + switch(x & 7) { + case 0: + decoder->private_->frame.header.channel_assignment = FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE; + break; + case 1: + decoder->private_->frame.header.channel_assignment = FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE; + break; + case 2: + decoder->private_->frame.header.channel_assignment = FLAC__CHANNEL_ASSIGNMENT_MID_SIDE; + break; + default: + is_unparseable = true; + break; + } + } + else { + decoder->private_->frame.header.channels = (unsigned)x + 1; + decoder->private_->frame.header.channel_assignment = FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT; + } + + switch(x = (unsigned)(raw_header[3] & 0x0e) >> 1) { + case 0: + if(decoder->private_->has_stream_info) + decoder->private_->frame.header.bits_per_sample = decoder->private_->stream_info.data.stream_info.bits_per_sample; + else + is_unparseable = true; + break; + case 1: + decoder->private_->frame.header.bits_per_sample = 8; + break; + case 2: + decoder->private_->frame.header.bits_per_sample = 12; + break; + case 4: + decoder->private_->frame.header.bits_per_sample = 16; + break; + case 5: + decoder->private_->frame.header.bits_per_sample = 20; + break; + case 6: + decoder->private_->frame.header.bits_per_sample = 24; + break; + case 3: + case 7: + is_unparseable = true; + break; + default: + FLAC__ASSERT(0); + break; + } + + /* check to make sure that reserved bit is 0 */ + if(raw_header[3] & 0x01) /* MAGIC NUMBER */ + is_unparseable = true; + + /* read the frame's starting sample number (or frame number as the case may be) */ + if( + raw_header[1] & 0x01 || + /*@@@ this clause is a concession to the old way of doing variable blocksize; the only known implementation is flake and can probably be removed without inconveniencing anyone */ + (decoder->private_->has_stream_info && decoder->private_->stream_info.data.stream_info.min_blocksize != decoder->private_->stream_info.data.stream_info.max_blocksize) + ) { /* variable blocksize */ + if(!FLAC__bitreader_read_utf8_uint64(decoder->private_->input, &xx, raw_header, &raw_header_len)) + return false; /* read_callback_ sets the state for us */ + if(xx == FLAC__U64L(0xffffffffffffffff)) { /* i.e. non-UTF8 code... */ + decoder->private_->lookahead = raw_header[raw_header_len-1]; /* back up as much as we can */ + decoder->private_->cached = true; + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + decoder->private_->frame.header.number_type = FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER; + decoder->private_->frame.header.number.sample_number = xx; + } + else { /* fixed blocksize */ + if(!FLAC__bitreader_read_utf8_uint32(decoder->private_->input, &x, raw_header, &raw_header_len)) + return false; /* read_callback_ sets the state for us */ + if(x == 0xffffffff) { /* i.e. non-UTF8 code... */ + decoder->private_->lookahead = raw_header[raw_header_len-1]; /* back up as much as we can */ + decoder->private_->cached = true; + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + decoder->private_->frame.header.number_type = FLAC__FRAME_NUMBER_TYPE_FRAME_NUMBER; + decoder->private_->frame.header.number.frame_number = x; + } + + if(blocksize_hint) { + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + raw_header[raw_header_len++] = (FLAC__byte)x; + if(blocksize_hint == 7) { + FLAC__uint32 _x; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &_x, 8)) + return false; /* read_callback_ sets the state for us */ + raw_header[raw_header_len++] = (FLAC__byte)_x; + x = (x << 8) | _x; + } + decoder->private_->frame.header.blocksize = x+1; + } + + if(sample_rate_hint) { + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + raw_header[raw_header_len++] = (FLAC__byte)x; + if(sample_rate_hint != 12) { + FLAC__uint32 _x; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &_x, 8)) + return false; /* read_callback_ sets the state for us */ + raw_header[raw_header_len++] = (FLAC__byte)_x; + x = (x << 8) | _x; + } + if(sample_rate_hint == 12) + decoder->private_->frame.header.sample_rate = x*1000; + else if(sample_rate_hint == 13) + decoder->private_->frame.header.sample_rate = x; + else + decoder->private_->frame.header.sample_rate = x*10; + } + + /* read the CRC-8 byte */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + crc8 = (FLAC__byte)x; + + if(FLAC__crc8(raw_header, raw_header_len) != crc8) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + + /* calculate the sample number from the frame number if needed */ + decoder->private_->next_fixed_block_size = 0; + if(decoder->private_->frame.header.number_type == FLAC__FRAME_NUMBER_TYPE_FRAME_NUMBER) { + x = decoder->private_->frame.header.number.frame_number; + decoder->private_->frame.header.number_type = FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER; + if(decoder->private_->fixed_block_size) + decoder->private_->frame.header.number.sample_number = (FLAC__uint64)decoder->private_->fixed_block_size * (FLAC__uint64)x; + else if(decoder->private_->has_stream_info) { + if(decoder->private_->stream_info.data.stream_info.min_blocksize == decoder->private_->stream_info.data.stream_info.max_blocksize) { + decoder->private_->frame.header.number.sample_number = (FLAC__uint64)decoder->private_->stream_info.data.stream_info.min_blocksize * (FLAC__uint64)x; + decoder->private_->next_fixed_block_size = decoder->private_->stream_info.data.stream_info.max_blocksize; + } + else + is_unparseable = true; + } + else if(x == 0) { + decoder->private_->frame.header.number.sample_number = 0; + decoder->private_->next_fixed_block_size = decoder->private_->frame.header.blocksize; + } + else { + /* can only get here if the stream has invalid frame numbering and no STREAMINFO, so assume it's not the last (possibly short) frame */ + decoder->private_->frame.header.number.sample_number = (FLAC__uint64)decoder->private_->frame.header.blocksize * (FLAC__uint64)x; + } + } + + if(is_unparseable) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + + return true; +} + +FLAC__bool read_subframe_(FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, FLAC__bool do_full_decode) +{ + FLAC__uint32 x; + FLAC__bool wasted_bits; + unsigned i; + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) /* MAGIC NUMBER */ + return false; /* read_callback_ sets the state for us */ + + wasted_bits = (x & 1); + x &= 0xfe; + + if(wasted_bits) { + unsigned u; + if(!FLAC__bitreader_read_unary_unsigned(decoder->private_->input, &u)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->frame.subframes[channel].wasted_bits = u+1; + bps -= decoder->private_->frame.subframes[channel].wasted_bits; + } + else + decoder->private_->frame.subframes[channel].wasted_bits = 0; + + /* + * Lots of magic numbers here + */ + if(x & 0x80) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + else if(x == 0) { + if(!read_subframe_constant_(decoder, channel, bps, do_full_decode)) + return false; + } + else if(x == 2) { + if(!read_subframe_verbatim_(decoder, channel, bps, do_full_decode)) + return false; + } + else if(x < 16) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + else if(x <= 24) { + if(!read_subframe_fixed_(decoder, channel, bps, (x>>1)&7, do_full_decode)) + return false; + if(decoder->protected_->state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC) /* means bad sync or got corruption */ + return true; + } + else if(x < 64) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + else { + if(!read_subframe_lpc_(decoder, channel, bps, ((x>>1)&31)+1, do_full_decode)) + return false; + if(decoder->protected_->state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC) /* means bad sync or got corruption */ + return true; + } + + if(wasted_bits && do_full_decode) { + x = decoder->private_->frame.subframes[channel].wasted_bits; + for(i = 0; i < decoder->private_->frame.header.blocksize; i++) + decoder->private_->output[channel][i] <<= x; + } + + return true; +} + +FLAC__bool read_subframe_constant_(FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, FLAC__bool do_full_decode) +{ + FLAC__Subframe_Constant *subframe = &decoder->private_->frame.subframes[channel].data.constant; + FLAC__int32 x; + unsigned i; + FLAC__int32 *output = decoder->private_->output[channel]; + + decoder->private_->frame.subframes[channel].type = FLAC__SUBFRAME_TYPE_CONSTANT; + + if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &x, bps)) + return false; /* read_callback_ sets the state for us */ + + subframe->value = x; + + /* decode the subframe */ + if(do_full_decode) { + for(i = 0; i < decoder->private_->frame.header.blocksize; i++) + output[i] = x; + } + + return true; +} + +FLAC__bool read_subframe_fixed_(FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, const unsigned order, FLAC__bool do_full_decode) +{ + FLAC__Subframe_Fixed *subframe = &decoder->private_->frame.subframes[channel].data.fixed; + FLAC__int32 i32; + FLAC__uint32 u32; + unsigned u; + + decoder->private_->frame.subframes[channel].type = FLAC__SUBFRAME_TYPE_FIXED; + + subframe->residual = decoder->private_->residual[channel]; + subframe->order = order; + + /* read warm-up samples */ + for(u = 0; u < order; u++) { + if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &i32, bps)) + return false; /* read_callback_ sets the state for us */ + subframe->warmup[u] = i32; + } + + /* read entropy coding method info */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &u32, FLAC__ENTROPY_CODING_METHOD_TYPE_LEN)) + return false; /* read_callback_ sets the state for us */ + subframe->entropy_coding_method.type = (FLAC__EntropyCodingMethodType)u32; + switch(subframe->entropy_coding_method.type) { + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE: + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2: + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &u32, FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN)) + return false; /* read_callback_ sets the state for us */ + subframe->entropy_coding_method.data.partitioned_rice.order = u32; + subframe->entropy_coding_method.data.partitioned_rice.contents = &decoder->private_->partitioned_rice_contents[channel]; + break; + default: + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + + /* read residual */ + switch(subframe->entropy_coding_method.type) { + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE: + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2: + if(!read_residual_partitioned_rice_(decoder, order, subframe->entropy_coding_method.data.partitioned_rice.order, &decoder->private_->partitioned_rice_contents[channel], decoder->private_->residual[channel], /*is_extended=*/subframe->entropy_coding_method.type == FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2)) + return false; + break; + default: + FLAC__ASSERT(0); + } + + /* decode the subframe */ + if(do_full_decode) { + memcpy(decoder->private_->output[channel], subframe->warmup, sizeof(FLAC__int32) * order); + FLAC__fixed_restore_signal(decoder->private_->residual[channel], decoder->private_->frame.header.blocksize-order, order, decoder->private_->output[channel]+order); + } + + return true; +} + +FLAC__bool read_subframe_lpc_(FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, const unsigned order, FLAC__bool do_full_decode) +{ + FLAC__Subframe_LPC *subframe = &decoder->private_->frame.subframes[channel].data.lpc; + FLAC__int32 i32; + FLAC__uint32 u32; + unsigned u; + + decoder->private_->frame.subframes[channel].type = FLAC__SUBFRAME_TYPE_LPC; + + subframe->residual = decoder->private_->residual[channel]; + subframe->order = order; + + /* read warm-up samples */ + for(u = 0; u < order; u++) { + if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &i32, bps)) + return false; /* read_callback_ sets the state for us */ + subframe->warmup[u] = i32; + } + + /* read qlp coeff precision */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &u32, FLAC__SUBFRAME_LPC_QLP_COEFF_PRECISION_LEN)) + return false; /* read_callback_ sets the state for us */ + if(u32 == (1u << FLAC__SUBFRAME_LPC_QLP_COEFF_PRECISION_LEN) - 1) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + subframe->qlp_coeff_precision = u32+1; + + /* read qlp shift */ + if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &i32, FLAC__SUBFRAME_LPC_QLP_SHIFT_LEN)) + return false; /* read_callback_ sets the state for us */ + subframe->quantization_level = i32; + + /* read quantized lp coefficiencts */ + for(u = 0; u < order; u++) { + if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &i32, subframe->qlp_coeff_precision)) + return false; /* read_callback_ sets the state for us */ + subframe->qlp_coeff[u] = i32; + } + + /* read entropy coding method info */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &u32, FLAC__ENTROPY_CODING_METHOD_TYPE_LEN)) + return false; /* read_callback_ sets the state for us */ + subframe->entropy_coding_method.type = (FLAC__EntropyCodingMethodType)u32; + switch(subframe->entropy_coding_method.type) { + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE: + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2: + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &u32, FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN)) + return false; /* read_callback_ sets the state for us */ + subframe->entropy_coding_method.data.partitioned_rice.order = u32; + subframe->entropy_coding_method.data.partitioned_rice.contents = &decoder->private_->partitioned_rice_contents[channel]; + break; + default: + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + + /* read residual */ + switch(subframe->entropy_coding_method.type) { + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE: + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2: + if(!read_residual_partitioned_rice_(decoder, order, subframe->entropy_coding_method.data.partitioned_rice.order, &decoder->private_->partitioned_rice_contents[channel], decoder->private_->residual[channel], /*is_extended=*/subframe->entropy_coding_method.type == FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2)) + return false; + break; + default: + FLAC__ASSERT(0); + } + + /* decode the subframe */ + if(do_full_decode) { + memcpy(decoder->private_->output[channel], subframe->warmup, sizeof(FLAC__int32) * order); + /*@@@@@@ technically not pessimistic enough, should be more like + if( (FLAC__uint64)order * ((((FLAC__uint64)1)<qlp_coeff_precision)-1) < (((FLAC__uint64)-1) << 32) ) + */ + if(bps + subframe->qlp_coeff_precision + FLAC__bitmath_ilog2(order) <= 32) + if(bps <= 16 && subframe->qlp_coeff_precision <= 16) { + if(order <= 8) + decoder->private_->local_lpc_restore_signal_16bit_order8(decoder->private_->residual[channel], decoder->private_->frame.header.blocksize-order, subframe->qlp_coeff, order, subframe->quantization_level, decoder->private_->output[channel]+order); + else + decoder->private_->local_lpc_restore_signal_16bit(decoder->private_->residual[channel], decoder->private_->frame.header.blocksize-order, subframe->qlp_coeff, order, subframe->quantization_level, decoder->private_->output[channel]+order); + } + else + decoder->private_->local_lpc_restore_signal(decoder->private_->residual[channel], decoder->private_->frame.header.blocksize-order, subframe->qlp_coeff, order, subframe->quantization_level, decoder->private_->output[channel]+order); + else + decoder->private_->local_lpc_restore_signal_64bit(decoder->private_->residual[channel], decoder->private_->frame.header.blocksize-order, subframe->qlp_coeff, order, subframe->quantization_level, decoder->private_->output[channel]+order); + } + + return true; +} + +FLAC__bool read_subframe_verbatim_(FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, FLAC__bool do_full_decode) +{ + FLAC__Subframe_Verbatim *subframe = &decoder->private_->frame.subframes[channel].data.verbatim; + FLAC__int32 x, *residual = decoder->private_->residual[channel]; + unsigned i; + + decoder->private_->frame.subframes[channel].type = FLAC__SUBFRAME_TYPE_VERBATIM; + + subframe->data = residual; + + for(i = 0; i < decoder->private_->frame.header.blocksize; i++) { + if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &x, bps)) + return false; /* read_callback_ sets the state for us */ + residual[i] = x; + } + + /* decode the subframe */ + if(do_full_decode) + memcpy(decoder->private_->output[channel], subframe->data, sizeof(FLAC__int32) * decoder->private_->frame.header.blocksize); + + return true; +} + +FLAC__bool read_residual_partitioned_rice_(FLAC__StreamDecoder *decoder, unsigned predictor_order, unsigned partition_order, FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents, FLAC__int32 *residual, FLAC__bool is_extended) +{ + FLAC__uint32 rice_parameter; + int i; + unsigned partition, sample, u; + const unsigned partitions = 1u << partition_order; + const unsigned partition_samples = partition_order > 0? decoder->private_->frame.header.blocksize >> partition_order : decoder->private_->frame.header.blocksize - predictor_order; + const unsigned plen = is_extended? FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN : FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN; + const unsigned pesc = is_extended? FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_ESCAPE_PARAMETER : FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER; + + /* sanity checks */ + if(partition_order == 0) { + if(decoder->private_->frame.header.blocksize < predictor_order) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + } + else { + if(partition_samples < predictor_order) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + } + + if(!FLAC__format_entropy_coding_method_partitioned_rice_contents_ensure_size(partitioned_rice_contents, max(6, partition_order))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + + sample = 0; + for(partition = 0; partition < partitions; partition++) { + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &rice_parameter, plen)) + return false; /* read_callback_ sets the state for us */ + partitioned_rice_contents->parameters[partition] = rice_parameter; + if(rice_parameter < pesc) { + partitioned_rice_contents->raw_bits[partition] = 0; + u = (partition_order == 0 || partition > 0)? partition_samples : partition_samples - predictor_order; + if(!decoder->private_->local_bitreader_read_rice_signed_block(decoder->private_->input, (int*) residual + sample, u, rice_parameter)) + return false; /* read_callback_ sets the state for us */ + sample += u; + } + else { + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &rice_parameter, FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_RAW_LEN)) + return false; /* read_callback_ sets the state for us */ + partitioned_rice_contents->raw_bits[partition] = rice_parameter; + for(u = (partition_order == 0 || partition > 0)? 0 : predictor_order; u < partition_samples; u++, sample++) { + if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, (FLAC__int32*) &i, rice_parameter)) + return false; /* read_callback_ sets the state for us */ + residual[sample] = i; + } + } + } + + return true; +} + +FLAC__bool read_zero_padding_(FLAC__StreamDecoder *decoder) +{ + if(!FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)) { + FLAC__uint32 zero = 0; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &zero, FLAC__bitreader_bits_left_for_byte_alignment(decoder->private_->input))) + return false; /* read_callback_ sets the state for us */ + if(zero != 0) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + } + } + return true; +} + +FLAC__bool read_callback_(FLAC__byte buffer[], size_t *bytes, void *client_data) +{ + FLAC__StreamDecoder *decoder = (FLAC__StreamDecoder *)client_data; + + if( +#if FLAC__HAS_OGG + /* see [1] HACK NOTE below for why we don't call the eof_callback when decoding Ogg FLAC */ + !decoder->private_->is_ogg && +#endif + decoder->private_->eof_callback && decoder->private_->eof_callback(decoder, decoder->private_->client_data) + ) { + *bytes = 0; + decoder->protected_->state = FLAC__STREAM_DECODER_END_OF_STREAM; + return false; + } + else if(*bytes > 0) { + /* While seeking, it is possible for our seek to land in the + * middle of audio data that looks exactly like a frame header + * from a future version of an encoder. When that happens, our + * error callback will get an + * FLAC__STREAM_DECODER_UNPARSEABLE_STREAM and increment its + * unparseable_frame_count. But there is a remote possibility + * that it is properly synced at such a "future-codec frame", + * so to make sure, we wait to see many "unparseable" errors in + * a row before bailing out. + */ + if(decoder->private_->is_seeking && decoder->private_->unparseable_frame_count > 20) { + decoder->protected_->state = FLAC__STREAM_DECODER_ABORTED; + return false; + } + else { + const FLAC__StreamDecoderReadStatus status = +#if FLAC__HAS_OGG + decoder->private_->is_ogg? + read_callback_ogg_aspect_(decoder, buffer, bytes) : +#endif + decoder->private_->read_callback(decoder, buffer, bytes, decoder->private_->client_data) + ; + if(status == FLAC__STREAM_DECODER_READ_STATUS_ABORT) { + decoder->protected_->state = FLAC__STREAM_DECODER_ABORTED; + return false; + } + else if(*bytes == 0) { + if( + status == FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM || + ( +#if FLAC__HAS_OGG + /* see [1] HACK NOTE below for why we don't call the eof_callback when decoding Ogg FLAC */ + !decoder->private_->is_ogg && +#endif + decoder->private_->eof_callback && decoder->private_->eof_callback(decoder, decoder->private_->client_data) + ) + ) { + decoder->protected_->state = FLAC__STREAM_DECODER_END_OF_STREAM; + return false; + } + else + return true; + } + else + return true; + } + } + else { + /* abort to avoid a deadlock */ + decoder->protected_->state = FLAC__STREAM_DECODER_ABORTED; + return false; + } + /* [1] @@@ HACK NOTE: The end-of-stream checking has to be hacked around + * for Ogg FLAC. This is because the ogg decoder aspect can lose sync + * and at the same time hit the end of the stream (for example, seeking + * to a point that is after the beginning of the last Ogg page). There + * is no way to report an Ogg sync loss through the callbacks (see note + * in read_callback_ogg_aspect_()) so it returns CONTINUE with *bytes==0. + * So to keep the decoder from stopping at this point we gate the call + * to the eof_callback and let the Ogg decoder aspect set the + * end-of-stream state when it is needed. + */ +} + +#if FLAC__HAS_OGG +FLAC__StreamDecoderReadStatus read_callback_ogg_aspect_(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes) +{ + switch(FLAC__ogg_decoder_aspect_read_callback_wrapper(&decoder->protected_->ogg_decoder_aspect, buffer, bytes, read_callback_proxy_, decoder, decoder->private_->client_data)) { + case FLAC__OGG_DECODER_ASPECT_READ_STATUS_OK: + return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; + /* we don't really have a way to handle lost sync via read + * callback so we'll let it pass and let the underlying + * FLAC decoder catch the error + */ + case FLAC__OGG_DECODER_ASPECT_READ_STATUS_LOST_SYNC: + return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; + case FLAC__OGG_DECODER_ASPECT_READ_STATUS_END_OF_STREAM: + return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; + case FLAC__OGG_DECODER_ASPECT_READ_STATUS_NOT_FLAC: + case FLAC__OGG_DECODER_ASPECT_READ_STATUS_UNSUPPORTED_MAPPING_VERSION: + case FLAC__OGG_DECODER_ASPECT_READ_STATUS_ABORT: + case FLAC__OGG_DECODER_ASPECT_READ_STATUS_ERROR: + case FLAC__OGG_DECODER_ASPECT_READ_STATUS_MEMORY_ALLOCATION_ERROR: + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + default: + FLAC__ASSERT(0); + /* double protection */ + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + } +} + +FLAC__OggDecoderAspectReadStatus read_callback_proxy_(const void *void_decoder, FLAC__byte buffer[], size_t *bytes, void *client_data) +{ + FLAC__StreamDecoder *decoder = (FLAC__StreamDecoder*)void_decoder; + + switch(decoder->private_->read_callback(decoder, buffer, bytes, client_data)) { + case FLAC__STREAM_DECODER_READ_STATUS_CONTINUE: + return FLAC__OGG_DECODER_ASPECT_READ_STATUS_OK; + case FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM: + return FLAC__OGG_DECODER_ASPECT_READ_STATUS_END_OF_STREAM; + case FLAC__STREAM_DECODER_READ_STATUS_ABORT: + return FLAC__OGG_DECODER_ASPECT_READ_STATUS_ABORT; + default: + /* double protection: */ + FLAC__ASSERT(0); + return FLAC__OGG_DECODER_ASPECT_READ_STATUS_ABORT; + } +} +#endif + +FLAC__StreamDecoderWriteStatus write_audio_frame_to_client_(FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[]) +{ + if(decoder->private_->is_seeking) { + FLAC__uint64 this_frame_sample = frame->header.number.sample_number; + FLAC__uint64 next_frame_sample = this_frame_sample + (FLAC__uint64)frame->header.blocksize; + FLAC__uint64 target_sample = decoder->private_->target_sample; + + FLAC__ASSERT(frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER); + +#if FLAC__HAS_OGG + decoder->private_->got_a_frame = true; +#endif + decoder->private_->last_frame = *frame; /* save the frame */ + if(this_frame_sample <= target_sample && target_sample < next_frame_sample) { /* we hit our target frame */ + unsigned delta = (unsigned)(target_sample - this_frame_sample); + /* kick out of seek mode */ + decoder->private_->is_seeking = false; + /* shift out the samples before target_sample */ + if(delta > 0) { + unsigned channel; + const FLAC__int32 *newbuffer[FLAC__MAX_CHANNELS]; + for(channel = 0; channel < frame->header.channels; channel++) + newbuffer[channel] = buffer[channel] + delta; + decoder->private_->last_frame.header.blocksize -= delta; + decoder->private_->last_frame.header.number.sample_number += (FLAC__uint64)delta; + /* write the relevant samples */ + return decoder->private_->write_callback(decoder, &decoder->private_->last_frame, newbuffer, decoder->private_->client_data); + } + else { + /* write the relevant samples */ + return decoder->private_->write_callback(decoder, frame, buffer, decoder->private_->client_data); + } + } + else { + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; + } + } + else { + /* + * If we never got STREAMINFO, turn off MD5 checking to save + * cycles since we don't have a sum to compare to anyway + */ + if(!decoder->private_->has_stream_info) + decoder->private_->do_md5_checking = false; + if(decoder->private_->do_md5_checking) { + if(!FLAC__MD5Accumulate(&decoder->private_->md5context, buffer, frame->header.channels, frame->header.blocksize, (frame->header.bits_per_sample+7) / 8)) + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + return decoder->private_->write_callback(decoder, frame, buffer, decoder->private_->client_data); + } +} + +void send_error_to_client_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status) +{ + if(!decoder->private_->is_seeking) + decoder->private_->error_callback(decoder, status, decoder->private_->client_data); + else if(status == FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM) + decoder->private_->unparseable_frame_count++; +} + +FLAC__bool seek_to_absolute_sample_(FLAC__StreamDecoder *decoder, FLAC__uint64 stream_length, FLAC__uint64 target_sample) +{ + FLAC__uint64 first_frame_offset = decoder->private_->first_frame_offset, lower_bound, upper_bound, lower_bound_sample, upper_bound_sample, this_frame_sample; + FLAC__int64 pos = -1; + int i; + unsigned approx_bytes_per_frame; + FLAC__bool first_seek = true; + const FLAC__uint64 total_samples = FLAC__stream_decoder_get_total_samples(decoder); + const unsigned min_blocksize = decoder->private_->stream_info.data.stream_info.min_blocksize; + const unsigned max_blocksize = decoder->private_->stream_info.data.stream_info.max_blocksize; + const unsigned max_framesize = decoder->private_->stream_info.data.stream_info.max_framesize; + const unsigned min_framesize = decoder->private_->stream_info.data.stream_info.min_framesize; + /* take these from the current frame in case they've changed mid-stream */ + unsigned channels = FLAC__stream_decoder_get_channels(decoder); + unsigned bps = FLAC__stream_decoder_get_bits_per_sample(decoder); + const FLAC__StreamMetadata_SeekTable *seek_table = decoder->private_->has_seek_table? &decoder->private_->seek_table.data.seek_table : 0; + + /* use values from stream info if we didn't decode a frame */ + if(channels == 0) + channels = decoder->private_->stream_info.data.stream_info.channels; + if(bps == 0) + bps = decoder->private_->stream_info.data.stream_info.bits_per_sample; + + /* we are just guessing here */ + if(max_framesize > 0) + approx_bytes_per_frame = (max_framesize + min_framesize) / 2 + 1; + /* + * Check if it's a known fixed-blocksize stream. Note that though + * the spec doesn't allow zeroes in the STREAMINFO block, we may + * never get a STREAMINFO block when decoding so the value of + * min_blocksize might be zero. + */ + else if(min_blocksize == max_blocksize && min_blocksize > 0) { + /* note there are no () around 'bps/8' to keep precision up since it's an integer calulation */ + approx_bytes_per_frame = min_blocksize * channels * bps/8 + 64; + } + else + approx_bytes_per_frame = 4096 * channels * bps/8 + 64; + + /* + * First, we set an upper and lower bound on where in the + * stream we will search. For now we assume the worst case + * scenario, which is our best guess at the beginning of + * the first frame and end of the stream. + */ + lower_bound = first_frame_offset; + lower_bound_sample = 0; + upper_bound = stream_length; + upper_bound_sample = total_samples > 0 ? total_samples : target_sample /*estimate it*/; + + /* + * Now we refine the bounds if we have a seektable with + * suitable points. Note that according to the spec they + * must be ordered by ascending sample number. + * + * Note: to protect against invalid seek tables we will ignore points + * that have frame_samples==0 or sample_number>=total_samples + */ + if(seek_table) { + FLAC__uint64 new_lower_bound = lower_bound; + FLAC__uint64 new_upper_bound = upper_bound; + FLAC__uint64 new_lower_bound_sample = lower_bound_sample; + FLAC__uint64 new_upper_bound_sample = upper_bound_sample; + + /* find the closest seek point <= target_sample, if it exists */ + for(i = (int)seek_table->num_points - 1; i >= 0; i--) { + if( + seek_table->points[i].sample_number != FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER && + seek_table->points[i].frame_samples > 0 && /* defense against bad seekpoints */ + (total_samples <= 0 || seek_table->points[i].sample_number < total_samples) && /* defense against bad seekpoints */ + seek_table->points[i].sample_number <= target_sample + ) + break; + } + if(i >= 0) { /* i.e. we found a suitable seek point... */ + new_lower_bound = first_frame_offset + seek_table->points[i].stream_offset; + new_lower_bound_sample = seek_table->points[i].sample_number; + } + + /* find the closest seek point > target_sample, if it exists */ + for(i = 0; i < (int)seek_table->num_points; i++) { + if( + seek_table->points[i].sample_number != FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER && + seek_table->points[i].frame_samples > 0 && /* defense against bad seekpoints */ + (total_samples <= 0 || seek_table->points[i].sample_number < total_samples) && /* defense against bad seekpoints */ + seek_table->points[i].sample_number > target_sample + ) + break; + } + if(i < (int)seek_table->num_points) { /* i.e. we found a suitable seek point... */ + new_upper_bound = first_frame_offset + seek_table->points[i].stream_offset; + new_upper_bound_sample = seek_table->points[i].sample_number; + } + /* final protection against unsorted seek tables; keep original values if bogus */ + if(new_upper_bound >= new_lower_bound) { + lower_bound = new_lower_bound; + upper_bound = new_upper_bound; + lower_bound_sample = new_lower_bound_sample; + upper_bound_sample = new_upper_bound_sample; + } + } + + FLAC__ASSERT(upper_bound_sample >= lower_bound_sample); + /* there are 2 insidious ways that the following equality occurs, which + * we need to fix: + * 1) total_samples is 0 (unknown) and target_sample is 0 + * 2) total_samples is 0 (unknown) and target_sample happens to be + * exactly equal to the last seek point in the seek table; this + * means there is no seek point above it, and upper_bound_samples + * remains equal to the estimate (of target_samples) we made above + * in either case it does not hurt to move upper_bound_sample up by 1 + */ + if(upper_bound_sample == lower_bound_sample) + upper_bound_sample++; + + decoder->private_->target_sample = target_sample; + while(1) { + /* check if the bounds are still ok */ + if (lower_bound_sample >= upper_bound_sample || lower_bound > upper_bound) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } +#ifndef FLAC__INTEGER_ONLY_LIBRARY +#if defined _MSC_VER || defined __MINGW32__ + /* with VC++ you have to spoon feed it the casting */ + pos = (FLAC__int64)lower_bound + (FLAC__int64)((FLAC__double)(FLAC__int64)(target_sample - lower_bound_sample) / (FLAC__double)(FLAC__int64)(upper_bound_sample - lower_bound_sample) * (FLAC__double)(FLAC__int64)(upper_bound - lower_bound)) - approx_bytes_per_frame; +#else + pos = (FLAC__int64)lower_bound + (FLAC__int64)((FLAC__double)(target_sample - lower_bound_sample) / (FLAC__double)(upper_bound_sample - lower_bound_sample) * (FLAC__double)(upper_bound - lower_bound)) - approx_bytes_per_frame; +#endif +#else + /* a little less accurate: */ + if(upper_bound - lower_bound < 0xffffffff) + pos = (FLAC__int64)lower_bound + (FLAC__int64)(((target_sample - lower_bound_sample) * (upper_bound - lower_bound)) / (upper_bound_sample - lower_bound_sample)) - approx_bytes_per_frame; + else /* @@@ WATCHOUT, ~2TB limit */ + pos = (FLAC__int64)lower_bound + (FLAC__int64)((((target_sample - lower_bound_sample)>>8) * ((upper_bound - lower_bound)>>8)) / ((upper_bound_sample - lower_bound_sample)>>16)) - approx_bytes_per_frame; +#endif + if(pos >= (FLAC__int64)upper_bound) + pos = (FLAC__int64)upper_bound - 1; + if(pos < (FLAC__int64)lower_bound) + pos = (FLAC__int64)lower_bound; + if(decoder->private_->seek_callback(decoder, (FLAC__uint64)pos, decoder->private_->client_data) != FLAC__STREAM_DECODER_SEEK_STATUS_OK) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + if(!FLAC__stream_decoder_flush(decoder)) { + /* above call sets the state for us */ + return false; + } + /* Now we need to get a frame. First we need to reset our + * unparseable_frame_count; if we get too many unparseable + * frames in a row, the read callback will return + * FLAC__STREAM_DECODER_READ_STATUS_ABORT, causing + * FLAC__stream_decoder_process_single() to return false. + */ + decoder->private_->unparseable_frame_count = 0; + if(!FLAC__stream_decoder_process_single(decoder)) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + /* our write callback will change the state when it gets to the target frame */ + /* actually, we could have got_a_frame if our decoder is at FLAC__STREAM_DECODER_END_OF_STREAM so we need to check for that also */ +#if 0 + /*@@@@@@ used to be the following; not clear if the check for end of stream is needed anymore */ + if(decoder->protected_->state != FLAC__SEEKABLE_STREAM_DECODER_SEEKING && decoder->protected_->state != FLAC__STREAM_DECODER_END_OF_STREAM) + break; +#endif + if(!decoder->private_->is_seeking) + break; + + FLAC__ASSERT(decoder->private_->last_frame.header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER); + this_frame_sample = decoder->private_->last_frame.header.number.sample_number; + + if (0 == decoder->private_->samples_decoded || (this_frame_sample + decoder->private_->last_frame.header.blocksize >= upper_bound_sample && !first_seek)) { + if (pos == (FLAC__int64)lower_bound) { + /* can't move back any more than the first frame, something is fatally wrong */ + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + /* our last move backwards wasn't big enough, try again */ + approx_bytes_per_frame = approx_bytes_per_frame? approx_bytes_per_frame * 2 : 16; + continue; + } + /* allow one seek over upper bound, so we can get a correct upper_bound_sample for streams with unknown total_samples */ + first_seek = false; + + /* make sure we are not seeking in corrupted stream */ + if (this_frame_sample < lower_bound_sample) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + + /* we need to narrow the search */ + if(target_sample < this_frame_sample) { + upper_bound_sample = this_frame_sample + decoder->private_->last_frame.header.blocksize; +/*@@@@@@ what will decode position be if at end of stream? */ + if(!FLAC__stream_decoder_get_decode_position(decoder, &upper_bound)) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + approx_bytes_per_frame = (unsigned)(2 * (upper_bound - pos) / 3 + 16); + } + else { /* target_sample >= this_frame_sample + this frame's blocksize */ + lower_bound_sample = this_frame_sample + decoder->private_->last_frame.header.blocksize; + if(!FLAC__stream_decoder_get_decode_position(decoder, &lower_bound)) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + approx_bytes_per_frame = (unsigned)(2 * (lower_bound - pos) / 3 + 16); + } + } + + return true; +} + +#if FLAC__HAS_OGG +FLAC__bool seek_to_absolute_sample_ogg_(FLAC__StreamDecoder *decoder, FLAC__uint64 stream_length, FLAC__uint64 target_sample) +{ + FLAC__uint64 left_pos = 0, right_pos = stream_length; + FLAC__uint64 left_sample = 0, right_sample = FLAC__stream_decoder_get_total_samples(decoder); + FLAC__uint64 this_frame_sample = (FLAC__uint64)0 - 1; + FLAC__uint64 pos = 0; /* only initialized to avoid compiler warning */ + FLAC__bool did_a_seek; + unsigned iteration = 0; + + /* In the first iterations, we will calculate the target byte position + * by the distance from the target sample to left_sample and + * right_sample (let's call it "proportional search"). After that, we + * will switch to binary search. + */ + unsigned BINARY_SEARCH_AFTER_ITERATION = 2; + + /* We will switch to a linear search once our current sample is less + * than this number of samples ahead of the target sample + */ + static const FLAC__uint64 LINEAR_SEARCH_WITHIN_SAMPLES = FLAC__MAX_BLOCK_SIZE * 2; + + /* If the total number of samples is unknown, use a large value, and + * force binary search immediately. + */ + if(right_sample == 0) { + right_sample = (FLAC__uint64)(-1); + BINARY_SEARCH_AFTER_ITERATION = 0; + } + + decoder->private_->target_sample = target_sample; + for( ; ; iteration++) { + if (iteration == 0 || this_frame_sample > target_sample || target_sample - this_frame_sample > LINEAR_SEARCH_WITHIN_SAMPLES) { + if (iteration >= BINARY_SEARCH_AFTER_ITERATION) { + pos = (right_pos + left_pos) / 2; + } + else { +#ifndef FLAC__INTEGER_ONLY_LIBRARY +#if defined _MSC_VER || defined __MINGW32__ + /* with MSVC you have to spoon feed it the casting */ + pos = (FLAC__uint64)((FLAC__double)(FLAC__int64)(target_sample - left_sample) / (FLAC__double)(FLAC__int64)(right_sample - left_sample) * (FLAC__double)(FLAC__int64)(right_pos - left_pos)); +#else + pos = (FLAC__uint64)((FLAC__double)(target_sample - left_sample) / (FLAC__double)(right_sample - left_sample) * (FLAC__double)(right_pos - left_pos)); +#endif +#else + /* a little less accurate: */ + if ((target_sample-left_sample <= 0xffffffff) && (right_pos-left_pos <= 0xffffffff)) + pos = (FLAC__int64)(((target_sample-left_sample) * (right_pos-left_pos)) / (right_sample-left_sample)); + else /* @@@ WATCHOUT, ~2TB limit */ + pos = (FLAC__int64)((((target_sample-left_sample)>>8) * ((right_pos-left_pos)>>8)) / ((right_sample-left_sample)>>16)); +#endif + /* @@@ TODO: might want to limit pos to some distance + * before EOF, to make sure we land before the last frame, + * thereby getting a this_frame_sample and so having a better + * estimate. + */ + } + + /* physical seek */ + if(decoder->private_->seek_callback((FLAC__StreamDecoder*)decoder, (FLAC__uint64)pos, decoder->private_->client_data) != FLAC__STREAM_DECODER_SEEK_STATUS_OK) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + if(!FLAC__stream_decoder_flush(decoder)) { + /* above call sets the state for us */ + return false; + } + did_a_seek = true; + } + else + did_a_seek = false; + + decoder->private_->got_a_frame = false; + if(!FLAC__stream_decoder_process_single(decoder)) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + if(!decoder->private_->got_a_frame) { + if(did_a_seek) { + /* this can happen if we seek to a point after the last frame; we drop + * to binary search right away in this case to avoid any wasted + * iterations of proportional search. + */ + right_pos = pos; + BINARY_SEARCH_AFTER_ITERATION = 0; + } + else { + /* this can probably only happen if total_samples is unknown and the + * target_sample is past the end of the stream + */ + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + } + /* our write callback will change the state when it gets to the target frame */ + else if(!decoder->private_->is_seeking) { + break; + } + else { + this_frame_sample = decoder->private_->last_frame.header.number.sample_number; + FLAC__ASSERT(decoder->private_->last_frame.header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER); + + if (did_a_seek) { + if (this_frame_sample <= target_sample) { + /* The 'equal' case should not happen, since + * FLAC__stream_decoder_process_single() + * should recognize that it has hit the + * target sample and we would exit through + * the 'break' above. + */ + FLAC__ASSERT(this_frame_sample != target_sample); + + left_sample = this_frame_sample; + /* sanity check to avoid infinite loop */ + if (left_pos == pos) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + left_pos = pos; + } + else if(this_frame_sample > target_sample) { + right_sample = this_frame_sample; + /* sanity check to avoid infinite loop */ + if (right_pos == pos) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + right_pos = pos; + } + } + } + } + + return true; +} +#endif + +FLAC__StreamDecoderReadStatus file_read_callback_dec(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data) +{ + (void)client_data; + + if(*bytes > 0) { + *bytes = fread(buffer, sizeof(FLAC__byte), *bytes, decoder->private_->file); + if(ferror(decoder->private_->file)) + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + else if(*bytes == 0) + return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; + else + return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; + } + else + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; /* abort to avoid a deadlock */ +} + +FLAC__StreamDecoderSeekStatus file_seek_callback_dec(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data) +{ + (void)client_data; + + if(decoder->private_->file == stdin) + return FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED; + else if(fseeko(decoder->private_->file, (off_t)absolute_byte_offset, SEEK_SET) < 0) + return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR; + else + return FLAC__STREAM_DECODER_SEEK_STATUS_OK; +} + +FLAC__StreamDecoderTellStatus file_tell_callback_dec(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data) +{ + off_t pos; + (void)client_data; + + if(decoder->private_->file == stdin) + return FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED; + else if((pos = ftello(decoder->private_->file)) < 0) + return FLAC__STREAM_DECODER_TELL_STATUS_ERROR; + else { + *absolute_byte_offset = (FLAC__uint64)pos; + return FLAC__STREAM_DECODER_TELL_STATUS_OK; + } +} + +FLAC__StreamDecoderLengthStatus file_length_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data) +{ + struct stat filestats; + (void)client_data; + + if(decoder->private_->file == stdin) + return FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED; + else if(fstat(fileno(decoder->private_->file), &filestats) != 0) + return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR; + else { + *stream_length = (FLAC__uint64)filestats.st_size; + return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; + } +} + +FLAC__bool file_eof_callback_(const FLAC__StreamDecoder *decoder, void *client_data) +{ + (void)client_data; + + return feof(decoder->private_->file)? true : false; +} + +#endif +/********* End of inlined file: stream_decoder.c *********/ + +/********* Start of inlined file: stream_encoder.c *********/ + +/********* Start of inlined file: juce_FlacHeader.h *********/ +// This file is included at the start of each FLAC .c file, just to do a few housekeeping +// tasks.. + +#define VERSION "1.2.1" + +#define FLAC__NO_DLL 1 + +#ifdef _MSC_VER + #pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4312) +#endif + +#if ! (defined (_WIN32) || defined (_WIN64) || defined (LINUX)) + #define FLAC__SYS_DARWIN 1 +#endif +/********* End of inlined file: juce_FlacHeader.h *********/ + +#if JUCE_USE_FLAC + +#if HAVE_CONFIG_H +# include +#endif + +#if defined _MSC_VER || defined __MINGW32__ +#include /* for _setmode() */ +#include /* for _O_BINARY */ +#endif +#if defined __CYGWIN__ || defined __EMX__ +#include /* for setmode(), O_BINARY */ +#include /* for _O_BINARY */ +#endif +#include +#include +#include /* for malloc() */ +#include /* for memcpy() */ +#include /* for off_t */ +#if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__ +#if _MSC_VER <= 1600 || defined __BORLANDC__ /* @@@ [2G limit] */ +#define fseeko fseek +#define ftello ftell +#endif +#endif + +/********* Start of inlined file: stream_encoder.h *********/ +#ifndef FLAC__PROTECTED__STREAM_ENCODER_H +#define FLAC__PROTECTED__STREAM_ENCODER_H + +#if FLAC__HAS_OGG +#include "private/ogg_encoder_aspect.h" +#endif + +#ifndef FLAC__INTEGER_ONLY_LIBRARY + +#define FLAC__MAX_APODIZATION_FUNCTIONS 32 + +typedef enum { + FLAC__APODIZATION_BARTLETT, + FLAC__APODIZATION_BARTLETT_HANN, + FLAC__APODIZATION_BLACKMAN, + FLAC__APODIZATION_BLACKMAN_HARRIS_4TERM_92DB_SIDELOBE, + FLAC__APODIZATION_CONNES, + FLAC__APODIZATION_FLATTOP, + FLAC__APODIZATION_GAUSS, + FLAC__APODIZATION_HAMMING, + FLAC__APODIZATION_HANN, + FLAC__APODIZATION_KAISER_BESSEL, + FLAC__APODIZATION_NUTTALL, + FLAC__APODIZATION_RECTANGLE, + FLAC__APODIZATION_TRIANGLE, + FLAC__APODIZATION_TUKEY, + FLAC__APODIZATION_WELCH +} FLAC__ApodizationFunction; + +typedef struct { + FLAC__ApodizationFunction type; + union { + struct { + FLAC__real stddev; + } gauss; + struct { + FLAC__real p; + } tukey; + } parameters; +} FLAC__ApodizationSpecification; + +#endif // #ifndef FLAC__INTEGER_ONLY_LIBRARY + +typedef struct FLAC__StreamEncoderProtected { + FLAC__StreamEncoderState state; + FLAC__bool verify; + FLAC__bool streamable_subset; + FLAC__bool do_md5; + FLAC__bool do_mid_side_stereo; + FLAC__bool loose_mid_side_stereo; + unsigned channels; + unsigned bits_per_sample; + unsigned sample_rate; + unsigned blocksize; +#ifndef FLAC__INTEGER_ONLY_LIBRARY + unsigned num_apodizations; + FLAC__ApodizationSpecification apodizations[FLAC__MAX_APODIZATION_FUNCTIONS]; +#endif + unsigned max_lpc_order; + unsigned qlp_coeff_precision; + FLAC__bool do_qlp_coeff_prec_search; + FLAC__bool do_exhaustive_model_search; + FLAC__bool do_escape_coding; + unsigned min_residual_partition_order; + unsigned max_residual_partition_order; + unsigned rice_parameter_search_dist; + FLAC__uint64 total_samples_estimate; + FLAC__StreamMetadata **metadata; + unsigned num_metadata_blocks; + FLAC__uint64 streaminfo_offset, seektable_offset, audio_offset; +#if FLAC__HAS_OGG + FLAC__OggEncoderAspect ogg_encoder_aspect; +#endif +} FLAC__StreamEncoderProtected; + +#endif +/********* End of inlined file: stream_encoder.h *********/ + +#if FLAC__HAS_OGG +#include "include/private/ogg_helper.h" +#include "include/private/ogg_mapping.h" +#endif + +/********* Start of inlined file: stream_encoder_framing.h *********/ +#ifndef FLAC__PRIVATE__STREAM_ENCODER_FRAMING_H +#define FLAC__PRIVATE__STREAM_ENCODER_FRAMING_H + +FLAC__bool FLAC__add_metadata_block(const FLAC__StreamMetadata *metadata, FLAC__BitWriter *bw); +FLAC__bool FLAC__frame_add_header(const FLAC__FrameHeader *header, FLAC__BitWriter *bw); +FLAC__bool FLAC__subframe_add_constant(const FLAC__Subframe_Constant *subframe, unsigned subframe_bps, unsigned wasted_bits, FLAC__BitWriter *bw); +FLAC__bool FLAC__subframe_add_fixed(const FLAC__Subframe_Fixed *subframe, unsigned residual_samples, unsigned subframe_bps, unsigned wasted_bits, FLAC__BitWriter *bw); +FLAC__bool FLAC__subframe_add_lpc(const FLAC__Subframe_LPC *subframe, unsigned residual_samples, unsigned subframe_bps, unsigned wasted_bits, FLAC__BitWriter *bw); +FLAC__bool FLAC__subframe_add_verbatim(const FLAC__Subframe_Verbatim *subframe, unsigned samples, unsigned subframe_bps, unsigned wasted_bits, FLAC__BitWriter *bw); + +#endif +/********* End of inlined file: stream_encoder_framing.h *********/ + +/********* Start of inlined file: window.h *********/ +#ifndef FLAC__PRIVATE__WINDOW_H +#define FLAC__PRIVATE__WINDOW_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef FLAC__INTEGER_ONLY_LIBRARY + +/* + * FLAC__window_*() + * -------------------------------------------------------------------- + * Calculates window coefficients according to different apodization + * functions. + * + * OUT window[0,L-1] + * IN L (number of points in window) + */ +void FLAC__window_bartlett(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_bartlett_hann(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_blackman(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_blackman_harris_4term_92db_sidelobe(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_connes(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_flattop(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_gauss(FLAC__real *window, const FLAC__int32 L, const FLAC__real stddev); /* 0.0 < stddev <= 0.5 */ +void FLAC__window_hamming(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_hann(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_kaiser_bessel(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_nuttall(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_rectangle(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_triangle(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_tukey(FLAC__real *window, const FLAC__int32 L, const FLAC__real p); +void FLAC__window_welch(FLAC__real *window, const FLAC__int32 L); + +#endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */ + +#endif +/********* End of inlined file: window.h *********/ + +#ifndef FLaC__INLINE +#define FLaC__INLINE +#endif + +#ifdef min +#undef min +#endif +#define min(x,y) ((x)<(y)?(x):(y)) + +#ifdef max +#undef max +#endif +#define max(x,y) ((x)>(y)?(x):(y)) + +/* Exact Rice codeword length calculation is off by default. The simple + * (and fast) estimation (of how many bits a residual value will be + * encoded with) in this encoder is very good, almost always yielding + * compression within 0.1% of exact calculation. + */ +#undef EXACT_RICE_BITS_CALCULATION +/* Rice parameter searching is off by default. The simple (and fast) + * parameter estimation in this encoder is very good, almost always + * yielding compression within 0.1% of the optimal parameters. + */ +#undef ENABLE_RICE_PARAMETER_SEARCH + +typedef struct { + FLAC__int32 *data[FLAC__MAX_CHANNELS]; + unsigned size; /* of each data[] in samples */ + unsigned tail; +} verify_input_fifo; + +typedef struct { + const FLAC__byte *data; + unsigned capacity; + unsigned bytes; +} verify_output; + +typedef enum { + ENCODER_IN_MAGIC = 0, + ENCODER_IN_METADATA = 1, + ENCODER_IN_AUDIO = 2 +} EncoderStateHint; + +static struct CompressionLevels { + FLAC__bool do_mid_side_stereo; + FLAC__bool loose_mid_side_stereo; + unsigned max_lpc_order; + unsigned qlp_coeff_precision; + FLAC__bool do_qlp_coeff_prec_search; + FLAC__bool do_escape_coding; + FLAC__bool do_exhaustive_model_search; + unsigned min_residual_partition_order; + unsigned max_residual_partition_order; + unsigned rice_parameter_search_dist; +} compression_levels_[] = { + { false, false, 0, 0, false, false, false, 0, 3, 0 }, + { true , true , 0, 0, false, false, false, 0, 3, 0 }, + { true , false, 0, 0, false, false, false, 0, 3, 0 }, + { false, false, 6, 0, false, false, false, 0, 4, 0 }, + { true , true , 8, 0, false, false, false, 0, 4, 0 }, + { true , false, 8, 0, false, false, false, 0, 5, 0 }, + { true , false, 8, 0, false, false, false, 0, 6, 0 }, + { true , false, 8, 0, false, false, true , 0, 6, 0 }, + { true , false, 12, 0, false, false, true , 0, 6, 0 } +}; + +/*********************************************************************** + * + * Private class method prototypes + * + ***********************************************************************/ + +static void set_defaults_enc(FLAC__StreamEncoder *encoder); +static void free_(FLAC__StreamEncoder *encoder); +static FLAC__bool resize_buffers_(FLAC__StreamEncoder *encoder, unsigned new_blocksize); +static FLAC__bool write_bitbuffer_(FLAC__StreamEncoder *encoder, unsigned samples, FLAC__bool is_last_block); +static FLAC__StreamEncoderWriteStatus write_frame_(FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, FLAC__bool is_last_block); +static void update_metadata_(const FLAC__StreamEncoder *encoder); +#if FLAC__HAS_OGG +static void update_ogg_metadata_(FLAC__StreamEncoder *encoder); +#endif +static FLAC__bool process_frame_(FLAC__StreamEncoder *encoder, FLAC__bool is_fractional_block, FLAC__bool is_last_block); +static FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder, FLAC__bool is_fractional_block); + +static FLAC__bool process_subframe_( + FLAC__StreamEncoder *encoder, + unsigned min_partition_order, + unsigned max_partition_order, + const FLAC__FrameHeader *frame_header, + unsigned subframe_bps, + const FLAC__int32 integer_signal[], + FLAC__Subframe *subframe[2], + FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents[2], + FLAC__int32 *residual[2], + unsigned *best_subframe, + unsigned *best_bits +); + +static FLAC__bool add_subframe_( + FLAC__StreamEncoder *encoder, + unsigned blocksize, + unsigned subframe_bps, + const FLAC__Subframe *subframe, + FLAC__BitWriter *frame +); + +static unsigned evaluate_constant_subframe_( + FLAC__StreamEncoder *encoder, + const FLAC__int32 signal, + unsigned blocksize, + unsigned subframe_bps, + FLAC__Subframe *subframe +); + +static unsigned evaluate_fixed_subframe_( + FLAC__StreamEncoder *encoder, + const FLAC__int32 signal[], + FLAC__int32 residual[], + FLAC__uint64 abs_residual_partition_sums[], + unsigned raw_bits_per_partition[], + unsigned blocksize, + unsigned subframe_bps, + unsigned order, + unsigned rice_parameter, + unsigned rice_parameter_limit, + unsigned min_partition_order, + unsigned max_partition_order, + FLAC__bool do_escape_coding, + unsigned rice_parameter_search_dist, + FLAC__Subframe *subframe, + FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents +); + +#ifndef FLAC__INTEGER_ONLY_LIBRARY +static unsigned evaluate_lpc_subframe_( + FLAC__StreamEncoder *encoder, + const FLAC__int32 signal[], + FLAC__int32 residual[], + FLAC__uint64 abs_residual_partition_sums[], + unsigned raw_bits_per_partition[], + const FLAC__real lp_coeff[], + unsigned blocksize, + unsigned subframe_bps, + unsigned order, + unsigned qlp_coeff_precision, + unsigned rice_parameter, + unsigned rice_parameter_limit, + unsigned min_partition_order, + unsigned max_partition_order, + FLAC__bool do_escape_coding, + unsigned rice_parameter_search_dist, + FLAC__Subframe *subframe, + FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents +); +#endif + +static unsigned evaluate_verbatim_subframe_( + FLAC__StreamEncoder *encoder, + const FLAC__int32 signal[], + unsigned blocksize, + unsigned subframe_bps, + FLAC__Subframe *subframe +); + +static unsigned find_best_partition_order_( + struct FLAC__StreamEncoderPrivate *private_, + const FLAC__int32 residual[], + FLAC__uint64 abs_residual_partition_sums[], + unsigned raw_bits_per_partition[], + unsigned residual_samples, + unsigned predictor_order, + unsigned rice_parameter, + unsigned rice_parameter_limit, + unsigned min_partition_order, + unsigned max_partition_order, + unsigned bps, + FLAC__bool do_escape_coding, + unsigned rice_parameter_search_dist, + FLAC__EntropyCodingMethod *best_ecm +); + +static void precompute_partition_info_sums_( + const FLAC__int32 residual[], + FLAC__uint64 abs_residual_partition_sums[], + unsigned residual_samples, + unsigned predictor_order, + unsigned min_partition_order, + unsigned max_partition_order, + unsigned bps +); + +static void precompute_partition_info_escapes_( + const FLAC__int32 residual[], + unsigned raw_bits_per_partition[], + unsigned residual_samples, + unsigned predictor_order, + unsigned min_partition_order, + unsigned max_partition_order +); + +static FLAC__bool set_partitioned_rice_( +#ifdef EXACT_RICE_BITS_CALCULATION + const FLAC__int32 residual[], +#endif + const FLAC__uint64 abs_residual_partition_sums[], + const unsigned raw_bits_per_partition[], + const unsigned residual_samples, + const unsigned predictor_order, + const unsigned suggested_rice_parameter, + const unsigned rice_parameter_limit, + const unsigned rice_parameter_search_dist, + const unsigned partition_order, + const FLAC__bool search_for_escapes, + FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents, + unsigned *bits +); + +static unsigned get_wasted_bits_(FLAC__int32 signal[], unsigned samples); + +/* verify-related routines: */ +static void append_to_verify_fifo_( + verify_input_fifo *fifo, + const FLAC__int32 * const input[], + unsigned input_offset, + unsigned channels, + unsigned wide_samples +); + +static void append_to_verify_fifo_interleaved_( + verify_input_fifo *fifo, + const FLAC__int32 input[], + unsigned input_offset, + unsigned channels, + unsigned wide_samples +); + +static FLAC__StreamDecoderReadStatus verify_read_callback_(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data); +static FLAC__StreamDecoderWriteStatus verify_write_callback_(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data); +static void verify_metadata_callback_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data); +static void verify_error_callback_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data); + +static FLAC__StreamEncoderReadStatus file_read_callback_enc(const FLAC__StreamEncoder *encoder, FLAC__byte buffer[], size_t *bytes, void *client_data); +static FLAC__StreamEncoderSeekStatus file_seek_callback_enc(const FLAC__StreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data); +static FLAC__StreamEncoderTellStatus file_tell_callback_enc(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_byte_offset, void *client_data); +static FLAC__StreamEncoderWriteStatus file_write_callback_(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame, void *client_data); +static FILE *get_binary_stdout_(void); + +/*********************************************************************** + * + * Private class data + * + ***********************************************************************/ + +typedef struct FLAC__StreamEncoderPrivate { + unsigned input_capacity; /* current size (in samples) of the signal and residual buffers */ + FLAC__int32 *integer_signal[FLAC__MAX_CHANNELS]; /* the integer version of the input signal */ + FLAC__int32 *integer_signal_mid_side[2]; /* the integer version of the mid-side input signal (stereo only) */ +#ifndef FLAC__INTEGER_ONLY_LIBRARY + FLAC__real *real_signal[FLAC__MAX_CHANNELS]; /* (@@@ currently unused) the floating-point version of the input signal */ + FLAC__real *real_signal_mid_side[2]; /* (@@@ currently unused) the floating-point version of the mid-side input signal (stereo only) */ + FLAC__real *window[FLAC__MAX_APODIZATION_FUNCTIONS]; /* the pre-computed floating-point window for each apodization function */ + FLAC__real *windowed_signal; /* the integer_signal[] * current window[] */ +#endif + unsigned subframe_bps[FLAC__MAX_CHANNELS]; /* the effective bits per sample of the input signal (stream bps - wasted bits) */ + unsigned subframe_bps_mid_side[2]; /* the effective bits per sample of the mid-side input signal (stream bps - wasted bits + 0/1) */ + FLAC__int32 *residual_workspace[FLAC__MAX_CHANNELS][2]; /* each channel has a candidate and best workspace where the subframe residual signals will be stored */ + FLAC__int32 *residual_workspace_mid_side[2][2]; + FLAC__Subframe subframe_workspace[FLAC__MAX_CHANNELS][2]; + FLAC__Subframe subframe_workspace_mid_side[2][2]; + FLAC__Subframe *subframe_workspace_ptr[FLAC__MAX_CHANNELS][2]; + FLAC__Subframe *subframe_workspace_ptr_mid_side[2][2]; + FLAC__EntropyCodingMethod_PartitionedRiceContents partitioned_rice_contents_workspace[FLAC__MAX_CHANNELS][2]; + FLAC__EntropyCodingMethod_PartitionedRiceContents partitioned_rice_contents_workspace_mid_side[FLAC__MAX_CHANNELS][2]; + FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents_workspace_ptr[FLAC__MAX_CHANNELS][2]; + FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents_workspace_ptr_mid_side[FLAC__MAX_CHANNELS][2]; + unsigned best_subframe[FLAC__MAX_CHANNELS]; /* index (0 or 1) into 2nd dimension of the above workspaces */ + unsigned best_subframe_mid_side[2]; + unsigned best_subframe_bits[FLAC__MAX_CHANNELS]; /* size in bits of the best subframe for each channel */ + unsigned best_subframe_bits_mid_side[2]; + FLAC__uint64 *abs_residual_partition_sums; /* workspace where the sum of abs(candidate residual) for each partition is stored */ + unsigned *raw_bits_per_partition; /* workspace where the sum of silog2(candidate residual) for each partition is stored */ + FLAC__BitWriter *frame; /* the current frame being worked on */ + unsigned loose_mid_side_stereo_frames; /* rounded number of frames the encoder will use before trying both independent and mid/side frames again */ + unsigned loose_mid_side_stereo_frame_count; /* number of frames using the current channel assignment */ + FLAC__ChannelAssignment last_channel_assignment; + FLAC__StreamMetadata streaminfo; /* scratchpad for STREAMINFO as it is built */ + FLAC__StreamMetadata_SeekTable *seek_table; /* pointer into encoder->protected_->metadata_ where the seek table is */ + unsigned current_sample_number; + unsigned current_frame_number; + FLAC__MD5Context md5context; + FLAC__CPUInfo cpuinfo; +#ifndef FLAC__INTEGER_ONLY_LIBRARY + unsigned (*local_fixed_compute_best_predictor)(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); +#else + unsigned (*local_fixed_compute_best_predictor)(const FLAC__int32 data[], unsigned data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); +#endif +#ifndef FLAC__INTEGER_ONLY_LIBRARY + void (*local_lpc_compute_autocorrelation)(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); + void (*local_lpc_compute_residual_from_qlp_coefficients)(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); + void (*local_lpc_compute_residual_from_qlp_coefficients_64bit)(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); + void (*local_lpc_compute_residual_from_qlp_coefficients_16bit)(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); +#endif + FLAC__bool use_wide_by_block; /* use slow 64-bit versions of some functions because of the block size */ + FLAC__bool use_wide_by_partition; /* use slow 64-bit versions of some functions because of the min partition order and blocksize */ + FLAC__bool use_wide_by_order; /* use slow 64-bit versions of some functions because of the lpc order */ + FLAC__bool disable_constant_subframes; + FLAC__bool disable_fixed_subframes; + FLAC__bool disable_verbatim_subframes; +#if FLAC__HAS_OGG + FLAC__bool is_ogg; +#endif + FLAC__StreamEncoderReadCallback read_callback; /* currently only needed for Ogg FLAC */ + FLAC__StreamEncoderSeekCallback seek_callback; + FLAC__StreamEncoderTellCallback tell_callback; + FLAC__StreamEncoderWriteCallback write_callback; + FLAC__StreamEncoderMetadataCallback metadata_callback; + FLAC__StreamEncoderProgressCallback progress_callback; + void *client_data; + unsigned first_seekpoint_to_check; + FILE *file; /* only used when encoding to a file */ + FLAC__uint64 bytes_written; + FLAC__uint64 samples_written; + unsigned frames_written; + unsigned total_frames_estimate; + /* unaligned (original) pointers to allocated data */ + FLAC__int32 *integer_signal_unaligned[FLAC__MAX_CHANNELS]; + FLAC__int32 *integer_signal_mid_side_unaligned[2]; +#ifndef FLAC__INTEGER_ONLY_LIBRARY + FLAC__real *real_signal_unaligned[FLAC__MAX_CHANNELS]; /* (@@@ currently unused) */ + FLAC__real *real_signal_mid_side_unaligned[2]; /* (@@@ currently unused) */ + FLAC__real *window_unaligned[FLAC__MAX_APODIZATION_FUNCTIONS]; + FLAC__real *windowed_signal_unaligned; +#endif + FLAC__int32 *residual_workspace_unaligned[FLAC__MAX_CHANNELS][2]; + FLAC__int32 *residual_workspace_mid_side_unaligned[2][2]; + FLAC__uint64 *abs_residual_partition_sums_unaligned; + unsigned *raw_bits_per_partition_unaligned; + /* + * These fields have been moved here from private function local + * declarations merely to save stack space during encoding. + */ +#ifndef FLAC__INTEGER_ONLY_LIBRARY + FLAC__real lp_coeff[FLAC__MAX_LPC_ORDER][FLAC__MAX_LPC_ORDER]; /* from process_subframe_() */ +#endif + FLAC__EntropyCodingMethod_PartitionedRiceContents partitioned_rice_contents_extra[2]; /* from find_best_partition_order_() */ + /* + * The data for the verify section + */ + struct { + FLAC__StreamDecoder *decoder; + EncoderStateHint state_hint; + FLAC__bool needs_magic_hack; + verify_input_fifo input_fifo; + verify_output output; + struct { + FLAC__uint64 absolute_sample; + unsigned frame_number; + unsigned channel; + unsigned sample; + FLAC__int32 expected; + FLAC__int32 got; + } error_stats; + } verify; + FLAC__bool is_being_deleted; /* if true, call to ..._finish() from ..._delete() will not call the callbacks */ +} FLAC__StreamEncoderPrivate; + +/*********************************************************************** + * + * Public static class data + * + ***********************************************************************/ + +FLAC_API const char * const FLAC__StreamEncoderStateString[] = { + "FLAC__STREAM_ENCODER_OK", + "FLAC__STREAM_ENCODER_UNINITIALIZED", + "FLAC__STREAM_ENCODER_OGG_ERROR", + "FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR", + "FLAC__STREAM_ENCODER_VERIFY_MISMATCH_IN_AUDIO_DATA", + "FLAC__STREAM_ENCODER_CLIENT_ERROR", + "FLAC__STREAM_ENCODER_IO_ERROR", + "FLAC__STREAM_ENCODER_FRAMING_ERROR", + "FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR" +}; + +FLAC_API const char * const FLAC__StreamEncoderInitStatusString[] = { + "FLAC__STREAM_ENCODER_INIT_STATUS_OK", + "FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR", + "FLAC__STREAM_ENCODER_INIT_STATUS_UNSUPPORTED_CONTAINER", + "FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_CALLBACKS", + "FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_NUMBER_OF_CHANNELS", + "FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_BITS_PER_SAMPLE", + "FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_SAMPLE_RATE", + "FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_BLOCK_SIZE", + "FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_MAX_LPC_ORDER", + "FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_QLP_COEFF_PRECISION", + "FLAC__STREAM_ENCODER_INIT_STATUS_BLOCK_SIZE_TOO_SMALL_FOR_LPC_ORDER", + "FLAC__STREAM_ENCODER_INIT_STATUS_NOT_STREAMABLE", + "FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_METADATA", + "FLAC__STREAM_ENCODER_INIT_STATUS_ALREADY_INITIALIZED" +}; + +FLAC_API const char * const FLAC__treamEncoderReadStatusString[] = { + "FLAC__STREAM_ENCODER_READ_STATUS_CONTINUE", + "FLAC__STREAM_ENCODER_READ_STATUS_END_OF_STREAM", + "FLAC__STREAM_ENCODER_READ_STATUS_ABORT", + "FLAC__STREAM_ENCODER_READ_STATUS_UNSUPPORTED" +}; + +FLAC_API const char * const FLAC__StreamEncoderWriteStatusString[] = { + "FLAC__STREAM_ENCODER_WRITE_STATUS_OK", + "FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR" +}; + +FLAC_API const char * const FLAC__StreamEncoderSeekStatusString[] = { + "FLAC__STREAM_ENCODER_SEEK_STATUS_OK", + "FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR", + "FLAC__STREAM_ENCODER_SEEK_STATUS_UNSUPPORTED" +}; + +FLAC_API const char * const FLAC__StreamEncoderTellStatusString[] = { + "FLAC__STREAM_ENCODER_TELL_STATUS_OK", + "FLAC__STREAM_ENCODER_TELL_STATUS_ERROR", + "FLAC__STREAM_ENCODER_TELL_STATUS_UNSUPPORTED" +}; + +/* Number of samples that will be overread to watch for end of stream. By + * 'overread', we mean that the FLAC__stream_encoder_process*() calls will + * always try to read blocksize+1 samples before encoding a block, so that + * even if the stream has a total sample count that is an integral multiple + * of the blocksize, we will still notice when we are encoding the last + * block. This is needed, for example, to correctly set the end-of-stream + * marker in Ogg FLAC. + * + * WATCHOUT: some parts of the code assert that OVERREAD_ == 1 and there's + * not really any reason to change it. + */ +static const unsigned OVERREAD_ = 1; + +/*********************************************************************** + * + * Class constructor/destructor + * + */ +FLAC_API FLAC__StreamEncoder *FLAC__stream_encoder_new(void) +{ + FLAC__StreamEncoder *encoder; + unsigned i; + + FLAC__ASSERT(sizeof(int) >= 4); /* we want to die right away if this is not true */ + + encoder = (FLAC__StreamEncoder*)calloc(1, sizeof(FLAC__StreamEncoder)); + if(encoder == 0) { + return 0; + } + + encoder->protected_ = (FLAC__StreamEncoderProtected*)calloc(1, sizeof(FLAC__StreamEncoderProtected)); + if(encoder->protected_ == 0) { + free(encoder); + return 0; + } + + encoder->private_ = (FLAC__StreamEncoderPrivate*)calloc(1, sizeof(FLAC__StreamEncoderPrivate)); + if(encoder->private_ == 0) { + free(encoder->protected_); + free(encoder); + return 0; + } + + encoder->private_->frame = FLAC__bitwriter_new(); + if(encoder->private_->frame == 0) { + free(encoder->private_); + free(encoder->protected_); + free(encoder); + return 0; + } + + encoder->private_->file = 0; + + set_defaults_enc(encoder); + + encoder->private_->is_being_deleted = false; + + for(i = 0; i < FLAC__MAX_CHANNELS; i++) { + encoder->private_->subframe_workspace_ptr[i][0] = &encoder->private_->subframe_workspace[i][0]; + encoder->private_->subframe_workspace_ptr[i][1] = &encoder->private_->subframe_workspace[i][1]; + } + for(i = 0; i < 2; i++) { + encoder->private_->subframe_workspace_ptr_mid_side[i][0] = &encoder->private_->subframe_workspace_mid_side[i][0]; + encoder->private_->subframe_workspace_ptr_mid_side[i][1] = &encoder->private_->subframe_workspace_mid_side[i][1]; + } + for(i = 0; i < FLAC__MAX_CHANNELS; i++) { + encoder->private_->partitioned_rice_contents_workspace_ptr[i][0] = &encoder->private_->partitioned_rice_contents_workspace[i][0]; + encoder->private_->partitioned_rice_contents_workspace_ptr[i][1] = &encoder->private_->partitioned_rice_contents_workspace[i][1]; + } + for(i = 0; i < 2; i++) { + encoder->private_->partitioned_rice_contents_workspace_ptr_mid_side[i][0] = &encoder->private_->partitioned_rice_contents_workspace_mid_side[i][0]; + encoder->private_->partitioned_rice_contents_workspace_ptr_mid_side[i][1] = &encoder->private_->partitioned_rice_contents_workspace_mid_side[i][1]; + } + + for(i = 0; i < FLAC__MAX_CHANNELS; i++) { + FLAC__format_entropy_coding_method_partitioned_rice_contents_init(&encoder->private_->partitioned_rice_contents_workspace[i][0]); + FLAC__format_entropy_coding_method_partitioned_rice_contents_init(&encoder->private_->partitioned_rice_contents_workspace[i][1]); + } + for(i = 0; i < 2; i++) { + FLAC__format_entropy_coding_method_partitioned_rice_contents_init(&encoder->private_->partitioned_rice_contents_workspace_mid_side[i][0]); + FLAC__format_entropy_coding_method_partitioned_rice_contents_init(&encoder->private_->partitioned_rice_contents_workspace_mid_side[i][1]); + } + for(i = 0; i < 2; i++) + FLAC__format_entropy_coding_method_partitioned_rice_contents_init(&encoder->private_->partitioned_rice_contents_extra[i]); + + encoder->protected_->state = FLAC__STREAM_ENCODER_UNINITIALIZED; + + return encoder; +} + +FLAC_API void FLAC__stream_encoder_delete(FLAC__StreamEncoder *encoder) +{ + unsigned i; + + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->protected_); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->private_->frame); + + encoder->private_->is_being_deleted = true; + + (void)FLAC__stream_encoder_finish(encoder); + + if(0 != encoder->private_->verify.decoder) + FLAC__stream_decoder_delete(encoder->private_->verify.decoder); + + for(i = 0; i < FLAC__MAX_CHANNELS; i++) { + FLAC__format_entropy_coding_method_partitioned_rice_contents_clear(&encoder->private_->partitioned_rice_contents_workspace[i][0]); + FLAC__format_entropy_coding_method_partitioned_rice_contents_clear(&encoder->private_->partitioned_rice_contents_workspace[i][1]); + } + for(i = 0; i < 2; i++) { + FLAC__format_entropy_coding_method_partitioned_rice_contents_clear(&encoder->private_->partitioned_rice_contents_workspace_mid_side[i][0]); + FLAC__format_entropy_coding_method_partitioned_rice_contents_clear(&encoder->private_->partitioned_rice_contents_workspace_mid_side[i][1]); + } + for(i = 0; i < 2; i++) + FLAC__format_entropy_coding_method_partitioned_rice_contents_clear(&encoder->private_->partitioned_rice_contents_extra[i]); + + FLAC__bitwriter_delete(encoder->private_->frame); + free(encoder->private_); + free(encoder->protected_); + free(encoder); +} + +/*********************************************************************** + * + * Public class methods + * + ***********************************************************************/ + +static FLAC__StreamEncoderInitStatus init_stream_internal_enc( + FLAC__StreamEncoder *encoder, + FLAC__StreamEncoderReadCallback read_callback, + FLAC__StreamEncoderWriteCallback write_callback, + FLAC__StreamEncoderSeekCallback seek_callback, + FLAC__StreamEncoderTellCallback tell_callback, + FLAC__StreamEncoderMetadataCallback metadata_callback, + void *client_data, + FLAC__bool is_ogg +) +{ + unsigned i; + FLAC__bool metadata_has_seektable, metadata_has_vorbis_comment, metadata_picture_has_type1, metadata_picture_has_type2; + + FLAC__ASSERT(0 != encoder); + + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return FLAC__STREAM_ENCODER_INIT_STATUS_ALREADY_INITIALIZED; + +#if !FLAC__HAS_OGG + if(is_ogg) + return FLAC__STREAM_ENCODER_INIT_STATUS_UNSUPPORTED_CONTAINER; +#endif + + if(0 == write_callback || (seek_callback && 0 == tell_callback)) + return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_CALLBACKS; + + if(encoder->protected_->channels == 0 || encoder->protected_->channels > FLAC__MAX_CHANNELS) + return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_NUMBER_OF_CHANNELS; + + if(encoder->protected_->channels != 2) { + encoder->protected_->do_mid_side_stereo = false; + encoder->protected_->loose_mid_side_stereo = false; + } + else if(!encoder->protected_->do_mid_side_stereo) + encoder->protected_->loose_mid_side_stereo = false; + + if(encoder->protected_->bits_per_sample >= 32) + encoder->protected_->do_mid_side_stereo = false; /* since we currenty do 32-bit math, the side channel would have 33 bps and overflow */ + + if(encoder->protected_->bits_per_sample < FLAC__MIN_BITS_PER_SAMPLE || encoder->protected_->bits_per_sample > FLAC__REFERENCE_CODEC_MAX_BITS_PER_SAMPLE) + return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_BITS_PER_SAMPLE; + + if(!FLAC__format_sample_rate_is_valid(encoder->protected_->sample_rate)) + return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_SAMPLE_RATE; + + if(encoder->protected_->blocksize == 0) { + if(encoder->protected_->max_lpc_order == 0) + encoder->protected_->blocksize = 1152; + else + encoder->protected_->blocksize = 4096; + } + + if(encoder->protected_->blocksize < FLAC__MIN_BLOCK_SIZE || encoder->protected_->blocksize > FLAC__MAX_BLOCK_SIZE) + return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_BLOCK_SIZE; + + if(encoder->protected_->max_lpc_order > FLAC__MAX_LPC_ORDER) + return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_MAX_LPC_ORDER; + + if(encoder->protected_->blocksize < encoder->protected_->max_lpc_order) + return FLAC__STREAM_ENCODER_INIT_STATUS_BLOCK_SIZE_TOO_SMALL_FOR_LPC_ORDER; + + if(encoder->protected_->qlp_coeff_precision == 0) { + if(encoder->protected_->bits_per_sample < 16) { + /* @@@ need some data about how to set this here w.r.t. blocksize and sample rate */ + /* @@@ until then we'll make a guess */ + encoder->protected_->qlp_coeff_precision = max(FLAC__MIN_QLP_COEFF_PRECISION, 2 + encoder->protected_->bits_per_sample / 2); + } + else if(encoder->protected_->bits_per_sample == 16) { + if(encoder->protected_->blocksize <= 192) + encoder->protected_->qlp_coeff_precision = 7; + else if(encoder->protected_->blocksize <= 384) + encoder->protected_->qlp_coeff_precision = 8; + else if(encoder->protected_->blocksize <= 576) + encoder->protected_->qlp_coeff_precision = 9; + else if(encoder->protected_->blocksize <= 1152) + encoder->protected_->qlp_coeff_precision = 10; + else if(encoder->protected_->blocksize <= 2304) + encoder->protected_->qlp_coeff_precision = 11; + else if(encoder->protected_->blocksize <= 4608) + encoder->protected_->qlp_coeff_precision = 12; + else + encoder->protected_->qlp_coeff_precision = 13; + } + else { + if(encoder->protected_->blocksize <= 384) + encoder->protected_->qlp_coeff_precision = FLAC__MAX_QLP_COEFF_PRECISION-2; + else if(encoder->protected_->blocksize <= 1152) + encoder->protected_->qlp_coeff_precision = FLAC__MAX_QLP_COEFF_PRECISION-1; + else + encoder->protected_->qlp_coeff_precision = FLAC__MAX_QLP_COEFF_PRECISION; + } + FLAC__ASSERT(encoder->protected_->qlp_coeff_precision <= FLAC__MAX_QLP_COEFF_PRECISION); + } + else if(encoder->protected_->qlp_coeff_precision < FLAC__MIN_QLP_COEFF_PRECISION || encoder->protected_->qlp_coeff_precision > FLAC__MAX_QLP_COEFF_PRECISION) + return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_QLP_COEFF_PRECISION; + + if(encoder->protected_->streamable_subset) { + if( + encoder->protected_->blocksize != 192 && + encoder->protected_->blocksize != 576 && + encoder->protected_->blocksize != 1152 && + encoder->protected_->blocksize != 2304 && + encoder->protected_->blocksize != 4608 && + encoder->protected_->blocksize != 256 && + encoder->protected_->blocksize != 512 && + encoder->protected_->blocksize != 1024 && + encoder->protected_->blocksize != 2048 && + encoder->protected_->blocksize != 4096 && + encoder->protected_->blocksize != 8192 && + encoder->protected_->blocksize != 16384 + ) + return FLAC__STREAM_ENCODER_INIT_STATUS_NOT_STREAMABLE; + if(!FLAC__format_sample_rate_is_subset(encoder->protected_->sample_rate)) + return FLAC__STREAM_ENCODER_INIT_STATUS_NOT_STREAMABLE; + if( + encoder->protected_->bits_per_sample != 8 && + encoder->protected_->bits_per_sample != 12 && + encoder->protected_->bits_per_sample != 16 && + encoder->protected_->bits_per_sample != 20 && + encoder->protected_->bits_per_sample != 24 + ) + return FLAC__STREAM_ENCODER_INIT_STATUS_NOT_STREAMABLE; + if(encoder->protected_->max_residual_partition_order > FLAC__SUBSET_MAX_RICE_PARTITION_ORDER) + return FLAC__STREAM_ENCODER_INIT_STATUS_NOT_STREAMABLE; + if( + encoder->protected_->sample_rate <= 48000 && + ( + encoder->protected_->blocksize > FLAC__SUBSET_MAX_BLOCK_SIZE_48000HZ || + encoder->protected_->max_lpc_order > FLAC__SUBSET_MAX_LPC_ORDER_48000HZ + ) + ) { + return FLAC__STREAM_ENCODER_INIT_STATUS_NOT_STREAMABLE; + } + } + + if(encoder->protected_->max_residual_partition_order >= (1u << FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN)) + encoder->protected_->max_residual_partition_order = (1u << FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN) - 1; + if(encoder->protected_->min_residual_partition_order >= encoder->protected_->max_residual_partition_order) + encoder->protected_->min_residual_partition_order = encoder->protected_->max_residual_partition_order; + +#if FLAC__HAS_OGG + /* reorder metadata if necessary to ensure that any VORBIS_COMMENT is the first, according to the mapping spec */ + if(is_ogg && 0 != encoder->protected_->metadata && encoder->protected_->num_metadata_blocks > 1) { + unsigned i; + for(i = 1; i < encoder->protected_->num_metadata_blocks; i++) { + if(0 != encoder->protected_->metadata[i] && encoder->protected_->metadata[i]->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) { + FLAC__StreamMetadata *vc = encoder->protected_->metadata[i]; + for( ; i > 0; i--) + encoder->protected_->metadata[i] = encoder->protected_->metadata[i-1]; + encoder->protected_->metadata[0] = vc; + break; + } + } + } +#endif + /* keep track of any SEEKTABLE block */ + if(0 != encoder->protected_->metadata && encoder->protected_->num_metadata_blocks > 0) { + unsigned i; + for(i = 0; i < encoder->protected_->num_metadata_blocks; i++) { + if(0 != encoder->protected_->metadata[i] && encoder->protected_->metadata[i]->type == FLAC__METADATA_TYPE_SEEKTABLE) { + encoder->private_->seek_table = &encoder->protected_->metadata[i]->data.seek_table; + break; /* take only the first one */ + } + } + } + + /* validate metadata */ + if(0 == encoder->protected_->metadata && encoder->protected_->num_metadata_blocks > 0) + return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_METADATA; + metadata_has_seektable = false; + metadata_has_vorbis_comment = false; + metadata_picture_has_type1 = false; + metadata_picture_has_type2 = false; + for(i = 0; i < encoder->protected_->num_metadata_blocks; i++) { + const FLAC__StreamMetadata *m = encoder->protected_->metadata[i]; + if(m->type == FLAC__METADATA_TYPE_STREAMINFO) + return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_METADATA; + else if(m->type == FLAC__METADATA_TYPE_SEEKTABLE) { + if(metadata_has_seektable) /* only one is allowed */ + return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_METADATA; + metadata_has_seektable = true; + if(!FLAC__format_seektable_is_legal(&m->data.seek_table)) + return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_METADATA; + } + else if(m->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) { + if(metadata_has_vorbis_comment) /* only one is allowed */ + return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_METADATA; + metadata_has_vorbis_comment = true; + } + else if(m->type == FLAC__METADATA_TYPE_CUESHEET) { + if(!FLAC__format_cuesheet_is_legal(&m->data.cue_sheet, m->data.cue_sheet.is_cd, /*violation=*/0)) + return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_METADATA; + } + else if(m->type == FLAC__METADATA_TYPE_PICTURE) { + if(!FLAC__format_picture_is_legal(&m->data.picture, /*violation=*/0)) + return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_METADATA; + if(m->data.picture.type == FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON_STANDARD) { + if(metadata_picture_has_type1) /* there should only be 1 per stream */ + return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_METADATA; + metadata_picture_has_type1 = true; + /* standard icon must be 32x32 pixel PNG */ + if( + m->data.picture.type == FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON_STANDARD && + ( + (strcmp(m->data.picture.mime_type, "image/png") && strcmp(m->data.picture.mime_type, "-->")) || + m->data.picture.width != 32 || + m->data.picture.height != 32 + ) + ) + return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_METADATA; + } + else if(m->data.picture.type == FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON) { + if(metadata_picture_has_type2) /* there should only be 1 per stream */ + return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_METADATA; + metadata_picture_has_type2 = true; + } + } + } + + encoder->private_->input_capacity = 0; + for(i = 0; i < encoder->protected_->channels; i++) { + encoder->private_->integer_signal_unaligned[i] = encoder->private_->integer_signal[i] = 0; +#ifndef FLAC__INTEGER_ONLY_LIBRARY + encoder->private_->real_signal_unaligned[i] = encoder->private_->real_signal[i] = 0; +#endif + } + for(i = 0; i < 2; i++) { + encoder->private_->integer_signal_mid_side_unaligned[i] = encoder->private_->integer_signal_mid_side[i] = 0; +#ifndef FLAC__INTEGER_ONLY_LIBRARY + encoder->private_->real_signal_mid_side_unaligned[i] = encoder->private_->real_signal_mid_side[i] = 0; +#endif + } +#ifndef FLAC__INTEGER_ONLY_LIBRARY + for(i = 0; i < encoder->protected_->num_apodizations; i++) + encoder->private_->window_unaligned[i] = encoder->private_->window[i] = 0; + encoder->private_->windowed_signal_unaligned = encoder->private_->windowed_signal = 0; +#endif + for(i = 0; i < encoder->protected_->channels; i++) { + encoder->private_->residual_workspace_unaligned[i][0] = encoder->private_->residual_workspace[i][0] = 0; + encoder->private_->residual_workspace_unaligned[i][1] = encoder->private_->residual_workspace[i][1] = 0; + encoder->private_->best_subframe[i] = 0; + } + for(i = 0; i < 2; i++) { + encoder->private_->residual_workspace_mid_side_unaligned[i][0] = encoder->private_->residual_workspace_mid_side[i][0] = 0; + encoder->private_->residual_workspace_mid_side_unaligned[i][1] = encoder->private_->residual_workspace_mid_side[i][1] = 0; + encoder->private_->best_subframe_mid_side[i] = 0; + } + encoder->private_->abs_residual_partition_sums_unaligned = encoder->private_->abs_residual_partition_sums = 0; + encoder->private_->raw_bits_per_partition_unaligned = encoder->private_->raw_bits_per_partition = 0; +#ifndef FLAC__INTEGER_ONLY_LIBRARY + encoder->private_->loose_mid_side_stereo_frames = (unsigned)((FLAC__double)encoder->protected_->sample_rate * 0.4 / (FLAC__double)encoder->protected_->blocksize + 0.5); +#else + /* 26214 is the approximate fixed-point equivalent to 0.4 (0.4 * 2^16) */ + /* sample rate can be up to 655350 Hz, and thus use 20 bits, so we do the multiply÷ by hand */ + FLAC__ASSERT(FLAC__MAX_SAMPLE_RATE <= 655350); + FLAC__ASSERT(FLAC__MAX_BLOCK_SIZE <= 65535); + FLAC__ASSERT(encoder->protected_->sample_rate <= 655350); + FLAC__ASSERT(encoder->protected_->blocksize <= 65535); + encoder->private_->loose_mid_side_stereo_frames = (unsigned)FLAC__fixedpoint_trunc((((FLAC__uint64)(encoder->protected_->sample_rate) * (FLAC__uint64)(26214)) << 16) / (encoder->protected_->blocksize<<16) + FLAC__FP_ONE_HALF); +#endif + if(encoder->private_->loose_mid_side_stereo_frames == 0) + encoder->private_->loose_mid_side_stereo_frames = 1; + encoder->private_->loose_mid_side_stereo_frame_count = 0; + encoder->private_->current_sample_number = 0; + encoder->private_->current_frame_number = 0; + + encoder->private_->use_wide_by_block = (encoder->protected_->bits_per_sample + FLAC__bitmath_ilog2(encoder->protected_->blocksize)+1 > 30); + encoder->private_->use_wide_by_order = (encoder->protected_->bits_per_sample + FLAC__bitmath_ilog2(max(encoder->protected_->max_lpc_order, FLAC__MAX_FIXED_ORDER))+1 > 30); /*@@@ need to use this? */ + encoder->private_->use_wide_by_partition = (false); /*@@@ need to set this */ + + /* + * get the CPU info and set the function pointers + */ + FLAC__cpu_info(&encoder->private_->cpuinfo); + /* first default to the non-asm routines */ +#ifndef FLAC__INTEGER_ONLY_LIBRARY + encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation; +#endif + encoder->private_->local_fixed_compute_best_predictor = FLAC__fixed_compute_best_predictor; +#ifndef FLAC__INTEGER_ONLY_LIBRARY + encoder->private_->local_lpc_compute_residual_from_qlp_coefficients = FLAC__lpc_compute_residual_from_qlp_coefficients; + encoder->private_->local_lpc_compute_residual_from_qlp_coefficients_64bit = FLAC__lpc_compute_residual_from_qlp_coefficients_wide; + encoder->private_->local_lpc_compute_residual_from_qlp_coefficients_16bit = FLAC__lpc_compute_residual_from_qlp_coefficients; +#endif + /* now override with asm where appropriate */ +#ifndef FLAC__INTEGER_ONLY_LIBRARY +# ifndef FLAC__NO_ASM + if(encoder->private_->cpuinfo.use_asm) { +# ifdef FLAC__CPU_IA32 + FLAC__ASSERT(encoder->private_->cpuinfo.type == FLAC__CPUINFO_TYPE_IA32); +# ifdef FLAC__HAS_NASM + if(encoder->private_->cpuinfo.data.ia32.sse) { + if(encoder->protected_->max_lpc_order < 4) + encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_4; + else if(encoder->protected_->max_lpc_order < 8) + encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_8; + else if(encoder->protected_->max_lpc_order < 12) + encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_12; + else + encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_asm_ia32; + } + else if(encoder->private_->cpuinfo.data.ia32._3dnow) + encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_asm_ia32_3dnow; + else + encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_asm_ia32; + if(encoder->private_->cpuinfo.data.ia32.mmx) { + encoder->private_->local_lpc_compute_residual_from_qlp_coefficients = FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32; + encoder->private_->local_lpc_compute_residual_from_qlp_coefficients_16bit = FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32_mmx; + } + else { + encoder->private_->local_lpc_compute_residual_from_qlp_coefficients = FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32; + encoder->private_->local_lpc_compute_residual_from_qlp_coefficients_16bit = FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32; + } + if(encoder->private_->cpuinfo.data.ia32.mmx && encoder->private_->cpuinfo.data.ia32.cmov) + encoder->private_->local_fixed_compute_best_predictor = FLAC__fixed_compute_best_predictor_asm_ia32_mmx_cmov; +# endif /* FLAC__HAS_NASM */ +# endif /* FLAC__CPU_IA32 */ + } +# endif /* !FLAC__NO_ASM */ +#endif /* !FLAC__INTEGER_ONLY_LIBRARY */ + /* finally override based on wide-ness if necessary */ + if(encoder->private_->use_wide_by_block) { + encoder->private_->local_fixed_compute_best_predictor = FLAC__fixed_compute_best_predictor_wide; + } + + /* set state to OK; from here on, errors are fatal and we'll override the state then */ + encoder->protected_->state = FLAC__STREAM_ENCODER_OK; + +#if FLAC__HAS_OGG + encoder->private_->is_ogg = is_ogg; + if(is_ogg && !FLAC__ogg_encoder_aspect_init(&encoder->protected_->ogg_encoder_aspect)) { + encoder->protected_->state = FLAC__STREAM_ENCODER_OGG_ERROR; + return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR; + } +#endif + + encoder->private_->read_callback = read_callback; + encoder->private_->write_callback = write_callback; + encoder->private_->seek_callback = seek_callback; + encoder->private_->tell_callback = tell_callback; + encoder->private_->metadata_callback = metadata_callback; + encoder->private_->client_data = client_data; + + if(!resize_buffers_(encoder, encoder->protected_->blocksize)) { + /* the above function sets the state for us in case of an error */ + return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR; + } + + if(!FLAC__bitwriter_init(encoder->private_->frame)) { + encoder->protected_->state = FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR; + return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR; + } + + /* + * Set up the verify stuff if necessary + */ + if(encoder->protected_->verify) { + /* + * First, set up the fifo which will hold the + * original signal to compare against + */ + encoder->private_->verify.input_fifo.size = encoder->protected_->blocksize+OVERREAD_; + for(i = 0; i < encoder->protected_->channels; i++) { + if(0 == (encoder->private_->verify.input_fifo.data[i] = (FLAC__int32*)safe_malloc_mul_2op_(sizeof(FLAC__int32), /*times*/encoder->private_->verify.input_fifo.size))) { + encoder->protected_->state = FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR; + return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR; + } + } + encoder->private_->verify.input_fifo.tail = 0; + + /* + * Now set up a stream decoder for verification + */ + encoder->private_->verify.decoder = FLAC__stream_decoder_new(); + if(0 == encoder->private_->verify.decoder) { + encoder->protected_->state = FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR; + return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR; + } + + if(FLAC__stream_decoder_init_stream(encoder->private_->verify.decoder, verify_read_callback_, /*seek_callback=*/0, /*tell_callback=*/0, /*length_callback=*/0, /*eof_callback=*/0, verify_write_callback_, verify_metadata_callback_, verify_error_callback_, /*client_data=*/encoder) != FLAC__STREAM_DECODER_INIT_STATUS_OK) { + encoder->protected_->state = FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR; + return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR; + } + } + encoder->private_->verify.error_stats.absolute_sample = 0; + encoder->private_->verify.error_stats.frame_number = 0; + encoder->private_->verify.error_stats.channel = 0; + encoder->private_->verify.error_stats.sample = 0; + encoder->private_->verify.error_stats.expected = 0; + encoder->private_->verify.error_stats.got = 0; + + /* + * These must be done before we write any metadata, because that + * calls the write_callback, which uses these values. + */ + encoder->private_->first_seekpoint_to_check = 0; + encoder->private_->samples_written = 0; + encoder->protected_->streaminfo_offset = 0; + encoder->protected_->seektable_offset = 0; + encoder->protected_->audio_offset = 0; + + /* + * write the stream header + */ + if(encoder->protected_->verify) + encoder->private_->verify.state_hint = ENCODER_IN_MAGIC; + if(!FLAC__bitwriter_write_raw_uint32(encoder->private_->frame, FLAC__STREAM_SYNC, FLAC__STREAM_SYNC_LEN)) { + encoder->protected_->state = FLAC__STREAM_ENCODER_FRAMING_ERROR; + return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR; + } + if(!write_bitbuffer_(encoder, 0, /*is_last_block=*/false)) { + /* the above function sets the state for us in case of an error */ + return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR; + } + + /* + * write the STREAMINFO metadata block + */ + if(encoder->protected_->verify) + encoder->private_->verify.state_hint = ENCODER_IN_METADATA; + encoder->private_->streaminfo.type = FLAC__METADATA_TYPE_STREAMINFO; + encoder->private_->streaminfo.is_last = false; /* we will have at a minimum a VORBIS_COMMENT afterwards */ + encoder->private_->streaminfo.length = FLAC__STREAM_METADATA_STREAMINFO_LENGTH; + encoder->private_->streaminfo.data.stream_info.min_blocksize = encoder->protected_->blocksize; /* this encoder uses the same blocksize for the whole stream */ + encoder->private_->streaminfo.data.stream_info.max_blocksize = encoder->protected_->blocksize; + encoder->private_->streaminfo.data.stream_info.min_framesize = 0; /* we don't know this yet; have to fill it in later */ + encoder->private_->streaminfo.data.stream_info.max_framesize = 0; /* we don't know this yet; have to fill it in later */ + encoder->private_->streaminfo.data.stream_info.sample_rate = encoder->protected_->sample_rate; + encoder->private_->streaminfo.data.stream_info.channels = encoder->protected_->channels; + encoder->private_->streaminfo.data.stream_info.bits_per_sample = encoder->protected_->bits_per_sample; + encoder->private_->streaminfo.data.stream_info.total_samples = encoder->protected_->total_samples_estimate; /* we will replace this later with the real total */ + memset(encoder->private_->streaminfo.data.stream_info.md5sum, 0, 16); /* we don't know this yet; have to fill it in later */ + if(encoder->protected_->do_md5) + FLAC__MD5Init(&encoder->private_->md5context); + if(!FLAC__add_metadata_block(&encoder->private_->streaminfo, encoder->private_->frame)) { + encoder->protected_->state = FLAC__STREAM_ENCODER_FRAMING_ERROR; + return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR; + } + if(!write_bitbuffer_(encoder, 0, /*is_last_block=*/false)) { + /* the above function sets the state for us in case of an error */ + return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR; + } + + /* + * Now that the STREAMINFO block is written, we can init this to an + * absurdly-high value... + */ + encoder->private_->streaminfo.data.stream_info.min_framesize = (1u << FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN) - 1; + /* ... and clear this to 0 */ + encoder->private_->streaminfo.data.stream_info.total_samples = 0; + + /* + * Check to see if the supplied metadata contains a VORBIS_COMMENT; + * if not, we will write an empty one (FLAC__add_metadata_block() + * automatically supplies the vendor string). + * + * WATCHOUT: the Ogg FLAC mapping requires us to write this block after + * the STREAMINFO. (In the case that metadata_has_vorbis_comment is + * true it will have already insured that the metadata list is properly + * ordered.) + */ + if(!metadata_has_vorbis_comment) { + FLAC__StreamMetadata vorbis_comment; + vorbis_comment.type = FLAC__METADATA_TYPE_VORBIS_COMMENT; + vorbis_comment.is_last = (encoder->protected_->num_metadata_blocks == 0); + vorbis_comment.length = 4 + 4; /* MAGIC NUMBER */ + vorbis_comment.data.vorbis_comment.vendor_string.length = 0; + vorbis_comment.data.vorbis_comment.vendor_string.entry = 0; + vorbis_comment.data.vorbis_comment.num_comments = 0; + vorbis_comment.data.vorbis_comment.comments = 0; + if(!FLAC__add_metadata_block(&vorbis_comment, encoder->private_->frame)) { + encoder->protected_->state = FLAC__STREAM_ENCODER_FRAMING_ERROR; + return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR; + } + if(!write_bitbuffer_(encoder, 0, /*is_last_block=*/false)) { + /* the above function sets the state for us in case of an error */ + return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR; + } + } + + /* + * write the user's metadata blocks + */ + for(i = 0; i < encoder->protected_->num_metadata_blocks; i++) { + encoder->protected_->metadata[i]->is_last = (i == encoder->protected_->num_metadata_blocks - 1); + if(!FLAC__add_metadata_block(encoder->protected_->metadata[i], encoder->private_->frame)) { + encoder->protected_->state = FLAC__STREAM_ENCODER_FRAMING_ERROR; + return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR; + } + if(!write_bitbuffer_(encoder, 0, /*is_last_block=*/false)) { + /* the above function sets the state for us in case of an error */ + return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR; + } + } + + /* now that all the metadata is written, we save the stream offset */ + if(encoder->private_->tell_callback && encoder->private_->tell_callback(encoder, &encoder->protected_->audio_offset, encoder->private_->client_data) == FLAC__STREAM_ENCODER_TELL_STATUS_ERROR) { /* FLAC__STREAM_ENCODER_TELL_STATUS_UNSUPPORTED just means we didn't get the offset; no error */ + encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR; + return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR; + } + + if(encoder->protected_->verify) + encoder->private_->verify.state_hint = ENCODER_IN_AUDIO; + + return FLAC__STREAM_ENCODER_INIT_STATUS_OK; +} + +FLAC_API FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_stream( + FLAC__StreamEncoder *encoder, + FLAC__StreamEncoderWriteCallback write_callback, + FLAC__StreamEncoderSeekCallback seek_callback, + FLAC__StreamEncoderTellCallback tell_callback, + FLAC__StreamEncoderMetadataCallback metadata_callback, + void *client_data +) +{ + return init_stream_internal_enc( + encoder, + /*read_callback=*/0, + write_callback, + seek_callback, + tell_callback, + metadata_callback, + client_data, + /*is_ogg=*/false + ); +} + +FLAC_API FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_ogg_stream( + FLAC__StreamEncoder *encoder, + FLAC__StreamEncoderReadCallback read_callback, + FLAC__StreamEncoderWriteCallback write_callback, + FLAC__StreamEncoderSeekCallback seek_callback, + FLAC__StreamEncoderTellCallback tell_callback, + FLAC__StreamEncoderMetadataCallback metadata_callback, + void *client_data +) +{ + return init_stream_internal_enc( + encoder, + read_callback, + write_callback, + seek_callback, + tell_callback, + metadata_callback, + client_data, + /*is_ogg=*/true + ); +} + +static FLAC__StreamEncoderInitStatus init_FILE_internal_enc( + FLAC__StreamEncoder *encoder, + FILE *file, + FLAC__StreamEncoderProgressCallback progress_callback, + void *client_data, + FLAC__bool is_ogg +) +{ + FLAC__StreamEncoderInitStatus init_status; + + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != file); + + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return FLAC__STREAM_ENCODER_INIT_STATUS_ALREADY_INITIALIZED; + + /* double protection */ + if(file == 0) { + encoder->protected_->state = FLAC__STREAM_ENCODER_IO_ERROR; + return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR; + } + + /* + * To make sure that our file does not go unclosed after an error, we + * must assign the FILE pointer before any further error can occur in + * this routine. + */ + if(file == stdout) + file = get_binary_stdout_(); /* just to be safe */ + + encoder->private_->file = file; + + encoder->private_->progress_callback = progress_callback; + encoder->private_->bytes_written = 0; + encoder->private_->samples_written = 0; + encoder->private_->frames_written = 0; + + init_status = init_stream_internal_enc( + encoder, + encoder->private_->file == stdout? 0 : is_ogg? file_read_callback_enc : 0, + file_write_callback_, + encoder->private_->file == stdout? 0 : file_seek_callback_enc, + encoder->private_->file == stdout? 0 : file_tell_callback_enc, + /*metadata_callback=*/0, + client_data, + is_ogg + ); + if(init_status != FLAC__STREAM_ENCODER_INIT_STATUS_OK) { + /* the above function sets the state for us in case of an error */ + return init_status; + } + + { + unsigned blocksize = FLAC__stream_encoder_get_blocksize(encoder); + + FLAC__ASSERT(blocksize != 0); + encoder->private_->total_frames_estimate = (unsigned)((FLAC__stream_encoder_get_total_samples_estimate(encoder) + blocksize - 1) / blocksize); + } + + return init_status; +} + +FLAC_API FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_FILE( + FLAC__StreamEncoder *encoder, + FILE *file, + FLAC__StreamEncoderProgressCallback progress_callback, + void *client_data +) +{ + return init_FILE_internal_enc(encoder, file, progress_callback, client_data, /*is_ogg=*/false); +} + +FLAC_API FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_ogg_FILE( + FLAC__StreamEncoder *encoder, + FILE *file, + FLAC__StreamEncoderProgressCallback progress_callback, + void *client_data +) +{ + return init_FILE_internal_enc(encoder, file, progress_callback, client_data, /*is_ogg=*/true); +} + +static FLAC__StreamEncoderInitStatus init_file_internal_enc( + FLAC__StreamEncoder *encoder, + const char *filename, + FLAC__StreamEncoderProgressCallback progress_callback, + void *client_data, + FLAC__bool is_ogg +) +{ + FILE *file; + + FLAC__ASSERT(0 != encoder); + + /* + * To make sure that our file does not go unclosed after an error, we + * have to do the same entrance checks here that are later performed + * in FLAC__stream_encoder_init_FILE() before the FILE* is assigned. + */ + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return FLAC__STREAM_ENCODER_INIT_STATUS_ALREADY_INITIALIZED; + + file = filename? fopen(filename, "w+b") : stdout; + + if(file == 0) { + encoder->protected_->state = FLAC__STREAM_ENCODER_IO_ERROR; + return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR; + } + + return init_FILE_internal_enc(encoder, file, progress_callback, client_data, is_ogg); +} + +FLAC_API FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_file( + FLAC__StreamEncoder *encoder, + const char *filename, + FLAC__StreamEncoderProgressCallback progress_callback, + void *client_data +) +{ + return init_file_internal_enc(encoder, filename, progress_callback, client_data, /*is_ogg=*/false); +} + +FLAC_API FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_ogg_file( + FLAC__StreamEncoder *encoder, + const char *filename, + FLAC__StreamEncoderProgressCallback progress_callback, + void *client_data +) +{ + return init_file_internal_enc(encoder, filename, progress_callback, client_data, /*is_ogg=*/true); +} + +FLAC_API FLAC__bool FLAC__stream_encoder_finish(FLAC__StreamEncoder *encoder) +{ + FLAC__bool error = false; + + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + + if(encoder->protected_->state == FLAC__STREAM_ENCODER_UNINITIALIZED) + return true; + + if(encoder->protected_->state == FLAC__STREAM_ENCODER_OK && !encoder->private_->is_being_deleted) { + if(encoder->private_->current_sample_number != 0) { + const FLAC__bool is_fractional_block = encoder->protected_->blocksize != encoder->private_->current_sample_number; + encoder->protected_->blocksize = encoder->private_->current_sample_number; + if(!process_frame_(encoder, is_fractional_block, /*is_last_block=*/true)) + error = true; + } + } + + if(encoder->protected_->do_md5) + FLAC__MD5Final(encoder->private_->streaminfo.data.stream_info.md5sum, &encoder->private_->md5context); + + if(!encoder->private_->is_being_deleted) { + if(encoder->protected_->state == FLAC__STREAM_ENCODER_OK) { + if(encoder->private_->seek_callback) { +#if FLAC__HAS_OGG + if(encoder->private_->is_ogg) + update_ogg_metadata_(encoder); + else +#endif + update_metadata_(encoder); + + /* check if an error occurred while updating metadata */ + if(encoder->protected_->state != FLAC__STREAM_ENCODER_OK) + error = true; + } + if(encoder->private_->metadata_callback) + encoder->private_->metadata_callback(encoder, &encoder->private_->streaminfo, encoder->private_->client_data); + } + + if(encoder->protected_->verify && 0 != encoder->private_->verify.decoder && !FLAC__stream_decoder_finish(encoder->private_->verify.decoder)) { + if(!error) + encoder->protected_->state = FLAC__STREAM_ENCODER_VERIFY_MISMATCH_IN_AUDIO_DATA; + error = true; + } + } + + if(0 != encoder->private_->file) { + if(encoder->private_->file != stdout) + fclose(encoder->private_->file); + encoder->private_->file = 0; + } + +#if FLAC__HAS_OGG + if(encoder->private_->is_ogg) + FLAC__ogg_encoder_aspect_finish(&encoder->protected_->ogg_encoder_aspect); +#endif + + free_(encoder); + set_defaults_enc(encoder); + + if(!error) + encoder->protected_->state = FLAC__STREAM_ENCODER_UNINITIALIZED; + + return !error; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_set_ogg_serial_number(FLAC__StreamEncoder *encoder, long value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; +#if FLAC__HAS_OGG + /* can't check encoder->private_->is_ogg since that's not set until init time */ + FLAC__ogg_encoder_aspect_set_serial_number(&encoder->protected_->ogg_encoder_aspect, value); + return true; +#else + (void)value; + return false; +#endif +} + +FLAC_API FLAC__bool FLAC__stream_encoder_set_verify(FLAC__StreamEncoder *encoder, FLAC__bool value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; +#ifndef FLAC__MANDATORY_VERIFY_WHILE_ENCODING + encoder->protected_->verify = value; +#endif + return true; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_set_streamable_subset(FLAC__StreamEncoder *encoder, FLAC__bool value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; + encoder->protected_->streamable_subset = value; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_set_do_md5(FLAC__StreamEncoder *encoder, FLAC__bool value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; + encoder->protected_->do_md5 = value; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_set_channels(FLAC__StreamEncoder *encoder, unsigned value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; + encoder->protected_->channels = value; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_set_bits_per_sample(FLAC__StreamEncoder *encoder, unsigned value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; + encoder->protected_->bits_per_sample = value; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_set_sample_rate(FLAC__StreamEncoder *encoder, unsigned value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; + encoder->protected_->sample_rate = value; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_set_compression_level(FLAC__StreamEncoder *encoder, unsigned value) +{ + FLAC__bool ok = true; + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; + if(value >= sizeof(compression_levels_)/sizeof(compression_levels_[0])) + value = sizeof(compression_levels_)/sizeof(compression_levels_[0]) - 1; + ok &= FLAC__stream_encoder_set_do_mid_side_stereo (encoder, compression_levels_[value].do_mid_side_stereo); + ok &= FLAC__stream_encoder_set_loose_mid_side_stereo (encoder, compression_levels_[value].loose_mid_side_stereo); +#ifndef FLAC__INTEGER_ONLY_LIBRARY +#if 0 + /* was: */ + ok &= FLAC__stream_encoder_set_apodization (encoder, compression_levels_[value].apodization); + /* but it's too hard to specify the string in a locale-specific way */ +#else + encoder->protected_->num_apodizations = 1; + encoder->protected_->apodizations[0].type = FLAC__APODIZATION_TUKEY; + encoder->protected_->apodizations[0].parameters.tukey.p = 0.5; +#endif +#endif + ok &= FLAC__stream_encoder_set_max_lpc_order (encoder, compression_levels_[value].max_lpc_order); + ok &= FLAC__stream_encoder_set_qlp_coeff_precision (encoder, compression_levels_[value].qlp_coeff_precision); + ok &= FLAC__stream_encoder_set_do_qlp_coeff_prec_search (encoder, compression_levels_[value].do_qlp_coeff_prec_search); + ok &= FLAC__stream_encoder_set_do_escape_coding (encoder, compression_levels_[value].do_escape_coding); + ok &= FLAC__stream_encoder_set_do_exhaustive_model_search (encoder, compression_levels_[value].do_exhaustive_model_search); + ok &= FLAC__stream_encoder_set_min_residual_partition_order(encoder, compression_levels_[value].min_residual_partition_order); + ok &= FLAC__stream_encoder_set_max_residual_partition_order(encoder, compression_levels_[value].max_residual_partition_order); + ok &= FLAC__stream_encoder_set_rice_parameter_search_dist (encoder, compression_levels_[value].rice_parameter_search_dist); + return ok; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_set_blocksize(FLAC__StreamEncoder *encoder, unsigned value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; + encoder->protected_->blocksize = value; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_set_do_mid_side_stereo(FLAC__StreamEncoder *encoder, FLAC__bool value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; + encoder->protected_->do_mid_side_stereo = value; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_set_loose_mid_side_stereo(FLAC__StreamEncoder *encoder, FLAC__bool value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; + encoder->protected_->loose_mid_side_stereo = value; + return true; +} + +/*@@@@add to tests*/ +FLAC_API FLAC__bool FLAC__stream_encoder_set_apodization(FLAC__StreamEncoder *encoder, const char *specification) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + FLAC__ASSERT(0 != specification); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; +#ifdef FLAC__INTEGER_ONLY_LIBRARY + (void)specification; /* silently ignore since we haven't integerized; will always use a rectangular window */ +#else + encoder->protected_->num_apodizations = 0; + while(1) { + const char *s = strchr(specification, ';'); + const size_t n = s? (size_t)(s - specification) : strlen(specification); + if (n==8 && 0 == strncmp("bartlett" , specification, n)) + encoder->protected_->apodizations[encoder->protected_->num_apodizations++].type = FLAC__APODIZATION_BARTLETT; + else if(n==13 && 0 == strncmp("bartlett_hann", specification, n)) + encoder->protected_->apodizations[encoder->protected_->num_apodizations++].type = FLAC__APODIZATION_BARTLETT_HANN; + else if(n==8 && 0 == strncmp("blackman" , specification, n)) + encoder->protected_->apodizations[encoder->protected_->num_apodizations++].type = FLAC__APODIZATION_BLACKMAN; + else if(n==26 && 0 == strncmp("blackman_harris_4term_92db", specification, n)) + encoder->protected_->apodizations[encoder->protected_->num_apodizations++].type = FLAC__APODIZATION_BLACKMAN_HARRIS_4TERM_92DB_SIDELOBE; + else if(n==6 && 0 == strncmp("connes" , specification, n)) + encoder->protected_->apodizations[encoder->protected_->num_apodizations++].type = FLAC__APODIZATION_CONNES; + else if(n==7 && 0 == strncmp("flattop" , specification, n)) + encoder->protected_->apodizations[encoder->protected_->num_apodizations++].type = FLAC__APODIZATION_FLATTOP; + else if(n>7 && 0 == strncmp("gauss(" , specification, 6)) { + FLAC__real stddev = (FLAC__real)strtod(specification+6, 0); + if (stddev > 0.0 && stddev <= 0.5) { + encoder->protected_->apodizations[encoder->protected_->num_apodizations].parameters.gauss.stddev = stddev; + encoder->protected_->apodizations[encoder->protected_->num_apodizations++].type = FLAC__APODIZATION_GAUSS; + } + } + else if(n==7 && 0 == strncmp("hamming" , specification, n)) + encoder->protected_->apodizations[encoder->protected_->num_apodizations++].type = FLAC__APODIZATION_HAMMING; + else if(n==4 && 0 == strncmp("hann" , specification, n)) + encoder->protected_->apodizations[encoder->protected_->num_apodizations++].type = FLAC__APODIZATION_HANN; + else if(n==13 && 0 == strncmp("kaiser_bessel", specification, n)) + encoder->protected_->apodizations[encoder->protected_->num_apodizations++].type = FLAC__APODIZATION_KAISER_BESSEL; + else if(n==7 && 0 == strncmp("nuttall" , specification, n)) + encoder->protected_->apodizations[encoder->protected_->num_apodizations++].type = FLAC__APODIZATION_NUTTALL; + else if(n==9 && 0 == strncmp("rectangle" , specification, n)) + encoder->protected_->apodizations[encoder->protected_->num_apodizations++].type = FLAC__APODIZATION_RECTANGLE; + else if(n==8 && 0 == strncmp("triangle" , specification, n)) + encoder->protected_->apodizations[encoder->protected_->num_apodizations++].type = FLAC__APODIZATION_TRIANGLE; + else if(n>7 && 0 == strncmp("tukey(" , specification, 6)) { + FLAC__real p = (FLAC__real)strtod(specification+6, 0); + if (p >= 0.0 && p <= 1.0) { + encoder->protected_->apodizations[encoder->protected_->num_apodizations].parameters.tukey.p = p; + encoder->protected_->apodizations[encoder->protected_->num_apodizations++].type = FLAC__APODIZATION_TUKEY; + } + } + else if(n==5 && 0 == strncmp("welch" , specification, n)) + encoder->protected_->apodizations[encoder->protected_->num_apodizations++].type = FLAC__APODIZATION_WELCH; + if (encoder->protected_->num_apodizations == 32) + break; + if (s) + specification = s+1; + else + break; + } + if(encoder->protected_->num_apodizations == 0) { + encoder->protected_->num_apodizations = 1; + encoder->protected_->apodizations[0].type = FLAC__APODIZATION_TUKEY; + encoder->protected_->apodizations[0].parameters.tukey.p = 0.5; + } +#endif + return true; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_set_max_lpc_order(FLAC__StreamEncoder *encoder, unsigned value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; + encoder->protected_->max_lpc_order = value; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_set_qlp_coeff_precision(FLAC__StreamEncoder *encoder, unsigned value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; + encoder->protected_->qlp_coeff_precision = value; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_set_do_qlp_coeff_prec_search(FLAC__StreamEncoder *encoder, FLAC__bool value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; + encoder->protected_->do_qlp_coeff_prec_search = value; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_set_do_escape_coding(FLAC__StreamEncoder *encoder, FLAC__bool value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; +#if 0 + /*@@@ deprecated: */ + encoder->protected_->do_escape_coding = value; +#else + (void)value; +#endif + return true; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_set_do_exhaustive_model_search(FLAC__StreamEncoder *encoder, FLAC__bool value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; + encoder->protected_->do_exhaustive_model_search = value; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_set_min_residual_partition_order(FLAC__StreamEncoder *encoder, unsigned value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; + encoder->protected_->min_residual_partition_order = value; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_set_max_residual_partition_order(FLAC__StreamEncoder *encoder, unsigned value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; + encoder->protected_->max_residual_partition_order = value; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_set_rice_parameter_search_dist(FLAC__StreamEncoder *encoder, unsigned value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; +#if 0 + /*@@@ deprecated: */ + encoder->protected_->rice_parameter_search_dist = value; +#else + (void)value; +#endif + return true; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_set_total_samples_estimate(FLAC__StreamEncoder *encoder, FLAC__uint64 value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; + encoder->protected_->total_samples_estimate = value; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_set_metadata(FLAC__StreamEncoder *encoder, FLAC__StreamMetadata **metadata, unsigned num_blocks) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; + if(0 == metadata) + num_blocks = 0; + if(0 == num_blocks) + metadata = 0; + /* realloc() does not do exactly what we want so... */ + if(encoder->protected_->metadata) { + free(encoder->protected_->metadata); + encoder->protected_->metadata = 0; + encoder->protected_->num_metadata_blocks = 0; + } + if(num_blocks) { + FLAC__StreamMetadata **m; + if(0 == (m = (FLAC__StreamMetadata**)safe_malloc_mul_2op_(sizeof(m[0]), /*times*/num_blocks))) + return false; + memcpy(m, metadata, sizeof(m[0]) * num_blocks); + encoder->protected_->metadata = m; + encoder->protected_->num_metadata_blocks = num_blocks; + } +#if FLAC__HAS_OGG + if(!FLAC__ogg_encoder_aspect_set_num_metadata(&encoder->protected_->ogg_encoder_aspect, num_blocks)) + return false; +#endif + return true; +} + +/* + * These three functions are not static, but not publically exposed in + * include/FLAC/ either. They are used by the test suite. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_disable_constant_subframes(FLAC__StreamEncoder *encoder, FLAC__bool value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; + encoder->private_->disable_constant_subframes = value; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_disable_fixed_subframes(FLAC__StreamEncoder *encoder, FLAC__bool value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; + encoder->private_->disable_fixed_subframes = value; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_disable_verbatim_subframes(FLAC__StreamEncoder *encoder, FLAC__bool value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; + encoder->private_->disable_verbatim_subframes = value; + return true; +} + +FLAC_API FLAC__StreamEncoderState FLAC__stream_encoder_get_state(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + return encoder->protected_->state; +} + +FLAC_API FLAC__StreamDecoderState FLAC__stream_encoder_get_verify_decoder_state(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->verify) + return FLAC__stream_decoder_get_state(encoder->private_->verify.decoder); + else + return FLAC__STREAM_DECODER_UNINITIALIZED; +} + +FLAC_API const char *FLAC__stream_encoder_get_resolved_state_string(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR) + return FLAC__StreamEncoderStateString[encoder->protected_->state]; + else + return FLAC__stream_decoder_get_resolved_state_string(encoder->private_->verify.decoder); +} + +FLAC_API void FLAC__stream_encoder_get_verify_decoder_error_stats(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_sample, unsigned *frame_number, unsigned *channel, unsigned *sample, FLAC__int32 *expected, FLAC__int32 *got) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(0 != absolute_sample) + *absolute_sample = encoder->private_->verify.error_stats.absolute_sample; + if(0 != frame_number) + *frame_number = encoder->private_->verify.error_stats.frame_number; + if(0 != channel) + *channel = encoder->private_->verify.error_stats.channel; + if(0 != sample) + *sample = encoder->private_->verify.error_stats.sample; + if(0 != expected) + *expected = encoder->private_->verify.error_stats.expected; + if(0 != got) + *got = encoder->private_->verify.error_stats.got; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_get_verify(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + return encoder->protected_->verify; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_get_streamable_subset(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + return encoder->protected_->streamable_subset; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_get_do_md5(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + return encoder->protected_->do_md5; +} + +FLAC_API unsigned FLAC__stream_encoder_get_channels(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + return encoder->protected_->channels; +} + +FLAC_API unsigned FLAC__stream_encoder_get_bits_per_sample(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + return encoder->protected_->bits_per_sample; +} + +FLAC_API unsigned FLAC__stream_encoder_get_sample_rate(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + return encoder->protected_->sample_rate; +} + +FLAC_API unsigned FLAC__stream_encoder_get_blocksize(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + return encoder->protected_->blocksize; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_get_do_mid_side_stereo(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + return encoder->protected_->do_mid_side_stereo; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_get_loose_mid_side_stereo(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + return encoder->protected_->loose_mid_side_stereo; +} + +FLAC_API unsigned FLAC__stream_encoder_get_max_lpc_order(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + return encoder->protected_->max_lpc_order; +} + +FLAC_API unsigned FLAC__stream_encoder_get_qlp_coeff_precision(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + return encoder->protected_->qlp_coeff_precision; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_get_do_qlp_coeff_prec_search(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + return encoder->protected_->do_qlp_coeff_prec_search; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_get_do_escape_coding(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + return encoder->protected_->do_escape_coding; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_get_do_exhaustive_model_search(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + return encoder->protected_->do_exhaustive_model_search; +} + +FLAC_API unsigned FLAC__stream_encoder_get_min_residual_partition_order(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + return encoder->protected_->min_residual_partition_order; +} + +FLAC_API unsigned FLAC__stream_encoder_get_max_residual_partition_order(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + return encoder->protected_->max_residual_partition_order; +} + +FLAC_API unsigned FLAC__stream_encoder_get_rice_parameter_search_dist(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + return encoder->protected_->rice_parameter_search_dist; +} + +FLAC_API FLAC__uint64 FLAC__stream_encoder_get_total_samples_estimate(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + return encoder->protected_->total_samples_estimate; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_process(FLAC__StreamEncoder *encoder, const FLAC__int32 * const buffer[], unsigned samples) +{ + unsigned i, j = 0, channel; + const unsigned channels = encoder->protected_->channels, blocksize = encoder->protected_->blocksize; + + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + FLAC__ASSERT(encoder->protected_->state == FLAC__STREAM_ENCODER_OK); + + do { + const unsigned n = min(blocksize+OVERREAD_-encoder->private_->current_sample_number, samples-j); + + if(encoder->protected_->verify) + append_to_verify_fifo_(&encoder->private_->verify.input_fifo, buffer, j, channels, n); + + for(channel = 0; channel < channels; channel++) + memcpy(&encoder->private_->integer_signal[channel][encoder->private_->current_sample_number], &buffer[channel][j], sizeof(buffer[channel][0]) * n); + + if(encoder->protected_->do_mid_side_stereo) { + FLAC__ASSERT(channels == 2); + /* "i <= blocksize" to overread 1 sample; see comment in OVERREAD_ decl */ + for(i = encoder->private_->current_sample_number; i <= blocksize && j < samples; i++, j++) { + encoder->private_->integer_signal_mid_side[1][i] = buffer[0][j] - buffer[1][j]; + encoder->private_->integer_signal_mid_side[0][i] = (buffer[0][j] + buffer[1][j]) >> 1; /* NOTE: not the same as 'mid = (buffer[0][j] + buffer[1][j]) / 2' ! */ + } + } + else + j += n; + + encoder->private_->current_sample_number += n; + + /* we only process if we have a full block + 1 extra sample; final block is always handled by FLAC__stream_encoder_finish() */ + if(encoder->private_->current_sample_number > blocksize) { + FLAC__ASSERT(encoder->private_->current_sample_number == blocksize+OVERREAD_); + FLAC__ASSERT(OVERREAD_ == 1); /* assert we only overread 1 sample which simplifies the rest of the code below */ + if(!process_frame_(encoder, /*is_fractional_block=*/false, /*is_last_block=*/false)) + return false; + /* move unprocessed overread samples to beginnings of arrays */ + for(channel = 0; channel < channels; channel++) + encoder->private_->integer_signal[channel][0] = encoder->private_->integer_signal[channel][blocksize]; + if(encoder->protected_->do_mid_side_stereo) { + encoder->private_->integer_signal_mid_side[0][0] = encoder->private_->integer_signal_mid_side[0][blocksize]; + encoder->private_->integer_signal_mid_side[1][0] = encoder->private_->integer_signal_mid_side[1][blocksize]; + } + encoder->private_->current_sample_number = 1; + } + } while(j < samples); + + return true; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_process_interleaved(FLAC__StreamEncoder *encoder, const FLAC__int32 buffer[], unsigned samples) +{ + unsigned i, j, k, channel; + FLAC__int32 x, mid, side; + const unsigned channels = encoder->protected_->channels, blocksize = encoder->protected_->blocksize; + + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + FLAC__ASSERT(encoder->protected_->state == FLAC__STREAM_ENCODER_OK); + + j = k = 0; + /* + * we have several flavors of the same basic loop, optimized for + * different conditions: + */ + if(encoder->protected_->do_mid_side_stereo && channels == 2) { + /* + * stereo coding: unroll channel loop + */ + do { + if(encoder->protected_->verify) + append_to_verify_fifo_interleaved_(&encoder->private_->verify.input_fifo, buffer, j, channels, min(blocksize+OVERREAD_-encoder->private_->current_sample_number, samples-j)); + + /* "i <= blocksize" to overread 1 sample; see comment in OVERREAD_ decl */ + for(i = encoder->private_->current_sample_number; i <= blocksize && j < samples; i++, j++) { + encoder->private_->integer_signal[0][i] = mid = side = buffer[k++]; + x = buffer[k++]; + encoder->private_->integer_signal[1][i] = x; + mid += x; + side -= x; + mid >>= 1; /* NOTE: not the same as 'mid = (left + right) / 2' ! */ + encoder->private_->integer_signal_mid_side[1][i] = side; + encoder->private_->integer_signal_mid_side[0][i] = mid; + } + encoder->private_->current_sample_number = i; + /* we only process if we have a full block + 1 extra sample; final block is always handled by FLAC__stream_encoder_finish() */ + if(i > blocksize) { + if(!process_frame_(encoder, /*is_fractional_block=*/false, /*is_last_block=*/false)) + return false; + /* move unprocessed overread samples to beginnings of arrays */ + FLAC__ASSERT(i == blocksize+OVERREAD_); + FLAC__ASSERT(OVERREAD_ == 1); /* assert we only overread 1 sample which simplifies the rest of the code below */ + encoder->private_->integer_signal[0][0] = encoder->private_->integer_signal[0][blocksize]; + encoder->private_->integer_signal[1][0] = encoder->private_->integer_signal[1][blocksize]; + encoder->private_->integer_signal_mid_side[0][0] = encoder->private_->integer_signal_mid_side[0][blocksize]; + encoder->private_->integer_signal_mid_side[1][0] = encoder->private_->integer_signal_mid_side[1][blocksize]; + encoder->private_->current_sample_number = 1; + } + } while(j < samples); + } + else { + /* + * independent channel coding: buffer each channel in inner loop + */ + do { + if(encoder->protected_->verify) + append_to_verify_fifo_interleaved_(&encoder->private_->verify.input_fifo, buffer, j, channels, min(blocksize+OVERREAD_-encoder->private_->current_sample_number, samples-j)); + + /* "i <= blocksize" to overread 1 sample; see comment in OVERREAD_ decl */ + for(i = encoder->private_->current_sample_number; i <= blocksize && j < samples; i++, j++) { + for(channel = 0; channel < channels; channel++) + encoder->private_->integer_signal[channel][i] = buffer[k++]; + } + encoder->private_->current_sample_number = i; + /* we only process if we have a full block + 1 extra sample; final block is always handled by FLAC__stream_encoder_finish() */ + if(i > blocksize) { + if(!process_frame_(encoder, /*is_fractional_block=*/false, /*is_last_block=*/false)) + return false; + /* move unprocessed overread samples to beginnings of arrays */ + FLAC__ASSERT(i == blocksize+OVERREAD_); + FLAC__ASSERT(OVERREAD_ == 1); /* assert we only overread 1 sample which simplifies the rest of the code below */ + for(channel = 0; channel < channels; channel++) + encoder->private_->integer_signal[channel][0] = encoder->private_->integer_signal[channel][blocksize]; + encoder->private_->current_sample_number = 1; + } + } while(j < samples); + } + + return true; +} + +/*********************************************************************** + * + * Private class methods + * + ***********************************************************************/ + +void set_defaults_enc(FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + +#ifdef FLAC__MANDATORY_VERIFY_WHILE_ENCODING + encoder->protected_->verify = true; +#else + encoder->protected_->verify = false; +#endif + encoder->protected_->streamable_subset = true; + encoder->protected_->do_md5 = true; + encoder->protected_->do_mid_side_stereo = false; + encoder->protected_->loose_mid_side_stereo = false; + encoder->protected_->channels = 2; + encoder->protected_->bits_per_sample = 16; + encoder->protected_->sample_rate = 44100; + encoder->protected_->blocksize = 0; +#ifndef FLAC__INTEGER_ONLY_LIBRARY + encoder->protected_->num_apodizations = 1; + encoder->protected_->apodizations[0].type = FLAC__APODIZATION_TUKEY; + encoder->protected_->apodizations[0].parameters.tukey.p = 0.5; +#endif + encoder->protected_->max_lpc_order = 0; + encoder->protected_->qlp_coeff_precision = 0; + encoder->protected_->do_qlp_coeff_prec_search = false; + encoder->protected_->do_exhaustive_model_search = false; + encoder->protected_->do_escape_coding = false; + encoder->protected_->min_residual_partition_order = 0; + encoder->protected_->max_residual_partition_order = 0; + encoder->protected_->rice_parameter_search_dist = 0; + encoder->protected_->total_samples_estimate = 0; + encoder->protected_->metadata = 0; + encoder->protected_->num_metadata_blocks = 0; + + encoder->private_->seek_table = 0; + encoder->private_->disable_constant_subframes = false; + encoder->private_->disable_fixed_subframes = false; + encoder->private_->disable_verbatim_subframes = false; +#if FLAC__HAS_OGG + encoder->private_->is_ogg = false; +#endif + encoder->private_->read_callback = 0; + encoder->private_->write_callback = 0; + encoder->private_->seek_callback = 0; + encoder->private_->tell_callback = 0; + encoder->private_->metadata_callback = 0; + encoder->private_->progress_callback = 0; + encoder->private_->client_data = 0; + +#if FLAC__HAS_OGG + FLAC__ogg_encoder_aspect_set_defaults(&encoder->protected_->ogg_encoder_aspect); +#endif +} + +void free_(FLAC__StreamEncoder *encoder) +{ + unsigned i, channel; + + FLAC__ASSERT(0 != encoder); + if(encoder->protected_->metadata) { + free(encoder->protected_->metadata); + encoder->protected_->metadata = 0; + encoder->protected_->num_metadata_blocks = 0; + } + for(i = 0; i < encoder->protected_->channels; i++) { + if(0 != encoder->private_->integer_signal_unaligned[i]) { + free(encoder->private_->integer_signal_unaligned[i]); + encoder->private_->integer_signal_unaligned[i] = 0; + } +#ifndef FLAC__INTEGER_ONLY_LIBRARY + if(0 != encoder->private_->real_signal_unaligned[i]) { + free(encoder->private_->real_signal_unaligned[i]); + encoder->private_->real_signal_unaligned[i] = 0; + } +#endif + } + for(i = 0; i < 2; i++) { + if(0 != encoder->private_->integer_signal_mid_side_unaligned[i]) { + free(encoder->private_->integer_signal_mid_side_unaligned[i]); + encoder->private_->integer_signal_mid_side_unaligned[i] = 0; + } +#ifndef FLAC__INTEGER_ONLY_LIBRARY + if(0 != encoder->private_->real_signal_mid_side_unaligned[i]) { + free(encoder->private_->real_signal_mid_side_unaligned[i]); + encoder->private_->real_signal_mid_side_unaligned[i] = 0; + } +#endif + } +#ifndef FLAC__INTEGER_ONLY_LIBRARY + for(i = 0; i < encoder->protected_->num_apodizations; i++) { + if(0 != encoder->private_->window_unaligned[i]) { + free(encoder->private_->window_unaligned[i]); + encoder->private_->window_unaligned[i] = 0; + } + } + if(0 != encoder->private_->windowed_signal_unaligned) { + free(encoder->private_->windowed_signal_unaligned); + encoder->private_->windowed_signal_unaligned = 0; + } +#endif + for(channel = 0; channel < encoder->protected_->channels; channel++) { + for(i = 0; i < 2; i++) { + if(0 != encoder->private_->residual_workspace_unaligned[channel][i]) { + free(encoder->private_->residual_workspace_unaligned[channel][i]); + encoder->private_->residual_workspace_unaligned[channel][i] = 0; + } + } + } + for(channel = 0; channel < 2; channel++) { + for(i = 0; i < 2; i++) { + if(0 != encoder->private_->residual_workspace_mid_side_unaligned[channel][i]) { + free(encoder->private_->residual_workspace_mid_side_unaligned[channel][i]); + encoder->private_->residual_workspace_mid_side_unaligned[channel][i] = 0; + } + } + } + if(0 != encoder->private_->abs_residual_partition_sums_unaligned) { + free(encoder->private_->abs_residual_partition_sums_unaligned); + encoder->private_->abs_residual_partition_sums_unaligned = 0; + } + if(0 != encoder->private_->raw_bits_per_partition_unaligned) { + free(encoder->private_->raw_bits_per_partition_unaligned); + encoder->private_->raw_bits_per_partition_unaligned = 0; + } + if(encoder->protected_->verify) { + for(i = 0; i < encoder->protected_->channels; i++) { + if(0 != encoder->private_->verify.input_fifo.data[i]) { + free(encoder->private_->verify.input_fifo.data[i]); + encoder->private_->verify.input_fifo.data[i] = 0; + } + } + } + FLAC__bitwriter_free(encoder->private_->frame); +} + +FLAC__bool resize_buffers_(FLAC__StreamEncoder *encoder, unsigned new_blocksize) +{ + FLAC__bool ok; + unsigned i, channel; + + FLAC__ASSERT(new_blocksize > 0); + FLAC__ASSERT(encoder->protected_->state == FLAC__STREAM_ENCODER_OK); + FLAC__ASSERT(encoder->private_->current_sample_number == 0); + + /* To avoid excessive malloc'ing, we only grow the buffer; no shrinking. */ + if(new_blocksize <= encoder->private_->input_capacity) + return true; + + ok = true; + + /* WATCHOUT: FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32_mmx() + * requires that the input arrays (in our case the integer signals) + * have a buffer of up to 3 zeroes in front (at negative indices) for + * alignment purposes; we use 4 in front to keep the data well-aligned. + */ + + for(i = 0; ok && i < encoder->protected_->channels; i++) { + ok = ok && FLAC__memory_alloc_aligned_int32_array(new_blocksize+4+OVERREAD_, &encoder->private_->integer_signal_unaligned[i], &encoder->private_->integer_signal[i]); + memset(encoder->private_->integer_signal[i], 0, sizeof(FLAC__int32)*4); + encoder->private_->integer_signal[i] += 4; +#ifndef FLAC__INTEGER_ONLY_LIBRARY +#if 0 /* @@@ currently unused */ + if(encoder->protected_->max_lpc_order > 0) + ok = ok && FLAC__memory_alloc_aligned_real_array(new_blocksize+OVERREAD_, &encoder->private_->real_signal_unaligned[i], &encoder->private_->real_signal[i]); +#endif +#endif + } + for(i = 0; ok && i < 2; i++) { + ok = ok && FLAC__memory_alloc_aligned_int32_array(new_blocksize+4+OVERREAD_, &encoder->private_->integer_signal_mid_side_unaligned[i], &encoder->private_->integer_signal_mid_side[i]); + memset(encoder->private_->integer_signal_mid_side[i], 0, sizeof(FLAC__int32)*4); + encoder->private_->integer_signal_mid_side[i] += 4; +#ifndef FLAC__INTEGER_ONLY_LIBRARY +#if 0 /* @@@ currently unused */ + if(encoder->protected_->max_lpc_order > 0) + ok = ok && FLAC__memory_alloc_aligned_real_array(new_blocksize+OVERREAD_, &encoder->private_->real_signal_mid_side_unaligned[i], &encoder->private_->real_signal_mid_side[i]); +#endif +#endif + } +#ifndef FLAC__INTEGER_ONLY_LIBRARY + if(ok && encoder->protected_->max_lpc_order > 0) { + for(i = 0; ok && i < encoder->protected_->num_apodizations; i++) + ok = ok && FLAC__memory_alloc_aligned_real_array(new_blocksize, &encoder->private_->window_unaligned[i], &encoder->private_->window[i]); + ok = ok && FLAC__memory_alloc_aligned_real_array(new_blocksize, &encoder->private_->windowed_signal_unaligned, &encoder->private_->windowed_signal); + } +#endif + for(channel = 0; ok && channel < encoder->protected_->channels; channel++) { + for(i = 0; ok && i < 2; i++) { + ok = ok && FLAC__memory_alloc_aligned_int32_array(new_blocksize, &encoder->private_->residual_workspace_unaligned[channel][i], &encoder->private_->residual_workspace[channel][i]); + } + } + for(channel = 0; ok && channel < 2; channel++) { + for(i = 0; ok && i < 2; i++) { + ok = ok && FLAC__memory_alloc_aligned_int32_array(new_blocksize, &encoder->private_->residual_workspace_mid_side_unaligned[channel][i], &encoder->private_->residual_workspace_mid_side[channel][i]); + } + } + /* the *2 is an approximation to the series 1 + 1/2 + 1/4 + ... that sums tree occupies in a flat array */ + /*@@@ new_blocksize*2 is too pessimistic, but to fix, we need smarter logic because a smaller new_blocksize can actually increase the # of partitions; would require moving this out into a separate function, then checking its capacity against the need of the current blocksize&min/max_partition_order (and maybe predictor order) */ + ok = ok && FLAC__memory_alloc_aligned_uint64_array(new_blocksize * 2, &encoder->private_->abs_residual_partition_sums_unaligned, &encoder->private_->abs_residual_partition_sums); + if(encoder->protected_->do_escape_coding) + ok = ok && FLAC__memory_alloc_aligned_unsigned_array(new_blocksize * 2, &encoder->private_->raw_bits_per_partition_unaligned, &encoder->private_->raw_bits_per_partition); + + /* now adjust the windows if the blocksize has changed */ +#ifndef FLAC__INTEGER_ONLY_LIBRARY + if(ok && new_blocksize != encoder->private_->input_capacity && encoder->protected_->max_lpc_order > 0) { + for(i = 0; ok && i < encoder->protected_->num_apodizations; i++) { + switch(encoder->protected_->apodizations[i].type) { + case FLAC__APODIZATION_BARTLETT: + FLAC__window_bartlett(encoder->private_->window[i], new_blocksize); + break; + case FLAC__APODIZATION_BARTLETT_HANN: + FLAC__window_bartlett_hann(encoder->private_->window[i], new_blocksize); + break; + case FLAC__APODIZATION_BLACKMAN: + FLAC__window_blackman(encoder->private_->window[i], new_blocksize); + break; + case FLAC__APODIZATION_BLACKMAN_HARRIS_4TERM_92DB_SIDELOBE: + FLAC__window_blackman_harris_4term_92db_sidelobe(encoder->private_->window[i], new_blocksize); + break; + case FLAC__APODIZATION_CONNES: + FLAC__window_connes(encoder->private_->window[i], new_blocksize); + break; + case FLAC__APODIZATION_FLATTOP: + FLAC__window_flattop(encoder->private_->window[i], new_blocksize); + break; + case FLAC__APODIZATION_GAUSS: + FLAC__window_gauss(encoder->private_->window[i], new_blocksize, encoder->protected_->apodizations[i].parameters.gauss.stddev); + break; + case FLAC__APODIZATION_HAMMING: + FLAC__window_hamming(encoder->private_->window[i], new_blocksize); + break; + case FLAC__APODIZATION_HANN: + FLAC__window_hann(encoder->private_->window[i], new_blocksize); + break; + case FLAC__APODIZATION_KAISER_BESSEL: + FLAC__window_kaiser_bessel(encoder->private_->window[i], new_blocksize); + break; + case FLAC__APODIZATION_NUTTALL: + FLAC__window_nuttall(encoder->private_->window[i], new_blocksize); + break; + case FLAC__APODIZATION_RECTANGLE: + FLAC__window_rectangle(encoder->private_->window[i], new_blocksize); + break; + case FLAC__APODIZATION_TRIANGLE: + FLAC__window_triangle(encoder->private_->window[i], new_blocksize); + break; + case FLAC__APODIZATION_TUKEY: + FLAC__window_tukey(encoder->private_->window[i], new_blocksize, encoder->protected_->apodizations[i].parameters.tukey.p); + break; + case FLAC__APODIZATION_WELCH: + FLAC__window_welch(encoder->private_->window[i], new_blocksize); + break; + default: + FLAC__ASSERT(0); + /* double protection */ + FLAC__window_hann(encoder->private_->window[i], new_blocksize); + break; + } + } + } +#endif + + if(ok) + encoder->private_->input_capacity = new_blocksize; + else + encoder->protected_->state = FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR; + + return ok; +} + +FLAC__bool write_bitbuffer_(FLAC__StreamEncoder *encoder, unsigned samples, FLAC__bool is_last_block) +{ + const FLAC__byte *buffer; + size_t bytes; + + FLAC__ASSERT(FLAC__bitwriter_is_byte_aligned(encoder->private_->frame)); + + if(!FLAC__bitwriter_get_buffer(encoder->private_->frame, &buffer, &bytes)) { + encoder->protected_->state = FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR; + return false; + } + + if(encoder->protected_->verify) { + encoder->private_->verify.output.data = buffer; + encoder->private_->verify.output.bytes = bytes; + if(encoder->private_->verify.state_hint == ENCODER_IN_MAGIC) { + encoder->private_->verify.needs_magic_hack = true; + } + else { + if(!FLAC__stream_decoder_process_single(encoder->private_->verify.decoder)) { + FLAC__bitwriter_release_buffer(encoder->private_->frame); + FLAC__bitwriter_clear(encoder->private_->frame); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_VERIFY_MISMATCH_IN_AUDIO_DATA) + encoder->protected_->state = FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR; + return false; + } + } + } + + if(write_frame_(encoder, buffer, bytes, samples, is_last_block) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK) { + FLAC__bitwriter_release_buffer(encoder->private_->frame); + FLAC__bitwriter_clear(encoder->private_->frame); + encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR; + return false; + } + + FLAC__bitwriter_release_buffer(encoder->private_->frame); + FLAC__bitwriter_clear(encoder->private_->frame); + + if(samples > 0) { + encoder->private_->streaminfo.data.stream_info.min_framesize = min(bytes, encoder->private_->streaminfo.data.stream_info.min_framesize); + encoder->private_->streaminfo.data.stream_info.max_framesize = max(bytes, encoder->private_->streaminfo.data.stream_info.max_framesize); + } + + return true; +} + +FLAC__StreamEncoderWriteStatus write_frame_(FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, FLAC__bool is_last_block) +{ + FLAC__StreamEncoderWriteStatus status; + FLAC__uint64 output_position = 0; + + /* FLAC__STREAM_ENCODER_TELL_STATUS_UNSUPPORTED just means we didn't get the offset; no error */ + if(encoder->private_->tell_callback && encoder->private_->tell_callback(encoder, &output_position, encoder->private_->client_data) == FLAC__STREAM_ENCODER_TELL_STATUS_ERROR) { + encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR; + return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR; + } + + /* + * Watch for the STREAMINFO block and first SEEKTABLE block to go by and store their offsets. + */ + if(samples == 0) { + FLAC__MetadataType type = (FLAC__MetadataType) (buffer[0] & 0x7f); + if(type == FLAC__METADATA_TYPE_STREAMINFO) + encoder->protected_->streaminfo_offset = output_position; + else if(type == FLAC__METADATA_TYPE_SEEKTABLE && encoder->protected_->seektable_offset == 0) + encoder->protected_->seektable_offset = output_position; + } + + /* + * Mark the current seek point if hit (if audio_offset == 0 that + * means we're still writing metadata and haven't hit the first + * frame yet) + */ + if(0 != encoder->private_->seek_table && encoder->protected_->audio_offset > 0 && encoder->private_->seek_table->num_points > 0) { + const unsigned blocksize = FLAC__stream_encoder_get_blocksize(encoder); + const FLAC__uint64 frame_first_sample = encoder->private_->samples_written; + const FLAC__uint64 frame_last_sample = frame_first_sample + (FLAC__uint64)blocksize - 1; + FLAC__uint64 test_sample; + unsigned i; + for(i = encoder->private_->first_seekpoint_to_check; i < encoder->private_->seek_table->num_points; i++) { + test_sample = encoder->private_->seek_table->points[i].sample_number; + if(test_sample > frame_last_sample) { + break; + } + else if(test_sample >= frame_first_sample) { + encoder->private_->seek_table->points[i].sample_number = frame_first_sample; + encoder->private_->seek_table->points[i].stream_offset = output_position - encoder->protected_->audio_offset; + encoder->private_->seek_table->points[i].frame_samples = blocksize; + encoder->private_->first_seekpoint_to_check++; + /* DO NOT: "break;" and here's why: + * The seektable template may contain more than one target + * sample for any given frame; we will keep looping, generating + * duplicate seekpoints for them, and we'll clean it up later, + * just before writing the seektable back to the metadata. + */ + } + else { + encoder->private_->first_seekpoint_to_check++; + } + } + } + +#if FLAC__HAS_OGG + if(encoder->private_->is_ogg) { + status = FLAC__ogg_encoder_aspect_write_callback_wrapper( + &encoder->protected_->ogg_encoder_aspect, + buffer, + bytes, + samples, + encoder->private_->current_frame_number, + is_last_block, + (FLAC__OggEncoderAspectWriteCallbackProxy)encoder->private_->write_callback, + encoder, + encoder->private_->client_data + ); + } + else +#endif + status = encoder->private_->write_callback(encoder, buffer, bytes, samples, encoder->private_->current_frame_number, encoder->private_->client_data); + + if(status == FLAC__STREAM_ENCODER_WRITE_STATUS_OK) { + encoder->private_->bytes_written += bytes; + encoder->private_->samples_written += samples; + /* we keep a high watermark on the number of frames written because + * when the encoder goes back to write metadata, 'current_frame' + * will drop back to 0. + */ + encoder->private_->frames_written = max(encoder->private_->frames_written, encoder->private_->current_frame_number+1); + } + else + encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR; + + return status; +} + +/* Gets called when the encoding process has finished so that we can update the STREAMINFO and SEEKTABLE blocks. */ +void update_metadata_(const FLAC__StreamEncoder *encoder) +{ + FLAC__byte b[max(6, FLAC__STREAM_METADATA_SEEKPOINT_LENGTH)]; + const FLAC__StreamMetadata *metadata = &encoder->private_->streaminfo; + const FLAC__uint64 samples = metadata->data.stream_info.total_samples; + const unsigned min_framesize = metadata->data.stream_info.min_framesize; + const unsigned max_framesize = metadata->data.stream_info.max_framesize; + const unsigned bps = metadata->data.stream_info.bits_per_sample; + FLAC__StreamEncoderSeekStatus seek_status; + + FLAC__ASSERT(metadata->type == FLAC__METADATA_TYPE_STREAMINFO); + + /* All this is based on intimate knowledge of the stream header + * layout, but a change to the header format that would break this + * would also break all streams encoded in the previous format. + */ + + /* + * Write MD5 signature + */ + { + const unsigned md5_offset = + FLAC__STREAM_METADATA_HEADER_LENGTH + + ( + FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN + + FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN + ) / 8; + + if((seek_status = encoder->private_->seek_callback(encoder, encoder->protected_->streaminfo_offset + md5_offset, encoder->private_->client_data)) != FLAC__STREAM_ENCODER_SEEK_STATUS_OK) { + if(seek_status == FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR) + encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR; + return; + } + if(encoder->private_->write_callback(encoder, metadata->data.stream_info.md5sum, 16, 0, 0, encoder->private_->client_data) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK) { + encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR; + return; + } + } + + /* + * Write total samples + */ + { + const unsigned total_samples_byte_offset = + FLAC__STREAM_METADATA_HEADER_LENGTH + + ( + FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN + + FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN + - 4 + ) / 8; + + b[0] = ((FLAC__byte)(bps-1) << 4) | (FLAC__byte)((samples >> 32) & 0x0F); + b[1] = (FLAC__byte)((samples >> 24) & 0xFF); + b[2] = (FLAC__byte)((samples >> 16) & 0xFF); + b[3] = (FLAC__byte)((samples >> 8) & 0xFF); + b[4] = (FLAC__byte)(samples & 0xFF); + if((seek_status = encoder->private_->seek_callback(encoder, encoder->protected_->streaminfo_offset + total_samples_byte_offset, encoder->private_->client_data)) != FLAC__STREAM_ENCODER_SEEK_STATUS_OK) { + if(seek_status == FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR) + encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR; + return; + } + if(encoder->private_->write_callback(encoder, b, 5, 0, 0, encoder->private_->client_data) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK) { + encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR; + return; + } + } + + /* + * Write min/max framesize + */ + { + const unsigned min_framesize_offset = + FLAC__STREAM_METADATA_HEADER_LENGTH + + ( + FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN + ) / 8; + + b[0] = (FLAC__byte)((min_framesize >> 16) & 0xFF); + b[1] = (FLAC__byte)((min_framesize >> 8) & 0xFF); + b[2] = (FLAC__byte)(min_framesize & 0xFF); + b[3] = (FLAC__byte)((max_framesize >> 16) & 0xFF); + b[4] = (FLAC__byte)((max_framesize >> 8) & 0xFF); + b[5] = (FLAC__byte)(max_framesize & 0xFF); + if((seek_status = encoder->private_->seek_callback(encoder, encoder->protected_->streaminfo_offset + min_framesize_offset, encoder->private_->client_data)) != FLAC__STREAM_ENCODER_SEEK_STATUS_OK) { + if(seek_status == FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR) + encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR; + return; + } + if(encoder->private_->write_callback(encoder, b, 6, 0, 0, encoder->private_->client_data) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK) { + encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR; + return; + } + } + + /* + * Write seektable + */ + if(0 != encoder->private_->seek_table && encoder->private_->seek_table->num_points > 0 && encoder->protected_->seektable_offset > 0) { + unsigned i; + + FLAC__format_seektable_sort(encoder->private_->seek_table); + + FLAC__ASSERT(FLAC__format_seektable_is_legal(encoder->private_->seek_table)); + + if((seek_status = encoder->private_->seek_callback(encoder, encoder->protected_->seektable_offset + FLAC__STREAM_METADATA_HEADER_LENGTH, encoder->private_->client_data)) != FLAC__STREAM_ENCODER_SEEK_STATUS_OK) { + if(seek_status == FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR) + encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR; + return; + } + + for(i = 0; i < encoder->private_->seek_table->num_points; i++) { + FLAC__uint64 xx; + unsigned x; + xx = encoder->private_->seek_table->points[i].sample_number; + b[7] = (FLAC__byte)xx; xx >>= 8; + b[6] = (FLAC__byte)xx; xx >>= 8; + b[5] = (FLAC__byte)xx; xx >>= 8; + b[4] = (FLAC__byte)xx; xx >>= 8; + b[3] = (FLAC__byte)xx; xx >>= 8; + b[2] = (FLAC__byte)xx; xx >>= 8; + b[1] = (FLAC__byte)xx; xx >>= 8; + b[0] = (FLAC__byte)xx; xx >>= 8; + xx = encoder->private_->seek_table->points[i].stream_offset; + b[15] = (FLAC__byte)xx; xx >>= 8; + b[14] = (FLAC__byte)xx; xx >>= 8; + b[13] = (FLAC__byte)xx; xx >>= 8; + b[12] = (FLAC__byte)xx; xx >>= 8; + b[11] = (FLAC__byte)xx; xx >>= 8; + b[10] = (FLAC__byte)xx; xx >>= 8; + b[9] = (FLAC__byte)xx; xx >>= 8; + b[8] = (FLAC__byte)xx; xx >>= 8; + x = encoder->private_->seek_table->points[i].frame_samples; + b[17] = (FLAC__byte)x; x >>= 8; + b[16] = (FLAC__byte)x; x >>= 8; + if(encoder->private_->write_callback(encoder, b, 18, 0, 0, encoder->private_->client_data) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK) { + encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR; + return; + } + } + } +} + +#if FLAC__HAS_OGG +/* Gets called when the encoding process has finished so that we can update the STREAMINFO and SEEKTABLE blocks. */ +void update_ogg_metadata_(FLAC__StreamEncoder *encoder) +{ + /* the # of bytes in the 1st packet that precede the STREAMINFO */ + static const unsigned FIRST_OGG_PACKET_STREAMINFO_PREFIX_LENGTH = + FLAC__OGG_MAPPING_PACKET_TYPE_LENGTH + + FLAC__OGG_MAPPING_MAGIC_LENGTH + + FLAC__OGG_MAPPING_VERSION_MAJOR_LENGTH + + FLAC__OGG_MAPPING_VERSION_MINOR_LENGTH + + FLAC__OGG_MAPPING_NUM_HEADERS_LENGTH + + FLAC__STREAM_SYNC_LENGTH + ; + FLAC__byte b[max(6, FLAC__STREAM_METADATA_SEEKPOINT_LENGTH)]; + const FLAC__StreamMetadata *metadata = &encoder->private_->streaminfo; + const FLAC__uint64 samples = metadata->data.stream_info.total_samples; + const unsigned min_framesize = metadata->data.stream_info.min_framesize; + const unsigned max_framesize = metadata->data.stream_info.max_framesize; + ogg_page page; + + FLAC__ASSERT(metadata->type == FLAC__METADATA_TYPE_STREAMINFO); + FLAC__ASSERT(0 != encoder->private_->seek_callback); + + /* Pre-check that client supports seeking, since we don't want the + * ogg_helper code to ever have to deal with this condition. + */ + if(encoder->private_->seek_callback(encoder, 0, encoder->private_->client_data) == FLAC__STREAM_ENCODER_SEEK_STATUS_UNSUPPORTED) + return; + + /* All this is based on intimate knowledge of the stream header + * layout, but a change to the header format that would break this + * would also break all streams encoded in the previous format. + */ + + /** + ** Write STREAMINFO stats + **/ + simple_ogg_page__init(&page); + if(!simple_ogg_page__get_at(encoder, encoder->protected_->streaminfo_offset, &page, encoder->private_->seek_callback, encoder->private_->read_callback, encoder->private_->client_data)) { + simple_ogg_page__clear(&page); + return; /* state already set */ + } + + /* + * Write MD5 signature + */ + { + const unsigned md5_offset = + FIRST_OGG_PACKET_STREAMINFO_PREFIX_LENGTH + + FLAC__STREAM_METADATA_HEADER_LENGTH + + ( + FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN + + FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN + ) / 8; + + if(md5_offset + 16 > (unsigned)page.body_len) { + encoder->protected_->state = FLAC__STREAM_ENCODER_OGG_ERROR; + simple_ogg_page__clear(&page); + return; + } + memcpy(page.body + md5_offset, metadata->data.stream_info.md5sum, 16); + } + + /* + * Write total samples + */ + { + const unsigned total_samples_byte_offset = + FIRST_OGG_PACKET_STREAMINFO_PREFIX_LENGTH + + FLAC__STREAM_METADATA_HEADER_LENGTH + + ( + FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN + + FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN + - 4 + ) / 8; + + if(total_samples_byte_offset + 5 > (unsigned)page.body_len) { + encoder->protected_->state = FLAC__STREAM_ENCODER_OGG_ERROR; + simple_ogg_page__clear(&page); + return; + } + b[0] = (FLAC__byte)page.body[total_samples_byte_offset] & 0xF0; + b[0] |= (FLAC__byte)((samples >> 32) & 0x0F); + b[1] = (FLAC__byte)((samples >> 24) & 0xFF); + b[2] = (FLAC__byte)((samples >> 16) & 0xFF); + b[3] = (FLAC__byte)((samples >> 8) & 0xFF); + b[4] = (FLAC__byte)(samples & 0xFF); + memcpy(page.body + total_samples_byte_offset, b, 5); + } + + /* + * Write min/max framesize + */ + { + const unsigned min_framesize_offset = + FIRST_OGG_PACKET_STREAMINFO_PREFIX_LENGTH + + FLAC__STREAM_METADATA_HEADER_LENGTH + + ( + FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN + ) / 8; + + if(min_framesize_offset + 6 > (unsigned)page.body_len) { + encoder->protected_->state = FLAC__STREAM_ENCODER_OGG_ERROR; + simple_ogg_page__clear(&page); + return; + } + b[0] = (FLAC__byte)((min_framesize >> 16) & 0xFF); + b[1] = (FLAC__byte)((min_framesize >> 8) & 0xFF); + b[2] = (FLAC__byte)(min_framesize & 0xFF); + b[3] = (FLAC__byte)((max_framesize >> 16) & 0xFF); + b[4] = (FLAC__byte)((max_framesize >> 8) & 0xFF); + b[5] = (FLAC__byte)(max_framesize & 0xFF); + memcpy(page.body + min_framesize_offset, b, 6); + } + if(!simple_ogg_page__set_at(encoder, encoder->protected_->streaminfo_offset, &page, encoder->private_->seek_callback, encoder->private_->write_callback, encoder->private_->client_data)) { + simple_ogg_page__clear(&page); + return; /* state already set */ + } + simple_ogg_page__clear(&page); + + /* + * Write seektable + */ + if(0 != encoder->private_->seek_table && encoder->private_->seek_table->num_points > 0 && encoder->protected_->seektable_offset > 0) { + unsigned i; + FLAC__byte *p; + + FLAC__format_seektable_sort(encoder->private_->seek_table); + + FLAC__ASSERT(FLAC__format_seektable_is_legal(encoder->private_->seek_table)); + + simple_ogg_page__init(&page); + if(!simple_ogg_page__get_at(encoder, encoder->protected_->seektable_offset, &page, encoder->private_->seek_callback, encoder->private_->read_callback, encoder->private_->client_data)) { + simple_ogg_page__clear(&page); + return; /* state already set */ + } + + if((FLAC__STREAM_METADATA_HEADER_LENGTH + 18*encoder->private_->seek_table->num_points) != (unsigned)page.body_len) { + encoder->protected_->state = FLAC__STREAM_ENCODER_OGG_ERROR; + simple_ogg_page__clear(&page); + return; + } + + for(i = 0, p = page.body + FLAC__STREAM_METADATA_HEADER_LENGTH; i < encoder->private_->seek_table->num_points; i++, p += 18) { + FLAC__uint64 xx; + unsigned x; + xx = encoder->private_->seek_table->points[i].sample_number; + b[7] = (FLAC__byte)xx; xx >>= 8; + b[6] = (FLAC__byte)xx; xx >>= 8; + b[5] = (FLAC__byte)xx; xx >>= 8; + b[4] = (FLAC__byte)xx; xx >>= 8; + b[3] = (FLAC__byte)xx; xx >>= 8; + b[2] = (FLAC__byte)xx; xx >>= 8; + b[1] = (FLAC__byte)xx; xx >>= 8; + b[0] = (FLAC__byte)xx; xx >>= 8; + xx = encoder->private_->seek_table->points[i].stream_offset; + b[15] = (FLAC__byte)xx; xx >>= 8; + b[14] = (FLAC__byte)xx; xx >>= 8; + b[13] = (FLAC__byte)xx; xx >>= 8; + b[12] = (FLAC__byte)xx; xx >>= 8; + b[11] = (FLAC__byte)xx; xx >>= 8; + b[10] = (FLAC__byte)xx; xx >>= 8; + b[9] = (FLAC__byte)xx; xx >>= 8; + b[8] = (FLAC__byte)xx; xx >>= 8; + x = encoder->private_->seek_table->points[i].frame_samples; + b[17] = (FLAC__byte)x; x >>= 8; + b[16] = (FLAC__byte)x; x >>= 8; + memcpy(p, b, 18); + } + + if(!simple_ogg_page__set_at(encoder, encoder->protected_->seektable_offset, &page, encoder->private_->seek_callback, encoder->private_->write_callback, encoder->private_->client_data)) { + simple_ogg_page__clear(&page); + return; /* state already set */ + } + simple_ogg_page__clear(&page); + } +} +#endif + +FLAC__bool process_frame_(FLAC__StreamEncoder *encoder, FLAC__bool is_fractional_block, FLAC__bool is_last_block) +{ + FLAC__uint16 crc; + FLAC__ASSERT(encoder->protected_->state == FLAC__STREAM_ENCODER_OK); + + /* + * Accumulate raw signal to the MD5 signature + */ + if(encoder->protected_->do_md5 && !FLAC__MD5Accumulate(&encoder->private_->md5context, (const FLAC__int32 * const *)encoder->private_->integer_signal, encoder->protected_->channels, encoder->protected_->blocksize, (encoder->protected_->bits_per_sample+7) / 8)) { + encoder->protected_->state = FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR; + return false; + } + + /* + * Process the frame header and subframes into the frame bitbuffer + */ + if(!process_subframes_(encoder, is_fractional_block)) { + /* the above function sets the state for us in case of an error */ + return false; + } + + /* + * Zero-pad the frame to a byte_boundary + */ + if(!FLAC__bitwriter_zero_pad_to_byte_boundary(encoder->private_->frame)) { + encoder->protected_->state = FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR; + return false; + } + + /* + * CRC-16 the whole thing + */ + FLAC__ASSERT(FLAC__bitwriter_is_byte_aligned(encoder->private_->frame)); + if( + !FLAC__bitwriter_get_write_crc16(encoder->private_->frame, &crc) || + !FLAC__bitwriter_write_raw_uint32(encoder->private_->frame, crc, FLAC__FRAME_FOOTER_CRC_LEN) + ) { + encoder->protected_->state = FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR; + return false; + } + + /* + * Write it + */ + if(!write_bitbuffer_(encoder, encoder->protected_->blocksize, is_last_block)) { + /* the above function sets the state for us in case of an error */ + return false; + } + + /* + * Get ready for the next frame + */ + encoder->private_->current_sample_number = 0; + encoder->private_->current_frame_number++; + encoder->private_->streaminfo.data.stream_info.total_samples += (FLAC__uint64)encoder->protected_->blocksize; + + return true; +} + +FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder, FLAC__bool is_fractional_block) +{ + FLAC__FrameHeader frame_header; + unsigned channel, min_partition_order = encoder->protected_->min_residual_partition_order, max_partition_order; + FLAC__bool do_independent, do_mid_side; + + /* + * Calculate the min,max Rice partition orders + */ + if(is_fractional_block) { + max_partition_order = 0; + } + else { + max_partition_order = FLAC__format_get_max_rice_partition_order_from_blocksize(encoder->protected_->blocksize); + max_partition_order = min(max_partition_order, encoder->protected_->max_residual_partition_order); + } + min_partition_order = min(min_partition_order, max_partition_order); + + /* + * Setup the frame + */ + frame_header.blocksize = encoder->protected_->blocksize; + frame_header.sample_rate = encoder->protected_->sample_rate; + frame_header.channels = encoder->protected_->channels; + frame_header.channel_assignment = FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT; /* the default unless the encoder determines otherwise */ + frame_header.bits_per_sample = encoder->protected_->bits_per_sample; + frame_header.number_type = FLAC__FRAME_NUMBER_TYPE_FRAME_NUMBER; + frame_header.number.frame_number = encoder->private_->current_frame_number; + + /* + * Figure out what channel assignments to try + */ + if(encoder->protected_->do_mid_side_stereo) { + if(encoder->protected_->loose_mid_side_stereo) { + if(encoder->private_->loose_mid_side_stereo_frame_count == 0) { + do_independent = true; + do_mid_side = true; + } + else { + do_independent = (encoder->private_->last_channel_assignment == FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT); + do_mid_side = !do_independent; + } + } + else { + do_independent = true; + do_mid_side = true; + } + } + else { + do_independent = true; + do_mid_side = false; + } + + FLAC__ASSERT(do_independent || do_mid_side); + + /* + * Check for wasted bits; set effective bps for each subframe + */ + if(do_independent) { + for(channel = 0; channel < encoder->protected_->channels; channel++) { + const unsigned w = get_wasted_bits_(encoder->private_->integer_signal[channel], encoder->protected_->blocksize); + encoder->private_->subframe_workspace[channel][0].wasted_bits = encoder->private_->subframe_workspace[channel][1].wasted_bits = w; + encoder->private_->subframe_bps[channel] = encoder->protected_->bits_per_sample - w; + } + } + if(do_mid_side) { + FLAC__ASSERT(encoder->protected_->channels == 2); + for(channel = 0; channel < 2; channel++) { + const unsigned w = get_wasted_bits_(encoder->private_->integer_signal_mid_side[channel], encoder->protected_->blocksize); + encoder->private_->subframe_workspace_mid_side[channel][0].wasted_bits = encoder->private_->subframe_workspace_mid_side[channel][1].wasted_bits = w; + encoder->private_->subframe_bps_mid_side[channel] = encoder->protected_->bits_per_sample - w + (channel==0? 0:1); + } + } + + /* + * First do a normal encoding pass of each independent channel + */ + if(do_independent) { + for(channel = 0; channel < encoder->protected_->channels; channel++) { + if(! + process_subframe_( + encoder, + min_partition_order, + max_partition_order, + &frame_header, + encoder->private_->subframe_bps[channel], + encoder->private_->integer_signal[channel], + encoder->private_->subframe_workspace_ptr[channel], + encoder->private_->partitioned_rice_contents_workspace_ptr[channel], + encoder->private_->residual_workspace[channel], + encoder->private_->best_subframe+channel, + encoder->private_->best_subframe_bits+channel + ) + ) + return false; + } + } + + /* + * Now do mid and side channels if requested + */ + if(do_mid_side) { + FLAC__ASSERT(encoder->protected_->channels == 2); + + for(channel = 0; channel < 2; channel++) { + if(! + process_subframe_( + encoder, + min_partition_order, + max_partition_order, + &frame_header, + encoder->private_->subframe_bps_mid_side[channel], + encoder->private_->integer_signal_mid_side[channel], + encoder->private_->subframe_workspace_ptr_mid_side[channel], + encoder->private_->partitioned_rice_contents_workspace_ptr_mid_side[channel], + encoder->private_->residual_workspace_mid_side[channel], + encoder->private_->best_subframe_mid_side+channel, + encoder->private_->best_subframe_bits_mid_side+channel + ) + ) + return false; + } + } + + /* + * Compose the frame bitbuffer + */ + if(do_mid_side) { + unsigned left_bps = 0, right_bps = 0; /* initialized only to prevent superfluous compiler warning */ + FLAC__Subframe *left_subframe = 0, *right_subframe = 0; /* initialized only to prevent superfluous compiler warning */ + FLAC__ChannelAssignment channel_assignment; + + FLAC__ASSERT(encoder->protected_->channels == 2); + + if(encoder->protected_->loose_mid_side_stereo && encoder->private_->loose_mid_side_stereo_frame_count > 0) { + channel_assignment = (encoder->private_->last_channel_assignment == FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT? FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT : FLAC__CHANNEL_ASSIGNMENT_MID_SIDE); + } + else { + unsigned bits[4]; /* WATCHOUT - indexed by FLAC__ChannelAssignment */ + unsigned min_bits; + int ca; + + FLAC__ASSERT(FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT == 0); + FLAC__ASSERT(FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE == 1); + FLAC__ASSERT(FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE == 2); + FLAC__ASSERT(FLAC__CHANNEL_ASSIGNMENT_MID_SIDE == 3); + FLAC__ASSERT(do_independent && do_mid_side); + + /* We have to figure out which channel assignent results in the smallest frame */ + bits[FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT] = encoder->private_->best_subframe_bits [0] + encoder->private_->best_subframe_bits [1]; + bits[FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE ] = encoder->private_->best_subframe_bits [0] + encoder->private_->best_subframe_bits_mid_side[1]; + bits[FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE ] = encoder->private_->best_subframe_bits [1] + encoder->private_->best_subframe_bits_mid_side[1]; + bits[FLAC__CHANNEL_ASSIGNMENT_MID_SIDE ] = encoder->private_->best_subframe_bits_mid_side[0] + encoder->private_->best_subframe_bits_mid_side[1]; + + channel_assignment = FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT; + min_bits = bits[channel_assignment]; + for(ca = 1; ca <= 3; ca++) { + if(bits[ca] < min_bits) { + min_bits = bits[ca]; + channel_assignment = (FLAC__ChannelAssignment)ca; + } + } + } + + frame_header.channel_assignment = channel_assignment; + + if(!FLAC__frame_add_header(&frame_header, encoder->private_->frame)) { + encoder->protected_->state = FLAC__STREAM_ENCODER_FRAMING_ERROR; + return false; + } + + switch(channel_assignment) { + case FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT: + left_subframe = &encoder->private_->subframe_workspace [0][encoder->private_->best_subframe [0]]; + right_subframe = &encoder->private_->subframe_workspace [1][encoder->private_->best_subframe [1]]; + break; + case FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE: + left_subframe = &encoder->private_->subframe_workspace [0][encoder->private_->best_subframe [0]]; + right_subframe = &encoder->private_->subframe_workspace_mid_side[1][encoder->private_->best_subframe_mid_side[1]]; + break; + case FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE: + left_subframe = &encoder->private_->subframe_workspace_mid_side[1][encoder->private_->best_subframe_mid_side[1]]; + right_subframe = &encoder->private_->subframe_workspace [1][encoder->private_->best_subframe [1]]; + break; + case FLAC__CHANNEL_ASSIGNMENT_MID_SIDE: + left_subframe = &encoder->private_->subframe_workspace_mid_side[0][encoder->private_->best_subframe_mid_side[0]]; + right_subframe = &encoder->private_->subframe_workspace_mid_side[1][encoder->private_->best_subframe_mid_side[1]]; + break; + default: + FLAC__ASSERT(0); + } + + switch(channel_assignment) { + case FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT: + left_bps = encoder->private_->subframe_bps [0]; + right_bps = encoder->private_->subframe_bps [1]; + break; + case FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE: + left_bps = encoder->private_->subframe_bps [0]; + right_bps = encoder->private_->subframe_bps_mid_side[1]; + break; + case FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE: + left_bps = encoder->private_->subframe_bps_mid_side[1]; + right_bps = encoder->private_->subframe_bps [1]; + break; + case FLAC__CHANNEL_ASSIGNMENT_MID_SIDE: + left_bps = encoder->private_->subframe_bps_mid_side[0]; + right_bps = encoder->private_->subframe_bps_mid_side[1]; + break; + default: + FLAC__ASSERT(0); + } + + /* note that encoder_add_subframe_ sets the state for us in case of an error */ + if(!add_subframe_(encoder, frame_header.blocksize, left_bps , left_subframe , encoder->private_->frame)) + return false; + if(!add_subframe_(encoder, frame_header.blocksize, right_bps, right_subframe, encoder->private_->frame)) + return false; + } + else { + if(!FLAC__frame_add_header(&frame_header, encoder->private_->frame)) { + encoder->protected_->state = FLAC__STREAM_ENCODER_FRAMING_ERROR; + return false; + } + + for(channel = 0; channel < encoder->protected_->channels; channel++) { + if(!add_subframe_(encoder, frame_header.blocksize, encoder->private_->subframe_bps[channel], &encoder->private_->subframe_workspace[channel][encoder->private_->best_subframe[channel]], encoder->private_->frame)) { + /* the above function sets the state for us in case of an error */ + return false; + } + } + } + + if(encoder->protected_->loose_mid_side_stereo) { + encoder->private_->loose_mid_side_stereo_frame_count++; + if(encoder->private_->loose_mid_side_stereo_frame_count >= encoder->private_->loose_mid_side_stereo_frames) + encoder->private_->loose_mid_side_stereo_frame_count = 0; + } + + encoder->private_->last_channel_assignment = frame_header.channel_assignment; + + return true; +} + +FLAC__bool process_subframe_( + FLAC__StreamEncoder *encoder, + unsigned min_partition_order, + unsigned max_partition_order, + const FLAC__FrameHeader *frame_header, + unsigned subframe_bps, + const FLAC__int32 integer_signal[], + FLAC__Subframe *subframe[2], + FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents[2], + FLAC__int32 *residual[2], + unsigned *best_subframe, + unsigned *best_bits +) +{ +#ifndef FLAC__INTEGER_ONLY_LIBRARY + FLAC__float fixed_residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]; +#else + FLAC__fixedpoint fixed_residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]; +#endif +#ifndef FLAC__INTEGER_ONLY_LIBRARY + FLAC__double lpc_residual_bits_per_sample; + FLAC__real autoc[FLAC__MAX_LPC_ORDER+1]; /* WATCHOUT: the size is important even though encoder->protected_->max_lpc_order might be less; some asm routines need all the space */ + FLAC__double lpc_error[FLAC__MAX_LPC_ORDER]; + unsigned min_lpc_order, max_lpc_order, lpc_order; + unsigned min_qlp_coeff_precision, max_qlp_coeff_precision, qlp_coeff_precision; +#endif + unsigned min_fixed_order, max_fixed_order, guess_fixed_order, fixed_order; + unsigned rice_parameter; + unsigned _candidate_bits, _best_bits; + unsigned _best_subframe; + /* only use RICE2 partitions if stream bps > 16 */ + const unsigned rice_parameter_limit = FLAC__stream_encoder_get_bits_per_sample(encoder) > 16? FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_ESCAPE_PARAMETER : FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER; + + FLAC__ASSERT(frame_header->blocksize > 0); + + /* verbatim subframe is the baseline against which we measure other compressed subframes */ + _best_subframe = 0; + if(encoder->private_->disable_verbatim_subframes && frame_header->blocksize >= FLAC__MAX_FIXED_ORDER) + _best_bits = UINT_MAX; + else + _best_bits = evaluate_verbatim_subframe_(encoder, integer_signal, frame_header->blocksize, subframe_bps, subframe[_best_subframe]); + + if(frame_header->blocksize >= FLAC__MAX_FIXED_ORDER) { + unsigned signal_is_constant = false; + guess_fixed_order = encoder->private_->local_fixed_compute_best_predictor(integer_signal+FLAC__MAX_FIXED_ORDER, frame_header->blocksize-FLAC__MAX_FIXED_ORDER, fixed_residual_bits_per_sample); + /* check for constant subframe */ + if( + !encoder->private_->disable_constant_subframes && +#ifndef FLAC__INTEGER_ONLY_LIBRARY + fixed_residual_bits_per_sample[1] == 0.0 +#else + fixed_residual_bits_per_sample[1] == FLAC__FP_ZERO +#endif + ) { + /* the above means it's possible all samples are the same value; now double-check it: */ + unsigned i; + signal_is_constant = true; + for(i = 1; i < frame_header->blocksize; i++) { + if(integer_signal[0] != integer_signal[i]) { + signal_is_constant = false; + break; + } + } + } + if(signal_is_constant) { + _candidate_bits = evaluate_constant_subframe_(encoder, integer_signal[0], frame_header->blocksize, subframe_bps, subframe[!_best_subframe]); + if(_candidate_bits < _best_bits) { + _best_subframe = !_best_subframe; + _best_bits = _candidate_bits; + } + } + else { + if(!encoder->private_->disable_fixed_subframes || (encoder->protected_->max_lpc_order == 0 && _best_bits == UINT_MAX)) { + /* encode fixed */ + if(encoder->protected_->do_exhaustive_model_search) { + min_fixed_order = 0; + max_fixed_order = FLAC__MAX_FIXED_ORDER; + } + else { + min_fixed_order = max_fixed_order = guess_fixed_order; + } + if(max_fixed_order >= frame_header->blocksize) + max_fixed_order = frame_header->blocksize - 1; + for(fixed_order = min_fixed_order; fixed_order <= max_fixed_order; fixed_order++) { +#ifndef FLAC__INTEGER_ONLY_LIBRARY + if(fixed_residual_bits_per_sample[fixed_order] >= (FLAC__float)subframe_bps) + continue; /* don't even try */ + rice_parameter = (fixed_residual_bits_per_sample[fixed_order] > 0.0)? (unsigned)(fixed_residual_bits_per_sample[fixed_order]+0.5) : 0; /* 0.5 is for rounding */ +#else + if(FLAC__fixedpoint_trunc(fixed_residual_bits_per_sample[fixed_order]) >= (int)subframe_bps) + continue; /* don't even try */ + rice_parameter = (fixed_residual_bits_per_sample[fixed_order] > FLAC__FP_ZERO)? (unsigned)FLAC__fixedpoint_trunc(fixed_residual_bits_per_sample[fixed_order]+FLAC__FP_ONE_HALF) : 0; /* 0.5 is for rounding */ +#endif + rice_parameter++; /* to account for the signed->unsigned conversion during rice coding */ + if(rice_parameter >= rice_parameter_limit) { +#ifdef DEBUG_VERBOSE + fprintf(stderr, "clipping rice_parameter (%u -> %u) @0\n", rice_parameter, rice_parameter_limit - 1); +#endif + rice_parameter = rice_parameter_limit - 1; + } + _candidate_bits = + evaluate_fixed_subframe_( + encoder, + integer_signal, + residual[!_best_subframe], + encoder->private_->abs_residual_partition_sums, + encoder->private_->raw_bits_per_partition, + frame_header->blocksize, + subframe_bps, + fixed_order, + rice_parameter, + rice_parameter_limit, + min_partition_order, + max_partition_order, + encoder->protected_->do_escape_coding, + encoder->protected_->rice_parameter_search_dist, + subframe[!_best_subframe], + partitioned_rice_contents[!_best_subframe] + ); + if(_candidate_bits < _best_bits) { + _best_subframe = !_best_subframe; + _best_bits = _candidate_bits; + } + } + } + +#ifndef FLAC__INTEGER_ONLY_LIBRARY + /* encode lpc */ + if(encoder->protected_->max_lpc_order > 0) { + if(encoder->protected_->max_lpc_order >= frame_header->blocksize) + max_lpc_order = frame_header->blocksize-1; + else + max_lpc_order = encoder->protected_->max_lpc_order; + if(max_lpc_order > 0) { + unsigned a; + for (a = 0; a < encoder->protected_->num_apodizations; a++) { + FLAC__lpc_window_data(integer_signal, encoder->private_->window[a], encoder->private_->windowed_signal, frame_header->blocksize); + encoder->private_->local_lpc_compute_autocorrelation(encoder->private_->windowed_signal, frame_header->blocksize, max_lpc_order+1, autoc); + /* if autoc[0] == 0.0, the signal is constant and we usually won't get here, but it can happen */ + if(autoc[0] != 0.0) { + FLAC__lpc_compute_lp_coefficients(autoc, &max_lpc_order, encoder->private_->lp_coeff, lpc_error); + if(encoder->protected_->do_exhaustive_model_search) { + min_lpc_order = 1; + } + else { + const unsigned guess_lpc_order = + FLAC__lpc_compute_best_order( + lpc_error, + max_lpc_order, + frame_header->blocksize, + subframe_bps + ( + encoder->protected_->do_qlp_coeff_prec_search? + FLAC__MIN_QLP_COEFF_PRECISION : /* have to guess; use the min possible size to avoid accidentally favoring lower orders */ + encoder->protected_->qlp_coeff_precision + ) + ); + min_lpc_order = max_lpc_order = guess_lpc_order; + } + if(max_lpc_order >= frame_header->blocksize) + max_lpc_order = frame_header->blocksize - 1; + for(lpc_order = min_lpc_order; lpc_order <= max_lpc_order; lpc_order++) { + lpc_residual_bits_per_sample = FLAC__lpc_compute_expected_bits_per_residual_sample(lpc_error[lpc_order-1], frame_header->blocksize-lpc_order); + if(lpc_residual_bits_per_sample >= (FLAC__double)subframe_bps) + continue; /* don't even try */ + rice_parameter = (lpc_residual_bits_per_sample > 0.0)? (unsigned)(lpc_residual_bits_per_sample+0.5) : 0; /* 0.5 is for rounding */ + rice_parameter++; /* to account for the signed->unsigned conversion during rice coding */ + if(rice_parameter >= rice_parameter_limit) { +#ifdef DEBUG_VERBOSE + fprintf(stderr, "clipping rice_parameter (%u -> %u) @1\n", rice_parameter, rice_parameter_limit - 1); +#endif + rice_parameter = rice_parameter_limit - 1; + } + if(encoder->protected_->do_qlp_coeff_prec_search) { + min_qlp_coeff_precision = FLAC__MIN_QLP_COEFF_PRECISION; + /* try to ensure a 32-bit datapath throughout for 16bps(+1bps for side channel) or less */ + if(subframe_bps <= 17) { + max_qlp_coeff_precision = min(32 - subframe_bps - lpc_order, FLAC__MAX_QLP_COEFF_PRECISION); + max_qlp_coeff_precision = max(max_qlp_coeff_precision, min_qlp_coeff_precision); + } + else + max_qlp_coeff_precision = FLAC__MAX_QLP_COEFF_PRECISION; + } + else { + min_qlp_coeff_precision = max_qlp_coeff_precision = encoder->protected_->qlp_coeff_precision; + } + for(qlp_coeff_precision = min_qlp_coeff_precision; qlp_coeff_precision <= max_qlp_coeff_precision; qlp_coeff_precision++) { + _candidate_bits = + evaluate_lpc_subframe_( + encoder, + integer_signal, + residual[!_best_subframe], + encoder->private_->abs_residual_partition_sums, + encoder->private_->raw_bits_per_partition, + encoder->private_->lp_coeff[lpc_order-1], + frame_header->blocksize, + subframe_bps, + lpc_order, + qlp_coeff_precision, + rice_parameter, + rice_parameter_limit, + min_partition_order, + max_partition_order, + encoder->protected_->do_escape_coding, + encoder->protected_->rice_parameter_search_dist, + subframe[!_best_subframe], + partitioned_rice_contents[!_best_subframe] + ); + if(_candidate_bits > 0) { /* if == 0, there was a problem quantizing the lpcoeffs */ + if(_candidate_bits < _best_bits) { + _best_subframe = !_best_subframe; + _best_bits = _candidate_bits; + } + } + } + } + } + } + } + } +#endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */ + } + } + + /* under rare circumstances this can happen when all but lpc subframe types are disabled: */ + if(_best_bits == UINT_MAX) { + FLAC__ASSERT(_best_subframe == 0); + _best_bits = evaluate_verbatim_subframe_(encoder, integer_signal, frame_header->blocksize, subframe_bps, subframe[_best_subframe]); + } + + *best_subframe = _best_subframe; + *best_bits = _best_bits; + + return true; +} + +FLAC__bool add_subframe_( + FLAC__StreamEncoder *encoder, + unsigned blocksize, + unsigned subframe_bps, + const FLAC__Subframe *subframe, + FLAC__BitWriter *frame +) +{ + switch(subframe->type) { + case FLAC__SUBFRAME_TYPE_CONSTANT: + if(!FLAC__subframe_add_constant(&(subframe->data.constant), subframe_bps, subframe->wasted_bits, frame)) { + encoder->protected_->state = FLAC__STREAM_ENCODER_FRAMING_ERROR; + return false; + } + break; + case FLAC__SUBFRAME_TYPE_FIXED: + if(!FLAC__subframe_add_fixed(&(subframe->data.fixed), blocksize - subframe->data.fixed.order, subframe_bps, subframe->wasted_bits, frame)) { + encoder->protected_->state = FLAC__STREAM_ENCODER_FRAMING_ERROR; + return false; + } + break; + case FLAC__SUBFRAME_TYPE_LPC: + if(!FLAC__subframe_add_lpc(&(subframe->data.lpc), blocksize - subframe->data.lpc.order, subframe_bps, subframe->wasted_bits, frame)) { + encoder->protected_->state = FLAC__STREAM_ENCODER_FRAMING_ERROR; + return false; + } + break; + case FLAC__SUBFRAME_TYPE_VERBATIM: + if(!FLAC__subframe_add_verbatim(&(subframe->data.verbatim), blocksize, subframe_bps, subframe->wasted_bits, frame)) { + encoder->protected_->state = FLAC__STREAM_ENCODER_FRAMING_ERROR; + return false; + } + break; + default: + FLAC__ASSERT(0); + } + + return true; +} + +#define SPOTCHECK_ESTIMATE 0 +#if SPOTCHECK_ESTIMATE +static void spotcheck_subframe_estimate_( + FLAC__StreamEncoder *encoder, + unsigned blocksize, + unsigned subframe_bps, + const FLAC__Subframe *subframe, + unsigned estimate +) +{ + FLAC__bool ret; + FLAC__BitWriter *frame = FLAC__bitwriter_new(); + if(frame == 0) { + fprintf(stderr, "EST: can't allocate frame\n"); + return; + } + if(!FLAC__bitwriter_init(frame)) { + fprintf(stderr, "EST: can't init frame\n"); + return; + } + ret = add_subframe_(encoder, blocksize, subframe_bps, subframe, frame); + FLAC__ASSERT(ret); + { + const unsigned actual = FLAC__bitwriter_get_input_bits_unconsumed(frame); + if(estimate != actual) + fprintf(stderr, "EST: bad, frame#%u sub#%%d type=%8s est=%u, actual=%u, delta=%d\n", encoder->private_->current_frame_number, FLAC__SubframeTypeString[subframe->type], estimate, actual, (int)actual-(int)estimate); + } + FLAC__bitwriter_delete(frame); +} +#endif + +unsigned evaluate_constant_subframe_( + FLAC__StreamEncoder *encoder, + const FLAC__int32 signal, + unsigned blocksize, + unsigned subframe_bps, + FLAC__Subframe *subframe +) +{ + unsigned estimate; + subframe->type = FLAC__SUBFRAME_TYPE_CONSTANT; + subframe->data.constant.value = signal; + + estimate = FLAC__SUBFRAME_ZERO_PAD_LEN + FLAC__SUBFRAME_TYPE_LEN + FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN + subframe->wasted_bits + subframe_bps; + +#if SPOTCHECK_ESTIMATE + spotcheck_subframe_estimate_(encoder, blocksize, subframe_bps, subframe, estimate); +#else + (void)encoder, (void)blocksize; +#endif + + return estimate; +} + +unsigned evaluate_fixed_subframe_( + FLAC__StreamEncoder *encoder, + const FLAC__int32 signal[], + FLAC__int32 residual[], + FLAC__uint64 abs_residual_partition_sums[], + unsigned raw_bits_per_partition[], + unsigned blocksize, + unsigned subframe_bps, + unsigned order, + unsigned rice_parameter, + unsigned rice_parameter_limit, + unsigned min_partition_order, + unsigned max_partition_order, + FLAC__bool do_escape_coding, + unsigned rice_parameter_search_dist, + FLAC__Subframe *subframe, + FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents +) +{ + unsigned i, residual_bits, estimate; + const unsigned residual_samples = blocksize - order; + + FLAC__fixed_compute_residual(signal+order, residual_samples, order, residual); + + subframe->type = FLAC__SUBFRAME_TYPE_FIXED; + + subframe->data.fixed.entropy_coding_method.type = FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE; + subframe->data.fixed.entropy_coding_method.data.partitioned_rice.contents = partitioned_rice_contents; + subframe->data.fixed.residual = residual; + + residual_bits = + find_best_partition_order_( + encoder->private_, + residual, + abs_residual_partition_sums, + raw_bits_per_partition, + residual_samples, + order, + rice_parameter, + rice_parameter_limit, + min_partition_order, + max_partition_order, + subframe_bps, + do_escape_coding, + rice_parameter_search_dist, + &subframe->data.fixed.entropy_coding_method + ); + + subframe->data.fixed.order = order; + for(i = 0; i < order; i++) + subframe->data.fixed.warmup[i] = signal[i]; + + estimate = FLAC__SUBFRAME_ZERO_PAD_LEN + FLAC__SUBFRAME_TYPE_LEN + FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN + subframe->wasted_bits + (order * subframe_bps) + residual_bits; + +#if SPOTCHECK_ESTIMATE + spotcheck_subframe_estimate_(encoder, blocksize, subframe_bps, subframe, estimate); +#endif + + return estimate; +} + +#ifndef FLAC__INTEGER_ONLY_LIBRARY +unsigned evaluate_lpc_subframe_( + FLAC__StreamEncoder *encoder, + const FLAC__int32 signal[], + FLAC__int32 residual[], + FLAC__uint64 abs_residual_partition_sums[], + unsigned raw_bits_per_partition[], + const FLAC__real lp_coeff[], + unsigned blocksize, + unsigned subframe_bps, + unsigned order, + unsigned qlp_coeff_precision, + unsigned rice_parameter, + unsigned rice_parameter_limit, + unsigned min_partition_order, + unsigned max_partition_order, + FLAC__bool do_escape_coding, + unsigned rice_parameter_search_dist, + FLAC__Subframe *subframe, + FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents +) +{ + FLAC__int32 qlp_coeff[FLAC__MAX_LPC_ORDER]; + unsigned i, residual_bits, estimate; + int quantization, ret; + const unsigned residual_samples = blocksize - order; + + /* try to keep qlp coeff precision such that only 32-bit math is required for decode of <=16bps streams */ + if(subframe_bps <= 16) { + FLAC__ASSERT(order > 0); + FLAC__ASSERT(order <= FLAC__MAX_LPC_ORDER); + qlp_coeff_precision = min(qlp_coeff_precision, 32 - subframe_bps - FLAC__bitmath_ilog2(order)); + } + + ret = FLAC__lpc_quantize_coefficients(lp_coeff, order, qlp_coeff_precision, qlp_coeff, &quantization); + if(ret != 0) + return 0; /* this is a hack to indicate to the caller that we can't do lp at this order on this subframe */ + + if(subframe_bps + qlp_coeff_precision + FLAC__bitmath_ilog2(order) <= 32) + if(subframe_bps <= 16 && qlp_coeff_precision <= 16) + encoder->private_->local_lpc_compute_residual_from_qlp_coefficients_16bit(signal+order, residual_samples, qlp_coeff, order, quantization, residual); + else + encoder->private_->local_lpc_compute_residual_from_qlp_coefficients(signal+order, residual_samples, qlp_coeff, order, quantization, residual); + else + encoder->private_->local_lpc_compute_residual_from_qlp_coefficients_64bit(signal+order, residual_samples, qlp_coeff, order, quantization, residual); + + subframe->type = FLAC__SUBFRAME_TYPE_LPC; + + subframe->data.lpc.entropy_coding_method.type = FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE; + subframe->data.lpc.entropy_coding_method.data.partitioned_rice.contents = partitioned_rice_contents; + subframe->data.lpc.residual = residual; + + residual_bits = + find_best_partition_order_( + encoder->private_, + residual, + abs_residual_partition_sums, + raw_bits_per_partition, + residual_samples, + order, + rice_parameter, + rice_parameter_limit, + min_partition_order, + max_partition_order, + subframe_bps, + do_escape_coding, + rice_parameter_search_dist, + &subframe->data.lpc.entropy_coding_method + ); + + subframe->data.lpc.order = order; + subframe->data.lpc.qlp_coeff_precision = qlp_coeff_precision; + subframe->data.lpc.quantization_level = quantization; + memcpy(subframe->data.lpc.qlp_coeff, qlp_coeff, sizeof(FLAC__int32)*FLAC__MAX_LPC_ORDER); + for(i = 0; i < order; i++) + subframe->data.lpc.warmup[i] = signal[i]; + + estimate = FLAC__SUBFRAME_ZERO_PAD_LEN + FLAC__SUBFRAME_TYPE_LEN + FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN + subframe->wasted_bits + FLAC__SUBFRAME_LPC_QLP_COEFF_PRECISION_LEN + FLAC__SUBFRAME_LPC_QLP_SHIFT_LEN + (order * (qlp_coeff_precision + subframe_bps)) + residual_bits; + +#if SPOTCHECK_ESTIMATE + spotcheck_subframe_estimate_(encoder, blocksize, subframe_bps, subframe, estimate); +#endif + + return estimate; +} +#endif + +unsigned evaluate_verbatim_subframe_( + FLAC__StreamEncoder *encoder, + const FLAC__int32 signal[], + unsigned blocksize, + unsigned subframe_bps, + FLAC__Subframe *subframe +) +{ + unsigned estimate; + + subframe->type = FLAC__SUBFRAME_TYPE_VERBATIM; + + subframe->data.verbatim.data = signal; + + estimate = FLAC__SUBFRAME_ZERO_PAD_LEN + FLAC__SUBFRAME_TYPE_LEN + FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN + subframe->wasted_bits + (blocksize * subframe_bps); + +#if SPOTCHECK_ESTIMATE + spotcheck_subframe_estimate_(encoder, blocksize, subframe_bps, subframe, estimate); +#else + (void)encoder; +#endif + + return estimate; +} + +unsigned find_best_partition_order_( + FLAC__StreamEncoderPrivate *private_, + const FLAC__int32 residual[], + FLAC__uint64 abs_residual_partition_sums[], + unsigned raw_bits_per_partition[], + unsigned residual_samples, + unsigned predictor_order, + unsigned rice_parameter, + unsigned rice_parameter_limit, + unsigned min_partition_order, + unsigned max_partition_order, + unsigned bps, + FLAC__bool do_escape_coding, + unsigned rice_parameter_search_dist, + FLAC__EntropyCodingMethod *best_ecm +) +{ + unsigned residual_bits, best_residual_bits = 0; + unsigned best_parameters_index = 0; + unsigned best_partition_order = 0; + const unsigned blocksize = residual_samples + predictor_order; + + max_partition_order = FLAC__format_get_max_rice_partition_order_from_blocksize_limited_max_and_predictor_order(max_partition_order, blocksize, predictor_order); + min_partition_order = min(min_partition_order, max_partition_order); + + precompute_partition_info_sums_(residual, abs_residual_partition_sums, residual_samples, predictor_order, min_partition_order, max_partition_order, bps); + + if(do_escape_coding) + precompute_partition_info_escapes_(residual, raw_bits_per_partition, residual_samples, predictor_order, min_partition_order, max_partition_order); + + { + int partition_order; + unsigned sum; + + for(partition_order = (int)max_partition_order, sum = 0; partition_order >= (int)min_partition_order; partition_order--) { + if(! + set_partitioned_rice_( +#ifdef EXACT_RICE_BITS_CALCULATION + residual, +#endif + abs_residual_partition_sums+sum, + raw_bits_per_partition+sum, + residual_samples, + predictor_order, + rice_parameter, + rice_parameter_limit, + rice_parameter_search_dist, + (unsigned)partition_order, + do_escape_coding, + &private_->partitioned_rice_contents_extra[!best_parameters_index], + &residual_bits + ) + ) + { + FLAC__ASSERT(best_residual_bits != 0); + break; + } + sum += 1u << partition_order; + if(best_residual_bits == 0 || residual_bits < best_residual_bits) { + best_residual_bits = residual_bits; + best_parameters_index = !best_parameters_index; + best_partition_order = partition_order; + } + } + } + + best_ecm->data.partitioned_rice.order = best_partition_order; + + { + /* + * We are allowed to de-const the pointer based on our special + * knowledge; it is const to the outside world. + */ + FLAC__EntropyCodingMethod_PartitionedRiceContents* prc = (FLAC__EntropyCodingMethod_PartitionedRiceContents*)best_ecm->data.partitioned_rice.contents; + unsigned partition; + + /* save best parameters and raw_bits */ + FLAC__format_entropy_coding_method_partitioned_rice_contents_ensure_size(prc, max(6, best_partition_order)); + memcpy(prc->parameters, private_->partitioned_rice_contents_extra[best_parameters_index].parameters, sizeof(unsigned)*(1<<(best_partition_order))); + if(do_escape_coding) + memcpy(prc->raw_bits, private_->partitioned_rice_contents_extra[best_parameters_index].raw_bits, sizeof(unsigned)*(1<<(best_partition_order))); + /* + * Now need to check if the type should be changed to + * FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2 based on the + * size of the rice parameters. + */ + for(partition = 0; partition < (1u<parameters[partition] >= FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER) { + best_ecm->type = FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2; + break; + } + } + } + + return best_residual_bits; +} + +#if defined(FLAC__CPU_IA32) && !defined FLAC__NO_ASM && defined FLAC__HAS_NASM +extern void precompute_partition_info_sums_32bit_asm_ia32_( + const FLAC__int32 residual[], + FLAC__uint64 abs_residual_partition_sums[], + unsigned blocksize, + unsigned predictor_order, + unsigned min_partition_order, + unsigned max_partition_order +); +#endif + +void precompute_partition_info_sums_( + const FLAC__int32 residual[], + FLAC__uint64 abs_residual_partition_sums[], + unsigned residual_samples, + unsigned predictor_order, + unsigned min_partition_order, + unsigned max_partition_order, + unsigned bps +) +{ + const unsigned default_partition_samples = (residual_samples + predictor_order) >> max_partition_order; + unsigned partitions = 1u << max_partition_order; + + FLAC__ASSERT(default_partition_samples > predictor_order); + +#if defined(FLAC__CPU_IA32) && !defined FLAC__NO_ASM && defined FLAC__HAS_NASM + /* slightly pessimistic but still catches all common cases */ + /* WATCHOUT: "+ bps" is an assumption that the average residual magnitude will not be more than "bps" bits */ + if(FLAC__bitmath_ilog2(default_partition_samples) + bps < 32) { + precompute_partition_info_sums_32bit_asm_ia32_(residual, abs_residual_partition_sums, residual_samples + predictor_order, predictor_order, min_partition_order, max_partition_order); + return; + } +#endif + + /* first do max_partition_order */ + { + unsigned partition, residual_sample, end = (unsigned)(-(int)predictor_order); + /* slightly pessimistic but still catches all common cases */ + /* WATCHOUT: "+ bps" is an assumption that the average residual magnitude will not be more than "bps" bits */ + if(FLAC__bitmath_ilog2(default_partition_samples) + bps < 32) { + FLAC__uint32 abs_residual_partition_sum; + + for(partition = residual_sample = 0; partition < partitions; partition++) { + end += default_partition_samples; + abs_residual_partition_sum = 0; + for( ; residual_sample < end; residual_sample++) + abs_residual_partition_sum += abs(residual[residual_sample]); /* abs(INT_MIN) is undefined, but if the residual is INT_MIN we have bigger problems */ + abs_residual_partition_sums[partition] = abs_residual_partition_sum; + } + } + else { /* have to pessimistically use 64 bits for accumulator */ + FLAC__uint64 abs_residual_partition_sum; + + for(partition = residual_sample = 0; partition < partitions; partition++) { + end += default_partition_samples; + abs_residual_partition_sum = 0; + for( ; residual_sample < end; residual_sample++) + abs_residual_partition_sum += abs(residual[residual_sample]); /* abs(INT_MIN) is undefined, but if the residual is INT_MIN we have bigger problems */ + abs_residual_partition_sums[partition] = abs_residual_partition_sum; + } + } + } + + /* now merge partitions for lower orders */ + { + unsigned from_partition = 0, to_partition = partitions; + int partition_order; + for(partition_order = (int)max_partition_order - 1; partition_order >= (int)min_partition_order; partition_order--) { + unsigned i; + partitions >>= 1; + for(i = 0; i < partitions; i++) { + abs_residual_partition_sums[to_partition++] = + abs_residual_partition_sums[from_partition ] + + abs_residual_partition_sums[from_partition+1]; + from_partition += 2; + } + } + } +} + +void precompute_partition_info_escapes_( + const FLAC__int32 residual[], + unsigned raw_bits_per_partition[], + unsigned residual_samples, + unsigned predictor_order, + unsigned min_partition_order, + unsigned max_partition_order +) +{ + int partition_order; + unsigned from_partition, to_partition = 0; + const unsigned blocksize = residual_samples + predictor_order; + + /* first do max_partition_order */ + for(partition_order = (int)max_partition_order; partition_order >= 0; partition_order--) { + FLAC__int32 r; + FLAC__uint32 rmax; + unsigned partition, partition_sample, partition_samples, residual_sample; + const unsigned partitions = 1u << partition_order; + const unsigned default_partition_samples = blocksize >> partition_order; + + FLAC__ASSERT(default_partition_samples > predictor_order); + + for(partition = residual_sample = 0; partition < partitions; partition++) { + partition_samples = default_partition_samples; + if(partition == 0) + partition_samples -= predictor_order; + rmax = 0; + for(partition_sample = 0; partition_sample < partition_samples; partition_sample++) { + r = residual[residual_sample++]; + /* OPT: maybe faster: rmax |= r ^ (r>>31) */ + if(r < 0) + rmax |= ~r; + else + rmax |= r; + } + /* now we know all residual values are in the range [-rmax-1,rmax] */ + raw_bits_per_partition[partition] = rmax? FLAC__bitmath_ilog2(rmax) + 2 : 1; + } + to_partition = partitions; + break; /*@@@ yuck, should remove the 'for' loop instead */ + } + + /* now merge partitions for lower orders */ + for(from_partition = 0, --partition_order; partition_order >= (int)min_partition_order; partition_order--) { + unsigned m; + unsigned i; + const unsigned partitions = 1u << partition_order; + for(i = 0; i < partitions; i++) { + m = raw_bits_per_partition[from_partition]; + from_partition++; + raw_bits_per_partition[to_partition] = max(m, raw_bits_per_partition[from_partition]); + from_partition++; + to_partition++; + } + } +} + +#ifdef EXACT_RICE_BITS_CALCULATION +static FLaC__INLINE unsigned count_rice_bits_in_partition_( + const unsigned rice_parameter, + const unsigned partition_samples, + const FLAC__int32 *residual +) +{ + unsigned i, partition_bits = + FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN + /* actually could end up being FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN but err on side of 16bps */ + (1+rice_parameter) * partition_samples /* 1 for unary stop bit + rice_parameter for the binary portion */ + ; + for(i = 0; i < partition_samples; i++) + partition_bits += ( (FLAC__uint32)((residual[i]<<1)^(residual[i]>>31)) >> rice_parameter ); + return partition_bits; +} +#else +static FLaC__INLINE unsigned count_rice_bits_in_partition_( + const unsigned rice_parameter, + const unsigned partition_samples, + const FLAC__uint64 abs_residual_partition_sum +) +{ + return + FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN + /* actually could end up being FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN but err on side of 16bps */ + (1+rice_parameter) * partition_samples + /* 1 for unary stop bit + rice_parameter for the binary portion */ + ( + rice_parameter? + (unsigned)(abs_residual_partition_sum >> (rice_parameter-1)) /* rice_parameter-1 because the real coder sign-folds instead of using a sign bit */ + : (unsigned)(abs_residual_partition_sum << 1) /* can't shift by negative number, so reverse */ + ) + - (partition_samples >> 1) + /* -(partition_samples>>1) to subtract out extra contributions to the abs_residual_partition_sum. + * The actual number of bits used is closer to the sum(for all i in the partition) of abs(residual[i])>>(rice_parameter-1) + * By using the abs_residual_partition sum, we also add in bits in the LSBs that would normally be shifted out. + * So the subtraction term tries to guess how many extra bits were contributed. + * If the LSBs are randomly distributed, this should average to 0.5 extra bits per sample. + */ + ; +} +#endif + +FLAC__bool set_partitioned_rice_( +#ifdef EXACT_RICE_BITS_CALCULATION + const FLAC__int32 residual[], +#endif + const FLAC__uint64 abs_residual_partition_sums[], + const unsigned raw_bits_per_partition[], + const unsigned residual_samples, + const unsigned predictor_order, + const unsigned suggested_rice_parameter, + const unsigned rice_parameter_limit, + const unsigned rice_parameter_search_dist, + const unsigned partition_order, + const FLAC__bool search_for_escapes, + FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents, + unsigned *bits +) +{ + unsigned rice_parameter, partition_bits; + unsigned best_partition_bits, best_rice_parameter = 0; + unsigned bits_ = FLAC__ENTROPY_CODING_METHOD_TYPE_LEN + FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN; + unsigned *parameters, *raw_bits; +#ifdef ENABLE_RICE_PARAMETER_SEARCH + unsigned min_rice_parameter, max_rice_parameter; +#else + (void)rice_parameter_search_dist; +#endif + + FLAC__ASSERT(suggested_rice_parameter < FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_ESCAPE_PARAMETER); + FLAC__ASSERT(rice_parameter_limit <= FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_ESCAPE_PARAMETER); + + FLAC__format_entropy_coding_method_partitioned_rice_contents_ensure_size(partitioned_rice_contents, max(6, partition_order)); + parameters = partitioned_rice_contents->parameters; + raw_bits = partitioned_rice_contents->raw_bits; + + if(partition_order == 0) { + best_partition_bits = (unsigned)(-1); +#ifdef ENABLE_RICE_PARAMETER_SEARCH + if(rice_parameter_search_dist) { + if(suggested_rice_parameter < rice_parameter_search_dist) + min_rice_parameter = 0; + else + min_rice_parameter = suggested_rice_parameter - rice_parameter_search_dist; + max_rice_parameter = suggested_rice_parameter + rice_parameter_search_dist; + if(max_rice_parameter >= rice_parameter_limit) { +#ifdef DEBUG_VERBOSE + fprintf(stderr, "clipping rice_parameter (%u -> %u) @5\n", max_rice_parameter, rice_parameter_limit - 1); +#endif + max_rice_parameter = rice_parameter_limit - 1; + } + } + else + min_rice_parameter = max_rice_parameter = suggested_rice_parameter; + + for(rice_parameter = min_rice_parameter; rice_parameter <= max_rice_parameter; rice_parameter++) { +#else + rice_parameter = suggested_rice_parameter; +#endif +#ifdef EXACT_RICE_BITS_CALCULATION + partition_bits = count_rice_bits_in_partition_(rice_parameter, residual_samples, residual); +#else + partition_bits = count_rice_bits_in_partition_(rice_parameter, residual_samples, abs_residual_partition_sums[0]); +#endif + if(partition_bits < best_partition_bits) { + best_rice_parameter = rice_parameter; + best_partition_bits = partition_bits; + } +#ifdef ENABLE_RICE_PARAMETER_SEARCH + } +#endif + if(search_for_escapes) { + partition_bits = FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN + FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_RAW_LEN + raw_bits_per_partition[0] * residual_samples; + if(partition_bits <= best_partition_bits) { + raw_bits[0] = raw_bits_per_partition[0]; + best_rice_parameter = 0; /* will be converted to appropriate escape parameter later */ + best_partition_bits = partition_bits; + } + else + raw_bits[0] = 0; + } + parameters[0] = best_rice_parameter; + bits_ += best_partition_bits; + } + else { + unsigned partition, residual_sample; + unsigned partition_samples; + FLAC__uint64 mean, k; + const unsigned partitions = 1u << partition_order; + for(partition = residual_sample = 0; partition < partitions; partition++) { + partition_samples = (residual_samples+predictor_order) >> partition_order; + if(partition == 0) { + if(partition_samples <= predictor_order) + return false; + else + partition_samples -= predictor_order; + } + mean = abs_residual_partition_sums[partition]; + /* we are basically calculating the size in bits of the + * average residual magnitude in the partition: + * rice_parameter = floor(log2(mean/partition_samples)) + * 'mean' is not a good name for the variable, it is + * actually the sum of magnitudes of all residual values + * in the partition, so the actual mean is + * mean/partition_samples + */ + for(rice_parameter = 0, k = partition_samples; k < mean; rice_parameter++, k <<= 1) + ; + if(rice_parameter >= rice_parameter_limit) { +#ifdef DEBUG_VERBOSE + fprintf(stderr, "clipping rice_parameter (%u -> %u) @6\n", rice_parameter, rice_parameter_limit - 1); +#endif + rice_parameter = rice_parameter_limit - 1; + } + + best_partition_bits = (unsigned)(-1); +#ifdef ENABLE_RICE_PARAMETER_SEARCH + if(rice_parameter_search_dist) { + if(rice_parameter < rice_parameter_search_dist) + min_rice_parameter = 0; + else + min_rice_parameter = rice_parameter - rice_parameter_search_dist; + max_rice_parameter = rice_parameter + rice_parameter_search_dist; + if(max_rice_parameter >= rice_parameter_limit) { +#ifdef DEBUG_VERBOSE + fprintf(stderr, "clipping rice_parameter (%u -> %u) @7\n", max_rice_parameter, rice_parameter_limit - 1); +#endif + max_rice_parameter = rice_parameter_limit - 1; + } + } + else + min_rice_parameter = max_rice_parameter = rice_parameter; + + for(rice_parameter = min_rice_parameter; rice_parameter <= max_rice_parameter; rice_parameter++) { +#endif +#ifdef EXACT_RICE_BITS_CALCULATION + partition_bits = count_rice_bits_in_partition_(rice_parameter, partition_samples, residual+residual_sample); +#else + partition_bits = count_rice_bits_in_partition_(rice_parameter, partition_samples, abs_residual_partition_sums[partition]); +#endif + if(partition_bits < best_partition_bits) { + best_rice_parameter = rice_parameter; + best_partition_bits = partition_bits; + } +#ifdef ENABLE_RICE_PARAMETER_SEARCH + } +#endif + if(search_for_escapes) { + partition_bits = FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN + FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_RAW_LEN + raw_bits_per_partition[partition] * partition_samples; + if(partition_bits <= best_partition_bits) { + raw_bits[partition] = raw_bits_per_partition[partition]; + best_rice_parameter = 0; /* will be converted to appropriate escape parameter later */ + best_partition_bits = partition_bits; + } + else + raw_bits[partition] = 0; + } + parameters[partition] = best_rice_parameter; + bits_ += best_partition_bits; + residual_sample += partition_samples; + } + } + + *bits = bits_; + return true; +} + +unsigned get_wasted_bits_(FLAC__int32 signal[], unsigned samples) +{ + unsigned i, shift; + FLAC__int32 x = 0; + + for(i = 0; i < samples && !(x&1); i++) + x |= signal[i]; + + if(x == 0) { + shift = 0; + } + else { + for(shift = 0; !(x&1); shift++) + x >>= 1; + } + + if(shift > 0) { + for(i = 0; i < samples; i++) + signal[i] >>= shift; + } + + return shift; +} + +void append_to_verify_fifo_(verify_input_fifo *fifo, const FLAC__int32 * const input[], unsigned input_offset, unsigned channels, unsigned wide_samples) +{ + unsigned channel; + + for(channel = 0; channel < channels; channel++) + memcpy(&fifo->data[channel][fifo->tail], &input[channel][input_offset], sizeof(FLAC__int32) * wide_samples); + + fifo->tail += wide_samples; + + FLAC__ASSERT(fifo->tail <= fifo->size); +} + +void append_to_verify_fifo_interleaved_(verify_input_fifo *fifo, const FLAC__int32 input[], unsigned input_offset, unsigned channels, unsigned wide_samples) +{ + unsigned channel; + unsigned sample, wide_sample; + unsigned tail = fifo->tail; + + sample = input_offset * channels; + for(wide_sample = 0; wide_sample < wide_samples; wide_sample++) { + for(channel = 0; channel < channels; channel++) + fifo->data[channel][tail] = input[sample++]; + tail++; + } + fifo->tail = tail; + + FLAC__ASSERT(fifo->tail <= fifo->size); +} + +FLAC__StreamDecoderReadStatus verify_read_callback_(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data) +{ + FLAC__StreamEncoder *encoder = (FLAC__StreamEncoder*)client_data; + const size_t encoded_bytes = encoder->private_->verify.output.bytes; + (void)decoder; + + if(encoder->private_->verify.needs_magic_hack) { + FLAC__ASSERT(*bytes >= FLAC__STREAM_SYNC_LENGTH); + *bytes = FLAC__STREAM_SYNC_LENGTH; + memcpy(buffer, FLAC__STREAM_SYNC_STRING, *bytes); + encoder->private_->verify.needs_magic_hack = false; + } + else { + if(encoded_bytes == 0) { + /* + * If we get here, a FIFO underflow has occurred, + * which means there is a bug somewhere. + */ + FLAC__ASSERT(0); + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + } + else if(encoded_bytes < *bytes) + *bytes = encoded_bytes; + memcpy(buffer, encoder->private_->verify.output.data, *bytes); + encoder->private_->verify.output.data += *bytes; + encoder->private_->verify.output.bytes -= *bytes; + } + + return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; +} + +FLAC__StreamDecoderWriteStatus verify_write_callback_(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data) +{ + FLAC__StreamEncoder *encoder = (FLAC__StreamEncoder *)client_data; + unsigned channel; + const unsigned channels = frame->header.channels; + const unsigned blocksize = frame->header.blocksize; + const unsigned bytes_per_block = sizeof(FLAC__int32) * blocksize; + + (void)decoder; + + for(channel = 0; channel < channels; channel++) { + if(0 != memcmp(buffer[channel], encoder->private_->verify.input_fifo.data[channel], bytes_per_block)) { + unsigned i, sample = 0; + FLAC__int32 expect = 0, got = 0; + + for(i = 0; i < blocksize; i++) { + if(buffer[channel][i] != encoder->private_->verify.input_fifo.data[channel][i]) { + sample = i; + expect = (FLAC__int32)encoder->private_->verify.input_fifo.data[channel][i]; + got = (FLAC__int32)buffer[channel][i]; + break; + } + } + FLAC__ASSERT(i < blocksize); + FLAC__ASSERT(frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER); + encoder->private_->verify.error_stats.absolute_sample = frame->header.number.sample_number + sample; + encoder->private_->verify.error_stats.frame_number = (unsigned)(frame->header.number.sample_number / blocksize); + encoder->private_->verify.error_stats.channel = channel; + encoder->private_->verify.error_stats.sample = sample; + encoder->private_->verify.error_stats.expected = expect; + encoder->private_->verify.error_stats.got = got; + encoder->protected_->state = FLAC__STREAM_ENCODER_VERIFY_MISMATCH_IN_AUDIO_DATA; + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + } + /* dequeue the frame from the fifo */ + encoder->private_->verify.input_fifo.tail -= blocksize; + FLAC__ASSERT(encoder->private_->verify.input_fifo.tail <= OVERREAD_); + for(channel = 0; channel < channels; channel++) + memmove(&encoder->private_->verify.input_fifo.data[channel][0], &encoder->private_->verify.input_fifo.data[channel][blocksize], encoder->private_->verify.input_fifo.tail * sizeof(encoder->private_->verify.input_fifo.data[0][0])); + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; +} + +void verify_metadata_callback_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) +{ + (void)decoder, (void)metadata, (void)client_data; +} + +void verify_error_callback_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) +{ + FLAC__StreamEncoder *encoder = (FLAC__StreamEncoder*)client_data; + (void)decoder, (void)status; + encoder->protected_->state = FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR; +} + +FLAC__StreamEncoderReadStatus file_read_callback_enc(const FLAC__StreamEncoder *encoder, FLAC__byte buffer[], size_t *bytes, void *client_data) +{ + (void)client_data; + + *bytes = fread(buffer, 1, *bytes, encoder->private_->file); + if (*bytes == 0) { + if (feof(encoder->private_->file)) + return FLAC__STREAM_ENCODER_READ_STATUS_END_OF_STREAM; + else if (ferror(encoder->private_->file)) + return FLAC__STREAM_ENCODER_READ_STATUS_ABORT; + } + return FLAC__STREAM_ENCODER_READ_STATUS_CONTINUE; +} + +FLAC__StreamEncoderSeekStatus file_seek_callback_enc(const FLAC__StreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data) +{ + (void)client_data; + + if(fseeko(encoder->private_->file, (off_t)absolute_byte_offset, SEEK_SET) < 0) + return FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR; + else + return FLAC__STREAM_ENCODER_SEEK_STATUS_OK; +} + +FLAC__StreamEncoderTellStatus file_tell_callback_enc(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_byte_offset, void *client_data) +{ + off_t offset; + + (void)client_data; + + offset = ftello(encoder->private_->file); + + if(offset < 0) { + return FLAC__STREAM_ENCODER_TELL_STATUS_ERROR; + } + else { + *absolute_byte_offset = (FLAC__uint64)offset; + return FLAC__STREAM_ENCODER_TELL_STATUS_OK; + } +} + +#ifdef FLAC__VALGRIND_TESTING +static size_t local__fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) +{ + size_t ret = fwrite(ptr, size, nmemb, stream); + if(!ferror(stream)) + fflush(stream); + return ret; +} +#else +#define local__fwrite fwrite +#endif + +FLAC__StreamEncoderWriteStatus file_write_callback_(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame, void *client_data) +{ + (void)client_data, (void)current_frame; + + if(local__fwrite(buffer, sizeof(FLAC__byte), bytes, encoder->private_->file) == bytes) { + FLAC__bool call_it = 0 != encoder->private_->progress_callback && ( +#if FLAC__HAS_OGG + /* We would like to be able to use 'samples > 0' in the + * clause here but currently because of the nature of our + * Ogg writing implementation, 'samples' is always 0 (see + * ogg_encoder_aspect.c). The downside is extra progress + * callbacks. + */ + encoder->private_->is_ogg? true : +#endif + samples > 0 + ); + if(call_it) { + /* NOTE: We have to add +bytes, +samples, and +1 to the stats + * because at this point in the callback chain, the stats + * have not been updated. Only after we return and control + * gets back to write_frame_() are the stats updated + */ + encoder->private_->progress_callback(encoder, encoder->private_->bytes_written+bytes, encoder->private_->samples_written+samples, encoder->private_->frames_written+(samples?1:0), encoder->private_->total_frames_estimate, encoder->private_->client_data); + } + return FLAC__STREAM_ENCODER_WRITE_STATUS_OK; + } + else + return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR; +} + +/* + * This will forcibly set stdout to binary mode (for OSes that require it) + */ +FILE *get_binary_stdout_(void) +{ + /* if something breaks here it is probably due to the presence or + * absence of an underscore before the identifiers 'setmode', + * 'fileno', and/or 'O_BINARY'; check your system header files. + */ +#if defined _MSC_VER || defined __MINGW32__ + _setmode(_fileno(stdout), _O_BINARY); +#elif defined __CYGWIN__ + /* almost certainly not needed for any modern Cygwin, but let's be safe... */ + setmode(_fileno(stdout), _O_BINARY); +#elif defined __EMX__ + setmode(fileno(stdout), O_BINARY); +#endif + + return stdout; +} + +#endif +/********* End of inlined file: stream_encoder.c *********/ + +/********* Start of inlined file: stream_encoder_framing.c *********/ + +/********* Start of inlined file: juce_FlacHeader.h *********/ +// This file is included at the start of each FLAC .c file, just to do a few housekeeping +// tasks.. + +#define VERSION "1.2.1" + +#define FLAC__NO_DLL 1 + +#ifdef _MSC_VER + #pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4312) +#endif + +#if ! (defined (_WIN32) || defined (_WIN64) || defined (LINUX)) + #define FLAC__SYS_DARWIN 1 +#endif +/********* End of inlined file: juce_FlacHeader.h *********/ + +#if JUCE_USE_FLAC + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include /* for strlen() */ + +#ifdef max +#undef max +#endif +#define max(x,y) ((x)>(y)?(x):(y)) + +static FLAC__bool add_entropy_coding_method_(FLAC__BitWriter *bw, const FLAC__EntropyCodingMethod *method); +static FLAC__bool add_residual_partitioned_rice_(FLAC__BitWriter *bw, const FLAC__int32 residual[], const unsigned residual_samples, const unsigned predictor_order, const unsigned rice_parameters[], const unsigned raw_bits[], const unsigned partition_order, const FLAC__bool is_extended); + +FLAC__bool FLAC__add_metadata_block(const FLAC__StreamMetadata *metadata, FLAC__BitWriter *bw) +{ + unsigned i, j; + const unsigned vendor_string_length = (unsigned)strlen(FLAC__VENDOR_STRING); + + if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->is_last, FLAC__STREAM_METADATA_IS_LAST_LEN)) + return false; + + if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->type, FLAC__STREAM_METADATA_TYPE_LEN)) + return false; + + /* + * First, for VORBIS_COMMENTs, adjust the length to reflect our vendor string + */ + i = metadata->length; + if(metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) { + FLAC__ASSERT(metadata->data.vorbis_comment.vendor_string.length == 0 || 0 != metadata->data.vorbis_comment.vendor_string.entry); + i -= metadata->data.vorbis_comment.vendor_string.length; + i += vendor_string_length; + } + FLAC__ASSERT(i < (1u << FLAC__STREAM_METADATA_LENGTH_LEN)); + if(!FLAC__bitwriter_write_raw_uint32(bw, i, FLAC__STREAM_METADATA_LENGTH_LEN)) + return false; + + switch(metadata->type) { + case FLAC__METADATA_TYPE_STREAMINFO: + FLAC__ASSERT(metadata->data.stream_info.min_blocksize < (1u << FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN)); + if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.stream_info.min_blocksize, FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN)) + return false; + FLAC__ASSERT(metadata->data.stream_info.max_blocksize < (1u << FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN)); + if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.stream_info.max_blocksize, FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN)) + return false; + FLAC__ASSERT(metadata->data.stream_info.min_framesize < (1u << FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN)); + if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.stream_info.min_framesize, FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN)) + return false; + FLAC__ASSERT(metadata->data.stream_info.max_framesize < (1u << FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN)); + if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.stream_info.max_framesize, FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN)) + return false; + FLAC__ASSERT(FLAC__format_sample_rate_is_valid(metadata->data.stream_info.sample_rate)); + if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.stream_info.sample_rate, FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN)) + return false; + FLAC__ASSERT(metadata->data.stream_info.channels > 0); + FLAC__ASSERT(metadata->data.stream_info.channels <= (1u << FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN)); + if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.stream_info.channels-1, FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN)) + return false; + FLAC__ASSERT(metadata->data.stream_info.bits_per_sample > 0); + FLAC__ASSERT(metadata->data.stream_info.bits_per_sample <= (1u << FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN)); + if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.stream_info.bits_per_sample-1, FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN)) + return false; + if(!FLAC__bitwriter_write_raw_uint64(bw, metadata->data.stream_info.total_samples, FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN)) + return false; + if(!FLAC__bitwriter_write_byte_block(bw, metadata->data.stream_info.md5sum, 16)) + return false; + break; + case FLAC__METADATA_TYPE_PADDING: + if(!FLAC__bitwriter_write_zeroes(bw, metadata->length * 8)) + return false; + break; + case FLAC__METADATA_TYPE_APPLICATION: + if(!FLAC__bitwriter_write_byte_block(bw, metadata->data.application.id, FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8)) + return false; + if(!FLAC__bitwriter_write_byte_block(bw, metadata->data.application.data, metadata->length - (FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8))) + return false; + break; + case FLAC__METADATA_TYPE_SEEKTABLE: + for(i = 0; i < metadata->data.seek_table.num_points; i++) { + if(!FLAC__bitwriter_write_raw_uint64(bw, metadata->data.seek_table.points[i].sample_number, FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN)) + return false; + if(!FLAC__bitwriter_write_raw_uint64(bw, metadata->data.seek_table.points[i].stream_offset, FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN)) + return false; + if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.seek_table.points[i].frame_samples, FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN)) + return false; + } + break; + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + if(!FLAC__bitwriter_write_raw_uint32_little_endian(bw, vendor_string_length)) + return false; + if(!FLAC__bitwriter_write_byte_block(bw, (const FLAC__byte*)FLAC__VENDOR_STRING, vendor_string_length)) + return false; + if(!FLAC__bitwriter_write_raw_uint32_little_endian(bw, metadata->data.vorbis_comment.num_comments)) + return false; + for(i = 0; i < metadata->data.vorbis_comment.num_comments; i++) { + if(!FLAC__bitwriter_write_raw_uint32_little_endian(bw, metadata->data.vorbis_comment.comments[i].length)) + return false; + if(!FLAC__bitwriter_write_byte_block(bw, metadata->data.vorbis_comment.comments[i].entry, metadata->data.vorbis_comment.comments[i].length)) + return false; + } + break; + case FLAC__METADATA_TYPE_CUESHEET: + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN % 8 == 0); + if(!FLAC__bitwriter_write_byte_block(bw, (const FLAC__byte*)metadata->data.cue_sheet.media_catalog_number, FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN/8)) + return false; + if(!FLAC__bitwriter_write_raw_uint64(bw, metadata->data.cue_sheet.lead_in, FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN)) + return false; + if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.cue_sheet.is_cd? 1 : 0, FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN)) + return false; + if(!FLAC__bitwriter_write_zeroes(bw, FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN)) + return false; + if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.cue_sheet.num_tracks, FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN)) + return false; + for(i = 0; i < metadata->data.cue_sheet.num_tracks; i++) { + const FLAC__StreamMetadata_CueSheet_Track *track = metadata->data.cue_sheet.tracks + i; + + if(!FLAC__bitwriter_write_raw_uint64(bw, track->offset, FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN)) + return false; + if(!FLAC__bitwriter_write_raw_uint32(bw, track->number, FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN)) + return false; + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN % 8 == 0); + if(!FLAC__bitwriter_write_byte_block(bw, (const FLAC__byte*)track->isrc, FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN/8)) + return false; + if(!FLAC__bitwriter_write_raw_uint32(bw, track->type, FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN)) + return false; + if(!FLAC__bitwriter_write_raw_uint32(bw, track->pre_emphasis, FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN)) + return false; + if(!FLAC__bitwriter_write_zeroes(bw, FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN)) + return false; + if(!FLAC__bitwriter_write_raw_uint32(bw, track->num_indices, FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN)) + return false; + for(j = 0; j < track->num_indices; j++) { + const FLAC__StreamMetadata_CueSheet_Index *index = track->indices + j; + + if(!FLAC__bitwriter_write_raw_uint64(bw, index->offset, FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN)) + return false; + if(!FLAC__bitwriter_write_raw_uint32(bw, index->number, FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN)) + return false; + if(!FLAC__bitwriter_write_zeroes(bw, FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN)) + return false; + } + } + break; + case FLAC__METADATA_TYPE_PICTURE: + { + size_t len; + if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.picture.type, FLAC__STREAM_METADATA_PICTURE_TYPE_LEN)) + return false; + len = strlen(metadata->data.picture.mime_type); + if(!FLAC__bitwriter_write_raw_uint32(bw, len, FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN)) + return false; + if(!FLAC__bitwriter_write_byte_block(bw, (const FLAC__byte*)metadata->data.picture.mime_type, len)) + return false; + len = strlen((const char *)metadata->data.picture.description); + if(!FLAC__bitwriter_write_raw_uint32(bw, len, FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN)) + return false; + if(!FLAC__bitwriter_write_byte_block(bw, metadata->data.picture.description, len)) + return false; + if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.picture.width, FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN)) + return false; + if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.picture.height, FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN)) + return false; + if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.picture.depth, FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN)) + return false; + if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.picture.colors, FLAC__STREAM_METADATA_PICTURE_COLORS_LEN)) + return false; + if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.picture.data_length, FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN)) + return false; + if(!FLAC__bitwriter_write_byte_block(bw, metadata->data.picture.data, metadata->data.picture.data_length)) + return false; + } + break; + default: + if(!FLAC__bitwriter_write_byte_block(bw, metadata->data.unknown.data, metadata->length)) + return false; + break; + } + + FLAC__ASSERT(FLAC__bitwriter_is_byte_aligned(bw)); + return true; +} + +FLAC__bool FLAC__frame_add_header(const FLAC__FrameHeader *header, FLAC__BitWriter *bw) +{ + unsigned u, blocksize_hint, sample_rate_hint; + FLAC__byte crc; + + FLAC__ASSERT(FLAC__bitwriter_is_byte_aligned(bw)); + + if(!FLAC__bitwriter_write_raw_uint32(bw, FLAC__FRAME_HEADER_SYNC, FLAC__FRAME_HEADER_SYNC_LEN)) + return false; + + if(!FLAC__bitwriter_write_raw_uint32(bw, 0, FLAC__FRAME_HEADER_RESERVED_LEN)) + return false; + + if(!FLAC__bitwriter_write_raw_uint32(bw, (header->number_type == FLAC__FRAME_NUMBER_TYPE_FRAME_NUMBER)? 0 : 1, FLAC__FRAME_HEADER_BLOCKING_STRATEGY_LEN)) + return false; + + FLAC__ASSERT(header->blocksize > 0 && header->blocksize <= FLAC__MAX_BLOCK_SIZE); + /* when this assertion holds true, any legal blocksize can be expressed in the frame header */ + FLAC__ASSERT(FLAC__MAX_BLOCK_SIZE <= 65535u); + blocksize_hint = 0; + switch(header->blocksize) { + case 192: u = 1; break; + case 576: u = 2; break; + case 1152: u = 3; break; + case 2304: u = 4; break; + case 4608: u = 5; break; + case 256: u = 8; break; + case 512: u = 9; break; + case 1024: u = 10; break; + case 2048: u = 11; break; + case 4096: u = 12; break; + case 8192: u = 13; break; + case 16384: u = 14; break; + case 32768: u = 15; break; + default: + if(header->blocksize <= 0x100) + blocksize_hint = u = 6; + else + blocksize_hint = u = 7; + break; + } + if(!FLAC__bitwriter_write_raw_uint32(bw, u, FLAC__FRAME_HEADER_BLOCK_SIZE_LEN)) + return false; + + FLAC__ASSERT(FLAC__format_sample_rate_is_valid(header->sample_rate)); + sample_rate_hint = 0; + switch(header->sample_rate) { + case 88200: u = 1; break; + case 176400: u = 2; break; + case 192000: u = 3; break; + case 8000: u = 4; break; + case 16000: u = 5; break; + case 22050: u = 6; break; + case 24000: u = 7; break; + case 32000: u = 8; break; + case 44100: u = 9; break; + case 48000: u = 10; break; + case 96000: u = 11; break; + default: + if(header->sample_rate <= 255000 && header->sample_rate % 1000 == 0) + sample_rate_hint = u = 12; + else if(header->sample_rate % 10 == 0) + sample_rate_hint = u = 14; + else if(header->sample_rate <= 0xffff) + sample_rate_hint = u = 13; + else + u = 0; + break; + } + if(!FLAC__bitwriter_write_raw_uint32(bw, u, FLAC__FRAME_HEADER_SAMPLE_RATE_LEN)) + return false; + + FLAC__ASSERT(header->channels > 0 && header->channels <= (1u << FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN) && header->channels <= FLAC__MAX_CHANNELS); + switch(header->channel_assignment) { + case FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT: + u = header->channels - 1; + break; + case FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE: + FLAC__ASSERT(header->channels == 2); + u = 8; + break; + case FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE: + FLAC__ASSERT(header->channels == 2); + u = 9; + break; + case FLAC__CHANNEL_ASSIGNMENT_MID_SIDE: + FLAC__ASSERT(header->channels == 2); + u = 10; + break; + default: + FLAC__ASSERT(0); + } + if(!FLAC__bitwriter_write_raw_uint32(bw, u, FLAC__FRAME_HEADER_CHANNEL_ASSIGNMENT_LEN)) + return false; + + FLAC__ASSERT(header->bits_per_sample > 0 && header->bits_per_sample <= (1u << FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN)); + switch(header->bits_per_sample) { + case 8 : u = 1; break; + case 12: u = 2; break; + case 16: u = 4; break; + case 20: u = 5; break; + case 24: u = 6; break; + default: u = 0; break; + } + if(!FLAC__bitwriter_write_raw_uint32(bw, u, FLAC__FRAME_HEADER_BITS_PER_SAMPLE_LEN)) + return false; + + if(!FLAC__bitwriter_write_raw_uint32(bw, 0, FLAC__FRAME_HEADER_ZERO_PAD_LEN)) + return false; + + if(header->number_type == FLAC__FRAME_NUMBER_TYPE_FRAME_NUMBER) { + if(!FLAC__bitwriter_write_utf8_uint32(bw, header->number.frame_number)) + return false; + } + else { + if(!FLAC__bitwriter_write_utf8_uint64(bw, header->number.sample_number)) + return false; + } + + if(blocksize_hint) + if(!FLAC__bitwriter_write_raw_uint32(bw, header->blocksize-1, (blocksize_hint==6)? 8:16)) + return false; + + switch(sample_rate_hint) { + case 12: + if(!FLAC__bitwriter_write_raw_uint32(bw, header->sample_rate / 1000, 8)) + return false; + break; + case 13: + if(!FLAC__bitwriter_write_raw_uint32(bw, header->sample_rate, 16)) + return false; + break; + case 14: + if(!FLAC__bitwriter_write_raw_uint32(bw, header->sample_rate / 10, 16)) + return false; + break; + } + + /* write the CRC */ + if(!FLAC__bitwriter_get_write_crc8(bw, &crc)) + return false; + if(!FLAC__bitwriter_write_raw_uint32(bw, crc, FLAC__FRAME_HEADER_CRC_LEN)) + return false; + + return true; +} + +FLAC__bool FLAC__subframe_add_constant(const FLAC__Subframe_Constant *subframe, unsigned subframe_bps, unsigned wasted_bits, FLAC__BitWriter *bw) +{ + FLAC__bool ok; + + ok = + FLAC__bitwriter_write_raw_uint32(bw, FLAC__SUBFRAME_TYPE_CONSTANT_BYTE_ALIGNED_MASK | (wasted_bits? 1:0), FLAC__SUBFRAME_ZERO_PAD_LEN + FLAC__SUBFRAME_TYPE_LEN + FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN) && + (wasted_bits? FLAC__bitwriter_write_unary_unsigned(bw, wasted_bits-1) : true) && + FLAC__bitwriter_write_raw_int32(bw, subframe->value, subframe_bps) + ; + + return ok; +} + +FLAC__bool FLAC__subframe_add_fixed(const FLAC__Subframe_Fixed *subframe, unsigned residual_samples, unsigned subframe_bps, unsigned wasted_bits, FLAC__BitWriter *bw) +{ + unsigned i; + + if(!FLAC__bitwriter_write_raw_uint32(bw, FLAC__SUBFRAME_TYPE_FIXED_BYTE_ALIGNED_MASK | (subframe->order<<1) | (wasted_bits? 1:0), FLAC__SUBFRAME_ZERO_PAD_LEN + FLAC__SUBFRAME_TYPE_LEN + FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN)) + return false; + if(wasted_bits) + if(!FLAC__bitwriter_write_unary_unsigned(bw, wasted_bits-1)) + return false; + + for(i = 0; i < subframe->order; i++) + if(!FLAC__bitwriter_write_raw_int32(bw, subframe->warmup[i], subframe_bps)) + return false; + + if(!add_entropy_coding_method_(bw, &subframe->entropy_coding_method)) + return false; + switch(subframe->entropy_coding_method.type) { + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE: + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2: + if(!add_residual_partitioned_rice_( + bw, + subframe->residual, + residual_samples, + subframe->order, + subframe->entropy_coding_method.data.partitioned_rice.contents->parameters, + subframe->entropy_coding_method.data.partitioned_rice.contents->raw_bits, + subframe->entropy_coding_method.data.partitioned_rice.order, + /*is_extended=*/subframe->entropy_coding_method.type == FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2 + )) + return false; + break; + default: + FLAC__ASSERT(0); + } + + return true; +} + +FLAC__bool FLAC__subframe_add_lpc(const FLAC__Subframe_LPC *subframe, unsigned residual_samples, unsigned subframe_bps, unsigned wasted_bits, FLAC__BitWriter *bw) +{ + unsigned i; + + if(!FLAC__bitwriter_write_raw_uint32(bw, FLAC__SUBFRAME_TYPE_LPC_BYTE_ALIGNED_MASK | ((subframe->order-1)<<1) | (wasted_bits? 1:0), FLAC__SUBFRAME_ZERO_PAD_LEN + FLAC__SUBFRAME_TYPE_LEN + FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN)) + return false; + if(wasted_bits) + if(!FLAC__bitwriter_write_unary_unsigned(bw, wasted_bits-1)) + return false; + + for(i = 0; i < subframe->order; i++) + if(!FLAC__bitwriter_write_raw_int32(bw, subframe->warmup[i], subframe_bps)) + return false; + + if(!FLAC__bitwriter_write_raw_uint32(bw, subframe->qlp_coeff_precision-1, FLAC__SUBFRAME_LPC_QLP_COEFF_PRECISION_LEN)) + return false; + if(!FLAC__bitwriter_write_raw_int32(bw, subframe->quantization_level, FLAC__SUBFRAME_LPC_QLP_SHIFT_LEN)) + return false; + for(i = 0; i < subframe->order; i++) + if(!FLAC__bitwriter_write_raw_int32(bw, subframe->qlp_coeff[i], subframe->qlp_coeff_precision)) + return false; + + if(!add_entropy_coding_method_(bw, &subframe->entropy_coding_method)) + return false; + switch(subframe->entropy_coding_method.type) { + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE: + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2: + if(!add_residual_partitioned_rice_( + bw, + subframe->residual, + residual_samples, + subframe->order, + subframe->entropy_coding_method.data.partitioned_rice.contents->parameters, + subframe->entropy_coding_method.data.partitioned_rice.contents->raw_bits, + subframe->entropy_coding_method.data.partitioned_rice.order, + /*is_extended=*/subframe->entropy_coding_method.type == FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2 + )) + return false; + break; + default: + FLAC__ASSERT(0); + } + + return true; +} + +FLAC__bool FLAC__subframe_add_verbatim(const FLAC__Subframe_Verbatim *subframe, unsigned samples, unsigned subframe_bps, unsigned wasted_bits, FLAC__BitWriter *bw) +{ + unsigned i; + const FLAC__int32 *signal = subframe->data; + + if(!FLAC__bitwriter_write_raw_uint32(bw, FLAC__SUBFRAME_TYPE_VERBATIM_BYTE_ALIGNED_MASK | (wasted_bits? 1:0), FLAC__SUBFRAME_ZERO_PAD_LEN + FLAC__SUBFRAME_TYPE_LEN + FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN)) + return false; + if(wasted_bits) + if(!FLAC__bitwriter_write_unary_unsigned(bw, wasted_bits-1)) + return false; + + for(i = 0; i < samples; i++) + if(!FLAC__bitwriter_write_raw_int32(bw, signal[i], subframe_bps)) + return false; + + return true; +} + +FLAC__bool add_entropy_coding_method_(FLAC__BitWriter *bw, const FLAC__EntropyCodingMethod *method) +{ + if(!FLAC__bitwriter_write_raw_uint32(bw, method->type, FLAC__ENTROPY_CODING_METHOD_TYPE_LEN)) + return false; + switch(method->type) { + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE: + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2: + if(!FLAC__bitwriter_write_raw_uint32(bw, method->data.partitioned_rice.order, FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN)) + return false; + break; + default: + FLAC__ASSERT(0); + } + return true; +} + +FLAC__bool add_residual_partitioned_rice_(FLAC__BitWriter *bw, const FLAC__int32 residual[], const unsigned residual_samples, const unsigned predictor_order, const unsigned rice_parameters[], const unsigned raw_bits[], const unsigned partition_order, const FLAC__bool is_extended) +{ + const unsigned plen = is_extended? FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN : FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN; + const unsigned pesc = is_extended? FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_ESCAPE_PARAMETER : FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER; + + if(partition_order == 0) { + unsigned i; + + if(raw_bits[0] == 0) { + if(!FLAC__bitwriter_write_raw_uint32(bw, rice_parameters[0], plen)) + return false; + if(!FLAC__bitwriter_write_rice_signed_block(bw, residual, residual_samples, rice_parameters[0])) + return false; + } + else { + FLAC__ASSERT(rice_parameters[0] == 0); + if(!FLAC__bitwriter_write_raw_uint32(bw, pesc, plen)) + return false; + if(!FLAC__bitwriter_write_raw_uint32(bw, raw_bits[0], FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_RAW_LEN)) + return false; + for(i = 0; i < residual_samples; i++) { + if(!FLAC__bitwriter_write_raw_int32(bw, residual[i], raw_bits[0])) + return false; + } + } + return true; + } + else { + unsigned i, j, k = 0, k_last = 0; + unsigned partition_samples; + const unsigned default_partition_samples = (residual_samples+predictor_order) >> partition_order; + for(i = 0; i < (1u< +#endif + +#include + +#ifndef FLAC__INTEGER_ONLY_LIBRARY + +#ifndef M_PI +/* math.h in VC++ doesn't seem to have this (how Microsoft is that?) */ +#define M_PI 3.14159265358979323846 +#endif + +void FLAC__window_bartlett(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + if (L & 1) { + for (n = 0; n <= N/2; n++) + window[n] = 2.0f * n / (float)N; + for (; n <= N; n++) + window[n] = 2.0f - 2.0f * n / (float)N; + } + else { + for (n = 0; n <= L/2-1; n++) + window[n] = 2.0f * n / (float)N; + for (; n <= N; n++) + window[n] = 2.0f - 2.0f * (N-n) / (float)N; + } +} + +void FLAC__window_bartlett_hann(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + for (n = 0; n < L; n++) + window[n] = (FLAC__real)(0.62f - 0.48f * fabs((float)n/(float)N+0.5f) + 0.38f * cos(2.0f * M_PI * ((float)n/(float)N+0.5f))); +} + +void FLAC__window_blackman(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + for (n = 0; n < L; n++) + window[n] = (FLAC__real)(0.42f - 0.5f * cos(2.0f * M_PI * n / N) + 0.08f * cos(4.0f * M_PI * n / N)); +} + +/* 4-term -92dB side-lobe */ +void FLAC__window_blackman_harris_4term_92db_sidelobe(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + for (n = 0; n <= N; n++) + window[n] = (FLAC__real)(0.35875f - 0.48829f * cos(2.0f * M_PI * n / N) + 0.14128f * cos(4.0f * M_PI * n / N) - 0.01168f * cos(6.0f * M_PI * n / N)); +} + +void FLAC__window_connes(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + const double N2 = (double)N / 2.; + FLAC__int32 n; + + for (n = 0; n <= N; n++) { + double k = ((double)n - N2) / N2; + k = 1.0f - k * k; + window[n] = (FLAC__real)(k * k); + } +} + +void FLAC__window_flattop(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + for (n = 0; n < L; n++) + window[n] = (FLAC__real)(1.0f - 1.93f * cos(2.0f * M_PI * n / N) + 1.29f * cos(4.0f * M_PI * n / N) - 0.388f * cos(6.0f * M_PI * n / N) + 0.0322f * cos(8.0f * M_PI * n / N)); +} + +void FLAC__window_gauss(FLAC__real *window, const FLAC__int32 L, const FLAC__real stddev) +{ + const FLAC__int32 N = L - 1; + const double N2 = (double)N / 2.; + FLAC__int32 n; + + for (n = 0; n <= N; n++) { + const double k = ((double)n - N2) / (stddev * N2); + window[n] = (FLAC__real)exp(-0.5f * k * k); + } +} + +void FLAC__window_hamming(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + for (n = 0; n < L; n++) + window[n] = (FLAC__real)(0.54f - 0.46f * cos(2.0f * M_PI * n / N)); +} + +void FLAC__window_hann(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + for (n = 0; n < L; n++) + window[n] = (FLAC__real)(0.5f - 0.5f * cos(2.0f * M_PI * n / N)); +} + +void FLAC__window_kaiser_bessel(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + for (n = 0; n < L; n++) + window[n] = (FLAC__real)(0.402f - 0.498f * cos(2.0f * M_PI * n / N) + 0.098f * cos(4.0f * M_PI * n / N) - 0.001f * cos(6.0f * M_PI * n / N)); +} + +void FLAC__window_nuttall(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + for (n = 0; n < L; n++) + window[n] = (FLAC__real)(0.3635819f - 0.4891775f*cos(2.0f*M_PI*n/N) + 0.1365995f*cos(4.0f*M_PI*n/N) - 0.0106411f*cos(6.0f*M_PI*n/N)); +} + +void FLAC__window_rectangle(FLAC__real *window, const FLAC__int32 L) +{ + FLAC__int32 n; + + for (n = 0; n < L; n++) + window[n] = 1.0f; +} + +void FLAC__window_triangle(FLAC__real *window, const FLAC__int32 L) +{ + FLAC__int32 n; + + if (L & 1) { + for (n = 1; n <= L+1/2; n++) + window[n-1] = 2.0f * n / ((float)L + 1.0f); + for (; n <= L; n++) + window[n-1] = - (float)(2 * (L - n + 1)) / ((float)L + 1.0f); + } + else { + for (n = 1; n <= L/2; n++) + window[n-1] = 2.0f * n / (float)L; + for (; n <= L; n++) + window[n-1] = ((float)(2 * (L - n)) + 1.0f) / (float)L; + } +} + +void FLAC__window_tukey(FLAC__real *window, const FLAC__int32 L, const FLAC__real p) +{ + if (p <= 0.0) + FLAC__window_rectangle(window, L); + else if (p >= 1.0) + FLAC__window_hann(window, L); + else { + const FLAC__int32 Np = (FLAC__int32)(p / 2.0f * L) - 1; + FLAC__int32 n; + /* start with rectangle... */ + FLAC__window_rectangle(window, L); + /* ...replace ends with hann */ + if (Np > 0) { + for (n = 0; n <= Np; n++) { + window[n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * n / Np)); + window[L-Np-1+n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * (n+Np) / Np)); + } + } + } +} + +void FLAC__window_welch(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + const double N2 = (double)N / 2.; + FLAC__int32 n; + + for (n = 0; n <= N; n++) { + const double k = ((double)n - N2) / N2; + window[n] = (FLAC__real)(1.0f - k * k); + } +} + +#endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */ + +#endif +/********* End of inlined file: window_flac.c *********/ + + } +} + +#ifdef _MSC_VER + #pragma warning (pop) +#endif + +BEGIN_JUCE_NAMESPACE + +using namespace FlacNamespace; + +#define flacFormatName TRANS("FLAC file") +static const tchar* const flacExtensions[] = { T(".flac"), 0 }; + +class FlacReader : public AudioFormatReader +{ + FLAC__StreamDecoder* decoder; + AudioSampleBuffer reservoir; + int reservoirStart, samplesInReservoir; + bool ok, scanningForLength; + +public: + + FlacReader (InputStream* const in) + : AudioFormatReader (in, flacFormatName), + reservoir (2, 0), + reservoirStart (0), + samplesInReservoir (0), + scanningForLength (false) + { + using namespace FlacNamespace; + lengthInSamples = 0; + + decoder = FLAC__stream_decoder_new(); + + ok = FLAC__stream_decoder_init_stream (decoder, + readCallback_, seekCallback_, tellCallback_, lengthCallback_, + eofCallback_, writeCallback_, metadataCallback_, errorCallback_, + (void*) this) == FLAC__STREAM_DECODER_INIT_STATUS_OK; + + if (ok) + { + FLAC__stream_decoder_process_until_end_of_metadata (decoder); + + if (lengthInSamples == 0 && sampleRate > 0) + { + // the length hasn't been stored in the metadata, so we'll need to + // work it out the length the hard way, by scanning the whole file.. + scanningForLength = true; + FLAC__stream_decoder_process_until_end_of_stream (decoder); + scanningForLength = false; + const int64 tempLength = lengthInSamples; + + FLAC__stream_decoder_reset (decoder); + FLAC__stream_decoder_process_until_end_of_metadata (decoder); + lengthInSamples = tempLength; + } + } + } + + ~FlacReader() + { + FLAC__stream_decoder_delete (decoder); + } + + void useMetadata (const FLAC__StreamMetadata_StreamInfo& info) + { + sampleRate = info.sample_rate; + bitsPerSample = info.bits_per_sample; + lengthInSamples = (unsigned int) info.total_samples; + numChannels = info.channels; + + reservoir.setSize (numChannels, 2 * info.max_blocksize, false, false, true); + } + + // returns the number of samples read + bool read (int** destSamples, + int64 startSampleInFile, + int numSamples) + { + using namespace FlacNamespace; + + if (! ok) + return false; + + int offset = 0; + + if (startSampleInFile < 0) + { + const int num = (int) jmin ((int64) numSamples, -startSampleInFile); + + int n = 0; + while (destSamples[n] != 0) + { + zeromem (destSamples[n], sizeof (int) * num); + ++n; + } + + offset += num; + startSampleInFile += num; + numSamples -= num; + } + + while (numSamples > 0) + { + if (startSampleInFile >= reservoirStart + && startSampleInFile < reservoirStart + samplesInReservoir) + { + const int num = (int) jmin ((int64) numSamples, + reservoirStart + samplesInReservoir - startSampleInFile); + + jassert (num > 0); + + int n = 0; + while (destSamples[n] != 0) + { + memcpy (destSamples[n] + offset, + reservoir.getSampleData (n, (int) (startSampleInFile - reservoirStart)), + sizeof (int) * num); + ++n; + } + + offset += num; + startSampleInFile += num; + numSamples -= num; + } + else + { + if (startSampleInFile < reservoirStart + || startSampleInFile > reservoirStart + jmax (samplesInReservoir, 511)) + { + if (startSampleInFile >= (int) lengthInSamples) + { + samplesInReservoir = 0; + break; + } + + // had some problems with flac crashing if the read pos is aligned more + // accurately than this. Probably fixed in newer versions of the library, though. + reservoirStart = (int) (startSampleInFile & ~511); + FLAC__stream_decoder_seek_absolute (decoder, (FLAC__uint64) reservoirStart); + } + else + { + reservoirStart += samplesInReservoir; + } + + samplesInReservoir = 0; + + FLAC__stream_decoder_process_single (decoder); + + if (samplesInReservoir == 0) + break; + } + } + + if (numSamples > 0) + { + int n = 0; + while (destSamples[n] != 0) + { + zeromem (destSamples[n] + offset, sizeof (int) * numSamples); + ++n; + } + } + + return true; + } + + void useSamples (const FLAC__int32* const buffer[], int numSamples) + { + if (scanningForLength) + { + lengthInSamples += numSamples; + } + else + { + if (numSamples > reservoir.getNumSamples()) + reservoir.setSize (numChannels, numSamples, false, false, true); + + const int bitsToShift = 32 - bitsPerSample; + + for (int i = 0; i < (int) numChannels; ++i) + { + const FLAC__int32* src = buffer[i]; + + int n = i; + while (src == 0 && n > 0) + src = buffer [--n]; + + if (src != 0) + { + int* dest = (int*) reservoir.getSampleData(i); + + for (int j = 0; j < numSamples; ++j) + dest[j] = src[j] << bitsToShift; + } + } + + samplesInReservoir = numSamples; + } + } + + static FLAC__StreamDecoderReadStatus readCallback_ (const FLAC__StreamDecoder*, FLAC__byte buffer[], size_t* bytes, void* client_data) + { + *bytes = (unsigned int) ((const FlacReader*) client_data)->input->read (buffer, (int) *bytes); + return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; + } + + static FLAC__StreamDecoderSeekStatus seekCallback_ (const FLAC__StreamDecoder*, FLAC__uint64 absolute_byte_offset, void* client_data) + { + ((const FlacReader*) client_data)->input->setPosition ((int) absolute_byte_offset); + return FLAC__STREAM_DECODER_SEEK_STATUS_OK; + } + + static FLAC__StreamDecoderTellStatus tellCallback_ (const FLAC__StreamDecoder*, FLAC__uint64* absolute_byte_offset, void* client_data) + { + *absolute_byte_offset = ((const FlacReader*) client_data)->input->getPosition(); + return FLAC__STREAM_DECODER_TELL_STATUS_OK; + } + + static FLAC__StreamDecoderLengthStatus lengthCallback_ (const FLAC__StreamDecoder*, FLAC__uint64* stream_length, void* client_data) + { + *stream_length = ((const FlacReader*) client_data)->input->getTotalLength(); + return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; + } + + static FLAC__bool eofCallback_ (const FLAC__StreamDecoder*, void* client_data) + { + return ((const FlacReader*) client_data)->input->isExhausted(); + } + + static FLAC__StreamDecoderWriteStatus writeCallback_ (const FLAC__StreamDecoder*, + const FLAC__Frame* frame, + const FLAC__int32* const buffer[], + void* client_data) + { + ((FlacReader*) client_data)->useSamples (buffer, frame->header.blocksize); + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; + } + + static void metadataCallback_ (const FLAC__StreamDecoder*, + const FLAC__StreamMetadata* metadata, + void* client_data) + { + ((FlacReader*) client_data)->useMetadata (metadata->data.stream_info); + } + + static void errorCallback_ (const FLAC__StreamDecoder*, FLAC__StreamDecoderErrorStatus, void*) + { + } + + juce_UseDebuggingNewOperator +}; + +class FlacWriter : public AudioFormatWriter +{ + FLAC__StreamEncoder* encoder; + MemoryBlock temp; + +public: + bool ok; + + FlacWriter (OutputStream* const out, + const double sampleRate, + const int numChannels, + const int bitsPerSample_) + : AudioFormatWriter (out, flacFormatName, + sampleRate, + numChannels, + bitsPerSample_) + { + using namespace FlacNamespace; + encoder = FLAC__stream_encoder_new(); + + FLAC__stream_encoder_set_do_mid_side_stereo (encoder, numChannels == 2); + FLAC__stream_encoder_set_loose_mid_side_stereo (encoder, numChannels == 2); + FLAC__stream_encoder_set_channels (encoder, numChannels); + FLAC__stream_encoder_set_bits_per_sample (encoder, jmin (24, bitsPerSample)); + FLAC__stream_encoder_set_sample_rate (encoder, (unsigned int) sampleRate); + FLAC__stream_encoder_set_blocksize (encoder, 2048); + FLAC__stream_encoder_set_do_escape_coding (encoder, true); + + ok = FLAC__stream_encoder_init_stream (encoder, + encodeWriteCallback, encodeSeekCallback, + encodeTellCallback, encodeMetadataCallback, + (void*) this) == FLAC__STREAM_ENCODER_INIT_STATUS_OK; + } + + ~FlacWriter() + { + if (ok) + { + FLAC__stream_encoder_finish (encoder); + output->flush(); + } + else + { + output = 0; // to stop the base class deleting this, as it needs to be returned + // to the caller of createWriter() + } + + FLAC__stream_encoder_delete (encoder); + } + + bool write (const int** samplesToWrite, int numSamples) + { + if (! ok) + return false; + + int* buf[3]; + const int bitsToShift = 32 - bitsPerSample; + + if (bitsToShift > 0) + { + const int numChannels = (samplesToWrite[1] == 0) ? 1 : 2; + temp.setSize (sizeof (int) * numSamples * numChannels); + + buf[0] = (int*) temp.getData(); + buf[1] = buf[0] + numSamples; + buf[2] = 0; + + for (int i = numChannels; --i >= 0;) + { + if (samplesToWrite[i] != 0) + { + for (int j = 0; j < numSamples; ++j) + buf [i][j] = (samplesToWrite [i][j] >> bitsToShift); + } + } + + samplesToWrite = (const int**) buf; + } + + return FLAC__stream_encoder_process (encoder, + (const FLAC__int32**) samplesToWrite, + numSamples) != 0; + } + + bool writeData (const void* const data, const int size) const + { + return output->write (data, size); + } + + static void packUint32 (FLAC__uint32 val, FLAC__byte* b, const int bytes) + { + b += bytes; + + for (int i = 0; i < bytes; ++i) + { + *(--b) = (FLAC__byte) (val & 0xff); + val >>= 8; + } + } + + void writeMetaData (const FLAC__StreamMetadata* metadata) + { + using namespace FlacNamespace; + const FLAC__StreamMetadata_StreamInfo& info = metadata->data.stream_info; + + unsigned char buffer [FLAC__STREAM_METADATA_STREAMINFO_LENGTH]; + const unsigned int channelsMinus1 = info.channels - 1; + const unsigned int bitsMinus1 = info.bits_per_sample - 1; + + packUint32 (info.min_blocksize, buffer, 2); + packUint32 (info.max_blocksize, buffer + 2, 2); + packUint32 (info.min_framesize, buffer + 4, 3); + packUint32 (info.max_framesize, buffer + 7, 3); + buffer[10] = (uint8) ((info.sample_rate >> 12) & 0xff); + buffer[11] = (uint8) ((info.sample_rate >> 4) & 0xff); + buffer[12] = (uint8) (((info.sample_rate & 0x0f) << 4) | (channelsMinus1 << 1) | (bitsMinus1 >> 4)); + buffer[13] = (FLAC__byte) (((bitsMinus1 & 0x0f) << 4) | (unsigned int) ((info.total_samples >> 32) & 0x0f)); + packUint32 ((FLAC__uint32) info.total_samples, buffer + 14, 4); + memcpy (buffer + 18, info.md5sum, 16); + + const bool ok = output->setPosition (4); + (void) ok; + + // if this fails, you've given it an output stream that can't seek! It needs + // to be able to seek back to write the header + jassert (ok); + + output->writeIntBigEndian (FLAC__STREAM_METADATA_STREAMINFO_LENGTH); + output->write (buffer, FLAC__STREAM_METADATA_STREAMINFO_LENGTH); + } + + static FLAC__StreamEncoderWriteStatus encodeWriteCallback (const FLAC__StreamEncoder*, + const FLAC__byte buffer[], + size_t bytes, + unsigned int /*samples*/, + unsigned int /*current_frame*/, + void* client_data) + { + using namespace FlacNamespace; + return ((FlacWriter*) client_data)->writeData (buffer, (int) bytes) + ? FLAC__STREAM_ENCODER_WRITE_STATUS_OK + : FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR; + } + + static FLAC__StreamEncoderSeekStatus encodeSeekCallback (const FLAC__StreamEncoder*, FLAC__uint64, void*) + { + return FLAC__STREAM_ENCODER_SEEK_STATUS_UNSUPPORTED; + } + + static FLAC__StreamEncoderTellStatus encodeTellCallback (const FLAC__StreamEncoder*, FLAC__uint64*, void*) + { + return FLAC__STREAM_ENCODER_TELL_STATUS_UNSUPPORTED; + } + + static void encodeMetadataCallback (const FLAC__StreamEncoder*, + const FLAC__StreamMetadata* metadata, + void* client_data) + { + ((FlacWriter*) client_data)->writeMetaData (metadata); + } + + juce_UseDebuggingNewOperator +}; + +FlacAudioFormat::FlacAudioFormat() + : AudioFormat (flacFormatName, (const tchar**) flacExtensions) +{ +} + +FlacAudioFormat::~FlacAudioFormat() +{ +} + +const Array FlacAudioFormat::getPossibleSampleRates() +{ + const int rates[] = { 22050, 32000, 44100, 48000, 88200, 96000, 0 }; + return Array (rates); +} + +const Array FlacAudioFormat::getPossibleBitDepths() +{ + const int depths[] = { 16, 24, 0 }; + return Array (depths); +} + +bool FlacAudioFormat::canDoStereo() +{ + return true; +} + +bool FlacAudioFormat::canDoMono() +{ + return true; +} + +bool FlacAudioFormat::isCompressed() +{ + return true; +} + +AudioFormatReader* FlacAudioFormat::createReaderFor (InputStream* in, + const bool deleteStreamIfOpeningFails) +{ + FlacReader* r = new FlacReader (in); + + if (r->sampleRate == 0) + { + if (! deleteStreamIfOpeningFails) + r->input = 0; + + deleteAndZero (r); + } + + return r; +} + +AudioFormatWriter* FlacAudioFormat::createWriterFor (OutputStream* out, + double sampleRate, + unsigned int numberOfChannels, + int bitsPerSample, + const StringPairArray& /*metadataValues*/, + int /*qualityOptionIndex*/) +{ + if (getPossibleBitDepths().contains (bitsPerSample)) + { + FlacWriter* w = new FlacWriter (out, + sampleRate, + numberOfChannels, + bitsPerSample); + + if (! w->ok) + deleteAndZero (w); + + return w; + } + + return 0; +} + +END_JUCE_NAMESPACE + +#endif +/********* End of inlined file: juce_FlacAudioFormat.cpp *********/ + +/********* Start of inlined file: juce_OggVorbisAudioFormat.cpp *********/ + +#if JUCE_USE_OGGVORBIS + +#if JUCE_MAC + #define __MACOSX__ 1 +#endif + +namespace OggVorbisNamespace +{ + extern "C" + { + +/********* Start of inlined file: vorbisenc.h *********/ +#ifndef _OV_ENC_H_ +#define _OV_ENC_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +/********* Start of inlined file: codec.h *********/ +#ifndef _vorbis_codec_h_ +#define _vorbis_codec_h_ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +/********* Start of inlined file: ogg.h *********/ +#ifndef _OGG_H +#define _OGG_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********* Start of inlined file: os_types.h *********/ +#ifndef _OS_TYPES_H +#define _OS_TYPES_H + +/* make it easy on the folks that want to compile the libs with a + different malloc than stdlib */ +#define _ogg_malloc malloc +#define _ogg_calloc calloc +#define _ogg_realloc realloc +#define _ogg_free free + +#if defined(_WIN32) + +# if defined(__CYGWIN__) +# include <_G_config.h> + typedef _G_int64_t ogg_int64_t; + typedef _G_int32_t ogg_int32_t; + typedef _G_uint32_t ogg_uint32_t; + typedef _G_int16_t ogg_int16_t; + typedef _G_uint16_t ogg_uint16_t; +# elif defined(__MINGW32__) + typedef short ogg_int16_t; + typedef unsigned short ogg_uint16_t; + typedef int ogg_int32_t; + typedef unsigned int ogg_uint32_t; + typedef long long ogg_int64_t; + typedef unsigned long long ogg_uint64_t; +# elif defined(__MWERKS__) + typedef long long ogg_int64_t; + typedef int ogg_int32_t; + typedef unsigned int ogg_uint32_t; + typedef short ogg_int16_t; + typedef unsigned short ogg_uint16_t; +# else + /* MSVC/Borland */ + typedef __int64 ogg_int64_t; + typedef __int32 ogg_int32_t; + typedef unsigned __int32 ogg_uint32_t; + typedef __int16 ogg_int16_t; + typedef unsigned __int16 ogg_uint16_t; +# endif + +#elif defined(__MACOS__) + +# include + typedef SInt16 ogg_int16_t; + typedef UInt16 ogg_uint16_t; + typedef SInt32 ogg_int32_t; + typedef UInt32 ogg_uint32_t; + typedef SInt64 ogg_int64_t; + +#elif defined(__MACOSX__) /* MacOS X Framework build */ + +# include + typedef int16_t ogg_int16_t; + typedef u_int16_t ogg_uint16_t; + typedef int32_t ogg_int32_t; + typedef u_int32_t ogg_uint32_t; + typedef int64_t ogg_int64_t; + +#elif defined(__BEOS__) + + /* Be */ +# include + typedef int16_t ogg_int16_t; + typedef u_int16_t ogg_uint16_t; + typedef int32_t ogg_int32_t; + typedef u_int32_t ogg_uint32_t; + typedef int64_t ogg_int64_t; + +#elif defined (__EMX__) + + /* OS/2 GCC */ + typedef short ogg_int16_t; + typedef unsigned short ogg_uint16_t; + typedef int ogg_int32_t; + typedef unsigned int ogg_uint32_t; + typedef long long ogg_int64_t; + +#elif defined (DJGPP) + + /* DJGPP */ + typedef short ogg_int16_t; + typedef int ogg_int32_t; + typedef unsigned int ogg_uint32_t; + typedef long long ogg_int64_t; + +#elif defined(R5900) + + /* PS2 EE */ + typedef long ogg_int64_t; + typedef int ogg_int32_t; + typedef unsigned ogg_uint32_t; + typedef short ogg_int16_t; + +#elif defined(__SYMBIAN32__) + + /* Symbian GCC */ + typedef signed short ogg_int16_t; + typedef unsigned short ogg_uint16_t; + typedef signed int ogg_int32_t; + typedef unsigned int ogg_uint32_t; + typedef long long int ogg_int64_t; + +#else + +# include + +/********* Start of inlined file: config_types.h *********/ +#ifndef __CONFIG_TYPES_H__ +#define __CONFIG_TYPES_H__ + +typedef int16_t ogg_int16_t; +typedef unsigned short ogg_uint16_t; +typedef int32_t ogg_int32_t; +typedef unsigned int ogg_uint32_t; +typedef int64_t ogg_int64_t; + +#endif +/********* End of inlined file: config_types.h *********/ + +#endif + +#endif /* _OS_TYPES_H */ +/********* End of inlined file: os_types.h *********/ + +typedef struct { + long endbyte; + int endbit; + + unsigned char *buffer; + unsigned char *ptr; + long storage; +} oggpack_buffer; + +/* ogg_page is used to encapsulate the data in one Ogg bitstream page *****/ + +typedef struct { + unsigned char *header; + long header_len; + unsigned char *body; + long body_len; +} ogg_page; + +ogg_uint32_t bitreverse(ogg_uint32_t x){ + x= ((x>>16)&0x0000ffffUL) | ((x<<16)&0xffff0000UL); + x= ((x>> 8)&0x00ff00ffUL) | ((x<< 8)&0xff00ff00UL); + x= ((x>> 4)&0x0f0f0f0fUL) | ((x<< 4)&0xf0f0f0f0UL); + x= ((x>> 2)&0x33333333UL) | ((x<< 2)&0xccccccccUL); + return((x>> 1)&0x55555555UL) | ((x<< 1)&0xaaaaaaaaUL); +} + +/* ogg_stream_state contains the current encode/decode state of a logical + Ogg bitstream **********************************************************/ + +typedef struct { + unsigned char *body_data; /* bytes from packet bodies */ + long body_storage; /* storage elements allocated */ + long body_fill; /* elements stored; fill mark */ + long body_returned; /* elements of fill returned */ + + int *lacing_vals; /* The values that will go to the segment table */ + ogg_int64_t *granule_vals; /* granulepos values for headers. Not compact + this way, but it is simple coupled to the + lacing fifo */ + long lacing_storage; + long lacing_fill; + long lacing_packet; + long lacing_returned; + + unsigned char header[282]; /* working space for header encode */ + int header_fill; + + int e_o_s; /* set when we have buffered the last packet in the + logical bitstream */ + int b_o_s; /* set after we've written the initial page + of a logical bitstream */ + long serialno; + long pageno; + ogg_int64_t packetno; /* sequence number for decode; the framing + knows where there's a hole in the data, + but we need coupling so that the codec + (which is in a seperate abstraction + layer) also knows about the gap */ + ogg_int64_t granulepos; + +} ogg_stream_state; + +/* ogg_packet is used to encapsulate the data and metadata belonging + to a single raw Ogg/Vorbis packet *************************************/ + +typedef struct { + unsigned char *packet; + long bytes; + long b_o_s; + long e_o_s; + + ogg_int64_t granulepos; + + ogg_int64_t packetno; /* sequence number for decode; the framing + knows where there's a hole in the data, + but we need coupling so that the codec + (which is in a seperate abstraction + layer) also knows about the gap */ +} ogg_packet; + +typedef struct { + unsigned char *data; + int storage; + int fill; + int returned; + + int unsynced; + int headerbytes; + int bodybytes; +} ogg_sync_state; + +/* Ogg BITSTREAM PRIMITIVES: bitstream ************************/ + +extern void oggpack_writeinit(oggpack_buffer *b); +extern void oggpack_writetrunc(oggpack_buffer *b,long bits); +extern void oggpack_writealign(oggpack_buffer *b); +extern void oggpack_writecopy(oggpack_buffer *b,void *source,long bits); +extern void oggpack_reset(oggpack_buffer *b); +extern void oggpack_writeclear(oggpack_buffer *b); +extern void oggpack_readinit(oggpack_buffer *b,unsigned char *buf,int bytes); +extern void oggpack_write(oggpack_buffer *b,unsigned long value,int bits); +extern long oggpack_look(oggpack_buffer *b,int bits); +extern long oggpack_look1(oggpack_buffer *b); +extern void oggpack_adv(oggpack_buffer *b,int bits); +extern void oggpack_adv1(oggpack_buffer *b); +extern long oggpack_read(oggpack_buffer *b,int bits); +extern long oggpack_read1(oggpack_buffer *b); +extern long oggpack_bytes(oggpack_buffer *b); +extern long oggpack_bits(oggpack_buffer *b); +extern unsigned char *oggpack_get_buffer(oggpack_buffer *b); + +extern void oggpackB_writeinit(oggpack_buffer *b); +extern void oggpackB_writetrunc(oggpack_buffer *b,long bits); +extern void oggpackB_writealign(oggpack_buffer *b); +extern void oggpackB_writecopy(oggpack_buffer *b,void *source,long bits); +extern void oggpackB_reset(oggpack_buffer *b); +extern void oggpackB_writeclear(oggpack_buffer *b); +extern void oggpackB_readinit(oggpack_buffer *b,unsigned char *buf,int bytes); +extern void oggpackB_write(oggpack_buffer *b,unsigned long value,int bits); +extern long oggpackB_look(oggpack_buffer *b,int bits); +extern long oggpackB_look1(oggpack_buffer *b); +extern void oggpackB_adv(oggpack_buffer *b,int bits); +extern void oggpackB_adv1(oggpack_buffer *b); +extern long oggpackB_read(oggpack_buffer *b,int bits); +extern long oggpackB_read1(oggpack_buffer *b); +extern long oggpackB_bytes(oggpack_buffer *b); +extern long oggpackB_bits(oggpack_buffer *b); +extern unsigned char *oggpackB_get_buffer(oggpack_buffer *b); + +/* Ogg BITSTREAM PRIMITIVES: encoding **************************/ + +extern int ogg_stream_packetin(ogg_stream_state *os, ogg_packet *op); +extern int ogg_stream_pageout(ogg_stream_state *os, ogg_page *og); +extern int ogg_stream_flush(ogg_stream_state *os, ogg_page *og); + +/* Ogg BITSTREAM PRIMITIVES: decoding **************************/ + +extern int ogg_sync_init(ogg_sync_state *oy); +extern int ogg_sync_clear(ogg_sync_state *oy); +extern int ogg_sync_reset(ogg_sync_state *oy); +extern int ogg_sync_destroy(ogg_sync_state *oy); + +extern char *ogg_sync_buffer(ogg_sync_state *oy, long size); +extern int ogg_sync_wrote(ogg_sync_state *oy, long bytes); +extern long ogg_sync_pageseek(ogg_sync_state *oy,ogg_page *og); +extern int ogg_sync_pageout(ogg_sync_state *oy, ogg_page *og); +extern int ogg_stream_pagein(ogg_stream_state *os, ogg_page *og); +extern int ogg_stream_packetout(ogg_stream_state *os,ogg_packet *op); +extern int ogg_stream_packetpeek(ogg_stream_state *os,ogg_packet *op); + +/* Ogg BITSTREAM PRIMITIVES: general ***************************/ + +extern int ogg_stream_init(ogg_stream_state *os,int serialno); +extern int ogg_stream_clear(ogg_stream_state *os); +extern int ogg_stream_reset(ogg_stream_state *os); +extern int ogg_stream_reset_serialno(ogg_stream_state *os,int serialno); +extern int ogg_stream_destroy(ogg_stream_state *os); +extern int ogg_stream_eos(ogg_stream_state *os); + +extern void ogg_page_checksum_set(ogg_page *og); + +extern int ogg_page_version(ogg_page *og); +extern int ogg_page_continued(ogg_page *og); +extern int ogg_page_bos(ogg_page *og); +extern int ogg_page_eos(ogg_page *og); +extern ogg_int64_t ogg_page_granulepos(ogg_page *og); +extern int ogg_page_serialno(ogg_page *og); +extern long ogg_page_pageno(ogg_page *og); +extern int ogg_page_packets(ogg_page *og); + +extern void ogg_packet_clear(ogg_packet *op); + +#ifdef __cplusplus +} +#endif + +#endif /* _OGG_H */ +/********* End of inlined file: ogg.h *********/ + +typedef struct vorbis_info{ + int version; + int channels; + long rate; + + /* The below bitrate declarations are *hints*. + Combinations of the three values carry the following implications: + + all three set to the same value: + implies a fixed rate bitstream + only nominal set: + implies a VBR stream that averages the nominal bitrate. No hard + upper/lower limit + upper and or lower set: + implies a VBR bitstream that obeys the bitrate limits. nominal + may also be set to give a nominal rate. + none set: + the coder does not care to speculate. + */ + + long bitrate_upper; + long bitrate_nominal; + long bitrate_lower; + long bitrate_window; + + void *codec_setup; +} vorbis_info; + +/* vorbis_dsp_state buffers the current vorbis audio + analysis/synthesis state. The DSP state belongs to a specific + logical bitstream ****************************************************/ +typedef struct vorbis_dsp_state{ + int analysisp; + vorbis_info *vi; + + float **pcm; + float **pcmret; + int pcm_storage; + int pcm_current; + int pcm_returned; + + int preextrapolate; + int eofflag; + + long lW; + long W; + long nW; + long centerW; + + ogg_int64_t granulepos; + ogg_int64_t sequence; + + ogg_int64_t glue_bits; + ogg_int64_t time_bits; + ogg_int64_t floor_bits; + ogg_int64_t res_bits; + + void *backend_state; +} vorbis_dsp_state; + +typedef struct vorbis_block{ + /* necessary stream state for linking to the framing abstraction */ + float **pcm; /* this is a pointer into local storage */ + oggpack_buffer opb; + + long lW; + long W; + long nW; + int pcmend; + int mode; + + int eofflag; + ogg_int64_t granulepos; + ogg_int64_t sequence; + vorbis_dsp_state *vd; /* For read-only access of configuration */ + + /* local storage to avoid remallocing; it's up to the mapping to + structure it */ + void *localstore; + long localtop; + long localalloc; + long totaluse; + struct alloc_chain *reap; + + /* bitmetrics for the frame */ + long glue_bits; + long time_bits; + long floor_bits; + long res_bits; + + void *internal; + +} vorbis_block; + +/* vorbis_block is a single block of data to be processed as part of +the analysis/synthesis stream; it belongs to a specific logical +bitstream, but is independant from other vorbis_blocks belonging to +that logical bitstream. *************************************************/ + +struct alloc_chain{ + void *ptr; + struct alloc_chain *next; +}; + +/* vorbis_info contains all the setup information specific to the + specific compression/decompression mode in progress (eg, + psychoacoustic settings, channel setup, options, codebook + etc). vorbis_info and substructures are in backends.h. +*********************************************************************/ + +/* the comments are not part of vorbis_info so that vorbis_info can be + static storage */ +typedef struct vorbis_comment{ + /* unlimited user comment fields. libvorbis writes 'libvorbis' + whatever vendor is set to in encode */ + char **user_comments; + int *comment_lengths; + int comments; + char *vendor; + +} vorbis_comment; + +/* libvorbis encodes in two abstraction layers; first we perform DSP + and produce a packet (see docs/analysis.txt). The packet is then + coded into a framed OggSquish bitstream by the second layer (see + docs/framing.txt). Decode is the reverse process; we sync/frame + the bitstream and extract individual packets, then decode the + packet back into PCM audio. + + The extra framing/packetizing is used in streaming formats, such as + files. Over the net (such as with UDP), the framing and + packetization aren't necessary as they're provided by the transport + and the streaming layer is not used */ + +/* Vorbis PRIMITIVES: general ***************************************/ + +extern void vorbis_info_init(vorbis_info *vi); +extern void vorbis_info_clear(vorbis_info *vi); +extern int vorbis_info_blocksize(vorbis_info *vi,int zo); +extern void vorbis_comment_init(vorbis_comment *vc); +extern void vorbis_comment_add(vorbis_comment *vc, char *comment); +extern void vorbis_comment_add_tag(vorbis_comment *vc, + char *tag, char *contents); +extern char *vorbis_comment_query(vorbis_comment *vc, char *tag, int count); +extern int vorbis_comment_query_count(vorbis_comment *vc, char *tag); +extern void vorbis_comment_clear(vorbis_comment *vc); + +extern int vorbis_block_init(vorbis_dsp_state *v, vorbis_block *vb); +extern int vorbis_block_clear(vorbis_block *vb); +extern void vorbis_dsp_clear(vorbis_dsp_state *v); +extern double vorbis_granule_time(vorbis_dsp_state *v, + ogg_int64_t granulepos); + +/* Vorbis PRIMITIVES: analysis/DSP layer ****************************/ + +extern int vorbis_analysis_init(vorbis_dsp_state *v,vorbis_info *vi); +extern int vorbis_commentheader_out(vorbis_comment *vc, ogg_packet *op); +extern int vorbis_analysis_headerout(vorbis_dsp_state *v, + vorbis_comment *vc, + ogg_packet *op, + ogg_packet *op_comm, + ogg_packet *op_code); +extern float **vorbis_analysis_buffer(vorbis_dsp_state *v,int vals); +extern int vorbis_analysis_wrote(vorbis_dsp_state *v,int vals); +extern int vorbis_analysis_blockout(vorbis_dsp_state *v,vorbis_block *vb); +extern int vorbis_analysis(vorbis_block *vb,ogg_packet *op); + +extern int vorbis_bitrate_addblock(vorbis_block *vb); +extern int vorbis_bitrate_flushpacket(vorbis_dsp_state *vd, + ogg_packet *op); + +/* Vorbis PRIMITIVES: synthesis layer *******************************/ +extern int vorbis_synthesis_headerin(vorbis_info *vi,vorbis_comment *vc, + ogg_packet *op); + +extern int vorbis_synthesis_init(vorbis_dsp_state *v,vorbis_info *vi); +extern int vorbis_synthesis_restart(vorbis_dsp_state *v); +extern int vorbis_synthesis(vorbis_block *vb,ogg_packet *op); +extern int vorbis_synthesis_trackonly(vorbis_block *vb,ogg_packet *op); +extern int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb); +extern int vorbis_synthesis_pcmout(vorbis_dsp_state *v,float ***pcm); +extern int vorbis_synthesis_lapout(vorbis_dsp_state *v,float ***pcm); +extern int vorbis_synthesis_read(vorbis_dsp_state *v,int samples); +extern long vorbis_packet_blocksize(vorbis_info *vi,ogg_packet *op); + +extern int vorbis_synthesis_halfrate(vorbis_info *v,int flag); +extern int vorbis_synthesis_halfrate_p(vorbis_info *v); + +/* Vorbis ERRORS and return codes ***********************************/ + +#define OV_FALSE -1 +#define OV_EOF -2 +#define OV_HOLE -3 + +#define OV_EREAD -128 +#define OV_EFAULT -129 +#define OV_EIMPL -130 +#define OV_EINVAL -131 +#define OV_ENOTVORBIS -132 +#define OV_EBADHEADER -133 +#define OV_EVERSION -134 +#define OV_ENOTAUDIO -135 +#define OV_EBADPACKET -136 +#define OV_EBADLINK -137 +#define OV_ENOSEEK -138 + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif +/********* End of inlined file: codec.h *********/ + +extern int vorbis_encode_init(vorbis_info *vi, + long channels, + long rate, + + long max_bitrate, + long nominal_bitrate, + long min_bitrate); + +extern int vorbis_encode_setup_managed(vorbis_info *vi, + long channels, + long rate, + + long max_bitrate, + long nominal_bitrate, + long min_bitrate); + +extern int vorbis_encode_setup_vbr(vorbis_info *vi, + long channels, + long rate, + + float quality /* quality level from 0. (lo) to 1. (hi) */ + ); + +extern int vorbis_encode_init_vbr(vorbis_info *vi, + long channels, + long rate, + + float base_quality /* quality level from 0. (lo) to 1. (hi) */ + ); + +extern int vorbis_encode_setup_init(vorbis_info *vi); + +extern int vorbis_encode_ctl(vorbis_info *vi,int number,void *arg); + + /* deprecated rate management supported only for compatability */ +#define OV_ECTL_RATEMANAGE_GET 0x10 +#define OV_ECTL_RATEMANAGE_SET 0x11 +#define OV_ECTL_RATEMANAGE_AVG 0x12 +#define OV_ECTL_RATEMANAGE_HARD 0x13 + +struct ovectl_ratemanage_arg { + int management_active; + + long bitrate_hard_min; + long bitrate_hard_max; + double bitrate_hard_window; + + long bitrate_av_lo; + long bitrate_av_hi; + double bitrate_av_window; + double bitrate_av_window_center; +}; + + /* new rate setup */ +#define OV_ECTL_RATEMANAGE2_GET 0x14 +#define OV_ECTL_RATEMANAGE2_SET 0x15 + +struct ovectl_ratemanage2_arg { + int management_active; + + long bitrate_limit_min_kbps; + long bitrate_limit_max_kbps; + long bitrate_limit_reservoir_bits; + double bitrate_limit_reservoir_bias; + + long bitrate_average_kbps; + double bitrate_average_damping; +}; + +#define OV_ECTL_LOWPASS_GET 0x20 +#define OV_ECTL_LOWPASS_SET 0x21 + +#define OV_ECTL_IBLOCK_GET 0x30 +#define OV_ECTL_IBLOCK_SET 0x31 + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif +/********* End of inlined file: vorbisenc.h *********/ + +/********* Start of inlined file: vorbisfile.h *********/ +#ifndef _OV_FILE_H_ +#define _OV_FILE_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +#include + +/* The function prototypes for the callbacks are basically the same as for + * the stdio functions fread, fseek, fclose, ftell. + * The one difference is that the FILE * arguments have been replaced with + * a void * - this is to be used as a pointer to whatever internal data these + * functions might need. In the stdio case, it's just a FILE * cast to a void * + * + * If you use other functions, check the docs for these functions and return + * the right values. For seek_func(), you *MUST* return -1 if the stream is + * unseekable + */ +typedef struct { + size_t (*read_func) (void *ptr, size_t size, size_t nmemb, void *datasource); + int (*seek_func) (void *datasource, ogg_int64_t offset, int whence); + int (*close_func) (void *datasource); + long (*tell_func) (void *datasource); +} ov_callbacks; + +#define NOTOPEN 0 +#define PARTOPEN 1 +#define OPENED 2 +#define STREAMSET 3 +#define INITSET 4 + +typedef struct OggVorbis_File { + void *datasource; /* Pointer to a FILE *, etc. */ + int seekable; + ogg_int64_t offset; + ogg_int64_t end; + ogg_sync_state oy; + + /* If the FILE handle isn't seekable (eg, a pipe), only the current + stream appears */ + int links; + ogg_int64_t *offsets; + ogg_int64_t *dataoffsets; + long *serialnos; + ogg_int64_t *pcmlengths; /* overloaded to maintain binary + compatability; x2 size, stores both + beginning and end values */ + vorbis_info *vi; + vorbis_comment *vc; + + /* Decoding working state local storage */ + ogg_int64_t pcm_offset; + int ready_state; + long current_serialno; + int current_link; + + double bittrack; + double samptrack; + + ogg_stream_state os; /* take physical pages, weld into a logical + stream of packets */ + vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */ + vorbis_block vb; /* local working space for packet->PCM decode */ + + ov_callbacks callbacks; + +} OggVorbis_File; + +extern int ov_clear(OggVorbis_File *vf); +extern int ov_open(FILE *f,OggVorbis_File *vf,char *initial,long ibytes); +extern int ov_open_callbacks(void *datasource, OggVorbis_File *vf, + char *initial, long ibytes, ov_callbacks callbacks); + +extern int ov_test(FILE *f,OggVorbis_File *vf,char *initial,long ibytes); +extern int ov_test_callbacks(void *datasource, OggVorbis_File *vf, + char *initial, long ibytes, ov_callbacks callbacks); +extern int ov_test_open(OggVorbis_File *vf); + +extern long ov_bitrate(OggVorbis_File *vf,int i); +extern long ov_bitrate_instant(OggVorbis_File *vf); +extern long ov_streams(OggVorbis_File *vf); +extern long ov_seekable(OggVorbis_File *vf); +extern long ov_serialnumber(OggVorbis_File *vf,int i); + +extern ogg_int64_t ov_raw_total(OggVorbis_File *vf,int i); +extern ogg_int64_t ov_pcm_total(OggVorbis_File *vf,int i); +extern double ov_time_total(OggVorbis_File *vf,int i); + +extern int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos); +extern int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos); +extern int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos); +extern int ov_time_seek(OggVorbis_File *vf,double pos); +extern int ov_time_seek_page(OggVorbis_File *vf,double pos); + +extern int ov_raw_seek_lap(OggVorbis_File *vf,ogg_int64_t pos); +extern int ov_pcm_seek_lap(OggVorbis_File *vf,ogg_int64_t pos); +extern int ov_pcm_seek_page_lap(OggVorbis_File *vf,ogg_int64_t pos); +extern int ov_time_seek_lap(OggVorbis_File *vf,double pos); +extern int ov_time_seek_page_lap(OggVorbis_File *vf,double pos); + +extern ogg_int64_t ov_raw_tell(OggVorbis_File *vf); +extern ogg_int64_t ov_pcm_tell(OggVorbis_File *vf); +extern double ov_time_tell(OggVorbis_File *vf); + +extern vorbis_info *ov_info(OggVorbis_File *vf,int link); +extern vorbis_comment *ov_comment(OggVorbis_File *vf,int link); + +extern long ov_read_float(OggVorbis_File *vf,float ***pcm_channels,int samples, + int *bitstream); +extern long ov_read(OggVorbis_File *vf,char *buffer,int length, + int bigendianp,int word,int sgned,int *bitstream); +extern int ov_crosslap(OggVorbis_File *vf1,OggVorbis_File *vf2); + +extern int ov_halfrate(OggVorbis_File *vf,int flag); +extern int ov_halfrate_p(OggVorbis_File *vf); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif +/********* End of inlined file: vorbisfile.h *********/ + +/********* Start of inlined file: bitwise.c *********/ +/* We're 'LSb' endian; if we write a word but read individual bits, + then we'll read the lsb first */ + +/********* Start of inlined file: juce_OggVorbisHeader.h *********/ +// This file is included at the start of each Ogg-Vorbis .c file, just to do a few housekeeping +// tasks.. + +#ifdef _MSC_VER + #pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4305 4189 4706) +#endif +/********* End of inlined file: juce_OggVorbisHeader.h *********/ + +#if JUCE_USE_OGGVORBIS + +#include +#include + +#define BUFFER_INCREMENT 256 + +static const unsigned long mask[]= +{0x00000000,0x00000001,0x00000003,0x00000007,0x0000000f, + 0x0000001f,0x0000003f,0x0000007f,0x000000ff,0x000001ff, + 0x000003ff,0x000007ff,0x00000fff,0x00001fff,0x00003fff, + 0x00007fff,0x0000ffff,0x0001ffff,0x0003ffff,0x0007ffff, + 0x000fffff,0x001fffff,0x003fffff,0x007fffff,0x00ffffff, + 0x01ffffff,0x03ffffff,0x07ffffff,0x0fffffff,0x1fffffff, + 0x3fffffff,0x7fffffff,0xffffffff }; + +static const unsigned int mask8B[]= +{0x00,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff}; + +void oggpack_writeinit(oggpack_buffer *b){ + memset(b,0,sizeof(*b)); + b->ptr=b->buffer=(unsigned char*) _ogg_malloc(BUFFER_INCREMENT); + b->buffer[0]='\0'; + b->storage=BUFFER_INCREMENT; +} + +void oggpackB_writeinit(oggpack_buffer *b){ + oggpack_writeinit(b); +} + +void oggpack_writetrunc(oggpack_buffer *b,long bits){ + long bytes=bits>>3; + bits-=bytes*8; + b->ptr=b->buffer+bytes; + b->endbit=bits; + b->endbyte=bytes; + *b->ptr&=mask[bits]; +} + +void oggpackB_writetrunc(oggpack_buffer *b,long bits){ + long bytes=bits>>3; + bits-=bytes*8; + b->ptr=b->buffer+bytes; + b->endbit=bits; + b->endbyte=bytes; + *b->ptr&=mask8B[bits]; +} + +/* Takes only up to 32 bits. */ +void oggpack_write(oggpack_buffer *b,unsigned long value,int bits){ + if(b->endbyte+4>=b->storage){ + b->buffer=(unsigned char*) _ogg_realloc(b->buffer,b->storage+BUFFER_INCREMENT); + b->storage+=BUFFER_INCREMENT; + b->ptr=b->buffer+b->endbyte; + } + + value&=mask[bits]; + bits+=b->endbit; + + b->ptr[0]|=value<endbit; + + if(bits>=8){ + b->ptr[1]=(unsigned char)(value>>(8-b->endbit)); + if(bits>=16){ + b->ptr[2]=(unsigned char)(value>>(16-b->endbit)); + if(bits>=24){ + b->ptr[3]=(unsigned char)(value>>(24-b->endbit)); + if(bits>=32){ + if(b->endbit) + b->ptr[4]=(unsigned char)(value>>(32-b->endbit)); + else + b->ptr[4]=0; + } + } + } + } + + b->endbyte+=bits/8; + b->ptr+=bits/8; + b->endbit=bits&7; +} + +/* Takes only up to 32 bits. */ +void oggpackB_write(oggpack_buffer *b,unsigned long value,int bits){ + if(b->endbyte+4>=b->storage){ + b->buffer=(unsigned char*) _ogg_realloc(b->buffer,b->storage+BUFFER_INCREMENT); + b->storage+=BUFFER_INCREMENT; + b->ptr=b->buffer+b->endbyte; + } + + value=(value&mask[bits])<<(32-bits); + bits+=b->endbit; + + b->ptr[0]|=value>>(24+b->endbit); + + if(bits>=8){ + b->ptr[1]=(unsigned char)(value>>(16+b->endbit)); + if(bits>=16){ + b->ptr[2]=(unsigned char)(value>>(8+b->endbit)); + if(bits>=24){ + b->ptr[3]=(unsigned char)(value>>(b->endbit)); + if(bits>=32){ + if(b->endbit) + b->ptr[4]=(unsigned char)(value<<(8-b->endbit)); + else + b->ptr[4]=0; + } + } + } + } + + b->endbyte+=bits/8; + b->ptr+=bits/8; + b->endbit=bits&7; +} + +void oggpack_writealign(oggpack_buffer *b){ + int bits=8-b->endbit; + if(bits<8) + oggpack_write(b,0,bits); +} + +void oggpackB_writealign(oggpack_buffer *b){ + int bits=8-b->endbit; + if(bits<8) + oggpackB_write(b,0,bits); +} + +static void oggpack_writecopy_helper(oggpack_buffer *b, + void *source, + long bits, + void (*w)(oggpack_buffer *, + unsigned long, + int), + int msb){ + unsigned char *ptr=(unsigned char *)source; + + long bytes=bits/8; + bits-=bytes*8; + + if(b->endbit){ + int i; + /* unaligned copy. Do it the hard way. */ + for(i=0;iendbyte+bytes+1>=b->storage){ + b->storage=b->endbyte+bytes+BUFFER_INCREMENT; + b->buffer=(unsigned char*) _ogg_realloc(b->buffer,b->storage); + b->ptr=b->buffer+b->endbyte; + } + + memmove(b->ptr,source,bytes); + b->ptr+=bytes; + b->endbyte+=bytes; + *b->ptr=0; + + } + if(bits){ + if(msb) + w(b,(unsigned long)(ptr[bytes]>>(8-bits)),bits); + else + w(b,(unsigned long)(ptr[bytes]),bits); + } +} + +void oggpack_writecopy(oggpack_buffer *b,void *source,long bits){ + oggpack_writecopy_helper(b,source,bits,oggpack_write,0); +} + +void oggpackB_writecopy(oggpack_buffer *b,void *source,long bits){ + oggpack_writecopy_helper(b,source,bits,oggpackB_write,1); +} + +void oggpack_reset(oggpack_buffer *b){ + b->ptr=b->buffer; + b->buffer[0]=0; + b->endbit=b->endbyte=0; +} + +void oggpackB_reset(oggpack_buffer *b){ + oggpack_reset(b); +} + +void oggpack_writeclear(oggpack_buffer *b){ + _ogg_free(b->buffer); + memset(b,0,sizeof(*b)); +} + +void oggpackB_writeclear(oggpack_buffer *b){ + oggpack_writeclear(b); +} + +void oggpack_readinit(oggpack_buffer *b,unsigned char *buf,int bytes){ + memset(b,0,sizeof(*b)); + b->buffer=b->ptr=buf; + b->storage=bytes; +} + +void oggpackB_readinit(oggpack_buffer *b,unsigned char *buf,int bytes){ + oggpack_readinit(b,buf,bytes); +} + +/* Read in bits without advancing the bitptr; bits <= 32 */ +long oggpack_look(oggpack_buffer *b,int bits){ + unsigned long ret; + unsigned long m=mask[bits]; + + bits+=b->endbit; + + if(b->endbyte+4>=b->storage){ + /* not the main path */ + if(b->endbyte*8+bits>b->storage*8)return(-1); + } + + ret=b->ptr[0]>>b->endbit; + if(bits>8){ + ret|=b->ptr[1]<<(8-b->endbit); + if(bits>16){ + ret|=b->ptr[2]<<(16-b->endbit); + if(bits>24){ + ret|=b->ptr[3]<<(24-b->endbit); + if(bits>32 && b->endbit) + ret|=b->ptr[4]<<(32-b->endbit); + } + } + } + return(m&ret); +} + +/* Read in bits without advancing the bitptr; bits <= 32 */ +long oggpackB_look(oggpack_buffer *b,int bits){ + unsigned long ret; + int m=32-bits; + + bits+=b->endbit; + + if(b->endbyte+4>=b->storage){ + /* not the main path */ + if(b->endbyte*8+bits>b->storage*8)return(-1); + } + + ret=b->ptr[0]<<(24+b->endbit); + if(bits>8){ + ret|=b->ptr[1]<<(16+b->endbit); + if(bits>16){ + ret|=b->ptr[2]<<(8+b->endbit); + if(bits>24){ + ret|=b->ptr[3]<<(b->endbit); + if(bits>32 && b->endbit) + ret|=b->ptr[4]>>(8-b->endbit); + } + } + } + return ((ret&0xffffffff)>>(m>>1))>>((m+1)>>1); +} + +long oggpack_look1(oggpack_buffer *b){ + if(b->endbyte>=b->storage)return(-1); + return((b->ptr[0]>>b->endbit)&1); +} + +long oggpackB_look1(oggpack_buffer *b){ + if(b->endbyte>=b->storage)return(-1); + return((b->ptr[0]>>(7-b->endbit))&1); +} + +void oggpack_adv(oggpack_buffer *b,int bits){ + bits+=b->endbit; + b->ptr+=bits/8; + b->endbyte+=bits/8; + b->endbit=bits&7; +} + +void oggpackB_adv(oggpack_buffer *b,int bits){ + oggpack_adv(b,bits); +} + +void oggpack_adv1(oggpack_buffer *b){ + if(++(b->endbit)>7){ + b->endbit=0; + b->ptr++; + b->endbyte++; + } +} + +void oggpackB_adv1(oggpack_buffer *b){ + oggpack_adv1(b); +} + +/* bits <= 32 */ +long oggpack_read(oggpack_buffer *b,int bits){ + long ret; + unsigned long m=mask[bits]; + + bits+=b->endbit; + + if(b->endbyte+4>=b->storage){ + /* not the main path */ + ret=-1L; + if(b->endbyte*8+bits>b->storage*8)goto overflow; + } + + ret=b->ptr[0]>>b->endbit; + if(bits>8){ + ret|=b->ptr[1]<<(8-b->endbit); + if(bits>16){ + ret|=b->ptr[2]<<(16-b->endbit); + if(bits>24){ + ret|=b->ptr[3]<<(24-b->endbit); + if(bits>32 && b->endbit){ + ret|=b->ptr[4]<<(32-b->endbit); + } + } + } + } + ret&=m; + + overflow: + + b->ptr+=bits/8; + b->endbyte+=bits/8; + b->endbit=bits&7; + return(ret); +} + +/* bits <= 32 */ +long oggpackB_read(oggpack_buffer *b,int bits){ + long ret; + long m=32-bits; + + bits+=b->endbit; + + if(b->endbyte+4>=b->storage){ + /* not the main path */ + ret=-1L; + if(b->endbyte*8+bits>b->storage*8)goto overflow; + } + + ret=b->ptr[0]<<(24+b->endbit); + if(bits>8){ + ret|=b->ptr[1]<<(16+b->endbit); + if(bits>16){ + ret|=b->ptr[2]<<(8+b->endbit); + if(bits>24){ + ret|=b->ptr[3]<<(b->endbit); + if(bits>32 && b->endbit) + ret|=b->ptr[4]>>(8-b->endbit); + } + } + } + ret=((ret&0xffffffffUL)>>(m>>1))>>((m+1)>>1); + + overflow: + + b->ptr+=bits/8; + b->endbyte+=bits/8; + b->endbit=bits&7; + return(ret); +} + +long oggpack_read1(oggpack_buffer *b){ + long ret; + + if(b->endbyte>=b->storage){ + /* not the main path */ + ret=-1L; + goto overflow; + } + + ret=(b->ptr[0]>>b->endbit)&1; + + overflow: + + b->endbit++; + if(b->endbit>7){ + b->endbit=0; + b->ptr++; + b->endbyte++; + } + return(ret); +} + +long oggpackB_read1(oggpack_buffer *b){ + long ret; + + if(b->endbyte>=b->storage){ + /* not the main path */ + ret=-1L; + goto overflow; + } + + ret=(b->ptr[0]>>(7-b->endbit))&1; + + overflow: + + b->endbit++; + if(b->endbit>7){ + b->endbit=0; + b->ptr++; + b->endbyte++; + } + return(ret); +} + +long oggpack_bytes(oggpack_buffer *b){ + return(b->endbyte+(b->endbit+7)/8); +} + +long oggpack_bits(oggpack_buffer *b){ + return(b->endbyte*8+b->endbit); +} + +long oggpackB_bytes(oggpack_buffer *b){ + return oggpack_bytes(b); +} + +long oggpackB_bits(oggpack_buffer *b){ + return oggpack_bits(b); +} + +unsigned char *oggpack_get_buffer(oggpack_buffer *b){ + return(b->buffer); +} + +unsigned char *oggpackB_get_buffer(oggpack_buffer *b){ + return oggpack_get_buffer(b); +} + +/* Self test of the bitwise routines; everything else is based on + them, so they damned well better be solid. */ + +#ifdef _V_SELFTEST +#include + +static int ilog(unsigned int v){ + int ret=0; + while(v){ + ret++; + v>>=1; + } + return(ret); +} + +oggpack_buffer o; +oggpack_buffer r; + +void report(char *in){ + fprintf(stderr,"%s",in); + exit(1); +} + +void cliptest(unsigned long *b,int vals,int bits,int *comp,int compsize){ + long bytes,i; + unsigned char *buffer; + + oggpack_reset(&o); + for(i=0;i +#include + +/* A complete description of Ogg framing exists in docs/framing.html */ + +int ogg_page_version(ogg_page *og){ + return((int)(og->header[4])); +} + +int ogg_page_continued(ogg_page *og){ + return((int)(og->header[5]&0x01)); +} + +int ogg_page_bos(ogg_page *og){ + return((int)(og->header[5]&0x02)); +} + +int ogg_page_eos(ogg_page *og){ + return((int)(og->header[5]&0x04)); +} + +ogg_int64_t ogg_page_granulepos(ogg_page *og){ + unsigned char *page=og->header; + ogg_int64_t granulepos=page[13]&(0xff); + granulepos= (granulepos<<8)|(page[12]&0xff); + granulepos= (granulepos<<8)|(page[11]&0xff); + granulepos= (granulepos<<8)|(page[10]&0xff); + granulepos= (granulepos<<8)|(page[9]&0xff); + granulepos= (granulepos<<8)|(page[8]&0xff); + granulepos= (granulepos<<8)|(page[7]&0xff); + granulepos= (granulepos<<8)|(page[6]&0xff); + return(granulepos); +} + +int ogg_page_serialno(ogg_page *og){ + return(og->header[14] | + (og->header[15]<<8) | + (og->header[16]<<16) | + (og->header[17]<<24)); +} + +long ogg_page_pageno(ogg_page *og){ + return(og->header[18] | + (og->header[19]<<8) | + (og->header[20]<<16) | + (og->header[21]<<24)); +} + +/* returns the number of packets that are completed on this page (if + the leading packet is begun on a previous page, but ends on this + page, it's counted */ + +/* NOTE: +If a page consists of a packet begun on a previous page, and a new +packet begun (but not completed) on this page, the return will be: + ogg_page_packets(page) ==1, + ogg_page_continued(page) !=0 + +If a page happens to be a single packet that was begun on a +previous page, and spans to the next page (in the case of a three or +more page packet), the return will be: + ogg_page_packets(page) ==0, + ogg_page_continued(page) !=0 +*/ + +int ogg_page_packets(ogg_page *og){ + int i,n=og->header[26],count=0; + for(i=0;iheader[27+i]<255)count++; + return(count); +} + +#if 0 +/* helper to initialize lookup for direct-table CRC (illustrative; we + use the static init below) */ + +static ogg_uint32_t _ogg_crc_entry(unsigned long index){ + int i; + unsigned long r; + + r = index << 24; + for (i=0; i<8; i++) + if (r & 0x80000000UL) + r = (r << 1) ^ 0x04c11db7; /* The same as the ethernet generator + polynomial, although we use an + unreflected alg and an init/final + of 0, not 0xffffffff */ + else + r<<=1; + return (r & 0xffffffffUL); +} +#endif + +static const ogg_uint32_t crc_lookup[256]={ + 0x00000000,0x04c11db7,0x09823b6e,0x0d4326d9, + 0x130476dc,0x17c56b6b,0x1a864db2,0x1e475005, + 0x2608edb8,0x22c9f00f,0x2f8ad6d6,0x2b4bcb61, + 0x350c9b64,0x31cd86d3,0x3c8ea00a,0x384fbdbd, + 0x4c11db70,0x48d0c6c7,0x4593e01e,0x4152fda9, + 0x5f15adac,0x5bd4b01b,0x569796c2,0x52568b75, + 0x6a1936c8,0x6ed82b7f,0x639b0da6,0x675a1011, + 0x791d4014,0x7ddc5da3,0x709f7b7a,0x745e66cd, + 0x9823b6e0,0x9ce2ab57,0x91a18d8e,0x95609039, + 0x8b27c03c,0x8fe6dd8b,0x82a5fb52,0x8664e6e5, + 0xbe2b5b58,0xbaea46ef,0xb7a96036,0xb3687d81, + 0xad2f2d84,0xa9ee3033,0xa4ad16ea,0xa06c0b5d, + 0xd4326d90,0xd0f37027,0xddb056fe,0xd9714b49, + 0xc7361b4c,0xc3f706fb,0xceb42022,0xca753d95, + 0xf23a8028,0xf6fb9d9f,0xfbb8bb46,0xff79a6f1, + 0xe13ef6f4,0xe5ffeb43,0xe8bccd9a,0xec7dd02d, + 0x34867077,0x30476dc0,0x3d044b19,0x39c556ae, + 0x278206ab,0x23431b1c,0x2e003dc5,0x2ac12072, + 0x128e9dcf,0x164f8078,0x1b0ca6a1,0x1fcdbb16, + 0x018aeb13,0x054bf6a4,0x0808d07d,0x0cc9cdca, + 0x7897ab07,0x7c56b6b0,0x71159069,0x75d48dde, + 0x6b93dddb,0x6f52c06c,0x6211e6b5,0x66d0fb02, + 0x5e9f46bf,0x5a5e5b08,0x571d7dd1,0x53dc6066, + 0x4d9b3063,0x495a2dd4,0x44190b0d,0x40d816ba, + 0xaca5c697,0xa864db20,0xa527fdf9,0xa1e6e04e, + 0xbfa1b04b,0xbb60adfc,0xb6238b25,0xb2e29692, + 0x8aad2b2f,0x8e6c3698,0x832f1041,0x87ee0df6, + 0x99a95df3,0x9d684044,0x902b669d,0x94ea7b2a, + 0xe0b41de7,0xe4750050,0xe9362689,0xedf73b3e, + 0xf3b06b3b,0xf771768c,0xfa325055,0xfef34de2, + 0xc6bcf05f,0xc27dede8,0xcf3ecb31,0xcbffd686, + 0xd5b88683,0xd1799b34,0xdc3abded,0xd8fba05a, + 0x690ce0ee,0x6dcdfd59,0x608edb80,0x644fc637, + 0x7a089632,0x7ec98b85,0x738aad5c,0x774bb0eb, + 0x4f040d56,0x4bc510e1,0x46863638,0x42472b8f, + 0x5c007b8a,0x58c1663d,0x558240e4,0x51435d53, + 0x251d3b9e,0x21dc2629,0x2c9f00f0,0x285e1d47, + 0x36194d42,0x32d850f5,0x3f9b762c,0x3b5a6b9b, + 0x0315d626,0x07d4cb91,0x0a97ed48,0x0e56f0ff, + 0x1011a0fa,0x14d0bd4d,0x19939b94,0x1d528623, + 0xf12f560e,0xf5ee4bb9,0xf8ad6d60,0xfc6c70d7, + 0xe22b20d2,0xe6ea3d65,0xeba91bbc,0xef68060b, + 0xd727bbb6,0xd3e6a601,0xdea580d8,0xda649d6f, + 0xc423cd6a,0xc0e2d0dd,0xcda1f604,0xc960ebb3, + 0xbd3e8d7e,0xb9ff90c9,0xb4bcb610,0xb07daba7, + 0xae3afba2,0xaafbe615,0xa7b8c0cc,0xa379dd7b, + 0x9b3660c6,0x9ff77d71,0x92b45ba8,0x9675461f, + 0x8832161a,0x8cf30bad,0x81b02d74,0x857130c3, + 0x5d8a9099,0x594b8d2e,0x5408abf7,0x50c9b640, + 0x4e8ee645,0x4a4ffbf2,0x470cdd2b,0x43cdc09c, + 0x7b827d21,0x7f436096,0x7200464f,0x76c15bf8, + 0x68860bfd,0x6c47164a,0x61043093,0x65c52d24, + 0x119b4be9,0x155a565e,0x18197087,0x1cd86d30, + 0x029f3d35,0x065e2082,0x0b1d065b,0x0fdc1bec, + 0x3793a651,0x3352bbe6,0x3e119d3f,0x3ad08088, + 0x2497d08d,0x2056cd3a,0x2d15ebe3,0x29d4f654, + 0xc5a92679,0xc1683bce,0xcc2b1d17,0xc8ea00a0, + 0xd6ad50a5,0xd26c4d12,0xdf2f6bcb,0xdbee767c, + 0xe3a1cbc1,0xe760d676,0xea23f0af,0xeee2ed18, + 0xf0a5bd1d,0xf464a0aa,0xf9278673,0xfde69bc4, + 0x89b8fd09,0x8d79e0be,0x803ac667,0x84fbdbd0, + 0x9abc8bd5,0x9e7d9662,0x933eb0bb,0x97ffad0c, + 0xafb010b1,0xab710d06,0xa6322bdf,0xa2f33668, + 0xbcb4666d,0xb8757bda,0xb5365d03,0xb1f740b4}; + +/* init the encode/decode logical stream state */ + +int ogg_stream_init(ogg_stream_state *os,int serialno){ + if(os){ + memset(os,0,sizeof(*os)); + os->body_storage=16*1024; + os->body_data=(unsigned char*) _ogg_malloc(os->body_storage*sizeof(*os->body_data)); + + os->lacing_storage=1024; + os->lacing_vals=(int*) _ogg_malloc(os->lacing_storage*sizeof(*os->lacing_vals)); + os->granule_vals=(ogg_int64_t*) _ogg_malloc(os->lacing_storage*sizeof(*os->granule_vals)); + + os->serialno=serialno; + + return(0); + } + return(-1); +} + +/* _clear does not free os, only the non-flat storage within */ +int ogg_stream_clear(ogg_stream_state *os){ + if(os){ + if(os->body_data)_ogg_free(os->body_data); + if(os->lacing_vals)_ogg_free(os->lacing_vals); + if(os->granule_vals)_ogg_free(os->granule_vals); + + memset(os,0,sizeof(*os)); + } + return(0); +} + +int ogg_stream_destroy(ogg_stream_state *os){ + if(os){ + ogg_stream_clear(os); + _ogg_free(os); + } + return(0); +} + +/* Helpers for ogg_stream_encode; this keeps the structure and + what's happening fairly clear */ + +static void _os_body_expand(ogg_stream_state *os,int needed){ + if(os->body_storage<=os->body_fill+needed){ + os->body_storage+=(needed+1024); + os->body_data=(unsigned char*) _ogg_realloc(os->body_data,os->body_storage*sizeof(*os->body_data)); + } +} + +static void _os_lacing_expand(ogg_stream_state *os,int needed){ + if(os->lacing_storage<=os->lacing_fill+needed){ + os->lacing_storage+=(needed+32); + os->lacing_vals=(int*)_ogg_realloc(os->lacing_vals,os->lacing_storage*sizeof(*os->lacing_vals)); + os->granule_vals=(ogg_int64_t*)_ogg_realloc(os->granule_vals,os->lacing_storage*sizeof(*os->granule_vals)); + } +} + +/* checksum the page */ +/* Direct table CRC; note that this will be faster in the future if we + perform the checksum silmultaneously with other copies */ + +void ogg_page_checksum_set(ogg_page *og){ + if(og){ + ogg_uint32_t crc_reg=0; + int i; + + /* safety; needed for API behavior, but not framing code */ + og->header[22]=0; + og->header[23]=0; + og->header[24]=0; + og->header[25]=0; + + for(i=0;iheader_len;i++) + crc_reg=(crc_reg<<8)^crc_lookup[((crc_reg >> 24)&0xff)^og->header[i]]; + for(i=0;ibody_len;i++) + crc_reg=(crc_reg<<8)^crc_lookup[((crc_reg >> 24)&0xff)^og->body[i]]; + + og->header[22]=(unsigned char)(crc_reg&0xff); + og->header[23]=(unsigned char)((crc_reg>>8)&0xff); + og->header[24]=(unsigned char)((crc_reg>>16)&0xff); + og->header[25]=(unsigned char)((crc_reg>>24)&0xff); + } +} + +/* submit data to the internal buffer of the framing engine */ +int ogg_stream_packetin(ogg_stream_state *os,ogg_packet *op){ + int lacing_vals=op->bytes/255+1,i; + + if(os->body_returned){ + /* advance packet data according to the body_returned pointer. We + had to keep it around to return a pointer into the buffer last + call */ + + os->body_fill-=os->body_returned; + if(os->body_fill) + memmove(os->body_data,os->body_data+os->body_returned, + os->body_fill); + os->body_returned=0; + } + + /* make sure we have the buffer storage */ + _os_body_expand(os,op->bytes); + _os_lacing_expand(os,lacing_vals); + + /* Copy in the submitted packet. Yes, the copy is a waste; this is + the liability of overly clean abstraction for the time being. It + will actually be fairly easy to eliminate the extra copy in the + future */ + + memcpy(os->body_data+os->body_fill,op->packet,op->bytes); + os->body_fill+=op->bytes; + + /* Store lacing vals for this packet */ + for(i=0;ilacing_vals[os->lacing_fill+i]=255; + os->granule_vals[os->lacing_fill+i]=os->granulepos; + } + os->lacing_vals[os->lacing_fill+i]=(op->bytes)%255; + os->granulepos=os->granule_vals[os->lacing_fill+i]=op->granulepos; + + /* flag the first segment as the beginning of the packet */ + os->lacing_vals[os->lacing_fill]|= 0x100; + + os->lacing_fill+=lacing_vals; + + /* for the sake of completeness */ + os->packetno++; + + if(op->e_o_s)os->e_o_s=1; + + return(0); +} + +/* This will flush remaining packets into a page (returning nonzero), + even if there is not enough data to trigger a flush normally + (undersized page). If there are no packets or partial packets to + flush, ogg_stream_flush returns 0. Note that ogg_stream_flush will + try to flush a normal sized page like ogg_stream_pageout; a call to + ogg_stream_flush does not guarantee that all packets have flushed. + Only a return value of 0 from ogg_stream_flush indicates all packet + data is flushed into pages. + + since ogg_stream_flush will flush the last page in a stream even if + it's undersized, you almost certainly want to use ogg_stream_pageout + (and *not* ogg_stream_flush) unless you specifically need to flush + an page regardless of size in the middle of a stream. */ + +int ogg_stream_flush(ogg_stream_state *os,ogg_page *og){ + int i; + int vals=0; + int maxvals=(os->lacing_fill>255?255:os->lacing_fill); + int bytes=0; + long acc=0; + ogg_int64_t granule_pos=-1; + + if(maxvals==0)return(0); + + /* construct a page */ + /* decide how many segments to include */ + + /* If this is the initial header case, the first page must only include + the initial header packet */ + if(os->b_o_s==0){ /* 'initial header page' case */ + granule_pos=0; + for(vals=0;valslacing_vals[vals]&0x0ff)<255){ + vals++; + break; + } + } + }else{ + for(vals=0;vals4096)break; + acc+=os->lacing_vals[vals]&0x0ff; + if((os->lacing_vals[vals]&0xff)<255) + granule_pos=os->granule_vals[vals]; + } + } + + /* construct the header in temp storage */ + memcpy(os->header,"OggS",4); + + /* stream structure version */ + os->header[4]=0x00; + + /* continued packet flag? */ + os->header[5]=0x00; + if((os->lacing_vals[0]&0x100)==0)os->header[5]|=0x01; + /* first page flag? */ + if(os->b_o_s==0)os->header[5]|=0x02; + /* last page flag? */ + if(os->e_o_s && os->lacing_fill==vals)os->header[5]|=0x04; + os->b_o_s=1; + + /* 64 bits of PCM position */ + for(i=6;i<14;i++){ + os->header[i]=(unsigned char)(granule_pos&0xff); + granule_pos>>=8; + } + + /* 32 bits of stream serial number */ + { + long serialno=os->serialno; + for(i=14;i<18;i++){ + os->header[i]=(unsigned char)(serialno&0xff); + serialno>>=8; + } + } + + /* 32 bits of page counter (we have both counter and page header + because this val can roll over) */ + if(os->pageno==-1)os->pageno=0; /* because someone called + stream_reset; this would be a + strange thing to do in an + encode stream, but it has + plausible uses */ + { + long pageno=os->pageno++; + for(i=18;i<22;i++){ + os->header[i]=(unsigned char)(pageno&0xff); + pageno>>=8; + } + } + + /* zero for computation; filled in later */ + os->header[22]=0; + os->header[23]=0; + os->header[24]=0; + os->header[25]=0; + + /* segment table */ + os->header[26]=(unsigned char)(vals&0xff); + for(i=0;iheader[i+27]=(unsigned char)(os->lacing_vals[i]&0xff); + + /* set pointers in the ogg_page struct */ + og->header=os->header; + og->header_len=os->header_fill=vals+27; + og->body=os->body_data+os->body_returned; + og->body_len=bytes; + + /* advance the lacing data and set the body_returned pointer */ + + os->lacing_fill-=vals; + memmove(os->lacing_vals,os->lacing_vals+vals,os->lacing_fill*sizeof(*os->lacing_vals)); + memmove(os->granule_vals,os->granule_vals+vals,os->lacing_fill*sizeof(*os->granule_vals)); + os->body_returned+=bytes; + + /* calculate the checksum */ + + ogg_page_checksum_set(og); + + /* done */ + return(1); +} + +/* This constructs pages from buffered packet segments. The pointers +returned are to static buffers; do not free. The returned buffers are +good only until the next call (using the same ogg_stream_state) */ + +int ogg_stream_pageout(ogg_stream_state *os, ogg_page *og){ + + if((os->e_o_s&&os->lacing_fill) || /* 'were done, now flush' case */ + os->body_fill-os->body_returned > 4096 ||/* 'page nominal size' case */ + os->lacing_fill>=255 || /* 'segment table full' case */ + (os->lacing_fill&&!os->b_o_s)){ /* 'initial header page' case */ + + return(ogg_stream_flush(os,og)); + } + + /* not enough data to construct a page and not end of stream */ + return(0); +} + +int ogg_stream_eos(ogg_stream_state *os){ + return os->e_o_s; +} + +/* DECODING PRIMITIVES: packet streaming layer **********************/ + +/* This has two layers to place more of the multi-serialno and paging + control in the application's hands. First, we expose a data buffer + using ogg_sync_buffer(). The app either copies into the + buffer, or passes it directly to read(), etc. We then call + ogg_sync_wrote() to tell how many bytes we just added. + + Pages are returned (pointers into the buffer in ogg_sync_state) + by ogg_sync_pageout(). The page is then submitted to + ogg_stream_pagein() along with the appropriate + ogg_stream_state* (ie, matching serialno). We then get raw + packets out calling ogg_stream_packetout() with a + ogg_stream_state. */ + +/* initialize the struct to a known state */ +int ogg_sync_init(ogg_sync_state *oy){ + if(oy){ + memset(oy,0,sizeof(*oy)); + } + return(0); +} + +/* clear non-flat storage within */ +int ogg_sync_clear(ogg_sync_state *oy){ + if(oy){ + if(oy->data)_ogg_free(oy->data); + ogg_sync_init(oy); + } + return(0); +} + +int ogg_sync_destroy(ogg_sync_state *oy){ + if(oy){ + ogg_sync_clear(oy); + _ogg_free(oy); + } + return(0); +} + +char *ogg_sync_buffer(ogg_sync_state *oy, long size){ + + /* first, clear out any space that has been previously returned */ + if(oy->returned){ + oy->fill-=oy->returned; + if(oy->fill>0) + memmove(oy->data,oy->data+oy->returned,oy->fill); + oy->returned=0; + } + + if(size>oy->storage-oy->fill){ + /* We need to extend the internal buffer */ + long newsize=size+oy->fill+4096; /* an extra page to be nice */ + + if(oy->data) + oy->data=(unsigned char*) _ogg_realloc(oy->data,newsize); + else + oy->data=(unsigned char*) _ogg_malloc(newsize); + oy->storage=newsize; + } + + /* expose a segment at least as large as requested at the fill mark */ + return((char *)oy->data+oy->fill); +} + +int ogg_sync_wrote(ogg_sync_state *oy, long bytes){ + if(oy->fill+bytes>oy->storage)return(-1); + oy->fill+=bytes; + return(0); +} + +/* sync the stream. This is meant to be useful for finding page + boundaries. + + return values for this: + -n) skipped n bytes + 0) page not ready; more data (no bytes skipped) + n) page synced at current location; page length n bytes + +*/ + +long ogg_sync_pageseek(ogg_sync_state *oy,ogg_page *og){ + unsigned char *page=oy->data+oy->returned; + unsigned char *next; + long bytes=oy->fill-oy->returned; + + if(oy->headerbytes==0){ + int headerbytes,i; + if(bytes<27)return(0); /* not enough for a header */ + + /* verify capture pattern */ + if(memcmp(page,"OggS",4))goto sync_fail; + + headerbytes=page[26]+27; + if(bytesbodybytes+=page[27+i]; + oy->headerbytes=headerbytes; + } + + if(oy->bodybytes+oy->headerbytes>bytes)return(0); + + /* The whole test page is buffered. Verify the checksum */ + { + /* Grab the checksum bytes, set the header field to zero */ + char chksum[4]; + ogg_page log; + + memcpy(chksum,page+22,4); + memset(page+22,0,4); + + /* set up a temp page struct and recompute the checksum */ + log.header=page; + log.header_len=oy->headerbytes; + log.body=page+oy->headerbytes; + log.body_len=oy->bodybytes; + ogg_page_checksum_set(&log); + + /* Compare */ + if(memcmp(chksum,page+22,4)){ + /* D'oh. Mismatch! Corrupt page (or miscapture and not a page + at all) */ + /* replace the computed checksum with the one actually read in */ + memcpy(page+22,chksum,4); + + /* Bad checksum. Lose sync */ + goto sync_fail; + } + } + + /* yes, have a whole page all ready to go */ + { + unsigned char *page=oy->data+oy->returned; + long bytes; + + if(og){ + og->header=page; + og->header_len=oy->headerbytes; + og->body=page+oy->headerbytes; + og->body_len=oy->bodybytes; + } + + oy->unsynced=0; + oy->returned+=(bytes=oy->headerbytes+oy->bodybytes); + oy->headerbytes=0; + oy->bodybytes=0; + return(bytes); + } + + sync_fail: + + oy->headerbytes=0; + oy->bodybytes=0; + + /* search for possible capture */ + next=(unsigned char*)memchr(page+1,'O',bytes-1); + if(!next) + next=oy->data+oy->fill; + + oy->returned=next-oy->data; + return(-(next-page)); +} + +/* sync the stream and get a page. Keep trying until we find a page. + Supress 'sync errors' after reporting the first. + + return values: + -1) recapture (hole in data) + 0) need more data + 1) page returned + + Returns pointers into buffered data; invalidated by next call to + _stream, _clear, _init, or _buffer */ + +int ogg_sync_pageout(ogg_sync_state *oy, ogg_page *og){ + + /* all we need to do is verify a page at the head of the stream + buffer. If it doesn't verify, we look for the next potential + frame */ + + for(;;){ + long ret=ogg_sync_pageseek(oy,og); + if(ret>0){ + /* have a page */ + return(1); + } + if(ret==0){ + /* need more data */ + return(0); + } + + /* head did not start a synced page... skipped some bytes */ + if(!oy->unsynced){ + oy->unsynced=1; + return(-1); + } + + /* loop. keep looking */ + + } +} + +/* add the incoming page to the stream state; we decompose the page + into packet segments here as well. */ + +int ogg_stream_pagein(ogg_stream_state *os, ogg_page *og){ + unsigned char *header=og->header; + unsigned char *body=og->body; + long bodysize=og->body_len; + int segptr=0; + + int version=ogg_page_version(og); + int continued=ogg_page_continued(og); + int bos=ogg_page_bos(og); + int eos=ogg_page_eos(og); + ogg_int64_t granulepos=ogg_page_granulepos(og); + int serialno=ogg_page_serialno(og); + long pageno=ogg_page_pageno(og); + int segments=header[26]; + + /* clean up 'returned data' */ + { + long lr=os->lacing_returned; + long br=os->body_returned; + + /* body data */ + if(br){ + os->body_fill-=br; + if(os->body_fill) + memmove(os->body_data,os->body_data+br,os->body_fill); + os->body_returned=0; + } + + if(lr){ + /* segment table */ + if(os->lacing_fill-lr){ + memmove(os->lacing_vals,os->lacing_vals+lr, + (os->lacing_fill-lr)*sizeof(*os->lacing_vals)); + memmove(os->granule_vals,os->granule_vals+lr, + (os->lacing_fill-lr)*sizeof(*os->granule_vals)); + } + os->lacing_fill-=lr; + os->lacing_packet-=lr; + os->lacing_returned=0; + } + } + + /* check the serial number */ + if(serialno!=os->serialno)return(-1); + if(version>0)return(-1); + + _os_lacing_expand(os,segments+1); + + /* are we in sequence? */ + if(pageno!=os->pageno){ + int i; + + /* unroll previous partial packet (if any) */ + for(i=os->lacing_packet;ilacing_fill;i++) + os->body_fill-=os->lacing_vals[i]&0xff; + os->lacing_fill=os->lacing_packet; + + /* make a note of dropped data in segment table */ + if(os->pageno!=-1){ + os->lacing_vals[os->lacing_fill++]=0x400; + os->lacing_packet++; + } + } + + /* are we a 'continued packet' page? If so, we may need to skip + some segments */ + if(continued){ + if(os->lacing_fill<1 || + os->lacing_vals[os->lacing_fill-1]==0x400){ + bos=0; + for(;segptrbody_data+os->body_fill,body,bodysize); + os->body_fill+=bodysize; + } + + { + int saved=-1; + while(segptrlacing_vals[os->lacing_fill]=val; + os->granule_vals[os->lacing_fill]=-1; + + if(bos){ + os->lacing_vals[os->lacing_fill]|=0x100; + bos=0; + } + + if(val<255)saved=os->lacing_fill; + + os->lacing_fill++; + segptr++; + + if(val<255)os->lacing_packet=os->lacing_fill; + } + + /* set the granulepos on the last granuleval of the last full packet */ + if(saved!=-1){ + os->granule_vals[saved]=granulepos; + } + + } + + if(eos){ + os->e_o_s=1; + if(os->lacing_fill>0) + os->lacing_vals[os->lacing_fill-1]|=0x200; + } + + os->pageno=pageno+1; + + return(0); +} + +/* clear things to an initial state. Good to call, eg, before seeking */ +int ogg_sync_reset(ogg_sync_state *oy){ + oy->fill=0; + oy->returned=0; + oy->unsynced=0; + oy->headerbytes=0; + oy->bodybytes=0; + return(0); +} + +int ogg_stream_reset(ogg_stream_state *os){ + os->body_fill=0; + os->body_returned=0; + + os->lacing_fill=0; + os->lacing_packet=0; + os->lacing_returned=0; + + os->header_fill=0; + + os->e_o_s=0; + os->b_o_s=0; + os->pageno=-1; + os->packetno=0; + os->granulepos=0; + + return(0); +} + +int ogg_stream_reset_serialno(ogg_stream_state *os,int serialno){ + ogg_stream_reset(os); + os->serialno=serialno; + return(0); +} + +static int _packetout(ogg_stream_state *os,ogg_packet *op,int adv){ + + /* The last part of decode. We have the stream broken into packet + segments. Now we need to group them into packets (or return the + out of sync markers) */ + + int ptr=os->lacing_returned; + + if(os->lacing_packet<=ptr)return(0); + + if(os->lacing_vals[ptr]&0x400){ + /* we need to tell the codec there's a gap; it might need to + handle previous packet dependencies. */ + os->lacing_returned++; + os->packetno++; + return(-1); + } + + if(!op && !adv)return(1); /* just using peek as an inexpensive way + to ask if there's a whole packet + waiting */ + + /* Gather the whole packet. We'll have no holes or a partial packet */ + { + int size=os->lacing_vals[ptr]&0xff; + int bytes=size; + int eos=os->lacing_vals[ptr]&0x200; /* last packet of the stream? */ + int bos=os->lacing_vals[ptr]&0x100; /* first packet of the stream? */ + + while(size==255){ + int val=os->lacing_vals[++ptr]; + size=val&0xff; + if(val&0x200)eos=0x200; + bytes+=size; + } + + if(op){ + op->e_o_s=eos; + op->b_o_s=bos; + op->packet=os->body_data+os->body_returned; + op->packetno=os->packetno; + op->granulepos=os->granule_vals[ptr]; + op->bytes=bytes; + } + + if(adv){ + os->body_returned+=bytes; + os->lacing_returned=ptr+1; + os->packetno++; + } + } + return(1); +} + +int ogg_stream_packetout(ogg_stream_state *os,ogg_packet *op){ + return _packetout(os,op,1); +} + +int ogg_stream_packetpeek(ogg_stream_state *os,ogg_packet *op){ + return _packetout(os,op,0); +} + +void ogg_packet_clear(ogg_packet *op) { + _ogg_free(op->packet); + memset(op, 0, sizeof(*op)); +} + +#ifdef _V_SELFTEST +#include + +ogg_stream_state os_en, os_de; +ogg_sync_state oy; + +void checkpacket(ogg_packet *op,int len, int no, int pos){ + long j; + static int sequence=0; + static int lastno=0; + + if(op->bytes!=len){ + fprintf(stderr,"incorrect packet length!\n"); + exit(1); + } + if(op->granulepos!=pos){ + fprintf(stderr,"incorrect packet position!\n"); + exit(1); + } + + /* packet number just follows sequence/gap; adjust the input number + for that */ + if(no==0){ + sequence=0; + }else{ + sequence++; + if(no>lastno+1) + sequence++; + } + lastno=no; + if(op->packetno!=sequence){ + fprintf(stderr,"incorrect packet sequence %ld != %d\n", + (long)(op->packetno),sequence); + exit(1); + } + + /* Test data */ + for(j=0;jbytes;j++) + if(op->packet[j]!=((j+no)&0xff)){ + fprintf(stderr,"body data mismatch (1) at pos %ld: %x!=%lx!\n\n", + j,op->packet[j],(j+no)&0xff); + exit(1); + } +} + +void check_page(unsigned char *data,const int *header,ogg_page *og){ + long j; + /* Test data */ + for(j=0;jbody_len;j++) + if(og->body[j]!=data[j]){ + fprintf(stderr,"body data mismatch (2) at pos %ld: %x!=%x!\n\n", + j,data[j],og->body[j]); + exit(1); + } + + /* Test header */ + for(j=0;jheader_len;j++){ + if(og->header[j]!=header[j]){ + fprintf(stderr,"header content mismatch at pos %ld:\n",j); + for(j=0;jheader[j]); + fprintf(stderr,"\n"); + exit(1); + } + } + if(og->header_len!=header[26]+27){ + fprintf(stderr,"header length incorrect! (%ld!=%d)\n", + og->header_len,header[26]+27); + exit(1); + } +} + +void print_header(ogg_page *og){ + int j; + fprintf(stderr,"\nHEADER:\n"); + fprintf(stderr," capture: %c %c %c %c version: %d flags: %x\n", + og->header[0],og->header[1],og->header[2],og->header[3], + (int)og->header[4],(int)og->header[5]); + + fprintf(stderr," granulepos: %d serialno: %d pageno: %ld\n", + (og->header[9]<<24)|(og->header[8]<<16)| + (og->header[7]<<8)|og->header[6], + (og->header[17]<<24)|(og->header[16]<<16)| + (og->header[15]<<8)|og->header[14], + ((long)(og->header[21])<<24)|(og->header[20]<<16)| + (og->header[19]<<8)|og->header[18]); + + fprintf(stderr," checksum: %02x:%02x:%02x:%02x\n segments: %d (", + (int)og->header[22],(int)og->header[23], + (int)og->header[24],(int)og->header[25], + (int)og->header[26]); + + for(j=27;jheader_len;j++) + fprintf(stderr,"%d ",(int)og->header[j]); + fprintf(stderr,")\n\n"); +} + +void copy_page(ogg_page *og){ + unsigned char *temp=_ogg_malloc(og->header_len); + memcpy(temp,og->header,og->header_len); + og->header=temp; + + temp=_ogg_malloc(og->body_len); + memcpy(temp,og->body,og->body_len); + og->body=temp; +} + +void free_page(ogg_page *og){ + _ogg_free (og->header); + _ogg_free (og->body); +} + +void error(void){ + fprintf(stderr,"error!\n"); + exit(1); +} + +/* 17 only */ +const int head1_0[] = {0x4f,0x67,0x67,0x53,0,0x06, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0x15,0xed,0xec,0x91, + 1, + 17}; + +/* 17, 254, 255, 256, 500, 510, 600 byte, pad */ +const int head1_1[] = {0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0x59,0x10,0x6c,0x2c, + 1, + 17}; +const int head2_1[] = {0x4f,0x67,0x67,0x53,0,0x04, + 0x07,0x18,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0x89,0x33,0x85,0xce, + 13, + 254,255,0,255,1,255,245,255,255,0, + 255,255,90}; + +/* nil packets; beginning,middle,end */ +const int head1_2[] = {0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0xff,0x7b,0x23,0x17, + 1, + 0}; +const int head2_2[] = {0x4f,0x67,0x67,0x53,0,0x04, + 0x07,0x28,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0x5c,0x3f,0x66,0xcb, + 17, + 17,254,255,0,0,255,1,0,255,245,255,255,0, + 255,255,90,0}; + +/* large initial packet */ +const int head1_3[] = {0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0x01,0x27,0x31,0xaa, + 18, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,10}; + +const int head2_3[] = {0x4f,0x67,0x67,0x53,0,0x04, + 0x07,0x08,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0x7f,0x4e,0x8a,0xd2, + 4, + 255,4,255,0}; + +/* continuing packet test */ +const int head1_4[] = {0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0xff,0x7b,0x23,0x17, + 1, + 0}; + +const int head2_4[] = {0x4f,0x67,0x67,0x53,0,0x00, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0x54,0x05,0x51,0xc8, + 17, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255}; + +const int head3_4[] = {0x4f,0x67,0x67,0x53,0,0x05, + 0x07,0x0c,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,2,0,0,0, + 0xc8,0xc3,0xcb,0xed, + 5, + 10,255,4,255,0}; + +/* page with the 255 segment limit */ +const int head1_5[] = {0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0xff,0x7b,0x23,0x17, + 1, + 0}; + +const int head2_5[] = {0x4f,0x67,0x67,0x53,0,0x00, + 0x07,0xfc,0x03,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0xed,0x2a,0x2e,0xa7, + 255, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10}; + +const int head3_5[] = {0x4f,0x67,0x67,0x53,0,0x04, + 0x07,0x00,0x04,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,2,0,0,0, + 0x6c,0x3b,0x82,0x3d, + 1, + 50}; + +/* packet that overspans over an entire page */ +const int head1_6[] = {0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0xff,0x7b,0x23,0x17, + 1, + 0}; + +const int head2_6[] = {0x4f,0x67,0x67,0x53,0,0x00, + 0x07,0x04,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0x3c,0xd9,0x4d,0x3f, + 17, + 100,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255}; + +const int head3_6[] = {0x4f,0x67,0x67,0x53,0,0x01, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0x01,0x02,0x03,0x04,2,0,0,0, + 0x01,0xd2,0xe5,0xe5, + 17, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255}; + +const int head4_6[] = {0x4f,0x67,0x67,0x53,0,0x05, + 0x07,0x10,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,3,0,0,0, + 0xef,0xdd,0x88,0xde, + 7, + 255,255,75,255,4,255,0}; + +/* packet that overspans over an entire page */ +const int head1_7[] = {0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0xff,0x7b,0x23,0x17, + 1, + 0}; + +const int head2_7[] = {0x4f,0x67,0x67,0x53,0,0x00, + 0x07,0x04,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0x3c,0xd9,0x4d,0x3f, + 17, + 100,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255}; + +const int head3_7[] = {0x4f,0x67,0x67,0x53,0,0x05, + 0x07,0x08,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,2,0,0,0, + 0xd4,0xe0,0x60,0xe5, + 1,0}; + +void test_pack(const int *pl, const int **headers, int byteskip, + int pageskip, int packetskip){ + unsigned char *data=_ogg_malloc(1024*1024); /* for scripted test cases only */ + long inptr=0; + long outptr=0; + long deptr=0; + long depacket=0; + long granule_pos=7,pageno=0; + int i,j,packets,pageout=pageskip; + int eosflag=0; + int bosflag=0; + + int byteskipcount=0; + + ogg_stream_reset(&os_en); + ogg_stream_reset(&os_de); + ogg_sync_reset(&oy); + + for(packets=0;packetsbyteskip){ + memcpy(next,og.header,byteskipcount-byteskip); + next+=byteskipcount-byteskip; + byteskipcount=byteskip; + } + + byteskipcount+=og.body_len; + if(byteskipcount>byteskip){ + memcpy(next,og.body,byteskipcount-byteskip); + next+=byteskipcount-byteskip; + byteskipcount=byteskip; + } + + ogg_sync_wrote(&oy,next-buf); + + while(1){ + int ret=ogg_sync_pageout(&oy,&og_de); + if(ret==0)break; + if(ret<0)continue; + /* got a page. Happy happy. Verify that it's good. */ + + fprintf(stderr,"(%ld), ",pageout); + + check_page(data+deptr,headers[pageout],&og_de); + deptr+=og_de.body_len; + pageout++; + + /* submit it to deconstitution */ + ogg_stream_pagein(&os_de,&og_de); + + /* packets out? */ + while(ogg_stream_packetpeek(&os_de,&op_de2)>0){ + ogg_stream_packetpeek(&os_de,NULL); + ogg_stream_packetout(&os_de,&op_de); /* just catching them all */ + + /* verify peek and out match */ + if(memcmp(&op_de,&op_de2,sizeof(op_de))){ + fprintf(stderr,"packetout != packetpeek! pos=%ld\n", + depacket); + exit(1); + } + + /* verify the packet! */ + /* check data */ + if(memcmp(data+depacket,op_de.packet,op_de.bytes)){ + fprintf(stderr,"packet data mismatch in decode! pos=%ld\n", + depacket); + exit(1); + } + /* check bos flag */ + if(bosflag==0 && op_de.b_o_s==0){ + fprintf(stderr,"b_o_s flag not set on packet!\n"); + exit(1); + } + if(bosflag && op_de.b_o_s){ + fprintf(stderr,"b_o_s flag incorrectly set on packet!\n"); + exit(1); + } + bosflag=1; + depacket+=op_de.bytes; + + /* check eos flag */ + if(eosflag){ + fprintf(stderr,"Multiple decoded packets with eos flag!\n"); + exit(1); + } + + if(op_de.e_o_s)eosflag=1; + + /* check granulepos flag */ + if(op_de.granulepos!=-1){ + fprintf(stderr," granule:%ld ",(long)op_de.granulepos); + } + } + } + } + } + } + } + _ogg_free(data); + if(headers[pageno]!=NULL){ + fprintf(stderr,"did not write last page!\n"); + exit(1); + } + if(headers[pageout]!=NULL){ + fprintf(stderr,"did not decode last page!\n"); + exit(1); + } + if(inptr!=outptr){ + fprintf(stderr,"encoded page data incomplete!\n"); + exit(1); + } + if(inptr!=deptr){ + fprintf(stderr,"decoded page data incomplete!\n"); + exit(1); + } + if(inptr!=depacket){ + fprintf(stderr,"decoded packet data incomplete!\n"); + exit(1); + } + if(!eosflag){ + fprintf(stderr,"Never got a packet with EOS set!\n"); + exit(1); + } + fprintf(stderr,"ok.\n"); +} + +int main(void){ + + ogg_stream_init(&os_en,0x04030201); + ogg_stream_init(&os_de,0x04030201); + ogg_sync_init(&oy); + + /* Exercise each code path in the framing code. Also verify that + the checksums are working. */ + + { + /* 17 only */ + const int packets[]={17, -1}; + const int *headret[]={head1_0,NULL}; + + fprintf(stderr,"testing single page encoding... "); + test_pack(packets,headret,0,0,0); + } + + { + /* 17, 254, 255, 256, 500, 510, 600 byte, pad */ + const int packets[]={17, 254, 255, 256, 500, 510, 600, -1}; + const int *headret[]={head1_1,head2_1,NULL}; + + fprintf(stderr,"testing basic page encoding... "); + test_pack(packets,headret,0,0,0); + } + + { + /* nil packets; beginning,middle,end */ + const int packets[]={0,17, 254, 255, 0, 256, 0, 500, 510, 600, 0, -1}; + const int *headret[]={head1_2,head2_2,NULL}; + + fprintf(stderr,"testing basic nil packets... "); + test_pack(packets,headret,0,0,0); + } + + { + /* large initial packet */ + const int packets[]={4345,259,255,-1}; + const int *headret[]={head1_3,head2_3,NULL}; + + fprintf(stderr,"testing initial-packet lacing > 4k... "); + test_pack(packets,headret,0,0,0); + } + + { + /* continuing packet test */ + const int packets[]={0,4345,259,255,-1}; + const int *headret[]={head1_4,head2_4,head3_4,NULL}; + + fprintf(stderr,"testing single packet page span... "); + test_pack(packets,headret,0,0,0); + } + + /* page with the 255 segment limit */ + { + + const int packets[]={0,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,50,-1}; + const int *headret[]={head1_5,head2_5,head3_5,NULL}; + + fprintf(stderr,"testing max packet segments... "); + test_pack(packets,headret,0,0,0); + } + + { + /* packet that overspans over an entire page */ + const int packets[]={0,100,9000,259,255,-1}; + const int *headret[]={head1_6,head2_6,head3_6,head4_6,NULL}; + + fprintf(stderr,"testing very large packets... "); + test_pack(packets,headret,0,0,0); + } + + { + /* test for the libogg 1.1.1 resync in large continuation bug + found by Josh Coalson) */ + const int packets[]={0,100,9000,259,255,-1}; + const int *headret[]={head1_6,head2_6,head3_6,head4_6,NULL}; + + fprintf(stderr,"testing continuation resync in very large packets... "); + test_pack(packets,headret,100,2,3); + } + + { + /* term only page. why not? */ + const int packets[]={0,100,4080,-1}; + const int *headret[]={head1_7,head2_7,head3_7,NULL}; + + fprintf(stderr,"testing zero data page (1 nil packet)... "); + test_pack(packets,headret,0,0,0); + } + + { + /* build a bunch of pages for testing */ + unsigned char *data=_ogg_malloc(1024*1024); + int pl[]={0,100,4079,2956,2057,76,34,912,0,234,1000,1000,1000,300,-1}; + int inptr=0,i,j; + ogg_page og[5]; + + ogg_stream_reset(&os_en); + + for(i=0;pl[i]!=-1;i++){ + ogg_packet op; + int len=pl[i]; + + op.packet=data+inptr; + op.bytes=len; + op.e_o_s=(pl[i+1]<0?1:0); + op.granulepos=(i+1)*1000; + + for(j=0;j0)error(); + + /* Test fractional page inputs: incomplete fixed header */ + memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header+3, + 20); + ogg_sync_wrote(&oy,20); + if(ogg_sync_pageout(&oy,&og_de)>0)error(); + + /* Test fractional page inputs: incomplete header */ + memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header+23, + 5); + ogg_sync_wrote(&oy,5); + if(ogg_sync_pageout(&oy,&og_de)>0)error(); + + /* Test fractional page inputs: incomplete body */ + + memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header+28, + og[1].header_len-28); + ogg_sync_wrote(&oy,og[1].header_len-28); + if(ogg_sync_pageout(&oy,&og_de)>0)error(); + + memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body,1000); + ogg_sync_wrote(&oy,1000); + if(ogg_sync_pageout(&oy,&og_de)>0)error(); + + memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body+1000, + og[1].body_len-1000); + ogg_sync_wrote(&oy,og[1].body_len-1000); + if(ogg_sync_pageout(&oy,&og_de)<=0)error(); + + fprintf(stderr,"ok.\n"); + } + + /* Test fractional page inputs: page + incomplete capture */ + { + ogg_page og_de; + fprintf(stderr,"Testing sync on 1+partial inputs... "); + ogg_sync_reset(&oy); + + memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header, + og[1].header_len); + ogg_sync_wrote(&oy,og[1].header_len); + + memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body, + og[1].body_len); + ogg_sync_wrote(&oy,og[1].body_len); + + memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header, + 20); + ogg_sync_wrote(&oy,20); + if(ogg_sync_pageout(&oy,&og_de)<=0)error(); + if(ogg_sync_pageout(&oy,&og_de)>0)error(); + + memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header+20, + og[1].header_len-20); + ogg_sync_wrote(&oy,og[1].header_len-20); + memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body, + og[1].body_len); + ogg_sync_wrote(&oy,og[1].body_len); + if(ogg_sync_pageout(&oy,&og_de)<=0)error(); + + fprintf(stderr,"ok.\n"); + } + + /* Test recapture: garbage + page */ + { + ogg_page og_de; + fprintf(stderr,"Testing search for capture... "); + ogg_sync_reset(&oy); + + /* 'garbage' */ + memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body, + og[1].body_len); + ogg_sync_wrote(&oy,og[1].body_len); + + memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header, + og[1].header_len); + ogg_sync_wrote(&oy,og[1].header_len); + + memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body, + og[1].body_len); + ogg_sync_wrote(&oy,og[1].body_len); + + memcpy(ogg_sync_buffer(&oy,og[2].header_len),og[2].header, + 20); + ogg_sync_wrote(&oy,20); + if(ogg_sync_pageout(&oy,&og_de)>0)error(); + if(ogg_sync_pageout(&oy,&og_de)<=0)error(); + if(ogg_sync_pageout(&oy,&og_de)>0)error(); + + memcpy(ogg_sync_buffer(&oy,og[2].header_len),og[2].header+20, + og[2].header_len-20); + ogg_sync_wrote(&oy,og[2].header_len-20); + memcpy(ogg_sync_buffer(&oy,og[2].body_len),og[2].body, + og[2].body_len); + ogg_sync_wrote(&oy,og[2].body_len); + if(ogg_sync_pageout(&oy,&og_de)<=0)error(); + + fprintf(stderr,"ok.\n"); + } + + /* Test recapture: page + garbage + page */ + { + ogg_page og_de; + fprintf(stderr,"Testing recapture... "); + ogg_sync_reset(&oy); + + memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header, + og[1].header_len); + ogg_sync_wrote(&oy,og[1].header_len); + + memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body, + og[1].body_len); + ogg_sync_wrote(&oy,og[1].body_len); + + memcpy(ogg_sync_buffer(&oy,og[2].header_len),og[2].header, + og[2].header_len); + ogg_sync_wrote(&oy,og[2].header_len); + + memcpy(ogg_sync_buffer(&oy,og[2].header_len),og[2].header, + og[2].header_len); + ogg_sync_wrote(&oy,og[2].header_len); + + if(ogg_sync_pageout(&oy,&og_de)<=0)error(); + + memcpy(ogg_sync_buffer(&oy,og[2].body_len),og[2].body, + og[2].body_len-5); + ogg_sync_wrote(&oy,og[2].body_len-5); + + memcpy(ogg_sync_buffer(&oy,og[3].header_len),og[3].header, + og[3].header_len); + ogg_sync_wrote(&oy,og[3].header_len); + + memcpy(ogg_sync_buffer(&oy,og[3].body_len),og[3].body, + og[3].body_len); + ogg_sync_wrote(&oy,og[3].body_len); + + if(ogg_sync_pageout(&oy,&og_de)>0)error(); + if(ogg_sync_pageout(&oy,&og_de)<=0)error(); + + fprintf(stderr,"ok.\n"); + } + + /* Free page data that was previously copied */ + { + for(i=0;i<5;i++){ + free_page(&og[i]); + } + } + } + + return(0); +} + +#endif + +#endif +/********* End of inlined file: framing.c *********/ + +/********* Start of inlined file: analysis.c *********/ + +/********* Start of inlined file: juce_OggVorbisHeader.h *********/ +// This file is included at the start of each Ogg-Vorbis .c file, just to do a few housekeeping +// tasks.. + +#ifdef _MSC_VER + #pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4305 4189 4706) +#endif +/********* End of inlined file: juce_OggVorbisHeader.h *********/ + +#if JUCE_USE_OGGVORBIS + +#include +#include +#include + +/********* Start of inlined file: codec_internal.h *********/ +#ifndef _V_CODECI_H_ +#define _V_CODECI_H_ + +/********* Start of inlined file: envelope.h *********/ +#ifndef _V_ENVELOPE_ +#define _V_ENVELOPE_ + +/********* Start of inlined file: mdct.h *********/ +#ifndef _OGG_mdct_H_ +#define _OGG_mdct_H_ + +/*#define MDCT_INTEGERIZED <- be warned there could be some hurt left here*/ +#ifdef MDCT_INTEGERIZED + +#define DATA_TYPE int +#define REG_TYPE register int +#define TRIGBITS 14 +#define cPI3_8 6270 +#define cPI2_8 11585 +#define cPI1_8 15137 + +#define FLOAT_CONV(x) ((int)((x)*(1<>TRIGBITS) +#define HALVE(x) ((x)>>1) + +#else + +#define DATA_TYPE float +#define REG_TYPE float +#define cPI3_8 .38268343236508977175F +#define cPI2_8 .70710678118654752441F +#define cPI1_8 .92387953251128675613F + +#define FLOAT_CONV(x) (x) +#define MULT_NORM(x) (x) +#define HALVE(x) ((x)*.5f) + +#endif + +typedef struct { + int n; + int log2n; + + DATA_TYPE *trig; + int *bitrev; + + DATA_TYPE scale; +} mdct_lookup; + +extern void mdct_init(mdct_lookup *lookup,int n); +extern void mdct_clear(mdct_lookup *l); +extern void mdct_forward(mdct_lookup *init, DATA_TYPE *in, DATA_TYPE *out); +extern void mdct_backward(mdct_lookup *init, DATA_TYPE *in, DATA_TYPE *out); + +#endif +/********* End of inlined file: mdct.h *********/ + +#define VE_PRE 16 +#define VE_WIN 4 +#define VE_POST 2 +#define VE_AMP (VE_PRE+VE_POST-1) + +#define VE_BANDS 7 +#define VE_NEARDC 15 + +#define VE_MINSTRETCH 2 /* a bit less than short block */ +#define VE_MAXSTRETCH 12 /* one-third full block */ + +typedef struct { + float ampbuf[VE_AMP]; + int ampptr; + + float nearDC[VE_NEARDC]; + float nearDC_acc; + float nearDC_partialacc; + int nearptr; + +} envelope_filter_state; + +typedef struct { + int begin; + int end; + float *window; + float total; +} envelope_band; + +typedef struct { + int ch; + int winlength; + int searchstep; + float minenergy; + + mdct_lookup mdct; + float *mdct_win; + + envelope_band band[VE_BANDS]; + envelope_filter_state *filter; + int stretch; + + int *mark; + + long storage; + long current; + long curmark; + long cursor; +} envelope_lookup; + +extern void _ve_envelope_init(envelope_lookup *e,vorbis_info *vi); +extern void _ve_envelope_clear(envelope_lookup *e); +extern long _ve_envelope_search(vorbis_dsp_state *v); +extern void _ve_envelope_shift(envelope_lookup *e,long shift); +extern int _ve_envelope_mark(vorbis_dsp_state *v); + +#endif +/********* End of inlined file: envelope.h *********/ + +/********* Start of inlined file: codebook.h *********/ +#ifndef _V_CODEBOOK_H_ +#define _V_CODEBOOK_H_ + +/* This structure encapsulates huffman and VQ style encoding books; it + doesn't do anything specific to either. + + valuelist/quantlist are nonNULL (and q_* significant) only if + there's entry->value mapping to be done. + + If encode-side mapping must be done (and thus the entry needs to be + hunted), the auxiliary encode pointer will point to a decision + tree. This is true of both VQ and huffman, but is mostly useful + with VQ. + +*/ + +typedef struct static_codebook{ + long dim; /* codebook dimensions (elements per vector) */ + long entries; /* codebook entries */ + long *lengthlist; /* codeword lengths in bits */ + + /* mapping ***************************************************************/ + int maptype; /* 0=none + 1=implicitly populated values from map column + 2=listed arbitrary values */ + + /* The below does a linear, single monotonic sequence mapping. */ + long q_min; /* packed 32 bit float; quant value 0 maps to minval */ + long q_delta; /* packed 32 bit float; val 1 - val 0 == delta */ + int q_quant; /* bits: 0 < quant <= 16 */ + int q_sequencep; /* bitflag */ + + long *quantlist; /* map == 1: (int)(entries^(1/dim)) element column map + map == 2: list of dim*entries quantized entry vals + */ + + /* encode helpers ********************************************************/ + struct encode_aux_nearestmatch *nearest_tree; + struct encode_aux_threshmatch *thresh_tree; + struct encode_aux_pigeonhole *pigeon_tree; + + int allocedp; +} static_codebook; + +/* this structures an arbitrary trained book to quickly find the + nearest cell match */ +typedef struct encode_aux_nearestmatch{ + /* pre-calculated partitioning tree */ + long *ptr0; + long *ptr1; + + long *p; /* decision points (each is an entry) */ + long *q; /* decision points (each is an entry) */ + long aux; /* number of tree entries */ + long alloc; +} encode_aux_nearestmatch; + +/* assumes a maptype of 1; encode side only, so that's OK */ +typedef struct encode_aux_threshmatch{ + float *quantthresh; + long *quantmap; + int quantvals; + int threshvals; +} encode_aux_threshmatch; + +typedef struct encode_aux_pigeonhole{ + float min; + float del; + + int mapentries; + int quantvals; + long *pigeonmap; + + long fittotal; + long *fitlist; + long *fitmap; + long *fitlength; +} encode_aux_pigeonhole; + +typedef struct codebook{ + long dim; /* codebook dimensions (elements per vector) */ + long entries; /* codebook entries */ + long used_entries; /* populated codebook entries */ + const static_codebook *c; + + /* for encode, the below are entry-ordered, fully populated */ + /* for decode, the below are ordered by bitreversed codeword and only + used entries are populated */ + float *valuelist; /* list of dim*entries actual entry values */ + ogg_uint32_t *codelist; /* list of bitstream codewords for each entry */ + + int *dec_index; /* only used if sparseness collapsed */ + char *dec_codelengths; + ogg_uint32_t *dec_firsttable; + int dec_firsttablen; + int dec_maxlength; + +} codebook; + +extern void vorbis_staticbook_clear(static_codebook *b); +extern void vorbis_staticbook_destroy(static_codebook *b); +extern int vorbis_book_init_encode(codebook *dest,const static_codebook *source); +extern int vorbis_book_init_decode(codebook *dest,const static_codebook *source); +extern void vorbis_book_clear(codebook *b); + +extern float *_book_unquantize(const static_codebook *b,int n,int *map); +extern float *_book_logdist(const static_codebook *b,float *vals); +extern float _float32_unpack(long val); +extern long _float32_pack(float val); +extern int _best(codebook *book, float *a, int step); +extern int _ilog(unsigned int v); +extern long _book_maptype1_quantvals(const static_codebook *b); + +extern int vorbis_book_besterror(codebook *book,float *a,int step,int addmul); +extern long vorbis_book_codeword(codebook *book,int entry); +extern long vorbis_book_codelen(codebook *book,int entry); + +extern int vorbis_staticbook_pack(const static_codebook *c,oggpack_buffer *b); +extern int vorbis_staticbook_unpack(oggpack_buffer *b,static_codebook *c); + +extern int vorbis_book_encode(codebook *book, int a, oggpack_buffer *b); +extern int vorbis_book_errorv(codebook *book, float *a); +extern int vorbis_book_encodev(codebook *book, int best,float *a, + oggpack_buffer *b); + +extern long vorbis_book_decode(codebook *book, oggpack_buffer *b); +extern long vorbis_book_decodevs_add(codebook *book, float *a, + oggpack_buffer *b,int n); +extern long vorbis_book_decodev_set(codebook *book, float *a, + oggpack_buffer *b,int n); +extern long vorbis_book_decodev_add(codebook *book, float *a, + oggpack_buffer *b,int n); +extern long vorbis_book_decodevv_add(codebook *book, float **a, + long off,int ch, + oggpack_buffer *b,int n); + +#endif +/********* End of inlined file: codebook.h *********/ + +#define BLOCKTYPE_IMPULSE 0 +#define BLOCKTYPE_PADDING 1 +#define BLOCKTYPE_TRANSITION 0 +#define BLOCKTYPE_LONG 1 + +#define PACKETBLOBS 15 + +typedef struct vorbis_block_internal{ + float **pcmdelay; /* this is a pointer into local storage */ + float ampmax; + int blocktype; + + oggpack_buffer *packetblob[PACKETBLOBS]; /* initialized, must be freed; + blob [PACKETBLOBS/2] points to + the oggpack_buffer in the + main vorbis_block */ +} vorbis_block_internal; + +typedef void vorbis_look_floor; +typedef void vorbis_look_residue; +typedef void vorbis_look_transform; + +/* mode ************************************************************/ +typedef struct { + int blockflag; + int windowtype; + int transformtype; + int mapping; +} vorbis_info_mode; + +typedef void vorbis_info_floor; +typedef void vorbis_info_residue; +typedef void vorbis_info_mapping; + +/********* Start of inlined file: psy.h *********/ +#ifndef _V_PSY_H_ +#define _V_PSY_H_ + +/********* Start of inlined file: smallft.h *********/ +#ifndef _V_SMFT_H_ +#define _V_SMFT_H_ + +typedef struct { + int n; + float *trigcache; + int *splitcache; +} drft_lookup; + +extern void drft_forward(drft_lookup *l,float *data); +extern void drft_backward(drft_lookup *l,float *data); +extern void drft_init(drft_lookup *l,int n); +extern void drft_clear(drft_lookup *l); + +#endif +/********* End of inlined file: smallft.h *********/ + +/********* Start of inlined file: backends.h *********/ +/* this is exposed up here because we need it for static modes. + Lookups for each backend aren't exposed because there's no reason + to do so */ + +#ifndef _vorbis_backend_h_ +#define _vorbis_backend_h_ + +/* this would all be simpler/shorter with templates, but.... */ +/* Floor backend generic *****************************************/ +typedef struct{ + void (*pack) (vorbis_info_floor *,oggpack_buffer *); + vorbis_info_floor *(*unpack)(vorbis_info *,oggpack_buffer *); + vorbis_look_floor *(*look) (vorbis_dsp_state *,vorbis_info_floor *); + void (*free_info) (vorbis_info_floor *); + void (*free_look) (vorbis_look_floor *); + void *(*inverse1) (struct vorbis_block *,vorbis_look_floor *); + int (*inverse2) (struct vorbis_block *,vorbis_look_floor *, + void *buffer,float *); +} vorbis_func_floor; + +typedef struct{ + int order; + long rate; + long barkmap; + + int ampbits; + int ampdB; + + int numbooks; /* <= 16 */ + int books[16]; + + float lessthan; /* encode-only config setting hacks for libvorbis */ + float greaterthan; /* encode-only config setting hacks for libvorbis */ + +} vorbis_info_floor0; + +#define VIF_POSIT 63 +#define VIF_CLASS 16 +#define VIF_PARTS 31 +typedef struct{ + int partitions; /* 0 to 31 */ + int partitionclass[VIF_PARTS]; /* 0 to 15 */ + + int class_dim[VIF_CLASS]; /* 1 to 8 */ + int class_subs[VIF_CLASS]; /* 0,1,2,3 (bits: 1< + +/********* Start of inlined file: misc.h *********/ +#ifndef _V_RANDOM_H_ +#define _V_RANDOM_H_ + +extern int analysis_noisy; + +extern void *_vorbis_block_alloc(vorbis_block *vb,long bytes); +extern void _vorbis_block_ripcord(vorbis_block *vb); +extern void _analysis_output(char *base,int i,float *v,int n,int bark,int dB, + ogg_int64_t off); + +#ifdef DEBUG_MALLOC + +#define _VDBG_GRAPHFILE "malloc.m" +extern void *_VDBG_malloc(void *ptr,long bytes,char *file,long line); +extern void _VDBG_free(void *ptr,char *file,long line); + +#ifndef MISC_C +#undef _ogg_malloc +#undef _ogg_calloc +#undef _ogg_realloc +#undef _ogg_free + +#define _ogg_malloc(x) _VDBG_malloc(NULL,(x),__FILE__,__LINE__) +#define _ogg_calloc(x,y) _VDBG_malloc(NULL,(x)*(y),__FILE__,__LINE__) +#define _ogg_realloc(x,y) _VDBG_malloc((x),(y),__FILE__,__LINE__) +#define _ogg_free(x) _VDBG_free((x),__FILE__,__LINE__) +#endif +#endif + +#endif +/********* End of inlined file: misc.h *********/ + +#ifndef _V_IFDEFJAIL_H_ +# define _V_IFDEFJAIL_H_ + +# ifdef __GNUC__ +# define STIN static __inline__ +# elif _WIN32 +# define STIN static __inline +# else +# define STIN static +# endif + +#ifdef DJGPP +# define rint(x) (floor((x)+0.5f)) +#endif + +#ifndef M_PI +# define M_PI (3.1415926536f) +#endif + +#if defined(_WIN32) && !defined(__SYMBIAN32__) +# include +# define rint(x) (floor((x)+0.5f)) +# define NO_FLOAT_MATH_LIB +# define FAST_HYPOT(a, b) sqrt((a)*(a) + (b)*(b)) +#endif + +#if defined(__SYMBIAN32__) && defined(__WINS__) +void *_alloca(size_t size); +# define alloca _alloca +#endif + +#ifndef FAST_HYPOT +# define FAST_HYPOT hypot +#endif + +#endif + +#ifdef HAVE_ALLOCA_H +# include +#endif + +#ifdef USE_MEMORY_H +# include +#endif + +#ifndef min +# define min(x,y) ((x)>(y)?(y):(x)) +#endif + +#ifndef max +# define max(x,y) ((x)<(y)?(y):(x)) +#endif + +#if defined(__i386__) && defined(__GNUC__) && !defined(__BEOS__) +# define VORBIS_FPU_CONTROL +/* both GCC and MSVC are kinda stupid about rounding/casting to int. + Because of encapsulation constraints (GCC can't see inside the asm + block and so we end up doing stupid things like a store/load that + is collectively a noop), we do it this way */ + +/* we must set up the fpu before this works!! */ + +typedef ogg_int16_t vorbis_fpu_control; + +static inline void vorbis_fpu_setround(vorbis_fpu_control *fpu){ + ogg_int16_t ret; + ogg_int16_t temp; + __asm__ __volatile__("fnstcw %0\n\t" + "movw %0,%%dx\n\t" + "orw $62463,%%dx\n\t" + "movw %%dx,%1\n\t" + "fldcw %1\n\t":"=m"(ret):"m"(temp): "dx"); + *fpu=ret; +} + +static inline void vorbis_fpu_restore(vorbis_fpu_control fpu){ + __asm__ __volatile__("fldcw %0":: "m"(fpu)); +} + +/* assumes the FPU is in round mode! */ +static inline int vorbis_ftoi(double f){ /* yes, double! Otherwise, + we get extra fst/fld to + truncate precision */ + int i; + __asm__("fistl %0": "=m"(i) : "t"(f)); + return(i); +} +#endif + +#if defined(_WIN32) && defined(_X86_) && !defined(__GNUC__) && !defined(__BORLANDC__) +# define VORBIS_FPU_CONTROL + +typedef ogg_int16_t vorbis_fpu_control; + +static __inline int vorbis_ftoi(double f){ + int i; + __asm{ + fld f + fistp i + } + return i; +} + +static __inline void vorbis_fpu_setround(vorbis_fpu_control *fpu){ +} + +static __inline void vorbis_fpu_restore(vorbis_fpu_control fpu){ +} + +#endif + +#ifndef VORBIS_FPU_CONTROL + +typedef int vorbis_fpu_control; + +static int vorbis_ftoi(double f){ + return (int)(f+.5); +} + +/* We don't have special code for this compiler/arch, so do it the slow way */ +# define vorbis_fpu_setround(vorbis_fpu_control) {} +# define vorbis_fpu_restore(vorbis_fpu_control) {} + +#endif + +#endif /* _OS_H */ +/********* End of inlined file: os.h *********/ + +/* encode side bitrate tracking */ +typedef struct bitrate_manager_state { + int managed; + + long avg_reservoir; + long minmax_reservoir; + long avg_bitsper; + long min_bitsper; + long max_bitsper; + + long short_per_long; + double avgfloat; + + vorbis_block *vb; + int choice; +} bitrate_manager_state; + +typedef struct bitrate_manager_info{ + long avg_rate; + long min_rate; + long max_rate; + long reservoir_bits; + double reservoir_bias; + + double slew_damp; + +} bitrate_manager_info; + +extern void vorbis_bitrate_init(vorbis_info *vi,bitrate_manager_state *bs); +extern void vorbis_bitrate_clear(bitrate_manager_state *bs); +extern int vorbis_bitrate_managed(vorbis_block *vb); +extern int vorbis_bitrate_addblock(vorbis_block *vb); +extern int vorbis_bitrate_flushpacket(vorbis_dsp_state *vd, ogg_packet *op); + +#endif +/********* End of inlined file: bitrate.h *********/ + +static int ilog(unsigned int v){ + int ret=0; + while(v){ + ret++; + v>>=1; + } + return(ret); +} + +static int ilog2(unsigned int v){ + int ret=0; + if(v)--v; + while(v){ + ret++; + v>>=1; + } + return(ret); +} + +typedef struct private_state { + /* local lookup storage */ + envelope_lookup *ve; /* envelope lookup */ + int window[2]; + vorbis_look_transform **transform[2]; /* block, type */ + drft_lookup fft_look[2]; + + int modebits; + vorbis_look_floor **flr; + vorbis_look_residue **residue; + vorbis_look_psy *psy; + vorbis_look_psy_global *psy_g_look; + + /* local storage, only used on the encoding side. This way the + application does not need to worry about freeing some packets' + memory and not others'; packet storage is always tracked. + Cleared next call to a _dsp_ function */ + unsigned char *header; + unsigned char *header1; + unsigned char *header2; + + bitrate_manager_state bms; + + ogg_int64_t sample_count; +} private_state; + +/* codec_setup_info contains all the setup information specific to the + specific compression/decompression mode in progress (eg, + psychoacoustic settings, channel setup, options, codebook + etc). +*********************************************************************/ + +/********* Start of inlined file: highlevel.h *********/ +typedef struct highlevel_byblocktype { + double tone_mask_setting; + double tone_peaklimit_setting; + double noise_bias_setting; + double noise_compand_setting; +} highlevel_byblocktype; + +typedef struct highlevel_encode_setup { + void *setup; + int set_in_stone; + + double base_setting; + double long_setting; + double short_setting; + double impulse_noisetune; + + int managed; + long bitrate_min; + long bitrate_av; + double bitrate_av_damp; + long bitrate_max; + long bitrate_reservoir; + double bitrate_reservoir_bias; + + int impulse_block_p; + int noise_normalize_p; + + double stereo_point_setting; + double lowpass_kHz; + + double ath_floating_dB; + double ath_absolute_dB; + + double amplitude_track_dBpersec; + double trigger_setting; + + highlevel_byblocktype block[4]; /* padding, impulse, transition, long */ + +} highlevel_encode_setup; +/********* End of inlined file: highlevel.h *********/ + +typedef struct codec_setup_info { + + /* Vorbis supports only short and long blocks, but allows the + encoder to choose the sizes */ + + long blocksizes[2]; + + /* modes are the primary means of supporting on-the-fly different + blocksizes, different channel mappings (LR or M/A), + different residue backends, etc. Each mode consists of a + blocksize flag and a mapping (along with the mapping setup */ + + int modes; + int maps; + int floors; + int residues; + int books; + int psys; /* encode only */ + + vorbis_info_mode *mode_param[64]; + int map_type[64]; + vorbis_info_mapping *map_param[64]; + int floor_type[64]; + vorbis_info_floor *floor_param[64]; + int residue_type[64]; + vorbis_info_residue *residue_param[64]; + static_codebook *book_param[256]; + codebook *fullbooks; + + vorbis_info_psy *psy_param[4]; /* encode only */ + vorbis_info_psy_global psy_g_param; + + bitrate_manager_info bi; + highlevel_encode_setup hi; /* used only by vorbisenc.c. It's a + highly redundant structure, but + improves clarity of program flow. */ + int halfrate_flag; /* painless downsample for decode */ +} codec_setup_info; + +extern vorbis_look_psy_global *_vp_global_look(vorbis_info *vi); +extern void _vp_global_free(vorbis_look_psy_global *look); + +#endif +/********* End of inlined file: codec_internal.h *********/ + +/********* Start of inlined file: registry.h *********/ +#ifndef _V_REG_H_ +#define _V_REG_H_ + +#define VI_TRANSFORMB 1 +#define VI_WINDOWB 1 +#define VI_TIMEB 1 +#define VI_FLOORB 2 +#define VI_RESB 3 +#define VI_MAPB 1 + +extern vorbis_func_floor *_floor_P[]; +extern vorbis_func_residue *_residue_P[]; +extern vorbis_func_mapping *_mapping_P[]; + +#endif +/********* End of inlined file: registry.h *********/ + +/********* Start of inlined file: scales.h *********/ +#ifndef _V_SCALES_H_ +#define _V_SCALES_H_ + +#include + +/* 20log10(x) */ +#define VORBIS_IEEE_FLOAT32 1 +#ifdef VORBIS_IEEE_FLOAT32 + +static float unitnorm(float x){ + union { + ogg_uint32_t i; + float f; + } ix; + ix.f = x; + ix.i = (ix.i & 0x80000000U) | (0x3f800000U); + return ix.f; +} + +/* Segher was off (too high) by ~ .3 decibel. Center the conversion correctly. */ +static float todB(const float *x){ + union { + ogg_uint32_t i; + float f; + } ix; + ix.f = *x; + ix.i = ix.i&0x7fffffff; + return (float)(ix.i * 7.17711438e-7f -764.6161886f); +} + +#define todB_nn(x) todB(x) + +#else + +static float unitnorm(float x){ + if(x<0)return(-1.f); + return(1.f); +} + +#define todB(x) (*(x)==0?-400.f:log(*(x)**(x))*4.34294480f) +#define todB_nn(x) (*(x)==0.f?-400.f:log(*(x))*8.6858896f) + +#endif + +#define fromdB(x) (exp((x)*.11512925f)) + +/* The bark scale equations are approximations, since the original + table was somewhat hand rolled. The below are chosen to have the + best possible fit to the rolled tables, thus their somewhat odd + appearance (these are more accurate and over a longer range than + the oft-quoted bark equations found in the texts I have). The + approximations are valid from 0 - 30kHz (nyquist) or so. + + all f in Hz, z in Bark */ + +#define toBARK(n) (13.1f*atan(.00074f*(n))+2.24f*atan((n)*(n)*1.85e-8f)+1e-4f*(n)) +#define fromBARK(z) (102.f*(z)-2.f*pow(z,2.f)+.4f*pow(z,3.f)+pow(1.46f,z)-1.f) +#define toMEL(n) (log(1.f+(n)*.001f)*1442.695f) +#define fromMEL(m) (1000.f*exp((m)/1442.695f)-1000.f) + +/* Frequency to octave. We arbitrarily declare 63.5 Hz to be octave + 0.0 */ + +#define toOC(n) (log(n)*1.442695f-5.965784f) +#define fromOC(o) (exp(((o)+5.965784f)*.693147f)) + +#endif +/********* End of inlined file: scales.h *********/ + +int analysis_noisy=1; + +/* decides between modes, dispatches to the appropriate mapping. */ +int vorbis_analysis(vorbis_block *vb, ogg_packet *op){ + int ret,i; + vorbis_block_internal *vbi=(vorbis_block_internal *)vb->internal; + + vb->glue_bits=0; + vb->time_bits=0; + vb->floor_bits=0; + vb->res_bits=0; + + /* first things first. Make sure encode is ready */ + for(i=0;ipacketblob[i]); + + /* we only have one mapping type (0), and we let the mapping code + itself figure out what soft mode to use. This allows easier + bitrate management */ + + if((ret=_mapping_P[0]->forward(vb))) + return(ret); + + if(op){ + if(vorbis_bitrate_managed(vb)) + /* The app is using a bitmanaged mode... but not using the + bitrate management interface. */ + return(OV_EINVAL); + + op->packet=oggpack_get_buffer(&vb->opb); + op->bytes=oggpack_bytes(&vb->opb); + op->b_o_s=0; + op->e_o_s=vb->eofflag; + op->granulepos=vb->granulepos; + op->packetno=vb->sequence; /* for sake of completeness */ + } + return(0); +} + +/* there was no great place to put this.... */ +void _analysis_output_always(char *base,int i,float *v,int n,int bark,int dB,ogg_int64_t off){ + int j; + FILE *of; + char buffer[80]; + + /* if(i==5870){*/ + sprintf(buffer,"%s_%d.m",base,i); + of=fopen(buffer,"w"); + + if(!of)perror("failed to open data dump file"); + + for(j=0;j +#include +#include + +/* compute bitrate tracking setup */ +void vorbis_bitrate_init(vorbis_info *vi,bitrate_manager_state *bm){ + codec_setup_info *ci=(codec_setup_info *)vi->codec_setup; + bitrate_manager_info *bi=&ci->bi; + + memset(bm,0,sizeof(*bm)); + + if(bi && (bi->reservoir_bits>0)){ + long ratesamples=vi->rate; + int halfsamples=ci->blocksizes[0]>>1; + + bm->short_per_long=ci->blocksizes[1]/ci->blocksizes[0]; + bm->managed=1; + + bm->avg_bitsper= rint(1.*bi->avg_rate*halfsamples/ratesamples); + bm->min_bitsper= rint(1.*bi->min_rate*halfsamples/ratesamples); + bm->max_bitsper= rint(1.*bi->max_rate*halfsamples/ratesamples); + + bm->avgfloat=PACKETBLOBS/2; + + /* not a necessary fix, but one that leads to a more balanced + typical initialization */ + { + long desired_fill=bi->reservoir_bits*bi->reservoir_bias; + bm->minmax_reservoir=desired_fill; + bm->avg_reservoir=desired_fill; + } + + } +} + +void vorbis_bitrate_clear(bitrate_manager_state *bm){ + memset(bm,0,sizeof(*bm)); + return; +} + +int vorbis_bitrate_managed(vorbis_block *vb){ + vorbis_dsp_state *vd=vb->vd; + private_state *b=(private_state*)vd->backend_state; + bitrate_manager_state *bm=&b->bms; + + if(bm && bm->managed)return(1); + return(0); +} + +/* finish taking in the block we just processed */ +int vorbis_bitrate_addblock(vorbis_block *vb){ + vorbis_block_internal *vbi=(vorbis_block_internal*)vb->internal; + vorbis_dsp_state *vd=vb->vd; + private_state *b=(private_state*)vd->backend_state; + bitrate_manager_state *bm=&b->bms; + vorbis_info *vi=vd->vi; + codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; + bitrate_manager_info *bi=&ci->bi; + + int choice=rint(bm->avgfloat); + long this_bits=oggpack_bytes(vbi->packetblob[choice])*8; + long min_target_bits=(vb->W?bm->min_bitsper*bm->short_per_long:bm->min_bitsper); + long max_target_bits=(vb->W?bm->max_bitsper*bm->short_per_long:bm->max_bitsper); + int samples=ci->blocksizes[vb->W]>>1; + long desired_fill=bi->reservoir_bits*bi->reservoir_bias; + if(!bm->managed){ + /* not a bitrate managed stream, but for API simplicity, we'll + buffer the packet to keep the code path clean */ + + if(bm->vb)return(-1); /* one has been submitted without + being claimed */ + bm->vb=vb; + return(0); + } + + bm->vb=vb; + + /* look ahead for avg floater */ + if(bm->avg_bitsper>0){ + double slew=0.; + long avg_target_bits=(vb->W?bm->avg_bitsper*bm->short_per_long:bm->avg_bitsper); + double slewlimit= 15./bi->slew_damp; + + /* choosing a new floater: + if we're over target, we slew down + if we're under target, we slew up + + choose slew as follows: look through packetblobs of this frame + and set slew as the first in the appropriate direction that + gives us the slew we want. This may mean no slew if delta is + already favorable. + + Then limit slew to slew max */ + + if(bm->avg_reservoir+(this_bits-avg_target_bits)>desired_fill){ + while(choice>0 && this_bits>avg_target_bits && + bm->avg_reservoir+(this_bits-avg_target_bits)>desired_fill){ + choice--; + this_bits=oggpack_bytes(vbi->packetblob[choice])*8; + } + }else if(bm->avg_reservoir+(this_bits-avg_target_bits)avg_reservoir+(this_bits-avg_target_bits)packetblob[choice])*8; + } + } + + slew=rint(choice-bm->avgfloat)/samples*vi->rate; + if(slew<-slewlimit)slew=-slewlimit; + if(slew>slewlimit)slew=slewlimit; + choice=rint(bm->avgfloat+= slew/vi->rate*samples); + this_bits=oggpack_bytes(vbi->packetblob[choice])*8; + } + + /* enforce min(if used) on the current floater (if used) */ + if(bm->min_bitsper>0){ + /* do we need to force the bitrate up? */ + if(this_bitsminmax_reservoir-(min_target_bits-this_bits)<0){ + choice++; + if(choice>=PACKETBLOBS)break; + this_bits=oggpack_bytes(vbi->packetblob[choice])*8; + } + } + } + + /* enforce max (if used) on the current floater (if used) */ + if(bm->max_bitsper>0){ + /* do we need to force the bitrate down? */ + if(this_bits>max_target_bits){ + while(bm->minmax_reservoir+(this_bits-max_target_bits)>bi->reservoir_bits){ + choice--; + if(choice<0)break; + this_bits=oggpack_bytes(vbi->packetblob[choice])*8; + } + } + } + + /* Choice of packetblobs now made based on floater, and min/max + requirements. Now boundary check extreme choices */ + + if(choice<0){ + /* choosing a smaller packetblob is insufficient to trim bitrate. + frame will need to be truncated */ + long maxsize=(max_target_bits+(bi->reservoir_bits-bm->minmax_reservoir))/8; + bm->choice=choice=0; + + if(oggpack_bytes(vbi->packetblob[choice])>maxsize){ + + oggpack_writetrunc(vbi->packetblob[choice],maxsize*8); + this_bits=oggpack_bytes(vbi->packetblob[choice])*8; + } + }else{ + long minsize=(min_target_bits-bm->minmax_reservoir+7)/8; + if(choice>=PACKETBLOBS) + choice=PACKETBLOBS-1; + + bm->choice=choice; + + /* prop up bitrate according to demand. pad this frame out with zeroes */ + minsize-=oggpack_bytes(vbi->packetblob[choice]); + while(minsize-->0)oggpack_write(vbi->packetblob[choice],0,8); + this_bits=oggpack_bytes(vbi->packetblob[choice])*8; + + } + + /* now we have the final packet and the final packet size. Update statistics */ + /* min and max reservoir */ + if(bm->min_bitsper>0 || bm->max_bitsper>0){ + + if(max_target_bits>0 && this_bits>max_target_bits){ + bm->minmax_reservoir+=(this_bits-max_target_bits); + }else if(min_target_bits>0 && this_bitsminmax_reservoir+=(this_bits-min_target_bits); + }else{ + /* inbetween; we want to take reservoir toward but not past desired_fill */ + if(bm->minmax_reservoir>desired_fill){ + if(max_target_bits>0){ /* logical bulletproofing against initialization state */ + bm->minmax_reservoir+=(this_bits-max_target_bits); + if(bm->minmax_reservoirminmax_reservoir=desired_fill; + }else{ + bm->minmax_reservoir=desired_fill; + } + }else{ + if(min_target_bits>0){ /* logical bulletproofing against initialization state */ + bm->minmax_reservoir+=(this_bits-min_target_bits); + if(bm->minmax_reservoir>desired_fill)bm->minmax_reservoir=desired_fill; + }else{ + bm->minmax_reservoir=desired_fill; + } + } + } + } + + /* avg reservoir */ + if(bm->avg_bitsper>0){ + long avg_target_bits=(vb->W?bm->avg_bitsper*bm->short_per_long:bm->avg_bitsper); + bm->avg_reservoir+=this_bits-avg_target_bits; + } + + return(0); +} + +int vorbis_bitrate_flushpacket(vorbis_dsp_state *vd,ogg_packet *op){ + private_state *b=(private_state*)vd->backend_state; + bitrate_manager_state *bm=&b->bms; + vorbis_block *vb=bm->vb; + int choice=PACKETBLOBS/2; + if(!vb)return 0; + + if(op){ + vorbis_block_internal *vbi=(vorbis_block_internal*)vb->internal; + + if(vorbis_bitrate_managed(vb)) + choice=bm->choice; + + op->packet=oggpack_get_buffer(vbi->packetblob[choice]); + op->bytes=oggpack_bytes(vbi->packetblob[choice]); + op->b_o_s=0; + op->e_o_s=vb->eofflag; + op->granulepos=vb->granulepos; + op->packetno=vb->sequence; /* for sake of completeness */ + } + + bm->vb=0; + return(1); +} + +#endif +/********* End of inlined file: bitrate.c *********/ + +/********* Start of inlined file: block.c *********/ + +/********* Start of inlined file: juce_OggVorbisHeader.h *********/ +// This file is included at the start of each Ogg-Vorbis .c file, just to do a few housekeeping +// tasks.. + +#ifdef _MSC_VER + #pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4305 4189 4706) +#endif +/********* End of inlined file: juce_OggVorbisHeader.h *********/ + +#if JUCE_USE_OGGVORBIS + +#include +#include +#include + +/********* Start of inlined file: window.h *********/ +#ifndef _V_WINDOW_ +#define _V_WINDOW_ + +extern float *_vorbis_window_get(int n); +extern void _vorbis_apply_window(float *d,int *winno,long *blocksizes, + int lW,int W,int nW); + +#endif +/********* End of inlined file: window.h *********/ + +/********* Start of inlined file: lpc.h *********/ +#ifndef _V_LPC_H_ +#define _V_LPC_H_ + +/* simple linear scale LPC code */ +extern float vorbis_lpc_from_data(float *data,float *lpc,int n,int m); + +extern void vorbis_lpc_predict(float *coeff,float *prime,int m, + float *data,long n); + +#endif +/********* End of inlined file: lpc.h *********/ + +/* pcm accumulator examples (not exhaustive): + + <-------------- lW ----------------> + <--------------- W ----------------> +: .....|..... _______________ | +: .''' | '''_--- | |\ | +:.....''' |_____--- '''......| | \_______| +:.................|__________________|_______|__|______| + |<------ Sl ------>| > Sr < |endW + |beginSl |endSl | |endSr + |beginW |endlW |beginSr + + |< lW >| + <--------------- W ----------------> + | | .. ______________ | + | | ' `/ | ---_ | + |___.'___/`. | ---_____| + |_______|__|_______|_________________| + | >|Sl|< |<------ Sr ----->|endW + | | |endSl |beginSr |endSr + |beginW | |endlW + mult[0] |beginSl mult[n] + + <-------------- lW -----------------> + |<--W-->| +: .............. ___ | | +: .''' |`/ \ | | +:.....''' |/`....\|...| +:.........................|___|___|___| + |Sl |Sr |endW + | | |endSr + | |beginSr + | |endSl + |beginSl + |beginW +*/ + +/* block abstraction setup *********************************************/ + +#ifndef WORD_ALIGN +#define WORD_ALIGN 8 +#endif + +int vorbis_block_init(vorbis_dsp_state *v, vorbis_block *vb){ + int i; + memset(vb,0,sizeof(*vb)); + vb->vd=v; + vb->localalloc=0; + vb->localstore=NULL; + if(v->analysisp){ + vorbis_block_internal *vbi=(vorbis_block_internal*) + (vb->internal=(vorbis_block_internal*)_ogg_calloc(1,sizeof(vorbis_block_internal))); + vbi->ampmax=-9999; + + for(i=0;ipacketblob[i]=&vb->opb; + }else{ + vbi->packetblob[i]= + (oggpack_buffer*) _ogg_calloc(1,sizeof(oggpack_buffer)); + } + oggpack_writeinit(vbi->packetblob[i]); + } + } + + return(0); +} + +void *_vorbis_block_alloc(vorbis_block *vb,long bytes){ + bytes=(bytes+(WORD_ALIGN-1)) & ~(WORD_ALIGN-1); + if(bytes+vb->localtop>vb->localalloc){ + /* can't just _ogg_realloc... there are outstanding pointers */ + if(vb->localstore){ + struct alloc_chain *link=(struct alloc_chain*)_ogg_malloc(sizeof(*link)); + vb->totaluse+=vb->localtop; + link->next=vb->reap; + link->ptr=vb->localstore; + vb->reap=link; + } + /* highly conservative */ + vb->localalloc=bytes; + vb->localstore=_ogg_malloc(vb->localalloc); + vb->localtop=0; + } + { + void *ret=(void *)(((char *)vb->localstore)+vb->localtop); + vb->localtop+=bytes; + return ret; + } +} + +/* reap the chain, pull the ripcord */ +void _vorbis_block_ripcord(vorbis_block *vb){ + /* reap the chain */ + struct alloc_chain *reap=vb->reap; + while(reap){ + struct alloc_chain *next=reap->next; + _ogg_free(reap->ptr); + memset(reap,0,sizeof(*reap)); + _ogg_free(reap); + reap=next; + } + /* consolidate storage */ + if(vb->totaluse){ + vb->localstore=_ogg_realloc(vb->localstore,vb->totaluse+vb->localalloc); + vb->localalloc+=vb->totaluse; + vb->totaluse=0; + } + + /* pull the ripcord */ + vb->localtop=0; + vb->reap=NULL; +} + +int vorbis_block_clear(vorbis_block *vb){ + int i; + vorbis_block_internal *vbi=(vorbis_block_internal*)vb->internal; + + _vorbis_block_ripcord(vb); + if(vb->localstore)_ogg_free(vb->localstore); + + if(vbi){ + for(i=0;ipacketblob[i]); + if(i!=PACKETBLOBS/2)_ogg_free(vbi->packetblob[i]); + } + _ogg_free(vbi); + } + memset(vb,0,sizeof(*vb)); + return(0); +} + +/* Analysis side code, but directly related to blocking. Thus it's + here and not in analysis.c (which is for analysis transforms only). + The init is here because some of it is shared */ + +static int _vds_shared_init(vorbis_dsp_state *v,vorbis_info *vi,int encp){ + int i; + codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; + private_state *b=NULL; + int hs; + + if(ci==NULL) return 1; + hs=ci->halfrate_flag; + + memset(v,0,sizeof(*v)); + b=(private_state*) (v->backend_state=(private_state*)_ogg_calloc(1,sizeof(*b))); + + v->vi=vi; + b->modebits=ilog2(ci->modes); + + b->transform[0]=(vorbis_look_transform**)_ogg_calloc(VI_TRANSFORMB,sizeof(*b->transform[0])); + b->transform[1]=(vorbis_look_transform**)_ogg_calloc(VI_TRANSFORMB,sizeof(*b->transform[1])); + + /* MDCT is tranform 0 */ + + b->transform[0][0]=_ogg_calloc(1,sizeof(mdct_lookup)); + b->transform[1][0]=_ogg_calloc(1,sizeof(mdct_lookup)); + mdct_init((mdct_lookup*)b->transform[0][0],ci->blocksizes[0]>>hs); + mdct_init((mdct_lookup*)b->transform[1][0],ci->blocksizes[1]>>hs); + + /* Vorbis I uses only window type 0 */ + b->window[0]=ilog2(ci->blocksizes[0])-6; + b->window[1]=ilog2(ci->blocksizes[1])-6; + + if(encp){ /* encode/decode differ here */ + + /* analysis always needs an fft */ + drft_init(&b->fft_look[0],ci->blocksizes[0]); + drft_init(&b->fft_look[1],ci->blocksizes[1]); + + /* finish the codebooks */ + if(!ci->fullbooks){ + ci->fullbooks=(codebook*) _ogg_calloc(ci->books,sizeof(*ci->fullbooks)); + for(i=0;ibooks;i++) + vorbis_book_init_encode(ci->fullbooks+i,ci->book_param[i]); + } + + b->psy=(vorbis_look_psy*)_ogg_calloc(ci->psys,sizeof(*b->psy)); + for(i=0;ipsys;i++){ + _vp_psy_init(b->psy+i, + ci->psy_param[i], + &ci->psy_g_param, + ci->blocksizes[ci->psy_param[i]->blockflag]/2, + vi->rate); + } + + v->analysisp=1; + }else{ + /* finish the codebooks */ + if(!ci->fullbooks){ + ci->fullbooks=(codebook*) _ogg_calloc(ci->books,sizeof(*ci->fullbooks)); + for(i=0;ibooks;i++){ + vorbis_book_init_decode(ci->fullbooks+i,ci->book_param[i]); + /* decode codebooks are now standalone after init */ + vorbis_staticbook_destroy(ci->book_param[i]); + ci->book_param[i]=NULL; + } + } + } + + /* initialize the storage vectors. blocksize[1] is small for encode, + but the correct size for decode */ + v->pcm_storage=ci->blocksizes[1]; + v->pcm=(float**)_ogg_malloc(vi->channels*sizeof(*v->pcm)); + v->pcmret=(float**)_ogg_malloc(vi->channels*sizeof(*v->pcmret)); + { + int i; + for(i=0;ichannels;i++) + v->pcm[i]=(float*)_ogg_calloc(v->pcm_storage,sizeof(*v->pcm[i])); + } + + /* all 1 (large block) or 0 (small block) */ + /* explicitly set for the sake of clarity */ + v->lW=0; /* previous window size */ + v->W=0; /* current window size */ + + /* all vector indexes */ + v->centerW=ci->blocksizes[1]/2; + + v->pcm_current=v->centerW; + + /* initialize all the backend lookups */ + b->flr=(vorbis_look_floor**)_ogg_calloc(ci->floors,sizeof(*b->flr)); + b->residue=(vorbis_look_residue**)_ogg_calloc(ci->residues,sizeof(*b->residue)); + + for(i=0;ifloors;i++) + b->flr[i]=_floor_P[ci->floor_type[i]]-> + look(v,ci->floor_param[i]); + + for(i=0;iresidues;i++) + b->residue[i]=_residue_P[ci->residue_type[i]]-> + look(v,ci->residue_param[i]); + + return 0; +} + +/* arbitrary settings and spec-mandated numbers get filled in here */ +int vorbis_analysis_init(vorbis_dsp_state *v,vorbis_info *vi){ + private_state *b=NULL; + + if(_vds_shared_init(v,vi,1))return 1; + b=(private_state*)v->backend_state; + b->psy_g_look=_vp_global_look(vi); + + /* Initialize the envelope state storage */ + b->ve=(envelope_lookup*)_ogg_calloc(1,sizeof(*b->ve)); + _ve_envelope_init(b->ve,vi); + + vorbis_bitrate_init(vi,&b->bms); + + /* compressed audio packets start after the headers + with sequence number 3 */ + v->sequence=3; + + return(0); +} + +void vorbis_dsp_clear(vorbis_dsp_state *v){ + int i; + if(v){ + vorbis_info *vi=v->vi; + codec_setup_info *ci=(codec_setup_info*)(vi?vi->codec_setup:NULL); + private_state *b=(private_state*)v->backend_state; + + if(b){ + + if(b->ve){ + _ve_envelope_clear(b->ve); + _ogg_free(b->ve); + } + + if(b->transform[0]){ + mdct_clear((mdct_lookup*) b->transform[0][0]); + _ogg_free(b->transform[0][0]); + _ogg_free(b->transform[0]); + } + if(b->transform[1]){ + mdct_clear((mdct_lookup*) b->transform[1][0]); + _ogg_free(b->transform[1][0]); + _ogg_free(b->transform[1]); + } + + if(b->flr){ + for(i=0;ifloors;i++) + _floor_P[ci->floor_type[i]]-> + free_look(b->flr[i]); + _ogg_free(b->flr); + } + if(b->residue){ + for(i=0;iresidues;i++) + _residue_P[ci->residue_type[i]]-> + free_look(b->residue[i]); + _ogg_free(b->residue); + } + if(b->psy){ + for(i=0;ipsys;i++) + _vp_psy_clear(b->psy+i); + _ogg_free(b->psy); + } + + if(b->psy_g_look)_vp_global_free(b->psy_g_look); + vorbis_bitrate_clear(&b->bms); + + drft_clear(&b->fft_look[0]); + drft_clear(&b->fft_look[1]); + + } + + if(v->pcm){ + for(i=0;ichannels;i++) + if(v->pcm[i])_ogg_free(v->pcm[i]); + _ogg_free(v->pcm); + if(v->pcmret)_ogg_free(v->pcmret); + } + + if(b){ + /* free header, header1, header2 */ + if(b->header)_ogg_free(b->header); + if(b->header1)_ogg_free(b->header1); + if(b->header2)_ogg_free(b->header2); + _ogg_free(b); + } + + memset(v,0,sizeof(*v)); + } +} + +float **vorbis_analysis_buffer(vorbis_dsp_state *v, int vals){ + int i; + vorbis_info *vi=v->vi; + private_state *b=(private_state*)v->backend_state; + + /* free header, header1, header2 */ + if(b->header)_ogg_free(b->header);b->header=NULL; + if(b->header1)_ogg_free(b->header1);b->header1=NULL; + if(b->header2)_ogg_free(b->header2);b->header2=NULL; + + /* Do we have enough storage space for the requested buffer? If not, + expand the PCM (and envelope) storage */ + + if(v->pcm_current+vals>=v->pcm_storage){ + v->pcm_storage=v->pcm_current+vals*2; + + for(i=0;ichannels;i++){ + v->pcm[i]=(float*)_ogg_realloc(v->pcm[i],v->pcm_storage*sizeof(*v->pcm[i])); + } + } + + for(i=0;ichannels;i++) + v->pcmret[i]=v->pcm[i]+v->pcm_current; + + return(v->pcmret); +} + +static void _preextrapolate_helper(vorbis_dsp_state *v){ + int i; + int order=32; + float *lpc=(float*)alloca(order*sizeof(*lpc)); + float *work=(float*)alloca(v->pcm_current*sizeof(*work)); + long j; + v->preextrapolate=1; + + if(v->pcm_current-v->centerW>order*2){ /* safety */ + for(i=0;ivi->channels;i++){ + /* need to run the extrapolation in reverse! */ + for(j=0;jpcm_current;j++) + work[j]=v->pcm[i][v->pcm_current-j-1]; + + /* prime as above */ + vorbis_lpc_from_data(work,lpc,v->pcm_current-v->centerW,order); + + /* run the predictor filter */ + vorbis_lpc_predict(lpc,work+v->pcm_current-v->centerW-order, + order, + work+v->pcm_current-v->centerW, + v->centerW); + + for(j=0;jpcm_current;j++) + v->pcm[i][v->pcm_current-j-1]=work[j]; + + } + } +} + +/* call with val<=0 to set eof */ + +int vorbis_analysis_wrote(vorbis_dsp_state *v, int vals){ + vorbis_info *vi=v->vi; + codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; + + if(vals<=0){ + int order=32; + int i; + float *lpc=(float*) alloca(order*sizeof(*lpc)); + + /* if it wasn't done earlier (very short sample) */ + if(!v->preextrapolate) + _preextrapolate_helper(v); + + /* We're encoding the end of the stream. Just make sure we have + [at least] a few full blocks of zeroes at the end. */ + /* actually, we don't want zeroes; that could drop a large + amplitude off a cliff, creating spread spectrum noise that will + suck to encode. Extrapolate for the sake of cleanliness. */ + + vorbis_analysis_buffer(v,ci->blocksizes[1]*3); + v->eofflag=v->pcm_current; + v->pcm_current+=ci->blocksizes[1]*3; + + for(i=0;ichannels;i++){ + if(v->eofflag>order*2){ + /* extrapolate with LPC to fill in */ + long n; + + /* make a predictor filter */ + n=v->eofflag; + if(n>ci->blocksizes[1])n=ci->blocksizes[1]; + vorbis_lpc_from_data(v->pcm[i]+v->eofflag-n,lpc,n,order); + + /* run the predictor filter */ + vorbis_lpc_predict(lpc,v->pcm[i]+v->eofflag-order,order, + v->pcm[i]+v->eofflag,v->pcm_current-v->eofflag); + }else{ + /* not enough data to extrapolate (unlikely to happen due to + guarding the overlap, but bulletproof in case that + assumtion goes away). zeroes will do. */ + memset(v->pcm[i]+v->eofflag,0, + (v->pcm_current-v->eofflag)*sizeof(*v->pcm[i])); + + } + } + }else{ + + if(v->pcm_current+vals>v->pcm_storage) + return(OV_EINVAL); + + v->pcm_current+=vals; + + /* we may want to reverse extrapolate the beginning of a stream + too... in case we're beginning on a cliff! */ + /* clumsy, but simple. It only runs once, so simple is good. */ + if(!v->preextrapolate && v->pcm_current-v->centerW>ci->blocksizes[1]) + _preextrapolate_helper(v); + + } + return(0); +} + +/* do the deltas, envelope shaping, pre-echo and determine the size of + the next block on which to continue analysis */ +int vorbis_analysis_blockout(vorbis_dsp_state *v,vorbis_block *vb){ + int i; + vorbis_info *vi=v->vi; + codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; + private_state *b=(private_state*)v->backend_state; + vorbis_look_psy_global *g=b->psy_g_look; + long beginW=v->centerW-ci->blocksizes[v->W]/2,centerNext; + vorbis_block_internal *vbi=(vorbis_block_internal *)vb->internal; + + /* check to see if we're started... */ + if(!v->preextrapolate)return(0); + + /* check to see if we're done... */ + if(v->eofflag==-1)return(0); + + /* By our invariant, we have lW, W and centerW set. Search for + the next boundary so we can determine nW (the next window size) + which lets us compute the shape of the current block's window */ + + /* we do an envelope search even on a single blocksize; we may still + be throwing more bits at impulses, and envelope search handles + marking impulses too. */ + { + long bp=_ve_envelope_search(v); + if(bp==-1){ + + if(v->eofflag==0)return(0); /* not enough data currently to search for a + full long block */ + v->nW=0; + }else{ + + if(ci->blocksizes[0]==ci->blocksizes[1]) + v->nW=0; + else + v->nW=bp; + } + } + + centerNext=v->centerW+ci->blocksizes[v->W]/4+ci->blocksizes[v->nW]/4; + + { + /* center of next block + next block maximum right side. */ + + long blockbound=centerNext+ci->blocksizes[v->nW]/2; + if(v->pcm_currentlW=v->lW; + vb->W=v->W; + vb->nW=v->nW; + + if(v->W){ + if(!v->lW || !v->nW){ + vbi->blocktype=BLOCKTYPE_TRANSITION; + /*fprintf(stderr,"-");*/ + }else{ + vbi->blocktype=BLOCKTYPE_LONG; + /*fprintf(stderr,"_");*/ + } + }else{ + if(_ve_envelope_mark(v)){ + vbi->blocktype=BLOCKTYPE_IMPULSE; + /*fprintf(stderr,"|");*/ + + }else{ + vbi->blocktype=BLOCKTYPE_PADDING; + /*fprintf(stderr,".");*/ + + } + } + + vb->vd=v; + vb->sequence=v->sequence++; + vb->granulepos=v->granulepos; + vb->pcmend=ci->blocksizes[v->W]; + + /* copy the vectors; this uses the local storage in vb */ + + /* this tracks 'strongest peak' for later psychoacoustics */ + /* moved to the global psy state; clean this mess up */ + if(vbi->ampmax>g->ampmax)g->ampmax=vbi->ampmax; + g->ampmax=_vp_ampmax_decay(g->ampmax,v); + vbi->ampmax=g->ampmax; + + vb->pcm=(float**)_vorbis_block_alloc(vb,sizeof(*vb->pcm)*vi->channels); + vbi->pcmdelay=(float**)_vorbis_block_alloc(vb,sizeof(*vbi->pcmdelay)*vi->channels); + for(i=0;ichannels;i++){ + vbi->pcmdelay[i]= + (float*) _vorbis_block_alloc(vb,(vb->pcmend+beginW)*sizeof(*vbi->pcmdelay[i])); + memcpy(vbi->pcmdelay[i],v->pcm[i],(vb->pcmend+beginW)*sizeof(*vbi->pcmdelay[i])); + vb->pcm[i]=vbi->pcmdelay[i]+beginW; + + /* before we added the delay + vb->pcm[i]=_vorbis_block_alloc(vb,vb->pcmend*sizeof(*vb->pcm[i])); + memcpy(vb->pcm[i],v->pcm[i]+beginW,ci->blocksizes[v->W]*sizeof(*vb->pcm[i])); + */ + + } + + /* handle eof detection: eof==0 means that we've not yet received EOF + eof>0 marks the last 'real' sample in pcm[] + eof<0 'no more to do'; doesn't get here */ + + if(v->eofflag){ + if(v->centerW>=v->eofflag){ + v->eofflag=-1; + vb->eofflag=1; + return(1); + } + } + + /* advance storage vectors and clean up */ + { + int new_centerNext=ci->blocksizes[1]/2; + int movementW=centerNext-new_centerNext; + + if(movementW>0){ + + _ve_envelope_shift(b->ve,movementW); + v->pcm_current-=movementW; + + for(i=0;ichannels;i++) + memmove(v->pcm[i],v->pcm[i]+movementW, + v->pcm_current*sizeof(*v->pcm[i])); + + v->lW=v->W; + v->W=v->nW; + v->centerW=new_centerNext; + + if(v->eofflag){ + v->eofflag-=movementW; + if(v->eofflag<=0)v->eofflag=-1; + /* do not add padding to end of stream! */ + if(v->centerW>=v->eofflag){ + v->granulepos+=movementW-(v->centerW-v->eofflag); + }else{ + v->granulepos+=movementW; + } + }else{ + v->granulepos+=movementW; + } + } + } + + /* done */ + return(1); +} + +int vorbis_synthesis_restart(vorbis_dsp_state *v){ + vorbis_info *vi=v->vi; + codec_setup_info *ci; + int hs; + + if(!v->backend_state)return -1; + if(!vi)return -1; + ci=(codec_setup_info*) vi->codec_setup; + if(!ci)return -1; + hs=ci->halfrate_flag; + + v->centerW=ci->blocksizes[1]>>(hs+1); + v->pcm_current=v->centerW>>hs; + + v->pcm_returned=-1; + v->granulepos=-1; + v->sequence=-1; + v->eofflag=0; + ((private_state *)(v->backend_state))->sample_count=-1; + + return(0); +} + +int vorbis_synthesis_init(vorbis_dsp_state *v,vorbis_info *vi){ + if(_vds_shared_init(v,vi,0)) return 1; + vorbis_synthesis_restart(v); + + return 0; +} + +/* Unlike in analysis, the window is only partially applied for each + block. The time domain envelope is not yet handled at the point of + calling (as it relies on the previous block). */ + +int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb){ + vorbis_info *vi=v->vi; + codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; + private_state *b=(private_state*)v->backend_state; + int hs=ci->halfrate_flag; + int i,j; + + if(!vb)return(OV_EINVAL); + if(v->pcm_current>v->pcm_returned && v->pcm_returned!=-1)return(OV_EINVAL); + + v->lW=v->W; + v->W=vb->W; + v->nW=-1; + + if((v->sequence==-1)|| + (v->sequence+1 != vb->sequence)){ + v->granulepos=-1; /* out of sequence; lose count */ + b->sample_count=-1; + } + + v->sequence=vb->sequence; + + if(vb->pcm){ /* no pcm to process if vorbis_synthesis_trackonly + was called on block */ + int n=ci->blocksizes[v->W]>>(hs+1); + int n0=ci->blocksizes[0]>>(hs+1); + int n1=ci->blocksizes[1]>>(hs+1); + + int thisCenter; + int prevCenter; + + v->glue_bits+=vb->glue_bits; + v->time_bits+=vb->time_bits; + v->floor_bits+=vb->floor_bits; + v->res_bits+=vb->res_bits; + + if(v->centerW){ + thisCenter=n1; + prevCenter=0; + }else{ + thisCenter=0; + prevCenter=n1; + } + + /* v->pcm is now used like a two-stage double buffer. We don't want + to have to constantly shift *or* adjust memory usage. Don't + accept a new block until the old is shifted out */ + + for(j=0;jchannels;j++){ + /* the overlap/add section */ + if(v->lW){ + if(v->W){ + /* large/large */ + float *w=_vorbis_window_get(b->window[1]-hs); + float *pcm=v->pcm[j]+prevCenter; + float *p=vb->pcm[j]; + for(i=0;iwindow[0]-hs); + float *pcm=v->pcm[j]+prevCenter+n1/2-n0/2; + float *p=vb->pcm[j]; + for(i=0;iW){ + /* small/large */ + float *w=_vorbis_window_get(b->window[0]-hs); + float *pcm=v->pcm[j]+prevCenter; + float *p=vb->pcm[j]+n1/2-n0/2; + for(i=0;iwindow[0]-hs); + float *pcm=v->pcm[j]+prevCenter; + float *p=vb->pcm[j]; + for(i=0;ipcm[j]+thisCenter; + float *p=vb->pcm[j]+n; + for(i=0;icenterW) + v->centerW=0; + else + v->centerW=n1; + + /* deal with initial packet state; we do this using the explicit + pcm_returned==-1 flag otherwise we're sensitive to first block + being short or long */ + + if(v->pcm_returned==-1){ + v->pcm_returned=thisCenter; + v->pcm_current=thisCenter; + }else{ + v->pcm_returned=prevCenter; + v->pcm_current=prevCenter+ + ((ci->blocksizes[v->lW]/4+ + ci->blocksizes[v->W]/4)>>hs); + } + + } + + /* track the frame number... This is for convenience, but also + making sure our last packet doesn't end with added padding. If + the last packet is partial, the number of samples we'll have to + return will be past the vb->granulepos. + + This is not foolproof! It will be confused if we begin + decoding at the last page after a seek or hole. In that case, + we don't have a starting point to judge where the last frame + is. For this reason, vorbisfile will always try to make sure + it reads the last two marked pages in proper sequence */ + + if(b->sample_count==-1){ + b->sample_count=0; + }else{ + b->sample_count+=ci->blocksizes[v->lW]/4+ci->blocksizes[v->W]/4; + } + + if(v->granulepos==-1){ + if(vb->granulepos!=-1){ /* only set if we have a position to set to */ + + v->granulepos=vb->granulepos; + + /* is this a short page? */ + if(b->sample_count>v->granulepos){ + /* corner case; if this is both the first and last audio page, + then spec says the end is cut, not beginning */ + if(vb->eofflag){ + /* trim the end */ + /* no preceeding granulepos; assume we started at zero (we'd + have to in a short single-page stream) */ + /* granulepos could be -1 due to a seek, but that would result + in a long count, not short count */ + + v->pcm_current-=(b->sample_count-v->granulepos)>>hs; + }else{ + /* trim the beginning */ + v->pcm_returned+=(b->sample_count-v->granulepos)>>hs; + if(v->pcm_returned>v->pcm_current) + v->pcm_returned=v->pcm_current; + } + + } + + } + }else{ + v->granulepos+=ci->blocksizes[v->lW]/4+ci->blocksizes[v->W]/4; + if(vb->granulepos!=-1 && v->granulepos!=vb->granulepos){ + + if(v->granulepos>vb->granulepos){ + long extra=v->granulepos-vb->granulepos; + + if(extra) + if(vb->eofflag){ + /* partial last frame. Strip the extra samples off */ + v->pcm_current-=extra>>hs; + } /* else {Shouldn't happen *unless* the bitstream is out of + spec. Either way, believe the bitstream } */ + } /* else {Shouldn't happen *unless* the bitstream is out of + spec. Either way, believe the bitstream } */ + v->granulepos=vb->granulepos; + } + } + + /* Update, cleanup */ + + if(vb->eofflag)v->eofflag=1; + return(0); + +} + +/* pcm==NULL indicates we just want the pending samples, no more */ +int vorbis_synthesis_pcmout(vorbis_dsp_state *v,float ***pcm){ + vorbis_info *vi=v->vi; + + if(v->pcm_returned>-1 && v->pcm_returnedpcm_current){ + if(pcm){ + int i; + for(i=0;ichannels;i++) + v->pcmret[i]=v->pcm[i]+v->pcm_returned; + *pcm=v->pcmret; + } + return(v->pcm_current-v->pcm_returned); + } + return(0); +} + +int vorbis_synthesis_read(vorbis_dsp_state *v,int n){ + if(n && v->pcm_returned+n>v->pcm_current)return(OV_EINVAL); + v->pcm_returned+=n; + return(0); +} + +/* intended for use with a specific vorbisfile feature; we want access + to the [usually synthetic/postextrapolated] buffer and lapping at + the end of a decode cycle, specifically, a half-short-block worth. + This funtion works like pcmout above, except it will also expose + this implicit buffer data not normally decoded. */ +int vorbis_synthesis_lapout(vorbis_dsp_state *v,float ***pcm){ + vorbis_info *vi=v->vi; + codec_setup_info *ci=(codec_setup_info *)vi->codec_setup; + int hs=ci->halfrate_flag; + + int n=ci->blocksizes[v->W]>>(hs+1); + int n0=ci->blocksizes[0]>>(hs+1); + int n1=ci->blocksizes[1]>>(hs+1); + int i,j; + + if(v->pcm_returned<0)return 0; + + /* our returned data ends at pcm_returned; because the synthesis pcm + buffer is a two-fragment ring, that means our data block may be + fragmented by buffering, wrapping or a short block not filling + out a buffer. To simplify things, we unfragment if it's at all + possibly needed. Otherwise, we'd need to call lapout more than + once as well as hold additional dsp state. Opt for + simplicity. */ + + /* centerW was advanced by blockin; it would be the center of the + *next* block */ + if(v->centerW==n1){ + /* the data buffer wraps; swap the halves */ + /* slow, sure, small */ + for(j=0;jchannels;j++){ + float *p=v->pcm[j]; + for(i=0;ipcm_current-=n1; + v->pcm_returned-=n1; + v->centerW=0; + } + + /* solidify buffer into contiguous space */ + if((v->lW^v->W)==1){ + /* long/short or short/long */ + for(j=0;jchannels;j++){ + float *s=v->pcm[j]; + float *d=v->pcm[j]+(n1-n0)/2; + for(i=(n1+n0)/2-1;i>=0;--i) + d[i]=s[i]; + } + v->pcm_returned+=(n1-n0)/2; + v->pcm_current+=(n1-n0)/2; + }else{ + if(v->lW==0){ + /* short/short */ + for(j=0;jchannels;j++){ + float *s=v->pcm[j]; + float *d=v->pcm[j]+n1-n0; + for(i=n0-1;i>=0;--i) + d[i]=s[i]; + } + v->pcm_returned+=n1-n0; + v->pcm_current+=n1-n0; + } + } + + if(pcm){ + int i; + for(i=0;ichannels;i++) + v->pcmret[i]=v->pcm[i]+v->pcm_returned; + *pcm=v->pcmret; + } + + return(n1+n-v->pcm_returned); + +} + +float *vorbis_window(vorbis_dsp_state *v,int W){ + vorbis_info *vi=v->vi; + codec_setup_info *ci=(codec_setup_info*) vi->codec_setup; + int hs=ci->halfrate_flag; + private_state *b=(private_state*)v->backend_state; + + if(b->window[W]-1<0)return NULL; + return _vorbis_window_get(b->window[W]-hs); +} + +#endif +/********* End of inlined file: block.c *********/ + +/********* Start of inlined file: codebook.c *********/ + +/********* Start of inlined file: juce_OggVorbisHeader.h *********/ +// This file is included at the start of each Ogg-Vorbis .c file, just to do a few housekeeping +// tasks.. + +#ifdef _MSC_VER + #pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4305 4189 4706) +#endif +/********* End of inlined file: juce_OggVorbisHeader.h *********/ + +#if JUCE_USE_OGGVORBIS + +#include +#include +#include + +/* packs the given codebook into the bitstream **************************/ + +int vorbis_staticbook_pack(const static_codebook *c,oggpack_buffer *opb){ + long i,j; + int ordered=0; + + /* first the basic parameters */ + oggpack_write(opb,0x564342,24); + oggpack_write(opb,c->dim,16); + oggpack_write(opb,c->entries,24); + + /* pack the codewords. There are two packings; length ordered and + length random. Decide between the two now. */ + + for(i=1;ientries;i++) + if(c->lengthlist[i-1]==0 || c->lengthlist[i]lengthlist[i-1])break; + if(i==c->entries)ordered=1; + + if(ordered){ + /* length ordered. We only need to say how many codewords of + each length. The actual codewords are generated + deterministically */ + + long count=0; + oggpack_write(opb,1,1); /* ordered */ + oggpack_write(opb,c->lengthlist[0]-1,5); /* 1 to 32 */ + + for(i=1;ientries;i++){ + long thisx=c->lengthlist[i]; + long last=c->lengthlist[i-1]; + if(thisx>last){ + for(j=last;jentries-count)); + count=i; + } + } + } + oggpack_write(opb,i-count,_ilog(c->entries-count)); + + }else{ + /* length random. Again, we don't code the codeword itself, just + the length. This time, though, we have to encode each length */ + oggpack_write(opb,0,1); /* unordered */ + + /* algortihmic mapping has use for 'unused entries', which we tag + here. The algorithmic mapping happens as usual, but the unused + entry has no codeword. */ + for(i=0;ientries;i++) + if(c->lengthlist[i]==0)break; + + if(i==c->entries){ + oggpack_write(opb,0,1); /* no unused entries */ + for(i=0;ientries;i++) + oggpack_write(opb,c->lengthlist[i]-1,5); + }else{ + oggpack_write(opb,1,1); /* we have unused entries; thus we tag */ + for(i=0;ientries;i++){ + if(c->lengthlist[i]==0){ + oggpack_write(opb,0,1); + }else{ + oggpack_write(opb,1,1); + oggpack_write(opb,c->lengthlist[i]-1,5); + } + } + } + } + + /* is the entry number the desired return value, or do we have a + mapping? If we have a mapping, what type? */ + oggpack_write(opb,c->maptype,4); + switch(c->maptype){ + case 0: + /* no mapping */ + break; + case 1:case 2: + /* implicitly populated value mapping */ + /* explicitly populated value mapping */ + + if(!c->quantlist){ + /* no quantlist? error */ + return(-1); + } + + /* values that define the dequantization */ + oggpack_write(opb,c->q_min,32); + oggpack_write(opb,c->q_delta,32); + oggpack_write(opb,c->q_quant-1,4); + oggpack_write(opb,c->q_sequencep,1); + + { + int quantvals; + switch(c->maptype){ + case 1: + /* a single column of (c->entries/c->dim) quantized values for + building a full value list algorithmically (square lattice) */ + quantvals=_book_maptype1_quantvals(c); + break; + case 2: + /* every value (c->entries*c->dim total) specified explicitly */ + quantvals=c->entries*c->dim; + break; + default: /* NOT_REACHABLE */ + quantvals=-1; + } + + /* quantized values */ + for(i=0;iquantlist[i]),c->q_quant); + + } + break; + default: + /* error case; we don't have any other map types now */ + return(-1); + } + + return(0); +} + +/* unpacks a codebook from the packet buffer into the codebook struct, + readies the codebook auxiliary structures for decode *************/ +int vorbis_staticbook_unpack(oggpack_buffer *opb,static_codebook *s){ + long i,j; + memset(s,0,sizeof(*s)); + s->allocedp=1; + + /* make sure alignment is correct */ + if(oggpack_read(opb,24)!=0x564342)goto _eofout; + + /* first the basic parameters */ + s->dim=oggpack_read(opb,16); + s->entries=oggpack_read(opb,24); + if(s->entries==-1)goto _eofout; + + /* codeword ordering.... length ordered or unordered? */ + switch((int)oggpack_read(opb,1)){ + case 0: + /* unordered */ + s->lengthlist=(long*)_ogg_malloc(sizeof(*s->lengthlist)*s->entries); + + /* allocated but unused entries? */ + if(oggpack_read(opb,1)){ + /* yes, unused entries */ + + for(i=0;ientries;i++){ + if(oggpack_read(opb,1)){ + long num=oggpack_read(opb,5); + if(num==-1)goto _eofout; + s->lengthlist[i]=num+1; + }else + s->lengthlist[i]=0; + } + }else{ + /* all entries used; no tagging */ + for(i=0;ientries;i++){ + long num=oggpack_read(opb,5); + if(num==-1)goto _eofout; + s->lengthlist[i]=num+1; + } + } + + break; + case 1: + /* ordered */ + { + long length=oggpack_read(opb,5)+1; + s->lengthlist=(long*)_ogg_malloc(sizeof(*s->lengthlist)*s->entries); + + for(i=0;ientries;){ + long num=oggpack_read(opb,_ilog(s->entries-i)); + if(num==-1)goto _eofout; + for(j=0;jentries;j++,i++) + s->lengthlist[i]=length; + length++; + } + } + break; + default: + /* EOF */ + return(-1); + } + + /* Do we have a mapping to unpack? */ + switch((s->maptype=oggpack_read(opb,4))){ + case 0: + /* no mapping */ + break; + case 1: case 2: + /* implicitly populated value mapping */ + /* explicitly populated value mapping */ + + s->q_min=oggpack_read(opb,32); + s->q_delta=oggpack_read(opb,32); + s->q_quant=oggpack_read(opb,4)+1; + s->q_sequencep=oggpack_read(opb,1); + + { + int quantvals=0; + switch(s->maptype){ + case 1: + quantvals=_book_maptype1_quantvals(s); + break; + case 2: + quantvals=s->entries*s->dim; + break; + } + + /* quantized values */ + s->quantlist=(long*)_ogg_malloc(sizeof(*s->quantlist)*quantvals); + for(i=0;iquantlist[i]=oggpack_read(opb,s->q_quant); + + if(quantvals&&s->quantlist[quantvals-1]==-1)goto _eofout; + } + break; + default: + goto _errout; + } + + /* all set */ + return(0); + + _errout: + _eofout: + vorbis_staticbook_clear(s); + return(-1); +} + +/* returns the number of bits ************************************************/ +int vorbis_book_encode(codebook *book, int a, oggpack_buffer *b){ + oggpack_write(b,book->codelist[a],book->c->lengthlist[a]); + return(book->c->lengthlist[a]); +} + +/* One the encode side, our vector writers are each designed for a +specific purpose, and the encoder is not flexible without modification: + +The LSP vector coder uses a single stage nearest-match with no +interleave, so no step and no error return. This is specced by floor0 +and doesn't change. + +Residue0 encoding interleaves, uses multiple stages, and each stage +peels of a specific amount of resolution from a lattice (thus we want +to match by threshold, not nearest match). Residue doesn't *have* to +be encoded that way, but to change it, one will need to add more +infrastructure on the encode side (decode side is specced and simpler) */ + +/* floor0 LSP (single stage, non interleaved, nearest match) */ +/* returns entry number and *modifies a* to the quantization value *****/ +int vorbis_book_errorv(codebook *book,float *a){ + int dim=book->dim,k; + int best=_best(book,a,1); + for(k=0;kvaluelist+best*dim)[k]; + return(best); +} + +/* returns the number of bits and *modifies a* to the quantization value *****/ +int vorbis_book_encodev(codebook *book,int best,float *a,oggpack_buffer *b){ + int k,dim=book->dim; + for(k=0;kvaluelist+best*dim)[k]; + return(vorbis_book_encode(book,best,b)); +} + +/* the 'eliminate the decode tree' optimization actually requires the + codewords to be MSb first, not LSb. This is an annoying inelegancy + (and one of the first places where carefully thought out design + turned out to be wrong; Vorbis II and future Ogg codecs should go + to an MSb bitpacker), but not actually the huge hit it appears to + be. The first-stage decode table catches most words so that + bitreverse is not in the main execution path. */ + +STIN long decode_packed_entry_number(codebook *book, oggpack_buffer *b){ + int read=book->dec_maxlength; + long lo,hi; + long lok = oggpack_look(b,book->dec_firsttablen); + + if (lok >= 0) { + long entry = book->dec_firsttable[lok]; + if(entry&0x80000000UL){ + lo=(entry>>15)&0x7fff; + hi=book->used_entries-(entry&0x7fff); + }else{ + oggpack_adv(b, book->dec_codelengths[entry-1]); + return(entry-1); + } + }else{ + lo=0; + hi=book->used_entries; + } + + lok = oggpack_look(b, read); + + while(lok<0 && read>1) + lok = oggpack_look(b, --read); + if(lok<0)return -1; + + /* bisect search for the codeword in the ordered list */ + { + ogg_uint32_t testword=bitreverse((ogg_uint32_t)lok); + + while(hi-lo>1){ + long p=(hi-lo)>>1; + long test=book->codelist[lo+p]>testword; + lo+=p&(test-1); + hi-=p&(-test); + } + + if(book->dec_codelengths[lo]<=read){ + oggpack_adv(b, book->dec_codelengths[lo]); + return(lo); + } + } + + oggpack_adv(b, read); + return(-1); +} + +/* Decode side is specced and easier, because we don't need to find + matches using different criteria; we simply read and map. There are + two things we need to do 'depending': + + We may need to support interleave. We don't really, but it's + convenient to do it here rather than rebuild the vector later. + + Cascades may be additive or multiplicitive; this is not inherent in + the codebook, but set in the code using the codebook. Like + interleaving, it's easiest to do it here. + addmul==0 -> declarative (set the value) + addmul==1 -> additive + addmul==2 -> multiplicitive */ + +/* returns the [original, not compacted] entry number or -1 on eof *********/ +long vorbis_book_decode(codebook *book, oggpack_buffer *b){ + long packed_entry=decode_packed_entry_number(book,b); + if(packed_entry>=0) + return(book->dec_index[packed_entry]); + + /* if there's no dec_index, the codebook unpacking isn't collapsed */ + return(packed_entry); +} + +/* returns 0 on OK or -1 on eof *************************************/ +long vorbis_book_decodevs_add(codebook *book,float *a,oggpack_buffer *b,int n){ + int step=n/book->dim; + long *entry = (long*)alloca(sizeof(*entry)*step); + float **t = (float**)alloca(sizeof(*t)*step); + int i,j,o; + + for (i = 0; i < step; i++) { + entry[i]=decode_packed_entry_number(book,b); + if(entry[i]==-1)return(-1); + t[i] = book->valuelist+entry[i]*book->dim; + } + for(i=0,o=0;idim;i++,o+=step) + for (j=0;jdim>8){ + for(i=0;ivaluelist+entry*book->dim; + for (j=0;jdim;) + a[i++]+=t[j++]; + } + }else{ + for(i=0;ivaluelist+entry*book->dim; + j=0; + switch((int)book->dim){ + case 8: + a[i++]+=t[j++]; + case 7: + a[i++]+=t[j++]; + case 6: + a[i++]+=t[j++]; + case 5: + a[i++]+=t[j++]; + case 4: + a[i++]+=t[j++]; + case 3: + a[i++]+=t[j++]; + case 2: + a[i++]+=t[j++]; + case 1: + a[i++]+=t[j++]; + case 0: + break; + } + } + } + return(0); +} + +long vorbis_book_decodev_set(codebook *book,float *a,oggpack_buffer *b,int n){ + int i,j,entry; + float *t; + + for(i=0;ivaluelist+entry*book->dim; + for (j=0;jdim;) + a[i++]=t[j++]; + } + return(0); +} + +long vorbis_book_decodevv_add(codebook *book,float **a,long offset,int ch, + oggpack_buffer *b,int n){ + long i,j,entry; + int chptr=0; + + for(i=offset/ch;i<(offset+n)/ch;){ + entry = decode_packed_entry_number(book,b); + if(entry==-1)return(-1); + { + const float *t = book->valuelist+entry*book->dim; + for (j=0;jdim;j++){ + a[chptr++][i]+=t[j]; + if(chptr==ch){ + chptr=0; + i++; + } + } + } + } + return(0); +} + +#ifdef _V_SELFTEST +/* Simple enough; pack a few candidate codebooks, unpack them. Code a + number of vectors through (keeping track of the quantized values), + and decode using the unpacked book. quantized version of in should + exactly equal out */ + +#include + +#include "vorbis/book/lsp20_0.vqh" +#include "vorbis/book/res0a_13.vqh" +#define TESTSIZE 40 + +float test1[TESTSIZE]={ + 0.105939f, + 0.215373f, + 0.429117f, + 0.587974f, + + 0.181173f, + 0.296583f, + 0.515707f, + 0.715261f, + + 0.162327f, + 0.263834f, + 0.342876f, + 0.406025f, + + 0.103571f, + 0.223561f, + 0.368513f, + 0.540313f, + + 0.136672f, + 0.395882f, + 0.587183f, + 0.652476f, + + 0.114338f, + 0.417300f, + 0.525486f, + 0.698679f, + + 0.147492f, + 0.324481f, + 0.643089f, + 0.757582f, + + 0.139556f, + 0.215795f, + 0.324559f, + 0.399387f, + + 0.120236f, + 0.267420f, + 0.446940f, + 0.608760f, + + 0.115587f, + 0.287234f, + 0.571081f, + 0.708603f, +}; + +float test3[TESTSIZE]={ + 0,1,-2,3,4,-5,6,7,8,9, + 8,-2,7,-1,4,6,8,3,1,-9, + 10,11,12,13,14,15,26,17,18,19, + 30,-25,-30,-1,-5,-32,4,3,-2,0}; + +static_codebook *testlist[]={&_vq_book_lsp20_0, + &_vq_book_res0a_13,NULL}; +float *testvec[]={test1,test3}; + +int main(){ + oggpack_buffer write; + oggpack_buffer read; + long ptr=0,i; + oggpack_writeinit(&write); + + fprintf(stderr,"Testing codebook abstraction...:\n"); + + while(testlist[ptr]){ + codebook c; + static_codebook s; + float *qv=alloca(sizeof(*qv)*TESTSIZE); + float *iv=alloca(sizeof(*iv)*TESTSIZE); + memcpy(qv,testvec[ptr],sizeof(*qv)*TESTSIZE); + memset(iv,0,sizeof(*iv)*TESTSIZE); + + fprintf(stderr,"\tpacking/coding %ld... ",ptr); + + /* pack the codebook, write the testvector */ + oggpack_reset(&write); + vorbis_book_init_encode(&c,testlist[ptr]); /* get it into memory + we can write */ + vorbis_staticbook_pack(testlist[ptr],&write); + fprintf(stderr,"Codebook size %ld bytes... ",oggpack_bytes(&write)); + for(i=0;i.000001){ + fprintf(stderr,"read (%g) != written (%g) at position (%ld)\n", + iv[i],qv[i],i); + exit(1); + } + + fprintf(stderr,"OK\n"); + ptr++; + } + + /* The above is the trivial stuff; now try unquantizing a log scale codebook */ + + exit(0); +} + +#endif + +#endif +/********* End of inlined file: codebook.c *********/ + +/********* Start of inlined file: envelope.c *********/ + +/********* Start of inlined file: juce_OggVorbisHeader.h *********/ +// This file is included at the start of each Ogg-Vorbis .c file, just to do a few housekeeping +// tasks.. + +#ifdef _MSC_VER + #pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4305 4189 4706) +#endif +/********* End of inlined file: juce_OggVorbisHeader.h *********/ + +#if JUCE_USE_OGGVORBIS + +#include +#include +#include +#include + +void _ve_envelope_init(envelope_lookup *e,vorbis_info *vi){ + codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; + vorbis_info_psy_global *gi=&ci->psy_g_param; + int ch=vi->channels; + int i,j; + int n=e->winlength=128; + e->searchstep=64; /* not random */ + + e->minenergy=gi->preecho_minenergy; + e->ch=ch; + e->storage=128; + e->cursor=ci->blocksizes[1]/2; + e->mdct_win=(float*)_ogg_calloc(n,sizeof(*e->mdct_win)); + mdct_init(&e->mdct,n); + + for(i=0;imdct_win[i]=sin(i/(n-1.)*M_PI); + e->mdct_win[i]*=e->mdct_win[i]; + } + + /* magic follows */ + e->band[0].begin=2; e->band[0].end=4; + e->band[1].begin=4; e->band[1].end=5; + e->band[2].begin=6; e->band[2].end=6; + e->band[3].begin=9; e->band[3].end=8; + e->band[4].begin=13; e->band[4].end=8; + e->band[5].begin=17; e->band[5].end=8; + e->band[6].begin=22; e->band[6].end=8; + + for(j=0;jband[j].end; + e->band[j].window=(float*)_ogg_malloc(n*sizeof(*e->band[0].window)); + for(i=0;iband[j].window[i]=sin((i+.5)/n*M_PI); + e->band[j].total+=e->band[j].window[i]; + } + e->band[j].total=1./e->band[j].total; + } + + e->filter=(envelope_filter_state*)_ogg_calloc(VE_BANDS*ch,sizeof(*e->filter)); + e->mark=(int*)_ogg_calloc(e->storage,sizeof(*e->mark)); + +} + +void _ve_envelope_clear(envelope_lookup *e){ + int i; + mdct_clear(&e->mdct); + for(i=0;iband[i].window); + _ogg_free(e->mdct_win); + _ogg_free(e->filter); + _ogg_free(e->mark); + memset(e,0,sizeof(*e)); +} + +/* fairly straight threshhold-by-band based until we find something + that works better and isn't patented. */ + +static int _ve_amp(envelope_lookup *ve, + vorbis_info_psy_global *gi, + float *data, + envelope_band *bands, + envelope_filter_state *filters, + long pos){ + long n=ve->winlength; + int ret=0; + long i,j; + float decay; + + /* we want to have a 'minimum bar' for energy, else we're just + basing blocks on quantization noise that outweighs the signal + itself (for low power signals) */ + + float minV=ve->minenergy; + float *vec=(float*) alloca(n*sizeof(*vec)); + + /* stretch is used to gradually lengthen the number of windows + considered prevoius-to-potential-trigger */ + int stretch=max(VE_MINSTRETCH,ve->stretch/2); + float penalty=gi->stretch_penalty-(ve->stretch/2-VE_MINSTRETCH); + if(penalty<0.f)penalty=0.f; + if(penalty>gi->stretch_penalty)penalty=gi->stretch_penalty; + + /*_analysis_output_always("lpcm",seq2,data,n,0,0, + totalshift+pos*ve->searchstep);*/ + + /* window and transform */ + for(i=0;imdct_win[i]; + mdct_forward(&ve->mdct,vec,vec); + + /*_analysis_output_always("mdct",seq2,vec,n/2,0,1,0); */ + + /* near-DC spreading function; this has nothing to do with + psychoacoustics, just sidelobe leakage and window size */ + { + float temp=vec[0]*vec[0]+.7*vec[1]*vec[1]+.2*vec[2]*vec[2]; + int ptr=filters->nearptr; + + /* the accumulation is regularly refreshed from scratch to avoid + floating point creep */ + if(ptr==0){ + decay=filters->nearDC_acc=filters->nearDC_partialacc+temp; + filters->nearDC_partialacc=temp; + }else{ + decay=filters->nearDC_acc+=temp; + filters->nearDC_partialacc+=temp; + } + filters->nearDC_acc-=filters->nearDC[ptr]; + filters->nearDC[ptr]=temp; + + decay*=(1./(VE_NEARDC+1)); + filters->nearptr++; + if(filters->nearptr>=VE_NEARDC)filters->nearptr=0; + decay=todB(&decay)*.5-15.f; + } + + /* perform spreading and limiting, also smooth the spectrum. yes, + the MDCT results in all real coefficients, but it still *behaves* + like real/imaginary pairs */ + for(i=0;i>1]=val; + decay-=8.; + } + + /*_analysis_output_always("spread",seq2++,vec,n/4,0,0,0);*/ + + /* perform preecho/postecho triggering by band */ + for(j=0;j=VE_AMP)filters[j].ampptr=0; + } + + /* look at min/max, decide trigger */ + if(valmax>gi->preecho_thresh[j]+penalty){ + ret|=1; + ret|=4; + } + if(valminpostecho_thresh[j]-penalty)ret|=2; + } + + return(ret); +} + +#if 0 +static int seq=0; +static ogg_int64_t totalshift=-1024; +#endif + +long _ve_envelope_search(vorbis_dsp_state *v){ + vorbis_info *vi=v->vi; + codec_setup_info *ci=(codec_setup_info *)vi->codec_setup; + vorbis_info_psy_global *gi=&ci->psy_g_param; + envelope_lookup *ve=((private_state *)(v->backend_state))->ve; + long i,j; + + int first=ve->current/ve->searchstep; + int last=v->pcm_current/ve->searchstep-VE_WIN; + if(first<0)first=0; + + /* make sure we have enough storage to match the PCM */ + if(last+VE_WIN+VE_POST>ve->storage){ + ve->storage=last+VE_WIN+VE_POST; /* be sure */ + ve->mark=(int*)_ogg_realloc(ve->mark,ve->storage*sizeof(*ve->mark)); + } + + for(j=first;jstretch++; + if(ve->stretch>VE_MAXSTRETCH*2) + ve->stretch=VE_MAXSTRETCH*2; + + for(i=0;ich;i++){ + float *pcm=v->pcm[i]+ve->searchstep*(j); + ret|=_ve_amp(ve,gi,pcm,ve->band,ve->filter+i*VE_BANDS,j); + } + + ve->mark[j+VE_POST]=0; + if(ret&1){ + ve->mark[j]=1; + ve->mark[j+1]=1; + } + + if(ret&2){ + ve->mark[j]=1; + if(j>0)ve->mark[j-1]=1; + } + + if(ret&4)ve->stretch=-1; + } + + ve->current=last*ve->searchstep; + + { + long centerW=v->centerW; + long testW= + centerW+ + ci->blocksizes[v->W]/4+ + ci->blocksizes[1]/2+ + ci->blocksizes[0]/4; + + j=ve->cursor; + + while(jcurrent-(ve->searchstep)){/* account for postecho + working back one window */ + if(j>=testW)return(1); + + ve->cursor=j; + + if(ve->mark[j/ve->searchstep]){ + if(j>centerW){ + +#if 0 + if(j>ve->curmark){ + float *marker=alloca(v->pcm_current*sizeof(*marker)); + int l,m; + memset(marker,0,sizeof(*marker)*v->pcm_current); + fprintf(stderr,"mark! seq=%d, cursor:%fs time:%fs\n", + seq, + (totalshift+ve->cursor)/44100., + (totalshift+j)/44100.); + _analysis_output_always("pcmL",seq,v->pcm[0],v->pcm_current,0,0,totalshift); + _analysis_output_always("pcmR",seq,v->pcm[1],v->pcm_current,0,0,totalshift); + + _analysis_output_always("markL",seq,v->pcm[0],j,0,0,totalshift); + _analysis_output_always("markR",seq,v->pcm[1],j,0,0,totalshift); + + for(m=0;msearchstep]=ve->filter[m].markers[l]*.1; + _analysis_output_always(buf,seq,marker,v->pcm_current,0,0,totalshift); + } + + for(m=0;msearchstep]=ve->filter[m+VE_BANDS].markers[l]*.1; + _analysis_output_always(buf,seq,marker,v->pcm_current,0,0,totalshift); + } + + for(l=0;lsearchstep]=ve->mark[l]*.4; + _analysis_output_always("mark",seq,marker,v->pcm_current,0,0,totalshift); + + seq++; + + } +#endif + + ve->curmark=j; + if(j>=testW)return(1); + return(0); + } + } + j+=ve->searchstep; + } + } + + return(-1); +} + +int _ve_envelope_mark(vorbis_dsp_state *v){ + envelope_lookup *ve=((private_state *)(v->backend_state))->ve; + vorbis_info *vi=v->vi; + codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; + long centerW=v->centerW; + long beginW=centerW-ci->blocksizes[v->W]/4; + long endW=centerW+ci->blocksizes[v->W]/4; + if(v->W){ + beginW-=ci->blocksizes[v->lW]/4; + endW+=ci->blocksizes[v->nW]/4; + }else{ + beginW-=ci->blocksizes[0]/4; + endW+=ci->blocksizes[0]/4; + } + + if(ve->curmark>=beginW && ve->curmarksearchstep; + long last=endW/ve->searchstep; + long i; + for(i=first;imark[i])return(1); + } + return(0); +} + +void _ve_envelope_shift(envelope_lookup *e,long shift){ + int smallsize=e->current/e->searchstep+VE_POST; /* adjust for placing marks + ahead of ve->current */ + int smallshift=shift/e->searchstep; + + memmove(e->mark,e->mark+smallshift,(smallsize-smallshift)*sizeof(*e->mark)); + +#if 0 + for(i=0;ich;i++) + memmove(e->filter[i].markers, + e->filter[i].markers+smallshift, + (1024-smallshift)*sizeof(*(*e->filter).markers)); + totalshift+=shift; +#endif + + e->current-=shift; + if(e->curmark>=0) + e->curmark-=shift; + e->cursor-=shift; +} + +#endif +/********* End of inlined file: envelope.c *********/ + +/********* Start of inlined file: floor0.c *********/ + +/********* Start of inlined file: juce_OggVorbisHeader.h *********/ +// This file is included at the start of each Ogg-Vorbis .c file, just to do a few housekeeping +// tasks.. + +#ifdef _MSC_VER + #pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4305 4189 4706) +#endif +/********* End of inlined file: juce_OggVorbisHeader.h *********/ + +#if JUCE_USE_OGGVORBIS + +#include +#include +#include + +/********* Start of inlined file: lsp.h *********/ +#ifndef _V_LSP_H_ +#define _V_LSP_H_ + +extern int vorbis_lpc_to_lsp(float *lpc,float *lsp,int m); + +extern void vorbis_lsp_to_curve(float *curve,int *map,int n,int ln, + float *lsp,int m, + float amp,float ampoffset); + +#endif +/********* End of inlined file: lsp.h *********/ + +#include + +typedef struct { + int ln; + int m; + int **linearmap; + int n[2]; + + vorbis_info_floor0 *vi; + + long bits; + long frames; +} vorbis_look_floor0; + +/***********************************************/ + +static void floor0_free_info(vorbis_info_floor *i){ + vorbis_info_floor0 *info=(vorbis_info_floor0 *)i; + if(info){ + memset(info,0,sizeof(*info)); + _ogg_free(info); + } +} + +static void floor0_free_look(vorbis_look_floor *i){ + vorbis_look_floor0 *look=(vorbis_look_floor0 *)i; + if(look){ + + if(look->linearmap){ + + if(look->linearmap[0])_ogg_free(look->linearmap[0]); + if(look->linearmap[1])_ogg_free(look->linearmap[1]); + + _ogg_free(look->linearmap); + } + memset(look,0,sizeof(*look)); + _ogg_free(look); + } +} + +static vorbis_info_floor *floor0_unpack (vorbis_info *vi,oggpack_buffer *opb){ + codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; + int j; + + vorbis_info_floor0 *info=(vorbis_info_floor0*)_ogg_malloc(sizeof(*info)); + info->order=oggpack_read(opb,8); + info->rate=oggpack_read(opb,16); + info->barkmap=oggpack_read(opb,16); + info->ampbits=oggpack_read(opb,6); + info->ampdB=oggpack_read(opb,8); + info->numbooks=oggpack_read(opb,4)+1; + + if(info->order<1)goto err_out; + if(info->rate<1)goto err_out; + if(info->barkmap<1)goto err_out; + if(info->numbooks<1)goto err_out; + + for(j=0;jnumbooks;j++){ + info->books[j]=oggpack_read(opb,8); + if(info->books[j]<0 || info->books[j]>=ci->books)goto err_out; + } + return(info); + + err_out: + floor0_free_info(info); + return(NULL); +} + +/* initialize Bark scale and normalization lookups. We could do this + with static tables, but Vorbis allows a number of possible + combinations, so it's best to do it computationally. + + The below is authoritative in terms of defining scale mapping. + Note that the scale depends on the sampling rate as well as the + linear block and mapping sizes */ + +static void floor0_map_lazy_init(vorbis_block *vb, + vorbis_info_floor *infoX, + vorbis_look_floor0 *look){ + if(!look->linearmap[vb->W]){ + vorbis_dsp_state *vd=vb->vd; + vorbis_info *vi=vd->vi; + codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; + vorbis_info_floor0 *info=(vorbis_info_floor0 *)infoX; + int W=vb->W; + int n=ci->blocksizes[W]/2,j; + + /* we choose a scaling constant so that: + floor(bark(rate/2-1)*C)=mapped-1 + floor(bark(rate/2)*C)=mapped */ + float scale=look->ln/toBARK(info->rate/2.f); + + /* the mapping from a linear scale to a smaller bark scale is + straightforward. We do *not* make sure that the linear mapping + does not skip bark-scale bins; the decoder simply skips them and + the encoder may do what it wishes in filling them. They're + necessary in some mapping combinations to keep the scale spacing + accurate */ + look->linearmap[W]=(int*)_ogg_malloc((n+1)*sizeof(**look->linearmap)); + for(j=0;jrate/2.f)/n*j) + *scale); /* bark numbers represent band edges */ + if(val>=look->ln)val=look->ln-1; /* guard against the approximation */ + look->linearmap[W][j]=val; + } + look->linearmap[W][j]=-1; + look->n[W]=n; + } +} + +static vorbis_look_floor *floor0_look(vorbis_dsp_state *vd, + vorbis_info_floor *i){ + vorbis_info_floor0 *info=(vorbis_info_floor0*)i; + vorbis_look_floor0 *look=(vorbis_look_floor0*)_ogg_calloc(1,sizeof(*look)); + look->m=info->order; + look->ln=info->barkmap; + look->vi=info; + + look->linearmap=(int**)_ogg_calloc(2,sizeof(*look->linearmap)); + + return look; +} + +static void *floor0_inverse1(vorbis_block *vb,vorbis_look_floor *i){ + vorbis_look_floor0 *look=(vorbis_look_floor0 *)i; + vorbis_info_floor0 *info=look->vi; + int j,k; + + int ampraw=oggpack_read(&vb->opb,info->ampbits); + if(ampraw>0){ /* also handles the -1 out of data case */ + long maxval=(1<ampbits)-1; + float amp=(float)ampraw/maxval*info->ampdB; + int booknum=oggpack_read(&vb->opb,_ilog(info->numbooks)); + + if(booknum!=-1 && booknumnumbooks){ /* be paranoid */ + codec_setup_info *ci=(codec_setup_info *)vb->vd->vi->codec_setup; + codebook *b=ci->fullbooks+info->books[booknum]; + float last=0.f; + + /* the additional b->dim is a guard against any possible stack + smash; b->dim is provably more than we can overflow the + vector */ + float *lsp=(float*)_vorbis_block_alloc(vb,sizeof(*lsp)*(look->m+b->dim+1)); + + for(j=0;jm;j+=b->dim) + if(vorbis_book_decodev_set(b,lsp+j,&vb->opb,b->dim)==-1)goto eop; + for(j=0;jm;){ + for(k=0;kdim;k++,j++)lsp[j]+=last; + last=lsp[j-1]; + } + + lsp[look->m]=amp; + return(lsp); + } + } + eop: + return(NULL); +} + +static int floor0_inverse2(vorbis_block *vb,vorbis_look_floor *i, + void *memo,float *out){ + vorbis_look_floor0 *look=(vorbis_look_floor0 *)i; + vorbis_info_floor0 *info=look->vi; + + floor0_map_lazy_init(vb,info,look); + + if(memo){ + float *lsp=(float *)memo; + float amp=lsp[look->m]; + + /* take the coefficients back to a spectral envelope curve */ + vorbis_lsp_to_curve(out, + look->linearmap[vb->W], + look->n[vb->W], + look->ln, + lsp,look->m,amp,(float)info->ampdB); + return(1); + } + memset(out,0,sizeof(*out)*look->n[vb->W]); + return(0); +} + +/* export hooks */ +vorbis_func_floor floor0_exportbundle={ + NULL,&floor0_unpack,&floor0_look,&floor0_free_info, + &floor0_free_look,&floor0_inverse1,&floor0_inverse2 +}; + +#endif +/********* End of inlined file: floor0.c *********/ + +/********* Start of inlined file: floor1.c *********/ + +/********* Start of inlined file: juce_OggVorbisHeader.h *********/ +// This file is included at the start of each Ogg-Vorbis .c file, just to do a few housekeeping +// tasks.. + +#ifdef _MSC_VER + #pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4305 4189 4706) +#endif +/********* End of inlined file: juce_OggVorbisHeader.h *********/ + +#if JUCE_USE_OGGVORBIS + +#include +#include +#include + +#include + +#define floor1_rangedB 140 /* floor 1 fixed at -140dB to 0dB range */ + +typedef struct { + int sorted_index[VIF_POSIT+2]; + int forward_index[VIF_POSIT+2]; + int reverse_index[VIF_POSIT+2]; + + int hineighbor[VIF_POSIT]; + int loneighbor[VIF_POSIT]; + int posts; + + int n; + int quant_q; + vorbis_info_floor1 *vi; + + long phrasebits; + long postbits; + long frames; +} vorbis_look_floor1; + +typedef struct lsfit_acc{ + long x0; + long x1; + + long xa; + long ya; + long x2a; + long y2a; + long xya; + long an; +} lsfit_acc; + +/***********************************************/ + +static void floor1_free_info(vorbis_info_floor *i){ + vorbis_info_floor1 *info=(vorbis_info_floor1 *)i; + if(info){ + memset(info,0,sizeof(*info)); + _ogg_free(info); + } +} + +static void floor1_free_look(vorbis_look_floor *i){ + vorbis_look_floor1 *look=(vorbis_look_floor1 *)i; + if(look){ + /*fprintf(stderr,"floor 1 bit usage %f:%f (%f total)\n", + (float)look->phrasebits/look->frames, + (float)look->postbits/look->frames, + (float)(look->postbits+look->phrasebits)/look->frames);*/ + + memset(look,0,sizeof(*look)); + _ogg_free(look); + } +} + +static void floor1_pack (vorbis_info_floor *i,oggpack_buffer *opb){ + vorbis_info_floor1 *info=(vorbis_info_floor1 *)i; + int j,k; + int count=0; + int rangebits; + int maxposit=info->postlist[1]; + int maxclass=-1; + + /* save out partitions */ + oggpack_write(opb,info->partitions,5); /* only 0 to 31 legal */ + for(j=0;jpartitions;j++){ + oggpack_write(opb,info->partitionclass[j],4); /* only 0 to 15 legal */ + if(maxclasspartitionclass[j])maxclass=info->partitionclass[j]; + } + + /* save out partition classes */ + for(j=0;jclass_dim[j]-1,3); /* 1 to 8 */ + oggpack_write(opb,info->class_subs[j],2); /* 0 to 3 */ + if(info->class_subs[j])oggpack_write(opb,info->class_book[j],8); + for(k=0;k<(1<class_subs[j]);k++) + oggpack_write(opb,info->class_subbook[j][k]+1,8); + } + + /* save out the post list */ + oggpack_write(opb,info->mult-1,2); /* only 1,2,3,4 legal now */ + oggpack_write(opb,ilog2(maxposit),4); + rangebits=ilog2(maxposit); + + for(j=0,k=0;jpartitions;j++){ + count+=info->class_dim[info->partitionclass[j]]; + for(;kpostlist[k+2],rangebits); + } +} + +static vorbis_info_floor *floor1_unpack (vorbis_info *vi,oggpack_buffer *opb){ + codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; + int j,k,count=0,maxclass=-1,rangebits; + + vorbis_info_floor1 *info=(vorbis_info_floor1*)_ogg_calloc(1,sizeof(*info)); + /* read partitions */ + info->partitions=oggpack_read(opb,5); /* only 0 to 31 legal */ + for(j=0;jpartitions;j++){ + info->partitionclass[j]=oggpack_read(opb,4); /* only 0 to 15 legal */ + if(maxclasspartitionclass[j])maxclass=info->partitionclass[j]; + } + + /* read partition classes */ + for(j=0;jclass_dim[j]=oggpack_read(opb,3)+1; /* 1 to 8 */ + info->class_subs[j]=oggpack_read(opb,2); /* 0,1,2,3 bits */ + if(info->class_subs[j]<0) + goto err_out; + if(info->class_subs[j])info->class_book[j]=oggpack_read(opb,8); + if(info->class_book[j]<0 || info->class_book[j]>=ci->books) + goto err_out; + for(k=0;k<(1<class_subs[j]);k++){ + info->class_subbook[j][k]=oggpack_read(opb,8)-1; + if(info->class_subbook[j][k]<-1 || info->class_subbook[j][k]>=ci->books) + goto err_out; + } + } + + /* read the post list */ + info->mult=oggpack_read(opb,2)+1; /* only 1,2,3,4 legal now */ + rangebits=oggpack_read(opb,4); + + for(j=0,k=0;jpartitions;j++){ + count+=info->class_dim[info->partitionclass[j]]; + for(;kpostlist[k+2]=oggpack_read(opb,rangebits); + if(t<0 || t>=(1<postlist[0]=0; + info->postlist[1]=1<vi=info; + look->n=info->postlist[1]; + + /* we drop each position value in-between already decoded values, + and use linear interpolation to predict each new value past the + edges. The positions are read in the order of the position + list... we precompute the bounding positions in the lookup. Of + course, the neighbors can change (if a position is declined), but + this is an initial mapping */ + + for(i=0;ipartitions;i++)n+=info->class_dim[info->partitionclass[i]]; + n+=2; + look->posts=n; + + /* also store a sorted position index */ + for(i=0;ipostlist+i; + qsort(sortpointer,n,sizeof(*sortpointer),icomp); + + /* points from sort order back to range number */ + for(i=0;iforward_index[i]=sortpointer[i]-info->postlist; + /* points from range order to sorted position */ + for(i=0;ireverse_index[look->forward_index[i]]=i; + /* we actually need the post values too */ + for(i=0;isorted_index[i]=info->postlist[look->forward_index[i]]; + + /* quantize values to multiplier spec */ + switch(info->mult){ + case 1: /* 1024 -> 256 */ + look->quant_q=256; + break; + case 2: /* 1024 -> 128 */ + look->quant_q=128; + break; + case 3: /* 1024 -> 86 */ + look->quant_q=86; + break; + case 4: /* 1024 -> 64 */ + look->quant_q=64; + break; + } + + /* discover our neighbors for decode where we don't use fit flags + (that would push the neighbors outward) */ + for(i=0;in; + int currentx=info->postlist[i+2]; + for(j=0;jpostlist[j]; + if(x>lx && xcurrentx){ + hi=j; + hx=x; + } + } + look->loneighbor[i]=lo; + look->hineighbor[i]=hi; + } + + return(look); +} + +static int render_point(int x0,int x1,int y0,int y1,int x){ + y0&=0x7fff; /* mask off flag */ + y1&=0x7fff; + + { + int dy=y1-y0; + int adx=x1-x0; + int ady=abs(dy); + int err=ady*(x-x0); + + int off=err/adx; + if(dy<0)return(y0-off); + return(y0+off); + } +} + +static int vorbis_dBquant(const float *x){ + int i= *x*7.3142857f+1023.5f; + if(i>1023)return(1023); + if(i<0)return(0); + return i; +} + +static float FLOOR1_fromdB_LOOKUP[256]={ + 1.0649863e-07F, 1.1341951e-07F, 1.2079015e-07F, 1.2863978e-07F, + 1.3699951e-07F, 1.4590251e-07F, 1.5538408e-07F, 1.6548181e-07F, + 1.7623575e-07F, 1.8768855e-07F, 1.9988561e-07F, 2.128753e-07F, + 2.2670913e-07F, 2.4144197e-07F, 2.5713223e-07F, 2.7384213e-07F, + 2.9163793e-07F, 3.1059021e-07F, 3.3077411e-07F, 3.5226968e-07F, + 3.7516214e-07F, 3.9954229e-07F, 4.2550680e-07F, 4.5315863e-07F, + 4.8260743e-07F, 5.1396998e-07F, 5.4737065e-07F, 5.8294187e-07F, + 6.2082472e-07F, 6.6116941e-07F, 7.0413592e-07F, 7.4989464e-07F, + 7.9862701e-07F, 8.5052630e-07F, 9.0579828e-07F, 9.6466216e-07F, + 1.0273513e-06F, 1.0941144e-06F, 1.1652161e-06F, 1.2409384e-06F, + 1.3215816e-06F, 1.4074654e-06F, 1.4989305e-06F, 1.5963394e-06F, + 1.7000785e-06F, 1.8105592e-06F, 1.9282195e-06F, 2.0535261e-06F, + 2.1869758e-06F, 2.3290978e-06F, 2.4804557e-06F, 2.6416497e-06F, + 2.8133190e-06F, 2.9961443e-06F, 3.1908506e-06F, 3.3982101e-06F, + 3.6190449e-06F, 3.8542308e-06F, 4.1047004e-06F, 4.3714470e-06F, + 4.6555282e-06F, 4.9580707e-06F, 5.2802740e-06F, 5.6234160e-06F, + 5.9888572e-06F, 6.3780469e-06F, 6.7925283e-06F, 7.2339451e-06F, + 7.7040476e-06F, 8.2047000e-06F, 8.7378876e-06F, 9.3057248e-06F, + 9.9104632e-06F, 1.0554501e-05F, 1.1240392e-05F, 1.1970856e-05F, + 1.2748789e-05F, 1.3577278e-05F, 1.4459606e-05F, 1.5399272e-05F, + 1.6400004e-05F, 1.7465768e-05F, 1.8600792e-05F, 1.9809576e-05F, + 2.1096914e-05F, 2.2467911e-05F, 2.3928002e-05F, 2.5482978e-05F, + 2.7139006e-05F, 2.8902651e-05F, 3.0780908e-05F, 3.2781225e-05F, + 3.4911534e-05F, 3.7180282e-05F, 3.9596466e-05F, 4.2169667e-05F, + 4.4910090e-05F, 4.7828601e-05F, 5.0936773e-05F, 5.4246931e-05F, + 5.7772202e-05F, 6.1526565e-05F, 6.5524908e-05F, 6.9783085e-05F, + 7.4317983e-05F, 7.9147585e-05F, 8.4291040e-05F, 8.9768747e-05F, + 9.5602426e-05F, 0.00010181521F, 0.00010843174F, 0.00011547824F, + 0.00012298267F, 0.00013097477F, 0.00013948625F, 0.00014855085F, + 0.00015820453F, 0.00016848555F, 0.00017943469F, 0.00019109536F, + 0.00020351382F, 0.00021673929F, 0.00023082423F, 0.00024582449F, + 0.00026179955F, 0.00027881276F, 0.00029693158F, 0.00031622787F, + 0.00033677814F, 0.00035866388F, 0.00038197188F, 0.00040679456F, + 0.00043323036F, 0.00046138411F, 0.00049136745F, 0.00052329927F, + 0.00055730621F, 0.00059352311F, 0.00063209358F, 0.00067317058F, + 0.00071691700F, 0.00076350630F, 0.00081312324F, 0.00086596457F, + 0.00092223983F, 0.00098217216F, 0.0010459992F, 0.0011139742F, + 0.0011863665F, 0.0012634633F, 0.0013455702F, 0.0014330129F, + 0.0015261382F, 0.0016253153F, 0.0017309374F, 0.0018434235F, + 0.0019632195F, 0.0020908006F, 0.0022266726F, 0.0023713743F, + 0.0025254795F, 0.0026895994F, 0.0028643847F, 0.0030505286F, + 0.0032487691F, 0.0034598925F, 0.0036847358F, 0.0039241906F, + 0.0041792066F, 0.0044507950F, 0.0047400328F, 0.0050480668F, + 0.0053761186F, 0.0057254891F, 0.0060975636F, 0.0064938176F, + 0.0069158225F, 0.0073652516F, 0.0078438871F, 0.0083536271F, + 0.0088964928F, 0.009474637F, 0.010090352F, 0.010746080F, + 0.011444421F, 0.012188144F, 0.012980198F, 0.013823725F, + 0.014722068F, 0.015678791F, 0.016697687F, 0.017782797F, + 0.018938423F, 0.020169149F, 0.021479854F, 0.022875735F, + 0.024362330F, 0.025945531F, 0.027631618F, 0.029427276F, + 0.031339626F, 0.033376252F, 0.035545228F, 0.037855157F, + 0.040315199F, 0.042935108F, 0.045725273F, 0.048696758F, + 0.051861348F, 0.055231591F, 0.058820850F, 0.062643361F, + 0.066714279F, 0.071049749F, 0.075666962F, 0.080584227F, + 0.085821044F, 0.091398179F, 0.097337747F, 0.10366330F, + 0.11039993F, 0.11757434F, 0.12521498F, 0.13335215F, + 0.14201813F, 0.15124727F, 0.16107617F, 0.17154380F, + 0.18269168F, 0.19456402F, 0.20720788F, 0.22067342F, + 0.23501402F, 0.25028656F, 0.26655159F, 0.28387361F, + 0.30232132F, 0.32196786F, 0.34289114F, 0.36517414F, + 0.38890521F, 0.41417847F, 0.44109412F, 0.46975890F, + 0.50028648F, 0.53279791F, 0.56742212F, 0.60429640F, + 0.64356699F, 0.68538959F, 0.72993007F, 0.77736504F, + 0.82788260F, 0.88168307F, 0.9389798F, 1.F, +}; + +static void render_line(int x0,int x1,int y0,int y1,float *d){ + int dy=y1-y0; + int adx=x1-x0; + int ady=abs(dy); + int base=dy/adx; + int sy=(dy<0?base-1:base+1); + int x=x0; + int y=y0; + int err=0; + + ady-=abs(base*adx); + + d[x]*=FLOOR1_fromdB_LOOKUP[y]; + while(++x=adx){ + err-=adx; + y+=sy; + }else{ + y+=base; + } + d[x]*=FLOOR1_fromdB_LOOKUP[y]; + } +} + +static void render_line0(int x0,int x1,int y0,int y1,int *d){ + int dy=y1-y0; + int adx=x1-x0; + int ady=abs(dy); + int base=dy/adx; + int sy=(dy<0?base-1:base+1); + int x=x0; + int y=y0; + int err=0; + + ady-=abs(base*adx); + + d[x]=y; + while(++x=adx){ + err-=adx; + y+=sy; + }else{ + y+=base; + } + d[x]=y; + } +} + +/* the floor has already been filtered to only include relevant sections */ +static int accumulate_fit(const float *flr,const float *mdct, + int x0, int x1,lsfit_acc *a, + int n,vorbis_info_floor1 *info){ + long i; + long xa=0,ya=0,x2a=0,y2a=0,xya=0,na=0, xb=0,yb=0,x2b=0,y2b=0,xyb=0,nb=0; + + memset(a,0,sizeof(*a)); + a->x0=x0; + a->x1=x1; + if(x1>=n)x1=n-1; + + for(i=x0;i<=x1;i++){ + int quantized=vorbis_dBquant(flr+i); + if(quantized){ + if(mdct[i]+info->twofitatten>=flr[i]){ + xa += i; + ya += quantized; + x2a += i*i; + y2a += quantized*quantized; + xya += i*quantized; + na++; + }else{ + xb += i; + yb += quantized; + x2b += i*i; + y2b += quantized*quantized; + xyb += i*quantized; + nb++; + } + } + } + + xb+=xa; + yb+=ya; + x2b+=x2a; + y2b+=y2a; + xyb+=xya; + nb+=na; + + /* weight toward the actually used frequencies if we meet the threshhold */ + { + int weight=nb*info->twofitweight/(na+1); + + a->xa=xa*weight+xb; + a->ya=ya*weight+yb; + a->x2a=x2a*weight+x2b; + a->y2a=y2a*weight+y2b; + a->xya=xya*weight+xyb; + a->an=na*weight+nb; + } + + return(na); +} + +static void fit_line(lsfit_acc *a,int fits,int *y0,int *y1){ + long x=0,y=0,x2=0,y2=0,xy=0,an=0,i; + long x0=a[0].x0; + long x1=a[fits-1].x1; + + for(i=0;i=0){ + x+= x0; + y+= *y0; + x2+= x0 * x0; + y2+= *y0 * *y0; + xy+= *y0 * x0; + an++; + } + + if(*y1>=0){ + x+= x1; + y+= *y1; + x2+= x1 * x1; + y2+= *y1 * *y1; + xy+= *y1 * x1; + an++; + } + + if(an){ + /* need 64 bit multiplies, which C doesn't give portably as int */ + double fx=x; + double fy=y; + double fx2=x2; + double fxy=xy; + double denom=1./(an*fx2-fx*fx); + double a=(fy*fx2-fxy*fx)*denom; + double b=(an*fxy-fx*fy)*denom; + *y0=rint(a+b*x0); + *y1=rint(a+b*x1); + + /* limit to our range! */ + if(*y0>1023)*y0=1023; + if(*y1>1023)*y1=1023; + if(*y0<0)*y0=0; + if(*y1<0)*y1=0; + + }else{ + *y0=0; + *y1=0; + } +} + +/*static void fit_line_point(lsfit_acc *a,int fits,int *y0,int *y1){ + long y=0; + int i; + + for(i=0;itwofitatten>=mask[x]){ + if(y+info->maxovermaxunder>val)return(1); + } + + while(++x=adx){ + err-=adx; + y+=sy; + }else{ + y+=base; + } + + val=vorbis_dBquant(mask+x); + mse+=((y-val)*(y-val)); + n++; + if(mdct[x]+info->twofitatten>=mask[x]){ + if(val){ + if(y+info->maxovermaxunder>val)return(1); + } + } + } + + if(info->maxover*info->maxover/n>info->maxerr)return(0); + if(info->maxunder*info->maxunder/n>info->maxerr)return(0); + if(mse/n>info->maxerr)return(1); + return(0); +} + +static int post_Y(int *A,int *B,int pos){ + if(A[pos]<0) + return B[pos]; + if(B[pos]<0) + return A[pos]; + + return (A[pos]+B[pos])>>1; +} + +int *floor1_fit(vorbis_block *vb,void *look_, + const float *logmdct, /* in */ + const float *logmask){ + long i,j; + vorbis_look_floor1 *look = (vorbis_look_floor1*) look_; + vorbis_info_floor1 *info=look->vi; + long n=look->n; + long posts=look->posts; + long nonzero=0; + lsfit_acc fits[VIF_POSIT+1]; + int fit_valueA[VIF_POSIT+2]; /* index by range list position */ + int fit_valueB[VIF_POSIT+2]; /* index by range list position */ + + int loneighbor[VIF_POSIT+2]; /* sorted index of range list position (+2) */ + int hineighbor[VIF_POSIT+2]; + int *output=NULL; + int memo[VIF_POSIT+2]; + + for(i=0;isorted_index[i], + look->sorted_index[i+1],fits+i, + n,info); + } + + if(nonzero){ + /* start by fitting the implicit base case.... */ + int y0=-200; + int y1=-200; + fit_line(fits,posts-1,&y0,&y1); + + fit_valueA[0]=y0; + fit_valueB[0]=y0; + fit_valueB[1]=y1; + fit_valueA[1]=y1; + + /* Non degenerate case */ + /* start progressive splitting. This is a greedy, non-optimal + algorithm, but simple and close enough to the best + answer. */ + for(i=2;ireverse_index[i]; + int ln=loneighbor[sortpos]; + int hn=hineighbor[sortpos]; + + /* eliminate repeat searches of a particular range with a memo */ + if(memo[ln]!=hn){ + /* haven't performed this error search yet */ + int lsortpos=look->reverse_index[ln]; + int hsortpos=look->reverse_index[hn]; + memo[ln]=hn; + + { + /* A note: we want to bound/minimize *local*, not global, error */ + int lx=info->postlist[ln]; + int hx=info->postlist[hn]; + int ly=post_Y(fit_valueA,fit_valueB,ln); + int hy=post_Y(fit_valueA,fit_valueB,hn); + + if(ly==-1 || hy==-1){ + exit(1); + } + + if(inspect_error(lx,hx,ly,hy,logmask,logmdct,info)){ + /* outside error bounds/begin search area. Split it. */ + int ly0=-200; + int ly1=-200; + int hy0=-200; + int hy1=-200; + fit_line(fits+lsortpos,sortpos-lsortpos,&ly0,&ly1); + fit_line(fits+sortpos,hsortpos-sortpos,&hy0,&hy1); + + /* store new edge values */ + fit_valueB[ln]=ly0; + if(ln==0)fit_valueA[ln]=ly0; + fit_valueA[i]=ly1; + fit_valueB[i]=hy0; + fit_valueA[hn]=hy1; + if(hn==1)fit_valueB[hn]=hy1; + + if(ly1>=0 || hy0>=0){ + /* store new neighbor values */ + for(j=sortpos-1;j>=0;j--) + if(hineighbor[j]==hn) + hineighbor[j]=i; + else + break; + for(j=sortpos+1;jloneighbor[i-2]; + int hn=look->hineighbor[i-2]; + int x0=info->postlist[ln]; + int x1=info->postlist[hn]; + int y0=output[ln]; + int y1=output[hn]; + + int predicted=render_point(x0,x1,y0,y1,info->postlist[i]); + int vx=post_Y(fit_valueA,fit_valueB,i); + + if(vx>=0 && predicted!=vx){ + output[i]=vx; + }else{ + output[i]= predicted|0x8000; + } + } + } + + return(output); + +} + +int *floor1_interpolate_fit(vorbis_block *vb,void *look_, + int *A,int *B, + int del){ + + long i; + vorbis_look_floor1* look = (vorbis_look_floor1*) look_; + long posts=look->posts; + int *output=NULL; + + if(A && B){ + output=(int*)_vorbis_block_alloc(vb,sizeof(*output)*posts); + + for(i=0;i>16; + if(A[i]&0x8000 && B[i]&0x8000)output[i]|=0x8000; + } + } + + return(output); +} + +int floor1_encode(oggpack_buffer *opb,vorbis_block *vb, + void*look_, + int *post,int *ilogmask){ + + long i,j; + vorbis_look_floor1 *look = (vorbis_look_floor1 *) look_; + vorbis_info_floor1 *info=look->vi; + long posts=look->posts; + codec_setup_info *ci=(codec_setup_info*)vb->vd->vi->codec_setup; + int out[VIF_POSIT+2]; + static_codebook **sbooks=ci->book_param; + codebook *books=ci->fullbooks; + static long seq=0; + + /* quantize values to multiplier spec */ + if(post){ + for(i=0;imult){ + case 1: /* 1024 -> 256 */ + val>>=2; + break; + case 2: /* 1024 -> 128 */ + val>>=3; + break; + case 3: /* 1024 -> 86 */ + val/=12; + break; + case 4: /* 1024 -> 64 */ + val>>=4; + break; + } + post[i]=val | (post[i]&0x8000); + } + + out[0]=post[0]; + out[1]=post[1]; + + /* find prediction values for each post and subtract them */ + for(i=2;iloneighbor[i-2]; + int hn=look->hineighbor[i-2]; + int x0=info->postlist[ln]; + int x1=info->postlist[hn]; + int y0=post[ln]; + int y1=post[hn]; + + int predicted=render_point(x0,x1,y0,y1,info->postlist[i]); + + if((post[i]&0x8000) || (predicted==post[i])){ + post[i]=predicted|0x8000; /* in case there was roundoff jitter + in interpolation */ + out[i]=0; + }else{ + int headroom=(look->quant_q-predictedquant_q-predicted:predicted); + + int val=post[i]-predicted; + + /* at this point the 'deviation' value is in the range +/- max + range, but the real, unique range can always be mapped to + only [0-maxrange). So we want to wrap the deviation into + this limited range, but do it in the way that least screws + an essentially gaussian probability distribution. */ + + if(val<0) + if(val<-headroom) + val=headroom-val-1; + else + val=-1-(val<<1); + else + if(val>=headroom) + val= val+headroom; + else + val<<=1; + + out[i]=val; + post[ln]&=0x7fff; + post[hn]&=0x7fff; + } + } + + /* we have everything we need. pack it out */ + /* mark nontrivial floor */ + oggpack_write(opb,1,1); + + /* beginning/end post */ + look->frames++; + look->postbits+=ilog(look->quant_q-1)*2; + oggpack_write(opb,out[0],ilog(look->quant_q-1)); + oggpack_write(opb,out[1],ilog(look->quant_q-1)); + + /* partition by partition */ + for(i=0,j=2;ipartitions;i++){ + int classx=info->partitionclass[i]; + int cdim=info->class_dim[classx]; + int csubbits=info->class_subs[classx]; + int csub=1<class_subbook[classx][k]; + if(booknum<0){ + maxval[k]=1; + }else{ + maxval[k]=sbooks[info->class_subbook[classx][k]]->entries; + } + } + for(k=0;kphrasebits+= + vorbis_book_encode(books+info->class_book[classx],cval,opb); + +#ifdef TRAIN_FLOOR1 + { + FILE *of; + char buffer[80]; + sprintf(buffer,"line_%dx%ld_class%d.vqd", + vb->pcmend/2,posts-2,class); + of=fopen(buffer,"a"); + fprintf(of,"%d\n",cval); + fclose(of); + } +#endif + } + + /* write post values */ + for(k=0;kclass_subbook[classx][bookas[k]]; + if(book>=0){ + /* hack to allow training with 'bad' books */ + if(out[j+k]<(books+book)->entries) + look->postbits+=vorbis_book_encode(books+book, + out[j+k],opb); + /*else + fprintf(stderr,"+!");*/ + +#ifdef TRAIN_FLOOR1 + { + FILE *of; + char buffer[80]; + sprintf(buffer,"line_%dx%ld_%dsub%d.vqd", + vb->pcmend/2,posts-2,class,bookas[k]); + of=fopen(buffer,"a"); + fprintf(of,"%d\n",out[j+k]); + fclose(of); + } +#endif + } + } + j+=cdim; + } + + { + /* generate quantized floor equivalent to what we'd unpack in decode */ + /* render the lines */ + int hx=0; + int lx=0; + int ly=post[0]*info->mult; + for(j=1;jposts;j++){ + int current=look->forward_index[j]; + int hy=post[current]&0x7fff; + if(hy==post[current]){ + + hy*=info->mult; + hx=info->postlist[current]; + + render_line0(lx,hx,ly,hy,ilogmask); + + lx=hx; + ly=hy; + } + } + for(j=hx;jpcmend/2;j++)ilogmask[j]=ly; /* be certain */ + seq++; + return(1); + } + }else{ + oggpack_write(opb,0,1); + memset(ilogmask,0,vb->pcmend/2*sizeof(*ilogmask)); + seq++; + return(0); + } +} + +static void *floor1_inverse1(vorbis_block *vb,vorbis_look_floor *in){ + vorbis_look_floor1 *look=(vorbis_look_floor1 *)in; + vorbis_info_floor1 *info=look->vi; + codec_setup_info *ci=(codec_setup_info*)vb->vd->vi->codec_setup; + + int i,j,k; + codebook *books=ci->fullbooks; + + /* unpack wrapped/predicted values from stream */ + if(oggpack_read(&vb->opb,1)==1){ + int *fit_value=(int*)_vorbis_block_alloc(vb,(look->posts)*sizeof(*fit_value)); + + fit_value[0]=oggpack_read(&vb->opb,ilog(look->quant_q-1)); + fit_value[1]=oggpack_read(&vb->opb,ilog(look->quant_q-1)); + + /* partition by partition */ + for(i=0,j=2;ipartitions;i++){ + int classx=info->partitionclass[i]; + int cdim=info->class_dim[classx]; + int csubbits=info->class_subs[classx]; + int csub=1<class_book[classx],&vb->opb); + + if(cval==-1)goto eop; + } + + for(k=0;kclass_subbook[classx][cval&(csub-1)]; + cval>>=csubbits; + if(book>=0){ + if((fit_value[j+k]=vorbis_book_decode(books+book,&vb->opb))==-1) + goto eop; + }else{ + fit_value[j+k]=0; + } + } + j+=cdim; + } + + /* unwrap positive values and reconsitute via linear interpolation */ + for(i=2;iposts;i++){ + int predicted=render_point(info->postlist[look->loneighbor[i-2]], + info->postlist[look->hineighbor[i-2]], + fit_value[look->loneighbor[i-2]], + fit_value[look->hineighbor[i-2]], + info->postlist[i]); + int hiroom=look->quant_q-predicted; + int loroom=predicted; + int room=(hiroom=room){ + if(hiroom>loroom){ + val = val-loroom; + }else{ + val = -1-(val-hiroom); + } + }else{ + if(val&1){ + val= -((val+1)>>1); + }else{ + val>>=1; + } + } + + fit_value[i]=val+predicted; + fit_value[look->loneighbor[i-2]]&=0x7fff; + fit_value[look->hineighbor[i-2]]&=0x7fff; + + }else{ + fit_value[i]=predicted|0x8000; + } + + } + + return(fit_value); + } + eop: + return(NULL); +} + +static int floor1_inverse2(vorbis_block *vb,vorbis_look_floor *in,void *memo, + float *out){ + vorbis_look_floor1 *look=(vorbis_look_floor1 *)in; + vorbis_info_floor1 *info=look->vi; + + codec_setup_info *ci=(codec_setup_info*)vb->vd->vi->codec_setup; + int n=ci->blocksizes[vb->W]/2; + int j; + + if(memo){ + /* render the lines */ + int *fit_value=(int *)memo; + int hx=0; + int lx=0; + int ly=fit_value[0]*info->mult; + for(j=1;jposts;j++){ + int current=look->forward_index[j]; + int hy=fit_value[current]&0x7fff; + if(hy==fit_value[current]){ + + hy*=info->mult; + hx=info->postlist[current]; + + render_line(lx,hx,ly,hy,out); + + lx=hx; + ly=hy; + } + } + for(j=hx;j +#include +#include + +static void _v_writestring(oggpack_buffer *o,char *s, int bytes){ + + while(bytes--){ + oggpack_write(o,*s++,8); + } +} + +static void _v_readstring(oggpack_buffer *o,char *buf,int bytes){ + while(bytes--){ + *buf++=oggpack_read(o,8); + } +} + +void vorbis_comment_init(vorbis_comment *vc){ + memset(vc,0,sizeof(*vc)); +} + +void vorbis_comment_add(vorbis_comment *vc,char *comment){ + vc->user_comments=(char**)_ogg_realloc(vc->user_comments, + (vc->comments+2)*sizeof(*vc->user_comments)); + vc->comment_lengths=(int*)_ogg_realloc(vc->comment_lengths, + (vc->comments+2)*sizeof(*vc->comment_lengths)); + vc->comment_lengths[vc->comments]=strlen(comment); + vc->user_comments[vc->comments]=(char*)_ogg_malloc(vc->comment_lengths[vc->comments]+1); + strcpy(vc->user_comments[vc->comments], comment); + vc->comments++; + vc->user_comments[vc->comments]=NULL; +} + +void vorbis_comment_add_tag(vorbis_comment *vc, char *tag, char *contents){ + char *comment=(char*)alloca(strlen(tag)+strlen(contents)+2); /* +2 for = and \0 */ + strcpy(comment, tag); + strcat(comment, "="); + strcat(comment, contents); + vorbis_comment_add(vc, comment); +} + +/* This is more or less the same as strncasecmp - but that doesn't exist + * everywhere, and this is a fairly trivial function, so we include it */ +static int tagcompare(const char *s1, const char *s2, int n){ + int c=0; + while(c < n){ + if(toupper(s1[c]) != toupper(s2[c])) + return !0; + c++; + } + return 0; +} + +char *vorbis_comment_query(vorbis_comment *vc, char *tag, int count){ + long i; + int found = 0; + int taglen = strlen(tag)+1; /* +1 for the = we append */ + char *fulltag = (char*)alloca(taglen+ 1); + + strcpy(fulltag, tag); + strcat(fulltag, "="); + + for(i=0;icomments;i++){ + if(!tagcompare(vc->user_comments[i], fulltag, taglen)){ + if(count == found) + /* We return a pointer to the data, not a copy */ + return vc->user_comments[i] + taglen; + else + found++; + } + } + return NULL; /* didn't find anything */ +} + +int vorbis_comment_query_count(vorbis_comment *vc, char *tag){ + int i,count=0; + int taglen = strlen(tag)+1; /* +1 for the = we append */ + char *fulltag = (char*)alloca(taglen+1); + strcpy(fulltag,tag); + strcat(fulltag, "="); + + for(i=0;icomments;i++){ + if(!tagcompare(vc->user_comments[i], fulltag, taglen)) + count++; + } + + return count; +} + +void vorbis_comment_clear(vorbis_comment *vc){ + if(vc){ + long i; + for(i=0;icomments;i++) + if(vc->user_comments[i])_ogg_free(vc->user_comments[i]); + if(vc->user_comments)_ogg_free(vc->user_comments); + if(vc->comment_lengths)_ogg_free(vc->comment_lengths); + if(vc->vendor)_ogg_free(vc->vendor); + } + memset(vc,0,sizeof(*vc)); +} + +/* blocksize 0 is guaranteed to be short, 1 is guarantted to be long. + They may be equal, but short will never ge greater than long */ +int vorbis_info_blocksize(vorbis_info *vi,int zo){ + codec_setup_info *ci = (codec_setup_info*)vi->codec_setup; + return ci ? ci->blocksizes[zo] : -1; +} + +/* used by synthesis, which has a full, alloced vi */ +void vorbis_info_init(vorbis_info *vi){ + memset(vi,0,sizeof(*vi)); + vi->codec_setup=_ogg_calloc(1,sizeof(codec_setup_info)); +} + +void vorbis_info_clear(vorbis_info *vi){ + codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; + int i; + + if(ci){ + + for(i=0;imodes;i++) + if(ci->mode_param[i])_ogg_free(ci->mode_param[i]); + + for(i=0;imaps;i++) /* unpack does the range checking */ + _mapping_P[ci->map_type[i]]->free_info(ci->map_param[i]); + + for(i=0;ifloors;i++) /* unpack does the range checking */ + _floor_P[ci->floor_type[i]]->free_info(ci->floor_param[i]); + + for(i=0;iresidues;i++) /* unpack does the range checking */ + _residue_P[ci->residue_type[i]]->free_info(ci->residue_param[i]); + + for(i=0;ibooks;i++){ + if(ci->book_param[i]){ + /* knows if the book was not alloced */ + vorbis_staticbook_destroy(ci->book_param[i]); + } + if(ci->fullbooks) + vorbis_book_clear(ci->fullbooks+i); + } + if(ci->fullbooks) + _ogg_free(ci->fullbooks); + + for(i=0;ipsys;i++) + _vi_psy_free(ci->psy_param[i]); + + _ogg_free(ci); + } + + memset(vi,0,sizeof(*vi)); +} + +/* Header packing/unpacking ********************************************/ + +static int _vorbis_unpack_info(vorbis_info *vi,oggpack_buffer *opb){ + codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; + if(!ci)return(OV_EFAULT); + + vi->version=oggpack_read(opb,32); + if(vi->version!=0)return(OV_EVERSION); + + vi->channels=oggpack_read(opb,8); + vi->rate=oggpack_read(opb,32); + + vi->bitrate_upper=oggpack_read(opb,32); + vi->bitrate_nominal=oggpack_read(opb,32); + vi->bitrate_lower=oggpack_read(opb,32); + + ci->blocksizes[0]=1<blocksizes[1]=1<rate<1)goto err_out; + if(vi->channels<1)goto err_out; + if(ci->blocksizes[0]<8)goto err_out; + if(ci->blocksizes[1]blocksizes[0])goto err_out; + + if(oggpack_read(opb,1)!=1)goto err_out; /* EOP check */ + + return(0); + err_out: + vorbis_info_clear(vi); + return(OV_EBADHEADER); +} + +static int _vorbis_unpack_comment(vorbis_comment *vc,oggpack_buffer *opb){ + int i; + int vendorlen=oggpack_read(opb,32); + if(vendorlen<0)goto err_out; + vc->vendor=(char*)_ogg_calloc(vendorlen+1,1); + _v_readstring(opb,vc->vendor,vendorlen); + vc->comments=oggpack_read(opb,32); + if(vc->comments<0)goto err_out; + vc->user_comments=(char**)_ogg_calloc(vc->comments+1,sizeof(*vc->user_comments)); + vc->comment_lengths=(int*)_ogg_calloc(vc->comments+1, sizeof(*vc->comment_lengths)); + + for(i=0;icomments;i++){ + int len=oggpack_read(opb,32); + if(len<0)goto err_out; + vc->comment_lengths[i]=len; + vc->user_comments[i]=(char*)_ogg_calloc(len+1,1); + _v_readstring(opb,vc->user_comments[i],len); + } + if(oggpack_read(opb,1)!=1)goto err_out; /* EOP check */ + + return(0); + err_out: + vorbis_comment_clear(vc); + return(OV_EBADHEADER); +} + +/* all of the real encoding details are here. The modes, books, + everything */ +static int _vorbis_unpack_books(vorbis_info *vi,oggpack_buffer *opb){ + codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; + int i; + if(!ci)return(OV_EFAULT); + + /* codebooks */ + ci->books=oggpack_read(opb,8)+1; + /*ci->book_param=_ogg_calloc(ci->books,sizeof(*ci->book_param));*/ + for(i=0;ibooks;i++){ + ci->book_param[i]=(static_codebook*)_ogg_calloc(1,sizeof(*ci->book_param[i])); + if(vorbis_staticbook_unpack(opb,ci->book_param[i]))goto err_out; + } + + /* time backend settings; hooks are unused */ + { + int times=oggpack_read(opb,6)+1; + for(i=0;i=VI_TIMEB)goto err_out; + } + } + + /* floor backend settings */ + ci->floors=oggpack_read(opb,6)+1; + /*ci->floor_type=_ogg_malloc(ci->floors*sizeof(*ci->floor_type));*/ + /*ci->floor_param=_ogg_calloc(ci->floors,sizeof(void *));*/ + for(i=0;ifloors;i++){ + ci->floor_type[i]=oggpack_read(opb,16); + if(ci->floor_type[i]<0 || ci->floor_type[i]>=VI_FLOORB)goto err_out; + ci->floor_param[i]=_floor_P[ci->floor_type[i]]->unpack(vi,opb); + if(!ci->floor_param[i])goto err_out; + } + + /* residue backend settings */ + ci->residues=oggpack_read(opb,6)+1; + /*ci->residue_type=_ogg_malloc(ci->residues*sizeof(*ci->residue_type));*/ + /*ci->residue_param=_ogg_calloc(ci->residues,sizeof(void *));*/ + for(i=0;iresidues;i++){ + ci->residue_type[i]=oggpack_read(opb,16); + if(ci->residue_type[i]<0 || ci->residue_type[i]>=VI_RESB)goto err_out; + ci->residue_param[i]=_residue_P[ci->residue_type[i]]->unpack(vi,opb); + if(!ci->residue_param[i])goto err_out; + } + + /* map backend settings */ + ci->maps=oggpack_read(opb,6)+1; + /*ci->map_type=_ogg_malloc(ci->maps*sizeof(*ci->map_type));*/ + /*ci->map_param=_ogg_calloc(ci->maps,sizeof(void *));*/ + for(i=0;imaps;i++){ + ci->map_type[i]=oggpack_read(opb,16); + if(ci->map_type[i]<0 || ci->map_type[i]>=VI_MAPB)goto err_out; + ci->map_param[i]=_mapping_P[ci->map_type[i]]->unpack(vi,opb); + if(!ci->map_param[i])goto err_out; + } + + /* mode settings */ + ci->modes=oggpack_read(opb,6)+1; + /*vi->mode_param=_ogg_calloc(vi->modes,sizeof(void *));*/ + for(i=0;imodes;i++){ + ci->mode_param[i]=(vorbis_info_mode*)_ogg_calloc(1,sizeof(*ci->mode_param[i])); + ci->mode_param[i]->blockflag=oggpack_read(opb,1); + ci->mode_param[i]->windowtype=oggpack_read(opb,16); + ci->mode_param[i]->transformtype=oggpack_read(opb,16); + ci->mode_param[i]->mapping=oggpack_read(opb,8); + + if(ci->mode_param[i]->windowtype>=VI_WINDOWB)goto err_out; + if(ci->mode_param[i]->transformtype>=VI_WINDOWB)goto err_out; + if(ci->mode_param[i]->mapping>=ci->maps)goto err_out; + } + + if(oggpack_read(opb,1)!=1)goto err_out; /* top level EOP check */ + + return(0); + err_out: + vorbis_info_clear(vi); + return(OV_EBADHEADER); +} + +/* The Vorbis header is in three packets; the initial small packet in + the first page that identifies basic parameters, a second packet + with bitstream comments and a third packet that holds the + codebook. */ + +int vorbis_synthesis_headerin(vorbis_info *vi,vorbis_comment *vc,ogg_packet *op){ + oggpack_buffer opb; + + if(op){ + oggpack_readinit(&opb,op->packet,op->bytes); + + /* Which of the three types of header is this? */ + /* Also verify header-ness, vorbis */ + { + char buffer[6]; + int packtype=oggpack_read(&opb,8); + memset(buffer,0,6); + _v_readstring(&opb,buffer,6); + if(memcmp(buffer,"vorbis",6)){ + /* not a vorbis header */ + return(OV_ENOTVORBIS); + } + switch(packtype){ + case 0x01: /* least significant *bit* is read first */ + if(!op->b_o_s){ + /* Not the initial packet */ + return(OV_EBADHEADER); + } + if(vi->rate!=0){ + /* previously initialized info header */ + return(OV_EBADHEADER); + } + + return(_vorbis_unpack_info(vi,&opb)); + + case 0x03: /* least significant *bit* is read first */ + if(vi->rate==0){ + /* um... we didn't get the initial header */ + return(OV_EBADHEADER); + } + + return(_vorbis_unpack_comment(vc,&opb)); + + case 0x05: /* least significant *bit* is read first */ + if(vi->rate==0 || vc->vendor==NULL){ + /* um... we didn;t get the initial header or comments yet */ + return(OV_EBADHEADER); + } + + return(_vorbis_unpack_books(vi,&opb)); + + default: + /* Not a valid vorbis header type */ + return(OV_EBADHEADER); + break; + } + } + } + return(OV_EBADHEADER); +} + +/* pack side **********************************************************/ + +static int _vorbis_pack_info(oggpack_buffer *opb,vorbis_info *vi){ + codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; + if(!ci)return(OV_EFAULT); + + /* preamble */ + oggpack_write(opb,0x01,8); + _v_writestring(opb,"vorbis", 6); + + /* basic information about the stream */ + oggpack_write(opb,0x00,32); + oggpack_write(opb,vi->channels,8); + oggpack_write(opb,vi->rate,32); + + oggpack_write(opb,vi->bitrate_upper,32); + oggpack_write(opb,vi->bitrate_nominal,32); + oggpack_write(opb,vi->bitrate_lower,32); + + oggpack_write(opb,ilog2(ci->blocksizes[0]),4); + oggpack_write(opb,ilog2(ci->blocksizes[1]),4); + oggpack_write(opb,1,1); + + return(0); +} + +static int _vorbis_pack_comment(oggpack_buffer *opb,vorbis_comment *vc){ + char temp[]="Xiph.Org libVorbis I 20050304"; + int bytes = strlen(temp); + + /* preamble */ + oggpack_write(opb,0x03,8); + _v_writestring(opb,"vorbis", 6); + + /* vendor */ + oggpack_write(opb,bytes,32); + _v_writestring(opb,temp, bytes); + + /* comments */ + + oggpack_write(opb,vc->comments,32); + if(vc->comments){ + int i; + for(i=0;icomments;i++){ + if(vc->user_comments[i]){ + oggpack_write(opb,vc->comment_lengths[i],32); + _v_writestring(opb,vc->user_comments[i], vc->comment_lengths[i]); + }else{ + oggpack_write(opb,0,32); + } + } + } + oggpack_write(opb,1,1); + + return(0); +} + +static int _vorbis_pack_books(oggpack_buffer *opb,vorbis_info *vi){ + codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; + int i; + if(!ci)return(OV_EFAULT); + + oggpack_write(opb,0x05,8); + _v_writestring(opb,"vorbis", 6); + + /* books */ + oggpack_write(opb,ci->books-1,8); + for(i=0;ibooks;i++) + if(vorbis_staticbook_pack(ci->book_param[i],opb))goto err_out; + + /* times; hook placeholders */ + oggpack_write(opb,0,6); + oggpack_write(opb,0,16); + + /* floors */ + oggpack_write(opb,ci->floors-1,6); + for(i=0;ifloors;i++){ + oggpack_write(opb,ci->floor_type[i],16); + if(_floor_P[ci->floor_type[i]]->pack) + _floor_P[ci->floor_type[i]]->pack(ci->floor_param[i],opb); + else + goto err_out; + } + + /* residues */ + oggpack_write(opb,ci->residues-1,6); + for(i=0;iresidues;i++){ + oggpack_write(opb,ci->residue_type[i],16); + _residue_P[ci->residue_type[i]]->pack(ci->residue_param[i],opb); + } + + /* maps */ + oggpack_write(opb,ci->maps-1,6); + for(i=0;imaps;i++){ + oggpack_write(opb,ci->map_type[i],16); + _mapping_P[ci->map_type[i]]->pack(vi,ci->map_param[i],opb); + } + + /* modes */ + oggpack_write(opb,ci->modes-1,6); + for(i=0;imodes;i++){ + oggpack_write(opb,ci->mode_param[i]->blockflag,1); + oggpack_write(opb,ci->mode_param[i]->windowtype,16); + oggpack_write(opb,ci->mode_param[i]->transformtype,16); + oggpack_write(opb,ci->mode_param[i]->mapping,8); + } + oggpack_write(opb,1,1); + + return(0); +err_out: + return(-1); +} + +int vorbis_commentheader_out(vorbis_comment *vc, + ogg_packet *op){ + + oggpack_buffer opb; + + oggpack_writeinit(&opb); + if(_vorbis_pack_comment(&opb,vc)) return OV_EIMPL; + + op->packet = (unsigned char*) _ogg_malloc(oggpack_bytes(&opb)); + memcpy(op->packet, opb.buffer, oggpack_bytes(&opb)); + + op->bytes=oggpack_bytes(&opb); + op->b_o_s=0; + op->e_o_s=0; + op->granulepos=0; + op->packetno=1; + + return 0; +} + +int vorbis_analysis_headerout(vorbis_dsp_state *v, + vorbis_comment *vc, + ogg_packet *op, + ogg_packet *op_comm, + ogg_packet *op_code){ + int ret=OV_EIMPL; + vorbis_info *vi=v->vi; + oggpack_buffer opb; + private_state *b=(private_state*)v->backend_state; + + if(!b){ + ret=OV_EFAULT; + goto err_out; + } + + /* first header packet **********************************************/ + + oggpack_writeinit(&opb); + if(_vorbis_pack_info(&opb,vi))goto err_out; + + /* build the packet */ + if(b->header)_ogg_free(b->header); + b->header=(unsigned char*) _ogg_malloc(oggpack_bytes(&opb)); + memcpy(b->header,opb.buffer,oggpack_bytes(&opb)); + op->packet=b->header; + op->bytes=oggpack_bytes(&opb); + op->b_o_s=1; + op->e_o_s=0; + op->granulepos=0; + op->packetno=0; + + /* second header packet (comments) **********************************/ + + oggpack_reset(&opb); + if(_vorbis_pack_comment(&opb,vc))goto err_out; + + if(b->header1)_ogg_free(b->header1); + b->header1=(unsigned char*) _ogg_malloc(oggpack_bytes(&opb)); + memcpy(b->header1,opb.buffer,oggpack_bytes(&opb)); + op_comm->packet=b->header1; + op_comm->bytes=oggpack_bytes(&opb); + op_comm->b_o_s=0; + op_comm->e_o_s=0; + op_comm->granulepos=0; + op_comm->packetno=1; + + /* third header packet (modes/codebooks) ****************************/ + + oggpack_reset(&opb); + if(_vorbis_pack_books(&opb,vi))goto err_out; + + if(b->header2)_ogg_free(b->header2); + b->header2=(unsigned char*) _ogg_malloc(oggpack_bytes(&opb)); + memcpy(b->header2,opb.buffer,oggpack_bytes(&opb)); + op_code->packet=b->header2; + op_code->bytes=oggpack_bytes(&opb); + op_code->b_o_s=0; + op_code->e_o_s=0; + op_code->granulepos=0; + op_code->packetno=2; + + oggpack_writeclear(&opb); + return(0); + err_out: + oggpack_writeclear(&opb); + memset(op,0,sizeof(*op)); + memset(op_comm,0,sizeof(*op_comm)); + memset(op_code,0,sizeof(*op_code)); + + if(b->header)_ogg_free(b->header); + if(b->header1)_ogg_free(b->header1); + if(b->header2)_ogg_free(b->header2); + b->header=NULL; + b->header1=NULL; + b->header2=NULL; + return(ret); +} + +double vorbis_granule_time(vorbis_dsp_state *v,ogg_int64_t granulepos){ + if(granulepos>=0) + return((double)granulepos/v->vi->rate); + return(-1); +} + +#endif +/********* End of inlined file: info.c *********/ + +/********* Start of inlined file: lpc.c *********/ +/* Some of these routines (autocorrelator, LPC coefficient estimator) + are derived from code written by Jutta Degener and Carsten Bormann; + thus we include their copyright below. The entirety of this file + is freely redistributable on the condition that both of these + copyright notices are preserved without modification. */ + +/* Preserved Copyright: *********************************************/ + +/* Copyright 1992, 1993, 1994 by Jutta Degener and Carsten Bormann, +Technische Universita"t Berlin + +Any use of this software is permitted provided that this notice is not +removed and that neither the authors nor the Technische Universita"t +Berlin are deemed to have made any representations as to the +suitability of this software for any purpose nor are held responsible +for any defects of this software. THERE IS ABSOLUTELY NO WARRANTY FOR +THIS SOFTWARE. + +As a matter of courtesy, the authors request to be informed about uses +this software has found, about bugs in this software, and about any +improvements that may be of general interest. + +Berlin, 28.11.1994 +Jutta Degener +Carsten Bormann + +*********************************************************************/ + +/********* Start of inlined file: juce_OggVorbisHeader.h *********/ +// This file is included at the start of each Ogg-Vorbis .c file, just to do a few housekeeping +// tasks.. + +#ifdef _MSC_VER + #pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4305 4189 4706) +#endif +/********* End of inlined file: juce_OggVorbisHeader.h *********/ + +#if JUCE_USE_OGGVORBIS + +#include +#include +#include + +/* Autocorrelation LPC coeff generation algorithm invented by + N. Levinson in 1947, modified by J. Durbin in 1959. */ + +/* Input : n elements of time doamin data + Output: m lpc coefficients, excitation energy */ + +float vorbis_lpc_from_data(float *data,float *lpci,int n,int m){ + double *aut=(double*)alloca(sizeof(*aut)*(m+1)); + double *lpc=(double*)alloca(sizeof(*lpc)*(m)); + double error; + int i,j; + + /* autocorrelation, p+1 lag coefficients */ + j=m+1; + while(j--){ + double d=0; /* double needed for accumulator depth */ + for(i=j;i +#include +#include + +/********* Start of inlined file: lookup.h *********/ +#ifndef _V_LOOKUP_H_ + +#ifdef FLOAT_LOOKUP +extern float vorbis_coslook(float a); +extern float vorbis_invsqlook(float a); +extern float vorbis_invsq2explook(int a); +extern float vorbis_fromdBlook(float a); +#endif +#ifdef INT_LOOKUP +extern long vorbis_invsqlook_i(long a,long e); +extern long vorbis_coslook_i(long a); +extern float vorbis_fromdBlook_i(long a); +#endif + +#endif +/********* End of inlined file: lookup.h *********/ + +/* three possible LSP to f curve functions; the exact computation + (float), a lookup based float implementation, and an integer + implementation. The float lookup is likely the optimal choice on + any machine with an FPU. The integer implementation is *not* fixed + point (due to the need for a large dynamic range and thus a + seperately tracked exponent) and thus much more complex than the + relatively simple float implementations. It's mostly for future + work on a fully fixed point implementation for processors like the + ARM family. */ + +/* undefine both for the 'old' but more precise implementation */ +#define FLOAT_LOOKUP +#undef INT_LOOKUP + +#ifdef FLOAT_LOOKUP + +/********* Start of inlined file: lookup.c *********/ + +/********* Start of inlined file: juce_OggVorbisHeader.h *********/ +// This file is included at the start of each Ogg-Vorbis .c file, just to do a few housekeeping +// tasks.. + +#ifdef _MSC_VER + #pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4305 4189 4706) +#endif +/********* End of inlined file: juce_OggVorbisHeader.h *********/ + +#if JUCE_USE_OGGVORBIS + +#include + +/********* Start of inlined file: lookup.h *********/ +#ifndef _V_LOOKUP_H_ + +#ifdef FLOAT_LOOKUP +extern float vorbis_coslook(float a); +extern float vorbis_invsqlook(float a); +extern float vorbis_invsq2explook(int a); +extern float vorbis_fromdBlook(float a); +#endif +#ifdef INT_LOOKUP +extern long vorbis_invsqlook_i(long a,long e); +extern long vorbis_coslook_i(long a); +extern float vorbis_fromdBlook_i(long a); +#endif + +#endif +/********* End of inlined file: lookup.h *********/ + +/********* Start of inlined file: lookup_data.h *********/ +#ifndef _V_LOOKUP_DATA_H_ + +#ifdef FLOAT_LOOKUP +#define COS_LOOKUP_SZ 128 +static float COS_LOOKUP[COS_LOOKUP_SZ+1]={ + +1.0000000000000f,+0.9996988186962f,+0.9987954562052f,+0.9972904566787f, + +0.9951847266722f,+0.9924795345987f,+0.9891765099648f,+0.9852776423889f, + +0.9807852804032f,+0.9757021300385f,+0.9700312531945f,+0.9637760657954f, + +0.9569403357322f,+0.9495281805930f,+0.9415440651830f,+0.9329927988347f, + +0.9238795325113f,+0.9142097557035f,+0.9039892931234f,+0.8932243011955f, + +0.8819212643484f,+0.8700869911087f,+0.8577286100003f,+0.8448535652497f, + +0.8314696123025f,+0.8175848131516f,+0.8032075314806f,+0.7883464276266f, + +0.7730104533627f,+0.7572088465065f,+0.7409511253550f,+0.7242470829515f, + +0.7071067811865f,+0.6895405447371f,+0.6715589548470f,+0.6531728429538f, + +0.6343932841636f,+0.6152315905806f,+0.5956993044924f,+0.5758081914178f, + +0.5555702330196f,+0.5349976198871f,+0.5141027441932f,+0.4928981922298f, + +0.4713967368260f,+0.4496113296546f,+0.4275550934303f,+0.4052413140050f, + +0.3826834323651f,+0.3598950365350f,+0.3368898533922f,+0.3136817403989f, + +0.2902846772545f,+0.2667127574749f,+0.2429801799033f,+0.2191012401569f, + +0.1950903220161f,+0.1709618887603f,+0.1467304744554f,+0.1224106751992f, + +0.0980171403296f,+0.0735645635997f,+0.0490676743274f,+0.0245412285229f, + +0.0000000000000f,-0.0245412285229f,-0.0490676743274f,-0.0735645635997f, + -0.0980171403296f,-0.1224106751992f,-0.1467304744554f,-0.1709618887603f, + -0.1950903220161f,-0.2191012401569f,-0.2429801799033f,-0.2667127574749f, + -0.2902846772545f,-0.3136817403989f,-0.3368898533922f,-0.3598950365350f, + -0.3826834323651f,-0.4052413140050f,-0.4275550934303f,-0.4496113296546f, + -0.4713967368260f,-0.4928981922298f,-0.5141027441932f,-0.5349976198871f, + -0.5555702330196f,-0.5758081914178f,-0.5956993044924f,-0.6152315905806f, + -0.6343932841636f,-0.6531728429538f,-0.6715589548470f,-0.6895405447371f, + -0.7071067811865f,-0.7242470829515f,-0.7409511253550f,-0.7572088465065f, + -0.7730104533627f,-0.7883464276266f,-0.8032075314806f,-0.8175848131516f, + -0.8314696123025f,-0.8448535652497f,-0.8577286100003f,-0.8700869911087f, + -0.8819212643484f,-0.8932243011955f,-0.9039892931234f,-0.9142097557035f, + -0.9238795325113f,-0.9329927988347f,-0.9415440651830f,-0.9495281805930f, + -0.9569403357322f,-0.9637760657954f,-0.9700312531945f,-0.9757021300385f, + -0.9807852804032f,-0.9852776423889f,-0.9891765099648f,-0.9924795345987f, + -0.9951847266722f,-0.9972904566787f,-0.9987954562052f,-0.9996988186962f, + -1.0000000000000f, +}; + +#define INVSQ_LOOKUP_SZ 32 +static float INVSQ_LOOKUP[INVSQ_LOOKUP_SZ+1]={ + 1.414213562373f,1.392621247646f,1.371988681140f,1.352246807566f, + 1.333333333333f,1.315191898443f,1.297771369046f,1.281025230441f, + 1.264911064067f,1.249390095109f,1.234426799697f,1.219988562661f, + 1.206045378311f,1.192569588000f,1.179535649239f,1.166919931983f, + 1.154700538379f,1.142857142857f,1.131370849898f,1.120224067222f, + 1.109400392450f,1.098884511590f,1.088662107904f,1.078719779941f, + 1.069044967650f,1.059625885652f,1.050451462878f,1.041511287847f, + 1.032795558989f,1.024295039463f,1.016001016002f,1.007905261358f, + 1.000000000000f, +}; + +#define INVSQ2EXP_LOOKUP_MIN (-32) +#define INVSQ2EXP_LOOKUP_MAX 32 +static float INVSQ2EXP_LOOKUP[INVSQ2EXP_LOOKUP_MAX-\ + INVSQ2EXP_LOOKUP_MIN+1]={ + 65536.f, 46340.95001f, 32768.f, 23170.47501f, + 16384.f, 11585.2375f, 8192.f, 5792.618751f, + 4096.f, 2896.309376f, 2048.f, 1448.154688f, + 1024.f, 724.0773439f, 512.f, 362.038672f, + 256.f, 181.019336f, 128.f, 90.50966799f, + 64.f, 45.254834f, 32.f, 22.627417f, + 16.f, 11.3137085f, 8.f, 5.656854249f, + 4.f, 2.828427125f, 2.f, 1.414213562f, + 1.f, 0.7071067812f, 0.5f, 0.3535533906f, + 0.25f, 0.1767766953f, 0.125f, 0.08838834765f, + 0.0625f, 0.04419417382f, 0.03125f, 0.02209708691f, + 0.015625f, 0.01104854346f, 0.0078125f, 0.005524271728f, + 0.00390625f, 0.002762135864f, 0.001953125f, 0.001381067932f, + 0.0009765625f, 0.000690533966f, 0.00048828125f, 0.000345266983f, + 0.000244140625f,0.0001726334915f,0.0001220703125f,8.631674575e-05f, + 6.103515625e-05f,4.315837288e-05f,3.051757812e-05f,2.157918644e-05f, + 1.525878906e-05f, +}; + +#endif + +#define FROMdB_LOOKUP_SZ 35 +#define FROMdB2_LOOKUP_SZ 32 +#define FROMdB_SHIFT 5 +#define FROMdB2_SHIFT 3 +#define FROMdB2_MASK 31 +static float FROMdB_LOOKUP[FROMdB_LOOKUP_SZ]={ + 1.f, 0.6309573445f, 0.3981071706f, 0.2511886432f, + 0.1584893192f, 0.1f, 0.06309573445f, 0.03981071706f, + 0.02511886432f, 0.01584893192f, 0.01f, 0.006309573445f, + 0.003981071706f, 0.002511886432f, 0.001584893192f, 0.001f, + 0.0006309573445f,0.0003981071706f,0.0002511886432f,0.0001584893192f, + 0.0001f,6.309573445e-05f,3.981071706e-05f,2.511886432e-05f, + 1.584893192e-05f, 1e-05f,6.309573445e-06f,3.981071706e-06f, + 2.511886432e-06f,1.584893192e-06f, 1e-06f,6.309573445e-07f, + 3.981071706e-07f,2.511886432e-07f,1.584893192e-07f, +}; + +static float FROMdB2_LOOKUP[FROMdB2_LOOKUP_SZ]={ + 0.9928302478f, 0.9786445908f, 0.9646616199f, 0.9508784391f, + 0.9372921937f, 0.92390007f, 0.9106992942f, 0.8976871324f, + 0.8848608897f, 0.8722179097f, 0.8597555737f, 0.8474713009f, + 0.835362547f, 0.8234268041f, 0.8116616003f, 0.8000644989f, + 0.7886330981f, 0.7773650302f, 0.7662579617f, 0.755309592f, + 0.7445176537f, 0.7338799116f, 0.7233941627f, 0.7130582353f, + 0.7028699885f, 0.6928273125f, 0.6829281272f, 0.6731703824f, + 0.6635520573f, 0.6540711597f, 0.6447257262f, 0.6355138211f, +}; + +#ifdef INT_LOOKUP + +#define INVSQ_LOOKUP_I_SHIFT 10 +#define INVSQ_LOOKUP_I_MASK 1023 +static long INVSQ_LOOKUP_I[64+1]={ + 92682l, 91966l, 91267l, 90583l, + 89915l, 89261l, 88621l, 87995l, + 87381l, 86781l, 86192l, 85616l, + 85051l, 84497l, 83953l, 83420l, + 82897l, 82384l, 81880l, 81385l, + 80899l, 80422l, 79953l, 79492l, + 79039l, 78594l, 78156l, 77726l, + 77302l, 76885l, 76475l, 76072l, + 75674l, 75283l, 74898l, 74519l, + 74146l, 73778l, 73415l, 73058l, + 72706l, 72359l, 72016l, 71679l, + 71347l, 71019l, 70695l, 70376l, + 70061l, 69750l, 69444l, 69141l, + 68842l, 68548l, 68256l, 67969l, + 67685l, 67405l, 67128l, 66855l, + 66585l, 66318l, 66054l, 65794l, + 65536l, +}; + +#define COS_LOOKUP_I_SHIFT 9 +#define COS_LOOKUP_I_MASK 511 +#define COS_LOOKUP_I_SZ 128 +static long COS_LOOKUP_I[COS_LOOKUP_I_SZ+1]={ + 16384l, 16379l, 16364l, 16340l, + 16305l, 16261l, 16207l, 16143l, + 16069l, 15986l, 15893l, 15791l, + 15679l, 15557l, 15426l, 15286l, + 15137l, 14978l, 14811l, 14635l, + 14449l, 14256l, 14053l, 13842l, + 13623l, 13395l, 13160l, 12916l, + 12665l, 12406l, 12140l, 11866l, + 11585l, 11297l, 11003l, 10702l, + 10394l, 10080l, 9760l, 9434l, + 9102l, 8765l, 8423l, 8076l, + 7723l, 7366l, 7005l, 6639l, + 6270l, 5897l, 5520l, 5139l, + 4756l, 4370l, 3981l, 3590l, + 3196l, 2801l, 2404l, 2006l, + 1606l, 1205l, 804l, 402l, + 0l, -401l, -803l, -1204l, + -1605l, -2005l, -2403l, -2800l, + -3195l, -3589l, -3980l, -4369l, + -4755l, -5138l, -5519l, -5896l, + -6269l, -6638l, -7004l, -7365l, + -7722l, -8075l, -8422l, -8764l, + -9101l, -9433l, -9759l, -10079l, + -10393l, -10701l, -11002l, -11296l, + -11584l, -11865l, -12139l, -12405l, + -12664l, -12915l, -13159l, -13394l, + -13622l, -13841l, -14052l, -14255l, + -14448l, -14634l, -14810l, -14977l, + -15136l, -15285l, -15425l, -15556l, + -15678l, -15790l, -15892l, -15985l, + -16068l, -16142l, -16206l, -16260l, + -16304l, -16339l, -16363l, -16378l, + -16383l, +}; + +#endif + +#endif +/********* End of inlined file: lookup_data.h *********/ + +#ifdef FLOAT_LOOKUP + +/* interpolated lookup based cos function, domain 0 to PI only */ +float vorbis_coslook(float a){ + double d=a*(.31830989*(float)COS_LOOKUP_SZ); + int i=vorbis_ftoi(d-.5); + + return COS_LOOKUP[i]+ (d-i)*(COS_LOOKUP[i+1]-COS_LOOKUP[i]); +} + +/* interpolated 1./sqrt(p) where .5 <= p < 1. */ +float vorbis_invsqlook(float a){ + double d=a*(2.f*(float)INVSQ_LOOKUP_SZ)-(float)INVSQ_LOOKUP_SZ; + int i=vorbis_ftoi(d-.5f); + return INVSQ_LOOKUP[i]+ (d-i)*(INVSQ_LOOKUP[i+1]-INVSQ_LOOKUP[i]); +} + +/* interpolated 1./sqrt(p) where .5 <= p < 1. */ +float vorbis_invsq2explook(int a){ + return INVSQ2EXP_LOOKUP[a-INVSQ2EXP_LOOKUP_MIN]; +} + +#include +/* interpolated lookup based fromdB function, domain -140dB to 0dB only */ +float vorbis_fromdBlook(float a){ + int i=vorbis_ftoi(a*((float)(-(1<=(FROMdB_LOOKUP_SZ<>FROMdB_SHIFT]*FROMdB2_LOOKUP[i&FROMdB2_MASK]); +} + +#endif + +#ifdef INT_LOOKUP +/* interpolated 1./sqrt(p) where .5 <= a < 1. (.100000... to .111111...) in + 16.16 format + + returns in m.8 format */ +long vorbis_invsqlook_i(long a,long e){ + long i=(a&0x7fff)>>(INVSQ_LOOKUP_I_SHIFT-1); + long d=(a&INVSQ_LOOKUP_I_MASK)<<(16-INVSQ_LOOKUP_I_SHIFT); /* 0.16 */ + long val=INVSQ_LOOKUP_I[i]- /* 1.16 */ + (((INVSQ_LOOKUP_I[i]-INVSQ_LOOKUP_I[i+1])* /* 0.16 */ + d)>>16); /* result 1.16 */ + + e+=32; + if(e&1)val=(val*5792)>>13; /* multiply val by 1/sqrt(2) */ + e=(e>>1)-8; + + return(val>>e); +} + +/* interpolated lookup based fromdB function, domain -140dB to 0dB only */ +/* a is in n.12 format */ +float vorbis_fromdBlook_i(long a){ + int i=(-a)>>(12-FROMdB2_SHIFT); + return (i<0)?1.f: + ((i>=(FROMdB_LOOKUP_SZ<>FROMdB_SHIFT]*FROMdB2_LOOKUP[i&FROMdB2_MASK]); +} + +/* interpolated lookup based cos function, domain 0 to PI only */ +/* a is in 0.16 format, where 0==0, 2^^16-1==PI, return 0.14 */ +long vorbis_coslook_i(long a){ + int i=a>>COS_LOOKUP_I_SHIFT; + int d=a&COS_LOOKUP_I_MASK; + return COS_LOOKUP_I[i]- ((d*(COS_LOOKUP_I[i]-COS_LOOKUP_I[i+1]))>> + COS_LOOKUP_I_SHIFT); +} + +#endif + +#endif +/********* End of inlined file: lookup.c *********/ + + /* catch this in the build system; we #include for + compilers (like gcc) that can't inline across + modules */ + +/* side effect: changes *lsp to cosines of lsp */ +void vorbis_lsp_to_curve(float *curve,int *map,int n,int ln,float *lsp,int m, + float amp,float ampoffset){ + int i; + float wdel=M_PI/ln; + vorbis_fpu_control fpu; + (void) fpu; // to avoid an unused variable warning + + vorbis_fpu_setround(&fpu); + for(i=0;i>1; + + do{ + q*=ftmp[0]-w; + p*=ftmp[1]-w; + ftmp+=2; + }while(--c); + + if(m&1){ + /* odd order filter; slightly assymetric */ + /* the last coefficient */ + q*=ftmp[0]-w; + q*=q; + p*=p*(1.f-w*w); + }else{ + /* even order filter; still symmetric */ + q*=q*(1.f+w); + p*=p*(1.f-w); + } + + q=frexp(p+q,&qexp); + q=vorbis_fromdBlook(amp* + vorbis_invsqlook(q)* + vorbis_invsq2explook(qexp+m)- + ampoffset); + + do{ + curve[i++]*=q; + }while(map[i]==k); + } + vorbis_fpu_restore(fpu); +} + +#else + +#ifdef INT_LOOKUP + +/********* Start of inlined file: lookup.c *********/ + +/********* Start of inlined file: juce_OggVorbisHeader.h *********/ +// This file is included at the start of each Ogg-Vorbis .c file, just to do a few housekeeping +// tasks.. + +#ifdef _MSC_VER + #pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4305 4189 4706) +#endif +/********* End of inlined file: juce_OggVorbisHeader.h *********/ + +#if JUCE_USE_OGGVORBIS + +#include + +/********* Start of inlined file: lookup.h *********/ +#ifndef _V_LOOKUP_H_ + +#ifdef FLOAT_LOOKUP +extern float vorbis_coslook(float a); +extern float vorbis_invsqlook(float a); +extern float vorbis_invsq2explook(int a); +extern float vorbis_fromdBlook(float a); +#endif +#ifdef INT_LOOKUP +extern long vorbis_invsqlook_i(long a,long e); +extern long vorbis_coslook_i(long a); +extern float vorbis_fromdBlook_i(long a); +#endif + +#endif +/********* End of inlined file: lookup.h *********/ + +/********* Start of inlined file: lookup_data.h *********/ +#ifndef _V_LOOKUP_DATA_H_ + +#ifdef FLOAT_LOOKUP +#define COS_LOOKUP_SZ 128 +static float COS_LOOKUP[COS_LOOKUP_SZ+1]={ + +1.0000000000000f,+0.9996988186962f,+0.9987954562052f,+0.9972904566787f, + +0.9951847266722f,+0.9924795345987f,+0.9891765099648f,+0.9852776423889f, + +0.9807852804032f,+0.9757021300385f,+0.9700312531945f,+0.9637760657954f, + +0.9569403357322f,+0.9495281805930f,+0.9415440651830f,+0.9329927988347f, + +0.9238795325113f,+0.9142097557035f,+0.9039892931234f,+0.8932243011955f, + +0.8819212643484f,+0.8700869911087f,+0.8577286100003f,+0.8448535652497f, + +0.8314696123025f,+0.8175848131516f,+0.8032075314806f,+0.7883464276266f, + +0.7730104533627f,+0.7572088465065f,+0.7409511253550f,+0.7242470829515f, + +0.7071067811865f,+0.6895405447371f,+0.6715589548470f,+0.6531728429538f, + +0.6343932841636f,+0.6152315905806f,+0.5956993044924f,+0.5758081914178f, + +0.5555702330196f,+0.5349976198871f,+0.5141027441932f,+0.4928981922298f, + +0.4713967368260f,+0.4496113296546f,+0.4275550934303f,+0.4052413140050f, + +0.3826834323651f,+0.3598950365350f,+0.3368898533922f,+0.3136817403989f, + +0.2902846772545f,+0.2667127574749f,+0.2429801799033f,+0.2191012401569f, + +0.1950903220161f,+0.1709618887603f,+0.1467304744554f,+0.1224106751992f, + +0.0980171403296f,+0.0735645635997f,+0.0490676743274f,+0.0245412285229f, + +0.0000000000000f,-0.0245412285229f,-0.0490676743274f,-0.0735645635997f, + -0.0980171403296f,-0.1224106751992f,-0.1467304744554f,-0.1709618887603f, + -0.1950903220161f,-0.2191012401569f,-0.2429801799033f,-0.2667127574749f, + -0.2902846772545f,-0.3136817403989f,-0.3368898533922f,-0.3598950365350f, + -0.3826834323651f,-0.4052413140050f,-0.4275550934303f,-0.4496113296546f, + -0.4713967368260f,-0.4928981922298f,-0.5141027441932f,-0.5349976198871f, + -0.5555702330196f,-0.5758081914178f,-0.5956993044924f,-0.6152315905806f, + -0.6343932841636f,-0.6531728429538f,-0.6715589548470f,-0.6895405447371f, + -0.7071067811865f,-0.7242470829515f,-0.7409511253550f,-0.7572088465065f, + -0.7730104533627f,-0.7883464276266f,-0.8032075314806f,-0.8175848131516f, + -0.8314696123025f,-0.8448535652497f,-0.8577286100003f,-0.8700869911087f, + -0.8819212643484f,-0.8932243011955f,-0.9039892931234f,-0.9142097557035f, + -0.9238795325113f,-0.9329927988347f,-0.9415440651830f,-0.9495281805930f, + -0.9569403357322f,-0.9637760657954f,-0.9700312531945f,-0.9757021300385f, + -0.9807852804032f,-0.9852776423889f,-0.9891765099648f,-0.9924795345987f, + -0.9951847266722f,-0.9972904566787f,-0.9987954562052f,-0.9996988186962f, + -1.0000000000000f, +}; + +#define INVSQ_LOOKUP_SZ 32 +static float INVSQ_LOOKUP[INVSQ_LOOKUP_SZ+1]={ + 1.414213562373f,1.392621247646f,1.371988681140f,1.352246807566f, + 1.333333333333f,1.315191898443f,1.297771369046f,1.281025230441f, + 1.264911064067f,1.249390095109f,1.234426799697f,1.219988562661f, + 1.206045378311f,1.192569588000f,1.179535649239f,1.166919931983f, + 1.154700538379f,1.142857142857f,1.131370849898f,1.120224067222f, + 1.109400392450f,1.098884511590f,1.088662107904f,1.078719779941f, + 1.069044967650f,1.059625885652f,1.050451462878f,1.041511287847f, + 1.032795558989f,1.024295039463f,1.016001016002f,1.007905261358f, + 1.000000000000f, +}; + +#define INVSQ2EXP_LOOKUP_MIN (-32) +#define INVSQ2EXP_LOOKUP_MAX 32 +static float INVSQ2EXP_LOOKUP[INVSQ2EXP_LOOKUP_MAX-\ + INVSQ2EXP_LOOKUP_MIN+1]={ + 65536.f, 46340.95001f, 32768.f, 23170.47501f, + 16384.f, 11585.2375f, 8192.f, 5792.618751f, + 4096.f, 2896.309376f, 2048.f, 1448.154688f, + 1024.f, 724.0773439f, 512.f, 362.038672f, + 256.f, 181.019336f, 128.f, 90.50966799f, + 64.f, 45.254834f, 32.f, 22.627417f, + 16.f, 11.3137085f, 8.f, 5.656854249f, + 4.f, 2.828427125f, 2.f, 1.414213562f, + 1.f, 0.7071067812f, 0.5f, 0.3535533906f, + 0.25f, 0.1767766953f, 0.125f, 0.08838834765f, + 0.0625f, 0.04419417382f, 0.03125f, 0.02209708691f, + 0.015625f, 0.01104854346f, 0.0078125f, 0.005524271728f, + 0.00390625f, 0.002762135864f, 0.001953125f, 0.001381067932f, + 0.0009765625f, 0.000690533966f, 0.00048828125f, 0.000345266983f, + 0.000244140625f,0.0001726334915f,0.0001220703125f,8.631674575e-05f, + 6.103515625e-05f,4.315837288e-05f,3.051757812e-05f,2.157918644e-05f, + 1.525878906e-05f, +}; + +#endif + +#define FROMdB_LOOKUP_SZ 35 +#define FROMdB2_LOOKUP_SZ 32 +#define FROMdB_SHIFT 5 +#define FROMdB2_SHIFT 3 +#define FROMdB2_MASK 31 +static float FROMdB_LOOKUP[FROMdB_LOOKUP_SZ]={ + 1.f, 0.6309573445f, 0.3981071706f, 0.2511886432f, + 0.1584893192f, 0.1f, 0.06309573445f, 0.03981071706f, + 0.02511886432f, 0.01584893192f, 0.01f, 0.006309573445f, + 0.003981071706f, 0.002511886432f, 0.001584893192f, 0.001f, + 0.0006309573445f,0.0003981071706f,0.0002511886432f,0.0001584893192f, + 0.0001f,6.309573445e-05f,3.981071706e-05f,2.511886432e-05f, + 1.584893192e-05f, 1e-05f,6.309573445e-06f,3.981071706e-06f, + 2.511886432e-06f,1.584893192e-06f, 1e-06f,6.309573445e-07f, + 3.981071706e-07f,2.511886432e-07f,1.584893192e-07f, +}; + +static float FROMdB2_LOOKUP[FROMdB2_LOOKUP_SZ]={ + 0.9928302478f, 0.9786445908f, 0.9646616199f, 0.9508784391f, + 0.9372921937f, 0.92390007f, 0.9106992942f, 0.8976871324f, + 0.8848608897f, 0.8722179097f, 0.8597555737f, 0.8474713009f, + 0.835362547f, 0.8234268041f, 0.8116616003f, 0.8000644989f, + 0.7886330981f, 0.7773650302f, 0.7662579617f, 0.755309592f, + 0.7445176537f, 0.7338799116f, 0.7233941627f, 0.7130582353f, + 0.7028699885f, 0.6928273125f, 0.6829281272f, 0.6731703824f, + 0.6635520573f, 0.6540711597f, 0.6447257262f, 0.6355138211f, +}; + +#ifdef INT_LOOKUP + +#define INVSQ_LOOKUP_I_SHIFT 10 +#define INVSQ_LOOKUP_I_MASK 1023 +static long INVSQ_LOOKUP_I[64+1]={ + 92682l, 91966l, 91267l, 90583l, + 89915l, 89261l, 88621l, 87995l, + 87381l, 86781l, 86192l, 85616l, + 85051l, 84497l, 83953l, 83420l, + 82897l, 82384l, 81880l, 81385l, + 80899l, 80422l, 79953l, 79492l, + 79039l, 78594l, 78156l, 77726l, + 77302l, 76885l, 76475l, 76072l, + 75674l, 75283l, 74898l, 74519l, + 74146l, 73778l, 73415l, 73058l, + 72706l, 72359l, 72016l, 71679l, + 71347l, 71019l, 70695l, 70376l, + 70061l, 69750l, 69444l, 69141l, + 68842l, 68548l, 68256l, 67969l, + 67685l, 67405l, 67128l, 66855l, + 66585l, 66318l, 66054l, 65794l, + 65536l, +}; + +#define COS_LOOKUP_I_SHIFT 9 +#define COS_LOOKUP_I_MASK 511 +#define COS_LOOKUP_I_SZ 128 +static long COS_LOOKUP_I[COS_LOOKUP_I_SZ+1]={ + 16384l, 16379l, 16364l, 16340l, + 16305l, 16261l, 16207l, 16143l, + 16069l, 15986l, 15893l, 15791l, + 15679l, 15557l, 15426l, 15286l, + 15137l, 14978l, 14811l, 14635l, + 14449l, 14256l, 14053l, 13842l, + 13623l, 13395l, 13160l, 12916l, + 12665l, 12406l, 12140l, 11866l, + 11585l, 11297l, 11003l, 10702l, + 10394l, 10080l, 9760l, 9434l, + 9102l, 8765l, 8423l, 8076l, + 7723l, 7366l, 7005l, 6639l, + 6270l, 5897l, 5520l, 5139l, + 4756l, 4370l, 3981l, 3590l, + 3196l, 2801l, 2404l, 2006l, + 1606l, 1205l, 804l, 402l, + 0l, -401l, -803l, -1204l, + -1605l, -2005l, -2403l, -2800l, + -3195l, -3589l, -3980l, -4369l, + -4755l, -5138l, -5519l, -5896l, + -6269l, -6638l, -7004l, -7365l, + -7722l, -8075l, -8422l, -8764l, + -9101l, -9433l, -9759l, -10079l, + -10393l, -10701l, -11002l, -11296l, + -11584l, -11865l, -12139l, -12405l, + -12664l, -12915l, -13159l, -13394l, + -13622l, -13841l, -14052l, -14255l, + -14448l, -14634l, -14810l, -14977l, + -15136l, -15285l, -15425l, -15556l, + -15678l, -15790l, -15892l, -15985l, + -16068l, -16142l, -16206l, -16260l, + -16304l, -16339l, -16363l, -16378l, + -16383l, +}; + +#endif + +#endif +/********* End of inlined file: lookup_data.h *********/ + +#ifdef FLOAT_LOOKUP + +/* interpolated lookup based cos function, domain 0 to PI only */ +float vorbis_coslook(float a){ + double d=a*(.31830989*(float)COS_LOOKUP_SZ); + int i=vorbis_ftoi(d-.5); + + return COS_LOOKUP[i]+ (d-i)*(COS_LOOKUP[i+1]-COS_LOOKUP[i]); +} + +/* interpolated 1./sqrt(p) where .5 <= p < 1. */ +float vorbis_invsqlook(float a){ + double d=a*(2.f*(float)INVSQ_LOOKUP_SZ)-(float)INVSQ_LOOKUP_SZ; + int i=vorbis_ftoi(d-.5f); + return INVSQ_LOOKUP[i]+ (d-i)*(INVSQ_LOOKUP[i+1]-INVSQ_LOOKUP[i]); +} + +/* interpolated 1./sqrt(p) where .5 <= p < 1. */ +float vorbis_invsq2explook(int a){ + return INVSQ2EXP_LOOKUP[a-INVSQ2EXP_LOOKUP_MIN]; +} + +#include +/* interpolated lookup based fromdB function, domain -140dB to 0dB only */ +float vorbis_fromdBlook(float a){ + int i=vorbis_ftoi(a*((float)(-(1<=(FROMdB_LOOKUP_SZ<>FROMdB_SHIFT]*FROMdB2_LOOKUP[i&FROMdB2_MASK]); +} + +#endif + +#ifdef INT_LOOKUP +/* interpolated 1./sqrt(p) where .5 <= a < 1. (.100000... to .111111...) in + 16.16 format + + returns in m.8 format */ +long vorbis_invsqlook_i(long a,long e){ + long i=(a&0x7fff)>>(INVSQ_LOOKUP_I_SHIFT-1); + long d=(a&INVSQ_LOOKUP_I_MASK)<<(16-INVSQ_LOOKUP_I_SHIFT); /* 0.16 */ + long val=INVSQ_LOOKUP_I[i]- /* 1.16 */ + (((INVSQ_LOOKUP_I[i]-INVSQ_LOOKUP_I[i+1])* /* 0.16 */ + d)>>16); /* result 1.16 */ + + e+=32; + if(e&1)val=(val*5792)>>13; /* multiply val by 1/sqrt(2) */ + e=(e>>1)-8; + + return(val>>e); +} + +/* interpolated lookup based fromdB function, domain -140dB to 0dB only */ +/* a is in n.12 format */ +float vorbis_fromdBlook_i(long a){ + int i=(-a)>>(12-FROMdB2_SHIFT); + return (i<0)?1.f: + ((i>=(FROMdB_LOOKUP_SZ<>FROMdB_SHIFT]*FROMdB2_LOOKUP[i&FROMdB2_MASK]); +} + +/* interpolated lookup based cos function, domain 0 to PI only */ +/* a is in 0.16 format, where 0==0, 2^^16-1==PI, return 0.14 */ +long vorbis_coslook_i(long a){ + int i=a>>COS_LOOKUP_I_SHIFT; + int d=a&COS_LOOKUP_I_MASK; + return COS_LOOKUP_I[i]- ((d*(COS_LOOKUP_I[i]-COS_LOOKUP_I[i+1]))>> + COS_LOOKUP_I_SHIFT); +} + +#endif + +#endif +/********* End of inlined file: lookup.c *********/ + + /* catch this in the build system; we #include for + compilers (like gcc) that can't inline across + modules */ + +static int MLOOP_1[64]={ + 0,10,11,11, 12,12,12,12, 13,13,13,13, 13,13,13,13, + 14,14,14,14, 14,14,14,14, 14,14,14,14, 14,14,14,14, + 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, + 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, +}; + +static int MLOOP_2[64]={ + 0,4,5,5, 6,6,6,6, 7,7,7,7, 7,7,7,7, + 8,8,8,8, 8,8,8,8, 8,8,8,8, 8,8,8,8, + 9,9,9,9, 9,9,9,9, 9,9,9,9, 9,9,9,9, + 9,9,9,9, 9,9,9,9, 9,9,9,9, 9,9,9,9, +}; + +static int MLOOP_3[8]={0,1,2,2,3,3,3,3}; + +/* side effect: changes *lsp to cosines of lsp */ +void vorbis_lsp_to_curve(float *curve,int *map,int n,int ln,float *lsp,int m, + float amp,float ampoffset){ + + /* 0 <= m < 256 */ + + /* set up for using all int later */ + int i; + int ampoffseti=rint(ampoffset*4096.f); + int ampi=rint(amp*16.f); + long *ilsp=alloca(m*sizeof(*ilsp)); + for(i=0;i>25])) + if(!(shift=MLOOP_2[(pi|qi)>>19])) + shift=MLOOP_3[(pi|qi)>>16]; + qi=(qi>>shift)*labs(ilsp[j-1]-wi); + pi=(pi>>shift)*labs(ilsp[j]-wi); + qexp+=shift; + } + if(!(shift=MLOOP_1[(pi|qi)>>25])) + if(!(shift=MLOOP_2[(pi|qi)>>19])) + shift=MLOOP_3[(pi|qi)>>16]; + + /* pi,qi normalized collectively, both tracked using qexp */ + + if(m&1){ + /* odd order filter; slightly assymetric */ + /* the last coefficient */ + qi=(qi>>shift)*labs(ilsp[j-1]-wi); + pi=(pi>>shift)<<14; + qexp+=shift; + + if(!(shift=MLOOP_1[(pi|qi)>>25])) + if(!(shift=MLOOP_2[(pi|qi)>>19])) + shift=MLOOP_3[(pi|qi)>>16]; + + pi>>=shift; + qi>>=shift; + qexp+=shift-14*((m+1)>>1); + + pi=((pi*pi)>>16); + qi=((qi*qi)>>16); + qexp=qexp*2+m; + + pi*=(1<<14)-((wi*wi)>>14); + qi+=pi>>14; + + }else{ + /* even order filter; still symmetric */ + + /* p*=p(1-w), q*=q(1+w), let normalization drift because it isn't + worth tracking step by step */ + + pi>>=shift; + qi>>=shift; + qexp+=shift-7*m; + + pi=((pi*pi)>>16); + qi=((qi*qi)>>16); + qexp=qexp*2+m; + + pi*=(1<<14)-wi; + qi*=(1<<14)+wi; + qi=(qi+pi)>>14; + + } + + /* we've let the normalization drift because it wasn't important; + however, for the lookup, things must be normalized again. We + need at most one right shift or a number of left shifts */ + + if(qi&0xffff0000){ /* checks for 1.xxxxxxxxxxxxxxxx */ + qi>>=1; qexp++; + }else + while(qi && !(qi&0x8000)){ /* checks for 0.0xxxxxxxxxxxxxxx or less*/ + qi<<=1; qexp--; + } + + amp=vorbis_fromdBlook_i(ampi* /* n.4 */ + vorbis_invsqlook_i(qi,qexp)- + /* m.8, m+n<=8 */ + ampoffseti); /* 8.12[0] */ + + curve[i]*=amp; + while(map[++i]==k)curve[i]*=amp; + } +} + +#else + +/* old, nonoptimized but simple version for any poor sap who needs to + figure out what the hell this code does, or wants the other + fraction of a dB precision */ + +/* side effect: changes *lsp to cosines of lsp */ +void vorbis_lsp_to_curve(float *curve,int *map,int n,int ln,float *lsp,int m, + float amp,float ampoffset){ + int i; + float wdel=M_PI/ln; + for(i=0;i= i; j--) { + g[j-2] -= g[j]; + g[j] += g[j]; + } + } +} + +static int comp(const void *a,const void *b){ + return (*(float *)a<*(float *)b)-(*(float *)a>*(float *)b); +} + +/* Newton-Raphson-Maehly actually functioned as a decent root finder, + but there are root sets for which it gets into limit cycles + (exacerbated by zero suppression) and fails. We can't afford to + fail, even if the failure is 1 in 100,000,000, so we now use + Laguerre and later polish with Newton-Raphson (which can then + afford to fail) */ + +#define EPSILON 10e-7 +static int Laguerre_With_Deflation(float *a,int ord,float *r){ + int i,m; + double lastdelta=0.f; + double *defl=(double*)alloca(sizeof(*defl)*(ord+1)); + for(i=0;i<=ord;i++)defl[i]=a[i]; + + for(m=ord;m>0;m--){ + double newx=0.f,delta; + + /* iterate a root */ + while(1){ + double p=defl[m],pp=0.f,ppp=0.f,denom; + + /* eval the polynomial and its first two derivatives */ + for(i=m;i>0;i--){ + ppp = newx*ppp + pp; + pp = newx*pp + p; + p = newx*p + defl[i-1]; + } + + /* Laguerre's method */ + denom=(m-1) * ((m-1)*pp*pp - m*p*ppp); + if(denom<0) + return(-1); /* complex root! The LPC generator handed us a bad filter */ + + if(pp>0){ + denom = pp + sqrt(denom); + if(denom-(EPSILON))denom=-(EPSILON); + } + + delta = m*p/denom; + newx -= delta; + + if(delta<0.f)delta*=-1; + + if(fabs(delta/newx)<10e-12)break; + lastdelta=delta; + } + + r[m-1]=newx; + + /* forward deflation */ + + for(i=m;i>0;i--) + defl[i-1]+=newx*defl[i]; + defl++; + + } + return(0); +} + +/* for spit-and-polish only */ +static int Newton_Raphson(float *a,int ord,float *r){ + int i, k, count=0; + double error=1.f; + double *root=(double*)alloca(ord*sizeof(*root)); + + for(i=0; i1e-20){ + error=0; + + for(i=0; i= 0; k--) { + + pp= pp* rooti + p; + p = p * rooti + a[k]; + } + + delta = p/pp; + root[i] -= delta; + error+= delta*delta; + } + + if(count>40)return(-1); + + count++; + } + + /* Replaced the original bubble sort with a real sort. With your + help, we can eliminate the bubble sort in our lifetime. --Monty */ + + for(i=0; i>1; + int g1_order,g2_order; + float *g1=(float*)alloca(sizeof(*g1)*(order2+1)); + float *g2=(float*)alloca(sizeof(*g2)*(order2+1)); + float *g1r=(float*)alloca(sizeof(*g1r)*(order2+1)); + float *g2r=(float*)alloca(sizeof(*g2r)*(order2+1)); + int i; + + /* even and odd are slightly different base cases */ + g1_order=(m+1)>>1; + g2_order=(m) >>1; + + /* Compute the lengths of the x polynomials. */ + /* Compute the first half of K & R F1 & F2 polynomials. */ + /* Compute half of the symmetric and antisymmetric polynomials. */ + /* Remove the roots at +1 and -1. */ + + g1[g1_order] = 1.f; + for(i=1;i<=g1_order;i++) g1[g1_order-i] = lpc[i-1]+lpc[m-i]; + g2[g2_order] = 1.f; + for(i=1;i<=g2_order;i++) g2[g2_order-i] = lpc[i-1]-lpc[m-i]; + + if(g1_order>g2_order){ + for(i=2; i<=g2_order;i++) g2[g2_order-i] += g2[g2_order-i+2]; + }else{ + for(i=1; i<=g1_order;i++) g1[g1_order-i] -= g1[g1_order-i+1]; + for(i=1; i<=g2_order;i++) g2[g2_order-i] += g2[g2_order-i+1]; + } + + /* Convert into polynomials in cos(alpha) */ + cheby(g1,g1_order); + cheby(g2,g2_order); + + /* Find the roots of the 2 even polynomials.*/ + if(Laguerre_With_Deflation(g1,g1_order,g1r) || + Laguerre_With_Deflation(g2,g2_order,g2r)) + return(-1); + + Newton_Raphson(g1,g1_order,g1r); /* if it fails, it leaves g1r alone */ + Newton_Raphson(g2,g2_order,g2r); /* if it fails, it leaves g2r alone */ + + qsort(g1r,g1_order,sizeof(*g1r),comp); + qsort(g2r,g2_order,sizeof(*g2r),comp); + + for(i=0;i +#include +#include +#include + +/* simplistic, wasteful way of doing this (unique lookup for each + mode/submapping); there should be a central repository for + identical lookups. That will require minor work, so I'm putting it + off as low priority. + + Why a lookup for each backend in a given mode? Because the + blocksize is set by the mode, and low backend lookups may require + parameters from other areas of the mode/mapping */ + +static void mapping0_free_info(vorbis_info_mapping *i){ + vorbis_info_mapping0 *info=(vorbis_info_mapping0 *)i; + if(info){ + memset(info,0,sizeof(*info)); + _ogg_free(info); + } +} + +static void mapping0_pack(vorbis_info *vi,vorbis_info_mapping *vm, + oggpack_buffer *opb){ + int i; + vorbis_info_mapping0 *info=(vorbis_info_mapping0 *)vm; + + /* another 'we meant to do it this way' hack... up to beta 4, we + packed 4 binary zeros here to signify one submapping in use. We + now redefine that to mean four bitflags that indicate use of + deeper features; bit0:submappings, bit1:coupling, + bit2,3:reserved. This is backward compatable with all actual uses + of the beta code. */ + + if(info->submaps>1){ + oggpack_write(opb,1,1); + oggpack_write(opb,info->submaps-1,4); + }else + oggpack_write(opb,0,1); + + if(info->coupling_steps>0){ + oggpack_write(opb,1,1); + oggpack_write(opb,info->coupling_steps-1,8); + + for(i=0;icoupling_steps;i++){ + oggpack_write(opb,info->coupling_mag[i],ilog(vi->channels)); + oggpack_write(opb,info->coupling_ang[i],ilog(vi->channels)); + } + }else + oggpack_write(opb,0,1); + + oggpack_write(opb,0,2); /* 2,3:reserved */ + + /* we don't write the channel submappings if we only have one... */ + if(info->submaps>1){ + for(i=0;ichannels;i++) + oggpack_write(opb,info->chmuxlist[i],4); + } + for(i=0;isubmaps;i++){ + oggpack_write(opb,0,8); /* time submap unused */ + oggpack_write(opb,info->floorsubmap[i],8); + oggpack_write(opb,info->residuesubmap[i],8); + } +} + +/* also responsible for range checking */ +static vorbis_info_mapping *mapping0_unpack(vorbis_info *vi,oggpack_buffer *opb){ + int i; + vorbis_info_mapping0 *info=(vorbis_info_mapping0*)_ogg_calloc(1,sizeof(*info)); + codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; + memset(info,0,sizeof(*info)); + + if(oggpack_read(opb,1)) + info->submaps=oggpack_read(opb,4)+1; + else + info->submaps=1; + + if(oggpack_read(opb,1)){ + info->coupling_steps=oggpack_read(opb,8)+1; + + for(i=0;icoupling_steps;i++){ + int testM=info->coupling_mag[i]=oggpack_read(opb,ilog(vi->channels)); + int testA=info->coupling_ang[i]=oggpack_read(opb,ilog(vi->channels)); + + if(testM<0 || + testA<0 || + testM==testA || + testM>=vi->channels || + testA>=vi->channels) goto err_out; + } + + } + + if(oggpack_read(opb,2)>0)goto err_out; /* 2,3:reserved */ + + if(info->submaps>1){ + for(i=0;ichannels;i++){ + info->chmuxlist[i]=oggpack_read(opb,4); + if(info->chmuxlist[i]>=info->submaps)goto err_out; + } + } + for(i=0;isubmaps;i++){ + oggpack_read(opb,8); /* time submap unused */ + info->floorsubmap[i]=oggpack_read(opb,8); + if(info->floorsubmap[i]>=ci->floors)goto err_out; + info->residuesubmap[i]=oggpack_read(opb,8); + if(info->residuesubmap[i]>=ci->residues)goto err_out; + } + + return info; + + err_out: + mapping0_free_info(info); + return(NULL); +} + +#if 0 +static long seq=0; +static ogg_int64_t total=0; +static float FLOOR1_fromdB_LOOKUP[256]={ + 1.0649863e-07F, 1.1341951e-07F, 1.2079015e-07F, 1.2863978e-07F, + 1.3699951e-07F, 1.4590251e-07F, 1.5538408e-07F, 1.6548181e-07F, + 1.7623575e-07F, 1.8768855e-07F, 1.9988561e-07F, 2.128753e-07F, + 2.2670913e-07F, 2.4144197e-07F, 2.5713223e-07F, 2.7384213e-07F, + 2.9163793e-07F, 3.1059021e-07F, 3.3077411e-07F, 3.5226968e-07F, + 3.7516214e-07F, 3.9954229e-07F, 4.2550680e-07F, 4.5315863e-07F, + 4.8260743e-07F, 5.1396998e-07F, 5.4737065e-07F, 5.8294187e-07F, + 6.2082472e-07F, 6.6116941e-07F, 7.0413592e-07F, 7.4989464e-07F, + 7.9862701e-07F, 8.5052630e-07F, 9.0579828e-07F, 9.6466216e-07F, + 1.0273513e-06F, 1.0941144e-06F, 1.1652161e-06F, 1.2409384e-06F, + 1.3215816e-06F, 1.4074654e-06F, 1.4989305e-06F, 1.5963394e-06F, + 1.7000785e-06F, 1.8105592e-06F, 1.9282195e-06F, 2.0535261e-06F, + 2.1869758e-06F, 2.3290978e-06F, 2.4804557e-06F, 2.6416497e-06F, + 2.8133190e-06F, 2.9961443e-06F, 3.1908506e-06F, 3.3982101e-06F, + 3.6190449e-06F, 3.8542308e-06F, 4.1047004e-06F, 4.3714470e-06F, + 4.6555282e-06F, 4.9580707e-06F, 5.2802740e-06F, 5.6234160e-06F, + 5.9888572e-06F, 6.3780469e-06F, 6.7925283e-06F, 7.2339451e-06F, + 7.7040476e-06F, 8.2047000e-06F, 8.7378876e-06F, 9.3057248e-06F, + 9.9104632e-06F, 1.0554501e-05F, 1.1240392e-05F, 1.1970856e-05F, + 1.2748789e-05F, 1.3577278e-05F, 1.4459606e-05F, 1.5399272e-05F, + 1.6400004e-05F, 1.7465768e-05F, 1.8600792e-05F, 1.9809576e-05F, + 2.1096914e-05F, 2.2467911e-05F, 2.3928002e-05F, 2.5482978e-05F, + 2.7139006e-05F, 2.8902651e-05F, 3.0780908e-05F, 3.2781225e-05F, + 3.4911534e-05F, 3.7180282e-05F, 3.9596466e-05F, 4.2169667e-05F, + 4.4910090e-05F, 4.7828601e-05F, 5.0936773e-05F, 5.4246931e-05F, + 5.7772202e-05F, 6.1526565e-05F, 6.5524908e-05F, 6.9783085e-05F, + 7.4317983e-05F, 7.9147585e-05F, 8.4291040e-05F, 8.9768747e-05F, + 9.5602426e-05F, 0.00010181521F, 0.00010843174F, 0.00011547824F, + 0.00012298267F, 0.00013097477F, 0.00013948625F, 0.00014855085F, + 0.00015820453F, 0.00016848555F, 0.00017943469F, 0.00019109536F, + 0.00020351382F, 0.00021673929F, 0.00023082423F, 0.00024582449F, + 0.00026179955F, 0.00027881276F, 0.00029693158F, 0.00031622787F, + 0.00033677814F, 0.00035866388F, 0.00038197188F, 0.00040679456F, + 0.00043323036F, 0.00046138411F, 0.00049136745F, 0.00052329927F, + 0.00055730621F, 0.00059352311F, 0.00063209358F, 0.00067317058F, + 0.00071691700F, 0.00076350630F, 0.00081312324F, 0.00086596457F, + 0.00092223983F, 0.00098217216F, 0.0010459992F, 0.0011139742F, + 0.0011863665F, 0.0012634633F, 0.0013455702F, 0.0014330129F, + 0.0015261382F, 0.0016253153F, 0.0017309374F, 0.0018434235F, + 0.0019632195F, 0.0020908006F, 0.0022266726F, 0.0023713743F, + 0.0025254795F, 0.0026895994F, 0.0028643847F, 0.0030505286F, + 0.0032487691F, 0.0034598925F, 0.0036847358F, 0.0039241906F, + 0.0041792066F, 0.0044507950F, 0.0047400328F, 0.0050480668F, + 0.0053761186F, 0.0057254891F, 0.0060975636F, 0.0064938176F, + 0.0069158225F, 0.0073652516F, 0.0078438871F, 0.0083536271F, + 0.0088964928F, 0.009474637F, 0.010090352F, 0.010746080F, + 0.011444421F, 0.012188144F, 0.012980198F, 0.013823725F, + 0.014722068F, 0.015678791F, 0.016697687F, 0.017782797F, + 0.018938423F, 0.020169149F, 0.021479854F, 0.022875735F, + 0.024362330F, 0.025945531F, 0.027631618F, 0.029427276F, + 0.031339626F, 0.033376252F, 0.035545228F, 0.037855157F, + 0.040315199F, 0.042935108F, 0.045725273F, 0.048696758F, + 0.051861348F, 0.055231591F, 0.058820850F, 0.062643361F, + 0.066714279F, 0.071049749F, 0.075666962F, 0.080584227F, + 0.085821044F, 0.091398179F, 0.097337747F, 0.10366330F, + 0.11039993F, 0.11757434F, 0.12521498F, 0.13335215F, + 0.14201813F, 0.15124727F, 0.16107617F, 0.17154380F, + 0.18269168F, 0.19456402F, 0.20720788F, 0.22067342F, + 0.23501402F, 0.25028656F, 0.26655159F, 0.28387361F, + 0.30232132F, 0.32196786F, 0.34289114F, 0.36517414F, + 0.38890521F, 0.41417847F, 0.44109412F, 0.46975890F, + 0.50028648F, 0.53279791F, 0.56742212F, 0.60429640F, + 0.64356699F, 0.68538959F, 0.72993007F, 0.77736504F, + 0.82788260F, 0.88168307F, 0.9389798F, 1.F, +}; + +#endif + +extern int *floor1_fit(vorbis_block *vb,void *look, + const float *logmdct, /* in */ + const float *logmask); +extern int *floor1_interpolate_fit(vorbis_block *vb,void *look, + int *A,int *B, + int del); +extern int floor1_encode(oggpack_buffer *opb,vorbis_block *vb, + void*look, + int *post,int *ilogmask); + +static int mapping0_forward(vorbis_block *vb){ + vorbis_dsp_state *vd=vb->vd; + vorbis_info *vi=vd->vi; + codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; + private_state *b=(private_state*)vb->vd->backend_state; + vorbis_block_internal *vbi=(vorbis_block_internal *)vb->internal; + int n=vb->pcmend; + int i,j,k; + + int *nonzero = (int*) alloca(sizeof(*nonzero)*vi->channels); + float **gmdct = (float**) _vorbis_block_alloc(vb,vi->channels*sizeof(*gmdct)); + int **ilogmaskch= (int**) _vorbis_block_alloc(vb,vi->channels*sizeof(*ilogmaskch)); + int ***floor_posts = (int***) _vorbis_block_alloc(vb,vi->channels*sizeof(*floor_posts)); + + float global_ampmax=vbi->ampmax; + float *local_ampmax=(float*)alloca(sizeof(*local_ampmax)*vi->channels); + int blocktype=vbi->blocktype; + + int modenumber=vb->W; + vorbis_info_mapping0 *info=(vorbis_info_mapping0*)ci->map_param[modenumber]; + vorbis_look_psy *psy_look= + b->psy+blocktype+(vb->W?2:0); + + vb->mode=modenumber; + + for(i=0;ichannels;i++){ + float scale=4.f/n; + float scale_dB; + + float *pcm =vb->pcm[i]; + float *logfft =pcm; + + gmdct[i]=(float*)_vorbis_block_alloc(vb,n/2*sizeof(**gmdct)); + + scale_dB=todB(&scale) + .345; /* + .345 is a hack; the original + todB estimation used on IEEE 754 + compliant machines had a bug that + returned dB values about a third + of a decibel too high. The bug + was harmless because tunings + implicitly took that into + account. However, fixing the bug + in the estimator requires + changing all the tunings as well. + For now, it's easier to sync + things back up here, and + recalibrate the tunings in the + next major model upgrade. */ + +#if 0 + if(vi->channels==2) + if(i==0) + _analysis_output("pcmL",seq,pcm,n,0,0,total-n/2); + else + _analysis_output("pcmR",seq,pcm,n,0,0,total-n/2); +#endif + + /* window the PCM data */ + _vorbis_apply_window(pcm,b->window,ci->blocksizes,vb->lW,vb->W,vb->nW); + +#if 0 + if(vi->channels==2) + if(i==0) + _analysis_output("windowedL",seq,pcm,n,0,0,total-n/2); + else + _analysis_output("windowedR",seq,pcm,n,0,0,total-n/2); +#endif + + /* transform the PCM data */ + /* only MDCT right now.... */ + mdct_forward((mdct_lookup*) b->transform[vb->W][0],pcm,gmdct[i]); + + /* FFT yields more accurate tonal estimation (not phase sensitive) */ + drft_forward(&b->fft_look[vb->W],pcm); + logfft[0]=scale_dB+todB(pcm) + .345; /* + .345 is a hack; the + original todB estimation used on + IEEE 754 compliant machines had a + bug that returned dB values about + a third of a decibel too high. + The bug was harmless because + tunings implicitly took that into + account. However, fixing the bug + in the estimator requires + changing all the tunings as well. + For now, it's easier to sync + things back up here, and + recalibrate the tunings in the + next major model upgrade. */ + local_ampmax[i]=logfft[0]; + for(j=1;j>1]=scale_dB+.5f*todB(&temp) + .345; /* + + .345 is a hack; the original todB + estimation used on IEEE 754 + compliant machines had a bug that + returned dB values about a third + of a decibel too high. The bug + was harmless because tunings + implicitly took that into + account. However, fixing the bug + in the estimator requires + changing all the tunings as well. + For now, it's easier to sync + things back up here, and + recalibrate the tunings in the + next major model upgrade. */ + if(temp>local_ampmax[i])local_ampmax[i]=temp; + } + + if(local_ampmax[i]>0.f)local_ampmax[i]=0.f; + if(local_ampmax[i]>global_ampmax)global_ampmax=local_ampmax[i]; + +#if 0 + if(vi->channels==2){ + if(i==0){ + _analysis_output("fftL",seq,logfft,n/2,1,0,0); + }else{ + _analysis_output("fftR",seq,logfft,n/2,1,0,0); + } + } +#endif + + } + + { + float *noise = (float*) _vorbis_block_alloc(vb,n/2*sizeof(*noise)); + float *tone = (float*) _vorbis_block_alloc(vb,n/2*sizeof(*tone)); + + for(i=0;ichannels;i++){ + /* the encoder setup assumes that all the modes used by any + specific bitrate tweaking use the same floor */ + + int submap=info->chmuxlist[i]; + + /* the following makes things clearer to *me* anyway */ + float *mdct =gmdct[i]; + float *logfft =vb->pcm[i]; + + float *logmdct =logfft+n/2; + float *logmask =logfft; + + vb->mode=modenumber; + + floor_posts[i]=(int**) _vorbis_block_alloc(vb,PACKETBLOBS*sizeof(**floor_posts)); + memset(floor_posts[i],0,sizeof(**floor_posts)*PACKETBLOBS); + + for(j=0;jchannels==2){ + if(i==0) + _analysis_output("mdctL",seq,logmdct,n/2,1,0,0); + else + _analysis_output("mdctR",seq,logmdct,n/2,1,0,0); + }else{ + _analysis_output("mdct",seq,logmdct,n/2,1,0,0); + } +#endif + + /* first step; noise masking. Not only does 'noise masking' + give us curves from which we can decide how much resolution + to give noise parts of the spectrum, it also implicitly hands + us a tonality estimate (the larger the value in the + 'noise_depth' vector, the more tonal that area is) */ + + _vp_noisemask(psy_look, + logmdct, + noise); /* noise does not have by-frequency offset + bias applied yet */ +#if 0 + if(vi->channels==2){ + if(i==0) + _analysis_output("noiseL",seq,noise,n/2,1,0,0); + else + _analysis_output("noiseR",seq,noise,n/2,1,0,0); + } +#endif + + /* second step: 'all the other crap'; all the stuff that isn't + computed/fit for bitrate management goes in the second psy + vector. This includes tone masking, peak limiting and ATH */ + + _vp_tonemask(psy_look, + logfft, + tone, + global_ampmax, + local_ampmax[i]); + +#if 0 + if(vi->channels==2){ + if(i==0) + _analysis_output("toneL",seq,tone,n/2,1,0,0); + else + _analysis_output("toneR",seq,tone,n/2,1,0,0); + } +#endif + + /* third step; we offset the noise vectors, overlay tone + masking. We then do a floor1-specific line fit. If we're + performing bitrate management, the line fit is performed + multiple times for up/down tweakage on demand. */ + +#if 0 + { + float aotuv[psy_look->n]; +#endif + + _vp_offset_and_mix(psy_look, + noise, + tone, + 1, + logmask, + mdct, + logmdct); + +#if 0 + if(vi->channels==2){ + if(i==0) + _analysis_output("aotuvM1_L",seq,aotuv,psy_look->n,1,1,0); + else + _analysis_output("aotuvM1_R",seq,aotuv,psy_look->n,1,1,0); + } + } +#endif + +#if 0 + if(vi->channels==2){ + if(i==0) + _analysis_output("mask1L",seq,logmask,n/2,1,0,0); + else + _analysis_output("mask1R",seq,logmask,n/2,1,0,0); + } +#endif + + /* this algorithm is hardwired to floor 1 for now; abort out if + we're *not* floor1. This won't happen unless someone has + broken the encode setup lib. Guard it anyway. */ + if(ci->floor_type[info->floorsubmap[submap]]!=1)return(-1); + + floor_posts[i][PACKETBLOBS/2]= + floor1_fit(vb,b->flr[info->floorsubmap[submap]], + logmdct, + logmask); + + /* are we managing bitrate? If so, perform two more fits for + later rate tweaking (fits represent hi/lo) */ + if(vorbis_bitrate_managed(vb) && floor_posts[i][PACKETBLOBS/2]){ + /* higher rate by way of lower noise curve */ + + _vp_offset_and_mix(psy_look, + noise, + tone, + 2, + logmask, + mdct, + logmdct); + +#if 0 + if(vi->channels==2){ + if(i==0) + _analysis_output("mask2L",seq,logmask,n/2,1,0,0); + else + _analysis_output("mask2R",seq,logmask,n/2,1,0,0); + } +#endif + + floor_posts[i][PACKETBLOBS-1]= + floor1_fit(vb,b->flr[info->floorsubmap[submap]], + logmdct, + logmask); + + /* lower rate by way of higher noise curve */ + _vp_offset_and_mix(psy_look, + noise, + tone, + 0, + logmask, + mdct, + logmdct); + +#if 0 + if(vi->channels==2) + if(i==0) + _analysis_output("mask0L",seq,logmask,n/2,1,0,0); + else + _analysis_output("mask0R",seq,logmask,n/2,1,0,0); +#endif + + floor_posts[i][0]= + floor1_fit(vb,b->flr[info->floorsubmap[submap]], + logmdct, + logmask); + + /* we also interpolate a range of intermediate curves for + intermediate rates */ + for(k=1;kflr[info->floorsubmap[submap]], + floor_posts[i][0], + floor_posts[i][PACKETBLOBS/2], + k*65536/(PACKETBLOBS/2)); + for(k=PACKETBLOBS/2+1;kflr[info->floorsubmap[submap]], + floor_posts[i][PACKETBLOBS/2], + floor_posts[i][PACKETBLOBS-1], + (k-PACKETBLOBS/2)*65536/(PACKETBLOBS/2)); + } + } + } + vbi->ampmax=global_ampmax; + + /* + the next phases are performed once for vbr-only and PACKETBLOB + times for bitrate managed modes. + + 1) encode actual mode being used + 2) encode the floor for each channel, compute coded mask curve/res + 3) normalize and couple. + 4) encode residue + 5) save packet bytes to the packetblob vector + + */ + + /* iterate over the many masking curve fits we've created */ + + { + float **res_bundle=(float**) alloca(sizeof(*res_bundle)*vi->channels); + float **couple_bundle=(float**) alloca(sizeof(*couple_bundle)*vi->channels); + int *zerobundle=(int*) alloca(sizeof(*zerobundle)*vi->channels); + int **sortindex=(int**) alloca(sizeof(*sortindex)*vi->channels); + float **mag_memo; + int **mag_sort; + + if(info->coupling_steps){ + mag_memo=_vp_quantize_couple_memo(vb, + &ci->psy_g_param, + psy_look, + info, + gmdct); + + mag_sort=_vp_quantize_couple_sort(vb, + psy_look, + info, + mag_memo); + + hf_reduction(&ci->psy_g_param, + psy_look, + info, + mag_memo); + } + + memset(sortindex,0,sizeof(*sortindex)*vi->channels); + if(psy_look->vi->normal_channel_p){ + for(i=0;ichannels;i++){ + float *mdct =gmdct[i]; + sortindex[i]=(int*) alloca(sizeof(**sortindex)*n/2); + _vp_noise_normalize_sort(psy_look,mdct,sortindex[i]); + } + } + + for(k=(vorbis_bitrate_managed(vb)?0:PACKETBLOBS/2); + k<=(vorbis_bitrate_managed(vb)?PACKETBLOBS-1:PACKETBLOBS/2); + k++){ + oggpack_buffer *opb=vbi->packetblob[k]; + + /* start out our new packet blob with packet type and mode */ + /* Encode the packet type */ + oggpack_write(opb,0,1); + /* Encode the modenumber */ + /* Encode frame mode, pre,post windowsize, then dispatch */ + oggpack_write(opb,modenumber,b->modebits); + if(vb->W){ + oggpack_write(opb,vb->lW,1); + oggpack_write(opb,vb->nW,1); + } + + /* encode floor, compute masking curve, sep out residue */ + for(i=0;ichannels;i++){ + int submap=info->chmuxlist[i]; + float *mdct =gmdct[i]; + float *res =vb->pcm[i]; + int *ilogmask=ilogmaskch[i]= + (int*) _vorbis_block_alloc(vb,n/2*sizeof(**gmdct)); + + nonzero[i]=floor1_encode(opb,vb,b->flr[info->floorsubmap[submap]], + floor_posts[i][k], + ilogmask); +#if 0 + { + char buf[80]; + sprintf(buf,"maskI%c%d",i?'R':'L',k); + float work[n/2]; + for(j=0;jpsy_g_param.sliding_lowpass[vb->W][k]); + + _vp_noise_normalize(psy_look,res,res+n/2,sortindex[i]); + +#if 0 + { + char buf[80]; + float work[n/2]; + for(j=0;jcoupling_steps){ + _vp_couple(k, + &ci->psy_g_param, + psy_look, + info, + vb->pcm, + mag_memo, + mag_sort, + ilogmaskch, + nonzero, + ci->psy_g_param.sliding_lowpass[vb->W][k]); + } + + /* classify and encode by submap */ + for(i=0;isubmaps;i++){ + int ch_in_bundle=0; + long **classifications; + int resnum=info->residuesubmap[i]; + + for(j=0;jchannels;j++){ + if(info->chmuxlist[j]==i){ + zerobundle[ch_in_bundle]=0; + if(nonzero[j])zerobundle[ch_in_bundle]=1; + res_bundle[ch_in_bundle]=vb->pcm[j]; + couple_bundle[ch_in_bundle++]=vb->pcm[j]+n/2; + } + } + + classifications=_residue_P[ci->residue_type[resnum]]-> + classx(vb,b->residue[resnum],couple_bundle,zerobundle,ch_in_bundle); + + _residue_P[ci->residue_type[resnum]]-> + forward(opb,vb,b->residue[resnum], + couple_bundle,NULL,zerobundle,ch_in_bundle,classifications); + } + + /* ok, done encoding. Next protopacket. */ + } + + } + +#if 0 + seq++; + total+=ci->blocksizes[vb->W]/4+ci->blocksizes[vb->nW]/4; +#endif + return(0); +} + +static int mapping0_inverse(vorbis_block *vb,vorbis_info_mapping *l){ + vorbis_dsp_state *vd=vb->vd; + vorbis_info *vi=vd->vi; + codec_setup_info *ci=(codec_setup_info*) vi->codec_setup; + private_state *b=(private_state*)vd->backend_state; + vorbis_info_mapping0 *info=(vorbis_info_mapping0 *)l; + + int i,j; + long n=vb->pcmend=ci->blocksizes[vb->W]; + + float **pcmbundle=(float**) alloca(sizeof(*pcmbundle)*vi->channels); + int *zerobundle=(int*) alloca(sizeof(*zerobundle)*vi->channels); + + int *nonzero =(int*) alloca(sizeof(*nonzero)*vi->channels); + void **floormemo=(void**) alloca(sizeof(*floormemo)*vi->channels); + + /* recover the spectral envelope; store it in the PCM vector for now */ + for(i=0;ichannels;i++){ + int submap=info->chmuxlist[i]; + floormemo[i]=_floor_P[ci->floor_type[info->floorsubmap[submap]]]-> + inverse1(vb,b->flr[info->floorsubmap[submap]]); + if(floormemo[i]) + nonzero[i]=1; + else + nonzero[i]=0; + memset(vb->pcm[i],0,sizeof(*vb->pcm[i])*n/2); + } + + /* channel coupling can 'dirty' the nonzero listing */ + for(i=0;icoupling_steps;i++){ + if(nonzero[info->coupling_mag[i]] || + nonzero[info->coupling_ang[i]]){ + nonzero[info->coupling_mag[i]]=1; + nonzero[info->coupling_ang[i]]=1; + } + } + + /* recover the residue into our working vectors */ + for(i=0;isubmaps;i++){ + int ch_in_bundle=0; + for(j=0;jchannels;j++){ + if(info->chmuxlist[j]==i){ + if(nonzero[j]) + zerobundle[ch_in_bundle]=1; + else + zerobundle[ch_in_bundle]=0; + pcmbundle[ch_in_bundle++]=vb->pcm[j]; + } + } + + _residue_P[ci->residue_type[info->residuesubmap[i]]]-> + inverse(vb,b->residue[info->residuesubmap[i]], + pcmbundle,zerobundle,ch_in_bundle); + } + + /* channel coupling */ + for(i=info->coupling_steps-1;i>=0;i--){ + float *pcmM=vb->pcm[info->coupling_mag[i]]; + float *pcmA=vb->pcm[info->coupling_ang[i]]; + + for(j=0;j0) + if(ang>0){ + pcmM[j]=mag; + pcmA[j]=mag-ang; + }else{ + pcmA[j]=mag; + pcmM[j]=mag+ang; + } + else + if(ang>0){ + pcmM[j]=mag; + pcmA[j]=mag+ang; + }else{ + pcmA[j]=mag; + pcmM[j]=mag-ang; + } + } + } + + /* compute and apply spectral envelope */ + for(i=0;ichannels;i++){ + float *pcm=vb->pcm[i]; + int submap=info->chmuxlist[i]; + _floor_P[ci->floor_type[info->floorsubmap[submap]]]-> + inverse2(vb,b->flr[info->floorsubmap[submap]], + floormemo[i],pcm); + } + + /* transform the PCM data; takes PCM vector, vb; modifies PCM vector */ + /* only MDCT right now.... */ + for(i=0;ichannels;i++){ + float *pcm=vb->pcm[i]; + mdct_backward((mdct_lookup*) b->transform[vb->W][0],pcm,pcm); + } + + /* all done! */ + return(0); +} + +/* export hooks */ +vorbis_func_mapping mapping0_exportbundle={ + &mapping0_pack, + &mapping0_unpack, + &mapping0_free_info, + &mapping0_forward, + &mapping0_inverse +}; + +#endif +/********* End of inlined file: mapping0.c *********/ + +/********* Start of inlined file: mdct.c *********/ +/* this can also be run as an integer transform by uncommenting a + define in mdct.h; the integerization is a first pass and although + it's likely stable for Vorbis, the dynamic range is constrained and + roundoff isn't done (so it's noisy). Consider it functional, but + only a starting point. There's no point on a machine with an FPU */ + +/********* Start of inlined file: juce_OggVorbisHeader.h *********/ +// This file is included at the start of each Ogg-Vorbis .c file, just to do a few housekeeping +// tasks.. + +#ifdef _MSC_VER + #pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4305 4189 4706) +#endif +/********* End of inlined file: juce_OggVorbisHeader.h *********/ + +#if JUCE_USE_OGGVORBIS + +#include +#include +#include +#include + +/* build lookups for trig functions; also pre-figure scaling and + some window function algebra. */ + +void mdct_init(mdct_lookup *lookup,int n){ + int *bitrev=(int*) _ogg_malloc(sizeof(*bitrev)*(n/4)); + DATA_TYPE *T=(DATA_TYPE*) _ogg_malloc(sizeof(*T)*(n+n/4)); + + int i; + int n2=n>>1; + int log2n=lookup->log2n=rint(log((float)n)/log(2.f)); + lookup->n=n; + lookup->trig=T; + lookup->bitrev=bitrev; + +/* trig lookups... */ + + for(i=0;i>j;j++) + if((msb>>j)&i)acc|=1<scale=FLOAT_CONV(4.f/n); +} + +/* 8 point butterfly (in place, 4 register) */ +STIN void mdct_butterfly_8(DATA_TYPE *x){ + REG_TYPE r0 = x[6] + x[2]; + REG_TYPE r1 = x[6] - x[2]; + REG_TYPE r2 = x[4] + x[0]; + REG_TYPE r3 = x[4] - x[0]; + + x[6] = r0 + r2; + x[4] = r0 - r2; + + r0 = x[5] - x[1]; + r2 = x[7] - x[3]; + x[0] = r1 + r0; + x[2] = r1 - r0; + + r0 = x[5] + x[1]; + r1 = x[7] + x[3]; + x[3] = r2 + r3; + x[1] = r2 - r3; + x[7] = r1 + r0; + x[5] = r1 - r0; + +} + +/* 16 point butterfly (in place, 4 register) */ +STIN void mdct_butterfly_16(DATA_TYPE *x){ + REG_TYPE r0 = x[1] - x[9]; + REG_TYPE r1 = x[0] - x[8]; + + x[8] += x[0]; + x[9] += x[1]; + x[0] = MULT_NORM((r0 + r1) * cPI2_8); + x[1] = MULT_NORM((r0 - r1) * cPI2_8); + + r0 = x[3] - x[11]; + r1 = x[10] - x[2]; + x[10] += x[2]; + x[11] += x[3]; + x[2] = r0; + x[3] = r1; + + r0 = x[12] - x[4]; + r1 = x[13] - x[5]; + x[12] += x[4]; + x[13] += x[5]; + x[4] = MULT_NORM((r0 - r1) * cPI2_8); + x[5] = MULT_NORM((r0 + r1) * cPI2_8); + + r0 = x[14] - x[6]; + r1 = x[15] - x[7]; + x[14] += x[6]; + x[15] += x[7]; + x[6] = r0; + x[7] = r1; + + mdct_butterfly_8(x); + mdct_butterfly_8(x+8); +} + +/* 32 point butterfly (in place, 4 register) */ +STIN void mdct_butterfly_32(DATA_TYPE *x){ + REG_TYPE r0 = x[30] - x[14]; + REG_TYPE r1 = x[31] - x[15]; + + x[30] += x[14]; + x[31] += x[15]; + x[14] = r0; + x[15] = r1; + + r0 = x[28] - x[12]; + r1 = x[29] - x[13]; + x[28] += x[12]; + x[29] += x[13]; + x[12] = MULT_NORM( r0 * cPI1_8 - r1 * cPI3_8 ); + x[13] = MULT_NORM( r0 * cPI3_8 + r1 * cPI1_8 ); + + r0 = x[26] - x[10]; + r1 = x[27] - x[11]; + x[26] += x[10]; + x[27] += x[11]; + x[10] = MULT_NORM(( r0 - r1 ) * cPI2_8); + x[11] = MULT_NORM(( r0 + r1 ) * cPI2_8); + + r0 = x[24] - x[8]; + r1 = x[25] - x[9]; + x[24] += x[8]; + x[25] += x[9]; + x[8] = MULT_NORM( r0 * cPI3_8 - r1 * cPI1_8 ); + x[9] = MULT_NORM( r1 * cPI3_8 + r0 * cPI1_8 ); + + r0 = x[22] - x[6]; + r1 = x[7] - x[23]; + x[22] += x[6]; + x[23] += x[7]; + x[6] = r1; + x[7] = r0; + + r0 = x[4] - x[20]; + r1 = x[5] - x[21]; + x[20] += x[4]; + x[21] += x[5]; + x[4] = MULT_NORM( r1 * cPI1_8 + r0 * cPI3_8 ); + x[5] = MULT_NORM( r1 * cPI3_8 - r0 * cPI1_8 ); + + r0 = x[2] - x[18]; + r1 = x[3] - x[19]; + x[18] += x[2]; + x[19] += x[3]; + x[2] = MULT_NORM(( r1 + r0 ) * cPI2_8); + x[3] = MULT_NORM(( r1 - r0 ) * cPI2_8); + + r0 = x[0] - x[16]; + r1 = x[1] - x[17]; + x[16] += x[0]; + x[17] += x[1]; + x[0] = MULT_NORM( r1 * cPI3_8 + r0 * cPI1_8 ); + x[1] = MULT_NORM( r1 * cPI1_8 - r0 * cPI3_8 ); + + mdct_butterfly_16(x); + mdct_butterfly_16(x+16); + +} + +/* N point first stage butterfly (in place, 2 register) */ +STIN void mdct_butterfly_first(DATA_TYPE *T, + DATA_TYPE *x, + int points){ + + DATA_TYPE *x1 = x + points - 8; + DATA_TYPE *x2 = x + (points>>1) - 8; + REG_TYPE r0; + REG_TYPE r1; + + do{ + + r0 = x1[6] - x2[6]; + r1 = x1[7] - x2[7]; + x1[6] += x2[6]; + x1[7] += x2[7]; + x2[6] = MULT_NORM(r1 * T[1] + r0 * T[0]); + x2[7] = MULT_NORM(r1 * T[0] - r0 * T[1]); + + r0 = x1[4] - x2[4]; + r1 = x1[5] - x2[5]; + x1[4] += x2[4]; + x1[5] += x2[5]; + x2[4] = MULT_NORM(r1 * T[5] + r0 * T[4]); + x2[5] = MULT_NORM(r1 * T[4] - r0 * T[5]); + + r0 = x1[2] - x2[2]; + r1 = x1[3] - x2[3]; + x1[2] += x2[2]; + x1[3] += x2[3]; + x2[2] = MULT_NORM(r1 * T[9] + r0 * T[8]); + x2[3] = MULT_NORM(r1 * T[8] - r0 * T[9]); + + r0 = x1[0] - x2[0]; + r1 = x1[1] - x2[1]; + x1[0] += x2[0]; + x1[1] += x2[1]; + x2[0] = MULT_NORM(r1 * T[13] + r0 * T[12]); + x2[1] = MULT_NORM(r1 * T[12] - r0 * T[13]); + + x1-=8; + x2-=8; + T+=16; + + }while(x2>=x); +} + +/* N/stage point generic N stage butterfly (in place, 2 register) */ +STIN void mdct_butterfly_generic(DATA_TYPE *T, + DATA_TYPE *x, + int points, + int trigint){ + + DATA_TYPE *x1 = x + points - 8; + DATA_TYPE *x2 = x + (points>>1) - 8; + REG_TYPE r0; + REG_TYPE r1; + + do{ + + r0 = x1[6] - x2[6]; + r1 = x1[7] - x2[7]; + x1[6] += x2[6]; + x1[7] += x2[7]; + x2[6] = MULT_NORM(r1 * T[1] + r0 * T[0]); + x2[7] = MULT_NORM(r1 * T[0] - r0 * T[1]); + + T+=trigint; + + r0 = x1[4] - x2[4]; + r1 = x1[5] - x2[5]; + x1[4] += x2[4]; + x1[5] += x2[5]; + x2[4] = MULT_NORM(r1 * T[1] + r0 * T[0]); + x2[5] = MULT_NORM(r1 * T[0] - r0 * T[1]); + + T+=trigint; + + r0 = x1[2] - x2[2]; + r1 = x1[3] - x2[3]; + x1[2] += x2[2]; + x1[3] += x2[3]; + x2[2] = MULT_NORM(r1 * T[1] + r0 * T[0]); + x2[3] = MULT_NORM(r1 * T[0] - r0 * T[1]); + + T+=trigint; + + r0 = x1[0] - x2[0]; + r1 = x1[1] - x2[1]; + x1[0] += x2[0]; + x1[1] += x2[1]; + x2[0] = MULT_NORM(r1 * T[1] + r0 * T[0]); + x2[1] = MULT_NORM(r1 * T[0] - r0 * T[1]); + + T+=trigint; + x1-=8; + x2-=8; + + }while(x2>=x); +} + +STIN void mdct_butterflies(mdct_lookup *init, + DATA_TYPE *x, + int points){ + + DATA_TYPE *T=init->trig; + int stages=init->log2n-5; + int i,j; + + if(--stages>0){ + mdct_butterfly_first(T,x,points); + } + + for(i=1;--stages>0;i++){ + for(j=0;j<(1<>i)*j,points>>i,4<trig)_ogg_free(l->trig); + if(l->bitrev)_ogg_free(l->bitrev); + memset(l,0,sizeof(*l)); + } +} + +STIN void mdct_bitreverse(mdct_lookup *init, + DATA_TYPE *x){ + int n = init->n; + int *bit = init->bitrev; + DATA_TYPE *w0 = x; + DATA_TYPE *w1 = x = w0+(n>>1); + DATA_TYPE *T = init->trig+n; + + do{ + DATA_TYPE *x0 = x+bit[0]; + DATA_TYPE *x1 = x+bit[1]; + + REG_TYPE r0 = x0[1] - x1[1]; + REG_TYPE r1 = x0[0] + x1[0]; + REG_TYPE r2 = MULT_NORM(r1 * T[0] + r0 * T[1]); + REG_TYPE r3 = MULT_NORM(r1 * T[1] - r0 * T[0]); + + w1 -= 4; + + r0 = HALVE(x0[1] + x1[1]); + r1 = HALVE(x0[0] - x1[0]); + + w0[0] = r0 + r2; + w1[2] = r0 - r2; + w0[1] = r1 + r3; + w1[3] = r3 - r1; + + x0 = x+bit[2]; + x1 = x+bit[3]; + + r0 = x0[1] - x1[1]; + r1 = x0[0] + x1[0]; + r2 = MULT_NORM(r1 * T[2] + r0 * T[3]); + r3 = MULT_NORM(r1 * T[3] - r0 * T[2]); + + r0 = HALVE(x0[1] + x1[1]); + r1 = HALVE(x0[0] - x1[0]); + + w0[2] = r0 + r2; + w1[0] = r0 - r2; + w0[3] = r1 + r3; + w1[1] = r3 - r1; + + T += 4; + bit += 4; + w0 += 4; + + }while(w0n; + int n2=n>>1; + int n4=n>>2; + + /* rotate */ + + DATA_TYPE *iX = in+n2-7; + DATA_TYPE *oX = out+n2+n4; + DATA_TYPE *T = init->trig+n4; + + do{ + oX -= 4; + oX[0] = MULT_NORM(-iX[2] * T[3] - iX[0] * T[2]); + oX[1] = MULT_NORM (iX[0] * T[3] - iX[2] * T[2]); + oX[2] = MULT_NORM(-iX[6] * T[1] - iX[4] * T[0]); + oX[3] = MULT_NORM (iX[4] * T[1] - iX[6] * T[0]); + iX -= 8; + T += 4; + }while(iX>=in); + + iX = in+n2-8; + oX = out+n2+n4; + T = init->trig+n4; + + do{ + T -= 4; + oX[0] = MULT_NORM (iX[4] * T[3] + iX[6] * T[2]); + oX[1] = MULT_NORM (iX[4] * T[2] - iX[6] * T[3]); + oX[2] = MULT_NORM (iX[0] * T[1] + iX[2] * T[0]); + oX[3] = MULT_NORM (iX[0] * T[0] - iX[2] * T[1]); + iX -= 8; + oX += 4; + }while(iX>=in); + + mdct_butterflies(init,out+n2,n2); + mdct_bitreverse(init,out); + + /* roatate + window */ + + { + DATA_TYPE *oX1=out+n2+n4; + DATA_TYPE *oX2=out+n2+n4; + DATA_TYPE *iX =out; + T =init->trig+n2; + + do{ + oX1-=4; + + oX1[3] = MULT_NORM (iX[0] * T[1] - iX[1] * T[0]); + oX2[0] = -MULT_NORM (iX[0] * T[0] + iX[1] * T[1]); + + oX1[2] = MULT_NORM (iX[2] * T[3] - iX[3] * T[2]); + oX2[1] = -MULT_NORM (iX[2] * T[2] + iX[3] * T[3]); + + oX1[1] = MULT_NORM (iX[4] * T[5] - iX[5] * T[4]); + oX2[2] = -MULT_NORM (iX[4] * T[4] + iX[5] * T[5]); + + oX1[0] = MULT_NORM (iX[6] * T[7] - iX[7] * T[6]); + oX2[3] = -MULT_NORM (iX[6] * T[6] + iX[7] * T[7]); + + oX2+=4; + iX += 8; + T += 8; + }while(iXoX2); + } +} + +void mdct_forward(mdct_lookup *init, DATA_TYPE *in, DATA_TYPE *out){ + int n=init->n; + int n2=n>>1; + int n4=n>>2; + int n8=n>>3; + DATA_TYPE *w=(DATA_TYPE*) alloca(n*sizeof(*w)); /* forward needs working space */ + DATA_TYPE *w2=w+n2; + + /* rotate */ + + /* window + rotate + step 1 */ + + REG_TYPE r0; + REG_TYPE r1; + DATA_TYPE *x0=in+n2+n4; + DATA_TYPE *x1=x0+1; + DATA_TYPE *T=init->trig+n2; + + int i=0; + + for(i=0;itrig+n2; + x0=out+n2; + + for(i=0;iscale); + x0[0] =MULT_NORM((w[0]*T[1]-w[1]*T[0])*init->scale); + w+=2; + T+=2; + } +} + +#endif +/********* End of inlined file: mdct.c *********/ + +/********* Start of inlined file: psy.c *********/ + +/********* Start of inlined file: juce_OggVorbisHeader.h *********/ +// This file is included at the start of each Ogg-Vorbis .c file, just to do a few housekeeping +// tasks.. + +#ifdef _MSC_VER + #pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4305 4189 4706) +#endif +/********* End of inlined file: juce_OggVorbisHeader.h *********/ + +#if JUCE_USE_OGGVORBIS + +#include +#include +#include + +/********* Start of inlined file: masking.h *********/ +#ifndef _V_MASKING_H_ +#define _V_MASKING_H_ + +/* more detailed ATH; the bass if flat to save stressing the floor + overly for only a bin or two of savings. */ + +#define MAX_ATH 88 +static float ATH[]={ + /*15*/ -51, -52, -53, -54, -55, -56, -57, -58, + /*31*/ -59, -60, -61, -62, -63, -64, -65, -66, + /*63*/ -67, -68, -69, -70, -71, -72, -73, -74, + /*125*/ -75, -76, -77, -78, -80, -81, -82, -83, + /*250*/ -84, -85, -86, -87, -88, -88, -89, -89, + /*500*/ -90, -91, -91, -92, -93, -94, -95, -96, + /*1k*/ -96, -97, -98, -98, -99, -99,-100,-100, + /*2k*/ -101,-102,-103,-104,-106,-107,-107,-107, + /*4k*/ -107,-105,-103,-102,-101, -99, -98, -96, + /*8k*/ -95, -95, -96, -97, -96, -95, -93, -90, + /*16k*/ -80, -70, -50, -40, -30, -30, -30, -30 +}; + +/* The tone masking curves from Ehmer's and Fielder's papers have been + replaced by an empirically collected data set. The previously + published values were, far too often, simply on crack. */ + +#define EHMER_OFFSET 16 +#define EHMER_MAX 56 + +/* masking tones from -50 to 0dB, 62.5 through 16kHz at half octaves + test tones from -2 octaves to +5 octaves sampled at eighth octaves */ +/* (Vorbis 0dB, the loudest possible tone, is assumed to be ~100dB SPL + for collection of these curves) */ + +static float tonemasks[P_BANDS][6][EHMER_MAX]={ + /* 62.5 Hz */ + {{ -60, -60, -60, -60, -60, -60, -60, -60, + -60, -60, -60, -60, -62, -62, -65, -73, + -69, -68, -68, -67, -70, -70, -72, -74, + -75, -79, -79, -80, -83, -88, -93, -100, + -110, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -48, -48, -48, -48, -48, -48, -48, -48, + -48, -48, -48, -48, -48, -53, -61, -66, + -66, -68, -67, -70, -76, -76, -72, -73, + -75, -76, -78, -79, -83, -88, -93, -100, + -110, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -37, -37, -37, -37, -37, -37, -37, -37, + -38, -40, -42, -46, -48, -53, -55, -62, + -65, -58, -56, -56, -61, -60, -65, -67, + -69, -71, -77, -77, -78, -80, -82, -84, + -88, -93, -98, -106, -112, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -25, -25, -25, -25, -25, -25, -25, -25, + -25, -26, -27, -29, -32, -38, -48, -52, + -52, -50, -48, -48, -51, -52, -54, -60, + -67, -67, -66, -68, -69, -73, -73, -76, + -80, -81, -81, -85, -85, -86, -88, -93, + -100, -110, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -16, -16, -16, -16, -16, -16, -16, -16, + -17, -19, -20, -22, -26, -28, -31, -40, + -47, -39, -39, -40, -42, -43, -47, -51, + -57, -52, -55, -55, -60, -58, -62, -63, + -70, -67, -69, -72, -73, -77, -80, -82, + -83, -87, -90, -94, -98, -104, -115, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -8, -8, -8, -8, -8, -8, -8, -8, + -8, -8, -10, -11, -15, -19, -25, -30, + -34, -31, -30, -31, -29, -32, -35, -42, + -48, -42, -44, -46, -50, -50, -51, -52, + -59, -54, -55, -55, -58, -62, -63, -66, + -72, -73, -76, -75, -78, -80, -80, -81, + -84, -88, -90, -94, -98, -101, -106, -110}}, + /* 88Hz */ + {{ -66, -66, -66, -66, -66, -66, -66, -66, + -66, -66, -66, -66, -66, -67, -67, -67, + -76, -72, -71, -74, -76, -76, -75, -78, + -79, -79, -81, -83, -86, -89, -93, -97, + -100, -105, -110, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -47, -47, -47, -47, -47, -47, -47, -47, + -47, -47, -47, -48, -51, -55, -59, -66, + -66, -66, -67, -66, -68, -69, -70, -74, + -79, -77, -77, -78, -80, -81, -82, -84, + -86, -88, -91, -95, -100, -108, -116, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -36, -36, -36, -36, -36, -36, -36, -36, + -36, -37, -37, -41, -44, -48, -51, -58, + -62, -60, -57, -59, -59, -60, -63, -65, + -72, -71, -70, -72, -74, -77, -76, -78, + -81, -81, -80, -83, -86, -91, -96, -100, + -105, -110, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -28, -28, -28, -28, -28, -28, -28, -28, + -28, -30, -32, -32, -33, -35, -41, -49, + -50, -49, -47, -48, -48, -52, -51, -57, + -65, -61, -59, -61, -64, -69, -70, -74, + -77, -77, -78, -81, -84, -85, -87, -90, + -92, -96, -100, -107, -112, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -19, -19, -19, -19, -19, -19, -19, -19, + -20, -21, -23, -27, -30, -35, -36, -41, + -46, -44, -42, -40, -41, -41, -43, -48, + -55, -53, -52, -53, -56, -59, -58, -60, + -67, -66, -69, -71, -72, -75, -79, -81, + -84, -87, -90, -93, -97, -101, -107, -114, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -9, -9, -9, -9, -9, -9, -9, -9, + -11, -12, -12, -15, -16, -20, -23, -30, + -37, -34, -33, -34, -31, -32, -32, -38, + -47, -44, -41, -40, -47, -49, -46, -46, + -58, -50, -50, -54, -58, -62, -64, -67, + -67, -70, -72, -76, -79, -83, -87, -91, + -96, -100, -104, -110, -999, -999, -999, -999}}, + /* 125 Hz */ + {{ -62, -62, -62, -62, -62, -62, -62, -62, + -62, -62, -63, -64, -66, -67, -66, -68, + -75, -72, -76, -75, -76, -78, -79, -82, + -84, -85, -90, -94, -101, -110, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -59, -59, -59, -59, -59, -59, -59, -59, + -59, -59, -59, -60, -60, -61, -63, -66, + -71, -68, -70, -70, -71, -72, -72, -75, + -81, -78, -79, -82, -83, -86, -90, -97, + -103, -113, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -53, -53, -53, -53, -53, -53, -53, -53, + -53, -54, -55, -57, -56, -57, -55, -61, + -65, -60, -60, -62, -63, -63, -66, -68, + -74, -73, -75, -75, -78, -80, -80, -82, + -85, -90, -96, -101, -108, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -46, -46, -46, -46, -46, -46, -46, -46, + -46, -46, -47, -47, -47, -47, -48, -51, + -57, -51, -49, -50, -51, -53, -54, -59, + -66, -60, -62, -67, -67, -70, -72, -75, + -76, -78, -81, -85, -88, -94, -97, -104, + -112, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -36, -36, -36, -36, -36, -36, -36, -36, + -39, -41, -42, -42, -39, -38, -41, -43, + -52, -44, -40, -39, -37, -37, -40, -47, + -54, -50, -48, -50, -55, -61, -59, -62, + -66, -66, -66, -69, -69, -73, -74, -74, + -75, -77, -79, -82, -87, -91, -95, -100, + -108, -115, -999, -999, -999, -999, -999, -999}, + { -28, -26, -24, -22, -20, -20, -23, -29, + -30, -31, -28, -27, -28, -28, -28, -35, + -40, -33, -32, -29, -30, -30, -30, -37, + -45, -41, -37, -38, -45, -47, -47, -48, + -53, -49, -48, -50, -49, -49, -51, -52, + -58, -56, -57, -56, -60, -61, -62, -70, + -72, -74, -78, -83, -88, -93, -100, -106}}, + /* 177 Hz */ + {{-999, -999, -999, -999, -999, -999, -999, -999, + -999, -110, -105, -100, -95, -91, -87, -83, + -80, -78, -76, -78, -78, -81, -83, -85, + -86, -85, -86, -87, -90, -97, -107, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -110, -105, -100, -95, -90, + -85, -81, -77, -73, -70, -67, -67, -68, + -75, -73, -70, -69, -70, -72, -75, -79, + -84, -83, -84, -86, -88, -89, -89, -93, + -98, -105, -112, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-105, -100, -95, -90, -85, -80, -76, -71, + -68, -68, -65, -63, -63, -62, -62, -64, + -65, -64, -61, -62, -63, -64, -66, -68, + -73, -73, -74, -75, -76, -81, -83, -85, + -88, -89, -92, -95, -100, -108, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -80, -75, -71, -68, -65, -63, -62, -61, + -61, -61, -61, -59, -56, -57, -53, -50, + -58, -52, -50, -50, -52, -53, -54, -58, + -67, -63, -67, -68, -72, -75, -78, -80, + -81, -81, -82, -85, -89, -90, -93, -97, + -101, -107, -114, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -65, -61, -59, -57, -56, -55, -55, -56, + -56, -57, -55, -53, -52, -47, -44, -44, + -50, -44, -41, -39, -39, -42, -40, -46, + -51, -49, -50, -53, -54, -63, -60, -61, + -62, -66, -66, -66, -70, -73, -74, -75, + -76, -75, -79, -85, -89, -91, -96, -102, + -110, -999, -999, -999, -999, -999, -999, -999}, + { -52, -50, -49, -49, -48, -48, -48, -49, + -50, -50, -49, -46, -43, -39, -35, -33, + -38, -36, -32, -29, -32, -32, -32, -35, + -44, -39, -38, -38, -46, -50, -45, -46, + -53, -50, -50, -50, -54, -54, -53, -53, + -56, -57, -59, -66, -70, -72, -74, -79, + -83, -85, -90, -97, -114, -999, -999, -999}}, + /* 250 Hz */ + {{-999, -999, -999, -999, -999, -999, -110, -105, + -100, -95, -90, -86, -80, -75, -75, -79, + -80, -79, -80, -81, -82, -88, -95, -103, + -110, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -108, -103, -98, -93, + -88, -83, -79, -78, -75, -71, -67, -68, + -73, -73, -72, -73, -75, -77, -80, -82, + -88, -93, -100, -107, -114, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -110, -105, -101, -96, -90, + -86, -81, -77, -73, -69, -66, -61, -62, + -66, -64, -62, -65, -66, -70, -72, -76, + -81, -80, -84, -90, -95, -102, -110, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -107, -103, -97, -92, -88, + -83, -79, -74, -70, -66, -59, -53, -58, + -62, -55, -54, -54, -54, -58, -61, -62, + -72, -70, -72, -75, -78, -80, -81, -80, + -83, -83, -88, -93, -100, -107, -115, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -105, -100, -95, -90, -85, + -80, -75, -70, -66, -62, -56, -48, -44, + -48, -46, -46, -43, -46, -48, -48, -51, + -58, -58, -59, -60, -62, -62, -61, -61, + -65, -64, -65, -68, -70, -74, -75, -78, + -81, -86, -95, -110, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -105, -100, -95, -90, -85, -80, + -75, -70, -65, -61, -55, -49, -39, -33, + -40, -35, -32, -38, -40, -33, -35, -37, + -46, -41, -45, -44, -46, -42, -45, -46, + -52, -50, -50, -50, -54, -54, -55, -57, + -62, -64, -66, -68, -70, -76, -81, -90, + -100, -110, -999, -999, -999, -999, -999, -999}}, + /* 354 hz */ + {{-999, -999, -999, -999, -999, -999, -999, -999, + -105, -98, -90, -85, -82, -83, -80, -78, + -84, -79, -80, -83, -87, -89, -91, -93, + -99, -106, -117, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -105, -98, -90, -85, -80, -75, -70, -68, + -74, -72, -74, -77, -80, -82, -85, -87, + -92, -89, -91, -95, -100, -106, -112, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -105, -98, -90, -83, -75, -71, -63, -64, + -67, -62, -64, -67, -70, -73, -77, -81, + -84, -83, -85, -89, -90, -93, -98, -104, + -109, -114, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -103, -96, -88, -81, -75, -68, -58, -54, + -56, -54, -56, -56, -58, -60, -63, -66, + -74, -69, -72, -72, -75, -74, -77, -81, + -81, -82, -84, -87, -93, -96, -99, -104, + -110, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -108, -102, -96, + -91, -85, -80, -74, -68, -60, -51, -46, + -48, -46, -43, -45, -47, -47, -49, -48, + -56, -53, -55, -58, -57, -63, -58, -60, + -66, -64, -67, -70, -70, -74, -77, -84, + -86, -89, -91, -93, -94, -101, -109, -118, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -108, -103, -98, -93, -88, + -83, -78, -73, -68, -60, -53, -44, -35, + -38, -38, -34, -34, -36, -40, -41, -44, + -51, -45, -46, -47, -46, -54, -50, -49, + -50, -50, -50, -51, -54, -57, -58, -60, + -66, -66, -66, -64, -65, -68, -77, -82, + -87, -95, -110, -999, -999, -999, -999, -999}}, + /* 500 Hz */ + {{-999, -999, -999, -999, -999, -999, -999, -999, + -107, -102, -97, -92, -87, -83, -78, -75, + -82, -79, -83, -85, -89, -92, -95, -98, + -101, -105, -109, -113, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -106, + -100, -95, -90, -86, -81, -78, -74, -69, + -74, -74, -76, -79, -83, -84, -86, -89, + -92, -97, -93, -100, -103, -107, -110, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -106, -100, + -95, -90, -87, -83, -80, -75, -69, -60, + -66, -66, -68, -70, -74, -78, -79, -81, + -81, -83, -84, -87, -93, -96, -99, -103, + -107, -110, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -108, -103, -98, + -93, -89, -85, -82, -78, -71, -62, -55, + -58, -58, -54, -54, -55, -59, -61, -62, + -70, -66, -66, -67, -70, -72, -75, -78, + -84, -84, -84, -88, -91, -90, -95, -98, + -102, -103, -106, -110, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -108, -103, -98, -94, + -90, -87, -82, -79, -73, -67, -58, -47, + -50, -45, -41, -45, -48, -44, -44, -49, + -54, -51, -48, -47, -49, -50, -51, -57, + -58, -60, -63, -69, -70, -69, -71, -74, + -78, -82, -90, -95, -101, -105, -110, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -105, -101, -97, -93, -90, + -85, -80, -77, -72, -65, -56, -48, -37, + -40, -36, -34, -40, -50, -47, -38, -41, + -47, -38, -35, -39, -38, -43, -40, -45, + -50, -45, -44, -47, -50, -55, -48, -48, + -52, -66, -70, -76, -82, -90, -97, -105, + -110, -999, -999, -999, -999, -999, -999, -999}}, + /* 707 Hz */ + {{-999, -999, -999, -999, -999, -999, -999, -999, + -999, -108, -103, -98, -93, -86, -79, -76, + -83, -81, -85, -87, -89, -93, -98, -102, + -107, -112, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -108, -103, -98, -93, -86, -79, -71, + -77, -74, -77, -79, -81, -84, -85, -90, + -92, -93, -92, -98, -101, -108, -112, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -108, -103, -98, -93, -87, -78, -68, -65, + -66, -62, -65, -67, -70, -73, -75, -78, + -82, -82, -83, -84, -91, -93, -98, -102, + -106, -110, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -105, -100, -95, -90, -82, -74, -62, -57, + -58, -56, -51, -52, -52, -54, -54, -58, + -66, -59, -60, -63, -66, -69, -73, -79, + -83, -84, -80, -81, -81, -82, -88, -92, + -98, -105, -113, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -107, + -102, -97, -92, -84, -79, -69, -57, -47, + -52, -47, -44, -45, -50, -52, -42, -42, + -53, -43, -43, -48, -51, -56, -55, -52, + -57, -59, -61, -62, -67, -71, -78, -83, + -86, -94, -98, -103, -110, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -105, -100, + -95, -90, -84, -78, -70, -61, -51, -41, + -40, -38, -40, -46, -52, -51, -41, -40, + -46, -40, -38, -38, -41, -46, -41, -46, + -47, -43, -43, -45, -41, -45, -56, -67, + -68, -83, -87, -90, -95, -102, -107, -113, + -999, -999, -999, -999, -999, -999, -999, -999}}, + /* 1000 Hz */ + {{-999, -999, -999, -999, -999, -999, -999, -999, + -999, -109, -105, -101, -96, -91, -84, -77, + -82, -82, -85, -89, -94, -100, -106, -110, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -106, -103, -98, -92, -85, -80, -71, + -75, -72, -76, -80, -84, -86, -89, -93, + -100, -107, -113, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -107, + -104, -101, -97, -92, -88, -84, -80, -64, + -66, -63, -64, -66, -69, -73, -77, -83, + -83, -86, -91, -98, -104, -111, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -107, + -104, -101, -97, -92, -90, -84, -74, -57, + -58, -52, -55, -54, -50, -52, -50, -52, + -63, -62, -69, -76, -77, -78, -78, -79, + -82, -88, -94, -100, -106, -111, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -106, -102, + -98, -95, -90, -85, -83, -78, -70, -50, + -50, -41, -44, -49, -47, -50, -50, -44, + -55, -46, -47, -48, -48, -54, -49, -49, + -58, -62, -71, -81, -87, -92, -97, -102, + -108, -114, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -106, -102, + -98, -95, -90, -85, -83, -78, -70, -45, + -43, -41, -47, -50, -51, -50, -49, -45, + -47, -41, -44, -41, -39, -43, -38, -37, + -40, -41, -44, -50, -58, -65, -73, -79, + -85, -92, -97, -101, -105, -109, -113, -999, + -999, -999, -999, -999, -999, -999, -999, -999}}, + /* 1414 Hz */ + {{-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -107, -100, -95, -87, -81, + -85, -83, -88, -93, -100, -107, -114, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -107, -101, -95, -88, -83, -76, + -73, -72, -79, -84, -90, -95, -100, -105, + -110, -115, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -104, -98, -92, -87, -81, -70, + -65, -62, -67, -71, -74, -80, -85, -91, + -95, -99, -103, -108, -111, -114, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -103, -97, -90, -85, -76, -60, + -56, -54, -60, -62, -61, -56, -63, -65, + -73, -74, -77, -75, -78, -81, -86, -87, + -88, -91, -94, -98, -103, -110, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -105, + -100, -97, -92, -86, -81, -79, -70, -57, + -51, -47, -51, -58, -60, -56, -53, -50, + -58, -52, -50, -50, -53, -55, -64, -69, + -71, -85, -82, -78, -81, -85, -95, -102, + -112, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -105, + -100, -97, -92, -85, -83, -79, -72, -49, + -40, -43, -43, -54, -56, -51, -50, -40, + -43, -38, -36, -35, -37, -38, -37, -44, + -54, -60, -57, -60, -70, -75, -84, -92, + -103, -112, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}}, + /* 2000 Hz */ + {{-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -110, -102, -95, -89, -82, + -83, -84, -90, -92, -99, -107, -113, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -107, -101, -95, -89, -83, -72, + -74, -78, -85, -88, -88, -90, -92, -98, + -105, -111, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -109, -103, -97, -93, -87, -81, -70, + -70, -67, -75, -73, -76, -79, -81, -83, + -88, -89, -97, -103, -110, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -107, -100, -94, -88, -83, -75, -63, + -59, -59, -63, -66, -60, -62, -67, -67, + -77, -76, -81, -88, -86, -92, -96, -102, + -109, -116, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -105, -98, -92, -86, -81, -73, -56, + -52, -47, -55, -60, -58, -52, -51, -45, + -49, -50, -53, -54, -61, -71, -70, -69, + -78, -79, -87, -90, -96, -104, -112, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -103, -96, -90, -86, -78, -70, -51, + -42, -47, -48, -55, -54, -54, -53, -42, + -35, -28, -33, -38, -37, -44, -47, -49, + -54, -63, -68, -78, -82, -89, -94, -99, + -104, -109, -114, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}}, + /* 2828 Hz */ + {{-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -110, -100, -90, -79, + -85, -81, -82, -82, -89, -94, -99, -103, + -109, -115, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -105, -97, -85, -72, + -74, -70, -70, -70, -76, -85, -91, -93, + -97, -103, -109, -115, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -112, -93, -81, -68, + -62, -60, -60, -57, -63, -70, -77, -82, + -90, -93, -98, -104, -109, -113, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -113, -100, -93, -84, -63, + -58, -48, -53, -54, -52, -52, -57, -64, + -66, -76, -83, -81, -85, -85, -90, -95, + -98, -101, -103, -106, -108, -111, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -105, -95, -86, -74, -53, + -50, -38, -43, -49, -43, -42, -39, -39, + -46, -52, -57, -56, -72, -69, -74, -81, + -87, -92, -94, -97, -99, -102, -105, -108, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -108, -99, -90, -76, -66, -45, + -43, -41, -44, -47, -43, -47, -40, -30, + -31, -31, -39, -33, -40, -41, -43, -53, + -59, -70, -73, -77, -79, -82, -84, -87, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}}, + /* 4000 Hz */ + {{-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -110, -91, -76, + -75, -85, -93, -98, -104, -110, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -110, -91, -70, + -70, -75, -86, -89, -94, -98, -101, -106, + -110, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -110, -95, -80, -60, + -65, -64, -74, -83, -88, -91, -95, -99, + -103, -107, -110, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -110, -95, -80, -58, + -55, -49, -66, -68, -71, -78, -78, -80, + -88, -85, -89, -97, -100, -105, -110, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -110, -95, -80, -53, + -52, -41, -59, -59, -49, -58, -56, -63, + -86, -79, -90, -93, -98, -103, -107, -112, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -110, -97, -91, -73, -45, + -40, -33, -53, -61, -49, -54, -50, -50, + -60, -52, -67, -74, -81, -92, -96, -100, + -105, -110, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}}, + /* 5657 Hz */ + {{-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -113, -106, -99, -92, -77, + -80, -88, -97, -106, -115, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -116, -109, -102, -95, -89, -74, + -72, -88, -87, -95, -102, -109, -116, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -116, -109, -102, -95, -89, -75, + -66, -74, -77, -78, -86, -87, -90, -96, + -105, -115, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -115, -108, -101, -94, -88, -66, + -56, -61, -70, -65, -78, -72, -83, -84, + -93, -98, -105, -110, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -110, -105, -95, -89, -82, -57, + -52, -52, -59, -56, -59, -58, -69, -67, + -88, -82, -82, -89, -94, -100, -108, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -110, -101, -96, -90, -83, -77, -54, + -43, -38, -50, -48, -52, -48, -42, -42, + -51, -52, -53, -59, -65, -71, -78, -85, + -95, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}}, + /* 8000 Hz */ + {{-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -120, -105, -86, -68, + -78, -79, -90, -100, -110, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -120, -105, -86, -66, + -73, -77, -88, -96, -105, -115, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -120, -105, -92, -80, -61, + -64, -68, -80, -87, -92, -100, -110, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -120, -104, -91, -79, -52, + -60, -54, -64, -69, -77, -80, -82, -84, + -85, -87, -88, -90, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -118, -100, -87, -77, -49, + -50, -44, -58, -61, -61, -67, -65, -62, + -62, -62, -65, -68, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -115, -98, -84, -62, -49, + -44, -38, -46, -49, -49, -46, -39, -37, + -39, -40, -42, -43, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}}, + /* 11314 Hz */ + {{-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -110, -88, -74, + -77, -82, -82, -85, -90, -94, -99, -104, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -110, -88, -66, + -70, -81, -80, -81, -84, -88, -91, -93, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -110, -88, -61, + -63, -70, -71, -74, -77, -80, -83, -85, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -110, -86, -62, + -63, -62, -62, -58, -52, -50, -50, -52, + -54, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -118, -108, -84, -53, + -50, -50, -50, -55, -47, -45, -40, -40, + -40, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -118, -100, -73, -43, + -37, -42, -43, -53, -38, -37, -35, -35, + -38, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}}, + /* 16000 Hz */ + {{-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -110, -100, -91, -84, -74, + -80, -80, -80, -80, -80, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -110, -100, -91, -84, -74, + -68, -68, -68, -68, -68, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -110, -100, -86, -78, -70, + -60, -45, -30, -21, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -110, -100, -87, -78, -67, + -48, -38, -29, -21, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -110, -100, -86, -69, -56, + -45, -35, -33, -29, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -110, -100, -83, -71, -48, + -27, -38, -37, -34, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}} +}; + +#endif +/********* End of inlined file: masking.h *********/ + +#define NEGINF -9999.f +static double stereo_threshholds[]={0.0, .5, 1.0, 1.5, 2.5, 4.5, 8.5, 16.5, 9e10}; +static double stereo_threshholds_limited[]={0.0, .5, 1.0, 1.5, 2.0, 2.5, 4.5, 8.5, 9e10}; + +vorbis_look_psy_global *_vp_global_look(vorbis_info *vi){ + codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; + vorbis_info_psy_global *gi=&ci->psy_g_param; + vorbis_look_psy_global *look=(vorbis_look_psy_global*)_ogg_calloc(1,sizeof(*look)); + + look->channels=vi->channels; + + look->ampmax=-9999.; + look->gi=gi; + return(look); +} + +void _vp_global_free(vorbis_look_psy_global *look){ + if(look){ + memset(look,0,sizeof(*look)); + _ogg_free(look); + } +} + +void _vi_gpsy_free(vorbis_info_psy_global *i){ + if(i){ + memset(i,0,sizeof(*i)); + _ogg_free(i); + } +} + +void _vi_psy_free(vorbis_info_psy *i){ + if(i){ + memset(i,0,sizeof(*i)); + _ogg_free(i); + } +} + +static void min_curve(float *c, + float *c2){ + int i; + for(i=0;ic[i])c[i]=c2[i]; +} + +static void attenuate_curve(float *c,float att){ + int i; + for(i=0;iATH[j+k+ath_offset])min=ATH[j+k+ath_offset]; + }else{ + if(min>ATH[MAX_ATH-1])min=ATH[MAX_ATH-1]; + } + ath[j]=min; + } + + /* copy curves into working space, replicate the 50dB curve to 30 + and 40, replicate the 100dB curve to 110 */ + for(j=0;j<6;j++) + memcpy(workc[i][j+2],tonemasks[i][j],EHMER_MAX*sizeof(*tonemasks[i][j])); + memcpy(workc[i][0],tonemasks[i][0],EHMER_MAX*sizeof(*tonemasks[i][0])); + memcpy(workc[i][1],tonemasks[i][0],EHMER_MAX*sizeof(*tonemasks[i][0])); + + /* apply centered curve boost/decay */ + for(j=0;j0)adj=0.; + if(adj>0. && center_boost<0)adj=0.; + workc[i][j][k]+=adj; + } + } + + /* normalize curves so the driving amplitude is 0dB */ + /* make temp curves with the ATH overlayed */ + for(j=0;j an eighth of an octave and that the eighth + octave values may also be composited. */ + + /* which octave curves will we be compositing? */ + bin=floor(fromOC(i*.5)/binHz); + lo_curve= ceil(toOC(bin*binHz+1)*2); + hi_curve= floor(toOC((bin+1)*binHz)*2); + if(lo_curve>i)lo_curve=i; + if(lo_curve<0)lo_curve=0; + if(hi_curve>=P_BANDS)hi_curve=P_BANDS-1; + + for(m=0;mn)lo_bin=n; + if(lo_binn)hi_bin=n; + + for(;lworkc[k][m][j]) + brute_buffer[l]=workc[k][m][j]; + } + + for(;lworkc[k][m][EHMER_MAX-1]) + brute_buffer[l]=workc[k][m][EHMER_MAX-1]; + + } + + /* be equally paranoid about being valid up to next half ocatve */ + if(i+1n)lo_bin=n; + if(lo_binn)hi_bin=n; + + for(;lworkc[k][m][j]) + brute_buffer[l]=workc[k][m][j]; + } + + for(;lworkc[k][m][EHMER_MAX-1]) + brute_buffer[l]=workc[k][m][EHMER_MAX-1]; + + } + + for(j=0;j=n){ + ret[i][m][j+2]=-999.; + }else{ + ret[i][m][j+2]=brute_buffer[bin]; + } + } + } + + /* add fenceposts */ + for(j=0;j-200.f)break; + ret[i][m][0]=j; + + for(j=EHMER_MAX-1;j>EHMER_OFFSET+1;j--) + if(ret[i][m][j+2]>-200.f) + break; + ret[i][m][1]=j; + + } + } + + return(ret); +} + +void _vp_psy_init(vorbis_look_psy *p,vorbis_info_psy *vi, + vorbis_info_psy_global *gi,int n,long rate){ + long i,j,lo=-99,hi=1; + long maxoc; + memset(p,0,sizeof(*p)); + + p->eighth_octave_lines=gi->eighth_octave_lines; + p->shiftoc=rint(log(gi->eighth_octave_lines*8.f)/log(2.f))-1; + + p->firstoc=toOC(.25f*rate*.5/n)*(1<<(p->shiftoc+1))-gi->eighth_octave_lines; + maxoc=toOC((n+.25f)*rate*.5/n)*(1<<(p->shiftoc+1))+.5f; + p->total_octave_lines=maxoc-p->firstoc+1; + p->ath=(float*)_ogg_malloc(n*sizeof(*p->ath)); + + p->octave=(long*)_ogg_malloc(n*sizeof(*p->octave)); + p->bark=(long*)_ogg_malloc(n*sizeof(*p->bark)); + p->vi=vi; + p->n=n; + p->rate=rate; + + /* AoTuV HF weighting */ + p->m_val = 1.; + if(rate < 26000) p->m_val = 0; + else if(rate < 38000) p->m_val = .94; /* 32kHz */ + else if(rate > 46000) p->m_val = 1.275; /* 48kHz */ + + /* set up the lookups for a given blocksize and sample rate */ + + for(i=0,j=0;iath[j]=base+100.; + base+=delta; + } + } + } + + for(i=0;inoisewindowlominnoisewindowlo);lo++); + + for(;hi<=n && (hinoisewindowhimin || + toBARK(rate/(2*n)*hi)<(bark+vi->noisewindowhi));hi++); + + p->bark[i]=((lo-1)<<16)+(hi-1); + + } + + for(i=0;ioctave[i]=toOC((i+.25f)*.5*rate/n)*(1<<(p->shiftoc+1))+.5f; + + p->tonecurves=setup_tone_curves(vi->toneatt,rate*.5/n,n, + vi->tone_centerboost,vi->tone_decay); + + /* set up rolling noise median */ + p->noiseoffset=(float**)_ogg_malloc(P_NOISECURVES*sizeof(*p->noiseoffset)); + for(i=0;inoiseoffset[i]=(float*)_ogg_malloc(n*sizeof(**p->noiseoffset)); + + for(i=0;i=P_BANDS-1)halfoc=P_BANDS-1; + inthalfoc=(int)halfoc; + del=halfoc-inthalfoc; + + for(j=0;jnoiseoffset[j][i]= + p->vi->noiseoff[j][inthalfoc]*(1.-del) + + p->vi->noiseoff[j][inthalfoc+1]*del; + + } +#if 0 + { + static int ls=0; + _analysis_output_always("noiseoff0",ls,p->noiseoffset[0],n,1,0,0); + _analysis_output_always("noiseoff1",ls,p->noiseoffset[1],n,1,0,0); + _analysis_output_always("noiseoff2",ls++,p->noiseoffset[2],n,1,0,0); + } +#endif +} + +void _vp_psy_clear(vorbis_look_psy *p){ + int i,j; + if(p){ + if(p->ath)_ogg_free(p->ath); + if(p->octave)_ogg_free(p->octave); + if(p->bark)_ogg_free(p->bark); + if(p->tonecurves){ + for(i=0;itonecurves[i][j]); + } + _ogg_free(p->tonecurves[i]); + } + _ogg_free(p->tonecurves); + } + if(p->noiseoffset){ + for(i=0;inoiseoffset[i]); + } + _ogg_free(p->noiseoffset); + } + memset(p,0,sizeof(*p)); + } +} + +/* octave/(8*eighth_octave_lines) x scale and dB y scale */ +static void seed_curve(float *seed, + const float **curves, + float amp, + int oc, int n, + int linesper,float dBoffset){ + int i,post1; + int seedptr; + const float *posts,*curve; + + int choice=(int)((amp+dBoffset-P_LEVEL_0)*.1f); + choice=max(choice,0); + choice=min(choice,P_LEVELS-1); + posts=curves[choice]; + curve=posts+2; + post1=(int)posts[1]; + seedptr=oc+(posts[0]-EHMER_OFFSET)*linesper-(linesper>>1); + + for(i=posts[0];i0){ + float lin=amp+curve[i]; + if(seed[seedptr]=n)break; + } +} + +static void seed_loop(vorbis_look_psy *p, + const float ***curves, + const float *f, + const float *flr, + float *seed, + float specmax){ + vorbis_info_psy *vi=p->vi; + long n=p->n,i; + float dBoffset=vi->max_curve_dB-specmax; + + /* prime the working vector with peak values */ + + for(i=0;ioctave[i]; + while(i+1octave[i+1]==oc){ + i++; + if(f[i]>max)max=f[i]; + } + + if(max+6.f>flr[i]){ + oc=oc>>p->shiftoc; + + if(oc>=P_BANDS)oc=P_BANDS-1; + if(oc<0)oc=0; + + seed_curve(seed, + curves[oc], + max, + p->octave[i]-p->firstoc, + p->total_octave_lines, + p->eighth_octave_lines, + dBoffset); + } + } +} + +static void seed_chase(float *seeds, int linesper, long n){ + long *posstack=(long*)alloca(n*sizeof(*posstack)); + float *ampstack=(float*)alloca(n*sizeof(*ampstack)); + long stack=0; + long pos=0; + long i; + + for(i=0;i1 && ampstack[stack-1]<=ampstack[stack-2] && + iampstack[i]){ + endpos=posstack[i+1]; + }else{ + endpos=posstack[i]+linesper+1; /* +1 is important, else bin 0 is + discarded in short frames */ + } + if(endpos>n)endpos=n; + for(;pos +static void max_seeds(vorbis_look_psy *p, + float *seed, + float *flr){ + long n=p->total_octave_lines; + int linesper=p->eighth_octave_lines; + long linpos=0; + long pos; + + seed_chase(seed,linesper,n); /* for masking */ + + pos=p->octave[0]-p->firstoc-(linesper>>1); + + while(linpos+1n){ + float minV=seed[pos]; + long end=((p->octave[linpos]+p->octave[linpos+1])>>1)-p->firstoc; + if(minV>p->vi->tone_abs_limit)minV=p->vi->tone_abs_limit; + while(pos+1<=end){ + pos++; + if((seed[pos]>NEGINF && seed[pos]firstoc; + for(;linposn && p->octave[linpos]<=end;linpos++) + if(flr[linpos]total_octave_lines-1]; + for(;linposn;linpos++) + if(flr[linpos]> 16; + if( lo>=0 ) break; + hi = b[i] & 0xffff; + + tN = N[hi] + N[-lo]; + tX = X[hi] - X[-lo]; + tXX = XX[hi] + XX[-lo]; + tY = Y[hi] + Y[-lo]; + tXY = XY[hi] - XY[-lo]; + + A = tY * tXX - tX * tXY; + B = tN * tXY - tX * tY; + D = tN * tXX - tX * tX; + R = (A + x * B) / D; + if (R < 0.f) + R = 0.f; + + noise[i] = R - offset; + } + + for ( ;; i++, x += 1.f) { + + lo = b[i] >> 16; + hi = b[i] & 0xffff; + if(hi>=n)break; + + tN = N[hi] - N[lo]; + tX = X[hi] - X[lo]; + tXX = XX[hi] - XX[lo]; + tY = Y[hi] - Y[lo]; + tXY = XY[hi] - XY[lo]; + + A = tY * tXX - tX * tXY; + B = tN * tXY - tX * tY; + D = tN * tXX - tX * tX; + R = (A + x * B) / D; + if (R < 0.f) R = 0.f; + + noise[i] = R - offset; + } + for ( ; i < n; i++, x += 1.f) { + + R = (A + x * B) / D; + if (R < 0.f) R = 0.f; + + noise[i] = R - offset; + } + + if (fixed <= 0) return; + + for (i = 0, x = 0.f;; i++, x += 1.f) { + hi = i + fixed / 2; + lo = hi - fixed; + if(lo>=0)break; + + tN = N[hi] + N[-lo]; + tX = X[hi] - X[-lo]; + tXX = XX[hi] + XX[-lo]; + tY = Y[hi] + Y[-lo]; + tXY = XY[hi] - XY[-lo]; + + A = tY * tXX - tX * tXY; + B = tN * tXY - tX * tY; + D = tN * tXX - tX * tX; + R = (A + x * B) / D; + + if (R - offset < noise[i]) noise[i] = R - offset; + } + for ( ;; i++, x += 1.f) { + + hi = i + fixed / 2; + lo = hi - fixed; + if(hi>=n)break; + + tN = N[hi] - N[lo]; + tX = X[hi] - X[lo]; + tXX = XX[hi] - XX[lo]; + tY = Y[hi] - Y[lo]; + tXY = XY[hi] - XY[lo]; + + A = tY * tXX - tX * tXY; + B = tN * tXY - tX * tY; + D = tN * tXX - tX * tX; + R = (A + x * B) / D; + + if (R - offset < noise[i]) noise[i] = R - offset; + } + for ( ; i < n; i++, x += 1.f) { + R = (A + x * B) / D; + if (R - offset < noise[i]) noise[i] = R - offset; + } +} + +static float FLOOR1_fromdB_INV_LOOKUP[256]={ + 0.F, 8.81683e+06F, 8.27882e+06F, 7.77365e+06F, + 7.29930e+06F, 6.85389e+06F, 6.43567e+06F, 6.04296e+06F, + 5.67422e+06F, 5.32798e+06F, 5.00286e+06F, 4.69759e+06F, + 4.41094e+06F, 4.14178e+06F, 3.88905e+06F, 3.65174e+06F, + 3.42891e+06F, 3.21968e+06F, 3.02321e+06F, 2.83873e+06F, + 2.66551e+06F, 2.50286e+06F, 2.35014e+06F, 2.20673e+06F, + 2.07208e+06F, 1.94564e+06F, 1.82692e+06F, 1.71544e+06F, + 1.61076e+06F, 1.51247e+06F, 1.42018e+06F, 1.33352e+06F, + 1.25215e+06F, 1.17574e+06F, 1.10400e+06F, 1.03663e+06F, + 973377.F, 913981.F, 858210.F, 805842.F, + 756669.F, 710497.F, 667142.F, 626433.F, + 588208.F, 552316.F, 518613.F, 486967.F, + 457252.F, 429351.F, 403152.F, 378551.F, + 355452.F, 333762.F, 313396.F, 294273.F, + 276316.F, 259455.F, 243623.F, 228757.F, + 214798.F, 201691.F, 189384.F, 177828.F, + 166977.F, 156788.F, 147221.F, 138237.F, + 129802.F, 121881.F, 114444.F, 107461.F, + 100903.F, 94746.3F, 88964.9F, 83536.2F, + 78438.8F, 73652.5F, 69158.2F, 64938.1F, + 60975.6F, 57254.9F, 53761.2F, 50480.6F, + 47400.3F, 44507.9F, 41792.0F, 39241.9F, + 36847.3F, 34598.9F, 32487.7F, 30505.3F, + 28643.8F, 26896.0F, 25254.8F, 23713.7F, + 22266.7F, 20908.0F, 19632.2F, 18434.2F, + 17309.4F, 16253.1F, 15261.4F, 14330.1F, + 13455.7F, 12634.6F, 11863.7F, 11139.7F, + 10460.0F, 9821.72F, 9222.39F, 8659.64F, + 8131.23F, 7635.06F, 7169.17F, 6731.70F, + 6320.93F, 5935.23F, 5573.06F, 5232.99F, + 4913.67F, 4613.84F, 4332.30F, 4067.94F, + 3819.72F, 3586.64F, 3367.78F, 3162.28F, + 2969.31F, 2788.13F, 2617.99F, 2458.24F, + 2308.24F, 2167.39F, 2035.14F, 1910.95F, + 1794.35F, 1684.85F, 1582.04F, 1485.51F, + 1394.86F, 1309.75F, 1229.83F, 1154.78F, + 1084.32F, 1018.15F, 956.024F, 897.687F, + 842.910F, 791.475F, 743.179F, 697.830F, + 655.249F, 615.265F, 577.722F, 542.469F, + 509.367F, 478.286F, 449.101F, 421.696F, + 395.964F, 371.803F, 349.115F, 327.812F, + 307.809F, 289.026F, 271.390F, 254.830F, + 239.280F, 224.679F, 210.969F, 198.096F, + 186.008F, 174.658F, 164.000F, 153.993F, + 144.596F, 135.773F, 127.488F, 119.708F, + 112.404F, 105.545F, 99.1046F, 93.0572F, + 87.3788F, 82.0469F, 77.0404F, 72.3394F, + 67.9252F, 63.7804F, 59.8885F, 56.2341F, + 52.8027F, 49.5807F, 46.5553F, 43.7144F, + 41.0470F, 38.5423F, 36.1904F, 33.9821F, + 31.9085F, 29.9614F, 28.1332F, 26.4165F, + 24.8045F, 23.2910F, 21.8697F, 20.5352F, + 19.2822F, 18.1056F, 17.0008F, 15.9634F, + 14.9893F, 14.0746F, 13.2158F, 12.4094F, + 11.6522F, 10.9411F, 10.2735F, 9.64662F, + 9.05798F, 8.50526F, 7.98626F, 7.49894F, + 7.04135F, 6.61169F, 6.20824F, 5.82941F, + 5.47370F, 5.13970F, 4.82607F, 4.53158F, + 4.25507F, 3.99542F, 3.75162F, 3.52269F, + 3.30774F, 3.10590F, 2.91638F, 2.73842F, + 2.57132F, 2.41442F, 2.26709F, 2.12875F, + 1.99885F, 1.87688F, 1.76236F, 1.65482F, + 1.55384F, 1.45902F, 1.36999F, 1.28640F, + 1.20790F, 1.13419F, 1.06499F, 1.F +}; + +void _vp_remove_floor(vorbis_look_psy *p, + float *mdct, + int *codedflr, + float *residue, + int sliding_lowpass){ + + int i,n=p->n; + + if(sliding_lowpass>n)sliding_lowpass=n; + + for(i=0;in; + float *work=(float*) alloca(n*sizeof(*work)); + + bark_noise_hybridmp(n,p->bark,logmdct,logmask, + 140.,-1); + + for(i=0;ibark,work,logmask,0., + p->vi->noisewindowfixed); + + for(i=0;i=NOISE_COMPAND_LEVELS)dB=NOISE_COMPAND_LEVELS-1; + if(dB<0)dB=0; + logmask[i]= work[i]+p->vi->noisecompand[dB]; + } + +} + +void _vp_tonemask(vorbis_look_psy *p, + float *logfft, + float *logmask, + float global_specmax, + float local_specmax){ + + int i,n=p->n; + + float *seed=(float*) alloca(sizeof(*seed)*p->total_octave_lines); + float att=local_specmax+p->vi->ath_adjatt; + for(i=0;itotal_octave_lines;i++)seed[i]=NEGINF; + + /* set the ATH (floating below localmax, not global max by a + specified att) */ + if(attvi->ath_maxatt)att=p->vi->ath_maxatt; + + for(i=0;iath[i]+att; + + /* tone masking */ + seed_loop(p,(const float ***)p->tonecurves,logfft,logmask,seed,global_specmax); + max_seeds(p,seed,logmask); + +} + +void _vp_offset_and_mix(vorbis_look_psy *p, + float *noise, + float *tone, + int offset_select, + float *logmask, + float *mdct, + float *logmdct){ + int i,n=p->n; + float de, coeffi, cx;/* AoTuV */ + float toneatt=p->vi->tone_masteratt[offset_select]; + + cx = p->m_val; + + for(i=0;inoiseoffset[offset_select][i]; + if(val>p->vi->noisemaxsupp)val=p->vi->noisemaxsupp; + logmask[i]=max(val,tone[i]+toneatt); + + /* AoTuV */ + /** @ M1 ** + The following codes improve a noise problem. + A fundamental idea uses the value of masking and carries out + the relative compensation of the MDCT. + However, this code is not perfect and all noise problems cannot be solved. + by Aoyumi @ 2004/04/18 + */ + + if(offset_select == 1) { + coeffi = -17.2; /* coeffi is a -17.2dB threshold */ + val = val - logmdct[i]; /* val == mdct line value relative to floor in dB */ + + if(val > coeffi){ + /* mdct value is > -17.2 dB below floor */ + + de = 1.0-((val-coeffi)*0.005*cx); + /* pro-rated attenuation: + -0.00 dB boost if mdct value is -17.2dB (relative to floor) + -0.77 dB boost if mdct value is 0dB (relative to floor) + -1.64 dB boost if mdct value is +17.2dB (relative to floor) + etc... */ + + if(de < 0) de = 0.0001; + }else + /* mdct value is <= -17.2 dB below floor */ + + de = 1.0-((val-coeffi)*0.0003*cx); + /* pro-rated attenuation: + +0.00 dB atten if mdct value is -17.2dB (relative to floor) + +0.45 dB atten if mdct value is -34.4dB (relative to floor) + etc... */ + + mdct[i] *= de; + + } + } +} + +float _vp_ampmax_decay(float amp,vorbis_dsp_state *vd){ + vorbis_info *vi=vd->vi; + codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; + vorbis_info_psy_global *gi=&ci->psy_g_param; + + int n=ci->blocksizes[vd->W]/2; + float secs=(float)n/vi->rate; + + amp+=secs*gi->ampmax_att_per_sec; + if(amp<-9999)amp=-9999; + return(amp); +} + +static void couple_lossless(float A, float B, + float *qA, float *qB){ + int test1=fabs(*qA)>fabs(*qB); + test1-= fabs(*qA)fabs(B))<<1)-1; + if(test1==1){ + *qB=(*qA>0.f?*qA-*qB:*qB-*qA); + }else{ + float temp=*qB; + *qB=(*qB>0.f?*qA-*qB:*qB-*qA); + *qA=temp; + } + + if(*qB>fabs(*qA)*1.9999f){ + *qB= -fabs(*qA)*2.f; + *qA= -*qA; + } +} + +static float hypot_lookup[32]={ + -0.009935, -0.011245, -0.012726, -0.014397, + -0.016282, -0.018407, -0.020800, -0.023494, + -0.026522, -0.029923, -0.033737, -0.038010, + -0.042787, -0.048121, -0.054064, -0.060671, + -0.068000, -0.076109, -0.085054, -0.094892, + -0.105675, -0.117451, -0.130260, -0.144134, + -0.159093, -0.175146, -0.192286, -0.210490, + -0.229718, -0.249913, -0.271001, -0.292893}; + +static void precomputed_couple_point(float premag, + int floorA,int floorB, + float *mag, float *ang){ + + int test=(floorA>floorB)-1; + int offset=31-abs(floorA-floorB); + float floormag=hypot_lookup[((offset<0)-1)&offset]+1.f; + + floormag*=FLOOR1_fromdB_INV_LOOKUP[(floorB&test)|(floorA&(~test))]; + + *mag=premag*floormag; + *ang=0.f; +} + +/* just like below, this is currently set up to only do + single-step-depth coupling. Otherwise, we'd have to do more + copying (which will be inevitable later) */ + +/* doing the real circular magnitude calculation is audibly superior + to (A+B)/sqrt(2) */ +static float dipole_hypot(float a, float b){ + if(a>0.){ + if(b>0.)return sqrt(a*a+b*b); + if(a>-b)return sqrt(a*a-b*b); + return -sqrt(b*b-a*a); + } + if(b<0.)return -sqrt(a*a+b*b); + if(-a>b)return -sqrt(a*a-b*b); + return sqrt(b*b-a*a); +} +static float round_hypot(float a, float b){ + if(a>0.){ + if(b>0.)return sqrt(a*a+b*b); + if(a>-b)return sqrt(a*a+b*b); + return -sqrt(b*b+a*a); + } + if(b<0.)return -sqrt(a*a+b*b); + if(-a>b)return -sqrt(a*a+b*b); + return sqrt(b*b+a*a); +} + +/* revert to round hypot for now */ +float **_vp_quantize_couple_memo(vorbis_block *vb, + vorbis_info_psy_global *g, + vorbis_look_psy *p, + vorbis_info_mapping0 *vi, + float **mdct){ + + int i,j,n=p->n; + float **ret=(float**) _vorbis_block_alloc(vb,vi->coupling_steps*sizeof(*ret)); + int limit=g->coupling_pointlimit[p->vi->blockflag][PACKETBLOBS/2]; + + for(i=0;icoupling_steps;i++){ + float *mdctM=mdct[vi->coupling_mag[i]]; + float *mdctA=mdct[vi->coupling_ang[i]]; + ret[i]=(float*) _vorbis_block_alloc(vb,n*sizeof(**ret)); + for(j=0;jf2); +} + +int **_vp_quantize_couple_sort(vorbis_block *vb, + vorbis_look_psy *p, + vorbis_info_mapping0 *vi, + float **mags){ + + if(p->vi->normal_point_p){ + int i,j,k,n=p->n; + int **ret=(int**) _vorbis_block_alloc(vb,vi->coupling_steps*sizeof(*ret)); + int partition=p->vi->normal_partition; + float **work=(float**) alloca(sizeof(*work)*partition); + + for(i=0;icoupling_steps;i++){ + ret[i]=(int*) _vorbis_block_alloc(vb,n*sizeof(**ret)); + + for(j=0;jn; + vorbis_info_psy *vi=p->vi; + int partition=vi->normal_partition; + float **work=(float**) alloca(sizeof(*work)*partition); + int start=vi->normal_start; + + for(j=start;jn)partition=n-j; + for(i=0;in; + vorbis_info_psy *vi=p->vi; + int partition=vi->normal_partition; + int start=vi->normal_start; + + if(start>n)start=n; + + if(vi->normal_channel_p){ + for(;j=.25f){ + out[k]=rint(in[k]); + acc-=in[k]*in[k]; + flag=1; + }else{ + if(accnormal_thresh)break; + out[k]=unitnorm(in[k]); + acc-=1.; + } + } + + for(;in; + + /* perform any requested channel coupling */ + /* point stereo can only be used in a first stage (in this encoder) + because of the dependency on floor lookups */ + for(i=0;icoupling_steps;i++){ + + /* once we're doing multistage coupling in which a channel goes + through more than one coupling step, the floor vector + magnitudes will also have to be recalculated an propogated + along with PCM. Right now, we're not (that will wait until 5.1 + most likely), so the code isn't here yet. The memory management + here is all assuming single depth couplings anyway. */ + + /* make sure coupling a zero and a nonzero channel results in two + nonzero channels. */ + if(nonzero[vi->coupling_mag[i]] || + nonzero[vi->coupling_ang[i]]){ + + float *rM=res[vi->coupling_mag[i]]; + float *rA=res[vi->coupling_ang[i]]; + float *qM=rM+n; + float *qA=rA+n; + int *floorM=ifloor[vi->coupling_mag[i]]; + int *floorA=ifloor[vi->coupling_ang[i]]; + float prepoint=stereo_threshholds[g->coupling_prepointamp[blobno]]; + float postpoint=stereo_threshholds[g->coupling_postpointamp[blobno]]; + int partition=(p->vi->normal_point_p?p->vi->normal_partition:p->n); + int limit=g->coupling_pointlimit[p->vi->blockflag][blobno]; + int pointlimit=limit; + + nonzero[vi->coupling_mag[i]]=1; + nonzero[vi->coupling_ang[i]]=1; + + /* The threshold of a stereo is changed with the size of n */ + if(n > 1000) + postpoint=stereo_threshholds_limited[g->coupling_postpointamp[blobno]]; + + for(j=0;jn;j+=partition){ + float acc=0.f; + + for(k=0;k=limit && fabs(rM[l])vi->normal_point_p){ + for(k=0;k=p->vi->normal_thresh;k++){ + int l=mag_sort[i][j+k]; + if(l=pointlimit && rint(qM[l])==0.f){ + qM[l]=unitnorm(qM[l]); + acc-=1.f; + } + } + } + } + } + } +} + +/* AoTuV */ +/** @ M2 ** + The boost problem by the combination of noise normalization and point stereo is eased. + However, this is a temporary patch. + by Aoyumi @ 2004/04/18 +*/ + +void hf_reduction(vorbis_info_psy_global *g, + vorbis_look_psy *p, + vorbis_info_mapping0 *vi, + float **mdct){ + + int i,j,n=p->n, de=0.3*p->m_val; + int limit=g->coupling_pointlimit[p->vi->blockflag][PACKETBLOBS/2]; + + for(i=0; icoupling_steps; i++){ + /* for(j=start; j +#include +#include + +#if defined(TRAIN_RES) || defined (TRAIN_RESAUX) +#include +#endif + +typedef struct { + vorbis_info_residue0 *info; + + int parts; + int stages; + codebook *fullbooks; + codebook *phrasebook; + codebook ***partbooks; + + int partvals; + int **decodemap; + + long postbits; + long phrasebits; + long frames; + +#if defined(TRAIN_RES) || defined(TRAIN_RESAUX) + int train_seq; + long *training_data[8][64]; + float training_max[8][64]; + float training_min[8][64]; + float tmin; + float tmax; +#endif + +} vorbis_look_residue0; + +void res0_free_info(vorbis_info_residue *i){ + vorbis_info_residue0 *info=(vorbis_info_residue0 *)i; + if(info){ + memset(info,0,sizeof(*info)); + _ogg_free(info); + } +} + +void res0_free_look(vorbis_look_residue *i){ + int j; + if(i){ + + vorbis_look_residue0 *look=(vorbis_look_residue0 *)i; + +#ifdef TRAIN_RES + { + int j,k,l; + for(j=0;jparts;j++){ + /*fprintf(stderr,"partition %d: ",j);*/ + for(k=0;k<8;k++) + if(look->training_data[k][j]){ + char buffer[80]; + FILE *of; + codebook *statebook=look->partbooks[j][k]; + + /* long and short into the same bucket by current convention */ + sprintf(buffer,"res_part%d_pass%d.vqd",j,k); + of=fopen(buffer,"a"); + + for(l=0;lentries;l++) + fprintf(of,"%d:%ld\n",l,look->training_data[k][j][l]); + + fclose(of); + + /*fprintf(stderr,"%d(%.2f|%.2f) ",k, + look->training_min[k][j],look->training_max[k][j]);*/ + + _ogg_free(look->training_data[k][j]); + look->training_data[k][j]=NULL; + } + /*fprintf(stderr,"\n");*/ + } + } + fprintf(stderr,"min/max residue: %g::%g\n",look->tmin,look->tmax); + + /*fprintf(stderr,"residue bit usage %f:%f (%f total)\n", + (float)look->phrasebits/look->frames, + (float)look->postbits/look->frames, + (float)(look->postbits+look->phrasebits)/look->frames);*/ +#endif + + /*vorbis_info_residue0 *info=look->info; + + fprintf(stderr, + "%ld frames encoded in %ld phrasebits and %ld residue bits " + "(%g/frame) \n",look->frames,look->phrasebits, + look->resbitsflat, + (look->phrasebits+look->resbitsflat)/(float)look->frames); + + for(j=0;jparts;j++){ + long acc=0; + fprintf(stderr,"\t[%d] == ",j); + for(k=0;kstages;k++) + if((info->secondstages[j]>>k)&1){ + fprintf(stderr,"%ld,",look->resbits[j][k]); + acc+=look->resbits[j][k]; + } + + fprintf(stderr,":: (%ld vals) %1.2fbits/sample\n",look->resvals[j], + acc?(float)acc/(look->resvals[j]*info->grouping):0); + } + fprintf(stderr,"\n");*/ + + for(j=0;jparts;j++) + if(look->partbooks[j])_ogg_free(look->partbooks[j]); + _ogg_free(look->partbooks); + for(j=0;jpartvals;j++) + _ogg_free(look->decodemap[j]); + _ogg_free(look->decodemap); + + memset(look,0,sizeof(*look)); + _ogg_free(look); + } +} + +static int icount(unsigned int v){ + int ret=0; + while(v){ + ret+=v&1; + v>>=1; + } + return(ret); +} + +void res0_pack(vorbis_info_residue *vr,oggpack_buffer *opb){ + vorbis_info_residue0 *info=(vorbis_info_residue0 *)vr; + int j,acc=0; + oggpack_write(opb,info->begin,24); + oggpack_write(opb,info->end,24); + + oggpack_write(opb,info->grouping-1,24); /* residue vectors to group and + code with a partitioned book */ + oggpack_write(opb,info->partitions-1,6); /* possible partition choices */ + oggpack_write(opb,info->groupbook,8); /* group huffman book */ + + /* secondstages is a bitmask; as encoding progresses pass by pass, a + bitmask of one indicates this partition class has bits to write + this pass */ + for(j=0;jpartitions;j++){ + if(ilog(info->secondstages[j])>3){ + /* yes, this is a minor hack due to not thinking ahead */ + oggpack_write(opb,info->secondstages[j],3); + oggpack_write(opb,1,1); + oggpack_write(opb,info->secondstages[j]>>3,5); + }else + oggpack_write(opb,info->secondstages[j],4); /* trailing zero */ + acc+=icount(info->secondstages[j]); + } + for(j=0;jbooklist[j],8); + +} + +/* vorbis_info is for range checking */ +vorbis_info_residue *res0_unpack(vorbis_info *vi,oggpack_buffer *opb){ + int j,acc=0; + vorbis_info_residue0 *info=(vorbis_info_residue0*) _ogg_calloc(1,sizeof(*info)); + codec_setup_info *ci=(codec_setup_info*) vi->codec_setup; + + info->begin=oggpack_read(opb,24); + info->end=oggpack_read(opb,24); + info->grouping=oggpack_read(opb,24)+1; + info->partitions=oggpack_read(opb,6)+1; + info->groupbook=oggpack_read(opb,8); + + for(j=0;jpartitions;j++){ + int cascade=oggpack_read(opb,3); + if(oggpack_read(opb,1)) + cascade|=(oggpack_read(opb,5)<<3); + info->secondstages[j]=cascade; + + acc+=icount(cascade); + } + for(j=0;jbooklist[j]=oggpack_read(opb,8); + + if(info->groupbook>=ci->books)goto errout; + for(j=0;jbooklist[j]>=ci->books)goto errout; + + return(info); + errout: + res0_free_info(info); + return(NULL); +} + +vorbis_look_residue *res0_look(vorbis_dsp_state *vd, + vorbis_info_residue *vr){ + vorbis_info_residue0 *info=(vorbis_info_residue0 *)vr; + vorbis_look_residue0 *look=(vorbis_look_residue0 *)_ogg_calloc(1,sizeof(*look)); + codec_setup_info *ci=(codec_setup_info*)vd->vi->codec_setup; + + int j,k,acc=0; + int dim; + int maxstage=0; + look->info=info; + + look->parts=info->partitions; + look->fullbooks=ci->fullbooks; + look->phrasebook=ci->fullbooks+info->groupbook; + dim=look->phrasebook->dim; + + look->partbooks=(codebook***)_ogg_calloc(look->parts,sizeof(*look->partbooks)); + + for(j=0;jparts;j++){ + int stages=ilog(info->secondstages[j]); + if(stages){ + if(stages>maxstage)maxstage=stages; + look->partbooks[j]=(codebook**) _ogg_calloc(stages,sizeof(*look->partbooks[j])); + for(k=0;ksecondstages[j]&(1<partbooks[j][k]=ci->fullbooks+info->booklist[acc++]; +#ifdef TRAIN_RES + look->training_data[k][j]=_ogg_calloc(look->partbooks[j][k]->entries, + sizeof(***look->training_data)); +#endif + } + } + } + + look->partvals=rint(pow((float)look->parts,(float)dim)); + look->stages=maxstage; + look->decodemap=(int**)_ogg_malloc(look->partvals*sizeof(*look->decodemap)); + for(j=0;jpartvals;j++){ + long val=j; + long mult=look->partvals/look->parts; + look->decodemap[j]=(int*)_ogg_malloc(dim*sizeof(*look->decodemap[j])); + for(k=0;kparts; + look->decodemap[j][k]=deco; + } + } +#if defined(TRAIN_RES) || defined (TRAIN_RESAUX) + { + static int train_seq=0; + look->train_seq=train_seq++; + } +#endif + return(look); +} + +/* break an abstraction and copy some code for performance purposes */ +static int local_book_besterror(codebook *book,float *a){ + int dim=book->dim,i,k,o; + int best=0; + encode_aux_threshmatch *tt=book->c->thresh_tree; + + /* find the quant val of each scalar */ + for(k=0,o=dim;kthreshvals>>1; + + if(valquantthresh[i]){ + if(valquantthresh[i-1]){ + for(--i;i>0;--i) + if(val>=tt->quantthresh[i-1]) + break; + } + }else{ + + for(++i;ithreshvals-1;++i) + if(valquantthresh[i])break; + + } + + best=(best*tt->quantvals)+tt->quantmap[i]; + } + /* regular lattices are easy :-) */ + + if(book->c->lengthlist[best]<=0){ + const static_codebook *c=book->c; + int i,j; + float bestf=0.f; + float *e=book->valuelist; + best=-1; + for(i=0;ientries;i++){ + if(c->lengthlist[i]>0){ + float thisx=0.f; + for(j=0;jvaluelist+best*dim; + for(i=0;idim; + int step=n/dim; + + for(i=0;iinfo; + + /* move all this setup out later */ + int samples_per_partition=info->grouping; + int possible_partitions=info->partitions; + int n=info->end-info->begin; + + int partvals=n/samples_per_partition; + long **partword=(long**)_vorbis_block_alloc(vb,ch*sizeof(*partword)); + float scale=100./samples_per_partition; + + /* we find the partition type for each partition of each + channel. We'll go back and do the interleaved encoding in a + bit. For now, clarity */ + + for(i=0;ibegin; + for(j=0;jmax)max=fabs(in[j][offset+k]); + ent+=fabs(rint(in[j][offset+k])); + } + ent*=scale; + + for(k=0;kclassmetric1[k] && + (info->classmetric2[k]<0 || (int)entclassmetric2[k])) + break; + + partword[j][i]=k; + } + } + +#ifdef TRAIN_RESAUX + { + FILE *of; + char buffer[80]; + + for(i=0;itrain_seq); + of=fopen(buffer,"a"); + for(j=0;jframes++; + + return(partword); +} + +/* designed for stereo or other modes where the partition size is an + integer multiple of the number of channels encoded in the current + submap */ +static long **_2class(vorbis_block *vb,vorbis_look_residue *vl,float **in, + int ch){ + long i,j,k,l; + vorbis_look_residue0 *look=(vorbis_look_residue0 *)vl; + vorbis_info_residue0 *info=look->info; + + /* move all this setup out later */ + int samples_per_partition=info->grouping; + int possible_partitions=info->partitions; + int n=info->end-info->begin; + + int partvals=n/samples_per_partition; + long **partword=(long**)_vorbis_block_alloc(vb,sizeof(*partword)); + +#if defined(TRAIN_RES) || defined (TRAIN_RESAUX) + FILE *of; + char buffer[80]; +#endif + + partword[0]=(long*)_vorbis_block_alloc(vb,n*ch/samples_per_partition*sizeof(*partword[0])); + memset(partword[0],0,n*ch/samples_per_partition*sizeof(*partword[0])); + + for(i=0,l=info->begin/ch;imagmax)magmax=fabs(in[0][l]); + for(k=1;kangmax)angmax=fabs(in[k][l]); + l++; + } + + for(j=0;jclassmetric1[j] && + angmax<=info->classmetric2[j]) + break; + + partword[0][i]=j; + + } + +#ifdef TRAIN_RESAUX + sprintf(buffer,"resaux_%d.vqd",look->train_seq); + of=fopen(buffer,"a"); + for(i=0;iframes++; + + return(partword); +} + +static int _01forward(oggpack_buffer *opb, + vorbis_block *vb,vorbis_look_residue *vl, + float **in,int ch, + long **partword, + int (*encode)(oggpack_buffer *,float *,int, + codebook *,long *)){ + long i,j,k,s; + vorbis_look_residue0 *look=(vorbis_look_residue0 *)vl; + vorbis_info_residue0 *info=look->info; + + /* move all this setup out later */ + int samples_per_partition=info->grouping; + int possible_partitions=info->partitions; + int partitions_per_word=look->phrasebook->dim; + int n=info->end-info->begin; + + int partvals=n/samples_per_partition; + long resbits[128]; + long resvals[128]; + +#ifdef TRAIN_RES + for(i=0;ibegin;jend;j++){ + if(in[i][j]>look->tmax)look->tmax=in[i][j]; + if(in[i][j]tmin)look->tmin=in[i][j]; + } +#endif + + memset(resbits,0,sizeof(resbits)); + memset(resvals,0,sizeof(resvals)); + + /* we code the partition words for each channel, then the residual + words for a partition per channel until we've written all the + residual words for that partition word. Then write the next + partition channel words... */ + + for(s=0;sstages;s++){ + + for(i=0;iphrasebook->entries) + look->phrasebits+=vorbis_book_encode(look->phrasebook,val,opb); +#if 0 /*def TRAIN_RES*/ + else + fprintf(stderr,"!"); +#endif + + } + } + + /* now we encode interleaved residual values for the partitions */ + for(k=0;kbegin; + + for(j=0;jsecondstages[partword[j][i]]&(1<partbooks[partword[j][i]][s]; + if(statebook){ + int ret; + long *accumulator=NULL; + +#ifdef TRAIN_RES + accumulator=look->training_data[s][partword[j][i]]; + { + int l; + float *samples=in[j]+offset; + for(l=0;ltraining_min[s][partword[j][i]]) + look->training_min[s][partword[j][i]]=samples[l]; + if(samples[l]>look->training_max[s][partword[j][i]]) + look->training_max[s][partword[j][i]]=samples[l]; + } + } +#endif + + ret=encode(opb,in[j]+offset,samples_per_partition, + statebook,accumulator); + + look->postbits+=ret; + resbits[partword[j][i]]+=ret; + } + } + } + } + } + } + + /*{ + long total=0; + long totalbits=0; + fprintf(stderr,"%d :: ",vb->mode); + for(k=0;kinfo; + + /* move all this setup out later */ + int samples_per_partition=info->grouping; + int partitions_per_word=look->phrasebook->dim; + int n=info->end-info->begin; + + int partvals=n/samples_per_partition; + int partwords=(partvals+partitions_per_word-1)/partitions_per_word; + int ***partword=(int***)alloca(ch*sizeof(*partword)); + + for(j=0;jstages;s++){ + + /* each loop decodes on partition codeword containing + partitions_pre_word partitions */ + for(i=0,l=0;iphrasebook,&vb->opb); + if(temp==-1)goto eopbreak; + partword[j][l]=look->decodemap[temp]; + if(partword[j][l]==NULL)goto errout; + } + } + + /* now we decode residual values for the partitions */ + for(k=0;kbegin+i*samples_per_partition; + if(info->secondstages[partword[j][l][k]]&(1<partbooks[partword[j][l][k]][s]; + if(stagebook){ + if(decodepart(stagebook,in[j]+offset,&vb->opb, + samples_per_partition)==-1)goto eopbreak; + } + } + } + } + } + + errout: + eopbreak: + return(0); +} + +#if 0 +/* residue 0 and 1 are just slight variants of one another. 0 is + interleaved, 1 is not */ +long **res0_class(vorbis_block *vb,vorbis_look_residue *vl, + float **in,int *nonzero,int ch){ + /* we encode only the nonzero parts of a bundle */ + int i,used=0; + for(i=0;ipcmend/2; + for(i=0;ipcmend/2; + for(i=0;ipcmend/2,used=0; + + /* don't duplicate the code; use a working vector hack for now and + reshape ourselves into a single channel res1 */ + /* ugly; reallocs for each coupling pass :-( */ + float *work=(float*)_vorbis_block_alloc(vb,ch*n*sizeof(*work)); + for(i=0;iinfo; + + /* move all this setup out later */ + int samples_per_partition=info->grouping; + int partitions_per_word=look->phrasebook->dim; + int n=info->end-info->begin; + + int partvals=n/samples_per_partition; + int partwords=(partvals+partitions_per_word-1)/partitions_per_word; + int **partword=(int**)_vorbis_block_alloc(vb,partwords*sizeof(*partword)); + + for(i=0;istages;s++){ + for(i=0,l=0;iphrasebook,&vb->opb); + if(temp==-1)goto eopbreak; + partword[l]=look->decodemap[temp]; + if(partword[l]==NULL)goto errout; + } + + /* now we decode residual values for the partitions */ + for(k=0;ksecondstages[partword[l][k]]&(1<partbooks[partword[l][k]][s]; + + if(stagebook){ + if(vorbis_book_decodevv_add(stagebook,in, + i*samples_per_partition+info->begin,ch, + &vb->opb,samples_per_partition)==-1) + goto eopbreak; + } + } + } + } + + errout: + eopbreak: + return(0); +} + +vorbis_func_residue residue0_exportbundle={ + NULL, + &res0_unpack, + &res0_look, + &res0_free_info, + &res0_free_look, + NULL, + NULL, + &res0_inverse +}; + +vorbis_func_residue residue1_exportbundle={ + &res0_pack, + &res0_unpack, + &res0_look, + &res0_free_info, + &res0_free_look, + &res1_class, + &res1_forward, + &res1_inverse +}; + +vorbis_func_residue residue2_exportbundle={ + &res0_pack, + &res0_unpack, + &res0_look, + &res0_free_info, + &res0_free_look, + &res2_class, + &res2_forward, + &res2_inverse +}; + +#endif +/********* End of inlined file: res0.c *********/ + +/********* Start of inlined file: sharedbook.c *********/ + +/********* Start of inlined file: juce_OggVorbisHeader.h *********/ +// This file is included at the start of each Ogg-Vorbis .c file, just to do a few housekeeping +// tasks.. + +#ifdef _MSC_VER + #pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4305 4189 4706) +#endif +/********* End of inlined file: juce_OggVorbisHeader.h *********/ + +#if JUCE_USE_OGGVORBIS + +#include +#include +#include + +/**** pack/unpack helpers ******************************************/ +int _ilog(unsigned int v){ + int ret=0; + while(v){ + ret++; + v>>=1; + } + return(ret); +} + +/* 32 bit float (not IEEE; nonnormalized mantissa + + biased exponent) : neeeeeee eeemmmmm mmmmmmmm mmmmmmmm + Why not IEEE? It's just not that important here. */ + +#define VQ_FEXP 10 +#define VQ_FMAN 21 +#define VQ_FEXP_BIAS 768 /* bias toward values smaller than 1. */ + +/* doesn't currently guard under/overflow */ +long _float32_pack(float val){ + int sign=0; + long exp; + long mant; + if(val<0){ + sign=0x80000000; + val= -val; + } + exp= floor(log(val)/log(2.f)); + mant=rint(ldexp(val,(VQ_FMAN-1)-exp)); + exp=(exp+VQ_FEXP_BIAS)<>VQ_FMAN; + if(sign)mant= -mant; + return(ldexp(mant,exp-(VQ_FMAN-1)-VQ_FEXP_BIAS)); +} + +/* given a list of word lengths, generate a list of codewords. Works + for length ordered or unordered, always assigns the lowest valued + codewords first. Extended to handle unused entries (length 0) */ +ogg_uint32_t *_make_words(long *l,long n,long sparsecount){ + long i,j,count=0; + ogg_uint32_t marker[33]; + ogg_uint32_t *r=(ogg_uint32_t*)_ogg_malloc((sparsecount?sparsecount:n)*sizeof(*r)); + memset(marker,0,sizeof(marker)); + + for(i=0;i0){ + ogg_uint32_t entry=marker[length]; + + /* when we claim a node for an entry, we also claim the nodes + below it (pruning off the imagined tree that may have dangled + from it) as well as blocking the use of any nodes directly + above for leaves */ + + /* update ourself */ + if(length<32 && (entry>>length)){ + /* error condition; the lengths must specify an overpopulated tree */ + _ogg_free(r); + return(NULL); + } + r[count++]=entry; + + /* Look to see if the next shorter marker points to the node + above. if so, update it and repeat. */ + { + for(j=length;j>0;j--){ + + if(marker[j]&1){ + /* have to jump branches */ + if(j==1) + marker[1]++; + else + marker[j]=marker[j-1]<<1; + break; /* invariant says next upper marker would already + have been moved if it was on the same path */ + } + marker[j]++; + } + } + + /* prune the tree; the implicit invariant says all the longer + markers were dangling from our just-taken node. Dangle them + from our *new* node. */ + for(j=length+1;j<33;j++) + if((marker[j]>>1) == entry){ + entry=marker[j]; + marker[j]=marker[j-1]<<1; + }else + break; + }else + if(sparsecount==0)count++; + } + + /* bitreverse the words because our bitwise packer/unpacker is LSb + endian */ + for(i=0,count=0;i>j)&1; + } + + if(sparsecount){ + if(l[i]) + r[count++]=temp; + }else + r[count++]=temp; + } + + return(r); +} + +/* there might be a straightforward one-line way to do the below + that's portable and totally safe against roundoff, but I haven't + thought of it. Therefore, we opt on the side of caution */ +long _book_maptype1_quantvals(const static_codebook *b){ + long vals=floor(pow((float)b->entries,1.f/b->dim)); + + /* the above *should* be reliable, but we'll not assume that FP is + ever reliable when bitstream sync is at stake; verify via integer + means that vals really is the greatest value of dim for which + vals^b->bim <= b->entries */ + /* treat the above as an initial guess */ + while(1){ + long acc=1; + long acc1=1; + int i; + for(i=0;idim;i++){ + acc*=vals; + acc1*=vals+1; + } + if(acc<=b->entries && acc1>b->entries){ + return(vals); + }else{ + if(acc>b->entries){ + vals--; + }else{ + vals++; + } + } + } +} + +/* unpack the quantized list of values for encode/decode ***********/ +/* we need to deal with two map types: in map type 1, the values are + generated algorithmically (each column of the vector counts through + the values in the quant vector). in map type 2, all the values came + in in an explicit list. Both value lists must be unpacked */ +float *_book_unquantize(const static_codebook *b,int n,int *sparsemap){ + long j,k,count=0; + if(b->maptype==1 || b->maptype==2){ + int quantvals; + float mindel=_float32_unpack(b->q_min); + float delta=_float32_unpack(b->q_delta); + float *r=(float*)_ogg_calloc(n*b->dim,sizeof(*r)); + + /* maptype 1 and 2 both use a quantized value vector, but + different sizes */ + switch(b->maptype){ + case 1: + /* most of the time, entries%dimensions == 0, but we need to be + well defined. We define that the possible vales at each + scalar is values == entries/dim. If entries%dim != 0, we'll + have 'too few' values (values*dimentries;j++){ + if((sparsemap && b->lengthlist[j]) || !sparsemap){ + float last=0.f; + int indexdiv=1; + for(k=0;kdim;k++){ + int index= (j/indexdiv)%quantvals; + float val=b->quantlist[index]; + val=fabs(val)*delta+mindel+last; + if(b->q_sequencep)last=val; + if(sparsemap) + r[sparsemap[count]*b->dim+k]=val; + else + r[count*b->dim+k]=val; + indexdiv*=quantvals; + } + count++; + } + + } + break; + case 2: + for(j=0;jentries;j++){ + if((sparsemap && b->lengthlist[j]) || !sparsemap){ + float last=0.f; + + for(k=0;kdim;k++){ + float val=b->quantlist[j*b->dim+k]; + val=fabs(val)*delta+mindel+last; + if(b->q_sequencep)last=val; + if(sparsemap) + r[sparsemap[count]*b->dim+k]=val; + else + r[count*b->dim+k]=val; + } + count++; + } + } + break; + } + + return(r); + } + return(NULL); +} + +void vorbis_staticbook_clear(static_codebook *b){ + if(b->allocedp){ + if(b->quantlist)_ogg_free(b->quantlist); + if(b->lengthlist)_ogg_free(b->lengthlist); + if(b->nearest_tree){ + _ogg_free(b->nearest_tree->ptr0); + _ogg_free(b->nearest_tree->ptr1); + _ogg_free(b->nearest_tree->p); + _ogg_free(b->nearest_tree->q); + memset(b->nearest_tree,0,sizeof(*b->nearest_tree)); + _ogg_free(b->nearest_tree); + } + if(b->thresh_tree){ + _ogg_free(b->thresh_tree->quantthresh); + _ogg_free(b->thresh_tree->quantmap); + memset(b->thresh_tree,0,sizeof(*b->thresh_tree)); + _ogg_free(b->thresh_tree); + } + + memset(b,0,sizeof(*b)); + } +} + +void vorbis_staticbook_destroy(static_codebook *b){ + if(b->allocedp){ + vorbis_staticbook_clear(b); + _ogg_free(b); + } +} + +void vorbis_book_clear(codebook *b){ + /* static book is not cleared; we're likely called on the lookup and + the static codebook belongs to the info struct */ + if(b->valuelist)_ogg_free(b->valuelist); + if(b->codelist)_ogg_free(b->codelist); + + if(b->dec_index)_ogg_free(b->dec_index); + if(b->dec_codelengths)_ogg_free(b->dec_codelengths); + if(b->dec_firsttable)_ogg_free(b->dec_firsttable); + + memset(b,0,sizeof(*b)); +} + +int vorbis_book_init_encode(codebook *c,const static_codebook *s){ + + memset(c,0,sizeof(*c)); + c->c=s; + c->entries=s->entries; + c->used_entries=s->entries; + c->dim=s->dim; + c->codelist=_make_words(s->lengthlist,s->entries,0); + c->valuelist=_book_unquantize(s,s->entries,NULL); + + return(0); +} + +static int sort32a(const void *a,const void *b){ + return ( **(ogg_uint32_t **)a>**(ogg_uint32_t **)b)- + ( **(ogg_uint32_t **)a<**(ogg_uint32_t **)b); +} + +/* decode codebook arrangement is more heavily optimized than encode */ +int vorbis_book_init_decode(codebook *c,const static_codebook *s){ + int i,j,n=0,tabn; + int *sortindex; + memset(c,0,sizeof(*c)); + + /* count actually used entries */ + for(i=0;ientries;i++) + if(s->lengthlist[i]>0) + n++; + + c->entries=s->entries; + c->used_entries=n; + c->dim=s->dim; + + /* two different remappings go on here. + + First, we collapse the likely sparse codebook down only to + actually represented values/words. This collapsing needs to be + indexed as map-valueless books are used to encode original entry + positions as integers. + + Second, we reorder all vectors, including the entry index above, + by sorted bitreversed codeword to allow treeless decode. */ + + { + /* perform sort */ + ogg_uint32_t *codes=_make_words(s->lengthlist,s->entries,c->used_entries); + ogg_uint32_t **codep=(ogg_uint32_t**)alloca(sizeof(*codep)*n); + + if(codes==NULL)goto err_out; + + for(i=0;icodelist=(ogg_uint32_t*)_ogg_malloc(n*sizeof(*c->codelist)); + /* the index is a reverse index */ + for(i=0;icodelist[sortindex[i]]=codes[i]; + _ogg_free(codes); + } + + c->valuelist=_book_unquantize(s,n,sortindex); + c->dec_index=(int*)_ogg_malloc(n*sizeof(*c->dec_index)); + + for(n=0,i=0;ientries;i++) + if(s->lengthlist[i]>0) + c->dec_index[sortindex[n++]]=i; + + c->dec_codelengths=(char*)_ogg_malloc(n*sizeof(*c->dec_codelengths)); + for(n=0,i=0;ientries;i++) + if(s->lengthlist[i]>0) + c->dec_codelengths[sortindex[n++]]=s->lengthlist[i]; + + c->dec_firsttablen=_ilog(c->used_entries)-4; /* this is magic */ + if(c->dec_firsttablen<5)c->dec_firsttablen=5; + if(c->dec_firsttablen>8)c->dec_firsttablen=8; + + tabn=1<dec_firsttablen; + c->dec_firsttable=(ogg_uint32_t*)_ogg_calloc(tabn,sizeof(*c->dec_firsttable)); + c->dec_maxlength=0; + + for(i=0;idec_maxlengthdec_codelengths[i]) + c->dec_maxlength=c->dec_codelengths[i]; + if(c->dec_codelengths[i]<=c->dec_firsttablen){ + ogg_uint32_t orig=bitreverse(c->codelist[i]); + for(j=0;j<(1<<(c->dec_firsttablen-c->dec_codelengths[i]));j++) + c->dec_firsttable[orig|(j<dec_codelengths[i])]=i+1; + } + } + + /* now fill in 'unused' entries in the firsttable with hi/lo search + hints for the non-direct-hits */ + { + ogg_uint32_t mask=0xfffffffeUL<<(31-c->dec_firsttablen); + long lo=0,hi=0; + + for(i=0;idec_firsttablen); + if(c->dec_firsttable[bitreverse(word)]==0){ + while((lo+1)codelist[lo+1]<=word)lo++; + while( hi=(c->codelist[hi]&mask))hi++; + + /* we only actually have 15 bits per hint to play with here. + In order to overflow gracefully (nothing breaks, efficiency + just drops), encode as the difference from the extremes. */ + { + unsigned long loval=lo; + unsigned long hival=n-hi; + + if(loval>0x7fff)loval=0x7fff; + if(hival>0x7fff)hival=0x7fff; + c->dec_firsttable[bitreverse(word)]= + 0x80000000UL | (loval<<15) | hival; + } + } + } + } + + return(0); + err_out: + vorbis_book_clear(c); + return(-1); +} + +static float _dist(int el,float *ref, float *b,int step){ + int i; + float acc=0.f; + for(i=0;ic->thresh_tree; + +#if 0 + encode_aux_nearestmatch *nt=book->c->nearest_tree; + encode_aux_pigeonhole *pt=book->c->pigeon_tree; +#endif + int dim=book->dim; + int k,o; + /*int savebest=-1; + float saverr;*/ + + /* do we have a threshhold encode hint? */ + if(tt){ + int index=0,i; + /* find the quant val of each scalar */ + for(k=0,o=step*(dim-1);kthreshvals>>1; + if(a[o]quantthresh[i]){ + + for(;i>0;i--) + if(a[o]>=tt->quantthresh[i-1]) + break; + + }else{ + + for(i++;ithreshvals-1;i++) + if(a[o]quantthresh[i])break; + + } + + index=(index*tt->quantvals)+tt->quantmap[i]; + } + /* regular lattices are easy :-) */ + if(book->c->lengthlist[index]>0) /* is this unused? If so, we'll + use a decision tree after all + and fall through*/ + return(index); + } + +#if 0 + /* do we have a pigeonhole encode hint? */ + if(pt){ + const static_codebook *c=book->c; + int i,besti=-1; + float best=0.f; + int entry=0; + + /* dealing with sequentialness is a pain in the ass */ + if(c->q_sequencep){ + int pv; + long mul=1; + float qlast=0; + for(k=0,o=0;kmin)/pt->del); + if(pv<0 || pv>=pt->mapentries)break; + entry+=pt->pigeonmap[pv]*mul; + mul*=pt->quantvals; + qlast+=pv*pt->del+pt->min; + } + }else{ + for(k=0,o=step*(dim-1);kmin)/pt->del); + if(pv<0 || pv>=pt->mapentries)break; + entry=entry*pt->quantvals+pt->pigeonmap[pv]; + } + } + + /* must be within the pigeonholable range; if we quant outside (or + in an entry that we define no list for), brute force it */ + if(k==dim && pt->fitlength[entry]){ + /* search the abbreviated list */ + long *list=pt->fitlist+pt->fitmap[entry]; + for(i=0;ifitlength[entry];i++){ + float this=_dist(dim,book->valuelist+list[i]*dim,a,step); + if(besti==-1 || thisvaluelist+nt->p[ptr]; + float *q=book->valuelist+nt->q[ptr]; + + for(k=0,o=0;k0.f) /* in A */ + ptr= -nt->ptr0[ptr]; + else /* in B */ + ptr= -nt->ptr1[ptr]; + if(ptr<=0)break; + } + return(-ptr); + } +#endif + + /* brute force it! */ + { + const static_codebook *c=book->c; + int i,besti=-1; + float best=0.f; + float *e=book->valuelist; + for(i=0;ientries;i++){ + if(c->lengthlist[i]>0){ + float thisx=_dist(dim,e,a,step); + if(besti==-1 || thisxvaluelist+savebest*dim)[i]); + fprintf(stderr,"\n" + "bruteforce (entry %d, err %g):",besti,best); + for(i=0;ivaluelist+besti*dim)[i]); + fprintf(stderr,"\n"); + }*/ + return(besti); + } +} + +long vorbis_book_codeword(codebook *book,int entry){ + if(book->c) /* only use with encode; decode optimizations are + allowed to break this */ + return book->codelist[entry]; + return -1; +} + +long vorbis_book_codelen(codebook *book,int entry){ + if(book->c) /* only use with encode; decode optimizations are + allowed to break this */ + return book->c->lengthlist[entry]; + return -1; +} + +#ifdef _V_SELFTEST + +/* Unit tests of the dequantizer; this stuff will be OK + cross-platform, I simply want to be sure that special mapping cases + actually work properly; a bug could go unnoticed for a while */ + +#include + +/* cases: + + no mapping + full, explicit mapping + algorithmic mapping + + nonsequential + sequential +*/ + +static long full_quantlist1[]={0,1,2,3, 4,5,6,7, 8,3,6,1}; +static long partial_quantlist1[]={0,7,2}; + +/* no mapping */ +static_codebook test1={ + 4,16, + NULL, + 0, + 0,0,0,0, + NULL, + NULL,NULL +}; +static float *test1_result=NULL; + +/* linear, full mapping, nonsequential */ +static_codebook test2={ + 4,3, + NULL, + 2, + -533200896,1611661312,4,0, + full_quantlist1, + NULL,NULL +}; +static float test2_result[]={-3,-2,-1,0, 1,2,3,4, 5,0,3,-2}; + +/* linear, full mapping, sequential */ +static_codebook test3={ + 4,3, + NULL, + 2, + -533200896,1611661312,4,1, + full_quantlist1, + NULL,NULL +}; +static float test3_result[]={-3,-5,-6,-6, 1,3,6,10, 5,5,8,6}; + +/* linear, algorithmic mapping, nonsequential */ +static_codebook test4={ + 3,27, + NULL, + 1, + -533200896,1611661312,4,0, + partial_quantlist1, + NULL,NULL +}; +static float test4_result[]={-3,-3,-3, 4,-3,-3, -1,-3,-3, + -3, 4,-3, 4, 4,-3, -1, 4,-3, + -3,-1,-3, 4,-1,-3, -1,-1,-3, + -3,-3, 4, 4,-3, 4, -1,-3, 4, + -3, 4, 4, 4, 4, 4, -1, 4, 4, + -3,-1, 4, 4,-1, 4, -1,-1, 4, + -3,-3,-1, 4,-3,-1, -1,-3,-1, + -3, 4,-1, 4, 4,-1, -1, 4,-1, + -3,-1,-1, 4,-1,-1, -1,-1,-1}; + +/* linear, algorithmic mapping, sequential */ +static_codebook test5={ + 3,27, + NULL, + 1, + -533200896,1611661312,4,1, + partial_quantlist1, + NULL,NULL +}; +static float test5_result[]={-3,-6,-9, 4, 1,-2, -1,-4,-7, + -3, 1,-2, 4, 8, 5, -1, 3, 0, + -3,-4,-7, 4, 3, 0, -1,-2,-5, + -3,-6,-2, 4, 1, 5, -1,-4, 0, + -3, 1, 5, 4, 8,12, -1, 3, 7, + -3,-4, 0, 4, 3, 7, -1,-2, 2, + -3,-6,-7, 4, 1, 0, -1,-4,-5, + -3, 1, 0, 4, 8, 7, -1, 3, 2, + -3,-4,-5, 4, 3, 2, -1,-2,-3}; + +void run_test(static_codebook *b,float *comp){ + float *out=_book_unquantize(b,b->entries,NULL); + int i; + + if(comp){ + if(!out){ + fprintf(stderr,"_book_unquantize incorrectly returned NULL\n"); + exit(1); + } + + for(i=0;ientries*b->dim;i++) + if(fabs(out[i]-comp[i])>.0001){ + fprintf(stderr,"disagreement in unquantized and reference data:\n" + "position %d, %g != %g\n",i,out[i],comp[i]); + exit(1); + } + + }else{ + if(out){ + fprintf(stderr,"_book_unquantize returned a value array: \n" + " correct result should have been NULL\n"); + exit(1); + } + } +} + +int main(){ + /* run the nine dequant tests, and compare to the hand-rolled results */ + fprintf(stderr,"Dequant test 1... "); + run_test(&test1,test1_result); + fprintf(stderr,"OK\nDequant test 2... "); + run_test(&test2,test2_result); + fprintf(stderr,"OK\nDequant test 3... "); + run_test(&test3,test3_result); + fprintf(stderr,"OK\nDequant test 4... "); + run_test(&test4,test4_result); + fprintf(stderr,"OK\nDequant test 5... "); + run_test(&test5,test5_result); + fprintf(stderr,"OK\n\n"); + + return(0); +} + +#endif + +#endif +/********* End of inlined file: sharedbook.c *********/ + +/********* Start of inlined file: smallft.c *********/ +/* FFT implementation from OggSquish, minus cosine transforms, + * minus all but radix 2/4 case. In Vorbis we only need this + * cut-down version. + * + * To do more than just power-of-two sized vectors, see the full + * version I wrote for NetLib. + * + * Note that the packing is a little strange; rather than the FFT r/i + * packing following R_0, I_n, R_1, I_1, R_2, I_2 ... R_n-1, I_n-1, + * it follows R_0, R_1, I_1, R_2, I_2 ... R_n-1, I_n-1, I_n like the + * FORTRAN version + */ + +/********* Start of inlined file: juce_OggVorbisHeader.h *********/ +// This file is included at the start of each Ogg-Vorbis .c file, just to do a few housekeeping +// tasks.. + +#ifdef _MSC_VER + #pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4305 4189 4706) +#endif +/********* End of inlined file: juce_OggVorbisHeader.h *********/ + +#if JUCE_USE_OGGVORBIS + +#include +#include +#include + +static void drfti1(int n, float *wa, int *ifac){ + static int ntryh[4] = { 4,2,3,5 }; + static float tpi = 6.28318530717958648f; + float arg,argh,argld,fi; + int ntry=0,i,j=-1; + int k1, l1, l2, ib; + int ld, ii, ip, is, nq, nr; + int ido, ipm, nfm1; + int nl=n; + int nf=0; + + L101: + j++; + if (j < 4) + ntry=ntryh[j]; + else + ntry+=2; + + L104: + nq=nl/ntry; + nr=nl-ntry*nq; + if (nr!=0) goto L101; + + nf++; + ifac[nf+1]=ntry; + nl=nq; + if(ntry!=2)goto L107; + if(nf==1)goto L107; + + for (i=1;i>1; + ipp2=ip; + idp2=ido; + nbd=(ido-1)>>1; + t0=l1*ido; + t10=ip*ido; + + if(ido==1)goto L119; + for(ik=0;ikl1){ + for(j=1;j>1; + ipp2=ip; + ipph=(ip+1)>>1; + if(idol1)goto L139; + + is= -ido-1; + t1=0; + for(j=1;jn==1)return; + drftf1(l->n,data,l->trigcache,l->trigcache+l->n,l->splitcache); +} + +void drft_backward(drft_lookup *l,float *data){ + if (l->n==1)return; + drftb1(l->n,data,l->trigcache,l->trigcache+l->n,l->splitcache); +} + +void drft_init(drft_lookup *l,int n){ + l->n=n; + l->trigcache=(float*)_ogg_calloc(3*n,sizeof(*l->trigcache)); + l->splitcache=(int*)_ogg_calloc(32,sizeof(*l->splitcache)); + fdrffti(n, l->trigcache, l->splitcache); +} + +void drft_clear(drft_lookup *l){ + if(l){ + if(l->trigcache)_ogg_free(l->trigcache); + if(l->splitcache)_ogg_free(l->splitcache); + memset(l,0,sizeof(*l)); + } +} + +#endif +/********* End of inlined file: smallft.c *********/ + +/********* Start of inlined file: synthesis.c *********/ + +/********* Start of inlined file: juce_OggVorbisHeader.h *********/ +// This file is included at the start of each Ogg-Vorbis .c file, just to do a few housekeeping +// tasks.. + +#ifdef _MSC_VER + #pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4305 4189 4706) +#endif +/********* End of inlined file: juce_OggVorbisHeader.h *********/ + +#if JUCE_USE_OGGVORBIS + +#include + +int vorbis_synthesis(vorbis_block *vb,ogg_packet *op){ + vorbis_dsp_state *vd=vb->vd; + private_state *b=(private_state*)vd->backend_state; + vorbis_info *vi=vd->vi; + codec_setup_info *ci=(codec_setup_info*) vi->codec_setup; + oggpack_buffer *opb=&vb->opb; + int type,mode,i; + + /* first things first. Make sure decode is ready */ + _vorbis_block_ripcord(vb); + oggpack_readinit(opb,op->packet,op->bytes); + + /* Check the packet type */ + if(oggpack_read(opb,1)!=0){ + /* Oops. This is not an audio data packet */ + return(OV_ENOTAUDIO); + } + + /* read our mode and pre/post windowsize */ + mode=oggpack_read(opb,b->modebits); + if(mode==-1)return(OV_EBADPACKET); + + vb->mode=mode; + vb->W=ci->mode_param[mode]->blockflag; + if(vb->W){ + + /* this doesn;t get mapped through mode selection as it's used + only for window selection */ + vb->lW=oggpack_read(opb,1); + vb->nW=oggpack_read(opb,1); + if(vb->nW==-1) return(OV_EBADPACKET); + }else{ + vb->lW=0; + vb->nW=0; + } + + /* more setup */ + vb->granulepos=op->granulepos; + vb->sequence=op->packetno; + vb->eofflag=op->e_o_s; + + /* alloc pcm passback storage */ + vb->pcmend=ci->blocksizes[vb->W]; + vb->pcm=(float**)_vorbis_block_alloc(vb,sizeof(*vb->pcm)*vi->channels); + for(i=0;ichannels;i++) + vb->pcm[i]=(float*)_vorbis_block_alloc(vb,vb->pcmend*sizeof(*vb->pcm[i])); + + /* unpack_header enforces range checking */ + type=ci->map_type[ci->mode_param[mode]->mapping]; + + return(_mapping_P[type]->inverse(vb,ci->map_param[ci->mode_param[mode]-> + mapping])); +} + +/* used to track pcm position without actually performing decode. + Useful for sequential 'fast forward' */ +int vorbis_synthesis_trackonly(vorbis_block *vb,ogg_packet *op){ + vorbis_dsp_state *vd=vb->vd; + private_state *b=(private_state*)vd->backend_state; + vorbis_info *vi=vd->vi; + codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; + oggpack_buffer *opb=&vb->opb; + int mode; + + /* first things first. Make sure decode is ready */ + _vorbis_block_ripcord(vb); + oggpack_readinit(opb,op->packet,op->bytes); + + /* Check the packet type */ + if(oggpack_read(opb,1)!=0){ + /* Oops. This is not an audio data packet */ + return(OV_ENOTAUDIO); + } + + /* read our mode and pre/post windowsize */ + mode=oggpack_read(opb,b->modebits); + if(mode==-1)return(OV_EBADPACKET); + + vb->mode=mode; + vb->W=ci->mode_param[mode]->blockflag; + if(vb->W){ + vb->lW=oggpack_read(opb,1); + vb->nW=oggpack_read(opb,1); + if(vb->nW==-1) return(OV_EBADPACKET); + }else{ + vb->lW=0; + vb->nW=0; + } + + /* more setup */ + vb->granulepos=op->granulepos; + vb->sequence=op->packetno; + vb->eofflag=op->e_o_s; + + /* no pcm */ + vb->pcmend=0; + vb->pcm=NULL; + + return(0); +} + +long vorbis_packet_blocksize(vorbis_info *vi,ogg_packet *op){ + codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; + oggpack_buffer opb; + int mode; + + oggpack_readinit(&opb,op->packet,op->bytes); + + /* Check the packet type */ + if(oggpack_read(&opb,1)!=0){ + /* Oops. This is not an audio data packet */ + return(OV_ENOTAUDIO); + } + + { + int modebits=0; + int v=ci->modes; + while(v>1){ + modebits++; + v>>=1; + } + + /* read our mode and pre/post windowsize */ + mode=oggpack_read(&opb,modebits); + } + if(mode==-1)return(OV_EBADPACKET); + return(ci->blocksizes[ci->mode_param[mode]->blockflag]); +} + +int vorbis_synthesis_halfrate(vorbis_info *vi,int flag){ + /* set / clear half-sample-rate mode */ + codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; + + /* right now, our MDCT can't handle < 64 sample windows. */ + if(ci->blocksizes[0]<=64 && flag)return -1; + ci->halfrate_flag=(flag?1:0); + return 0; +} + +int vorbis_synthesis_halfrate_p(vorbis_info *vi){ + codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; + return ci->halfrate_flag; +} + +#endif +/********* End of inlined file: synthesis.c *********/ + +/********* Start of inlined file: vorbisenc.c *********/ + +/********* Start of inlined file: juce_OggVorbisHeader.h *********/ +// This file is included at the start of each Ogg-Vorbis .c file, just to do a few housekeeping +// tasks.. + +#ifdef _MSC_VER + #pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4305 4189 4706) +#endif +/********* End of inlined file: juce_OggVorbisHeader.h *********/ + +#if JUCE_USE_OGGVORBIS + +#include +#include +#include + +/* careful with this; it's using static array sizing to make managing + all the modes a little less annoying. If we use a residue backend + with > 12 partition types, or a different division of iteration, + this needs to be updated. */ +typedef struct { + static_codebook *books[12][3]; +} static_bookblock; + +typedef struct { + int res_type; + int limit_type; /* 0 lowpass limited, 1 point stereo limited */ + vorbis_info_residue0 *res; + static_codebook *book_aux; + static_codebook *book_aux_managed; + static_bookblock *books_base; + static_bookblock *books_base_managed; +} vorbis_residue_template; + +typedef struct { + vorbis_info_mapping0 *map; + vorbis_residue_template *res; +} vorbis_mapping_template; + +typedef struct vp_adjblock{ + int block[P_BANDS]; +} vp_adjblock; + +typedef struct { + int data[NOISE_COMPAND_LEVELS]; +} compandblock; + +/* high level configuration information for setting things up + step-by-step with the detailed vorbis_encode_ctl interface. + There's a fair amount of redundancy such that interactive setup + does not directly deal with any vorbis_info or codec_setup_info + initialization; it's all stored (until full init) in this highlevel + setup, then flushed out to the real codec setup structs later. */ + +typedef struct { + int att[P_NOISECURVES]; + float boost; + float decay; +} att3; +typedef struct { int data[P_NOISECURVES]; } adj3; + +typedef struct { + int pre[PACKETBLOBS]; + int post[PACKETBLOBS]; + float kHz[PACKETBLOBS]; + float lowpasskHz[PACKETBLOBS]; +} adj_stereo; + +typedef struct { + int lo; + int hi; + int fixed; +} noiseguard; +typedef struct { + int data[P_NOISECURVES][17]; +} noise3; + +typedef struct { + int mappings; + double *rate_mapping; + double *quality_mapping; + int coupling_restriction; + long samplerate_min_restriction; + long samplerate_max_restriction; + + int *blocksize_short; + int *blocksize_long; + + att3 *psy_tone_masteratt; + int *psy_tone_0dB; + int *psy_tone_dBsuppress; + + vp_adjblock *psy_tone_adj_impulse; + vp_adjblock *psy_tone_adj_long; + vp_adjblock *psy_tone_adj_other; + + noiseguard *psy_noiseguards; + noise3 *psy_noise_bias_impulse; + noise3 *psy_noise_bias_padding; + noise3 *psy_noise_bias_trans; + noise3 *psy_noise_bias_long; + int *psy_noise_dBsuppress; + + compandblock *psy_noise_compand; + double *psy_noise_compand_short_mapping; + double *psy_noise_compand_long_mapping; + + int *psy_noise_normal_start[2]; + int *psy_noise_normal_partition[2]; + double *psy_noise_normal_thresh; + + int *psy_ath_float; + int *psy_ath_abs; + + double *psy_lowpass; + + vorbis_info_psy_global *global_params; + double *global_mapping; + adj_stereo *stereo_modes; + + static_codebook ***floor_books; + vorbis_info_floor1 *floor_params; + int *floor_short_mapping; + int *floor_long_mapping; + + vorbis_mapping_template *maps; +} ve_setup_data_template; + +/* a few static coder conventions */ +static vorbis_info_mode _mode_template[2]={ + {0,0,0,0}, + {1,0,0,1} +}; + +static vorbis_info_mapping0 _map_nominal[2]={ + {1, {0,0}, {0}, {0}, 1,{0},{1}}, + {1, {0,0}, {1}, {1}, 1,{0},{1}} +}; + +/********* Start of inlined file: setup_44.h *********/ + +/********* Start of inlined file: floor_all.h *********/ + +/********* Start of inlined file: floor_books.h *********/ + +static long _huff_lengthlist_line_256x7_0sub1[] = { + 0, 2, 3, 3, 3, 3, 4, 3, 4, +}; + +static static_codebook _huff_book_line_256x7_0sub1 = { + 1, 9, + _huff_lengthlist_line_256x7_0sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_256x7_0sub2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 3, 4, 3, 5, 3, + 6, 3, 6, 4, 6, 4, 7, 5, 7, +}; + +static static_codebook _huff_book_line_256x7_0sub2 = { + 1, 25, + _huff_lengthlist_line_256x7_0sub2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_256x7_0sub3[] = { + 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, 3, 5, 2, 5, 3, 5, 3, + 6, 3, 6, 4, 7, 6, 7, 8, 7, 9, 8, 9, 9, 9,10, 9, + 11,13,11,13,10,10,13,13,13,13,13,13,12,12,12,12, +}; + +static static_codebook _huff_book_line_256x7_0sub3 = { + 1, 64, + _huff_lengthlist_line_256x7_0sub3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_256x7_1sub1[] = { + 0, 3, 3, 3, 3, 2, 4, 3, 4, +}; + +static static_codebook _huff_book_line_256x7_1sub1 = { + 1, 9, + _huff_lengthlist_line_256x7_1sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_256x7_1sub2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 3, 4, 3, 4, 4, + 5, 4, 6, 5, 6, 7, 6, 8, 8, +}; + +static static_codebook _huff_book_line_256x7_1sub2 = { + 1, 25, + _huff_lengthlist_line_256x7_1sub2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_256x7_1sub3[] = { + 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, 3, 2, 4, 3, 6, 3, 7, + 3, 8, 5, 8, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, +}; + +static static_codebook _huff_book_line_256x7_1sub3 = { + 1, 64, + _huff_lengthlist_line_256x7_1sub3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_256x7_class0[] = { + 7, 5, 5, 9, 9, 6, 6, 9,12, 8, 7, 8,11, 8, 9,15, + 6, 3, 3, 7, 7, 4, 3, 6, 9, 6, 5, 6, 8, 6, 8,15, + 8, 5, 5, 9, 8, 5, 4, 6,10, 7, 5, 5,11, 8, 7,15, + 14,15,13,13,13,13, 8,11,15,10, 7, 6,11, 9,10,15, +}; + +static static_codebook _huff_book_line_256x7_class0 = { + 1, 64, + _huff_lengthlist_line_256x7_class0, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_256x7_class1[] = { + 5, 6, 8,15, 6, 9,10,15,10,11,12,15,15,15,15,15, + 4, 6, 7,15, 6, 7, 8,15, 9, 8, 9,15,15,15,15,15, + 6, 8, 9,15, 7, 7, 8,15,10, 9,10,15,15,15,15,15, + 15,13,15,15,15,10,11,15,15,13,13,15,15,15,15,15, + 4, 6, 7,15, 6, 8, 9,15,10,10,12,15,15,15,15,15, + 2, 5, 6,15, 5, 6, 7,15, 8, 6, 7,15,15,15,15,15, + 5, 6, 8,15, 5, 6, 7,15, 9, 6, 7,15,15,15,15,15, + 14,12,13,15,12,10,11,15,15,15,15,15,15,15,15,15, + 7, 8, 9,15, 9,10,10,15,15,14,14,15,15,15,15,15, + 5, 6, 7,15, 7, 8, 9,15,12, 9,10,15,15,15,15,15, + 7, 7, 9,15, 7, 7, 8,15,12, 8, 9,15,15,15,15,15, + 13,13,14,15,12,11,12,15,15,15,15,15,15,15,15,15, + 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, + 13,13,13,15,15,15,15,15,15,15,15,15,15,15,15,15, + 15,12,13,15,15,12,13,15,15,14,15,15,15,15,15,15, + 15,15,15,15,15,15,13,15,15,15,15,15,15,15,15,15, +}; + +static static_codebook _huff_book_line_256x7_class1 = { + 1, 256, + _huff_lengthlist_line_256x7_class1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_512x17_0sub0[] = { + 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 6, 5, 6, 6, 6, 6, 5, 6, 6, 7, 6, 7, 6, 7, 6, + 7, 6, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 9, 7, 9, 7, + 9, 7, 9, 8, 9, 8,10, 8,10, 8,10, 7,10, 6,10, 8, + 10, 8,11, 7,10, 7,11, 8,11,11,12,12,11,11,12,11, + 13,11,13,11,13,12,15,12,13,13,14,14,14,14,14,15, + 15,15,16,14,17,19,19,18,18,18,18,18,18,18,18,18, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +}; + +static static_codebook _huff_book_line_512x17_0sub0 = { + 1, 128, + _huff_lengthlist_line_512x17_0sub0, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_512x17_1sub0[] = { + 2, 4, 5, 4, 5, 4, 5, 4, 5, 5, 5, 5, 5, 5, 6, 5, + 6, 5, 6, 6, 7, 6, 7, 6, 8, 7, 8, 7, 8, 7, 8, 7, +}; + +static static_codebook _huff_book_line_512x17_1sub0 = { + 1, 32, + _huff_lengthlist_line_512x17_1sub0, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_512x17_1sub1[] = { + 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, + 4, 3, 5, 3, 5, 4, 5, 4, 5, 4, 5, 5, 5, 5, 6, 5, + 6, 5, 7, 5, 8, 6, 8, 6, 8, 6, 8, 6, 8, 7, 9, 7, + 9, 7,11, 9,11,11,12,11,14,12,14,16,14,16,13,16, + 14,16,12,15,13,16,14,16,13,14,12,15,13,15,13,13, + 13,15,12,14,14,15,13,15,12,15,15,15,15,15,15,15, + 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, +}; + +static static_codebook _huff_book_line_512x17_1sub1 = { + 1, 128, + _huff_lengthlist_line_512x17_1sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_512x17_2sub1[] = { + 0, 4, 5, 4, 4, 4, 5, 4, 4, 4, 5, 4, 5, 4, 5, 3, + 5, 3, +}; + +static static_codebook _huff_book_line_512x17_2sub1 = { + 1, 18, + _huff_lengthlist_line_512x17_2sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_512x17_2sub2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 3, 4, 3, 4, 4, 5, 4, 5, 4, 6, 4, 6, 5, + 6, 5, 7, 5, 7, 6, 8, 6, 8, 6, 8, 7, 8, 7, 9, 7, + 9, 8, +}; + +static static_codebook _huff_book_line_512x17_2sub2 = { + 1, 50, + _huff_lengthlist_line_512x17_2sub2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_512x17_2sub3[] = { + 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, 3, 3, 3, 3, 4, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 7, 8, 8,11, 8, 9, 9, 9,10,11,11,11, 9,10,10,11, + 11,11,11,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, +}; + +static static_codebook _huff_book_line_512x17_2sub3 = { + 1, 128, + _huff_lengthlist_line_512x17_2sub3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_512x17_3sub1[] = { + 0, 4, 4, 4, 4, 4, 4, 3, 4, 4, 4, 4, 4, 5, 4, 5, + 5, 5, +}; + +static static_codebook _huff_book_line_512x17_3sub1 = { + 1, 18, + _huff_lengthlist_line_512x17_3sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_512x17_3sub2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2, 3, 3, 4, 3, 5, 4, 6, 4, 6, 5, 7, 6, 7, + 6, 8, 6, 8, 7, 9, 8,10, 8,12, 9,13,10,15,10,15, + 11,14, +}; + +static static_codebook _huff_book_line_512x17_3sub2 = { + 1, 50, + _huff_lengthlist_line_512x17_3sub2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_512x17_3sub3[] = { + 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, 4, 8, 4, 8, 4, 8, 4, 8, 5, 8, 5, 8, 6, 8, + 4, 8, 4, 8, 5, 8, 5, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, +}; + +static static_codebook _huff_book_line_512x17_3sub3 = { + 1, 128, + _huff_lengthlist_line_512x17_3sub3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_512x17_class1[] = { + 1, 2, 3, 6, 5, 4, 7, 7, +}; + +static static_codebook _huff_book_line_512x17_class1 = { + 1, 8, + _huff_lengthlist_line_512x17_class1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_512x17_class2[] = { + 3, 3, 3,14, 5, 4, 4,11, 8, 6, 6,10,17,12,11,17, + 6, 5, 5,15, 5, 3, 4,11, 8, 5, 5, 8,16, 9,10,14, + 10, 8, 9,17, 8, 6, 6,13,10, 7, 7,10,16,11,13,14, + 17,17,17,17,17,16,16,16,16,15,16,16,16,16,16,16, +}; + +static static_codebook _huff_book_line_512x17_class2 = { + 1, 64, + _huff_lengthlist_line_512x17_class2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_512x17_class3[] = { + 2, 4, 6,17, 4, 5, 7,17, 8, 7,10,17,17,17,17,17, + 3, 4, 6,15, 3, 3, 6,15, 7, 6, 9,17,17,17,17,17, + 6, 8,10,17, 6, 6, 8,16, 9, 8,10,17,17,15,16,17, + 17,17,17,17,12,15,15,16,12,15,15,16,16,16,16,16, +}; + +static static_codebook _huff_book_line_512x17_class3 = { + 1, 64, + _huff_lengthlist_line_512x17_class3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x4_class0[] = { + 7, 7, 7,11, 6, 6, 7,11, 7, 6, 6,10,12,10,10,13, + 7, 7, 8,11, 7, 7, 7,11, 7, 6, 7,10,11,10,10,13, + 10,10, 9,12, 9, 9, 9,11, 8, 8, 8,11,13,11,10,14, + 15,15,14,15,15,14,13,14,15,12,12,17,17,17,17,17, + 7, 7, 6, 9, 6, 6, 6, 9, 7, 6, 6, 8,11,11,10,12, + 7, 7, 7, 9, 7, 6, 6, 9, 7, 6, 6, 9,13,10,10,11, + 10, 9, 8,10, 9, 8, 8,10, 8, 8, 7, 9,13,12,10,11, + 17,14,14,13,15,14,12,13,17,13,12,15,17,17,14,17, + 7, 6, 6, 7, 6, 6, 5, 7, 6, 6, 6, 6,11, 9, 9, 9, + 7, 7, 6, 7, 7, 6, 6, 7, 6, 6, 6, 6,10, 9, 8, 9, + 10, 9, 8, 8, 9, 8, 7, 8, 8, 7, 6, 8,11,10, 9,10, + 17,17,12,15,15,15,12,14,14,14,10,12,15,13,12,13, + 11,10, 8,10,11,10, 8, 8,10, 9, 7, 7,10, 9, 9,11, + 11,11, 9,10,11,10, 8, 9,10, 8, 6, 8,10, 9, 9,11, + 14,13,10,12,12,11,10,10, 8, 7, 8,10,10,11,11,12, + 17,17,15,17,17,17,17,17,17,13,12,17,17,17,14,17, +}; + +static static_codebook _huff_book_line_128x4_class0 = { + 1, 256, + _huff_lengthlist_line_128x4_class0, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x4_0sub0[] = { + 2, 2, 2, 2, +}; + +static static_codebook _huff_book_line_128x4_0sub0 = { + 1, 4, + _huff_lengthlist_line_128x4_0sub0, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x4_0sub1[] = { + 0, 0, 0, 0, 3, 2, 3, 2, 3, 3, +}; + +static static_codebook _huff_book_line_128x4_0sub1 = { + 1, 10, + _huff_lengthlist_line_128x4_0sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x4_0sub2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 4, 3, 4, 3, + 4, 4, 5, 4, 5, 4, 6, 5, 6, +}; + +static static_codebook _huff_book_line_128x4_0sub2 = { + 1, 25, + _huff_lengthlist_line_128x4_0sub2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x4_0sub3[] = { + 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, 4, 3, 5, 3, 5, 3, + 5, 4, 6, 5, 6, 5, 7, 6, 6, 7, 7, 9, 9,11,11,16, + 11,14,10,11,11,13,16,15,15,15,15,15,15,15,15,15, +}; + +static static_codebook _huff_book_line_128x4_0sub3 = { + 1, 64, + _huff_lengthlist_line_128x4_0sub3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_256x4_class0[] = { + 6, 7, 7,12, 6, 6, 7,12, 7, 6, 6,10,15,12,11,13, + 7, 7, 8,13, 7, 7, 8,12, 7, 7, 7,11,12,12,11,13, + 10, 9, 9,11, 9, 9, 9,10,10, 8, 8,12,14,12,12,14, + 11,11,12,14,11,12,11,15,15,12,13,15,15,15,15,15, + 6, 6, 7,10, 6, 6, 6,11, 7, 6, 6, 9,14,12,11,13, + 7, 7, 7,10, 6, 6, 7, 9, 7, 7, 6,10,13,12,10,12, + 9, 9, 9,11, 9, 9, 8, 9, 9, 8, 8,10,13,12,10,12, + 12,12,11,13,12,12,11,12,15,13,12,15,15,15,14,14, + 6, 6, 6, 8, 6, 6, 5, 6, 7, 7, 6, 5,11,10, 9, 8, + 7, 6, 6, 7, 6, 6, 5, 6, 7, 7, 6, 6,11,10, 9, 8, + 8, 8, 8, 9, 8, 8, 7, 8, 8, 8, 6, 7,11,10, 9, 9, + 14,11,10,14,14,11,10,15,13,11, 9,11,15,12,12,11, + 11, 9, 8, 8,10, 9, 8, 9,11,10, 9, 8,12,11,12,11, + 13,10, 8, 9,11,10, 8, 9,10, 9, 8, 9,10, 8,12,12, + 15,11,10,10,13,11,10,10, 8, 8, 7,12,10, 9,11,12, + 15,12,11,15,13,11,11,15,12,14,11,13,15,15,13,13, +}; + +static static_codebook _huff_book_line_256x4_class0 = { + 1, 256, + _huff_lengthlist_line_256x4_class0, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_256x4_0sub0[] = { + 2, 2, 2, 2, +}; + +static static_codebook _huff_book_line_256x4_0sub0 = { + 1, 4, + _huff_lengthlist_line_256x4_0sub0, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_256x4_0sub1[] = { + 0, 0, 0, 0, 2, 2, 3, 3, 3, 3, +}; + +static static_codebook _huff_book_line_256x4_0sub1 = { + 1, 10, + _huff_lengthlist_line_256x4_0sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_256x4_0sub2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 3, 4, 3, 4, 3, + 5, 3, 5, 4, 5, 4, 6, 4, 6, +}; + +static static_codebook _huff_book_line_256x4_0sub2 = { + 1, 25, + _huff_lengthlist_line_256x4_0sub2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_256x4_0sub3[] = { + 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, 4, 3, 5, 3, 5, 3, + 6, 4, 7, 4, 7, 5, 7, 6, 7, 6, 7, 8,10,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,12,12,12,12,12, +}; + +static static_codebook _huff_book_line_256x4_0sub3 = { + 1, 64, + _huff_lengthlist_line_256x4_0sub3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x7_class0[] = { + 10, 7, 8,13, 9, 6, 7,11,10, 8, 8,12,17,17,17,17, + 7, 5, 5, 9, 6, 4, 4, 8, 8, 5, 5, 8,16,14,13,16, + 7, 5, 5, 7, 6, 3, 3, 5, 8, 5, 4, 7,14,12,12,15, + 10, 7, 8, 9, 7, 5, 5, 6, 9, 6, 5, 5,15,12, 9,10, +}; + +static static_codebook _huff_book_line_128x7_class0 = { + 1, 64, + _huff_lengthlist_line_128x7_class0, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x7_class1[] = { + 8,13,17,17, 8,11,17,17,11,13,17,17,17,17,17,17, + 6,10,16,17, 6,10,15,17, 8,10,16,17,17,17,17,17, + 9,13,15,17, 8,11,17,17,10,12,17,17,17,17,17,17, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 6,11,15,17, 7,10,15,17, 8,10,17,17,17,15,17,17, + 4, 8,13,17, 4, 7,13,17, 6, 8,15,17,16,15,17,17, + 6,11,15,17, 6, 9,13,17, 8,10,17,17,15,17,17,17, + 16,17,17,17,12,14,15,17,13,14,15,17,17,17,17,17, + 5,10,14,17, 5, 9,14,17, 7, 9,15,17,15,15,17,17, + 3, 7,12,17, 3, 6,11,17, 5, 7,13,17,12,12,17,17, + 5, 9,14,17, 3, 7,11,17, 5, 8,13,17,13,11,16,17, + 12,17,17,17, 9,14,15,17,10,11,14,17,16,14,17,17, + 8,12,17,17, 8,12,17,17,10,12,17,17,17,17,17,17, + 5,10,17,17, 5, 9,15,17, 7, 9,17,17,13,13,17,17, + 7,11,17,17, 6,10,15,17, 7, 9,15,17,12,11,17,17, + 12,15,17,17,11,14,17,17,11,10,15,17,17,16,17,17, +}; + +static static_codebook _huff_book_line_128x7_class1 = { + 1, 256, + _huff_lengthlist_line_128x7_class1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x7_0sub1[] = { + 0, 3, 3, 3, 3, 3, 3, 3, 3, +}; + +static static_codebook _huff_book_line_128x7_0sub1 = { + 1, 9, + _huff_lengthlist_line_128x7_0sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x7_0sub2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 4, 4, 4, 4, + 5, 4, 5, 4, 5, 4, 6, 4, 6, +}; + +static static_codebook _huff_book_line_128x7_0sub2 = { + 1, 25, + _huff_lengthlist_line_128x7_0sub2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x7_0sub3[] = { + 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, 3, 5, 3, 5, 3, 5, 4, + 5, 4, 5, 5, 5, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, + 7, 8, 9,11,13,13,13,13,13,13,13,13,13,13,13,13, +}; + +static static_codebook _huff_book_line_128x7_0sub3 = { + 1, 64, + _huff_lengthlist_line_128x7_0sub3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x7_1sub1[] = { + 0, 3, 3, 2, 3, 3, 4, 3, 4, +}; + +static static_codebook _huff_book_line_128x7_1sub1 = { + 1, 9, + _huff_lengthlist_line_128x7_1sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x7_1sub2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 3, 6, 3, 6, 3, + 6, 3, 7, 3, 8, 4, 9, 4, 9, +}; + +static static_codebook _huff_book_line_128x7_1sub2 = { + 1, 25, + _huff_lengthlist_line_128x7_1sub2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x7_1sub3[] = { + 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, 7, 2, 7, 3, 8, 4, + 9, 5, 9, 8,10,11,11,12,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,14,14,14,14,13,13,13,13, +}; + +static static_codebook _huff_book_line_128x7_1sub3 = { + 1, 64, + _huff_lengthlist_line_128x7_1sub3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x11_class1[] = { + 1, 6, 3, 7, 2, 4, 5, 7, +}; + +static static_codebook _huff_book_line_128x11_class1 = { + 1, 8, + _huff_lengthlist_line_128x11_class1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x11_class2[] = { + 1, 6,12,16, 4,12,15,16, 9,15,16,16,16,16,16,16, + 2, 5,11,16, 5,11,13,16, 9,13,16,16,16,16,16,16, + 4, 8,12,16, 5, 9,12,16, 9,13,15,16,16,16,16,16, + 15,16,16,16,11,14,13,16,12,15,16,16,16,16,16,15, +}; + +static static_codebook _huff_book_line_128x11_class2 = { + 1, 64, + _huff_lengthlist_line_128x11_class2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x11_class3[] = { + 7, 6, 9,17, 7, 6, 8,17,12, 9,11,16,16,16,16,16, + 5, 4, 7,16, 5, 3, 6,14, 9, 6, 8,15,16,16,16,16, + 5, 4, 6,13, 3, 2, 4,11, 7, 4, 6,13,16,11,10,14, + 12,12,12,16, 9, 7,10,15,12, 9,11,16,16,15,15,16, +}; + +static static_codebook _huff_book_line_128x11_class3 = { + 1, 64, + _huff_lengthlist_line_128x11_class3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x11_0sub0[] = { + 5, 5, 5, 5, 5, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, + 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 6, 6, 6, 7, 6, + 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 8, 6, 8, 6, 8, 7, + 8, 7, 8, 7, 8, 7, 9, 7, 9, 8, 9, 8, 9, 8,10, 8, + 10, 9,10, 9,10, 9,11, 9,11, 9,10,10,11,10,11,10, + 11,11,11,11,11,11,12,13,14,14,14,15,15,16,16,16, + 17,15,16,15,16,16,17,17,16,17,17,17,17,17,17,17, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +}; + +static static_codebook _huff_book_line_128x11_0sub0 = { + 1, 128, + _huff_lengthlist_line_128x11_0sub0, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x11_1sub0[] = { + 2, 5, 5, 5, 5, 5, 5, 4, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 5, 6, 5, 6, 5, 7, 6, 7, 6, 7, 6, 8, 6, 8, 6, +}; + +static static_codebook _huff_book_line_128x11_1sub0 = { + 1, 32, + _huff_lengthlist_line_128x11_1sub0, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x11_1sub1[] = { + 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, + 5, 3, 5, 3, 6, 4, 6, 4, 7, 4, 7, 4, 7, 4, 8, 4, + 8, 4, 9, 5, 9, 5, 9, 5, 9, 6,10, 6,10, 6,11, 7, + 10, 7,10, 8,11, 9,11, 9,11,10,11,11,12,11,11,12, + 15,15,12,14,11,14,12,14,11,14,13,14,12,14,11,14, + 11,14,12,14,11,14,11,14,13,13,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, +}; + +static static_codebook _huff_book_line_128x11_1sub1 = { + 1, 128, + _huff_lengthlist_line_128x11_1sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x11_2sub1[] = { + 0, 4, 5, 4, 5, 4, 5, 3, 5, 3, 5, 3, 5, 4, 4, 4, + 5, 5, +}; + +static static_codebook _huff_book_line_128x11_2sub1 = { + 1, 18, + _huff_lengthlist_line_128x11_2sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x11_2sub2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3, 3, 3, 4, 4, 4, 4, 5, 4, 5, 4, 6, 5, 7, + 5, 7, 6, 8, 6, 8, 6, 9, 7, 9, 7,10, 7, 9, 8,11, + 8,11, +}; + +static static_codebook _huff_book_line_128x11_2sub2 = { + 1, 50, + _huff_lengthlist_line_128x11_2sub2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x11_2sub3[] = { + 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, 4, 8, 3, 8, 4, 8, 4, 8, 6, 8, 5, 8, 4, 8, + 4, 8, 6, 8, 7, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, +}; + +static static_codebook _huff_book_line_128x11_2sub3 = { + 1, 128, + _huff_lengthlist_line_128x11_2sub3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x11_3sub1[] = { + 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 4, + 5, 4, +}; + +static static_codebook _huff_book_line_128x11_3sub1 = { + 1, 18, + _huff_lengthlist_line_128x11_3sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x11_3sub2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 3, 5, 4, 6, 4, 6, 4, 7, 4, 7, 4, 8, 4, + 8, 4, 9, 4, 9, 4,10, 4,10, 5,10, 5,11, 5,12, 6, + 12, 6, +}; + +static static_codebook _huff_book_line_128x11_3sub2 = { + 1, 50, + _huff_lengthlist_line_128x11_3sub2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x11_3sub3[] = { + 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, 7, 1, 6, 3, 7, 3, 8, 4, 8, 5, 8, 8, 8, 9, + 7, 8, 8, 7, 7, 7, 8, 9,10, 9, 9,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10, 9, 9, +}; + +static static_codebook _huff_book_line_128x11_3sub3 = { + 1, 128, + _huff_lengthlist_line_128x11_3sub3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x17_class1[] = { + 1, 3, 4, 7, 2, 5, 6, 7, +}; + +static static_codebook _huff_book_line_128x17_class1 = { + 1, 8, + _huff_lengthlist_line_128x17_class1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x17_class2[] = { + 1, 4,10,19, 3, 8,13,19, 7,12,19,19,19,19,19,19, + 2, 6,11,19, 8,13,19,19, 9,11,19,19,19,19,19,19, + 6, 7,13,19, 9,13,19,19,10,13,18,18,18,18,18,18, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +}; + +static static_codebook _huff_book_line_128x17_class2 = { + 1, 64, + _huff_lengthlist_line_128x17_class2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x17_class3[] = { + 3, 6,10,17, 4, 8,11,20, 8,10,11,20,20,20,20,20, + 2, 4, 8,18, 4, 6, 8,17, 7, 8,10,20,20,17,20,20, + 3, 5, 8,17, 3, 4, 6,17, 8, 8,10,17,17,12,16,20, + 13,13,15,20,10,10,12,20,15,14,15,20,20,20,19,19, +}; + +static static_codebook _huff_book_line_128x17_class3 = { + 1, 64, + _huff_lengthlist_line_128x17_class3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x17_0sub0[] = { + 5, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, + 7, 5, 7, 5, 7, 5, 7, 5, 7, 5, 7, 5, 8, 5, 8, 5, + 8, 5, 8, 5, 8, 6, 8, 6, 8, 6, 9, 6, 9, 6, 9, 6, + 9, 6, 9, 7, 9, 7, 9, 7, 9, 7,10, 7,10, 8,10, 8, + 10, 8,10, 8,10, 8,11, 8,11, 8,11, 8,11, 8,11, 9, + 12, 9,12, 9,12, 9,12, 9,12,10,12,10,13,11,13,11, + 14,12,14,13,15,14,16,14,17,15,18,16,20,20,20,20, + 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, +}; + +static static_codebook _huff_book_line_128x17_0sub0 = { + 1, 128, + _huff_lengthlist_line_128x17_0sub0, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x17_1sub0[] = { + 2, 5, 5, 4, 5, 4, 5, 4, 5, 5, 5, 5, 5, 5, 6, 5, + 6, 5, 6, 5, 7, 6, 7, 6, 7, 6, 8, 6, 9, 7, 9, 7, +}; + +static static_codebook _huff_book_line_128x17_1sub0 = { + 1, 32, + _huff_lengthlist_line_128x17_1sub0, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x17_1sub1[] = { + 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, + 4, 3, 5, 3, 5, 3, 6, 3, 6, 4, 6, 4, 7, 4, 7, 5, + 8, 5, 8, 6, 9, 7, 9, 7, 9, 8,10, 9,10, 9,11,10, + 11,11,11,11,11,11,12,12,12,13,12,13,12,14,12,15, + 12,14,12,16,13,17,13,17,14,17,14,16,13,17,14,17, + 14,17,15,17,15,15,16,17,17,17,17,17,17,17,17,17, + 17,17,17,17,17,17,16,16,16,16,16,16,16,16,16,16, +}; + +static static_codebook _huff_book_line_128x17_1sub1 = { + 1, 128, + _huff_lengthlist_line_128x17_1sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x17_2sub1[] = { + 0, 4, 5, 4, 6, 4, 8, 3, 9, 3, 9, 2, 9, 3, 8, 4, + 9, 4, +}; + +static static_codebook _huff_book_line_128x17_2sub1 = { + 1, 18, + _huff_lengthlist_line_128x17_2sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x17_2sub2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 1, 5, 3, 5, 3, 5, 4, 7, 5,10, 7,10, 7, + 12,10,14,10,14, 9,14,11,14,14,14,13,13,13,13,13, + 13,13, +}; + +static static_codebook _huff_book_line_128x17_2sub2 = { + 1, 50, + _huff_lengthlist_line_128x17_2sub2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x17_2sub3[] = { + 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, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, +}; + +static static_codebook _huff_book_line_128x17_2sub3 = { + 1, 128, + _huff_lengthlist_line_128x17_2sub3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x17_3sub1[] = { + 0, 4, 4, 4, 4, 4, 4, 4, 5, 3, 5, 3, 5, 4, 6, 4, + 6, 4, +}; + +static static_codebook _huff_book_line_128x17_3sub1 = { + 1, 18, + _huff_lengthlist_line_128x17_3sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x17_3sub2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 3, 6, 3, 6, 4, 7, 4, 7, 4, 7, 4, 8, 4, + 8, 4, 8, 4, 8, 4, 9, 4, 9, 5,10, 5,10, 7,10, 8, + 10, 8, +}; + +static static_codebook _huff_book_line_128x17_3sub2 = { + 1, 50, + _huff_lengthlist_line_128x17_3sub2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x17_3sub3[] = { + 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, 3, 2, 4, 3, 4, 4, 4, 5, 4, 7, 5, 8, 5,11, + 6,10, 6,12, 7,12, 7,12, 8,12, 8,12,10,12,12,12, + 12,12,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, +}; + +static static_codebook _huff_book_line_128x17_3sub3 = { + 1, 128, + _huff_lengthlist_line_128x17_3sub3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_1024x27_class1[] = { + 2,10, 8,14, 7,12,11,14, 1, 5, 3, 7, 4, 9, 7,13, +}; + +static static_codebook _huff_book_line_1024x27_class1 = { + 1, 16, + _huff_lengthlist_line_1024x27_class1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_1024x27_class2[] = { + 1, 4, 2, 6, 3, 7, 5, 7, +}; + +static static_codebook _huff_book_line_1024x27_class2 = { + 1, 8, + _huff_lengthlist_line_1024x27_class2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_1024x27_class3[] = { + 1, 5, 7,21, 5, 8, 9,21,10, 9,12,20,20,16,20,20, + 4, 8, 9,20, 6, 8, 9,20,11,11,13,20,20,15,17,20, + 9,11,14,20, 8,10,15,20,11,13,15,20,20,20,20,20, + 20,20,20,20,13,20,20,20,18,18,20,20,20,20,20,20, + 3, 6, 8,20, 6, 7, 9,20,10, 9,12,20,20,20,20,20, + 5, 7, 9,20, 6, 6, 9,20,10, 9,12,20,20,20,20,20, + 8,10,13,20, 8, 9,12,20,11,10,12,20,20,20,20,20, + 18,20,20,20,15,17,18,20,18,17,18,20,20,20,20,20, + 7,10,12,20, 8, 9,11,20,14,13,14,20,20,20,20,20, + 6, 9,12,20, 7, 8,11,20,12,11,13,20,20,20,20,20, + 9,11,15,20, 8,10,14,20,12,11,14,20,20,20,20,20, + 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, + 11,16,18,20,15,15,17,20,20,17,20,20,20,20,20,20, + 9,14,16,20,12,12,15,20,17,15,18,20,20,20,20,20, + 16,19,18,20,15,16,20,20,17,17,20,20,20,20,20,20, + 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, +}; + +static static_codebook _huff_book_line_1024x27_class3 = { + 1, 256, + _huff_lengthlist_line_1024x27_class3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_1024x27_class4[] = { + 2, 3, 7,13, 4, 4, 7,15, 8, 6, 9,17,21,16,15,21, + 2, 5, 7,11, 5, 5, 7,14, 9, 7,10,16,17,15,16,21, + 4, 7,10,17, 7, 7, 9,15,11, 9,11,16,21,18,15,21, + 18,21,21,21,15,17,17,19,21,19,18,20,21,21,21,20, +}; + +static static_codebook _huff_book_line_1024x27_class4 = { + 1, 64, + _huff_lengthlist_line_1024x27_class4, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_1024x27_0sub0[] = { + 5, 5, 5, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, + 6, 5, 6, 5, 6, 5, 6, 5, 7, 5, 7, 5, 7, 5, 7, 5, + 8, 6, 8, 6, 8, 6, 9, 6, 9, 6,10, 6,10, 6,11, 6, + 11, 7,11, 7,12, 7,12, 7,12, 7,12, 7,12, 7,12, 7, + 12, 7,12, 8,13, 8,12, 8,12, 8,13, 8,13, 9,13, 9, + 13, 9,13, 9,12,10,12,10,13,10,14,11,14,12,14,13, + 14,13,14,14,15,16,15,15,15,14,15,17,21,22,22,21, + 22,22,22,22,22,22,21,21,21,21,21,21,21,21,21,21, +}; + +static static_codebook _huff_book_line_1024x27_0sub0 = { + 1, 128, + _huff_lengthlist_line_1024x27_0sub0, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_1024x27_1sub0[] = { + 2, 5, 5, 4, 5, 4, 5, 4, 5, 4, 6, 5, 6, 5, 6, 5, + 6, 5, 7, 5, 7, 6, 8, 6, 8, 6, 8, 6, 9, 6, 9, 6, +}; + +static static_codebook _huff_book_line_1024x27_1sub0 = { + 1, 32, + _huff_lengthlist_line_1024x27_1sub0, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_1024x27_1sub1[] = { + 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, + 8, 5, 8, 4, 9, 4, 9, 4, 9, 4, 9, 4, 9, 4, 9, 4, + 9, 4, 9, 4, 9, 4, 8, 4, 8, 4, 9, 5, 9, 5, 9, 5, + 9, 5, 9, 6,10, 6,10, 7,10, 8,11, 9,11,11,12,13, + 12,14,13,15,13,15,14,16,14,17,15,17,15,15,16,16, + 15,16,16,16,15,18,16,15,17,17,19,19,19,19,19,19, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, +}; + +static static_codebook _huff_book_line_1024x27_1sub1 = { + 1, 128, + _huff_lengthlist_line_1024x27_1sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_1024x27_2sub0[] = { + 1, 5, 5, 5, 5, 5, 5, 5, 6, 5, 6, 5, 6, 5, 6, 5, + 6, 6, 7, 7, 7, 7, 8, 7, 8, 8, 9, 8,10, 9,10, 9, +}; + +static static_codebook _huff_book_line_1024x27_2sub0 = { + 1, 32, + _huff_lengthlist_line_1024x27_2sub0, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_1024x27_2sub1[] = { + 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, + 4, 3, 4, 3, 4, 4, 5, 4, 5, 4, 5, 5, 6, 5, 6, 5, + 7, 5, 7, 6, 7, 6, 8, 7, 8, 7, 8, 7, 9, 8, 9, 9, + 9, 9,10,10,10,11, 9,12, 9,12, 9,15,10,14, 9,13, + 10,13,10,12,10,12,10,13,10,12,11,13,11,14,12,13, + 13,14,14,13,14,15,14,16,13,13,14,16,16,16,16,16, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,15,15, +}; + +static static_codebook _huff_book_line_1024x27_2sub1 = { + 1, 128, + _huff_lengthlist_line_1024x27_2sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_1024x27_3sub1[] = { + 0, 4, 5, 4, 5, 3, 5, 3, 5, 3, 5, 4, 4, 4, 4, 5, + 5, 5, +}; + +static static_codebook _huff_book_line_1024x27_3sub1 = { + 1, 18, + _huff_lengthlist_line_1024x27_3sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_1024x27_3sub2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3, 3, 4, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, + 5, 7, 5, 8, 6, 8, 6, 9, 7,10, 7,10, 8,10, 8,11, + 9,11, +}; + +static static_codebook _huff_book_line_1024x27_3sub2 = { + 1, 50, + _huff_lengthlist_line_1024x27_3sub2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_1024x27_3sub3[] = { + 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, 3, 7, 3, 8, 3,10, 3, 8, 3, 9, 3, 8, 4, 9, + 4, 9, 5, 9, 6,10, 6, 9, 7,11, 7,12, 9,13,10,13, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +}; + +static static_codebook _huff_book_line_1024x27_3sub3 = { + 1, 128, + _huff_lengthlist_line_1024x27_3sub3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_1024x27_4sub1[] = { + 0, 4, 5, 4, 5, 4, 5, 4, 5, 3, 5, 3, 5, 3, 5, 4, + 5, 4, +}; + +static static_codebook _huff_book_line_1024x27_4sub1 = { + 1, 18, + _huff_lengthlist_line_1024x27_4sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_1024x27_4sub2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 2, 4, 2, 5, 3, 5, 4, 6, 6, 6, 7, 7, 8, + 7, 8, 7, 8, 7, 9, 8, 9, 8, 9, 8,10, 8,11, 9,12, + 9,12, +}; + +static static_codebook _huff_book_line_1024x27_4sub2 = { + 1, 50, + _huff_lengthlist_line_1024x27_4sub2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_1024x27_4sub3[] = { + 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, 2, 5, 2, 6, 3, 6, 4, 7, 4, 7, 5, 9, 5,11, + 6,11, 6,11, 7,11, 6,11, 6,11, 9,11, 8,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,10,10,10,10,10,10, +}; + +static static_codebook _huff_book_line_1024x27_4sub3 = { + 1, 128, + _huff_lengthlist_line_1024x27_4sub3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_2048x27_class1[] = { + 2, 6, 8, 9, 7,11,13,13, 1, 3, 5, 5, 6, 6,12,10, +}; + +static static_codebook _huff_book_line_2048x27_class1 = { + 1, 16, + _huff_lengthlist_line_2048x27_class1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_2048x27_class2[] = { + 1, 2, 3, 6, 4, 7, 5, 7, +}; + +static static_codebook _huff_book_line_2048x27_class2 = { + 1, 8, + _huff_lengthlist_line_2048x27_class2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_2048x27_class3[] = { + 3, 3, 6,16, 5, 5, 7,16, 9, 8,11,16,16,16,16,16, + 5, 5, 8,16, 5, 5, 7,16, 8, 7, 9,16,16,16,16,16, + 9, 9,12,16, 6, 8,11,16, 9,10,11,16,16,16,16,16, + 16,16,16,16,13,16,16,16,15,16,16,16,16,16,16,16, + 5, 4, 7,16, 6, 5, 8,16, 9, 8,10,16,16,16,16,16, + 5, 5, 7,15, 5, 4, 6,15, 7, 6, 8,16,16,16,16,16, + 9, 9,11,15, 7, 7, 9,16, 8, 8, 9,16,16,16,16,16, + 16,16,16,16,15,15,15,16,15,15,14,16,16,16,16,16, + 8, 8,11,16, 8, 9,10,16,11,10,14,16,16,16,16,16, + 6, 8,10,16, 6, 7,10,16, 8, 8,11,16,14,16,16,16, + 10,11,14,16, 9, 9,11,16,10,10,11,16,16,16,16,16, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 16,16,16,16,15,16,16,16,16,16,16,16,16,16,16,16, + 12,16,15,16,12,14,16,16,16,16,16,16,16,16,16,16, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +}; + +static static_codebook _huff_book_line_2048x27_class3 = { + 1, 256, + _huff_lengthlist_line_2048x27_class3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_2048x27_class4[] = { + 2, 4, 7,13, 4, 5, 7,15, 8, 7,10,16,16,14,16,16, + 2, 4, 7,16, 3, 4, 7,14, 8, 8,10,16,16,16,15,16, + 6, 8,11,16, 7, 7, 9,16,11, 9,13,16,16,16,15,16, + 16,16,16,16,14,16,16,16,16,16,16,16,16,16,16,16, +}; + +static static_codebook _huff_book_line_2048x27_class4 = { + 1, 64, + _huff_lengthlist_line_2048x27_class4, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_2048x27_0sub0[] = { + 5, 5, 5, 5, 5, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, + 6, 5, 7, 5, 7, 5, 7, 5, 8, 5, 8, 5, 8, 5, 9, 5, + 9, 6,10, 6,10, 6,11, 6,11, 6,11, 6,11, 6,11, 6, + 11, 6,11, 6,12, 7,11, 7,11, 7,11, 7,11, 7,10, 7, + 11, 7,11, 7,12, 7,11, 8,11, 8,11, 8,11, 8,13, 8, + 12, 9,11, 9,11, 9,11,10,12,10,12, 9,12,10,12,11, + 14,12,16,12,12,11,14,16,17,17,17,17,17,17,17,17, + 17,17,17,17,17,17,17,17,17,17,17,17,16,16,16,16, +}; + +static static_codebook _huff_book_line_2048x27_0sub0 = { + 1, 128, + _huff_lengthlist_line_2048x27_0sub0, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_2048x27_1sub0[] = { + 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 6, 6, 6, 6, 6, 6, 7, 6, 7, 6, 7, 6, 7, 6, +}; + +static static_codebook _huff_book_line_2048x27_1sub0 = { + 1, 32, + _huff_lengthlist_line_2048x27_1sub0, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_2048x27_1sub1[] = { + 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, + 6, 5, 7, 5, 7, 4, 7, 4, 8, 4, 8, 4, 8, 4, 8, 3, + 8, 4, 9, 4, 9, 4, 9, 4, 9, 4, 9, 5, 9, 5, 9, 6, + 9, 7, 9, 8, 9, 9, 9,10, 9,11, 9,14, 9,15,10,15, + 10,15,10,15,10,15,11,15,10,14,12,14,11,14,13,14, + 13,15,15,15,12,15,15,15,13,15,13,15,13,15,15,15, + 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,14, +}; + +static static_codebook _huff_book_line_2048x27_1sub1 = { + 1, 128, + _huff_lengthlist_line_2048x27_1sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_2048x27_2sub0[] = { + 2, 4, 5, 4, 5, 4, 5, 4, 5, 5, 5, 5, 5, 5, 6, 5, + 6, 5, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, +}; + +static static_codebook _huff_book_line_2048x27_2sub0 = { + 1, 32, + _huff_lengthlist_line_2048x27_2sub0, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_2048x27_2sub1[] = { + 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, + 3, 4, 3, 4, 3, 4, 4, 5, 4, 5, 5, 5, 6, 6, 6, 7, + 6, 8, 6, 8, 6, 9, 7,10, 7,10, 7,10, 7,12, 7,12, + 7,12, 9,12,11,12,10,12,10,12,11,12,12,12,10,12, + 10,12,10,12, 9,12,11,12,12,12,12,12,11,12,11,12, + 12,12,12,12,12,12,12,12,10,10,12,12,12,12,12,10, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +}; + +static static_codebook _huff_book_line_2048x27_2sub1 = { + 1, 128, + _huff_lengthlist_line_2048x27_2sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_2048x27_3sub1[] = { + 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, +}; + +static static_codebook _huff_book_line_2048x27_3sub1 = { + 1, 18, + _huff_lengthlist_line_2048x27_3sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_2048x27_3sub2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, + 6, 7, 6, 7, 6, 8, 6, 9, 7, 9, 7, 9, 9,11, 9,12, + 10,12, +}; + +static static_codebook _huff_book_line_2048x27_3sub2 = { + 1, 50, + _huff_lengthlist_line_2048x27_3sub2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_2048x27_3sub3[] = { + 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, 3, 6, 3, 7, 3, 7, 5, 7, 7, 7, 7, 7, 6, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, +}; + +static static_codebook _huff_book_line_2048x27_3sub3 = { + 1, 128, + _huff_lengthlist_line_2048x27_3sub3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_2048x27_4sub1[] = { + 0, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 4, 5, 4, 5, 4, + 4, 5, +}; + +static static_codebook _huff_book_line_2048x27_4sub1 = { + 1, 18, + _huff_lengthlist_line_2048x27_4sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_2048x27_4sub2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3, 2, 4, 3, 4, 4, 4, 5, 5, 6, 5, 6, 5, 7, + 6, 6, 6, 7, 7, 7, 8, 9, 9, 9,12,10,11,10,10,12, + 10,10, +}; + +static static_codebook _huff_book_line_2048x27_4sub2 = { + 1, 50, + _huff_lengthlist_line_2048x27_4sub2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_2048x27_4sub3[] = { + 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, 3, 6, 5, 7, 5, 7, 7, 7, 7, 7, 5, 7, 5, 7, + 5, 7, 5, 7, 7, 7, 7, 7, 4, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, +}; + +static static_codebook _huff_book_line_2048x27_4sub3 = { + 1, 128, + _huff_lengthlist_line_2048x27_4sub3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_256x4low_class0[] = { + 4, 5, 6,11, 5, 5, 6,10, 7, 7, 6, 6,14,13, 9, 9, + 6, 6, 6,10, 6, 6, 6, 9, 8, 7, 7, 9,14,12, 8,11, + 8, 7, 7,11, 8, 8, 7,11, 9, 9, 7, 9,13,11, 9,13, + 19,19,18,19,15,16,16,19,11,11,10,13,10,10, 9,15, + 5, 5, 6,13, 6, 6, 6,11, 8, 7, 6, 7,14,11,10,11, + 6, 6, 6,12, 7, 6, 6,11, 8, 7, 7,11,13,11, 9,11, + 9, 7, 6,12, 8, 7, 6,12, 9, 8, 8,11,13,10, 7,13, + 19,19,17,19,17,14,14,19,12,10, 8,12,13,10, 9,16, + 7, 8, 7,12, 7, 7, 7,11, 8, 7, 7, 8,12,12,11,11, + 8, 8, 7,12, 8, 7, 6,11, 8, 7, 7,10,10,11,10,11, + 9, 8, 8,13, 9, 8, 7,12,10, 9, 7,11, 9, 8, 7,11, + 18,18,15,18,18,16,17,18,15,11,10,18,11, 9, 9,18, + 16,16,13,16,12,11,10,16,12,11, 9, 6,15,12,11,13, + 16,16,14,14,13,11,12,16,12, 9, 9,13,13,10,10,12, + 17,18,17,17,14,15,14,16,14,12,14,15,12,10,11,12, + 18,18,18,18,18,18,18,18,18,12,13,18,16,11, 9,18, +}; + +static static_codebook _huff_book_line_256x4low_class0 = { + 1, 256, + _huff_lengthlist_line_256x4low_class0, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_256x4low_0sub0[] = { + 1, 3, 2, 3, +}; + +static static_codebook _huff_book_line_256x4low_0sub0 = { + 1, 4, + _huff_lengthlist_line_256x4low_0sub0, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_256x4low_0sub1[] = { + 0, 0, 0, 0, 2, 3, 2, 3, 3, 3, +}; + +static static_codebook _huff_book_line_256x4low_0sub1 = { + 1, 10, + _huff_lengthlist_line_256x4low_0sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_256x4low_0sub2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 4, 3, 4, + 4, 4, 4, 4, 5, 5, 5, 6, 6, +}; + +static static_codebook _huff_book_line_256x4low_0sub2 = { + 1, 25, + _huff_lengthlist_line_256x4low_0sub2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_256x4low_0sub3[] = { + 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, 3, 4, 2, 4, 3, 5, 4, + 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 7, 7, 8, 6, 9, + 7,12,11,16,13,16,12,15,13,15,12,14,12,15,15,15, +}; + +static static_codebook _huff_book_line_256x4low_0sub3 = { + 1, 64, + _huff_lengthlist_line_256x4low_0sub3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; +/********* End of inlined file: floor_books.h *********/ + +static static_codebook *_floor_128x4_books[]={ + &_huff_book_line_128x4_class0, + &_huff_book_line_128x4_0sub0, + &_huff_book_line_128x4_0sub1, + &_huff_book_line_128x4_0sub2, + &_huff_book_line_128x4_0sub3, +}; +static static_codebook *_floor_256x4_books[]={ + &_huff_book_line_256x4_class0, + &_huff_book_line_256x4_0sub0, + &_huff_book_line_256x4_0sub1, + &_huff_book_line_256x4_0sub2, + &_huff_book_line_256x4_0sub3, +}; +static static_codebook *_floor_128x7_books[]={ + &_huff_book_line_128x7_class0, + &_huff_book_line_128x7_class1, + + &_huff_book_line_128x7_0sub1, + &_huff_book_line_128x7_0sub2, + &_huff_book_line_128x7_0sub3, + &_huff_book_line_128x7_1sub1, + &_huff_book_line_128x7_1sub2, + &_huff_book_line_128x7_1sub3, +}; +static static_codebook *_floor_256x7_books[]={ + &_huff_book_line_256x7_class0, + &_huff_book_line_256x7_class1, + + &_huff_book_line_256x7_0sub1, + &_huff_book_line_256x7_0sub2, + &_huff_book_line_256x7_0sub3, + &_huff_book_line_256x7_1sub1, + &_huff_book_line_256x7_1sub2, + &_huff_book_line_256x7_1sub3, +}; +static static_codebook *_floor_128x11_books[]={ + &_huff_book_line_128x11_class1, + &_huff_book_line_128x11_class2, + &_huff_book_line_128x11_class3, + + &_huff_book_line_128x11_0sub0, + &_huff_book_line_128x11_1sub0, + &_huff_book_line_128x11_1sub1, + &_huff_book_line_128x11_2sub1, + &_huff_book_line_128x11_2sub2, + &_huff_book_line_128x11_2sub3, + &_huff_book_line_128x11_3sub1, + &_huff_book_line_128x11_3sub2, + &_huff_book_line_128x11_3sub3, +}; +static static_codebook *_floor_128x17_books[]={ + &_huff_book_line_128x17_class1, + &_huff_book_line_128x17_class2, + &_huff_book_line_128x17_class3, + + &_huff_book_line_128x17_0sub0, + &_huff_book_line_128x17_1sub0, + &_huff_book_line_128x17_1sub1, + &_huff_book_line_128x17_2sub1, + &_huff_book_line_128x17_2sub2, + &_huff_book_line_128x17_2sub3, + &_huff_book_line_128x17_3sub1, + &_huff_book_line_128x17_3sub2, + &_huff_book_line_128x17_3sub3, +}; +static static_codebook *_floor_256x4low_books[]={ + &_huff_book_line_256x4low_class0, + &_huff_book_line_256x4low_0sub0, + &_huff_book_line_256x4low_0sub1, + &_huff_book_line_256x4low_0sub2, + &_huff_book_line_256x4low_0sub3, +}; +static static_codebook *_floor_1024x27_books[]={ + &_huff_book_line_1024x27_class1, + &_huff_book_line_1024x27_class2, + &_huff_book_line_1024x27_class3, + &_huff_book_line_1024x27_class4, + + &_huff_book_line_1024x27_0sub0, + &_huff_book_line_1024x27_1sub0, + &_huff_book_line_1024x27_1sub1, + &_huff_book_line_1024x27_2sub0, + &_huff_book_line_1024x27_2sub1, + &_huff_book_line_1024x27_3sub1, + &_huff_book_line_1024x27_3sub2, + &_huff_book_line_1024x27_3sub3, + &_huff_book_line_1024x27_4sub1, + &_huff_book_line_1024x27_4sub2, + &_huff_book_line_1024x27_4sub3, +}; +static static_codebook *_floor_2048x27_books[]={ + &_huff_book_line_2048x27_class1, + &_huff_book_line_2048x27_class2, + &_huff_book_line_2048x27_class3, + &_huff_book_line_2048x27_class4, + + &_huff_book_line_2048x27_0sub0, + &_huff_book_line_2048x27_1sub0, + &_huff_book_line_2048x27_1sub1, + &_huff_book_line_2048x27_2sub0, + &_huff_book_line_2048x27_2sub1, + &_huff_book_line_2048x27_3sub1, + &_huff_book_line_2048x27_3sub2, + &_huff_book_line_2048x27_3sub3, + &_huff_book_line_2048x27_4sub1, + &_huff_book_line_2048x27_4sub2, + &_huff_book_line_2048x27_4sub3, +}; + +static static_codebook *_floor_512x17_books[]={ + &_huff_book_line_512x17_class1, + &_huff_book_line_512x17_class2, + &_huff_book_line_512x17_class3, + + &_huff_book_line_512x17_0sub0, + &_huff_book_line_512x17_1sub0, + &_huff_book_line_512x17_1sub1, + &_huff_book_line_512x17_2sub1, + &_huff_book_line_512x17_2sub2, + &_huff_book_line_512x17_2sub3, + &_huff_book_line_512x17_3sub1, + &_huff_book_line_512x17_3sub2, + &_huff_book_line_512x17_3sub3, +}; + +static static_codebook **_floor_books[10]={ + _floor_128x4_books, + _floor_256x4_books, + _floor_128x7_books, + _floor_256x7_books, + _floor_128x11_books, + _floor_128x17_books, + _floor_256x4low_books, + _floor_1024x27_books, + _floor_2048x27_books, + _floor_512x17_books, +}; + +static vorbis_info_floor1 _floor[10]={ + /* 128 x 4 */ + { + 1,{0},{4},{2},{0}, + {{1,2,3,4}}, + 4,{0,128, 33,8,16,70}, + + 60,30,500, 1.,18., -1 + }, + /* 256 x 4 */ + { + 1,{0},{4},{2},{0}, + {{1,2,3,4}}, + 4,{0,256, 66,16,32,140}, + + 60,30,500, 1.,18., -1 + }, + /* 128 x 7 */ + { + 2,{0,1},{3,4},{2,2},{0,1}, + {{-1,2,3,4},{-1,5,6,7}}, + 4,{0,128, 14,4,58, 2,8,28,90}, + + 60,30,500, 1.,18., -1 + }, + /* 256 x 7 */ + { + 2,{0,1},{3,4},{2,2},{0,1}, + {{-1,2,3,4},{-1,5,6,7}}, + 4,{0,256, 28,8,116, 4,16,56,180}, + + 60,30,500, 1.,18., -1 + }, + /* 128 x 11 */ + { + 4,{0,1,2,3},{2,3,3,3},{0,1,2,2},{-1,0,1,2}, + {{3},{4,5},{-1,6,7,8},{-1,9,10,11}}, + + 2,{0,128, 8,33, 4,16,70, 2,6,12, 23,46,90}, + + 60,30,500, 1,18., -1 + }, + /* 128 x 17 */ + { + 6,{0,1,1,2,3,3},{2,3,3,3},{0,1,2,2},{-1,0,1,2}, + {{3},{4,5},{-1,6,7,8},{-1,9,10,11}}, + 2,{0,128, 12,46, 4,8,16, 23,33,70, 2,6,10, 14,19,28, 39,58,90}, + + 60,30,500, 1,18., -1 + }, + /* 256 x 4 (low bitrate version) */ + { + 1,{0},{4},{2},{0}, + {{1,2,3,4}}, + 4,{0,256, 66,16,32,140}, + + 60,30,500, 1.,18., -1 + }, + /* 1024 x 27 */ + { + 8,{0,1,2,2,3,3,4,4},{3,4,3,4,3},{0,1,1,2,2},{-1,0,1,2,3}, + {{4},{5,6},{7,8},{-1,9,10,11},{-1,12,13,14}}, + 2,{0,1024, 93,23,372, 6,46,186,750, 14,33,65, 130,260,556, + 3,10,18,28, 39,55,79,111, 158,220,312, 464,650,850}, + + 60,30,500, 3,18., -1 /* lowpass */ + }, + /* 2048 x 27 */ + { + 8,{0,1,2,2,3,3,4,4},{3,4,3,4,3},{0,1,1,2,2},{-1,0,1,2,3}, + {{4},{5,6},{7,8},{-1,9,10,11},{-1,12,13,14}}, + 2,{0,2048, 186,46,744, 12,92,372,1500, 28,66,130, 260,520,1112, + 6,20,36,56, 78,110,158,222, 316,440,624, 928,1300,1700}, + + 60,30,500, 3,18., -1 /* lowpass */ + }, + /* 512 x 17 */ + { + 6,{0,1,1,2,3,3},{2,3,3,3},{0,1,2,2},{-1,0,1,2}, + {{3},{4,5},{-1,6,7,8},{-1,9,10,11}}, + 2,{0,512, 46,186, 16,33,65, 93,130,278, + 7,23,39, 55,79,110, 156,232,360}, + + 60,30,500, 1,18., -1 /* lowpass! */ + }, + +}; +/********* End of inlined file: floor_all.h *********/ + +/********* Start of inlined file: residue_44.h *********/ + +/********* Start of inlined file: res_books_stereo.h *********/ + +static long _vq_quantlist__16c0_s_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__16c0_s_p1_0[] = { + 1, 4, 4, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, 0, 0, 0, + 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5, 8, 8, 0, 0, 0, 0, 0, 0, 8, 9,10, 0, 0, 0, + 0, 0, 0, 7, 9,10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 8, 8, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, + 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 5, 8, 8, 0, 0, 0, 0, + 0, 0, 8,10,10, 0, 0, 0, 0, 0, 0, 8,10,10, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7,10,10, 0, 0, 0, + 0, 0, 0, 9, 9,12, 0, 0, 0, 0, 0, 0,10,12,11, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7,10,10, 0, 0, + 0, 0, 0, 0, 9,12,10, 0, 0, 0, 0, 0, 0,10,11,12, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 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, 5, 8, 8, 0, 0, 0, 0, 0, 0, 8,10,10, 0, 0, + 0, 0, 0, 0, 8,10,10, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7,10,10, 0, 0, 0, 0, 0, 0,10,12,11, 0, + 0, 0, 0, 0, 0, 9,10,12, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7,10,10, 0, 0, 0, 0, 0, 0,10,11,12, + 0, 0, 0, 0, 0, 0, 9,12, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__16c0_s_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__16c0_s_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__16c0_s_p1_0 = { + _vq_quantthresh__16c0_s_p1_0, + _vq_quantmap__16c0_s_p1_0, + 3, + 3 +}; + +static static_codebook _16c0_s_p1_0 = { + 8, 6561, + _vq_lengthlist__16c0_s_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__16c0_s_p1_0, + NULL, + &_vq_auxt__16c0_s_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c0_s_p2_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__16c0_s_p2_0[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__16c0_s_p2_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__16c0_s_p2_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__16c0_s_p2_0 = { + _vq_quantthresh__16c0_s_p2_0, + _vq_quantmap__16c0_s_p2_0, + 5, + 5 +}; + +static static_codebook _16c0_s_p2_0 = { + 4, 625, + _vq_lengthlist__16c0_s_p2_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__16c0_s_p2_0, + NULL, + &_vq_auxt__16c0_s_p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c0_s_p3_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__16c0_s_p3_0[] = { + 1, 4, 4, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 6, 6, 7, 6, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 6, 6, 9, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__16c0_s_p3_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__16c0_s_p3_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__16c0_s_p3_0 = { + _vq_quantthresh__16c0_s_p3_0, + _vq_quantmap__16c0_s_p3_0, + 5, + 5 +}; + +static static_codebook _16c0_s_p3_0 = { + 4, 625, + _vq_lengthlist__16c0_s_p3_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__16c0_s_p3_0, + NULL, + &_vq_auxt__16c0_s_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c0_s_p4_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__16c0_s_p4_0[] = { + 1, 3, 2, 7, 8, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, + 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 7, 7, + 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, + 8, 8, 0, 0, 0, 0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__16c0_s_p4_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__16c0_s_p4_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__16c0_s_p4_0 = { + _vq_quantthresh__16c0_s_p4_0, + _vq_quantmap__16c0_s_p4_0, + 9, + 9 +}; + +static static_codebook _16c0_s_p4_0 = { + 2, 81, + _vq_lengthlist__16c0_s_p4_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__16c0_s_p4_0, + NULL, + &_vq_auxt__16c0_s_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c0_s_p5_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__16c0_s_p5_0[] = { + 1, 3, 3, 6, 6, 6, 6, 8, 8, 0, 0, 0, 7, 7, 7, 7, + 8, 8, 0, 0, 0, 7, 7, 7, 7, 8, 8, 0, 0, 0, 7, 7, + 8, 8, 9, 9, 0, 0, 0, 7, 7, 8, 8, 9, 9, 0, 0, 0, + 8, 9, 8, 8,10,10, 0, 0, 0, 8, 8, 8, 8,10,10, 0, + 0, 0,10,10, 9, 9,10,10, 0, 0, 0, 0, 0, 9, 9,10, + 10, +}; + +static float _vq_quantthresh__16c0_s_p5_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__16c0_s_p5_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__16c0_s_p5_0 = { + _vq_quantthresh__16c0_s_p5_0, + _vq_quantmap__16c0_s_p5_0, + 9, + 9 +}; + +static static_codebook _16c0_s_p5_0 = { + 2, 81, + _vq_lengthlist__16c0_s_p5_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__16c0_s_p5_0, + NULL, + &_vq_auxt__16c0_s_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c0_s_p6_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__16c0_s_p6_0[] = { + 1, 3, 4, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9,10,10,11, + 11, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10,10,10,11, + 11,11, 0, 0, 0, 6, 6, 8, 8, 9, 9, 9, 9,10,10,11, + 11,11,11, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10,10, + 11,11,12,12, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10, + 10,11,11,12,12, 0, 0, 0, 8, 8, 9, 9,10,10,10,10, + 11,11,12,12,12,12, 0, 0, 0, 8, 8, 9, 9,10,10,10, + 10,11,11,12,12,12,13, 0, 0, 0, 9, 9, 9, 9,10,10, + 10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0,10,10,10, + 10,10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, 9, 9, + 10,10,11,11,12,12,13,13,13,13, 0, 0, 0, 0, 0, 9, + 9,10,10,11,11,12,12,13,13,13,14, 0, 0, 0, 0, 0, + 10,10,10,11,11,11,12,12,13,13,13,14, 0, 0, 0, 0, + 0, 0, 0,10,10,11,11,12,12,13,13,14,14, 0, 0, 0, + 0, 0, 0, 0,11,11,12,12,13,13,13,13,14,14, 0, 0, + 0, 0, 0, 0, 0,11,11,12,12,12,13,13,14,15,14, 0, + 0, 0, 0, 0, 0, 0,12,12,12,12,13,13,13,14,14,15, + 0, 0, 0, 0, 0, 0, 0, 0, 0,12,12,13,13,14,13,14, + 14, +}; + +static float _vq_quantthresh__16c0_s_p6_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__16c0_s_p6_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__16c0_s_p6_0 = { + _vq_quantthresh__16c0_s_p6_0, + _vq_quantmap__16c0_s_p6_0, + 17, + 17 +}; + +static static_codebook _16c0_s_p6_0 = { + 2, 289, + _vq_lengthlist__16c0_s_p6_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__16c0_s_p6_0, + NULL, + &_vq_auxt__16c0_s_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c0_s_p7_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__16c0_s_p7_0[] = { + 1, 4, 4, 6, 6, 6, 7, 6, 6, 4, 7, 7,11,10,10,11, + 11,10, 4, 7, 7,10,10,10,11,10,10, 6,10,10,11,11, + 11,11,11,10, 6, 9, 9,11,12,12,11, 9, 9, 6, 9,10, + 11,12,12,11, 9,10, 7,11,11,11,11,11,12,13,12, 6, + 9,10,11,10,10,12,13,13, 6,10, 9,11,10,10,11,12, + 13, +}; + +static float _vq_quantthresh__16c0_s_p7_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__16c0_s_p7_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__16c0_s_p7_0 = { + _vq_quantthresh__16c0_s_p7_0, + _vq_quantmap__16c0_s_p7_0, + 3, + 3 +}; + +static static_codebook _16c0_s_p7_0 = { + 4, 81, + _vq_lengthlist__16c0_s_p7_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__16c0_s_p7_0, + NULL, + &_vq_auxt__16c0_s_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c0_s_p7_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__16c0_s_p7_1[] = { + 1, 3, 4, 6, 6, 7, 7, 8, 8, 8, 8,10,10,10, 7, 7, + 8, 8, 8, 9, 9, 9,10,10,10, 6, 7, 8, 8, 8, 8, 9, + 8,10,10,10, 7, 7, 8, 8, 9, 9, 9, 9,10,10,10, 7, + 7, 8, 8, 9, 9, 8, 9,10,10,10, 8, 8, 9, 9, 9, 9, + 9, 9,11,11,11, 8, 8, 9, 9, 9, 9, 9,10,10,11,11, + 9, 9, 9, 9, 9, 9, 9,10,11,11,11,10,11, 9, 9, 9, + 9,10, 9,11,11,11,10,11,10,10, 9, 9,10,10,11,11, + 11,11,11, 9, 9, 9, 9,10,10, +}; + +static float _vq_quantthresh__16c0_s_p7_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__16c0_s_p7_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__16c0_s_p7_1 = { + _vq_quantthresh__16c0_s_p7_1, + _vq_quantmap__16c0_s_p7_1, + 11, + 11 +}; + +static static_codebook _16c0_s_p7_1 = { + 2, 121, + _vq_lengthlist__16c0_s_p7_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__16c0_s_p7_1, + NULL, + &_vq_auxt__16c0_s_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__16c0_s_p8_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__16c0_s_p8_0[] = { + 1, 4, 4, 7, 7, 7, 7, 7, 6, 8, 8,10,10, 6, 5, 6, + 8, 8, 8, 8, 8, 8, 8, 9,10,10, 7, 6, 6, 8, 8, 8, + 8, 8, 8, 8, 8,10,10, 0, 8, 8, 8, 8, 9, 8, 9, 9, + 9,10,10,10, 0, 9, 8, 8, 8, 9, 9, 8, 8, 9, 9,10, + 10, 0,12,11, 8, 8, 9, 9, 9, 9,10,10,11,10, 0,12, + 13, 8, 8, 9,10, 9, 9,11,11,11,12, 0, 0, 0, 8, 8, + 8, 8,10, 9,12,13,12,14, 0, 0, 0, 8, 8, 8, 9,10, + 10,12,12,13,14, 0, 0, 0,13,13, 9, 9,11,11, 0, 0, + 14, 0, 0, 0, 0,14,14,10,10,12,11,12,14,14,14, 0, + 0, 0, 0, 0,11,11,13,13,14,13,14,14, 0, 0, 0, 0, + 0,12,13,13,12,13,14,14,14, +}; + +static float _vq_quantthresh__16c0_s_p8_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__16c0_s_p8_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__16c0_s_p8_0 = { + _vq_quantthresh__16c0_s_p8_0, + _vq_quantmap__16c0_s_p8_0, + 13, + 13 +}; + +static static_codebook _16c0_s_p8_0 = { + 2, 169, + _vq_lengthlist__16c0_s_p8_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__16c0_s_p8_0, + NULL, + &_vq_auxt__16c0_s_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c0_s_p8_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__16c0_s_p8_1[] = { + 1, 4, 3, 5, 5, 7, 7, 7, 6, 6, 7, 7, 7, 5, 5, 7, + 7, 7, 6, 6, 7, 7, 7, 6, 6, +}; + +static float _vq_quantthresh__16c0_s_p8_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__16c0_s_p8_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__16c0_s_p8_1 = { + _vq_quantthresh__16c0_s_p8_1, + _vq_quantmap__16c0_s_p8_1, + 5, + 5 +}; + +static static_codebook _16c0_s_p8_1 = { + 2, 25, + _vq_lengthlist__16c0_s_p8_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__16c0_s_p8_1, + NULL, + &_vq_auxt__16c0_s_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__16c0_s_p9_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__16c0_s_p9_0[] = { + 1, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, +}; + +static float _vq_quantthresh__16c0_s_p9_0[] = { + -157.5, 157.5, +}; + +static long _vq_quantmap__16c0_s_p9_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__16c0_s_p9_0 = { + _vq_quantthresh__16c0_s_p9_0, + _vq_quantmap__16c0_s_p9_0, + 3, + 3 +}; + +static static_codebook _16c0_s_p9_0 = { + 4, 81, + _vq_lengthlist__16c0_s_p9_0, + 1, -518803456, 1628680192, 2, 0, + _vq_quantlist__16c0_s_p9_0, + NULL, + &_vq_auxt__16c0_s_p9_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c0_s_p9_1[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__16c0_s_p9_1[] = { + 1, 5, 5, 5, 5, 9,11,11,10,10,10,10,10,10,10, 7, + 6, 6, 6, 6,10,10,10,10,10,10,10,10,10,10, 7, 6, + 6, 6, 6,10, 9,10,10,10,10,10,10,10,10,10, 7, 7, + 8, 9,10,10,10,10,10,10,10,10,10,10,10, 8, 7,10, + 10,10, 9,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10, +}; + +static float _vq_quantthresh__16c0_s_p9_1[] = { + -136.5, -115.5, -94.5, -73.5, -52.5, -31.5, -10.5, 10.5, + 31.5, 52.5, 73.5, 94.5, 115.5, 136.5, +}; + +static long _vq_quantmap__16c0_s_p9_1[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__16c0_s_p9_1 = { + _vq_quantthresh__16c0_s_p9_1, + _vq_quantmap__16c0_s_p9_1, + 15, + 15 +}; + +static static_codebook _16c0_s_p9_1 = { + 2, 225, + _vq_lengthlist__16c0_s_p9_1, + 1, -520986624, 1620377600, 4, 0, + _vq_quantlist__16c0_s_p9_1, + NULL, + &_vq_auxt__16c0_s_p9_1, + NULL, + 0 +}; + +static long _vq_quantlist__16c0_s_p9_2[] = { + 10, + 9, + 11, + 8, + 12, + 7, + 13, + 6, + 14, + 5, + 15, + 4, + 16, + 3, + 17, + 2, + 18, + 1, + 19, + 0, + 20, +}; + +static long _vq_lengthlist__16c0_s_p9_2[] = { + 1, 5, 5, 7, 8, 8, 7, 9, 9, 9,12,12,11,12,12,10, + 10,11,12,12,12,11,12,12, 8, 9, 8, 7, 9,10,10,11, + 11,10,11,12,10,12,10,12,12,12,11,12,11, 9, 8, 8, + 9,10, 9, 8, 9,10,12,12,11,11,12,11,10,11,12,11, + 12,12, 8, 9, 9, 9,10,11,12,11,12,11,11,11,11,12, + 12,11,11,12,12,11,11, 9, 9, 8, 9, 9,11, 9, 9,10, + 9,11,11,11,11,12,11,11,10,12,12,12, 9,12,11,10, + 11,11,11,11,12,12,12,11,11,11,12,10,12,12,12,10, + 10, 9,10, 9,10,10, 9, 9, 9,10,10,12,10,11,11, 9, + 11,11,10,11,11,11,10,10,10, 9, 9,10,10, 9, 9,10, + 11,11,10,11,10,11,10,11,11,10,11,11,11,10, 9,10, + 10, 9,10, 9, 9,11, 9, 9,11,10,10,11,11,10,10,11, + 10,11, 8, 9,11,11,10, 9,10,11,11,10,11,11,10,10, + 10,11,10, 9,10,10,11, 9,10,10, 9,11,10,10,10,10, + 11,10,11,11, 9,11,10,11,10,10,11,11,10,10,10, 9, + 10,10,11,11,11, 9,10,10,10,10,10,11,10,10,10, 9, + 10,10,11,10,10,10,10,10, 9,10,11,10,10,10,10,11, + 11,11,10,10,10,10,10,11,10,11,10,11,10,10,10, 9, + 11,11,10,10,10,11,11,10,10,10,10,10,10,10,10,11, + 11, 9,10,10,10,11,10,11,10,10,10,11, 9,10,11,10, + 11,10,10, 9,10,10,10,11,10,11,10,10,10,10,10,11, + 11,10,11,11,10,10,11,11,10, 9, 9,10,10,10,10,10, + 9,11, 9,10,10,10,11,11,10,10,10,10,11,11,11,10, + 9, 9,10,10,11,10,10,10,10,10,11,11,11,10,10,10, + 11,11,11, 9,10,10,10,10, 9,10, 9,10,11,10,11,10, + 10,11,11,10,11,11,11,11,11,10,11,10,10,10, 9,11, + 11,10,11,11,11,11,11,11,11,11,11,10,11,10,10,10, + 10,11,10,10,11, 9,10,10,10, +}; + +static float _vq_quantthresh__16c0_s_p9_2[] = { + -9.5, -8.5, -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, + -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, + 6.5, 7.5, 8.5, 9.5, +}; + +static long _vq_quantmap__16c0_s_p9_2[] = { + 19, 17, 15, 13, 11, 9, 7, 5, + 3, 1, 0, 2, 4, 6, 8, 10, + 12, 14, 16, 18, 20, +}; + +static encode_aux_threshmatch _vq_auxt__16c0_s_p9_2 = { + _vq_quantthresh__16c0_s_p9_2, + _vq_quantmap__16c0_s_p9_2, + 21, + 21 +}; + +static static_codebook _16c0_s_p9_2 = { + 2, 441, + _vq_lengthlist__16c0_s_p9_2, + 1, -529268736, 1611661312, 5, 0, + _vq_quantlist__16c0_s_p9_2, + NULL, + &_vq_auxt__16c0_s_p9_2, + NULL, + 0 +}; + +static long _huff_lengthlist__16c0_s_single[] = { + 3, 4,19, 7, 9, 7, 8,11, 9,12, 4, 1,19, 6, 7, 7, + 8,10,11,13,18,18,18,18,18,18,18,18,18,18, 8, 6, + 18, 8, 9, 9,11,12,14,18, 9, 6,18, 9, 7, 8, 9,11, + 12,18, 7, 6,18, 8, 7, 7, 7, 9,11,17, 8, 8,18, 9, + 7, 6, 6, 8,11,17,10,10,18,12, 9, 8, 7, 9,12,18, + 13,15,18,15,13,11,10,11,15,18,14,18,18,18,18,18, + 16,16,18,18, +}; + +static static_codebook _huff_book__16c0_s_single = { + 2, 100, + _huff_lengthlist__16c0_s_single, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__16c1_s_long[] = { + 2, 5,20, 7,10, 7, 8,10,11,11, 4, 2,20, 5, 8, 6, + 7, 9,10,10,20,20,20,20,19,19,19,19,19,19, 7, 5, + 19, 6,10, 7, 9,11,13,17,11, 8,19,10, 7, 7, 8,10, + 11,15, 7, 5,19, 7, 7, 5, 6, 9,11,16, 7, 6,19, 8, + 7, 6, 6, 7, 9,13, 9, 9,19,11, 9, 8, 6, 7, 8,13, + 12,14,19,16,13,10, 9, 8, 9,13,14,17,19,18,18,17, + 12,11,11,13, +}; + +static static_codebook _huff_book__16c1_s_long = { + 2, 100, + _huff_lengthlist__16c1_s_long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__16c1_s_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__16c1_s_p1_0[] = { + 1, 5, 5, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, 0, 0, 0, + 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5, 8, 7, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, + 0, 0, 0, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 7, 8, 0, 0, 0, 0, 0, 0, 7, 9, 8, 0, 0, + 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 5, 8, 7, 0, 0, 0, 0, + 0, 0, 8, 9, 9, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, + 0, 0, 0, 9, 9,11, 0, 0, 0, 0, 0, 0, 9,11,10, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, + 0, 0, 0, 0, 8,11, 9, 0, 0, 0, 0, 0, 0, 9,10,11, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 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, 5, 7, 8, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, + 0, 0, 0, 0, 8, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 9,11,10, 0, + 0, 0, 0, 0, 0, 8, 9,11, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 9,10,11, + 0, 0, 0, 0, 0, 0, 9,11, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__16c1_s_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__16c1_s_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__16c1_s_p1_0 = { + _vq_quantthresh__16c1_s_p1_0, + _vq_quantmap__16c1_s_p1_0, + 3, + 3 +}; + +static static_codebook _16c1_s_p1_0 = { + 8, 6561, + _vq_lengthlist__16c1_s_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__16c1_s_p1_0, + NULL, + &_vq_auxt__16c1_s_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c1_s_p2_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__16c1_s_p2_0[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__16c1_s_p2_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__16c1_s_p2_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__16c1_s_p2_0 = { + _vq_quantthresh__16c1_s_p2_0, + _vq_quantmap__16c1_s_p2_0, + 5, + 5 +}; + +static static_codebook _16c1_s_p2_0 = { + 4, 625, + _vq_lengthlist__16c1_s_p2_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__16c1_s_p2_0, + NULL, + &_vq_auxt__16c1_s_p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c1_s_p3_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__16c1_s_p3_0[] = { + 1, 4, 4, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 5, 7, 7, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 5, 5, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 7, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 7, 7, 9, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__16c1_s_p3_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__16c1_s_p3_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__16c1_s_p3_0 = { + _vq_quantthresh__16c1_s_p3_0, + _vq_quantmap__16c1_s_p3_0, + 5, + 5 +}; + +static static_codebook _16c1_s_p3_0 = { + 4, 625, + _vq_lengthlist__16c1_s_p3_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__16c1_s_p3_0, + NULL, + &_vq_auxt__16c1_s_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c1_s_p4_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__16c1_s_p4_0[] = { + 1, 2, 3, 7, 7, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, + 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 7, 7, + 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, + 8, 8, 0, 0, 0, 0, 0, 0, 0, 8, 9, 0, 0, 0, 0, 0, + 0, 0,10,10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__16c1_s_p4_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__16c1_s_p4_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__16c1_s_p4_0 = { + _vq_quantthresh__16c1_s_p4_0, + _vq_quantmap__16c1_s_p4_0, + 9, + 9 +}; + +static static_codebook _16c1_s_p4_0 = { + 2, 81, + _vq_lengthlist__16c1_s_p4_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__16c1_s_p4_0, + NULL, + &_vq_auxt__16c1_s_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c1_s_p5_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__16c1_s_p5_0[] = { + 1, 3, 3, 5, 5, 6, 6, 8, 8, 0, 0, 0, 7, 7, 7, 7, + 9, 9, 0, 0, 0, 7, 7, 7, 7, 9, 9, 0, 0, 0, 8, 8, + 8, 8, 9, 9, 0, 0, 0, 8, 8, 8, 8,10,10, 0, 0, 0, + 9, 9, 8, 8,10,10, 0, 0, 0, 9, 9, 8, 8,10,10, 0, + 0, 0,10,10, 9, 9,10,10, 0, 0, 0, 0, 0, 9, 9,10, + 10, +}; + +static float _vq_quantthresh__16c1_s_p5_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__16c1_s_p5_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__16c1_s_p5_0 = { + _vq_quantthresh__16c1_s_p5_0, + _vq_quantmap__16c1_s_p5_0, + 9, + 9 +}; + +static static_codebook _16c1_s_p5_0 = { + 2, 81, + _vq_lengthlist__16c1_s_p5_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__16c1_s_p5_0, + NULL, + &_vq_auxt__16c1_s_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c1_s_p6_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__16c1_s_p6_0[] = { + 1, 3, 3, 6, 6, 8, 8, 9, 9, 9, 9,10,10,11,11,12, + 12, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10,10,11,11, + 12,12, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10,10,11, + 11,12,12, 0, 0, 0, 8, 8, 8, 9,10, 9,10,10,10,10, + 11,11,12,12, 0, 0, 0, 8, 8, 9, 9,10,10,10,10,11, + 11,11,12,12,12, 0, 0, 0, 8, 8, 9, 9,10,10,10,10, + 11,11,12,12,12,12, 0, 0, 0, 8, 8, 9, 9,10,10,10, + 10,11,11,12,12,13,13, 0, 0, 0, 9, 9, 9, 9,10,10, + 10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, 9, 9,10, + 10,10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, 9, 9, + 10,10,11,11,12,12,12,12,13,13, 0, 0, 0, 0, 0, 9, + 9,10,10,11,11,12,12,12,12,13,13, 0, 0, 0, 0, 0, + 10,10,11,10,11,11,12,12,13,13,13,13, 0, 0, 0, 0, + 0, 0, 0,10,10,11,11,12,12,13,13,13,13, 0, 0, 0, + 0, 0, 0, 0,11,11,12,12,12,12,13,13,14,14, 0, 0, + 0, 0, 0, 0, 0,11,11,12,12,12,12,13,13,14,14, 0, + 0, 0, 0, 0, 0, 0,12,12,12,12,13,13,13,13,14,14, + 0, 0, 0, 0, 0, 0, 0, 0, 0,12,12,13,13,13,13,14, + 14, +}; + +static float _vq_quantthresh__16c1_s_p6_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__16c1_s_p6_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__16c1_s_p6_0 = { + _vq_quantthresh__16c1_s_p6_0, + _vq_quantmap__16c1_s_p6_0, + 17, + 17 +}; + +static static_codebook _16c1_s_p6_0 = { + 2, 289, + _vq_lengthlist__16c1_s_p6_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__16c1_s_p6_0, + NULL, + &_vq_auxt__16c1_s_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c1_s_p7_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__16c1_s_p7_0[] = { + 1, 4, 4, 6, 6, 6, 7, 6, 6, 4, 7, 7,10, 9,10,10, + 10, 9, 4, 7, 7,10,10,10,11,10,10, 6,10,10,11,11, + 11,11,10,10, 6,10, 9,11,11,11,11,10,10, 6,10,10, + 11,11,11,11,10,10, 7,11,11,11,11,11,12,12,11, 6, + 10,10,11,10,10,11,11,11, 6,10,10,10,11,10,11,11, + 11, +}; + +static float _vq_quantthresh__16c1_s_p7_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__16c1_s_p7_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__16c1_s_p7_0 = { + _vq_quantthresh__16c1_s_p7_0, + _vq_quantmap__16c1_s_p7_0, + 3, + 3 +}; + +static static_codebook _16c1_s_p7_0 = { + 4, 81, + _vq_lengthlist__16c1_s_p7_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__16c1_s_p7_0, + NULL, + &_vq_auxt__16c1_s_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c1_s_p7_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__16c1_s_p7_1[] = { + 2, 3, 3, 5, 6, 7, 7, 7, 7, 8, 8,10,10,10, 6, 6, + 7, 7, 8, 8, 8, 8,10,10,10, 6, 6, 7, 7, 8, 8, 8, + 8,10,10,10, 7, 7, 7, 7, 8, 8, 8, 8,10,10,10, 7, + 7, 7, 7, 8, 8, 8, 8,10,10,10, 7, 7, 8, 8, 8, 8, + 8, 8,10,10,10, 7, 7, 8, 8, 8, 8, 8, 8,10,10,10, + 8, 8, 8, 8, 8, 8, 9, 9,10,10,10,10,10, 8, 8, 8, + 8, 9, 9,10,10,10,10,10, 9, 9, 8, 8, 9, 9,10,10, + 10,10,10, 8, 8, 8, 8, 9, 9, +}; + +static float _vq_quantthresh__16c1_s_p7_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__16c1_s_p7_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__16c1_s_p7_1 = { + _vq_quantthresh__16c1_s_p7_1, + _vq_quantmap__16c1_s_p7_1, + 11, + 11 +}; + +static static_codebook _16c1_s_p7_1 = { + 2, 121, + _vq_lengthlist__16c1_s_p7_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__16c1_s_p7_1, + NULL, + &_vq_auxt__16c1_s_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__16c1_s_p8_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__16c1_s_p8_0[] = { + 1, 4, 4, 6, 6, 7, 7, 7, 7, 8, 8, 9, 9, 6, 5, 5, + 7, 8, 8, 9, 8, 8, 9, 9,10,11, 6, 5, 5, 8, 8, 9, + 9, 8, 8, 9,10,10,11, 0, 8, 8, 8, 9, 9, 9, 9, 9, + 10,10,11,11, 0, 9, 9, 9, 8, 9, 9, 9, 9,10,10,11, + 11, 0,13,13, 9, 9,10,10,10,10,11,11,12,12, 0,14, + 13, 9, 9,10,10,10,10,11,11,12,12, 0, 0, 0,10,10, + 9, 9,11,11,12,12,13,12, 0, 0, 0,10,10, 9, 9,10, + 10,12,12,13,13, 0, 0, 0,13,14,11,10,11,11,12,12, + 13,14, 0, 0, 0,14,14,10,10,11,11,12,12,13,13, 0, + 0, 0, 0, 0,12,12,12,12,13,13,14,15, 0, 0, 0, 0, + 0,12,12,12,12,13,13,14,15, +}; + +static float _vq_quantthresh__16c1_s_p8_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__16c1_s_p8_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__16c1_s_p8_0 = { + _vq_quantthresh__16c1_s_p8_0, + _vq_quantmap__16c1_s_p8_0, + 13, + 13 +}; + +static static_codebook _16c1_s_p8_0 = { + 2, 169, + _vq_lengthlist__16c1_s_p8_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__16c1_s_p8_0, + NULL, + &_vq_auxt__16c1_s_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c1_s_p8_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__16c1_s_p8_1[] = { + 2, 3, 3, 5, 5, 6, 6, 6, 5, 5, 6, 6, 6, 5, 5, 6, + 6, 6, 5, 5, 6, 6, 6, 5, 5, +}; + +static float _vq_quantthresh__16c1_s_p8_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__16c1_s_p8_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__16c1_s_p8_1 = { + _vq_quantthresh__16c1_s_p8_1, + _vq_quantmap__16c1_s_p8_1, + 5, + 5 +}; + +static static_codebook _16c1_s_p8_1 = { + 2, 25, + _vq_lengthlist__16c1_s_p8_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__16c1_s_p8_1, + NULL, + &_vq_auxt__16c1_s_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__16c1_s_p9_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__16c1_s_p9_0[] = { + 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, +}; + +static float _vq_quantthresh__16c1_s_p9_0[] = { + -1732.5, -1417.5, -1102.5, -787.5, -472.5, -157.5, 157.5, 472.5, + 787.5, 1102.5, 1417.5, 1732.5, +}; + +static long _vq_quantmap__16c1_s_p9_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__16c1_s_p9_0 = { + _vq_quantthresh__16c1_s_p9_0, + _vq_quantmap__16c1_s_p9_0, + 13, + 13 +}; + +static static_codebook _16c1_s_p9_0 = { + 2, 169, + _vq_lengthlist__16c1_s_p9_0, + 1, -513964032, 1628680192, 4, 0, + _vq_quantlist__16c1_s_p9_0, + NULL, + &_vq_auxt__16c1_s_p9_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c1_s_p9_1[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__16c1_s_p9_1[] = { + 1, 4, 4, 4, 4, 8, 8,12,13,14,14,14,14,14,14, 6, + 6, 6, 6, 6,10, 9,14,14,14,14,14,14,14,14, 7, 6, + 5, 6, 6,10, 9,12,13,13,13,13,13,13,13,13, 7, 7, + 9, 9,11,11,12,13,13,13,13,13,13,13,13, 7, 7, 8, + 8,11,12,13,13,13,13,13,13,13,13,13,12,12,10,10, + 13,12,13,13,13,13,13,13,13,13,13,12,12,10,10,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,12,13,12, + 13,13,13,13,13,13,13,13,13,13,13,13,12,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,12,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,12,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13, +}; + +static float _vq_quantthresh__16c1_s_p9_1[] = { + -136.5, -115.5, -94.5, -73.5, -52.5, -31.5, -10.5, 10.5, + 31.5, 52.5, 73.5, 94.5, 115.5, 136.5, +}; + +static long _vq_quantmap__16c1_s_p9_1[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__16c1_s_p9_1 = { + _vq_quantthresh__16c1_s_p9_1, + _vq_quantmap__16c1_s_p9_1, + 15, + 15 +}; + +static static_codebook _16c1_s_p9_1 = { + 2, 225, + _vq_lengthlist__16c1_s_p9_1, + 1, -520986624, 1620377600, 4, 0, + _vq_quantlist__16c1_s_p9_1, + NULL, + &_vq_auxt__16c1_s_p9_1, + NULL, + 0 +}; + +static long _vq_quantlist__16c1_s_p9_2[] = { + 10, + 9, + 11, + 8, + 12, + 7, + 13, + 6, + 14, + 5, + 15, + 4, + 16, + 3, + 17, + 2, + 18, + 1, + 19, + 0, + 20, +}; + +static long _vq_lengthlist__16c1_s_p9_2[] = { + 1, 4, 4, 6, 6, 7, 7, 8, 7, 8, 8, 9, 9, 9, 9,10, + 10,10, 9,10,10,11,12,12, 8, 8, 8, 8, 9, 9, 9, 9, + 10,10,10,10,10,11,11,10,12,11,11,13,11, 7, 7, 8, + 8, 8, 8, 9, 9, 9,10,10,10,10, 9,10,10,11,11,12, + 11,11, 8, 8, 8, 8, 9, 9,10,10,10,10,11,11,11,11, + 11,11,11,12,11,12,12, 8, 8, 9, 9, 9, 9, 9,10,10, + 10,10,10,10,11,11,11,11,11,11,12,11, 9, 9, 9, 9, + 10,10,10,10,11,10,11,11,11,11,11,11,12,12,12,12, + 11, 9, 9, 9, 9,10,10,10,10,11,11,11,11,11,11,11, + 11,11,12,12,12,13, 9,10,10, 9,11,10,10,10,10,11, + 11,11,11,11,10,11,12,11,12,12,11,12,11,10, 9,10, + 10,11,10,11,11,11,11,11,11,11,11,11,12,12,11,12, + 12,12,10,10,10,11,10,11,11,11,11,11,11,11,11,11, + 11,11,12,13,12,12,11, 9,10,10,11,11,10,11,11,11, + 12,11,11,11,11,11,12,12,13,13,12,13,10,10,12,10, + 11,11,11,11,11,11,11,11,11,12,12,11,13,12,12,12, + 12,13,12,11,11,11,11,11,11,12,11,12,11,11,11,11, + 12,12,13,12,11,12,12,11,11,11,11,11,12,11,11,11, + 11,12,11,11,12,11,12,13,13,12,12,12,12,11,11,11, + 11,11,12,11,11,12,11,12,11,11,11,11,13,12,12,12, + 12,13,11,11,11,12,12,11,11,11,12,11,12,12,12,11, + 12,13,12,11,11,12,12,11,12,11,11,11,12,12,11,12, + 11,11,11,12,12,12,12,13,12,13,12,12,12,12,11,11, + 12,11,11,11,11,11,11,12,12,12,13,12,11,13,13,12, + 12,11,12,10,11,11,11,11,12,11,12,12,11,12,12,13, + 12,12,13,12,12,12,12,12,11,12,12,12,11,12,11,11, + 11,12,13,12,13,13,13,13,13,12,13,13,12,12,13,11, + 11,11,11,11,12,11,11,12,11, +}; + +static float _vq_quantthresh__16c1_s_p9_2[] = { + -9.5, -8.5, -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, + -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, + 6.5, 7.5, 8.5, 9.5, +}; + +static long _vq_quantmap__16c1_s_p9_2[] = { + 19, 17, 15, 13, 11, 9, 7, 5, + 3, 1, 0, 2, 4, 6, 8, 10, + 12, 14, 16, 18, 20, +}; + +static encode_aux_threshmatch _vq_auxt__16c1_s_p9_2 = { + _vq_quantthresh__16c1_s_p9_2, + _vq_quantmap__16c1_s_p9_2, + 21, + 21 +}; + +static static_codebook _16c1_s_p9_2 = { + 2, 441, + _vq_lengthlist__16c1_s_p9_2, + 1, -529268736, 1611661312, 5, 0, + _vq_quantlist__16c1_s_p9_2, + NULL, + &_vq_auxt__16c1_s_p9_2, + NULL, + 0 +}; + +static long _huff_lengthlist__16c1_s_short[] = { + 5, 6,17, 8,12, 9,10,10,12,13, 5, 2,17, 4, 9, 5, + 7, 8,11,13,16,16,16,16,16,16,16,16,16,16, 6, 4, + 16, 5,10, 5, 7,10,14,16,13, 9,16,11, 8, 7, 8, 9, + 13,16, 7, 4,16, 5, 7, 4, 6, 8,11,13, 8, 6,16, 7, + 8, 5, 5, 7, 9,13, 9, 8,16, 9, 8, 6, 6, 7, 9,13, + 11,11,16,10,10, 7, 7, 7, 9,13,13,13,16,13,13, 9, + 9, 9,10,13, +}; + +static static_codebook _huff_book__16c1_s_short = { + 2, 100, + _huff_lengthlist__16c1_s_short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__16c2_s_long[] = { + 4, 7, 9, 9, 9, 8, 9,10,15,19, 5, 4, 5, 6, 7, 7, + 8, 9,14,16, 6, 5, 4, 5, 6, 7, 8,10,12,19, 7, 6, + 5, 4, 5, 6, 7, 9,11,18, 8, 7, 6, 5, 5, 5, 7, 9, + 10,17, 8, 7, 7, 5, 5, 5, 6, 7,12,18, 8, 8, 8, 7, + 7, 5, 5, 7,12,18, 8, 9,10, 9, 9, 7, 6, 7,12,17, + 14,18,16,16,15,12,11,10,12,18,15,17,18,18,18,15, + 14,14,16,18, +}; + +static static_codebook _huff_book__16c2_s_long = { + 2, 100, + _huff_lengthlist__16c2_s_long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__16c2_s_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__16c2_s_p1_0[] = { + 1, 3, 3, 0, 0, 0, 0, 0, 0, 4, 5, 5, 0, 0, 0, 0, + 0, 0, 4, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__16c2_s_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__16c2_s_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__16c2_s_p1_0 = { + _vq_quantthresh__16c2_s_p1_0, + _vq_quantmap__16c2_s_p1_0, + 3, + 3 +}; + +static static_codebook _16c2_s_p1_0 = { + 4, 81, + _vq_lengthlist__16c2_s_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__16c2_s_p1_0, + NULL, + &_vq_auxt__16c2_s_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c2_s_p2_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__16c2_s_p2_0[] = { + 2, 4, 3, 7, 7, 0, 0, 0, 7, 8, 0, 0, 0, 8, 8, 0, + 0, 0, 8, 8, 0, 0, 0, 8, 8, 4, 5, 4, 8, 8, 0, 0, + 0, 8, 8, 0, 0, 0, 8, 8, 0, 0, 0, 9, 9, 0, 0, 0, + 9, 9, 4, 4, 5, 8, 8, 0, 0, 0, 8, 8, 0, 0, 0, 8, + 8, 0, 0, 0, 9, 9, 0, 0, 0, 9, 9, 7, 8, 8,10,10, + 0, 0, 0,12,11, 0, 0, 0,11,11, 0, 0, 0,14,13, 0, + 0, 0,14,13, 7, 8, 8, 9,10, 0, 0, 0,11,12, 0, 0, + 0,11,11, 0, 0, 0,14,14, 0, 0, 0,13,14, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8,11,11, 0, 0, 0, + 11,11, 0, 0, 0,12,11, 0, 0, 0,12,12, 0, 0, 0,13, + 13, 8, 8, 8,11,11, 0, 0, 0,11,11, 0, 0, 0,11,12, + 0, 0, 0,12,13, 0, 0, 0,13,13, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 8, 8, 8,12,11, 0, 0, 0,12,11, 0, + 0, 0,11,11, 0, 0, 0,13,13, 0, 0, 0,13,12, 8, 8, + 8,11,12, 0, 0, 0,11,12, 0, 0, 0,11,11, 0, 0, 0, + 13,13, 0, 0, 0,12,13, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 8, 9, 9,14,13, 0, 0, 0,13,12, 0, 0, 0,13, + 13, 0, 0, 0,13,12, 0, 0, 0,13,13, 8, 9, 9,13,14, + 0, 0, 0,12,13, 0, 0, 0,13,13, 0, 0, 0,12,13, 0, + 0, 0,13,13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, + 9, 9,14,13, 0, 0, 0,13,13, 0, 0, 0,13,12, 0, 0, + 0,13,13, 0, 0, 0,13,12, 8, 9, 9,14,14, 0, 0, 0, + 13,13, 0, 0, 0,12,13, 0, 0, 0,13,13, 0, 0, 0,12, + 13, +}; + +static float _vq_quantthresh__16c2_s_p2_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__16c2_s_p2_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__16c2_s_p2_0 = { + _vq_quantthresh__16c2_s_p2_0, + _vq_quantmap__16c2_s_p2_0, + 5, + 5 +}; + +static static_codebook _16c2_s_p2_0 = { + 4, 625, + _vq_lengthlist__16c2_s_p2_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__16c2_s_p2_0, + NULL, + &_vq_auxt__16c2_s_p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c2_s_p3_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__16c2_s_p3_0[] = { + 1, 3, 3, 6, 6, 7, 7, 8, 8, 0, 0, 0, 6, 6, 7, 7, + 9, 9, 0, 0, 0, 6, 6, 7, 7, 9, 9, 0, 0, 0, 7, 7, + 8, 8,10,10, 0, 0, 0, 7, 7, 8, 8,10,10, 0, 0, 0, + 7, 7, 9, 9,10,10, 0, 0, 0, 7, 7, 9, 9,10,10, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__16c2_s_p3_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__16c2_s_p3_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__16c2_s_p3_0 = { + _vq_quantthresh__16c2_s_p3_0, + _vq_quantmap__16c2_s_p3_0, + 9, + 9 +}; + +static static_codebook _16c2_s_p3_0 = { + 2, 81, + _vq_lengthlist__16c2_s_p3_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__16c2_s_p3_0, + NULL, + &_vq_auxt__16c2_s_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c2_s_p4_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__16c2_s_p4_0[] = { + 2, 3, 3, 5, 5, 6, 6, 7, 7, 7, 7, 8, 8, 9, 9,10, + 10, 0, 0, 0, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9,10,10, + 11,11, 0, 0, 0, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9,10, + 10,10,11, 0, 0, 0, 6, 6, 8, 8, 8, 8, 9, 9,10,10, + 10,11,11,11, 0, 0, 0, 6, 6, 8, 8, 9, 9, 9, 9,10, + 10,11,11,11,11, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9, + 10,10,11,11,12,12, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, + 9,10,10,11,11,12,12, 0, 0, 0, 7, 7, 8, 8, 9, 9, + 10,10,11,11,12,12,12,12, 0, 0, 0, 0, 0, 8, 8, 9, + 9,10,10,11,11,12,12,12,12, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__16c2_s_p4_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__16c2_s_p4_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__16c2_s_p4_0 = { + _vq_quantthresh__16c2_s_p4_0, + _vq_quantmap__16c2_s_p4_0, + 17, + 17 +}; + +static static_codebook _16c2_s_p4_0 = { + 2, 289, + _vq_lengthlist__16c2_s_p4_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__16c2_s_p4_0, + NULL, + &_vq_auxt__16c2_s_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c2_s_p5_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__16c2_s_p5_0[] = { + 1, 4, 4, 5, 7, 7, 6, 7, 7, 4, 6, 6,10,10,10,10, + 10,10, 4, 7, 6,10,10,10,10,10,10, 5, 9, 9, 9,12, + 11,10,11,12, 7,10,10,12,12,12,12,12,12, 7,10,10, + 11,12,12,12,12,13, 6,10,10,10,12,12,10,12,12, 7, + 10,10,11,13,12,12,12,12, 7,10,10,11,12,12,12,12, + 12, +}; + +static float _vq_quantthresh__16c2_s_p5_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__16c2_s_p5_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__16c2_s_p5_0 = { + _vq_quantthresh__16c2_s_p5_0, + _vq_quantmap__16c2_s_p5_0, + 3, + 3 +}; + +static static_codebook _16c2_s_p5_0 = { + 4, 81, + _vq_lengthlist__16c2_s_p5_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__16c2_s_p5_0, + NULL, + &_vq_auxt__16c2_s_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c2_s_p5_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__16c2_s_p5_1[] = { + 2, 3, 3, 6, 6, 7, 7, 7, 7, 8, 8,11,11,11, 6, 6, + 7, 7, 8, 8, 8, 8,11,11,11, 6, 6, 7, 7, 8, 8, 8, + 8,11,11,11, 6, 6, 8, 8, 8, 8, 9, 9,11,11,11, 6, + 6, 8, 8, 8, 8, 9, 9,11,11,11, 7, 7, 8, 8, 8, 8, + 8, 8,11,11,11, 7, 7, 8, 8, 8, 8, 8, 9,11,11,11, + 8, 8, 8, 8, 8, 8, 8, 8,11,11,11,11,11, 8, 8, 8, + 8, 8, 8,11,11,11,11,11, 8, 8, 8, 8, 8, 8,11,11, + 11,11,11, 7, 7, 8, 8, 8, 8, +}; + +static float _vq_quantthresh__16c2_s_p5_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__16c2_s_p5_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__16c2_s_p5_1 = { + _vq_quantthresh__16c2_s_p5_1, + _vq_quantmap__16c2_s_p5_1, + 11, + 11 +}; + +static static_codebook _16c2_s_p5_1 = { + 2, 121, + _vq_lengthlist__16c2_s_p5_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__16c2_s_p5_1, + NULL, + &_vq_auxt__16c2_s_p5_1, + NULL, + 0 +}; + +static long _vq_quantlist__16c2_s_p6_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__16c2_s_p6_0[] = { + 1, 4, 4, 7, 6, 8, 8, 9, 9,10,10,11,11, 5, 5, 5, + 7, 7, 9, 9, 9, 9,11,11,12,12, 6, 5, 5, 7, 7, 9, + 9,10,10,11,11,12,12, 0, 6, 6, 7, 7, 9, 9,10,10, + 11,11,12,12, 0, 7, 7, 7, 7, 9, 9,10,10,11,12,12, + 12, 0,11,11, 8, 8,10,10,11,11,12,12,13,13, 0,11, + 12, 8, 8,10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static float _vq_quantthresh__16c2_s_p6_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__16c2_s_p6_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__16c2_s_p6_0 = { + _vq_quantthresh__16c2_s_p6_0, + _vq_quantmap__16c2_s_p6_0, + 13, + 13 +}; + +static static_codebook _16c2_s_p6_0 = { + 2, 169, + _vq_lengthlist__16c2_s_p6_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__16c2_s_p6_0, + NULL, + &_vq_auxt__16c2_s_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c2_s_p6_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__16c2_s_p6_1[] = { + 2, 3, 3, 5, 5, 6, 6, 6, 5, 5, 6, 6, 6, 5, 5, 6, + 6, 6, 5, 5, 6, 6, 6, 5, 5, +}; + +static float _vq_quantthresh__16c2_s_p6_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__16c2_s_p6_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__16c2_s_p6_1 = { + _vq_quantthresh__16c2_s_p6_1, + _vq_quantmap__16c2_s_p6_1, + 5, + 5 +}; + +static static_codebook _16c2_s_p6_1 = { + 2, 25, + _vq_lengthlist__16c2_s_p6_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__16c2_s_p6_1, + NULL, + &_vq_auxt__16c2_s_p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__16c2_s_p7_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__16c2_s_p7_0[] = { + 1, 4, 4, 7, 7, 8, 8, 9, 9,10,10,11,11, 5, 5, 5, + 8, 8, 9, 9,10,10,11,11,12,12, 6, 5, 5, 8, 8, 9, + 9,10,10,11,11,12,13,18, 6, 6, 7, 7, 9, 9,10,10, + 12,12,13,13,18, 6, 6, 7, 7, 9, 9,10,10,12,12,13, + 13,18,11,10, 8, 8,10,10,11,11,12,12,13,13,18,11, + 11, 8, 8,10,10,11,11,12,13,13,13,18,18,18,10,11, + 11,11,12,12,13,13,14,14,18,18,18,11,11,11,11,12, + 12,13,13,14,14,18,18,18,14,14,12,12,12,12,14,14, + 15,14,18,18,18,15,15,11,12,12,12,13,13,15,15,18, + 18,18,18,18,13,13,13,13,13,14,17,16,18,18,18,18, + 18,13,14,13,13,14,13,15,14, +}; + +static float _vq_quantthresh__16c2_s_p7_0[] = { + -60.5, -49.5, -38.5, -27.5, -16.5, -5.5, 5.5, 16.5, + 27.5, 38.5, 49.5, 60.5, +}; + +static long _vq_quantmap__16c2_s_p7_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__16c2_s_p7_0 = { + _vq_quantthresh__16c2_s_p7_0, + _vq_quantmap__16c2_s_p7_0, + 13, + 13 +}; + +static static_codebook _16c2_s_p7_0 = { + 2, 169, + _vq_lengthlist__16c2_s_p7_0, + 1, -523206656, 1618345984, 4, 0, + _vq_quantlist__16c2_s_p7_0, + NULL, + &_vq_auxt__16c2_s_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c2_s_p7_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__16c2_s_p7_1[] = { + 2, 4, 4, 6, 6, 7, 7, 7, 7, 7, 7, 9, 9, 9, 6, 6, + 7, 7, 8, 8, 8, 8, 9, 9, 9, 6, 6, 7, 7, 8, 8, 8, + 8, 9, 9, 9, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 7, + 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 7, 7, 7, 7, 8, 8, + 8, 8, 9, 9, 9, 7, 7, 7, 7, 7, 7, 8, 8, 9, 9, 9, + 7, 7, 8, 8, 7, 7, 8, 8, 9, 9, 9, 9, 9, 7, 7, 7, + 7, 8, 8, 9, 9, 9, 9, 9, 8, 8, 7, 7, 8, 8, 9, 9, + 9, 9, 9, 7, 7, 7, 7, 8, 8, +}; + +static float _vq_quantthresh__16c2_s_p7_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__16c2_s_p7_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__16c2_s_p7_1 = { + _vq_quantthresh__16c2_s_p7_1, + _vq_quantmap__16c2_s_p7_1, + 11, + 11 +}; + +static static_codebook _16c2_s_p7_1 = { + 2, 121, + _vq_lengthlist__16c2_s_p7_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__16c2_s_p7_1, + NULL, + &_vq_auxt__16c2_s_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__16c2_s_p8_0[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__16c2_s_p8_0[] = { + 1, 4, 4, 7, 6, 7, 7, 6, 6, 8, 8, 9, 9,10,10, 6, + 6, 6, 8, 8, 9, 8, 8, 8, 9, 9,11,10,11,11, 7, 6, + 6, 8, 8, 9, 8, 7, 7, 9, 9,10,10,12,11,14, 8, 8, + 8, 9, 9, 9, 9, 9,10, 9,10,10,11,13,14, 8, 8, 8, + 8, 9, 9, 8, 8, 9, 9,10,10,11,12,14,13,11, 9, 9, + 9, 9, 9, 9, 9,10,11,10,13,12,14,11,13, 8, 9, 9, + 9, 9, 9,10,10,11,10,13,12,14,14,14, 8, 9, 9, 9, + 11,11,11,11,11,12,13,13,14,14,14, 9, 8, 9, 9,10, + 10,12,10,11,12,12,14,14,14,14,11,12,10,10,12,12, + 12,12,13,14,12,12,14,14,14,12,12, 9,10,11,11,12, + 14,12,14,14,14,14,14,14,14,14,11,11,12,11,12,14, + 14,14,14,14,14,14,14,14,14,12,11,11,11,11,14,14, + 14,14,14,14,14,14,14,14,14,14,13,12,14,14,14,14, + 14,14,14,14,14,14,14,14,14,12,12,12,13,14,14,13, + 13, +}; + +static float _vq_quantthresh__16c2_s_p8_0[] = { + -136.5, -115.5, -94.5, -73.5, -52.5, -31.5, -10.5, 10.5, + 31.5, 52.5, 73.5, 94.5, 115.5, 136.5, +}; + +static long _vq_quantmap__16c2_s_p8_0[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__16c2_s_p8_0 = { + _vq_quantthresh__16c2_s_p8_0, + _vq_quantmap__16c2_s_p8_0, + 15, + 15 +}; + +static static_codebook _16c2_s_p8_0 = { + 2, 225, + _vq_lengthlist__16c2_s_p8_0, + 1, -520986624, 1620377600, 4, 0, + _vq_quantlist__16c2_s_p8_0, + NULL, + &_vq_auxt__16c2_s_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c2_s_p8_1[] = { + 10, + 9, + 11, + 8, + 12, + 7, + 13, + 6, + 14, + 5, + 15, + 4, + 16, + 3, + 17, + 2, + 18, + 1, + 19, + 0, + 20, +}; + +static long _vq_lengthlist__16c2_s_p8_1[] = { + 2, 4, 4, 6, 6, 7, 7, 7, 7, 8, 7, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8,11,12,11, 7, 7, 8, 8, 8, 8, 9, 9, + 9, 9, 9, 9, 9, 9, 9,10, 9, 9,11,11,10, 7, 7, 8, + 8, 8, 8, 9, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,11, + 11,11, 8, 7, 8, 8, 9, 9, 9, 9, 9, 9,10,10, 9,10, + 10, 9,10,10,11,11,12, 8, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 9, 9,10, 9,10,10,10,10,11,11,11, 8, 8, 9, 9, + 9, 9, 9, 9, 9,10,10,10,10,10,10,10,10,10,11,11, + 11, 8, 8, 9, 8, 9, 9, 9, 9,10, 9, 9, 9,10,10,10, + 10, 9,10,11,11,11, 9, 9, 9, 9,10, 9, 9, 9,10,10, + 9,10, 9,10,10,10,10,10,11,12,11,11,11, 9, 9, 9, + 9, 9,10,10, 9,10,10,10,10,10,10,10,10,12,11,13, + 13,11, 9, 9, 9, 9,10,10, 9,10,10,10,10,11,10,10, + 10,10,11,12,11,12,11, 9, 9, 9,10,10, 9,10,10,10, + 10,10,10,10,10,10,10,11,11,11,12,11, 9,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,11,12,12,12, + 11,11,11,10, 9,10,10,10,10,10,10,10,10,11,10,10, + 10,11,11,11,11,11,11,11,10,10,10,11,10,10,10,10, + 10,10,10,10,10,10,11,11,11,11,12,12,11,10,10,10, + 10,10,10,10,10,11,10,10,10,11,10,12,11,11,12,11, + 11,11,10,10,10,10,10,11,10,10,10,10,10,11,10,10, + 11,11,11,12,11,12,11,11,12,10,10,10,10,10,10,10, + 11,10,10,11,10,12,11,11,11,12,11,11,11,11,10,10, + 10,10,10,10,10,11,11,11,10,11,12,11,11,11,12,11, + 12,11,12,10,11,10,10,10,10,11,10,10,10,10,10,10, + 12,11,11,11,11,11,12,12,10,10,10,10,10,11,10,10, + 11,10,11,11,11,11,11,11,11,11,11,11,11,11,12,11, + 10,11,10,10,10,10,10,10,10, +}; + +static float _vq_quantthresh__16c2_s_p8_1[] = { + -9.5, -8.5, -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, + -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, + 6.5, 7.5, 8.5, 9.5, +}; + +static long _vq_quantmap__16c2_s_p8_1[] = { + 19, 17, 15, 13, 11, 9, 7, 5, + 3, 1, 0, 2, 4, 6, 8, 10, + 12, 14, 16, 18, 20, +}; + +static encode_aux_threshmatch _vq_auxt__16c2_s_p8_1 = { + _vq_quantthresh__16c2_s_p8_1, + _vq_quantmap__16c2_s_p8_1, + 21, + 21 +}; + +static static_codebook _16c2_s_p8_1 = { + 2, 441, + _vq_lengthlist__16c2_s_p8_1, + 1, -529268736, 1611661312, 5, 0, + _vq_quantlist__16c2_s_p8_1, + NULL, + &_vq_auxt__16c2_s_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__16c2_s_p9_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__16c2_s_p9_0[] = { + 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, +}; + +static float _vq_quantthresh__16c2_s_p9_0[] = { + -5120.5, -4189.5, -3258.5, -2327.5, -1396.5, -465.5, 465.5, 1396.5, + 2327.5, 3258.5, 4189.5, 5120.5, +}; + +static long _vq_quantmap__16c2_s_p9_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__16c2_s_p9_0 = { + _vq_quantthresh__16c2_s_p9_0, + _vq_quantmap__16c2_s_p9_0, + 13, + 13 +}; + +static static_codebook _16c2_s_p9_0 = { + 2, 169, + _vq_lengthlist__16c2_s_p9_0, + 1, -510275072, 1631393792, 4, 0, + _vq_quantlist__16c2_s_p9_0, + NULL, + &_vq_auxt__16c2_s_p9_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c2_s_p9_1[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__16c2_s_p9_1[] = { + 1, 5, 5, 9, 8, 7, 7, 7, 6,10,11,11,11,11,11,11, + 11, 8, 7, 6, 8, 8,10, 9,10,10,10, 9,11,10,10,10, + 10,10, 8, 6, 6, 8, 8, 9, 8, 9, 8, 9,10,10,10,10, + 10,10,10,10, 8,10, 9, 9, 9, 9,10,10,10,10,10,10, + 10,10,10,10,10, 8, 9, 9, 9,10,10, 9,10,10,10,10, + 10,10,10,10,10,10,10,10, 9, 8, 9, 9,10,10,10,10, + 10,10,10,10,10,10,10,10, 9, 8, 8, 9, 9,10,10,10, + 10,10,10,10,10,10,10,10,10,10, 9,10, 9, 9,10,10, + 10,10,10,10,10,10,10,10,10,10,10, 9, 8, 9, 9,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, 9, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 8,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10, 9,10, 9,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10, 9,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10, +}; + +static float _vq_quantthresh__16c2_s_p9_1[] = { + -367.5, -318.5, -269.5, -220.5, -171.5, -122.5, -73.5, -24.5, + 24.5, 73.5, 122.5, 171.5, 220.5, 269.5, 318.5, 367.5, +}; + +static long _vq_quantmap__16c2_s_p9_1[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__16c2_s_p9_1 = { + _vq_quantthresh__16c2_s_p9_1, + _vq_quantmap__16c2_s_p9_1, + 17, + 17 +}; + +static static_codebook _16c2_s_p9_1 = { + 2, 289, + _vq_lengthlist__16c2_s_p9_1, + 1, -518488064, 1622704128, 5, 0, + _vq_quantlist__16c2_s_p9_1, + NULL, + &_vq_auxt__16c2_s_p9_1, + NULL, + 0 +}; + +static long _vq_quantlist__16c2_s_p9_2[] = { + 13, + 12, + 14, + 11, + 15, + 10, + 16, + 9, + 17, + 8, + 18, + 7, + 19, + 6, + 20, + 5, + 21, + 4, + 22, + 3, + 23, + 2, + 24, + 1, + 25, + 0, + 26, +}; + +static long _vq_lengthlist__16c2_s_p9_2[] = { + 1, 4, 4, 5, 5, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 8, 7, 8, 7, 7, 4, 4, +}; + +static float _vq_quantthresh__16c2_s_p9_2[] = { + -12.5, -11.5, -10.5, -9.5, -8.5, -7.5, -6.5, -5.5, + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5, 10.5, + 11.5, 12.5, +}; + +static long _vq_quantmap__16c2_s_p9_2[] = { + 25, 23, 21, 19, 17, 15, 13, 11, + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, 12, 14, 16, 18, 20, + 22, 24, 26, +}; + +static encode_aux_threshmatch _vq_auxt__16c2_s_p9_2 = { + _vq_quantthresh__16c2_s_p9_2, + _vq_quantmap__16c2_s_p9_2, + 27, + 27 +}; + +static static_codebook _16c2_s_p9_2 = { + 1, 27, + _vq_lengthlist__16c2_s_p9_2, + 1, -528875520, 1611661312, 5, 0, + _vq_quantlist__16c2_s_p9_2, + NULL, + &_vq_auxt__16c2_s_p9_2, + NULL, + 0 +}; + +static long _huff_lengthlist__16c2_s_short[] = { + 7,10,11,11,11,14,15,15,17,14, 8, 6, 7, 7, 8, 9, + 11,11,14,17, 9, 6, 6, 6, 7, 7,10,11,15,16, 9, 6, + 6, 4, 4, 5, 8, 9,12,16,10, 6, 6, 4, 4, 4, 6, 9, + 13,16,10, 7, 6, 5, 4, 3, 5, 7,13,16,11, 9, 8, 7, + 6, 5, 5, 6,12,15,10,10,10, 9, 7, 6, 6, 7,11,15, + 13,13,13,13,11,10,10, 9,12,16,16,16,16,14,16,15, + 15,12,14,14, +}; + +static static_codebook _huff_book__16c2_s_short = { + 2, 100, + _huff_lengthlist__16c2_s_short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__8c0_s_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__8c0_s_p1_0[] = { + 1, 5, 4, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, 0, 0, 0, + 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5, 8, 8, 0, 0, 0, 0, 0, 0, 7, 8, 9, 0, 0, 0, + 0, 0, 0, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 8, 8, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, + 0, 0, 0, 0, 7, 9, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 5, 8, 8, 0, 0, 0, 0, + 0, 0, 8,10,10, 0, 0, 0, 0, 0, 0, 8, 9, 9, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7,10, 9, 0, 0, 0, + 0, 0, 0, 8, 9,11, 0, 0, 0, 0, 0, 0, 9,11,11, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 9,10, 0, 0, + 0, 0, 0, 0, 9,11,10, 0, 0, 0, 0, 0, 0, 9,11,11, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 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, 5, 8, 8, 0, 0, 0, 0, 0, 0, 8, 9, 9, 0, 0, + 0, 0, 0, 0, 8, 9,10, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 9,11,11, 0, + 0, 0, 0, 0, 0, 9,10,11, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 9,10, 0, 0, 0, 0, 0, 0, 9,11,11, + 0, 0, 0, 0, 0, 0, 8,11, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__8c0_s_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__8c0_s_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__8c0_s_p1_0 = { + _vq_quantthresh__8c0_s_p1_0, + _vq_quantmap__8c0_s_p1_0, + 3, + 3 +}; + +static static_codebook _8c0_s_p1_0 = { + 8, 6561, + _vq_lengthlist__8c0_s_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__8c0_s_p1_0, + NULL, + &_vq_auxt__8c0_s_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__8c0_s_p2_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__8c0_s_p2_0[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__8c0_s_p2_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__8c0_s_p2_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__8c0_s_p2_0 = { + _vq_quantthresh__8c0_s_p2_0, + _vq_quantmap__8c0_s_p2_0, + 5, + 5 +}; + +static static_codebook _8c0_s_p2_0 = { + 4, 625, + _vq_lengthlist__8c0_s_p2_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__8c0_s_p2_0, + NULL, + &_vq_auxt__8c0_s_p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__8c0_s_p3_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__8c0_s_p3_0[] = { + 1, 4, 4, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 6, 7, 7, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 5, 5, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 7, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 7, 7, 8, 8, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__8c0_s_p3_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__8c0_s_p3_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__8c0_s_p3_0 = { + _vq_quantthresh__8c0_s_p3_0, + _vq_quantmap__8c0_s_p3_0, + 5, + 5 +}; + +static static_codebook _8c0_s_p3_0 = { + 4, 625, + _vq_lengthlist__8c0_s_p3_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__8c0_s_p3_0, + NULL, + &_vq_auxt__8c0_s_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__8c0_s_p4_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__8c0_s_p4_0[] = { + 1, 2, 3, 7, 7, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, + 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 7, 7, + 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, + 8, 8, 0, 0, 0, 0, 0, 0, 0, 9, 8, 0, 0, 0, 0, 0, + 0, 0,10,10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__8c0_s_p4_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__8c0_s_p4_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__8c0_s_p4_0 = { + _vq_quantthresh__8c0_s_p4_0, + _vq_quantmap__8c0_s_p4_0, + 9, + 9 +}; + +static static_codebook _8c0_s_p4_0 = { + 2, 81, + _vq_lengthlist__8c0_s_p4_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__8c0_s_p4_0, + NULL, + &_vq_auxt__8c0_s_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__8c0_s_p5_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__8c0_s_p5_0[] = { + 1, 3, 3, 5, 5, 7, 6, 8, 8, 0, 0, 0, 7, 7, 7, 7, + 8, 8, 0, 0, 0, 7, 7, 7, 7, 8, 9, 0, 0, 0, 8, 8, + 8, 8, 9, 9, 0, 0, 0, 8, 8, 8, 8, 9, 9, 0, 0, 0, + 9, 9, 8, 8,10,10, 0, 0, 0, 9, 9, 8, 8,10,10, 0, + 0, 0,10,10, 9, 9,10,10, 0, 0, 0, 0, 0, 9, 9,10, + 10, +}; + +static float _vq_quantthresh__8c0_s_p5_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__8c0_s_p5_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__8c0_s_p5_0 = { + _vq_quantthresh__8c0_s_p5_0, + _vq_quantmap__8c0_s_p5_0, + 9, + 9 +}; + +static static_codebook _8c0_s_p5_0 = { + 2, 81, + _vq_lengthlist__8c0_s_p5_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__8c0_s_p5_0, + NULL, + &_vq_auxt__8c0_s_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__8c0_s_p6_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__8c0_s_p6_0[] = { + 1, 3, 3, 6, 6, 8, 8, 9, 9, 8, 8,10, 9,10,10,11, + 11, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10,10,11,11, + 11,12, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10,10,11, + 11,12,11, 0, 0, 0, 8, 8, 9, 9,10,10, 9, 9,10,10, + 11,11,12,12, 0, 0, 0, 8, 8, 9, 9,10,10, 9, 9,11, + 10,11,11,12,12, 0, 0, 0, 9, 9, 9, 9,10,10,10,10, + 11,11,11,12,12,12, 0, 0, 0, 9, 9, 9, 9,10,10,10, + 10,11,11,12,12,13,13, 0, 0, 0,10,10,10,10,11,11, + 10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0,10, 9,10, + 11,10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, 9, 9, + 10, 9,10,11,12,12,13,13,14,13, 0, 0, 0, 0, 0, 9, + 9, 9,10,10,10,11,11,13,12,13,13, 0, 0, 0, 0, 0, + 10,10,10,10,11,11,12,12,13,13,14,14, 0, 0, 0, 0, + 0, 0, 0,10,10,11,11,12,12,13,13,13,14, 0, 0, 0, + 0, 0, 0, 0,11,11,11,11,12,12,13,14,14,14, 0, 0, + 0, 0, 0, 0, 0,11,11,11,11,12,12,13,13,14,13, 0, + 0, 0, 0, 0, 0, 0,11,11,12,12,13,13,14,14,14,14, + 0, 0, 0, 0, 0, 0, 0, 0, 0,12,12,12,12,13,13,14, + 14, +}; + +static float _vq_quantthresh__8c0_s_p6_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__8c0_s_p6_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__8c0_s_p6_0 = { + _vq_quantthresh__8c0_s_p6_0, + _vq_quantmap__8c0_s_p6_0, + 17, + 17 +}; + +static static_codebook _8c0_s_p6_0 = { + 2, 289, + _vq_lengthlist__8c0_s_p6_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__8c0_s_p6_0, + NULL, + &_vq_auxt__8c0_s_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__8c0_s_p7_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__8c0_s_p7_0[] = { + 1, 4, 4, 7, 6, 6, 7, 6, 6, 4, 7, 7,11, 9,10,12, + 9,10, 4, 7, 7,10,10,10,11, 9, 9, 6,11,10,11,11, + 12,11,11,11, 6,10,10,11,11,12,11,10,10, 6, 9,10, + 11,11,11,11,10,10, 7,10,11,12,11,11,12,11,12, 6, + 9, 9,10, 9, 9,11,10,10, 6, 9, 9,10,10,10,11,10, + 10, +}; + +static float _vq_quantthresh__8c0_s_p7_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__8c0_s_p7_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__8c0_s_p7_0 = { + _vq_quantthresh__8c0_s_p7_0, + _vq_quantmap__8c0_s_p7_0, + 3, + 3 +}; + +static static_codebook _8c0_s_p7_0 = { + 4, 81, + _vq_lengthlist__8c0_s_p7_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__8c0_s_p7_0, + NULL, + &_vq_auxt__8c0_s_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__8c0_s_p7_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__8c0_s_p7_1[] = { + 1, 3, 3, 6, 6, 8, 8, 9, 9, 9, 9,10,10,10, 7, 7, + 8, 8, 9, 9, 9, 9,10,10, 9, 7, 7, 8, 8, 9, 9, 9, + 9,10,10,10, 8, 8, 9, 9, 9, 9, 9, 9,10,10,10, 8, + 8, 9, 9, 9, 9, 8, 9,10,10,10, 8, 8, 9, 9, 9,10, + 10,10,10,10,10, 9, 9, 9, 9, 9, 9,10,10,11,10,11, + 9, 9, 9, 9,10,10,10,10,11,11,11,10,10, 9, 9,10, + 10,10, 9,11,10,10,10,10,10,10, 9, 9,10,10,11,11, + 10,10,10, 9, 9, 9,10,10,10, +}; + +static float _vq_quantthresh__8c0_s_p7_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__8c0_s_p7_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__8c0_s_p7_1 = { + _vq_quantthresh__8c0_s_p7_1, + _vq_quantmap__8c0_s_p7_1, + 11, + 11 +}; + +static static_codebook _8c0_s_p7_1 = { + 2, 121, + _vq_lengthlist__8c0_s_p7_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__8c0_s_p7_1, + NULL, + &_vq_auxt__8c0_s_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__8c0_s_p8_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__8c0_s_p8_0[] = { + 1, 4, 4, 7, 6, 7, 7, 7, 7, 8, 8, 9, 9, 7, 6, 6, + 7, 7, 8, 8, 7, 7, 8, 9,10,10, 7, 6, 6, 7, 7, 8, + 7, 7, 7, 9, 9,10,12, 0, 8, 8, 8, 8, 8, 9, 8, 8, + 9, 9,10,10, 0, 8, 8, 8, 8, 8, 9, 8, 9, 9, 9,11, + 10, 0, 0,13, 9, 8, 9, 9, 9, 9,10,10,11,11, 0,13, + 0, 9, 9, 9, 9, 9, 9,11,10,11,11, 0, 0, 0, 8, 9, + 10, 9,10,10,13,11,12,12, 0, 0, 0, 8, 9, 9, 9,10, + 10,13,12,12,13, 0, 0, 0,12, 0,10,10,12,11,10,11, + 12,12, 0, 0, 0,13,13,10,10,10,11,12, 0,13, 0, 0, + 0, 0, 0, 0,13,11, 0,12,12,12,13,12, 0, 0, 0, 0, + 0, 0,13,13,11,13,13,11,12, +}; + +static float _vq_quantthresh__8c0_s_p8_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__8c0_s_p8_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__8c0_s_p8_0 = { + _vq_quantthresh__8c0_s_p8_0, + _vq_quantmap__8c0_s_p8_0, + 13, + 13 +}; + +static static_codebook _8c0_s_p8_0 = { + 2, 169, + _vq_lengthlist__8c0_s_p8_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__8c0_s_p8_0, + NULL, + &_vq_auxt__8c0_s_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__8c0_s_p8_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__8c0_s_p8_1[] = { + 1, 3, 4, 5, 5, 7, 6, 6, 6, 5, 7, 7, 7, 6, 6, 7, + 7, 7, 6, 6, 7, 7, 7, 6, 6, +}; + +static float _vq_quantthresh__8c0_s_p8_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__8c0_s_p8_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__8c0_s_p8_1 = { + _vq_quantthresh__8c0_s_p8_1, + _vq_quantmap__8c0_s_p8_1, + 5, + 5 +}; + +static static_codebook _8c0_s_p8_1 = { + 2, 25, + _vq_lengthlist__8c0_s_p8_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__8c0_s_p8_1, + NULL, + &_vq_auxt__8c0_s_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__8c0_s_p9_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__8c0_s_p9_0[] = { + 1, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, +}; + +static float _vq_quantthresh__8c0_s_p9_0[] = { + -157.5, 157.5, +}; + +static long _vq_quantmap__8c0_s_p9_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__8c0_s_p9_0 = { + _vq_quantthresh__8c0_s_p9_0, + _vq_quantmap__8c0_s_p9_0, + 3, + 3 +}; + +static static_codebook _8c0_s_p9_0 = { + 4, 81, + _vq_lengthlist__8c0_s_p9_0, + 1, -518803456, 1628680192, 2, 0, + _vq_quantlist__8c0_s_p9_0, + NULL, + &_vq_auxt__8c0_s_p9_0, + NULL, + 0 +}; + +static long _vq_quantlist__8c0_s_p9_1[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__8c0_s_p9_1[] = { + 1, 4, 4, 5, 5,10, 8,11,11,11,11,11,11,11,11, 6, + 6, 6, 7, 6,11,10,11,11,11,11,11,11,11,11, 7, 5, + 6, 6, 6, 8, 7,11,11,11,11,11,11,11,11,11, 7, 8, + 8, 8, 9, 9,11,11,11,11,11,11,11,11,11, 9, 8, 7, + 8, 9,11,11,11,11,11,11,11,11,11,11,11,10,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,10,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11, +}; + +static float _vq_quantthresh__8c0_s_p9_1[] = { + -136.5, -115.5, -94.5, -73.5, -52.5, -31.5, -10.5, 10.5, + 31.5, 52.5, 73.5, 94.5, 115.5, 136.5, +}; + +static long _vq_quantmap__8c0_s_p9_1[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__8c0_s_p9_1 = { + _vq_quantthresh__8c0_s_p9_1, + _vq_quantmap__8c0_s_p9_1, + 15, + 15 +}; + +static static_codebook _8c0_s_p9_1 = { + 2, 225, + _vq_lengthlist__8c0_s_p9_1, + 1, -520986624, 1620377600, 4, 0, + _vq_quantlist__8c0_s_p9_1, + NULL, + &_vq_auxt__8c0_s_p9_1, + NULL, + 0 +}; + +static long _vq_quantlist__8c0_s_p9_2[] = { + 10, + 9, + 11, + 8, + 12, + 7, + 13, + 6, + 14, + 5, + 15, + 4, + 16, + 3, + 17, + 2, + 18, + 1, + 19, + 0, + 20, +}; + +static long _vq_lengthlist__8c0_s_p9_2[] = { + 1, 5, 5, 7, 7, 8, 7, 8, 8,10,10, 9, 9,10,10,10, + 11,11,10,12,11,12,12,12, 9, 8, 8, 8, 8, 8, 9,10, + 10,10,10,11,11,11,10,11,11,12,12,11,12, 8, 8, 7, + 7, 8, 9,10,10,10, 9,10,10, 9,10,10,11,11,11,11, + 11,11, 9, 9, 9, 9, 8, 9,10,10,11,10,10,11,11,12, + 10,10,12,12,11,11,10, 9, 9,10, 8, 9,10,10,10, 9, + 10,10,11,11,10,11,10,10,10,12,12,12, 9,10, 9,10, + 9, 9,10,10,11,11,11,11,10,10,10,11,12,11,12,11, + 12,10,11,10,11, 9,10, 9,10, 9,10,10, 9,10,10,11, + 10,11,11,11,11,12,11, 9,10,10,10,10,11,11,11,11, + 11,10,11,11,11,11,10,12,10,12,12,11,12,10,10,11, + 10, 9,11,10,11, 9,10,11,10,10,10,11,11,11,11,12, + 12,10, 9, 9,11,10, 9,12,11,10,12,12,11,11,11,11, + 10,11,11,12,11,10,12, 9,11,10,11,10,10,11,10,11, + 9,10,10,10,11,12,11,11,12,11,10,10,11,11, 9,10, + 10,12,10,11,10,10,10, 9,10,10,10,10, 9,10,10,11, + 11,11,11,12,11,10,10,10,10,11,11,10,11,11, 9,11, + 10,12,10,12,11,10,11,10,10,10,11,10,10,11,11,10, + 11,10,10,10,10,11,11,12,10,10,10,11,10,11,12,11, + 10,11,10,10,11,11,10,12,10, 9,10,10,11,11,11,10, + 12,10,10,11,11,11,10,10,11,10,10,10,11,10,11,10, + 12,11,11,10,10,10,12,10,10,11, 9,10,11,11,11,10, + 10,11,10,10, 9,11,11,12,12,11,12,11,11,11,11,11, + 11, 9,10,11,10,12,10,10,10,10,11,10,10,11,10,10, + 12,10,10,10,10,10, 9,12,10,10,10,10,12, 9,11,10, + 10,11,10,12,12,10,12,12,12,10,10,10,10, 9,10,11, + 10,10,12,10,10,12,11,10,11,10,10,12,11,10,12,10, + 10,11, 9,11,10, 9,10, 9,10, +}; + +static float _vq_quantthresh__8c0_s_p9_2[] = { + -9.5, -8.5, -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, + -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, + 6.5, 7.5, 8.5, 9.5, +}; + +static long _vq_quantmap__8c0_s_p9_2[] = { + 19, 17, 15, 13, 11, 9, 7, 5, + 3, 1, 0, 2, 4, 6, 8, 10, + 12, 14, 16, 18, 20, +}; + +static encode_aux_threshmatch _vq_auxt__8c0_s_p9_2 = { + _vq_quantthresh__8c0_s_p9_2, + _vq_quantmap__8c0_s_p9_2, + 21, + 21 +}; + +static static_codebook _8c0_s_p9_2 = { + 2, 441, + _vq_lengthlist__8c0_s_p9_2, + 1, -529268736, 1611661312, 5, 0, + _vq_quantlist__8c0_s_p9_2, + NULL, + &_vq_auxt__8c0_s_p9_2, + NULL, + 0 +}; + +static long _huff_lengthlist__8c0_s_single[] = { + 4, 5,18, 7,10, 6, 7, 8, 9,10, 5, 2,18, 5, 7, 5, + 6, 7, 8,11,17,17,17,17,17,17,17,17,17,17, 7, 4, + 17, 6, 9, 6, 8,10,12,15,11, 7,17, 9, 6, 6, 7, 9, + 11,15, 6, 4,17, 6, 6, 4, 5, 8,11,16, 6, 6,17, 8, + 6, 5, 6, 9,13,16, 8, 9,17,11, 9, 8, 8,11,13,17, + 9,12,17,15,14,13,12,13,14,17,12,15,17,17,17,17, + 17,16,17,17, +}; + +static static_codebook _huff_book__8c0_s_single = { + 2, 100, + _huff_lengthlist__8c0_s_single, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__8c1_s_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__8c1_s_p1_0[] = { + 1, 5, 5, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, 0, 0, 0, + 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5, 8, 7, 0, 0, 0, 0, 0, 0, 7, 8, 9, 0, 0, 0, + 0, 0, 0, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 7, 8, 0, 0, 0, 0, 0, 0, 7, 9, 8, 0, 0, + 0, 0, 0, 0, 7, 9, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 5, 8, 8, 0, 0, 0, 0, + 0, 0, 8, 9, 9, 0, 0, 0, 0, 0, 0, 8, 9, 9, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, + 0, 0, 0, 8, 8,10, 0, 0, 0, 0, 0, 0, 9,10,10, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, + 0, 0, 0, 0, 8,10, 9, 0, 0, 0, 0, 0, 0, 9,10,10, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 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, 5, 8, 8, 0, 0, 0, 0, 0, 0, 8, 9, 9, 0, 0, + 0, 0, 0, 0, 8, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 9,10,10, 0, + 0, 0, 0, 0, 0, 8, 9,10, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 9,10,10, + 0, 0, 0, 0, 0, 0, 8,10, 8, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__8c1_s_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__8c1_s_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__8c1_s_p1_0 = { + _vq_quantthresh__8c1_s_p1_0, + _vq_quantmap__8c1_s_p1_0, + 3, + 3 +}; + +static static_codebook _8c1_s_p1_0 = { + 8, 6561, + _vq_lengthlist__8c1_s_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__8c1_s_p1_0, + NULL, + &_vq_auxt__8c1_s_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__8c1_s_p2_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__8c1_s_p2_0[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__8c1_s_p2_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__8c1_s_p2_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__8c1_s_p2_0 = { + _vq_quantthresh__8c1_s_p2_0, + _vq_quantmap__8c1_s_p2_0, + 5, + 5 +}; + +static static_codebook _8c1_s_p2_0 = { + 4, 625, + _vq_lengthlist__8c1_s_p2_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__8c1_s_p2_0, + NULL, + &_vq_auxt__8c1_s_p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__8c1_s_p3_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__8c1_s_p3_0[] = { + 2, 4, 4, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 6, 6, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 4, 4, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 7, 7, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 6, 6, 7, 7, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__8c1_s_p3_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__8c1_s_p3_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__8c1_s_p3_0 = { + _vq_quantthresh__8c1_s_p3_0, + _vq_quantmap__8c1_s_p3_0, + 5, + 5 +}; + +static static_codebook _8c1_s_p3_0 = { + 4, 625, + _vq_lengthlist__8c1_s_p3_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__8c1_s_p3_0, + NULL, + &_vq_auxt__8c1_s_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__8c1_s_p4_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__8c1_s_p4_0[] = { + 1, 2, 3, 7, 7, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, + 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 7, 7, + 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, + 8, 8, 0, 0, 0, 0, 0, 0, 0, 9, 8, 0, 0, 0, 0, 0, + 0, 0,10,10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__8c1_s_p4_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__8c1_s_p4_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__8c1_s_p4_0 = { + _vq_quantthresh__8c1_s_p4_0, + _vq_quantmap__8c1_s_p4_0, + 9, + 9 +}; + +static static_codebook _8c1_s_p4_0 = { + 2, 81, + _vq_lengthlist__8c1_s_p4_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__8c1_s_p4_0, + NULL, + &_vq_auxt__8c1_s_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__8c1_s_p5_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__8c1_s_p5_0[] = { + 1, 3, 3, 4, 5, 6, 6, 8, 8, 0, 0, 0, 8, 8, 7, 7, + 9, 9, 0, 0, 0, 8, 8, 7, 7, 9, 9, 0, 0, 0, 9,10, + 8, 8, 9, 9, 0, 0, 0,10,10, 8, 8, 9, 9, 0, 0, 0, + 11,10, 8, 8,10,10, 0, 0, 0,11,11, 8, 8,10,10, 0, + 0, 0,12,12, 9, 9,10,10, 0, 0, 0, 0, 0, 9, 9,10, + 10, +}; + +static float _vq_quantthresh__8c1_s_p5_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__8c1_s_p5_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__8c1_s_p5_0 = { + _vq_quantthresh__8c1_s_p5_0, + _vq_quantmap__8c1_s_p5_0, + 9, + 9 +}; + +static static_codebook _8c1_s_p5_0 = { + 2, 81, + _vq_lengthlist__8c1_s_p5_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__8c1_s_p5_0, + NULL, + &_vq_auxt__8c1_s_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__8c1_s_p6_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__8c1_s_p6_0[] = { + 1, 3, 3, 5, 5, 8, 8, 8, 8, 9, 9,10,10,11,11,11, + 11, 0, 0, 0, 8, 8, 8, 8, 9, 9, 9, 9,10,10,11,11, + 12,12, 0, 0, 0, 8, 8, 8, 8, 9, 9, 9, 9,10,10,11, + 11,12,12, 0, 0, 0, 9, 9, 8, 8,10,10,10,10,11,11, + 12,12,12,12, 0, 0, 0, 9, 9, 8, 8,10,10,10,10,11, + 11,12,12,12,12, 0, 0, 0,10,10, 9, 9,10,10,10,10, + 11,11,12,12,13,13, 0, 0, 0,10,10, 9, 9,10,10,10, + 10,11,11,12,12,13,13, 0, 0, 0,11,11, 9, 9,10,10, + 10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, 9, 9,10, + 10,10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, 9, 9, + 10,10,11,11,12,12,12,12,13,13, 0, 0, 0, 0, 0, 9, + 9,10,10,11,11,12,11,12,12,13,13, 0, 0, 0, 0, 0, + 10,10,11,11,11,11,12,12,13,12,13,13, 0, 0, 0, 0, + 0, 0, 0,11,10,11,11,12,12,13,13,13,13, 0, 0, 0, + 0, 0, 0, 0,11,11,12,12,12,12,13,13,13,14, 0, 0, + 0, 0, 0, 0, 0,11,11,12,12,12,12,13,13,14,13, 0, + 0, 0, 0, 0, 0, 0,12,12,12,12,13,13,13,13,14,14, + 0, 0, 0, 0, 0, 0, 0, 0, 0,12,12,13,13,13,13,14, + 14, +}; + +static float _vq_quantthresh__8c1_s_p6_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__8c1_s_p6_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__8c1_s_p6_0 = { + _vq_quantthresh__8c1_s_p6_0, + _vq_quantmap__8c1_s_p6_0, + 17, + 17 +}; + +static static_codebook _8c1_s_p6_0 = { + 2, 289, + _vq_lengthlist__8c1_s_p6_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__8c1_s_p6_0, + NULL, + &_vq_auxt__8c1_s_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__8c1_s_p7_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__8c1_s_p7_0[] = { + 1, 4, 4, 6, 6, 6, 7, 6, 6, 4, 7, 7,10, 9, 9,10, + 9, 9, 5, 7, 7,10, 9, 9,10, 9, 9, 6,10,10,10,10, + 10,11,10,10, 6, 9, 9,10, 9,10,11,10,10, 6, 9, 9, + 10, 9, 9,11, 9,10, 7,10,10,11,11,11,11,10,10, 6, + 9, 9,10,10,10,11, 9, 9, 6, 9, 9,10,10,10,10, 9, + 9, +}; + +static float _vq_quantthresh__8c1_s_p7_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__8c1_s_p7_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__8c1_s_p7_0 = { + _vq_quantthresh__8c1_s_p7_0, + _vq_quantmap__8c1_s_p7_0, + 3, + 3 +}; + +static static_codebook _8c1_s_p7_0 = { + 4, 81, + _vq_lengthlist__8c1_s_p7_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__8c1_s_p7_0, + NULL, + &_vq_auxt__8c1_s_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__8c1_s_p7_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__8c1_s_p7_1[] = { + 2, 3, 3, 5, 5, 7, 7, 7, 7, 7, 7,10,10, 9, 7, 7, + 7, 7, 8, 8, 8, 8, 9, 9, 9, 7, 7, 7, 7, 8, 8, 8, + 8,10,10,10, 7, 7, 7, 7, 8, 8, 8, 8,10,10,10, 7, + 7, 7, 7, 8, 8, 8, 8,10,10,10, 8, 8, 8, 8, 8, 8, + 8, 8,10,10,10, 8, 8, 8, 8, 8, 8, 8, 8,10,10,10, + 8, 8, 8, 8, 8, 8, 8, 8,10,10,10,10,10, 8, 8, 8, + 8, 8, 8,10,10,10,10,10, 8, 8, 8, 8, 8, 8,10,10, + 10,10,10, 8, 8, 8, 8, 8, 8, +}; + +static float _vq_quantthresh__8c1_s_p7_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__8c1_s_p7_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__8c1_s_p7_1 = { + _vq_quantthresh__8c1_s_p7_1, + _vq_quantmap__8c1_s_p7_1, + 11, + 11 +}; + +static static_codebook _8c1_s_p7_1 = { + 2, 121, + _vq_lengthlist__8c1_s_p7_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__8c1_s_p7_1, + NULL, + &_vq_auxt__8c1_s_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__8c1_s_p8_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__8c1_s_p8_0[] = { + 1, 4, 4, 6, 6, 8, 8, 8, 8, 9, 9,10,10, 7, 5, 5, + 7, 7, 8, 8, 8, 8, 9,10,11,11, 7, 5, 5, 7, 7, 8, + 8, 9, 9,10,10,11,11, 0, 8, 8, 8, 8, 9, 9, 9, 9, + 9,10,11,11, 0, 8, 8, 8, 8, 9, 9, 9, 9,10,10,11, + 11, 0,12,12, 9, 9, 9, 9,10, 9,10,11,11,11, 0,13, + 12, 9, 8, 9, 9,10,10,11,11,12,11, 0, 0, 0, 9, 9, + 9, 9,10,10,11,11,12,12, 0, 0, 0,10,10, 9, 9,10, + 10,11,11,12,12, 0, 0, 0,13,13,10,10,11,11,12,11, + 13,12, 0, 0, 0,14,14,10,10,11,10,11,11,12,12, 0, + 0, 0, 0, 0,12,12,11,11,12,12,13,13, 0, 0, 0, 0, + 0,12,12,11,10,12,11,13,12, +}; + +static float _vq_quantthresh__8c1_s_p8_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__8c1_s_p8_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__8c1_s_p8_0 = { + _vq_quantthresh__8c1_s_p8_0, + _vq_quantmap__8c1_s_p8_0, + 13, + 13 +}; + +static static_codebook _8c1_s_p8_0 = { + 2, 169, + _vq_lengthlist__8c1_s_p8_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__8c1_s_p8_0, + NULL, + &_vq_auxt__8c1_s_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__8c1_s_p8_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__8c1_s_p8_1[] = { + 2, 3, 3, 5, 5, 6, 6, 6, 5, 5, 6, 6, 6, 5, 5, 6, + 6, 6, 5, 5, 6, 6, 6, 5, 5, +}; + +static float _vq_quantthresh__8c1_s_p8_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__8c1_s_p8_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__8c1_s_p8_1 = { + _vq_quantthresh__8c1_s_p8_1, + _vq_quantmap__8c1_s_p8_1, + 5, + 5 +}; + +static static_codebook _8c1_s_p8_1 = { + 2, 25, + _vq_lengthlist__8c1_s_p8_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__8c1_s_p8_1, + NULL, + &_vq_auxt__8c1_s_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__8c1_s_p9_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__8c1_s_p9_0[] = { + 1, 3, 3,10,10,10,10,10,10,10,10,10,10, 5, 6, 6, + 10,10,10,10,10,10,10,10,10,10, 6, 7, 8,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10, 9, 9, 9, 9, +}; + +static float _vq_quantthresh__8c1_s_p9_0[] = { + -1732.5, -1417.5, -1102.5, -787.5, -472.5, -157.5, 157.5, 472.5, + 787.5, 1102.5, 1417.5, 1732.5, +}; + +static long _vq_quantmap__8c1_s_p9_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__8c1_s_p9_0 = { + _vq_quantthresh__8c1_s_p9_0, + _vq_quantmap__8c1_s_p9_0, + 13, + 13 +}; + +static static_codebook _8c1_s_p9_0 = { + 2, 169, + _vq_lengthlist__8c1_s_p9_0, + 1, -513964032, 1628680192, 4, 0, + _vq_quantlist__8c1_s_p9_0, + NULL, + &_vq_auxt__8c1_s_p9_0, + NULL, + 0 +}; + +static long _vq_quantlist__8c1_s_p9_1[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__8c1_s_p9_1[] = { + 1, 4, 4, 5, 5, 7, 7, 9, 9,11,11,12,12,13,13, 6, + 5, 5, 6, 6, 9, 9,10,10,12,12,12,13,15,14, 6, 5, + 5, 7, 7, 9, 9,10,10,12,12,12,13,14,13,17, 7, 7, + 8, 8,10,10,11,11,12,13,13,13,13,13,17, 7, 7, 8, + 8,10,10,11,11,13,13,13,13,14,14,17,11,11, 9, 9, + 11,11,12,12,12,13,13,14,15,13,17,12,12, 9, 9,11, + 11,12,12,13,13,13,13,14,16,17,17,17,11,12,12,12, + 13,13,13,14,15,14,15,15,17,17,17,12,12,11,11,13, + 13,14,14,15,14,15,15,17,17,17,15,15,13,13,14,14, + 15,14,15,15,16,15,17,17,17,15,15,13,13,13,14,14, + 15,15,15,15,16,17,17,17,17,16,14,15,14,14,15,14, + 14,15,15,15,17,17,17,17,17,14,14,16,14,15,15,15, + 15,15,15,17,17,17,17,17,17,16,16,15,17,15,15,14, + 17,15,17,16,17,17,17,17,16,15,14,15,15,15,15,15, + 15, +}; + +static float _vq_quantthresh__8c1_s_p9_1[] = { + -136.5, -115.5, -94.5, -73.5, -52.5, -31.5, -10.5, 10.5, + 31.5, 52.5, 73.5, 94.5, 115.5, 136.5, +}; + +static long _vq_quantmap__8c1_s_p9_1[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__8c1_s_p9_1 = { + _vq_quantthresh__8c1_s_p9_1, + _vq_quantmap__8c1_s_p9_1, + 15, + 15 +}; + +static static_codebook _8c1_s_p9_1 = { + 2, 225, + _vq_lengthlist__8c1_s_p9_1, + 1, -520986624, 1620377600, 4, 0, + _vq_quantlist__8c1_s_p9_1, + NULL, + &_vq_auxt__8c1_s_p9_1, + NULL, + 0 +}; + +static long _vq_quantlist__8c1_s_p9_2[] = { + 10, + 9, + 11, + 8, + 12, + 7, + 13, + 6, + 14, + 5, + 15, + 4, + 16, + 3, + 17, + 2, + 18, + 1, + 19, + 0, + 20, +}; + +static long _vq_lengthlist__8c1_s_p9_2[] = { + 2, 4, 4, 6, 6, 7, 7, 8, 8, 8, 8, 9, 8, 9, 9, 9, + 9, 9, 9, 9, 9,11,11,12, 7, 7, 7, 7, 8, 8, 9, 9, + 9, 9,10,10,10,10,10,10,10,10,11,11,11, 7, 7, 7, + 7, 8, 8, 9, 8, 9, 9, 9, 9, 9, 9,10,10,10,10,11, + 11,12, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9,10,10,10,10, + 10,10,10,10,11,11,11, 7, 7, 8, 8, 8, 8, 9, 9, 9, + 9,10,10,10,10,10,10,10,10,11,11,11, 8, 8, 8, 8, + 9, 9, 9, 9, 9, 9,10,10,10,10,10,10,10,10,11,11, + 11, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9,10,10,10,10,10, + 10,10,10,11,12,11, 9, 9, 8, 9, 9, 9, 9, 9,10,10, + 10,10,10,10,10,10,10,10,11,11,11,11,11, 8, 8, 9, + 9, 9, 9,10,10,10,10,10,10,10,10,10,10,11,12,11, + 12,11, 9, 9, 9, 9, 9,10,10,10,10,10,10,10,10,10, + 10,10,11,11,11,11,11, 9, 9, 9, 9,10,10,10,10,10, + 10,10,10,10,10,10,10,12,11,12,11,11, 9, 9, 9,10, + 10,10,10,10,10,10,10,10,10,10,10,10,12,11,11,11, + 11,11,11,10,10,10,10,10,10,10,10,10,10,10,10,10, + 11,11,11,12,11,11,12,11,10,10,10,10,10,10,10,10, + 10,10,10,10,11,10,11,11,11,11,11,11,11,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,11,11,12,11,12, + 11,11,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 11,11,12,11,12,11,11,11,11,10,10,10,10,10,10,10, + 10,10,10,10,10,11,11,12,11,11,12,11,11,12,10,10, + 11,10,10,10,10,10,10,10,10,10,11,11,11,11,11,11, + 11,11,11,10,10,10,10,10,10,10,10,10,10,10,10,12, + 12,11,12,11,11,12,12,12,11,11,10,10,10,10,10,10, + 10,10,10,11,12,12,11,12,12,11,12,11,11,11,11,10, + 10,10,10,10,10,10,10,10,10, +}; + +static float _vq_quantthresh__8c1_s_p9_2[] = { + -9.5, -8.5, -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, + -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, + 6.5, 7.5, 8.5, 9.5, +}; + +static long _vq_quantmap__8c1_s_p9_2[] = { + 19, 17, 15, 13, 11, 9, 7, 5, + 3, 1, 0, 2, 4, 6, 8, 10, + 12, 14, 16, 18, 20, +}; + +static encode_aux_threshmatch _vq_auxt__8c1_s_p9_2 = { + _vq_quantthresh__8c1_s_p9_2, + _vq_quantmap__8c1_s_p9_2, + 21, + 21 +}; + +static static_codebook _8c1_s_p9_2 = { + 2, 441, + _vq_lengthlist__8c1_s_p9_2, + 1, -529268736, 1611661312, 5, 0, + _vq_quantlist__8c1_s_p9_2, + NULL, + &_vq_auxt__8c1_s_p9_2, + NULL, + 0 +}; + +static long _huff_lengthlist__8c1_s_single[] = { + 4, 6,18, 8,11, 8, 8, 9, 9,10, 4, 4,18, 5, 9, 5, + 6, 7, 8,10,18,18,18,18,17,17,17,17,17,17, 7, 5, + 17, 6,11, 6, 7, 8, 9,12,12, 9,17,12, 8, 8, 9,10, + 10,13, 7, 5,17, 6, 8, 4, 5, 6, 8,10, 6, 5,17, 6, + 8, 5, 4, 5, 7, 9, 7, 7,17, 8, 9, 6, 5, 5, 6, 8, + 8, 8,17, 9,11, 8, 6, 6, 6, 7, 9,10,17,12,12,10, + 9, 7, 7, 8, +}; + +static static_codebook _huff_book__8c1_s_single = { + 2, 100, + _huff_lengthlist__8c1_s_single, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44c2_s_long[] = { + 6, 6,12,10,10,10, 9,10,12,12, 6, 1,10, 5, 6, 6, + 7, 9,11,14,12, 9, 8,11, 7, 8, 9,11,13,15,10, 5, + 12, 7, 8, 7, 9,12,14,15,10, 6, 7, 8, 5, 6, 7, 9, + 12,14, 9, 6, 8, 7, 6, 6, 7, 9,12,12, 9, 7, 9, 9, + 7, 6, 6, 7,10,10,10, 9,10,11, 8, 7, 6, 6, 8,10, + 12,11,13,13,11,10, 8, 8, 8,10,11,13,15,15,14,13, + 10, 8, 8, 9, +}; + +static static_codebook _huff_book__44c2_s_long = { + 2, 100, + _huff_lengthlist__44c2_s_long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44c2_s_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c2_s_p1_0[] = { + 2, 4, 4, 0, 0, 0, 0, 0, 0, 5, 6, 6, 0, 0, 0, 0, + 0, 0, 5, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 7, 8, 8, 0, 0, 0, + 0, 0, 0, 6, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 6, 8, 7, 0, 0, + 0, 0, 0, 0, 7, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 5, 7, 7, 0, 0, 0, 0, + 0, 0, 7, 8, 8, 0, 0, 0, 0, 0, 0, 7, 8, 8, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 8, 8, 0, 0, 0, + 0, 0, 0, 8, 9, 9, 0, 0, 0, 0, 0, 0, 8, 9, 9, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 8, 8, 0, 0, + 0, 0, 0, 0, 8, 9, 8, 0, 0, 0, 0, 0, 0, 8, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 7, 7, 0, 0, 0, 0, 0, 0, 7, 8, 8, 0, 0, + 0, 0, 0, 0, 7, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 6, 8, 8, 0, 0, 0, 0, 0, 0, 8, 9, 9, 0, + 0, 0, 0, 0, 0, 8, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 8, 8, 0, 0, 0, 0, 0, 0, 8, 9, 9, + 0, 0, 0, 0, 0, 0, 8, 9, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c2_s_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44c2_s_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c2_s_p1_0 = { + _vq_quantthresh__44c2_s_p1_0, + _vq_quantmap__44c2_s_p1_0, + 3, + 3 +}; + +static static_codebook _44c2_s_p1_0 = { + 8, 6561, + _vq_lengthlist__44c2_s_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44c2_s_p1_0, + NULL, + &_vq_auxt__44c2_s_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c2_s_p2_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c2_s_p2_0[] = { + 1, 4, 4, 0, 0, 0, 7, 7, 0, 0, 0, 7, 7, 0, 0, 0, + 8, 8, 0, 0, 0, 0, 0, 0, 0, 4, 6, 6, 0, 0, 0, 8, + 8, 0, 0, 0, 8, 8, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, + 0, 0, 4, 6, 6, 0, 0, 0, 8, 8, 0, 0, 0, 8, 8, 0, + 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 7, 8, 8, 0, 0, 0,11,11, 0, 0, + 0,11,11, 0, 0, 0,12,11, 0, 0, 0, 0, 0, 0, 0, 7, + 8, 8, 0, 0, 0,10,11, 0, 0, 0,11,11, 0, 0, 0,11, + 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 6, 8, 8, 0, 0, 0,11,11, 0, 0, 0,11,11, + 0, 0, 0,12,12, 0, 0, 0, 0, 0, 0, 0, 6, 8, 8, 0, + 0, 0,10,11, 0, 0, 0,10,11, 0, 0, 0,11,11, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 9, 9, 0, 0, 0,11,12, 0, 0, 0,11,12, 0, 0, 0, + 12,11, 0, 0, 0, 0, 0, 0, 0, 8,10, 9, 0, 0, 0,12, + 11, 0, 0, 0,12,11, 0, 0, 0,11,12, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, +}; + +static float _vq_quantthresh__44c2_s_p2_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c2_s_p2_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c2_s_p2_0 = { + _vq_quantthresh__44c2_s_p2_0, + _vq_quantmap__44c2_s_p2_0, + 5, + 5 +}; + +static static_codebook _44c2_s_p2_0 = { + 4, 625, + _vq_lengthlist__44c2_s_p2_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c2_s_p2_0, + NULL, + &_vq_auxt__44c2_s_p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c2_s_p3_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c2_s_p3_0[] = { + 2, 4, 3, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 6, 6, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 4, 4, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 6, 7, 9, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c2_s_p3_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c2_s_p3_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c2_s_p3_0 = { + _vq_quantthresh__44c2_s_p3_0, + _vq_quantmap__44c2_s_p3_0, + 5, + 5 +}; + +static static_codebook _44c2_s_p3_0 = { + 4, 625, + _vq_lengthlist__44c2_s_p3_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c2_s_p3_0, + NULL, + &_vq_auxt__44c2_s_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c2_s_p4_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44c2_s_p4_0[] = { + 1, 3, 3, 6, 6, 0, 0, 0, 0, 0, 6, 6, 6, 6, 0, 0, + 0, 0, 0, 6, 6, 6, 6, 0, 0, 0, 0, 0, 7, 7, 6, 6, + 0, 0, 0, 0, 0, 0, 0, 6, 7, 0, 0, 0, 0, 0, 0, 0, + 7, 8, 0, 0, 0, 0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c2_s_p4_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44c2_s_p4_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44c2_s_p4_0 = { + _vq_quantthresh__44c2_s_p4_0, + _vq_quantmap__44c2_s_p4_0, + 9, + 9 +}; + +static static_codebook _44c2_s_p4_0 = { + 2, 81, + _vq_lengthlist__44c2_s_p4_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44c2_s_p4_0, + NULL, + &_vq_auxt__44c2_s_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c2_s_p5_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44c2_s_p5_0[] = { + 1, 3, 3, 6, 6, 7, 7, 9, 9, 0, 7, 7, 7, 7, 7, 7, + 9, 9, 0, 7, 7, 7, 7, 7, 7, 9, 9, 0, 8, 8, 7, 7, + 8, 8,10,10, 0, 0, 0, 7, 7, 8, 8,10,10, 0, 0, 0, + 9, 9, 8, 8,10,10, 0, 0, 0, 9, 9, 8, 8,10,10, 0, + 0, 0,10,10, 9, 9,11,11, 0, 0, 0, 0, 0, 9, 9,11, + 11, +}; + +static float _vq_quantthresh__44c2_s_p5_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44c2_s_p5_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44c2_s_p5_0 = { + _vq_quantthresh__44c2_s_p5_0, + _vq_quantmap__44c2_s_p5_0, + 9, + 9 +}; + +static static_codebook _44c2_s_p5_0 = { + 2, 81, + _vq_lengthlist__44c2_s_p5_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44c2_s_p5_0, + NULL, + &_vq_auxt__44c2_s_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c2_s_p6_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44c2_s_p6_0[] = { + 1, 4, 3, 6, 6, 8, 8, 9, 9, 9, 9, 9, 9,10,10,11, + 11, 0, 7, 7, 7, 7, 8, 8, 9, 9, 9, 9,10,10,11,11, + 12,11, 0, 7, 7, 7, 7, 8, 8, 9, 9, 9, 9,10,10,11, + 11,11,12, 0, 8, 8, 7, 7, 9, 9,10,10, 9, 9,10,10, + 11,11,12,12, 0, 0, 0, 7, 7, 9, 9,10,10,10, 9,10, + 10,11,11,12,12, 0, 0, 0, 8, 8, 9, 9,10,10,10,10, + 11,11,11,11,12,12, 0, 0, 0, 8, 8, 9, 9,10,10,10, + 10,11,11,12,12,12,12, 0, 0, 0, 9, 9, 9, 9,10,10, + 10,10,11,11,12,12,12,12, 0, 0, 0, 0, 0, 9, 9,10, + 10,10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, 9, 9, + 10,10,11,11,11,11,12,12,13,13, 0, 0, 0, 0, 0, 9, + 9,10,10,11,11,11,11,12,12,13,13, 0, 0, 0, 0, 0, + 10,10,10,10,11,11,12,12,13,12,13,13, 0, 0, 0, 0, + 0, 0, 0,10,10,11,11,12,12,13,13,13,13, 0, 0, 0, + 0, 0, 0, 0,11,11,12,12,12,12,13,13,13,14, 0, 0, + 0, 0, 0, 0, 0,11,11,12,12,12,12,13,13,13,14, 0, + 0, 0, 0, 0, 0, 0,12,12,12,12,13,13,13,13,14,14, + 0, 0, 0, 0, 0, 0, 0, 0, 0,12,12,13,13,13,13,14, + 14, +}; + +static float _vq_quantthresh__44c2_s_p6_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44c2_s_p6_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44c2_s_p6_0 = { + _vq_quantthresh__44c2_s_p6_0, + _vq_quantmap__44c2_s_p6_0, + 17, + 17 +}; + +static static_codebook _44c2_s_p6_0 = { + 2, 289, + _vq_lengthlist__44c2_s_p6_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44c2_s_p6_0, + NULL, + &_vq_auxt__44c2_s_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c2_s_p7_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c2_s_p7_0[] = { + 1, 4, 4, 7, 6, 6, 7, 6, 6, 4, 7, 7,10, 9, 9,11, + 9, 9, 4, 7, 7,10, 9, 9,10, 9, 9, 7,10,10,11,10, + 11,11,10,11, 6, 9, 9,11,10,10,11,10,10, 6, 9, 9, + 11,10,11,11,10,10, 7,11,10,11,11,11,12,11,11, 6, + 9, 9,11,10,10,11,11,10, 6, 9, 9,11,10,10,12,10, + 11, +}; + +static float _vq_quantthresh__44c2_s_p7_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__44c2_s_p7_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c2_s_p7_0 = { + _vq_quantthresh__44c2_s_p7_0, + _vq_quantmap__44c2_s_p7_0, + 3, + 3 +}; + +static static_codebook _44c2_s_p7_0 = { + 4, 81, + _vq_lengthlist__44c2_s_p7_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__44c2_s_p7_0, + NULL, + &_vq_auxt__44c2_s_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c2_s_p7_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44c2_s_p7_1[] = { + 2, 3, 4, 6, 6, 7, 7, 7, 7, 7, 7, 9, 7, 7, 6, 6, + 7, 7, 8, 8, 8, 8, 9, 6, 6, 6, 6, 7, 7, 8, 8, 8, + 8,10, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8,10,10,10, 7, + 7, 7, 7, 8, 8, 8, 8,10,10,10, 7, 7, 8, 8, 8, 8, + 8, 8,10,10,10, 7, 8, 8, 8, 8, 8, 8, 8,10,10,10, + 8, 8, 8, 8, 8, 8, 8, 8,10,10,10,10,10, 8, 8, 8, + 8, 8, 8,10,10,10,10,10, 9, 9, 8, 8, 8, 8,10,10, + 10,10,10, 8, 8, 8, 8, 8, 8, +}; + +static float _vq_quantthresh__44c2_s_p7_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44c2_s_p7_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44c2_s_p7_1 = { + _vq_quantthresh__44c2_s_p7_1, + _vq_quantmap__44c2_s_p7_1, + 11, + 11 +}; + +static static_codebook _44c2_s_p7_1 = { + 2, 121, + _vq_lengthlist__44c2_s_p7_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44c2_s_p7_1, + NULL, + &_vq_auxt__44c2_s_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c2_s_p8_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c2_s_p8_0[] = { + 1, 4, 4, 6, 6, 7, 7, 7, 7, 8, 8, 9, 9, 6, 5, 5, + 7, 7, 8, 8, 8, 8, 9, 9,10,10, 7, 6, 5, 7, 7, 8, + 8, 8, 8, 9, 9,10,10, 0, 8, 8, 8, 8, 9, 9, 9, 9, + 10,10,11,11, 0, 8, 8, 8, 8, 9, 9, 9, 9,10,10,11, + 11, 0,12,12, 9, 9,10,10,10,10,11,11,11,11, 0,13, + 13, 9, 9,10,10,10,10,11,11,12,12, 0, 0, 0,10,10, + 10,10,11,11,12,12,12,13, 0, 0, 0,10,10,10,10,11, + 11,12,12,12,12, 0, 0, 0,14,14,10,11,11,11,12,12, + 13,13, 0, 0, 0,14,14,11,10,11,11,13,12,13,13, 0, + 0, 0, 0, 0,12,12,11,12,13,12,14,14, 0, 0, 0, 0, + 0,12,12,12,12,13,12,14,14, +}; + +static float _vq_quantthresh__44c2_s_p8_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44c2_s_p8_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c2_s_p8_0 = { + _vq_quantthresh__44c2_s_p8_0, + _vq_quantmap__44c2_s_p8_0, + 13, + 13 +}; + +static static_codebook _44c2_s_p8_0 = { + 2, 169, + _vq_lengthlist__44c2_s_p8_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44c2_s_p8_0, + NULL, + &_vq_auxt__44c2_s_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c2_s_p8_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c2_s_p8_1[] = { + 2, 4, 4, 5, 4, 6, 5, 5, 5, 5, 6, 5, 5, 5, 5, 6, + 5, 5, 5, 5, 6, 6, 6, 5, 5, +}; + +static float _vq_quantthresh__44c2_s_p8_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c2_s_p8_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c2_s_p8_1 = { + _vq_quantthresh__44c2_s_p8_1, + _vq_quantmap__44c2_s_p8_1, + 5, + 5 +}; + +static static_codebook _44c2_s_p8_1 = { + 2, 25, + _vq_lengthlist__44c2_s_p8_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c2_s_p8_1, + NULL, + &_vq_auxt__44c2_s_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c2_s_p9_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c2_s_p9_0[] = { + 1, 5, 4,12,12,12,12,12,12,12,12,12,12, 4, 9, 8, + 11,11,11,11,11,11,11,11,11,11, 2, 8, 7,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,10,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11, +}; + +static float _vq_quantthresh__44c2_s_p9_0[] = { + -1215.5, -994.5, -773.5, -552.5, -331.5, -110.5, 110.5, 331.5, + 552.5, 773.5, 994.5, 1215.5, +}; + +static long _vq_quantmap__44c2_s_p9_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c2_s_p9_0 = { + _vq_quantthresh__44c2_s_p9_0, + _vq_quantmap__44c2_s_p9_0, + 13, + 13 +}; + +static static_codebook _44c2_s_p9_0 = { + 2, 169, + _vq_lengthlist__44c2_s_p9_0, + 1, -514541568, 1627103232, 4, 0, + _vq_quantlist__44c2_s_p9_0, + NULL, + &_vq_auxt__44c2_s_p9_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c2_s_p9_1[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c2_s_p9_1[] = { + 1, 4, 4, 6, 6, 7, 6, 8, 8,10, 9,10,10, 6, 5, 5, + 7, 7, 8, 7,10, 9,11,11,12,13, 6, 5, 5, 7, 7, 8, + 8,10,10,11,11,13,13,18, 8, 8, 8, 8, 9, 9,10,10, + 12,12,12,13,18, 8, 8, 8, 8, 9, 9,10,10,12,12,13, + 13,18,11,11, 8, 8,10,10,11,11,12,11,13,12,18,11, + 11, 9, 7,10,10,11,11,11,12,12,13,17,17,17,10,10, + 11,11,12,12,12,10,12,12,17,17,17,11,10,11,10,13, + 12,11,12,12,12,17,17,17,15,14,11,11,12,11,13,10, + 13,12,17,17,17,14,14,12,10,11,11,13,13,13,13,17, + 17,16,17,16,13,13,12,10,13,10,14,13,17,16,17,16, + 17,13,12,12,10,13,11,14,14, +}; + +static float _vq_quantthresh__44c2_s_p9_1[] = { + -93.5, -76.5, -59.5, -42.5, -25.5, -8.5, 8.5, 25.5, + 42.5, 59.5, 76.5, 93.5, +}; + +static long _vq_quantmap__44c2_s_p9_1[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c2_s_p9_1 = { + _vq_quantthresh__44c2_s_p9_1, + _vq_quantmap__44c2_s_p9_1, + 13, + 13 +}; + +static static_codebook _44c2_s_p9_1 = { + 2, 169, + _vq_lengthlist__44c2_s_p9_1, + 1, -522616832, 1620115456, 4, 0, + _vq_quantlist__44c2_s_p9_1, + NULL, + &_vq_auxt__44c2_s_p9_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c2_s_p9_2[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44c2_s_p9_2[] = { + 2, 4, 4, 6, 6, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, + 8,10, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, + 9, 9,10, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, + 9, 9, 9,10, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, + 9, 9, 9, 9,10,10,10, 8, 7, 8, 8, 8, 8, 9, 9, 9, + 9, 9, 9, 9, 9,10,11,11, 8, 8, 8, 8, 9, 9, 9, 9, + 9, 9,10, 9, 9, 9,10,11,10, 8, 8, 8, 8, 9, 9, 9, + 9, 9, 9, 9,10,10,10,10,11,10, 8, 8, 9, 9, 9, 9, + 9, 9,10, 9, 9,10, 9,10,11,10,11,11,11, 8, 8, 9, + 9, 9, 9, 9, 9, 9, 9,10,10,11,11,11,11,11, 9, 9, + 9, 9, 9, 9,10, 9, 9, 9,10,10,11,11,11,11,11, 9, + 9, 9, 9, 9, 9, 9, 9, 9,10, 9,10,11,11,11,11,11, + 9, 9, 9, 9,10,10, 9, 9, 9,10,10,10,11,11,11,11, + 11,11,11, 9, 9, 9,10, 9, 9,10,10,10,10,11,11,10, + 11,11,11,11,10, 9,10,10, 9, 9, 9, 9,10,10,11,10, + 11,11,11,11,11, 9, 9, 9, 9,10, 9,10,10,10,10,11, + 10,11,11,11,11,11,10,10, 9, 9,10, 9,10,10,10,10, + 10,10,10,11,11,11,11,11,11, 9, 9,10, 9,10, 9,10, + 10, +}; + +static float _vq_quantthresh__44c2_s_p9_2[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44c2_s_p9_2[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44c2_s_p9_2 = { + _vq_quantthresh__44c2_s_p9_2, + _vq_quantmap__44c2_s_p9_2, + 17, + 17 +}; + +static static_codebook _44c2_s_p9_2 = { + 2, 289, + _vq_lengthlist__44c2_s_p9_2, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44c2_s_p9_2, + NULL, + &_vq_auxt__44c2_s_p9_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44c2_s_short[] = { + 11, 9,13,12,12,11,12,12,13,15, 8, 2,11, 4, 8, 5, + 7,10,12,15,13, 7,10, 9, 8, 8,10,13,17,17,11, 4, + 12, 5, 9, 5, 8,11,14,16,12, 6, 8, 7, 6, 6, 8,11, + 13,16,11, 4, 9, 5, 6, 4, 6,10,13,16,11, 6,11, 7, + 7, 6, 7,10,13,15,13, 9,12, 9, 8, 6, 8,10,12,14, + 14,10,10, 8, 6, 5, 6, 9,11,13,15,11,11, 9, 6, 5, + 6, 8, 9,12, +}; + +static static_codebook _huff_book__44c2_s_short = { + 2, 100, + _huff_lengthlist__44c2_s_short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44c3_s_long[] = { + 5, 6,11,11,11,11,10,10,12,11, 5, 2,11, 5, 6, 6, + 7, 9,11,13,13,10, 7,11, 6, 7, 8, 9,10,12,11, 5, + 11, 6, 8, 7, 9,11,14,15,11, 6, 6, 8, 4, 5, 7, 8, + 10,13,10, 5, 7, 7, 5, 5, 6, 8,10,11,10, 7, 7, 8, + 6, 5, 5, 7, 9, 9,11, 8, 8,11, 8, 7, 6, 6, 7, 9, + 12,11,10,13, 9, 9, 7, 7, 7, 9,11,13,12,15,12,11, + 9, 8, 8, 8, +}; + +static static_codebook _huff_book__44c3_s_long = { + 2, 100, + _huff_lengthlist__44c3_s_long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44c3_s_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c3_s_p1_0[] = { + 2, 4, 4, 0, 0, 0, 0, 0, 0, 5, 6, 6, 0, 0, 0, 0, + 0, 0, 5, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 7, 8, 8, 0, 0, 0, + 0, 0, 0, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 6, 8, 7, 0, 0, + 0, 0, 0, 0, 7, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 5, 7, 7, 0, 0, 0, 0, + 0, 0, 7, 8, 8, 0, 0, 0, 0, 0, 0, 7, 8, 8, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 8, 8, 0, 0, 0, + 0, 0, 0, 8, 8, 9, 0, 0, 0, 0, 0, 0, 8, 9, 9, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 8, 8, 0, 0, + 0, 0, 0, 0, 7, 9, 8, 0, 0, 0, 0, 0, 0, 8, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 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, 5, 7, 7, 0, 0, 0, 0, 0, 0, 7, 8, 8, 0, 0, + 0, 0, 0, 0, 7, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 6, 8, 8, 0, 0, 0, 0, 0, 0, 8, 9, 9, 0, + 0, 0, 0, 0, 0, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 8, 8, 0, 0, 0, 0, 0, 0, 8, 9, 9, + 0, 0, 0, 0, 0, 0, 8, 9, 8, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c3_s_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44c3_s_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c3_s_p1_0 = { + _vq_quantthresh__44c3_s_p1_0, + _vq_quantmap__44c3_s_p1_0, + 3, + 3 +}; + +static static_codebook _44c3_s_p1_0 = { + 8, 6561, + _vq_lengthlist__44c3_s_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44c3_s_p1_0, + NULL, + &_vq_auxt__44c3_s_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c3_s_p2_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c3_s_p2_0[] = { + 2, 5, 5, 0, 0, 0, 5, 5, 0, 0, 0, 5, 5, 0, 0, 0, + 7, 8, 0, 0, 0, 0, 0, 0, 0, 5, 6, 6, 0, 0, 0, 7, + 7, 0, 0, 0, 7, 7, 0, 0, 0,10,10, 0, 0, 0, 0, 0, + 0, 0, 5, 6, 6, 0, 0, 0, 7, 7, 0, 0, 0, 7, 7, 0, + 0, 0,10,10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, 0, 0, 7, 7, 0, 0, + 0, 7, 7, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 5, + 7, 7, 0, 0, 0, 7, 7, 0, 0, 0, 7, 7, 0, 0, 0, 9, + 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 5, 7, 7, 0, 0, 0, 7, 7, 0, 0, 0, 7, 7, + 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, + 0, 0, 7, 7, 0, 0, 0, 7, 7, 0, 0, 0, 9, 9, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8,10,10, 0, 0, 0, 9, 9, 0, 0, 0, 9, 9, 0, 0, 0, + 10,10, 0, 0, 0, 0, 0, 0, 0, 8,10,10, 0, 0, 0, 9, + 9, 0, 0, 0, 9, 9, 0, 0, 0,10,10, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, +}; + +static float _vq_quantthresh__44c3_s_p2_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c3_s_p2_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c3_s_p2_0 = { + _vq_quantthresh__44c3_s_p2_0, + _vq_quantmap__44c3_s_p2_0, + 5, + 5 +}; + +static static_codebook _44c3_s_p2_0 = { + 4, 625, + _vq_lengthlist__44c3_s_p2_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c3_s_p2_0, + NULL, + &_vq_auxt__44c3_s_p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c3_s_p3_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c3_s_p3_0[] = { + 2, 4, 3, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 6, 6, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 4, 4, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 6, 7, 9, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c3_s_p3_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c3_s_p3_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c3_s_p3_0 = { + _vq_quantthresh__44c3_s_p3_0, + _vq_quantmap__44c3_s_p3_0, + 5, + 5 +}; + +static static_codebook _44c3_s_p3_0 = { + 4, 625, + _vq_lengthlist__44c3_s_p3_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c3_s_p3_0, + NULL, + &_vq_auxt__44c3_s_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c3_s_p4_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44c3_s_p4_0[] = { + 2, 3, 3, 6, 6, 0, 0, 0, 0, 0, 4, 4, 6, 6, 0, 0, + 0, 0, 0, 4, 4, 6, 6, 0, 0, 0, 0, 0, 5, 5, 6, 6, + 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, + 7, 8, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c3_s_p4_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44c3_s_p4_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44c3_s_p4_0 = { + _vq_quantthresh__44c3_s_p4_0, + _vq_quantmap__44c3_s_p4_0, + 9, + 9 +}; + +static static_codebook _44c3_s_p4_0 = { + 2, 81, + _vq_lengthlist__44c3_s_p4_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44c3_s_p4_0, + NULL, + &_vq_auxt__44c3_s_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c3_s_p5_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44c3_s_p5_0[] = { + 1, 3, 4, 6, 6, 7, 7, 9, 9, 0, 5, 5, 7, 7, 7, 8, + 9, 9, 0, 5, 5, 7, 7, 8, 8, 9, 9, 0, 7, 7, 8, 8, + 8, 8,10,10, 0, 0, 0, 8, 8, 8, 8,10,10, 0, 0, 0, + 9, 9, 9, 9,10,10, 0, 0, 0, 9, 9, 9, 9,10,10, 0, + 0, 0,10,10,10,10,11,11, 0, 0, 0, 0, 0,10,10,11, + 11, +}; + +static float _vq_quantthresh__44c3_s_p5_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44c3_s_p5_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44c3_s_p5_0 = { + _vq_quantthresh__44c3_s_p5_0, + _vq_quantmap__44c3_s_p5_0, + 9, + 9 +}; + +static static_codebook _44c3_s_p5_0 = { + 2, 81, + _vq_lengthlist__44c3_s_p5_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44c3_s_p5_0, + NULL, + &_vq_auxt__44c3_s_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c3_s_p6_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44c3_s_p6_0[] = { + 2, 3, 3, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9,10,10,11, + 10, 0, 5, 5, 7, 7, 8, 8, 9, 9, 9, 9,10,10,10,10, + 11,11, 0, 5, 5, 7, 7, 8, 8, 9, 9, 9, 9,10,10,10, + 10,11,11, 0, 6, 6, 7, 7, 8, 8, 9, 9, 9, 9,10,10, + 11,11,11,11, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10, + 10,11,11,11,12, 0, 0, 0, 8, 8, 8, 8, 9, 9, 9, 9, + 10,10,11,11,12,12, 0, 0, 0, 8, 8, 8, 8, 9, 9, 9, + 9,10,10,11,11,12,12, 0, 0, 0, 9, 9, 9, 9,10,10, + 10,10,11,10,11,11,12,12, 0, 0, 0, 0, 0, 9, 9,10, + 10,10,10,11,11,11,11,12,12, 0, 0, 0, 0, 0, 9, 8, + 9, 9,10,10,11,11,12,12,12,12, 0, 0, 0, 0, 0, 8, + 8, 9, 9,10,10,11,11,12,11,12,12, 0, 0, 0, 0, 0, + 9,10,10,10,11,11,11,11,12,12,13,13, 0, 0, 0, 0, + 0, 0, 0,10,10,10,10,11,11,12,12,13,13, 0, 0, 0, + 0, 0, 0, 0,11,11,11,11,12,12,12,12,13,13, 0, 0, + 0, 0, 0, 0, 0,11,11,11,11,12,12,12,12,13,13, 0, + 0, 0, 0, 0, 0, 0,11,11,12,12,12,12,13,13,13,13, + 0, 0, 0, 0, 0, 0, 0, 0, 0,12,12,12,12,13,13,13, + 13, +}; + +static float _vq_quantthresh__44c3_s_p6_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44c3_s_p6_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44c3_s_p6_0 = { + _vq_quantthresh__44c3_s_p6_0, + _vq_quantmap__44c3_s_p6_0, + 17, + 17 +}; + +static static_codebook _44c3_s_p6_0 = { + 2, 289, + _vq_lengthlist__44c3_s_p6_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44c3_s_p6_0, + NULL, + &_vq_auxt__44c3_s_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c3_s_p7_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c3_s_p7_0[] = { + 1, 4, 4, 7, 6, 6, 7, 6, 6, 4, 7, 7,10, 9, 9,11, + 9, 9, 4, 7, 7,10, 9, 9,11, 9, 9, 7,10,10,11,11, + 10,12,11,11, 6, 9, 9,11,10,10,11,10,10, 6, 9, 9, + 11,10,10,11,10,10, 7,11,11,11,11,11,12,11,11, 6, + 9, 9,11,10,10,11,10,10, 6, 9, 9,11,10,10,11,10, + 10, +}; + +static float _vq_quantthresh__44c3_s_p7_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__44c3_s_p7_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c3_s_p7_0 = { + _vq_quantthresh__44c3_s_p7_0, + _vq_quantmap__44c3_s_p7_0, + 3, + 3 +}; + +static static_codebook _44c3_s_p7_0 = { + 4, 81, + _vq_lengthlist__44c3_s_p7_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__44c3_s_p7_0, + NULL, + &_vq_auxt__44c3_s_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c3_s_p7_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44c3_s_p7_1[] = { + 2, 4, 4, 6, 6, 7, 7, 7, 7, 8, 8,10, 5, 5, 6, 6, + 7, 7, 8, 8, 8, 8,10, 5, 5, 6, 6, 7, 7, 8, 8, 8, + 8,10, 6, 6, 7, 7, 8, 8, 8, 8, 8, 8,10,10,10, 7, + 7, 8, 7, 8, 8, 8, 8,10,10,10, 8, 8, 8, 8, 8, 8, + 8, 8,10,10,10, 7, 8, 8, 8, 8, 8, 8, 8,10,10,10, + 8, 8, 8, 8, 8, 8, 8, 8,10,10,10,10,10, 8, 8, 8, + 8, 8, 8,10,10,10,10,10, 9, 9, 8, 8, 9, 8,10,10, + 10,10,10, 8, 8, 8, 8, 8, 8, +}; + +static float _vq_quantthresh__44c3_s_p7_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44c3_s_p7_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44c3_s_p7_1 = { + _vq_quantthresh__44c3_s_p7_1, + _vq_quantmap__44c3_s_p7_1, + 11, + 11 +}; + +static static_codebook _44c3_s_p7_1 = { + 2, 121, + _vq_lengthlist__44c3_s_p7_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44c3_s_p7_1, + NULL, + &_vq_auxt__44c3_s_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c3_s_p8_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c3_s_p8_0[] = { + 1, 4, 4, 6, 6, 7, 7, 8, 8, 9, 9,10,10, 6, 5, 5, + 7, 7, 8, 8, 8, 8, 9, 9,10,10, 7, 5, 5, 7, 7, 8, + 8, 8, 8, 9, 9,11,10, 0, 8, 8, 8, 8, 9, 9, 9, 9, + 10,10,11,11, 0, 8, 8, 8, 8, 9, 9, 9, 9,10,10,11, + 11, 0,12,12, 9, 9,10,10,10,10,11,11,11,12, 0,13, + 13, 9, 9,10,10,10,10,11,11,12,12, 0, 0, 0,10,10, + 10,10,11,11,12,12,12,12, 0, 0, 0,10,10,10,10,11, + 11,12,12,12,12, 0, 0, 0,14,14,11,11,11,11,12,12, + 13,13, 0, 0, 0,14,14,11,11,11,11,12,12,13,13, 0, + 0, 0, 0, 0,12,12,12,12,13,13,14,13, 0, 0, 0, 0, + 0,13,13,12,12,13,12,14,13, +}; + +static float _vq_quantthresh__44c3_s_p8_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44c3_s_p8_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c3_s_p8_0 = { + _vq_quantthresh__44c3_s_p8_0, + _vq_quantmap__44c3_s_p8_0, + 13, + 13 +}; + +static static_codebook _44c3_s_p8_0 = { + 2, 169, + _vq_lengthlist__44c3_s_p8_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44c3_s_p8_0, + NULL, + &_vq_auxt__44c3_s_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c3_s_p8_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c3_s_p8_1[] = { + 2, 4, 4, 5, 5, 6, 5, 5, 5, 5, 6, 4, 5, 5, 5, 6, + 5, 5, 5, 5, 6, 6, 6, 5, 5, +}; + +static float _vq_quantthresh__44c3_s_p8_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c3_s_p8_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c3_s_p8_1 = { + _vq_quantthresh__44c3_s_p8_1, + _vq_quantmap__44c3_s_p8_1, + 5, + 5 +}; + +static static_codebook _44c3_s_p8_1 = { + 2, 25, + _vq_lengthlist__44c3_s_p8_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c3_s_p8_1, + NULL, + &_vq_auxt__44c3_s_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c3_s_p9_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c3_s_p9_0[] = { + 1, 4, 4,12,12,12,12,12,12,12,12,12,12, 4, 9, 8, + 12,12,12,12,12,12,12,12,12,12, 2, 9, 7,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,11,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11, +}; + +static float _vq_quantthresh__44c3_s_p9_0[] = { + -1402.5, -1147.5, -892.5, -637.5, -382.5, -127.5, 127.5, 382.5, + 637.5, 892.5, 1147.5, 1402.5, +}; + +static long _vq_quantmap__44c3_s_p9_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c3_s_p9_0 = { + _vq_quantthresh__44c3_s_p9_0, + _vq_quantmap__44c3_s_p9_0, + 13, + 13 +}; + +static static_codebook _44c3_s_p9_0 = { + 2, 169, + _vq_lengthlist__44c3_s_p9_0, + 1, -514332672, 1627381760, 4, 0, + _vq_quantlist__44c3_s_p9_0, + NULL, + &_vq_auxt__44c3_s_p9_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c3_s_p9_1[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__44c3_s_p9_1[] = { + 1, 4, 4, 6, 6, 7, 7, 8, 7, 9, 9,10,10,10,10, 6, + 5, 5, 7, 7, 8, 8,10, 8,11,10,12,12,13,13, 6, 5, + 5, 7, 7, 8, 8,10, 9,11,11,12,12,13,12,18, 8, 8, + 8, 8, 9, 9,10, 9,11,10,12,12,13,13,18, 8, 8, 8, + 8, 9, 9,10,10,11,11,13,12,14,13,18,11,11, 9, 9, + 10,10,11,11,11,12,13,12,13,14,18,11,11, 9, 8,11, + 10,11,11,11,11,12,12,14,13,18,18,18,10,11,10,11, + 12,12,12,12,13,12,14,13,18,18,18,10,11,11, 9,12, + 11,12,12,12,13,13,13,18,18,17,14,14,11,11,12,12, + 13,12,14,12,14,13,18,18,18,14,14,11,10,12, 9,12, + 13,13,13,13,13,18,18,17,16,18,13,13,12,12,13,11, + 14,12,14,14,17,18,18,17,18,13,12,13,10,12,11,14, + 14,14,14,17,18,18,18,18,15,16,12,12,13,10,14,12, + 14,15,18,18,18,16,17,16,14,12,11,13,10,13,13,14, + 15, +}; + +static float _vq_quantthresh__44c3_s_p9_1[] = { + -110.5, -93.5, -76.5, -59.5, -42.5, -25.5, -8.5, 8.5, + 25.5, 42.5, 59.5, 76.5, 93.5, 110.5, +}; + +static long _vq_quantmap__44c3_s_p9_1[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__44c3_s_p9_1 = { + _vq_quantthresh__44c3_s_p9_1, + _vq_quantmap__44c3_s_p9_1, + 15, + 15 +}; + +static static_codebook _44c3_s_p9_1 = { + 2, 225, + _vq_lengthlist__44c3_s_p9_1, + 1, -522338304, 1620115456, 4, 0, + _vq_quantlist__44c3_s_p9_1, + NULL, + &_vq_auxt__44c3_s_p9_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c3_s_p9_2[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44c3_s_p9_2[] = { + 2, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, + 8,10, 6, 6, 7, 7, 8, 7, 8, 8, 8, 8, 8, 9, 9, 9, + 9, 9,10, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, + 9, 9, 9,10, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, + 9, 9, 9, 9,10,10,10, 7, 7, 8, 8, 8, 9, 9, 9, 9, + 9, 9, 9, 9, 9,11,11,11, 8, 8, 8, 8, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9,10,10,10, 8, 8, 8, 8, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9,10,10,10, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9,10, 9,10,10,10,11,11, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9,11,10,11,11,11, 9, 9, + 9, 9, 9, 9,10,10, 9, 9,10, 9,11,10,11,11,11, 9, + 9, 9, 9, 9, 9, 9, 9,10,10,10, 9,11,11,11,11,11, + 9, 9, 9, 9,10,10, 9, 9, 9, 9,10, 9,11,11,11,11, + 11,11,11, 9, 9, 9, 9, 9, 9,10,10,10,10,11,11,11, + 11,11,11,11,10, 9,10,10, 9,10, 9, 9,10, 9,11,10, + 10,11,11,11,11, 9,10, 9, 9, 9, 9,10,10,10,10,11, + 11,11,11,11,11,10,10,10, 9, 9,10, 9,10, 9,10,10, + 10,10,11,11,11,11,11,11,11, 9, 9, 9, 9, 9,10,10, + 10, +}; + +static float _vq_quantthresh__44c3_s_p9_2[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44c3_s_p9_2[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44c3_s_p9_2 = { + _vq_quantthresh__44c3_s_p9_2, + _vq_quantmap__44c3_s_p9_2, + 17, + 17 +}; + +static static_codebook _44c3_s_p9_2 = { + 2, 289, + _vq_lengthlist__44c3_s_p9_2, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44c3_s_p9_2, + NULL, + &_vq_auxt__44c3_s_p9_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44c3_s_short[] = { + 10, 9,13,11,14,10,12,13,13,14, 7, 2,12, 5,10, 5, + 7,10,12,14,12, 6, 9, 8, 7, 7, 9,11,13,16,10, 4, + 12, 5,10, 6, 8,12,14,16,12, 6, 8, 7, 6, 5, 7,11, + 12,16,10, 4, 8, 5, 6, 4, 6, 9,13,16,10, 6,10, 7, + 7, 6, 7, 9,13,15,12, 9,11, 9, 8, 6, 7,10,12,14, + 14,11,10, 9, 6, 5, 6, 9,11,13,15,13,11,10, 6, 5, + 6, 8, 9,11, +}; + +static static_codebook _huff_book__44c3_s_short = { + 2, 100, + _huff_lengthlist__44c3_s_short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44c4_s_long[] = { + 4, 7,11,11,11,11,10,11,12,11, 5, 2,11, 5, 6, 6, + 7, 9,11,12,11, 9, 6,10, 6, 7, 8, 9,10,11,11, 5, + 11, 7, 8, 8, 9,11,13,14,11, 6, 5, 8, 4, 5, 7, 8, + 10,11,10, 6, 7, 7, 5, 5, 6, 8, 9,11,10, 7, 8, 9, + 6, 6, 6, 7, 8, 9,11, 9, 9,11, 7, 7, 6, 6, 7, 9, + 12,12,10,13, 9, 8, 7, 7, 7, 8,11,13,11,14,11,10, + 9, 8, 7, 7, +}; + +static static_codebook _huff_book__44c4_s_long = { + 2, 100, + _huff_lengthlist__44c4_s_long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44c4_s_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c4_s_p1_0[] = { + 2, 4, 4, 0, 0, 0, 0, 0, 0, 5, 6, 6, 0, 0, 0, 0, + 0, 0, 5, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 7, 8, 8, 0, 0, 0, + 0, 0, 0, 6, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 6, 8, 7, 0, 0, + 0, 0, 0, 0, 7, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 5, 7, 7, 0, 0, 0, 0, + 0, 0, 7, 8, 8, 0, 0, 0, 0, 0, 0, 7, 8, 8, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 8, 8, 0, 0, 0, + 0, 0, 0, 8, 9, 9, 0, 0, 0, 0, 0, 0, 8, 9, 9, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 8, 8, 0, 0, + 0, 0, 0, 0, 8, 9, 8, 0, 0, 0, 0, 0, 0, 8, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 7, 7, 0, 0, 0, 0, 0, 0, 7, 8, 8, 0, 0, + 0, 0, 0, 0, 7, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 6, 8, 8, 0, 0, 0, 0, 0, 0, 8, 9, 9, 0, + 0, 0, 0, 0, 0, 8, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 8, 8, 0, 0, 0, 0, 0, 0, 8, 9, 9, + 0, 0, 0, 0, 0, 0, 8, 9, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c4_s_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44c4_s_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c4_s_p1_0 = { + _vq_quantthresh__44c4_s_p1_0, + _vq_quantmap__44c4_s_p1_0, + 3, + 3 +}; + +static static_codebook _44c4_s_p1_0 = { + 8, 6561, + _vq_lengthlist__44c4_s_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44c4_s_p1_0, + NULL, + &_vq_auxt__44c4_s_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c4_s_p2_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c4_s_p2_0[] = { + 2, 5, 5, 0, 0, 0, 5, 5, 0, 0, 0, 5, 5, 0, 0, 0, + 7, 7, 0, 0, 0, 0, 0, 0, 0, 5, 6, 6, 0, 0, 0, 7, + 7, 0, 0, 0, 7, 7, 0, 0, 0,10,10, 0, 0, 0, 0, 0, + 0, 0, 5, 6, 6, 0, 0, 0, 7, 7, 0, 0, 0, 7, 7, 0, + 0, 0,10,10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 5, 8, 7, 0, 0, 0, 7, 7, 0, 0, + 0, 7, 7, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 5, + 7, 8, 0, 0, 0, 7, 7, 0, 0, 0, 7, 7, 0, 0, 0, 9, + 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 5, 7, 7, 0, 0, 0, 7, 7, 0, 0, 0, 7, 7, + 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, + 0, 0, 7, 7, 0, 0, 0, 7, 7, 0, 0, 0, 9, 9, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 7,10,10, 0, 0, 0, 9, 9, 0, 0, 0, 9, 9, 0, 0, 0, + 10,10, 0, 0, 0, 0, 0, 0, 0, 8,10,10, 0, 0, 0, 9, + 9, 0, 0, 0, 9, 9, 0, 0, 0,10,10, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, +}; + +static float _vq_quantthresh__44c4_s_p2_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c4_s_p2_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c4_s_p2_0 = { + _vq_quantthresh__44c4_s_p2_0, + _vq_quantmap__44c4_s_p2_0, + 5, + 5 +}; + +static static_codebook _44c4_s_p2_0 = { + 4, 625, + _vq_lengthlist__44c4_s_p2_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c4_s_p2_0, + NULL, + &_vq_auxt__44c4_s_p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c4_s_p3_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c4_s_p3_0[] = { + 2, 3, 3, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 4, 6, 6, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 4, 5, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 6, 7, 9, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c4_s_p3_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c4_s_p3_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c4_s_p3_0 = { + _vq_quantthresh__44c4_s_p3_0, + _vq_quantmap__44c4_s_p3_0, + 5, + 5 +}; + +static static_codebook _44c4_s_p3_0 = { + 4, 625, + _vq_lengthlist__44c4_s_p3_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c4_s_p3_0, + NULL, + &_vq_auxt__44c4_s_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c4_s_p4_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44c4_s_p4_0[] = { + 2, 3, 3, 6, 6, 0, 0, 0, 0, 0, 4, 4, 6, 6, 0, 0, + 0, 0, 0, 4, 4, 6, 6, 0, 0, 0, 0, 0, 5, 5, 6, 6, + 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, + 7, 8, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c4_s_p4_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44c4_s_p4_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44c4_s_p4_0 = { + _vq_quantthresh__44c4_s_p4_0, + _vq_quantmap__44c4_s_p4_0, + 9, + 9 +}; + +static static_codebook _44c4_s_p4_0 = { + 2, 81, + _vq_lengthlist__44c4_s_p4_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44c4_s_p4_0, + NULL, + &_vq_auxt__44c4_s_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c4_s_p5_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44c4_s_p5_0[] = { + 2, 3, 3, 6, 6, 7, 7, 9, 9, 0, 4, 4, 6, 6, 7, 7, + 9, 9, 0, 4, 5, 6, 6, 7, 7, 9, 9, 0, 6, 6, 7, 7, + 8, 8,10,10, 0, 0, 0, 7, 7, 8, 8,10, 9, 0, 0, 0, + 9, 8, 8, 8,10,10, 0, 0, 0, 8, 8, 8, 8,10,10, 0, + 0, 0,10,10, 9, 9,11,11, 0, 0, 0, 0, 0, 9, 9,10, + 10, +}; + +static float _vq_quantthresh__44c4_s_p5_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44c4_s_p5_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44c4_s_p5_0 = { + _vq_quantthresh__44c4_s_p5_0, + _vq_quantmap__44c4_s_p5_0, + 9, + 9 +}; + +static static_codebook _44c4_s_p5_0 = { + 2, 81, + _vq_lengthlist__44c4_s_p5_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44c4_s_p5_0, + NULL, + &_vq_auxt__44c4_s_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c4_s_p6_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44c4_s_p6_0[] = { + 2, 4, 4, 6, 6, 8, 8, 9, 9, 8, 8, 9, 9,10,10,11, + 11, 0, 4, 4, 6, 6, 8, 8, 9, 9, 9, 9,10,10,11,11, + 11,11, 0, 4, 4, 7, 6, 8, 8, 9, 9, 9, 9,10,10,11, + 11,11,11, 0, 6, 6, 7, 7, 8, 8, 9, 9, 9, 9,10,10, + 11,11,11,12, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10, + 10,11,11,12,12, 0, 0, 0, 8, 8, 8, 8, 9, 9, 9, 9, + 10,10,11,11,12,12, 0, 0, 0, 8, 8, 8, 8, 9, 9, 9, + 9,10,10,11,11,12,12, 0, 0, 0, 9, 9, 9, 9,10,10, + 10,10,11,11,11,11,12,12, 0, 0, 0, 0, 0, 9, 9,10, + 10,10,10,11,11,11,11,12,12, 0, 0, 0, 0, 0, 9, 9, + 9,10,10,10,11,11,11,11,12,12, 0, 0, 0, 0, 0, 9, + 9, 9, 9,10,10,11,11,11,12,12,12, 0, 0, 0, 0, 0, + 10,10,10,10,11,11,11,11,12,12,13,12, 0, 0, 0, 0, + 0, 0, 0,10,10,11,11,11,11,12,12,12,12, 0, 0, 0, + 0, 0, 0, 0,11,11,11,11,12,12,12,12,13,13, 0, 0, + 0, 0, 0, 0, 0,11,11,11,11,12,12,12,12,13,13, 0, + 0, 0, 0, 0, 0, 0,12,12,12,12,12,12,13,13,13,13, + 0, 0, 0, 0, 0, 0, 0, 0, 0,12,12,12,12,12,13,13, + 13, +}; + +static float _vq_quantthresh__44c4_s_p6_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44c4_s_p6_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44c4_s_p6_0 = { + _vq_quantthresh__44c4_s_p6_0, + _vq_quantmap__44c4_s_p6_0, + 17, + 17 +}; + +static static_codebook _44c4_s_p6_0 = { + 2, 289, + _vq_lengthlist__44c4_s_p6_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44c4_s_p6_0, + NULL, + &_vq_auxt__44c4_s_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c4_s_p7_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c4_s_p7_0[] = { + 1, 4, 4, 7, 6, 6, 7, 6, 6, 4, 7, 7,10, 9, 9,11, + 9, 9, 4, 7, 7,10, 9, 9,11, 9, 9, 7,10,10,11,11, + 10,11,11,11, 6, 9, 9,11,10,10,11,10,10, 6, 9, 9, + 11,10,10,11,10,10, 7,11,11,12,11,11,12,11,11, 6, + 9, 9,11,10,10,11,10,10, 6, 9, 9,11,10,10,11,10, + 10, +}; + +static float _vq_quantthresh__44c4_s_p7_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__44c4_s_p7_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c4_s_p7_0 = { + _vq_quantthresh__44c4_s_p7_0, + _vq_quantmap__44c4_s_p7_0, + 3, + 3 +}; + +static static_codebook _44c4_s_p7_0 = { + 4, 81, + _vq_lengthlist__44c4_s_p7_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__44c4_s_p7_0, + NULL, + &_vq_auxt__44c4_s_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c4_s_p7_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44c4_s_p7_1[] = { + 2, 4, 4, 6, 6, 7, 7, 7, 7, 8, 8,10, 5, 5, 6, 6, + 7, 7, 8, 8, 8, 8,10, 5, 5, 6, 6, 7, 7, 8, 8, 8, + 8,10, 6, 6, 7, 7, 8, 8, 8, 8, 8, 8,10,10,10, 7, + 7, 8, 8, 8, 8, 8, 8,10,10,10, 8, 7, 8, 8, 8, 8, + 8, 8,10,10,10, 7, 7, 8, 8, 8, 8, 8, 8,10,10,10, + 8, 8, 8, 8, 8, 8, 8, 8,10,10,10,10,10, 8, 8, 8, + 8, 8, 8,10,10,10,10,10, 9, 9, 8, 8, 9, 8,10,10, + 10,10,10, 8, 8, 8, 8, 9, 9, +}; + +static float _vq_quantthresh__44c4_s_p7_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44c4_s_p7_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44c4_s_p7_1 = { + _vq_quantthresh__44c4_s_p7_1, + _vq_quantmap__44c4_s_p7_1, + 11, + 11 +}; + +static static_codebook _44c4_s_p7_1 = { + 2, 121, + _vq_lengthlist__44c4_s_p7_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44c4_s_p7_1, + NULL, + &_vq_auxt__44c4_s_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c4_s_p8_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c4_s_p8_0[] = { + 1, 4, 4, 6, 6, 7, 7, 8, 8, 9, 9,10,10, 6, 5, 5, + 7, 7, 8, 8, 8, 8, 9,10,11,11, 7, 5, 5, 7, 7, 8, + 8, 9, 9,10,10,11,11, 0, 8, 8, 8, 8, 9, 9, 9, 9, + 10,10,11,11, 0, 8, 8, 8, 8, 9, 9, 9, 9,10,10,11, + 11, 0,12,12, 9, 9, 9, 9,10,10,10,10,11,11, 0,13, + 13, 9, 9,10, 9,10,10,11,11,11,12, 0, 0, 0,10,10, + 10,10,10,10,11,11,12,12, 0, 0, 0,10,10,10,10,10, + 10,11,11,12,12, 0, 0, 0,14,14,11,11,11,11,12,12, + 12,12, 0, 0, 0,14,14,11,11,11,11,12,12,12,13, 0, + 0, 0, 0, 0,12,12,12,12,12,12,13,13, 0, 0, 0, 0, + 0,13,12,12,12,12,12,13,13, +}; + +static float _vq_quantthresh__44c4_s_p8_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44c4_s_p8_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c4_s_p8_0 = { + _vq_quantthresh__44c4_s_p8_0, + _vq_quantmap__44c4_s_p8_0, + 13, + 13 +}; + +static static_codebook _44c4_s_p8_0 = { + 2, 169, + _vq_lengthlist__44c4_s_p8_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44c4_s_p8_0, + NULL, + &_vq_auxt__44c4_s_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c4_s_p8_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c4_s_p8_1[] = { + 2, 4, 4, 5, 5, 6, 5, 5, 5, 5, 6, 5, 4, 5, 5, 6, + 5, 5, 5, 5, 6, 6, 6, 5, 5, +}; + +static float _vq_quantthresh__44c4_s_p8_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c4_s_p8_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c4_s_p8_1 = { + _vq_quantthresh__44c4_s_p8_1, + _vq_quantmap__44c4_s_p8_1, + 5, + 5 +}; + +static static_codebook _44c4_s_p8_1 = { + 2, 25, + _vq_lengthlist__44c4_s_p8_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c4_s_p8_1, + NULL, + &_vq_auxt__44c4_s_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c4_s_p9_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c4_s_p9_0[] = { + 1, 3, 3,12,12,12,12,12,12,12,12,12,12, 4, 7, 7, + 12,12,12,12,12,12,12,12,12,12, 3, 8, 8,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12, +}; + +static float _vq_quantthresh__44c4_s_p9_0[] = { + -1732.5, -1417.5, -1102.5, -787.5, -472.5, -157.5, 157.5, 472.5, + 787.5, 1102.5, 1417.5, 1732.5, +}; + +static long _vq_quantmap__44c4_s_p9_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c4_s_p9_0 = { + _vq_quantthresh__44c4_s_p9_0, + _vq_quantmap__44c4_s_p9_0, + 13, + 13 +}; + +static static_codebook _44c4_s_p9_0 = { + 2, 169, + _vq_lengthlist__44c4_s_p9_0, + 1, -513964032, 1628680192, 4, 0, + _vq_quantlist__44c4_s_p9_0, + NULL, + &_vq_auxt__44c4_s_p9_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c4_s_p9_1[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__44c4_s_p9_1[] = { + 1, 4, 4, 5, 5, 7, 7, 9, 8,10, 9,10,10,10,10, 6, + 5, 5, 7, 7, 9, 8,10, 9,11,10,12,12,13,13, 6, 5, + 5, 7, 7, 9, 9,10,10,11,11,12,12,12,13,19, 8, 8, + 8, 8, 9, 9,10,10,12,11,12,12,13,13,19, 8, 8, 8, + 8, 9, 9,11,11,12,12,13,13,13,13,19,12,12, 9, 9, + 11,11,11,11,12,11,13,12,13,13,18,12,12, 9, 9,11, + 10,11,11,12,12,12,13,13,14,19,18,18,11,11,11,11, + 12,12,13,12,13,13,14,14,16,18,18,11,11,11,10,12, + 11,13,13,13,13,13,14,17,18,18,14,15,11,12,12,13, + 13,13,13,14,14,14,18,18,18,15,15,12,10,13,10,13, + 13,13,13,13,14,18,17,18,17,18,12,13,12,13,13,13, + 14,14,16,14,18,17,18,18,17,13,12,13,10,12,12,14, + 14,14,14,17,18,18,18,18,14,15,12,12,13,12,14,14, + 15,15,18,18,18,17,18,15,14,12,11,12,12,14,14,14, + 15, +}; + +static float _vq_quantthresh__44c4_s_p9_1[] = { + -136.5, -115.5, -94.5, -73.5, -52.5, -31.5, -10.5, 10.5, + 31.5, 52.5, 73.5, 94.5, 115.5, 136.5, +}; + +static long _vq_quantmap__44c4_s_p9_1[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__44c4_s_p9_1 = { + _vq_quantthresh__44c4_s_p9_1, + _vq_quantmap__44c4_s_p9_1, + 15, + 15 +}; + +static static_codebook _44c4_s_p9_1 = { + 2, 225, + _vq_lengthlist__44c4_s_p9_1, + 1, -520986624, 1620377600, 4, 0, + _vq_quantlist__44c4_s_p9_1, + NULL, + &_vq_auxt__44c4_s_p9_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c4_s_p9_2[] = { + 10, + 9, + 11, + 8, + 12, + 7, + 13, + 6, + 14, + 5, + 15, + 4, + 16, + 3, + 17, + 2, + 18, + 1, + 19, + 0, + 20, +}; + +static long _vq_lengthlist__44c4_s_p9_2[] = { + 2, 5, 5, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, + 8, 9, 9, 9, 9,11, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, + 9, 9, 9, 9, 9, 9,10,10,10,10,11, 6, 6, 7, 7, 8, + 8, 8, 8, 9, 9, 9, 9, 9, 9,10, 9,10,10,10,10,11, + 7, 7, 7, 7, 8, 8, 9, 9, 9, 9, 9, 9, 9,10,10,10, + 10,10,10,10,12,11,11, 7, 7, 8, 8, 9, 9, 9, 9, 9, + 9,10,10,10,10,10,10,10,10,12,11,12, 8, 8, 8, 8, + 9, 9, 9, 9, 9,10,10,10,10,10,10,10,10,10,11,11, + 11, 8, 8, 8, 8, 9, 9, 9, 9,10,10,10,10,10,10,10, + 10,10,10,11,11,12, 9, 9, 9, 9, 9, 9,10, 9,10,10, + 10,10,10,10,10,10,10,10,11,11,11,11,11, 9, 9, 9, + 9,10,10,10,10,10,10,10,10,10,10,10,10,11,12,11, + 11,11, 9, 9, 9,10,10,10,10,10,10,10,10,10,10,10, + 10,10,11,11,11,11,11, 9, 9, 9, 9,10,10,10,10,10, + 10,10,10,10,10,10,10,11,11,11,12,12,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,11,12,11,12, + 11,11,11, 9,10,10,10,10,10,10,10,10,10,10,10,10, + 10,11,12,11,11,11,11,11,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,11,11,11,12,11,11,11,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,12,11,11,12,11, + 11,11,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 11,11,11,11,11,11,11,11,11,10,10,10,10,10,10,10, + 10,10,10,10,10,11,11,11,11,12,12,11,11,11,11,11, + 11,11,10,10,10,10,10,10,10,10,12,12,12,11,11,11, + 12,11,11,11,10,10,10,10,10,10,10,10,10,10,10,12, + 11,12,12,12,12,12,11,12,11,11,10,10,10,10,10,10, + 10,10,10,10,12,12,12,12,11,11,11,11,11,11,11,10, + 10,10,10,10,10,10,10,10,10, +}; + +static float _vq_quantthresh__44c4_s_p9_2[] = { + -9.5, -8.5, -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, + -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, + 6.5, 7.5, 8.5, 9.5, +}; + +static long _vq_quantmap__44c4_s_p9_2[] = { + 19, 17, 15, 13, 11, 9, 7, 5, + 3, 1, 0, 2, 4, 6, 8, 10, + 12, 14, 16, 18, 20, +}; + +static encode_aux_threshmatch _vq_auxt__44c4_s_p9_2 = { + _vq_quantthresh__44c4_s_p9_2, + _vq_quantmap__44c4_s_p9_2, + 21, + 21 +}; + +static static_codebook _44c4_s_p9_2 = { + 2, 441, + _vq_lengthlist__44c4_s_p9_2, + 1, -529268736, 1611661312, 5, 0, + _vq_quantlist__44c4_s_p9_2, + NULL, + &_vq_auxt__44c4_s_p9_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44c4_s_short[] = { + 4, 7,14,10,15,10,12,15,16,15, 4, 2,11, 5,10, 6, + 8,11,14,14,14,10, 7,11, 6, 8,10,11,13,15, 9, 4, + 11, 5, 9, 6, 9,12,14,15,14, 9, 6, 9, 4, 5, 7,10, + 12,13, 9, 5, 7, 6, 5, 5, 7,10,13,13,10, 8, 9, 8, + 7, 6, 8,10,14,14,13,11,10,10, 7, 7, 8,11,14,15, + 13,12, 9, 9, 6, 5, 7,10,14,17,15,13,11,10, 6, 6, + 7, 9,12,17, +}; + +static static_codebook _huff_book__44c4_s_short = { + 2, 100, + _huff_lengthlist__44c4_s_short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44c5_s_long[] = { + 3, 8, 9,13,10,12,12,12,12,12, 6, 4, 6, 8, 6, 8, + 10,10,11,12, 8, 5, 4,10, 4, 7, 8, 9,10,11,13, 8, + 10, 8, 9, 9,11,12,13,14,10, 6, 4, 9, 3, 5, 6, 8, + 10,11,11, 8, 6, 9, 5, 5, 6, 7, 9,11,12, 9, 7,11, + 6, 6, 6, 7, 8,10,12,11, 9,12, 7, 7, 6, 6, 7, 9, + 13,12,10,13, 9, 8, 7, 7, 7, 8,11,15,11,15,11,10, + 9, 8, 7, 7, +}; + +static static_codebook _huff_book__44c5_s_long = { + 2, 100, + _huff_lengthlist__44c5_s_long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44c5_s_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c5_s_p1_0[] = { + 2, 4, 4, 0, 0, 0, 0, 0, 0, 4, 7, 7, 0, 0, 0, 0, + 0, 0, 4, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, + 0, 0, 0, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 7, 7, 0, 0, 0, 0, 0, 0, 7, 9, 8, 0, 0, + 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 7, 7, 0, 0, 0, 0, + 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, + 0, 0, 0, 9,10,11, 0, 0, 0, 0, 0, 0, 9,10,10, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, + 0, 0, 0, 0, 8,10, 9, 0, 0, 0, 0, 0, 0, 9,10,11, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 7, 7, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, + 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 9,11,10, 0, + 0, 0, 0, 0, 0, 8, 9,10, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 9,10,10, + 0, 0, 0, 0, 0, 0, 9,11,10, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c5_s_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44c5_s_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c5_s_p1_0 = { + _vq_quantthresh__44c5_s_p1_0, + _vq_quantmap__44c5_s_p1_0, + 3, + 3 +}; + +static static_codebook _44c5_s_p1_0 = { + 8, 6561, + _vq_lengthlist__44c5_s_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44c5_s_p1_0, + NULL, + &_vq_auxt__44c5_s_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c5_s_p2_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c5_s_p2_0[] = { + 2, 4, 4, 0, 0, 0, 5, 5, 0, 0, 0, 5, 5, 0, 0, 0, + 8, 7, 0, 0, 0, 0, 0, 0, 0, 4, 6, 6, 0, 0, 0, 8, + 8, 0, 0, 0, 8, 7, 0, 0, 0,10,10, 0, 0, 0, 0, 0, + 0, 0, 4, 6, 6, 0, 0, 0, 8, 8, 0, 0, 0, 7, 8, 0, + 0, 0,10,10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 5, 8, 7, 0, 0, 0, 8, 8, 0, 0, + 0, 8, 8, 0, 0, 0,10,10, 0, 0, 0, 0, 0, 0, 0, 5, + 7, 8, 0, 0, 0, 8, 8, 0, 0, 0, 8, 8, 0, 0, 0,10, + 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 5, 8, 8, 0, 0, 0, 8, 8, 0, 0, 0, 8, 8, + 0, 0, 0,10,10, 0, 0, 0, 0, 0, 0, 0, 5, 8, 8, 0, + 0, 0, 8, 8, 0, 0, 0, 8, 8, 0, 0, 0,10,10, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8,10,10, 0, 0, 0,10,10, 0, 0, 0, 9,10, 0, 0, 0, + 11,10, 0, 0, 0, 0, 0, 0, 0, 8,10,10, 0, 0, 0,10, + 10, 0, 0, 0,10,10, 0, 0, 0,10,11, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, +}; + +static float _vq_quantthresh__44c5_s_p2_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c5_s_p2_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c5_s_p2_0 = { + _vq_quantthresh__44c5_s_p2_0, + _vq_quantmap__44c5_s_p2_0, + 5, + 5 +}; + +static static_codebook _44c5_s_p2_0 = { + 4, 625, + _vq_lengthlist__44c5_s_p2_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c5_s_p2_0, + NULL, + &_vq_auxt__44c5_s_p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c5_s_p3_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c5_s_p3_0[] = { + 2, 4, 3, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 5, 6, 6, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3, 5, 5, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 5, 6, 6, 8, 8, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c5_s_p3_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c5_s_p3_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c5_s_p3_0 = { + _vq_quantthresh__44c5_s_p3_0, + _vq_quantmap__44c5_s_p3_0, + 5, + 5 +}; + +static static_codebook _44c5_s_p3_0 = { + 4, 625, + _vq_lengthlist__44c5_s_p3_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c5_s_p3_0, + NULL, + &_vq_auxt__44c5_s_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c5_s_p4_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44c5_s_p4_0[] = { + 2, 3, 3, 6, 6, 0, 0, 0, 0, 0, 4, 4, 6, 6, 0, 0, + 0, 0, 0, 4, 4, 6, 6, 0, 0, 0, 0, 0, 5, 5, 6, 6, + 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, + 7, 7, 0, 0, 0, 0, 0, 0, 0, 8, 7, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c5_s_p4_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44c5_s_p4_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44c5_s_p4_0 = { + _vq_quantthresh__44c5_s_p4_0, + _vq_quantmap__44c5_s_p4_0, + 9, + 9 +}; + +static static_codebook _44c5_s_p4_0 = { + 2, 81, + _vq_lengthlist__44c5_s_p4_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44c5_s_p4_0, + NULL, + &_vq_auxt__44c5_s_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c5_s_p5_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44c5_s_p5_0[] = { + 2, 4, 3, 6, 6, 7, 7, 9, 9, 0, 4, 4, 6, 6, 7, 7, + 9, 9, 0, 4, 4, 6, 6, 7, 7, 9, 9, 0, 6, 6, 7, 7, + 7, 7, 9, 9, 0, 0, 0, 7, 6, 7, 7, 9, 9, 0, 0, 0, + 8, 8, 8, 8,10,10, 0, 0, 0, 8, 8, 8, 8,10,10, 0, + 0, 0, 9, 9, 9, 9,10,10, 0, 0, 0, 0, 0, 9, 9,10, + 10, +}; + +static float _vq_quantthresh__44c5_s_p5_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44c5_s_p5_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44c5_s_p5_0 = { + _vq_quantthresh__44c5_s_p5_0, + _vq_quantmap__44c5_s_p5_0, + 9, + 9 +}; + +static static_codebook _44c5_s_p5_0 = { + 2, 81, + _vq_lengthlist__44c5_s_p5_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44c5_s_p5_0, + NULL, + &_vq_auxt__44c5_s_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c5_s_p6_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44c5_s_p6_0[] = { + 2, 4, 4, 6, 6, 8, 8, 9, 9, 9, 9,10,10,10,10,11, + 11, 0, 4, 4, 6, 6, 8, 8, 9, 9, 9, 9,10,10,11,11, + 12,12, 0, 4, 4, 6, 6, 8, 8, 9, 9, 9, 9,10,10,11, + 11,12,12, 0, 6, 6, 7, 7, 8, 8, 9, 9, 9, 9,10,10, + 11,11,12,12, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10, + 10,11,11,12,12, 0, 0, 0, 7, 7, 9, 9,10,10,10,10, + 11,11,11,11,12,12, 0, 0, 0, 7, 7, 8, 9,10,10,10, + 10,11,11,11,11,12,12, 0, 0, 0, 8, 8, 9, 9,10,10, + 10,10,11,11,12,12,12,12, 0, 0, 0, 0, 0, 9, 9,10, + 10,10,10,11,11,12,12,12,12, 0, 0, 0, 0, 0, 9, 9, + 10,10,10,10,11,11,12,12,12,12, 0, 0, 0, 0, 0, 9, + 9, 9,10,10,10,11,11,12,12,12,12, 0, 0, 0, 0, 0, + 10,10,10,10,11,11,11,12,12,12,13,13, 0, 0, 0, 0, + 0, 0, 0,10,10,11,11,11,11,12,12,13,13, 0, 0, 0, + 0, 0, 0, 0,11,11,11,11,12,12,12,13,13,13, 0, 0, + 0, 0, 0, 0, 0,11,11,11,11,12,12,12,12,13,13, 0, + 0, 0, 0, 0, 0, 0,12,12,12,12,13,12,13,13,13,13, + 0, 0, 0, 0, 0, 0, 0, 0, 0,12,12,12,12,13,13,13, + 13, +}; + +static float _vq_quantthresh__44c5_s_p6_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44c5_s_p6_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44c5_s_p6_0 = { + _vq_quantthresh__44c5_s_p6_0, + _vq_quantmap__44c5_s_p6_0, + 17, + 17 +}; + +static static_codebook _44c5_s_p6_0 = { + 2, 289, + _vq_lengthlist__44c5_s_p6_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44c5_s_p6_0, + NULL, + &_vq_auxt__44c5_s_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c5_s_p7_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c5_s_p7_0[] = { + 1, 4, 4, 7, 6, 6, 7, 6, 6, 4, 7, 7,10, 9, 9,11, + 9, 9, 4, 7, 7,10, 9, 9,11, 9, 9, 7,10,10,11,11, + 10,11,11,11, 6, 9, 9,11,10,10,11,10,10, 6, 9, 9, + 11,10,10,11,10,10, 7,11,11,12,11,11,12,11,11, 6, + 9, 9,11,10,10,11,10,10, 6, 9, 9,11,10,10,11,10, + 10, +}; + +static float _vq_quantthresh__44c5_s_p7_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__44c5_s_p7_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c5_s_p7_0 = { + _vq_quantthresh__44c5_s_p7_0, + _vq_quantmap__44c5_s_p7_0, + 3, + 3 +}; + +static static_codebook _44c5_s_p7_0 = { + 4, 81, + _vq_lengthlist__44c5_s_p7_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__44c5_s_p7_0, + NULL, + &_vq_auxt__44c5_s_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c5_s_p7_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44c5_s_p7_1[] = { + 2, 4, 4, 6, 6, 7, 7, 8, 8, 8, 8,10, 5, 5, 6, 6, + 7, 7, 8, 8, 8, 8,10, 5, 5, 6, 6, 7, 7, 8, 8, 8, + 8,10, 6, 6, 7, 7, 8, 8, 8, 8, 8, 8,10,10,10, 7, + 7, 8, 8, 8, 8, 8, 8,10,10,10, 7, 7, 8, 8, 8, 8, + 8, 8,10,10,10, 7, 7, 8, 8, 8, 8, 8, 8,10,10,10, + 8, 8, 8, 8, 8, 8, 8, 9,10,10,10,10,10, 8, 8, 8, + 8, 8, 8,10,10,10,10,10, 9, 9, 8, 8, 8, 8,10,10, + 10,10,10, 8, 8, 8, 8, 8, 8, +}; + +static float _vq_quantthresh__44c5_s_p7_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44c5_s_p7_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44c5_s_p7_1 = { + _vq_quantthresh__44c5_s_p7_1, + _vq_quantmap__44c5_s_p7_1, + 11, + 11 +}; + +static static_codebook _44c5_s_p7_1 = { + 2, 121, + _vq_lengthlist__44c5_s_p7_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44c5_s_p7_1, + NULL, + &_vq_auxt__44c5_s_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c5_s_p8_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c5_s_p8_0[] = { + 1, 4, 4, 6, 6, 7, 7, 8, 8, 9, 9,10,10, 6, 5, 5, + 7, 7, 8, 8, 8, 9,10,10,10,10, 7, 5, 5, 7, 7, 8, + 8, 9, 9,10,10,10,10, 0, 8, 8, 8, 8, 9, 9, 9, 9, + 10,10,11,11, 0, 8, 8, 8, 8, 9, 9, 9, 9,10,10,11, + 11, 0,12,12, 9, 9, 9,10,10,10,10,10,11,11, 0,13, + 13, 9, 9, 9, 9,10,10,11,11,11,11, 0, 0, 0,10,10, + 10,10,10,10,11,11,11,11, 0, 0, 0,10,10,10,10,10, + 10,11,11,12,12, 0, 0, 0,14,14,11,11,11,11,12,12, + 12,12, 0, 0, 0,14,14,11,11,11,11,12,12,12,12, 0, + 0, 0, 0, 0,12,12,12,12,12,12,13,13, 0, 0, 0, 0, + 0,12,12,12,12,12,12,13,13, +}; + +static float _vq_quantthresh__44c5_s_p8_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44c5_s_p8_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c5_s_p8_0 = { + _vq_quantthresh__44c5_s_p8_0, + _vq_quantmap__44c5_s_p8_0, + 13, + 13 +}; + +static static_codebook _44c5_s_p8_0 = { + 2, 169, + _vq_lengthlist__44c5_s_p8_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44c5_s_p8_0, + NULL, + &_vq_auxt__44c5_s_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c5_s_p8_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c5_s_p8_1[] = { + 2, 4, 4, 5, 5, 6, 5, 5, 5, 5, 6, 4, 5, 5, 5, 6, + 5, 5, 5, 5, 6, 6, 6, 5, 5, +}; + +static float _vq_quantthresh__44c5_s_p8_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c5_s_p8_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c5_s_p8_1 = { + _vq_quantthresh__44c5_s_p8_1, + _vq_quantmap__44c5_s_p8_1, + 5, + 5 +}; + +static static_codebook _44c5_s_p8_1 = { + 2, 25, + _vq_lengthlist__44c5_s_p8_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c5_s_p8_1, + NULL, + &_vq_auxt__44c5_s_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c5_s_p9_0[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__44c5_s_p9_0[] = { + 1, 3, 3,13,13,13,13,13,13,13,13,13,13,13,13, 4, + 7, 7,13,13,13,13,13,13,13,13,13,13,13,13, 3, 8, + 6,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,12,12,12,12,12,12,12, + 12, +}; + +static float _vq_quantthresh__44c5_s_p9_0[] = { + -2320.5, -1963.5, -1606.5, -1249.5, -892.5, -535.5, -178.5, 178.5, + 535.5, 892.5, 1249.5, 1606.5, 1963.5, 2320.5, +}; + +static long _vq_quantmap__44c5_s_p9_0[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__44c5_s_p9_0 = { + _vq_quantthresh__44c5_s_p9_0, + _vq_quantmap__44c5_s_p9_0, + 15, + 15 +}; + +static static_codebook _44c5_s_p9_0 = { + 2, 225, + _vq_lengthlist__44c5_s_p9_0, + 1, -512522752, 1628852224, 4, 0, + _vq_quantlist__44c5_s_p9_0, + NULL, + &_vq_auxt__44c5_s_p9_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c5_s_p9_1[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44c5_s_p9_1[] = { + 1, 4, 4, 5, 5, 7, 7, 9, 8,10, 9,10,10,11,10,11, + 11, 6, 5, 5, 7, 7, 8, 9,10,10,11,10,12,11,12,11, + 13,12, 6, 5, 5, 7, 7, 9, 9,10,10,11,11,12,12,13, + 12,13,13,18, 8, 8, 8, 8, 9, 9,10,11,11,11,12,11, + 13,11,13,12,18, 8, 8, 8, 8,10,10,11,11,12,12,13, + 13,13,13,13,14,18,12,12, 9, 9,11,11,11,11,12,12, + 13,12,13,12,13,13,20,13,12, 9, 9,11,11,11,11,12, + 12,13,13,13,14,14,13,20,18,19,11,12,11,11,12,12, + 13,13,13,13,13,13,14,13,18,19,19,12,11,11,11,12, + 12,13,12,13,13,13,14,14,13,18,17,19,14,15,12,12, + 12,13,13,13,14,14,14,14,14,14,19,19,19,16,15,12, + 11,13,12,14,14,14,13,13,14,14,14,19,18,19,18,19, + 13,13,13,13,14,14,14,13,14,14,14,14,18,17,19,19, + 19,13,13,13,11,13,11,13,14,14,14,14,14,19,17,17, + 18,18,16,16,13,13,13,13,14,13,15,15,14,14,19,19, + 17,17,18,16,16,13,11,14,10,13,12,14,14,14,14,19, + 19,19,19,19,18,17,13,14,13,11,14,13,14,14,15,15, + 19,19,19,17,19,18,18,14,13,12,11,14,11,15,15,15, + 15, +}; + +static float _vq_quantthresh__44c5_s_p9_1[] = { + -157.5, -136.5, -115.5, -94.5, -73.5, -52.5, -31.5, -10.5, + 10.5, 31.5, 52.5, 73.5, 94.5, 115.5, 136.5, 157.5, +}; + +static long _vq_quantmap__44c5_s_p9_1[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44c5_s_p9_1 = { + _vq_quantthresh__44c5_s_p9_1, + _vq_quantmap__44c5_s_p9_1, + 17, + 17 +}; + +static static_codebook _44c5_s_p9_1 = { + 2, 289, + _vq_lengthlist__44c5_s_p9_1, + 1, -520814592, 1620377600, 5, 0, + _vq_quantlist__44c5_s_p9_1, + NULL, + &_vq_auxt__44c5_s_p9_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c5_s_p9_2[] = { + 10, + 9, + 11, + 8, + 12, + 7, + 13, + 6, + 14, + 5, + 15, + 4, + 16, + 3, + 17, + 2, + 18, + 1, + 19, + 0, + 20, +}; + +static long _vq_lengthlist__44c5_s_p9_2[] = { + 3, 5, 5, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 9,11, 5, 6, 7, 7, 8, 7, 8, 8, 8, 8, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,11, 5, 5, 7, 7, 7, + 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,11, + 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, + 9,10, 9,10,11,11,11, 7, 7, 8, 8, 8, 8, 9, 9, 9, + 9, 9, 9,10,10,10,10,10,10,11,11,11, 8, 8, 8, 8, + 9, 9, 9, 9, 9, 9, 9,10,10,10,10,10,10,10,11,11, + 11, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9,10,10,10,10,10, + 10,10,10,11,11,11, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 10,10,10,10,10,10,10,10,11,11,11,11,11, 9, 9, 9, + 9, 9, 9,10, 9,10,10,10,10,10,10,10,10,11,11,11, + 11,11, 9, 9, 9, 9, 9, 9,10,10,10,10,10,10,10,10, + 10,10,11,11,11,11,11, 9, 9, 9, 9, 9, 9,10,10,10, + 10,10,10,10,10,10,10,11,11,11,11,11, 9, 9,10, 9, + 10,10,10,10,10,10,10,10,10,10,10,10,11,11,11,11, + 11,11,11, 9, 9,10,10,10,10,10,10,10,10,10,10,10, + 10,11,11,11,11,11,11,11,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,11,11,11,11,11,11,11,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,11,11,11,11,11, + 11,11,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 11,11,11,11,11,11,11,11,11,10,10,10,10,10,10,10, + 10,10,10,10,10,11,11,11,11,11,11,11,11,11,10,10, + 10,10,10,10,10,10,10,10,10,10,11,11,11,11,11,11, + 11,11,11,10,10,10,10,10,10,10,10,10,10,10,10,11, + 11,11,11,11,11,11,11,11,10,10,10,10,10,10,10,10, + 10,10,10,10,11,11,11,11,11,11,11,11,11,11,11,10, + 10,10,10,10,10,10,10,10,10, +}; + +static float _vq_quantthresh__44c5_s_p9_2[] = { + -9.5, -8.5, -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, + -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, + 6.5, 7.5, 8.5, 9.5, +}; + +static long _vq_quantmap__44c5_s_p9_2[] = { + 19, 17, 15, 13, 11, 9, 7, 5, + 3, 1, 0, 2, 4, 6, 8, 10, + 12, 14, 16, 18, 20, +}; + +static encode_aux_threshmatch _vq_auxt__44c5_s_p9_2 = { + _vq_quantthresh__44c5_s_p9_2, + _vq_quantmap__44c5_s_p9_2, + 21, + 21 +}; + +static static_codebook _44c5_s_p9_2 = { + 2, 441, + _vq_lengthlist__44c5_s_p9_2, + 1, -529268736, 1611661312, 5, 0, + _vq_quantlist__44c5_s_p9_2, + NULL, + &_vq_auxt__44c5_s_p9_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44c5_s_short[] = { + 5, 8,10,14,11,11,12,16,15,17, 5, 5, 7, 9, 7, 8, + 10,13,17,17, 7, 5, 5,10, 5, 7, 8,11,13,15,10, 8, + 10, 8, 8, 8,11,15,18,18, 8, 5, 5, 8, 3, 4, 6,10, + 14,16, 9, 7, 6, 7, 4, 3, 5, 9,14,18,10, 9, 8,10, + 6, 5, 6, 9,14,18,12,12,11,12, 8, 7, 8,11,14,18, + 14,13,12,10, 7, 5, 6, 9,14,18,14,14,13,10, 6, 5, + 6, 8,11,16, +}; + +static static_codebook _huff_book__44c5_s_short = { + 2, 100, + _huff_lengthlist__44c5_s_short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44c6_s_long[] = { + 3, 8,11,13,14,14,13,13,16,14, 6, 3, 4, 7, 9, 9, + 10,11,14,13,10, 4, 3, 5, 7, 7, 9,10,13,15,12, 7, + 4, 4, 6, 6, 8,10,13,15,12, 8, 6, 6, 6, 6, 8,10, + 13,14,11, 9, 7, 6, 6, 6, 7, 8,12,11,13,10, 9, 8, + 7, 6, 6, 7,11,11,13,11,10, 9, 9, 7, 7, 6,10,11, + 13,13,13,13,13,11, 9, 8,10,12,12,15,15,16,15,12, + 11,10,10,12, +}; + +static static_codebook _huff_book__44c6_s_long = { + 2, 100, + _huff_lengthlist__44c6_s_long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44c6_s_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c6_s_p1_0[] = { + 1, 5, 5, 0, 5, 5, 0, 5, 5, 5, 8, 7, 0, 9, 9, 0, + 9, 8, 5, 7, 8, 0, 9, 9, 0, 8, 9, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 5, 9, 8, 0, 8, 8, 0, 8, 8, 5, 8, 9, + 0, 8, 8, 0, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, + 9, 9, 0, 8, 8, 0, 8, 8, 5, 9, 9, 0, 8, 8, 0, 8, + 8, +}; + +static float _vq_quantthresh__44c6_s_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44c6_s_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c6_s_p1_0 = { + _vq_quantthresh__44c6_s_p1_0, + _vq_quantmap__44c6_s_p1_0, + 3, + 3 +}; + +static static_codebook _44c6_s_p1_0 = { + 4, 81, + _vq_lengthlist__44c6_s_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44c6_s_p1_0, + NULL, + &_vq_auxt__44c6_s_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c6_s_p2_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c6_s_p2_0[] = { + 3, 5, 5, 8, 8, 0, 5, 5, 8, 8, 0, 5, 5, 8, 8, 0, + 7, 7, 9, 9, 0, 0, 0, 9, 9, 5, 7, 7, 9, 9, 0, 8, + 8,10,10, 0, 8, 7,10, 9, 0,10,10,11,11, 0, 0, 0, + 11,11, 5, 7, 7, 9, 9, 0, 8, 8,10,10, 0, 7, 8, 9, + 10, 0,10,10,11,11, 0, 0, 0,11,11, 8, 9, 9,11,11, + 0,11,11,12,12, 0,11,10,12,12, 0,13,14,14,14, 0, + 0, 0,14,13, 8, 9, 9,11,11, 0,11,11,12,12, 0,10, + 11,12,12, 0,14,13,14,14, 0, 0, 0,13,14, 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, 5, 8, 7,11,10, 0, 7, 7,10,10, + 0, 7, 7,10,10, 0, 9, 9,11,10, 0, 0, 0,11,11, 5, + 7, 8,10,11, 0, 7, 7,10,10, 0, 7, 7,10,10, 0, 9, + 9,10,11, 0, 0, 0,11,11, 8,10, 9,12,12, 0,10,10, + 12,12, 0,10,10,12,12, 0,12,12,13,13, 0, 0, 0,13, + 13, 8, 9,10,12,12, 0,10,10,11,12, 0,10,10,12,12, + 0,12,12,13,13, 0, 0, 0,13,13, 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, 5, 8, 8,11,11, 0, 7, 7,10,10, 0, 7, 7, + 10,10, 0, 9, 9,10,11, 0, 0, 0,11,10, 5, 8, 8,11, + 11, 0, 7, 7,10,10, 0, 7, 7,10,10, 0, 9, 9,11,11, + 0, 0, 0,10,11, 8,10,10,12,12, 0,10,10,12,12, 0, + 10,10,12,12, 0,12,13,13,13, 0, 0, 0,14,13, 8,10, + 10,12,12, 0,10,10,12,12, 0,10,10,12,12, 0,13,12, + 13,13, 0, 0, 0,13,13, 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, + 7,10,10,14,13, 0, 9, 9,13,12, 0, 9, 9,12,12, 0, + 10,10,12,12, 0, 0, 0,12,12, 7,10,10,13,14, 0, 9, + 9,12,13, 0, 9, 9,12,12, 0,10,10,12,12, 0, 0, 0, + 12,12, 9,11,11,14,13, 0,11,10,14,13, 0,11,11,13, + 13, 0,12,12,13,13, 0, 0, 0,13,13, 9,11,11,13,14, + 0,10,11,13,14, 0,11,11,13,13, 0,12,12,13,13, 0, + 0, 0,13,13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, + 11,11,14,14, 0,11,11,13,13, 0,11,10,13,13, 0,12, + 12,13,13, 0, 0, 0,13,13, 9,11,11,14,14, 0,11,11, + 13,13, 0,10,11,13,13, 0,12,12,14,13, 0, 0, 0,13, + 13, +}; + +static float _vq_quantthresh__44c6_s_p2_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c6_s_p2_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c6_s_p2_0 = { + _vq_quantthresh__44c6_s_p2_0, + _vq_quantmap__44c6_s_p2_0, + 5, + 5 +}; + +static static_codebook _44c6_s_p2_0 = { + 4, 625, + _vq_lengthlist__44c6_s_p2_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c6_s_p2_0, + NULL, + &_vq_auxt__44c6_s_p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c6_s_p3_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44c6_s_p3_0[] = { + 2, 3, 4, 6, 6, 7, 7, 9, 9, 0, 4, 4, 6, 6, 7, 7, + 9,10, 0, 4, 4, 6, 6, 7, 7,10, 9, 0, 5, 5, 7, 7, + 8, 8,10,10, 0, 0, 0, 7, 6, 8, 8,10,10, 0, 0, 0, + 7, 7, 9, 9,11,11, 0, 0, 0, 7, 7, 9, 9,11,11, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c6_s_p3_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44c6_s_p3_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44c6_s_p3_0 = { + _vq_quantthresh__44c6_s_p3_0, + _vq_quantmap__44c6_s_p3_0, + 9, + 9 +}; + +static static_codebook _44c6_s_p3_0 = { + 2, 81, + _vq_lengthlist__44c6_s_p3_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44c6_s_p3_0, + NULL, + &_vq_auxt__44c6_s_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c6_s_p4_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44c6_s_p4_0[] = { + 2, 4, 4, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9,10,10, + 10, 0, 4, 4, 6, 6, 8, 8, 9, 9, 9, 9,10,10,10,10, + 11,11, 0, 4, 4, 6, 6, 8, 8, 9, 9, 9, 9,10,10,10, + 10,11,11, 0, 6, 6, 7, 7, 8, 8, 9, 9, 9, 9,10,10, + 11,11,11,11, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10, + 10,11,11,11,11, 0, 0, 0, 7, 7, 9, 9,10,10,10,10, + 11,11,11,11,12,12, 0, 0, 0, 7, 7, 9, 9,10,10,10, + 10,11,11,11,11,12,12, 0, 0, 0, 7, 7, 8, 8, 9, 9, + 10,10,11,11,12,12,12,12, 0, 0, 0, 0, 0, 8, 8, 9, + 9,10,10,11,11,12,12,12,12, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c6_s_p4_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44c6_s_p4_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44c6_s_p4_0 = { + _vq_quantthresh__44c6_s_p4_0, + _vq_quantmap__44c6_s_p4_0, + 17, + 17 +}; + +static static_codebook _44c6_s_p4_0 = { + 2, 289, + _vq_lengthlist__44c6_s_p4_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44c6_s_p4_0, + NULL, + &_vq_auxt__44c6_s_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c6_s_p5_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c6_s_p5_0[] = { + 1, 4, 4, 5, 7, 7, 6, 7, 7, 4, 6, 6, 9, 9,10,10, + 10, 9, 4, 6, 6, 9,10, 9,10, 9,10, 6, 9, 9,10,12, + 11,10,11,11, 7,10, 9,11,12,12,12,12,12, 7,10,10, + 11,12,12,12,12,12, 6,10,10,10,12,12,11,12,12, 7, + 9,10,11,12,12,12,12,12, 7,10, 9,12,12,12,12,12, + 12, +}; + +static float _vq_quantthresh__44c6_s_p5_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__44c6_s_p5_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c6_s_p5_0 = { + _vq_quantthresh__44c6_s_p5_0, + _vq_quantmap__44c6_s_p5_0, + 3, + 3 +}; + +static static_codebook _44c6_s_p5_0 = { + 4, 81, + _vq_lengthlist__44c6_s_p5_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__44c6_s_p5_0, + NULL, + &_vq_auxt__44c6_s_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c6_s_p5_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44c6_s_p5_1[] = { + 3, 5, 4, 6, 6, 7, 7, 8, 8, 8, 8,11, 4, 4, 6, 6, + 7, 7, 8, 8, 8, 8,11, 4, 4, 6, 6, 7, 7, 8, 8, 8, + 8,11, 6, 6, 6, 6, 8, 8, 8, 8, 9, 9,11,11,11, 6, + 6, 7, 8, 8, 8, 8, 9,11,11,11, 7, 7, 8, 8, 8, 8, + 8, 8,11,11,11, 7, 7, 8, 8, 8, 8, 8, 8,11,11,11, + 8, 8, 8, 8, 8, 8, 8, 8,11,11,11,10,10, 8, 8, 8, + 8, 8, 8,11,11,11,10,10, 8, 8, 8, 8, 8, 8,11,11, + 11,10,10, 7, 7, 8, 8, 8, 8, +}; + +static float _vq_quantthresh__44c6_s_p5_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44c6_s_p5_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44c6_s_p5_1 = { + _vq_quantthresh__44c6_s_p5_1, + _vq_quantmap__44c6_s_p5_1, + 11, + 11 +}; + +static static_codebook _44c6_s_p5_1 = { + 2, 121, + _vq_lengthlist__44c6_s_p5_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44c6_s_p5_1, + NULL, + &_vq_auxt__44c6_s_p5_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c6_s_p6_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c6_s_p6_0[] = { + 1, 4, 4, 6, 6, 8, 8, 8, 8,10, 9,10,10, 6, 5, 5, + 7, 7, 9, 9, 9, 9,10,10,11,11, 6, 5, 5, 7, 7, 9, + 9,10, 9,11,10,11,11, 0, 6, 6, 7, 7, 9, 9,10,10, + 11,11,12,12, 0, 7, 7, 7, 7, 9, 9,10,10,11,11,12, + 12, 0,11,11, 8, 8,10,10,11,11,12,12,12,12, 0,11, + 12, 9, 8,10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static float _vq_quantthresh__44c6_s_p6_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44c6_s_p6_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c6_s_p6_0 = { + _vq_quantthresh__44c6_s_p6_0, + _vq_quantmap__44c6_s_p6_0, + 13, + 13 +}; + +static static_codebook _44c6_s_p6_0 = { + 2, 169, + _vq_lengthlist__44c6_s_p6_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44c6_s_p6_0, + NULL, + &_vq_auxt__44c6_s_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c6_s_p6_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c6_s_p6_1[] = { + 3, 4, 4, 5, 5, 5, 4, 4, 5, 5, 5, 4, 4, 5, 5, 6, + 5, 5, 5, 5, 6, 6, 6, 5, 5, +}; + +static float _vq_quantthresh__44c6_s_p6_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c6_s_p6_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c6_s_p6_1 = { + _vq_quantthresh__44c6_s_p6_1, + _vq_quantmap__44c6_s_p6_1, + 5, + 5 +}; + +static static_codebook _44c6_s_p6_1 = { + 2, 25, + _vq_lengthlist__44c6_s_p6_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c6_s_p6_1, + NULL, + &_vq_auxt__44c6_s_p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c6_s_p7_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c6_s_p7_0[] = { + 1, 4, 4, 6, 6, 8, 8, 8, 8,10,10,11,10, 6, 5, 5, + 7, 7, 8, 8, 9, 9,10,10,12,11, 6, 5, 5, 7, 7, 8, + 8, 9, 9,10,10,12,11,21, 7, 7, 7, 7, 9, 9,10,10, + 11,11,12,12,21, 7, 7, 7, 7, 9, 9,10,10,11,11,12, + 12,21,12,12, 9, 9,10,10,11,11,11,11,12,12,21,12, + 12, 9, 9,10,10,11,11,12,12,12,12,21,21,21,11,11, + 10,10,11,12,12,12,13,13,21,21,21,11,11,10,10,12, + 12,12,12,13,13,21,21,21,15,15,11,11,12,12,13,13, + 13,13,21,21,21,15,16,11,11,12,12,13,13,14,14,21, + 21,21,21,20,13,13,13,13,13,13,14,14,20,20,20,20, + 20,13,13,13,13,13,13,14,14, +}; + +static float _vq_quantthresh__44c6_s_p7_0[] = { + -60.5, -49.5, -38.5, -27.5, -16.5, -5.5, 5.5, 16.5, + 27.5, 38.5, 49.5, 60.5, +}; + +static long _vq_quantmap__44c6_s_p7_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c6_s_p7_0 = { + _vq_quantthresh__44c6_s_p7_0, + _vq_quantmap__44c6_s_p7_0, + 13, + 13 +}; + +static static_codebook _44c6_s_p7_0 = { + 2, 169, + _vq_lengthlist__44c6_s_p7_0, + 1, -523206656, 1618345984, 4, 0, + _vq_quantlist__44c6_s_p7_0, + NULL, + &_vq_auxt__44c6_s_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c6_s_p7_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44c6_s_p7_1[] = { + 3, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7, 9, 5, 5, 6, 6, + 7, 7, 7, 7, 8, 7, 8, 5, 5, 6, 6, 7, 7, 7, 7, 7, + 7, 9, 6, 6, 7, 7, 7, 7, 8, 7, 7, 8, 9, 9, 9, 7, + 7, 7, 7, 7, 7, 7, 8, 9, 9, 9, 7, 7, 7, 7, 8, 8, + 8, 8, 9, 9, 9, 7, 7, 7, 7, 7, 7, 8, 8, 9, 9, 9, + 8, 8, 8, 8, 7, 7, 8, 8, 9, 9, 9, 9, 8, 8, 8, 7, + 7, 8, 8, 9, 9, 9, 8, 8, 8, 8, 7, 7, 8, 8, 9, 9, + 9, 8, 8, 7, 7, 7, 7, 8, 8, +}; + +static float _vq_quantthresh__44c6_s_p7_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44c6_s_p7_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44c6_s_p7_1 = { + _vq_quantthresh__44c6_s_p7_1, + _vq_quantmap__44c6_s_p7_1, + 11, + 11 +}; + +static static_codebook _44c6_s_p7_1 = { + 2, 121, + _vq_lengthlist__44c6_s_p7_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44c6_s_p7_1, + NULL, + &_vq_auxt__44c6_s_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c6_s_p8_0[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__44c6_s_p8_0[] = { + 1, 4, 4, 7, 7, 8, 8, 7, 7, 8, 7, 9, 8,10, 9, 6, + 5, 5, 8, 8, 9, 9, 8, 8, 9, 9,11,10,11,10, 6, 5, + 5, 8, 8, 9, 9, 8, 8, 9, 9,10,10,11,11,18, 8, 8, + 9, 8,10,10, 9, 9,10,10,10,10,11,10,18, 8, 8, 9, + 9,10,10, 9, 9,10,10,11,11,12,12,18,12,13, 9,10, + 10,10, 9,10,10,10,11,11,12,11,18,13,13, 9, 9,10, + 10,10,10,10,10,11,11,12,12,18,18,18,10,10, 9, 9, + 11,11,11,11,11,12,12,12,18,18,18,10, 9,10, 9,11, + 10,11,11,11,11,13,12,18,18,18,14,13,10,10,11,11, + 12,12,12,12,12,12,18,18,18,14,13,10,10,11,10,12, + 12,12,12,12,12,18,18,18,18,18,12,12,11,11,12,12, + 13,13,13,14,18,18,18,18,18,12,12,11,11,12,11,13, + 13,14,13,18,18,18,18,18,16,16,11,12,12,13,13,13, + 14,13,18,18,18,18,18,16,15,12,11,12,11,13,11,15, + 14, +}; + +static float _vq_quantthresh__44c6_s_p8_0[] = { + -136.5, -115.5, -94.5, -73.5, -52.5, -31.5, -10.5, 10.5, + 31.5, 52.5, 73.5, 94.5, 115.5, 136.5, +}; + +static long _vq_quantmap__44c6_s_p8_0[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__44c6_s_p8_0 = { + _vq_quantthresh__44c6_s_p8_0, + _vq_quantmap__44c6_s_p8_0, + 15, + 15 +}; + +static static_codebook _44c6_s_p8_0 = { + 2, 225, + _vq_lengthlist__44c6_s_p8_0, + 1, -520986624, 1620377600, 4, 0, + _vq_quantlist__44c6_s_p8_0, + NULL, + &_vq_auxt__44c6_s_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c6_s_p8_1[] = { + 10, + 9, + 11, + 8, + 12, + 7, + 13, + 6, + 14, + 5, + 15, + 4, + 16, + 3, + 17, + 2, + 18, + 1, + 19, + 0, + 20, +}; + +static long _vq_lengthlist__44c6_s_p8_1[] = { + 3, 5, 5, 6, 6, 7, 7, 7, 7, 8, 7, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8,10, 6, 6, 7, 7, 8, 8, 8, 8, 8, 8, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10, 6, 6, 7, 7, 8, + 8, 8, 8, 8, 8, 9, 8, 9, 9, 9, 9, 9, 9, 9, 9,10, + 7, 7, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9,10,11,11, 8, 7, 8, 8, 8, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9,11,11,11, 8, 8, 8, 8, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,11,11, + 11, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9,11,11,11, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9,11,11,11,11,11, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10,10, 9,11,11,11, + 11,11, 9, 9, 9, 9, 9, 9,10, 9, 9,10, 9,10, 9, 9, + 10, 9,11,11,11,11,11, 9, 9, 9, 9, 9, 9, 9,10,10, + 10,10, 9,10,10, 9,10,11,11,11,11,11, 9, 9, 9, 9, + 10,10,10, 9,10,10,10,10, 9,10,10, 9,11,11,11,11, + 11,11,11, 9, 9, 9, 9,10,10,10,10, 9,10,10,10,10, + 10,11,11,11,11,11,11,11,10, 9,10,10,10,10,10,10, + 10, 9,10, 9,10,10,11,11,11,11,11,11,11,10, 9,10, + 9,10,10, 9,10,10,10,10,10,10,10,11,11,11,11,11, + 11,11,10,10,10,10,10,10,10, 9,10,10,10,10,10, 9, + 11,11,11,11,11,11,11,11,11,10,10,10,10,10,10,10, + 10,10,10,10,10,11,11,11,11,11,11,11,11,11,10,10, + 10,10,10,10,10,10,10,10,10,10,11,11,11,11,11,11, + 11,11,11,10,10,10,10,10,10,10,10,10, 9,10,10,11, + 11,11,11,11,11,11,11,11,10,10,10, 9,10,10,10,10, + 10,10,10,10,10,11,11,11,11,11,11,11,11,10,11, 9, + 10,10,10,10,10,10,10,10,10, +}; + +static float _vq_quantthresh__44c6_s_p8_1[] = { + -9.5, -8.5, -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, + -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, + 6.5, 7.5, 8.5, 9.5, +}; + +static long _vq_quantmap__44c6_s_p8_1[] = { + 19, 17, 15, 13, 11, 9, 7, 5, + 3, 1, 0, 2, 4, 6, 8, 10, + 12, 14, 16, 18, 20, +}; + +static encode_aux_threshmatch _vq_auxt__44c6_s_p8_1 = { + _vq_quantthresh__44c6_s_p8_1, + _vq_quantmap__44c6_s_p8_1, + 21, + 21 +}; + +static static_codebook _44c6_s_p8_1 = { + 2, 441, + _vq_lengthlist__44c6_s_p8_1, + 1, -529268736, 1611661312, 5, 0, + _vq_quantlist__44c6_s_p8_1, + NULL, + &_vq_auxt__44c6_s_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c6_s_p9_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c6_s_p9_0[] = { + 1, 3, 3,11,11,11,11,11,11,11,11,11,11, 4, 7, 7, + 11,11,11,11,11,11,11,11,11,11, 5, 8, 9,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10, +}; + +static float _vq_quantthresh__44c6_s_p9_0[] = { + -3503.5, -2866.5, -2229.5, -1592.5, -955.5, -318.5, 318.5, 955.5, + 1592.5, 2229.5, 2866.5, 3503.5, +}; + +static long _vq_quantmap__44c6_s_p9_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c6_s_p9_0 = { + _vq_quantthresh__44c6_s_p9_0, + _vq_quantmap__44c6_s_p9_0, + 13, + 13 +}; + +static static_codebook _44c6_s_p9_0 = { + 2, 169, + _vq_lengthlist__44c6_s_p9_0, + 1, -511845376, 1630791680, 4, 0, + _vq_quantlist__44c6_s_p9_0, + NULL, + &_vq_auxt__44c6_s_p9_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c6_s_p9_1[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c6_s_p9_1[] = { + 1, 4, 4, 7, 7, 7, 7, 7, 6, 8, 8, 8, 8, 6, 6, 6, + 8, 8, 8, 8, 8, 7, 9, 8,10,10, 5, 6, 6, 8, 8, 9, + 9, 8, 8,10,10,10,10,16, 9, 9, 9, 9, 9, 9, 9, 8, + 10, 9,11,11,16, 8, 9, 9, 9, 9, 9, 9, 9,10,10,11, + 11,16,13,13, 9, 9,10, 9, 9,10,11,11,11,12,16,13, + 14, 9, 8,10, 8, 9, 9,10,10,12,11,16,14,16, 9, 9, + 9, 9,11,11,12,11,12,11,16,16,16, 9, 7, 9, 6,11, + 11,11,10,11,11,16,16,16,11,12, 9,10,11,11,12,11, + 13,13,16,16,16,12,11,10, 7,12,10,12,12,12,12,16, + 16,15,16,16,10,11,10,11,13,13,14,12,16,16,16,15, + 15,12,10,11,11,13,11,12,13, +}; + +static float _vq_quantthresh__44c6_s_p9_1[] = { + -269.5, -220.5, -171.5, -122.5, -73.5, -24.5, 24.5, 73.5, + 122.5, 171.5, 220.5, 269.5, +}; + +static long _vq_quantmap__44c6_s_p9_1[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c6_s_p9_1 = { + _vq_quantthresh__44c6_s_p9_1, + _vq_quantmap__44c6_s_p9_1, + 13, + 13 +}; + +static static_codebook _44c6_s_p9_1 = { + 2, 169, + _vq_lengthlist__44c6_s_p9_1, + 1, -518889472, 1622704128, 4, 0, + _vq_quantlist__44c6_s_p9_1, + NULL, + &_vq_auxt__44c6_s_p9_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c6_s_p9_2[] = { + 24, + 23, + 25, + 22, + 26, + 21, + 27, + 20, + 28, + 19, + 29, + 18, + 30, + 17, + 31, + 16, + 32, + 15, + 33, + 14, + 34, + 13, + 35, + 12, + 36, + 11, + 37, + 10, + 38, + 9, + 39, + 8, + 40, + 7, + 41, + 6, + 42, + 5, + 43, + 4, + 44, + 3, + 45, + 2, + 46, + 1, + 47, + 0, + 48, +}; + +static long _vq_lengthlist__44c6_s_p9_2[] = { + 2, 4, 3, 4, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, +}; + +static float _vq_quantthresh__44c6_s_p9_2[] = { + -23.5, -22.5, -21.5, -20.5, -19.5, -18.5, -17.5, -16.5, + -15.5, -14.5, -13.5, -12.5, -11.5, -10.5, -9.5, -8.5, + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, + 8.5, 9.5, 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, + 16.5, 17.5, 18.5, 19.5, 20.5, 21.5, 22.5, 23.5, +}; + +static long _vq_quantmap__44c6_s_p9_2[] = { + 47, 45, 43, 41, 39, 37, 35, 33, + 31, 29, 27, 25, 23, 21, 19, 17, + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, + 48, +}; + +static encode_aux_threshmatch _vq_auxt__44c6_s_p9_2 = { + _vq_quantthresh__44c6_s_p9_2, + _vq_quantmap__44c6_s_p9_2, + 49, + 49 +}; + +static static_codebook _44c6_s_p9_2 = { + 1, 49, + _vq_lengthlist__44c6_s_p9_2, + 1, -526909440, 1611661312, 6, 0, + _vq_quantlist__44c6_s_p9_2, + NULL, + &_vq_auxt__44c6_s_p9_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44c6_s_short[] = { + 3, 9,11,11,13,14,19,17,17,19, 5, 4, 5, 8,10,10, + 13,16,18,19, 7, 4, 4, 5, 8, 9,12,14,17,19, 8, 6, + 5, 5, 7, 7,10,13,16,18,10, 8, 7, 6, 5, 5, 8,11, + 17,19,11, 9, 7, 7, 5, 4, 5, 8,17,19,13,11, 8, 7, + 7, 5, 5, 7,16,18,14,13, 8, 6, 6, 5, 5, 7,16,18, + 18,16,10, 8, 8, 7, 7, 9,16,18,18,18,12,10,10, 9, + 9,10,17,18, +}; + +static static_codebook _huff_book__44c6_s_short = { + 2, 100, + _huff_lengthlist__44c6_s_short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44c7_s_long[] = { + 3, 8,11,13,15,14,14,13,15,14, 6, 4, 5, 7, 9,10, + 11,11,14,13,10, 4, 3, 5, 7, 8, 9,10,13,13,12, 7, + 4, 4, 5, 6, 8, 9,12,14,13, 9, 6, 5, 5, 6, 8, 9, + 12,14,12, 9, 7, 6, 5, 5, 6, 8,11,11,12,11, 9, 8, + 7, 6, 6, 7,10,11,13,11,10, 9, 8, 7, 6, 6, 9,11, + 13,13,12,12,12,10, 9, 8, 9,11,12,14,15,15,14,12, + 11,10,10,12, +}; + +static static_codebook _huff_book__44c7_s_long = { + 2, 100, + _huff_lengthlist__44c7_s_long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44c7_s_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c7_s_p1_0[] = { + 1, 5, 5, 0, 5, 5, 0, 5, 5, 5, 8, 7, 0, 9, 9, 0, + 9, 8, 5, 7, 8, 0, 9, 9, 0, 8, 9, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 5, 9, 9, 0, 8, 8, 0, 8, 8, 5, 8, 9, + 0, 8, 8, 0, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, + 9, 9, 0, 8, 8, 0, 8, 8, 5, 8, 9, 0, 8, 8, 0, 8, + 8, +}; + +static float _vq_quantthresh__44c7_s_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44c7_s_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c7_s_p1_0 = { + _vq_quantthresh__44c7_s_p1_0, + _vq_quantmap__44c7_s_p1_0, + 3, + 3 +}; + +static static_codebook _44c7_s_p1_0 = { + 4, 81, + _vq_lengthlist__44c7_s_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44c7_s_p1_0, + NULL, + &_vq_auxt__44c7_s_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c7_s_p2_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c7_s_p2_0[] = { + 3, 5, 5, 8, 8, 0, 5, 5, 8, 8, 0, 5, 5, 8, 8, 0, + 7, 7, 9, 9, 0, 0, 0, 9, 9, 5, 7, 7, 9, 9, 0, 8, + 8,10,10, 0, 8, 7,10, 9, 0,10,10,11,11, 0, 0, 0, + 11,11, 5, 7, 7, 9, 9, 0, 8, 8,10,10, 0, 7, 8, 9, + 10, 0,10,10,11,11, 0, 0, 0,11,11, 8, 9, 9,11,10, + 0,11,11,12,12, 0,11,10,12,12, 0,13,14,14,14, 0, + 0, 0,14,13, 8, 9, 9,10,11, 0,11,11,12,12, 0,10, + 11,12,12, 0,13,13,14,14, 0, 0, 0,13,14, 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, 5, 8, 7,11,10, 0, 7, 7,10,10, + 0, 7, 7,10,10, 0, 9, 9,11,10, 0, 0, 0,11,11, 5, + 7, 8,10,11, 0, 7, 7,10,10, 0, 7, 7,10,10, 0, 9, + 9,10,11, 0, 0, 0,11,11, 8,10, 9,12,12, 0,10,10, + 12,12, 0,10,10,12,12, 0,12,12,13,13, 0, 0, 0,13, + 13, 8, 9,10,12,12, 0,10,10,12,12, 0,10,10,11,12, + 0,12,12,13,13, 0, 0, 0,13,13, 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, 5, 8, 8,11,11, 0, 7, 7,10,10, 0, 7, 7, + 10,10, 0, 9, 9,10,11, 0, 0, 0,11,10, 5, 8, 8,10, + 11, 0, 7, 7,10,10, 0, 7, 7,10,10, 0, 9, 9,11,10, + 0, 0, 0,10,11, 9,10,10,12,12, 0,10,10,12,12, 0, + 10,10,12,12, 0,12,13,13,13, 0, 0, 0,13,12, 9,10, + 10,12,12, 0,10,10,12,12, 0,10,10,12,12, 0,13,12, + 13,13, 0, 0, 0,12,13, 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, + 7,10,10,14,13, 0, 9, 9,12,12, 0, 9, 9,12,12, 0, + 10,10,12,12, 0, 0, 0,12,12, 7,10,10,13,14, 0, 9, + 9,12,13, 0, 9, 9,12,12, 0,10,10,12,12, 0, 0, 0, + 12,12, 9,11,11,14,13, 0,11,10,13,12, 0,11,11,13, + 13, 0,12,12,13,13, 0, 0, 0,13,13, 9,11,11,13,14, + 0,10,11,12,13, 0,11,11,13,13, 0,12,12,13,13, 0, + 0, 0,13,13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, + 11,11,14,14, 0,10,11,13,13, 0,11,10,13,13, 0,12, + 12,13,13, 0, 0, 0,13,12, 9,11,11,14,14, 0,11,10, + 13,13, 0,10,11,13,13, 0,12,12,14,13, 0, 0, 0,13, + 13, +}; + +static float _vq_quantthresh__44c7_s_p2_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c7_s_p2_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c7_s_p2_0 = { + _vq_quantthresh__44c7_s_p2_0, + _vq_quantmap__44c7_s_p2_0, + 5, + 5 +}; + +static static_codebook _44c7_s_p2_0 = { + 4, 625, + _vq_lengthlist__44c7_s_p2_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c7_s_p2_0, + NULL, + &_vq_auxt__44c7_s_p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c7_s_p3_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44c7_s_p3_0[] = { + 2, 4, 4, 5, 5, 7, 7, 9, 9, 0, 4, 4, 6, 6, 7, 7, + 9, 9, 0, 4, 4, 6, 6, 7, 7, 9, 9, 0, 5, 5, 6, 6, + 8, 8,10,10, 0, 0, 0, 6, 6, 8, 8,10,10, 0, 0, 0, + 7, 7, 9, 9,10,10, 0, 0, 0, 7, 7, 8, 8,10,10, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c7_s_p3_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44c7_s_p3_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44c7_s_p3_0 = { + _vq_quantthresh__44c7_s_p3_0, + _vq_quantmap__44c7_s_p3_0, + 9, + 9 +}; + +static static_codebook _44c7_s_p3_0 = { + 2, 81, + _vq_lengthlist__44c7_s_p3_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44c7_s_p3_0, + NULL, + &_vq_auxt__44c7_s_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c7_s_p4_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44c7_s_p4_0[] = { + 3, 4, 4, 5, 5, 7, 7, 8, 8, 8, 8, 9, 9,10,10,11, + 11, 0, 4, 4, 6, 6, 7, 7, 8, 8, 9, 9,10,10,11,11, + 12,12, 0, 4, 4, 6, 6, 7, 7, 8, 8, 9, 9,10,10,11, + 11,12,12, 0, 5, 5, 6, 6, 8, 8, 9, 9, 9, 9,10,10, + 11,12,12,12, 0, 0, 0, 6, 6, 8, 7, 9, 9, 9, 9,10, + 10,11,11,12,12, 0, 0, 0, 7, 7, 8, 8, 9, 9,10,10, + 11,11,12,12,13,12, 0, 0, 0, 7, 7, 8, 8, 9, 9,10, + 10,11,11,12,12,12,13, 0, 0, 0, 7, 7, 8, 8, 9, 9, + 10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, 8, 8, 9, + 9,10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c7_s_p4_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44c7_s_p4_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44c7_s_p4_0 = { + _vq_quantthresh__44c7_s_p4_0, + _vq_quantmap__44c7_s_p4_0, + 17, + 17 +}; + +static static_codebook _44c7_s_p4_0 = { + 2, 289, + _vq_lengthlist__44c7_s_p4_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44c7_s_p4_0, + NULL, + &_vq_auxt__44c7_s_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c7_s_p5_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c7_s_p5_0[] = { + 1, 4, 4, 5, 7, 7, 6, 7, 7, 4, 6, 7,10,10,10,10, + 10, 9, 4, 6, 6,10,10,10,10, 9,10, 5,10,10, 9,11, + 12,10,11,12, 7,10,10,11,12,12,12,12,12, 7,10,10, + 11,12,12,12,12,12, 6,10,10,10,12,12,11,12,12, 7, + 10,10,12,12,12,12,11,12, 7,10,10,11,12,12,12,12, + 12, +}; + +static float _vq_quantthresh__44c7_s_p5_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__44c7_s_p5_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c7_s_p5_0 = { + _vq_quantthresh__44c7_s_p5_0, + _vq_quantmap__44c7_s_p5_0, + 3, + 3 +}; + +static static_codebook _44c7_s_p5_0 = { + 4, 81, + _vq_lengthlist__44c7_s_p5_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__44c7_s_p5_0, + NULL, + &_vq_auxt__44c7_s_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c7_s_p5_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44c7_s_p5_1[] = { + 3, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8,11, 4, 4, 6, 6, + 7, 7, 8, 8, 9, 9,11, 4, 4, 6, 6, 7, 7, 8, 8, 9, + 9,12, 5, 5, 6, 6, 7, 7, 9, 9, 9, 9,12,12,12, 6, + 6, 7, 7, 9, 9, 9, 9,11,11,11, 7, 7, 7, 7, 8, 8, + 9, 9,11,11,11, 7, 7, 7, 7, 8, 8, 9, 9,11,11,11, + 7, 7, 8, 8, 8, 8, 9, 9,11,11,11,11,11, 8, 8, 8, + 8, 8, 9,11,11,11,11,11, 8, 8, 8, 8, 8, 8,11,11, + 11,11,11, 7, 7, 8, 8, 8, 8, +}; + +static float _vq_quantthresh__44c7_s_p5_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44c7_s_p5_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44c7_s_p5_1 = { + _vq_quantthresh__44c7_s_p5_1, + _vq_quantmap__44c7_s_p5_1, + 11, + 11 +}; + +static static_codebook _44c7_s_p5_1 = { + 2, 121, + _vq_lengthlist__44c7_s_p5_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44c7_s_p5_1, + NULL, + &_vq_auxt__44c7_s_p5_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c7_s_p6_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c7_s_p6_0[] = { + 1, 4, 4, 6, 6, 7, 7, 8, 7, 9, 8,10,10, 6, 5, 5, + 7, 7, 8, 8, 9, 9, 9,10,11,11, 7, 5, 5, 7, 7, 8, + 8, 9, 9,10,10,11,11, 0, 7, 7, 7, 7, 9, 8, 9, 9, + 10,10,11,11, 0, 8, 8, 7, 7, 8, 9, 9, 9,10,10,11, + 11, 0,11,11, 9, 9,10,10,11,10,11,11,12,12, 0,12, + 12, 9, 9,10,10,11,11,11,11,12,12, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static float _vq_quantthresh__44c7_s_p6_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44c7_s_p6_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c7_s_p6_0 = { + _vq_quantthresh__44c7_s_p6_0, + _vq_quantmap__44c7_s_p6_0, + 13, + 13 +}; + +static static_codebook _44c7_s_p6_0 = { + 2, 169, + _vq_lengthlist__44c7_s_p6_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44c7_s_p6_0, + NULL, + &_vq_auxt__44c7_s_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c7_s_p6_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c7_s_p6_1[] = { + 3, 4, 4, 5, 5, 5, 4, 4, 5, 5, 5, 4, 4, 5, 5, 6, + 5, 5, 5, 5, 6, 6, 6, 5, 5, +}; + +static float _vq_quantthresh__44c7_s_p6_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c7_s_p6_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c7_s_p6_1 = { + _vq_quantthresh__44c7_s_p6_1, + _vq_quantmap__44c7_s_p6_1, + 5, + 5 +}; + +static static_codebook _44c7_s_p6_1 = { + 2, 25, + _vq_lengthlist__44c7_s_p6_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c7_s_p6_1, + NULL, + &_vq_auxt__44c7_s_p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c7_s_p7_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c7_s_p7_0[] = { + 1, 4, 4, 6, 6, 7, 8, 9, 9,10,10,12,11, 6, 5, 5, + 7, 7, 8, 8, 9,10,11,11,12,12, 7, 5, 5, 7, 7, 8, + 8,10,10,11,11,12,12,20, 7, 7, 7, 7, 8, 9,10,10, + 11,11,12,13,20, 7, 7, 7, 7, 9, 9,10,10,11,12,13, + 13,20,11,11, 8, 8, 9, 9,11,11,12,12,13,13,20,11, + 11, 8, 8, 9, 9,11,11,12,12,13,13,20,20,20,10,10, + 10,10,12,12,13,13,13,13,20,20,20,10,10,10,10,12, + 12,13,13,13,14,20,20,20,14,14,11,11,12,12,13,13, + 14,14,20,20,20,14,14,11,11,12,12,13,13,14,14,20, + 20,20,20,19,13,13,13,13,14,14,15,14,19,19,19,19, + 19,13,13,13,13,14,14,15,15, +}; + +static float _vq_quantthresh__44c7_s_p7_0[] = { + -60.5, -49.5, -38.5, -27.5, -16.5, -5.5, 5.5, 16.5, + 27.5, 38.5, 49.5, 60.5, +}; + +static long _vq_quantmap__44c7_s_p7_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c7_s_p7_0 = { + _vq_quantthresh__44c7_s_p7_0, + _vq_quantmap__44c7_s_p7_0, + 13, + 13 +}; + +static static_codebook _44c7_s_p7_0 = { + 2, 169, + _vq_lengthlist__44c7_s_p7_0, + 1, -523206656, 1618345984, 4, 0, + _vq_quantlist__44c7_s_p7_0, + NULL, + &_vq_auxt__44c7_s_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c7_s_p7_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44c7_s_p7_1[] = { + 4, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7, 8, 6, 6, 7, 7, + 7, 7, 7, 7, 7, 7, 8, 6, 6, 6, 7, 7, 7, 7, 7, 7, + 7, 8, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 7, + 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 7, 7, 7, 7, 7, 7, + 7, 7, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, + 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 7, 7, 7, + 7, 7, 7, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 8, 8, + 8, 8, 8, 7, 7, 7, 7, 7, 7, +}; + +static float _vq_quantthresh__44c7_s_p7_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44c7_s_p7_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44c7_s_p7_1 = { + _vq_quantthresh__44c7_s_p7_1, + _vq_quantmap__44c7_s_p7_1, + 11, + 11 +}; + +static static_codebook _44c7_s_p7_1 = { + 2, 121, + _vq_lengthlist__44c7_s_p7_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44c7_s_p7_1, + NULL, + &_vq_auxt__44c7_s_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c7_s_p8_0[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__44c7_s_p8_0[] = { + 1, 4, 4, 7, 7, 8, 8, 8, 7, 9, 8, 9, 9,10,10, 6, + 5, 5, 7, 7, 9, 9, 8, 8,10, 9,11,10,12,11, 6, 5, + 5, 8, 7, 9, 9, 8, 8,10,10,11,11,12,11,19, 8, 8, + 8, 8,10,10, 9, 9,10,10,11,11,12,11,19, 8, 8, 8, + 8,10,10, 9, 9,10,10,11,11,12,12,19,12,12, 9, 9, + 10,10, 9,10,10,10,11,11,12,12,19,12,12, 9, 9,10, + 10,10,10,10,10,12,12,12,12,19,19,19, 9, 9, 9, 9, + 11,10,11,11,12,11,13,13,19,19,19, 9, 9, 9, 9,11, + 10,11,11,11,12,13,13,19,19,19,13,13,10,10,11,11, + 12,12,12,12,13,12,19,19,19,14,13,10,10,11,11,12, + 12,12,13,13,13,19,19,19,19,19,12,12,12,11,12,13, + 14,13,13,13,19,19,19,19,19,12,12,12,11,12,12,13, + 14,13,14,19,19,19,19,19,16,16,12,13,12,13,13,14, + 15,14,19,18,18,18,18,16,15,12,11,12,11,14,12,14, + 14, +}; + +static float _vq_quantthresh__44c7_s_p8_0[] = { + -136.5, -115.5, -94.5, -73.5, -52.5, -31.5, -10.5, 10.5, + 31.5, 52.5, 73.5, 94.5, 115.5, 136.5, +}; + +static long _vq_quantmap__44c7_s_p8_0[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__44c7_s_p8_0 = { + _vq_quantthresh__44c7_s_p8_0, + _vq_quantmap__44c7_s_p8_0, + 15, + 15 +}; + +static static_codebook _44c7_s_p8_0 = { + 2, 225, + _vq_lengthlist__44c7_s_p8_0, + 1, -520986624, 1620377600, 4, 0, + _vq_quantlist__44c7_s_p8_0, + NULL, + &_vq_auxt__44c7_s_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c7_s_p8_1[] = { + 10, + 9, + 11, + 8, + 12, + 7, + 13, + 6, + 14, + 5, + 15, + 4, + 16, + 3, + 17, + 2, + 18, + 1, + 19, + 0, + 20, +}; + +static long _vq_lengthlist__44c7_s_p8_1[] = { + 3, 5, 5, 7, 6, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8,10, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10, 6, 6, 7, 7, 8, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10, + 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9,10,10,10, 8, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9,10,10,10, 8, 8, 8, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10,10, + 10, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9,10,10,10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9,10,11,10,10,10, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9,10, 9, 9,10, 9, 9,10,11,10, + 11,10, 9, 9, 9, 9, 9, 9, 9,10,10,10, 9,10, 9, 9, + 9, 9,11,10,11,10,10, 9, 9, 9, 9, 9, 9,10, 9, 9, + 10, 9, 9,10, 9, 9,10,11,10,10,11,10, 9, 9, 9, 9, + 9,10,10, 9,10,10,10,10, 9,10,10,10,10,10,10,11, + 11,11,10, 9, 9, 9,10,10,10,10,10,10,10,10,10,10, + 10,10,10,11,11,10,10,10,10,10,10,10,10,10,10,10, + 10, 9,10,10, 9,10,11,11,10,11,10,11,10, 9,10,10, + 9,10,10,10,10,10,10,10,10,10,10,11,11,11,11,10, + 11,11,10,10,10,10,10,10, 9,10, 9,10,10, 9,10, 9, + 10,10,10,11,10,11,10,11,11,10,10,10,10,10,10, 9, + 10,10,10,10,10,10,10,11,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,11,10,11, + 11,10,10,10,10, 9, 9,10,10, 9, 9,10, 9,10,10,10, + 10,11,11,10,10,10,10,10,10,10, 9, 9,10,10,10, 9, + 9,10,10,10,10,10,11,10,11,10,10,10,10,10,10, 9, + 10,10,10,10,10,10,10,10,10, +}; + +static float _vq_quantthresh__44c7_s_p8_1[] = { + -9.5, -8.5, -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, + -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, + 6.5, 7.5, 8.5, 9.5, +}; + +static long _vq_quantmap__44c7_s_p8_1[] = { + 19, 17, 15, 13, 11, 9, 7, 5, + 3, 1, 0, 2, 4, 6, 8, 10, + 12, 14, 16, 18, 20, +}; + +static encode_aux_threshmatch _vq_auxt__44c7_s_p8_1 = { + _vq_quantthresh__44c7_s_p8_1, + _vq_quantmap__44c7_s_p8_1, + 21, + 21 +}; + +static static_codebook _44c7_s_p8_1 = { + 2, 441, + _vq_lengthlist__44c7_s_p8_1, + 1, -529268736, 1611661312, 5, 0, + _vq_quantlist__44c7_s_p8_1, + NULL, + &_vq_auxt__44c7_s_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c7_s_p9_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c7_s_p9_0[] = { + 1, 3, 3,11,11,11,11,11,11,11,11,11,11, 4, 6, 6, + 11,11,11,11,11,11,11,11,11,11, 4, 7, 7,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11, +}; + +static float _vq_quantthresh__44c7_s_p9_0[] = { + -3503.5, -2866.5, -2229.5, -1592.5, -955.5, -318.5, 318.5, 955.5, + 1592.5, 2229.5, 2866.5, 3503.5, +}; + +static long _vq_quantmap__44c7_s_p9_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c7_s_p9_0 = { + _vq_quantthresh__44c7_s_p9_0, + _vq_quantmap__44c7_s_p9_0, + 13, + 13 +}; + +static static_codebook _44c7_s_p9_0 = { + 2, 169, + _vq_lengthlist__44c7_s_p9_0, + 1, -511845376, 1630791680, 4, 0, + _vq_quantlist__44c7_s_p9_0, + NULL, + &_vq_auxt__44c7_s_p9_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c7_s_p9_1[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c7_s_p9_1[] = { + 1, 4, 4, 7, 7, 7, 7, 7, 6, 8, 8, 8, 8, 6, 6, 6, + 8, 8, 9, 8, 8, 7, 9, 8,11,10, 5, 6, 6, 8, 8, 9, + 8, 8, 8,10, 9,11,11,16, 8, 8, 9, 8, 9, 9, 9, 8, + 10, 9,11,10,16, 8, 8, 9, 9,10,10, 9, 9,10,10,11, + 11,16,13,13, 9, 9,10,10, 9,10,11,11,12,11,16,13, + 13, 9, 8,10, 9,10,10,10,10,11,11,16,14,16, 8, 9, + 9, 9,11,10,11,11,12,11,16,16,16, 9, 7,10, 7,11, + 10,11,11,12,11,16,16,16,12,12, 9,10,11,11,12,11, + 12,12,16,16,16,12,10,10, 7,11, 8,12,11,12,12,16, + 16,15,16,16,11,12,10,10,12,11,12,12,16,16,16,15, + 15,11,11,10,10,12,12,12,12, +}; + +static float _vq_quantthresh__44c7_s_p9_1[] = { + -269.5, -220.5, -171.5, -122.5, -73.5, -24.5, 24.5, 73.5, + 122.5, 171.5, 220.5, 269.5, +}; + +static long _vq_quantmap__44c7_s_p9_1[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c7_s_p9_1 = { + _vq_quantthresh__44c7_s_p9_1, + _vq_quantmap__44c7_s_p9_1, + 13, + 13 +}; + +static static_codebook _44c7_s_p9_1 = { + 2, 169, + _vq_lengthlist__44c7_s_p9_1, + 1, -518889472, 1622704128, 4, 0, + _vq_quantlist__44c7_s_p9_1, + NULL, + &_vq_auxt__44c7_s_p9_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c7_s_p9_2[] = { + 24, + 23, + 25, + 22, + 26, + 21, + 27, + 20, + 28, + 19, + 29, + 18, + 30, + 17, + 31, + 16, + 32, + 15, + 33, + 14, + 34, + 13, + 35, + 12, + 36, + 11, + 37, + 10, + 38, + 9, + 39, + 8, + 40, + 7, + 41, + 6, + 42, + 5, + 43, + 4, + 44, + 3, + 45, + 2, + 46, + 1, + 47, + 0, + 48, +}; + +static long _vq_lengthlist__44c7_s_p9_2[] = { + 2, 4, 3, 4, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, +}; + +static float _vq_quantthresh__44c7_s_p9_2[] = { + -23.5, -22.5, -21.5, -20.5, -19.5, -18.5, -17.5, -16.5, + -15.5, -14.5, -13.5, -12.5, -11.5, -10.5, -9.5, -8.5, + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, + 8.5, 9.5, 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, + 16.5, 17.5, 18.5, 19.5, 20.5, 21.5, 22.5, 23.5, +}; + +static long _vq_quantmap__44c7_s_p9_2[] = { + 47, 45, 43, 41, 39, 37, 35, 33, + 31, 29, 27, 25, 23, 21, 19, 17, + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, + 48, +}; + +static encode_aux_threshmatch _vq_auxt__44c7_s_p9_2 = { + _vq_quantthresh__44c7_s_p9_2, + _vq_quantmap__44c7_s_p9_2, + 49, + 49 +}; + +static static_codebook _44c7_s_p9_2 = { + 1, 49, + _vq_lengthlist__44c7_s_p9_2, + 1, -526909440, 1611661312, 6, 0, + _vq_quantlist__44c7_s_p9_2, + NULL, + &_vq_auxt__44c7_s_p9_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44c7_s_short[] = { + 4,11,12,14,15,15,17,17,18,18, 5, 6, 6, 8, 9,10, + 13,17,18,19, 7, 5, 4, 6, 8, 9,11,15,19,19, 8, 6, + 5, 5, 6, 7,11,14,16,17, 9, 7, 7, 6, 7, 7,10,13, + 15,19,10, 8, 7, 6, 7, 6, 7, 9,14,16,12,10, 9, 7, + 7, 6, 4, 5,10,15,14,13,11, 7, 6, 6, 4, 2, 7,13, + 16,16,15, 9, 8, 8, 8, 6, 9,13,19,19,17,12,11,10, + 10, 9,11,14, +}; + +static static_codebook _huff_book__44c7_s_short = { + 2, 100, + _huff_lengthlist__44c7_s_short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44c8_s_long[] = { + 3, 8,12,13,14,14,14,13,14,14, 6, 4, 5, 8,10,10, + 11,11,14,13, 9, 5, 4, 5, 7, 8, 9,10,13,13,12, 7, + 5, 4, 5, 6, 8, 9,12,13,13, 9, 6, 5, 5, 5, 7, 9, + 11,14,12,10, 7, 6, 5, 4, 6, 7,10,11,12,11, 9, 8, + 7, 5, 5, 6,10,10,13,12,10, 9, 8, 6, 6, 5, 8,10, + 14,13,12,12,11,10, 9, 7, 8,10,12,13,14,14,13,12, + 11, 9, 9,10, +}; + +static static_codebook _huff_book__44c8_s_long = { + 2, 100, + _huff_lengthlist__44c8_s_long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44c8_s_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c8_s_p1_0[] = { + 1, 5, 5, 0, 5, 5, 0, 5, 5, 5, 7, 7, 0, 9, 8, 0, + 9, 8, 6, 7, 7, 0, 8, 9, 0, 8, 9, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 5, 9, 8, 0, 8, 8, 0, 8, 8, 5, 8, 9, + 0, 8, 8, 0, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, + 9, 8, 0, 8, 8, 0, 8, 8, 5, 8, 9, 0, 8, 8, 0, 8, + 8, +}; + +static float _vq_quantthresh__44c8_s_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44c8_s_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c8_s_p1_0 = { + _vq_quantthresh__44c8_s_p1_0, + _vq_quantmap__44c8_s_p1_0, + 3, + 3 +}; + +static static_codebook _44c8_s_p1_0 = { + 4, 81, + _vq_lengthlist__44c8_s_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44c8_s_p1_0, + NULL, + &_vq_auxt__44c8_s_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c8_s_p2_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c8_s_p2_0[] = { + 3, 5, 5, 8, 8, 0, 5, 5, 8, 8, 0, 5, 5, 8, 8, 0, + 7, 7, 9, 9, 0, 0, 0, 9, 9, 5, 7, 7, 9, 9, 0, 8, + 7,10, 9, 0, 8, 7,10, 9, 0,10,10,11,11, 0, 0, 0, + 11,11, 5, 7, 7, 9, 9, 0, 7, 8, 9,10, 0, 7, 8, 9, + 10, 0,10,10,11,11, 0, 0, 0,11,11, 8, 9, 9,11,10, + 0,11,10,12,11, 0,11,10,12,12, 0,13,13,14,14, 0, + 0, 0,14,13, 8, 9, 9,10,11, 0,10,11,12,12, 0,10, + 11,12,12, 0,13,13,14,14, 0, 0, 0,13,14, 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, 5, 8, 7,11,10, 0, 7, 7,10,10, + 0, 7, 7,10,10, 0, 9, 9,10,10, 0, 0, 0,11,10, 5, + 7, 8,10,11, 0, 7, 7,10,10, 0, 7, 7,10,10, 0, 9, + 9,10,10, 0, 0, 0,10,10, 8,10, 9,12,12, 0,10,10, + 12,11, 0,10,10,12,12, 0,12,12,13,12, 0, 0, 0,13, + 12, 8, 9,10,12,12, 0,10,10,11,12, 0,10,10,11,12, + 0,12,12,13,13, 0, 0, 0,12,13, 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, 6, 8, 7,11,10, 0, 7, 7,10,10, 0, 7, 7, + 10,10, 0, 9, 9,10,11, 0, 0, 0,10,10, 6, 7, 8,10, + 11, 0, 7, 7,10,10, 0, 7, 7,10,10, 0, 9, 9,10,10, + 0, 0, 0,10,10, 9,10, 9,12,12, 0,10,10,12,12, 0, + 10,10,12,11, 0,12,12,13,13, 0, 0, 0,13,12, 8, 9, + 10,12,12, 0,10,10,12,12, 0,10,10,11,12, 0,12,12, + 13,13, 0, 0, 0,12,13, 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, + 7,10,10,13,13, 0, 9, 9,12,12, 0, 9, 9,12,12, 0, + 10,10,12,12, 0, 0, 0,12,12, 7,10,10,13,13, 0, 9, + 9,12,12, 0, 9, 9,12,12, 0,10,10,12,12, 0, 0, 0, + 12,12, 9,11,11,14,13, 0,10,10,13,12, 0,11,10,13, + 12, 0,12,12,13,12, 0, 0, 0,13,13, 9,11,11,13,14, + 0,10,11,12,13, 0,10,11,13,13, 0,12,12,12,13, 0, + 0, 0,13,13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, + 11,11,14,14, 0,10,11,13,13, 0,11,10,13,13, 0,11, + 12,13,13, 0, 0, 0,13,12, 9,11,11,14,14, 0,11,10, + 13,13, 0,10,11,13,13, 0,12,12,13,13, 0, 0, 0,12, + 13, +}; + +static float _vq_quantthresh__44c8_s_p2_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c8_s_p2_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c8_s_p2_0 = { + _vq_quantthresh__44c8_s_p2_0, + _vq_quantmap__44c8_s_p2_0, + 5, + 5 +}; + +static static_codebook _44c8_s_p2_0 = { + 4, 625, + _vq_lengthlist__44c8_s_p2_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c8_s_p2_0, + NULL, + &_vq_auxt__44c8_s_p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c8_s_p3_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44c8_s_p3_0[] = { + 2, 4, 4, 5, 5, 7, 7, 9, 9, 0, 4, 4, 6, 6, 7, 7, + 9, 9, 0, 4, 4, 6, 6, 7, 7, 9, 9, 0, 5, 5, 6, 6, + 8, 8,10,10, 0, 0, 0, 6, 6, 8, 8,10,10, 0, 0, 0, + 7, 7, 9, 9,10,10, 0, 0, 0, 7, 7, 8, 8,10,10, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c8_s_p3_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44c8_s_p3_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44c8_s_p3_0 = { + _vq_quantthresh__44c8_s_p3_0, + _vq_quantmap__44c8_s_p3_0, + 9, + 9 +}; + +static static_codebook _44c8_s_p3_0 = { + 2, 81, + _vq_lengthlist__44c8_s_p3_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44c8_s_p3_0, + NULL, + &_vq_auxt__44c8_s_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c8_s_p4_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44c8_s_p4_0[] = { + 3, 4, 4, 5, 5, 7, 7, 8, 8, 8, 8, 9, 9,10,10,11, + 11, 0, 4, 4, 6, 6, 7, 7, 8, 8, 9, 8,10,10,11,11, + 11,11, 0, 4, 4, 6, 6, 7, 7, 8, 8, 9, 9,10,10,11, + 11,11,11, 0, 6, 5, 6, 6, 7, 7, 9, 9, 9, 9,10,10, + 11,11,12,12, 0, 0, 0, 6, 6, 7, 7, 9, 9, 9, 9,10, + 10,11,11,12,12, 0, 0, 0, 7, 7, 8, 8, 9, 9,10,10, + 11,11,11,12,12,12, 0, 0, 0, 7, 7, 8, 8, 9, 9,10, + 10,11,11,11,12,12,12, 0, 0, 0, 7, 7, 8, 8, 9, 9, + 10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, 8, 8, 9, + 9,10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c8_s_p4_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44c8_s_p4_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44c8_s_p4_0 = { + _vq_quantthresh__44c8_s_p4_0, + _vq_quantmap__44c8_s_p4_0, + 17, + 17 +}; + +static static_codebook _44c8_s_p4_0 = { + 2, 289, + _vq_lengthlist__44c8_s_p4_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44c8_s_p4_0, + NULL, + &_vq_auxt__44c8_s_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c8_s_p5_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c8_s_p5_0[] = { + 1, 4, 4, 5, 7, 7, 6, 7, 7, 4, 7, 6,10,10,10,10, + 10,10, 4, 6, 6,10,10,10,10, 9,10, 5,10,10, 9,11, + 11,10,11,11, 7,10,10,11,12,12,12,12,12, 7,10,10, + 11,12,12,12,12,12, 6,10,10,10,12,12,10,12,12, 7, + 10,10,11,12,12,12,12,12, 7,10,10,11,12,12,12,12, + 12, +}; + +static float _vq_quantthresh__44c8_s_p5_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__44c8_s_p5_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c8_s_p5_0 = { + _vq_quantthresh__44c8_s_p5_0, + _vq_quantmap__44c8_s_p5_0, + 3, + 3 +}; + +static static_codebook _44c8_s_p5_0 = { + 4, 81, + _vq_lengthlist__44c8_s_p5_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__44c8_s_p5_0, + NULL, + &_vq_auxt__44c8_s_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c8_s_p5_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44c8_s_p5_1[] = { + 3, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8,11, 4, 5, 6, 6, + 7, 7, 8, 8, 8, 8,11, 5, 5, 6, 6, 7, 7, 8, 8, 8, + 9,12, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9,12,12,12, 6, + 6, 7, 7, 8, 8, 9, 9,11,11,11, 6, 6, 7, 7, 8, 8, + 8, 8,11,11,11, 6, 6, 7, 7, 8, 8, 8, 8,11,11,11, + 7, 7, 7, 8, 8, 8, 8, 8,11,11,11,11,11, 7, 7, 8, + 8, 8, 8,11,11,11,11,11, 7, 7, 7, 7, 8, 8,11,11, + 11,11,11, 7, 7, 7, 7, 8, 8, +}; + +static float _vq_quantthresh__44c8_s_p5_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44c8_s_p5_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44c8_s_p5_1 = { + _vq_quantthresh__44c8_s_p5_1, + _vq_quantmap__44c8_s_p5_1, + 11, + 11 +}; + +static static_codebook _44c8_s_p5_1 = { + 2, 121, + _vq_lengthlist__44c8_s_p5_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44c8_s_p5_1, + NULL, + &_vq_auxt__44c8_s_p5_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c8_s_p6_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c8_s_p6_0[] = { + 1, 4, 4, 6, 6, 7, 7, 8, 8, 9, 9,10,10, 6, 5, 5, + 7, 7, 8, 8, 9, 9,10,10,11,11, 6, 5, 5, 7, 7, 8, + 8, 9, 9,10,10,11,11, 0, 7, 7, 7, 7, 9, 9,10,10, + 10,10,11,11, 0, 7, 7, 7, 7, 9, 9,10,10,10,10,11, + 11, 0,11,11, 9, 9,10,10,11,11,11,11,12,12, 0,12, + 12, 9, 9,10,10,11,11,12,12,12,12, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static float _vq_quantthresh__44c8_s_p6_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44c8_s_p6_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c8_s_p6_0 = { + _vq_quantthresh__44c8_s_p6_0, + _vq_quantmap__44c8_s_p6_0, + 13, + 13 +}; + +static static_codebook _44c8_s_p6_0 = { + 2, 169, + _vq_lengthlist__44c8_s_p6_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44c8_s_p6_0, + NULL, + &_vq_auxt__44c8_s_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c8_s_p6_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c8_s_p6_1[] = { + 3, 4, 4, 5, 5, 5, 4, 4, 5, 5, 5, 4, 4, 5, 5, 6, + 5, 5, 5, 5, 6, 6, 6, 5, 5, +}; + +static float _vq_quantthresh__44c8_s_p6_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c8_s_p6_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c8_s_p6_1 = { + _vq_quantthresh__44c8_s_p6_1, + _vq_quantmap__44c8_s_p6_1, + 5, + 5 +}; + +static static_codebook _44c8_s_p6_1 = { + 2, 25, + _vq_lengthlist__44c8_s_p6_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c8_s_p6_1, + NULL, + &_vq_auxt__44c8_s_p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c8_s_p7_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c8_s_p7_0[] = { + 1, 4, 4, 6, 6, 8, 7, 9, 9,10,10,12,12, 6, 5, 5, + 7, 7, 8, 8,10,10,11,11,12,12, 7, 5, 5, 7, 7, 8, + 8,10,10,11,11,12,12,21, 7, 7, 7, 7, 8, 9,10,10, + 11,11,12,12,21, 7, 7, 7, 7, 9, 9,10,10,12,12,13, + 13,21,11,11, 8, 8, 9, 9,11,11,12,12,13,13,21,11, + 11, 8, 8, 9, 9,11,11,12,12,13,13,21,21,21,10,10, + 10,10,11,11,12,13,13,13,21,21,21,10,10,10,10,11, + 11,13,13,14,13,21,21,21,13,13,11,11,12,12,13,13, + 14,14,21,21,21,14,14,11,11,12,12,13,13,14,14,21, + 21,21,21,20,13,13,13,12,14,14,16,15,20,20,20,20, + 20,13,13,13,13,14,13,15,15, +}; + +static float _vq_quantthresh__44c8_s_p7_0[] = { + -60.5, -49.5, -38.5, -27.5, -16.5, -5.5, 5.5, 16.5, + 27.5, 38.5, 49.5, 60.5, +}; + +static long _vq_quantmap__44c8_s_p7_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c8_s_p7_0 = { + _vq_quantthresh__44c8_s_p7_0, + _vq_quantmap__44c8_s_p7_0, + 13, + 13 +}; + +static static_codebook _44c8_s_p7_0 = { + 2, 169, + _vq_lengthlist__44c8_s_p7_0, + 1, -523206656, 1618345984, 4, 0, + _vq_quantlist__44c8_s_p7_0, + NULL, + &_vq_auxt__44c8_s_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c8_s_p7_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44c8_s_p7_1[] = { + 4, 5, 6, 6, 6, 7, 7, 7, 7, 7, 7, 8, 6, 6, 6, 7, + 7, 7, 7, 7, 7, 7, 8, 6, 6, 6, 6, 7, 7, 7, 7, 7, + 7, 8, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 7, + 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 7, 7, 7, 7, 7, 7, + 7, 7, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, + 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 7, 7, 7, + 7, 7, 7, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 8, 8, + 8, 8, 8, 7, 7, 7, 7, 7, 7, +}; + +static float _vq_quantthresh__44c8_s_p7_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44c8_s_p7_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44c8_s_p7_1 = { + _vq_quantthresh__44c8_s_p7_1, + _vq_quantmap__44c8_s_p7_1, + 11, + 11 +}; + +static static_codebook _44c8_s_p7_1 = { + 2, 121, + _vq_lengthlist__44c8_s_p7_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44c8_s_p7_1, + NULL, + &_vq_auxt__44c8_s_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c8_s_p8_0[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__44c8_s_p8_0[] = { + 1, 4, 4, 7, 6, 8, 8, 8, 7, 9, 8,10,10,11,10, 6, + 5, 5, 7, 7, 9, 9, 8, 8,10,10,11,11,12,11, 6, 5, + 5, 7, 7, 9, 9, 9, 9,10,10,11,11,12,12,20, 8, 8, + 8, 8, 9, 9, 9, 9,10,10,11,11,12,12,20, 8, 8, 8, + 8,10, 9, 9, 9,10,10,11,11,12,12,20,12,12, 9, 9, + 10,10,10,10,10,11,12,12,12,12,20,12,12, 9, 9,10, + 10,10,10,11,11,12,12,13,13,20,20,20, 9, 9, 9, 9, + 11,10,11,11,12,12,12,13,20,19,19, 9, 9, 9, 9,11, + 11,11,12,12,12,13,13,19,19,19,13,13,10,10,11,11, + 12,12,13,13,13,13,19,19,19,14,13,11,10,11,11,12, + 12,12,13,13,13,19,19,19,19,19,12,12,12,12,13,13, + 13,13,14,13,19,19,19,19,19,12,12,12,11,12,12,13, + 14,14,14,19,19,19,19,19,16,15,13,12,13,13,13,14, + 14,14,19,19,19,19,19,17,17,13,12,13,11,14,13,15, + 15, +}; + +static float _vq_quantthresh__44c8_s_p8_0[] = { + -136.5, -115.5, -94.5, -73.5, -52.5, -31.5, -10.5, 10.5, + 31.5, 52.5, 73.5, 94.5, 115.5, 136.5, +}; + +static long _vq_quantmap__44c8_s_p8_0[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__44c8_s_p8_0 = { + _vq_quantthresh__44c8_s_p8_0, + _vq_quantmap__44c8_s_p8_0, + 15, + 15 +}; + +static static_codebook _44c8_s_p8_0 = { + 2, 225, + _vq_lengthlist__44c8_s_p8_0, + 1, -520986624, 1620377600, 4, 0, + _vq_quantlist__44c8_s_p8_0, + NULL, + &_vq_auxt__44c8_s_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c8_s_p8_1[] = { + 10, + 9, + 11, + 8, + 12, + 7, + 13, + 6, + 14, + 5, + 15, + 4, + 16, + 3, + 17, + 2, + 18, + 1, + 19, + 0, + 20, +}; + +static long _vq_lengthlist__44c8_s_p8_1[] = { + 4, 5, 5, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8,10, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10, 6, 6, 7, 7, 8, + 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10, + 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9,10,10,10, 8, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9,10,10,10, 8, 8, 8, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10,10, + 10, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9,10,10,10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9,10,10,10,10,10, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10,10,10, + 10,10, 9, 9, 9, 9, 9, 9, 9, 9,10, 9, 9, 9, 9, 9, + 9, 9,10,10,10,10,10, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9,10,10,10,10,10, 9, 9, 9, 9, + 9, 9, 9, 9,10,10,10, 9, 9, 9, 9, 9,10,10,10,10, + 10,10,10, 9, 9, 9, 9, 9,10,10,10, 9, 9, 9, 9, 9, + 9,10,10,10,10,10,10,10, 9,10,10, 9,10,10,10,10, + 9,10, 9,10,10, 9,10,10,10,10,10,10,10, 9,10,10, + 10,10,10,10, 9, 9,10,10, 9,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, 9, 9, 9,10, 9, 9, 9, 9, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10, 9, 9, + 10, 9,10, 9,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10, 9, 9,10, 9, 9, 9,10,10,10,10,10,10, + 10,10,10,10,10, 9, 9, 9, 9, 9, 9,10, 9, 9,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10, 9,10, 9, + 9,10, 9, 9,10,10,10,10,10,10,10,10,10,10,10,10, + 10, 9, 9,10,10, 9,10, 9, 9, +}; + +static float _vq_quantthresh__44c8_s_p8_1[] = { + -9.5, -8.5, -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, + -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, + 6.5, 7.5, 8.5, 9.5, +}; + +static long _vq_quantmap__44c8_s_p8_1[] = { + 19, 17, 15, 13, 11, 9, 7, 5, + 3, 1, 0, 2, 4, 6, 8, 10, + 12, 14, 16, 18, 20, +}; + +static encode_aux_threshmatch _vq_auxt__44c8_s_p8_1 = { + _vq_quantthresh__44c8_s_p8_1, + _vq_quantmap__44c8_s_p8_1, + 21, + 21 +}; + +static static_codebook _44c8_s_p8_1 = { + 2, 441, + _vq_lengthlist__44c8_s_p8_1, + 1, -529268736, 1611661312, 5, 0, + _vq_quantlist__44c8_s_p8_1, + NULL, + &_vq_auxt__44c8_s_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c8_s_p9_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44c8_s_p9_0[] = { + 1, 4, 3,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11, 4, 7, 7,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11, 4, 8,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10, +}; + +static float _vq_quantthresh__44c8_s_p9_0[] = { + -6982.5, -6051.5, -5120.5, -4189.5, -3258.5, -2327.5, -1396.5, -465.5, + 465.5, 1396.5, 2327.5, 3258.5, 4189.5, 5120.5, 6051.5, 6982.5, +}; + +static long _vq_quantmap__44c8_s_p9_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44c8_s_p9_0 = { + _vq_quantthresh__44c8_s_p9_0, + _vq_quantmap__44c8_s_p9_0, + 17, + 17 +}; + +static static_codebook _44c8_s_p9_0 = { + 2, 289, + _vq_lengthlist__44c8_s_p9_0, + 1, -509798400, 1631393792, 5, 0, + _vq_quantlist__44c8_s_p9_0, + NULL, + &_vq_auxt__44c8_s_p9_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c8_s_p9_1[] = { + 9, + 8, + 10, + 7, + 11, + 6, + 12, + 5, + 13, + 4, + 14, + 3, + 15, + 2, + 16, + 1, + 17, + 0, + 18, +}; + +static long _vq_lengthlist__44c8_s_p9_1[] = { + 1, 4, 4, 7, 6, 7, 7, 7, 7, 8, 8, 9, 9,10,10,10, + 10,11,11, 6, 6, 6, 8, 8, 9, 8, 8, 7,10, 8,11,10, + 12,11,12,12,13,13, 5, 5, 6, 8, 8, 9, 9, 8, 8,10, + 9,11,11,12,12,13,13,13,13,17, 8, 8, 9, 9, 9, 9, + 9, 9,10, 9,12,10,12,12,13,12,13,13,17, 9, 8, 9, + 9, 9, 9, 9, 9,10,10,12,12,12,12,13,13,13,13,17, + 13,13, 9, 9,10,10,10,10,11,11,12,11,13,12,13,13, + 14,15,17,13,13, 9, 8,10, 9,10,10,11,11,12,12,14, + 13,15,13,14,15,17,17,17, 9,10, 9,10,11,11,12,12, + 12,12,13,13,14,14,15,15,17,17,17, 9, 8, 9, 8,11, + 11,12,12,12,12,14,13,14,14,14,15,17,17,17,12,14, + 9,10,11,11,12,12,14,13,13,14,15,13,15,15,17,17, + 17,13,11,10, 8,11, 9,13,12,13,13,13,13,13,14,14, + 14,17,17,17,17,17,11,12,11,11,13,13,14,13,15,14, + 13,15,16,15,17,17,17,17,17,11,11,12, 8,13,12,14, + 13,17,14,15,14,15,14,17,17,17,17,17,15,15,12,12, + 12,12,13,14,14,14,15,14,17,14,17,17,17,17,17,16, + 17,12,12,13,12,13,13,14,14,14,14,14,14,17,17,17, + 17,17,17,17,14,14,13,12,13,13,15,15,14,13,15,17, + 17,17,17,17,17,17,17,13,14,13,13,13,13,14,15,15, + 15,14,15,17,17,17,17,17,17,17,16,15,13,14,13,13, + 14,14,15,14,14,16,17,17,17,17,17,17,17,16,16,13, + 14,13,13,14,14,15,14,15,14, +}; + +static float _vq_quantthresh__44c8_s_p9_1[] = { + -416.5, -367.5, -318.5, -269.5, -220.5, -171.5, -122.5, -73.5, + -24.5, 24.5, 73.5, 122.5, 171.5, 220.5, 269.5, 318.5, + 367.5, 416.5, +}; + +static long _vq_quantmap__44c8_s_p9_1[] = { + 17, 15, 13, 11, 9, 7, 5, 3, + 1, 0, 2, 4, 6, 8, 10, 12, + 14, 16, 18, +}; + +static encode_aux_threshmatch _vq_auxt__44c8_s_p9_1 = { + _vq_quantthresh__44c8_s_p9_1, + _vq_quantmap__44c8_s_p9_1, + 19, + 19 +}; + +static static_codebook _44c8_s_p9_1 = { + 2, 361, + _vq_lengthlist__44c8_s_p9_1, + 1, -518287360, 1622704128, 5, 0, + _vq_quantlist__44c8_s_p9_1, + NULL, + &_vq_auxt__44c8_s_p9_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c8_s_p9_2[] = { + 24, + 23, + 25, + 22, + 26, + 21, + 27, + 20, + 28, + 19, + 29, + 18, + 30, + 17, + 31, + 16, + 32, + 15, + 33, + 14, + 34, + 13, + 35, + 12, + 36, + 11, + 37, + 10, + 38, + 9, + 39, + 8, + 40, + 7, + 41, + 6, + 42, + 5, + 43, + 4, + 44, + 3, + 45, + 2, + 46, + 1, + 47, + 0, + 48, +}; + +static long _vq_lengthlist__44c8_s_p9_2[] = { + 2, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, +}; + +static float _vq_quantthresh__44c8_s_p9_2[] = { + -23.5, -22.5, -21.5, -20.5, -19.5, -18.5, -17.5, -16.5, + -15.5, -14.5, -13.5, -12.5, -11.5, -10.5, -9.5, -8.5, + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, + 8.5, 9.5, 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, + 16.5, 17.5, 18.5, 19.5, 20.5, 21.5, 22.5, 23.5, +}; + +static long _vq_quantmap__44c8_s_p9_2[] = { + 47, 45, 43, 41, 39, 37, 35, 33, + 31, 29, 27, 25, 23, 21, 19, 17, + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, + 48, +}; + +static encode_aux_threshmatch _vq_auxt__44c8_s_p9_2 = { + _vq_quantthresh__44c8_s_p9_2, + _vq_quantmap__44c8_s_p9_2, + 49, + 49 +}; + +static static_codebook _44c8_s_p9_2 = { + 1, 49, + _vq_lengthlist__44c8_s_p9_2, + 1, -526909440, 1611661312, 6, 0, + _vq_quantlist__44c8_s_p9_2, + NULL, + &_vq_auxt__44c8_s_p9_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44c8_s_short[] = { + 4,11,13,14,15,15,18,17,19,17, 5, 6, 8, 9,10,10, + 12,15,19,19, 6, 6, 6, 6, 8, 8,11,14,18,19, 8, 6, + 5, 4, 6, 7,10,13,16,17, 9, 7, 6, 5, 6, 7, 9,12, + 15,19,10, 8, 7, 6, 6, 6, 7, 9,13,15,12,10, 9, 8, + 7, 6, 4, 5,10,15,13,13,11, 8, 6, 6, 4, 2, 7,12, + 17,15,16,10, 8, 8, 7, 6, 9,12,19,18,17,13,11,10, + 10, 9,11,14, +}; + +static static_codebook _huff_book__44c8_s_short = { + 2, 100, + _huff_lengthlist__44c8_s_short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44c9_s_long[] = { + 3, 8,12,14,15,15,15,13,15,15, 6, 5, 8,10,12,12, + 13,12,14,13,10, 6, 5, 6, 8, 9,11,11,13,13,13, 8, + 5, 4, 5, 6, 8,10,11,13,14,10, 7, 5, 4, 5, 7, 9, + 11,12,13,11, 8, 6, 5, 4, 5, 7, 9,11,12,11,10, 8, + 7, 5, 4, 5, 9,10,13,13,11,10, 8, 6, 5, 4, 7, 9, + 15,14,13,12,10, 9, 8, 7, 8, 9,12,12,14,13,12,11, + 10, 9, 8, 9, +}; + +static static_codebook _huff_book__44c9_s_long = { + 2, 100, + _huff_lengthlist__44c9_s_long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44c9_s_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c9_s_p1_0[] = { + 1, 5, 5, 0, 5, 5, 0, 5, 5, 6, 8, 8, 0, 9, 8, 0, + 9, 8, 6, 8, 8, 0, 8, 9, 0, 8, 9, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 5, 8, 8, 0, 7, 7, 0, 8, 8, 5, 8, 8, + 0, 7, 8, 0, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, + 9, 8, 0, 8, 8, 0, 7, 7, 5, 8, 9, 0, 8, 8, 0, 7, + 7, +}; + +static float _vq_quantthresh__44c9_s_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44c9_s_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c9_s_p1_0 = { + _vq_quantthresh__44c9_s_p1_0, + _vq_quantmap__44c9_s_p1_0, + 3, + 3 +}; + +static static_codebook _44c9_s_p1_0 = { + 4, 81, + _vq_lengthlist__44c9_s_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44c9_s_p1_0, + NULL, + &_vq_auxt__44c9_s_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c9_s_p2_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c9_s_p2_0[] = { + 3, 5, 5, 8, 8, 0, 5, 5, 8, 8, 0, 5, 5, 8, 8, 0, + 7, 7, 9, 9, 0, 0, 0, 9, 9, 6, 7, 7, 9, 8, 0, 8, + 8, 9, 9, 0, 8, 7, 9, 9, 0, 9,10,10,10, 0, 0, 0, + 11,10, 6, 7, 7, 8, 9, 0, 8, 8, 9, 9, 0, 7, 8, 9, + 9, 0,10, 9,11,10, 0, 0, 0,10,10, 8, 9, 8,10,10, + 0,10,10,12,11, 0,10,10,11,11, 0,12,13,13,13, 0, + 0, 0,13,12, 8, 8, 9,10,10, 0,10,10,11,12, 0,10, + 10,11,11, 0,13,12,13,13, 0, 0, 0,13,13, 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, 6, 8, 7,10,10, 0, 7, 7,10, 9, + 0, 7, 7,10,10, 0, 9, 9,10,10, 0, 0, 0,10,10, 6, + 7, 8,10,10, 0, 7, 7, 9,10, 0, 7, 7,10,10, 0, 9, + 9,10,10, 0, 0, 0,10,10, 8, 9, 9,11,11, 0,10,10, + 11,11, 0,10,10,11,11, 0,12,12,12,12, 0, 0, 0,12, + 12, 8, 9,10,11,11, 0, 9,10,11,11, 0,10,10,11,11, + 0,12,12,12,12, 0, 0, 0,12,12, 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, 5, 8, 7,10,10, 0, 7, 7,10,10, 0, 7, 7, + 10, 9, 0, 9, 9,10,10, 0, 0, 0,10,10, 6, 7, 8,10, + 10, 0, 7, 7,10,10, 0, 7, 7, 9,10, 0, 9, 9,10,10, + 0, 0, 0,10,10, 8,10, 9,12,11, 0,10,10,12,11, 0, + 10, 9,11,11, 0,11,12,12,12, 0, 0, 0,12,12, 8, 9, + 10,11,12, 0,10,10,11,11, 0, 9,10,11,11, 0,12,11, + 12,12, 0, 0, 0,12,12, 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, + 7,10, 9,12,12, 0, 9, 9,12,11, 0, 9, 9,11,11, 0, + 10,10,12,11, 0, 0, 0,11,12, 7, 9,10,12,12, 0, 9, + 9,11,12, 0, 9, 9,11,11, 0,10,10,11,12, 0, 0, 0, + 11,11, 9,11,10,13,12, 0,10,10,12,12, 0,10,10,12, + 12, 0,11,11,12,12, 0, 0, 0,13,12, 9,10,11,12,13, + 0,10,10,12,12, 0,10,10,12,12, 0,11,12,12,12, 0, + 0, 0,12,13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, + 11,10,13,13, 0,10,10,12,12, 0,10,10,12,12, 0,11, + 12,12,12, 0, 0, 0,12,12, 9,10,11,13,13, 0,10,10, + 12,12, 0,10,10,12,12, 0,12,11,13,12, 0, 0, 0,12, + 12, +}; + +static float _vq_quantthresh__44c9_s_p2_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c9_s_p2_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c9_s_p2_0 = { + _vq_quantthresh__44c9_s_p2_0, + _vq_quantmap__44c9_s_p2_0, + 5, + 5 +}; + +static static_codebook _44c9_s_p2_0 = { + 4, 625, + _vq_lengthlist__44c9_s_p2_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c9_s_p2_0, + NULL, + &_vq_auxt__44c9_s_p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c9_s_p3_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44c9_s_p3_0[] = { + 3, 4, 4, 5, 5, 6, 6, 8, 8, 0, 4, 4, 5, 5, 6, 7, + 8, 8, 0, 4, 4, 5, 5, 7, 7, 8, 8, 0, 5, 5, 6, 6, + 7, 7, 9, 9, 0, 0, 0, 6, 6, 7, 7, 9, 9, 0, 0, 0, + 7, 7, 8, 8, 9, 9, 0, 0, 0, 7, 7, 8, 8, 9, 9, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c9_s_p3_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44c9_s_p3_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44c9_s_p3_0 = { + _vq_quantthresh__44c9_s_p3_0, + _vq_quantmap__44c9_s_p3_0, + 9, + 9 +}; + +static static_codebook _44c9_s_p3_0 = { + 2, 81, + _vq_lengthlist__44c9_s_p3_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44c9_s_p3_0, + NULL, + &_vq_auxt__44c9_s_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c9_s_p4_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44c9_s_p4_0[] = { + 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9,10,10,10, + 10, 0, 5, 4, 5, 5, 7, 7, 8, 8, 8, 8, 9, 9,10,10, + 11,11, 0, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9,10, + 10,11,11, 0, 6, 5, 6, 6, 7, 7, 8, 8, 9, 9,10,10, + 11,11,11,12, 0, 0, 0, 6, 6, 7, 7, 8, 8, 9, 9,10, + 10,11,11,12,12, 0, 0, 0, 7, 7, 7, 7, 9, 9, 9, 9, + 10,10,11,11,12,12, 0, 0, 0, 7, 7, 7, 8, 9, 9, 9, + 9,10,10,11,11,12,12, 0, 0, 0, 7, 7, 8, 8, 9, 9, + 10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, 8, 8, 9, + 9,10,10,11,11,12,12,12,12, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c9_s_p4_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44c9_s_p4_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44c9_s_p4_0 = { + _vq_quantthresh__44c9_s_p4_0, + _vq_quantmap__44c9_s_p4_0, + 17, + 17 +}; + +static static_codebook _44c9_s_p4_0 = { + 2, 289, + _vq_lengthlist__44c9_s_p4_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44c9_s_p4_0, + NULL, + &_vq_auxt__44c9_s_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c9_s_p5_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c9_s_p5_0[] = { + 1, 4, 4, 5, 7, 7, 6, 7, 7, 4, 7, 6, 9,10,10,10, + 10, 9, 4, 6, 7, 9,10,10,10, 9,10, 5, 9, 9, 9,11, + 11,10,11,11, 7,10, 9,11,12,11,12,12,12, 7, 9,10, + 11,11,12,12,12,12, 6,10,10,10,12,12,10,12,11, 7, + 10,10,11,12,12,11,12,12, 7,10,10,11,12,12,12,12, + 12, +}; + +static float _vq_quantthresh__44c9_s_p5_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__44c9_s_p5_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c9_s_p5_0 = { + _vq_quantthresh__44c9_s_p5_0, + _vq_quantmap__44c9_s_p5_0, + 3, + 3 +}; + +static static_codebook _44c9_s_p5_0 = { + 4, 81, + _vq_lengthlist__44c9_s_p5_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__44c9_s_p5_0, + NULL, + &_vq_auxt__44c9_s_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c9_s_p5_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44c9_s_p5_1[] = { + 4, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7,11, 5, 5, 6, 6, + 7, 7, 7, 7, 8, 8,11, 5, 5, 6, 6, 7, 7, 7, 7, 8, + 8,11, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8,11,11,11, 6, + 6, 7, 7, 7, 8, 8, 8,11,11,11, 6, 6, 7, 7, 7, 8, + 8, 8,11,11,11, 6, 6, 7, 7, 7, 7, 8, 8,11,11,11, + 7, 7, 7, 7, 7, 7, 8, 8,11,11,11,10,10, 7, 7, 7, + 7, 8, 8,11,11,11,11,11, 7, 7, 7, 7, 7, 7,11,11, + 11,11,11, 7, 7, 7, 7, 7, 7, +}; + +static float _vq_quantthresh__44c9_s_p5_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44c9_s_p5_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44c9_s_p5_1 = { + _vq_quantthresh__44c9_s_p5_1, + _vq_quantmap__44c9_s_p5_1, + 11, + 11 +}; + +static static_codebook _44c9_s_p5_1 = { + 2, 121, + _vq_lengthlist__44c9_s_p5_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44c9_s_p5_1, + NULL, + &_vq_auxt__44c9_s_p5_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c9_s_p6_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c9_s_p6_0[] = { + 2, 4, 4, 6, 6, 7, 7, 7, 7, 8, 8, 9, 9, 5, 4, 4, + 6, 6, 8, 8, 9, 9, 9, 9,10,10, 6, 4, 4, 6, 6, 8, + 8, 9, 9, 9, 9,10,10, 0, 6, 6, 7, 7, 8, 8, 9, 9, + 10,10,11,11, 0, 6, 6, 7, 7, 8, 8, 9, 9,10,10,11, + 11, 0,10,10, 8, 8, 9, 9,10,10,11,11,12,12, 0,11, + 11, 8, 8, 9, 9,10,10,11,11,12,12, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static float _vq_quantthresh__44c9_s_p6_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44c9_s_p6_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c9_s_p6_0 = { + _vq_quantthresh__44c9_s_p6_0, + _vq_quantmap__44c9_s_p6_0, + 13, + 13 +}; + +static static_codebook _44c9_s_p6_0 = { + 2, 169, + _vq_lengthlist__44c9_s_p6_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44c9_s_p6_0, + NULL, + &_vq_auxt__44c9_s_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c9_s_p6_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c9_s_p6_1[] = { + 4, 4, 4, 5, 5, 5, 4, 4, 5, 5, 5, 4, 4, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, +}; + +static float _vq_quantthresh__44c9_s_p6_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c9_s_p6_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c9_s_p6_1 = { + _vq_quantthresh__44c9_s_p6_1, + _vq_quantmap__44c9_s_p6_1, + 5, + 5 +}; + +static static_codebook _44c9_s_p6_1 = { + 2, 25, + _vq_lengthlist__44c9_s_p6_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c9_s_p6_1, + NULL, + &_vq_auxt__44c9_s_p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c9_s_p7_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c9_s_p7_0[] = { + 2, 4, 4, 6, 6, 7, 7, 8, 8,10,10,11,11, 6, 4, 4, + 6, 6, 8, 8, 9, 9,10,10,12,12, 6, 4, 5, 6, 6, 8, + 8, 9, 9,10,10,12,12,20, 6, 6, 6, 6, 8, 8, 9,10, + 11,11,12,12,20, 6, 6, 6, 6, 8, 8,10,10,11,11,12, + 12,20,10,10, 7, 7, 9, 9,10,10,11,11,12,12,20,11, + 11, 7, 7, 9, 9,10,10,11,11,12,12,20,20,20, 9, 9, + 9, 9,11,11,12,12,13,13,20,20,20, 9, 9, 9, 9,11, + 11,12,12,13,13,20,20,20,13,13,10,10,11,11,12,13, + 13,13,20,20,20,13,13,10,10,11,11,12,13,13,13,20, + 20,20,20,19,12,12,12,12,13,13,14,15,19,19,19,19, + 19,12,12,12,12,13,13,14,14, +}; + +static float _vq_quantthresh__44c9_s_p7_0[] = { + -60.5, -49.5, -38.5, -27.5, -16.5, -5.5, 5.5, 16.5, + 27.5, 38.5, 49.5, 60.5, +}; + +static long _vq_quantmap__44c9_s_p7_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c9_s_p7_0 = { + _vq_quantthresh__44c9_s_p7_0, + _vq_quantmap__44c9_s_p7_0, + 13, + 13 +}; + +static static_codebook _44c9_s_p7_0 = { + 2, 169, + _vq_lengthlist__44c9_s_p7_0, + 1, -523206656, 1618345984, 4, 0, + _vq_quantlist__44c9_s_p7_0, + NULL, + &_vq_auxt__44c9_s_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c9_s_p7_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44c9_s_p7_1[] = { + 5, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 7, 7, 7, 7, 7, + 7, 8, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 8, 8, 8, 6, + 6, 7, 7, 7, 7, 7, 7, 8, 8, 8, 7, 7, 7, 7, 7, 7, + 7, 7, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, + 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 7, 7, 7, + 7, 7, 7, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 8, 8, + 8, 8, 8, 7, 7, 7, 7, 7, 7, +}; + +static float _vq_quantthresh__44c9_s_p7_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44c9_s_p7_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44c9_s_p7_1 = { + _vq_quantthresh__44c9_s_p7_1, + _vq_quantmap__44c9_s_p7_1, + 11, + 11 +}; + +static static_codebook _44c9_s_p7_1 = { + 2, 121, + _vq_lengthlist__44c9_s_p7_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44c9_s_p7_1, + NULL, + &_vq_auxt__44c9_s_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c9_s_p8_0[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__44c9_s_p8_0[] = { + 1, 4, 4, 7, 6, 8, 8, 8, 8, 9, 9,10,10,11,10, 6, + 5, 5, 7, 7, 9, 9, 8, 9,10,10,11,11,12,12, 6, 5, + 5, 7, 7, 9, 9, 9, 9,10,10,11,11,12,12,21, 7, 8, + 8, 8, 9, 9, 9, 9,10,10,11,11,12,12,21, 8, 8, 8, + 8, 9, 9, 9, 9,10,10,11,11,12,12,21,11,12, 9, 9, + 10,10,10,10,10,11,11,12,12,12,21,12,12, 9, 8,10, + 10,10,10,11,11,12,12,13,13,21,21,21, 9, 9, 9, 9, + 11,11,11,11,12,12,12,13,21,20,20, 9, 9, 9, 9,10, + 11,11,11,12,12,13,13,20,20,20,13,13,10,10,11,11, + 12,12,13,13,13,13,20,20,20,13,13,10,10,11,11,12, + 12,13,13,13,13,20,20,20,20,20,12,12,12,12,12,12, + 13,13,14,14,20,20,20,20,20,12,12,12,11,13,12,13, + 13,14,14,20,20,20,20,20,15,16,13,12,13,13,14,13, + 14,14,20,20,20,20,20,16,15,12,12,13,12,14,13,14, + 14, +}; + +static float _vq_quantthresh__44c9_s_p8_0[] = { + -136.5, -115.5, -94.5, -73.5, -52.5, -31.5, -10.5, 10.5, + 31.5, 52.5, 73.5, 94.5, 115.5, 136.5, +}; + +static long _vq_quantmap__44c9_s_p8_0[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__44c9_s_p8_0 = { + _vq_quantthresh__44c9_s_p8_0, + _vq_quantmap__44c9_s_p8_0, + 15, + 15 +}; + +static static_codebook _44c9_s_p8_0 = { + 2, 225, + _vq_lengthlist__44c9_s_p8_0, + 1, -520986624, 1620377600, 4, 0, + _vq_quantlist__44c9_s_p8_0, + NULL, + &_vq_auxt__44c9_s_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c9_s_p8_1[] = { + 10, + 9, + 11, + 8, + 12, + 7, + 13, + 6, + 14, + 5, + 15, + 4, + 16, + 3, + 17, + 2, + 18, + 1, + 19, + 0, + 20, +}; + +static long _vq_lengthlist__44c9_s_p8_1[] = { + 4, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8,10, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10, 6, 6, 7, 7, 8, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10, + 7, 7, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9,10,10,10, 8, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9,10,10,10, 8, 8, 8, 8, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10,10, + 10, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9,10,10,10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9,10,10,10,10,10, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10,10,10, + 10,10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9,10,10,10,10,10, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9,10,10,10,10,10, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9,10, 9, 9, 9,10,10,10,10, + 10,10,10, 9, 9, 9, 9, 9, 9,10, 9, 9, 9, 9, 9, 9, + 9,10,10,10,10,10,10,10, 9, 9, 9,10,10,10,10,10, + 9, 9, 9, 9, 9, 9,10,10,10,10,10,10,10, 9, 9,10, + 9,10, 9, 9, 9, 9, 9, 9, 9, 9,10,10,10,10,10,10, + 10,10,10,10, 9, 9,10,10, 9, 9, 9, 9, 9, 9, 9, 9, + 10,10,10,10,10,10,10,10,10,10,10,10,10, 9, 9, 9, + 9, 9, 9, 9, 9,10,10,10,10,10,10,10,10,10,10,10, + 10,10, 9, 9,10, 9, 9, 9, 9, 9,10,10,10,10,10,10, + 10,10,10,10,10, 9, 9,10,10, 9, 9,10, 9, 9, 9,10, + 10,10,10,10,10,10,10,10,10,10, 9, 9,10, 9, 9, 9, + 9, 9, 9, 9,10,10,10,10,10,10,10,10,10,10,10, 9, + 9, 9, 9,10, 9, 9, 9, 9, 9, +}; + +static float _vq_quantthresh__44c9_s_p8_1[] = { + -9.5, -8.5, -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, + -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, + 6.5, 7.5, 8.5, 9.5, +}; + +static long _vq_quantmap__44c9_s_p8_1[] = { + 19, 17, 15, 13, 11, 9, 7, 5, + 3, 1, 0, 2, 4, 6, 8, 10, + 12, 14, 16, 18, 20, +}; + +static encode_aux_threshmatch _vq_auxt__44c9_s_p8_1 = { + _vq_quantthresh__44c9_s_p8_1, + _vq_quantmap__44c9_s_p8_1, + 21, + 21 +}; + +static static_codebook _44c9_s_p8_1 = { + 2, 441, + _vq_lengthlist__44c9_s_p8_1, + 1, -529268736, 1611661312, 5, 0, + _vq_quantlist__44c9_s_p8_1, + NULL, + &_vq_auxt__44c9_s_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c9_s_p9_0[] = { + 9, + 8, + 10, + 7, + 11, + 6, + 12, + 5, + 13, + 4, + 14, + 3, + 15, + 2, + 16, + 1, + 17, + 0, + 18, +}; + +static long _vq_lengthlist__44c9_s_p9_0[] = { + 1, 4, 3,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12, 4, 5, 6,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12, 4, 6, 6,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,11,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11, +}; + +static float _vq_quantthresh__44c9_s_p9_0[] = { + -7913.5, -6982.5, -6051.5, -5120.5, -4189.5, -3258.5, -2327.5, -1396.5, + -465.5, 465.5, 1396.5, 2327.5, 3258.5, 4189.5, 5120.5, 6051.5, + 6982.5, 7913.5, +}; + +static long _vq_quantmap__44c9_s_p9_0[] = { + 17, 15, 13, 11, 9, 7, 5, 3, + 1, 0, 2, 4, 6, 8, 10, 12, + 14, 16, 18, +}; + +static encode_aux_threshmatch _vq_auxt__44c9_s_p9_0 = { + _vq_quantthresh__44c9_s_p9_0, + _vq_quantmap__44c9_s_p9_0, + 19, + 19 +}; + +static static_codebook _44c9_s_p9_0 = { + 2, 361, + _vq_lengthlist__44c9_s_p9_0, + 1, -508535424, 1631393792, 5, 0, + _vq_quantlist__44c9_s_p9_0, + NULL, + &_vq_auxt__44c9_s_p9_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c9_s_p9_1[] = { + 9, + 8, + 10, + 7, + 11, + 6, + 12, + 5, + 13, + 4, + 14, + 3, + 15, + 2, + 16, + 1, + 17, + 0, + 18, +}; + +static long _vq_lengthlist__44c9_s_p9_1[] = { + 1, 4, 4, 7, 7, 7, 7, 8, 7, 9, 8, 9, 9,10,10,11, + 11,11,11, 6, 5, 5, 8, 8, 9, 9, 9, 8,10, 9,11,10, + 12,12,13,12,13,13, 5, 5, 5, 8, 8, 9, 9, 9, 9,10, + 10,11,11,12,12,13,12,13,13,17, 8, 8, 9, 9, 9, 9, + 9, 9,10,10,12,11,13,12,13,13,13,13,18, 8, 8, 9, + 9, 9, 9, 9, 9,11,11,12,12,13,13,13,13,13,13,17, + 13,12, 9, 9,10,10,10,10,11,11,12,12,12,13,13,13, + 14,14,18,13,12, 9, 9,10,10,10,10,11,11,12,12,13, + 13,13,14,14,14,17,18,18,10,10,10,10,11,11,11,12, + 12,12,14,13,14,13,13,14,18,18,18,10, 9,10, 9,11, + 11,12,12,12,12,13,13,15,14,14,14,18,18,16,13,14, + 10,11,11,11,12,13,13,13,13,14,13,13,14,14,18,18, + 18,14,12,11, 9,11,10,13,12,13,13,13,14,14,14,13, + 14,18,18,17,18,18,11,12,12,12,13,13,14,13,14,14, + 13,14,14,14,18,18,18,18,17,12,10,12, 9,13,11,13, + 14,14,14,14,14,15,14,18,18,17,17,18,14,15,12,13, + 13,13,14,13,14,14,15,14,15,14,18,17,18,18,18,15, + 15,12,10,14,10,14,14,13,13,14,14,14,14,18,16,18, + 18,18,18,17,14,14,13,14,14,13,13,14,14,14,15,15, + 18,18,18,18,17,17,17,14,14,14,12,14,13,14,14,15, + 14,15,14,18,18,18,18,18,18,18,17,16,13,13,13,14, + 14,14,14,15,16,15,18,18,18,18,18,18,18,17,17,13, + 13,13,13,14,13,14,15,15,15, +}; + +static float _vq_quantthresh__44c9_s_p9_1[] = { + -416.5, -367.5, -318.5, -269.5, -220.5, -171.5, -122.5, -73.5, + -24.5, 24.5, 73.5, 122.5, 171.5, 220.5, 269.5, 318.5, + 367.5, 416.5, +}; + +static long _vq_quantmap__44c9_s_p9_1[] = { + 17, 15, 13, 11, 9, 7, 5, 3, + 1, 0, 2, 4, 6, 8, 10, 12, + 14, 16, 18, +}; + +static encode_aux_threshmatch _vq_auxt__44c9_s_p9_1 = { + _vq_quantthresh__44c9_s_p9_1, + _vq_quantmap__44c9_s_p9_1, + 19, + 19 +}; + +static static_codebook _44c9_s_p9_1 = { + 2, 361, + _vq_lengthlist__44c9_s_p9_1, + 1, -518287360, 1622704128, 5, 0, + _vq_quantlist__44c9_s_p9_1, + NULL, + &_vq_auxt__44c9_s_p9_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c9_s_p9_2[] = { + 24, + 23, + 25, + 22, + 26, + 21, + 27, + 20, + 28, + 19, + 29, + 18, + 30, + 17, + 31, + 16, + 32, + 15, + 33, + 14, + 34, + 13, + 35, + 12, + 36, + 11, + 37, + 10, + 38, + 9, + 39, + 8, + 40, + 7, + 41, + 6, + 42, + 5, + 43, + 4, + 44, + 3, + 45, + 2, + 46, + 1, + 47, + 0, + 48, +}; + +static long _vq_lengthlist__44c9_s_p9_2[] = { + 2, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, +}; + +static float _vq_quantthresh__44c9_s_p9_2[] = { + -23.5, -22.5, -21.5, -20.5, -19.5, -18.5, -17.5, -16.5, + -15.5, -14.5, -13.5, -12.5, -11.5, -10.5, -9.5, -8.5, + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, + 8.5, 9.5, 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, + 16.5, 17.5, 18.5, 19.5, 20.5, 21.5, 22.5, 23.5, +}; + +static long _vq_quantmap__44c9_s_p9_2[] = { + 47, 45, 43, 41, 39, 37, 35, 33, + 31, 29, 27, 25, 23, 21, 19, 17, + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, + 48, +}; + +static encode_aux_threshmatch _vq_auxt__44c9_s_p9_2 = { + _vq_quantthresh__44c9_s_p9_2, + _vq_quantmap__44c9_s_p9_2, + 49, + 49 +}; + +static static_codebook _44c9_s_p9_2 = { + 1, 49, + _vq_lengthlist__44c9_s_p9_2, + 1, -526909440, 1611661312, 6, 0, + _vq_quantlist__44c9_s_p9_2, + NULL, + &_vq_auxt__44c9_s_p9_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44c9_s_short[] = { + 5,13,18,16,17,17,19,18,19,19, 5, 7,10,11,12,12, + 13,16,17,18, 6, 6, 7, 7, 9, 9,10,14,17,19, 8, 7, + 6, 5, 6, 7, 9,12,19,17, 8, 7, 7, 6, 5, 6, 8,11, + 15,19, 9, 8, 7, 6, 5, 5, 6, 8,13,15,11,10, 8, 8, + 7, 5, 4, 4,10,14,12,13,11, 9, 7, 6, 4, 2, 6,12, + 18,16,16,13, 8, 7, 7, 5, 8,13,16,17,18,15,11, 9, + 9, 8,10,13, +}; + +static static_codebook _huff_book__44c9_s_short = { + 2, 100, + _huff_lengthlist__44c9_s_short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44c0_s_long[] = { + 5, 4, 8, 9, 8, 9,10,12,15, 4, 1, 5, 5, 6, 8,11, + 12,12, 8, 5, 8, 9, 9,11,13,12,12, 9, 5, 8, 5, 7, + 9,12,13,13, 8, 6, 8, 7, 7, 9,11,11,11, 9, 7, 9, + 7, 7, 7, 7,10,12,10,10,11, 9, 8, 7, 7, 9,11,11, + 12,13,12,11, 9, 8, 9,11,13,16,16,15,15,12,10,11, + 12, +}; + +static static_codebook _huff_book__44c0_s_long = { + 2, 81, + _huff_lengthlist__44c0_s_long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_s_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c0_s_p1_0[] = { + 1, 5, 5, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, 0, 0, 0, + 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5, 8, 7, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, + 0, 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, + 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 5, 7, 7, 0, 0, 0, 0, + 0, 0, 8,10, 9, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, + 0, 0, 0, 9,10,11, 0, 0, 0, 0, 0, 0, 9,11,10, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, + 0, 0, 0, 0, 9,11, 9, 0, 0, 0, 0, 0, 0, 9,10,11, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 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, 5, 7, 7, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, + 0, 0, 0, 0, 8, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 9,11,10, 0, + 0, 0, 0, 0, 0, 9, 9,11, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 9,10, 0, 0, 0, 0, 0, 0, 9,10,11, + 0, 0, 0, 0, 0, 0, 9,11,10, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c0_s_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44c0_s_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_s_p1_0 = { + _vq_quantthresh__44c0_s_p1_0, + _vq_quantmap__44c0_s_p1_0, + 3, + 3 +}; + +static static_codebook _44c0_s_p1_0 = { + 8, 6561, + _vq_lengthlist__44c0_s_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44c0_s_p1_0, + NULL, + &_vq_auxt__44c0_s_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_s_p2_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c0_s_p2_0[] = { + 1, 4, 4, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 5, 7, 6, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 5, 6, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 7, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 7, 7, 9, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c0_s_p2_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c0_s_p2_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_s_p2_0 = { + _vq_quantthresh__44c0_s_p2_0, + _vq_quantmap__44c0_s_p2_0, + 5, + 5 +}; + +static static_codebook _44c0_s_p2_0 = { + 4, 625, + _vq_lengthlist__44c0_s_p2_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c0_s_p2_0, + NULL, + &_vq_auxt__44c0_s_p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_s_p3_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44c0_s_p3_0[] = { + 1, 3, 2, 8, 7, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, + 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 7, 7, + 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, + 8, 8, 0, 0, 0, 0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c0_s_p3_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44c0_s_p3_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_s_p3_0 = { + _vq_quantthresh__44c0_s_p3_0, + _vq_quantmap__44c0_s_p3_0, + 9, + 9 +}; + +static static_codebook _44c0_s_p3_0 = { + 2, 81, + _vq_lengthlist__44c0_s_p3_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44c0_s_p3_0, + NULL, + &_vq_auxt__44c0_s_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_s_p4_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44c0_s_p4_0[] = { + 1, 3, 3, 6, 6, 6, 6, 8, 8, 0, 0, 0, 7, 7, 7, 7, + 9, 9, 0, 0, 0, 7, 7, 7, 7, 9, 9, 0, 0, 0, 7, 7, + 7, 8, 9, 9, 0, 0, 0, 7, 7, 7, 7, 9, 9, 0, 0, 0, + 9, 9, 8, 8,10,10, 0, 0, 0, 8, 9, 8, 8,10,10, 0, + 0, 0,10,10, 9, 9,10,10, 0, 0, 0, 0, 0, 9, 9,10, + 10, +}; + +static float _vq_quantthresh__44c0_s_p4_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44c0_s_p4_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_s_p4_0 = { + _vq_quantthresh__44c0_s_p4_0, + _vq_quantmap__44c0_s_p4_0, + 9, + 9 +}; + +static static_codebook _44c0_s_p4_0 = { + 2, 81, + _vq_lengthlist__44c0_s_p4_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44c0_s_p4_0, + NULL, + &_vq_auxt__44c0_s_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_s_p5_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44c0_s_p5_0[] = { + 1, 4, 3, 6, 6, 8, 7, 8, 8, 8, 8, 9, 9,10,10,11, + 11, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9, 9,10,10,10, + 11,11, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10,10,10, + 10,11,11, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10,10, + 11,11,11,11, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10, + 10,11,11,11,11, 0, 0, 0, 8, 8, 9, 9, 9, 9,10,10, + 10,10,11,11,12,12, 0, 0, 0, 8, 8, 9, 9, 9, 9,10, + 10,10,10,11,11,12,12, 0, 0, 0, 9, 9, 9, 9,10,10, + 10,10,11,11,11,12,12,12, 0, 0, 0, 0, 0, 9, 9,10, + 10,10,10,11,11,11,11,12,12, 0, 0, 0, 0, 0, 9, 9, + 10,10,10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, 9, + 9,10,10,10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, + 10,10,11,11,11,11,11,12,12,12,13,13, 0, 0, 0, 0, + 0, 0, 0,11,10,11,11,11,11,12,12,13,13, 0, 0, 0, + 0, 0, 0, 0,11,11,12,11,12,12,12,12,13,13, 0, 0, + 0, 0, 0, 0, 0,11,11,11,12,12,12,12,13,13,13, 0, + 0, 0, 0, 0, 0, 0,12,12,12,12,12,13,13,13,14,14, + 0, 0, 0, 0, 0, 0, 0, 0, 0,12,12,12,12,13,13,14, + 14, +}; + +static float _vq_quantthresh__44c0_s_p5_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44c0_s_p5_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_s_p5_0 = { + _vq_quantthresh__44c0_s_p5_0, + _vq_quantmap__44c0_s_p5_0, + 17, + 17 +}; + +static static_codebook _44c0_s_p5_0 = { + 2, 289, + _vq_lengthlist__44c0_s_p5_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44c0_s_p5_0, + NULL, + &_vq_auxt__44c0_s_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_s_p6_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c0_s_p6_0[] = { + 1, 4, 4, 7, 6, 6, 7, 6, 6, 4, 7, 7,10, 9, 9,10, + 9, 9, 4, 6, 7,10, 9, 9,11, 9, 9, 7,10,10,11,11, + 11,12,10,11, 6, 9, 9,11,10,11,11,10,10, 6, 9, 9, + 11,10,11,11,10,10, 7,11,10,12,11,11,11,11,11, 7, + 9, 9,10,10,10,11,11,10, 6, 9, 9,11,10,10,11,10, + 10, +}; + +static float _vq_quantthresh__44c0_s_p6_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__44c0_s_p6_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_s_p6_0 = { + _vq_quantthresh__44c0_s_p6_0, + _vq_quantmap__44c0_s_p6_0, + 3, + 3 +}; + +static static_codebook _44c0_s_p6_0 = { + 4, 81, + _vq_lengthlist__44c0_s_p6_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__44c0_s_p6_0, + NULL, + &_vq_auxt__44c0_s_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_s_p6_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44c0_s_p6_1[] = { + 2, 3, 3, 6, 6, 7, 7, 7, 7, 7, 8,10,10,10, 6, 6, + 7, 7, 8, 8, 8, 8,10,10,10, 6, 6, 7, 7, 8, 8, 8, + 8,10,10,10, 7, 7, 7, 7, 8, 8, 8, 8,10,10,10, 7, + 7, 7, 7, 8, 8, 8, 8,10,10,10, 8, 7, 8, 8, 8, 8, + 8, 8,10,10,10, 7, 7, 8, 8, 8, 8, 8, 8,10,10,10, + 8, 8, 8, 8, 8, 8, 8, 8,10,10,10,10,10, 8, 8, 8, + 8, 8, 8,10,10,10,10,10, 9, 9, 8, 8, 8, 8,10,10, + 10,10,10, 8, 8, 8, 8, 8, 8, +}; + +static float _vq_quantthresh__44c0_s_p6_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44c0_s_p6_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_s_p6_1 = { + _vq_quantthresh__44c0_s_p6_1, + _vq_quantmap__44c0_s_p6_1, + 11, + 11 +}; + +static static_codebook _44c0_s_p6_1 = { + 2, 121, + _vq_lengthlist__44c0_s_p6_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44c0_s_p6_1, + NULL, + &_vq_auxt__44c0_s_p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_s_p7_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c0_s_p7_0[] = { + 1, 4, 4, 6, 6, 7, 7, 7, 7, 8, 8, 9, 9, 7, 5, 5, + 7, 7, 8, 8, 8, 8, 9, 9,10,10, 7, 5, 6, 7, 7, 8, + 8, 8, 8, 9, 9,10,10, 0, 8, 8, 8, 8, 9, 9, 9, 9, + 10,10,11,11, 0, 8, 8, 8, 8, 9, 9, 9, 9,10,10,11, + 11, 0,12,12, 9, 9,10,10,10,10,11,11,11,11, 0,13, + 13, 9, 9, 9, 9,10,10,11,11,11,12, 0, 0, 0,10,10, + 10,10,11,11,11,11,12,12, 0, 0, 0,10,10, 9, 9,11, + 11,11,12,12,12, 0, 0, 0,13,13,10,10,11,11,12,12, + 13,13, 0, 0, 0,14,14,10,10,11,11,12,12,13,13, 0, + 0, 0, 0, 0,11,11,11,11,13,12,13,13, 0, 0, 0, 0, + 0,12,12,11,11,12,12,13,13, +}; + +static float _vq_quantthresh__44c0_s_p7_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44c0_s_p7_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_s_p7_0 = { + _vq_quantthresh__44c0_s_p7_0, + _vq_quantmap__44c0_s_p7_0, + 13, + 13 +}; + +static static_codebook _44c0_s_p7_0 = { + 2, 169, + _vq_lengthlist__44c0_s_p7_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44c0_s_p7_0, + NULL, + &_vq_auxt__44c0_s_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_s_p7_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c0_s_p7_1[] = { + 2, 3, 3, 5, 5, 6, 6, 6, 5, 5, 6, 6, 6, 5, 5, 6, + 6, 6, 5, 5, 6, 6, 6, 5, 5, +}; + +static float _vq_quantthresh__44c0_s_p7_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c0_s_p7_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_s_p7_1 = { + _vq_quantthresh__44c0_s_p7_1, + _vq_quantmap__44c0_s_p7_1, + 5, + 5 +}; + +static static_codebook _44c0_s_p7_1 = { + 2, 25, + _vq_lengthlist__44c0_s_p7_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c0_s_p7_1, + NULL, + &_vq_auxt__44c0_s_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_s_p8_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c0_s_p8_0[] = { + 1, 5, 5,10,10, 6, 9, 8,10,10, 6,10, 9,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10, 8,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,10,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11, +}; + +static float _vq_quantthresh__44c0_s_p8_0[] = { + -331.5, -110.5, 110.5, 331.5, +}; + +static long _vq_quantmap__44c0_s_p8_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_s_p8_0 = { + _vq_quantthresh__44c0_s_p8_0, + _vq_quantmap__44c0_s_p8_0, + 5, + 5 +}; + +static static_codebook _44c0_s_p8_0 = { + 4, 625, + _vq_lengthlist__44c0_s_p8_0, + 1, -518283264, 1627103232, 3, 0, + _vq_quantlist__44c0_s_p8_0, + NULL, + &_vq_auxt__44c0_s_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_s_p8_1[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c0_s_p8_1[] = { + 1, 4, 4, 6, 6, 7, 7, 9, 9,11,12,13,12, 6, 5, 5, + 7, 7, 8, 8,10, 9,12,12,12,12, 6, 5, 5, 7, 7, 8, + 8,10, 9,12,11,11,13,16, 7, 7, 8, 8, 9, 9,10,10, + 12,12,13,12,16, 7, 7, 8, 7, 9, 9,10,10,11,12,12, + 13,16,10,10, 8, 8,10,10,11,12,12,12,13,13,16,11, + 10, 8, 7,11,10,11,11,12,11,13,13,16,16,16,10,10, + 10,10,11,11,13,12,13,13,16,16,16,11, 9,11, 9,15, + 13,12,13,13,13,16,16,16,15,13,11,11,12,13,12,12, + 14,13,16,16,16,14,13,11,11,13,12,14,13,13,13,16, + 16,16,16,16,13,13,13,12,14,13,14,14,16,16,16,16, + 16,13,13,12,12,14,14,15,13, +}; + +static float _vq_quantthresh__44c0_s_p8_1[] = { + -93.5, -76.5, -59.5, -42.5, -25.5, -8.5, 8.5, 25.5, + 42.5, 59.5, 76.5, 93.5, +}; + +static long _vq_quantmap__44c0_s_p8_1[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_s_p8_1 = { + _vq_quantthresh__44c0_s_p8_1, + _vq_quantmap__44c0_s_p8_1, + 13, + 13 +}; + +static static_codebook _44c0_s_p8_1 = { + 2, 169, + _vq_lengthlist__44c0_s_p8_1, + 1, -522616832, 1620115456, 4, 0, + _vq_quantlist__44c0_s_p8_1, + NULL, + &_vq_auxt__44c0_s_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_s_p8_2[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44c0_s_p8_2[] = { + 2, 4, 4, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, + 8,10,10,10, 7, 7, 7, 8, 8, 8, 9, 9, 9, 9, 9, 9, + 9, 9,10,10,10, 7, 7, 7, 7, 8, 8, 9, 9, 9, 9, 9, + 9, 9, 9,10,10,10, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, + 9,10, 9, 9,10,10,10, 7, 7, 8, 8, 9, 8, 9, 9, 9, + 9,10, 9, 9,10,10,10,10, 8, 8, 8, 8, 9, 8, 9, 9, + 9, 9, 9,10, 9,10,10,10,10, 7, 7, 8, 8, 9, 9, 9, + 9, 9, 9,10, 9,10,10,10,10,10, 8, 8, 8, 9, 9, 9, + 9, 9, 9, 9,10,10,10, 9,11,10,10,10,10, 8, 8, 9, + 9, 9, 9, 9,10, 9, 9, 9,10,10,10,10,11,11, 9, 9, + 9, 9, 9, 9, 9, 9,10, 9, 9,10,11,10,10,11,11, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9,10, 9,11,11,10,11,11, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10, 9,11,10,10,11, + 11,11,11, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10,10,10, + 11,11,11,11, 9,10, 9,10, 9, 9, 9, 9,10, 9,10,11, + 10,11,10,10,10,10,10, 9, 9, 9,10, 9, 9, 9,10,11, + 11,10,11,11,10,11,10,10,10, 9, 9, 9, 9,10, 9, 9, + 10,11,10,11,11,11,11,10,11,10,10, 9,10, 9, 9, 9, + 10, +}; + +static float _vq_quantthresh__44c0_s_p8_2[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44c0_s_p8_2[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_s_p8_2 = { + _vq_quantthresh__44c0_s_p8_2, + _vq_quantmap__44c0_s_p8_2, + 17, + 17 +}; + +static static_codebook _44c0_s_p8_2 = { + 2, 289, + _vq_lengthlist__44c0_s_p8_2, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44c0_s_p8_2, + NULL, + &_vq_auxt__44c0_s_p8_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44c0_s_short[] = { + 9, 8,12,11,12,13,14,14,16, 6, 1, 5, 6, 6, 9,12, + 14,17, 9, 4, 5, 9, 7, 9,13,15,16, 8, 5, 8, 6, 8, + 10,13,17,17, 9, 6, 7, 7, 8, 9,13,15,17,11, 8, 9, + 9, 9,10,12,16,16,13, 7, 8, 7, 7, 9,12,14,15,13, + 6, 7, 5, 5, 7,10,13,13,14, 7, 8, 5, 6, 7, 9,10, + 12, +}; + +static static_codebook _huff_book__44c0_s_short = { + 2, 81, + _huff_lengthlist__44c0_s_short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44c0_sm_long[] = { + 5, 4, 9,10, 9,10,11,12,13, 4, 1, 5, 7, 7, 9,11, + 12,14, 8, 5, 7, 9, 8,10,13,13,13,10, 7, 9, 4, 6, + 7,10,12,14, 9, 6, 7, 6, 6, 7,10,12,12, 9, 8, 9, + 7, 6, 7, 8,11,12,11,11,11, 9, 8, 7, 8,10,12,12, + 13,14,12,11, 9, 9, 9,12,12,17,17,15,16,12,10,11, + 13, +}; + +static static_codebook _huff_book__44c0_sm_long = { + 2, 81, + _huff_lengthlist__44c0_sm_long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_sm_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c0_sm_p1_0[] = { + 1, 5, 5, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, 0, 0, 0, + 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5, 8, 7, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, + 0, 0, 0, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 7, 9, 8, 0, 0, + 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 5, 8, 7, 0, 0, 0, 0, + 0, 0, 8, 9, 9, 0, 0, 0, 0, 0, 0, 8, 9, 9, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, + 0, 0, 0, 9,10,10, 0, 0, 0, 0, 0, 0, 9,10,10, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, + 0, 0, 0, 0, 8,10, 9, 0, 0, 0, 0, 0, 0, 9,10,10, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 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, 5, 7, 8, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, + 0, 0, 0, 0, 8, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 9,10,10, 0, + 0, 0, 0, 0, 0, 9, 9,10, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 9,10,10, + 0, 0, 0, 0, 0, 0, 9,10,10, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c0_sm_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44c0_sm_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_sm_p1_0 = { + _vq_quantthresh__44c0_sm_p1_0, + _vq_quantmap__44c0_sm_p1_0, + 3, + 3 +}; + +static static_codebook _44c0_sm_p1_0 = { + 8, 6561, + _vq_lengthlist__44c0_sm_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44c0_sm_p1_0, + NULL, + &_vq_auxt__44c0_sm_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_sm_p2_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c0_sm_p2_0[] = { + 1, 4, 4, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 5, 7, 7, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 5, 5, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 7, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 7, 7, 9, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c0_sm_p2_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c0_sm_p2_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_sm_p2_0 = { + _vq_quantthresh__44c0_sm_p2_0, + _vq_quantmap__44c0_sm_p2_0, + 5, + 5 +}; + +static static_codebook _44c0_sm_p2_0 = { + 4, 625, + _vq_lengthlist__44c0_sm_p2_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c0_sm_p2_0, + NULL, + &_vq_auxt__44c0_sm_p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_sm_p3_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44c0_sm_p3_0[] = { + 1, 3, 3, 7, 7, 0, 0, 0, 0, 0, 5, 4, 7, 7, 0, 0, + 0, 0, 0, 5, 5, 7, 7, 0, 0, 0, 0, 0, 6, 7, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0, 0, 0, + 9,10, 0, 0, 0, 0, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, + 0, 0,11,11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c0_sm_p3_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44c0_sm_p3_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_sm_p3_0 = { + _vq_quantthresh__44c0_sm_p3_0, + _vq_quantmap__44c0_sm_p3_0, + 9, + 9 +}; + +static static_codebook _44c0_sm_p3_0 = { + 2, 81, + _vq_lengthlist__44c0_sm_p3_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44c0_sm_p3_0, + NULL, + &_vq_auxt__44c0_sm_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_sm_p4_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44c0_sm_p4_0[] = { + 1, 4, 3, 6, 6, 7, 7, 9, 9, 0, 5, 5, 7, 7, 8, 7, + 9, 9, 0, 5, 5, 7, 7, 8, 8, 9, 9, 0, 7, 7, 8, 8, + 8, 8,10,10, 0, 0, 0, 8, 8, 8, 8,10,10, 0, 0, 0, + 9, 9, 9, 9,11,11, 0, 0, 0, 9, 9, 9, 9,11,11, 0, + 0, 0,10,10,10,10,11,11, 0, 0, 0, 0, 0, 9, 9,11, + 11, +}; + +static float _vq_quantthresh__44c0_sm_p4_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44c0_sm_p4_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_sm_p4_0 = { + _vq_quantthresh__44c0_sm_p4_0, + _vq_quantmap__44c0_sm_p4_0, + 9, + 9 +}; + +static static_codebook _44c0_sm_p4_0 = { + 2, 81, + _vq_lengthlist__44c0_sm_p4_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44c0_sm_p4_0, + NULL, + &_vq_auxt__44c0_sm_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_sm_p5_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44c0_sm_p5_0[] = { + 1, 4, 4, 6, 6, 8, 8, 8, 8, 8, 8, 9, 9,10,10,11, + 11, 0, 6, 6, 7, 7, 8, 8, 9, 9, 9, 9,10,10,10,11, + 11,11, 0, 5, 6, 7, 7, 8, 8, 9, 9, 9, 9,10,10,10, + 11,11,11, 0, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9,10,10, + 11,11,12,12, 0, 0, 0, 8, 8, 8, 8, 9, 9, 9, 9,10, + 10,11,11,12,12, 0, 0, 0, 8, 8, 9, 9,10,10,10,10, + 11,11,11,11,12,12, 0, 0, 0, 8, 8, 9, 9,10,10,10, + 10,11,11,11,11,12,12, 0, 0, 0, 9, 9, 9, 9,10,10, + 10,10,11,11,12,12,12,13, 0, 0, 0, 0, 0, 9, 9,10, + 10,10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, 9, 9, + 10,10,11,11,11,11,12,12,13,13, 0, 0, 0, 0, 0, 9, + 9,10,10,11,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, + 10,10,10,10,11,11,12,12,12,13,13,13, 0, 0, 0, 0, + 0, 0, 0,10,10,11,11,12,12,12,13,13,13, 0, 0, 0, + 0, 0, 0, 0,11,11,12,12,12,12,13,13,14,14, 0, 0, + 0, 0, 0, 0, 0,11,11,12,11,12,12,13,13,13,13, 0, + 0, 0, 0, 0, 0, 0,12,12,12,12,13,13,13,13,14,14, + 0, 0, 0, 0, 0, 0, 0, 0, 0,12,12,12,12,13,13,14, + 14, +}; + +static float _vq_quantthresh__44c0_sm_p5_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44c0_sm_p5_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_sm_p5_0 = { + _vq_quantthresh__44c0_sm_p5_0, + _vq_quantmap__44c0_sm_p5_0, + 17, + 17 +}; + +static static_codebook _44c0_sm_p5_0 = { + 2, 289, + _vq_lengthlist__44c0_sm_p5_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44c0_sm_p5_0, + NULL, + &_vq_auxt__44c0_sm_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_sm_p6_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c0_sm_p6_0[] = { + 1, 4, 4, 7, 6, 6, 7, 6, 6, 4, 7, 7,10, 9, 9,11, + 9, 9, 4, 7, 7,10, 9, 9,11, 9, 9, 7,10,10,10,11, + 11,11,10,10, 6, 9, 9,11,11,10,11,10,10, 6, 9, 9, + 11,10,11,11,10,10, 7,11,10,11,11,11,11,11,11, 6, + 9, 9,11,10,10,11,11,10, 6, 9, 9,11,10,10,11,10, + 11, +}; + +static float _vq_quantthresh__44c0_sm_p6_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__44c0_sm_p6_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_sm_p6_0 = { + _vq_quantthresh__44c0_sm_p6_0, + _vq_quantmap__44c0_sm_p6_0, + 3, + 3 +}; + +static static_codebook _44c0_sm_p6_0 = { + 4, 81, + _vq_lengthlist__44c0_sm_p6_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__44c0_sm_p6_0, + NULL, + &_vq_auxt__44c0_sm_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_sm_p6_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44c0_sm_p6_1[] = { + 2, 4, 4, 6, 6, 7, 7, 7, 7, 7, 8, 9, 5, 5, 6, 6, + 7, 7, 8, 8, 8, 8, 9, 5, 5, 6, 6, 7, 7, 8, 8, 8, + 8,10, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8,10,10,10, 7, + 7, 7, 7, 8, 8, 8, 8,10,10,10, 8, 8, 8, 8, 8, 8, + 8, 8,10,10,10, 8, 8, 8, 8, 8, 8, 8, 8,10,10,10, + 8, 8, 8, 8, 8, 8, 8, 8,10,10,10,10,10, 8, 8, 8, + 8, 8, 8,10,10,10,10,10, 9, 9, 8, 8, 8, 8,10,10, + 10,10,10, 8, 8, 8, 8, 8, 8, +}; + +static float _vq_quantthresh__44c0_sm_p6_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44c0_sm_p6_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_sm_p6_1 = { + _vq_quantthresh__44c0_sm_p6_1, + _vq_quantmap__44c0_sm_p6_1, + 11, + 11 +}; + +static static_codebook _44c0_sm_p6_1 = { + 2, 121, + _vq_lengthlist__44c0_sm_p6_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44c0_sm_p6_1, + NULL, + &_vq_auxt__44c0_sm_p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_sm_p7_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c0_sm_p7_0[] = { + 1, 4, 4, 6, 6, 7, 7, 7, 7, 8, 8, 9, 9, 7, 5, 5, + 7, 7, 8, 8, 8, 8, 9, 9,10,10, 7, 6, 5, 7, 7, 8, + 8, 8, 8, 9, 9,10,10, 0, 8, 8, 8, 8, 9, 9, 9, 9, + 10,10,11,11, 0, 8, 8, 8, 8, 9, 9, 9, 9,10,10,11, + 11, 0,12,12, 9, 9,10,10,10,10,11,11,11,11, 0,13, + 13, 9, 9, 9, 9,10,10,11,11,11,12, 0, 0, 0, 9,10, + 10,10,11,11,12,11,12,12, 0, 0, 0,10,10, 9, 9,11, + 11,12,12,12,12, 0, 0, 0,13,13,10,10,11,11,12,12, + 13,13, 0, 0, 0,14,14,10,10,11,11,12,12,13,13, 0, + 0, 0, 0, 0,11,12,11,11,13,12,13,13, 0, 0, 0, 0, + 0,12,12,11,11,13,12,14,14, +}; + +static float _vq_quantthresh__44c0_sm_p7_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44c0_sm_p7_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_sm_p7_0 = { + _vq_quantthresh__44c0_sm_p7_0, + _vq_quantmap__44c0_sm_p7_0, + 13, + 13 +}; + +static static_codebook _44c0_sm_p7_0 = { + 2, 169, + _vq_lengthlist__44c0_sm_p7_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44c0_sm_p7_0, + NULL, + &_vq_auxt__44c0_sm_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_sm_p7_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c0_sm_p7_1[] = { + 2, 4, 4, 4, 4, 6, 5, 5, 5, 5, 6, 5, 5, 5, 5, 6, + 6, 6, 5, 5, 6, 6, 6, 5, 5, +}; + +static float _vq_quantthresh__44c0_sm_p7_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c0_sm_p7_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_sm_p7_1 = { + _vq_quantthresh__44c0_sm_p7_1, + _vq_quantmap__44c0_sm_p7_1, + 5, + 5 +}; + +static static_codebook _44c0_sm_p7_1 = { + 2, 25, + _vq_lengthlist__44c0_sm_p7_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c0_sm_p7_1, + NULL, + &_vq_auxt__44c0_sm_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_sm_p8_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44c0_sm_p8_0[] = { + 1, 3, 3,11,11,11,11,11,11, 3, 7, 6,11,11,11,11, + 11,11, 4, 8, 7,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12, +}; + +static float _vq_quantthresh__44c0_sm_p8_0[] = { + -773.5, -552.5, -331.5, -110.5, 110.5, 331.5, 552.5, 773.5, +}; + +static long _vq_quantmap__44c0_sm_p8_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_sm_p8_0 = { + _vq_quantthresh__44c0_sm_p8_0, + _vq_quantmap__44c0_sm_p8_0, + 9, + 9 +}; + +static static_codebook _44c0_sm_p8_0 = { + 2, 81, + _vq_lengthlist__44c0_sm_p8_0, + 1, -516186112, 1627103232, 4, 0, + _vq_quantlist__44c0_sm_p8_0, + NULL, + &_vq_auxt__44c0_sm_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_sm_p8_1[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c0_sm_p8_1[] = { + 1, 4, 4, 6, 6, 7, 7, 9, 9,10,11,12,12, 6, 5, 5, + 7, 7, 8, 8,10,10,12,11,12,12, 6, 5, 5, 7, 7, 8, + 8,10,10,12,11,12,12,17, 7, 7, 8, 8, 9, 9,10,10, + 12,12,13,13,18, 7, 7, 8, 7, 9, 9,10,10,12,12,12, + 13,19,10,10, 8, 8,10,10,11,11,12,12,13,14,19,11, + 10, 8, 7,10,10,11,11,12,12,13,12,19,19,19,10,10, + 10,10,11,11,12,12,13,13,19,19,19,11, 9,11, 9,14, + 12,13,12,13,13,19,20,18,13,14,11,11,12,12,13,13, + 14,13,20,20,20,15,13,11,10,13,11,13,13,14,13,20, + 20,20,20,20,13,14,12,12,13,13,13,13,20,20,20,20, + 20,13,13,12,12,16,13,15,13, +}; + +static float _vq_quantthresh__44c0_sm_p8_1[] = { + -93.5, -76.5, -59.5, -42.5, -25.5, -8.5, 8.5, 25.5, + 42.5, 59.5, 76.5, 93.5, +}; + +static long _vq_quantmap__44c0_sm_p8_1[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_sm_p8_1 = { + _vq_quantthresh__44c0_sm_p8_1, + _vq_quantmap__44c0_sm_p8_1, + 13, + 13 +}; + +static static_codebook _44c0_sm_p8_1 = { + 2, 169, + _vq_lengthlist__44c0_sm_p8_1, + 1, -522616832, 1620115456, 4, 0, + _vq_quantlist__44c0_sm_p8_1, + NULL, + &_vq_auxt__44c0_sm_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_sm_p8_2[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44c0_sm_p8_2[] = { + 2, 5, 5, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, + 8,10, 6, 6, 7, 7, 7, 7, 8, 8, 9, 9, 9, 9, 9, 9, + 9, 9,10, 6, 6, 7, 7, 8, 7, 8, 8, 9, 9, 9, 9, 9, + 9, 9, 9,10, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 9,10,10,10, 7, 7, 8, 8, 9, 8, 9, 9, 9, + 9,10, 9, 9,10,10,10,11, 8, 8, 8, 8, 9, 9, 9, 9, + 9, 9, 9,10, 9,10,10,10,10, 8, 8, 8, 8, 9, 9, 9, + 9, 9, 9, 9, 9,10,10,11,10,10, 8, 8, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9,10,10,10,10,10,11,11, 8, 8, 9, + 9, 9, 9, 9, 9, 9, 9, 9,10,11,11,11,11,11, 9, 9, + 9, 9, 9, 9, 9, 9,10, 9,10, 9,11,11,10,11,11, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9,10, 9,11,11,10,11,11, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10, 9,11,10,11,11, + 11,11,11, 9, 9,10, 9, 9, 9, 9, 9, 9, 9,10,11,10, + 11,11,11,11,10,10,10,10, 9, 9, 9, 9, 9, 9,10,11, + 11,11,11,11,11, 9,10, 9, 9, 9, 9, 9, 9, 9, 9,11, + 11,10,11,11,11,10,10,10, 9, 9, 9, 9, 9, 9, 9, 9, + 10,11,10,11,11,11,11,11,11, 9, 9, 9, 9, 9, 9, 9, + 9, +}; + +static float _vq_quantthresh__44c0_sm_p8_2[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44c0_sm_p8_2[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_sm_p8_2 = { + _vq_quantthresh__44c0_sm_p8_2, + _vq_quantmap__44c0_sm_p8_2, + 17, + 17 +}; + +static static_codebook _44c0_sm_p8_2 = { + 2, 289, + _vq_lengthlist__44c0_sm_p8_2, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44c0_sm_p8_2, + NULL, + &_vq_auxt__44c0_sm_p8_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44c0_sm_short[] = { + 6, 6,12,13,13,14,16,17,17, 4, 2, 5, 8, 7, 9,12, + 15,15, 9, 4, 5, 9, 7, 9,12,16,18,11, 6, 7, 4, 6, + 8,11,14,18,10, 5, 6, 5, 5, 7,10,14,17,10, 5, 7, + 7, 6, 7,10,13,16,11, 5, 7, 7, 7, 8,10,12,15,13, + 6, 7, 5, 5, 7, 9,12,13,16, 8, 9, 6, 6, 7, 9,10, + 12, +}; + +static static_codebook _huff_book__44c0_sm_short = { + 2, 81, + _huff_lengthlist__44c0_sm_short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44c1_s_long[] = { + 5, 5, 9,10, 9, 9,10,11,12, 5, 1, 5, 6, 6, 7,10, + 12,14, 9, 5, 6, 8, 8,10,12,14,14,10, 5, 8, 5, 6, + 8,11,13,14, 9, 5, 7, 6, 6, 8,10,12,11, 9, 7, 9, + 7, 6, 6, 7,10,10,10, 9,12, 9, 8, 7, 7,10,12,11, + 11,13,12,10, 9, 8, 9,11,11,14,15,15,13,11, 9, 9, + 11, +}; + +static static_codebook _huff_book__44c1_s_long = { + 2, 81, + _huff_lengthlist__44c1_s_long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_s_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c1_s_p1_0[] = { + 2, 4, 4, 0, 0, 0, 0, 0, 0, 5, 7, 6, 0, 0, 0, 0, + 0, 0, 5, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 7, 8, 8, 0, 0, 0, + 0, 0, 0, 7, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 7, 8, 8, 0, 0, + 0, 0, 0, 0, 7, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 7, 7, 0, 0, 0, 0, + 0, 0, 7, 8, 8, 0, 0, 0, 0, 0, 0, 7, 8, 8, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 8, 8, 0, 0, 0, + 0, 0, 0, 8, 9,10, 0, 0, 0, 0, 0, 0, 8, 9, 9, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 8, 8, 0, 0, + 0, 0, 0, 0, 8, 9, 8, 0, 0, 0, 0, 0, 0, 8, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 7, 7, 0, 0, 0, 0, 0, 0, 7, 8, 8, 0, 0, + 0, 0, 0, 0, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 6, 8, 8, 0, 0, 0, 0, 0, 0, 8,10, 9, 0, + 0, 0, 0, 0, 0, 8, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 8, 8, 0, 0, 0, 0, 0, 0, 8, 9, 9, + 0, 0, 0, 0, 0, 0, 8, 9, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c1_s_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44c1_s_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_s_p1_0 = { + _vq_quantthresh__44c1_s_p1_0, + _vq_quantmap__44c1_s_p1_0, + 3, + 3 +}; + +static static_codebook _44c1_s_p1_0 = { + 8, 6561, + _vq_lengthlist__44c1_s_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44c1_s_p1_0, + NULL, + &_vq_auxt__44c1_s_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_s_p2_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c1_s_p2_0[] = { + 2, 3, 4, 6, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 6, 6, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 4, 5, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 6, 6, 8, 8, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c1_s_p2_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c1_s_p2_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_s_p2_0 = { + _vq_quantthresh__44c1_s_p2_0, + _vq_quantmap__44c1_s_p2_0, + 5, + 5 +}; + +static static_codebook _44c1_s_p2_0 = { + 4, 625, + _vq_lengthlist__44c1_s_p2_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c1_s_p2_0, + NULL, + &_vq_auxt__44c1_s_p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_s_p3_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44c1_s_p3_0[] = { + 1, 3, 2, 7, 7, 0, 0, 0, 0, 0,13,13, 6, 6, 0, 0, + 0, 0, 0,12, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 7, 7, + 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, + 8, 9, 0, 0, 0, 0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0, + 0, 0,11,10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c1_s_p3_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44c1_s_p3_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_s_p3_0 = { + _vq_quantthresh__44c1_s_p3_0, + _vq_quantmap__44c1_s_p3_0, + 9, + 9 +}; + +static static_codebook _44c1_s_p3_0 = { + 2, 81, + _vq_lengthlist__44c1_s_p3_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44c1_s_p3_0, + NULL, + &_vq_auxt__44c1_s_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_s_p4_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44c1_s_p4_0[] = { + 1, 3, 3, 6, 5, 6, 6, 8, 8, 0, 0, 0, 7, 7, 7, 7, + 9, 9, 0, 0, 0, 7, 7, 7, 7, 9, 9, 0, 0, 0, 7, 7, + 8, 8,10,10, 0, 0, 0, 7, 7, 8, 8,10,10, 0, 0, 0, + 9, 9, 8, 8,10,10, 0, 0, 0, 8, 8, 8, 8,10,10, 0, + 0, 0,10,10, 9, 9,11,11, 0, 0, 0, 0, 0, 9, 9,11, + 11, +}; + +static float _vq_quantthresh__44c1_s_p4_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44c1_s_p4_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_s_p4_0 = { + _vq_quantthresh__44c1_s_p4_0, + _vq_quantmap__44c1_s_p4_0, + 9, + 9 +}; + +static static_codebook _44c1_s_p4_0 = { + 2, 81, + _vq_lengthlist__44c1_s_p4_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44c1_s_p4_0, + NULL, + &_vq_auxt__44c1_s_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_s_p5_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44c1_s_p5_0[] = { + 1, 4, 3, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9,10,10,11, + 11, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10,10,10,10, + 11,11, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10,10,10, + 10,11,11, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10,10, + 11,11,11,11, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10, + 10,11,11,12,11, 0, 0, 0, 8, 8, 9, 9, 9,10,10,10, + 10,10,11,11,12,12, 0, 0, 0, 8, 8, 9, 9,10, 9,10, + 10,10,10,11,11,12,12, 0, 0, 0, 9, 9, 9, 9,10,10, + 10,10,11,11,12,12,12,12, 0, 0, 0, 0, 0, 9, 9,10, + 10,10,10,11,11,12,12,12,12, 0, 0, 0, 0, 0, 9, 9, + 10,10,10,11,11,11,12,12,13,13, 0, 0, 0, 0, 0, 9, + 9,10,10,10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, + 10,10,10,10,11,11,12,12,12,12,13,13, 0, 0, 0, 0, + 0, 0, 0,10,10,11,11,12,12,12,12,13,13, 0, 0, 0, + 0, 0, 0, 0,11,11,12,12,12,12,13,13,13,13, 0, 0, + 0, 0, 0, 0, 0,11,11,11,11,12,12,13,13,13,13, 0, + 0, 0, 0, 0, 0, 0,12,12,12,12,12,12,13,13,14,14, + 0, 0, 0, 0, 0, 0, 0, 0, 0,12,12,12,12,13,13,14, + 14, +}; + +static float _vq_quantthresh__44c1_s_p5_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44c1_s_p5_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_s_p5_0 = { + _vq_quantthresh__44c1_s_p5_0, + _vq_quantmap__44c1_s_p5_0, + 17, + 17 +}; + +static static_codebook _44c1_s_p5_0 = { + 2, 289, + _vq_lengthlist__44c1_s_p5_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44c1_s_p5_0, + NULL, + &_vq_auxt__44c1_s_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_s_p6_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c1_s_p6_0[] = { + 1, 4, 4, 7, 6, 6, 7, 6, 6, 4, 7, 7,10, 9, 9,11, + 9, 9, 4, 7, 7,10, 9, 9,11, 9, 9, 6,10,10,11,11, + 11,11,10,10, 6, 9, 9,11,10,10,11,10,10, 6, 9, 9, + 11,10,11,11,10,10, 7,11,10,11,11,11,12,11,11, 7, + 9, 9,11,10,10,11,11,10, 6, 9, 9,10,10,10,12,10, + 11, +}; + +static float _vq_quantthresh__44c1_s_p6_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__44c1_s_p6_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_s_p6_0 = { + _vq_quantthresh__44c1_s_p6_0, + _vq_quantmap__44c1_s_p6_0, + 3, + 3 +}; + +static static_codebook _44c1_s_p6_0 = { + 4, 81, + _vq_lengthlist__44c1_s_p6_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__44c1_s_p6_0, + NULL, + &_vq_auxt__44c1_s_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_s_p6_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44c1_s_p6_1[] = { + 2, 3, 3, 6, 6, 7, 7, 7, 7, 8, 8,10,10,10, 6, 6, + 7, 7, 8, 8, 8, 8,10,10,10, 6, 6, 7, 7, 8, 8, 8, + 8,10,10,10, 7, 7, 7, 7, 8, 8, 8, 8,10,10,10, 7, + 7, 7, 7, 8, 8, 8, 8,10,10,10, 7, 7, 8, 8, 8, 8, + 8, 8,10,10,10, 7, 7, 8, 8, 8, 8, 8, 8,10,10,10, + 8, 8, 8, 8, 8, 8, 8, 8,10,10,10,10,10, 8, 8, 8, + 8, 8, 8,10,10,10,10,10, 9, 9, 8, 8, 8, 8,10,10, + 10,10,10, 8, 8, 8, 8, 8, 8, +}; + +static float _vq_quantthresh__44c1_s_p6_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44c1_s_p6_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_s_p6_1 = { + _vq_quantthresh__44c1_s_p6_1, + _vq_quantmap__44c1_s_p6_1, + 11, + 11 +}; + +static static_codebook _44c1_s_p6_1 = { + 2, 121, + _vq_lengthlist__44c1_s_p6_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44c1_s_p6_1, + NULL, + &_vq_auxt__44c1_s_p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_s_p7_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c1_s_p7_0[] = { + 1, 4, 4, 6, 6, 7, 7, 7, 7, 8, 8,10, 9, 7, 5, 6, + 7, 7, 8, 8, 8, 8, 9, 9,10,10, 7, 5, 5, 7, 7, 8, + 8, 8, 8, 9, 9,10,10, 0, 8, 8, 8, 8, 9, 9, 9, 9, + 10,10,11,10, 0, 8, 8, 8, 8, 9, 9, 9, 9,10,10,11, + 11, 0,12,12, 9, 9, 9,10,10,10,11,11,11,11, 0,13, + 13, 9, 9, 9, 9,10,10,11,11,11,11, 0, 0, 0,10,10, + 10,10,11,11,12,11,12,12, 0, 0, 0,10,10,10, 9,11, + 11,12,11,13,12, 0, 0, 0,13,13,10,10,11,11,12,12, + 13,13, 0, 0, 0,14,14,10,10,11,11,12,12,13,13, 0, + 0, 0, 0, 0,11,12,11,11,12,12,14,13, 0, 0, 0, 0, + 0,12,11,11,11,13,10,14,13, +}; + +static float _vq_quantthresh__44c1_s_p7_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44c1_s_p7_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_s_p7_0 = { + _vq_quantthresh__44c1_s_p7_0, + _vq_quantmap__44c1_s_p7_0, + 13, + 13 +}; + +static static_codebook _44c1_s_p7_0 = { + 2, 169, + _vq_lengthlist__44c1_s_p7_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44c1_s_p7_0, + NULL, + &_vq_auxt__44c1_s_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_s_p7_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c1_s_p7_1[] = { + 2, 3, 3, 5, 5, 6, 6, 6, 5, 5, 6, 6, 6, 5, 5, 6, + 6, 6, 5, 5, 6, 6, 6, 5, 5, +}; + +static float _vq_quantthresh__44c1_s_p7_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c1_s_p7_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_s_p7_1 = { + _vq_quantthresh__44c1_s_p7_1, + _vq_quantmap__44c1_s_p7_1, + 5, + 5 +}; + +static static_codebook _44c1_s_p7_1 = { + 2, 25, + _vq_lengthlist__44c1_s_p7_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c1_s_p7_1, + NULL, + &_vq_auxt__44c1_s_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_s_p8_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c1_s_p8_0[] = { + 1, 4, 3,10,10,10,10,10,10,10,10,10,10, 4, 8, 6, + 10,10,10,10,10,10,10,10,10,10, 4, 8, 7,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10, +}; + +static float _vq_quantthresh__44c1_s_p8_0[] = { + -1215.5, -994.5, -773.5, -552.5, -331.5, -110.5, 110.5, 331.5, + 552.5, 773.5, 994.5, 1215.5, +}; + +static long _vq_quantmap__44c1_s_p8_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_s_p8_0 = { + _vq_quantthresh__44c1_s_p8_0, + _vq_quantmap__44c1_s_p8_0, + 13, + 13 +}; + +static static_codebook _44c1_s_p8_0 = { + 2, 169, + _vq_lengthlist__44c1_s_p8_0, + 1, -514541568, 1627103232, 4, 0, + _vq_quantlist__44c1_s_p8_0, + NULL, + &_vq_auxt__44c1_s_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_s_p8_1[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c1_s_p8_1[] = { + 1, 4, 4, 6, 5, 7, 7, 9, 9,10,10,12,12, 6, 5, 5, + 7, 7, 8, 8,10,10,12,11,12,12, 6, 5, 5, 7, 7, 8, + 8,10,10,11,11,12,12,15, 7, 7, 8, 8, 9, 9,11,11, + 12,12,13,12,15, 8, 8, 8, 7, 9, 9,10,10,12,12,13, + 13,16,11,10, 8, 8,10,10,11,11,12,12,13,13,16,11, + 11, 9, 8,11,10,11,11,12,12,13,12,16,16,16,10,11, + 10,11,12,12,12,12,13,13,16,16,16,11, 9,11, 9,14, + 12,12,12,13,13,16,16,16,12,14,11,12,12,12,13,13, + 14,13,16,16,16,15,13,12,10,13,10,13,14,13,13,16, + 16,16,16,16,13,14,12,13,13,12,13,13,16,16,16,16, + 16,13,12,12,11,14,12,15,13, +}; + +static float _vq_quantthresh__44c1_s_p8_1[] = { + -93.5, -76.5, -59.5, -42.5, -25.5, -8.5, 8.5, 25.5, + 42.5, 59.5, 76.5, 93.5, +}; + +static long _vq_quantmap__44c1_s_p8_1[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_s_p8_1 = { + _vq_quantthresh__44c1_s_p8_1, + _vq_quantmap__44c1_s_p8_1, + 13, + 13 +}; + +static static_codebook _44c1_s_p8_1 = { + 2, 169, + _vq_lengthlist__44c1_s_p8_1, + 1, -522616832, 1620115456, 4, 0, + _vq_quantlist__44c1_s_p8_1, + NULL, + &_vq_auxt__44c1_s_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_s_p8_2[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44c1_s_p8_2[] = { + 2, 4, 4, 6, 6, 6, 6, 7, 7, 8, 8, 8, 8, 8, 8, 8, + 8,10,10,10, 7, 7, 7, 7, 8, 8, 9, 9, 9, 9, 9, 9, + 9, 9,10,10,10, 7, 7, 8, 7, 8, 8, 9, 9, 9, 9, 9, + 9, 9, 9,10,10,10, 7, 7, 8, 8, 8, 9, 9, 9, 9, 9, + 9,10, 9, 9,10,10,10, 7, 7, 8, 8, 9, 8, 9, 9, 9, + 9,10, 9, 9,10,10,11,11, 8, 8, 8, 8, 9, 9, 9, 9, + 9, 9,10, 9, 9,10,10,10,10, 8, 8, 8, 8, 9, 9, 9, + 9, 9, 9, 9, 9,10,10,11,11,11, 8, 8, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9,10,10,10,10,11,11,11, 8, 8, 9, + 9, 9, 9,10, 9, 9, 9, 9, 9,11,11,11,11,11, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,11,10,10,11,11, 9, + 9, 9, 9, 9, 9, 9, 9, 9,10,10,10,10,11,10,11,11, + 9, 9, 9, 9, 9, 9, 9, 9, 9,10,10, 9,10,10,11,11, + 11,11,11, 9, 9, 9,10, 9, 9, 9, 9, 9, 9,10,11,11, + 11,11,11,11,10,10,10,10, 9, 9, 9, 9, 9, 9,10,11, + 11,11,11,11,11, 9,10, 9, 9, 9, 9,10, 9, 9, 9,11, + 11,11,11,11,11,11,10,10, 9, 9, 9, 9, 9, 9,10, 9, + 11,11,10,11,11,11,11,10,11, 9, 9, 9, 9, 9, 9, 9, + 9, +}; + +static float _vq_quantthresh__44c1_s_p8_2[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44c1_s_p8_2[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_s_p8_2 = { + _vq_quantthresh__44c1_s_p8_2, + _vq_quantmap__44c1_s_p8_2, + 17, + 17 +}; + +static static_codebook _44c1_s_p8_2 = { + 2, 289, + _vq_lengthlist__44c1_s_p8_2, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44c1_s_p8_2, + NULL, + &_vq_auxt__44c1_s_p8_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44c1_s_short[] = { + 6, 8,13,12,13,14,15,16,16, 4, 2, 4, 7, 6, 8,11, + 13,15,10, 4, 4, 8, 6, 8,11,14,17,11, 5, 6, 5, 6, + 8,12,14,17,11, 5, 5, 6, 5, 7,10,13,16,12, 6, 7, + 8, 7, 8,10,13,15,13, 8, 8, 7, 7, 8,10,12,15,15, + 7, 7, 5, 5, 7, 9,12,14,15, 8, 8, 6, 6, 7, 8,10, + 11, +}; + +static static_codebook _huff_book__44c1_s_short = { + 2, 81, + _huff_lengthlist__44c1_s_short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44c1_sm_long[] = { + 5, 4, 8,10, 9, 9,10,11,12, 4, 2, 5, 6, 6, 8,10, + 11,13, 8, 4, 6, 8, 7, 9,12,12,14,10, 6, 8, 4, 5, + 6, 9,11,12, 9, 5, 6, 5, 5, 6, 9,11,11, 9, 7, 9, + 6, 5, 5, 7,10,10,10, 9,11, 8, 7, 6, 7, 9,11,11, + 12,13,10,10, 9, 8, 9,11,11,15,15,12,13,11, 9,10, + 11, +}; + +static static_codebook _huff_book__44c1_sm_long = { + 2, 81, + _huff_lengthlist__44c1_sm_long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_sm_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c1_sm_p1_0[] = { + 1, 5, 5, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, 0, 0, 0, + 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5, 8, 7, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, + 0, 0, 0, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 7, 9, 8, 0, 0, + 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 5, 8, 7, 0, 0, 0, 0, + 0, 0, 8, 9, 9, 0, 0, 0, 0, 0, 0, 8, 9, 9, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, + 0, 0, 0, 9, 9,10, 0, 0, 0, 0, 0, 0, 9,10,10, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, + 0, 0, 0, 0, 8,10, 9, 0, 0, 0, 0, 0, 0, 9,10,10, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 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, 5, 7, 8, 0, 0, 0, 0, 0, 0, 8, 9, 9, 0, 0, + 0, 0, 0, 0, 8, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 9,10,10, 0, + 0, 0, 0, 0, 0, 8, 9,10, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 9,10,10, + 0, 0, 0, 0, 0, 0, 9,10, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c1_sm_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44c1_sm_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_sm_p1_0 = { + _vq_quantthresh__44c1_sm_p1_0, + _vq_quantmap__44c1_sm_p1_0, + 3, + 3 +}; + +static static_codebook _44c1_sm_p1_0 = { + 8, 6561, + _vq_lengthlist__44c1_sm_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44c1_sm_p1_0, + NULL, + &_vq_auxt__44c1_sm_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_sm_p2_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c1_sm_p2_0[] = { + 2, 3, 4, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 6, 6, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 4, 4, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 6, 7, 9, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c1_sm_p2_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c1_sm_p2_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_sm_p2_0 = { + _vq_quantthresh__44c1_sm_p2_0, + _vq_quantmap__44c1_sm_p2_0, + 5, + 5 +}; + +static static_codebook _44c1_sm_p2_0 = { + 4, 625, + _vq_lengthlist__44c1_sm_p2_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c1_sm_p2_0, + NULL, + &_vq_auxt__44c1_sm_p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_sm_p3_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44c1_sm_p3_0[] = { + 1, 3, 3, 7, 7, 0, 0, 0, 0, 0, 5, 5, 6, 6, 0, 0, + 0, 0, 0, 5, 5, 7, 7, 0, 0, 0, 0, 0, 7, 7, 7, 7, + 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, + 8, 9, 0, 0, 0, 0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0, + 0, 0,10,10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c1_sm_p3_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44c1_sm_p3_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_sm_p3_0 = { + _vq_quantthresh__44c1_sm_p3_0, + _vq_quantmap__44c1_sm_p3_0, + 9, + 9 +}; + +static static_codebook _44c1_sm_p3_0 = { + 2, 81, + _vq_lengthlist__44c1_sm_p3_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44c1_sm_p3_0, + NULL, + &_vq_auxt__44c1_sm_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_sm_p4_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44c1_sm_p4_0[] = { + 1, 3, 3, 6, 6, 7, 7, 9, 9, 0, 6, 6, 7, 7, 8, 8, + 9, 9, 0, 6, 6, 7, 7, 8, 8, 9, 9, 0, 7, 7, 8, 8, + 8, 8,10,10, 0, 0, 0, 8, 8, 8, 8,10,10, 0, 0, 0, + 8, 8, 9, 9,11,11, 0, 0, 0, 9, 9, 9, 9,11,11, 0, + 0, 0,10,10,10,10,11,11, 0, 0, 0, 0, 0, 9, 9,11, + 11, +}; + +static float _vq_quantthresh__44c1_sm_p4_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44c1_sm_p4_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_sm_p4_0 = { + _vq_quantthresh__44c1_sm_p4_0, + _vq_quantmap__44c1_sm_p4_0, + 9, + 9 +}; + +static static_codebook _44c1_sm_p4_0 = { + 2, 81, + _vq_lengthlist__44c1_sm_p4_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44c1_sm_p4_0, + NULL, + &_vq_auxt__44c1_sm_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_sm_p5_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44c1_sm_p5_0[] = { + 2, 3, 3, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9,10,10,11, + 11, 0, 5, 5, 6, 6, 8, 8, 9, 9, 9, 9,10,10,10,10, + 11,11, 0, 5, 5, 6, 6, 8, 8, 9, 9, 9, 9,10,10,10, + 10,11,11, 0, 7, 7, 7, 7, 8, 8, 9, 9, 9, 9,10,10, + 11,11,12,12, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10, + 10,11,11,12,12, 0, 0, 0, 8, 8, 8, 8, 9, 9,10,10, + 10,11,11,11,12,12, 0, 0, 0, 8, 8, 8, 8, 9, 9,10, + 10,10,10,11,11,12,12, 0, 0, 0, 9, 9, 9, 9,10,10, + 10,10,11,11,12,12,12,12, 0, 0, 0, 0, 0, 9, 9,10, + 10,10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, 9, 9, + 9, 9,10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, 9, + 9, 9, 9,10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, + 9, 9,10,10,11,11,12,12,12,12,13,13, 0, 0, 0, 0, + 0, 0, 0,10,10,11,11,12,12,12,12,13,13, 0, 0, 0, + 0, 0, 0, 0,11,11,11,11,12,12,13,13,13,13, 0, 0, + 0, 0, 0, 0, 0,11,11,11,11,12,12,13,13,13,13, 0, + 0, 0, 0, 0, 0, 0,11,11,12,12,12,12,13,13,14,14, + 0, 0, 0, 0, 0, 0, 0, 0, 0,12,12,12,12,13,13,14, + 14, +}; + +static float _vq_quantthresh__44c1_sm_p5_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44c1_sm_p5_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_sm_p5_0 = { + _vq_quantthresh__44c1_sm_p5_0, + _vq_quantmap__44c1_sm_p5_0, + 17, + 17 +}; + +static static_codebook _44c1_sm_p5_0 = { + 2, 289, + _vq_lengthlist__44c1_sm_p5_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44c1_sm_p5_0, + NULL, + &_vq_auxt__44c1_sm_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_sm_p6_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c1_sm_p6_0[] = { + 1, 4, 4, 7, 6, 6, 7, 6, 6, 4, 7, 7,10, 9, 9,11, + 9, 9, 4, 7, 7,10, 9, 9,11, 9, 9, 7,10,10,10,11, + 11,11,10,10, 6, 9, 9,11,11,10,11,10,10, 6, 9, 9, + 11,10,11,11,10,10, 7,11,11,11,11,11,11,11,11, 6, + 9, 9,11,10,10,11,11,10, 6, 9, 9,10,10,10,11,10, + 11, +}; + +static float _vq_quantthresh__44c1_sm_p6_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__44c1_sm_p6_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_sm_p6_0 = { + _vq_quantthresh__44c1_sm_p6_0, + _vq_quantmap__44c1_sm_p6_0, + 3, + 3 +}; + +static static_codebook _44c1_sm_p6_0 = { + 4, 81, + _vq_lengthlist__44c1_sm_p6_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__44c1_sm_p6_0, + NULL, + &_vq_auxt__44c1_sm_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_sm_p6_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44c1_sm_p6_1[] = { + 2, 4, 4, 6, 6, 7, 7, 7, 7, 8, 8,10, 5, 5, 6, 6, + 7, 7, 8, 8, 8, 8,10, 5, 5, 6, 6, 7, 7, 8, 8, 8, + 8,10, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8,10,10,10, 7, + 7, 7, 7, 8, 8, 8, 8,10,10,10, 7, 7, 8, 8, 8, 8, + 8, 8,10,10,10, 7, 7, 8, 8, 8, 8, 8, 8,10,10,10, + 8, 8, 8, 8, 8, 8, 9, 8,10,10,10,10,10, 8, 8, 8, + 8, 8, 8,10,10,10,10,10, 9, 9, 8, 8, 8, 8,10,10, + 10,10,10, 8, 8, 8, 8, 8, 8, +}; + +static float _vq_quantthresh__44c1_sm_p6_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44c1_sm_p6_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_sm_p6_1 = { + _vq_quantthresh__44c1_sm_p6_1, + _vq_quantmap__44c1_sm_p6_1, + 11, + 11 +}; + +static static_codebook _44c1_sm_p6_1 = { + 2, 121, + _vq_lengthlist__44c1_sm_p6_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44c1_sm_p6_1, + NULL, + &_vq_auxt__44c1_sm_p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_sm_p7_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c1_sm_p7_0[] = { + 1, 4, 4, 6, 6, 7, 7, 7, 7, 8, 8, 9, 9, 7, 5, 5, + 7, 7, 8, 8, 8, 8, 9, 9,10,10, 7, 5, 6, 7, 7, 8, + 8, 8, 8, 9, 9,11,10, 0, 8, 8, 8, 8, 9, 9, 9, 9, + 10,10,11,11, 0, 8, 8, 8, 8, 9, 9, 9, 9,10,10,11, + 11, 0,12,12, 9, 9,10,10,10,10,11,11,11,11, 0,13, + 13, 9, 9, 9, 9,10,10,11,11,12,12, 0, 0, 0, 9,10, + 9,10,11,11,12,11,13,12, 0, 0, 0,10,10, 9, 9,11, + 11,12,12,13,12, 0, 0, 0,13,13,10,10,11,11,12,12, + 13,13, 0, 0, 0,14,14,10,10,11,11,12,12,13,13, 0, + 0, 0, 0, 0,11,12,11,11,12,13,14,13, 0, 0, 0, 0, + 0,12,12,11,11,13,12,14,13, +}; + +static float _vq_quantthresh__44c1_sm_p7_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44c1_sm_p7_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_sm_p7_0 = { + _vq_quantthresh__44c1_sm_p7_0, + _vq_quantmap__44c1_sm_p7_0, + 13, + 13 +}; + +static static_codebook _44c1_sm_p7_0 = { + 2, 169, + _vq_lengthlist__44c1_sm_p7_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44c1_sm_p7_0, + NULL, + &_vq_auxt__44c1_sm_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_sm_p7_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c1_sm_p7_1[] = { + 2, 4, 4, 4, 5, 6, 5, 5, 5, 5, 6, 5, 5, 5, 5, 6, + 5, 5, 5, 5, 6, 6, 6, 5, 5, +}; + +static float _vq_quantthresh__44c1_sm_p7_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c1_sm_p7_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_sm_p7_1 = { + _vq_quantthresh__44c1_sm_p7_1, + _vq_quantmap__44c1_sm_p7_1, + 5, + 5 +}; + +static static_codebook _44c1_sm_p7_1 = { + 2, 25, + _vq_lengthlist__44c1_sm_p7_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c1_sm_p7_1, + NULL, + &_vq_auxt__44c1_sm_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_sm_p8_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c1_sm_p8_0[] = { + 1, 3, 3,13,13,13,13,13,13,13,13,13,13, 3, 6, 6, + 13,13,13,13,13,13,13,13,13,13, 4, 8, 7,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13, +}; + +static float _vq_quantthresh__44c1_sm_p8_0[] = { + -1215.5, -994.5, -773.5, -552.5, -331.5, -110.5, 110.5, 331.5, + 552.5, 773.5, 994.5, 1215.5, +}; + +static long _vq_quantmap__44c1_sm_p8_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_sm_p8_0 = { + _vq_quantthresh__44c1_sm_p8_0, + _vq_quantmap__44c1_sm_p8_0, + 13, + 13 +}; + +static static_codebook _44c1_sm_p8_0 = { + 2, 169, + _vq_lengthlist__44c1_sm_p8_0, + 1, -514541568, 1627103232, 4, 0, + _vq_quantlist__44c1_sm_p8_0, + NULL, + &_vq_auxt__44c1_sm_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_sm_p8_1[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c1_sm_p8_1[] = { + 1, 4, 4, 6, 6, 7, 7, 9, 9,10,11,12,12, 6, 5, 5, + 7, 7, 8, 7,10,10,11,11,12,12, 6, 5, 5, 7, 7, 8, + 8,10,10,11,11,12,12,16, 7, 7, 8, 8, 9, 9,11,11, + 12,12,13,13,17, 7, 7, 8, 7, 9, 9,11,10,12,12,13, + 13,19,11,10, 8, 8,10,10,11,11,12,12,13,13,19,11, + 11, 9, 7,11,10,11,11,12,12,13,12,19,19,19,10,10, + 10,10,11,12,12,12,13,14,18,19,19,11, 9,11, 9,13, + 12,12,12,13,13,19,20,19,13,15,11,11,12,12,13,13, + 14,13,18,19,20,15,13,12,10,13,10,13,13,13,14,20, + 20,20,20,20,13,14,12,12,13,12,13,13,20,20,20,20, + 20,13,12,12,12,14,12,14,13, +}; + +static float _vq_quantthresh__44c1_sm_p8_1[] = { + -93.5, -76.5, -59.5, -42.5, -25.5, -8.5, 8.5, 25.5, + 42.5, 59.5, 76.5, 93.5, +}; + +static long _vq_quantmap__44c1_sm_p8_1[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_sm_p8_1 = { + _vq_quantthresh__44c1_sm_p8_1, + _vq_quantmap__44c1_sm_p8_1, + 13, + 13 +}; + +static static_codebook _44c1_sm_p8_1 = { + 2, 169, + _vq_lengthlist__44c1_sm_p8_1, + 1, -522616832, 1620115456, 4, 0, + _vq_quantlist__44c1_sm_p8_1, + NULL, + &_vq_auxt__44c1_sm_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_sm_p8_2[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44c1_sm_p8_2[] = { + 2, 5, 5, 6, 6, 7, 6, 7, 7, 8, 8, 8, 8, 8, 8, 8, + 8,10, 6, 6, 7, 7, 7, 7, 8, 8, 9, 9, 9, 9, 9, 9, + 9, 9,10, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 9, 9,10, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 9,10,10,10, 7, 7, 8, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9,10,11,11, 8, 8, 8, 8, 9, 9, 9, 9, + 9, 9,10,10, 9,10,10,10,10, 8, 8, 8, 8, 9, 9, 9, + 9, 9, 9, 9, 9,10,10,11,10,10, 8, 8, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9,10, 9,10,10,10,11,11, 8, 8, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9,11,11,11,11,11, 9, 9, + 9, 9, 9, 9, 9, 9,10, 9,10, 9,11,11,11,11,11, 9, + 8, 9, 9, 9, 9, 9, 9, 9,10,10, 9,11,11,10,11,11, + 9, 9, 9, 9, 9, 9, 9, 9, 9,10,10, 9,11,11,11,11, + 11,11,11, 9, 9,10, 9, 9, 9, 9,10, 9,10,10,11,10, + 11,11,11,11, 9,10,10,10, 9, 9, 9, 9, 9, 9,10,11, + 11,11,11,11,11, 9, 9, 9, 9, 9, 9, 9, 9,10, 9,11, + 11,10,11,11,11,11,10,10, 9, 9, 9, 9, 9, 9,10, 9, + 10,11,10,11,11,11,11,11,11, 9, 9,10, 9, 9, 9, 9, + 9, +}; + +static float _vq_quantthresh__44c1_sm_p8_2[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44c1_sm_p8_2[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_sm_p8_2 = { + _vq_quantthresh__44c1_sm_p8_2, + _vq_quantmap__44c1_sm_p8_2, + 17, + 17 +}; + +static static_codebook _44c1_sm_p8_2 = { + 2, 289, + _vq_lengthlist__44c1_sm_p8_2, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44c1_sm_p8_2, + NULL, + &_vq_auxt__44c1_sm_p8_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44c1_sm_short[] = { + 4, 7,13,14,14,15,16,18,18, 4, 2, 5, 8, 7, 9,12, + 15,15,10, 4, 5,10, 6, 8,11,15,17,12, 5, 7, 5, 6, + 8,11,14,17,11, 5, 6, 6, 5, 6, 9,13,17,12, 6, 7, + 6, 5, 6, 8,12,14,14, 7, 8, 6, 6, 7, 9,11,14,14, + 8, 9, 6, 5, 6, 9,11,13,16,10,10, 7, 6, 7, 8,10, + 11, +}; + +static static_codebook _huff_book__44c1_sm_short = { + 2, 81, + _huff_lengthlist__44c1_sm_short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44cn1_s_long[] = { + 4, 4, 7, 8, 7, 8,10,12,17, 3, 1, 6, 6, 7, 8,10, + 12,15, 7, 6, 9, 9, 9,11,12,14,17, 8, 6, 9, 6, 7, + 9,11,13,17, 7, 6, 9, 7, 7, 8, 9,12,15, 8, 8,10, + 8, 7, 7, 7,10,14, 9,10,12,10, 8, 8, 8,10,14,11, + 13,15,13,12,11,11,12,16,17,18,18,19,20,18,16,16, + 20, +}; + +static static_codebook _huff_book__44cn1_s_long = { + 2, 81, + _huff_lengthlist__44cn1_s_long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_s_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44cn1_s_p1_0[] = { + 1, 4, 4, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, 0, 0, 0, + 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5, 8, 8, 0, 0, 0, 0, 0, 0, 8, 9, 9, 0, 0, 0, + 0, 0, 0, 7, 9,10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 8, 8, 0, 0, 0, 0, 0, 0, 7,10, 9, 0, 0, + 0, 0, 0, 0, 8,10, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 5, 8, 8, 0, 0, 0, 0, + 0, 0, 8,10,10, 0, 0, 0, 0, 0, 0, 8, 9,10, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7,10,10, 0, 0, 0, + 0, 0, 0, 9, 9,11, 0, 0, 0, 0, 0, 0,10,11,11, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7,10,10, 0, 0, + 0, 0, 0, 0, 9,11, 9, 0, 0, 0, 0, 0, 0,10,11,11, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 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, 5, 8, 8, 0, 0, 0, 0, 0, 0, 8,10,10, 0, 0, + 0, 0, 0, 0, 8,10,10, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7,10,10, 0, 0, 0, 0, 0, 0,10,11,11, 0, + 0, 0, 0, 0, 0, 9, 9,11, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7,10,10, 0, 0, 0, 0, 0, 0,10,11,11, + 0, 0, 0, 0, 0, 0, 9,11, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44cn1_s_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44cn1_s_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_s_p1_0 = { + _vq_quantthresh__44cn1_s_p1_0, + _vq_quantmap__44cn1_s_p1_0, + 3, + 3 +}; + +static static_codebook _44cn1_s_p1_0 = { + 8, 6561, + _vq_lengthlist__44cn1_s_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44cn1_s_p1_0, + NULL, + &_vq_auxt__44cn1_s_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_s_p2_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44cn1_s_p2_0[] = { + 1, 4, 4, 7, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 5, 7, 7, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 5, 5, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 7, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 7, 7, 9, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44cn1_s_p2_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44cn1_s_p2_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_s_p2_0 = { + _vq_quantthresh__44cn1_s_p2_0, + _vq_quantmap__44cn1_s_p2_0, + 5, + 5 +}; + +static static_codebook _44cn1_s_p2_0 = { + 4, 625, + _vq_lengthlist__44cn1_s_p2_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44cn1_s_p2_0, + NULL, + &_vq_auxt__44cn1_s_p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_s_p3_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44cn1_s_p3_0[] = { + 1, 2, 3, 7, 7, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, + 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 7, 7, + 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, + 9, 8, 0, 0, 0, 0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0, + 0, 0,10,10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44cn1_s_p3_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44cn1_s_p3_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_s_p3_0 = { + _vq_quantthresh__44cn1_s_p3_0, + _vq_quantmap__44cn1_s_p3_0, + 9, + 9 +}; + +static static_codebook _44cn1_s_p3_0 = { + 2, 81, + _vq_lengthlist__44cn1_s_p3_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44cn1_s_p3_0, + NULL, + &_vq_auxt__44cn1_s_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_s_p4_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44cn1_s_p4_0[] = { + 1, 3, 3, 6, 6, 6, 6, 8, 8, 0, 0, 0, 6, 6, 7, 7, + 9, 9, 0, 0, 0, 6, 6, 7, 7, 9, 9, 0, 0, 0, 7, 7, + 8, 8,10,10, 0, 0, 0, 7, 7, 8, 8,10,10, 0, 0, 0, + 9, 9, 9, 9,10,10, 0, 0, 0, 9, 9, 9, 9,10,10, 0, + 0, 0,10,10,10,10,11,11, 0, 0, 0, 0, 0,10,10,11, + 11, +}; + +static float _vq_quantthresh__44cn1_s_p4_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44cn1_s_p4_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_s_p4_0 = { + _vq_quantthresh__44cn1_s_p4_0, + _vq_quantmap__44cn1_s_p4_0, + 9, + 9 +}; + +static static_codebook _44cn1_s_p4_0 = { + 2, 81, + _vq_lengthlist__44cn1_s_p4_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44cn1_s_p4_0, + NULL, + &_vq_auxt__44cn1_s_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_s_p5_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44cn1_s_p5_0[] = { + 1, 4, 3, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9,10,10,10, + 10, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10,10,10,10, + 11,11, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10,10,10, + 10,11,11, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10,10, + 11,11,11,12, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10, + 10,11,11,11,11, 0, 0, 0, 8, 8, 9, 9, 9, 9,10,10, + 10,10,11,11,12,12, 0, 0, 0, 8, 8, 9, 9, 9, 9,10, + 10,10,11,11,11,12,12, 0, 0, 0, 9, 9,10, 9,10,10, + 10,10,11,11,11,11,12,12, 0, 0, 0, 0, 0, 9, 9,10, + 10,10,10,11,11,12,12,12,12, 0, 0, 0, 0, 0, 9, 9, + 10,10,10,11,11,11,12,12,13,13, 0, 0, 0, 0, 0, 9, + 9,10,10,10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, + 10,10,11,10,11,11,11,12,13,12,13,13, 0, 0, 0, 0, + 0, 0, 0,11,10,11,11,12,12,12,12,13,13, 0, 0, 0, + 0, 0, 0, 0,11,11,12,12,12,12,13,13,13,14, 0, 0, + 0, 0, 0, 0, 0,11,11,12,12,12,12,13,13,13,14, 0, + 0, 0, 0, 0, 0, 0,12,12,12,13,13,13,13,13,14,14, + 0, 0, 0, 0, 0, 0, 0, 0, 0,12,12,13,12,13,13,14, + 14, +}; + +static float _vq_quantthresh__44cn1_s_p5_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44cn1_s_p5_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_s_p5_0 = { + _vq_quantthresh__44cn1_s_p5_0, + _vq_quantmap__44cn1_s_p5_0, + 17, + 17 +}; + +static static_codebook _44cn1_s_p5_0 = { + 2, 289, + _vq_lengthlist__44cn1_s_p5_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44cn1_s_p5_0, + NULL, + &_vq_auxt__44cn1_s_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_s_p6_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44cn1_s_p6_0[] = { + 1, 4, 4, 7, 6, 6, 7, 6, 6, 4, 6, 6,10, 9, 9,11, + 9, 9, 4, 6, 6,10, 9, 9,10, 9, 9, 7,10,10,11,11, + 11,12,11,11, 7, 9, 9,11,11,10,11,10,10, 7, 9, 9, + 11,10,11,11,10,10, 7,10,10,11,11,11,12,11,11, 7, + 9, 9,11,10,10,11,10,10, 7, 9, 9,11,10,10,11,10, + 10, +}; + +static float _vq_quantthresh__44cn1_s_p6_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__44cn1_s_p6_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_s_p6_0 = { + _vq_quantthresh__44cn1_s_p6_0, + _vq_quantmap__44cn1_s_p6_0, + 3, + 3 +}; + +static static_codebook _44cn1_s_p6_0 = { + 4, 81, + _vq_lengthlist__44cn1_s_p6_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__44cn1_s_p6_0, + NULL, + &_vq_auxt__44cn1_s_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_s_p6_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44cn1_s_p6_1[] = { + 1, 4, 4, 6, 6, 7, 7, 8, 8, 8, 8,10,10,10, 7, 6, + 8, 8, 8, 8, 8, 8,10,10,10, 7, 6, 7, 7, 8, 8, 8, + 8,10,10,10, 7, 7, 8, 8, 8, 8, 8, 8,10,10,10, 7, + 7, 8, 8, 8, 8, 8, 8,10,10,10, 8, 8, 8, 8, 9, 9, + 9, 9,10,10,10, 8, 8, 8, 8, 9, 9, 9, 9,10,10,10, + 9, 9, 9, 9, 9, 9, 9, 9,10,10,10,10,10, 9, 9, 9, + 9, 9, 9,10,10,10,10,10, 9, 9, 9, 9, 9, 9,10,10, + 10,10,10, 9, 9, 9, 9, 9, 9, +}; + +static float _vq_quantthresh__44cn1_s_p6_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44cn1_s_p6_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_s_p6_1 = { + _vq_quantthresh__44cn1_s_p6_1, + _vq_quantmap__44cn1_s_p6_1, + 11, + 11 +}; + +static static_codebook _44cn1_s_p6_1 = { + 2, 121, + _vq_lengthlist__44cn1_s_p6_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44cn1_s_p6_1, + NULL, + &_vq_auxt__44cn1_s_p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_s_p7_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44cn1_s_p7_0[] = { + 1, 4, 4, 6, 6, 7, 7, 8, 8, 9, 9,10,10, 6, 5, 5, + 7, 7, 8, 8, 8, 8, 9, 9,11,11, 7, 5, 5, 7, 7, 8, + 8, 8, 8, 9,10,11,11, 0, 8, 8, 8, 8, 9, 9, 9, 9, + 10,10,11,11, 0, 8, 8, 8, 8, 9, 9, 9, 9,10,10,11, + 11, 0,12,12, 9, 9, 9,10,10,10,11,11,11,12, 0,13, + 13, 9, 9, 9, 9,10,10,11,11,11,12, 0, 0, 0,10,10, + 10,10,11,11,12,12,12,13, 0, 0, 0,10,10,10,10,11, + 11,12,12,13,12, 0, 0, 0,14,14,11,10,11,12,12,13, + 13,14, 0, 0, 0,15,15,11,11,12,11,12,12,14,13, 0, + 0, 0, 0, 0,12,12,12,12,13,13,14,14, 0, 0, 0, 0, + 0,13,13,12,12,13,13,13,14, +}; + +static float _vq_quantthresh__44cn1_s_p7_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44cn1_s_p7_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_s_p7_0 = { + _vq_quantthresh__44cn1_s_p7_0, + _vq_quantmap__44cn1_s_p7_0, + 13, + 13 +}; + +static static_codebook _44cn1_s_p7_0 = { + 2, 169, + _vq_lengthlist__44cn1_s_p7_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44cn1_s_p7_0, + NULL, + &_vq_auxt__44cn1_s_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_s_p7_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44cn1_s_p7_1[] = { + 2, 3, 3, 5, 5, 6, 6, 6, 5, 5, 6, 6, 6, 5, 5, 6, + 6, 6, 5, 5, 6, 6, 6, 5, 5, +}; + +static float _vq_quantthresh__44cn1_s_p7_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44cn1_s_p7_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_s_p7_1 = { + _vq_quantthresh__44cn1_s_p7_1, + _vq_quantmap__44cn1_s_p7_1, + 5, + 5 +}; + +static static_codebook _44cn1_s_p7_1 = { + 2, 25, + _vq_lengthlist__44cn1_s_p7_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44cn1_s_p7_1, + NULL, + &_vq_auxt__44cn1_s_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_s_p8_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44cn1_s_p8_0[] = { + 1, 7, 7,11,11, 8,11,11,11,11, 4,11, 3,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,10,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,10,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11, 7,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,10,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,10, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11, 8,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12, +}; + +static float _vq_quantthresh__44cn1_s_p8_0[] = { + -331.5, -110.5, 110.5, 331.5, +}; + +static long _vq_quantmap__44cn1_s_p8_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_s_p8_0 = { + _vq_quantthresh__44cn1_s_p8_0, + _vq_quantmap__44cn1_s_p8_0, + 5, + 5 +}; + +static static_codebook _44cn1_s_p8_0 = { + 4, 625, + _vq_lengthlist__44cn1_s_p8_0, + 1, -518283264, 1627103232, 3, 0, + _vq_quantlist__44cn1_s_p8_0, + NULL, + &_vq_auxt__44cn1_s_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_s_p8_1[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44cn1_s_p8_1[] = { + 1, 4, 4, 6, 6, 8, 8, 9,10,10,11,11,11, 6, 5, 5, + 7, 7, 8, 8, 9,10, 9,11,11,12, 5, 5, 5, 7, 7, 8, + 9,10,10,12,12,14,13,15, 7, 7, 8, 8, 9,10,11,11, + 10,12,10,11,15, 7, 8, 8, 8, 9, 9,11,11,13,12,12, + 13,15,10,10, 8, 8,10,10,12,12,11,14,10,10,15,11, + 11, 8, 8,10,10,12,13,13,14,15,13,15,15,15,10,10, + 10,10,12,12,13,12,13,10,15,15,15,10,10,11,10,13, + 11,13,13,15,13,15,15,15,13,13,10,11,11,11,12,10, + 14,11,15,15,14,14,13,10,10,12,11,13,13,14,14,15, + 15,15,15,15,11,11,11,11,12,11,15,12,15,15,15,15, + 15,12,12,11,11,14,12,13,14, +}; + +static float _vq_quantthresh__44cn1_s_p8_1[] = { + -93.5, -76.5, -59.5, -42.5, -25.5, -8.5, 8.5, 25.5, + 42.5, 59.5, 76.5, 93.5, +}; + +static long _vq_quantmap__44cn1_s_p8_1[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_s_p8_1 = { + _vq_quantthresh__44cn1_s_p8_1, + _vq_quantmap__44cn1_s_p8_1, + 13, + 13 +}; + +static static_codebook _44cn1_s_p8_1 = { + 2, 169, + _vq_lengthlist__44cn1_s_p8_1, + 1, -522616832, 1620115456, 4, 0, + _vq_quantlist__44cn1_s_p8_1, + NULL, + &_vq_auxt__44cn1_s_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_s_p8_2[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44cn1_s_p8_2[] = { + 3, 4, 3, 6, 6, 7, 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, + 9,10,11,11, 6, 6, 7, 7, 8, 8, 9, 9, 9, 9, 9, 9, + 9, 9,10,10,10, 6, 6, 7, 7, 8, 8, 9, 9, 9, 9, 9, + 9, 9, 9,10,10,10, 7, 7, 7, 8, 8, 8, 9, 9, 9, 9, + 9, 9,10, 9,10,11,10, 7, 6, 7, 7, 8, 8, 9, 9, 9, + 9, 9, 9, 9,10,10,10,11, 7, 7, 8, 8, 8, 8, 9, 9, + 9, 9, 9, 9, 9, 9,10,10,10, 7, 7, 8, 8, 8, 8, 9, + 9, 9, 9, 9, 9, 9,10,11,11,11, 8, 8, 8, 8, 8, 8, + 9, 9, 9, 9, 9, 9, 9, 9,11,10,10,11,11, 8, 8, 8, + 9, 9, 9, 9, 9, 9,10, 9,10,10,10,10,11,11, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,11,11,10,11,11, 9, + 9, 9, 9, 9, 9, 9, 9, 9,10,10,10,10,11,10,11,11, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,11,10,10,11, + 11,11,11, 9, 9, 9, 9, 9, 9, 9, 9,10,10,10,11,11, + 10,11,11,11, 9,10,10, 9, 9, 9, 9, 9, 9, 9,10,11, + 11,11,11,11,11, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,11, + 11,11,11,11,11,11,10,10, 9, 9, 9, 9, 9, 9, 9, 9, + 11,11,11,10,11,11,11,11,11, 9, 9, 9,10, 9, 9, 9, + 9, +}; + +static float _vq_quantthresh__44cn1_s_p8_2[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44cn1_s_p8_2[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_s_p8_2 = { + _vq_quantthresh__44cn1_s_p8_2, + _vq_quantmap__44cn1_s_p8_2, + 17, + 17 +}; + +static static_codebook _44cn1_s_p8_2 = { + 2, 289, + _vq_lengthlist__44cn1_s_p8_2, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44cn1_s_p8_2, + NULL, + &_vq_auxt__44cn1_s_p8_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44cn1_s_short[] = { + 10, 9,12,15,12,13,16,14,16, 7, 1, 5,14, 7,10,13, + 16,16, 9, 4, 6,16, 8,11,16,16,16,14, 4, 7,16, 9, + 12,14,16,16,10, 5, 7,14, 9,12,14,15,15,13, 8, 9, + 14,10,12,13,14,15,13, 9, 9, 7, 6, 8,11,12,12,14, + 8, 8, 5, 4, 5, 8,11,12,16,10,10, 6, 5, 6, 8, 9, + 10, +}; + +static static_codebook _huff_book__44cn1_s_short = { + 2, 81, + _huff_lengthlist__44cn1_s_short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44cn1_sm_long[] = { + 3, 3, 8, 8, 8, 8,10,12,14, 3, 2, 6, 7, 7, 8,10, + 12,16, 7, 6, 7, 9, 8,10,12,14,16, 8, 6, 8, 4, 5, + 7, 9,11,13, 7, 6, 8, 5, 6, 7, 9,11,14, 8, 8,10, + 7, 7, 6, 8,10,13, 9,11,12, 9, 9, 7, 8,10,12,10, + 13,15,11,11,10, 9,10,13,13,16,17,14,15,14,13,14, + 17, +}; + +static static_codebook _huff_book__44cn1_sm_long = { + 2, 81, + _huff_lengthlist__44cn1_sm_long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_sm_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44cn1_sm_p1_0[] = { + 1, 4, 5, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, 0, 0, 0, + 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5, 8, 8, 0, 0, 0, 0, 0, 0, 8, 9, 9, 0, 0, 0, + 0, 0, 0, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 8, 8, 0, 0, 0, 0, 0, 0, 7, 9, 8, 0, 0, + 0, 0, 0, 0, 8, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 5, 8, 8, 0, 0, 0, 0, + 0, 0, 8,10, 9, 0, 0, 0, 0, 0, 0, 8, 9, 9, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7,10, 9, 0, 0, 0, + 0, 0, 0, 9, 9,10, 0, 0, 0, 0, 0, 0, 9,10,10, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, + 0, 0, 0, 0, 8,10, 9, 0, 0, 0, 0, 0, 0, 9,10,10, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 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, 5, 8, 8, 0, 0, 0, 0, 0, 0, 8, 9, 9, 0, 0, + 0, 0, 0, 0, 8, 9,10, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 9,10,10, 0, + 0, 0, 0, 0, 0, 8, 9,10, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 9,10, 0, 0, 0, 0, 0, 0, 9,10,10, + 0, 0, 0, 0, 0, 0, 9,10, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44cn1_sm_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44cn1_sm_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_sm_p1_0 = { + _vq_quantthresh__44cn1_sm_p1_0, + _vq_quantmap__44cn1_sm_p1_0, + 3, + 3 +}; + +static static_codebook _44cn1_sm_p1_0 = { + 8, 6561, + _vq_lengthlist__44cn1_sm_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44cn1_sm_p1_0, + NULL, + &_vq_auxt__44cn1_sm_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_sm_p2_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44cn1_sm_p2_0[] = { + 1, 4, 4, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 5, 7, 7, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 5, 5, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 7, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 7, 7, 9, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44cn1_sm_p2_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44cn1_sm_p2_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_sm_p2_0 = { + _vq_quantthresh__44cn1_sm_p2_0, + _vq_quantmap__44cn1_sm_p2_0, + 5, + 5 +}; + +static static_codebook _44cn1_sm_p2_0 = { + 4, 625, + _vq_lengthlist__44cn1_sm_p2_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44cn1_sm_p2_0, + NULL, + &_vq_auxt__44cn1_sm_p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_sm_p3_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44cn1_sm_p3_0[] = { + 1, 3, 4, 7, 7, 0, 0, 0, 0, 0, 4, 4, 7, 7, 0, 0, + 0, 0, 0, 4, 5, 7, 7, 0, 0, 0, 0, 0, 6, 7, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 0, 0, 0, 0, 0, 0, 0,10, 9, 0, 0, 0, 0, 0, + 0, 0,11,11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44cn1_sm_p3_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44cn1_sm_p3_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_sm_p3_0 = { + _vq_quantthresh__44cn1_sm_p3_0, + _vq_quantmap__44cn1_sm_p3_0, + 9, + 9 +}; + +static static_codebook _44cn1_sm_p3_0 = { + 2, 81, + _vq_lengthlist__44cn1_sm_p3_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44cn1_sm_p3_0, + NULL, + &_vq_auxt__44cn1_sm_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_sm_p4_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44cn1_sm_p4_0[] = { + 1, 4, 3, 6, 6, 7, 7, 9, 9, 0, 5, 5, 7, 7, 8, 7, + 9, 9, 0, 5, 5, 7, 7, 8, 8, 9, 9, 0, 7, 7, 8, 8, + 8, 8,10,10, 0, 0, 0, 8, 8, 8, 8,10,10, 0, 0, 0, + 9, 9, 9, 9,10,10, 0, 0, 0, 9, 9, 9, 9,10,10, 0, + 0, 0,10,10,10,10,11,11, 0, 0, 0, 0, 0,10,10,11, + 11, +}; + +static float _vq_quantthresh__44cn1_sm_p4_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44cn1_sm_p4_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_sm_p4_0 = { + _vq_quantthresh__44cn1_sm_p4_0, + _vq_quantmap__44cn1_sm_p4_0, + 9, + 9 +}; + +static static_codebook _44cn1_sm_p4_0 = { + 2, 81, + _vq_lengthlist__44cn1_sm_p4_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44cn1_sm_p4_0, + NULL, + &_vq_auxt__44cn1_sm_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_sm_p5_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44cn1_sm_p5_0[] = { + 1, 4, 4, 6, 6, 8, 8, 9, 9, 8, 8, 9, 9,10,10,11, + 11, 0, 6, 6, 7, 7, 8, 8, 9, 9, 9, 9,10,10,11,11, + 12,12, 0, 6, 5, 7, 7, 8, 8, 9, 9, 9, 9,10,10,11, + 11,12,12, 0, 7, 7, 7, 7, 8, 8, 9, 9, 9, 9,10,10, + 11,11,12,12, 0, 0, 0, 7, 7, 8, 8, 9, 9,10,10,11, + 11,11,11,12,12, 0, 0, 0, 8, 8, 9, 9,10,10,10,10, + 11,11,12,12,12,12, 0, 0, 0, 8, 8, 9, 9,10,10,10, + 10,11,11,12,12,12,12, 0, 0, 0, 9, 9, 9, 9,10,10, + 10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, 9, 9,10, + 10,10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, 9, 9, + 10,10,11,11,12,12,13,13,13,13, 0, 0, 0, 0, 0, 9, + 9,10,10,11,11,12,12,12,13,13,13, 0, 0, 0, 0, 0, + 10,10,11,11,11,11,12,12,13,13,14,14, 0, 0, 0, 0, + 0, 0, 0,11,11,11,11,12,12,13,13,14,14, 0, 0, 0, + 0, 0, 0, 0,11,11,12,12,13,13,13,13,14,14, 0, 0, + 0, 0, 0, 0, 0,11,11,12,12,13,13,13,13,14,14, 0, + 0, 0, 0, 0, 0, 0,12,12,12,13,13,13,14,14,14,14, + 0, 0, 0, 0, 0, 0, 0, 0, 0,12,12,13,13,14,14,14, + 14, +}; + +static float _vq_quantthresh__44cn1_sm_p5_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44cn1_sm_p5_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_sm_p5_0 = { + _vq_quantthresh__44cn1_sm_p5_0, + _vq_quantmap__44cn1_sm_p5_0, + 17, + 17 +}; + +static static_codebook _44cn1_sm_p5_0 = { + 2, 289, + _vq_lengthlist__44cn1_sm_p5_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44cn1_sm_p5_0, + NULL, + &_vq_auxt__44cn1_sm_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_sm_p6_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44cn1_sm_p6_0[] = { + 1, 4, 4, 7, 6, 6, 7, 6, 6, 4, 7, 6,10, 9, 9,11, + 9, 9, 4, 6, 7,10, 9, 9,11, 9, 9, 7,10,10,10,11, + 11,11,11,10, 6, 9, 9,11,10,10,11,10,10, 6, 9, 9, + 11,10,11,11,10,10, 7,11,11,11,11,11,12,11,11, 7, + 9, 9,11,10,10,12,10,10, 7, 9, 9,11,10,10,11,10, + 10, +}; + +static float _vq_quantthresh__44cn1_sm_p6_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__44cn1_sm_p6_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_sm_p6_0 = { + _vq_quantthresh__44cn1_sm_p6_0, + _vq_quantmap__44cn1_sm_p6_0, + 3, + 3 +}; + +static static_codebook _44cn1_sm_p6_0 = { + 4, 81, + _vq_lengthlist__44cn1_sm_p6_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__44cn1_sm_p6_0, + NULL, + &_vq_auxt__44cn1_sm_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_sm_p6_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44cn1_sm_p6_1[] = { + 2, 4, 4, 5, 5, 7, 7, 7, 7, 8, 8,10, 5, 5, 6, 6, + 7, 7, 8, 8, 8, 8,10, 5, 5, 6, 6, 7, 7, 8, 8, 8, + 8,10, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8,10,10,10, 7, + 7, 7, 7, 8, 8, 8, 8,10,10,10, 8, 8, 8, 8, 8, 8, + 8, 8,10,10,10, 8, 8, 8, 8, 8, 8, 8, 8,10,10,10, + 8, 8, 8, 8, 8, 8, 9, 9,10,10,10,10,10, 8, 8, 8, + 8, 9, 9,10,10,10,10,10, 9, 9, 9, 9, 8, 9,10,10, + 10,10,10, 8, 9, 8, 8, 9, 8, +}; + +static float _vq_quantthresh__44cn1_sm_p6_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44cn1_sm_p6_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_sm_p6_1 = { + _vq_quantthresh__44cn1_sm_p6_1, + _vq_quantmap__44cn1_sm_p6_1, + 11, + 11 +}; + +static static_codebook _44cn1_sm_p6_1 = { + 2, 121, + _vq_lengthlist__44cn1_sm_p6_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44cn1_sm_p6_1, + NULL, + &_vq_auxt__44cn1_sm_p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_sm_p7_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44cn1_sm_p7_0[] = { + 1, 4, 4, 6, 6, 7, 7, 7, 7, 9, 9,10,10, 7, 5, 5, + 7, 7, 8, 8, 8, 8,10, 9,11,10, 7, 5, 5, 7, 7, 8, + 8, 8, 8, 9,10,11,11, 0, 8, 8, 8, 8, 9, 9, 9, 9, + 10,10,11,11, 0, 8, 8, 8, 8, 9, 9, 9, 9,10,10,11, + 11, 0,12,12, 9, 9, 9,10,10,10,11,11,12,12, 0,13, + 13, 9, 9, 9, 9,10,10,11,11,12,12, 0, 0, 0,10,10, + 10,10,11,11,12,12,12,13, 0, 0, 0,10,10,10,10,11, + 11,12,12,12,12, 0, 0, 0,14,14,11,11,11,11,12,13, + 13,13, 0, 0, 0,14,14,11,10,11,11,12,12,13,13, 0, + 0, 0, 0, 0,12,12,12,12,13,13,13,14, 0, 0, 0, 0, + 0,13,12,12,12,13,13,13,14, +}; + +static float _vq_quantthresh__44cn1_sm_p7_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44cn1_sm_p7_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_sm_p7_0 = { + _vq_quantthresh__44cn1_sm_p7_0, + _vq_quantmap__44cn1_sm_p7_0, + 13, + 13 +}; + +static static_codebook _44cn1_sm_p7_0 = { + 2, 169, + _vq_lengthlist__44cn1_sm_p7_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44cn1_sm_p7_0, + NULL, + &_vq_auxt__44cn1_sm_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_sm_p7_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44cn1_sm_p7_1[] = { + 2, 4, 4, 4, 5, 6, 5, 5, 5, 5, 6, 5, 5, 5, 5, 6, + 5, 5, 5, 5, 6, 6, 6, 5, 5, +}; + +static float _vq_quantthresh__44cn1_sm_p7_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44cn1_sm_p7_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_sm_p7_1 = { + _vq_quantthresh__44cn1_sm_p7_1, + _vq_quantmap__44cn1_sm_p7_1, + 5, + 5 +}; + +static static_codebook _44cn1_sm_p7_1 = { + 2, 25, + _vq_lengthlist__44cn1_sm_p7_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44cn1_sm_p7_1, + NULL, + &_vq_auxt__44cn1_sm_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_sm_p8_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44cn1_sm_p8_0[] = { + 1, 4, 4,12,11,13,13,14,14, 4, 7, 7,11,13,14,14, + 14,14, 3, 8, 3,14,14,14,14,14,14,14,10,12,14,14, + 14,14,14,14,14,14, 5,14, 8,14,14,14,14,14,12,14, + 13,14,14,14,14,14,14,14,13,14,10,14,14,14,14,14, + 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, + 14, +}; + +static float _vq_quantthresh__44cn1_sm_p8_0[] = { + -773.5, -552.5, -331.5, -110.5, 110.5, 331.5, 552.5, 773.5, +}; + +static long _vq_quantmap__44cn1_sm_p8_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_sm_p8_0 = { + _vq_quantthresh__44cn1_sm_p8_0, + _vq_quantmap__44cn1_sm_p8_0, + 9, + 9 +}; + +static static_codebook _44cn1_sm_p8_0 = { + 2, 81, + _vq_lengthlist__44cn1_sm_p8_0, + 1, -516186112, 1627103232, 4, 0, + _vq_quantlist__44cn1_sm_p8_0, + NULL, + &_vq_auxt__44cn1_sm_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_sm_p8_1[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44cn1_sm_p8_1[] = { + 1, 4, 4, 6, 6, 8, 8, 9, 9,10,11,11,11, 6, 5, 5, + 7, 7, 8, 8,10,10,10,11,11,11, 6, 5, 5, 7, 7, 8, + 8,10,10,11,12,12,12,14, 7, 7, 7, 8, 9, 9,11,11, + 11,12,11,12,17, 7, 7, 8, 7, 9, 9,11,11,12,12,12, + 12,14,11,11, 8, 8,10,10,11,12,12,13,11,12,14,11, + 11, 8, 8,10,10,11,12,12,13,13,12,14,15,14,10,10, + 10,10,11,12,12,12,12,11,14,13,16,10,10,10, 9,12, + 11,12,12,13,14,14,15,14,14,13,10,10,11,11,12,11, + 13,11,14,12,15,13,14,11,10,12,10,12,12,13,13,13, + 13,14,15,15,12,12,11,11,12,11,13,12,14,14,14,14, + 17,12,12,11,10,13,11,13,13, +}; + +static float _vq_quantthresh__44cn1_sm_p8_1[] = { + -93.5, -76.5, -59.5, -42.5, -25.5, -8.5, 8.5, 25.5, + 42.5, 59.5, 76.5, 93.5, +}; + +static long _vq_quantmap__44cn1_sm_p8_1[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_sm_p8_1 = { + _vq_quantthresh__44cn1_sm_p8_1, + _vq_quantmap__44cn1_sm_p8_1, + 13, + 13 +}; + +static static_codebook _44cn1_sm_p8_1 = { + 2, 169, + _vq_lengthlist__44cn1_sm_p8_1, + 1, -522616832, 1620115456, 4, 0, + _vq_quantlist__44cn1_sm_p8_1, + NULL, + &_vq_auxt__44cn1_sm_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_sm_p8_2[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44cn1_sm_p8_2[] = { + 3, 4, 4, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, + 9,10, 6, 6, 6, 6, 7, 7, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 9,10, 6, 6, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, + 9, 9, 9,10, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, + 9, 9, 9, 9,10,10,10, 7, 7, 7, 8, 8, 8, 9, 9, 9, + 9, 9, 9, 9, 9,10,10,10, 8, 8, 8, 8, 8, 8, 9, 9, + 9, 9, 9, 9, 9, 9,10,10,10, 8, 8, 8, 8, 8, 8, 9, + 9, 9, 9, 9, 9, 9, 9,11,10,11, 8, 8, 8, 8, 8, 8, + 9, 9, 9, 9, 9, 9, 9, 9,10,10,10,11,11, 8, 8, 8, + 8, 9, 9, 9, 9, 9, 9, 9, 9,11,10,11,11,11, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10,11,10,11,11, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10,11,11,10,11,11, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,11,10,11,11, + 11,11,11, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10,11,11, + 11,11,11,11, 9,10,10,10, 9, 9, 9, 9, 9, 9,11,10, + 11,11,11,11,11, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,11, + 11,11,11,11,11,11,10,10, 9, 9, 9, 9, 9, 9, 9, 9, + 10,11,11,11,11,11,11,11,11, 9, 9, 9, 9, 9, 9, 9, + 9, +}; + +static float _vq_quantthresh__44cn1_sm_p8_2[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44cn1_sm_p8_2[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_sm_p8_2 = { + _vq_quantthresh__44cn1_sm_p8_2, + _vq_quantmap__44cn1_sm_p8_2, + 17, + 17 +}; + +static static_codebook _44cn1_sm_p8_2 = { + 2, 289, + _vq_lengthlist__44cn1_sm_p8_2, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44cn1_sm_p8_2, + NULL, + &_vq_auxt__44cn1_sm_p8_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44cn1_sm_short[] = { + 5, 6,12,14,12,14,16,17,18, 4, 2, 5,11, 7,10,12, + 14,15, 9, 4, 5,11, 7,10,13,15,18,15, 6, 7, 5, 6, + 8,11,13,16,11, 5, 6, 5, 5, 6, 9,13,15,12, 5, 7, + 6, 5, 6, 9,12,14,12, 6, 7, 8, 6, 7, 9,12,13,14, + 8, 8, 7, 5, 5, 8,10,12,16, 9, 9, 8, 6, 6, 7, 9, + 9, +}; + +static static_codebook _huff_book__44cn1_sm_short = { + 2, 81, + _huff_lengthlist__44cn1_sm_short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; +/********* End of inlined file: res_books_stereo.h *********/ + +/***** residue backends *********************************************/ + +static vorbis_info_residue0 _residue_44_low={ + 0,-1, -1, 9,-1, + /* 0 1 2 3 4 5 6 7 */ + {0}, + {-1}, + { .5, 1.5, 2.5, 2.5, 4.5, 8.5, 16.5, 32.5}, + { .5, .5, .5, 999., 4.5, 8.5, 16.5, 32.5}, +}; + +static vorbis_info_residue0 _residue_44_mid={ + 0,-1, -1, 10,-1, + /* 0 1 2 3 4 5 6 7 8 */ + {0}, + {-1}, + { .5, 1.5, 1.5, 2.5, 2.5, 4.5, 8.5, 16.5, 32.5}, + { .5, .5, 999., .5, 999., 4.5, 8.5, 16.5, 32.5}, +}; + +static vorbis_info_residue0 _residue_44_high={ + 0,-1, -1, 10,-1, + /* 0 1 2 3 4 5 6 7 8 */ + {0}, + {-1}, + { .5, 1.5, 2.5, 4.5, 8.5, 16.5, 32.5, 71.5,157.5}, + { .5, 1.5, 2.5, 3.5, 4.5, 8.5, 16.5, 71.5,157.5}, +}; + +static static_bookblock _resbook_44s_n1={ + { + {0},{0,0,&_44cn1_s_p1_0},{0,0,&_44cn1_s_p2_0}, + {0,0,&_44cn1_s_p3_0},{0,0,&_44cn1_s_p4_0},{0,0,&_44cn1_s_p5_0}, + {&_44cn1_s_p6_0,&_44cn1_s_p6_1},{&_44cn1_s_p7_0,&_44cn1_s_p7_1}, + {&_44cn1_s_p8_0,&_44cn1_s_p8_1,&_44cn1_s_p8_2} + } +}; +static static_bookblock _resbook_44sm_n1={ + { + {0},{0,0,&_44cn1_sm_p1_0},{0,0,&_44cn1_sm_p2_0}, + {0,0,&_44cn1_sm_p3_0},{0,0,&_44cn1_sm_p4_0},{0,0,&_44cn1_sm_p5_0}, + {&_44cn1_sm_p6_0,&_44cn1_sm_p6_1},{&_44cn1_sm_p7_0,&_44cn1_sm_p7_1}, + {&_44cn1_sm_p8_0,&_44cn1_sm_p8_1,&_44cn1_sm_p8_2} + } +}; + +static static_bookblock _resbook_44s_0={ + { + {0},{0,0,&_44c0_s_p1_0},{0,0,&_44c0_s_p2_0}, + {0,0,&_44c0_s_p3_0},{0,0,&_44c0_s_p4_0},{0,0,&_44c0_s_p5_0}, + {&_44c0_s_p6_0,&_44c0_s_p6_1},{&_44c0_s_p7_0,&_44c0_s_p7_1}, + {&_44c0_s_p8_0,&_44c0_s_p8_1,&_44c0_s_p8_2} + } +}; +static static_bookblock _resbook_44sm_0={ + { + {0},{0,0,&_44c0_sm_p1_0},{0,0,&_44c0_sm_p2_0}, + {0,0,&_44c0_sm_p3_0},{0,0,&_44c0_sm_p4_0},{0,0,&_44c0_sm_p5_0}, + {&_44c0_sm_p6_0,&_44c0_sm_p6_1},{&_44c0_sm_p7_0,&_44c0_sm_p7_1}, + {&_44c0_sm_p8_0,&_44c0_sm_p8_1,&_44c0_sm_p8_2} + } +}; + +static static_bookblock _resbook_44s_1={ + { + {0},{0,0,&_44c1_s_p1_0},{0,0,&_44c1_s_p2_0}, + {0,0,&_44c1_s_p3_0},{0,0,&_44c1_s_p4_0},{0,0,&_44c1_s_p5_0}, + {&_44c1_s_p6_0,&_44c1_s_p6_1},{&_44c1_s_p7_0,&_44c1_s_p7_1}, + {&_44c1_s_p8_0,&_44c1_s_p8_1,&_44c1_s_p8_2} + } +}; +static static_bookblock _resbook_44sm_1={ + { + {0},{0,0,&_44c1_sm_p1_0},{0,0,&_44c1_sm_p2_0}, + {0,0,&_44c1_sm_p3_0},{0,0,&_44c1_sm_p4_0},{0,0,&_44c1_sm_p5_0}, + {&_44c1_sm_p6_0,&_44c1_sm_p6_1},{&_44c1_sm_p7_0,&_44c1_sm_p7_1}, + {&_44c1_sm_p8_0,&_44c1_sm_p8_1,&_44c1_sm_p8_2} + } +}; + +static static_bookblock _resbook_44s_2={ + { + {0},{0,0,&_44c2_s_p1_0},{0,0,&_44c2_s_p2_0},{0,0,&_44c2_s_p3_0}, + {0,0,&_44c2_s_p4_0},{0,0,&_44c2_s_p5_0},{0,0,&_44c2_s_p6_0}, + {&_44c2_s_p7_0,&_44c2_s_p7_1},{&_44c2_s_p8_0,&_44c2_s_p8_1}, + {&_44c2_s_p9_0,&_44c2_s_p9_1,&_44c2_s_p9_2} + } +}; +static static_bookblock _resbook_44s_3={ + { + {0},{0,0,&_44c3_s_p1_0},{0,0,&_44c3_s_p2_0},{0,0,&_44c3_s_p3_0}, + {0,0,&_44c3_s_p4_0},{0,0,&_44c3_s_p5_0},{0,0,&_44c3_s_p6_0}, + {&_44c3_s_p7_0,&_44c3_s_p7_1},{&_44c3_s_p8_0,&_44c3_s_p8_1}, + {&_44c3_s_p9_0,&_44c3_s_p9_1,&_44c3_s_p9_2} + } +}; +static static_bookblock _resbook_44s_4={ + { + {0},{0,0,&_44c4_s_p1_0},{0,0,&_44c4_s_p2_0},{0,0,&_44c4_s_p3_0}, + {0,0,&_44c4_s_p4_0},{0,0,&_44c4_s_p5_0},{0,0,&_44c4_s_p6_0}, + {&_44c4_s_p7_0,&_44c4_s_p7_1},{&_44c4_s_p8_0,&_44c4_s_p8_1}, + {&_44c4_s_p9_0,&_44c4_s_p9_1,&_44c4_s_p9_2} + } +}; +static static_bookblock _resbook_44s_5={ + { + {0},{0,0,&_44c5_s_p1_0},{0,0,&_44c5_s_p2_0},{0,0,&_44c5_s_p3_0}, + {0,0,&_44c5_s_p4_0},{0,0,&_44c5_s_p5_0},{0,0,&_44c5_s_p6_0}, + {&_44c5_s_p7_0,&_44c5_s_p7_1},{&_44c5_s_p8_0,&_44c5_s_p8_1}, + {&_44c5_s_p9_0,&_44c5_s_p9_1,&_44c5_s_p9_2} + } +}; +static static_bookblock _resbook_44s_6={ + { + {0},{0,0,&_44c6_s_p1_0},{0,0,&_44c6_s_p2_0},{0,0,&_44c6_s_p3_0}, + {0,0,&_44c6_s_p4_0}, + {&_44c6_s_p5_0,&_44c6_s_p5_1}, + {&_44c6_s_p6_0,&_44c6_s_p6_1}, + {&_44c6_s_p7_0,&_44c6_s_p7_1}, + {&_44c6_s_p8_0,&_44c6_s_p8_1}, + {&_44c6_s_p9_0,&_44c6_s_p9_1,&_44c6_s_p9_2} + } +}; +static static_bookblock _resbook_44s_7={ + { + {0},{0,0,&_44c7_s_p1_0},{0,0,&_44c7_s_p2_0},{0,0,&_44c7_s_p3_0}, + {0,0,&_44c7_s_p4_0}, + {&_44c7_s_p5_0,&_44c7_s_p5_1}, + {&_44c7_s_p6_0,&_44c7_s_p6_1}, + {&_44c7_s_p7_0,&_44c7_s_p7_1}, + {&_44c7_s_p8_0,&_44c7_s_p8_1}, + {&_44c7_s_p9_0,&_44c7_s_p9_1,&_44c7_s_p9_2} + } +}; +static static_bookblock _resbook_44s_8={ + { + {0},{0,0,&_44c8_s_p1_0},{0,0,&_44c8_s_p2_0},{0,0,&_44c8_s_p3_0}, + {0,0,&_44c8_s_p4_0}, + {&_44c8_s_p5_0,&_44c8_s_p5_1}, + {&_44c8_s_p6_0,&_44c8_s_p6_1}, + {&_44c8_s_p7_0,&_44c8_s_p7_1}, + {&_44c8_s_p8_0,&_44c8_s_p8_1}, + {&_44c8_s_p9_0,&_44c8_s_p9_1,&_44c8_s_p9_2} + } +}; +static static_bookblock _resbook_44s_9={ + { + {0},{0,0,&_44c9_s_p1_0},{0,0,&_44c9_s_p2_0},{0,0,&_44c9_s_p3_0}, + {0,0,&_44c9_s_p4_0}, + {&_44c9_s_p5_0,&_44c9_s_p5_1}, + {&_44c9_s_p6_0,&_44c9_s_p6_1}, + {&_44c9_s_p7_0,&_44c9_s_p7_1}, + {&_44c9_s_p8_0,&_44c9_s_p8_1}, + {&_44c9_s_p9_0,&_44c9_s_p9_1,&_44c9_s_p9_2} + } +}; + +static vorbis_residue_template _res_44s_n1[]={ + {2,0, &_residue_44_low, + &_huff_book__44cn1_s_short,&_huff_book__44cn1_sm_short, + &_resbook_44s_n1,&_resbook_44sm_n1}, + + {2,0, &_residue_44_low, + &_huff_book__44cn1_s_long,&_huff_book__44cn1_sm_long, + &_resbook_44s_n1,&_resbook_44sm_n1} +}; +static vorbis_residue_template _res_44s_0[]={ + {2,0, &_residue_44_low, + &_huff_book__44c0_s_short,&_huff_book__44c0_sm_short, + &_resbook_44s_0,&_resbook_44sm_0}, + + {2,0, &_residue_44_low, + &_huff_book__44c0_s_long,&_huff_book__44c0_sm_long, + &_resbook_44s_0,&_resbook_44sm_0} +}; +static vorbis_residue_template _res_44s_1[]={ + {2,0, &_residue_44_low, + &_huff_book__44c1_s_short,&_huff_book__44c1_sm_short, + &_resbook_44s_1,&_resbook_44sm_1}, + + {2,0, &_residue_44_low, + &_huff_book__44c1_s_long,&_huff_book__44c1_sm_long, + &_resbook_44s_1,&_resbook_44sm_1} +}; + +static vorbis_residue_template _res_44s_2[]={ + {2,0, &_residue_44_mid, + &_huff_book__44c2_s_short,&_huff_book__44c2_s_short, + &_resbook_44s_2,&_resbook_44s_2}, + + {2,0, &_residue_44_mid, + &_huff_book__44c2_s_long,&_huff_book__44c2_s_long, + &_resbook_44s_2,&_resbook_44s_2} +}; +static vorbis_residue_template _res_44s_3[]={ + {2,0, &_residue_44_mid, + &_huff_book__44c3_s_short,&_huff_book__44c3_s_short, + &_resbook_44s_3,&_resbook_44s_3}, + + {2,0, &_residue_44_mid, + &_huff_book__44c3_s_long,&_huff_book__44c3_s_long, + &_resbook_44s_3,&_resbook_44s_3} +}; +static vorbis_residue_template _res_44s_4[]={ + {2,0, &_residue_44_mid, + &_huff_book__44c4_s_short,&_huff_book__44c4_s_short, + &_resbook_44s_4,&_resbook_44s_4}, + + {2,0, &_residue_44_mid, + &_huff_book__44c4_s_long,&_huff_book__44c4_s_long, + &_resbook_44s_4,&_resbook_44s_4} +}; +static vorbis_residue_template _res_44s_5[]={ + {2,0, &_residue_44_mid, + &_huff_book__44c5_s_short,&_huff_book__44c5_s_short, + &_resbook_44s_5,&_resbook_44s_5}, + + {2,0, &_residue_44_mid, + &_huff_book__44c5_s_long,&_huff_book__44c5_s_long, + &_resbook_44s_5,&_resbook_44s_5} +}; +static vorbis_residue_template _res_44s_6[]={ + {2,0, &_residue_44_high, + &_huff_book__44c6_s_short,&_huff_book__44c6_s_short, + &_resbook_44s_6,&_resbook_44s_6}, + + {2,0, &_residue_44_high, + &_huff_book__44c6_s_long,&_huff_book__44c6_s_long, + &_resbook_44s_6,&_resbook_44s_6} +}; +static vorbis_residue_template _res_44s_7[]={ + {2,0, &_residue_44_high, + &_huff_book__44c7_s_short,&_huff_book__44c7_s_short, + &_resbook_44s_7,&_resbook_44s_7}, + + {2,0, &_residue_44_high, + &_huff_book__44c7_s_long,&_huff_book__44c7_s_long, + &_resbook_44s_7,&_resbook_44s_7} +}; +static vorbis_residue_template _res_44s_8[]={ + {2,0, &_residue_44_high, + &_huff_book__44c8_s_short,&_huff_book__44c8_s_short, + &_resbook_44s_8,&_resbook_44s_8}, + + {2,0, &_residue_44_high, + &_huff_book__44c8_s_long,&_huff_book__44c8_s_long, + &_resbook_44s_8,&_resbook_44s_8} +}; +static vorbis_residue_template _res_44s_9[]={ + {2,0, &_residue_44_high, + &_huff_book__44c9_s_short,&_huff_book__44c9_s_short, + &_resbook_44s_9,&_resbook_44s_9}, + + {2,0, &_residue_44_high, + &_huff_book__44c9_s_long,&_huff_book__44c9_s_long, + &_resbook_44s_9,&_resbook_44s_9} +}; + +static vorbis_mapping_template _mapres_template_44_stereo[]={ + { _map_nominal, _res_44s_n1 }, /* -1 */ + { _map_nominal, _res_44s_0 }, /* 0 */ + { _map_nominal, _res_44s_1 }, /* 1 */ + { _map_nominal, _res_44s_2 }, /* 2 */ + { _map_nominal, _res_44s_3 }, /* 3 */ + { _map_nominal, _res_44s_4 }, /* 4 */ + { _map_nominal, _res_44s_5 }, /* 5 */ + { _map_nominal, _res_44s_6 }, /* 6 */ + { _map_nominal, _res_44s_7 }, /* 7 */ + { _map_nominal, _res_44s_8 }, /* 8 */ + { _map_nominal, _res_44s_9 }, /* 9 */ +}; +/********* End of inlined file: residue_44.h *********/ + +/********* Start of inlined file: psych_44.h *********/ +/* preecho trigger settings *****************************************/ + +static vorbis_info_psy_global _psy_global_44[5]={ + + {8, /* lines per eighth octave */ + {20.f,14.f,12.f,12.f,12.f,12.f,12.f}, + {-60.f,-30.f,-40.f,-40.f,-40.f,-40.f,-40.f}, 2,-75.f, + -6.f, + {99.},{{99.},{99.}},{0},{0},{{0.},{0.}} + }, + {8, /* lines per eighth octave */ + {14.f,10.f,10.f,10.f,10.f,10.f,10.f}, + {-40.f,-30.f,-25.f,-25.f,-25.f,-25.f,-25.f}, 2,-80.f, + -6.f, + {99.},{{99.},{99.}},{0},{0},{{0.},{0.}} + }, + {8, /* lines per eighth octave */ + {12.f,10.f,10.f,10.f,10.f,10.f,10.f}, + {-20.f,-20.f,-15.f,-15.f,-15.f,-15.f,-15.f}, 0,-80.f, + -6.f, + {99.},{{99.},{99.}},{0},{0},{{0.},{0.}} + }, + {8, /* lines per eighth octave */ + {10.f,8.f,8.f,8.f,8.f,8.f,8.f}, + {-20.f,-15.f,-12.f,-12.f,-12.f,-12.f,-12.f}, 0,-80.f, + -6.f, + {99.},{{99.},{99.}},{0},{0},{{0.},{0.}} + }, + {8, /* lines per eighth octave */ + {10.f,6.f,6.f,6.f,6.f,6.f,6.f}, + {-15.f,-15.f,-12.f,-12.f,-12.f,-12.f,-12.f}, 0,-85.f, + -6.f, + {99.},{{99.},{99.}},{0},{0},{{0.},{0.}} + }, +}; + +/* noise compander lookups * low, mid, high quality ****************/ +static compandblock _psy_compand_44[6]={ + /* sub-mode Z short */ + {{ + 0, 1, 2, 3, 4, 5, 6, 7, /* 7dB */ + 8, 9,10,11,12,13,14, 15, /* 15dB */ + 16,17,18,19,20,21,22, 23, /* 23dB */ + 24,25,26,27,28,29,30, 31, /* 31dB */ + 32,33,34,35,36,37,38, 39, /* 39dB */ + }}, + /* mode_Z nominal short */ + {{ + 0, 1, 2, 3, 4, 5, 6, 6, /* 7dB */ + 7, 7, 7, 7, 6, 6, 6, 7, /* 15dB */ + 7, 8, 9,10,11,12,13, 14, /* 23dB */ + 15,16,17,17,17,18,18, 19, /* 31dB */ + 19,19,20,21,22,23,24, 25, /* 39dB */ + }}, + /* mode A short */ + {{ + 0, 1, 2, 3, 4, 5, 5, 5, /* 7dB */ + 6, 6, 6, 5, 4, 4, 4, 4, /* 15dB */ + 4, 4, 5, 5, 5, 6, 6, 6, /* 23dB */ + 7, 7, 7, 8, 8, 8, 9, 10, /* 31dB */ + 11,12,13,14,15,16,17, 18, /* 39dB */ + }}, + /* sub-mode Z long */ + {{ + 0, 1, 2, 3, 4, 5, 6, 7, /* 7dB */ + 8, 9,10,11,12,13,14, 15, /* 15dB */ + 16,17,18,19,20,21,22, 23, /* 23dB */ + 24,25,26,27,28,29,30, 31, /* 31dB */ + 32,33,34,35,36,37,38, 39, /* 39dB */ + }}, + /* mode_Z nominal long */ + {{ + 0, 1, 2, 3, 4, 5, 6, 7, /* 7dB */ + 8, 9,10,11,12,12,13, 13, /* 15dB */ + 13,14,14,14,15,15,15, 15, /* 23dB */ + 16,16,17,17,17,18,18, 19, /* 31dB */ + 19,19,20,21,22,23,24, 25, /* 39dB */ + }}, + /* mode A long */ + {{ + 0, 1, 2, 3, 4, 5, 6, 7, /* 7dB */ + 8, 8, 7, 6, 5, 4, 4, 4, /* 15dB */ + 4, 4, 5, 5, 5, 6, 6, 6, /* 23dB */ + 7, 7, 7, 8, 8, 8, 9, 10, /* 31dB */ + 11,12,13,14,15,16,17, 18, /* 39dB */ + }} +}; + +/* tonal masking curve level adjustments *************************/ + +static vp_adjblock _vp_tonemask_adj_longblock[12]={ + + /* 63 125 250 500 1 2 4 8 16 */ + + {{ -3, -8,-13,-15,-10,-10,-10,-10,-10,-10,-10, 0, 0, 0, 0, 0, 0}}, /* -1 */ + +/* {{-15,-15,-15,-15,-10, -8, -4, -2, 0, 0, 0, 10, 0, 0, 0, 0, 0}}, 0 */ + {{ -4,-10,-14,-16,-15,-14,-13,-12,-12,-12,-11, -1, -1, -1, -1, -1, 0}}, /* 0 */ + +/* {{-15,-15,-15,-15,-15,-12,-10, -8, 0, 0, 0, 5, 0, 0, 0, 0, 0}}, 1 */ + {{ -6,-12,-14,-16,-15,-15,-14,-13,-13,-12,-12, -2, -2, -1, -1, -1, 0}}, /* 1 */ + +/* {{-15,-15,-15,-15,-15,-12,-10, -8, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, 2 */ + {{-12,-13,-14,-16,-16,-16,-15,-14,-13,-12,-12, -6, -3, -1, -1, -1, 0}}, /* 2 */ + +/* {{-15,-15,-15,-15,-15,-12,-10, -8, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, 3 */ + {{-15,-15,-15,-16,-16,-16,-16,-14,-13,-13,-13,-10, -4, -2, -1, -1, 0}}, /* 3 */ + +/* {{-15,-15,-15,-15,-15,-12,-10, -8, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, *//* 4 */ + {{-16,-16,-16,-16,-16,-16,-16,-15,-14,-14,-13,-11, -7 -3, -1, -1 , 0}}, /* 4 */ + +/* {{-15,-15,-15,-15,-15,-12,-10, -8, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, 5 */ + {{-16,-16,-16,-16,-16,-16,-16,-15,-14,-14,-13,-11, -7 -3, -1, -1 , 0}}, /* 5 */ + +/* {{-15,-15,-15,-15,-15,-12,-10, -8, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, 6 */ + {{-16,-16,-16,-16,-16,-16,-16,-15,-14,-14,-14,-12, -8, -4, -2, -2, 0}}, /* 6 */ + +/* {{-15,-15,-15,-15,-15,-12,-10, -8, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, 7 */ + {{-16,-16,-16,-16,-16,-16,-16,-15,-14,-14,-14,-12, -9, -4, -2, -2, 0}}, /* 7 */ + +/* {{-15,-15,-15,-15,-15,-12,-10, -8, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, 8 */ + {{-16,-16,-16,-16,-16,-16,-16,-15,-14,-14,-14,-12, -9, -4, -2, -2, 0}}, /* 8 */ + +/* {{-15,-15,-15,-15,-15,-12,-10, -8, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, 9 */ + {{-16,-16,-16,-16,-16,-16,-16,-15,-14,-14,-14,-12, -9, -4, -2, -2, 0}}, /* 9 */ + +/* {{-15,-15,-15,-15,-15,-12,-10, -8, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, 10 */ + {{-16,-16,-16,-16,-16,-16,-16,-15,-14,-14,-14,-12, -9, -4, -2, -2, 0}}, /* 10 */ +}; + +static vp_adjblock _vp_tonemask_adj_otherblock[12]={ + /* 63 125 250 500 1 2 4 8 16 */ + + {{ -3, -8,-13,-15,-10,-10, -9, -9, -9, -9, -9, 1, 1, 1, 1, 1, 1}}, /* -1 */ + +/* {{-20,-20,-20,-20,-14,-12,-10, -8, -4, 0, 0, 10, 0, 0, 0, 0, 0}}, 0 */ + {{ -4,-10,-14,-16,-14,-13,-12,-12,-11,-11,-10, 0, 0, 0, 0, 0, 0}}, /* 0 */ + +/* {{-20,-20,-20,-20,-20,-18,-16,-14,-10, 0, 0, 5, 0, 0, 0, 0, 0}}, 1 */ + {{ -6,-12,-14,-16,-15,-15,-14,-13,-13,-12,-12, -2, -2, -1, 0, 0, 0}}, /* 1 */ + +/* {{-20,-20,-20,-20,-20,-18,-16,-14,-10, 0, 0, 0, 0, 0, 0, 0, 0}}, 2 */ + {{-12,-13,-14,-16,-16,-16,-15,-14,-13,-12,-12, -5, -2, -1, 0, 0, 0}}, /* 2 */ + +/* {{-20,-20,-20,-20,-20,-18,-16,-14,-10, 0, 0, 0, 0, 0, 0, 0, 0}}, 3 */ + {{-15,-15,-15,-16,-16,-16,-16,-14,-13,-13,-13,-10, -4, -2, 0, 0, 0}}, /* 3 */ + +/* {{-20,-20,-20,-20,-20,-18,-16,-14,-10, 0, 0, 0, 0, 0, 0, 0, 0}}, 4 */ + {{-16,-16,-16,-16,-16,-16,-16,-15,-14,-14,-13,-11, -7 -3, -1, -1 , 0}}, /* 4 */ + +/* {{-20,-20,-20,-20,-20,-18,-16,-14,-10, 0, 0, 0, 0, 0, 0, 0, 0}}, 5 */ + {{-16,-16,-16,-16,-16,-16,-16,-15,-14,-14,-13,-11, -7 -3, -1, -1 , 0}}, /* 5 */ + +/* {{-20,-20,-20,-20,-20,-18,-16,-14,-10, 0, 0, 0, 0, 0, 0, 0, 0}}, 6 */ + {{-16,-16,-16,-16,-16,-16,-16,-15,-14,-14,-14,-12, -8, -4, -2, -2, 0}}, /* 6 */ + +/* {{-20,-20,-20,-20,-20,-18,-16,-14,-10, 0, 0, 0, 0, 0, 0, 0, 0}}, 7 */ + {{-16,-16,-16,-16,-16,-16,-16,-15,-14,-14,-14,-12, -9, -4, -2, -2, 0}}, /* 7 */ + +/* {{-20,-20,-20,-20,-20,-18,-16,-14,-10, 0, 0, 0, 0, 0, 0, 0, 0}}, 8 */ + {{-16,-16,-16,-16,-16,-16,-16,-15,-14,-14,-14,-12, -9, -4, -2, -2, 0}}, /* 8 */ + +/* {{-20,-20,-20,-20,-20,-18,-16,-14,-10, 0, 0, 0, 0, 0, 0, 0, 0}}, 9 */ + {{-16,-16,-16,-16,-16,-16,-16,-15,-14,-14,-14,-12, -9, -4, -2, -2, 0}}, /* 9 */ + +/* {{-20,-20,-20,-20,-20,-18,-16,-14,-10, 0, 0, 0, 0, 0, 0, 0, 0}}, 10 */ + {{-16,-16,-16,-16,-16,-16,-16,-15,-14,-14,-14,-12, -9, -4, -2, -2, 0}}, /* 10 */ +}; + +/* noise bias (transition block) */ +static noise3 _psy_noisebias_trans[12]={ + /* 63 125 250 500 1k 2k 4k 8k 16k*/ + /* -1 */ + {{{-10,-10,-10,-10,-10, -4, 0, 0, 4, 8, 8, 8, 8, 10, 12, 14, 20}, + {-30,-30,-30,-30,-26,-20,-16, -8, -6, -6, -2, 2, 2, 3, 6, 6, 15}, + {-30,-30,-30,-30,-30,-24,-20,-14,-10, -6, -8, -8, -6, -6, -6, -4, -2}}}, + /* 0 + {{{-15,-15,-15,-15,-15,-12,-10, -8, 0, 2, 4, 4, 5, 5, 5, 8, 10}, + {-30,-30,-30,-30,-26,-22,-20,-14, -8, -4, 0, 0, 0, 0, 2, 4, 10}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -6, -6, -6, -6, -4, -4, -4, -2}}},*/ + {{{-15,-15,-15,-15,-15,-12, -6, -4, 0, 2, 4, 4, 5, 5, 5, 8, 10}, + {-30,-30,-30,-30,-26,-22,-20,-14, -8, -4, 0, 0, 0, 0, 2, 3, 6}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -6, -6, -6, -6, -4, -4, -4, -2}}}, + /* 1 + {{{-15,-15,-15,-15,-15,-12,-10, -8, 0, 2, 4, 4, 5, 5, 5, 8, 10}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -4, -2, -2, -2, -2, 0, 2, 8}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -8, -8, -8, -8, -6, -6, -6, -4}}},*/ + {{{-15,-15,-15,-15,-15,-12,-10, -8, 0, 2, 4, 4, 5, 5, 5, 8, 10}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -4, -2, -2, -2, -2, 0, 1, 4}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -8, -8, -8, -8, -6, -6, -6, -4}}}, + /* 2 + {{{-15,-15,-15,-15,-15,-12,-10, -8, 0, 2, 2, 2, 4, 4, 5, 6, 10}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -4, -2, -2, -2, -2, 0, 2, 6}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10,-10,-10,-10,-10, -8, -8, -8, -4}}}, */ + {{{-15,-15,-15,-15,-15,-12,-10, -8, 0, 2, 2, 2, 4, 4, 5, 6, 10}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -4, -3, -3, -3, -2, -1, 0, 3}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10,-10,-10,-10,-10, -8, -8, -7, -4}}}, + /* 3 + {{{-15,-15,-15,-15,-15,-12,-10, -8, 0, 2, 2, 2, 4, 4, 4, 5, 8}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -4, -3, -3, -3, -3, -1, 1, 6}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10,-10,-10,-10,-10, -8, -8, -8, -4}}},*/ + {{{-15,-15,-15,-15,-15,-12,-10, -8, 0, 2, 2, 2, 4, 4, 4, 5, 8}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -4, -3, -3, -3, -3, -2, 0, 2}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10,-10,-10,-10,-10, -8, -8, -8, -4}}}, + /* 4 + {{{-20,-20,-20,-20,-20,-18,-14, -8, -1, 1, 1, 1, 2, 3, 3, 4, 7}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -4, -3, -3, -3, -3, -1, 1, 5}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10,-10,-10,-10,-10, -8, -8, -8, -4}}},*/ + {{{-20,-20,-20,-20,-20,-18,-14, -8, -1, 1, 1, 1, 2, 3, 3, 4, 7}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -4, -3, -3, -3, -3, -2, -1, 1}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10,-10,-10,-10,-10, -8, -8, -8, -4}}}, + /* 5 + {{{-24,-24,-24,-24,-20,-18,-14, -8, -1, 1, 1, 1, 2, 3, 3, 4, 7}, + {-32,-32,-32,-32,-28,-24,-22,-16,-12, -6, -4, -4, -4, -4, -2, -1, 2}, + {-34,-34,-34,-34,-30,-24,-24,-18,-14,-12,-12,-12,-12,-10,-10, -9, -5}}}, */ + {{{-24,-24,-24,-24,-20,-18,-14, -8, -1, 1, 1, 1, 2, 3, 3, 4, 7}, + {-32,-32,-32,-32,-28,-24,-22,-16,-12, -6, -4, -4, -4, -4, -3, -1, 0}, + {-34,-34,-34,-34,-30,-24,-24,-18,-14,-12,-12,-12,-12,-10,-10, -9, -5}}}, + /* 6 + {{{-24,-24,-24,-24,-20,-18,-14, -8, -1, 1, 1, 1, 2, 3, 3, 4, 7}, + {-32,-32,-32,-32,-28,-24,-24,-18,-14, -8, -6, -6, -6, -6, -4, -2, 1}, + {-34,-34,-34,-34,-30,-26,-24,-18,-17,-15,-15,-15,-15,-13,-13,-12, -8}}},*/ + {{{-24,-24,-24,-24,-20,-18,-14, -8, -1, 1, 1, 1, 2, 3, 3, 4, 7}, + {-32,-32,-32,-32,-28,-24,-24,-18,-14, -8, -6, -6, -6, -6, -5, -2, 0}, + {-34,-34,-34,-34,-30,-26,-26,-24,-22,-19,-19,-19,-19,-18,-17,-16,-12}}}, + /* 7 + {{{-24,-24,-24,-24,-20,-18,-14, -8, -1, 1, 1, 1, 2, 3, 3, 4, 7}, + {-32,-32,-32,-32,-28,-24,-24,-18,-14,-12,-10, -8, -8, -8, -6, -4, 0}, + {-34,-34,-34,-34,-30,-26,-26,-24,-22,-19,-19,-19,-19,-18,-17,-16,-12}}},*/ + {{{-24,-24,-24,-24,-20,-18,-14, -8, -1, 1, 1, 1, 2, 3, 3, 4, 7}, + {-32,-32,-32,-32,-28,-24,-24,-24,-18,-14,-12,-10,-10,-10, -8, -6, -2}, + {-34,-34,-34,-34,-30,-26,-26,-26,-24,-24,-24,-24,-24,-24,-24,-20,-16}}}, + /* 8 + {{{-24,-24,-24,-24,-22,-20,-15,-10, -8, -2, 0, 0, 0, 1, 2, 3, 7}, + {-36,-36,-36,-36,-30,-30,-30,-24,-18,-14,-12,-10,-10,-10, -8, -6, -2}, + {-36,-36,-36,-36,-34,-30,-28,-26,-24,-24,-24,-24,-24,-24,-24,-20,-16}}},*/ + {{{-24,-24,-24,-24,-22,-20,-15,-10, -8, -2, 0, 0, 0, 1, 2, 3, 7}, + {-36,-36,-36,-36,-30,-30,-30,-24,-20,-16,-16,-16,-16,-14,-12,-10, -7}, + {-36,-36,-36,-36,-34,-30,-28,-26,-24,-30,-30,-30,-30,-30,-30,-24,-20}}}, + /* 9 + {{{-28,-28,-28,-28,-28,-28,-28,-20,-14, -8, -4, -4, -4, -4, -4, -2, 2}, + {-36,-36,-36,-36,-34,-32,-32,-28,-20,-16,-16,-16,-16,-14,-12,-10, -7}, + {-40,-40,-40,-40,-40,-40,-40,-32,-30,-30,-30,-30,-30,-30,-30,-24,-20}}},*/ + {{{-28,-28,-28,-28,-28,-28,-28,-20,-14, -8, -4, -4, -4, -4, -4, -2, 2}, + {-38,-38,-38,-38,-36,-34,-34,-30,-24,-20,-20,-20,-20,-18,-16,-12,-10}, + {-40,-40,-40,-40,-40,-40,-40,-38,-35,-35,-35,-35,-35,-35,-35,-35,-30}}}, + /* 10 */ + {{{-30,-30,-30,-30,-30,-30,-30,-28,-20,-14,-14,-14,-14,-14,-14,-12,-10}, + {-40,-40,-40,-40,-40,-40,-40,-40,-35,-30,-30,-30,-30,-30,-30,-30,-20}, + {-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40}}}, +}; + +/* noise bias (long block) */ +static noise3 _psy_noisebias_long[12]={ + /*63 125 250 500 1k 2k 4k 8k 16k*/ + /* -1 */ + {{{-10,-10,-10,-10,-10, -4, 0, 0, 0, 6, 6, 6, 6, 10, 10, 12, 20}, + {-20,-20,-20,-20,-20,-20,-10, -2, 0, 0, 0, 0, 0, 2, 4, 6, 15}, + {-20,-20,-20,-20,-20,-20,-20,-10, -6, -6, -6, -6, -6, -4, -4, -4, -2}}}, + + /* 0 */ + /* {{{-10,-10,-10,-10,-10,-10, -8, 2, 2, 2, 4, 4, 5, 5, 5, 8, 10}, + {-20,-20,-20,-20,-20,-20,-20,-14, -6, 0, 0, 0, 0, 0, 2, 4, 10}, + {-20,-20,-20,-20,-20,-20,-20,-14, -8, -6, -6, -6, -6, -4, -4, -4, -2}}},*/ + {{{-10,-10,-10,-10,-10,-10, -8, 2, 2, 2, 4, 4, 5, 5, 5, 8, 10}, + {-20,-20,-20,-20,-20,-20,-20,-14, -6, 0, 0, 0, 0, 0, 2, 3, 6}, + {-20,-20,-20,-20,-20,-20,-20,-14, -8, -6, -6, -6, -6, -4, -4, -4, -2}}}, + /* 1 */ + /* {{{-10,-10,-10,-10,-10,-10, -8, -4, 0, 2, 4, 4, 5, 5, 5, 8, 10}, + {-20,-20,-20,-20,-20,-20,-20,-14,-10, -4, -2, -2, -2, -2, 0, 2, 8}, + {-20,-20,-20,-20,-20,-20,-20,-14,-10, -8, -8, -8, -8, -6, -6, -6, -4}}},*/ + {{{-10,-10,-10,-10,-10,-10, -8, -4, 0, 2, 4, 4, 5, 5, 5, 8, 10}, + {-20,-20,-20,-20,-20,-20,-20,-14,-10, -4, -2, -2, -2, -2, 0, 1, 4}, + {-20,-20,-20,-20,-20,-20,-20,-14,-10, -8, -8, -8, -8, -6, -6, -6, -4}}}, + /* 2 */ + /* {{{-10,-10,-10,-10,-10,-10,-10, -8, 0, 2, 2, 2, 4, 4, 5, 6, 10}, + {-20,-20,-20,-20,-20,-20,-20,-14,-10, -4, -2, -2, -2, -2, 0, 2, 6}, + {-20,-20,-20,-20,-20,-20,-20,-14,-10,-10,-10,-10,-10, -8, -8, -8, -4}}},*/ + {{{-10,-10,-10,-10,-10,-10,-10, -8, 0, 2, 2, 2, 4, 4, 5, 6, 10}, + {-20,-20,-20,-20,-20,-20,-20,-14,-10, -4, -3, -3, -3, -2, -1, 0, 3}, + {-20,-20,-20,-20,-20,-20,-20,-14,-10,-10,-10,-10,-10, -8, -8, -8, -4}}}, + /* 3 */ + /* {{{-10,-10,-10,-10,-10,-10,-10, -8, 0, 2, 2, 2, 4, 4, 4, 5, 8}, + {-20,-20,-20,-20,-20,-20,-20,-14,-10, -4, -3, -3, -3, -3, -1, 1, 6}, + {-20,-20,-20,-20,-20,-20,-20,-14,-10,-10,-10,-10,-10, -8, -8, -8, -4}}},*/ + {{{-10,-10,-10,-10,-10,-10,-10, -8, 0, 2, 2, 2, 4, 4, 4, 5, 8}, + {-20,-20,-20,-20,-20,-20,-20,-14,-10, -4, -3, -3, -3, -3, -2, 0, 2}, + {-20,-20,-20,-20,-20,-20,-20,-14,-10,-10,-10,-10,-10, -8, -8, -8, -5}}}, + /* 4 */ + /* {{{-15,-15,-15,-15,-15,-15,-15,-10, -4, 1, 1, 1, 2, 3, 3, 4, 7}, + {-20,-20,-20,-20,-20,-20,-20,-14,-10, -4, -3, -3, -3, -3, -1, 1, 5}, + {-20,-20,-20,-20,-20,-20,-20,-14,-10,-10,-10,-10,-10, -8, -8, -8, -4}}},*/ + {{{-15,-15,-15,-15,-15,-15,-15,-10, -4, 1, 1, 1, 2, 3, 3, 4, 7}, + {-20,-20,-20,-20,-20,-20,-20,-14,-10, -4, -3, -3, -3, -3, -2, -1, 1}, + {-20,-20,-20,-20,-20,-20,-20,-14,-10,-10,-10,-10,-10, -8, -8, -8, -7}}}, + /* 5 */ + /* {{{-15,-15,-15,-15,-15,-15,-15,-10, -4, 1, 1, 1, 2, 3, 3, 4, 7}, + {-22,-22,-22,-22,-22,-22,-22,-16,-12, -6, -4, -4, -4, -4, -2, -1, 2}, + {-24,-24,-24,-24,-24,-24,-24,-18,-14,-12,-12,-12,-12,-10,-10, -9, -5}}},*/ + {{{-15,-15,-15,-15,-15,-15,-15,-10, -4, 1, 1, 1, 2, 3, 3, 4, 7}, + {-22,-22,-22,-22,-22,-22,-22,-16,-12, -6, -4, -4, -4, -4, -3, -1, 0}, + {-24,-24,-24,-24,-24,-24,-24,-18,-14,-12,-12,-12,-12,-10,-10, -9, -8}}}, + /* 6 */ + /* {{{-15,-15,-15,-15,-15,-15,-15,-10, -4, 1, 1, 1, 2, 3, 3, 4, 7}, + {-24,-24,-24,-24,-24,-24,-24,-18,-14, -8, -6, -6, -6, -6, -4, -2, 1}, + {-26,-26,-26,-26,-26,-26,-26,-18,-16,-15,-15,-15,-15,-13,-13,-12, -8}}},*/ + {{{-15,-15,-15,-15,-15,-15,-15,-10, -4, 1, 1, 1, 2, 3, 3, 4, 7}, + {-24,-24,-24,-24,-24,-24,-24,-18,-14, -8, -6, -6, -6, -6, -5, -2, 0}, + {-26,-26,-26,-26,-26,-26,-26,-18,-16,-15,-15,-15,-15,-13,-13,-12,-10}}}, + /* 7 */ + {{{-15,-15,-15,-15,-15,-15,-15,-10, -4, 1, 1, 1, 2, 3, 3, 4, 7}, + {-24,-24,-24,-24,-24,-24,-24,-18,-14,-10, -8, -8, -8, -8, -6, -4, 0}, + {-26,-26,-26,-26,-26,-26,-26,-22,-20,-19,-19,-19,-19,-18,-17,-16,-12}}}, + /* 8 */ + {{{-15,-15,-15,-15,-15,-15,-15,-10, -4, 0, 0, 0, 0, 1, 2, 3, 7}, + {-26,-26,-26,-26,-26,-26,-26,-20,-16,-12,-10,-10,-10,-10, -8, -6, -2}, + {-28,-28,-28,-28,-28,-28,-28,-26,-24,-24,-24,-24,-24,-24,-24,-20,-16}}}, + /* 9 */ + {{{-22,-22,-22,-22,-22,-22,-22,-18,-14, -8, -4, -4, -4, -4, -4, -2, 2}, + {-26,-26,-26,-26,-26,-26,-26,-22,-18,-16,-16,-16,-16,-14,-12,-10, -7}, + {-30,-30,-30,-30,-30,-30,-30,-30,-30,-30,-30,-30,-30,-30,-30,-24,-20}}}, + /* 10 */ + {{{-24,-24,-24,-24,-24,-24,-24,-24,-24,-18,-14,-14,-14,-14,-14,-12,-10}, + {-30,-30,-30,-30,-30,-30,-30,-30,-30,-30,-30,-30,-30,-30,-30,-30,-20}, + {-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40}}}, +}; + +/* noise bias (impulse block) */ +static noise3 _psy_noisebias_impulse[12]={ + /* 63 125 250 500 1k 2k 4k 8k 16k*/ + /* -1 */ + {{{-10,-10,-10,-10,-10, -4, 0, 0, 4, 8, 8, 8, 8, 10, 12, 14, 20}, + {-30,-30,-30,-30,-26,-20,-16, -8, -6, -6, -2, 2, 2, 3, 6, 6, 15}, + {-30,-30,-30,-30,-30,-24,-20,-14,-10, -6, -8, -8, -6, -6, -6, -4, -2}}}, + + /* 0 */ + /* {{{-10,-10,-10,-10,-10, -4, 0, 0, 4, 4, 8, 8, 8, 10, 12, 14, 20}, + {-30,-30,-30,-30,-26,-22,-20,-14, -6, -2, 0, 0, 0, 0, 2, 4, 10}, + {-30,-30,-30,-30,-30,-24,-20,-14,-10, -6, -8, -8, -6, -6, -6, -4, -2}}},*/ + {{{-10,-10,-10,-10,-10, -4, 0, 0, 4, 4, 8, 8, 8, 10, 12, 14, 20}, + {-30,-30,-30,-30,-26,-22,-20,-14, -6, -2, 0, 0, 0, 0, 2, 3, 6}, + {-30,-30,-30,-30,-30,-24,-20,-14,-10, -6, -8, -8, -6, -6, -6, -4, -2}}}, + /* 1 */ + {{{-12,-12,-12,-12,-12, -8, -6, -4, 0, 4, 4, 4, 4, 10, 12, 14, 20}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -6, -4, -4, -2, -2, -2, -2, 2}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -8,-10,-10, -8, -8, -8, -6, -4}}}, + /* 2 */ + {{{-14,-14,-14,-14,-14,-10, -8, -6, -2, 2, 2, 2, 2, 8, 10, 10, 16}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -6, -6, -6, -4, -4, -4, -2, 0}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10,-10,-10,-10,-10,-10,-10, -8, -4}}}, + /* 3 */ + {{{-14,-14,-14,-14,-14,-10, -8, -6, -2, 2, 2, 2, 2, 6, 8, 8, 14}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -6, -6, -6, -4, -4, -4, -2, 0}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10,-10,-10,-10,-10,-10,-10, -8, -4}}}, + /* 4 */ + {{{-16,-16,-16,-16,-16,-12,-10, -6, -2, 0, 0, 0, 0, 4, 6, 6, 12}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -6, -6, -6, -4, -4, -4, -2, 0}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10,-10,-10,-10,-10,-10,-10, -8, -4}}}, + /* 5 */ + {{{-20,-20,-20,-20,-20,-18,-14,-10, -4, 0, 0, 0, 0, 4, 4, 6, 11}, + {-32,-32,-32,-32,-28,-24,-22,-16,-10, -6, -8, -8, -6, -6, -6, -4, -2}, + {-34,-34,-34,-34,-30,-26,-24,-18,-14,-12,-12,-12,-12,-12,-10, -9, -5}}}, + /* 6 + {{{-20,-20,-20,-20,-20,-18,-14,-10, -4, 0, 0, 0, 0, 4, 4, 6, 11}, + {-34,-34,-34,-34,-30,-30,-24,-20,-12,-12,-14,-14,-10, -9, -8, -6, -4}, + {-34,-34,-34,-34,-34,-30,-26,-20,-16,-15,-15,-15,-15,-15,-13,-12, -8}}},*/ + {{{-20,-20,-20,-20,-20,-18,-14,-10, -4, 0, 0, 0, 0, 4, 4, 6, 11}, + {-34,-34,-34,-34,-30,-30,-30,-24,-16,-16,-16,-16,-16,-16,-14,-14,-12}, + {-36,-36,-36,-36,-36,-34,-28,-24,-20,-20,-20,-20,-20,-20,-20,-18,-16}}}, + /* 7 */ + /* {{{-22,-22,-22,-22,-22,-20,-14,-10, -6, 0, 0, 0, 0, 4, 4, 6, 11}, + {-34,-34,-34,-34,-30,-30,-24,-20,-14,-14,-16,-16,-14,-12,-10,-10,-10}, + {-34,-34,-34,-34,-32,-32,-30,-24,-20,-19,-19,-19,-19,-19,-17,-16,-12}}},*/ + {{{-22,-22,-22,-22,-22,-20,-14,-10, -6, 0, 0, 0, 0, 4, 4, 6, 11}, + {-34,-34,-34,-34,-30,-30,-30,-30,-26,-26,-26,-26,-26,-26,-26,-24,-22}, + {-40,-40,-40,-40,-40,-40,-40,-32,-30,-30,-30,-30,-30,-30,-30,-30,-24}}}, + /* 8 */ + /* {{{-24,-24,-24,-24,-24,-22,-14,-10, -6, -1, -1, -1, -1, 3, 3, 5, 10}, + {-34,-34,-34,-34,-30,-30,-30,-24,-20,-20,-20,-20,-20,-18,-16,-16,-14}, + {-36,-36,-36,-36,-36,-34,-28,-24,-24,-24,-24,-24,-24,-24,-24,-20,-16}}},*/ + {{{-24,-24,-24,-24,-24,-22,-14,-10, -6, -1, -1, -1, -1, 3, 3, 5, 10}, + {-34,-34,-34,-34,-34,-32,-32,-30,-26,-26,-26,-26,-26,-26,-26,-26,-24}, + {-40,-40,-40,-40,-40,-40,-40,-32,-30,-30,-30,-30,-30,-30,-30,-30,-24}}}, + /* 9 */ + /* {{{-28,-28,-28,-28,-28,-28,-28,-20,-14, -8, -4, -4, -4, -4, -4, -2, 2}, + {-36,-36,-36,-36,-34,-32,-32,-30,-26,-26,-26,-26,-26,-22,-20,-20,-18}, + {-40,-40,-40,-40,-40,-40,-40,-32,-30,-30,-30,-30,-30,-30,-30,-24,-20}}},*/ + {{{-28,-28,-28,-28,-28,-28,-28,-20,-14, -8, -4, -4, -4, -4, -4, -2, 2}, + {-36,-36,-36,-36,-34,-32,-32,-30,-26,-26,-26,-26,-26,-26,-26,-26,-26}, + {-40,-40,-40,-40,-40,-40,-40,-32,-30,-30,-30,-30,-30,-30,-30,-24,-20}}}, + /* 10 */ + {{{-30,-30,-30,-30,-30,-26,-24,-24,-24,-20,-16,-16,-16,-16,-16,-14,-12}, + {-40,-40,-40,-40,-40,-40,-40,-40,-35,-30,-30,-30,-30,-30,-30,-30,-26}, + {-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40}}}, +}; + +/* noise bias (padding block) */ +static noise3 _psy_noisebias_padding[12]={ + /* 63 125 250 500 1k 2k 4k 8k 16k*/ + + /* -1 */ + {{{-10,-10,-10,-10,-10, -4, 0, 0, 4, 8, 8, 8, 8, 10, 12, 14, 20}, + {-30,-30,-30,-30,-26,-20,-16, -8, -6, -6, -2, 2, 2, 3, 6, 6, 15}, + {-30,-30,-30,-30,-30,-24,-20,-14,-10, -6, -8, -8, -6, -6, -6, -4, -2}}}, + + /* 0 */ + {{{-10,-10,-10,-10,-10, -4, 0, 0, 4, 8, 8, 8, 8, 10, 12, 14, 20}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -4, -2, 2, 3, 6, 6, 8, 10}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -4, -4, -4, -4, -4, -2, 0, 2}}}, + /* 1 */ + {{{-12,-12,-12,-12,-12, -8, -6, -4, 0, 4, 4, 4, 4, 10, 12, 14, 20}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -4, 0, 0, 0, 2, 2, 4, 8}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -6, -6, -6, -6, -6, -4, -2, 0}}}, + /* 2 */ + /* {{{-14,-14,-14,-14,-14,-10, -8, -6, -2, 2, 2, 2, 2, 8, 10, 10, 16}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -4, 0, 0, 0, 2, 2, 4, 8}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -8, -8, -8, -8, -8, -6, -4, -2}}},*/ + {{{-14,-14,-14,-14,-14,-10, -8, -6, -2, 2, 2, 2, 2, 8, 10, 10, 16}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -6, -1, -1, -1, 0, 0, 2, 6}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -8, -8, -8, -8, -8, -6, -4, -2}}}, + /* 3 */ + {{{-14,-14,-14,-14,-14,-10, -8, -6, -2, 2, 2, 2, 2, 6, 8, 8, 14}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -6, -1, -1, -1, 0, 0, 2, 6}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -8, -8, -8, -8, -8, -6, -4, -2}}}, + /* 4 */ + {{{-16,-16,-16,-16,-16,-12,-10, -6, -2, 0, 0, 0, 0, 4, 6, 6, 12}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -6, -1, -1, -1, -1, 0, 2, 6}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -8, -8, -8, -8, -8, -6, -4, -2}}}, + /* 5 */ + {{{-20,-20,-20,-20,-20,-18,-14,-10, -4, 0, 0, 0, 0, 4, 6, 6, 12}, + {-32,-32,-32,-32,-28,-24,-22,-16,-12, -6, -3, -3, -3, -3, -2, 0, 4}, + {-34,-34,-34,-34,-30,-26,-24,-18,-14,-10,-10,-10,-10,-10, -8, -5, -3}}}, + /* 6 */ + {{{-20,-20,-20,-20,-20,-18,-14,-10, -4, 0, 0, 0, 0, 4, 6, 6, 12}, + {-34,-34,-34,-34,-30,-30,-24,-20,-14, -8, -4, -4, -4, -4, -3, -1, 4}, + {-34,-34,-34,-34,-34,-30,-26,-20,-16,-13,-13,-13,-13,-13,-11, -8, -6}}}, + /* 7 */ + {{{-20,-20,-20,-20,-20,-18,-14,-10, -4, 0, 0, 0, 0, 4, 6, 6, 12}, + {-34,-34,-34,-34,-30,-30,-30,-24,-16,-10, -8, -6, -6, -6, -5, -3, 1}, + {-34,-34,-34,-34,-32,-32,-28,-22,-18,-16,-16,-16,-16,-16,-14,-12,-10}}}, + /* 8 */ + {{{-22,-22,-22,-22,-22,-20,-14,-10, -4, 0, 0, 0, 0, 3, 5, 5, 11}, + {-34,-34,-34,-34,-30,-30,-30,-24,-16,-12,-10, -8, -8, -8, -7, -5, -2}, + {-36,-36,-36,-36,-36,-34,-28,-22,-20,-20,-20,-20,-20,-20,-20,-16,-14}}}, + /* 9 */ + {{{-28,-28,-28,-28,-28,-28,-28,-20,-14, -8, -2, -2, -2, -2, 0, 2, 6}, + {-36,-36,-36,-36,-34,-32,-32,-24,-16,-12,-12,-12,-12,-12,-10, -8, -5}, + {-40,-40,-40,-40,-40,-40,-40,-32,-26,-24,-24,-24,-24,-24,-24,-20,-18}}}, + /* 10 */ + {{{-30,-30,-30,-30,-30,-26,-24,-24,-24,-20,-12,-12,-12,-12,-12,-10, -8}, + {-40,-40,-40,-40,-40,-40,-40,-40,-35,-30,-25,-25,-25,-25,-25,-25,-15}, + {-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40}}}, +}; + +static noiseguard _psy_noiseguards_44[4]={ + {3,3,15}, + {3,3,15}, + {10,10,100}, + {10,10,100}, +}; + +static int _psy_tone_suppress[12]={ + -20,-20,-20,-20,-20,-24,-30,-40,-40,-45,-45,-45, +}; +static int _psy_tone_0dB[12]={ + 90,90,95,95,95,95,105,105,105,105,105,105, +}; +static int _psy_noise_suppress[12]={ + -20,-20,-24,-24,-24,-24,-30,-40,-40,-45,-45,-45, +}; + +static vorbis_info_psy _psy_info_template={ + /* blockflag */ + -1, + /* ath_adjatt, ath_maxatt */ + -140.,-140., + /* tonemask att boost/decay,suppr,curves */ + {0.f,0.f,0.f}, 0.,0., -40.f, {0.}, + + /*noisemaskp,supp, low/high window, low/hi guard, minimum */ + 1, -0.f, .5f, .5f, 0,0,0, + /* noiseoffset*3, noisecompand, max_curve_dB */ + {{-1},{-1},{-1}},{-1},105.f, + /* noise normalization - channel_p, point_p, start, partition, thresh. */ + 0,0,-1,-1,0., +}; + +/* ath ****************/ + +static int _psy_ath_floater[12]={ + -100,-100,-100,-100,-100,-100,-105,-105,-105,-105,-110,-120, +}; +static int _psy_ath_abs[12]={ + -130,-130,-130,-130,-140,-140,-140,-140,-140,-140,-140,-150, +}; + +/* stereo setup. These don't map directly to quality level, there's + an additional indirection as several of the below may be used in a + single bitmanaged stream + +****************/ + +/* various stereo possibilities */ + +/* stereo mode by base quality level */ +static adj_stereo _psy_stereo_modes_44[12]={ + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 -1 */ + {{ 4, 4, 4, 4, 4, 4, 4, 3, 2, 2, 1, 0, 0, 0, 0}, + { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 5, 4, 3}, + { 1, 2, 3, 4, 4, 4, 4, 4, 4, 5, 6, 7, 8, 8, 8}, + { 12,12.5, 13,13.5, 14,14.5, 15, 99, 99, 99, 99, 99, 99, 99, 99}}, + +/* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 0 */ +/*{{ 4, 4, 4, 4, 4, 4, 4, 3, 2, 2, 1, 0, 0, 0, 0}, + { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 5, 4, 3}, + { 1, 2, 3, 4, 5, 5, 6, 6, 6, 6, 6, 7, 8, 8, 8}, + { 12,12.5, 13,13.5, 14,14.5, 15, 99, 99, 99, 99, 99, 99, 99, 99}},*/ + {{ 4, 4, 4, 4, 4, 4, 4, 3, 2, 1, 0, 0, 0, 0, 0}, + { 8, 8, 8, 8, 6, 6, 5, 5, 5, 5, 5, 5, 5, 4, 3}, + { 1, 2, 3, 4, 4, 5, 6, 6, 6, 6, 6, 8, 8, 8, 8}, + { 12,12.5, 13,13.5, 14,14.5, 15, 99, 99, 99, 99, 99, 99, 99, 99}}, + + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 1 */ + {{ 3, 3, 3, 3, 3, 3, 3, 3, 2, 1, 0, 0, 0, 0, 0}, + { 8, 8, 8, 8, 6, 6, 5, 5, 5, 5, 5, 5, 5, 4, 3}, + { 1, 2, 3, 4, 4, 5, 6, 6, 6, 6, 6, 8, 8, 8, 8}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}}, + + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 2 */ + /* {{ 3, 3, 3, 3, 3, 3, 2, 2, 2, 1, 0, 0, 0, 0, 0}, + { 8, 8, 8, 6, 5, 5, 5, 5, 5, 5, 5, 4, 3, 2, 1}, + { 3, 4, 4, 4, 5, 6, 6, 6, 6, 6, 6, 8, 8, 8, 8}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}}, */ + {{ 3, 3, 3, 3, 3, 3, 3, 2, 1, 1, 0, 0, 0, 0, 0}, + { 8, 8, 6, 6, 5, 5, 4, 4, 4, 4, 4, 4, 3, 2, 1}, + { 3, 4, 4, 5, 5, 6, 6, 6, 6, 6, 6, 8, 8, 8, 8}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}}, + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 3 */ + {{ 2, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, + { 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 2, 1}, + { 4, 4, 5, 6, 6, 6, 6, 6, 8, 8, 10, 10, 10, 10, 10}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}}, + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 4 */ + {{ 2, 2, 2, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 2, 1, 0}, + { 6, 6, 6, 8, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}}, + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 5 */ + /* {{ 2, 2, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0}, + { 6, 6, 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}},*/ + {{ 2, 2, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0}, + { 6, 7, 8, 8, 8, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}}, + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 6 */ + /* {{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 3, 3, 3, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}}, */ + {{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 3, 3, 3, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 8, 8, 8, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}}, + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 7 */ + /* {{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 3, 3, 3, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}},*/ + {{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 3, 3, 3, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 8, 8, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}}, + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 8 */ + /* {{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 8, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}},*/ + {{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 8, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}}, + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 9 */ + {{ 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}, + { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}}, + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 10 */ + {{ 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}, + { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}}, +}; + +/* tone master attenuation by base quality mode and bitrate tweak */ +static att3 _psy_tone_masteratt_44[12]={ + {{ 35, 21, 9}, 0, 0}, /* -1 */ + {{ 30, 20, 8}, -2, 1.25}, /* 0 */ + /* {{ 25, 14, 4}, 0, 0}, *//* 1 */ + {{ 25, 12, 2}, 0, 0}, /* 1 */ + /* {{ 20, 10, -2}, 0, 0}, *//* 2 */ + {{ 20, 9, -3}, 0, 0}, /* 2 */ + {{ 20, 9, -4}, 0, 0}, /* 3 */ + {{ 20, 9, -4}, 0, 0}, /* 4 */ + {{ 20, 6, -6}, 0, 0}, /* 5 */ + {{ 20, 3, -10}, 0, 0}, /* 6 */ + {{ 18, 1, -14}, 0, 0}, /* 7 */ + {{ 18, 0, -16}, 0, 0}, /* 8 */ + {{ 18, -2, -16}, 0, 0}, /* 9 */ + {{ 12, -2, -20}, 0, 0}, /* 10 */ +}; + +/* lowpass by mode **************/ +static double _psy_lowpass_44[12]={ + /* 15.1,15.8,16.5,17.9,20.5,48.,999.,999.,999.,999.,999. */ + 13.9,15.1,15.8,16.5,17.2,18.9,20.1,48.,999.,999.,999.,999. +}; + +/* noise normalization **********/ + +static int _noise_start_short_44[11]={ + /* 16,16,16,16,32,32,9999,9999,9999,9999 */ + 32,16,16,16,32,9999,9999,9999,9999,9999,9999 +}; +static int _noise_start_long_44[11]={ + /* 128,128,128,256,512,512,9999,9999,9999,9999 */ + 256,128,128,256,512,9999,9999,9999,9999,9999,9999 +}; + +static int _noise_part_short_44[11]={ + 8,8,8,8,8,8,8,8,8,8,8 +}; +static int _noise_part_long_44[11]={ + 32,32,32,32,32,32,32,32,32,32,32 +}; + +static double _noise_thresh_44[11]={ + /* .2,.2,.3,.4,.5,.5,9999.,9999.,9999.,9999., */ + .2,.2,.2,.4,.6,9999.,9999.,9999.,9999.,9999.,9999., +}; + +static double _noise_thresh_5only[2]={ + .5,.5, +}; +/********* End of inlined file: psych_44.h *********/ + +static double rate_mapping_44_stereo[12]={ + 22500.,32000.,40000.,48000.,56000.,64000., + 80000.,96000.,112000.,128000.,160000.,250001. +}; + +static double quality_mapping_44[12]={ + -.1,.0,.1,.2,.3,.4,.5,.6,.7,.8,.9,1.0 +}; + +static int blocksize_short_44[11]={ + 512,256,256,256,256,256,256,256,256,256,256 +}; +static int blocksize_long_44[11]={ + 4096,2048,2048,2048,2048,2048,2048,2048,2048,2048,2048 +}; + +static double _psy_compand_short_mapping[12]={ + 0.5, 1., 1., 1.3, 1.6, 2., 2., 2., 2., 2., 2., 2. +}; +static double _psy_compand_long_mapping[12]={ + 3.5, 4., 4., 4.3, 4.6, 5., 5., 5., 5., 5., 5., 5. +}; + +static double _global_mapping_44[12]={ + /* 1., 1., 1.5, 2., 2., 2.5, 2.7, 3.0, 3.5, 4., 4. */ + 0., 1., 1., 1.5, 2., 2., 2.5, 2.7, 3.0, 3.7, 4., 4. +}; + +static int _floor_short_mapping_44[11]={ + 1,0,0,2,2,4,5,5,5,5,5 +}; +static int _floor_long_mapping_44[11]={ + 8,7,7,7,7,7,7,7,7,7,7 +}; + +ve_setup_data_template ve_setup_44_stereo={ + 11, + rate_mapping_44_stereo, + quality_mapping_44, + 2, + 40000, + 50000, + + blocksize_short_44, + blocksize_long_44, + + _psy_tone_masteratt_44, + _psy_tone_0dB, + _psy_tone_suppress, + + _vp_tonemask_adj_otherblock, + _vp_tonemask_adj_longblock, + _vp_tonemask_adj_otherblock, + + _psy_noiseguards_44, + _psy_noisebias_impulse, + _psy_noisebias_padding, + _psy_noisebias_trans, + _psy_noisebias_long, + _psy_noise_suppress, + + _psy_compand_44, + _psy_compand_short_mapping, + _psy_compand_long_mapping, + + {_noise_start_short_44,_noise_start_long_44}, + {_noise_part_short_44,_noise_part_long_44}, + _noise_thresh_44, + + _psy_ath_floater, + _psy_ath_abs, + + _psy_lowpass_44, + + _psy_global_44, + _global_mapping_44, + _psy_stereo_modes_44, + + _floor_books, + _floor, + _floor_short_mapping_44, + _floor_long_mapping_44, + + _mapres_template_44_stereo +}; +/********* End of inlined file: setup_44.h *********/ + +/********* Start of inlined file: setup_44u.h *********/ + +/********* Start of inlined file: residue_44u.h *********/ + +/********* Start of inlined file: res_books_uncoupled.h *********/ + +static long _vq_quantlist__16u0__p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__16u0__p1_0[] = { + 1, 4, 4, 5, 7, 7, 5, 7, 8, 5, 8, 8, 8,10,10, 8, + 10,11, 5, 8, 8, 8,10,10, 8,10,10, 4, 9, 9, 9,12, + 11, 8,11,11, 8,12,11,10,12,14,10,13,13, 7,11,11, + 10,14,12,11,14,14, 4, 9, 9, 8,11,11, 9,11,12, 7, + 11,11,10,13,14,10,12,14, 8,11,12,10,14,14,10,13, + 12, +}; + +static float _vq_quantthresh__16u0__p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__16u0__p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__16u0__p1_0 = { + _vq_quantthresh__16u0__p1_0, + _vq_quantmap__16u0__p1_0, + 3, + 3 +}; + +static static_codebook _16u0__p1_0 = { + 4, 81, + _vq_lengthlist__16u0__p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__16u0__p1_0, + NULL, + &_vq_auxt__16u0__p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u0__p2_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__16u0__p2_0[] = { + 2, 4, 4, 5, 6, 6, 5, 6, 6, 5, 7, 7, 7, 8, 9, 7, + 8, 9, 5, 7, 7, 7, 9, 8, 7, 9, 7, 4, 7, 7, 7, 9, + 9, 7, 8, 8, 6, 9, 8, 7, 8,11, 9,11,10, 6, 8, 9, + 8,11, 8, 9,10,11, 4, 7, 7, 7, 8, 8, 7, 9, 9, 6, + 9, 8, 9,11,10, 8, 8,11, 6, 8, 9, 9,10,11, 8,11, + 8, +}; + +static float _vq_quantthresh__16u0__p2_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__16u0__p2_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__16u0__p2_0 = { + _vq_quantthresh__16u0__p2_0, + _vq_quantmap__16u0__p2_0, + 3, + 3 +}; + +static static_codebook _16u0__p2_0 = { + 4, 81, + _vq_lengthlist__16u0__p2_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__16u0__p2_0, + NULL, + &_vq_auxt__16u0__p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u0__p3_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__16u0__p3_0[] = { + 1, 5, 5, 7, 7, 6, 7, 7, 8, 8, 6, 7, 8, 8, 8, 8, + 9, 9,11,11, 8, 9, 9,11,11, 6, 9, 8,10,10, 8,10, + 10,11,11, 8,10,10,11,11,10,11,10,13,12, 9,11,10, + 13,13, 6, 8, 9,10,10, 8,10,10,11,11, 8,10,10,11, + 11, 9,10,11,13,12,10,10,11,12,12, 8,11,11,14,13, + 10,12,11,15,13, 9,12,11,15,14,12,14,13,16,14,12, + 13,13,17,14, 8,11,11,13,14, 9,11,12,14,15,10,11, + 12,13,15,11,13,13,14,16,12,13,14,14,16, 5, 9, 9, + 11,11, 9,11,11,12,12, 8,11,11,12,12,11,12,12,15, + 14,10,12,12,15,15, 8,11,11,13,12,10,12,12,13,13, + 10,12,12,14,13,12,12,13,14,15,11,13,13,17,16, 7, + 11,11,13,13,10,12,12,14,13,10,12,12,13,14,12,13, + 12,15,14,11,13,13,15,14, 9,12,12,16,15,11,13,13, + 17,16,10,13,13,16,16,13,14,15,15,16,13,15,14,19, + 17, 9,12,12,14,16,11,13,13,15,16,10,13,13,17,16, + 13,14,13,17,15,12,15,15,16,17, 5, 9, 9,11,11, 8, + 11,11,13,12, 9,11,11,12,12,10,12,12,14,15,11,12, + 12,14,14, 7,11,10,13,12,10,12,12,14,13,10,11,12, + 13,13,11,13,13,15,16,12,12,13,15,15, 7,11,11,13, + 13,10,13,13,14,14,10,12,12,13,13,11,13,13,16,15, + 12,13,13,15,14, 9,12,12,15,15,10,13,13,17,16,11, + 12,13,15,15,12,15,14,18,18,13,14,14,16,17, 9,12, + 12,15,16,10,13,13,15,16,11,13,13,15,16,13,15,15, + 17,17,13,15,14,16,15, 7,11,11,15,16,10,13,12,16, + 17,10,12,13,15,17,15,16,16,18,17,13,15,15,17,18, + 8,12,12,16,16,11,13,14,17,18,11,13,13,18,16,15, + 17,16,17,19,14,15,15,17,16, 8,12,12,16,15,11,14, + 13,18,17,11,13,14,18,17,15,16,16,18,17,13,16,16, + 18,18,11,15,14,18,17,13,14,15,18, 0,12,15,15, 0, + 17,17,16,17,17,18,14,16,18,18, 0,11,14,14,17, 0, + 12,15,14,17,19,12,15,14,18, 0,15,18,16, 0,17,14, + 18,16,18, 0, 7,11,11,16,15,10,12,12,18,16,10,13, + 13,16,15,13,15,14,17,17,14,16,16,19,18, 8,12,12, + 16,16,11,13,13,18,16,11,13,14,17,16,14,15,15,19, + 18,15,16,16, 0,19, 8,12,12,16,17,11,13,13,17,17, + 11,14,13,17,17,13,15,15,17,19,15,17,17,19, 0,11, + 14,15,19,17,12,15,16,18,18,12,14,15,19,17,14,16, + 17, 0,18,16,16,19,17, 0,11,14,14,18,19,12,15,14, + 17,17,13,16,14,17,16,14,17,16,18,18,15,18,15, 0, + 18, +}; + +static float _vq_quantthresh__16u0__p3_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__16u0__p3_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__16u0__p3_0 = { + _vq_quantthresh__16u0__p3_0, + _vq_quantmap__16u0__p3_0, + 5, + 5 +}; + +static static_codebook _16u0__p3_0 = { + 4, 625, + _vq_lengthlist__16u0__p3_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__16u0__p3_0, + NULL, + &_vq_auxt__16u0__p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u0__p4_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__16u0__p4_0[] = { + 3, 5, 5, 8, 8, 6, 6, 6, 9, 9, 6, 6, 6, 9, 9, 9, + 10, 9,11,11, 9, 9, 9,11,11, 6, 7, 7,10,10, 7, 7, + 8,10,10, 7, 7, 8,10,10,10,10,10,11,12, 9,10,10, + 11,12, 6, 7, 7,10,10, 7, 8, 7,10,10, 7, 8, 7,10, + 10,10,11,10,12,11,10,10,10,13,10, 9,10,10,12,12, + 10,11,10,14,12, 9,11,11,13,13,11,12,13,13,13,11, + 12,12,15,13, 9,10,10,12,13, 9,11,10,12,13,10,10, + 11,12,13,11,12,12,12,13,11,12,12,13,13, 5, 7, 7, + 10,10, 7, 8, 8,10,10, 7, 8, 8,10,10,10,11,10,12, + 13,10,10,11,12,12, 6, 8, 8,11,10, 7, 8, 9,10,12, + 8, 9, 9,11,11,11,10,11,11,12,10,11,11,13,12, 7, + 8, 8,10,11, 8, 9, 8,11,10, 8, 9, 9,11,11,10,12, + 10,13,11,10,11,11,13,13,10,11,10,14,13,10,10,11, + 13,13,10,12,11,14,13,12,11,13,12,13,13,12,13,14, + 14,10,11,11,13,13,10,11,10,12,13,10,12,12,12,14, + 12,12,12,14,12,12,13,12,17,15, 5, 7, 7,10,10, 7, + 8, 8,10,10, 7, 8, 8,11,10,10,10,11,12,12,10,11, + 11,12,13, 6, 8, 8,11,10, 8, 9, 9,11,11, 7, 8, 9, + 10,11,11,11,11,12,12,10,10,11,12,13, 6, 8, 8,10, + 11, 8, 9, 9,11,11, 7, 9, 7,11,10,10,12,12,13,13, + 11,11,10,13,11, 9,11,10,14,13,11,11,11,15,13,10, + 10,11,13,13,12,13,13,14,14,12,11,12,12,13,10,11, + 11,12,13,10,11,12,13,13,10,11,10,13,12,12,12,13, + 14, 0,12,13,11,13,11, 8,10,10,13,13,10,11,11,14, + 13,10,11,11,13,12,13,14,14,14,15,12,12,12,15,14, + 9,11,10,13,12,10,10,11,13,14,11,11,11,15,12,13, + 12,14,15,16,13,13,13,14,13, 9,11,11,12,12,10,12, + 11,13,13,10,11,11,13,14,13,13,13,15,15,13,13,14, + 17,15,11,12,12,14,14,10,11,12,13,15,12,13,13, 0, + 15,13,11,14,12,16,14,16,14, 0,15,11,12,12,14,16, + 11,13,12,16,15,12,13,13,14,15,12,14,12,15,13,15, + 14,14,16,16, 8,10,10,13,13,10,11,10,13,14,10,11, + 11,13,13,13,13,12,14,14,14,13,13,16,17, 9,10,10, + 12,14,10,12,11,14,13,10,11,12,13,14,12,12,12,15, + 15,13,13,13,14,14, 9,10,10,13,13,10,11,12,12,14, + 10,11,10,13,13,13,13,13,14,16,13,13,13,14,14,11, + 12,13,15,13,12,14,13,14,16,12,12,13,13,14,13,14, + 14,17,15,13,12,17,13,16,11,12,13,14,15,12,13,14, + 14,17,11,12,11,14,14,13,16,14,16, 0,14,15,11,15, + 11, +}; + +static float _vq_quantthresh__16u0__p4_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__16u0__p4_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__16u0__p4_0 = { + _vq_quantthresh__16u0__p4_0, + _vq_quantmap__16u0__p4_0, + 5, + 5 +}; + +static static_codebook _16u0__p4_0 = { + 4, 625, + _vq_lengthlist__16u0__p4_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__16u0__p4_0, + NULL, + &_vq_auxt__16u0__p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u0__p5_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__16u0__p5_0[] = { + 1, 4, 4, 7, 7, 7, 7, 9, 9, 4, 6, 6, 8, 8, 8, 8, + 9, 9, 4, 6, 6, 8, 8, 8, 8, 9, 9, 7, 8, 8, 9, 9, + 9, 9,11,10, 7, 8, 8, 9, 9, 9, 9,10,11, 7, 8, 8, + 9, 9,10,10,11,11, 7, 8, 8, 9, 9,10,10,11,11, 9, + 9, 9,10,10,11,11,12,12, 9, 9, 9,10,10,11,11,12, + 12, +}; + +static float _vq_quantthresh__16u0__p5_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__16u0__p5_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__16u0__p5_0 = { + _vq_quantthresh__16u0__p5_0, + _vq_quantmap__16u0__p5_0, + 9, + 9 +}; + +static static_codebook _16u0__p5_0 = { + 2, 81, + _vq_lengthlist__16u0__p5_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__16u0__p5_0, + NULL, + &_vq_auxt__16u0__p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u0__p6_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__16u0__p6_0[] = { + 1, 4, 4, 7, 7,10,10,12,12,13,13,18,17, 3, 6, 6, + 9, 9,11,11,13,13,14,14,18,17, 3, 6, 6, 9, 9,11, + 11,13,13,14,14,17,18, 7, 9, 9,11,11,13,13,14,14, + 15,15, 0, 0, 7, 9, 9,11,11,13,13,14,14,15,16,19, + 18,10,11,11,13,13,14,14,16,15,17,18, 0, 0,10,11, + 11,13,13,14,14,15,15,16,18, 0, 0,11,13,13,14,14, + 15,15,17,17, 0,19, 0, 0,11,13,13,14,14,14,15,16, + 18, 0,19, 0, 0,13,14,14,15,15,18,17,18,18, 0,19, + 0, 0,13,14,14,15,16,16,16,18,18,19, 0, 0, 0,16, + 17,17, 0,17,19,19, 0,19, 0, 0, 0, 0,16,19,16,17, + 18, 0,19, 0, 0, 0, 0, 0, 0, +}; + +static float _vq_quantthresh__16u0__p6_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__16u0__p6_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__16u0__p6_0 = { + _vq_quantthresh__16u0__p6_0, + _vq_quantmap__16u0__p6_0, + 13, + 13 +}; + +static static_codebook _16u0__p6_0 = { + 2, 169, + _vq_lengthlist__16u0__p6_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__16u0__p6_0, + NULL, + &_vq_auxt__16u0__p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u0__p6_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__16u0__p6_1[] = { + 1, 4, 5, 6, 6, 4, 6, 6, 6, 6, 4, 6, 6, 6, 6, 6, + 6, 6, 7, 7, 6, 6, 6, 7, 7, +}; + +static float _vq_quantthresh__16u0__p6_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__16u0__p6_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__16u0__p6_1 = { + _vq_quantthresh__16u0__p6_1, + _vq_quantmap__16u0__p6_1, + 5, + 5 +}; + +static static_codebook _16u0__p6_1 = { + 2, 25, + _vq_lengthlist__16u0__p6_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__16u0__p6_1, + NULL, + &_vq_auxt__16u0__p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__16u0__p7_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__16u0__p7_0[] = { + 1, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, +}; + +static float _vq_quantthresh__16u0__p7_0[] = { + -157.5, 157.5, +}; + +static long _vq_quantmap__16u0__p7_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__16u0__p7_0 = { + _vq_quantthresh__16u0__p7_0, + _vq_quantmap__16u0__p7_0, + 3, + 3 +}; + +static static_codebook _16u0__p7_0 = { + 4, 81, + _vq_lengthlist__16u0__p7_0, + 1, -518803456, 1628680192, 2, 0, + _vq_quantlist__16u0__p7_0, + NULL, + &_vq_auxt__16u0__p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u0__p7_1[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__16u0__p7_1[] = { + 1, 5, 5, 6, 5, 9,10,11,11,10,10,10,10,10,10, 5, + 8, 8, 8,10,10,10,10,10,10,10,10,10,10,10, 5, 8, + 9, 9, 9,10,10,10,10,10,10,10,10,10,10, 5,10, 8, + 10,10,10,10,10,10,10,10,10,10,10,10, 4, 8, 9,10, + 10,10,10,10,10,10,10,10,10,10,10, 9,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10, 9,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10, +}; + +static float _vq_quantthresh__16u0__p7_1[] = { + -136.5, -115.5, -94.5, -73.5, -52.5, -31.5, -10.5, 10.5, + 31.5, 52.5, 73.5, 94.5, 115.5, 136.5, +}; + +static long _vq_quantmap__16u0__p7_1[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__16u0__p7_1 = { + _vq_quantthresh__16u0__p7_1, + _vq_quantmap__16u0__p7_1, + 15, + 15 +}; + +static static_codebook _16u0__p7_1 = { + 2, 225, + _vq_lengthlist__16u0__p7_1, + 1, -520986624, 1620377600, 4, 0, + _vq_quantlist__16u0__p7_1, + NULL, + &_vq_auxt__16u0__p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__16u0__p7_2[] = { + 10, + 9, + 11, + 8, + 12, + 7, + 13, + 6, + 14, + 5, + 15, + 4, + 16, + 3, + 17, + 2, + 18, + 1, + 19, + 0, + 20, +}; + +static long _vq_lengthlist__16u0__p7_2[] = { + 1, 6, 6, 7, 8, 7, 7,10, 9,10, 9,11,10, 9,11,10, + 9, 9, 9, 9,10, 6, 8, 7, 9, 9, 8, 8,10,10, 9,11, + 11,12,12,10, 9,11, 9,12,10, 9, 6, 9, 8, 9,12, 8, + 8,11, 9,11,11,12,11,12,12,10,11,11,10,10,11, 7, + 10, 9, 9, 9, 9, 9,10, 9,10, 9,10,10,12,10,10,10, + 11,12,10,10, 7, 9, 9, 9,10, 9, 9,10,10, 9, 9, 9, + 11,11,10,10,10,10, 9, 9,12, 7, 9,10, 9,11, 9,10, + 9,10,11,11,11,10,11,12, 9,12,11,10,10,10, 7, 9, + 9, 9, 9,10,12,10, 9,11,12,10,11,12,12,11, 9,10, + 11,10,11, 7, 9,10,10,11,10, 9,10,11,11,11,10,12, + 12,12,11,11,10,11,11,12, 8, 9,10,12,11,10,10,12, + 12,12,12,12,10,11,11, 9,11,10,12,11,11, 8, 9,10, + 10,11,12,11,11,10,10,10,12,12,12, 9,10,12,12,12, + 12,12, 8,10,11,10,10,12, 9,11,12,12,11,12,12,12, + 12,10,12,10,10,10,10, 8,12,11,11,11,10,10,11,12, + 12,12,12,11,12,12,12,11,11,11,12,10, 9,10,10,12, + 10,12,10,12,12,10,10,10,11,12,12,12,11,12,12,12, + 11,10,11,12,12,12,11,12,12,11,12,12,11,12,12,12, + 12,11,12,12,10,10,10,10,11,11,12,11,12,12,12,12, + 12,12,12,11,12,11,10,11,11,12,11,11, 9,10,10,10, + 12,10,10,11, 9,11,12,11,12,11,12,12,10,11,10,12, + 9, 9, 9,12,11,10,11,10,12,10,12,10,12,12,12,11, + 11,11,11,11,10, 9,10,10,11,10,11,11,12,11,10,11, + 12,12,12,11,11, 9,12,10,12, 9,10,12,10,10,11,10, + 11,11,12,11,10,11,10,11,11,11,11,12,11,11,10, 9, + 10,10,10, 9,11,11,10, 9,12,10,11,12,11,12,12,11, + 12,11,12,11,10,11,10,12,11,12,11,12,11,12,10,11, + 10,10,12,11,10,11,11,11,10, +}; + +static float _vq_quantthresh__16u0__p7_2[] = { + -9.5, -8.5, -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, + -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, + 6.5, 7.5, 8.5, 9.5, +}; + +static long _vq_quantmap__16u0__p7_2[] = { + 19, 17, 15, 13, 11, 9, 7, 5, + 3, 1, 0, 2, 4, 6, 8, 10, + 12, 14, 16, 18, 20, +}; + +static encode_aux_threshmatch _vq_auxt__16u0__p7_2 = { + _vq_quantthresh__16u0__p7_2, + _vq_quantmap__16u0__p7_2, + 21, + 21 +}; + +static static_codebook _16u0__p7_2 = { + 2, 441, + _vq_lengthlist__16u0__p7_2, + 1, -529268736, 1611661312, 5, 0, + _vq_quantlist__16u0__p7_2, + NULL, + &_vq_auxt__16u0__p7_2, + NULL, + 0 +}; + +static long _huff_lengthlist__16u0__single[] = { + 3, 5, 8, 7,14, 8, 9,19, 5, 2, 5, 5, 9, 6, 9,19, + 8, 4, 5, 7, 8, 9,13,19, 7, 4, 6, 5, 9, 6, 9,19, + 12, 8, 7, 9,10,11,13,19, 8, 5, 8, 6, 9, 6, 7,19, + 8, 8,10, 7, 7, 4, 5,19,12,17,19,15,18,13,11,18, +}; + +static static_codebook _huff_book__16u0__single = { + 2, 64, + _huff_lengthlist__16u0__single, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__16u1__long[] = { + 3, 6,10, 8,12, 8,14, 8,14,19, 5, 3, 5, 5, 7, 6, + 11, 7,16,19, 7, 5, 6, 7, 7, 9,11,12,19,19, 6, 4, + 7, 5, 7, 6,10, 7,18,18, 8, 6, 7, 7, 7, 7, 8, 9, + 18,18, 7, 5, 8, 5, 7, 5, 8, 6,18,18,12, 9,10, 9, + 9, 9, 8, 9,18,18, 8, 7,10, 6, 8, 5, 6, 4,11,18, + 11,15,16,12,11, 8, 8, 6, 9,18,14,18,18,18,16,16, + 16,13,16,18, +}; + +static static_codebook _huff_book__16u1__long = { + 2, 100, + _huff_lengthlist__16u1__long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__16u1__p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__16u1__p1_0[] = { + 1, 4, 4, 5, 7, 7, 5, 7, 7, 5, 8, 7, 7,10,10, 7, + 9,10, 5, 7, 8, 7,10, 9, 7,10,10, 5, 8, 8, 8,10, + 10, 8,10,10, 7,10,10,10,11,12,10,12,13, 7,10,10, + 9,13,11,10,12,13, 5, 8, 8, 8,10,10, 8,10,10, 7, + 10,10,10,12,12, 9,11,12, 7,10,11,10,12,12,10,13, + 11, +}; + +static float _vq_quantthresh__16u1__p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__16u1__p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__16u1__p1_0 = { + _vq_quantthresh__16u1__p1_0, + _vq_quantmap__16u1__p1_0, + 3, + 3 +}; + +static static_codebook _16u1__p1_0 = { + 4, 81, + _vq_lengthlist__16u1__p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__16u1__p1_0, + NULL, + &_vq_auxt__16u1__p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u1__p2_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__16u1__p2_0[] = { + 3, 4, 4, 5, 6, 6, 5, 6, 6, 5, 6, 6, 6, 7, 8, 6, + 7, 8, 5, 6, 6, 6, 8, 7, 6, 8, 7, 5, 6, 6, 6, 8, + 8, 6, 8, 8, 6, 8, 8, 7, 7,10, 8, 9, 9, 6, 8, 8, + 7, 9, 8, 8, 9,10, 5, 6, 6, 6, 8, 8, 7, 8, 8, 6, + 8, 8, 8,10, 9, 7, 8, 9, 6, 8, 8, 8, 9, 9, 7,10, + 8, +}; + +static float _vq_quantthresh__16u1__p2_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__16u1__p2_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__16u1__p2_0 = { + _vq_quantthresh__16u1__p2_0, + _vq_quantmap__16u1__p2_0, + 3, + 3 +}; + +static static_codebook _16u1__p2_0 = { + 4, 81, + _vq_lengthlist__16u1__p2_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__16u1__p2_0, + NULL, + &_vq_auxt__16u1__p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u1__p3_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__16u1__p3_0[] = { + 1, 5, 5, 8, 8, 6, 7, 7, 9, 9, 5, 7, 7, 9, 9, 9, + 10, 9,11,11, 9, 9,10,11,11, 6, 8, 8,10,10, 8, 9, + 10,11,11, 8, 9,10,11,11,10,11,11,12,13,10,11,11, + 13,13, 6, 8, 8,10,10, 8,10, 9,11,11, 8,10, 9,11, + 11,10,11,11,13,13,10,11,11,13,12, 9,11,11,14,13, + 10,12,12,15,14,10,12,11,14,13,12,13,13,15,15,12, + 13,13,16,14, 9,11,11,13,14,10,11,12,14,14,10,12, + 12,14,15,12,13,13,14,15,12,13,14,15,16, 5, 8, 8, + 11,11, 8,10,10,12,12, 8,10,10,12,12,11,12,12,14, + 14,11,12,12,14,14, 8,10,10,12,12, 9,11,12,12,13, + 10,12,12,13,13,12,12,13,14,15,11,13,13,15,15, 7, + 10,10,12,12, 9,12,11,13,12,10,11,12,13,13,12,13, + 12,15,14,11,12,13,15,15,10,12,12,15,14,11,13,13, + 16,15,11,13,13,16,15,14,13,14,15,16,13,15,15,17, + 17,10,12,12,14,15,11,12,12,15,15,11,13,13,15,16, + 13,15,13,16,15,13,15,15,16,17, 5, 8, 8,11,11, 8, + 10,10,12,12, 8,10,10,12,12,11,12,12,14,14,11,12, + 12,14,14, 7,10,10,12,12,10,12,12,14,13, 9,11,12, + 12,13,12,13,13,15,15,12,12,13,13,15, 7,10,10,12, + 13,10,11,12,13,13,10,12,11,13,13,11,13,13,15,15, + 12,13,12,15,14, 9,12,12,15,14,11,13,13,15,15,11, + 12,13,15,15,13,14,14,17,19,13,13,14,16,16,10,12, + 12,14,15,11,13,13,15,16,11,13,12,16,15,13,15,15, + 17,18,14,15,13,16,15, 8,11,11,15,14,10,12,12,16, + 15,10,12,12,16,16,14,15,15,18,17,13,14,15,16,18, + 9,12,12,15,15,11,12,14,16,17,11,13,13,16,15,15, + 15,15,17,18,14,15,16,17,17, 9,12,12,15,15,11,14, + 13,16,16,11,13,13,16,16,15,16,15,17,18,14,16,15, + 17,16,12,14,14,17,16,12,14,15,18,17,13,15,15,17, + 17,15,15,18,16,20,15,16,17,18,18,11,14,14,16,17, + 13,15,14,18,17,13,15,15,17,17,15,17,15,18,17,15, + 17,16,19,18, 8,11,11,14,15,10,12,12,15,15,10,12, + 12,16,16,13,14,14,17,16,14,15,15,17,17, 9,12,12, + 15,16,11,13,13,16,16,11,12,13,16,16,14,16,15,20, + 17,14,16,16,17,17, 9,12,12,15,16,11,13,13,16,17, + 11,13,13,17,16,14,15,15,17,18,15,15,15,18,18,11, + 14,14,17,16,13,15,15,17,17,13,14,14,18,17,15,16, + 16,18,19,15,15,17,17,19,11,14,14,16,17,13,15,14, + 17,19,13,15,14,18,17,15,17,16,18,18,15,17,15,18, + 16, +}; + +static float _vq_quantthresh__16u1__p3_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__16u1__p3_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__16u1__p3_0 = { + _vq_quantthresh__16u1__p3_0, + _vq_quantmap__16u1__p3_0, + 5, + 5 +}; + +static static_codebook _16u1__p3_0 = { + 4, 625, + _vq_lengthlist__16u1__p3_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__16u1__p3_0, + NULL, + &_vq_auxt__16u1__p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u1__p4_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__16u1__p4_0[] = { + 4, 5, 5, 8, 8, 6, 6, 7, 9, 9, 6, 6, 6, 9, 9, 9, + 10, 9,11,11, 9, 9,10,11,11, 6, 7, 7,10, 9, 7, 7, + 8, 9,10, 7, 7, 8,10,10,10,10,10,10,12, 9, 9,10, + 11,12, 6, 7, 7, 9, 9, 7, 8, 7,10,10, 7, 8, 7,10, + 10, 9,10, 9,12,11,10,10, 9,12,10, 9,10,10,12,11, + 10,10,10,12,12, 9,10,10,12,12,12,11,12,13,13,11, + 11,12,12,13, 9,10,10,11,12, 9,10,10,12,12,10,10, + 10,12,12,11,12,11,14,13,11,12,12,14,13, 5, 7, 7, + 10,10, 7, 8, 8,10,10, 7, 8, 7,10,10,10,10,10,12, + 12,10,10,10,12,12, 6, 8, 7,10,10, 7, 7, 9,10,11, + 8, 9, 9,11,10,10,10,11,11,13,10,10,11,12,13, 6, + 8, 8,10,10, 7, 9, 8,11,10, 8, 9, 9,10,11,10,11, + 10,13,11,10,11,10,12,12,10,11,10,12,11,10,10,10, + 12,13,10,11,11,13,12,11,11,13,11,14,12,12,13,14, + 14, 9,10,10,12,13,10,11,10,13,12,10,11,11,12,13, + 11,12,11,14,12,12,13,13,15,14, 5, 7, 7,10,10, 7, + 7, 8,10,10, 7, 8, 8,10,10,10,10,10,11,12,10,10, + 10,12,12, 7, 8, 8,10,10, 8, 9, 8,11,10, 7, 8, 9, + 10,11,10,11,11,12,12,10,10,11,11,13, 7, 7, 8,10, + 10, 8, 8, 9,10,11, 7, 9, 7,11,10,10,11,11,13,12, + 11,11,10,13,11, 9,10,10,12,12,10,11,11,13,12,10, + 10,11,12,12,12,13,13,14,14,11,11,12,12,14,10,10, + 11,12,12,10,11,11,12,13,10,10,10,13,12,12,13,13, + 15,14,12,13,10,14,11, 8,10,10,12,12,10,11,10,13, + 13, 9,10,10,12,12,12,13,13,15,14,11,12,12,13,13, + 9,10,10,13,12,10,10,11,13,13,10,11,10,13,12,12, + 12,13,14,15,12,13,12,15,13, 9,10,10,12,13,10,11, + 10,13,12,10,10,11,12,13,12,14,12,15,13,12,12,13, + 14,15,11,12,11,14,13,11,11,12,14,15,12,13,12,15, + 14,13,11,15,11,16,13,14,14,16,15,11,12,12,14,14, + 11,12,11,14,13,12,12,13,14,15,13,14,12,16,12,14, + 14,14,15,15, 8,10,10,12,12, 9,10,10,12,12,10,10, + 11,13,13,11,12,12,13,13,12,13,13,14,15, 9,10,10, + 13,12,10,11,11,13,12,10,10,11,13,13,12,13,12,15, + 14,12,12,13,13,16, 9, 9,10,12,13,10,10,11,12,13, + 10,11,10,13,13,12,12,13,13,15,13,13,12,15,13,11, + 12,12,14,14,12,13,12,15,14,11,11,12,13,14,14,14, + 14,16,15,13,12,15,12,16,11,11,12,13,14,12,13,13, + 14,15,10,12,11,14,13,14,15,14,16,16,13,14,11,15, + 11, +}; + +static float _vq_quantthresh__16u1__p4_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__16u1__p4_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__16u1__p4_0 = { + _vq_quantthresh__16u1__p4_0, + _vq_quantmap__16u1__p4_0, + 5, + 5 +}; + +static static_codebook _16u1__p4_0 = { + 4, 625, + _vq_lengthlist__16u1__p4_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__16u1__p4_0, + NULL, + &_vq_auxt__16u1__p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u1__p5_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__16u1__p5_0[] = { + 1, 4, 4, 7, 7, 7, 7, 9, 9, 4, 6, 6, 8, 8, 8, 8, + 10,10, 4, 5, 6, 8, 8, 8, 8,10,10, 7, 8, 8, 9, 9, + 9, 9,11,11, 7, 8, 8, 9, 9, 9, 9,11,11, 7, 8, 8, + 10, 9,11,11,12,11, 7, 8, 8, 9, 9,11,11,12,12, 9, + 10,10,11,11,12,12,13,12, 9,10,10,11,11,12,12,12, + 13, +}; + +static float _vq_quantthresh__16u1__p5_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__16u1__p5_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__16u1__p5_0 = { + _vq_quantthresh__16u1__p5_0, + _vq_quantmap__16u1__p5_0, + 9, + 9 +}; + +static static_codebook _16u1__p5_0 = { + 2, 81, + _vq_lengthlist__16u1__p5_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__16u1__p5_0, + NULL, + &_vq_auxt__16u1__p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u1__p6_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__16u1__p6_0[] = { + 3, 4, 4, 6, 6, 7, 7, 9, 9, 4, 4, 4, 6, 6, 8, 8, + 9, 9, 4, 4, 4, 6, 6, 7, 7, 9, 9, 6, 6, 6, 7, 7, + 8, 8,10, 9, 6, 6, 6, 7, 7, 8, 8, 9,10, 7, 8, 7, + 8, 8, 9, 9,10,10, 7, 8, 8, 8, 8, 9, 9,10,10, 9, + 9, 9,10,10,10,10,11,11, 9, 9, 9,10,10,10,10,11, + 11, +}; + +static float _vq_quantthresh__16u1__p6_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__16u1__p6_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__16u1__p6_0 = { + _vq_quantthresh__16u1__p6_0, + _vq_quantmap__16u1__p6_0, + 9, + 9 +}; + +static static_codebook _16u1__p6_0 = { + 2, 81, + _vq_lengthlist__16u1__p6_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__16u1__p6_0, + NULL, + &_vq_auxt__16u1__p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u1__p7_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__16u1__p7_0[] = { + 1, 4, 4, 4, 8, 8, 4, 8, 8, 5,11, 9, 8,12,11, 8, + 12,11, 5,10,11, 8,11,12, 8,11,12, 4,11,11,11,14, + 13,10,13,13, 8,14,13,12,14,16,12,16,15, 8,14,14, + 13,16,14,12,15,16, 4,11,11,10,14,13,11,14,14, 8, + 15,14,12,15,15,12,14,16, 8,14,14,11,16,15,12,15, + 13, +}; + +static float _vq_quantthresh__16u1__p7_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__16u1__p7_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__16u1__p7_0 = { + _vq_quantthresh__16u1__p7_0, + _vq_quantmap__16u1__p7_0, + 3, + 3 +}; + +static static_codebook _16u1__p7_0 = { + 4, 81, + _vq_lengthlist__16u1__p7_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__16u1__p7_0, + NULL, + &_vq_auxt__16u1__p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u1__p7_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__16u1__p7_1[] = { + 2, 4, 4, 6, 6, 7, 7, 8, 8, 8, 8, 4, 6, 5, 7, 7, + 8, 8, 8, 8, 8, 8, 4, 5, 6, 7, 7, 8, 8, 8, 8, 8, + 8, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 6, 7, 7, 8, + 8, 8, 8, 9, 9, 9, 9, 7, 8, 8, 8, 8, 9, 9, 9,10, + 9,10, 7, 8, 8, 8, 8, 9, 9, 9, 9,10, 9, 8, 8, 8, + 9, 9,10,10,10,10,10,10, 8, 8, 8, 9, 9, 9, 9,10, + 10,10,10, 8, 8, 8, 9, 9, 9,10,10,10,10,10, 8, 8, + 8, 9, 9,10,10,10,10,10,10, +}; + +static float _vq_quantthresh__16u1__p7_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__16u1__p7_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__16u1__p7_1 = { + _vq_quantthresh__16u1__p7_1, + _vq_quantmap__16u1__p7_1, + 11, + 11 +}; + +static static_codebook _16u1__p7_1 = { + 2, 121, + _vq_lengthlist__16u1__p7_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__16u1__p7_1, + NULL, + &_vq_auxt__16u1__p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__16u1__p8_0[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__16u1__p8_0[] = { + 1, 4, 4, 5, 5, 8, 8,10,10,12,12, 4, 7, 7, 8, 8, + 9, 9,12,11,14,13, 4, 7, 7, 7, 8, 9,10,11,11,13, + 12, 5, 8, 8, 9, 9,11,11,12,13,15,14, 5, 7, 8, 9, + 9,11,11,13,13,17,15, 8, 9,10,11,11,12,13,17,14, + 17,16, 8,10, 9,11,11,12,12,13,15,15,17,10,11,11, + 12,13,14,15,15,16,16,17, 9,11,11,12,12,14,15,17, + 15,15,16,11,14,12,14,15,16,15,16,16,16,15,11,13, + 13,14,14,15,15,16,16,15,16, +}; + +static float _vq_quantthresh__16u1__p8_0[] = { + -49.5, -38.5, -27.5, -16.5, -5.5, 5.5, 16.5, 27.5, + 38.5, 49.5, +}; + +static long _vq_quantmap__16u1__p8_0[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__16u1__p8_0 = { + _vq_quantthresh__16u1__p8_0, + _vq_quantmap__16u1__p8_0, + 11, + 11 +}; + +static static_codebook _16u1__p8_0 = { + 2, 121, + _vq_lengthlist__16u1__p8_0, + 1, -524582912, 1618345984, 4, 0, + _vq_quantlist__16u1__p8_0, + NULL, + &_vq_auxt__16u1__p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u1__p8_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__16u1__p8_1[] = { + 2, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8, 4, 6, 6, 7, 7, + 8, 7, 8, 8, 8, 8, 4, 6, 6, 7, 7, 7, 7, 8, 8, 8, + 8, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 9, 6, 7, 7, 7, + 7, 8, 8, 8, 8, 9, 9, 7, 7, 7, 8, 8, 8, 8, 9, 9, + 9, 9, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 8, 8, 8, + 8, 8, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 9, 9, 9, + 9, 9, 9, 8, 8, 8, 9, 8, 9, 9, 9, 9, 9, 9, 8, 8, + 8, 9, 9, 9, 9, 9, 9, 9, 9, +}; + +static float _vq_quantthresh__16u1__p8_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__16u1__p8_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__16u1__p8_1 = { + _vq_quantthresh__16u1__p8_1, + _vq_quantmap__16u1__p8_1, + 11, + 11 +}; + +static static_codebook _16u1__p8_1 = { + 2, 121, + _vq_lengthlist__16u1__p8_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__16u1__p8_1, + NULL, + &_vq_auxt__16u1__p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__16u1__p9_0[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__16u1__p9_0[] = { + 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, +}; + +static float _vq_quantthresh__16u1__p9_0[] = { + -1657.5, -1402.5, -1147.5, -892.5, -637.5, -382.5, -127.5, 127.5, + 382.5, 637.5, 892.5, 1147.5, 1402.5, 1657.5, +}; + +static long _vq_quantmap__16u1__p9_0[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__16u1__p9_0 = { + _vq_quantthresh__16u1__p9_0, + _vq_quantmap__16u1__p9_0, + 15, + 15 +}; + +static static_codebook _16u1__p9_0 = { + 2, 225, + _vq_lengthlist__16u1__p9_0, + 1, -514071552, 1627381760, 4, 0, + _vq_quantlist__16u1__p9_0, + NULL, + &_vq_auxt__16u1__p9_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u1__p9_1[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__16u1__p9_1[] = { + 1, 6, 5, 9, 9,10,10, 6, 7, 9, 9,10,10,10,10, 5, + 10, 8,10, 8,10,10, 8, 8,10, 9,10,10,10,10, 5, 8, + 9,10,10,10,10, 8,10,10,10,10,10,10,10, 9,10,10, + 10,10,10,10, 9, 9,10,10,10,10,10,10, 9, 9, 8, 9, + 10,10,10, 9,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10, 8,10,10,10,10, + 10,10,10,10,10,10,10,10,10, 6, 8, 8,10,10,10, 8, + 10,10,10,10,10,10,10,10, 5, 8, 8,10,10,10, 9, 9, + 10,10,10,10,10,10,10,10, 9,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, +}; + +static float _vq_quantthresh__16u1__p9_1[] = { + -110.5, -93.5, -76.5, -59.5, -42.5, -25.5, -8.5, 8.5, + 25.5, 42.5, 59.5, 76.5, 93.5, 110.5, +}; + +static long _vq_quantmap__16u1__p9_1[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__16u1__p9_1 = { + _vq_quantthresh__16u1__p9_1, + _vq_quantmap__16u1__p9_1, + 15, + 15 +}; + +static static_codebook _16u1__p9_1 = { + 2, 225, + _vq_lengthlist__16u1__p9_1, + 1, -522338304, 1620115456, 4, 0, + _vq_quantlist__16u1__p9_1, + NULL, + &_vq_auxt__16u1__p9_1, + NULL, + 0 +}; + +static long _vq_quantlist__16u1__p9_2[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__16u1__p9_2[] = { + 1, 6, 6, 7, 8, 8,11,10, 9, 9,11, 9,10, 9,11,11, + 9, 6, 7, 6,11, 8,11, 9,10,10,11, 9,11,10,10,10, + 11, 9, 5, 7, 7, 8, 8,10,11, 8, 8,11, 9, 9,10,11, + 9,10,11, 8, 9, 6, 8, 8, 9, 9,10,10,11,11,11, 9, + 11,10, 9,11, 8, 8, 8, 9, 8, 9,10,11, 9, 9,11,11, + 10, 9, 9,11,10, 8,11, 8, 9, 8,11, 9,10, 9,10,11, + 11,10,10, 9,10,10, 8, 8, 9,10,10,10, 9,11, 9,10, + 11,11,11,11,10, 9,11, 9, 9,11,11,10, 8,11,11,11, + 9,10,10,11,10,11,11, 9,11,10, 9,11,10,10,10,10, + 9,11,10,11,10, 9, 9,10,11, 9, 8,10,11,11,10,10, + 11, 9,11,10,11,11,10,11, 9, 9, 8,10, 8, 9,11, 9, + 8,10,10, 9,11,10,11,10,11, 9,11, 8,10,11,11,11, + 11,10,10,11,11,11,11,10,11,11,10, 9, 8,10,10, 9, + 11,10,11,11,11, 9, 9, 9,11,11,11,10,10, 9, 9,10, + 9,11,11,11,11, 8,10,11,10,11,11,10,11,11, 9, 9, + 9,10, 9,11, 9,11,11,11,11,11,10,11,11,10,11,10, + 11,11, 9,11,10,11,10, 9,10, 9,10,10,11,11,11,11, + 9,10, 9,10,11,11,10,11,11,11,11,11,11,10,11,11, + 10, +}; + +static float _vq_quantthresh__16u1__p9_2[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__16u1__p9_2[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__16u1__p9_2 = { + _vq_quantthresh__16u1__p9_2, + _vq_quantmap__16u1__p9_2, + 17, + 17 +}; + +static static_codebook _16u1__p9_2 = { + 2, 289, + _vq_lengthlist__16u1__p9_2, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__16u1__p9_2, + NULL, + &_vq_auxt__16u1__p9_2, + NULL, + 0 +}; + +static long _huff_lengthlist__16u1__short[] = { + 5, 7,10, 9,11,10,15,11,13,16, 6, 4, 6, 6, 7, 7, + 10, 9,12,16,10, 6, 5, 6, 6, 7,10,11,16,16, 9, 6, + 7, 6, 7, 7,10, 8,14,16,11, 6, 5, 4, 5, 6, 8, 9, + 15,16, 9, 6, 6, 5, 6, 6, 9, 8,14,16,12, 7, 6, 6, + 5, 6, 6, 7,13,16, 8, 6, 7, 6, 5, 5, 4, 4,11,16, + 9, 8, 9, 9, 7, 7, 6, 5,13,16,14,14,16,15,16,15, + 16,16,16,16, +}; + +static static_codebook _huff_book__16u1__short = { + 2, 100, + _huff_lengthlist__16u1__short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__16u2__long[] = { + 5, 7,10,10,10,11,11,13,18,19, 6, 5, 5, 6, 7, 8, + 9,12,19,19, 8, 5, 4, 4, 6, 7, 9,13,19,19, 8, 5, + 4, 4, 5, 6, 8,12,17,19, 7, 5, 5, 4, 4, 5, 7,12, + 18,18, 8, 7, 7, 6, 5, 5, 6,10,18,18, 9, 9, 9, 8, + 6, 5, 6, 9,18,18,11,13,13,13, 8, 7, 7, 9,16,18, + 13,17,18,16,11, 9, 9, 9,17,18,15,18,18,18,15,13, + 13,14,18,18, +}; + +static static_codebook _huff_book__16u2__long = { + 2, 100, + _huff_lengthlist__16u2__long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__16u2__short[] = { + 8,11,12,12,14,15,16,16,16,16, 9, 7, 7, 8, 9,11, + 13,14,16,16,13, 7, 6, 6, 7, 9,12,13,15,16,15, 7, + 6, 5, 4, 6,10,11,14,16,12, 8, 7, 4, 2, 4, 7,10, + 14,16,11, 9, 7, 5, 3, 4, 6, 9,14,16,11,10, 9, 7, + 5, 5, 6, 9,16,16,10,10, 9, 8, 6, 6, 7,10,16,16, + 11,11,11,10,10,10,11,14,16,16,16,14,14,13,14,16, + 16,16,16,16, +}; + +static static_codebook _huff_book__16u2__short = { + 2, 100, + _huff_lengthlist__16u2__short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__16u2_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__16u2_p1_0[] = { + 1, 5, 5, 5, 7, 7, 5, 7, 7, 5, 7, 7, 7, 9, 9, 7, + 9, 9, 5, 7, 7, 7, 9, 9, 7, 9, 9, 5, 7, 7, 8, 9, + 9, 7, 9, 9, 7, 9, 9, 9,10,10, 9,10,10, 7, 9, 9, + 9,10,10, 9,10,11, 5, 7, 8, 8, 9, 9, 8, 9, 9, 7, + 9, 9, 9,10,10, 9, 9,10, 7, 9, 9, 9,10,10, 9,11, + 10, +}; + +static float _vq_quantthresh__16u2_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__16u2_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__16u2_p1_0 = { + _vq_quantthresh__16u2_p1_0, + _vq_quantmap__16u2_p1_0, + 3, + 3 +}; + +static static_codebook _16u2_p1_0 = { + 4, 81, + _vq_lengthlist__16u2_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__16u2_p1_0, + NULL, + &_vq_auxt__16u2_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u2_p2_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__16u2_p2_0[] = { + 3, 5, 5, 8, 8, 5, 7, 7, 9, 9, 5, 7, 7, 9, 9, 9, + 10, 9,11,11, 9, 9, 9,11,11, 5, 7, 7, 9, 9, 7, 8, + 8,10,10, 7, 8, 8,10,10,10,10,10,12,12, 9,10,10, + 11,12, 5, 7, 7, 9, 9, 7, 8, 8,10,10, 7, 8, 8,10, + 10, 9,10,10,12,11,10,10,10,12,12, 9,10,10,12,12, + 10,11,10,13,12, 9,10,10,12,12,12,12,12,14,14,11, + 12,12,13,14, 9,10,10,12,12, 9,10,10,12,12,10,10, + 10,12,12,11,12,12,14,13,12,13,12,14,14, 5, 7, 7, + 9, 9, 7, 8, 8,10,10, 7, 8, 8,10,10,10,11,10,12, + 12,10,10,11,12,12, 7, 8, 8,10,10, 8, 9, 9,11,11, + 8, 9, 9,11,11,11,11,11,12,13,10,11,11,12,13, 7, + 8, 8,10,10, 8, 9, 8,11,10, 8, 9, 9,11,11,10,11, + 10,13,12,10,11,11,13,13, 9,11,10,13,13,10,11,11, + 13,13,10,11,11,13,13,12,12,13,13,15,12,12,13,14, + 15, 9,10,10,12,12,10,11,10,13,12,10,11,11,13,13, + 11,13,11,14,13,12,13,13,15,15, 5, 7, 7, 9, 9, 7, + 8, 8,10,10, 7, 8, 8,10,10,10,10,10,12,12,10,10, + 11,12,12, 7, 8, 8,10,10, 8, 9, 9,11,11, 8, 8, 9, + 10,11,10,11,11,13,13,10,10,11,12,13, 7, 8, 8,10, + 11, 8, 9, 9,11,11, 8, 9, 9,11,11,10,11,11,13,12, + 11,11,11,13,12, 9,10,10,12,12,10,11,11,13,13,10, + 10,11,12,13,12,13,13,15,14,11,11,13,12,14,10,10, + 11,13,13,10,11,11,13,13,10,11,11,13,13,12,13,13, + 14,14,12,13,12,14,13, 8,10, 9,12,12, 9,11,10,13, + 13, 9,10,10,12,13,12,13,13,14,14,12,12,13,14,14, + 9,11,10,13,13,10,11,11,13,13,10,11,11,13,13,12, + 13,13,15,15,13,13,13,14,15, 9,10,10,12,13,10,11, + 10,13,12,10,11,11,13,13,12,13,12,15,14,13,13,13, + 14,15,11,12,12,15,14,12,12,13,15,15,12,13,13,15, + 14,14,13,15,14,16,13,14,15,16,16,11,12,12,14,14, + 11,12,12,15,14,12,13,13,15,15,13,14,13,16,14,14, + 14,14,16,16, 8, 9, 9,12,12, 9,10,10,13,12, 9,10, + 10,13,13,12,12,12,14,14,12,12,13,15,15, 9,10,10, + 13,12,10,11,11,13,13,10,10,11,13,14,12,13,13,15, + 15,12,12,13,14,15, 9,10,10,13,13,10,11,11,13,13, + 10,11,11,13,13,12,13,13,14,14,13,14,13,15,14,11, + 12,12,14,14,12,13,13,15,14,11,12,12,14,15,14,14, + 14,16,15,13,12,14,14,16,11,12,13,14,15,12,13,13, + 14,16,12,13,12,15,14,13,15,14,16,16,14,15,13,16, + 13, +}; + +static float _vq_quantthresh__16u2_p2_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__16u2_p2_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__16u2_p2_0 = { + _vq_quantthresh__16u2_p2_0, + _vq_quantmap__16u2_p2_0, + 5, + 5 +}; + +static static_codebook _16u2_p2_0 = { + 4, 625, + _vq_lengthlist__16u2_p2_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__16u2_p2_0, + NULL, + &_vq_auxt__16u2_p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u2_p3_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__16u2_p3_0[] = { + 2, 4, 4, 6, 6, 7, 7, 9, 9, 4, 5, 5, 6, 6, 8, 7, + 9, 9, 4, 5, 5, 6, 6, 7, 8, 9, 9, 6, 6, 6, 7, 7, + 8, 8,10,10, 6, 6, 6, 7, 7, 8, 8, 9,10, 7, 8, 7, + 8, 8, 9, 9,10,10, 7, 8, 8, 8, 8, 9, 9,10,10, 9, + 9, 9,10, 9,10,10,11,11, 9, 9, 9,10,10,10,10,11, + 11, +}; + +static float _vq_quantthresh__16u2_p3_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__16u2_p3_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__16u2_p3_0 = { + _vq_quantthresh__16u2_p3_0, + _vq_quantmap__16u2_p3_0, + 9, + 9 +}; + +static static_codebook _16u2_p3_0 = { + 2, 81, + _vq_lengthlist__16u2_p3_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__16u2_p3_0, + NULL, + &_vq_auxt__16u2_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u2_p4_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__16u2_p4_0[] = { + 2, 4, 4, 6, 6, 7, 7, 8, 8, 9, 9,10,10,11,11,11, + 11, 5, 5, 5, 7, 7, 8, 8, 9, 9, 9, 9,10,10,11,11, + 12,11, 5, 5, 5, 7, 7, 8, 8, 9, 9, 9, 9,10,10,11, + 11,12,12, 6, 7, 7, 7, 7, 8, 8, 9, 9, 9, 9,10,10, + 11,11,12,12, 6, 7, 7, 7, 7, 8, 8, 9, 9, 9, 9,10, + 10,11,11,12,12, 7, 8, 8, 8, 8, 9, 9, 9, 9,10,10, + 11,11,12,12,12,12, 7, 8, 8, 8, 8, 9, 9, 9, 9,10, + 10,11,11,11,12,12,12, 9, 9, 9, 9, 9, 9,10,10,10, + 10,10,11,11,12,12,13,13, 8, 9, 9, 9, 9,10, 9,10, + 10,10,10,11,11,12,12,13,13, 9, 9, 9, 9, 9,10,10, + 10,10,11,11,11,12,12,12,13,13, 9, 9, 9, 9, 9,10, + 10,10,10,11,11,12,11,12,12,13,13,10,10,10,10,10, + 11,11,11,11,11,12,12,12,12,13,13,14,10,10,10,10, + 10,11,11,11,11,12,11,12,12,13,12,13,13,11,11,11, + 11,11,12,12,12,12,12,12,13,13,13,13,14,14,11,11, + 11,11,11,12,12,12,12,12,12,13,12,13,13,14,14,11, + 12,12,12,12,12,12,13,13,13,13,13,13,14,14,14,14, + 11,12,12,12,12,12,12,13,13,13,13,14,13,14,14,14, + 14, +}; + +static float _vq_quantthresh__16u2_p4_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__16u2_p4_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__16u2_p4_0 = { + _vq_quantthresh__16u2_p4_0, + _vq_quantmap__16u2_p4_0, + 17, + 17 +}; + +static static_codebook _16u2_p4_0 = { + 2, 289, + _vq_lengthlist__16u2_p4_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__16u2_p4_0, + NULL, + &_vq_auxt__16u2_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u2_p5_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__16u2_p5_0[] = { + 1, 4, 4, 5, 7, 7, 5, 7, 7, 5, 9, 8, 7,10, 9, 7, + 10, 9, 5, 8, 9, 7, 9,10, 7, 9,10, 4, 9, 9, 9,11, + 11, 8,11,11, 7,11,11,10,10,13,10,14,13, 7,11,11, + 10,13,11,10,13,14, 5, 9, 9, 8,11,11, 9,11,11, 7, + 11,11,10,14,13,10,12,14, 7,11,11,10,13,13,10,13, + 10, +}; + +static float _vq_quantthresh__16u2_p5_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__16u2_p5_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__16u2_p5_0 = { + _vq_quantthresh__16u2_p5_0, + _vq_quantmap__16u2_p5_0, + 3, + 3 +}; + +static static_codebook _16u2_p5_0 = { + 4, 81, + _vq_lengthlist__16u2_p5_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__16u2_p5_0, + NULL, + &_vq_auxt__16u2_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u2_p5_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__16u2_p5_1[] = { + 2, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8, 5, 5, 5, 7, 7, + 7, 7, 8, 8, 8, 8, 5, 5, 6, 7, 7, 7, 7, 8, 8, 8, + 8, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 6, 7, 7, 7, + 7, 8, 8, 8, 8, 8, 8, 7, 7, 7, 8, 8, 8, 8, 9, 9, + 9, 9, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 8, 8, 8, + 8, 8, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 9, 9, 9, + 9, 9, 9, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 8, 8, + 8, 8, 8, 9, 9, 9, 9, 9, 9, +}; + +static float _vq_quantthresh__16u2_p5_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__16u2_p5_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__16u2_p5_1 = { + _vq_quantthresh__16u2_p5_1, + _vq_quantmap__16u2_p5_1, + 11, + 11 +}; + +static static_codebook _16u2_p5_1 = { + 2, 121, + _vq_lengthlist__16u2_p5_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__16u2_p5_1, + NULL, + &_vq_auxt__16u2_p5_1, + NULL, + 0 +}; + +static long _vq_quantlist__16u2_p6_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__16u2_p6_0[] = { + 1, 4, 4, 7, 7, 8, 8, 8, 8, 9, 9,10,10, 4, 6, 6, + 8, 8, 9, 9, 9, 9,10,10,12,11, 4, 6, 6, 8, 8, 9, + 9, 9, 9,10,10,11,12, 7, 8, 8, 9, 9,10,10,10,10, + 12,12,13,12, 7, 8, 8, 9, 9,10,10,10,10,11,12,12, + 12, 8, 9, 9,10,10,11,11,11,11,12,12,13,13, 8, 9, + 9,10,10,11,11,11,11,12,13,13,13, 8, 9, 9,10,10, + 11,11,12,12,13,13,14,14, 8, 9, 9,10,10,11,11,12, + 12,13,13,14,14, 9,10,10,11,12,13,12,13,14,14,14, + 14,14, 9,10,10,11,12,12,13,13,13,14,14,14,14,10, + 11,11,12,12,13,13,14,14,15,15,15,15,10,11,11,12, + 12,13,13,14,14,14,14,15,15, +}; + +static float _vq_quantthresh__16u2_p6_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__16u2_p6_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__16u2_p6_0 = { + _vq_quantthresh__16u2_p6_0, + _vq_quantmap__16u2_p6_0, + 13, + 13 +}; + +static static_codebook _16u2_p6_0 = { + 2, 169, + _vq_lengthlist__16u2_p6_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__16u2_p6_0, + NULL, + &_vq_auxt__16u2_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u2_p6_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__16u2_p6_1[] = { + 2, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 6, 6, 5, 5, 5, 6, 6, +}; + +static float _vq_quantthresh__16u2_p6_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__16u2_p6_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__16u2_p6_1 = { + _vq_quantthresh__16u2_p6_1, + _vq_quantmap__16u2_p6_1, + 5, + 5 +}; + +static static_codebook _16u2_p6_1 = { + 2, 25, + _vq_lengthlist__16u2_p6_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__16u2_p6_1, + NULL, + &_vq_auxt__16u2_p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__16u2_p7_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__16u2_p7_0[] = { + 1, 4, 4, 7, 7, 7, 7, 8, 8, 9, 9,10,10, 4, 6, 6, + 9, 9, 9, 9, 9, 9,10,10,11,11, 4, 6, 6, 8, 9, 9, + 9, 9, 9,10,11,12,11, 7, 8, 9,10,10,10,10,11,10, + 11,12,12,13, 7, 9, 9,10,10,10,10,10,10,11,12,13, + 13, 7, 9, 8,10,10,11,11,11,12,12,13,13,14, 7, 9, + 9,10,10,11,11,11,12,13,13,13,13, 8, 9, 9,10,11, + 11,12,12,12,13,13,13,13, 8, 9, 9,10,11,11,11,12, + 12,13,13,14,14, 9,10,10,12,11,12,13,13,13,14,13, + 13,13, 9,10,10,11,11,12,12,13,14,13,13,14,13,10, + 11,11,12,13,14,14,14,15,14,14,14,14,10,11,11,12, + 12,13,13,13,14,14,14,15,14, +}; + +static float _vq_quantthresh__16u2_p7_0[] = { + -60.5, -49.5, -38.5, -27.5, -16.5, -5.5, 5.5, 16.5, + 27.5, 38.5, 49.5, 60.5, +}; + +static long _vq_quantmap__16u2_p7_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__16u2_p7_0 = { + _vq_quantthresh__16u2_p7_0, + _vq_quantmap__16u2_p7_0, + 13, + 13 +}; + +static static_codebook _16u2_p7_0 = { + 2, 169, + _vq_lengthlist__16u2_p7_0, + 1, -523206656, 1618345984, 4, 0, + _vq_quantlist__16u2_p7_0, + NULL, + &_vq_auxt__16u2_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u2_p7_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__16u2_p7_1[] = { + 3, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7, 5, 6, 6, 7, 7, + 7, 7, 7, 7, 8, 8, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, + 8, 6, 6, 7, 7, 7, 8, 7, 8, 8, 8, 8, 6, 7, 7, 7, + 7, 7, 7, 8, 8, 8, 8, 7, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 8, 8, 8, 8, 8, + 8, 8, 8, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, +}; + +static float _vq_quantthresh__16u2_p7_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__16u2_p7_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__16u2_p7_1 = { + _vq_quantthresh__16u2_p7_1, + _vq_quantmap__16u2_p7_1, + 11, + 11 +}; + +static static_codebook _16u2_p7_1 = { + 2, 121, + _vq_lengthlist__16u2_p7_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__16u2_p7_1, + NULL, + &_vq_auxt__16u2_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__16u2_p8_0[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__16u2_p8_0[] = { + 1, 5, 5, 7, 7, 8, 8, 7, 7, 8, 8,10, 9,11,11, 4, + 6, 6, 8, 8,10, 9, 9, 8, 9, 9,10,10,12,14, 4, 6, + 7, 8, 9, 9,10, 9, 8, 9, 9,10,12,12,11, 7, 8, 8, + 10,10,10,10, 9, 9,10,10,11,13,13,12, 7, 8, 8, 9, + 11,11,10, 9, 9,11,10,12,11,11,14, 8, 9, 9,11,10, + 11,11,10,10,11,11,13,12,14,12, 8, 9, 9,11,12,11, + 11,10,10,12,11,12,12,12,14, 7, 8, 8, 9, 9,10,10, + 10,11,12,11,13,13,14,12, 7, 8, 9, 9, 9,10,10,11, + 11,11,12,12,14,14,14, 8,10, 9,10,11,11,11,11,14, + 12,12,13,14,14,13, 9, 9, 9,10,11,11,11,12,12,12, + 14,12,14,13,14,10,10,10,12,11,12,11,14,13,14,13, + 14,14,13,14, 9,10,10,11,12,11,13,12,13,13,14,14, + 14,13,14,10,13,13,12,12,11,12,14,13,14,13,14,12, + 14,13,10,11,11,12,11,12,12,14,14,14,13,14,14,14, + 14, +}; + +static float _vq_quantthresh__16u2_p8_0[] = { + -136.5, -115.5, -94.5, -73.5, -52.5, -31.5, -10.5, 10.5, + 31.5, 52.5, 73.5, 94.5, 115.5, 136.5, +}; + +static long _vq_quantmap__16u2_p8_0[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__16u2_p8_0 = { + _vq_quantthresh__16u2_p8_0, + _vq_quantmap__16u2_p8_0, + 15, + 15 +}; + +static static_codebook _16u2_p8_0 = { + 2, 225, + _vq_lengthlist__16u2_p8_0, + 1, -520986624, 1620377600, 4, 0, + _vq_quantlist__16u2_p8_0, + NULL, + &_vq_auxt__16u2_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u2_p8_1[] = { + 10, + 9, + 11, + 8, + 12, + 7, + 13, + 6, + 14, + 5, + 15, + 4, + 16, + 3, + 17, + 2, + 18, + 1, + 19, + 0, + 20, +}; + +static long _vq_lengthlist__16u2_p8_1[] = { + 2, 5, 5, 7, 7, 8, 8, 8, 8, 9, 9,10, 9,10, 9, 9, + 9,10,10,10,10, 5, 6, 6, 7, 7, 8, 8, 9, 9, 9, 9, + 10, 9,10,10,10,10,10,10,11,10, 5, 6, 6, 7, 7, 8, + 8, 8, 9, 9,10,10,10,10,10,10,10,10,10,10,10, 7, + 7, 7, 8, 8, 9, 8, 9, 9,10, 9,10,10,10,10,10,10, + 11,10,11,10, 7, 7, 7, 8, 8, 8, 9, 9, 9,10, 9,10, + 10,10,10,10,10,10,10,10,10, 8, 8, 8, 9, 9, 9, 9, + 10, 9,10,10,10,10,10,10,10,11,10,10,11,10, 8, 8, + 8, 8, 9, 9, 9, 9,10,10,10,10,10,10,10,10,10,11, + 11,10,10, 8, 9, 9, 9, 9, 9, 9,10,10,10,10,10,10, + 11,10,11,10,11,10,11,10, 8, 9, 9, 9, 9, 9,10,10, + 10,10,10,10,10,10,10,10,11,11,10,10,10, 9,10, 9, + 9,10,10,10,11,10,10,10,10,10,10,10,10,11,11,11, + 11,11, 9, 9, 9,10, 9,10,10,10,10,10,10,11,10,11, + 10,11,11,11,11,10,10, 9,10, 9,10,10,10,10,11,10, + 10,10,10,10,11,10,11,10,11,10,10,11, 9,10,10,10, + 10,10,10,10,10,10,11,10,10,11,11,10,11,11,11,11, + 11, 9, 9,10,10,10,10,10,11,10,10,11,10,10,11,10, + 10,11,11,11,11,11, 9,10,10,10,10,10,10,10,11,10, + 11,10,11,10,11,11,11,11,11,10,11,10,10,10,10,10, + 10,10,10,10,11,11,11,11,11,11,11,11,11,10,11,11, + 10,10,10,10,10,11,10,10,10,11,10,11,11,11,11,10, + 12,11,11,11,10,10,10,10,10,10,11,10,10,10,11,11, + 12,11,11,11,11,11,11,11,11,11,10,10,10,11,10,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,10, + 10,10,11,10,11,10,10,11,11,11,11,11,11,11,11,11, + 11,11,11,10,10,10,10,10,10,10,11,11,10,11,11,10, + 11,11,10,11,11,11,10,11,11, +}; + +static float _vq_quantthresh__16u2_p8_1[] = { + -9.5, -8.5, -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, + -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, + 6.5, 7.5, 8.5, 9.5, +}; + +static long _vq_quantmap__16u2_p8_1[] = { + 19, 17, 15, 13, 11, 9, 7, 5, + 3, 1, 0, 2, 4, 6, 8, 10, + 12, 14, 16, 18, 20, +}; + +static encode_aux_threshmatch _vq_auxt__16u2_p8_1 = { + _vq_quantthresh__16u2_p8_1, + _vq_quantmap__16u2_p8_1, + 21, + 21 +}; + +static static_codebook _16u2_p8_1 = { + 2, 441, + _vq_lengthlist__16u2_p8_1, + 1, -529268736, 1611661312, 5, 0, + _vq_quantlist__16u2_p8_1, + NULL, + &_vq_auxt__16u2_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__16u2_p9_0[] = { + 5586, + 4655, + 6517, + 3724, + 7448, + 2793, + 8379, + 1862, + 9310, + 931, + 10241, + 0, + 11172, + 5521, + 5651, +}; + +static long _vq_lengthlist__16u2_p9_0[] = { + 1,10,10,10,10,10,10,10,10,10,10,10,10, 5, 4,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10, 4,10,10,10,10,10,10,10,10,10,10,10,10, + 6, 6, 5,10,10,10,10, 9, 9, 9, 9, 9, 9, 9, 9, 5, + 5, +}; + +static float _vq_quantthresh__16u2_p9_0[] = { + -5120.5, -4189.5, -3258.5, -2327.5, -1396.5, -498, -32.5, 32.5, + 498, 1396.5, 2327.5, 3258.5, 4189.5, 5120.5, +}; + +static long _vq_quantmap__16u2_p9_0[] = { + 11, 9, 7, 5, 3, 1, 13, 0, + 14, 2, 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__16u2_p9_0 = { + _vq_quantthresh__16u2_p9_0, + _vq_quantmap__16u2_p9_0, + 15, + 15 +}; + +static static_codebook _16u2_p9_0 = { + 2, 225, + _vq_lengthlist__16u2_p9_0, + 1, -510275072, 1611661312, 14, 0, + _vq_quantlist__16u2_p9_0, + NULL, + &_vq_auxt__16u2_p9_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u2_p9_1[] = { + 392, + 343, + 441, + 294, + 490, + 245, + 539, + 196, + 588, + 147, + 637, + 98, + 686, + 49, + 735, + 0, + 784, + 388, + 396, +}; + +static long _vq_lengthlist__16u2_p9_1[] = { + 1,12,10,12,10,12,10,12,11,12,12,12,12,12,12,12, + 12, 5, 5, 9,10,12,11,11,12,12,12,12,12,12,12,12, + 12,12,12,12,10, 9, 9,11, 9,11,11,12,11,12,12,12, + 12,12,12,12,12,12,12, 8, 8,10,11, 9,12,11,12,12, + 12,12,12,12,12,12,12,12,12,12, 9, 8,10,11,12,11, + 12,11,12,12,12,12,12,12,12,12,12,12,12, 8, 9,11, + 11,10,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 9,10,11,12,11,12,11,12,12,12,12,12,12,12,12,12, + 12,12,12, 9, 9,11,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11, 5, 8, 9, 9, 8,11, 9,11,11,11,11,11,11, + 11,11,11,11, 5, 5, 4, 8, 8, 8, 8,10, 9,10,10,11, + 11,11,11,11,11,11,11, 5, 4, +}; + +static float _vq_quantthresh__16u2_p9_1[] = { + -367.5, -318.5, -269.5, -220.5, -171.5, -122.5, -73.5, -26.5, + -2, 2, 26.5, 73.5, 122.5, 171.5, 220.5, 269.5, + 318.5, 367.5, +}; + +static long _vq_quantmap__16u2_p9_1[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 17, 0, 18, 2, 4, 6, 8, 10, + 12, 14, 16, +}; + +static encode_aux_threshmatch _vq_auxt__16u2_p9_1 = { + _vq_quantthresh__16u2_p9_1, + _vq_quantmap__16u2_p9_1, + 19, + 19 +}; + +static static_codebook _16u2_p9_1 = { + 2, 361, + _vq_lengthlist__16u2_p9_1, + 1, -518488064, 1611661312, 10, 0, + _vq_quantlist__16u2_p9_1, + NULL, + &_vq_auxt__16u2_p9_1, + NULL, + 0 +}; + +static long _vq_quantlist__16u2_p9_2[] = { + 24, + 23, + 25, + 22, + 26, + 21, + 27, + 20, + 28, + 19, + 29, + 18, + 30, + 17, + 31, + 16, + 32, + 15, + 33, + 14, + 34, + 13, + 35, + 12, + 36, + 11, + 37, + 10, + 38, + 9, + 39, + 8, + 40, + 7, + 41, + 6, + 42, + 5, + 43, + 4, + 44, + 3, + 45, + 2, + 46, + 1, + 47, + 0, + 48, +}; + +static long _vq_lengthlist__16u2_p9_2[] = { + 1, 3, 3, 4, 7, 7, 7, 8, 7, 7, 7, 7, 8, 8, 8, 8, + 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 9, 9, 8, 9, 9, + 9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,12,12,10, + 11, +}; + +static float _vq_quantthresh__16u2_p9_2[] = { + -23.5, -22.5, -21.5, -20.5, -19.5, -18.5, -17.5, -16.5, + -15.5, -14.5, -13.5, -12.5, -11.5, -10.5, -9.5, -8.5, + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, + 8.5, 9.5, 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, + 16.5, 17.5, 18.5, 19.5, 20.5, 21.5, 22.5, 23.5, +}; + +static long _vq_quantmap__16u2_p9_2[] = { + 47, 45, 43, 41, 39, 37, 35, 33, + 31, 29, 27, 25, 23, 21, 19, 17, + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, + 48, +}; + +static encode_aux_threshmatch _vq_auxt__16u2_p9_2 = { + _vq_quantthresh__16u2_p9_2, + _vq_quantmap__16u2_p9_2, + 49, + 49 +}; + +static static_codebook _16u2_p9_2 = { + 1, 49, + _vq_lengthlist__16u2_p9_2, + 1, -526909440, 1611661312, 6, 0, + _vq_quantlist__16u2_p9_2, + NULL, + &_vq_auxt__16u2_p9_2, + NULL, + 0 +}; + +static long _vq_quantlist__8u0__p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__8u0__p1_0[] = { + 1, 4, 4, 5, 7, 7, 5, 7, 7, 5, 8, 8, 8,10,10, 7, + 10,10, 5, 8, 8, 7,10,10, 8,10,10, 4, 9, 8, 8,11, + 11, 8,11,11, 7,11,11,10,11,13,10,13,13, 7,11,11, + 10,13,12,10,13,13, 5, 9, 8, 8,11,11, 8,11,11, 7, + 11,11, 9,13,13,10,12,13, 7,11,11,10,13,13,10,13, + 11, +}; + +static float _vq_quantthresh__8u0__p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__8u0__p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__8u0__p1_0 = { + _vq_quantthresh__8u0__p1_0, + _vq_quantmap__8u0__p1_0, + 3, + 3 +}; + +static static_codebook _8u0__p1_0 = { + 4, 81, + _vq_lengthlist__8u0__p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__8u0__p1_0, + NULL, + &_vq_auxt__8u0__p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__8u0__p2_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__8u0__p2_0[] = { + 2, 4, 4, 5, 6, 6, 5, 6, 6, 5, 7, 7, 6, 7, 8, 6, + 7, 8, 5, 7, 7, 6, 8, 8, 7, 9, 7, 5, 7, 7, 7, 9, + 9, 7, 8, 8, 6, 9, 8, 7, 7,10, 8,10,10, 6, 8, 8, + 8,10, 8, 8,10,10, 5, 7, 7, 7, 8, 8, 7, 8, 9, 6, + 8, 8, 8,10,10, 8, 8,10, 6, 8, 9, 8,10,10, 7,10, + 8, +}; + +static float _vq_quantthresh__8u0__p2_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__8u0__p2_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__8u0__p2_0 = { + _vq_quantthresh__8u0__p2_0, + _vq_quantmap__8u0__p2_0, + 3, + 3 +}; + +static static_codebook _8u0__p2_0 = { + 4, 81, + _vq_lengthlist__8u0__p2_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__8u0__p2_0, + NULL, + &_vq_auxt__8u0__p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__8u0__p3_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__8u0__p3_0[] = { + 1, 5, 5, 7, 7, 6, 7, 7, 9, 9, 6, 7, 7, 9, 9, 8, + 10, 9,11,11, 8, 9, 9,11,11, 6, 8, 8,10,10, 8,10, + 10,11,11, 8,10,10,11,11,10,11,11,12,12,10,11,11, + 12,13, 6, 8, 8,10,10, 8,10,10,11,11, 8,10,10,11, + 11, 9,10,11,12,12,10,11,11,12,12, 8,11,11,14,13, + 10,12,11,15,13,10,12,11,14,14,12,13,12,16,14,12, + 14,12,16,15, 8,11,11,13,14,10,11,12,13,15,10,11, + 12,13,15,11,12,13,14,15,12,12,14,14,16, 5, 8, 8, + 11,11, 9,11,11,12,12, 8,10,11,12,12,11,12,12,15, + 14,11,12,12,14,14, 7,11,10,13,12,10,11,12,13,14, + 10,12,12,14,13,12,13,13,14,15,12,13,13,15,15, 7, + 10,11,12,13,10,12,11,14,13,10,12,13,13,15,12,13, + 12,14,14,11,13,13,15,16, 9,12,12,15,14,11,13,13, + 15,16,11,13,13,16,16,13,14,15,15,15,12,14,15,17, + 16, 9,12,12,14,15,11,13,13,15,16,11,13,13,16,18, + 13,14,14,17,16,13,15,15,17,18, 5, 8, 9,11,11, 8, + 11,11,12,12, 8,10,11,12,12,11,12,12,14,14,11,12, + 12,14,15, 7,11,10,12,13,10,12,12,14,13,10,11,12, + 13,14,11,13,13,15,14,12,13,13,14,15, 7,10,11,13, + 13,10,12,12,13,14,10,12,12,13,13,11,13,13,16,16, + 12,13,13,15,14, 9,12,12,16,15,10,13,13,15,15,11, + 13,13,17,15,12,15,15,18,17,13,14,14,15,16, 9,12, + 12,15,15,11,13,13,15,16,11,13,13,15,15,12,15,15, + 16,16,13,15,14,17,15, 7,11,11,15,15,10,13,13,16, + 15,10,13,13,15,16,14,15,15,17,19,13,15,14,15,18, + 9,12,12,16,16,11,13,14,17,16,11,13,13,17,16,15, + 15,16,17,19,13,15,16, 0,18, 9,12,12,16,15,11,14, + 13,17,17,11,13,14,16,16,15,16,16,19,18,13,15,15, + 17,19,11,14,14,19,16,12,14,15, 0,18,12,16,15,18, + 17,15,15,18,16,19,14,15,17,19,19,11,14,14,18,19, + 13,15,14,19,19,12,16,15,18,17,15,17,15, 0,16,14, + 17,16,19, 0, 7,11,11,14,14,10,12,12,15,15,10,13, + 13,16,15,13,15,15,17, 0,14,15,15,16,19, 9,12,12, + 16,16,11,14,14,16,16,11,13,13,16,16,14,17,16,19, + 0,14,18,17,17,19, 9,12,12,15,16,11,13,13,15,17, + 12,14,13,19,16,13,15,15,17,19,15,17,16,17,19,11, + 14,14,19,16,12,15,15,19,17,13,14,15,17,19,14,16, + 17,19,19,16,15,16,17,19,11,15,14,16,16,12,15,15, + 19, 0,12,14,15,19,19,14,16,16, 0,18,15,19,14,18, + 16, +}; + +static float _vq_quantthresh__8u0__p3_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__8u0__p3_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__8u0__p3_0 = { + _vq_quantthresh__8u0__p3_0, + _vq_quantmap__8u0__p3_0, + 5, + 5 +}; + +static static_codebook _8u0__p3_0 = { + 4, 625, + _vq_lengthlist__8u0__p3_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__8u0__p3_0, + NULL, + &_vq_auxt__8u0__p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__8u0__p4_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__8u0__p4_0[] = { + 3, 5, 5, 8, 8, 5, 6, 7, 9, 9, 6, 7, 6, 9, 9, 9, + 9, 9,10,11, 9, 9, 9,11,10, 6, 7, 7,10,10, 7, 7, + 8,10,10, 7, 8, 8,10,10,10,10,10,10,11, 9,10,10, + 11,12, 6, 7, 7,10,10, 7, 8, 8,10,10, 7, 8, 7,10, + 10, 9,10,10,12,11,10,10,10,11,10, 9,10,10,12,11, + 10,10,10,13,11, 9,10,10,12,12,11,11,12,12,13,11, + 11,11,12,13, 9,10,10,12,12,10,10,11,12,12,10,10, + 11,12,12,11,11,11,13,13,11,12,12,13,13, 5, 7, 7, + 10,10, 7, 8, 8,10,10, 7, 8, 8,10,10,10,11,11,12, + 12,10,11,10,12,12, 7, 8, 8,11,11, 7, 8, 9,10,11, + 8, 9, 9,11,11,11,10,11,10,12,10,11,11,12,13, 7, + 8, 8,10,11, 8, 9, 8,12,10, 8, 9, 9,11,12,10,11, + 10,13,11,10,11,11,13,12, 9,11,10,13,12,10,10,11, + 12,12,10,11,11,13,13,12,10,13,11,14,11,12,12,15, + 13, 9,11,11,13,13,10,11,11,13,12,10,11,11,12,14, + 12,13,11,14,12,12,12,12,14,14, 5, 7, 7,10,10, 7, + 8, 8,10,10, 7, 8, 8,11,10,10,11,11,12,12,10,11, + 10,12,12, 7, 8, 8,10,11, 8, 9, 9,12,11, 8, 8, 9, + 10,11,10,11,11,12,13,11,10,11,11,13, 6, 8, 8,10, + 11, 8, 9, 9,11,11, 7, 9, 7,11,10,10,11,11,12,12, + 10,11,10,13,10, 9,11,10,13,12,10,12,11,13,13,10, + 10,11,12,13,11,12,13,15,14,11,11,13,12,13, 9,10, + 11,12,13,10,11,11,12,13,10,11,10,13,12,12,13,13, + 13,14,12,12,11,14,11, 8,10,10,12,13,10,11,11,13, + 13,10,11,10,13,13,12,13,14,15,14,12,12,12,14,13, + 9,10,10,13,12,10,10,12,13,13,10,11,11,15,12,12, + 12,13,15,14,12,13,13,15,13, 9,10,11,12,13,10,12, + 10,13,12,10,11,11,12,13,12,14,12,15,13,12,12,12, + 15,14,11,12,11,14,13,11,11,12,14,14,12,13,13,14, + 13,13,11,15,11,15,14,14,14,16,15,11,12,12,13,14, + 11,13,11,14,14,12,12,13,14,15,12,14,12,15,12,13, + 15,14,16,15, 8,10,10,12,12,10,10,10,12,13,10,11, + 11,13,13,12,12,12,13,14,13,13,13,15,15, 9,10,10, + 12,12,10,11,11,13,12,10,10,11,13,13,12,12,12,14, + 14,12,12,13,15,14, 9,10,10,13,12,10,10,12,12,13, + 10,11,10,13,13,12,13,13,14,14,12,13,12,14,13,11, + 12,12,14,13,12,13,12,14,14,10,12,12,14,14,14,14, + 14,16,14,13,12,14,12,15,10,12,12,14,15,12,13,13, + 14,16,11,12,11,15,14,13,14,14,14,15,13,14,11,14, + 12, +}; + +static float _vq_quantthresh__8u0__p4_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__8u0__p4_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__8u0__p4_0 = { + _vq_quantthresh__8u0__p4_0, + _vq_quantmap__8u0__p4_0, + 5, + 5 +}; + +static static_codebook _8u0__p4_0 = { + 4, 625, + _vq_lengthlist__8u0__p4_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__8u0__p4_0, + NULL, + &_vq_auxt__8u0__p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__8u0__p5_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__8u0__p5_0[] = { + 1, 4, 4, 7, 7, 7, 7, 9, 9, 4, 6, 6, 8, 7, 8, 8, + 10,10, 4, 6, 6, 8, 8, 8, 8,10,10, 6, 8, 8, 9, 9, + 9, 9,11,11, 7, 8, 8, 9, 9, 9, 9,11,11, 7, 8, 8, + 9, 9,10,10,12,11, 7, 8, 8, 9, 9,10,10,11,11, 9, + 10,10,11,11,11,12,12,12, 9,10,10,11,11,12,12,12, + 12, +}; + +static float _vq_quantthresh__8u0__p5_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__8u0__p5_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__8u0__p5_0 = { + _vq_quantthresh__8u0__p5_0, + _vq_quantmap__8u0__p5_0, + 9, + 9 +}; + +static static_codebook _8u0__p5_0 = { + 2, 81, + _vq_lengthlist__8u0__p5_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__8u0__p5_0, + NULL, + &_vq_auxt__8u0__p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__8u0__p6_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__8u0__p6_0[] = { + 1, 4, 4, 7, 7, 9, 9,11,11,12,12,16,16, 3, 6, 6, + 9, 9,11,11,12,12,13,14,18,16, 3, 6, 7, 9, 9,11, + 11,13,12,14,14,17,16, 7, 9, 9,11,11,12,12,14,14, + 14,14,17,16, 7, 9, 9,11,11,13,12,13,13,14,14,17, + 0, 9,11,11,12,13,14,14,14,13,15,14,17,17, 9,11, + 11,12,12,14,14,13,14,14,15, 0, 0,11,12,12,15,14, + 15,14,15,14,15,16,17, 0,11,12,13,13,13,14,14,15, + 14,15,15, 0, 0,12,14,14,15,15,14,16,15,15,17,16, + 0,18,13,14,14,15,14,15,14,15,16,17,16, 0, 0,17, + 17,18, 0,16,18,16, 0, 0, 0,17, 0, 0,16, 0, 0,16, + 16, 0,15, 0,17, 0, 0, 0, 0, +}; + +static float _vq_quantthresh__8u0__p6_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__8u0__p6_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__8u0__p6_0 = { + _vq_quantthresh__8u0__p6_0, + _vq_quantmap__8u0__p6_0, + 13, + 13 +}; + +static static_codebook _8u0__p6_0 = { + 2, 169, + _vq_lengthlist__8u0__p6_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__8u0__p6_0, + NULL, + &_vq_auxt__8u0__p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__8u0__p6_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__8u0__p6_1[] = { + 1, 4, 4, 6, 6, 4, 6, 5, 7, 7, 4, 5, 6, 7, 7, 6, + 7, 7, 7, 7, 6, 7, 7, 7, 7, +}; + +static float _vq_quantthresh__8u0__p6_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__8u0__p6_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__8u0__p6_1 = { + _vq_quantthresh__8u0__p6_1, + _vq_quantmap__8u0__p6_1, + 5, + 5 +}; + +static static_codebook _8u0__p6_1 = { + 2, 25, + _vq_lengthlist__8u0__p6_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__8u0__p6_1, + NULL, + &_vq_auxt__8u0__p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__8u0__p7_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__8u0__p7_0[] = { + 1, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, +}; + +static float _vq_quantthresh__8u0__p7_0[] = { + -157.5, 157.5, +}; + +static long _vq_quantmap__8u0__p7_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__8u0__p7_0 = { + _vq_quantthresh__8u0__p7_0, + _vq_quantmap__8u0__p7_0, + 3, + 3 +}; + +static static_codebook _8u0__p7_0 = { + 4, 81, + _vq_lengthlist__8u0__p7_0, + 1, -518803456, 1628680192, 2, 0, + _vq_quantlist__8u0__p7_0, + NULL, + &_vq_auxt__8u0__p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__8u0__p7_1[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__8u0__p7_1[] = { + 1, 5, 5, 5, 5,10,10,11,11,11,11,11,11,11,11, 5, + 7, 6, 8, 8, 9,10,11,11,11,11,11,11,11,11, 6, 6, + 7, 9, 7,11,10,11,11,11,11,11,11,11,11, 5, 6, 6, + 11, 8,11,11,11,11,11,11,11,11,11,11, 5, 6, 6, 9, + 10,11,10,11,11,11,11,11,11,11,11, 7,10,10,11,11, + 11,11,11,11,11,11,11,11,11,11, 7,11, 8,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10, +}; + +static float _vq_quantthresh__8u0__p7_1[] = { + -136.5, -115.5, -94.5, -73.5, -52.5, -31.5, -10.5, 10.5, + 31.5, 52.5, 73.5, 94.5, 115.5, 136.5, +}; + +static long _vq_quantmap__8u0__p7_1[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__8u0__p7_1 = { + _vq_quantthresh__8u0__p7_1, + _vq_quantmap__8u0__p7_1, + 15, + 15 +}; + +static static_codebook _8u0__p7_1 = { + 2, 225, + _vq_lengthlist__8u0__p7_1, + 1, -520986624, 1620377600, 4, 0, + _vq_quantlist__8u0__p7_1, + NULL, + &_vq_auxt__8u0__p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__8u0__p7_2[] = { + 10, + 9, + 11, + 8, + 12, + 7, + 13, + 6, + 14, + 5, + 15, + 4, + 16, + 3, + 17, + 2, + 18, + 1, + 19, + 0, + 20, +}; + +static long _vq_lengthlist__8u0__p7_2[] = { + 1, 6, 5, 7, 7, 9, 9, 9, 9,10,12,12,10,11,11,10, + 11,11,11,10,11, 6, 8, 8, 9, 9,10,10, 9,10,11,11, + 10,11,11,11,11,10,11,11,11,11, 6, 7, 8, 9, 9, 9, + 10,11,10,11,12,11,10,11,11,11,11,11,11,12,10, 8, + 9, 9,10, 9,10,10, 9,10,10,10,10,10, 9,10,10,10, + 10, 9,10,10, 9, 9, 9, 9,10,10, 9, 9,10,10,11,10, + 9,12,10,11,10, 9,10,10,10, 8, 9, 9,10, 9,10, 9, + 9,10,10, 9,10, 9,11,10,10,10,10,10, 9,10, 8, 8, + 9, 9,10, 9,11, 9, 8, 9, 9,10,11,10,10,10,11,12, + 9, 9,11, 8, 9, 8,11,10,11,10,10, 9,11,10,10,10, + 10,10,10,10,11,11,11,11, 8, 9, 9, 9,10,10,10,11, + 11,12,11,12,11,10,10,10,12,11,11,11,10, 8,10, 9, + 11,10,10,11,12,10,11,12,11,11,12,11,12,12,10,11, + 11,10, 9, 9,10,11,12,10,10,10,11,10,11,11,10,12, + 12,10,11,10,11,12,10, 9,10,10,11,10,11,11,11,11, + 11,12,11,11,11, 9,11,10,11,10,11,10, 9, 9,10,11, + 11,11,10,10,11,12,12,11,12,11,11,11,12,12,12,12, + 11, 9,11,11,12,10,11,11,11,11,11,11,12,11,11,12, + 11,11,11,10,11,11, 9,11,10,11,11,11,10,10,10,11, + 11,11,12,10,11,10,11,11,11,11,12, 9,11,10,11,11, + 10,10,11,11, 9,11,11,12,10,10,10,10,10,11,11,10, + 9,10,11,11,12,11,10,10,12,11,11,12,11,12,11,11, + 10,10,11,11,10,12,11,10,11,10,11,10,10,10,11,11, + 10,10,11,11,11,11,10,10,10,12,11,11,11,11,10, 9, + 10,11,11,11,12,11,11,11,12,10,11,11,11, 9,10,11, + 11,11,11,11,11,10,10,11,11,12,11,10,11,12,11,10, + 10,11, 9,10,11,11,11,11,11,10,11,11,10,12,11,11, + 11,12,11,11,11,10,10,11,11, +}; + +static float _vq_quantthresh__8u0__p7_2[] = { + -9.5, -8.5, -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, + -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, + 6.5, 7.5, 8.5, 9.5, +}; + +static long _vq_quantmap__8u0__p7_2[] = { + 19, 17, 15, 13, 11, 9, 7, 5, + 3, 1, 0, 2, 4, 6, 8, 10, + 12, 14, 16, 18, 20, +}; + +static encode_aux_threshmatch _vq_auxt__8u0__p7_2 = { + _vq_quantthresh__8u0__p7_2, + _vq_quantmap__8u0__p7_2, + 21, + 21 +}; + +static static_codebook _8u0__p7_2 = { + 2, 441, + _vq_lengthlist__8u0__p7_2, + 1, -529268736, 1611661312, 5, 0, + _vq_quantlist__8u0__p7_2, + NULL, + &_vq_auxt__8u0__p7_2, + NULL, + 0 +}; + +static long _huff_lengthlist__8u0__single[] = { + 4, 7,11, 9,12, 8, 7,10, 6, 4, 5, 5, 7, 5, 6,16, + 9, 5, 5, 6, 7, 7, 9,16, 7, 4, 6, 5, 7, 5, 7,17, + 10, 7, 7, 8, 7, 7, 8,18, 7, 5, 6, 4, 5, 4, 5,15, + 7, 6, 7, 5, 6, 4, 5,15,12,13,18,12,17,11, 9,17, +}; + +static static_codebook _huff_book__8u0__single = { + 2, 64, + _huff_lengthlist__8u0__single, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__8u1__p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__8u1__p1_0[] = { + 1, 4, 4, 5, 7, 7, 5, 7, 7, 5, 8, 8, 7, 9,10, 7, + 9, 9, 5, 8, 8, 7,10, 9, 7, 9, 9, 5, 8, 8, 8,10, + 10, 8,10,10, 7,10,10, 9,10,12,10,12,12, 7,10,10, + 9,12,11,10,12,12, 5, 8, 8, 8,10,10, 8,10,10, 7, + 10,10,10,12,12, 9,11,12, 7,10,10,10,12,12, 9,12, + 10, +}; + +static float _vq_quantthresh__8u1__p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__8u1__p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__8u1__p1_0 = { + _vq_quantthresh__8u1__p1_0, + _vq_quantmap__8u1__p1_0, + 3, + 3 +}; + +static static_codebook _8u1__p1_0 = { + 4, 81, + _vq_lengthlist__8u1__p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__8u1__p1_0, + NULL, + &_vq_auxt__8u1__p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__8u1__p2_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__8u1__p2_0[] = { + 3, 4, 5, 5, 6, 6, 5, 6, 6, 5, 7, 6, 6, 7, 8, 6, + 7, 8, 5, 6, 6, 6, 8, 7, 6, 8, 7, 5, 6, 6, 7, 8, + 8, 6, 7, 7, 6, 8, 7, 7, 7, 9, 8, 9, 9, 6, 7, 8, + 7, 9, 7, 8, 9, 9, 5, 6, 6, 6, 7, 7, 7, 8, 8, 6, + 8, 7, 8, 9, 9, 7, 7, 9, 6, 7, 8, 8, 9, 9, 7, 9, + 7, +}; + +static float _vq_quantthresh__8u1__p2_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__8u1__p2_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__8u1__p2_0 = { + _vq_quantthresh__8u1__p2_0, + _vq_quantmap__8u1__p2_0, + 3, + 3 +}; + +static static_codebook _8u1__p2_0 = { + 4, 81, + _vq_lengthlist__8u1__p2_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__8u1__p2_0, + NULL, + &_vq_auxt__8u1__p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__8u1__p3_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__8u1__p3_0[] = { + 1, 5, 5, 7, 7, 6, 7, 7, 9, 9, 6, 7, 7, 9, 9, 8, + 10, 9,11,11, 9, 9, 9,11,11, 6, 8, 8,10,10, 8,10, + 10,11,11, 8, 9,10,11,11,10,11,11,12,12,10,11,11, + 12,13, 6, 8, 8,10,10, 8,10, 9,11,11, 8,10, 9,11, + 11,10,11,11,12,12,10,11,11,12,12, 9,11,11,14,13, + 10,12,11,14,14,10,12,11,14,13,12,13,13,15,14,12, + 13,13,15,14, 8,11,11,13,14,10,11,12,13,15,10,11, + 12,14,14,12,13,13,14,15,12,13,13,14,15, 5, 8, 8, + 11,11, 8,10,10,12,12, 8,10,10,12,12,11,12,12,14, + 13,11,12,12,13,14, 8,10,10,12,12, 9,11,12,13,14, + 10,12,12,13,13,12,12,13,14,14,11,13,13,15,15, 7, + 10,10,12,12, 9,12,11,14,12,10,11,12,13,14,12,13, + 12,14,14,12,13,13,15,16,10,12,12,15,14,11,12,13, + 15,15,11,13,13,15,16,14,14,15,15,16,13,14,15,17, + 15, 9,12,12,14,15,11,13,12,15,15,11,13,13,15,15, + 13,14,13,15,14,13,14,14,17, 0, 5, 8, 8,11,11, 8, + 10,10,12,12, 8,10,10,12,12,11,12,12,14,14,11,12, + 12,14,14, 7,10,10,12,12,10,12,12,13,13, 9,11,12, + 12,13,11,12,13,15,15,11,12,13,14,15, 8,10,10,12, + 12,10,12,11,13,13,10,12,11,13,13,11,13,13,15,14, + 12,13,12,15,13, 9,12,12,14,14,11,13,13,16,15,11, + 12,13,16,15,13,14,15,16,16,13,13,15,15,16,10,12, + 12,15,14,11,13,13,14,16,11,13,13,15,16,13,15,15, + 16,17,13,15,14,16,15, 8,11,11,14,15,10,12,12,15, + 15,10,12,12,15,16,14,15,15,16,17,13,14,14,16,16, + 9,12,12,15,15,11,13,14,15,17,11,13,13,15,16,14, + 15,16,19,17,13,15,15, 0,17, 9,12,12,15,15,11,14, + 13,16,15,11,13,13,15,16,15,15,15,18,17,13,15,15, + 17,17,11,15,14,18,16,12,14,15,17,17,12,15,15,18, + 18,15,15,16,15,19,14,16,16, 0, 0,11,14,14,16,17, + 12,15,14,18,17,12,15,15,18,18,15,17,15,18,16,14, + 16,16,18,18, 7,11,11,14,14,10,12,12,15,15,10,12, + 13,15,15,13,14,15,16,16,14,15,15,18,18, 9,12,12, + 15,15,11,13,13,16,15,11,12,13,16,16,14,15,15,17, + 16,15,16,16,17,17, 9,12,12,15,15,11,13,13,15,17, + 11,14,13,16,15,13,15,15,17,17,15,15,15,18,17,11, + 14,14,17,15,12,14,15,17,18,13,13,15,17,17,14,16, + 16,19,18,16,15,17,17, 0,11,14,14,17,17,12,15,15, + 18, 0,12,15,14,18,16,14,17,17,19, 0,16,18,15, 0, + 16, +}; + +static float _vq_quantthresh__8u1__p3_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__8u1__p3_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__8u1__p3_0 = { + _vq_quantthresh__8u1__p3_0, + _vq_quantmap__8u1__p3_0, + 5, + 5 +}; + +static static_codebook _8u1__p3_0 = { + 4, 625, + _vq_lengthlist__8u1__p3_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__8u1__p3_0, + NULL, + &_vq_auxt__8u1__p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__8u1__p4_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__8u1__p4_0[] = { + 4, 5, 5, 9, 9, 6, 7, 7, 9, 9, 6, 7, 7, 9, 9, 9, + 9, 9,11,11, 9, 9, 9,11,11, 6, 7, 7, 9, 9, 7, 7, + 8, 9,10, 7, 7, 8, 9,10, 9, 9,10,10,11, 9, 9,10, + 10,12, 6, 7, 7, 9, 9, 7, 8, 7,10, 9, 7, 8, 7,10, + 9, 9,10, 9,12,11,10,10, 9,12,10, 9,10,10,12,11, + 9,10,10,12,11, 9,10,10,12,12,11,11,12,12,13,11, + 11,12,12,13, 9, 9,10,12,11, 9,10,10,12,12,10,10, + 10,12,12,11,12,11,13,12,11,12,11,13,12, 6, 7, 7, + 9, 9, 7, 8, 8,10,10, 7, 8, 7,10, 9,10,10,10,12, + 12,10,10,10,12,11, 7, 8, 7,10,10, 7, 7, 9,10,11, + 8, 9, 9,11,10,10,10,11,10,12,10,10,11,12,12, 7, + 8, 8,10,10, 7, 9, 8,11,10, 8, 8, 9,11,11,10,11, + 10,12,11,10,11,11,12,12, 9,10,10,12,12, 9,10,10, + 12,12,10,11,11,13,12,11,10,12,10,14,12,12,12,13, + 14, 9,10,10,12,12, 9,11,10,12,12,10,11,11,12,12, + 11,12,11,14,12,12,12,12,14,14, 5, 7, 7, 9, 9, 7, + 7, 7, 9,10, 7, 8, 8,10,10,10,10,10,11,11,10,10, + 10,12,12, 7, 8, 8,10,10, 8, 9, 8,11,10, 7, 8, 9, + 10,11,10,10,10,11,12,10,10,11,11,13, 6, 7, 8,10, + 10, 8, 9, 9,10,10, 7, 9, 7,11,10,10,11,10,12,12, + 10,11,10,12,10, 9,10,10,12,12,10,11,11,13,12, 9, + 10,10,12,12,12,12,12,14,13,11,11,12,11,14, 9,10, + 10,11,12,10,11,11,12,13, 9,10,10,12,12,12,12,12, + 14,13,11,12,10,14,11, 9, 9,10,11,12, 9,10,10,12, + 12, 9,10,10,12,12,12,12,12,14,14,11,12,12,13,12, + 9,10, 9,12,12, 9,10,11,12,13,10,11,10,13,11,12, + 12,13,13,14,12,12,12,13,13, 9,10,10,12,12,10,11, + 10,13,12,10,10,11,12,13,12,13,12,14,13,12,12,12, + 13,14,11,12,11,14,13,10,10,11,13,13,12,12,12,14, + 13,12,10,14,10,15,13,14,14,14,14,11,11,12,13,14, + 10,12,11,13,13,12,12,12,13,15,12,13,11,15,12,13, + 13,14,14,14, 9,10, 9,12,12, 9,10,10,12,12,10,10, + 10,12,12,11,11,12,12,13,12,12,12,14,14, 9,10,10, + 12,12,10,11,10,13,12,10,10,11,12,13,12,12,12,14, + 13,12,12,13,13,14, 9,10,10,12,13,10,10,11,11,12, + 9,11,10,13,12,12,12,12,13,14,12,13,12,14,13,11, + 12,11,13,13,12,13,12,14,13,10,11,12,13,13,13,13, + 13,14,15,12,11,14,12,14,11,11,12,12,13,12,12,12, + 13,14,10,12,10,14,13,13,13,13,14,15,12,14,11,15, + 10, +}; + +static float _vq_quantthresh__8u1__p4_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__8u1__p4_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__8u1__p4_0 = { + _vq_quantthresh__8u1__p4_0, + _vq_quantmap__8u1__p4_0, + 5, + 5 +}; + +static static_codebook _8u1__p4_0 = { + 4, 625, + _vq_lengthlist__8u1__p4_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__8u1__p4_0, + NULL, + &_vq_auxt__8u1__p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__8u1__p5_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__8u1__p5_0[] = { + 1, 4, 4, 7, 7, 7, 7, 9, 9, 4, 6, 5, 8, 7, 8, 8, + 10,10, 4, 6, 6, 8, 8, 8, 8,10,10, 7, 8, 8, 9, 9, + 9, 9,11,11, 7, 8, 8, 9, 9, 9, 9,11,11, 8, 8, 8, + 9, 9,10,10,12,11, 8, 8, 8, 9, 9,10,10,11,11, 9, + 10,10,11,11,11,11,13,12, 9,10,10,11,11,12,12,12, + 13, +}; + +static float _vq_quantthresh__8u1__p5_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__8u1__p5_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__8u1__p5_0 = { + _vq_quantthresh__8u1__p5_0, + _vq_quantmap__8u1__p5_0, + 9, + 9 +}; + +static static_codebook _8u1__p5_0 = { + 2, 81, + _vq_lengthlist__8u1__p5_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__8u1__p5_0, + NULL, + &_vq_auxt__8u1__p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__8u1__p6_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__8u1__p6_0[] = { + 3, 4, 4, 6, 6, 7, 7, 9, 9, 4, 4, 5, 6, 6, 7, 7, + 9, 9, 4, 4, 4, 6, 6, 7, 7, 9, 9, 6, 6, 6, 7, 7, + 8, 8, 9, 9, 6, 6, 6, 7, 7, 8, 8, 9, 9, 7, 7, 7, + 8, 8, 8, 9,10,10, 7, 7, 7, 8, 8, 9, 8,10,10, 9, + 9, 9, 9, 9,10,10,10,10, 9, 9, 9, 9, 9,10,10,10, + 10, +}; + +static float _vq_quantthresh__8u1__p6_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__8u1__p6_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__8u1__p6_0 = { + _vq_quantthresh__8u1__p6_0, + _vq_quantmap__8u1__p6_0, + 9, + 9 +}; + +static static_codebook _8u1__p6_0 = { + 2, 81, + _vq_lengthlist__8u1__p6_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__8u1__p6_0, + NULL, + &_vq_auxt__8u1__p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__8u1__p7_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__8u1__p7_0[] = { + 1, 4, 4, 5, 7, 7, 5, 7, 7, 5, 9, 9, 8,10,10, 8, + 10,10, 5, 9, 9, 7,10,10, 8,10,10, 4,10,10, 9,12, + 12, 9,11,11, 7,12,11,10,11,13,10,13,13, 7,12,12, + 10,13,12,10,13,13, 4,10,10, 9,12,12, 9,12,12, 7, + 12,12,10,13,13,10,12,13, 7,11,12,10,13,13,10,13, + 11, +}; + +static float _vq_quantthresh__8u1__p7_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__8u1__p7_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__8u1__p7_0 = { + _vq_quantthresh__8u1__p7_0, + _vq_quantmap__8u1__p7_0, + 3, + 3 +}; + +static static_codebook _8u1__p7_0 = { + 4, 81, + _vq_lengthlist__8u1__p7_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__8u1__p7_0, + NULL, + &_vq_auxt__8u1__p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__8u1__p7_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__8u1__p7_1[] = { + 2, 4, 4, 6, 6, 7, 7, 8, 8, 8, 8, 4, 5, 5, 7, 7, + 8, 8, 9, 9, 9, 9, 4, 5, 5, 7, 7, 8, 8, 9, 9, 9, + 9, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 6, 7, 7, 8, + 8, 8, 8, 9, 9, 9, 9, 8, 8, 8, 8, 8, 9, 9, 9, 9, + 9, 9, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 8, 9, 9, + 9, 9, 9, 9,10,10,10,10, 8, 9, 9, 9, 9, 9, 9,10, + 10,10,10, 8, 9, 9, 9, 9, 9, 9,10,10,10,10, 8, 9, + 9, 9, 9, 9, 9,10,10,10,10, +}; + +static float _vq_quantthresh__8u1__p7_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__8u1__p7_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__8u1__p7_1 = { + _vq_quantthresh__8u1__p7_1, + _vq_quantmap__8u1__p7_1, + 11, + 11 +}; + +static static_codebook _8u1__p7_1 = { + 2, 121, + _vq_lengthlist__8u1__p7_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__8u1__p7_1, + NULL, + &_vq_auxt__8u1__p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__8u1__p8_0[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__8u1__p8_0[] = { + 1, 4, 4, 6, 6, 8, 8,10,10,11,11, 4, 6, 6, 7, 7, + 9, 9,11,11,13,12, 4, 6, 6, 7, 7, 9, 9,11,11,12, + 12, 6, 7, 7, 9, 9,11,11,12,12,13,13, 6, 7, 7, 9, + 9,11,11,12,12,13,13, 8, 9, 9,11,11,12,12,13,13, + 14,14, 8, 9, 9,11,11,12,12,13,13,14,14, 9,11,11, + 12,12,13,13,14,14,15,15, 9,11,11,12,12,13,13,14, + 14,15,14,11,12,12,13,13,14,14,15,15,16,16,11,12, + 12,13,13,14,14,15,15,15,15, +}; + +static float _vq_quantthresh__8u1__p8_0[] = { + -49.5, -38.5, -27.5, -16.5, -5.5, 5.5, 16.5, 27.5, + 38.5, 49.5, +}; + +static long _vq_quantmap__8u1__p8_0[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__8u1__p8_0 = { + _vq_quantthresh__8u1__p8_0, + _vq_quantmap__8u1__p8_0, + 11, + 11 +}; + +static static_codebook _8u1__p8_0 = { + 2, 121, + _vq_lengthlist__8u1__p8_0, + 1, -524582912, 1618345984, 4, 0, + _vq_quantlist__8u1__p8_0, + NULL, + &_vq_auxt__8u1__p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__8u1__p8_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__8u1__p8_1[] = { + 2, 5, 5, 6, 6, 7, 7, 7, 7, 8, 8, 5, 6, 6, 7, 7, + 7, 7, 8, 8, 8, 8, 5, 6, 6, 7, 7, 7, 7, 8, 8, 8, + 8, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 6, 7, 7, 7, + 7, 8, 8, 8, 8, 8, 8, 7, 7, 7, 8, 8, 8, 8, 8, 8, + 8, 8, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 8, 9, 9, 7, 8, 8, 8, 8, 8, 8, 9, + 8, 9, 9, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 8, 8, + 8, 8, 8, 8, 8, 9, 9, 9, 9, +}; + +static float _vq_quantthresh__8u1__p8_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__8u1__p8_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__8u1__p8_1 = { + _vq_quantthresh__8u1__p8_1, + _vq_quantmap__8u1__p8_1, + 11, + 11 +}; + +static static_codebook _8u1__p8_1 = { + 2, 121, + _vq_lengthlist__8u1__p8_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__8u1__p8_1, + NULL, + &_vq_auxt__8u1__p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__8u1__p9_0[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__8u1__p9_0[] = { + 1, 4, 4,11,11,11,11,11,11,11,11,11,11,11,11, 3, + 11, 8,11,11,11,11,11,11,11,11,11,11,11,11, 3, 9, + 9,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10, +}; + +static float _vq_quantthresh__8u1__p9_0[] = { + -1657.5, -1402.5, -1147.5, -892.5, -637.5, -382.5, -127.5, 127.5, + 382.5, 637.5, 892.5, 1147.5, 1402.5, 1657.5, +}; + +static long _vq_quantmap__8u1__p9_0[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__8u1__p9_0 = { + _vq_quantthresh__8u1__p9_0, + _vq_quantmap__8u1__p9_0, + 15, + 15 +}; + +static static_codebook _8u1__p9_0 = { + 2, 225, + _vq_lengthlist__8u1__p9_0, + 1, -514071552, 1627381760, 4, 0, + _vq_quantlist__8u1__p9_0, + NULL, + &_vq_auxt__8u1__p9_0, + NULL, + 0 +}; + +static long _vq_quantlist__8u1__p9_1[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__8u1__p9_1[] = { + 1, 4, 4, 7, 7, 9, 9, 7, 7, 8, 8,10,10,11,11, 4, + 7, 7, 9, 9,10,10, 8, 8,10,10,10,11,10,11, 4, 7, + 7, 9, 9,10,10, 8, 8,10, 9,11,11,11,11, 7, 9, 9, + 12,12,11,12,10,10,11,10,12,11,11,11, 7, 9, 9,11, + 11,13,12, 9, 9,11,10,11,11,12,11, 9,10,10,12,12, + 14,14,10,10,11,12,12,11,11,11, 9,10,11,11,13,14, + 13,10,11,11,11,12,11,12,12, 7, 8, 8,10, 9,11,10, + 11,12,12,11,12,14,12,13, 7, 8, 8, 9,10,10,11,12, + 12,12,11,12,12,12,13, 9, 9, 9,11,11,13,12,12,12, + 12,11,12,12,13,12, 8,10,10,11,10,11,12,12,12,12, + 12,12,14,12,12, 9,11,11,11,12,12,12,12,13,13,12, + 12,13,13,12,10,11,11,12,11,12,12,12,11,12,13,12, + 12,12,13,11,11,12,12,12,13,12,12,11,12,13,13,12, + 12,13,12,11,12,12,13,13,12,13,12,13,13,13,13,14, + 13, +}; + +static float _vq_quantthresh__8u1__p9_1[] = { + -110.5, -93.5, -76.5, -59.5, -42.5, -25.5, -8.5, 8.5, + 25.5, 42.5, 59.5, 76.5, 93.5, 110.5, +}; + +static long _vq_quantmap__8u1__p9_1[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__8u1__p9_1 = { + _vq_quantthresh__8u1__p9_1, + _vq_quantmap__8u1__p9_1, + 15, + 15 +}; + +static static_codebook _8u1__p9_1 = { + 2, 225, + _vq_lengthlist__8u1__p9_1, + 1, -522338304, 1620115456, 4, 0, + _vq_quantlist__8u1__p9_1, + NULL, + &_vq_auxt__8u1__p9_1, + NULL, + 0 +}; + +static long _vq_quantlist__8u1__p9_2[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__8u1__p9_2[] = { + 2, 5, 4, 6, 6, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, + 9, 5, 6, 6, 7, 7, 8, 8, 9, 8, 9, 9, 9, 9, 9, 9, + 9, 9, 5, 6, 6, 7, 7, 8, 8, 8, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 7, 7, 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, + 9,10,10, 9, 7, 7, 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9,10,10, 8, 8, 8, 9, 9, 9, 9,10,10,10, 9, + 10,10,10,10,10,10, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, + 10,10,10,10,10,10,10, 9, 9, 9, 9, 9, 9, 9, 9,10, + 10,10,10,10,10,10,10,10, 9, 9, 9, 9, 9,10,10,10, + 10,10,10,10,10,10,10,10,10, 9, 9, 9, 9, 9, 9,10, + 10,10,10,10,10,10,10,10,10,10, 9, 9, 9, 9, 9,10, + 10,10,10,10,10,10,10,10,10,10,10, 9, 9, 9, 9,10, + 10,10,10,10,10,10,10,10,10,10,10,10, 9, 9, 9, 9, + 9,10,10,10,10,10,10,10,10,10,10,10,10, 9, 9, 9, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10, 9,10, + 9, 9, 9,10,10,10,10,10,10,10,10,10,10,10,10, 9, + 10, 9,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 9, 9,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10, +}; + +static float _vq_quantthresh__8u1__p9_2[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__8u1__p9_2[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__8u1__p9_2 = { + _vq_quantthresh__8u1__p9_2, + _vq_quantmap__8u1__p9_2, + 17, + 17 +}; + +static static_codebook _8u1__p9_2 = { + 2, 289, + _vq_lengthlist__8u1__p9_2, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__8u1__p9_2, + NULL, + &_vq_auxt__8u1__p9_2, + NULL, + 0 +}; + +static long _huff_lengthlist__8u1__single[] = { + 4, 7,13, 9,15, 9,16, 8,10,13, 7, 5, 8, 6, 9, 7, + 10, 7,10,11,11, 6, 7, 8, 8, 9, 9, 9,12,16, 8, 5, + 8, 6, 8, 6, 9, 7,10,12,11, 7, 7, 7, 6, 7, 7, 7, + 11,15, 7, 5, 8, 6, 7, 5, 7, 6, 9,13,13, 9, 9, 8, + 6, 6, 5, 5, 9,14, 8, 6, 8, 6, 6, 4, 5, 3, 5,13, + 9, 9,11, 8,10, 7, 8, 4, 5,12,11,16,17,15,17,12, + 13, 8, 8,15, +}; + +static static_codebook _huff_book__8u1__single = { + 2, 100, + _huff_lengthlist__8u1__single, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44u0__long[] = { + 5, 8,13,10,17,11,11,15, 7, 2, 4, 5, 8, 7, 9,16, + 13, 4, 3, 5, 6, 8,11,20,10, 4, 5, 5, 7, 6, 8,18, + 15, 7, 6, 7, 8,10,14,20,10, 6, 7, 6, 9, 7, 8,17, + 9, 8,10, 8,10, 5, 4,11,12,17,19,14,16,10, 7,12, +}; + +static static_codebook _huff_book__44u0__long = { + 2, 64, + _huff_lengthlist__44u0__long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44u0__p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u0__p1_0[] = { + 1, 4, 4, 5, 8, 7, 5, 7, 8, 5, 8, 8, 8,11,11, 8, + 10,10, 5, 8, 8, 8,11,10, 8,11,11, 4, 8, 8, 8,11, + 11, 8,11,11, 8,12,11,11,13,13,11,13,14, 7,11,11, + 10,13,12,11,13,14, 4, 8, 8, 8,11,11, 8,11,12, 8, + 11,11,11,13,13,10,12,13, 8,11,11,11,14,13,11,14, + 13, +}; + +static float _vq_quantthresh__44u0__p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44u0__p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u0__p1_0 = { + _vq_quantthresh__44u0__p1_0, + _vq_quantmap__44u0__p1_0, + 3, + 3 +}; + +static static_codebook _44u0__p1_0 = { + 4, 81, + _vq_lengthlist__44u0__p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44u0__p1_0, + NULL, + &_vq_auxt__44u0__p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u0__p2_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u0__p2_0[] = { + 2, 4, 4, 5, 6, 6, 5, 6, 6, 5, 7, 7, 7, 8, 8, 6, + 8, 8, 5, 7, 7, 6, 8, 8, 7, 8, 8, 4, 7, 7, 7, 8, + 8, 7, 8, 8, 7, 8, 8, 8, 9,10, 8,10,10, 6, 8, 8, + 8,10, 8, 8,10,10, 5, 7, 7, 7, 8, 8, 7, 8, 8, 6, + 8, 8, 8,10,10, 8, 8,10, 6, 8, 8, 8,10,10, 8,10, + 9, +}; + +static float _vq_quantthresh__44u0__p2_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44u0__p2_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u0__p2_0 = { + _vq_quantthresh__44u0__p2_0, + _vq_quantmap__44u0__p2_0, + 3, + 3 +}; + +static static_codebook _44u0__p2_0 = { + 4, 81, + _vq_lengthlist__44u0__p2_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44u0__p2_0, + NULL, + &_vq_auxt__44u0__p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u0__p3_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u0__p3_0[] = { + 1, 5, 5, 8, 8, 5, 8, 7, 9, 9, 5, 7, 8, 9, 9, 9, + 10, 9,12,12, 9, 9,10,12,12, 6, 8, 8,11,10, 8,10, + 10,11,11, 8, 9,10,11,11,10,11,11,14,13,10,11,11, + 13,13, 5, 8, 8,10,10, 8,10,10,11,11, 8,10,10,11, + 11,10,11,11,13,13,10,11,11,13,13, 9,11,11,15,14, + 10,12,12,15,14,10,12,11,15,14,13,14,14,16,16,12, + 14,13,17,15, 9,11,11,14,15,10,11,12,14,16,10,11, + 12,14,16,12,13,14,16,16,13,13,15,15,18, 5, 8, 8, + 11,11, 8,10,10,12,12, 8,10,10,12,13,11,12,12,14, + 14,11,12,12,15,15, 8,10,10,13,13,10,12,12,13,13, + 10,12,12,14,14,12,13,13,15,15,12,13,13,16,16, 7, + 10,10,12,12,10,12,11,13,13,10,12,12,13,14,12,13, + 12,15,14,12,13,13,16,16,10,12,12,17,16,12,13,13, + 16,15,11,13,13,17,17,15,15,15,16,17,14,15,15,19, + 19,10,12,12,15,16,11,13,12,15,18,11,13,13,16,16, + 14,15,15,17,17,14,15,15,17,19, 5, 8, 8,11,11, 8, + 10,10,12,12, 8,10,10,12,12,11,12,12,16,15,11,12, + 12,14,15, 7,10,10,13,13,10,12,12,14,13,10,11,12, + 13,13,12,13,13,16,16,12,12,13,15,15, 8,10,10,13, + 13,10,12,12,14,14,10,12,12,13,13,12,13,13,16,16, + 12,13,13,15,15,10,12,12,16,15,11,13,13,17,16,11, + 12,13,16,15,13,15,15,19,17,14,15,14,17,16,10,12, + 12,16,16,11,13,13,16,17,12,13,13,15,17,14,15,15, + 17,19,14,15,15,17,17, 8,11,11,16,16,10,13,12,17, + 17,10,12,13,16,16,15,17,16,20,19,14,15,17,18,19, + 9,12,12,16,17,11,13,14,17,18,11,13,13,19,18,16, + 17,18,19,19,15,16,16,19,19, 9,12,12,16,17,11,14, + 13,18,17,11,13,13,17,17,16,17,16,20,19,14,16,16, + 18,18,12,15,15,19,17,14,15,16, 0,20,13,15,16,20, + 17,18,16,20, 0, 0,15,16,19,20, 0,12,15,14,18,19, + 13,16,15,20,19,13,16,15,20,18,17,18,17, 0,20,16, + 17,16, 0, 0, 8,11,11,16,15,10,12,12,17,17,10,13, + 13,17,16,14,16,15,18,20,15,16,16,19,19, 9,12,12, + 16,16,11,13,13,17,16,11,13,14,17,18,15,15,16,20, + 20,16,16,17,19,19, 9,13,12,16,17,11,14,13,17,17, + 11,14,14,18,17,14,16,15,18,19,16,17,18,18,19,12, + 14,15,19,18,13,15,16,18, 0,13,14,15, 0, 0,16,16, + 17,20, 0,17,17,20,20, 0,12,15,15,19,20,13,15,15, + 0, 0,14,16,15, 0, 0,15,18,16, 0, 0,17,18,16, 0, + 19, +}; + +static float _vq_quantthresh__44u0__p3_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u0__p3_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u0__p3_0 = { + _vq_quantthresh__44u0__p3_0, + _vq_quantmap__44u0__p3_0, + 5, + 5 +}; + +static static_codebook _44u0__p3_0 = { + 4, 625, + _vq_lengthlist__44u0__p3_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u0__p3_0, + NULL, + &_vq_auxt__44u0__p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u0__p4_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u0__p4_0[] = { + 4, 5, 5, 9, 9, 5, 6, 6, 9, 9, 5, 6, 6, 9, 9, 9, + 10, 9,12,12, 9, 9,10,12,12, 5, 7, 7,10,10, 7, 7, + 8,10,10, 6, 7, 8,10,10,10,10,10,11,13,10, 9,10, + 12,13, 5, 7, 7,10,10, 6, 8, 7,10,10, 7, 8, 7,10, + 10, 9,10,10,12,12,10,10,10,13,11, 9,10,10,13,13, + 10,11,10,13,13,10,10,10,13,13,12,12,13,14,14,12, + 12,13,14,14, 9,10,10,13,13,10,10,10,13,13,10,10, + 10,13,13,12,13,12,15,14,12,13,12,15,15, 5, 7, 6, + 10,10, 7, 8, 8,10,10, 7, 8, 8,10,10,10,11,10,13, + 13,10,10,10,12,12, 7, 8, 8,11,10, 8, 8, 9,10,11, + 8, 9, 9,11,11,11,10,11,11,14,11,11,11,13,13, 6, + 8, 8,10,10, 7, 9, 8,11,10, 8, 9, 9,11,11,10,11, + 10,14,11,10,11,11,13,13,10,11,11,14,13,10,10,11, + 14,13,10,11,11,14,14,12,11,13,12,16,13,14,14,15, + 15,10,10,11,13,14,10,11,10,14,13,10,11,11,14,14, + 12,13,12,15,13,13,13,14,15,16, 5, 7, 7,10,10, 7, + 8, 8,10,10, 7, 8, 8,10,10,10,10,10,13,13,10,10, + 11,12,13, 6, 8, 8,11,10, 8, 9, 9,11,11, 7, 8, 9, + 10,11,10,11,11,13,13,10,10,11,11,13, 6, 8, 8,10, + 11, 8, 9, 9,11,11, 8, 9, 8,12,10,10,11,11,13,13, + 10,11,10,14,11,10,10,10,14,13,10,11,11,14,13,10, + 10,11,13,13,12,14,14,16,16,12,12,13,13,15,10,11, + 11,13,14,10,11,11,14,15,10,11,10,13,13,13,14,13, + 16,16,12,13,11,15,12, 9,10,10,13,13,10,11,11,14, + 13,10,10,11,13,14,13,14,13,16,16,13,13,13,15,16, + 9,10,10,13,13,10,10,11,13,14,10,11,11,15,13,13, + 13,14,14,18,13,13,14,16,15, 9,10,10,13,14,10,11, + 10,14,13,10,11,11,13,14,13,14,13,16,15,13,13,14, + 15,16,12,13,12,16,14,11,11,13,15,15,13,14,13,16, + 15,15,12,16,12,17,14,15,15,17,17,12,13,13,14,16, + 11,13,11,16,15,12,13,14,15,16,14,15,13, 0,14,14, + 16,16, 0, 0, 9,10,10,13,13,10,11,10,14,14,10,11, + 11,13,13,12,13,13,14,16,13,14,14,16,16, 9,10,10, + 14,14,11,11,11,14,13,10,10,11,14,14,13,13,13,16, + 16,13,13,14,14,17, 9,10,10,13,14,10,11,11,13,15, + 10,11,10,14,14,13,13,13,14,17,13,14,13,17,14,12, + 13,13,16,14,13,14,13,16,15,12,12,13,15,16,15,15, + 16,18,16,15,13,15,14, 0,12,12,13,14,16,13,13,14, + 15,16,11,12,11,16,14,15,16,16,17,17,14,15,12,17, + 12, +}; + +static float _vq_quantthresh__44u0__p4_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u0__p4_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u0__p4_0 = { + _vq_quantthresh__44u0__p4_0, + _vq_quantmap__44u0__p4_0, + 5, + 5 +}; + +static static_codebook _44u0__p4_0 = { + 4, 625, + _vq_lengthlist__44u0__p4_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u0__p4_0, + NULL, + &_vq_auxt__44u0__p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u0__p5_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44u0__p5_0[] = { + 1, 4, 4, 7, 7, 7, 7, 9, 9, 4, 6, 6, 8, 8, 8, 8, + 9, 9, 4, 6, 6, 8, 8, 8, 8, 9, 9, 7, 8, 8, 9, 9, + 9, 9,11,10, 7, 8, 8, 9, 9, 9, 9,10,10, 7, 8, 8, + 9, 9,10,10,11,11, 7, 8, 8, 9, 9,10,10,11,11, 9, + 9, 9,10,10,11,11,12,12, 9, 9, 9,10,11,11,11,12, + 12, +}; + +static float _vq_quantthresh__44u0__p5_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44u0__p5_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44u0__p5_0 = { + _vq_quantthresh__44u0__p5_0, + _vq_quantmap__44u0__p5_0, + 9, + 9 +}; + +static static_codebook _44u0__p5_0 = { + 2, 81, + _vq_lengthlist__44u0__p5_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44u0__p5_0, + NULL, + &_vq_auxt__44u0__p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u0__p6_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44u0__p6_0[] = { + 1, 4, 4, 6, 6, 8, 8,10, 9,11,10,14,13, 4, 6, 5, + 8, 8, 9, 9,11,10,11,11,14,14, 4, 5, 6, 8, 8, 9, + 9,10,10,11,11,14,14, 6, 8, 8, 9, 9,10,10,11,11, + 12,12,16,15, 7, 8, 8, 9, 9,10,10,11,11,12,12,15, + 15, 9,10,10,10,10,11,11,12,12,12,12,15,15, 9,10, + 9,10,11,11,11,12,12,12,13,15,15,10,10,11,11,11, + 12,12,13,12,13,13,16,15,10,11,11,11,11,12,12,13, + 12,13,13,16,17,11,11,12,12,12,13,13,13,14,14,15, + 17,17,11,11,12,12,12,13,13,13,14,14,14,16,18,14, + 15,15,15,15,16,16,16,16,17,18, 0, 0,14,15,15,15, + 15,17,16,17,18,17,17,18, 0, +}; + +static float _vq_quantthresh__44u0__p6_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44u0__p6_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44u0__p6_0 = { + _vq_quantthresh__44u0__p6_0, + _vq_quantmap__44u0__p6_0, + 13, + 13 +}; + +static static_codebook _44u0__p6_0 = { + 2, 169, + _vq_lengthlist__44u0__p6_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44u0__p6_0, + NULL, + &_vq_auxt__44u0__p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u0__p6_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u0__p6_1[] = { + 2, 4, 4, 5, 5, 4, 5, 5, 5, 5, 4, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 5, 6, 6, 6, 6, +}; + +static float _vq_quantthresh__44u0__p6_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u0__p6_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u0__p6_1 = { + _vq_quantthresh__44u0__p6_1, + _vq_quantmap__44u0__p6_1, + 5, + 5 +}; + +static static_codebook _44u0__p6_1 = { + 2, 25, + _vq_lengthlist__44u0__p6_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u0__p6_1, + NULL, + &_vq_auxt__44u0__p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u0__p7_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u0__p7_0[] = { + 1, 4, 4,11,11, 9,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11, 9,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,10,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10, +}; + +static float _vq_quantthresh__44u0__p7_0[] = { + -253.5, -84.5, 84.5, 253.5, +}; + +static long _vq_quantmap__44u0__p7_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u0__p7_0 = { + _vq_quantthresh__44u0__p7_0, + _vq_quantmap__44u0__p7_0, + 5, + 5 +}; + +static static_codebook _44u0__p7_0 = { + 4, 625, + _vq_lengthlist__44u0__p7_0, + 1, -518709248, 1626677248, 3, 0, + _vq_quantlist__44u0__p7_0, + NULL, + &_vq_auxt__44u0__p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u0__p7_1[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44u0__p7_1[] = { + 1, 4, 4, 6, 6, 6, 6, 7, 7, 8, 8, 9, 9, 5, 7, 7, + 8, 7, 7, 7, 9, 8,10, 9,10,11, 5, 7, 7, 8, 8, 7, + 7, 8, 9,10,10,11,11, 6, 8, 8, 9, 9, 9, 9,11,10, + 12,12,15,12, 6, 8, 8, 9, 9, 9, 9,11,11,12,11,14, + 12, 7, 8, 8,10,10,12,12,13,13,13,15,13,13, 7, 8, + 8,10,10,11,11,13,12,14,15,15,15, 9,10,10,11,12, + 13,13,14,15,14,15,14,15, 8,10,10,12,12,14,14,15, + 14,14,15,15,14,10,12,12,14,14,15,14,15,15,15,14, + 15,15,10,12,12,13,14,15,14,15,15,14,15,15,15,12, + 15,13,15,14,15,15,15,15,15,15,15,15,13,13,15,15, + 15,15,15,15,15,15,15,15,15, +}; + +static float _vq_quantthresh__44u0__p7_1[] = { + -71.5, -58.5, -45.5, -32.5, -19.5, -6.5, 6.5, 19.5, + 32.5, 45.5, 58.5, 71.5, +}; + +static long _vq_quantmap__44u0__p7_1[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44u0__p7_1 = { + _vq_quantthresh__44u0__p7_1, + _vq_quantmap__44u0__p7_1, + 13, + 13 +}; + +static static_codebook _44u0__p7_1 = { + 2, 169, + _vq_lengthlist__44u0__p7_1, + 1, -523010048, 1618608128, 4, 0, + _vq_quantlist__44u0__p7_1, + NULL, + &_vq_auxt__44u0__p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u0__p7_2[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44u0__p7_2[] = { + 2, 5, 4, 6, 6, 7, 7, 8, 8, 8, 8, 9, 8, 5, 5, 6, + 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 5, 6, 5, 7, 7, 8, + 8, 8, 8, 9, 9, 9, 9, 6, 7, 7, 8, 8, 8, 8, 9, 8, + 9, 9, 9, 9, 6, 7, 7, 8, 7, 8, 8, 9, 9, 9, 9, 9, + 9, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 7, 8, + 8, 9, 8, 9, 8, 9, 9, 9, 9, 9, 9, 8, 9, 8, 9, 9, + 9, 9, 9, 9, 9, 9,10,10, 8, 8, 9, 9, 9, 9, 9, 9, + 9, 9,10, 9,10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9,10,10,10, 9, 9, 9, 9, 9, + 9, 9, 9,10, 9, 9,10,10, 9, +}; + +static float _vq_quantthresh__44u0__p7_2[] = { + -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, + 2.5, 3.5, 4.5, 5.5, +}; + +static long _vq_quantmap__44u0__p7_2[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44u0__p7_2 = { + _vq_quantthresh__44u0__p7_2, + _vq_quantmap__44u0__p7_2, + 13, + 13 +}; + +static static_codebook _44u0__p7_2 = { + 2, 169, + _vq_lengthlist__44u0__p7_2, + 1, -531103744, 1611661312, 4, 0, + _vq_quantlist__44u0__p7_2, + NULL, + &_vq_auxt__44u0__p7_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44u0__short[] = { + 12,13,14,13,17,12,15,17, 5, 5, 6,10,10,11,15,16, + 4, 3, 3, 7, 5, 7,10,16, 7, 7, 7,10, 9,11,12,16, + 6, 5, 5, 9, 5, 6,10,16, 8, 7, 7, 9, 6, 7, 9,16, + 11, 7, 3, 6, 4, 5, 8,16,12, 9, 4, 8, 5, 7, 9,16, +}; + +static static_codebook _huff_book__44u0__short = { + 2, 64, + _huff_lengthlist__44u0__short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44u1__long[] = { + 5, 8,13,10,17,11,11,15, 7, 2, 4, 5, 8, 7, 9,16, + 13, 4, 3, 5, 6, 8,11,20,10, 4, 5, 5, 7, 6, 8,18, + 15, 7, 6, 7, 8,10,14,20,10, 6, 7, 6, 9, 7, 8,17, + 9, 8,10, 8,10, 5, 4,11,12,17,19,14,16,10, 7,12, +}; + +static static_codebook _huff_book__44u1__long = { + 2, 64, + _huff_lengthlist__44u1__long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44u1__p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u1__p1_0[] = { + 1, 4, 4, 5, 8, 7, 5, 7, 8, 5, 8, 8, 8,11,11, 8, + 10,10, 5, 8, 8, 8,11,10, 8,11,11, 4, 8, 8, 8,11, + 11, 8,11,11, 8,12,11,11,13,13,11,13,14, 7,11,11, + 10,13,12,11,13,14, 4, 8, 8, 8,11,11, 8,11,12, 8, + 11,11,11,13,13,10,12,13, 8,11,11,11,14,13,11,14, + 13, +}; + +static float _vq_quantthresh__44u1__p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44u1__p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u1__p1_0 = { + _vq_quantthresh__44u1__p1_0, + _vq_quantmap__44u1__p1_0, + 3, + 3 +}; + +static static_codebook _44u1__p1_0 = { + 4, 81, + _vq_lengthlist__44u1__p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44u1__p1_0, + NULL, + &_vq_auxt__44u1__p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u1__p2_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u1__p2_0[] = { + 2, 4, 4, 5, 6, 6, 5, 6, 6, 5, 7, 7, 7, 8, 8, 6, + 8, 8, 5, 7, 7, 6, 8, 8, 7, 8, 8, 4, 7, 7, 7, 8, + 8, 7, 8, 8, 7, 8, 8, 8, 9,10, 8,10,10, 6, 8, 8, + 8,10, 8, 8,10,10, 5, 7, 7, 7, 8, 8, 7, 8, 8, 6, + 8, 8, 8,10,10, 8, 8,10, 6, 8, 8, 8,10,10, 8,10, + 9, +}; + +static float _vq_quantthresh__44u1__p2_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44u1__p2_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u1__p2_0 = { + _vq_quantthresh__44u1__p2_0, + _vq_quantmap__44u1__p2_0, + 3, + 3 +}; + +static static_codebook _44u1__p2_0 = { + 4, 81, + _vq_lengthlist__44u1__p2_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44u1__p2_0, + NULL, + &_vq_auxt__44u1__p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u1__p3_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u1__p3_0[] = { + 1, 5, 5, 8, 8, 5, 8, 7, 9, 9, 5, 7, 8, 9, 9, 9, + 10, 9,12,12, 9, 9,10,12,12, 6, 8, 8,11,10, 8,10, + 10,11,11, 8, 9,10,11,11,10,11,11,14,13,10,11,11, + 13,13, 5, 8, 8,10,10, 8,10,10,11,11, 8,10,10,11, + 11,10,11,11,13,13,10,11,11,13,13, 9,11,11,15,14, + 10,12,12,15,14,10,12,11,15,14,13,14,14,16,16,12, + 14,13,17,15, 9,11,11,14,15,10,11,12,14,16,10,11, + 12,14,16,12,13,14,16,16,13,13,15,15,18, 5, 8, 8, + 11,11, 8,10,10,12,12, 8,10,10,12,13,11,12,12,14, + 14,11,12,12,15,15, 8,10,10,13,13,10,12,12,13,13, + 10,12,12,14,14,12,13,13,15,15,12,13,13,16,16, 7, + 10,10,12,12,10,12,11,13,13,10,12,12,13,14,12,13, + 12,15,14,12,13,13,16,16,10,12,12,17,16,12,13,13, + 16,15,11,13,13,17,17,15,15,15,16,17,14,15,15,19, + 19,10,12,12,15,16,11,13,12,15,18,11,13,13,16,16, + 14,15,15,17,17,14,15,15,17,19, 5, 8, 8,11,11, 8, + 10,10,12,12, 8,10,10,12,12,11,12,12,16,15,11,12, + 12,14,15, 7,10,10,13,13,10,12,12,14,13,10,11,12, + 13,13,12,13,13,16,16,12,12,13,15,15, 8,10,10,13, + 13,10,12,12,14,14,10,12,12,13,13,12,13,13,16,16, + 12,13,13,15,15,10,12,12,16,15,11,13,13,17,16,11, + 12,13,16,15,13,15,15,19,17,14,15,14,17,16,10,12, + 12,16,16,11,13,13,16,17,12,13,13,15,17,14,15,15, + 17,19,14,15,15,17,17, 8,11,11,16,16,10,13,12,17, + 17,10,12,13,16,16,15,17,16,20,19,14,15,17,18,19, + 9,12,12,16,17,11,13,14,17,18,11,13,13,19,18,16, + 17,18,19,19,15,16,16,19,19, 9,12,12,16,17,11,14, + 13,18,17,11,13,13,17,17,16,17,16,20,19,14,16,16, + 18,18,12,15,15,19,17,14,15,16, 0,20,13,15,16,20, + 17,18,16,20, 0, 0,15,16,19,20, 0,12,15,14,18,19, + 13,16,15,20,19,13,16,15,20,18,17,18,17, 0,20,16, + 17,16, 0, 0, 8,11,11,16,15,10,12,12,17,17,10,13, + 13,17,16,14,16,15,18,20,15,16,16,19,19, 9,12,12, + 16,16,11,13,13,17,16,11,13,14,17,18,15,15,16,20, + 20,16,16,17,19,19, 9,13,12,16,17,11,14,13,17,17, + 11,14,14,18,17,14,16,15,18,19,16,17,18,18,19,12, + 14,15,19,18,13,15,16,18, 0,13,14,15, 0, 0,16,16, + 17,20, 0,17,17,20,20, 0,12,15,15,19,20,13,15,15, + 0, 0,14,16,15, 0, 0,15,18,16, 0, 0,17,18,16, 0, + 19, +}; + +static float _vq_quantthresh__44u1__p3_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u1__p3_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u1__p3_0 = { + _vq_quantthresh__44u1__p3_0, + _vq_quantmap__44u1__p3_0, + 5, + 5 +}; + +static static_codebook _44u1__p3_0 = { + 4, 625, + _vq_lengthlist__44u1__p3_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u1__p3_0, + NULL, + &_vq_auxt__44u1__p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u1__p4_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u1__p4_0[] = { + 4, 5, 5, 9, 9, 5, 6, 6, 9, 9, 5, 6, 6, 9, 9, 9, + 10, 9,12,12, 9, 9,10,12,12, 5, 7, 7,10,10, 7, 7, + 8,10,10, 6, 7, 8,10,10,10,10,10,11,13,10, 9,10, + 12,13, 5, 7, 7,10,10, 6, 8, 7,10,10, 7, 8, 7,10, + 10, 9,10,10,12,12,10,10,10,13,11, 9,10,10,13,13, + 10,11,10,13,13,10,10,10,13,13,12,12,13,14,14,12, + 12,13,14,14, 9,10,10,13,13,10,10,10,13,13,10,10, + 10,13,13,12,13,12,15,14,12,13,12,15,15, 5, 7, 6, + 10,10, 7, 8, 8,10,10, 7, 8, 8,10,10,10,11,10,13, + 13,10,10,10,12,12, 7, 8, 8,11,10, 8, 8, 9,10,11, + 8, 9, 9,11,11,11,10,11,11,14,11,11,11,13,13, 6, + 8, 8,10,10, 7, 9, 8,11,10, 8, 9, 9,11,11,10,11, + 10,14,11,10,11,11,13,13,10,11,11,14,13,10,10,11, + 14,13,10,11,11,14,14,12,11,13,12,16,13,14,14,15, + 15,10,10,11,13,14,10,11,10,14,13,10,11,11,14,14, + 12,13,12,15,13,13,13,14,15,16, 5, 7, 7,10,10, 7, + 8, 8,10,10, 7, 8, 8,10,10,10,10,10,13,13,10,10, + 11,12,13, 6, 8, 8,11,10, 8, 9, 9,11,11, 7, 8, 9, + 10,11,10,11,11,13,13,10,10,11,11,13, 6, 8, 8,10, + 11, 8, 9, 9,11,11, 8, 9, 8,12,10,10,11,11,13,13, + 10,11,10,14,11,10,10,10,14,13,10,11,11,14,13,10, + 10,11,13,13,12,14,14,16,16,12,12,13,13,15,10,11, + 11,13,14,10,11,11,14,15,10,11,10,13,13,13,14,13, + 16,16,12,13,11,15,12, 9,10,10,13,13,10,11,11,14, + 13,10,10,11,13,14,13,14,13,16,16,13,13,13,15,16, + 9,10,10,13,13,10,10,11,13,14,10,11,11,15,13,13, + 13,14,14,18,13,13,14,16,15, 9,10,10,13,14,10,11, + 10,14,13,10,11,11,13,14,13,14,13,16,15,13,13,14, + 15,16,12,13,12,16,14,11,11,13,15,15,13,14,13,16, + 15,15,12,16,12,17,14,15,15,17,17,12,13,13,14,16, + 11,13,11,16,15,12,13,14,15,16,14,15,13, 0,14,14, + 16,16, 0, 0, 9,10,10,13,13,10,11,10,14,14,10,11, + 11,13,13,12,13,13,14,16,13,14,14,16,16, 9,10,10, + 14,14,11,11,11,14,13,10,10,11,14,14,13,13,13,16, + 16,13,13,14,14,17, 9,10,10,13,14,10,11,11,13,15, + 10,11,10,14,14,13,13,13,14,17,13,14,13,17,14,12, + 13,13,16,14,13,14,13,16,15,12,12,13,15,16,15,15, + 16,18,16,15,13,15,14, 0,12,12,13,14,16,13,13,14, + 15,16,11,12,11,16,14,15,16,16,17,17,14,15,12,17, + 12, +}; + +static float _vq_quantthresh__44u1__p4_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u1__p4_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u1__p4_0 = { + _vq_quantthresh__44u1__p4_0, + _vq_quantmap__44u1__p4_0, + 5, + 5 +}; + +static static_codebook _44u1__p4_0 = { + 4, 625, + _vq_lengthlist__44u1__p4_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u1__p4_0, + NULL, + &_vq_auxt__44u1__p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u1__p5_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44u1__p5_0[] = { + 1, 4, 4, 7, 7, 7, 7, 9, 9, 4, 6, 6, 8, 8, 8, 8, + 9, 9, 4, 6, 6, 8, 8, 8, 8, 9, 9, 7, 8, 8, 9, 9, + 9, 9,11,10, 7, 8, 8, 9, 9, 9, 9,10,10, 7, 8, 8, + 9, 9,10,10,11,11, 7, 8, 8, 9, 9,10,10,11,11, 9, + 9, 9,10,10,11,11,12,12, 9, 9, 9,10,11,11,11,12, + 12, +}; + +static float _vq_quantthresh__44u1__p5_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44u1__p5_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44u1__p5_0 = { + _vq_quantthresh__44u1__p5_0, + _vq_quantmap__44u1__p5_0, + 9, + 9 +}; + +static static_codebook _44u1__p5_0 = { + 2, 81, + _vq_lengthlist__44u1__p5_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44u1__p5_0, + NULL, + &_vq_auxt__44u1__p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u1__p6_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44u1__p6_0[] = { + 1, 4, 4, 6, 6, 8, 8,10, 9,11,10,14,13, 4, 6, 5, + 8, 8, 9, 9,11,10,11,11,14,14, 4, 5, 6, 8, 8, 9, + 9,10,10,11,11,14,14, 6, 8, 8, 9, 9,10,10,11,11, + 12,12,16,15, 7, 8, 8, 9, 9,10,10,11,11,12,12,15, + 15, 9,10,10,10,10,11,11,12,12,12,12,15,15, 9,10, + 9,10,11,11,11,12,12,12,13,15,15,10,10,11,11,11, + 12,12,13,12,13,13,16,15,10,11,11,11,11,12,12,13, + 12,13,13,16,17,11,11,12,12,12,13,13,13,14,14,15, + 17,17,11,11,12,12,12,13,13,13,14,14,14,16,18,14, + 15,15,15,15,16,16,16,16,17,18, 0, 0,14,15,15,15, + 15,17,16,17,18,17,17,18, 0, +}; + +static float _vq_quantthresh__44u1__p6_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44u1__p6_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44u1__p6_0 = { + _vq_quantthresh__44u1__p6_0, + _vq_quantmap__44u1__p6_0, + 13, + 13 +}; + +static static_codebook _44u1__p6_0 = { + 2, 169, + _vq_lengthlist__44u1__p6_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44u1__p6_0, + NULL, + &_vq_auxt__44u1__p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u1__p6_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u1__p6_1[] = { + 2, 4, 4, 5, 5, 4, 5, 5, 5, 5, 4, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 5, 6, 6, 6, 6, +}; + +static float _vq_quantthresh__44u1__p6_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u1__p6_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u1__p6_1 = { + _vq_quantthresh__44u1__p6_1, + _vq_quantmap__44u1__p6_1, + 5, + 5 +}; + +static static_codebook _44u1__p6_1 = { + 2, 25, + _vq_lengthlist__44u1__p6_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u1__p6_1, + NULL, + &_vq_auxt__44u1__p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u1__p7_0[] = { + 3, + 2, + 4, + 1, + 5, + 0, + 6, +}; + +static long _vq_lengthlist__44u1__p7_0[] = { + 1, 3, 2, 9, 9, 7, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, +}; + +static float _vq_quantthresh__44u1__p7_0[] = { + -422.5, -253.5, -84.5, 84.5, 253.5, 422.5, +}; + +static long _vq_quantmap__44u1__p7_0[] = { + 5, 3, 1, 0, 2, 4, 6, +}; + +static encode_aux_threshmatch _vq_auxt__44u1__p7_0 = { + _vq_quantthresh__44u1__p7_0, + _vq_quantmap__44u1__p7_0, + 7, + 7 +}; + +static static_codebook _44u1__p7_0 = { + 2, 49, + _vq_lengthlist__44u1__p7_0, + 1, -518017024, 1626677248, 3, 0, + _vq_quantlist__44u1__p7_0, + NULL, + &_vq_auxt__44u1__p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u1__p7_1[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44u1__p7_1[] = { + 1, 4, 4, 6, 6, 6, 6, 7, 7, 8, 8, 9, 9, 5, 7, 7, + 8, 7, 7, 7, 9, 8,10, 9,10,11, 5, 7, 7, 8, 8, 7, + 7, 8, 9,10,10,11,11, 6, 8, 8, 9, 9, 9, 9,11,10, + 12,12,15,12, 6, 8, 8, 9, 9, 9, 9,11,11,12,11,14, + 12, 7, 8, 8,10,10,12,12,13,13,13,15,13,13, 7, 8, + 8,10,10,11,11,13,12,14,15,15,15, 9,10,10,11,12, + 13,13,14,15,14,15,14,15, 8,10,10,12,12,14,14,15, + 14,14,15,15,14,10,12,12,14,14,15,14,15,15,15,14, + 15,15,10,12,12,13,14,15,14,15,15,14,15,15,15,12, + 15,13,15,14,15,15,15,15,15,15,15,15,13,13,15,15, + 15,15,15,15,15,15,15,15,15, +}; + +static float _vq_quantthresh__44u1__p7_1[] = { + -71.5, -58.5, -45.5, -32.5, -19.5, -6.5, 6.5, 19.5, + 32.5, 45.5, 58.5, 71.5, +}; + +static long _vq_quantmap__44u1__p7_1[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44u1__p7_1 = { + _vq_quantthresh__44u1__p7_1, + _vq_quantmap__44u1__p7_1, + 13, + 13 +}; + +static static_codebook _44u1__p7_1 = { + 2, 169, + _vq_lengthlist__44u1__p7_1, + 1, -523010048, 1618608128, 4, 0, + _vq_quantlist__44u1__p7_1, + NULL, + &_vq_auxt__44u1__p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u1__p7_2[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44u1__p7_2[] = { + 2, 5, 4, 6, 6, 7, 7, 8, 8, 8, 8, 9, 8, 5, 5, 6, + 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 5, 6, 5, 7, 7, 8, + 8, 8, 8, 9, 9, 9, 9, 6, 7, 7, 8, 8, 8, 8, 9, 8, + 9, 9, 9, 9, 6, 7, 7, 8, 7, 8, 8, 9, 9, 9, 9, 9, + 9, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 7, 8, + 8, 9, 8, 9, 8, 9, 9, 9, 9, 9, 9, 8, 9, 8, 9, 9, + 9, 9, 9, 9, 9, 9,10,10, 8, 8, 9, 9, 9, 9, 9, 9, + 9, 9,10, 9,10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9,10,10,10, 9, 9, 9, 9, 9, + 9, 9, 9,10, 9, 9,10,10, 9, +}; + +static float _vq_quantthresh__44u1__p7_2[] = { + -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, + 2.5, 3.5, 4.5, 5.5, +}; + +static long _vq_quantmap__44u1__p7_2[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44u1__p7_2 = { + _vq_quantthresh__44u1__p7_2, + _vq_quantmap__44u1__p7_2, + 13, + 13 +}; + +static static_codebook _44u1__p7_2 = { + 2, 169, + _vq_lengthlist__44u1__p7_2, + 1, -531103744, 1611661312, 4, 0, + _vq_quantlist__44u1__p7_2, + NULL, + &_vq_auxt__44u1__p7_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44u1__short[] = { + 12,13,14,13,17,12,15,17, 5, 5, 6,10,10,11,15,16, + 4, 3, 3, 7, 5, 7,10,16, 7, 7, 7,10, 9,11,12,16, + 6, 5, 5, 9, 5, 6,10,16, 8, 7, 7, 9, 6, 7, 9,16, + 11, 7, 3, 6, 4, 5, 8,16,12, 9, 4, 8, 5, 7, 9,16, +}; + +static static_codebook _huff_book__44u1__short = { + 2, 64, + _huff_lengthlist__44u1__short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44u2__long[] = { + 5, 9,14,12,15,13,10,13, 7, 4, 5, 6, 8, 7, 8,12, + 13, 4, 3, 5, 5, 6, 9,15,12, 6, 5, 6, 6, 6, 7,14, + 14, 7, 4, 6, 4, 6, 8,15,12, 6, 6, 5, 5, 5, 6,14, + 9, 7, 8, 6, 7, 5, 4,10,10,13,14,14,15,10, 6, 8, +}; + +static static_codebook _huff_book__44u2__long = { + 2, 64, + _huff_lengthlist__44u2__long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44u2__p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u2__p1_0[] = { + 1, 4, 4, 5, 8, 7, 5, 7, 8, 5, 8, 8, 8,11,11, 8, + 10,11, 5, 8, 8, 8,11,10, 8,11,11, 4, 8, 8, 8,11, + 11, 8,11,11, 8,11,11,11,13,14,11,13,13, 7,11,11, + 10,13,12,11,14,14, 4, 8, 8, 8,11,11, 8,11,11, 8, + 11,11,11,14,13,10,12,13, 8,11,11,11,13,13,11,13, + 13, +}; + +static float _vq_quantthresh__44u2__p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44u2__p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u2__p1_0 = { + _vq_quantthresh__44u2__p1_0, + _vq_quantmap__44u2__p1_0, + 3, + 3 +}; + +static static_codebook _44u2__p1_0 = { + 4, 81, + _vq_lengthlist__44u2__p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44u2__p1_0, + NULL, + &_vq_auxt__44u2__p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u2__p2_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u2__p2_0[] = { + 2, 5, 5, 5, 6, 6, 5, 6, 6, 5, 6, 6, 7, 8, 8, 6, + 8, 8, 5, 6, 6, 6, 8, 7, 7, 8, 8, 5, 6, 6, 7, 8, + 8, 6, 8, 8, 6, 8, 8, 8, 9,10, 8,10,10, 6, 8, 8, + 7,10, 8, 8,10,10, 5, 6, 6, 6, 8, 8, 7, 8, 8, 6, + 8, 8, 8,10,10, 8, 8,10, 6, 8, 8, 8,10,10, 8,10, + 9, +}; + +static float _vq_quantthresh__44u2__p2_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44u2__p2_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u2__p2_0 = { + _vq_quantthresh__44u2__p2_0, + _vq_quantmap__44u2__p2_0, + 3, + 3 +}; + +static static_codebook _44u2__p2_0 = { + 4, 81, + _vq_lengthlist__44u2__p2_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44u2__p2_0, + NULL, + &_vq_auxt__44u2__p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u2__p3_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u2__p3_0[] = { + 2, 4, 4, 7, 8, 5, 7, 7, 9, 9, 5, 7, 7, 9, 9, 8, + 9, 9,12,11, 8, 9, 9,11,12, 5, 7, 7,10,10, 7, 9, + 9,11,11, 7, 9, 9,10,11,10,11,11,13,13, 9,10,11, + 12,13, 5, 7, 7,10,10, 7, 9, 9,11,10, 7, 9, 9,11, + 11, 9,11,10,13,13,10,11,11,13,13, 8,10,10,14,13, + 10,11,11,15,14, 9,11,11,15,14,13,14,13,16,14,12, + 13,13,15,16, 8,10,10,13,14, 9,11,11,14,15,10,11, + 11,14,15,12,13,13,15,15,12,13,14,15,16, 5, 7, 7, + 10,10, 7, 9, 9,11,11, 7, 9, 9,11,12,10,11,11,14, + 13,10,11,11,14,14, 7, 9, 9,12,12, 9,11,11,13,13, + 9,11,11,13,13,12,13,12,14,14,11,12,13,15,15, 7, + 9, 9,12,12, 8,11,10,13,12, 9,11,11,13,13,11,13, + 12,15,13,11,13,13,15,16, 9,12,11,15,15,11,12,12, + 16,15,11,12,13,16,16,13,14,15,16,15,13,15,15,17, + 17, 9,11,11,14,15,10,12,12,15,15,11,13,12,15,16, + 13,15,14,16,16,13,15,15,17,19, 5, 7, 7,10,10, 7, + 9, 9,12,11, 7, 9, 9,11,11,10,11,11,14,14,10,11, + 11,13,14, 7, 9, 9,12,12, 9,11,11,13,13, 9,10,11, + 12,13,11,13,12,16,15,11,12,12,14,15, 7, 9, 9,12, + 12, 9,11,11,13,13, 9,11,11,13,12,11,13,12,15,16, + 12,13,13,15,14, 9,11,11,15,14,11,13,12,16,15,10, + 11,12,15,15,13,14,14,18,17,13,14,14,15,17,10,11, + 11,14,15,11,13,12,15,17,11,13,12,15,16,13,15,14, + 18,17,14,15,15,16,18, 7,10,10,14,14,10,12,12,15, + 15,10,12,12,15,15,14,15,15,18,17,13,15,15,16,16, + 9,11,11,16,15,11,13,13,16,18,11,13,13,16,16,15, + 16,16, 0, 0,14,15,16,18,17, 9,11,11,15,15,10,13, + 12,17,16,11,12,13,16,17,14,15,16,19,19,14,15,15, + 0,20,12,14,14, 0, 0,13,14,16,19,18,13,15,16,20, + 17,16,18, 0, 0, 0,15,16,17,18,19,11,14,14, 0,19, + 12,15,14,17,17,13,15,15, 0, 0,16,17,15,20,19,15, + 17,16,19, 0, 8,10,10,14,15,10,12,11,15,15,10,11, + 12,16,15,13,14,14,19,17,14,15,15, 0, 0, 9,11,11, + 16,15,11,13,13,17,16,10,12,13,16,17,14,15,15,18, + 18,14,15,16,20,19, 9,12,12, 0,15,11,13,13,16,17, + 11,13,13,19,17,14,16,16,18,17,15,16,16,17,19,11, + 14,14,18,18,13,14,15, 0, 0,12,14,15,19,18,15,16, + 19, 0,19,15,16,19,19,17,12,14,14,16,19,13,15,15, + 0,17,13,15,14,18,18,15,16,15, 0,18,16,17,17, 0, + 0, +}; + +static float _vq_quantthresh__44u2__p3_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u2__p3_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u2__p3_0 = { + _vq_quantthresh__44u2__p3_0, + _vq_quantmap__44u2__p3_0, + 5, + 5 +}; + +static static_codebook _44u2__p3_0 = { + 4, 625, + _vq_lengthlist__44u2__p3_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u2__p3_0, + NULL, + &_vq_auxt__44u2__p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u2__p4_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u2__p4_0[] = { + 4, 5, 5, 8, 8, 5, 7, 6, 9, 9, 5, 6, 7, 9, 9, 9, + 9, 9,11,11, 9, 9, 9,11,11, 5, 7, 7, 9, 9, 7, 8, + 8,10,10, 7, 7, 8,10,10,10,10,10,11,12, 9,10,10, + 11,12, 5, 7, 7, 9, 9, 6, 8, 7,10,10, 7, 8, 8,10, + 10, 9,10,10,12,11, 9,10,10,12,11, 9,10,10,12,12, + 10,10,10,13,12, 9,10,10,12,13,12,12,12,14,14,11, + 12,12,13,14, 9,10,10,12,12, 9,10,10,12,13,10,10, + 10,12,13,11,12,12,14,13,12,12,12,14,13, 5, 7, 7, + 10, 9, 7, 8, 8,10,10, 7, 8, 8,10,10,10,10,10,12, + 12,10,10,10,12,12, 7, 8, 8,11,10, 8, 8, 9,11,11, + 8, 9, 9,11,11,10,11,11,12,13,10,11,11,13,13, 6, + 8, 8,10,10, 7, 9, 8,11,10, 8, 9, 9,11,11,10,11, + 10,13,11,10,11,11,13,13, 9,10,10,13,13,10,11,11, + 13,13,10,11,11,14,13,12,11,13,12,15,12,13,13,15, + 15, 9,10,10,12,13,10,11,10,13,13,10,11,11,13,13, + 12,13,11,15,13,12,13,13,15,15, 5, 7, 7, 9,10, 7, + 8, 8,10,10, 7, 8, 8,10,10,10,10,10,12,12,10,10, + 11,12,12, 6, 8, 8,10,10, 8, 9, 9,11,11, 7, 8, 9, + 10,11,10,11,11,13,13,10,10,11,11,13, 7, 8, 8,10, + 11, 8, 9, 9,11,11, 8, 9, 8,11,11,10,11,11,13,13, + 10,11,11,13,12, 9,10,10,13,12,10,11,11,14,13,10, + 10,11,13,13,12,13,13,15,15,12,11,13,12,14, 9,10, + 10,12,13,10,11,11,13,14,10,11,11,13,13,12,13,13, + 15,15,12,13,12,15,12, 8, 9, 9,12,12, 9,11,10,13, + 13, 9,10,10,13,13,12,13,13,15,15,12,12,12,14,14, + 9,10,10,13,13,10,11,11,13,14,10,11,11,14,12,13, + 13,14,14,16,12,13,13,15,14, 9,10,10,13,13,10,11, + 10,14,13,10,11,11,13,14,12,14,13,16,14,13,13,13, + 14,15,11,13,12,15,14,11,12,13,14,15,12,13,13,16, + 15,14,12,15,12,16,14,15,15,17,16,11,12,12,14,15, + 11,13,11,15,14,12,13,13,15,16,13,15,12,17,13,14, + 15,15,16,16, 8, 9, 9,12,12, 9,10,10,13,13, 9,10, + 10,13,13,12,13,12,14,14,12,13,13,15,15, 9,10,10, + 13,13,10,11,11,14,13,10,10,11,13,14,12,13,13,15, + 14,12,12,14,14,16, 9,10,10,13,13,10,11,11,13,14, + 10,11,11,14,13,13,13,13,15,15,13,14,13,16,14,11, + 12,12,14,14,12,13,13,16,15,11,12,13,14,15,14,15, + 15,16,16,14,13,15,13,17,11,12,12,14,15,12,13,13, + 15,16,11,13,12,15,15,14,15,14,16,16,14,15,12,17, + 13, +}; + +static float _vq_quantthresh__44u2__p4_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u2__p4_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u2__p4_0 = { + _vq_quantthresh__44u2__p4_0, + _vq_quantmap__44u2__p4_0, + 5, + 5 +}; + +static static_codebook _44u2__p4_0 = { + 4, 625, + _vq_lengthlist__44u2__p4_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u2__p4_0, + NULL, + &_vq_auxt__44u2__p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u2__p5_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44u2__p5_0[] = { + 1, 4, 4, 7, 7, 8, 8, 9, 9, 4, 6, 5, 8, 8, 8, 8, + 10,10, 4, 5, 6, 8, 8, 8, 8,10,10, 7, 8, 8, 9, 9, + 9, 9,11,11, 7, 8, 8, 9, 9, 9, 9,11,11, 8, 8, 8, + 9, 9,10,11,12,12, 8, 8, 8, 9, 9,10,10,12,12,10, + 10,10,11,11,12,12,13,13,10,10,10,11,11,12,12,13, + 13, +}; + +static float _vq_quantthresh__44u2__p5_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44u2__p5_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44u2__p5_0 = { + _vq_quantthresh__44u2__p5_0, + _vq_quantmap__44u2__p5_0, + 9, + 9 +}; + +static static_codebook _44u2__p5_0 = { + 2, 81, + _vq_lengthlist__44u2__p5_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44u2__p5_0, + NULL, + &_vq_auxt__44u2__p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u2__p6_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44u2__p6_0[] = { + 1, 4, 4, 6, 6, 8, 8,10,10,11,11,14,13, 4, 6, 5, + 8, 8, 9, 9,11,10,12,11,15,14, 4, 5, 6, 8, 8, 9, + 9,11,11,11,11,14,14, 6, 8, 8,10, 9,11,11,11,11, + 12,12,15,15, 6, 8, 8, 9, 9,11,11,11,12,12,12,15, + 15, 8,10,10,11,11,11,11,12,12,13,13,15,16, 8,10, + 10,11,11,11,11,12,12,13,13,16,16,10,11,11,12,12, + 12,12,13,13,13,13,17,16,10,11,11,12,12,12,12,13, + 13,13,14,16,17,11,12,12,13,13,13,13,14,14,15,14, + 18,17,11,12,12,13,13,13,13,14,14,14,15,19,18,14, + 15,15,15,15,16,16,18,19,18,18, 0, 0,14,15,15,16, + 15,17,17,16,18,17,18, 0, 0, +}; + +static float _vq_quantthresh__44u2__p6_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44u2__p6_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44u2__p6_0 = { + _vq_quantthresh__44u2__p6_0, + _vq_quantmap__44u2__p6_0, + 13, + 13 +}; + +static static_codebook _44u2__p6_0 = { + 2, 169, + _vq_lengthlist__44u2__p6_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44u2__p6_0, + NULL, + &_vq_auxt__44u2__p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u2__p6_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u2__p6_1[] = { + 2, 4, 4, 5, 5, 4, 5, 5, 6, 5, 4, 5, 5, 5, 6, 5, + 6, 5, 6, 6, 5, 5, 6, 6, 6, +}; + +static float _vq_quantthresh__44u2__p6_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u2__p6_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u2__p6_1 = { + _vq_quantthresh__44u2__p6_1, + _vq_quantmap__44u2__p6_1, + 5, + 5 +}; + +static static_codebook _44u2__p6_1 = { + 2, 25, + _vq_lengthlist__44u2__p6_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u2__p6_1, + NULL, + &_vq_auxt__44u2__p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u2__p7_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44u2__p7_0[] = { + 1, 3, 2,12,12,12,12,12,12, 4,12,12,12,12,12,12, + 12,12, 5,12,12,12,12,12,12,12,12,12,12,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11, +}; + +static float _vq_quantthresh__44u2__p7_0[] = { + -591.5, -422.5, -253.5, -84.5, 84.5, 253.5, 422.5, 591.5, +}; + +static long _vq_quantmap__44u2__p7_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44u2__p7_0 = { + _vq_quantthresh__44u2__p7_0, + _vq_quantmap__44u2__p7_0, + 9, + 9 +}; + +static static_codebook _44u2__p7_0 = { + 2, 81, + _vq_lengthlist__44u2__p7_0, + 1, -516612096, 1626677248, 4, 0, + _vq_quantlist__44u2__p7_0, + NULL, + &_vq_auxt__44u2__p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u2__p7_1[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44u2__p7_1[] = { + 1, 4, 4, 7, 6, 7, 6, 8, 7, 9, 7, 9, 8, 4, 7, 6, + 8, 8, 9, 8,10, 9,10,10,11,11, 4, 7, 7, 8, 8, 8, + 8, 9,10,11,11,11,11, 6, 8, 8,10,10,10,10,11,11, + 12,12,12,12, 7, 8, 8,10,10,10,10,11,11,12,12,13, + 13, 7, 9, 9,11,10,12,12,13,13,14,13,14,14, 7, 9, + 9,10,11,11,12,13,13,13,13,16,14, 9,10,10,12,12, + 13,13,14,14,15,16,15,16, 9,10,10,12,12,12,13,14, + 14,14,15,16,15,10,12,12,13,13,15,13,16,16,15,17, + 17,17,10,11,11,12,14,14,14,15,15,17,17,15,17,11, + 12,12,14,14,14,15,15,15,17,16,17,17,10,12,12,13, + 14,14,14,17,15,17,17,17,17, +}; + +static float _vq_quantthresh__44u2__p7_1[] = { + -71.5, -58.5, -45.5, -32.5, -19.5, -6.5, 6.5, 19.5, + 32.5, 45.5, 58.5, 71.5, +}; + +static long _vq_quantmap__44u2__p7_1[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44u2__p7_1 = { + _vq_quantthresh__44u2__p7_1, + _vq_quantmap__44u2__p7_1, + 13, + 13 +}; + +static static_codebook _44u2__p7_1 = { + 2, 169, + _vq_lengthlist__44u2__p7_1, + 1, -523010048, 1618608128, 4, 0, + _vq_quantlist__44u2__p7_1, + NULL, + &_vq_auxt__44u2__p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u2__p7_2[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44u2__p7_2[] = { + 2, 5, 5, 6, 6, 7, 7, 8, 7, 8, 8, 8, 8, 5, 6, 6, + 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 5, 6, 6, 7, 7, 8, + 7, 8, 8, 8, 8, 8, 8, 6, 7, 7, 7, 8, 8, 8, 8, 8, + 9, 9, 9, 9, 6, 7, 7, 8, 7, 8, 8, 9, 9, 9, 9, 9, + 9, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 7, 8, + 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, +}; + +static float _vq_quantthresh__44u2__p7_2[] = { + -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, + 2.5, 3.5, 4.5, 5.5, +}; + +static long _vq_quantmap__44u2__p7_2[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44u2__p7_2 = { + _vq_quantthresh__44u2__p7_2, + _vq_quantmap__44u2__p7_2, + 13, + 13 +}; + +static static_codebook _44u2__p7_2 = { + 2, 169, + _vq_lengthlist__44u2__p7_2, + 1, -531103744, 1611661312, 4, 0, + _vq_quantlist__44u2__p7_2, + NULL, + &_vq_auxt__44u2__p7_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44u2__short[] = { + 13,15,17,17,15,15,12,17,11, 9, 7,10,10, 9,12,17, + 10, 6, 3, 6, 5, 7,10,17,15,10, 6, 9, 8, 9,11,17, + 15, 8, 4, 7, 3, 5, 9,16,16,10, 5, 8, 4, 5, 8,16, + 13,11, 5, 8, 3, 3, 5,14,13,12, 7,10, 5, 5, 7,14, +}; + +static static_codebook _huff_book__44u2__short = { + 2, 64, + _huff_lengthlist__44u2__short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44u3__long[] = { + 6, 9,13,12,14,11,10,13, 8, 4, 5, 7, 8, 7, 8,12, + 11, 4, 3, 5, 5, 7, 9,14,11, 6, 5, 6, 6, 6, 7,13, + 13, 7, 5, 6, 4, 5, 7,14,11, 7, 6, 6, 5, 5, 6,13, + 9, 7, 8, 6, 7, 5, 3, 9, 9,12,13,12,14,10, 6, 7, +}; + +static static_codebook _huff_book__44u3__long = { + 2, 64, + _huff_lengthlist__44u3__long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44u3__p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u3__p1_0[] = { + 1, 4, 4, 5, 8, 7, 5, 7, 8, 5, 8, 8, 8,10,11, 8, + 10,11, 5, 8, 8, 8,11,10, 8,11,11, 4, 8, 8, 8,11, + 11, 8,11,11, 8,11,11,11,13,14,11,14,14, 8,11,11, + 10,14,12,11,14,14, 4, 8, 8, 8,11,11, 8,11,11, 7, + 11,11,11,14,14,10,12,14, 8,11,11,11,14,14,11,14, + 13, +}; + +static float _vq_quantthresh__44u3__p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44u3__p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u3__p1_0 = { + _vq_quantthresh__44u3__p1_0, + _vq_quantmap__44u3__p1_0, + 3, + 3 +}; + +static static_codebook _44u3__p1_0 = { + 4, 81, + _vq_lengthlist__44u3__p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44u3__p1_0, + NULL, + &_vq_auxt__44u3__p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u3__p2_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u3__p2_0[] = { + 2, 5, 4, 5, 6, 6, 5, 6, 6, 5, 6, 6, 7, 8, 8, 6, + 8, 8, 5, 6, 6, 6, 8, 8, 7, 8, 8, 5, 7, 6, 7, 8, + 8, 6, 8, 8, 7, 8, 8, 8, 9,10, 8,10,10, 6, 8, 8, + 8,10, 8, 8,10,10, 5, 6, 6, 6, 8, 8, 7, 8, 8, 6, + 8, 8, 8,10,10, 8, 8,10, 7, 8, 8, 8,10,10, 8,10, + 9, +}; + +static float _vq_quantthresh__44u3__p2_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44u3__p2_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u3__p2_0 = { + _vq_quantthresh__44u3__p2_0, + _vq_quantmap__44u3__p2_0, + 3, + 3 +}; + +static static_codebook _44u3__p2_0 = { + 4, 81, + _vq_lengthlist__44u3__p2_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44u3__p2_0, + NULL, + &_vq_auxt__44u3__p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u3__p3_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u3__p3_0[] = { + 2, 4, 4, 7, 7, 5, 7, 7, 9, 9, 5, 7, 7, 9, 9, 8, + 9, 9,12,12, 8, 9, 9,11,12, 5, 7, 7,10,10, 7, 9, + 9,11,11, 7, 9, 9,10,11,10,11,11,13,13, 9,10,11, + 13,13, 5, 7, 7,10,10, 7, 9, 9,11,10, 7, 9, 9,11, + 11, 9,11,10,13,13,10,11,11,14,13, 8,10,10,14,13, + 10,11,11,15,14, 9,11,11,14,14,13,14,13,16,16,12, + 13,13,15,15, 8,10,10,13,14, 9,11,11,14,14,10,11, + 11,14,15,12,13,13,15,15,13,14,14,15,16, 5, 7, 7, + 10,10, 7, 9, 9,11,11, 7, 9, 9,11,12,10,11,11,14, + 14,10,11,11,14,14, 7, 9, 9,12,12, 9,11,11,13,13, + 9,11,11,13,13,12,12,13,15,15,11,12,13,15,16, 7, + 9, 9,11,11, 8,11,10,13,12, 9,11,11,13,13,11,13, + 12,15,13,11,13,13,15,16, 9,12,11,15,14,11,12,13, + 16,15,11,13,13,15,16,14,14,15,17,16,13,15,16, 0, + 17, 9,11,11,15,15,10,13,12,15,15,11,13,13,15,16, + 13,15,13,16,15,14,16,15, 0,19, 5, 7, 7,10,10, 7, + 9, 9,11,11, 7, 9, 9,11,11,10,12,11,14,14,10,11, + 12,14,14, 7, 9, 9,12,12, 9,11,11,14,13, 9,10,11, + 12,13,11,13,13,16,16,11,12,13,13,16, 7, 9, 9,12, + 12, 9,11,11,13,13, 9,11,11,13,13,11,13,13,15,15, + 12,13,12,15,14, 9,11,11,15,14,11,13,12,16,16,10, + 12,12,15,15,13,15,15,17,19,13,14,15,16,17,10,12, + 12,15,15,11,13,13,16,16,11,13,13,15,16,13,15,15, + 0, 0,14,15,15,16,16, 8,10,10,14,14,10,12,12,15, + 15,10,12,11,15,16,14,15,15,19,20,13,14,14,18,16, + 9,11,11,15,15,11,13,13,17,16,11,13,13,16,16,15, + 17,17,20,20,14,15,16,17,20, 9,11,11,15,15,10,13, + 12,16,15,11,13,13,15,17,14,16,15,18, 0,14,16,15, + 18,20,12,14,14, 0, 0,14,14,16, 0, 0,13,16,15, 0, + 0,17,17,18, 0, 0,16,17,19,19, 0,12,14,14,18, 0, + 12,16,14, 0,17,13,15,15,18, 0,16,18,17, 0,17,16, + 18,17, 0, 0, 7,10,10,14,14,10,12,11,15,15,10,12, + 12,16,15,13,15,15,18, 0,14,15,15,17, 0, 9,11,11, + 15,15,11,13,13,16,16,11,12,13,16,16,14,15,16,17, + 17,14,16,16,16,18, 9,11,12,16,16,11,13,13,17,17, + 11,14,13,20,17,15,16,16,19, 0,15,16,17, 0,19,11, + 13,14,17,16,14,15,15,20,18,13,14,15,17,19,16,18, + 18, 0,20,16,16,19,17, 0,12,15,14,17, 0,14,15,15, + 18,19,13,16,15,19,20,15,18,18, 0,20,17, 0,16, 0, + 0, +}; + +static float _vq_quantthresh__44u3__p3_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u3__p3_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u3__p3_0 = { + _vq_quantthresh__44u3__p3_0, + _vq_quantmap__44u3__p3_0, + 5, + 5 +}; + +static static_codebook _44u3__p3_0 = { + 4, 625, + _vq_lengthlist__44u3__p3_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u3__p3_0, + NULL, + &_vq_auxt__44u3__p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u3__p4_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u3__p4_0[] = { + 4, 5, 5, 8, 8, 5, 7, 6, 9, 9, 5, 6, 7, 9, 9, 9, + 9, 9,11,11, 9, 9, 9,11,11, 5, 7, 7, 9, 9, 7, 8, + 8,10,10, 7, 7, 8,10,10, 9,10,10,11,12, 9,10,10, + 11,12, 5, 7, 7, 9, 9, 7, 8, 7,10,10, 7, 8, 8,10, + 10, 9,10, 9,12,11, 9,10,10,12,11, 9,10, 9,12,12, + 9,10,10,13,12, 9,10,10,12,13,12,12,12,14,14,11, + 12,12,13,14, 9, 9,10,12,12, 9,10,10,12,12, 9,10, + 10,12,13,11,12,11,14,13,12,12,12,14,13, 5, 7, 7, + 9, 9, 7, 8, 8,10,10, 7, 8, 8,10,10,10,10,10,12, + 12, 9,10,10,12,12, 7, 8, 8,11,10, 8, 8, 9,11,11, + 8, 9, 9,11,11,11,11,11,12,13,10,11,11,13,13, 6, + 8, 8,10,10, 7, 9, 8,11,10, 8, 9, 9,11,11,10,11, + 10,13,11,10,11,11,13,13, 9,11,10,13,12,10,11,11, + 13,13,10,11,11,13,13,12,12,13,12,15,12,13,13,15, + 15, 9,10,10,12,13,10,11,10,13,12,10,11,11,13,14, + 12,13,11,15,13,12,13,13,15,15, 5, 7, 7, 9, 9, 7, + 8, 8,10,10, 7, 8, 8,10,10, 9,10,10,12,12,10,10, + 11,12,12, 6, 8, 8,10,10, 8, 9, 9,11,11, 7, 8, 9, + 10,11,10,11,11,13,13,10,10,11,11,13, 7, 8, 8,10, + 10, 8, 9, 9,11,11, 8, 9, 9,11,11,10,11,11,13,13, + 11,11,11,13,12, 9,10,10,13,12,10,11,11,14,13,10, + 10,11,12,13,12,13,13,15,15,12,11,13,13,14, 9,10, + 11,12,13,10,11,11,13,13,10,11,11,13,13,12,13,13, + 15,15,12,13,12,15,12, 8, 9, 9,12,12, 9,11,10,13, + 13, 9,10,10,13,13,12,13,13,15,14,12,12,12,14,13, + 9,10,10,13,12,10,11,11,13,13,10,11,11,14,12,13, + 13,14,14,16,12,13,13,15,15, 9,10,10,13,13,10,11, + 10,14,13,10,11,11,13,14,12,14,13,15,14,13,13,13, + 15,15,11,13,12,15,14,11,12,13,14,15,12,13,13,16, + 14,14,12,15,12,16,14,15,15,17,15,11,12,12,14,14, + 11,13,11,15,14,12,13,13,15,15,13,15,12,17,13,14, + 15,15,16,16, 8, 9, 9,12,12, 9,10,10,12,13, 9,10, + 10,13,13,12,12,12,14,14,12,13,13,15,15, 9,10,10, + 13,12,10,11,11,14,13,10,10,11,13,14,12,13,13,15, + 15,12,12,13,14,16, 9,10,10,13,13,10,11,11,13,14, + 10,11,11,14,13,12,13,13,14,15,13,14,13,16,14,11, + 12,12,14,14,12,13,13,15,14,11,12,13,14,15,14,15, + 15,16,16,13,13,15,13,16,11,12,12,14,15,12,13,13, + 14,15,11,13,12,15,14,14,15,15,16,16,14,15,12,16, + 13, +}; + +static float _vq_quantthresh__44u3__p4_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u3__p4_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u3__p4_0 = { + _vq_quantthresh__44u3__p4_0, + _vq_quantmap__44u3__p4_0, + 5, + 5 +}; + +static static_codebook _44u3__p4_0 = { + 4, 625, + _vq_lengthlist__44u3__p4_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u3__p4_0, + NULL, + &_vq_auxt__44u3__p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u3__p5_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44u3__p5_0[] = { + 2, 3, 3, 6, 6, 7, 7, 9, 9, 4, 5, 5, 7, 7, 8, 8, + 10,10, 4, 5, 5, 7, 7, 8, 8,10,10, 6, 7, 7, 8, 8, + 9, 9,11,10, 6, 7, 7, 8, 8, 9, 9,10,10, 7, 8, 8, + 9, 9,10,10,11,11, 7, 8, 8, 9, 9,10,10,11,11, 9, + 10,10,11,10,11,11,12,12, 9,10,10,10,10,11,11,12, + 12, +}; + +static float _vq_quantthresh__44u3__p5_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44u3__p5_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44u3__p5_0 = { + _vq_quantthresh__44u3__p5_0, + _vq_quantmap__44u3__p5_0, + 9, + 9 +}; + +static static_codebook _44u3__p5_0 = { + 2, 81, + _vq_lengthlist__44u3__p5_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44u3__p5_0, + NULL, + &_vq_auxt__44u3__p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u3__p6_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44u3__p6_0[] = { + 1, 4, 4, 6, 6, 8, 8, 9, 9,10,11,13,14, 4, 6, 5, + 8, 8, 9, 9,10,10,11,11,14,14, 4, 6, 6, 8, 8, 9, + 9,10,10,11,11,14,14, 6, 8, 8, 9, 9,10,10,11,11, + 12,12,15,15, 6, 8, 8, 9, 9,10,11,11,11,12,12,15, + 15, 8, 9, 9,11,10,11,11,12,12,13,13,15,16, 8, 9, + 9,10,11,11,11,12,12,13,13,16,16,10,10,11,11,11, + 12,12,13,13,13,14,17,16, 9,10,11,12,11,12,12,13, + 13,13,13,16,18,11,12,11,12,12,13,13,13,14,15,14, + 17,17,11,11,12,12,12,13,13,13,14,14,15,18,17,14, + 15,15,15,15,16,16,17,17,19,18, 0,20,14,15,14,15, + 15,16,16,16,17,18,16,20,18, +}; + +static float _vq_quantthresh__44u3__p6_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44u3__p6_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44u3__p6_0 = { + _vq_quantthresh__44u3__p6_0, + _vq_quantmap__44u3__p6_0, + 13, + 13 +}; + +static static_codebook _44u3__p6_0 = { + 2, 169, + _vq_lengthlist__44u3__p6_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44u3__p6_0, + NULL, + &_vq_auxt__44u3__p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u3__p6_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u3__p6_1[] = { + 2, 4, 4, 5, 5, 4, 5, 5, 6, 5, 4, 5, 5, 5, 6, 5, + 6, 5, 6, 6, 5, 5, 6, 6, 6, +}; + +static float _vq_quantthresh__44u3__p6_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u3__p6_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u3__p6_1 = { + _vq_quantthresh__44u3__p6_1, + _vq_quantmap__44u3__p6_1, + 5, + 5 +}; + +static static_codebook _44u3__p6_1 = { + 2, 25, + _vq_lengthlist__44u3__p6_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u3__p6_1, + NULL, + &_vq_auxt__44u3__p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u3__p7_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44u3__p7_0[] = { + 1, 3, 3,10,10,10,10,10,10, 4,10,10,10,10,10,10, + 10,10, 4,10,10,10,10,10,10,10,10,10,10, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, +}; + +static float _vq_quantthresh__44u3__p7_0[] = { + -892.5, -637.5, -382.5, -127.5, 127.5, 382.5, 637.5, 892.5, +}; + +static long _vq_quantmap__44u3__p7_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44u3__p7_0 = { + _vq_quantthresh__44u3__p7_0, + _vq_quantmap__44u3__p7_0, + 9, + 9 +}; + +static static_codebook _44u3__p7_0 = { + 2, 81, + _vq_lengthlist__44u3__p7_0, + 1, -515907584, 1627381760, 4, 0, + _vq_quantlist__44u3__p7_0, + NULL, + &_vq_auxt__44u3__p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u3__p7_1[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__44u3__p7_1[] = { + 1, 4, 4, 6, 6, 7, 6, 8, 7, 9, 8,10, 9,11,11, 4, + 7, 7, 8, 7, 9, 9,10,10,11,11,11,11,12,12, 4, 7, + 7, 7, 7, 9, 9,10,10,11,11,12,12,12,11, 6, 8, 8, + 9, 9,10,10,11,11,12,12,13,12,13,13, 6, 8, 8, 9, + 9,10,11,11,11,12,12,13,14,13,13, 8, 9, 9,11,11, + 12,12,12,13,14,13,14,14,14,15, 8, 9, 9,11,11,11, + 12,13,14,13,14,15,17,14,15, 9,10,10,12,12,13,13, + 13,14,15,15,15,16,16,16, 9,11,11,12,12,13,13,14, + 14,14,15,16,16,16,16,10,12,12,13,13,14,14,15,15, + 15,16,17,17,17,17,10,12,11,13,13,15,14,15,14,16, + 17,16,16,16,16,11,13,12,14,14,14,14,15,16,17,16, + 17,17,17,17,11,13,12,14,14,14,15,17,16,17,17,17, + 17,17,17,12,13,13,15,16,15,16,17,17,16,16,17,17, + 17,17,12,13,13,15,15,15,16,17,17,17,16,17,16,17, + 17, +}; + +static float _vq_quantthresh__44u3__p7_1[] = { + -110.5, -93.5, -76.5, -59.5, -42.5, -25.5, -8.5, 8.5, + 25.5, 42.5, 59.5, 76.5, 93.5, 110.5, +}; + +static long _vq_quantmap__44u3__p7_1[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__44u3__p7_1 = { + _vq_quantthresh__44u3__p7_1, + _vq_quantmap__44u3__p7_1, + 15, + 15 +}; + +static static_codebook _44u3__p7_1 = { + 2, 225, + _vq_lengthlist__44u3__p7_1, + 1, -522338304, 1620115456, 4, 0, + _vq_quantlist__44u3__p7_1, + NULL, + &_vq_auxt__44u3__p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u3__p7_2[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44u3__p7_2[] = { + 2, 5, 5, 7, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 5, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, + 10,10, 5, 6, 6, 7, 7, 8, 8, 8, 8, 9, 8, 9, 9, 9, + 9,10, 9, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, + 10,10,10,10, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9,10, + 9,10,10,10,10, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, + 10,10,10,10,10,10, 7, 8, 8, 9, 8, 9, 9, 9, 9,10, + 9,10,10,10,10,10,10, 8, 8, 8, 9, 9, 9, 9, 9, 9, + 9,10,10,10,10,10,10,10, 8, 9, 8, 9, 9, 9, 9,10, + 9,10,10,10,10,10,10,10,10, 9, 9, 9, 9, 9, 9,10, + 9,10,10,10,10,10,10,10,10,10, 9, 9, 9, 9, 9,10, + 9,10,10,10,10,10,10,10,10,10,10, 9, 9, 9,10, 9, + 10,10,10,10,10,10,10,10,10,10,10,10, 9, 9, 9,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10, 9, 9, 9, + 10,10,10,10,10,10,10,10,10,10,10,10,10,11, 9,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,11, 9, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 9,10,10,10,10,10,10,10,10,10,10,10,11,11,11,10, + 11, +}; + +static float _vq_quantthresh__44u3__p7_2[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44u3__p7_2[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44u3__p7_2 = { + _vq_quantthresh__44u3__p7_2, + _vq_quantmap__44u3__p7_2, + 17, + 17 +}; + +static static_codebook _44u3__p7_2 = { + 2, 289, + _vq_lengthlist__44u3__p7_2, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44u3__p7_2, + NULL, + &_vq_auxt__44u3__p7_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44u3__short[] = { + 14,14,14,15,13,15,12,16,10, 8, 7, 9, 9, 8,12,16, + 10, 5, 4, 6, 5, 6, 9,16,14, 8, 6, 8, 7, 8,10,16, + 14, 7, 4, 6, 3, 5, 8,16,15, 9, 5, 7, 4, 4, 7,16, + 13,10, 6, 7, 4, 3, 4,13,13,12, 7, 9, 5, 5, 6,12, +}; + +static static_codebook _huff_book__44u3__short = { + 2, 64, + _huff_lengthlist__44u3__short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44u4__long[] = { + 3, 8,12,12,13,12,11,13, 5, 4, 6, 7, 8, 8, 9,13, + 9, 5, 4, 5, 5, 7, 9,13, 9, 6, 5, 6, 6, 7, 8,12, + 12, 7, 5, 6, 4, 5, 8,13,11, 7, 6, 6, 5, 5, 6,12, + 10, 8, 8, 7, 7, 5, 3, 8,10,12,13,12,12, 9, 6, 7, +}; + +static static_codebook _huff_book__44u4__long = { + 2, 64, + _huff_lengthlist__44u4__long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44u4__p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u4__p1_0[] = { + 1, 4, 4, 5, 8, 7, 5, 7, 8, 5, 8, 8, 8,10,11, 8, + 10,11, 5, 8, 8, 8,11,10, 8,11,11, 4, 8, 8, 8,11, + 11, 8,11,11, 8,11,11,11,13,14,11,15,14, 8,11,11, + 10,13,12,11,14,14, 4, 8, 8, 8,11,11, 8,11,11, 7, + 11,11,11,15,14,10,12,14, 8,11,11,11,14,14,11,14, + 13, +}; + +static float _vq_quantthresh__44u4__p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44u4__p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u4__p1_0 = { + _vq_quantthresh__44u4__p1_0, + _vq_quantmap__44u4__p1_0, + 3, + 3 +}; + +static static_codebook _44u4__p1_0 = { + 4, 81, + _vq_lengthlist__44u4__p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44u4__p1_0, + NULL, + &_vq_auxt__44u4__p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u4__p2_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u4__p2_0[] = { + 2, 5, 5, 5, 6, 6, 5, 6, 6, 5, 6, 6, 7, 8, 8, 6, + 8, 8, 5, 6, 6, 6, 8, 8, 7, 8, 8, 5, 7, 6, 6, 8, + 8, 6, 8, 8, 6, 8, 8, 8, 9,10, 8,10,10, 6, 8, 8, + 8,10, 8, 8,10,10, 5, 6, 6, 6, 8, 8, 6, 8, 8, 6, + 8, 8, 8,10,10, 8, 8,10, 6, 8, 8, 8,10,10, 8,10, + 9, +}; + +static float _vq_quantthresh__44u4__p2_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44u4__p2_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u4__p2_0 = { + _vq_quantthresh__44u4__p2_0, + _vq_quantmap__44u4__p2_0, + 3, + 3 +}; + +static static_codebook _44u4__p2_0 = { + 4, 81, + _vq_lengthlist__44u4__p2_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44u4__p2_0, + NULL, + &_vq_auxt__44u4__p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u4__p3_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u4__p3_0[] = { + 2, 4, 4, 8, 8, 5, 7, 7, 9, 9, 5, 7, 7, 9, 9, 8, + 10, 9,12,12, 8, 9,10,12,12, 5, 7, 7,10,10, 7, 9, + 9,11,11, 7, 9, 9,11,11,10,12,11,14,14, 9,10,11, + 13,14, 5, 7, 7,10,10, 7, 9, 9,11,11, 7, 9, 9,11, + 11, 9,11,10,14,13,10,11,11,14,14, 8,10,10,14,13, + 10,12,12,15,14, 9,11,11,15,14,13,14,14,17,17,12, + 14,14,16,16, 8,10,10,14,14, 9,11,11,14,15,10,12, + 12,14,15,12,14,13,16,16,13,14,15,15,18, 4, 7, 7, + 10,10, 7, 9, 9,12,11, 7, 9, 9,11,12,10,12,11,15, + 14,10,11,12,14,15, 7, 9, 9,12,12, 9,11,12,13,13, + 9,11,12,13,13,12,13,13,15,16,11,13,13,15,16, 7, + 9, 9,12,12, 9,11,10,13,12, 9,11,12,13,14,11,13, + 12,16,14,12,13,13,15,16,10,12,12,16,15,11,13,13, + 17,16,11,13,13,17,16,14,15,15,17,17,14,16,16,18, + 20, 9,11,11,15,16,11,13,12,16,16,11,13,13,16,17, + 14,15,14,18,16,14,16,16,17,20, 5, 7, 7,10,10, 7, + 9, 9,12,11, 7, 9,10,11,12,10,12,11,15,15,10,12, + 12,14,14, 7, 9, 9,12,12, 9,12,11,14,13, 9,10,11, + 12,13,12,13,14,16,16,11,12,13,14,16, 7, 9, 9,12, + 12, 9,12,11,13,13, 9,12,11,13,13,11,13,13,16,16, + 12,13,13,16,15, 9,11,11,16,14,11,13,13,16,16,11, + 12,13,16,16,14,16,16,17,17,13,14,15,16,17,10,12, + 12,15,15,11,13,13,16,17,11,13,13,16,16,14,16,15, + 19,19,14,15,15,17,18, 8,10,10,14,14,10,12,12,15, + 15,10,12,12,16,16,14,16,15,20,19,13,15,15,17,16, + 9,12,12,16,16,11,13,13,16,18,11,14,13,16,17,16, + 17,16,20, 0,15,16,18,18,20, 9,11,11,15,15,11,14, + 12,17,16,11,13,13,17,17,15,17,15,20,20,14,16,16, + 17, 0,13,15,14,18,16,14,15,16, 0,18,14,16,16, 0, + 0,18,16, 0, 0,20,16,18,18, 0, 0,12,14,14,17,18, + 13,15,14,20,18,14,16,15,19,19,16,20,16, 0,18,16, + 19,17,19, 0, 8,10,10,14,14,10,12,12,16,15,10,12, + 12,16,16,13,15,15,18,17,14,16,16,19, 0, 9,11,11, + 16,15,11,14,13,18,17,11,12,13,17,18,14,17,16,18, + 18,15,16,17,18,18, 9,12,12,16,16,11,13,13,16,18, + 11,14,13,17,17,15,16,16,18,20,16,17,17,20,20,12, + 14,14,18,17,14,16,16, 0,19,13,14,15,18, 0,16, 0, + 0, 0, 0,16,16, 0,19,20,13,15,14, 0, 0,14,16,16, + 18,19,14,16,15, 0,20,16,20,18, 0,20,17,20,17, 0, + 0, +}; + +static float _vq_quantthresh__44u4__p3_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u4__p3_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u4__p3_0 = { + _vq_quantthresh__44u4__p3_0, + _vq_quantmap__44u4__p3_0, + 5, + 5 +}; + +static static_codebook _44u4__p3_0 = { + 4, 625, + _vq_lengthlist__44u4__p3_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u4__p3_0, + NULL, + &_vq_auxt__44u4__p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u4__p4_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u4__p4_0[] = { + 4, 5, 5, 8, 8, 5, 7, 6, 9, 9, 5, 6, 7, 9, 9, 9, + 9, 9,11,11, 8, 9, 9,11,11, 5, 7, 7, 9, 9, 7, 8, + 8,10,10, 7, 7, 8,10,10, 9,10,10,11,12, 9,10,10, + 11,12, 5, 7, 7, 9, 9, 7, 8, 7,10,10, 7, 8, 8,10, + 10, 9,10,10,12,11, 9,10,10,12,11, 9,10, 9,12,12, + 9,10,10,13,12, 9,10,10,12,12,12,12,12,14,14,11, + 12,12,13,14, 9, 9,10,12,12, 9,10,10,13,13, 9,10, + 10,12,13,11,12,12,14,13,11,12,12,14,14, 5, 7, 7, + 9, 9, 7, 8, 8,10,10, 7, 8, 8,10,10,10,10,10,12, + 12, 9,10,10,12,12, 7, 8, 8,11,10, 8, 8, 9,11,11, + 8, 9, 9,11,11,11,11,11,12,13,10,11,11,13,13, 6, + 8, 8,10,10, 7, 9, 8,11,10, 8, 9, 9,11,11,10,11, + 10,13,11,10,11,11,13,13, 9,11,10,13,12,10,11,11, + 13,14,10,11,11,14,13,12,12,13,12,15,12,13,13,15, + 15, 9,10,10,12,13,10,11,10,13,12,10,11,11,13,14, + 12,13,11,15,13,13,13,13,15,15, 5, 7, 7, 9, 9, 7, + 8, 8,10,10, 7, 8, 8,10,10, 9,10,10,12,12,10,10, + 11,12,13, 6, 8, 8,10,10, 8, 9, 9,11,11, 7, 8, 9, + 10,11,10,11,11,13,13,10,10,11,11,13, 7, 8, 8,10, + 11, 8, 9, 9,11,11, 8, 9, 8,11,11,10,11,11,13,13, + 11,12,11,13,12, 9,10,10,13,12,10,11,11,14,13,10, + 10,11,12,13,12,13,13,15,15,12,11,13,13,14, 9,10, + 11,12,13,10,11,11,13,14,10,11,11,13,13,12,13,13, + 15,15,12,13,12,15,12, 8, 9, 9,12,12, 9,11,10,13, + 13, 9,10,10,13,13,12,13,13,15,15,12,12,12,14,14, + 9,10,10,13,13,10,11,11,13,14,10,11,11,14,13,13, + 13,14,14,16,13,13,13,15,15, 9,10,10,13,13,10,11, + 10,14,13,10,11,11,13,14,12,14,13,16,14,12,13,13, + 14,15,11,12,12,15,14,11,12,13,14,15,12,13,13,16, + 15,14,12,15,12,16,14,15,15,16,16,11,12,12,14,14, + 11,13,12,15,14,12,13,13,15,16,13,15,13,17,13,14, + 15,15,16,17, 8, 9, 9,12,12, 9,10,10,12,13, 9,10, + 10,13,13,12,12,12,14,14,12,13,13,15,15, 9,10,10, + 13,12,10,11,11,14,13,10,10,11,13,14,13,13,13,15, + 15,12,13,14,14,16, 9,10,10,13,13,10,11,11,13,14, + 10,11,11,14,14,13,13,13,15,15,13,14,13,16,14,11, + 12,12,15,14,12,13,13,16,15,11,12,13,14,15,14,15, + 15,17,16,13,13,15,13,16,11,12,13,14,15,13,13,13, + 15,16,11,13,12,15,14,14,15,15,16,16,14,15,12,17, + 13, +}; + +static float _vq_quantthresh__44u4__p4_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u4__p4_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u4__p4_0 = { + _vq_quantthresh__44u4__p4_0, + _vq_quantmap__44u4__p4_0, + 5, + 5 +}; + +static static_codebook _44u4__p4_0 = { + 4, 625, + _vq_lengthlist__44u4__p4_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u4__p4_0, + NULL, + &_vq_auxt__44u4__p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u4__p5_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44u4__p5_0[] = { + 2, 3, 3, 6, 6, 7, 7, 9, 9, 4, 5, 5, 7, 7, 8, 8, + 10, 9, 4, 5, 5, 7, 7, 8, 8,10,10, 6, 7, 7, 8, 8, + 9, 9,11,10, 6, 7, 7, 8, 8, 9, 9,10,11, 7, 8, 8, + 9, 9,10,10,11,11, 7, 8, 8, 9, 9,10,10,11,11, 9, + 10,10,11,10,11,11,12,12, 9,10,10,10,11,11,11,12, + 12, +}; + +static float _vq_quantthresh__44u4__p5_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44u4__p5_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44u4__p5_0 = { + _vq_quantthresh__44u4__p5_0, + _vq_quantmap__44u4__p5_0, + 9, + 9 +}; + +static static_codebook _44u4__p5_0 = { + 2, 81, + _vq_lengthlist__44u4__p5_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44u4__p5_0, + NULL, + &_vq_auxt__44u4__p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u4__p6_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44u4__p6_0[] = { + 1, 4, 4, 6, 6, 8, 8, 9, 9,11,10,13,13, 4, 6, 5, + 8, 8, 9, 9,10,10,11,11,14,14, 4, 6, 6, 8, 8, 9, + 9,10,10,11,11,14,14, 6, 8, 8, 9, 9,10,10,11,11, + 12,12,15,15, 6, 8, 8, 9, 9,10,11,11,11,12,12,15, + 15, 8, 9, 9,11,10,11,11,12,12,13,13,16,16, 8, 9, + 9,10,10,11,11,12,12,13,13,16,16,10,10,10,12,11, + 12,12,13,13,14,14,16,16,10,10,10,11,12,12,12,13, + 13,13,14,16,17,11,12,11,12,12,13,13,14,14,15,14, + 18,17,11,11,12,12,12,13,13,14,14,14,15,19,18,14, + 15,14,15,15,17,16,17,17,17,17,21, 0,14,15,15,16, + 16,16,16,17,17,18,17,20,21, +}; + +static float _vq_quantthresh__44u4__p6_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44u4__p6_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44u4__p6_0 = { + _vq_quantthresh__44u4__p6_0, + _vq_quantmap__44u4__p6_0, + 13, + 13 +}; + +static static_codebook _44u4__p6_0 = { + 2, 169, + _vq_lengthlist__44u4__p6_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44u4__p6_0, + NULL, + &_vq_auxt__44u4__p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u4__p6_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u4__p6_1[] = { + 2, 4, 4, 5, 5, 4, 5, 5, 6, 5, 4, 5, 5, 5, 6, 5, + 6, 5, 6, 6, 5, 5, 6, 6, 6, +}; + +static float _vq_quantthresh__44u4__p6_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u4__p6_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u4__p6_1 = { + _vq_quantthresh__44u4__p6_1, + _vq_quantmap__44u4__p6_1, + 5, + 5 +}; + +static static_codebook _44u4__p6_1 = { + 2, 25, + _vq_lengthlist__44u4__p6_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u4__p6_1, + NULL, + &_vq_auxt__44u4__p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u4__p7_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44u4__p7_0[] = { + 1, 3, 3,12,12,12,12,12,12,12,12,12,12, 3,12,11, + 12,12,12,12,12,12,12,12,12,12, 4,11,10,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11, +}; + +static float _vq_quantthresh__44u4__p7_0[] = { + -1402.5, -1147.5, -892.5, -637.5, -382.5, -127.5, 127.5, 382.5, + 637.5, 892.5, 1147.5, 1402.5, +}; + +static long _vq_quantmap__44u4__p7_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44u4__p7_0 = { + _vq_quantthresh__44u4__p7_0, + _vq_quantmap__44u4__p7_0, + 13, + 13 +}; + +static static_codebook _44u4__p7_0 = { + 2, 169, + _vq_lengthlist__44u4__p7_0, + 1, -514332672, 1627381760, 4, 0, + _vq_quantlist__44u4__p7_0, + NULL, + &_vq_auxt__44u4__p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u4__p7_1[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__44u4__p7_1[] = { + 1, 4, 4, 6, 6, 7, 7, 9, 8,10, 8,10, 9,11,11, 4, + 7, 6, 8, 7, 9, 9,10,10,11,10,11,10,12,10, 4, 6, + 7, 8, 8, 9, 9,10,10,11,11,11,11,12,12, 6, 8, 8, + 10, 9,11,10,12,11,12,12,12,12,13,13, 6, 8, 8,10, + 10,10,11,11,11,12,12,13,12,13,13, 8, 9, 9,11,11, + 12,11,12,12,13,13,13,13,13,13, 8, 9, 9,11,11,11, + 12,12,12,13,13,13,13,13,13, 9,10,10,12,11,13,13, + 13,13,14,13,13,14,14,14, 9,10,11,11,12,12,13,13, + 13,13,13,14,15,14,14,10,11,11,12,12,13,13,14,14, + 14,14,14,15,16,16,10,11,11,12,13,13,13,13,15,14, + 14,15,16,15,16,10,12,12,13,13,14,14,14,15,15,15, + 15,15,15,16,11,12,12,13,13,14,14,14,15,15,15,16, + 15,17,16,11,12,12,13,13,13,15,15,14,16,16,16,16, + 16,17,11,12,12,13,13,14,14,15,14,15,15,17,17,16, + 16, +}; + +static float _vq_quantthresh__44u4__p7_1[] = { + -110.5, -93.5, -76.5, -59.5, -42.5, -25.5, -8.5, 8.5, + 25.5, 42.5, 59.5, 76.5, 93.5, 110.5, +}; + +static long _vq_quantmap__44u4__p7_1[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__44u4__p7_1 = { + _vq_quantthresh__44u4__p7_1, + _vq_quantmap__44u4__p7_1, + 15, + 15 +}; + +static static_codebook _44u4__p7_1 = { + 2, 225, + _vq_lengthlist__44u4__p7_1, + 1, -522338304, 1620115456, 4, 0, + _vq_quantlist__44u4__p7_1, + NULL, + &_vq_auxt__44u4__p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u4__p7_2[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44u4__p7_2[] = { + 2, 5, 5, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 5, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, + 9, 9, 5, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, + 10,10,10,10, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9,10, + 9,10, 9,10,10, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, + 10,10,10,10,10,10, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, + 9,10,10,10,10,10,10, 8, 9, 8, 9, 9, 9, 9, 9, 9, + 10,10,10,10,10,10,10,10, 8, 8, 8, 9, 9, 9, 9, 9, + 10,10,10,10,10,10,10,10,10, 9, 9, 9, 9, 9,10,10, + 10,10,10,10,10,10,10,10,10,10, 9, 9, 9, 9, 9,10, + 10,10,10,10,10,10,10,10,10,10,10, 9, 9, 9, 9,10, + 10,10,10,10,10,10,10,10,10,10,10,10, 9, 9, 9, 9, + 10,10,10,10,10,10,10,10,10,11,10,10,10, 9, 9, 9, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10, 9, 9, + 9,10,10,10,10,10,10,10,10,10,10,10,10,10,10, 9, + 10, 9,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 9,10, 9,10,10,10,10,10,10,10,10,10,10,11,10,10, + 10, +}; + +static float _vq_quantthresh__44u4__p7_2[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44u4__p7_2[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44u4__p7_2 = { + _vq_quantthresh__44u4__p7_2, + _vq_quantmap__44u4__p7_2, + 17, + 17 +}; + +static static_codebook _44u4__p7_2 = { + 2, 289, + _vq_lengthlist__44u4__p7_2, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44u4__p7_2, + NULL, + &_vq_auxt__44u4__p7_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44u4__short[] = { + 14,17,15,17,16,14,13,16,10, 7, 7,10,13,10,15,16, + 9, 4, 4, 6, 5, 7, 9,16,12, 8, 7, 8, 8, 8,11,16, + 14, 7, 4, 6, 3, 5, 8,15,13, 8, 5, 7, 4, 5, 7,16, + 12, 9, 6, 8, 3, 3, 5,16,14,13, 7,10, 5, 5, 7,15, +}; + +static static_codebook _huff_book__44u4__short = { + 2, 64, + _huff_lengthlist__44u4__short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44u5__long[] = { + 3, 8,13,12,14,12,16,11,13,14, 5, 4, 5, 6, 7, 8, + 10, 9,12,15,10, 5, 5, 5, 6, 8, 9, 9,13,15,10, 5, + 5, 6, 6, 7, 8, 8,11,13,12, 7, 5, 6, 4, 6, 7, 7, + 11,14,11, 7, 7, 6, 6, 6, 7, 6,10,14,14, 9, 8, 8, + 6, 7, 7, 7,11,16,11, 8, 8, 7, 6, 6, 7, 4, 7,12, + 10,10,12,10,10, 9,10, 5, 6, 9,10,12,15,13,14,14, + 14, 8, 7, 8, +}; + +static static_codebook _huff_book__44u5__long = { + 2, 100, + _huff_lengthlist__44u5__long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44u5__p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u5__p1_0[] = { + 1, 4, 4, 5, 8, 7, 5, 7, 7, 5, 8, 8, 8,10,10, 7, + 9,10, 5, 8, 8, 7,10, 9, 8,10,10, 5, 8, 8, 8,10, + 10, 8,10,10, 8,10,10,10,12,13,10,13,13, 7,10,10, + 10,13,11,10,13,13, 4, 8, 8, 8,11,10, 8,10,10, 7, + 10,10,10,13,13,10,11,13, 8,10,11,10,13,13,10,13, + 12, +}; + +static float _vq_quantthresh__44u5__p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44u5__p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u5__p1_0 = { + _vq_quantthresh__44u5__p1_0, + _vq_quantmap__44u5__p1_0, + 3, + 3 +}; + +static static_codebook _44u5__p1_0 = { + 4, 81, + _vq_lengthlist__44u5__p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44u5__p1_0, + NULL, + &_vq_auxt__44u5__p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u5__p2_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u5__p2_0[] = { + 3, 4, 4, 5, 6, 6, 5, 6, 6, 5, 6, 6, 6, 8, 8, 6, + 7, 8, 5, 6, 6, 6, 8, 7, 6, 8, 8, 5, 6, 6, 6, 8, + 8, 6, 8, 8, 6, 8, 8, 8, 9, 9, 8, 9, 9, 6, 8, 7, + 7, 9, 8, 8, 9, 9, 5, 6, 6, 6, 8, 7, 6, 8, 8, 6, + 8, 7, 8, 9, 9, 7, 8, 9, 6, 8, 8, 8, 9, 9, 8, 9, + 9, +}; + +static float _vq_quantthresh__44u5__p2_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44u5__p2_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u5__p2_0 = { + _vq_quantthresh__44u5__p2_0, + _vq_quantmap__44u5__p2_0, + 3, + 3 +}; + +static static_codebook _44u5__p2_0 = { + 4, 81, + _vq_lengthlist__44u5__p2_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44u5__p2_0, + NULL, + &_vq_auxt__44u5__p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u5__p3_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u5__p3_0[] = { + 2, 4, 5, 8, 8, 5, 7, 6, 9, 9, 5, 6, 7, 9, 9, 8, + 10, 9,13,12, 8, 9,10,12,12, 5, 7, 7,10,10, 7, 9, + 9,11,11, 6, 8, 9,11,11,10,11,11,14,14, 9,10,11, + 13,14, 5, 7, 7, 9,10, 7, 9, 8,11,11, 7, 9, 9,11, + 11, 9,11,10,14,13,10,11,11,14,14, 8,10,10,13,13, + 10,11,11,15,14, 9,11,11,14,14,13,14,14,17,16,12, + 13,13,15,16, 8,10,10,13,13, 9,11,11,14,15,10,11, + 11,14,15,12,14,13,16,16,13,15,14,15,17, 5, 7, 7, + 10,10, 7, 9, 9,11,11, 7, 9, 9,11,11,10,11,11,14, + 14,10,11,12,14,14, 7, 9, 9,12,11, 9,11,11,13,13, + 9,11,11,13,13,12,13,13,15,16,11,12,13,15,16, 6, + 9, 9,11,11, 8,11,10,13,12, 9,11,11,13,14,11,13, + 12,16,14,11,13,13,16,17,10,12,11,15,15,11,13,13, + 16,16,11,13,13,17,16,14,15,15,17,17,14,16,16,17, + 18, 9,11,11,14,15,10,12,12,15,15,11,13,13,16,17, + 13,15,13,17,15,14,15,16,18, 0, 5, 7, 7,10,10, 7, + 9, 9,11,11, 7, 9, 9,11,11,10,11,11,14,14,10,11, + 12,14,15, 6, 9, 9,12,11, 9,11,11,13,13, 8,10,11, + 12,13,11,13,13,16,15,11,12,13,14,15, 7, 9, 9,11, + 12, 9,11,11,13,13, 9,11,11,13,13,11,13,13,15,16, + 11,13,13,15,14, 9,11,11,15,14,11,13,13,17,15,10, + 12,12,15,15,14,16,16,17,17,13,13,15,15,17,10,11, + 12,15,15,11,13,13,16,16,11,13,13,15,15,14,15,15, + 18,18,14,15,15,17,17, 8,10,10,13,13,10,12,11,15, + 15,10,11,12,15,15,14,15,15,18,18,13,14,14,18,18, + 9,11,11,15,16,11,13,13,17,17,11,13,13,16,16,15, + 15,16,17, 0,14,15,17, 0, 0, 9,11,11,15,15,10,13, + 12,18,16,11,13,13,15,16,14,16,15,20,20,14,15,16, + 17, 0,13,14,14,20,16,14,15,16,19,18,14,15,15,19, + 0,18,16, 0,20,20,16,18,18, 0, 0,12,14,14,18,18, + 13,15,14,18,16,14,15,16,18,20,16,19,16, 0,17,17, + 18,18,19, 0, 8,10,10,14,14,10,11,11,14,15,10,11, + 12,15,15,13,15,14,19,17,13,15,15,17, 0, 9,11,11, + 16,15,11,13,13,16,16,10,12,13,15,17,14,16,16,18, + 18,14,15,15,18, 0, 9,11,11,15,15,11,13,13,16,17, + 11,13,13,18,17,14,18,16,18,18,15,17,17,18, 0,12, + 14,14,18,18,14,15,15,20, 0,13,14,15,17, 0,16,18, + 17, 0, 0,16,16, 0,17,20,12,14,14,18,18,14,16,15, + 0,18,14,16,15,18, 0,16,19,17, 0, 0,17,18,16, 0, + 0, +}; + +static float _vq_quantthresh__44u5__p3_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u5__p3_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u5__p3_0 = { + _vq_quantthresh__44u5__p3_0, + _vq_quantmap__44u5__p3_0, + 5, + 5 +}; + +static static_codebook _44u5__p3_0 = { + 4, 625, + _vq_lengthlist__44u5__p3_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u5__p3_0, + NULL, + &_vq_auxt__44u5__p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u5__p4_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u5__p4_0[] = { + 4, 5, 5, 8, 8, 6, 7, 6, 9, 9, 6, 6, 7, 9, 9, 8, + 9, 9,11,11, 8, 9, 9,11,11, 6, 7, 7, 9, 9, 7, 8, + 8,10,10, 6, 7, 8, 9,10, 9,10,10,11,12, 9, 9,10, + 11,12, 6, 7, 7, 9, 9, 6, 8, 7,10, 9, 7, 8, 8,10, + 10, 9,10, 9,12,11, 9,10,10,12,11, 8, 9, 9,12,11, + 9,10,10,12,12, 9,10,10,12,12,11,12,12,13,14,11, + 11,12,13,14, 8, 9, 9,11,12, 9,10,10,12,12, 9,10, + 10,12,12,11,12,11,14,13,11,12,12,13,13, 5, 7, 7, + 9, 9, 7, 8, 8,10,10, 7, 8, 8,10,10, 9,10,10,12, + 12, 9,10,10,12,12, 7, 8, 8,10,10, 8, 8, 9,10,11, + 8, 9, 9,11,11,10,10,11,11,13,10,11,11,12,13, 6, + 7, 8,10,10, 7, 9, 8,11,10, 8, 9, 9,11,11,10,11, + 10,13,11,10,11,11,12,12, 9,10,10,12,12,10,10,11, + 12,13,10,11,11,13,13,12,11,13,12,15,12,13,13,14, + 15, 9,10,10,12,12, 9,11,10,13,12,10,11,11,13,13, + 11,13,11,14,12,12,13,13,14,15, 5, 7, 7, 9, 9, 7, + 8, 8,10,10, 7, 8, 8,10,10, 9,10,10,12,12, 9,10, + 10,12,12, 6, 8, 7,10,10, 8, 9, 9,11,11, 7, 8, 9, + 10,11,10,11,11,12,12,10,10,11,11,13, 7, 8, 8,10, + 10, 8, 9, 9,11,11, 8, 9, 8,11,10,10,11,11,13,12, + 10,11,10,13,11, 9,10,10,12,12,10,11,11,13,12, 9, + 10,10,12,13,12,13,13,14,15,11,11,13,12,14, 9,10, + 10,12,12,10,11,11,13,13,10,11,10,13,12,12,13,13, + 14,14,12,13,11,14,12, 8, 9, 9,12,12, 9,10,10,12, + 12, 9,10,10,12,12,12,12,12,14,14,11,12,12,14,13, + 9,10,10,12,12,10,11,11,13,13,10,11,11,13,12,12, + 12,13,14,15,12,13,13,15,14, 9,10,10,12,12,10,11, + 10,13,12,10,11,11,12,13,12,13,12,15,13,12,13,13, + 14,15,11,12,12,14,13,11,12,12,14,15,12,13,13,15, + 14,13,12,14,12,16,13,14,14,15,15,11,11,12,14,14, + 11,12,11,14,13,12,13,13,14,15,13,14,12,16,12,14, + 14,15,16,16, 8, 9, 9,11,12, 9,10,10,12,12, 9,10, + 10,12,13,11,12,12,13,13,12,12,13,14,14, 9,10,10, + 12,12,10,11,10,13,12,10,10,11,12,13,12,13,13,15, + 14,12,12,13,13,15, 9,10,10,12,13,10,11,11,12,13, + 10,11,11,13,13,12,13,13,14,15,12,13,12,15,14,11, + 12,11,14,13,12,13,13,15,14,11,11,12,13,14,14,15, + 14,16,15,13,12,14,13,16,11,12,12,13,14,12,13,13, + 14,15,11,12,11,14,14,14,14,14,15,16,13,15,12,16, + 12, +}; + +static float _vq_quantthresh__44u5__p4_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u5__p4_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u5__p4_0 = { + _vq_quantthresh__44u5__p4_0, + _vq_quantmap__44u5__p4_0, + 5, + 5 +}; + +static static_codebook _44u5__p4_0 = { + 4, 625, + _vq_lengthlist__44u5__p4_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u5__p4_0, + NULL, + &_vq_auxt__44u5__p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u5__p5_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44u5__p5_0[] = { + 2, 3, 3, 6, 6, 8, 8,10,10, 4, 5, 5, 8, 7, 8, 8, + 11,10, 3, 5, 5, 7, 8, 8, 8,10,11, 6, 8, 7,10, 9, + 10,10,11,11, 6, 7, 8, 9, 9, 9,10,11,12, 8, 8, 8, + 10,10,11,11,13,12, 8, 8, 9, 9,10,11,11,12,13,10, + 11,10,12,11,13,12,14,14,10,10,11,11,12,12,13,14, + 14, +}; + +static float _vq_quantthresh__44u5__p5_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44u5__p5_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44u5__p5_0 = { + _vq_quantthresh__44u5__p5_0, + _vq_quantmap__44u5__p5_0, + 9, + 9 +}; + +static static_codebook _44u5__p5_0 = { + 2, 81, + _vq_lengthlist__44u5__p5_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44u5__p5_0, + NULL, + &_vq_auxt__44u5__p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u5__p6_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44u5__p6_0[] = { + 3, 4, 4, 5, 5, 7, 7, 9, 9, 4, 5, 4, 6, 6, 7, 7, + 9, 9, 4, 4, 5, 6, 6, 7, 7, 9, 9, 5, 6, 6, 7, 7, + 8, 8,10,10, 6, 6, 6, 7, 7, 8, 8,10,10, 7, 7, 7, + 8, 8, 9, 9,11,10, 7, 7, 7, 8, 8, 9, 9,10,11, 9, + 9, 9,10,10,11,10,11,11, 9, 9, 9,10,10,11,10,11, + 11, +}; + +static float _vq_quantthresh__44u5__p6_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44u5__p6_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44u5__p6_0 = { + _vq_quantthresh__44u5__p6_0, + _vq_quantmap__44u5__p6_0, + 9, + 9 +}; + +static static_codebook _44u5__p6_0 = { + 2, 81, + _vq_lengthlist__44u5__p6_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44u5__p6_0, + NULL, + &_vq_auxt__44u5__p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u5__p7_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u5__p7_0[] = { + 1, 4, 4, 5, 7, 7, 5, 7, 7, 5, 9, 9, 8,11,10, 7, + 11,10, 5, 9, 9, 7,10,10, 8,10,11, 4, 9, 9, 9,12, + 12, 9,12,12, 8,12,12,11,12,12,10,12,13, 7,12,12, + 11,12,12,10,12,13, 4, 9, 9, 9,12,12, 9,12,12, 7, + 12,11,10,13,13,11,12,12, 7,12,12,10,13,13,11,12, + 12, +}; + +static float _vq_quantthresh__44u5__p7_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__44u5__p7_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u5__p7_0 = { + _vq_quantthresh__44u5__p7_0, + _vq_quantmap__44u5__p7_0, + 3, + 3 +}; + +static static_codebook _44u5__p7_0 = { + 4, 81, + _vq_lengthlist__44u5__p7_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__44u5__p7_0, + NULL, + &_vq_auxt__44u5__p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u5__p7_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44u5__p7_1[] = { + 2, 4, 4, 6, 6, 7, 7, 8, 8, 8, 8, 4, 5, 5, 7, 7, + 8, 8, 9, 8, 8, 9, 4, 5, 5, 7, 7, 8, 8, 9, 9, 8, + 9, 6, 7, 7, 8, 8, 9, 8, 9, 9, 9, 9, 6, 7, 7, 8, + 8, 9, 9, 9, 9, 9, 9, 7, 8, 8, 9, 9, 9, 9, 9, 9, + 9, 9, 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 8, 9, 9, + 9, 9, 9, 9,10,10,10,10, 8, 9, 9, 9, 9, 9, 9,10, + 10,10,10, 8, 9, 9, 9, 9, 9, 9,10,10,10,10, 8, 9, + 9, 9, 9, 9, 9,10,10,10,10, +}; + +static float _vq_quantthresh__44u5__p7_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44u5__p7_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44u5__p7_1 = { + _vq_quantthresh__44u5__p7_1, + _vq_quantmap__44u5__p7_1, + 11, + 11 +}; + +static static_codebook _44u5__p7_1 = { + 2, 121, + _vq_lengthlist__44u5__p7_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44u5__p7_1, + NULL, + &_vq_auxt__44u5__p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u5__p8_0[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44u5__p8_0[] = { + 1, 4, 4, 6, 6, 8, 8, 9, 9,10,10, 4, 6, 6, 7, 7, + 9, 9,10,10,11,11, 4, 6, 6, 7, 7, 9, 9,10,10,11, + 11, 6, 8, 7, 9, 9,10,10,11,11,13,12, 6, 8, 8, 9, + 9,10,10,11,11,12,13, 8, 9, 9,10,10,12,12,13,12, + 14,13, 8, 9, 9,10,10,12,12,13,13,14,14, 9,11,11, + 12,12,13,13,14,14,15,14, 9,11,11,12,12,13,13,14, + 14,15,14,11,12,12,13,13,14,14,15,14,15,14,11,11, + 12,13,13,14,14,14,14,15,15, +}; + +static float _vq_quantthresh__44u5__p8_0[] = { + -49.5, -38.5, -27.5, -16.5, -5.5, 5.5, 16.5, 27.5, + 38.5, 49.5, +}; + +static long _vq_quantmap__44u5__p8_0[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44u5__p8_0 = { + _vq_quantthresh__44u5__p8_0, + _vq_quantmap__44u5__p8_0, + 11, + 11 +}; + +static static_codebook _44u5__p8_0 = { + 2, 121, + _vq_lengthlist__44u5__p8_0, + 1, -524582912, 1618345984, 4, 0, + _vq_quantlist__44u5__p8_0, + NULL, + &_vq_auxt__44u5__p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u5__p8_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44u5__p8_1[] = { + 3, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7, 5, 6, 5, 7, 6, + 7, 7, 8, 8, 8, 8, 5, 5, 5, 6, 6, 7, 7, 8, 8, 8, + 8, 6, 7, 6, 7, 7, 8, 8, 8, 8, 8, 8, 6, 6, 7, 7, + 7, 8, 8, 8, 8, 8, 8, 7, 7, 7, 8, 8, 8, 8, 8, 8, + 8, 8, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 7, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 7, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, +}; + +static float _vq_quantthresh__44u5__p8_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44u5__p8_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44u5__p8_1 = { + _vq_quantthresh__44u5__p8_1, + _vq_quantmap__44u5__p8_1, + 11, + 11 +}; + +static static_codebook _44u5__p8_1 = { + 2, 121, + _vq_lengthlist__44u5__p8_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44u5__p8_1, + NULL, + &_vq_auxt__44u5__p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u5__p9_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44u5__p9_0[] = { + 1, 3, 2,12,10,13,13,13,13,13,13,13,13, 4, 9, 9, + 13,13,13,13,13,13,13,13,13,13, 5,10, 9,13,13,13, + 13,13,13,13,13,13,13,12,13,13,13,13,13,13,13,13, + 13,13,13,13,11,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12, +}; + +static float _vq_quantthresh__44u5__p9_0[] = { + -1402.5, -1147.5, -892.5, -637.5, -382.5, -127.5, 127.5, 382.5, + 637.5, 892.5, 1147.5, 1402.5, +}; + +static long _vq_quantmap__44u5__p9_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44u5__p9_0 = { + _vq_quantthresh__44u5__p9_0, + _vq_quantmap__44u5__p9_0, + 13, + 13 +}; + +static static_codebook _44u5__p9_0 = { + 2, 169, + _vq_lengthlist__44u5__p9_0, + 1, -514332672, 1627381760, 4, 0, + _vq_quantlist__44u5__p9_0, + NULL, + &_vq_auxt__44u5__p9_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u5__p9_1[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__44u5__p9_1[] = { + 1, 4, 4, 7, 7, 8, 8, 8, 7, 8, 7, 9, 8, 9, 9, 4, + 7, 6, 9, 8,10,10, 9, 8, 9, 9, 9, 9, 9, 8, 5, 6, + 6, 8, 9,10,10, 9, 9, 9,10,10,10,10,11, 7, 8, 8, + 10,10,11,11,10,10,11,11,11,12,11,11, 7, 8, 8,10, + 10,11,11,10,10,11,11,12,11,11,11, 8, 9, 9,11,11, + 12,12,11,11,12,11,12,12,12,12, 8, 9,10,11,11,12, + 12,11,11,12,12,12,12,12,12, 8, 9, 9,10,10,12,11, + 12,12,12,12,12,12,12,13, 8, 9, 9,11,11,11,11,12, + 12,12,12,13,12,13,13, 9,10,10,11,11,12,12,12,13, + 12,13,13,13,14,13, 9,10,10,11,11,12,12,12,13,13, + 12,13,13,14,13, 9,11,10,12,11,13,12,12,13,13,13, + 13,13,13,14, 9,10,10,12,12,12,12,12,13,13,13,13, + 13,14,14,10,11,11,12,12,12,13,13,13,14,14,13,14, + 14,14,10,11,11,12,12,12,12,13,12,13,14,13,14,14, + 14, +}; + +static float _vq_quantthresh__44u5__p9_1[] = { + -110.5, -93.5, -76.5, -59.5, -42.5, -25.5, -8.5, 8.5, + 25.5, 42.5, 59.5, 76.5, 93.5, 110.5, +}; + +static long _vq_quantmap__44u5__p9_1[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__44u5__p9_1 = { + _vq_quantthresh__44u5__p9_1, + _vq_quantmap__44u5__p9_1, + 15, + 15 +}; + +static static_codebook _44u5__p9_1 = { + 2, 225, + _vq_lengthlist__44u5__p9_1, + 1, -522338304, 1620115456, 4, 0, + _vq_quantlist__44u5__p9_1, + NULL, + &_vq_auxt__44u5__p9_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u5__p9_2[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44u5__p9_2[] = { + 2, 5, 5, 7, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 5, 6, 6, 7, 7, 8, 8, 9, 8, 9, 9, 9, 9, 9, 9, + 9, 9, 5, 6, 6, 7, 7, 8, 8, 9, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 7, 7, 7, 8, 8, 9, 8, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 7, 7, 7, 8, 8, 9, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, + 9,10, 9,10,10,10, 8, 8, 8, 9, 8, 9, 9, 9, 9, 9, + 9, 9,10, 9,10, 9,10, 8, 9, 9, 9, 9, 9, 9, 9, 9, + 9,10, 9,10,10,10,10,10, 8, 9, 9, 9, 9, 9, 9,10, + 9,10, 9,10,10,10,10,10,10, 9, 9, 9, 9, 9,10, 9, + 10,10,10,10,10,10,10,10,10,10, 9, 9, 9, 9, 9, 9, + 9,10, 9,10, 9,10,10,10,10,10,10, 9, 9, 9, 9, 9, + 10,10,10,10,10,10,10,10,10,10,10,10, 9, 9, 9, 9, + 9, 9,10,10,10,10,10,10,10,10,10,10,10, 9, 9, 9, + 9,10,10, 9,10,10,10,10,10,10,10,10,10,10, 9, 9, + 9, 9, 9,10,10,10,10,10,10,10,10,10,10,10,10, 9, + 9, 9, 9, 9,10,10,10,10,10,10,10,10,10,10,10,10, + 9, 9, 9,10, 9,10,10,10,10,10,10,10,10,10,10,10, + 10, +}; + +static float _vq_quantthresh__44u5__p9_2[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44u5__p9_2[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44u5__p9_2 = { + _vq_quantthresh__44u5__p9_2, + _vq_quantmap__44u5__p9_2, + 17, + 17 +}; + +static static_codebook _44u5__p9_2 = { + 2, 289, + _vq_lengthlist__44u5__p9_2, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44u5__p9_2, + NULL, + &_vq_auxt__44u5__p9_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44u5__short[] = { + 4,10,17,13,17,13,17,17,17,17, 3, 6, 8, 9,11, 9, + 15,12,16,17, 6, 5, 5, 7, 7, 8,10,11,17,17, 7, 8, + 7, 9, 9,10,13,13,17,17, 8, 6, 5, 7, 4, 7, 5, 8, + 14,17, 9, 9, 8, 9, 7, 9, 8,10,16,17,12,10, 7, 8, + 4, 7, 4, 7,16,17,12,11, 9,10, 6, 9, 5, 7,14,17, + 14,13,10,15, 4, 8, 3, 5,14,17,17,14,11,15, 6,10, + 6, 8,15,17, +}; + +static static_codebook _huff_book__44u5__short = { + 2, 100, + _huff_lengthlist__44u5__short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44u6__long[] = { + 3, 9,14,13,14,13,16,12,13,14, 5, 4, 6, 6, 8, 9, + 11,10,12,15,10, 5, 5, 6, 6, 8,10,10,13,16,10, 6, + 6, 6, 6, 8, 9, 9,12,14,13, 7, 6, 6, 4, 6, 6, 7, + 11,14,10, 7, 7, 7, 6, 6, 6, 7,10,13,15,10, 9, 8, + 5, 6, 5, 6,10,14,10, 9, 8, 8, 6, 6, 5, 4, 6,11, + 11,11,12,11,10, 9, 9, 5, 5, 9,10,12,15,13,13,13, + 13, 8, 7, 7, +}; + +static static_codebook _huff_book__44u6__long = { + 2, 100, + _huff_lengthlist__44u6__long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44u6__p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u6__p1_0[] = { + 1, 4, 4, 4, 8, 7, 5, 7, 7, 5, 8, 8, 8,10,10, 7, + 9,10, 5, 8, 8, 7,10, 9, 8,10,10, 5, 8, 8, 8,10, + 10, 8,10,10, 8,10,10,10,12,13,10,13,13, 7,10,10, + 10,13,11,10,13,13, 5, 8, 8, 8,11,10, 8,10,10, 7, + 10,10,10,13,13,10,11,13, 8,10,11,10,13,13,10,13, + 12, +}; + +static float _vq_quantthresh__44u6__p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44u6__p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u6__p1_0 = { + _vq_quantthresh__44u6__p1_0, + _vq_quantmap__44u6__p1_0, + 3, + 3 +}; + +static static_codebook _44u6__p1_0 = { + 4, 81, + _vq_lengthlist__44u6__p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44u6__p1_0, + NULL, + &_vq_auxt__44u6__p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u6__p2_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u6__p2_0[] = { + 3, 4, 4, 5, 6, 6, 5, 6, 6, 5, 6, 6, 6, 8, 8, 6, + 7, 8, 5, 6, 6, 6, 8, 7, 6, 8, 8, 5, 6, 6, 6, 8, + 8, 6, 8, 8, 6, 8, 8, 8, 9, 9, 8, 9, 9, 6, 7, 7, + 7, 9, 8, 8, 9, 9, 5, 6, 6, 6, 8, 7, 6, 8, 8, 6, + 8, 8, 8, 9, 9, 7, 8, 9, 6, 8, 8, 8, 9, 9, 8, 9, + 9, +}; + +static float _vq_quantthresh__44u6__p2_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44u6__p2_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u6__p2_0 = { + _vq_quantthresh__44u6__p2_0, + _vq_quantmap__44u6__p2_0, + 3, + 3 +}; + +static static_codebook _44u6__p2_0 = { + 4, 81, + _vq_lengthlist__44u6__p2_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44u6__p2_0, + NULL, + &_vq_auxt__44u6__p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u6__p3_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u6__p3_0[] = { + 2, 5, 4, 8, 8, 5, 7, 6, 9, 9, 5, 6, 7, 9, 9, 8, + 9, 9,13,12, 8, 9,10,12,13, 5, 7, 7,10, 9, 7, 9, + 9,11,11, 7, 8, 9,11,11,10,11,11,14,14, 9,10,11, + 13,14, 5, 7, 7, 9,10, 6, 9, 8,11,11, 7, 9, 9,11, + 11, 9,11,10,14,13,10,11,11,14,13, 8,10,10,13,13, + 10,11,11,15,15, 9,11,11,14,14,13,14,14,17,16,12, + 13,14,16,16, 8,10,10,13,14, 9,11,11,14,15,10,11, + 12,14,15,12,14,13,16,15,13,14,14,15,17, 5, 7, 7, + 10,10, 7, 9, 9,11,11, 7, 9, 9,11,11,10,12,11,14, + 14,10,11,11,14,14, 7, 9, 9,12,11, 9,11,11,13,13, + 9,11,11,13,13,11,13,13,14,15,11,12,13,15,16, 6, + 9, 9,11,12, 8,11,10,13,12, 9,11,11,13,14,11,13, + 12,16,14,11,13,13,15,16,10,12,11,14,15,11,13,13, + 15,17,11,13,13,17,16,15,15,16,17,16,14,15,16,18, + 0, 9,11,11,14,15,10,12,12,16,15,11,13,13,16,16, + 13,15,14,18,15,14,16,16, 0, 0, 5, 7, 7,10,10, 7, + 9, 9,11,11, 7, 9, 9,11,11,10,11,11,14,14,10,11, + 12,14,14, 6, 9, 9,11,11, 9,11,11,13,13, 8,10,11, + 12,13,11,13,13,16,15,11,12,13,14,16, 7, 9, 9,11, + 12, 9,11,11,13,13, 9,11,11,13,13,11,13,13,16,15, + 11,13,12,15,15, 9,11,11,15,14,11,13,13,17,16,10, + 12,13,15,16,14,16,16, 0,18,14,14,15,15,17,10,11, + 12,15,15,11,13,13,16,16,11,13,13,16,16,14,16,16, + 19,17,14,15,15,17,17, 8,10,10,14,14,10,12,11,15, + 15,10,11,12,16,15,14,15,15,18,20,13,14,16,17,18, + 9,11,11,15,16,11,13,13,17,17,11,13,13,17,16,15, + 16,16, 0, 0,15,16,16, 0, 0, 9,11,11,15,15,10,13, + 12,17,15,11,13,13,17,16,15,17,15,20,19,15,16,16, + 19, 0,13,15,14, 0,17,14,15,16, 0,20,15,16,16, 0, + 19,17,18, 0, 0, 0,16,17,18, 0, 0,12,14,14,19,18, + 13,15,14, 0,17,14,15,16,19,19,16,18,16, 0,19,19, + 20,17,20, 0, 8,10,10,13,14,10,11,11,15,15,10,12, + 12,15,16,14,15,14,19,16,14,15,15, 0,18, 9,11,11, + 16,15,11,13,13, 0,16,11,12,13,16,17,14,16,17, 0, + 19,15,16,16,18, 0, 9,11,11,15,16,11,13,13,16,16, + 11,14,13,18,17,15,16,16,18,20,15,17,19, 0, 0,12, + 14,14,17,17,14,16,15, 0, 0,13,14,15,19, 0,16,18, + 20, 0, 0,16,16,18,18, 0,12,14,14,17,20,14,16,16, + 19, 0,14,16,14, 0,20,16,20,17, 0, 0,17, 0,15, 0, + 19, +}; + +static float _vq_quantthresh__44u6__p3_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u6__p3_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u6__p3_0 = { + _vq_quantthresh__44u6__p3_0, + _vq_quantmap__44u6__p3_0, + 5, + 5 +}; + +static static_codebook _44u6__p3_0 = { + 4, 625, + _vq_lengthlist__44u6__p3_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u6__p3_0, + NULL, + &_vq_auxt__44u6__p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u6__p4_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u6__p4_0[] = { + 4, 5, 5, 8, 8, 6, 7, 6, 9, 9, 6, 6, 7, 9, 9, 8, + 9, 9,11,11, 8, 9, 9,11,11, 6, 7, 7, 9, 9, 7, 8, + 8,10,10, 7, 7, 8, 9,10, 9,10,10,11,11, 9, 9,10, + 11,12, 6, 7, 7, 9, 9, 7, 8, 7,10, 9, 7, 8, 8,10, + 10, 9,10, 9,12,11, 9,10,10,12,11, 8, 9, 9,11,11, + 9,10,10,12,12, 9,10,10,12,12,11,12,12,14,13,11, + 11,12,13,13, 8, 9, 9,11,11, 9,10,10,12,12, 9,10, + 10,12,12,11,12,11,13,12,11,12,12,13,13, 5, 7, 7, + 9, 9, 7, 8, 7,10,10, 7, 7, 8,10,10, 9,10,10,12, + 11, 9,10,10,11,12, 7, 8, 8,10,10, 8, 8, 9,11,11, + 8, 9, 9,11,11,10,10,11,12,13,10,10,11,12,12, 6, + 7, 7,10,10, 7, 9, 8,11,10, 8, 8, 9,10,11,10,11, + 10,13,11,10,11,11,12,12, 9,10,10,12,12,10,10,11, + 13,13,10,11,11,12,13,12,12,12,13,14,12,12,13,14, + 14, 9,10,10,12,12, 9,10,10,13,12,10,11,11,13,13, + 11,12,11,14,12,12,13,13,14,14, 6, 7, 7, 9, 9, 7, + 8, 7,10,10, 7, 8, 8,10,10, 9,10,10,12,11, 9,10, + 10,11,12, 6, 7, 7,10,10, 8, 9, 8,11,10, 7, 8, 9, + 10,11,10,11,11,12,12,10,10,11,11,13, 7, 8, 8,10, + 10, 8, 9, 9,11,11, 8, 9, 8,11,11,10,11,10,13,12, + 10,11,11,13,12, 9,10,10,12,12,10,11,11,13,12, 9, + 10,10,12,13,12,13,12,14,14,11,11,12,12,14, 9,10, + 10,12,12,10,11,11,13,13,10,11,10,13,12,12,12,12, + 14,14,12,13,12,14,13, 8, 9, 9,11,11, 9,10,10,12, + 12, 9,10,10,12,12,11,12,12,14,13,11,12,12,13,14, + 9,10,10,12,12,10,11,11,13,13,10,11,11,13,13,12, + 12,13,14,15,12,12,13,14,14, 9,10,10,12,12, 9,11, + 10,13,12,10,10,11,12,13,12,13,12,14,13,12,12,13, + 14,15,11,12,12,14,13,11,12,12,14,14,12,13,13,14, + 14,13,13,14,14,16,13,14,14,15,15,11,12,11,13,13, + 11,12,11,14,13,12,12,13,14,15,12,14,12,15,12,13, + 14,15,15,16, 8, 9, 9,11,11, 9,10,10,12,12, 9,10, + 10,12,12,11,12,12,14,13,11,12,12,13,13, 9,10,10, + 12,12,10,11,10,13,12, 9,10,11,12,13,12,13,12,14, + 14,12,12,13,13,14, 9,10,10,12,12,10,11,11,13,13, + 10,11,11,13,13,12,13,12,14,14,12,13,13,14,14,11, + 11,11,13,13,12,13,12,14,14,11,11,12,13,14,14,14, + 14,16,15,12,12,14,12,15,11,12,12,13,14,12,13,13, + 14,15,11,12,12,14,14,13,14,14,16,16,13,14,13,16, + 13, +}; + +static float _vq_quantthresh__44u6__p4_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u6__p4_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u6__p4_0 = { + _vq_quantthresh__44u6__p4_0, + _vq_quantmap__44u6__p4_0, + 5, + 5 +}; + +static static_codebook _44u6__p4_0 = { + 4, 625, + _vq_lengthlist__44u6__p4_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u6__p4_0, + NULL, + &_vq_auxt__44u6__p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u6__p5_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44u6__p5_0[] = { + 2, 3, 3, 6, 6, 8, 8,10,10, 4, 5, 5, 8, 7, 8, 8, + 11,11, 3, 5, 5, 7, 8, 8, 8,11,11, 6, 8, 7, 9, 9, + 10, 9,12,11, 6, 7, 8, 9, 9, 9,10,11,12, 8, 8, 8, + 10, 9,12,11,13,13, 8, 8, 9, 9,10,11,12,13,13,10, + 11,11,12,12,13,13,14,14,10,10,11,11,12,13,13,14, + 14, +}; + +static float _vq_quantthresh__44u6__p5_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44u6__p5_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44u6__p5_0 = { + _vq_quantthresh__44u6__p5_0, + _vq_quantmap__44u6__p5_0, + 9, + 9 +}; + +static static_codebook _44u6__p5_0 = { + 2, 81, + _vq_lengthlist__44u6__p5_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44u6__p5_0, + NULL, + &_vq_auxt__44u6__p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u6__p6_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44u6__p6_0[] = { + 3, 4, 4, 5, 5, 7, 7, 9, 9, 4, 5, 4, 6, 6, 7, 7, + 9, 9, 4, 4, 5, 6, 6, 7, 8, 9, 9, 5, 6, 6, 7, 7, + 8, 8,10,10, 5, 6, 6, 7, 7, 8, 8,10,10, 7, 8, 7, + 8, 8,10, 9,11,11, 7, 7, 8, 8, 8, 9,10,10,11, 9, + 9, 9,10,10,11,11,12,11, 9, 9, 9,10,10,11,11,11, + 12, +}; + +static float _vq_quantthresh__44u6__p6_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44u6__p6_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44u6__p6_0 = { + _vq_quantthresh__44u6__p6_0, + _vq_quantmap__44u6__p6_0, + 9, + 9 +}; + +static static_codebook _44u6__p6_0 = { + 2, 81, + _vq_lengthlist__44u6__p6_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44u6__p6_0, + NULL, + &_vq_auxt__44u6__p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u6__p7_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u6__p7_0[] = { + 1, 4, 4, 5, 7, 7, 5, 7, 7, 5, 9, 8, 7,10,10, 8, + 10,10, 5, 8, 9, 7,10,10, 7,10, 9, 4, 8, 8, 9,11, + 11, 8,11,11, 7,11,11,10,10,13,10,13,13, 7,11,11, + 10,13,12,10,13,13, 5, 9, 8, 8,11,11, 9,11,11, 7, + 11,11,10,13,13,10,12,13, 7,11,11,10,13,13, 9,13, + 10, +}; + +static float _vq_quantthresh__44u6__p7_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__44u6__p7_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u6__p7_0 = { + _vq_quantthresh__44u6__p7_0, + _vq_quantmap__44u6__p7_0, + 3, + 3 +}; + +static static_codebook _44u6__p7_0 = { + 4, 81, + _vq_lengthlist__44u6__p7_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__44u6__p7_0, + NULL, + &_vq_auxt__44u6__p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u6__p7_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44u6__p7_1[] = { + 3, 4, 4, 6, 6, 7, 7, 8, 8, 8, 8, 4, 5, 5, 7, 6, + 8, 8, 8, 8, 8, 8, 4, 5, 5, 6, 7, 8, 8, 8, 8, 8, + 8, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 6, 7, 7, 7, + 7, 8, 8, 8, 8, 8, 8, 7, 8, 8, 8, 8, 8, 8, 9, 9, + 9, 9, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 8, 8, 8, + 8, 8, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 9, 9, 9, + 9, 9, 9, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 8, 8, + 8, 8, 8, 9, 9, 9, 9, 9, 9, +}; + +static float _vq_quantthresh__44u6__p7_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44u6__p7_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44u6__p7_1 = { + _vq_quantthresh__44u6__p7_1, + _vq_quantmap__44u6__p7_1, + 11, + 11 +}; + +static static_codebook _44u6__p7_1 = { + 2, 121, + _vq_lengthlist__44u6__p7_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44u6__p7_1, + NULL, + &_vq_auxt__44u6__p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u6__p8_0[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44u6__p8_0[] = { + 1, 4, 4, 6, 6, 8, 8, 9, 9,10,10, 4, 6, 6, 7, 7, + 9, 9,10,10,11,11, 4, 6, 6, 7, 7, 9, 9,10,10,11, + 11, 6, 8, 8, 9, 9,10,10,11,11,12,12, 6, 8, 8, 9, + 9,10,10,11,11,12,12, 8, 9, 9,10,10,11,11,12,12, + 13,13, 8, 9, 9,10,10,11,11,12,12,13,13,10,10,10, + 11,11,13,13,13,13,15,14, 9,10,10,12,11,12,13,13, + 13,14,15,11,12,12,13,13,13,13,15,14,15,15,11,11, + 12,13,13,14,14,14,15,15,15, +}; + +static float _vq_quantthresh__44u6__p8_0[] = { + -49.5, -38.5, -27.5, -16.5, -5.5, 5.5, 16.5, 27.5, + 38.5, 49.5, +}; + +static long _vq_quantmap__44u6__p8_0[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44u6__p8_0 = { + _vq_quantthresh__44u6__p8_0, + _vq_quantmap__44u6__p8_0, + 11, + 11 +}; + +static static_codebook _44u6__p8_0 = { + 2, 121, + _vq_lengthlist__44u6__p8_0, + 1, -524582912, 1618345984, 4, 0, + _vq_quantlist__44u6__p8_0, + NULL, + &_vq_auxt__44u6__p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u6__p8_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44u6__p8_1[] = { + 3, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7, 5, 6, 5, 7, 7, + 7, 7, 8, 7, 8, 8, 5, 5, 6, 6, 7, 7, 7, 7, 7, 8, + 8, 6, 7, 7, 7, 7, 8, 7, 8, 8, 8, 8, 6, 6, 7, 7, + 7, 7, 8, 8, 8, 8, 8, 7, 7, 7, 8, 8, 8, 8, 8, 8, + 8, 8, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 8, 7, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, +}; + +static float _vq_quantthresh__44u6__p8_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44u6__p8_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44u6__p8_1 = { + _vq_quantthresh__44u6__p8_1, + _vq_quantmap__44u6__p8_1, + 11, + 11 +}; + +static static_codebook _44u6__p8_1 = { + 2, 121, + _vq_lengthlist__44u6__p8_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44u6__p8_1, + NULL, + &_vq_auxt__44u6__p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u6__p9_0[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__44u6__p9_0[] = { + 1, 3, 2, 9, 8,15,15,15,15,15,15,15,15,15,15, 4, + 8, 9,13,14,14,14,14,14,14,14,14,14,14,14, 5, 8, + 9,14,14,14,14,14,14,14,14,14,14,14,14,11,14,14, + 14,14,14,14,14,14,14,14,14,14,14,14,11,14,14,14, + 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, + 14, +}; + +static float _vq_quantthresh__44u6__p9_0[] = { + -1657.5, -1402.5, -1147.5, -892.5, -637.5, -382.5, -127.5, 127.5, + 382.5, 637.5, 892.5, 1147.5, 1402.5, 1657.5, +}; + +static long _vq_quantmap__44u6__p9_0[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__44u6__p9_0 = { + _vq_quantthresh__44u6__p9_0, + _vq_quantmap__44u6__p9_0, + 15, + 15 +}; + +static static_codebook _44u6__p9_0 = { + 2, 225, + _vq_lengthlist__44u6__p9_0, + 1, -514071552, 1627381760, 4, 0, + _vq_quantlist__44u6__p9_0, + NULL, + &_vq_auxt__44u6__p9_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u6__p9_1[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__44u6__p9_1[] = { + 1, 4, 4, 7, 7, 8, 9, 8, 8, 9, 8, 9, 8, 9, 9, 4, + 7, 6, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 4, 7, + 6, 9, 9,10,10, 9, 9,10,10,10,10,11,11, 7, 9, 8, + 10,10,11,11,10,10,11,11,11,11,11,11, 7, 8, 9,10, + 10,11,11,10,10,11,11,11,11,11,12, 8,10,10,11,11, + 12,12,11,11,12,12,12,12,13,12, 8,10,10,11,11,12, + 11,11,11,11,12,12,12,12,13, 8, 9, 9,11,10,11,11, + 12,12,12,12,13,12,13,12, 8, 9, 9,11,11,11,11,12, + 12,12,12,12,13,13,13, 9,10,10,11,12,12,12,12,12, + 13,13,13,13,13,13, 9,10,10,11,11,12,12,12,12,13, + 13,13,13,14,13,10,10,10,12,11,12,12,13,13,13,13, + 13,13,13,13,10,10,11,11,11,12,12,13,13,13,13,13, + 13,13,13,10,11,11,12,12,13,12,12,13,13,13,13,13, + 13,14,10,11,11,12,12,13,12,13,13,13,14,13,13,14, + 13, +}; + +static float _vq_quantthresh__44u6__p9_1[] = { + -110.5, -93.5, -76.5, -59.5, -42.5, -25.5, -8.5, 8.5, + 25.5, 42.5, 59.5, 76.5, 93.5, 110.5, +}; + +static long _vq_quantmap__44u6__p9_1[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__44u6__p9_1 = { + _vq_quantthresh__44u6__p9_1, + _vq_quantmap__44u6__p9_1, + 15, + 15 +}; + +static static_codebook _44u6__p9_1 = { + 2, 225, + _vq_lengthlist__44u6__p9_1, + 1, -522338304, 1620115456, 4, 0, + _vq_quantlist__44u6__p9_1, + NULL, + &_vq_auxt__44u6__p9_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u6__p9_2[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44u6__p9_2[] = { + 3, 5, 5, 7, 7, 8, 8, 8, 8, 8, 8, 9, 8, 8, 9, 9, + 9, 5, 6, 6, 7, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, + 9, 9, 5, 6, 6, 7, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, + 9, 9, 9, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 8, 8, 8, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9,10, 9, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9,10,10, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9,10, 9, 9, 9,10, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9,10, 9, 9, 9,10, 9, 9,10, 9, + 9, 9, 9, 9, 9, 9, 9, 9,10,10,10, 9,10, 9,10,10, + 9, 9, 9, 9, 9, 9, 9, 9, 9,10,10, 9,10,10, 9, 9, + 10, +}; + +static float _vq_quantthresh__44u6__p9_2[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44u6__p9_2[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44u6__p9_2 = { + _vq_quantthresh__44u6__p9_2, + _vq_quantmap__44u6__p9_2, + 17, + 17 +}; + +static static_codebook _44u6__p9_2 = { + 2, 289, + _vq_lengthlist__44u6__p9_2, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44u6__p9_2, + NULL, + &_vq_auxt__44u6__p9_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44u6__short[] = { + 4,11,16,13,17,13,17,16,17,17, 4, 7, 9, 9,13,10, + 16,12,16,17, 7, 6, 5, 7, 8, 9,12,12,16,17, 6, 9, + 7, 9,10,10,15,15,17,17, 6, 7, 5, 7, 5, 7, 7,10, + 16,17, 7, 9, 8, 9, 8,10,11,11,15,17, 7, 7, 7, 8, + 5, 8, 8, 9,15,17, 8, 7, 9, 9, 7, 8, 7, 2, 7,15, + 14,13,13,15, 5,10, 4, 3, 6,17,17,15,13,17, 7,11, + 7, 6, 9,16, +}; + +static static_codebook _huff_book__44u6__short = { + 2, 100, + _huff_lengthlist__44u6__short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44u7__long[] = { + 3, 9,14,13,15,14,16,13,13,14, 5, 5, 7, 7, 8, 9, + 11,10,12,15,10, 6, 5, 6, 6, 9,10,10,13,16,10, 6, + 6, 6, 6, 8, 9, 9,12,15,14, 7, 6, 6, 5, 6, 6, 8, + 12,15,10, 8, 7, 7, 6, 7, 7, 7,11,13,14,10, 9, 8, + 5, 6, 4, 5, 9,12,10, 9, 9, 8, 6, 6, 5, 3, 6,11, + 12,11,12,12,10, 9, 8, 5, 5, 8,10,11,15,13,13,13, + 12, 8, 6, 7, +}; + +static static_codebook _huff_book__44u7__long = { + 2, 100, + _huff_lengthlist__44u7__long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44u7__p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u7__p1_0[] = { + 1, 4, 4, 4, 7, 7, 5, 7, 7, 5, 8, 8, 8,10,10, 7, + 10,10, 5, 8, 8, 7,10,10, 8,10,10, 5, 8, 8, 8,11, + 10, 8,10,10, 8,10,10,10,12,13,10,13,13, 7,10,10, + 10,13,12,10,13,13, 5, 8, 8, 8,11,10, 8,10,11, 7, + 10,10,10,13,13,10,12,13, 8,11,11,10,13,13,10,13, + 12, +}; + +static float _vq_quantthresh__44u7__p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44u7__p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u7__p1_0 = { + _vq_quantthresh__44u7__p1_0, + _vq_quantmap__44u7__p1_0, + 3, + 3 +}; + +static static_codebook _44u7__p1_0 = { + 4, 81, + _vq_lengthlist__44u7__p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44u7__p1_0, + NULL, + &_vq_auxt__44u7__p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u7__p2_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u7__p2_0[] = { + 3, 4, 4, 5, 6, 6, 5, 6, 6, 5, 6, 6, 6, 8, 8, 6, + 7, 8, 5, 6, 6, 6, 8, 7, 6, 8, 8, 5, 6, 6, 6, 8, + 7, 6, 8, 8, 6, 8, 8, 8, 9, 9, 8, 9, 9, 6, 8, 7, + 7, 9, 8, 8, 9, 9, 5, 6, 6, 6, 8, 7, 6, 8, 8, 6, + 8, 8, 8, 9, 9, 7, 8, 9, 6, 8, 8, 8, 9, 9, 8, 9, + 9, +}; + +static float _vq_quantthresh__44u7__p2_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44u7__p2_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u7__p2_0 = { + _vq_quantthresh__44u7__p2_0, + _vq_quantmap__44u7__p2_0, + 3, + 3 +}; + +static static_codebook _44u7__p2_0 = { + 4, 81, + _vq_lengthlist__44u7__p2_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44u7__p2_0, + NULL, + &_vq_auxt__44u7__p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u7__p3_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u7__p3_0[] = { + 2, 5, 4, 8, 8, 5, 7, 6, 9, 9, 5, 6, 7, 9, 9, 8, + 9, 9,13,12, 8, 9,10,12,13, 5, 7, 7,10, 9, 7, 9, + 9,11,11, 6, 8, 9,11,11,10,11,11,14,14, 9,10,11, + 13,14, 5, 7, 7, 9, 9, 7, 9, 8,11,11, 7, 9, 9,11, + 11, 9,11,10,14,13,10,11,11,14,14, 8,10,10,14,13, + 10,11,12,15,14, 9,11,11,15,14,13,14,14,16,16,12, + 13,14,17,16, 8,10,10,13,13, 9,11,11,14,15,10,11, + 12,14,15,12,14,13,16,16,13,14,15,15,17, 5, 7, 7, + 10,10, 7, 9, 9,11,11, 7, 9, 9,11,11,10,12,11,15, + 14,10,11,12,14,14, 7, 9, 9,12,12, 9,11,11,13,13, + 9,11,11,13,13,11,13,13,14,17,11,13,13,15,16, 6, + 9, 9,11,11, 8,11,10,13,12, 9,11,11,13,13,11,13, + 12,16,14,11,13,13,16,16,10,12,12,15,15,11,13,13, + 16,16,11,13,13,16,15,14,16,17,17,19,14,16,16,18, + 0, 9,11,11,14,15,10,13,12,16,15,11,13,13,16,16, + 14,15,14, 0,16,14,16,16,18, 0, 5, 7, 7,10,10, 7, + 9, 9,12,11, 7, 9, 9,11,12,10,11,11,15,14,10,11, + 12,14,14, 6, 9, 9,11,11, 9,11,11,13,13, 8,10,11, + 12,13,11,13,13,17,15,11,12,13,14,15, 7, 9, 9,11, + 12, 9,11,11,13,13, 9,11,11,13,13,11,13,12,16,16, + 11,13,13,15,14, 9,11,11,14,15,11,13,13,16,15,10, + 12,13,16,16,15,16,16, 0, 0,14,13,15,16,18,10,11, + 11,15,15,11,13,14,16,18,11,13,13,16,15,15,16,16, + 19, 0,14,15,15,16,16, 8,10,10,13,13,10,12,11,16, + 15,10,11,11,16,15,13,15,16,18, 0,13,14,15,17,17, + 9,11,11,15,15,11,13,13,16,18,11,13,13,16,17,15, + 16,16, 0, 0,15,18,16, 0,17, 9,11,11,15,15,11,13, + 12,17,15,11,13,14,16,17,15,18,15, 0,17,15,16,16, + 18,19,13,15,14, 0,18,14,16,16,19,18,14,16,15,19, + 19,16,18,19, 0, 0,16,17, 0, 0, 0,12,14,14,17,17, + 13,16,14, 0,18,14,16,15,18, 0,16,18,16,19,17,18, + 19,17, 0, 0, 8,10,10,14,14, 9,12,11,15,15,10,11, + 12,15,17,13,15,15,18,16,14,16,15,18,17, 9,11,11, + 16,15,11,13,13, 0,16,11,12,13,16,15,15,16,16, 0, + 17,15,15,16,18,17, 9,12,11,15,17,11,13,13,16,16, + 11,14,13,16,16,15,15,16,18,19,16,18,16, 0, 0,12, + 14,14, 0,16,14,16,16, 0,18,13,14,15,16, 0,17,16, + 18, 0, 0,16,16,17,19, 0,13,14,14,17, 0,14,17,16, + 0,19,14,15,15,18,19,17,16,18, 0, 0,15,19,16, 0, + 0, +}; + +static float _vq_quantthresh__44u7__p3_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u7__p3_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u7__p3_0 = { + _vq_quantthresh__44u7__p3_0, + _vq_quantmap__44u7__p3_0, + 5, + 5 +}; + +static static_codebook _44u7__p3_0 = { + 4, 625, + _vq_lengthlist__44u7__p3_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u7__p3_0, + NULL, + &_vq_auxt__44u7__p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u7__p4_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u7__p4_0[] = { + 4, 5, 5, 8, 8, 6, 7, 6, 9, 9, 6, 6, 7, 9, 9, 8, + 9, 9,11,11, 8, 9, 9,10,11, 6, 7, 7, 9, 9, 7, 8, + 8,10,10, 6, 7, 8, 9,10, 9,10,10,12,12, 9, 9,10, + 11,12, 6, 7, 7, 9, 9, 6, 8, 7,10, 9, 7, 8, 8,10, + 10, 9,10, 9,12,11, 9,10,10,12,11, 8, 9, 9,11,11, + 9,10,10,12,12, 9,10,10,12,12,11,12,12,13,14,11, + 11,12,13,13, 8, 9, 9,11,11, 9,10,10,12,11, 9,10, + 10,12,12,11,12,11,13,13,11,12,12,13,13, 6, 7, 7, + 9, 9, 7, 8, 7,10,10, 7, 7, 8,10,10, 9,10,10,12, + 11, 9,10,10,12,12, 7, 8, 8,10,10, 8, 8, 9,11,11, + 8, 9, 9,11,11,10,11,11,12,12,10,10,11,12,13, 6, + 7, 7,10,10, 7, 9, 8,11,10, 8, 8, 9,10,11,10,11, + 10,13,11,10,11,11,12,12, 9,10,10,12,12,10,10,11, + 13,13,10,11,11,13,12,12,12,13,13,14,12,12,13,14, + 14, 9,10,10,12,12, 9,10,10,12,12,10,11,11,13,13, + 11,12,11,14,12,12,13,13,14,14, 6, 7, 7, 9, 9, 7, + 8, 7,10,10, 7, 7, 8,10,10, 9,10,10,12,11, 9,10, + 10,11,12, 6, 7, 7,10,10, 8, 9, 8,11,10, 7, 8, 9, + 10,11,10,11,11,13,12,10,10,11,11,13, 7, 8, 8,10, + 10, 8, 9, 9,11,11, 8, 9, 9,11,11,10,11,10,13,12, + 10,11,11,12,12, 9,10,10,12,12,10,11,11,13,12, 9, + 10,10,12,13,12,13,12,14,14,11,11,12,12,14, 9,10, + 10,12,12,10,11,11,13,13,10,11,11,13,13,12,13,12, + 14,14,12,13,12,14,13, 8, 9, 9,11,11, 9,10,10,12, + 12, 9,10,10,12,12,11,12,12,14,13,11,12,12,13,13, + 9,10,10,12,12,10,11,11,13,13,10,11,11,13,12,12, + 13,13,14,14,12,12,13,14,14, 9,10,10,12,12, 9,11, + 10,13,12,10,10,11,12,13,11,13,12,14,13,12,12,13, + 14,14,11,12,12,13,13,11,12,13,14,14,12,13,13,14, + 14,13,13,14,14,16,13,14,14,16,16,11,11,11,13,13, + 11,12,11,14,13,12,12,13,14,15,13,14,12,16,13,14, + 14,14,15,16, 8, 9, 9,11,11, 9,10,10,12,12, 9,10, + 10,12,12,11,12,12,14,13,11,12,12,13,14, 9,10,10, + 12,12,10,11,10,13,12, 9,10,11,12,13,12,13,12,14, + 14,12,12,13,13,14, 9,10,10,12,12,10,11,11,12,13, + 10,11,11,13,13,12,13,12,14,14,12,13,13,14,14,11, + 12,12,13,13,12,13,12,14,14,11,11,12,13,14,13,15, + 14,16,15,13,12,14,13,16,11,12,12,13,13,12,13,13, + 14,14,12,12,12,14,14,13,14,14,15,15,13,14,13,16, + 14, +}; + +static float _vq_quantthresh__44u7__p4_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u7__p4_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u7__p4_0 = { + _vq_quantthresh__44u7__p4_0, + _vq_quantmap__44u7__p4_0, + 5, + 5 +}; + +static static_codebook _44u7__p4_0 = { + 4, 625, + _vq_lengthlist__44u7__p4_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u7__p4_0, + NULL, + &_vq_auxt__44u7__p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u7__p5_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44u7__p5_0[] = { + 2, 3, 3, 6, 6, 7, 8,10,10, 4, 5, 5, 8, 7, 8, 8, + 11,11, 3, 5, 5, 7, 7, 8, 9,11,11, 6, 8, 7, 9, 9, + 10,10,12,12, 6, 7, 8, 9,10,10,10,12,12, 8, 8, 8, + 10,10,12,11,13,13, 8, 8, 9,10,10,11,11,13,13,10, + 11,11,12,12,13,13,14,14,10,11,11,12,12,13,13,14, + 14, +}; + +static float _vq_quantthresh__44u7__p5_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44u7__p5_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44u7__p5_0 = { + _vq_quantthresh__44u7__p5_0, + _vq_quantmap__44u7__p5_0, + 9, + 9 +}; + +static static_codebook _44u7__p5_0 = { + 2, 81, + _vq_lengthlist__44u7__p5_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44u7__p5_0, + NULL, + &_vq_auxt__44u7__p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u7__p6_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44u7__p6_0[] = { + 3, 4, 4, 5, 5, 7, 7, 9, 9, 4, 5, 4, 6, 6, 8, 7, + 9, 9, 4, 4, 5, 6, 6, 7, 7, 9, 9, 5, 6, 6, 7, 7, + 8, 8,10,10, 5, 6, 6, 7, 7, 8, 8,10,10, 7, 8, 7, + 8, 8,10, 9,11,11, 7, 7, 8, 8, 8, 9,10,11,11, 9, + 9, 9,10,10,11,10,12,11, 9, 9, 9,10,10,11,11,11, + 12, +}; + +static float _vq_quantthresh__44u7__p6_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44u7__p6_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44u7__p6_0 = { + _vq_quantthresh__44u7__p6_0, + _vq_quantmap__44u7__p6_0, + 9, + 9 +}; + +static static_codebook _44u7__p6_0 = { + 2, 81, + _vq_lengthlist__44u7__p6_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44u7__p6_0, + NULL, + &_vq_auxt__44u7__p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u7__p7_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u7__p7_0[] = { + 1, 4, 4, 5, 7, 7, 5, 7, 7, 5, 9, 8, 8, 9, 9, 7, + 10,10, 5, 8, 9, 7, 9,10, 8, 9, 9, 4, 9, 9, 9,11, + 10, 8,10,10, 7,11,10,10,10,12,10,12,12, 7,10,10, + 10,12,11,10,12,12, 5, 9, 9, 8,10,10, 9,11,11, 7, + 11,10,10,12,12,10,11,12, 7,10,11,10,12,12,10,12, + 10, +}; + +static float _vq_quantthresh__44u7__p7_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__44u7__p7_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u7__p7_0 = { + _vq_quantthresh__44u7__p7_0, + _vq_quantmap__44u7__p7_0, + 3, + 3 +}; + +static static_codebook _44u7__p7_0 = { + 4, 81, + _vq_lengthlist__44u7__p7_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__44u7__p7_0, + NULL, + &_vq_auxt__44u7__p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u7__p7_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44u7__p7_1[] = { + 3, 4, 4, 6, 6, 7, 7, 8, 8, 8, 8, 4, 5, 5, 6, 6, + 8, 7, 8, 8, 8, 8, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8, + 8, 6, 7, 6, 7, 7, 8, 8, 9, 9, 9, 9, 6, 6, 7, 7, + 7, 8, 8, 9, 9, 9, 9, 7, 8, 7, 8, 8, 9, 9, 9, 9, + 9, 9, 7, 7, 8, 8, 8, 9, 9, 9, 9, 9, 9, 8, 8, 8, + 9, 9, 9, 9,10, 9, 9, 9, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 9,10, 8, 8, 8, 9, 9, 9, 9,10, 9,10,10, 8, 8, + 8, 9, 9, 9, 9, 9,10,10,10, +}; + +static float _vq_quantthresh__44u7__p7_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44u7__p7_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44u7__p7_1 = { + _vq_quantthresh__44u7__p7_1, + _vq_quantmap__44u7__p7_1, + 11, + 11 +}; + +static static_codebook _44u7__p7_1 = { + 2, 121, + _vq_lengthlist__44u7__p7_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44u7__p7_1, + NULL, + &_vq_auxt__44u7__p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u7__p8_0[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44u7__p8_0[] = { + 1, 4, 4, 6, 6, 8, 8,10,10,11,11, 4, 6, 6, 7, 7, + 9, 9,11,10,12,12, 5, 6, 5, 7, 7, 9, 9,10,11,12, + 12, 6, 7, 7, 8, 8,10,10,11,11,13,13, 6, 7, 7, 8, + 8,10,10,11,12,13,13, 8, 9, 9,10,10,11,11,12,12, + 14,14, 8, 9, 9,10,10,11,11,12,12,14,14,10,10,10, + 11,11,13,12,14,14,15,15,10,10,10,12,12,13,13,14, + 14,15,15,11,12,12,13,13,14,14,15,14,16,15,11,12, + 12,13,13,14,14,15,15,15,16, +}; + +static float _vq_quantthresh__44u7__p8_0[] = { + -49.5, -38.5, -27.5, -16.5, -5.5, 5.5, 16.5, 27.5, + 38.5, 49.5, +}; + +static long _vq_quantmap__44u7__p8_0[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44u7__p8_0 = { + _vq_quantthresh__44u7__p8_0, + _vq_quantmap__44u7__p8_0, + 11, + 11 +}; + +static static_codebook _44u7__p8_0 = { + 2, 121, + _vq_lengthlist__44u7__p8_0, + 1, -524582912, 1618345984, 4, 0, + _vq_quantlist__44u7__p8_0, + NULL, + &_vq_auxt__44u7__p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u7__p8_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44u7__p8_1[] = { + 4, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7, 5, 6, 6, 7, 7, + 7, 7, 7, 7, 7, 7, 5, 6, 6, 6, 7, 7, 7, 7, 7, 7, + 7, 6, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 6, 7, 7, 7, + 7, 7, 7, 7, 7, 8, 8, 7, 7, 7, 7, 7, 8, 7, 8, 8, + 8, 8, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 7, 7, 7, + 7, 7, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 8, 8, 8, + 8, 8, 8, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, + 7, 8, 8, 8, 8, 8, 8, 8, 8, +}; + +static float _vq_quantthresh__44u7__p8_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44u7__p8_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44u7__p8_1 = { + _vq_quantthresh__44u7__p8_1, + _vq_quantmap__44u7__p8_1, + 11, + 11 +}; + +static static_codebook _44u7__p8_1 = { + 2, 121, + _vq_lengthlist__44u7__p8_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44u7__p8_1, + NULL, + &_vq_auxt__44u7__p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u7__p9_0[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44u7__p9_0[] = { + 1, 3, 3,10,10,10,10,10,10,10,10, 4,10,10,10,10, + 10,10,10,10,10,10, 4,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, +}; + +static float _vq_quantthresh__44u7__p9_0[] = { + -2866.5, -2229.5, -1592.5, -955.5, -318.5, 318.5, 955.5, 1592.5, + 2229.5, 2866.5, +}; + +static long _vq_quantmap__44u7__p9_0[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44u7__p9_0 = { + _vq_quantthresh__44u7__p9_0, + _vq_quantmap__44u7__p9_0, + 11, + 11 +}; + +static static_codebook _44u7__p9_0 = { + 2, 121, + _vq_lengthlist__44u7__p9_0, + 1, -512171520, 1630791680, 4, 0, + _vq_quantlist__44u7__p9_0, + NULL, + &_vq_auxt__44u7__p9_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u7__p9_1[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44u7__p9_1[] = { + 1, 4, 4, 6, 5, 8, 6, 9, 8,10, 9,11,10, 4, 6, 6, + 8, 8, 9, 9,11,10,11,11,11,11, 4, 6, 6, 8, 8,10, + 9,11,11,11,11,11,12, 6, 8, 8,10,10,11,11,12,12, + 13,12,13,13, 6, 8, 8,10,10,11,11,12,12,12,13,14, + 13, 8,10,10,11,11,12,13,14,14,14,14,15,15, 8,10, + 10,11,12,12,13,13,14,14,14,14,15, 9,11,11,13,13, + 14,14,15,14,16,15,17,15, 9,11,11,12,13,14,14,15, + 14,15,15,15,16,10,12,12,13,14,15,15,15,15,16,17, + 16,17,10,13,12,13,14,14,16,16,16,16,15,16,17,11, + 13,13,14,15,14,17,15,16,17,17,17,17,11,13,13,14, + 15,15,15,15,17,17,16,17,16, +}; + +static float _vq_quantthresh__44u7__p9_1[] = { + -269.5, -220.5, -171.5, -122.5, -73.5, -24.5, 24.5, 73.5, + 122.5, 171.5, 220.5, 269.5, +}; + +static long _vq_quantmap__44u7__p9_1[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44u7__p9_1 = { + _vq_quantthresh__44u7__p9_1, + _vq_quantmap__44u7__p9_1, + 13, + 13 +}; + +static static_codebook _44u7__p9_1 = { + 2, 169, + _vq_lengthlist__44u7__p9_1, + 1, -518889472, 1622704128, 4, 0, + _vq_quantlist__44u7__p9_1, + NULL, + &_vq_auxt__44u7__p9_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u7__p9_2[] = { + 24, + 23, + 25, + 22, + 26, + 21, + 27, + 20, + 28, + 19, + 29, + 18, + 30, + 17, + 31, + 16, + 32, + 15, + 33, + 14, + 34, + 13, + 35, + 12, + 36, + 11, + 37, + 10, + 38, + 9, + 39, + 8, + 40, + 7, + 41, + 6, + 42, + 5, + 43, + 4, + 44, + 3, + 45, + 2, + 46, + 1, + 47, + 0, + 48, +}; + +static long _vq_lengthlist__44u7__p9_2[] = { + 2, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, + 8, +}; + +static float _vq_quantthresh__44u7__p9_2[] = { + -23.5, -22.5, -21.5, -20.5, -19.5, -18.5, -17.5, -16.5, + -15.5, -14.5, -13.5, -12.5, -11.5, -10.5, -9.5, -8.5, + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, + 8.5, 9.5, 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, + 16.5, 17.5, 18.5, 19.5, 20.5, 21.5, 22.5, 23.5, +}; + +static long _vq_quantmap__44u7__p9_2[] = { + 47, 45, 43, 41, 39, 37, 35, 33, + 31, 29, 27, 25, 23, 21, 19, 17, + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, + 48, +}; + +static encode_aux_threshmatch _vq_auxt__44u7__p9_2 = { + _vq_quantthresh__44u7__p9_2, + _vq_quantmap__44u7__p9_2, + 49, + 49 +}; + +static static_codebook _44u7__p9_2 = { + 1, 49, + _vq_lengthlist__44u7__p9_2, + 1, -526909440, 1611661312, 6, 0, + _vq_quantlist__44u7__p9_2, + NULL, + &_vq_auxt__44u7__p9_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44u7__short[] = { + 5,12,17,16,16,17,17,17,17,17, 4, 7,11,11,12, 9, + 17,10,17,17, 7, 7, 8, 9, 7, 9,11,10,15,17, 7, 9, + 10,11,10,12,14,12,16,17, 7, 8, 5, 7, 4, 7, 7, 8, + 16,16, 6,10, 9,10, 7,10,11,11,16,17, 6, 8, 8, 9, + 5, 7, 5, 8,16,17, 5, 5, 8, 7, 6, 7, 7, 6, 6,14, + 12,10,12,11, 7,11, 4, 4, 2, 7,17,15,15,15, 8,15, + 6, 8, 5, 9, +}; + +static static_codebook _huff_book__44u7__short = { + 2, 100, + _huff_lengthlist__44u7__short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44u8__long[] = { + 3, 9,13,14,14,15,14,14,15,15, 5, 4, 6, 8,10,12, + 12,14,15,15, 9, 5, 4, 5, 8,10,11,13,16,16,10, 7, + 4, 3, 5, 7, 9,11,13,13,10, 9, 7, 4, 4, 6, 8,10, + 12,14,13,11, 9, 6, 5, 5, 6, 8,12,14,13,11,10, 8, + 7, 6, 6, 7,10,14,13,11,12,10, 8, 7, 6, 6, 9,13, + 12,11,14,12,11, 9, 8, 7, 9,11,11,12,14,13,14,11, + 10, 8, 8, 9, +}; + +static static_codebook _huff_book__44u8__long = { + 2, 100, + _huff_lengthlist__44u8__long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44u8__short[] = { + 6,14,18,18,17,17,17,17,17,17, 4, 7, 9, 9,10,13, + 15,17,17,17, 6, 7, 5, 6, 8,11,16,17,16,17, 5, 7, + 5, 4, 6,10,14,17,17,17, 6, 6, 6, 5, 7,10,13,16, + 17,17, 7, 6, 7, 7, 7, 8, 7,10,15,16,12, 9, 9, 6, + 6, 5, 3, 5,11,15,14,14,13, 5, 5, 7, 3, 4, 8,15, + 17,17,13, 7, 7,10, 6, 6,10,15,17,17,16,10,11,14, + 10,10,15,17, +}; + +static static_codebook _huff_book__44u8__short = { + 2, 100, + _huff_lengthlist__44u8__short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44u8_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u8_p1_0[] = { + 1, 5, 5, 5, 7, 7, 5, 7, 7, 5, 7, 7, 8, 9, 9, 7, + 9, 9, 5, 7, 7, 7, 9, 9, 8, 9, 9, 5, 7, 7, 7, 9, + 9, 7, 9, 9, 7, 9, 9, 9,10,11, 9,11,10, 7, 9, 9, + 9,11,10, 9,10,11, 5, 7, 7, 7, 9, 9, 7, 9, 9, 7, + 9, 9, 9,11,10, 9,10,10, 8, 9, 9, 9,11,11, 9,11, + 10, +}; + +static float _vq_quantthresh__44u8_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44u8_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u8_p1_0 = { + _vq_quantthresh__44u8_p1_0, + _vq_quantmap__44u8_p1_0, + 3, + 3 +}; + +static static_codebook _44u8_p1_0 = { + 4, 81, + _vq_lengthlist__44u8_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44u8_p1_0, + NULL, + &_vq_auxt__44u8_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u8_p2_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u8_p2_0[] = { + 4, 5, 5, 8, 8, 5, 7, 6, 9, 9, 5, 6, 7, 9, 9, 8, + 9, 9,11,11, 8, 9, 9,11,11, 5, 7, 7, 9, 9, 7, 8, + 8,10,10, 7, 8, 8,10,10, 9,10,10,12,12, 9,10,10, + 11,12, 5, 7, 7, 9, 9, 7, 8, 7,10,10, 7, 8, 8,10, + 10, 9,10, 9,12,11, 9,10,10,12,12, 8, 9, 9,12,11, + 9,10,10,12,12, 9,10,10,12,12,11,12,12,14,14,11, + 11,12,13,14, 8, 9, 9,11,11, 9,10,10,12,12, 9,10, + 10,12,12,11,12,11,13,13,11,12,12,14,14, 5, 7, 7, + 9, 9, 7, 8, 8,10,10, 7, 8, 8,10,10, 9,10,10,12, + 12, 9,10,10,11,12, 7, 8, 8,10,10, 8, 9, 9,11,11, + 8, 9, 9,11,11,10,11,11,12,13,10,11,11,12,13, 6, + 8, 8,10,10, 8, 9, 8,11,10, 8, 9, 9,11,11,10,11, + 10,13,12,10,11,11,13,13, 9,10,10,12,12,10,11,11, + 13,13,10,11,11,13,13,12,12,13,13,14,12,13,13,14, + 14, 9,10,10,12,12,10,11,10,13,12,10,11,11,13,13, + 11,13,12,14,13,12,13,13,14,14, 5, 7, 7, 9, 9, 7, + 8, 8,10,10, 7, 8, 8,10,10, 9,10,10,12,12, 9,10, + 10,12,12, 7, 8, 8,10,10, 8, 9, 9,11,11, 8, 8, 9, + 10,11,10,11,11,13,13,10,10,11,12,13, 7, 8, 8,10, + 10, 8, 9, 9,11,11, 8, 9, 9,11,11,10,11,11,13,13, + 10,11,11,13,12, 9,10,10,12,12,10,11,11,13,13,10, + 10,11,12,13,12,13,13,14,14,12,12,13,13,14, 9,10, + 10,12,12,10,11,11,13,13,10,11,11,13,13,12,13,13, + 15,14,12,13,13,14,13, 8, 9, 9,11,11, 9,10,10,12, + 12, 9,10,10,12,12,12,12,12,14,13,11,12,12,14,14, + 9,10,10,12,12,10,11,11,13,13,10,11,11,13,13,12, + 13,13,14,15,12,13,13,14,15, 9,10,10,12,12,10,11, + 10,13,12,10,11,11,13,13,12,13,12,15,14,12,13,13, + 14,15,11,12,12,14,14,12,13,13,14,14,12,13,13,15, + 14,14,14,14,14,16,14,14,15,16,16,11,12,12,14,14, + 11,12,12,14,14,12,13,13,14,15,13,14,13,16,14,14, + 14,14,16,16, 8, 9, 9,11,11, 9,10,10,12,12, 9,10, + 10,12,12,11,12,12,14,13,11,12,12,14,14, 9,10,10, + 12,12,10,11,11,13,13,10,10,11,12,13,12,13,13,15, + 14,12,12,13,13,14, 9,10,10,12,12,10,11,11,13,13, + 10,11,11,13,13,12,13,13,14,14,12,13,13,15,14,11, + 12,12,14,13,12,13,13,15,14,11,12,12,13,14,14,15, + 14,16,15,13,13,14,13,16,11,12,12,14,14,12,13,13, + 14,15,12,13,12,15,14,14,14,14,16,15,14,15,13,16, + 14, +}; + +static float _vq_quantthresh__44u8_p2_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u8_p2_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u8_p2_0 = { + _vq_quantthresh__44u8_p2_0, + _vq_quantmap__44u8_p2_0, + 5, + 5 +}; + +static static_codebook _44u8_p2_0 = { + 4, 625, + _vq_lengthlist__44u8_p2_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u8_p2_0, + NULL, + &_vq_auxt__44u8_p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u8_p3_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44u8_p3_0[] = { + 3, 4, 4, 5, 5, 7, 7, 9, 9, 4, 5, 4, 6, 6, 7, 7, + 9, 9, 4, 4, 5, 6, 6, 7, 7, 9, 9, 5, 6, 6, 7, 7, + 8, 8,10,10, 6, 6, 6, 7, 7, 8, 8,10,10, 7, 7, 7, + 8, 8, 9, 9,11,10, 7, 7, 7, 8, 8, 9, 9,10,11, 9, + 9, 9,10,10,11,10,12,11, 9, 9, 9, 9,10,11,11,11, + 12, +}; + +static float _vq_quantthresh__44u8_p3_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44u8_p3_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44u8_p3_0 = { + _vq_quantthresh__44u8_p3_0, + _vq_quantmap__44u8_p3_0, + 9, + 9 +}; + +static static_codebook _44u8_p3_0 = { + 2, 81, + _vq_lengthlist__44u8_p3_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44u8_p3_0, + NULL, + &_vq_auxt__44u8_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u8_p4_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44u8_p4_0[] = { + 4, 4, 4, 6, 6, 7, 7, 8, 8, 8, 8,10,10,11,11,11, + 11, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9,10,10,11,11, + 12,12, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9,10,10,11, + 11,12,12, 6, 6, 6, 7, 7, 8, 8, 9, 9, 9, 9,10,10, + 11,11,12,12, 6, 6, 6, 7, 7, 8, 8, 9, 9, 9, 9,10, + 10,11,11,12,12, 7, 7, 7, 8, 8, 9, 8,10, 9,10, 9, + 11,10,12,11,13,12, 7, 7, 7, 8, 8, 8, 9, 9,10, 9, + 10,10,11,11,12,12,13, 8, 8, 8, 9, 9, 9, 9,10,10, + 11,10,11,11,12,12,13,13, 8, 8, 8, 9, 9, 9,10,10, + 10,10,11,11,11,12,12,12,13, 8, 9, 9, 9, 9,10, 9, + 11,10,11,11,12,11,13,12,13,13, 8, 9, 9, 9, 9, 9, + 10,10,11,11,11,11,12,12,13,13,13,10,10,10,10,10, + 11,10,11,11,12,11,13,12,13,13,14,13,10,10,10,10, + 10,10,11,11,11,11,12,12,13,13,13,13,14,11,11,11, + 11,11,12,11,12,12,13,12,13,13,14,13,14,14,11,11, + 11,11,11,11,12,12,12,12,13,13,13,13,14,14,14,11, + 12,12,12,12,13,12,13,12,13,13,14,13,14,14,14,14, + 11,12,12,12,12,12,12,13,13,13,13,13,14,14,14,14, + 14, +}; + +static float _vq_quantthresh__44u8_p4_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44u8_p4_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44u8_p4_0 = { + _vq_quantthresh__44u8_p4_0, + _vq_quantmap__44u8_p4_0, + 17, + 17 +}; + +static static_codebook _44u8_p4_0 = { + 2, 289, + _vq_lengthlist__44u8_p4_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44u8_p4_0, + NULL, + &_vq_auxt__44u8_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u8_p5_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u8_p5_0[] = { + 1, 4, 4, 5, 7, 7, 5, 7, 7, 5, 8, 8, 8, 9, 9, 7, + 9, 9, 5, 8, 8, 7, 9, 9, 8, 9, 9, 5, 8, 8, 8,10, + 10, 8,10,10, 7,10,10, 9,10,12, 9,12,11, 7,10,10, + 9,11,10, 9,11,12, 5, 8, 8, 8,10,10, 8,10,10, 7, + 10,10, 9,11,11, 9,10,11, 7,10,10, 9,11,11,10,12, + 10, +}; + +static float _vq_quantthresh__44u8_p5_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__44u8_p5_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u8_p5_0 = { + _vq_quantthresh__44u8_p5_0, + _vq_quantmap__44u8_p5_0, + 3, + 3 +}; + +static static_codebook _44u8_p5_0 = { + 4, 81, + _vq_lengthlist__44u8_p5_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__44u8_p5_0, + NULL, + &_vq_auxt__44u8_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u8_p5_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44u8_p5_1[] = { + 4, 5, 5, 6, 6, 7, 7, 7, 7, 8, 8, 5, 5, 5, 6, 6, + 7, 7, 8, 8, 8, 8, 5, 5, 5, 6, 6, 7, 7, 7, 8, 8, + 8, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 6, 6, 6, 7, + 7, 7, 7, 8, 8, 8, 8, 7, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 7, 8, 7, + 8, 8, 8, 8, 8, 8, 8, 8, 7, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 9, 9, +}; + +static float _vq_quantthresh__44u8_p5_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44u8_p5_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44u8_p5_1 = { + _vq_quantthresh__44u8_p5_1, + _vq_quantmap__44u8_p5_1, + 11, + 11 +}; + +static static_codebook _44u8_p5_1 = { + 2, 121, + _vq_lengthlist__44u8_p5_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44u8_p5_1, + NULL, + &_vq_auxt__44u8_p5_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u8_p6_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44u8_p6_0[] = { + 2, 4, 4, 6, 6, 7, 7, 8, 8, 9, 9,10,10, 4, 6, 5, + 7, 7, 8, 8, 8, 8, 9, 9,10,10, 4, 6, 6, 7, 7, 8, + 8, 8, 8, 9, 9,10,10, 6, 7, 7, 7, 8, 8, 8, 8, 9, + 9,10,10,10, 6, 7, 7, 8, 8, 8, 8, 9, 8,10, 9,11, + 10, 7, 8, 8, 8, 8, 8, 9, 9, 9,10,10,11,11, 7, 8, + 8, 8, 8, 9, 8, 9, 9,10,10,11,11, 8, 8, 8, 9, 9, + 9, 9, 9,10,10,10,11,11, 8, 8, 8, 9, 9, 9, 9,10, + 9,10,10,11,11, 9, 9, 9, 9,10,10,10,10,10,10,11, + 11,12, 9, 9, 9,10, 9,10,10,10,10,11,10,12,11,10, + 10,10,10,10,11,11,11,11,11,12,12,12,10,10,10,10, + 11,11,11,11,11,12,11,12,12, +}; + +static float _vq_quantthresh__44u8_p6_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44u8_p6_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44u8_p6_0 = { + _vq_quantthresh__44u8_p6_0, + _vq_quantmap__44u8_p6_0, + 13, + 13 +}; + +static static_codebook _44u8_p6_0 = { + 2, 169, + _vq_lengthlist__44u8_p6_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44u8_p6_0, + NULL, + &_vq_auxt__44u8_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u8_p6_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u8_p6_1[] = { + 3, 4, 4, 5, 5, 4, 5, 5, 5, 5, 4, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, +}; + +static float _vq_quantthresh__44u8_p6_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u8_p6_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u8_p6_1 = { + _vq_quantthresh__44u8_p6_1, + _vq_quantmap__44u8_p6_1, + 5, + 5 +}; + +static static_codebook _44u8_p6_1 = { + 2, 25, + _vq_lengthlist__44u8_p6_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u8_p6_1, + NULL, + &_vq_auxt__44u8_p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u8_p7_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44u8_p7_0[] = { + 1, 4, 5, 6, 6, 7, 7, 8, 8,10,10,11,11, 5, 6, 6, + 7, 7, 8, 8, 9, 9,11,10,12,11, 5, 6, 6, 7, 7, 8, + 8, 9, 9,10,11,11,12, 6, 7, 7, 8, 8, 9, 9,10,10, + 11,11,12,12, 6, 7, 7, 8, 8, 9, 9,10,10,11,12,13, + 12, 7, 8, 8, 9, 9,10,10,11,11,12,12,13,13, 8, 8, + 8, 9, 9,10,10,11,11,12,12,13,13, 9, 9, 9,10,10, + 11,11,12,12,13,13,14,14, 9, 9, 9,10,10,11,11,12, + 12,13,13,14,14,10,11,11,12,11,13,12,13,13,14,14, + 15,15,10,11,11,11,12,12,13,13,14,14,14,15,15,11, + 12,12,13,13,14,13,15,14,15,15,16,15,11,11,12,13, + 13,13,14,14,14,15,15,15,16, +}; + +static float _vq_quantthresh__44u8_p7_0[] = { + -60.5, -49.5, -38.5, -27.5, -16.5, -5.5, 5.5, 16.5, + 27.5, 38.5, 49.5, 60.5, +}; + +static long _vq_quantmap__44u8_p7_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44u8_p7_0 = { + _vq_quantthresh__44u8_p7_0, + _vq_quantmap__44u8_p7_0, + 13, + 13 +}; + +static static_codebook _44u8_p7_0 = { + 2, 169, + _vq_lengthlist__44u8_p7_0, + 1, -523206656, 1618345984, 4, 0, + _vq_quantlist__44u8_p7_0, + NULL, + &_vq_auxt__44u8_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u8_p7_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44u8_p7_1[] = { + 4, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7, 5, 6, 6, 7, 7, + 7, 7, 7, 7, 7, 7, 5, 6, 6, 7, 7, 7, 7, 7, 7, 7, + 7, 6, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 6, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 8, 7, 7, 7, 7, 7, 7, 7, 8, 8, + 8, 8, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 7, 7, 7, + 8, 7, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 8, 8, 8, + 8, 8, 8, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, + 7, 8, 8, 8, 8, 8, 8, 8, 8, +}; + +static float _vq_quantthresh__44u8_p7_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44u8_p7_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44u8_p7_1 = { + _vq_quantthresh__44u8_p7_1, + _vq_quantmap__44u8_p7_1, + 11, + 11 +}; + +static static_codebook _44u8_p7_1 = { + 2, 121, + _vq_lengthlist__44u8_p7_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44u8_p7_1, + NULL, + &_vq_auxt__44u8_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u8_p8_0[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__44u8_p8_0[] = { + 1, 4, 4, 7, 7, 8, 8, 8, 7, 9, 8,10, 9,11,10, 4, + 6, 6, 8, 8,10, 9, 9, 9,10,10,11,10,12,10, 4, 6, + 6, 8, 8,10,10, 9, 9,10,10,11,11,11,12, 7, 8, 8, + 10,10,11,11,11,10,12,11,12,12,13,11, 7, 8, 8,10, + 10,11,11,10,10,11,11,12,12,13,13, 8,10,10,11,11, + 12,11,12,11,13,12,13,12,14,13, 8,10, 9,11,11,12, + 12,12,12,12,12,13,13,14,13, 8, 9, 9,11,10,12,11, + 13,12,13,13,14,13,14,13, 8, 9, 9,10,11,12,12,12, + 12,13,13,14,15,14,14, 9,10,10,12,11,13,12,13,13, + 14,13,14,14,14,14, 9,10,10,12,12,12,12,13,13,14, + 14,14,15,14,14,10,11,11,13,12,13,12,14,14,14,14, + 14,14,15,15,10,11,11,12,12,13,13,14,14,14,15,15, + 14,16,15,11,12,12,13,12,14,14,14,13,15,14,15,15, + 15,17,11,12,12,13,13,14,14,14,15,15,14,15,15,14, + 17, +}; + +static float _vq_quantthresh__44u8_p8_0[] = { + -136.5, -115.5, -94.5, -73.5, -52.5, -31.5, -10.5, 10.5, + 31.5, 52.5, 73.5, 94.5, 115.5, 136.5, +}; + +static long _vq_quantmap__44u8_p8_0[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__44u8_p8_0 = { + _vq_quantthresh__44u8_p8_0, + _vq_quantmap__44u8_p8_0, + 15, + 15 +}; + +static static_codebook _44u8_p8_0 = { + 2, 225, + _vq_lengthlist__44u8_p8_0, + 1, -520986624, 1620377600, 4, 0, + _vq_quantlist__44u8_p8_0, + NULL, + &_vq_auxt__44u8_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u8_p8_1[] = { + 10, + 9, + 11, + 8, + 12, + 7, + 13, + 6, + 14, + 5, + 15, + 4, + 16, + 3, + 17, + 2, + 18, + 1, + 19, + 0, + 20, +}; + +static long _vq_lengthlist__44u8_p8_1[] = { + 4, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 6, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 5, 6, 6, 7, 7, 8, + 8, 9, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 7, + 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10,10, 9,10, 8, 8, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10, 9,10, + 10, 9,10, 8, 9, 8, 9, 9, 9, 9, 9, 9, 9, 9,10, 9, + 10,10,10,10,10,10,10,10, 8, 9, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9,10,10,10,10, 9,10,10, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9,10, 9,10,10,10,10,10,10, + 10,10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10, 9,10, + 10,10,10,10,10,10,10, 9, 9, 9, 9, 9, 9, 9,10, 9, + 10,10,10,10,10,10,10,10,10,10,10,10, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9,10,10,10,10,10,10,10,10,10,10, + 10, 9, 9, 9, 9, 9, 9,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10, 9, 9, 9, 9, 9, 9, 9,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10, 9, 9, 9, 9, 9, + 9, 9,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 9, 9, 9, 9, 9,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10, 9, 9, 9,10, 9,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10, 9, 9, 9, 9, 9,10, + 9,10,10,10,10,10,10,10,10,10,10,10,10,10,10, 9, + 9, 9, 9, 9, 9,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10, 9, 9, 9,10, 9,10, 9,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10, +}; + +static float _vq_quantthresh__44u8_p8_1[] = { + -9.5, -8.5, -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, + -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, + 6.5, 7.5, 8.5, 9.5, +}; + +static long _vq_quantmap__44u8_p8_1[] = { + 19, 17, 15, 13, 11, 9, 7, 5, + 3, 1, 0, 2, 4, 6, 8, 10, + 12, 14, 16, 18, 20, +}; + +static encode_aux_threshmatch _vq_auxt__44u8_p8_1 = { + _vq_quantthresh__44u8_p8_1, + _vq_quantmap__44u8_p8_1, + 21, + 21 +}; + +static static_codebook _44u8_p8_1 = { + 2, 441, + _vq_lengthlist__44u8_p8_1, + 1, -529268736, 1611661312, 5, 0, + _vq_quantlist__44u8_p8_1, + NULL, + &_vq_auxt__44u8_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u8_p9_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44u8_p9_0[] = { + 1, 3, 3, 9, 9, 9, 9, 9, 9, 4, 9, 9, 9, 9, 9, 9, + 9, 9, 5, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 8, 8, + 8, +}; + +static float _vq_quantthresh__44u8_p9_0[] = { + -3258.5, -2327.5, -1396.5, -465.5, 465.5, 1396.5, 2327.5, 3258.5, +}; + +static long _vq_quantmap__44u8_p9_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44u8_p9_0 = { + _vq_quantthresh__44u8_p9_0, + _vq_quantmap__44u8_p9_0, + 9, + 9 +}; + +static static_codebook _44u8_p9_0 = { + 2, 81, + _vq_lengthlist__44u8_p9_0, + 1, -511895552, 1631393792, 4, 0, + _vq_quantlist__44u8_p9_0, + NULL, + &_vq_auxt__44u8_p9_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u8_p9_1[] = { + 9, + 8, + 10, + 7, + 11, + 6, + 12, + 5, + 13, + 4, + 14, + 3, + 15, + 2, + 16, + 1, + 17, + 0, + 18, +}; + +static long _vq_lengthlist__44u8_p9_1[] = { + 1, 4, 4, 7, 7, 8, 7, 8, 6, 9, 7,10, 8,11,10,11, + 11,11,11, 4, 7, 6, 9, 9,10, 9, 9, 9,10,10,11,10, + 11,10,11,11,13,11, 4, 7, 7, 9, 9, 9, 9, 9, 9,10, + 10,11,10,11,11,11,12,11,12, 7, 9, 8,11,11,11,11, + 10,10,11,11,12,12,12,12,12,12,14,13, 7, 8, 9,10, + 11,11,11,10,10,11,11,11,11,12,12,14,12,13,14, 8, + 9, 9,11,11,11,11,11,11,12,12,14,12,15,14,14,14, + 15,14, 8, 9, 9,11,11,11,11,12,11,12,12,13,13,13, + 13,13,13,14,14, 8, 9, 9,11,10,12,11,12,12,13,13, + 13,13,15,14,14,14,16,16, 8, 9, 9,10,11,11,12,12, + 12,13,13,13,14,14,14,15,16,15,15, 9,10,10,11,12, + 12,13,13,13,14,14,16,14,14,16,16,16,16,15, 9,10, + 10,11,11,12,13,13,14,15,14,16,14,15,16,16,16,16, + 15,10,11,11,12,13,13,14,15,15,15,15,15,16,15,16, + 15,16,15,15,10,11,11,13,13,14,13,13,15,14,15,15, + 16,15,15,15,16,15,16,10,12,12,14,14,14,14,14,16, + 16,15,15,15,16,16,16,16,16,16,11,12,12,14,14,14, + 14,15,15,16,15,16,15,16,15,16,16,16,16,12,12,13, + 14,14,15,16,16,16,16,16,16,15,16,16,16,16,16,16, + 12,13,13,14,14,14,14,15,16,15,16,16,16,16,16,16, + 16,16,16,12,13,14,14,14,16,15,16,15,16,16,16,16, + 16,16,16,16,16,16,12,14,13,14,15,15,15,16,15,16, + 16,15,16,16,16,16,16,16,16, +}; + +static float _vq_quantthresh__44u8_p9_1[] = { + -416.5, -367.5, -318.5, -269.5, -220.5, -171.5, -122.5, -73.5, + -24.5, 24.5, 73.5, 122.5, 171.5, 220.5, 269.5, 318.5, + 367.5, 416.5, +}; + +static long _vq_quantmap__44u8_p9_1[] = { + 17, 15, 13, 11, 9, 7, 5, 3, + 1, 0, 2, 4, 6, 8, 10, 12, + 14, 16, 18, +}; + +static encode_aux_threshmatch _vq_auxt__44u8_p9_1 = { + _vq_quantthresh__44u8_p9_1, + _vq_quantmap__44u8_p9_1, + 19, + 19 +}; + +static static_codebook _44u8_p9_1 = { + 2, 361, + _vq_lengthlist__44u8_p9_1, + 1, -518287360, 1622704128, 5, 0, + _vq_quantlist__44u8_p9_1, + NULL, + &_vq_auxt__44u8_p9_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u8_p9_2[] = { + 24, + 23, + 25, + 22, + 26, + 21, + 27, + 20, + 28, + 19, + 29, + 18, + 30, + 17, + 31, + 16, + 32, + 15, + 33, + 14, + 34, + 13, + 35, + 12, + 36, + 11, + 37, + 10, + 38, + 9, + 39, + 8, + 40, + 7, + 41, + 6, + 42, + 5, + 43, + 4, + 44, + 3, + 45, + 2, + 46, + 1, + 47, + 0, + 48, +}; + +static long _vq_lengthlist__44u8_p9_2[] = { + 2, 3, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, +}; + +static float _vq_quantthresh__44u8_p9_2[] = { + -23.5, -22.5, -21.5, -20.5, -19.5, -18.5, -17.5, -16.5, + -15.5, -14.5, -13.5, -12.5, -11.5, -10.5, -9.5, -8.5, + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, + 8.5, 9.5, 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, + 16.5, 17.5, 18.5, 19.5, 20.5, 21.5, 22.5, 23.5, +}; + +static long _vq_quantmap__44u8_p9_2[] = { + 47, 45, 43, 41, 39, 37, 35, 33, + 31, 29, 27, 25, 23, 21, 19, 17, + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, + 48, +}; + +static encode_aux_threshmatch _vq_auxt__44u8_p9_2 = { + _vq_quantthresh__44u8_p9_2, + _vq_quantmap__44u8_p9_2, + 49, + 49 +}; + +static static_codebook _44u8_p9_2 = { + 1, 49, + _vq_lengthlist__44u8_p9_2, + 1, -526909440, 1611661312, 6, 0, + _vq_quantlist__44u8_p9_2, + NULL, + &_vq_auxt__44u8_p9_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44u9__long[] = { + 3, 9,13,13,14,15,14,14,15,15, 5, 5, 9,10,12,12, + 13,14,16,15,10, 6, 6, 6, 8,11,12,13,16,15,11, 7, + 5, 3, 5, 8,10,12,15,15,10,10, 7, 4, 3, 5, 8,10, + 12,12,12,12, 9, 7, 5, 4, 6, 8,10,13,13,12,11, 9, + 7, 5, 5, 6, 9,12,14,12,12,10, 8, 6, 6, 6, 7,11, + 13,12,14,13,10, 8, 7, 7, 7,10,11,11,12,13,12,11, + 10, 8, 8, 9, +}; + +static static_codebook _huff_book__44u9__long = { + 2, 100, + _huff_lengthlist__44u9__long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44u9__short[] = { + 9,16,18,18,17,17,17,17,17,17, 5, 8,11,12,11,12, + 17,17,16,16, 6, 6, 8, 8, 9,10,14,15,16,16, 6, 7, + 7, 4, 6, 9,13,16,16,16, 6, 6, 7, 4, 5, 8,11,15, + 17,16, 7, 6, 7, 6, 6, 8, 9,10,14,16,11, 8, 8, 7, + 6, 6, 3, 4,10,15,14,12,12,10, 5, 6, 3, 3, 8,13, + 15,17,15,11, 6, 8, 6, 6, 9,14,17,15,15,12, 8,10, + 9, 9,12,15, +}; + +static static_codebook _huff_book__44u9__short = { + 2, 100, + _huff_lengthlist__44u9__short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44u9_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u9_p1_0[] = { + 1, 5, 5, 5, 7, 7, 5, 7, 7, 5, 7, 7, 7, 9, 9, 7, + 9, 9, 5, 7, 7, 7, 9, 9, 7, 9, 9, 5, 7, 7, 7, 9, + 9, 7, 9, 9, 8, 9, 9, 9,10,11, 9,11,11, 7, 9, 9, + 9,11,10, 9,11,11, 5, 7, 7, 7, 9, 9, 8, 9,10, 7, + 9, 9, 9,11,11, 9,10,11, 7, 9,10, 9,11,11, 9,11, + 10, +}; + +static float _vq_quantthresh__44u9_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44u9_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u9_p1_0 = { + _vq_quantthresh__44u9_p1_0, + _vq_quantmap__44u9_p1_0, + 3, + 3 +}; + +static static_codebook _44u9_p1_0 = { + 4, 81, + _vq_lengthlist__44u9_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44u9_p1_0, + NULL, + &_vq_auxt__44u9_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u9_p2_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u9_p2_0[] = { + 3, 5, 5, 8, 8, 5, 7, 7, 9, 9, 6, 7, 7, 9, 9, 8, + 9, 9,11,10, 8, 9, 9,11,11, 6, 7, 7, 9, 9, 7, 8, + 8,10,10, 7, 8, 8, 9,10, 9,10,10,11,11, 9, 9,10, + 11,11, 6, 7, 7, 9, 9, 7, 8, 8,10, 9, 7, 8, 8,10, + 10, 9,10, 9,11,11, 9,10,10,11,11, 8, 9, 9,11,11, + 9,10,10,12,11, 9,10,10,11,12,11,11,11,13,13,11, + 11,11,12,13, 8, 9, 9,11,11, 9,10,10,11,11, 9,10, + 10,12,11,11,12,11,13,12,11,11,12,13,13, 6, 7, 7, + 9, 9, 7, 8, 8,10,10, 7, 8, 8,10,10, 9,10,10,12, + 11, 9,10,10,11,12, 7, 8, 8,10,10, 8, 9, 9,11,11, + 8, 9, 9,10,10,10,11,11,12,12,10,10,11,12,12, 7, + 8, 8,10,10, 8, 9, 8,10,10, 8, 9, 9,10,10,10,11, + 10,12,11,10,10,11,12,12, 9,10,10,11,12,10,11,11, + 12,12,10,11,10,12,12,12,12,12,13,13,11,12,12,13, + 13, 9,10,10,11,11, 9,10,10,12,12,10,11,11,12,13, + 11,12,11,13,12,12,12,12,13,14, 6, 7, 7, 9, 9, 7, + 8, 8,10,10, 7, 8, 8,10,10, 9,10,10,11,11, 9,10, + 10,11,12, 7, 8, 8,10,10, 8, 9, 9,11,10, 8, 8, 9, + 10,10,10,11,10,12,12,10,10,11,11,12, 7, 8, 8,10, + 10, 8, 9, 9,10,10, 8, 9, 9,10,10,10,11,10,12,12, + 10,11,10,12,12, 9,10,10,12,11,10,11,11,12,12, 9, + 10,10,12,12,12,12,12,13,13,11,11,12,12,14, 9,10, + 10,11,12,10,11,11,12,12,10,11,11,12,12,11,12,12, + 14,14,12,12,12,13,13, 8, 9, 9,11,11, 9,10,10,12, + 11, 9,10,10,12,12,11,12,11,13,13,11,11,12,13,13, + 9,10,10,12,12,10,11,11,12,12,10,11,11,12,12,12, + 12,12,14,14,12,12,12,13,13, 9,10,10,12,11,10,11, + 10,12,12,10,11,11,12,12,11,12,12,14,13,12,12,12, + 13,14,11,12,11,13,13,11,12,12,13,13,12,12,12,14, + 14,13,13,13,13,15,13,13,14,15,15,11,11,11,13,13, + 11,12,11,13,13,11,12,12,13,13,12,13,12,15,13,13, + 13,14,14,15, 8, 9, 9,11,11, 9,10,10,11,12, 9,10, + 10,11,12,11,12,11,13,13,11,12,12,13,13, 9,10,10, + 11,12,10,11,10,12,12,10,10,11,12,13,12,12,12,14, + 13,11,12,12,13,14, 9,10,10,12,12,10,11,11,12,12, + 10,11,11,12,12,12,12,12,14,13,12,12,12,14,13,11, + 11,11,13,13,11,12,12,14,13,11,11,12,13,13,13,13, + 13,15,14,12,12,13,13,15,11,12,12,13,13,12,12,12, + 13,14,11,12,12,13,13,13,13,14,14,15,13,13,13,14, + 14, +}; + +static float _vq_quantthresh__44u9_p2_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u9_p2_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u9_p2_0 = { + _vq_quantthresh__44u9_p2_0, + _vq_quantmap__44u9_p2_0, + 5, + 5 +}; + +static static_codebook _44u9_p2_0 = { + 4, 625, + _vq_lengthlist__44u9_p2_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u9_p2_0, + NULL, + &_vq_auxt__44u9_p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u9_p3_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44u9_p3_0[] = { + 3, 4, 4, 5, 5, 7, 7, 8, 8, 4, 5, 5, 6, 6, 7, 7, + 9, 9, 4, 4, 5, 6, 6, 7, 7, 9, 9, 5, 6, 6, 7, 7, + 8, 8, 9, 9, 5, 6, 6, 7, 7, 8, 8, 9, 9, 7, 7, 7, + 8, 8, 9, 9,10,10, 7, 7, 7, 8, 8, 9, 9,10,10, 8, + 9, 9,10, 9,10,10,11,11, 8, 9, 9, 9,10,10,10,11, + 11, +}; + +static float _vq_quantthresh__44u9_p3_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44u9_p3_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44u9_p3_0 = { + _vq_quantthresh__44u9_p3_0, + _vq_quantmap__44u9_p3_0, + 9, + 9 +}; + +static static_codebook _44u9_p3_0 = { + 2, 81, + _vq_lengthlist__44u9_p3_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44u9_p3_0, + NULL, + &_vq_auxt__44u9_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u9_p4_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44u9_p4_0[] = { + 4, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9,10,10,11, + 11, 5, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9,10,10, + 11,11, 5, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9,10, + 10,11,11, 6, 6, 6, 7, 6, 7, 7, 8, 8, 9, 9,10,10, + 11,11,12,11, 6, 6, 6, 6, 7, 7, 7, 8, 8, 9, 9,10, + 10,11,11,11,12, 7, 7, 7, 7, 7, 8, 8, 9, 9, 9, 9, + 10,10,11,11,12,12, 7, 7, 7, 7, 7, 8, 8, 9, 9, 9, + 9,10,10,11,11,12,12, 8, 8, 8, 8, 8, 9, 8,10, 9, + 10,10,11,10,12,11,13,12, 8, 8, 8, 8, 8, 9, 9, 9, + 10,10,10,10,11,11,12,12,12, 8, 8, 8, 9, 9, 9, 9, + 10,10,11,10,12,11,12,12,13,12, 8, 8, 8, 9, 9, 9, + 9,10,10,10,11,11,11,12,12,12,13, 9, 9, 9,10,10, + 10,10,11,10,11,11,12,11,13,12,13,13, 9, 9,10,10, + 10,10,10,10,11,11,11,11,12,12,13,13,13,10,11,10, + 11,11,11,11,12,11,12,12,13,12,13,13,14,13,10,10, + 10,11,11,11,11,11,12,12,12,12,13,13,13,13,14,11, + 11,11,12,11,12,12,12,12,13,13,13,13,14,13,14,14, + 11,11,11,11,12,12,12,12,12,12,13,13,13,13,14,14, + 14, +}; + +static float _vq_quantthresh__44u9_p4_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44u9_p4_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44u9_p4_0 = { + _vq_quantthresh__44u9_p4_0, + _vq_quantmap__44u9_p4_0, + 17, + 17 +}; + +static static_codebook _44u9_p4_0 = { + 2, 289, + _vq_lengthlist__44u9_p4_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44u9_p4_0, + NULL, + &_vq_auxt__44u9_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u9_p5_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u9_p5_0[] = { + 1, 4, 4, 5, 7, 7, 5, 7, 7, 5, 8, 8, 8, 9, 9, 7, + 9, 9, 5, 8, 8, 7, 9, 9, 8, 9, 9, 5, 8, 8, 8,10, + 10, 8,10,10, 7,10,10, 9,10,12, 9,11,11, 7,10,10, + 9,11,10, 9,11,12, 5, 8, 8, 8,10,10, 8,10,10, 7, + 10,10, 9,12,11, 9,10,11, 7,10,10, 9,11,11,10,12, + 10, +}; + +static float _vq_quantthresh__44u9_p5_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__44u9_p5_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u9_p5_0 = { + _vq_quantthresh__44u9_p5_0, + _vq_quantmap__44u9_p5_0, + 3, + 3 +}; + +static static_codebook _44u9_p5_0 = { + 4, 81, + _vq_lengthlist__44u9_p5_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__44u9_p5_0, + NULL, + &_vq_auxt__44u9_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u9_p5_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44u9_p5_1[] = { + 5, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7, 5, 6, 6, 6, 6, + 7, 7, 7, 7, 8, 7, 5, 6, 6, 6, 6, 7, 7, 7, 7, 7, + 7, 6, 6, 6, 7, 7, 7, 7, 7, 7, 8, 8, 6, 6, 6, 7, + 7, 7, 7, 7, 7, 8, 8, 7, 7, 7, 7, 7, 8, 7, 8, 8, + 8, 8, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 7, 7, 7, + 8, 7, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 7, 8, 7, 8, 8, 8, 8, 8, 8, 8, 8, 7, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, +}; + +static float _vq_quantthresh__44u9_p5_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44u9_p5_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44u9_p5_1 = { + _vq_quantthresh__44u9_p5_1, + _vq_quantmap__44u9_p5_1, + 11, + 11 +}; + +static static_codebook _44u9_p5_1 = { + 2, 121, + _vq_lengthlist__44u9_p5_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44u9_p5_1, + NULL, + &_vq_auxt__44u9_p5_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u9_p6_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44u9_p6_0[] = { + 2, 4, 4, 6, 6, 7, 7, 8, 8, 9, 9,10,10, 4, 6, 5, + 7, 7, 8, 8, 8, 8, 9, 9,10,10, 4, 5, 6, 7, 7, 8, + 8, 8, 8, 9, 9,10,10, 6, 7, 7, 8, 8, 8, 8, 9, 9, + 10,10,10,10, 6, 7, 7, 8, 8, 8, 8, 9, 9,10,10,10, + 10, 7, 8, 8, 8, 8, 9, 9, 9, 9,10,10,11,11, 7, 8, + 8, 8, 8, 9, 9, 9, 9,10,10,11,11, 8, 8, 8, 9, 9, + 9, 9, 9,10,10,10,11,11, 8, 8, 8, 9, 9, 9, 9,10, + 9,10,10,11,11, 9, 9, 9,10,10,10,10,10,11,11,11, + 11,12, 9, 9, 9,10,10,10,10,10,10,11,10,12,11,10, + 10,10,10,10,11,11,11,11,11,12,12,12,10,10,10,10, + 10,11,11,11,11,12,11,12,12, +}; + +static float _vq_quantthresh__44u9_p6_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44u9_p6_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44u9_p6_0 = { + _vq_quantthresh__44u9_p6_0, + _vq_quantmap__44u9_p6_0, + 13, + 13 +}; + +static static_codebook _44u9_p6_0 = { + 2, 169, + _vq_lengthlist__44u9_p6_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44u9_p6_0, + NULL, + &_vq_auxt__44u9_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u9_p6_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u9_p6_1[] = { + 4, 4, 4, 5, 5, 4, 5, 4, 5, 5, 4, 4, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, +}; + +static float _vq_quantthresh__44u9_p6_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u9_p6_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u9_p6_1 = { + _vq_quantthresh__44u9_p6_1, + _vq_quantmap__44u9_p6_1, + 5, + 5 +}; + +static static_codebook _44u9_p6_1 = { + 2, 25, + _vq_lengthlist__44u9_p6_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u9_p6_1, + NULL, + &_vq_auxt__44u9_p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u9_p7_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44u9_p7_0[] = { + 1, 4, 5, 6, 6, 7, 7, 8, 9,10,10,11,11, 5, 6, 6, + 7, 7, 8, 8, 9, 9,10,10,11,11, 5, 6, 6, 7, 7, 8, + 8, 9, 9,10,10,11,11, 6, 7, 7, 8, 8, 9, 9,10,10, + 11,11,12,12, 6, 7, 7, 8, 8, 9, 9,10,10,11,11,12, + 12, 8, 8, 8, 9, 9,10,10,11,11,12,12,13,13, 8, 8, + 8, 9, 9,10,10,11,11,12,12,13,13, 9, 9, 9,10,10, + 11,11,12,12,13,13,13,13, 9, 9, 9,10,10,11,11,12, + 12,13,13,14,14,10,10,10,11,11,12,12,13,13,14,13, + 15,14,10,10,10,11,11,12,12,13,13,14,14,14,14,11, + 11,12,12,12,13,13,14,14,14,14,15,15,11,11,12,12, + 12,13,13,14,14,14,15,15,15, +}; + +static float _vq_quantthresh__44u9_p7_0[] = { + -60.5, -49.5, -38.5, -27.5, -16.5, -5.5, 5.5, 16.5, + 27.5, 38.5, 49.5, 60.5, +}; + +static long _vq_quantmap__44u9_p7_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44u9_p7_0 = { + _vq_quantthresh__44u9_p7_0, + _vq_quantmap__44u9_p7_0, + 13, + 13 +}; + +static static_codebook _44u9_p7_0 = { + 2, 169, + _vq_lengthlist__44u9_p7_0, + 1, -523206656, 1618345984, 4, 0, + _vq_quantlist__44u9_p7_0, + NULL, + &_vq_auxt__44u9_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u9_p7_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44u9_p7_1[] = { + 5, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 7, 7, + 7, 7, 7, 7, 7, 7, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 8, 8, 7, 7, 7, 7, 7, 7, 7, 8, 7, 8, 8, 7, 7, + 7, 7, 7, 7, 7, 8, 8, 8, 8, +}; + +static float _vq_quantthresh__44u9_p7_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44u9_p7_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44u9_p7_1 = { + _vq_quantthresh__44u9_p7_1, + _vq_quantmap__44u9_p7_1, + 11, + 11 +}; + +static static_codebook _44u9_p7_1 = { + 2, 121, + _vq_lengthlist__44u9_p7_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44u9_p7_1, + NULL, + &_vq_auxt__44u9_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u9_p8_0[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__44u9_p8_0[] = { + 1, 4, 4, 7, 7, 8, 8, 8, 8, 9, 9,10, 9,11,10, 4, + 6, 6, 8, 8, 9, 9, 9, 9,10,10,11,10,12,10, 4, 6, + 6, 8, 8, 9,10, 9, 9,10,10,11,11,12,12, 7, 8, 8, + 10,10,11,11,10,10,11,11,12,12,13,12, 7, 8, 8,10, + 10,11,11,10,10,11,11,12,12,12,13, 8,10, 9,11,11, + 12,12,11,11,12,12,13,13,14,13, 8, 9, 9,11,11,12, + 12,11,12,12,12,13,13,14,13, 8, 9, 9,10,10,12,11, + 13,12,13,13,14,13,15,14, 8, 9, 9,10,10,11,12,12, + 12,13,13,13,14,14,14, 9,10,10,12,11,13,12,13,13, + 14,13,14,14,14,15, 9,10,10,11,12,12,12,13,13,14, + 14,14,15,15,15,10,11,11,12,12,13,13,14,14,14,14, + 15,14,16,15,10,11,11,12,12,13,13,13,14,14,14,14, + 14,15,16,11,12,12,13,13,14,13,14,14,15,14,15,16, + 16,16,11,12,12,13,13,14,13,14,14,15,15,15,16,15, + 15, +}; + +static float _vq_quantthresh__44u9_p8_0[] = { + -136.5, -115.5, -94.5, -73.5, -52.5, -31.5, -10.5, 10.5, + 31.5, 52.5, 73.5, 94.5, 115.5, 136.5, +}; + +static long _vq_quantmap__44u9_p8_0[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__44u9_p8_0 = { + _vq_quantthresh__44u9_p8_0, + _vq_quantmap__44u9_p8_0, + 15, + 15 +}; + +static static_codebook _44u9_p8_0 = { + 2, 225, + _vq_lengthlist__44u9_p8_0, + 1, -520986624, 1620377600, 4, 0, + _vq_quantlist__44u9_p8_0, + NULL, + &_vq_auxt__44u9_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u9_p8_1[] = { + 10, + 9, + 11, + 8, + 12, + 7, + 13, + 6, + 14, + 5, + 15, + 4, + 16, + 3, + 17, + 2, + 18, + 1, + 19, + 0, + 20, +}; + +static long _vq_lengthlist__44u9_p8_1[] = { + 4, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 6, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 6, 6, 6, 7, 7, 8, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 7, + 7, 7, 8, 8, 8, 8, 9, 8, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9,10, 9,10,10,10, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9,10,10, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 10, 9,10, 9,10,10,10,10, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9,10,10, 9,10,10,10,10,10, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9,10, 9,10,10,10,10,10,10, + 10,10, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10,10,10, + 10,10,10,10,10,10,10, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9,10,10,10,10,10,10,10,10,10,10, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9,10,10,10,10,10,10,10,10,10, + 10, 9, 9, 9, 9, 9, 9, 9,10, 9,10,10,10,10,10,10, + 10,10,10,10,10,10, 9, 9, 9, 9, 9, 9, 9, 9,10,10, + 10,10,10,10,10,10,10,10,10,10,10, 9, 9, 9, 9, 9, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 9, 9, 9, 9,10, 9, 9,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10, 9, 9, 9,10, 9,10, 9,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10, 9, 9, 9,10, 9,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, 9, + 9, 9, 9, 9,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10, 9, 9, 9,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10, +}; + +static float _vq_quantthresh__44u9_p8_1[] = { + -9.5, -8.5, -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, + -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, + 6.5, 7.5, 8.5, 9.5, +}; + +static long _vq_quantmap__44u9_p8_1[] = { + 19, 17, 15, 13, 11, 9, 7, 5, + 3, 1, 0, 2, 4, 6, 8, 10, + 12, 14, 16, 18, 20, +}; + +static encode_aux_threshmatch _vq_auxt__44u9_p8_1 = { + _vq_quantthresh__44u9_p8_1, + _vq_quantmap__44u9_p8_1, + 21, + 21 +}; + +static static_codebook _44u9_p8_1 = { + 2, 441, + _vq_lengthlist__44u9_p8_1, + 1, -529268736, 1611661312, 5, 0, + _vq_quantlist__44u9_p8_1, + NULL, + &_vq_auxt__44u9_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u9_p9_0[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__44u9_p9_0[] = { + 1, 3, 3,11,11,11,11,11,11,11,11,11,11,11,11, 4, + 10,11,11,11,11,11,11,11,11,11,11,11,11,11, 4,10, + 10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10, +}; + +static float _vq_quantthresh__44u9_p9_0[] = { + -6051.5, -5120.5, -4189.5, -3258.5, -2327.5, -1396.5, -465.5, 465.5, + 1396.5, 2327.5, 3258.5, 4189.5, 5120.5, 6051.5, +}; + +static long _vq_quantmap__44u9_p9_0[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__44u9_p9_0 = { + _vq_quantthresh__44u9_p9_0, + _vq_quantmap__44u9_p9_0, + 15, + 15 +}; + +static static_codebook _44u9_p9_0 = { + 2, 225, + _vq_lengthlist__44u9_p9_0, + 1, -510036736, 1631393792, 4, 0, + _vq_quantlist__44u9_p9_0, + NULL, + &_vq_auxt__44u9_p9_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u9_p9_1[] = { + 9, + 8, + 10, + 7, + 11, + 6, + 12, + 5, + 13, + 4, + 14, + 3, + 15, + 2, + 16, + 1, + 17, + 0, + 18, +}; + +static long _vq_lengthlist__44u9_p9_1[] = { + 1, 4, 4, 7, 7, 8, 7, 8, 7, 9, 8,10, 9,10,10,11, + 11,12,12, 4, 7, 6, 9, 9,10, 9, 9, 8,10,10,11,10, + 12,10,13,12,13,12, 4, 6, 6, 9, 9, 9, 9, 9, 9,10, + 10,11,11,11,12,12,12,12,12, 7, 9, 8,11,10,10,10, + 11,10,11,11,12,12,13,12,13,13,13,13, 7, 8, 9,10, + 10,11,11,10,10,11,11,11,12,13,13,13,13,14,14, 8, + 9, 9,11,11,12,11,12,12,13,12,12,13,13,14,15,14, + 14,14, 8, 9, 9,10,11,11,11,12,12,13,12,13,13,14, + 14,14,15,14,16, 8, 9, 9,11,10,12,12,12,12,15,13, + 13,13,17,14,15,15,15,14, 8, 9, 9,10,11,11,12,13, + 12,13,13,13,14,15,14,14,14,16,15, 9,11,10,12,12, + 13,13,13,13,14,14,16,15,14,14,14,15,15,17, 9,10, + 10,11,11,13,13,13,14,14,13,15,14,15,14,15,16,15, + 16,10,11,11,12,12,13,14,15,14,15,14,14,15,17,16, + 15,15,17,17,10,12,11,13,12,14,14,13,14,15,15,15, + 15,16,17,17,15,17,16,11,12,12,14,13,15,14,15,16, + 17,15,17,15,17,15,15,16,17,15,11,11,12,14,14,14, + 14,14,15,15,16,15,17,17,17,16,17,16,15,12,12,13, + 14,14,14,15,14,15,15,16,16,17,16,17,15,17,17,16, + 12,14,12,14,14,15,15,15,14,14,16,16,16,15,16,16, + 15,17,15,12,13,13,14,15,14,15,17,15,17,16,17,17, + 17,16,17,16,17,17,12,13,13,14,16,15,15,15,16,15, + 17,17,15,17,15,17,16,16,17, +}; + +static float _vq_quantthresh__44u9_p9_1[] = { + -416.5, -367.5, -318.5, -269.5, -220.5, -171.5, -122.5, -73.5, + -24.5, 24.5, 73.5, 122.5, 171.5, 220.5, 269.5, 318.5, + 367.5, 416.5, +}; + +static long _vq_quantmap__44u9_p9_1[] = { + 17, 15, 13, 11, 9, 7, 5, 3, + 1, 0, 2, 4, 6, 8, 10, 12, + 14, 16, 18, +}; + +static encode_aux_threshmatch _vq_auxt__44u9_p9_1 = { + _vq_quantthresh__44u9_p9_1, + _vq_quantmap__44u9_p9_1, + 19, + 19 +}; + +static static_codebook _44u9_p9_1 = { + 2, 361, + _vq_lengthlist__44u9_p9_1, + 1, -518287360, 1622704128, 5, 0, + _vq_quantlist__44u9_p9_1, + NULL, + &_vq_auxt__44u9_p9_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u9_p9_2[] = { + 24, + 23, + 25, + 22, + 26, + 21, + 27, + 20, + 28, + 19, + 29, + 18, + 30, + 17, + 31, + 16, + 32, + 15, + 33, + 14, + 34, + 13, + 35, + 12, + 36, + 11, + 37, + 10, + 38, + 9, + 39, + 8, + 40, + 7, + 41, + 6, + 42, + 5, + 43, + 4, + 44, + 3, + 45, + 2, + 46, + 1, + 47, + 0, + 48, +}; + +static long _vq_lengthlist__44u9_p9_2[] = { + 2, 4, 4, 5, 4, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 7, 6, 7, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, +}; + +static float _vq_quantthresh__44u9_p9_2[] = { + -23.5, -22.5, -21.5, -20.5, -19.5, -18.5, -17.5, -16.5, + -15.5, -14.5, -13.5, -12.5, -11.5, -10.5, -9.5, -8.5, + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, + 8.5, 9.5, 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, + 16.5, 17.5, 18.5, 19.5, 20.5, 21.5, 22.5, 23.5, +}; + +static long _vq_quantmap__44u9_p9_2[] = { + 47, 45, 43, 41, 39, 37, 35, 33, + 31, 29, 27, 25, 23, 21, 19, 17, + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, + 48, +}; + +static encode_aux_threshmatch _vq_auxt__44u9_p9_2 = { + _vq_quantthresh__44u9_p9_2, + _vq_quantmap__44u9_p9_2, + 49, + 49 +}; + +static static_codebook _44u9_p9_2 = { + 1, 49, + _vq_lengthlist__44u9_p9_2, + 1, -526909440, 1611661312, 6, 0, + _vq_quantlist__44u9_p9_2, + NULL, + &_vq_auxt__44u9_p9_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44un1__long[] = { + 5, 6,12, 9,14, 9, 9,19, 6, 1, 5, 5, 8, 7, 9,19, + 12, 4, 4, 7, 7, 9,11,18, 9, 5, 6, 6, 8, 7, 8,17, + 14, 8, 7, 8, 8,10,12,18, 9, 6, 8, 6, 8, 6, 8,18, + 9, 8,11, 8,11, 7, 5,15,16,18,18,18,17,15,11,18, +}; + +static static_codebook _huff_book__44un1__long = { + 2, 64, + _huff_lengthlist__44un1__long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44un1__p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44un1__p1_0[] = { + 1, 4, 4, 5, 8, 7, 5, 7, 8, 5, 8, 8, 8,10,11, 8, + 10,11, 5, 8, 8, 8,11,10, 8,11,10, 4, 9, 9, 8,11, + 11, 8,11,11, 8,12,11,10,12,14,11,13,13, 7,11,11, + 10,13,11,11,13,14, 4, 8, 9, 8,11,11, 8,11,12, 7, + 11,11,11,14,13,10,11,13, 8,11,12,11,13,13,10,14, + 12, +}; + +static float _vq_quantthresh__44un1__p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44un1__p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44un1__p1_0 = { + _vq_quantthresh__44un1__p1_0, + _vq_quantmap__44un1__p1_0, + 3, + 3 +}; + +static static_codebook _44un1__p1_0 = { + 4, 81, + _vq_lengthlist__44un1__p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44un1__p1_0, + NULL, + &_vq_auxt__44un1__p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44un1__p2_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44un1__p2_0[] = { + 2, 4, 4, 5, 6, 6, 5, 6, 6, 5, 7, 7, 7, 8, 8, 6, + 7, 9, 5, 7, 7, 6, 8, 7, 7, 9, 8, 4, 7, 7, 7, 9, + 8, 7, 8, 8, 7, 9, 8, 8, 8,10, 9,10,10, 6, 8, 8, + 7,10, 8, 9,10,10, 5, 7, 7, 7, 8, 8, 7, 8, 9, 6, + 8, 8, 9,10,10, 7, 8,10, 6, 8, 9, 9,10,10, 8,10, + 8, +}; + +static float _vq_quantthresh__44un1__p2_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44un1__p2_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44un1__p2_0 = { + _vq_quantthresh__44un1__p2_0, + _vq_quantmap__44un1__p2_0, + 3, + 3 +}; + +static static_codebook _44un1__p2_0 = { + 4, 81, + _vq_lengthlist__44un1__p2_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44un1__p2_0, + NULL, + &_vq_auxt__44un1__p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44un1__p3_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44un1__p3_0[] = { + 1, 5, 5, 8, 8, 5, 8, 7, 9, 9, 5, 7, 8, 9, 9, 9, + 10, 9,12,12, 9, 9,10,11,12, 6, 8, 8,10,10, 8,10, + 10,11,11, 8, 9,10,11,11,10,11,11,13,13,10,11,11, + 12,13, 6, 8, 8,10,10, 8,10, 9,11,11, 8,10,10,11, + 11,10,11,11,13,12,10,11,11,13,12, 9,11,11,15,13, + 10,12,11,15,13,10,11,11,15,14,12,14,13,16,15,12, + 13,13,17,16, 9,11,11,13,15,10,11,12,14,15,10,11, + 12,14,15,12,13,13,15,16,12,13,13,16,16, 5, 8, 8, + 11,11, 8,10,10,12,12, 8,10,10,12,12,11,12,12,14, + 14,11,12,12,14,14, 8,11,10,13,12,10,11,12,12,13, + 10,12,12,13,13,12,12,13,13,15,11,12,13,15,14, 7, + 10,10,12,12, 9,12,11,13,12,10,12,12,13,14,12,13, + 12,15,13,11,13,12,14,15,10,12,12,16,14,11,12,12, + 16,15,11,13,12,17,16,13,13,15,15,17,13,15,15,20, + 17,10,12,12,14,16,11,12,12,15,15,11,13,13,15,18, + 13,14,13,15,15,13,15,14,16,16, 5, 8, 8,11,11, 8, + 10,10,12,12, 8,10,10,12,12,11,12,12,14,14,11,12, + 12,14,15, 7,10,10,13,12,10,12,12,14,13, 9,10,12, + 12,13,11,13,13,15,15,11,12,13,13,15, 8,10,10,12, + 13,10,12,12,13,13,10,12,11,13,13,11,13,12,15,15, + 12,13,12,15,13,10,12,12,16,14,11,12,12,16,15,10, + 12,12,16,14,14,15,14,18,16,13,13,14,15,16,10,12, + 12,14,16,11,13,13,16,16,11,13,12,14,16,13,15,15, + 18,18,13,15,13,16,14, 8,11,11,16,16,10,13,13,17, + 16,10,12,12,16,15,14,16,15,20,17,13,14,14,17,17, + 9,12,12,16,16,11,13,14,16,17,11,13,13,16,16,15, + 15,19,18, 0,14,15,15,18,18, 9,12,12,17,16,11,13, + 12,17,16,11,12,13,15,17,15,16,15, 0,19,14,15,14, + 19,18,12,14,14, 0,16,13,14,14,19,18,13,15,16,17, + 16,15,15,17,18, 0,14,16,16,19, 0,12,14,14,16,18, + 13,15,13,17,18,13,15,14,17,18,15,18,14,18,18,16, + 17,16, 0,17, 8,11,11,15,15,10,12,12,16,16,10,13, + 13,16,16,13,15,14,17,17,14,15,17,17,18, 9,12,12, + 16,15,11,13,13,16,16,11,12,13,17,17,14,14,15,17, + 17,14,15,16, 0,18, 9,12,12,16,17,11,13,13,16,17, + 11,14,13,18,17,14,16,14,17,17,15,17,17,18,18,12, + 14,14, 0,16,13,15,15,19, 0,12,13,15, 0, 0,14,17, + 16,19, 0,16,15,18,18, 0,12,14,14,17, 0,13,14,14, + 17, 0,13,15,14, 0,18,15,16,16, 0,18,15,18,15, 0, + 17, +}; + +static float _vq_quantthresh__44un1__p3_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44un1__p3_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44un1__p3_0 = { + _vq_quantthresh__44un1__p3_0, + _vq_quantmap__44un1__p3_0, + 5, + 5 +}; + +static static_codebook _44un1__p3_0 = { + 4, 625, + _vq_lengthlist__44un1__p3_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44un1__p3_0, + NULL, + &_vq_auxt__44un1__p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44un1__p4_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44un1__p4_0[] = { + 3, 5, 5, 9, 9, 5, 6, 6,10, 9, 5, 6, 6, 9,10,10, + 10,10,12,11, 9,10,10,12,12, 5, 7, 7,10,10, 7, 7, + 8,10,11, 7, 7, 8,10,11,10,10,11,11,13,10,10,11, + 11,13, 6, 7, 7,10,10, 7, 8, 7,11,10, 7, 8, 7,10, + 10,10,11, 9,13,11,10,11,10,13,11,10,10,10,14,13, + 10,11,11,14,13,10,10,11,13,14,12,12,13,15,15,12, + 12,13,13,14,10,10,10,12,13,10,11,10,13,13,10,11, + 11,13,13,12,13,12,14,13,12,13,13,14,13, 5, 7, 7, + 10,10, 7, 8, 8,11,10, 7, 8, 8,10,10,11,11,11,13, + 13,10,11,11,12,12, 7, 8, 8,11,11, 7, 8, 9,10,12, + 8, 9, 9,11,11,11,10,12,11,14,11,11,12,13,13, 6, + 8, 8,10,11, 7, 9, 7,12,10, 8, 9,10,11,12,10,12, + 10,14,11,11,12,11,13,13,10,11,11,14,14,10,10,11, + 13,14,11,12,12,15,13,12,11,14,12,16,12,13,14,15, + 16,10,10,11,13,14,10,11,10,14,12,11,12,12,13,14, + 12,13,11,15,12,14,14,14,15,15, 5, 7, 7,10,10, 7, + 8, 8,10,10, 7, 8, 8,10,11,10,11,10,12,12,10,11, + 11,12,13, 6, 8, 8,11,11, 8, 9, 9,12,11, 7, 7, 9, + 10,12,11,11,11,12,13,11,10,12,11,15, 7, 8, 8,11, + 11, 8, 9, 9,11,11, 7, 9, 8,12,10,11,12,11,13,12, + 11,12,10,15,11,10,11,10,14,12,11,12,11,14,13,10, + 10,11,13,14,13,13,13,17,15,12,11,14,12,15,10,10, + 11,13,14,11,12,12,14,14,10,11,10,14,13,13,14,13, + 16,17,12,14,11,16,12, 9,10,10,14,13,10,11,10,14, + 14,10,11,11,13,13,13,14,14,16,15,12,13,13,14,14, + 9,11,10,14,13,10,10,12,13,14,11,12,11,14,13,13, + 14,14,14,15,13,14,14,15,15, 9,10,11,13,14,10,11, + 10,15,13,11,11,12,12,15,13,14,12,15,14,13,13,14, + 14,15,12,13,12,16,14,11,11,12,15,14,13,15,13,16, + 14,13,12,15,12,17,15,16,15,16,16,12,12,13,13,15, + 11,13,11,15,14,13,13,14,15,17,13,14,12, 0,13,14, + 15,14,15, 0, 9,10,10,13,13,10,11,11,13,13,10,11, + 11,13,13,12,13,12,14,14,13,14,14,15,17, 9,10,10, + 13,13,11,12,11,15,12,10,10,11,13,16,13,14,13,15, + 14,13,13,14,15,16,10,10,11,13,14,11,11,12,13,14, + 10,12,11,14,14,13,13,13,14,15,13,15,13,16,15,12, + 13,12,15,13,12,15,13,15,15,11,11,13,14,15,15,15, + 15,15,17,13,12,14,13,17,12,12,14,14,15,13,13,14, + 14,16,11,13,11,16,15,14,16,16,17, 0,14,13,11,16, + 12, +}; + +static float _vq_quantthresh__44un1__p4_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44un1__p4_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44un1__p4_0 = { + _vq_quantthresh__44un1__p4_0, + _vq_quantmap__44un1__p4_0, + 5, + 5 +}; + +static static_codebook _44un1__p4_0 = { + 4, 625, + _vq_lengthlist__44un1__p4_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44un1__p4_0, + NULL, + &_vq_auxt__44un1__p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44un1__p5_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44un1__p5_0[] = { + 1, 4, 4, 7, 7, 8, 8, 9, 9, 4, 6, 5, 8, 7, 8, 8, + 10, 9, 4, 6, 6, 8, 8, 8, 8,10,10, 7, 8, 7, 9, 9, + 9, 9,11,10, 7, 8, 8, 9, 9, 9, 9,10,11, 8, 8, 8, + 9, 9,10,10,11,11, 8, 8, 8, 9, 9,10,10,11,11, 9, + 10,10,11,10,11,11,12,12, 9,10,10,10,11,11,11,12, + 12, +}; + +static float _vq_quantthresh__44un1__p5_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44un1__p5_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44un1__p5_0 = { + _vq_quantthresh__44un1__p5_0, + _vq_quantmap__44un1__p5_0, + 9, + 9 +}; + +static static_codebook _44un1__p5_0 = { + 2, 81, + _vq_lengthlist__44un1__p5_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44un1__p5_0, + NULL, + &_vq_auxt__44un1__p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44un1__p6_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44un1__p6_0[] = { + 1, 4, 4, 6, 6, 8, 8,10,10,11,11,15,15, 4, 5, 5, + 8, 8, 9, 9,11,11,12,12,16,16, 4, 5, 6, 8, 8, 9, + 9,11,11,12,12,14,14, 7, 8, 8, 9, 9,10,10,11,12, + 13,13,16,17, 7, 8, 8, 9, 9,10,10,12,12,12,13,15, + 15, 9,10,10,10,10,11,11,12,12,13,13,15,16, 9, 9, + 9,10,10,11,11,13,12,13,13,17,17,10,11,11,11,12, + 12,12,13,13,14,15, 0,18,10,11,11,12,12,12,13,14, + 13,14,14,17,16,11,12,12,13,13,14,14,14,14,15,16, + 17,16,11,12,12,13,13,14,14,14,14,15,15,17,17,14, + 15,15,16,16,16,17,17,16, 0,17, 0,18,14,15,15,16, + 16, 0,15,18,18, 0,16, 0, 0, +}; + +static float _vq_quantthresh__44un1__p6_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44un1__p6_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44un1__p6_0 = { + _vq_quantthresh__44un1__p6_0, + _vq_quantmap__44un1__p6_0, + 13, + 13 +}; + +static static_codebook _44un1__p6_0 = { + 2, 169, + _vq_lengthlist__44un1__p6_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44un1__p6_0, + NULL, + &_vq_auxt__44un1__p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44un1__p6_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44un1__p6_1[] = { + 2, 4, 4, 5, 5, 4, 5, 5, 5, 5, 4, 5, 5, 6, 5, 5, + 6, 5, 6, 6, 5, 6, 6, 6, 6, +}; + +static float _vq_quantthresh__44un1__p6_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44un1__p6_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44un1__p6_1 = { + _vq_quantthresh__44un1__p6_1, + _vq_quantmap__44un1__p6_1, + 5, + 5 +}; + +static static_codebook _44un1__p6_1 = { + 2, 25, + _vq_lengthlist__44un1__p6_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44un1__p6_1, + NULL, + &_vq_auxt__44un1__p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__44un1__p7_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44un1__p7_0[] = { + 1, 5, 3,11,11,11,11,11,11,11, 8,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,10,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,10,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11, 8,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,10, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11, 7,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,10,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10, +}; + +static float _vq_quantthresh__44un1__p7_0[] = { + -253.5, -84.5, 84.5, 253.5, +}; + +static long _vq_quantmap__44un1__p7_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44un1__p7_0 = { + _vq_quantthresh__44un1__p7_0, + _vq_quantmap__44un1__p7_0, + 5, + 5 +}; + +static static_codebook _44un1__p7_0 = { + 4, 625, + _vq_lengthlist__44un1__p7_0, + 1, -518709248, 1626677248, 3, 0, + _vq_quantlist__44un1__p7_0, + NULL, + &_vq_auxt__44un1__p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44un1__p7_1[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44un1__p7_1[] = { + 1, 4, 4, 6, 6, 6, 6, 9, 8, 9, 8, 8, 8, 5, 7, 7, + 7, 7, 8, 8, 8,10, 8,10, 8, 9, 5, 7, 7, 8, 7, 7, + 8,10,10,11,10,12,11, 7, 8, 8, 9, 9, 9,10,11,11, + 11,11,11,11, 7, 8, 8, 8, 9, 9, 9,10,10,10,11,11, + 12, 7, 8, 8, 9, 9,10,11,11,12,11,12,11,11, 7, 8, + 8, 9, 9,10,10,11,11,11,12,12,11, 8,10,10,10,10, + 11,11,14,11,12,12,12,13, 9,10,10,10,10,12,11,14, + 11,14,11,12,13,10,11,11,11,11,13,11,14,14,13,13, + 13,14,11,11,11,12,11,12,12,12,13,14,14,13,14,12, + 11,12,12,12,12,13,13,13,14,13,14,14,11,12,12,14, + 12,13,13,12,13,13,14,14,14, +}; + +static float _vq_quantthresh__44un1__p7_1[] = { + -71.5, -58.5, -45.5, -32.5, -19.5, -6.5, 6.5, 19.5, + 32.5, 45.5, 58.5, 71.5, +}; + +static long _vq_quantmap__44un1__p7_1[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44un1__p7_1 = { + _vq_quantthresh__44un1__p7_1, + _vq_quantmap__44un1__p7_1, + 13, + 13 +}; + +static static_codebook _44un1__p7_1 = { + 2, 169, + _vq_lengthlist__44un1__p7_1, + 1, -523010048, 1618608128, 4, 0, + _vq_quantlist__44un1__p7_1, + NULL, + &_vq_auxt__44un1__p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44un1__p7_2[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44un1__p7_2[] = { + 3, 4, 4, 6, 6, 7, 7, 8, 8, 9, 9, 9, 8, 4, 5, 5, + 6, 6, 8, 8, 9, 8, 9, 9, 9, 9, 4, 5, 5, 7, 6, 8, + 8, 8, 8, 9, 8, 9, 8, 6, 7, 7, 7, 8, 8, 8, 9, 9, + 9, 9, 9, 9, 6, 7, 7, 7, 7, 8, 8, 9, 9, 9, 9, 9, + 9, 7, 8, 8, 8, 8, 9, 8, 9, 9,10, 9, 9,10, 7, 8, + 8, 8, 8, 9, 9, 9, 9, 9, 9,10,10, 8, 9, 9, 9, 9, + 9, 9, 9, 9,10,10, 9,10, 8, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9,10,10, 9, 9, 9,10, 9, 9,10, 9, 9,10,10, + 10,10, 9, 9, 9, 9, 9, 9, 9,10, 9,10,10,10,10, 9, + 9, 9,10, 9, 9,10,10, 9,10,10,10,10, 9, 9, 9,10, + 9, 9, 9,10,10,10,10,10,10, +}; + +static float _vq_quantthresh__44un1__p7_2[] = { + -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, + 2.5, 3.5, 4.5, 5.5, +}; + +static long _vq_quantmap__44un1__p7_2[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44un1__p7_2 = { + _vq_quantthresh__44un1__p7_2, + _vq_quantmap__44un1__p7_2, + 13, + 13 +}; + +static static_codebook _44un1__p7_2 = { + 2, 169, + _vq_lengthlist__44un1__p7_2, + 1, -531103744, 1611661312, 4, 0, + _vq_quantlist__44un1__p7_2, + NULL, + &_vq_auxt__44un1__p7_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44un1__short[] = { + 12,12,14,12,14,14,14,14,12, 6, 6, 8, 9, 9,11,14, + 12, 4, 2, 6, 6, 7,11,14,13, 6, 5, 7, 8, 9,11,14, + 13, 8, 5, 8, 6, 8,12,14,12, 7, 7, 8, 8, 8,10,14, + 12, 6, 3, 4, 4, 4, 7,14,11, 7, 4, 6, 6, 6, 8,14, +}; + +static static_codebook _huff_book__44un1__short = { + 2, 64, + _huff_lengthlist__44un1__short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; +/********* End of inlined file: res_books_uncoupled.h *********/ + +/***** residue backends *********************************************/ + +static vorbis_info_residue0 _residue_44_low_un={ + 0,-1, -1, 8,-1, + {0}, + {-1}, + { .5, 1.5, 1.5, 2.5, 2.5, 4.5, 28.5}, + { -1, 25, -1, 45, -1, -1, -1} +}; + +static vorbis_info_residue0 _residue_44_mid_un={ + 0,-1, -1, 10,-1, + /* 0 1 2 3 4 5 6 7 8 9 */ + {0}, + {-1}, + { .5, 1.5, 1.5, 2.5, 2.5, 4.5, 4.5, 16.5, 60.5}, + { -1, 30, -1, 50, -1, 80, -1, -1, -1} +}; + +static vorbis_info_residue0 _residue_44_hi_un={ + 0,-1, -1, 10,-1, + /* 0 1 2 3 4 5 6 7 8 9 */ + {0}, + {-1}, + { .5, 1.5, 2.5, 4.5, 8.5, 16.5, 32.5, 71.5,157.5}, + { -1, -1, -1, -1, -1, -1, -1, -1, -1} +}; + +/* mapping conventions: + only one submap (this would change for efficient 5.1 support for example)*/ +/* Four psychoacoustic profiles are used, one for each blocktype */ +static vorbis_info_mapping0 _map_nominal_u[2]={ + {1, {0,0}, {0}, {0}, 0,{0},{0}}, + {1, {0,0}, {1}, {1}, 0,{0},{0}} +}; + +static static_bookblock _resbook_44u_n1={ + { + {0}, + {0,0,&_44un1__p1_0}, + {0,0,&_44un1__p2_0}, + {0,0,&_44un1__p3_0}, + {0,0,&_44un1__p4_0}, + {0,0,&_44un1__p5_0}, + {&_44un1__p6_0,&_44un1__p6_1}, + {&_44un1__p7_0,&_44un1__p7_1,&_44un1__p7_2} + } +}; +static static_bookblock _resbook_44u_0={ + { + {0}, + {0,0,&_44u0__p1_0}, + {0,0,&_44u0__p2_0}, + {0,0,&_44u0__p3_0}, + {0,0,&_44u0__p4_0}, + {0,0,&_44u0__p5_0}, + {&_44u0__p6_0,&_44u0__p6_1}, + {&_44u0__p7_0,&_44u0__p7_1,&_44u0__p7_2} + } +}; +static static_bookblock _resbook_44u_1={ + { + {0}, + {0,0,&_44u1__p1_0}, + {0,0,&_44u1__p2_0}, + {0,0,&_44u1__p3_0}, + {0,0,&_44u1__p4_0}, + {0,0,&_44u1__p5_0}, + {&_44u1__p6_0,&_44u1__p6_1}, + {&_44u1__p7_0,&_44u1__p7_1,&_44u1__p7_2} + } +}; +static static_bookblock _resbook_44u_2={ + { + {0}, + {0,0,&_44u2__p1_0}, + {0,0,&_44u2__p2_0}, + {0,0,&_44u2__p3_0}, + {0,0,&_44u2__p4_0}, + {0,0,&_44u2__p5_0}, + {&_44u2__p6_0,&_44u2__p6_1}, + {&_44u2__p7_0,&_44u2__p7_1,&_44u2__p7_2} + } +}; +static static_bookblock _resbook_44u_3={ + { + {0}, + {0,0,&_44u3__p1_0}, + {0,0,&_44u3__p2_0}, + {0,0,&_44u3__p3_0}, + {0,0,&_44u3__p4_0}, + {0,0,&_44u3__p5_0}, + {&_44u3__p6_0,&_44u3__p6_1}, + {&_44u3__p7_0,&_44u3__p7_1,&_44u3__p7_2} + } +}; +static static_bookblock _resbook_44u_4={ + { + {0}, + {0,0,&_44u4__p1_0}, + {0,0,&_44u4__p2_0}, + {0,0,&_44u4__p3_0}, + {0,0,&_44u4__p4_0}, + {0,0,&_44u4__p5_0}, + {&_44u4__p6_0,&_44u4__p6_1}, + {&_44u4__p7_0,&_44u4__p7_1,&_44u4__p7_2} + } +}; +static static_bookblock _resbook_44u_5={ + { + {0}, + {0,0,&_44u5__p1_0}, + {0,0,&_44u5__p2_0}, + {0,0,&_44u5__p3_0}, + {0,0,&_44u5__p4_0}, + {0,0,&_44u5__p5_0}, + {0,0,&_44u5__p6_0}, + {&_44u5__p7_0,&_44u5__p7_1}, + {&_44u5__p8_0,&_44u5__p8_1}, + {&_44u5__p9_0,&_44u5__p9_1,&_44u5__p9_2} + } +}; +static static_bookblock _resbook_44u_6={ + { + {0}, + {0,0,&_44u6__p1_0}, + {0,0,&_44u6__p2_0}, + {0,0,&_44u6__p3_0}, + {0,0,&_44u6__p4_0}, + {0,0,&_44u6__p5_0}, + {0,0,&_44u6__p6_0}, + {&_44u6__p7_0,&_44u6__p7_1}, + {&_44u6__p8_0,&_44u6__p8_1}, + {&_44u6__p9_0,&_44u6__p9_1,&_44u6__p9_2} + } +}; +static static_bookblock _resbook_44u_7={ + { + {0}, + {0,0,&_44u7__p1_0}, + {0,0,&_44u7__p2_0}, + {0,0,&_44u7__p3_0}, + {0,0,&_44u7__p4_0}, + {0,0,&_44u7__p5_0}, + {0,0,&_44u7__p6_0}, + {&_44u7__p7_0,&_44u7__p7_1}, + {&_44u7__p8_0,&_44u7__p8_1}, + {&_44u7__p9_0,&_44u7__p9_1,&_44u7__p9_2} + } +}; +static static_bookblock _resbook_44u_8={ + { + {0}, + {0,0,&_44u8_p1_0}, + {0,0,&_44u8_p2_0}, + {0,0,&_44u8_p3_0}, + {0,0,&_44u8_p4_0}, + {&_44u8_p5_0,&_44u8_p5_1}, + {&_44u8_p6_0,&_44u8_p6_1}, + {&_44u8_p7_0,&_44u8_p7_1}, + {&_44u8_p8_0,&_44u8_p8_1}, + {&_44u8_p9_0,&_44u8_p9_1,&_44u8_p9_2} + } +}; +static static_bookblock _resbook_44u_9={ + { + {0}, + {0,0,&_44u9_p1_0}, + {0,0,&_44u9_p2_0}, + {0,0,&_44u9_p3_0}, + {0,0,&_44u9_p4_0}, + {&_44u9_p5_0,&_44u9_p5_1}, + {&_44u9_p6_0,&_44u9_p6_1}, + {&_44u9_p7_0,&_44u9_p7_1}, + {&_44u9_p8_0,&_44u9_p8_1}, + {&_44u9_p9_0,&_44u9_p9_1,&_44u9_p9_2} + } +}; + +static vorbis_residue_template _res_44u_n1[]={ + {1,0, &_residue_44_low_un, + &_huff_book__44un1__short,&_huff_book__44un1__short, + &_resbook_44u_n1,&_resbook_44u_n1}, + + {1,0, &_residue_44_low_un, + &_huff_book__44un1__long,&_huff_book__44un1__long, + &_resbook_44u_n1,&_resbook_44u_n1} +}; +static vorbis_residue_template _res_44u_0[]={ + {1,0, &_residue_44_low_un, + &_huff_book__44u0__short,&_huff_book__44u0__short, + &_resbook_44u_0,&_resbook_44u_0}, + + {1,0, &_residue_44_low_un, + &_huff_book__44u0__long,&_huff_book__44u0__long, + &_resbook_44u_0,&_resbook_44u_0} +}; +static vorbis_residue_template _res_44u_1[]={ + {1,0, &_residue_44_low_un, + &_huff_book__44u1__short,&_huff_book__44u1__short, + &_resbook_44u_1,&_resbook_44u_1}, + + {1,0, &_residue_44_low_un, + &_huff_book__44u1__long,&_huff_book__44u1__long, + &_resbook_44u_1,&_resbook_44u_1} +}; +static vorbis_residue_template _res_44u_2[]={ + {1,0, &_residue_44_low_un, + &_huff_book__44u2__short,&_huff_book__44u2__short, + &_resbook_44u_2,&_resbook_44u_2}, + + {1,0, &_residue_44_low_un, + &_huff_book__44u2__long,&_huff_book__44u2__long, + &_resbook_44u_2,&_resbook_44u_2} +}; +static vorbis_residue_template _res_44u_3[]={ + {1,0, &_residue_44_low_un, + &_huff_book__44u3__short,&_huff_book__44u3__short, + &_resbook_44u_3,&_resbook_44u_3}, + + {1,0, &_residue_44_low_un, + &_huff_book__44u3__long,&_huff_book__44u3__long, + &_resbook_44u_3,&_resbook_44u_3} +}; +static vorbis_residue_template _res_44u_4[]={ + {1,0, &_residue_44_low_un, + &_huff_book__44u4__short,&_huff_book__44u4__short, + &_resbook_44u_4,&_resbook_44u_4}, + + {1,0, &_residue_44_low_un, + &_huff_book__44u4__long,&_huff_book__44u4__long, + &_resbook_44u_4,&_resbook_44u_4} +}; + +static vorbis_residue_template _res_44u_5[]={ + {1,0, &_residue_44_mid_un, + &_huff_book__44u5__short,&_huff_book__44u5__short, + &_resbook_44u_5,&_resbook_44u_5}, + + {1,0, &_residue_44_mid_un, + &_huff_book__44u5__long,&_huff_book__44u5__long, + &_resbook_44u_5,&_resbook_44u_5} +}; + +static vorbis_residue_template _res_44u_6[]={ + {1,0, &_residue_44_mid_un, + &_huff_book__44u6__short,&_huff_book__44u6__short, + &_resbook_44u_6,&_resbook_44u_6}, + + {1,0, &_residue_44_mid_un, + &_huff_book__44u6__long,&_huff_book__44u6__long, + &_resbook_44u_6,&_resbook_44u_6} +}; + +static vorbis_residue_template _res_44u_7[]={ + {1,0, &_residue_44_mid_un, + &_huff_book__44u7__short,&_huff_book__44u7__short, + &_resbook_44u_7,&_resbook_44u_7}, + + {1,0, &_residue_44_mid_un, + &_huff_book__44u7__long,&_huff_book__44u7__long, + &_resbook_44u_7,&_resbook_44u_7} +}; + +static vorbis_residue_template _res_44u_8[]={ + {1,0, &_residue_44_hi_un, + &_huff_book__44u8__short,&_huff_book__44u8__short, + &_resbook_44u_8,&_resbook_44u_8}, + + {1,0, &_residue_44_hi_un, + &_huff_book__44u8__long,&_huff_book__44u8__long, + &_resbook_44u_8,&_resbook_44u_8} +}; +static vorbis_residue_template _res_44u_9[]={ + {1,0, &_residue_44_hi_un, + &_huff_book__44u9__short,&_huff_book__44u9__short, + &_resbook_44u_9,&_resbook_44u_9}, + + {1,0, &_residue_44_hi_un, + &_huff_book__44u9__long,&_huff_book__44u9__long, + &_resbook_44u_9,&_resbook_44u_9} +}; + +static vorbis_mapping_template _mapres_template_44_uncoupled[]={ + { _map_nominal_u, _res_44u_n1 }, /* -1 */ + { _map_nominal_u, _res_44u_0 }, /* 0 */ + { _map_nominal_u, _res_44u_1 }, /* 1 */ + { _map_nominal_u, _res_44u_2 }, /* 2 */ + { _map_nominal_u, _res_44u_3 }, /* 3 */ + { _map_nominal_u, _res_44u_4 }, /* 4 */ + { _map_nominal_u, _res_44u_5 }, /* 5 */ + { _map_nominal_u, _res_44u_6 }, /* 6 */ + { _map_nominal_u, _res_44u_7 }, /* 7 */ + { _map_nominal_u, _res_44u_8 }, /* 8 */ + { _map_nominal_u, _res_44u_9 }, /* 9 */ +}; +/********* End of inlined file: residue_44u.h *********/ + +static double rate_mapping_44_un[12]={ + 32000.,48000.,60000.,70000.,80000.,86000., + 96000.,110000.,120000.,140000.,160000.,240001. +}; + +ve_setup_data_template ve_setup_44_uncoupled={ + 11, + rate_mapping_44_un, + quality_mapping_44, + -1, + 40000, + 50000, + + blocksize_short_44, + blocksize_long_44, + + _psy_tone_masteratt_44, + _psy_tone_0dB, + _psy_tone_suppress, + + _vp_tonemask_adj_otherblock, + _vp_tonemask_adj_longblock, + _vp_tonemask_adj_otherblock, + + _psy_noiseguards_44, + _psy_noisebias_impulse, + _psy_noisebias_padding, + _psy_noisebias_trans, + _psy_noisebias_long, + _psy_noise_suppress, + + _psy_compand_44, + _psy_compand_short_mapping, + _psy_compand_long_mapping, + + {_noise_start_short_44,_noise_start_long_44}, + {_noise_part_short_44,_noise_part_long_44}, + _noise_thresh_44, + + _psy_ath_floater, + _psy_ath_abs, + + _psy_lowpass_44, + + _psy_global_44, + _global_mapping_44, + NULL, + + _floor_books, + _floor, + _floor_short_mapping_44, + _floor_long_mapping_44, + + _mapres_template_44_uncoupled +}; +/********* End of inlined file: setup_44u.h *********/ + +/********* Start of inlined file: setup_32.h *********/ +static double rate_mapping_32[12]={ + 18000.,28000.,35000.,45000.,56000.,60000., + 75000.,90000.,100000.,115000.,150000.,190000., +}; + +static double rate_mapping_32_un[12]={ + 30000.,42000.,52000.,64000.,72000.,78000., + 86000.,92000.,110000.,120000.,140000.,190000., +}; + +static double _psy_lowpass_32[12]={ + 12.3,13.,13.,14.,15.,99.,99.,99.,99.,99.,99.,99. +}; + +ve_setup_data_template ve_setup_32_stereo={ + 11, + rate_mapping_32, + quality_mapping_44, + 2, + 26000, + 40000, + + blocksize_short_44, + blocksize_long_44, + + _psy_tone_masteratt_44, + _psy_tone_0dB, + _psy_tone_suppress, + + _vp_tonemask_adj_otherblock, + _vp_tonemask_adj_longblock, + _vp_tonemask_adj_otherblock, + + _psy_noiseguards_44, + _psy_noisebias_impulse, + _psy_noisebias_padding, + _psy_noisebias_trans, + _psy_noisebias_long, + _psy_noise_suppress, + + _psy_compand_44, + _psy_compand_short_mapping, + _psy_compand_long_mapping, + + {_noise_start_short_44,_noise_start_long_44}, + {_noise_part_short_44,_noise_part_long_44}, + _noise_thresh_44, + + _psy_ath_floater, + _psy_ath_abs, + + _psy_lowpass_32, + + _psy_global_44, + _global_mapping_44, + _psy_stereo_modes_44, + + _floor_books, + _floor, + _floor_short_mapping_44, + _floor_long_mapping_44, + + _mapres_template_44_stereo +}; + +ve_setup_data_template ve_setup_32_uncoupled={ + 11, + rate_mapping_32_un, + quality_mapping_44, + -1, + 26000, + 40000, + + blocksize_short_44, + blocksize_long_44, + + _psy_tone_masteratt_44, + _psy_tone_0dB, + _psy_tone_suppress, + + _vp_tonemask_adj_otherblock, + _vp_tonemask_adj_longblock, + _vp_tonemask_adj_otherblock, + + _psy_noiseguards_44, + _psy_noisebias_impulse, + _psy_noisebias_padding, + _psy_noisebias_trans, + _psy_noisebias_long, + _psy_noise_suppress, + + _psy_compand_44, + _psy_compand_short_mapping, + _psy_compand_long_mapping, + + {_noise_start_short_44,_noise_start_long_44}, + {_noise_part_short_44,_noise_part_long_44}, + _noise_thresh_44, + + _psy_ath_floater, + _psy_ath_abs, + + _psy_lowpass_32, + + _psy_global_44, + _global_mapping_44, + NULL, + + _floor_books, + _floor, + _floor_short_mapping_44, + _floor_long_mapping_44, + + _mapres_template_44_uncoupled +}; +/********* End of inlined file: setup_32.h *********/ + +/********* Start of inlined file: setup_8.h *********/ + +/********* Start of inlined file: psych_8.h *********/ +static att3 _psy_tone_masteratt_8[3]={ + {{ 32, 25, 12}, 0, 0}, /* 0 */ + {{ 30, 25, 12}, 0, 0}, /* 0 */ + {{ 20, 0, -14}, 0, 0}, /* 0 */ +}; + +static vp_adjblock _vp_tonemask_adj_8[3]={ + /* adjust for mode zero */ + /* 63 125 250 500 1 2 4 8 16 */ + {{-15,-15,-15,-15,-10,-10, -6, 0, 0, 0, 0,10, 0, 0,99,99,99}}, /* 1 */ + {{-15,-15,-15,-15,-10,-10, -6, 0, 0, 0, 0,10, 0, 0,99,99,99}}, /* 1 */ + {{-15,-15,-15,-15,-10,-10, -6, 0, 0, 0, 0, 0, 0, 0,99,99,99}}, /* 1 */ +}; + +static noise3 _psy_noisebias_8[3]={ + /* 63 125 250 500 1k 2k 4k 8k 16k*/ + {{{-10,-10,-10,-10, -5, -5, -5, 0, 4, 8, 8, 8, 10, 10, 99, 99, 99}, + {-10,-10,-10,-10, -5, -5, -5, 0, 0, 4, 4, 4, 4, 4, 99, 99, 99}, + {-30,-30,-30,-30,-30,-24,-20,-14,-10, -6, -8, -8, -6, -6, 99, 99, 99}}}, + + {{{-10,-10,-10,-10, -5, -5, -5, 0, 4, 8, 8, 8, 10, 10, 99, 99, 99}, + {-10,-10,-10,-10,-10,-10, -5, -5, -5, 0, 0, 0, 0, 0, 99, 99, 99}, + {-30,-30,-30,-30,-30,-24,-20,-14,-10, -6, -8, -8, -6, -6, 99, 99, 99}}}, + + {{{-15,-15,-15,-15,-15,-12,-10, -8, 0, 2, 4, 4, 5, 5, 99, 99, 99}, + {-30,-30,-30,-30,-26,-22,-20,-14,-12,-12,-10,-10,-10,-10, 99, 99, 99}, + {-30,-30,-30,-30,-26,-26,-26,-26,-26,-26,-26,-26,-26,-24, 99, 99, 99}}}, +}; + +/* stereo mode by base quality level */ +static adj_stereo _psy_stereo_modes_8[3]={ + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 */ + {{ 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}, + { 6, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}, + { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}}, + {{ 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}, + { 6, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}, + { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}}, + {{ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}, + { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}, + { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}}, +}; + +static noiseguard _psy_noiseguards_8[2]={ + {10,10,-1}, + {10,10,-1}, +}; + +static compandblock _psy_compand_8[2]={ + {{ + 0, 1, 2, 3, 4, 5, 6, 7, /* 7dB */ + 8, 8, 9, 9,10,10,11, 11, /* 15dB */ + 12,12,13,13,14,14,15, 15, /* 23dB */ + 16,16,17,17,17,18,18, 19, /* 31dB */ + 19,19,20,21,22,23,24, 25, /* 39dB */ + }}, + {{ + 0, 1, 2, 3, 4, 5, 6, 6, /* 7dB */ + 7, 7, 6, 6, 5, 5, 4, 4, /* 15dB */ + 3, 3, 3, 4, 5, 6, 7, 8, /* 23dB */ + 9,10,11,12,13,14,15, 16, /* 31dB */ + 17,18,19,20,21,22,23, 24, /* 39dB */ + }}, +}; + +static double _psy_lowpass_8[3]={3.,4.,4.}; +static int _noise_start_8[2]={ + 64,64, +}; +static int _noise_part_8[2]={ + 8,8, +}; + +static int _psy_ath_floater_8[3]={ + -100,-100,-105, +}; + +static int _psy_ath_abs_8[3]={ + -130,-130,-140, +}; +/********* End of inlined file: psych_8.h *********/ + +/********* Start of inlined file: residue_8.h *********/ + +/***** residue backends *********************************************/ + +static static_bookblock _resbook_8s_0={ + { + {0},{0,0,&_8c0_s_p1_0},{0,0,&_8c0_s_p2_0},{0,0,&_8c0_s_p3_0}, + {0,0,&_8c0_s_p4_0},{0,0,&_8c0_s_p5_0},{0,0,&_8c0_s_p6_0}, + {&_8c0_s_p7_0,&_8c0_s_p7_1},{&_8c0_s_p8_0,&_8c0_s_p8_1}, + {&_8c0_s_p9_0,&_8c0_s_p9_1,&_8c0_s_p9_2} + } +}; +static static_bookblock _resbook_8s_1={ + { + {0},{0,0,&_8c1_s_p1_0},{0,0,&_8c1_s_p2_0},{0,0,&_8c1_s_p3_0}, + {0,0,&_8c1_s_p4_0},{0,0,&_8c1_s_p5_0},{0,0,&_8c1_s_p6_0}, + {&_8c1_s_p7_0,&_8c1_s_p7_1},{&_8c1_s_p8_0,&_8c1_s_p8_1}, + {&_8c1_s_p9_0,&_8c1_s_p9_1,&_8c1_s_p9_2} + } +}; + +static vorbis_residue_template _res_8s_0[]={ + {2,0, &_residue_44_mid, + &_huff_book__8c0_s_single,&_huff_book__8c0_s_single, + &_resbook_8s_0,&_resbook_8s_0}, +}; +static vorbis_residue_template _res_8s_1[]={ + {2,0, &_residue_44_mid, + &_huff_book__8c1_s_single,&_huff_book__8c1_s_single, + &_resbook_8s_1,&_resbook_8s_1}, +}; + +static vorbis_mapping_template _mapres_template_8_stereo[2]={ + { _map_nominal, _res_8s_0 }, /* 0 */ + { _map_nominal, _res_8s_1 }, /* 1 */ +}; + +static static_bookblock _resbook_8u_0={ + { + {0}, + {0,0,&_8u0__p1_0}, + {0,0,&_8u0__p2_0}, + {0,0,&_8u0__p3_0}, + {0,0,&_8u0__p4_0}, + {0,0,&_8u0__p5_0}, + {&_8u0__p6_0,&_8u0__p6_1}, + {&_8u0__p7_0,&_8u0__p7_1,&_8u0__p7_2} + } +}; +static static_bookblock _resbook_8u_1={ + { + {0}, + {0,0,&_8u1__p1_0}, + {0,0,&_8u1__p2_0}, + {0,0,&_8u1__p3_0}, + {0,0,&_8u1__p4_0}, + {0,0,&_8u1__p5_0}, + {0,0,&_8u1__p6_0}, + {&_8u1__p7_0,&_8u1__p7_1}, + {&_8u1__p8_0,&_8u1__p8_1}, + {&_8u1__p9_0,&_8u1__p9_1,&_8u1__p9_2} + } +}; + +static vorbis_residue_template _res_8u_0[]={ + {1,0, &_residue_44_low_un, + &_huff_book__8u0__single,&_huff_book__8u0__single, + &_resbook_8u_0,&_resbook_8u_0}, +}; +static vorbis_residue_template _res_8u_1[]={ + {1,0, &_residue_44_mid_un, + &_huff_book__8u1__single,&_huff_book__8u1__single, + &_resbook_8u_1,&_resbook_8u_1}, +}; + +static vorbis_mapping_template _mapres_template_8_uncoupled[2]={ + { _map_nominal_u, _res_8u_0 }, /* 0 */ + { _map_nominal_u, _res_8u_1 }, /* 1 */ +}; +/********* End of inlined file: residue_8.h *********/ + +static int blocksize_8[2]={ + 512,512 +}; + +static int _floor_mapping_8[2]={ + 6,6, +}; + +static double rate_mapping_8[3]={ + 6000.,9000.,32000., +}; + +static double rate_mapping_8_uncoupled[3]={ + 8000.,14000.,42000., +}; + +static double quality_mapping_8[3]={ + -.1,.0,1. +}; + +static double _psy_compand_8_mapping[3]={ 0., 1., 1.}; + +static double _global_mapping_8[3]={ 1., 2., 3. }; + +ve_setup_data_template ve_setup_8_stereo={ + 2, + rate_mapping_8, + quality_mapping_8, + 2, + 8000, + 9000, + + blocksize_8, + blocksize_8, + + _psy_tone_masteratt_8, + _psy_tone_0dB, + _psy_tone_suppress, + + _vp_tonemask_adj_8, + NULL, + _vp_tonemask_adj_8, + + _psy_noiseguards_8, + _psy_noisebias_8, + _psy_noisebias_8, + NULL, + NULL, + _psy_noise_suppress, + + _psy_compand_8, + _psy_compand_8_mapping, + NULL, + + {_noise_start_8,_noise_start_8}, + {_noise_part_8,_noise_part_8}, + _noise_thresh_5only, + + _psy_ath_floater_8, + _psy_ath_abs_8, + + _psy_lowpass_8, + + _psy_global_44, + _global_mapping_8, + _psy_stereo_modes_8, + + _floor_books, + _floor, + _floor_mapping_8, + NULL, + + _mapres_template_8_stereo +}; + +ve_setup_data_template ve_setup_8_uncoupled={ + 2, + rate_mapping_8_uncoupled, + quality_mapping_8, + -1, + 8000, + 9000, + + blocksize_8, + blocksize_8, + + _psy_tone_masteratt_8, + _psy_tone_0dB, + _psy_tone_suppress, + + _vp_tonemask_adj_8, + NULL, + _vp_tonemask_adj_8, + + _psy_noiseguards_8, + _psy_noisebias_8, + _psy_noisebias_8, + NULL, + NULL, + _psy_noise_suppress, + + _psy_compand_8, + _psy_compand_8_mapping, + NULL, + + {_noise_start_8,_noise_start_8}, + {_noise_part_8,_noise_part_8}, + _noise_thresh_5only, + + _psy_ath_floater_8, + _psy_ath_abs_8, + + _psy_lowpass_8, + + _psy_global_44, + _global_mapping_8, + _psy_stereo_modes_8, + + _floor_books, + _floor, + _floor_mapping_8, + NULL, + + _mapres_template_8_uncoupled +}; +/********* End of inlined file: setup_8.h *********/ + +/********* Start of inlined file: setup_11.h *********/ + +/********* Start of inlined file: psych_11.h *********/ +static double _psy_lowpass_11[3]={4.5,5.5,30.,}; + +static att3 _psy_tone_masteratt_11[3]={ + {{ 30, 25, 12}, 0, 0}, /* 0 */ + {{ 30, 25, 12}, 0, 0}, /* 0 */ + {{ 20, 0, -14}, 0, 0}, /* 0 */ +}; + +static vp_adjblock _vp_tonemask_adj_11[3]={ + /* adjust for mode zero */ + /* 63 125 250 500 1 2 4 8 16 */ + {{-20,-20,-20,-20,-20,-16,-10, 0, 0, 0, 0,10, 2, 0,99,99,99}}, /* 0 */ + {{-20,-20,-20,-20,-20,-16,-10, 0, 0, 0, 0, 5, 0, 0,99,99,99}}, /* 1 */ + {{-20,-20,-20,-20,-20,-16,-10, 0, 0, 0, 0, 0, 0, 0,99,99,99}}, /* 2 */ +}; + +static noise3 _psy_noisebias_11[3]={ + /* 63 125 250 500 1k 2k 4k 8k 16k*/ + {{{-10,-10,-10,-10, -5, -5, -5, 0, 4, 10, 10, 12, 12, 12, 99, 99, 99}, + {-15,-15,-15,-15,-10,-10, -5, 0, 0, 4, 4, 5, 5, 10, 99, 99, 99}, + {-30,-30,-30,-30,-30,-24,-20,-14,-10, -6, -8, -8, -6, -6, 99, 99, 99}}}, + + {{{-10,-10,-10,-10, -5, -5, -5, 0, 4, 10, 10, 12, 12, 12, 99, 99, 99}, + {-15,-15,-15,-15,-10,-10, -5, -5, -5, 0, 0, 0, 0, 0, 99, 99, 99}, + {-30,-30,-30,-30,-30,-24,-20,-14,-10, -6, -8, -8, -6, -6, 99, 99, 99}}}, + + {{{-15,-15,-15,-15,-15,-12,-10, -8, 0, 2, 4, 4, 5, 5, 99, 99, 99}, + {-30,-30,-30,-30,-26,-22,-20,-14,-12,-12,-10,-10,-10,-10, 99, 99, 99}, + {-30,-30,-30,-30,-26,-26,-26,-26,-26,-26,-26,-26,-26,-24, 99, 99, 99}}}, +}; + +static double _noise_thresh_11[3]={ .3,.5,.5 }; +/********* End of inlined file: psych_11.h *********/ + +static int blocksize_11[2]={ + 512,512 +}; + +static int _floor_mapping_11[2]={ + 6,6, +}; + +static double rate_mapping_11[3]={ + 8000.,13000.,44000., +}; + +static double rate_mapping_11_uncoupled[3]={ + 12000.,20000.,50000., +}; + +static double quality_mapping_11[3]={ + -.1,.0,1. +}; + +ve_setup_data_template ve_setup_11_stereo={ + 2, + rate_mapping_11, + quality_mapping_11, + 2, + 9000, + 15000, + + blocksize_11, + blocksize_11, + + _psy_tone_masteratt_11, + _psy_tone_0dB, + _psy_tone_suppress, + + _vp_tonemask_adj_11, + NULL, + _vp_tonemask_adj_11, + + _psy_noiseguards_8, + _psy_noisebias_11, + _psy_noisebias_11, + NULL, + NULL, + _psy_noise_suppress, + + _psy_compand_8, + _psy_compand_8_mapping, + NULL, + + {_noise_start_8,_noise_start_8}, + {_noise_part_8,_noise_part_8}, + _noise_thresh_11, + + _psy_ath_floater_8, + _psy_ath_abs_8, + + _psy_lowpass_11, + + _psy_global_44, + _global_mapping_8, + _psy_stereo_modes_8, + + _floor_books, + _floor, + _floor_mapping_11, + NULL, + + _mapres_template_8_stereo +}; + +ve_setup_data_template ve_setup_11_uncoupled={ + 2, + rate_mapping_11_uncoupled, + quality_mapping_11, + -1, + 9000, + 15000, + + blocksize_11, + blocksize_11, + + _psy_tone_masteratt_11, + _psy_tone_0dB, + _psy_tone_suppress, + + _vp_tonemask_adj_11, + NULL, + _vp_tonemask_adj_11, + + _psy_noiseguards_8, + _psy_noisebias_11, + _psy_noisebias_11, + NULL, + NULL, + _psy_noise_suppress, + + _psy_compand_8, + _psy_compand_8_mapping, + NULL, + + {_noise_start_8,_noise_start_8}, + {_noise_part_8,_noise_part_8}, + _noise_thresh_11, + + _psy_ath_floater_8, + _psy_ath_abs_8, + + _psy_lowpass_11, + + _psy_global_44, + _global_mapping_8, + _psy_stereo_modes_8, + + _floor_books, + _floor, + _floor_mapping_11, + NULL, + + _mapres_template_8_uncoupled +}; +/********* End of inlined file: setup_11.h *********/ + +/********* Start of inlined file: setup_16.h *********/ + +/********* Start of inlined file: psych_16.h *********/ +/* stereo mode by base quality level */ +static adj_stereo _psy_stereo_modes_16[4]={ + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 */ + {{ 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}, + { 6, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}, + { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 4, 4}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}}, + {{ 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}, + { 6, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}, + { 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 4, 4, 4, 4, 4}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}}, + {{ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}, + { 5, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}, + { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}}, + {{ 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}, + { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}}, +}; + +static double _psy_lowpass_16[4]={6.5,8,30.,99.}; + +static att3 _psy_tone_masteratt_16[4]={ + {{ 30, 25, 12}, 0, 0}, /* 0 */ + {{ 25, 22, 12}, 0, 0}, /* 0 */ + {{ 20, 12, 0}, 0, 0}, /* 0 */ + {{ 15, 0, -14}, 0, 0}, /* 0 */ +}; + +static vp_adjblock _vp_tonemask_adj_16[4]={ + /* adjust for mode zero */ + /* 63 125 250 500 1 2 4 8 16 */ + {{-20,-20,-20,-20,-20,-16,-10, 0, 0, 0, 0,10, 0, 0, 0, 0, 0}}, /* 0 */ + {{-20,-20,-20,-20,-20,-16,-10, 0, 0, 0, 0,10, 0, 0, 0, 0, 0}}, /* 1 */ + {{-20,-20,-20,-20,-20,-16,-10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, /* 2 */ + {{-30,-30,-30,-30,-30,-26,-20,-10, -5, 0, 0, 0, 0, 0, 0, 0, 0}}, /* 2 */ +}; + +static noise3 _psy_noisebias_16_short[4]={ + /* 63 125 250 500 1k 2k 4k 8k 16k*/ + {{{-15,-15,-15,-15,-15,-10,-10,-5, 4, 10, 10, 10, 10, 12, 12, 14, 20}, + {-15,-15,-15,-15,-15,-10,-10, -5, 0, 0, 4, 5, 5, 6, 8, 8, 15}, + {-30,-30,-30,-30,-30,-24,-20,-14,-10, -6, -8, -8, -6, -6, -6, -6, -6}}}, + + {{{-15,-15,-15,-15,-15,-10,-10,-5, 4, 6, 6, 6, 6, 8, 10, 12, 20}, + {-15,-15,-15,-15,-15,-15,-15,-10, -5, -5, -5, 4, 5, 6, 8, 8, 15}, + {-30,-30,-30,-30,-30,-24,-20,-14,-10,-10,-10,-10,-10,-10,-10,-10,-10}}}, + + {{{-15,-15,-15,-15,-15,-12,-10, -8, 0, 2, 4, 4, 5, 5, 5, 8, 12}, + {-20,-20,-20,-20,-16,-12,-20,-14,-10,-10, -8, 0, 0, 0, 0, 2, 5}, + {-30,-30,-30,-30,-26,-26,-26,-26,-26,-26,-26,-26,-26,-24,-20,-20,-20}}}, + + {{{-15,-15,-15,-15,-15,-12,-10, -8, -5, -5, -5, -5, -5, 0, 0, 0, 6}, + {-30,-30,-30,-30,-26,-22,-20,-14,-12,-12,-10,-10,-10,-10,-10,-10, -6}, + {-30,-30,-30,-30,-26,-26,-26,-26,-26,-26,-26,-26,-26,-24,-20,-20,-20}}}, +}; + +static noise3 _psy_noisebias_16_impulse[4]={ + /* 63 125 250 500 1k 2k 4k 8k 16k*/ + {{{-15,-15,-15,-15,-15,-10,-10,-5, 4, 10, 10, 10, 10, 12, 12, 14, 20}, + {-15,-15,-15,-15,-15,-10,-10, -5, 0, 0, 4, 5, 5, 6, 8, 8, 15}, + {-30,-30,-30,-30,-30,-24,-20,-14,-10, -6, -8, -8, -6, -6, -6, -6, -6}}}, + + {{{-15,-15,-15,-15,-15,-10,-10,-5, 4, 4, 4, 4, 5, 5, 6, 8, 15}, + {-15,-15,-15,-15,-15,-15,-15,-10, -5, -5, -5, 0, 0, 0, 0, 4, 10}, + {-30,-30,-30,-30,-30,-24,-20,-14,-10,-10,-10,-10,-10,-10,-10,-10,-10}}}, + + {{{-15,-15,-15,-15,-15,-12,-10, -8, 0, 0, 0, 0, 0, 0, 0, 4, 10}, + {-20,-20,-20,-20,-16,-12,-20,-14,-10,-10,-10,-10,-10,-10,-10, -7, -5}, + {-30,-30,-30,-30,-26,-26,-26,-26,-26,-26,-26,-26,-26,-24,-20,-20,-20}}}, + + {{{-15,-15,-15,-15,-15,-12,-10, -8, -5, -5, -5, -5, -5, 0, 0, 0, 6}, + {-30,-30,-30,-30,-26,-22,-20,-18,-18,-18,-20,-20,-20,-20,-20,-20,-16}, + {-30,-30,-30,-30,-26,-26,-26,-26,-26,-26,-26,-26,-26,-24,-20,-20,-20}}}, +}; + +static noise3 _psy_noisebias_16[4]={ + /* 63 125 250 500 1k 2k 4k 8k 16k*/ + {{{-10,-10,-10,-10, -5, -5, -5, 0, 4, 6, 8, 8, 10, 10, 10, 14, 20}, + {-10,-10,-10,-10,-10, -5, -2, -2, 0, 0, 0, 4, 5, 6, 8, 8, 15}, + {-30,-30,-30,-30,-30,-24,-20,-14,-10, -6, -8, -8, -6, -6, -6, -6, -6}}}, + + {{{-10,-10,-10,-10, -5, -5, -5, 0, 4, 6, 6, 6, 6, 8, 10, 12, 20}, + {-15,-15,-15,-15,-15,-10, -5, -5, 0, 0, 0, 4, 5, 6, 8, 8, 15}, + {-30,-30,-30,-30,-30,-24,-20,-14,-10, -6, -8, -8, -6, -6, -6, -6, -6}}}, + + {{{-15,-15,-15,-15,-15,-12,-10, -8, 0, 2, 4, 4, 5, 5, 5, 8, 12}, + {-20,-20,-20,-20,-16,-12,-20,-10, -5, -5, 0, 0, 0, 0, 0, 2, 5}, + {-30,-30,-30,-30,-26,-26,-26,-26,-26,-26,-26,-26,-26,-24,-20,-20,-20}}}, + + {{{-15,-15,-15,-15,-15,-12,-10, -8, -5, -5, -5, -5, -5, 0, 0, 0, 6}, + {-30,-30,-30,-30,-26,-22,-20,-14,-12,-12,-10,-10,-10,-10,-10,-10, -6}, + {-30,-30,-30,-30,-26,-26,-26,-26,-26,-26,-26,-26,-26,-24,-20,-20,-20}}}, +}; + +static double _noise_thresh_16[4]={ .3,.5,.5,.5 }; + +static int _noise_start_16[3]={ 256,256,9999 }; +static int _noise_part_16[4]={ 8,8,8,8 }; + +static int _psy_ath_floater_16[4]={ + -100,-100,-100,-105, +}; + +static int _psy_ath_abs_16[4]={ + -130,-130,-130,-140, +}; +/********* End of inlined file: psych_16.h *********/ + +/********* Start of inlined file: residue_16.h *********/ +/***** residue backends *********************************************/ + +static static_bookblock _resbook_16s_0={ + { + {0}, + {0,0,&_16c0_s_p1_0}, + {0,0,&_16c0_s_p2_0}, + {0,0,&_16c0_s_p3_0}, + {0,0,&_16c0_s_p4_0}, + {0,0,&_16c0_s_p5_0}, + {0,0,&_16c0_s_p6_0}, + {&_16c0_s_p7_0,&_16c0_s_p7_1}, + {&_16c0_s_p8_0,&_16c0_s_p8_1}, + {&_16c0_s_p9_0,&_16c0_s_p9_1,&_16c0_s_p9_2} + } +}; +static static_bookblock _resbook_16s_1={ + { + {0}, + {0,0,&_16c1_s_p1_0}, + {0,0,&_16c1_s_p2_0}, + {0,0,&_16c1_s_p3_0}, + {0,0,&_16c1_s_p4_0}, + {0,0,&_16c1_s_p5_0}, + {0,0,&_16c1_s_p6_0}, + {&_16c1_s_p7_0,&_16c1_s_p7_1}, + {&_16c1_s_p8_0,&_16c1_s_p8_1}, + {&_16c1_s_p9_0,&_16c1_s_p9_1,&_16c1_s_p9_2} + } +}; +static static_bookblock _resbook_16s_2={ + { + {0}, + {0,0,&_16c2_s_p1_0}, + {0,0,&_16c2_s_p2_0}, + {0,0,&_16c2_s_p3_0}, + {0,0,&_16c2_s_p4_0}, + {&_16c2_s_p5_0,&_16c2_s_p5_1}, + {&_16c2_s_p6_0,&_16c2_s_p6_1}, + {&_16c2_s_p7_0,&_16c2_s_p7_1}, + {&_16c2_s_p8_0,&_16c2_s_p8_1}, + {&_16c2_s_p9_0,&_16c2_s_p9_1,&_16c2_s_p9_2} + } +}; + +static vorbis_residue_template _res_16s_0[]={ + {2,0, &_residue_44_mid, + &_huff_book__16c0_s_single,&_huff_book__16c0_s_single, + &_resbook_16s_0,&_resbook_16s_0}, +}; +static vorbis_residue_template _res_16s_1[]={ + {2,0, &_residue_44_mid, + &_huff_book__16c1_s_short,&_huff_book__16c1_s_short, + &_resbook_16s_1,&_resbook_16s_1}, + + {2,0, &_residue_44_mid, + &_huff_book__16c1_s_long,&_huff_book__16c1_s_long, + &_resbook_16s_1,&_resbook_16s_1} +}; +static vorbis_residue_template _res_16s_2[]={ + {2,0, &_residue_44_high, + &_huff_book__16c2_s_short,&_huff_book__16c2_s_short, + &_resbook_16s_2,&_resbook_16s_2}, + + {2,0, &_residue_44_high, + &_huff_book__16c2_s_long,&_huff_book__16c2_s_long, + &_resbook_16s_2,&_resbook_16s_2} +}; + +static vorbis_mapping_template _mapres_template_16_stereo[3]={ + { _map_nominal, _res_16s_0 }, /* 0 */ + { _map_nominal, _res_16s_1 }, /* 1 */ + { _map_nominal, _res_16s_2 }, /* 2 */ +}; + +static static_bookblock _resbook_16u_0={ + { + {0}, + {0,0,&_16u0__p1_0}, + {0,0,&_16u0__p2_0}, + {0,0,&_16u0__p3_0}, + {0,0,&_16u0__p4_0}, + {0,0,&_16u0__p5_0}, + {&_16u0__p6_0,&_16u0__p6_1}, + {&_16u0__p7_0,&_16u0__p7_1,&_16u0__p7_2} + } +}; +static static_bookblock _resbook_16u_1={ + { + {0}, + {0,0,&_16u1__p1_0}, + {0,0,&_16u1__p2_0}, + {0,0,&_16u1__p3_0}, + {0,0,&_16u1__p4_0}, + {0,0,&_16u1__p5_0}, + {0,0,&_16u1__p6_0}, + {&_16u1__p7_0,&_16u1__p7_1}, + {&_16u1__p8_0,&_16u1__p8_1}, + {&_16u1__p9_0,&_16u1__p9_1,&_16u1__p9_2} + } +}; +static static_bookblock _resbook_16u_2={ + { + {0}, + {0,0,&_16u2_p1_0}, + {0,0,&_16u2_p2_0}, + {0,0,&_16u2_p3_0}, + {0,0,&_16u2_p4_0}, + {&_16u2_p5_0,&_16u2_p5_1}, + {&_16u2_p6_0,&_16u2_p6_1}, + {&_16u2_p7_0,&_16u2_p7_1}, + {&_16u2_p8_0,&_16u2_p8_1}, + {&_16u2_p9_0,&_16u2_p9_1,&_16u2_p9_2} + } +}; + +static vorbis_residue_template _res_16u_0[]={ + {1,0, &_residue_44_low_un, + &_huff_book__16u0__single,&_huff_book__16u0__single, + &_resbook_16u_0,&_resbook_16u_0}, +}; +static vorbis_residue_template _res_16u_1[]={ + {1,0, &_residue_44_mid_un, + &_huff_book__16u1__short,&_huff_book__16u1__short, + &_resbook_16u_1,&_resbook_16u_1}, + + {1,0, &_residue_44_mid_un, + &_huff_book__16u1__long,&_huff_book__16u1__long, + &_resbook_16u_1,&_resbook_16u_1} +}; +static vorbis_residue_template _res_16u_2[]={ + {1,0, &_residue_44_hi_un, + &_huff_book__16u2__short,&_huff_book__16u2__short, + &_resbook_16u_2,&_resbook_16u_2}, + + {1,0, &_residue_44_hi_un, + &_huff_book__16u2__long,&_huff_book__16u2__long, + &_resbook_16u_2,&_resbook_16u_2} +}; + +static vorbis_mapping_template _mapres_template_16_uncoupled[3]={ + { _map_nominal_u, _res_16u_0 }, /* 0 */ + { _map_nominal_u, _res_16u_1 }, /* 1 */ + { _map_nominal_u, _res_16u_2 }, /* 2 */ +}; +/********* End of inlined file: residue_16.h *********/ + +static int blocksize_16_short[3]={ + 1024,512,512 +}; +static int blocksize_16_long[3]={ + 1024,1024,1024 +}; + +static int _floor_mapping_16_short[3]={ + 9,3,3 +}; +static int _floor_mapping_16[3]={ + 9,9,9 +}; + +static double rate_mapping_16[4]={ + 12000.,20000.,44000.,86000. +}; + +static double rate_mapping_16_uncoupled[4]={ + 16000.,28000.,64000.,100000. +}; + +static double _global_mapping_16[4]={ 1., 2., 3., 4. }; + +static double quality_mapping_16[4]={ -.1,.05,.5,1. }; + +static double _psy_compand_16_mapping[4]={ 0., .8, 1., 1.}; + +ve_setup_data_template ve_setup_16_stereo={ + 3, + rate_mapping_16, + quality_mapping_16, + 2, + 15000, + 19000, + + blocksize_16_short, + blocksize_16_long, + + _psy_tone_masteratt_16, + _psy_tone_0dB, + _psy_tone_suppress, + + _vp_tonemask_adj_16, + _vp_tonemask_adj_16, + _vp_tonemask_adj_16, + + _psy_noiseguards_8, + _psy_noisebias_16_impulse, + _psy_noisebias_16_short, + _psy_noisebias_16_short, + _psy_noisebias_16, + _psy_noise_suppress, + + _psy_compand_8, + _psy_compand_16_mapping, + _psy_compand_16_mapping, + + {_noise_start_16,_noise_start_16}, + { _noise_part_16, _noise_part_16}, + _noise_thresh_16, + + _psy_ath_floater_16, + _psy_ath_abs_16, + + _psy_lowpass_16, + + _psy_global_44, + _global_mapping_16, + _psy_stereo_modes_16, + + _floor_books, + _floor, + _floor_mapping_16_short, + _floor_mapping_16, + + _mapres_template_16_stereo +}; + +ve_setup_data_template ve_setup_16_uncoupled={ + 3, + rate_mapping_16_uncoupled, + quality_mapping_16, + -1, + 15000, + 19000, + + blocksize_16_short, + blocksize_16_long, + + _psy_tone_masteratt_16, + _psy_tone_0dB, + _psy_tone_suppress, + + _vp_tonemask_adj_16, + _vp_tonemask_adj_16, + _vp_tonemask_adj_16, + + _psy_noiseguards_8, + _psy_noisebias_16_impulse, + _psy_noisebias_16_short, + _psy_noisebias_16_short, + _psy_noisebias_16, + _psy_noise_suppress, + + _psy_compand_8, + _psy_compand_16_mapping, + _psy_compand_16_mapping, + + {_noise_start_16,_noise_start_16}, + { _noise_part_16, _noise_part_16}, + _noise_thresh_16, + + _psy_ath_floater_16, + _psy_ath_abs_16, + + _psy_lowpass_16, + + _psy_global_44, + _global_mapping_16, + _psy_stereo_modes_16, + + _floor_books, + _floor, + _floor_mapping_16_short, + _floor_mapping_16, + + _mapres_template_16_uncoupled +}; +/********* End of inlined file: setup_16.h *********/ + +/********* Start of inlined file: setup_22.h *********/ +static double rate_mapping_22[4]={ + 15000.,20000.,44000.,86000. +}; + +static double rate_mapping_22_uncoupled[4]={ + 16000.,28000.,50000.,90000. +}; + +static double _psy_lowpass_22[4]={9.5,11.,30.,99.}; + +ve_setup_data_template ve_setup_22_stereo={ + 3, + rate_mapping_22, + quality_mapping_16, + 2, + 19000, + 26000, + + blocksize_16_short, + blocksize_16_long, + + _psy_tone_masteratt_16, + _psy_tone_0dB, + _psy_tone_suppress, + + _vp_tonemask_adj_16, + _vp_tonemask_adj_16, + _vp_tonemask_adj_16, + + _psy_noiseguards_8, + _psy_noisebias_16_impulse, + _psy_noisebias_16_short, + _psy_noisebias_16_short, + _psy_noisebias_16, + _psy_noise_suppress, + + _psy_compand_8, + _psy_compand_8_mapping, + _psy_compand_8_mapping, + + {_noise_start_16,_noise_start_16}, + { _noise_part_16, _noise_part_16}, + _noise_thresh_16, + + _psy_ath_floater_16, + _psy_ath_abs_16, + + _psy_lowpass_22, + + _psy_global_44, + _global_mapping_16, + _psy_stereo_modes_16, + + _floor_books, + _floor, + _floor_mapping_16_short, + _floor_mapping_16, + + _mapres_template_16_stereo +}; + +ve_setup_data_template ve_setup_22_uncoupled={ + 3, + rate_mapping_22_uncoupled, + quality_mapping_16, + -1, + 19000, + 26000, + + blocksize_16_short, + blocksize_16_long, + + _psy_tone_masteratt_16, + _psy_tone_0dB, + _psy_tone_suppress, + + _vp_tonemask_adj_16, + _vp_tonemask_adj_16, + _vp_tonemask_adj_16, + + _psy_noiseguards_8, + _psy_noisebias_16_impulse, + _psy_noisebias_16_short, + _psy_noisebias_16_short, + _psy_noisebias_16, + _psy_noise_suppress, + + _psy_compand_8, + _psy_compand_8_mapping, + _psy_compand_8_mapping, + + {_noise_start_16,_noise_start_16}, + { _noise_part_16, _noise_part_16}, + _noise_thresh_16, + + _psy_ath_floater_16, + _psy_ath_abs_16, + + _psy_lowpass_22, + + _psy_global_44, + _global_mapping_16, + _psy_stereo_modes_16, + + _floor_books, + _floor, + _floor_mapping_16_short, + _floor_mapping_16, + + _mapres_template_16_uncoupled +}; +/********* End of inlined file: setup_22.h *********/ + +/********* Start of inlined file: setup_X.h *********/ +static double rate_mapping_X[12]={ + -1.,-1.,-1.,-1.,-1.,-1., + -1.,-1.,-1.,-1.,-1.,-1. +}; + +ve_setup_data_template ve_setup_X_stereo={ + 11, + rate_mapping_X, + quality_mapping_44, + 2, + 50000, + 200000, + + blocksize_short_44, + blocksize_long_44, + + _psy_tone_masteratt_44, + _psy_tone_0dB, + _psy_tone_suppress, + + _vp_tonemask_adj_otherblock, + _vp_tonemask_adj_longblock, + _vp_tonemask_adj_otherblock, + + _psy_noiseguards_44, + _psy_noisebias_impulse, + _psy_noisebias_padding, + _psy_noisebias_trans, + _psy_noisebias_long, + _psy_noise_suppress, + + _psy_compand_44, + _psy_compand_short_mapping, + _psy_compand_long_mapping, + + {_noise_start_short_44,_noise_start_long_44}, + {_noise_part_short_44,_noise_part_long_44}, + _noise_thresh_44, + + _psy_ath_floater, + _psy_ath_abs, + + _psy_lowpass_44, + + _psy_global_44, + _global_mapping_44, + _psy_stereo_modes_44, + + _floor_books, + _floor, + _floor_short_mapping_44, + _floor_long_mapping_44, + + _mapres_template_44_stereo +}; + +ve_setup_data_template ve_setup_X_uncoupled={ + 11, + rate_mapping_X, + quality_mapping_44, + -1, + 50000, + 200000, + + blocksize_short_44, + blocksize_long_44, + + _psy_tone_masteratt_44, + _psy_tone_0dB, + _psy_tone_suppress, + + _vp_tonemask_adj_otherblock, + _vp_tonemask_adj_longblock, + _vp_tonemask_adj_otherblock, + + _psy_noiseguards_44, + _psy_noisebias_impulse, + _psy_noisebias_padding, + _psy_noisebias_trans, + _psy_noisebias_long, + _psy_noise_suppress, + + _psy_compand_44, + _psy_compand_short_mapping, + _psy_compand_long_mapping, + + {_noise_start_short_44,_noise_start_long_44}, + {_noise_part_short_44,_noise_part_long_44}, + _noise_thresh_44, + + _psy_ath_floater, + _psy_ath_abs, + + _psy_lowpass_44, + + _psy_global_44, + _global_mapping_44, + NULL, + + _floor_books, + _floor, + _floor_short_mapping_44, + _floor_long_mapping_44, + + _mapres_template_44_uncoupled +}; + +ve_setup_data_template ve_setup_XX_stereo={ + 2, + rate_mapping_X, + quality_mapping_8, + 2, + 0, + 8000, + + blocksize_8, + blocksize_8, + + _psy_tone_masteratt_8, + _psy_tone_0dB, + _psy_tone_suppress, + + _vp_tonemask_adj_8, + NULL, + _vp_tonemask_adj_8, + + _psy_noiseguards_8, + _psy_noisebias_8, + _psy_noisebias_8, + NULL, + NULL, + _psy_noise_suppress, + + _psy_compand_8, + _psy_compand_8_mapping, + NULL, + + {_noise_start_8,_noise_start_8}, + {_noise_part_8,_noise_part_8}, + _noise_thresh_5only, + + _psy_ath_floater_8, + _psy_ath_abs_8, + + _psy_lowpass_8, + + _psy_global_44, + _global_mapping_8, + _psy_stereo_modes_8, + + _floor_books, + _floor, + _floor_mapping_8, + NULL, + + _mapres_template_8_stereo +}; + +ve_setup_data_template ve_setup_XX_uncoupled={ + 2, + rate_mapping_X, + quality_mapping_8, + -1, + 0, + 8000, + + blocksize_8, + blocksize_8, + + _psy_tone_masteratt_8, + _psy_tone_0dB, + _psy_tone_suppress, + + _vp_tonemask_adj_8, + NULL, + _vp_tonemask_adj_8, + + _psy_noiseguards_8, + _psy_noisebias_8, + _psy_noisebias_8, + NULL, + NULL, + _psy_noise_suppress, + + _psy_compand_8, + _psy_compand_8_mapping, + NULL, + + {_noise_start_8,_noise_start_8}, + {_noise_part_8,_noise_part_8}, + _noise_thresh_5only, + + _psy_ath_floater_8, + _psy_ath_abs_8, + + _psy_lowpass_8, + + _psy_global_44, + _global_mapping_8, + _psy_stereo_modes_8, + + _floor_books, + _floor, + _floor_mapping_8, + NULL, + + _mapres_template_8_uncoupled +}; +/********* End of inlined file: setup_X.h *********/ + +static ve_setup_data_template *setup_list[]={ + &ve_setup_44_stereo, + &ve_setup_44_uncoupled, + + &ve_setup_32_stereo, + &ve_setup_32_uncoupled, + + &ve_setup_22_stereo, + &ve_setup_22_uncoupled, + &ve_setup_16_stereo, + &ve_setup_16_uncoupled, + + &ve_setup_11_stereo, + &ve_setup_11_uncoupled, + &ve_setup_8_stereo, + &ve_setup_8_uncoupled, + + &ve_setup_X_stereo, + &ve_setup_X_uncoupled, + &ve_setup_XX_stereo, + &ve_setup_XX_uncoupled, + 0 +}; + +static int vorbis_encode_toplevel_setup(vorbis_info *vi,int ch,long rate){ + if(vi && vi->codec_setup){ + + vi->version=0; + vi->channels=ch; + vi->rate=rate; + + return(0); + } + return(OV_EINVAL); +} + +static void vorbis_encode_floor_setup(vorbis_info *vi,double s,int block, + static_codebook ***books, + vorbis_info_floor1 *in, + int *x){ + int i,k,is=s; + vorbis_info_floor1 *f=(vorbis_info_floor1*) _ogg_calloc(1,sizeof(*f)); + codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; + + memcpy(f,in+x[is],sizeof(*f)); + /* fill in the lowpass field, even if it's temporary */ + f->n=ci->blocksizes[block]>>1; + + /* books */ + { + int partitions=f->partitions; + int maxclass=-1; + int maxbook=-1; + for(i=0;ipartitionclass[i]>maxclass)maxclass=f->partitionclass[i]; + for(i=0;i<=maxclass;i++){ + if(f->class_book[i]>maxbook)maxbook=f->class_book[i]; + f->class_book[i]+=ci->books; + for(k=0;k<(1<class_subs[i]);k++){ + if(f->class_subbook[i][k]>maxbook)maxbook=f->class_subbook[i][k]; + if(f->class_subbook[i][k]>=0)f->class_subbook[i][k]+=ci->books; + } + } + + for(i=0;i<=maxbook;i++) + ci->book_param[ci->books++]=books[x[is]][i]; + } + + /* for now, we're only using floor 1 */ + ci->floor_type[ci->floors]=1; + ci->floor_param[ci->floors]=f; + ci->floors++; + + return; +} + +static void vorbis_encode_global_psych_setup(vorbis_info *vi,double s, + vorbis_info_psy_global *in, + double *x){ + int i,is=s; + double ds=s-is; + codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; + vorbis_info_psy_global *g=&ci->psy_g_param; + + memcpy(g,in+(int)x[is],sizeof(*g)); + + ds=x[is]*(1.-ds)+x[is+1]*ds; + is=(int)ds; + ds-=is; + if(ds==0 && is>0){ + is--; + ds=1.; + } + + /* interpolate the trigger threshholds */ + for(i=0;i<4;i++){ + g->preecho_thresh[i]=in[is].preecho_thresh[i]*(1.-ds)+in[is+1].preecho_thresh[i]*ds; + g->postecho_thresh[i]=in[is].postecho_thresh[i]*(1.-ds)+in[is+1].postecho_thresh[i]*ds; + } + g->ampmax_att_per_sec=ci->hi.amplitude_track_dBpersec; + return; +} + +static void vorbis_encode_global_stereo(vorbis_info *vi, + highlevel_encode_setup *hi, + adj_stereo *p){ + float s=hi->stereo_point_setting; + int i,is=s; + double ds=s-is; + codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; + vorbis_info_psy_global *g=&ci->psy_g_param; + + if(p){ + memcpy(g->coupling_prepointamp,p[is].pre,sizeof(*p[is].pre)*PACKETBLOBS); + memcpy(g->coupling_postpointamp,p[is].post,sizeof(*p[is].post)*PACKETBLOBS); + + if(hi->managed){ + /* interpolate the kHz threshholds */ + for(i=0;icoupling_pointlimit[0][i]=kHz*1000./vi->rate*ci->blocksizes[0]; + g->coupling_pointlimit[1][i]=kHz*1000./vi->rate*ci->blocksizes[1]; + g->coupling_pkHz[i]=kHz; + + kHz=p[is].lowpasskHz[i]*(1.-ds)+p[is+1].lowpasskHz[i]*ds; + g->sliding_lowpass[0][i]=kHz*1000./vi->rate*ci->blocksizes[0]; + g->sliding_lowpass[1][i]=kHz*1000./vi->rate*ci->blocksizes[1]; + + } + }else{ + float kHz=p[is].kHz[PACKETBLOBS/2]*(1.-ds)+p[is+1].kHz[PACKETBLOBS/2]*ds; + for(i=0;icoupling_pointlimit[0][i]=kHz*1000./vi->rate*ci->blocksizes[0]; + g->coupling_pointlimit[1][i]=kHz*1000./vi->rate*ci->blocksizes[1]; + g->coupling_pkHz[i]=kHz; + } + + kHz=p[is].lowpasskHz[PACKETBLOBS/2]*(1.-ds)+p[is+1].lowpasskHz[PACKETBLOBS/2]*ds; + for(i=0;isliding_lowpass[0][i]=kHz*1000./vi->rate*ci->blocksizes[0]; + g->sliding_lowpass[1][i]=kHz*1000./vi->rate*ci->blocksizes[1]; + } + } + }else{ + for(i=0;isliding_lowpass[0][i]=ci->blocksizes[0]; + g->sliding_lowpass[1][i]=ci->blocksizes[1]; + } + } + return; +} + +static void vorbis_encode_psyset_setup(vorbis_info *vi,double s, + int *nn_start, + int *nn_partition, + double *nn_thresh, + int block){ + codec_setup_info *ci=(codec_setup_info*) vi->codec_setup; + vorbis_info_psy *p=ci->psy_param[block]; + highlevel_encode_setup *hi=&ci->hi; + int is=s; + + if(block>=ci->psys) + ci->psys=block+1; + if(!p){ + p=(vorbis_info_psy*)_ogg_calloc(1,sizeof(*p)); + ci->psy_param[block]=p; + } + + memcpy(p,&_psy_info_template,sizeof(*p)); + p->blockflag=block>>1; + + if(hi->noise_normalize_p){ + p->normal_channel_p=1; + p->normal_point_p=1; + p->normal_start=nn_start[is]; + p->normal_partition=nn_partition[is]; + p->normal_thresh=nn_thresh[is]; + } + + return; +} + +static void vorbis_encode_tonemask_setup(vorbis_info *vi,double s,int block, + att3 *att, + int *max, + vp_adjblock *in){ + int i,is=s; + double ds=s-is; + codec_setup_info *ci=(codec_setup_info*) vi->codec_setup; + vorbis_info_psy *p=ci->psy_param[block]; + + /* 0 and 2 are only used by bitmanagement, but there's no harm to always + filling the values in here */ + p->tone_masteratt[0]=att[is].att[0]*(1.-ds)+att[is+1].att[0]*ds; + p->tone_masteratt[1]=att[is].att[1]*(1.-ds)+att[is+1].att[1]*ds; + p->tone_masteratt[2]=att[is].att[2]*(1.-ds)+att[is+1].att[2]*ds; + p->tone_centerboost=att[is].boost*(1.-ds)+att[is+1].boost*ds; + p->tone_decay=att[is].decay*(1.-ds)+att[is+1].decay*ds; + + p->max_curve_dB=max[is]*(1.-ds)+max[is+1]*ds; + + for(i=0;itoneatt[i]=in[is].block[i]*(1.-ds)+in[is+1].block[i]*ds; + return; +} + +static void vorbis_encode_compand_setup(vorbis_info *vi,double s,int block, + compandblock *in, double *x){ + int i,is=s; + double ds=s-is; + codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; + vorbis_info_psy *p=ci->psy_param[block]; + + ds=x[is]*(1.-ds)+x[is+1]*ds; + is=(int)ds; + ds-=is; + if(ds==0 && is>0){ + is--; + ds=1.; + } + + /* interpolate the compander settings */ + for(i=0;inoisecompand[i]=in[is].data[i]*(1.-ds)+in[is+1].data[i]*ds; + return; +} + +static void vorbis_encode_peak_setup(vorbis_info *vi,double s,int block, + int *suppress){ + int is=s; + double ds=s-is; + codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; + vorbis_info_psy *p=ci->psy_param[block]; + + p->tone_abs_limit=suppress[is]*(1.-ds)+suppress[is+1]*ds; + + return; +} + +static void vorbis_encode_noisebias_setup(vorbis_info *vi,double s,int block, + int *suppress, + noise3 *in, + noiseguard *guard, + double userbias){ + int i,is=s,j; + double ds=s-is; + codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; + vorbis_info_psy *p=ci->psy_param[block]; + + p->noisemaxsupp=suppress[is]*(1.-ds)+suppress[is+1]*ds; + p->noisewindowlomin=guard[block].lo; + p->noisewindowhimin=guard[block].hi; + p->noisewindowfixed=guard[block].fixed; + + for(j=0;jnoiseoff[j][i]=in[is].data[j][i]*(1.-ds)+in[is+1].data[j][i]*ds; + + /* impulse blocks may take a user specified bias to boost the + nominal/high noise encoding depth */ + for(j=0;jnoiseoff[j][0]+6; /* the lowest it can go */ + for(i=0;inoiseoff[j][i]+=userbias; + if(p->noiseoff[j][i]noiseoff[j][i]=min; + } + } + + return; +} + +static void vorbis_encode_ath_setup(vorbis_info *vi,int block){ + codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; + vorbis_info_psy *p=ci->psy_param[block]; + + p->ath_adjatt=ci->hi.ath_floating_dB; + p->ath_maxatt=ci->hi.ath_absolute_dB; + return; +} + +static int book_dup_or_new(codec_setup_info *ci,static_codebook *book){ + int i; + for(i=0;ibooks;i++) + if(ci->book_param[i]==book)return(i); + + return(ci->books++); +} + +static void vorbis_encode_blocksize_setup(vorbis_info *vi,double s, + int *shortb,int *longb){ + + codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; + int is=s; + + int blockshort=shortb[is]; + int blocklong=longb[is]; + ci->blocksizes[0]=blockshort; + ci->blocksizes[1]=blocklong; + +} + +static void vorbis_encode_residue_setup(vorbis_info *vi, + int number, int block, + vorbis_residue_template *res){ + + codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; + int i,n; + + vorbis_info_residue0 *r=(vorbis_info_residue0*)(ci->residue_param[number]= + (vorbis_info_residue0*)_ogg_malloc(sizeof(*r))); + + memcpy(r,res->res,sizeof(*r)); + if(ci->residues<=number)ci->residues=number+1; + + switch(ci->blocksizes[block]){ + case 64:case 128:case 256: + r->grouping=16; + break; + default: + r->grouping=32; + break; + } + ci->residue_type[number]=res->res_type; + + /* to be adjusted by lowpass/pointlimit later */ + n=r->end=ci->blocksizes[block]>>1; + if(res->res_type==2) + n=r->end*=vi->channels; + + /* fill in all the books */ + { + int booklist=0,k; + + if(ci->hi.managed){ + for(i=0;ipartitions;i++) + for(k=0;k<3;k++) + if(res->books_base_managed->books[i][k]) + r->secondstages[i]|=(1<groupbook=book_dup_or_new(ci,res->book_aux_managed); + ci->book_param[r->groupbook]=res->book_aux_managed; + + for(i=0;ipartitions;i++){ + for(k=0;k<3;k++){ + if(res->books_base_managed->books[i][k]){ + int bookid=book_dup_or_new(ci,res->books_base_managed->books[i][k]); + r->booklist[booklist++]=bookid; + ci->book_param[bookid]=res->books_base_managed->books[i][k]; + } + } + } + + }else{ + + for(i=0;ipartitions;i++) + for(k=0;k<3;k++) + if(res->books_base->books[i][k]) + r->secondstages[i]|=(1<groupbook=book_dup_or_new(ci,res->book_aux); + ci->book_param[r->groupbook]=res->book_aux; + + for(i=0;ipartitions;i++){ + for(k=0;k<3;k++){ + if(res->books_base->books[i][k]){ + int bookid=book_dup_or_new(ci,res->books_base->books[i][k]); + r->booklist[booklist++]=bookid; + ci->book_param[bookid]=res->books_base->books[i][k]; + } + } + } + } + } + + /* lowpass setup/pointlimit */ + { + double freq=ci->hi.lowpass_kHz*1000.; + vorbis_info_floor1 *f=(vorbis_info_floor1*)ci->floor_param[block]; /* by convention */ + double nyq=vi->rate/2.; + long blocksize=ci->blocksizes[block]>>1; + + /* lowpass needs to be set in the floor and the residue. */ + if(freq>nyq)freq=nyq; + /* in the floor, the granularity can be very fine; it doesn't alter + the encoding structure, only the samples used to fit the floor + approximation */ + f->n=freq/nyq*blocksize; + + /* this res may by limited by the maximum pointlimit of the mode, + not the lowpass. the floor is always lowpass limited. */ + if(res->limit_type){ + if(ci->hi.managed) + freq=ci->psy_g_param.coupling_pkHz[PACKETBLOBS-1]*1000.; + else + freq=ci->psy_g_param.coupling_pkHz[PACKETBLOBS/2]*1000.; + if(freq>nyq)freq=nyq; + } + + /* in the residue, we're constrained, physically, by partition + boundaries. We still lowpass 'wherever', but we have to round up + here to next boundary, or the vorbis spec will round it *down* to + previous boundary in encode/decode */ + if(ci->residue_type[block]==2) + r->end=(int)((freq/nyq*blocksize*2)/r->grouping+.9)* /* round up only if we're well past */ + r->grouping; + else + r->end=(int)((freq/nyq*blocksize)/r->grouping+.9)* /* round up only if we're well past */ + r->grouping; + } +} + +/* we assume two maps in this encoder */ +static void vorbis_encode_map_n_res_setup(vorbis_info *vi,double s, + vorbis_mapping_template *maps){ + + codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; + int i,j,is=s,modes=2; + vorbis_info_mapping0 *map=maps[is].map; + vorbis_info_mode *mode=_mode_template; + vorbis_residue_template *res=maps[is].res; + + if(ci->blocksizes[0]==ci->blocksizes[1])modes=1; + + for(i=0;imap_param[i]=_ogg_calloc(1,sizeof(*map)); + ci->mode_param[i]=(vorbis_info_mode*)_ogg_calloc(1,sizeof(*mode)); + + memcpy(ci->mode_param[i],mode+i,sizeof(*_mode_template)); + if(i>=ci->modes)ci->modes=i+1; + + ci->map_type[i]=0; + memcpy(ci->map_param[i],map+i,sizeof(*map)); + if(i>=ci->maps)ci->maps=i+1; + + for(j=0;jcodec_setup; + highlevel_encode_setup *hi=&ci->hi; + ve_setup_data_template *setup=(ve_setup_data_template *)hi->setup; + int is=hi->base_setting; + double ds=hi->base_setting-is; + int ch=vi->channels; + double *r=setup->rate_mapping; + + if(r==NULL) + return(-1); + + return((r[is]*(1.-ds)+r[is+1]*ds)*ch); +} + +static void get_setup_template(vorbis_info *vi, + long ch,long srate, + double req,int q_or_bitrate){ + int i=0,j; + codec_setup_info *ci=(codec_setup_info*) vi->codec_setup; + highlevel_encode_setup *hi=&ci->hi; + if(q_or_bitrate)req/=ch; + + while(setup_list[i]){ + if(setup_list[i]->coupling_restriction==-1 || + setup_list[i]->coupling_restriction==ch){ + if(srate>=setup_list[i]->samplerate_min_restriction && + srate<=setup_list[i]->samplerate_max_restriction){ + int mappings=setup_list[i]->mappings; + double *map=(q_or_bitrate? + setup_list[i]->rate_mapping: + setup_list[i]->quality_mapping); + + /* the template matches. Does the requested quality mode + fall within this template's modes? */ + if(reqmap[setup_list[i]->mappings]){++i;continue;} + for(j=0;j=map[j] && reqsetup=setup_list[i]; + if(j==mappings) + hi->base_setting=j-.001; + else{ + float low=map[j]; + float high=map[j+1]; + float del=(req-low)/(high-low); + hi->base_setting=j+del; + } + + return; + } + } + i++; + } + + hi->setup=NULL; +} + +/* encoders will need to use vorbis_info_init beforehand and call + vorbis_info clear when all done */ + +/* two interfaces; this, more detailed one, and later a convenience + layer on top */ + +/* the final setup call */ +int vorbis_encode_setup_init(vorbis_info *vi){ + int i0=0,singleblock=0; + codec_setup_info *ci=(codec_setup_info*) vi->codec_setup; + ve_setup_data_template *setup=NULL; + highlevel_encode_setup *hi=&ci->hi; + + if(ci==NULL)return(OV_EINVAL); + if(!hi->impulse_block_p)i0=1; + + /* too low/high an ATH floater is nonsensical, but doesn't break anything */ + if(hi->ath_floating_dB>-80)hi->ath_floating_dB=-80; + if(hi->ath_floating_dB<-200)hi->ath_floating_dB=-200; + + /* again, bound this to avoid the app shooting itself int he foot + too badly */ + if(hi->amplitude_track_dBpersec>0.)hi->amplitude_track_dBpersec=0.; + if(hi->amplitude_track_dBpersec<-99999.)hi->amplitude_track_dBpersec=-99999.; + + /* get the appropriate setup template; matches the fetch in previous + stages */ + setup=(ve_setup_data_template *)hi->setup; + if(setup==NULL)return(OV_EINVAL); + + hi->set_in_stone=1; + /* choose block sizes from configured sizes as well as paying + attention to long_block_p and short_block_p. If the configured + short and long blocks are the same length, we set long_block_p + and unset short_block_p */ + vorbis_encode_blocksize_setup(vi,hi->base_setting, + setup->blocksize_short, + setup->blocksize_long); + if(ci->blocksizes[0]==ci->blocksizes[1])singleblock=1; + + /* floor setup; choose proper floor params. Allocated on the floor + stack in order; if we alloc only long floor, it's 0 */ + vorbis_encode_floor_setup(vi,hi->short_setting,0, + setup->floor_books, + setup->floor_params, + setup->floor_short_mapping); + if(!singleblock) + vorbis_encode_floor_setup(vi,hi->long_setting,1, + setup->floor_books, + setup->floor_params, + setup->floor_long_mapping); + + /* setup of [mostly] short block detection and stereo*/ + vorbis_encode_global_psych_setup(vi,hi->trigger_setting, + setup->global_params, + setup->global_mapping); + vorbis_encode_global_stereo(vi,hi,setup->stereo_modes); + + /* basic psych setup and noise normalization */ + vorbis_encode_psyset_setup(vi,hi->short_setting, + setup->psy_noise_normal_start[0], + setup->psy_noise_normal_partition[0], + setup->psy_noise_normal_thresh, + 0); + vorbis_encode_psyset_setup(vi,hi->short_setting, + setup->psy_noise_normal_start[0], + setup->psy_noise_normal_partition[0], + setup->psy_noise_normal_thresh, + 1); + if(!singleblock){ + vorbis_encode_psyset_setup(vi,hi->long_setting, + setup->psy_noise_normal_start[1], + setup->psy_noise_normal_partition[1], + setup->psy_noise_normal_thresh, + 2); + vorbis_encode_psyset_setup(vi,hi->long_setting, + setup->psy_noise_normal_start[1], + setup->psy_noise_normal_partition[1], + setup->psy_noise_normal_thresh, + 3); + } + + /* tone masking setup */ + vorbis_encode_tonemask_setup(vi,hi->block[i0].tone_mask_setting,0, + setup->psy_tone_masteratt, + setup->psy_tone_0dB, + setup->psy_tone_adj_impulse); + vorbis_encode_tonemask_setup(vi,hi->block[1].tone_mask_setting,1, + setup->psy_tone_masteratt, + setup->psy_tone_0dB, + setup->psy_tone_adj_other); + if(!singleblock){ + vorbis_encode_tonemask_setup(vi,hi->block[2].tone_mask_setting,2, + setup->psy_tone_masteratt, + setup->psy_tone_0dB, + setup->psy_tone_adj_other); + vorbis_encode_tonemask_setup(vi,hi->block[3].tone_mask_setting,3, + setup->psy_tone_masteratt, + setup->psy_tone_0dB, + setup->psy_tone_adj_long); + } + + /* noise companding setup */ + vorbis_encode_compand_setup(vi,hi->block[i0].noise_compand_setting,0, + setup->psy_noise_compand, + setup->psy_noise_compand_short_mapping); + vorbis_encode_compand_setup(vi,hi->block[1].noise_compand_setting,1, + setup->psy_noise_compand, + setup->psy_noise_compand_short_mapping); + if(!singleblock){ + vorbis_encode_compand_setup(vi,hi->block[2].noise_compand_setting,2, + setup->psy_noise_compand, + setup->psy_noise_compand_long_mapping); + vorbis_encode_compand_setup(vi,hi->block[3].noise_compand_setting,3, + setup->psy_noise_compand, + setup->psy_noise_compand_long_mapping); + } + + /* peak guarding setup */ + vorbis_encode_peak_setup(vi,hi->block[i0].tone_peaklimit_setting,0, + setup->psy_tone_dBsuppress); + vorbis_encode_peak_setup(vi,hi->block[1].tone_peaklimit_setting,1, + setup->psy_tone_dBsuppress); + if(!singleblock){ + vorbis_encode_peak_setup(vi,hi->block[2].tone_peaklimit_setting,2, + setup->psy_tone_dBsuppress); + vorbis_encode_peak_setup(vi,hi->block[3].tone_peaklimit_setting,3, + setup->psy_tone_dBsuppress); + } + + /* noise bias setup */ + vorbis_encode_noisebias_setup(vi,hi->block[i0].noise_bias_setting,0, + setup->psy_noise_dBsuppress, + setup->psy_noise_bias_impulse, + setup->psy_noiseguards, + (i0==0?hi->impulse_noisetune:0.)); + vorbis_encode_noisebias_setup(vi,hi->block[1].noise_bias_setting,1, + setup->psy_noise_dBsuppress, + setup->psy_noise_bias_padding, + setup->psy_noiseguards,0.); + if(!singleblock){ + vorbis_encode_noisebias_setup(vi,hi->block[2].noise_bias_setting,2, + setup->psy_noise_dBsuppress, + setup->psy_noise_bias_trans, + setup->psy_noiseguards,0.); + vorbis_encode_noisebias_setup(vi,hi->block[3].noise_bias_setting,3, + setup->psy_noise_dBsuppress, + setup->psy_noise_bias_long, + setup->psy_noiseguards,0.); + } + + vorbis_encode_ath_setup(vi,0); + vorbis_encode_ath_setup(vi,1); + if(!singleblock){ + vorbis_encode_ath_setup(vi,2); + vorbis_encode_ath_setup(vi,3); + } + + vorbis_encode_map_n_res_setup(vi,hi->base_setting,setup->maps); + + /* set bitrate readonlies and management */ + if(hi->bitrate_av>0) + vi->bitrate_nominal=hi->bitrate_av; + else{ + vi->bitrate_nominal=setting_to_approx_bitrate(vi); + } + + vi->bitrate_lower=hi->bitrate_min; + vi->bitrate_upper=hi->bitrate_max; + if(hi->bitrate_av) + vi->bitrate_window=(double)hi->bitrate_reservoir/hi->bitrate_av; + else + vi->bitrate_window=0.; + + if(hi->managed){ + ci->bi.avg_rate=hi->bitrate_av; + ci->bi.min_rate=hi->bitrate_min; + ci->bi.max_rate=hi->bitrate_max; + + ci->bi.reservoir_bits=hi->bitrate_reservoir; + ci->bi.reservoir_bias= + hi->bitrate_reservoir_bias; + + ci->bi.slew_damp=hi->bitrate_av_damp; + + } + + return(0); + +} + +static int vorbis_encode_setup_setting(vorbis_info *vi, + long channels, + long rate){ + int ret=0,i,is; + codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; + highlevel_encode_setup *hi=&ci->hi; + ve_setup_data_template *setup=(ve_setup_data_template*) hi->setup; + double ds; + + ret=vorbis_encode_toplevel_setup(vi,channels,rate); + if(ret)return(ret); + + is=hi->base_setting; + ds=hi->base_setting-is; + + hi->short_setting=hi->base_setting; + hi->long_setting=hi->base_setting; + + hi->managed=0; + + hi->impulse_block_p=1; + hi->noise_normalize_p=1; + + hi->stereo_point_setting=hi->base_setting; + hi->lowpass_kHz= + setup->psy_lowpass[is]*(1.-ds)+setup->psy_lowpass[is+1]*ds; + + hi->ath_floating_dB=setup->psy_ath_float[is]*(1.-ds)+ + setup->psy_ath_float[is+1]*ds; + hi->ath_absolute_dB=setup->psy_ath_abs[is]*(1.-ds)+ + setup->psy_ath_abs[is+1]*ds; + + hi->amplitude_track_dBpersec=-6.; + hi->trigger_setting=hi->base_setting; + + for(i=0;i<4;i++){ + hi->block[i].tone_mask_setting=hi->base_setting; + hi->block[i].tone_peaklimit_setting=hi->base_setting; + hi->block[i].noise_bias_setting=hi->base_setting; + hi->block[i].noise_compand_setting=hi->base_setting; + } + + return(ret); +} + +int vorbis_encode_setup_vbr(vorbis_info *vi, + long channels, + long rate, + float quality){ + codec_setup_info *ci=(codec_setup_info*) vi->codec_setup; + highlevel_encode_setup *hi=&ci->hi; + + quality+=.0000001; + if(quality>=1.)quality=.9999; + + get_setup_template(vi,channels,rate,quality,0); + if(!hi->setup)return OV_EIMPL; + + return vorbis_encode_setup_setting(vi,channels,rate); +} + +int vorbis_encode_init_vbr(vorbis_info *vi, + long channels, + long rate, + + float base_quality /* 0. to 1. */ + ){ + int ret=0; + + ret=vorbis_encode_setup_vbr(vi,channels,rate,base_quality); + + if(ret){ + vorbis_info_clear(vi); + return ret; + } + ret=vorbis_encode_setup_init(vi); + if(ret) + vorbis_info_clear(vi); + return(ret); +} + +int vorbis_encode_setup_managed(vorbis_info *vi, + long channels, + long rate, + + long max_bitrate, + long nominal_bitrate, + long min_bitrate){ + + codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; + highlevel_encode_setup *hi=&ci->hi; + double tnominal=nominal_bitrate; + int ret=0; + + if(nominal_bitrate<=0.){ + if(max_bitrate>0.){ + if(min_bitrate>0.) + nominal_bitrate=(max_bitrate+min_bitrate)*.5; + else + nominal_bitrate=max_bitrate*.875; + }else{ + if(min_bitrate>0.){ + nominal_bitrate=min_bitrate; + }else{ + return(OV_EINVAL); + } + } + } + + get_setup_template(vi,channels,rate,nominal_bitrate,1); + if(!hi->setup)return OV_EIMPL; + + ret=vorbis_encode_setup_setting(vi,channels,rate); + if(ret){ + vorbis_info_clear(vi); + return ret; + } + + /* initialize management with sane defaults */ + hi->managed=1; + hi->bitrate_min=min_bitrate; + hi->bitrate_max=max_bitrate; + hi->bitrate_av=tnominal; + hi->bitrate_av_damp=1.5f; /* full range in no less than 1.5 second */ + hi->bitrate_reservoir=nominal_bitrate*2; + hi->bitrate_reservoir_bias=.1; /* bias toward hoarding bits */ + + return(ret); + +} + +int vorbis_encode_init(vorbis_info *vi, + long channels, + long rate, + + long max_bitrate, + long nominal_bitrate, + long min_bitrate){ + + int ret=vorbis_encode_setup_managed(vi,channels,rate, + max_bitrate, + nominal_bitrate, + min_bitrate); + if(ret){ + vorbis_info_clear(vi); + return(ret); + } + + ret=vorbis_encode_setup_init(vi); + if(ret) + vorbis_info_clear(vi); + return(ret); +} + +int vorbis_encode_ctl(vorbis_info *vi,int number,void *arg){ + if(vi){ + codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; + highlevel_encode_setup *hi=&ci->hi; + int setp=(number&0xf); /* a read request has a low nibble of 0 */ + + if(setp && hi->set_in_stone)return(OV_EINVAL); + + switch(number){ + + /* now deprecated *****************/ + case OV_ECTL_RATEMANAGE_GET: + { + + struct ovectl_ratemanage_arg *ai= + (struct ovectl_ratemanage_arg *)arg; + + ai->management_active=hi->managed; + ai->bitrate_hard_window=ai->bitrate_av_window= + (double)hi->bitrate_reservoir/vi->rate; + ai->bitrate_av_window_center=1.; + ai->bitrate_hard_min=hi->bitrate_min; + ai->bitrate_hard_max=hi->bitrate_max; + ai->bitrate_av_lo=hi->bitrate_av; + ai->bitrate_av_hi=hi->bitrate_av; + + } + return(0); + + /* now deprecated *****************/ + case OV_ECTL_RATEMANAGE_SET: + { + struct ovectl_ratemanage_arg *ai= + (struct ovectl_ratemanage_arg *)arg; + if(ai==NULL){ + hi->managed=0; + }else{ + hi->managed=ai->management_active; + vorbis_encode_ctl(vi,OV_ECTL_RATEMANAGE_AVG,arg); + vorbis_encode_ctl(vi,OV_ECTL_RATEMANAGE_HARD,arg); + } + } + return 0; + + /* now deprecated *****************/ + case OV_ECTL_RATEMANAGE_AVG: + { + struct ovectl_ratemanage_arg *ai= + (struct ovectl_ratemanage_arg *)arg; + if(ai==NULL){ + hi->bitrate_av=0; + }else{ + hi->bitrate_av=(ai->bitrate_av_lo+ai->bitrate_av_hi)*.5; + } + } + return(0); + /* now deprecated *****************/ + case OV_ECTL_RATEMANAGE_HARD: + { + struct ovectl_ratemanage_arg *ai= + (struct ovectl_ratemanage_arg *)arg; + if(ai==NULL){ + hi->bitrate_min=0; + hi->bitrate_max=0; + }else{ + hi->bitrate_min=ai->bitrate_hard_min; + hi->bitrate_max=ai->bitrate_hard_max; + hi->bitrate_reservoir=ai->bitrate_hard_window* + (hi->bitrate_max+hi->bitrate_min)*.5; + } + if(hi->bitrate_reservoir<128.) + hi->bitrate_reservoir=128.; + } + return(0); + + /* replacement ratemanage interface */ + case OV_ECTL_RATEMANAGE2_GET: + { + struct ovectl_ratemanage2_arg *ai= + (struct ovectl_ratemanage2_arg *)arg; + if(ai==NULL)return OV_EINVAL; + + ai->management_active=hi->managed; + ai->bitrate_limit_min_kbps=hi->bitrate_min/1000; + ai->bitrate_limit_max_kbps=hi->bitrate_max/1000; + ai->bitrate_average_kbps=hi->bitrate_av/1000; + ai->bitrate_average_damping=hi->bitrate_av_damp; + ai->bitrate_limit_reservoir_bits=hi->bitrate_reservoir; + ai->bitrate_limit_reservoir_bias=hi->bitrate_reservoir_bias; + } + return (0); + case OV_ECTL_RATEMANAGE2_SET: + { + struct ovectl_ratemanage2_arg *ai= + (struct ovectl_ratemanage2_arg *)arg; + if(ai==NULL){ + hi->managed=0; + }else{ + /* sanity check; only catch invariant violations */ + if(ai->bitrate_limit_min_kbps>0 && + ai->bitrate_average_kbps>0 && + ai->bitrate_limit_min_kbps>ai->bitrate_average_kbps) + return OV_EINVAL; + + if(ai->bitrate_limit_max_kbps>0 && + ai->bitrate_average_kbps>0 && + ai->bitrate_limit_max_kbpsbitrate_average_kbps) + return OV_EINVAL; + + if(ai->bitrate_limit_min_kbps>0 && + ai->bitrate_limit_max_kbps>0 && + ai->bitrate_limit_min_kbps>ai->bitrate_limit_max_kbps) + return OV_EINVAL; + + if(ai->bitrate_average_damping <= 0.) + return OV_EINVAL; + + if(ai->bitrate_limit_reservoir_bits < 0) + return OV_EINVAL; + + if(ai->bitrate_limit_reservoir_bias < 0.) + return OV_EINVAL; + + if(ai->bitrate_limit_reservoir_bias > 1.) + return OV_EINVAL; + + hi->managed=ai->management_active; + hi->bitrate_min=ai->bitrate_limit_min_kbps * 1000; + hi->bitrate_max=ai->bitrate_limit_max_kbps * 1000; + hi->bitrate_av=ai->bitrate_average_kbps * 1000; + hi->bitrate_av_damp=ai->bitrate_average_damping; + hi->bitrate_reservoir=ai->bitrate_limit_reservoir_bits; + hi->bitrate_reservoir_bias=ai->bitrate_limit_reservoir_bias; + } + } + return 0; + + case OV_ECTL_LOWPASS_GET: + { + double *farg=(double *)arg; + *farg=hi->lowpass_kHz; + } + return(0); + case OV_ECTL_LOWPASS_SET: + { + double *farg=(double *)arg; + hi->lowpass_kHz=*farg; + + if(hi->lowpass_kHz<2.)hi->lowpass_kHz=2.; + if(hi->lowpass_kHz>99.)hi->lowpass_kHz=99.; + } + return(0); + case OV_ECTL_IBLOCK_GET: + { + double *farg=(double *)arg; + *farg=hi->impulse_noisetune; + } + return(0); + case OV_ECTL_IBLOCK_SET: + { + double *farg=(double *)arg; + hi->impulse_noisetune=*farg; + + if(hi->impulse_noisetune>0.)hi->impulse_noisetune=0.; + if(hi->impulse_noisetune<-15.)hi->impulse_noisetune=-15.; + } + return(0); + } + + return(OV_EIMPL); + } + return(OV_EINVAL); +} + +#endif +/********* End of inlined file: vorbisenc.c *********/ + +/********* Start of inlined file: vorbisfile.c *********/ + +/********* Start of inlined file: juce_OggVorbisHeader.h *********/ +// This file is included at the start of each Ogg-Vorbis .c file, just to do a few housekeeping +// tasks.. + +#ifdef _MSC_VER + #pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4305 4189 4706) +#endif +/********* End of inlined file: juce_OggVorbisHeader.h *********/ + +#if JUCE_USE_OGGVORBIS + +#include +#include +#include +#include +#include + +/* A 'chained bitstream' is a Vorbis bitstream that contains more than + one logical bitstream arranged end to end (the only form of Ogg + multiplexing allowed in a Vorbis bitstream; grouping [parallel + multiplexing] is not allowed in Vorbis) */ + +/* A Vorbis file can be played beginning to end (streamed) without + worrying ahead of time about chaining (see decoder_example.c). If + we have the whole file, however, and want random access + (seeking/scrubbing) or desire to know the total length/time of a + file, we need to account for the possibility of chaining. */ + +/* We can handle things a number of ways; we can determine the entire + bitstream structure right off the bat, or find pieces on demand. + This example determines and caches structure for the entire + bitstream, but builds a virtual decoder on the fly when moving + between links in the chain. */ + +/* There are also different ways to implement seeking. Enough + information exists in an Ogg bitstream to seek to + sample-granularity positions in the output. Or, one can seek by + picking some portion of the stream roughly in the desired area if + we only want coarse navigation through the stream. */ + +/************************************************************************* + * Many, many internal helpers. The intention is not to be confusing; + * rampant duplication and monolithic function implementation would be + * harder to understand anyway. The high level functions are last. Begin + * grokking near the end of the file */ + +/* read a little more data from the file/pipe into the ogg_sync framer +*/ +#define CHUNKSIZE 8500 /* a shade over 8k; anyone using pages well + over 8k gets what they deserve */ +static long _get_data(OggVorbis_File *vf){ + errno=0; + if(vf->datasource){ + char *buffer=ogg_sync_buffer(&vf->oy,CHUNKSIZE); + long bytes=(vf->callbacks.read_func)(buffer,1,CHUNKSIZE,vf->datasource); + if(bytes>0)ogg_sync_wrote(&vf->oy,bytes); + if(bytes==0 && errno)return(-1); + return(bytes); + }else + return(0); +} + +/* save a tiny smidge of verbosity to make the code more readable */ +static void _seek_helper(OggVorbis_File *vf,ogg_int64_t offset){ + if(vf->datasource){ + (vf->callbacks.seek_func)(vf->datasource, offset, SEEK_SET); + vf->offset=offset; + ogg_sync_reset(&vf->oy); + }else{ + /* shouldn't happen unless someone writes a broken callback */ + return; + } +} + +/* The read/seek functions track absolute position within the stream */ + +/* from the head of the stream, get the next page. boundary specifies + if the function is allowed to fetch more data from the stream (and + how much) or only use internally buffered data. + + boundary: -1) unbounded search + 0) read no additional data; use cached only + n) search for a new page beginning for n bytes + + return: <0) did not find a page (OV_FALSE, OV_EOF, OV_EREAD) + n) found a page at absolute offset n */ + +static ogg_int64_t _get_next_page(OggVorbis_File *vf,ogg_page *og, + ogg_int64_t boundary){ + if(boundary>0)boundary+=vf->offset; + while(1){ + long more; + + if(boundary>0 && vf->offset>=boundary)return(OV_FALSE); + more=ogg_sync_pageseek(&vf->oy,og); + + if(more<0){ + /* skipped n bytes */ + vf->offset-=more; + }else{ + if(more==0){ + /* send more paramedics */ + if(!boundary)return(OV_FALSE); + { + long ret=_get_data(vf); + if(ret==0)return(OV_EOF); + if(ret<0)return(OV_EREAD); + } + }else{ + /* got a page. Return the offset at the page beginning, + advance the internal offset past the page end */ + ogg_int64_t ret=vf->offset; + vf->offset+=more; + return(ret); + + } + } + } +} + +/* find the latest page beginning before the current stream cursor + position. Much dirtier than the above as Ogg doesn't have any + backward search linkage. no 'readp' as it will certainly have to + read. */ +/* returns offset or OV_EREAD, OV_FAULT */ +static ogg_int64_t _get_prev_page(OggVorbis_File *vf,ogg_page *og){ + ogg_int64_t begin=vf->offset; + ogg_int64_t end=begin; + ogg_int64_t ret; + ogg_int64_t offset=-1; + + while(offset==-1){ + begin-=CHUNKSIZE; + if(begin<0) + begin=0; + _seek_helper(vf,begin); + while(vf->offsetoffset); + if(ret==OV_EREAD)return(OV_EREAD); + if(ret<0){ + break; + }else{ + offset=ret; + } + } + } + + /* we have the offset. Actually snork and hold the page now */ + _seek_helper(vf,offset); + ret=_get_next_page(vf,og,CHUNKSIZE); + if(ret<0) + /* this shouldn't be possible */ + return(OV_EFAULT); + + return(offset); +} + +/* finds each bitstream link one at a time using a bisection search + (has to begin by knowing the offset of the lb's initial page). + Recurses for each link so it can alloc the link storage after + finding them all, then unroll and fill the cache at the same time */ +static int _bisect_forward_serialno(OggVorbis_File *vf, + ogg_int64_t begin, + ogg_int64_t searched, + ogg_int64_t end, + long currentno, + long m){ + ogg_int64_t endsearched=end; + ogg_int64_t next=end; + ogg_page og; + ogg_int64_t ret; + + /* the below guards against garbage seperating the last and + first pages of two links. */ + while(searched=0)next=ret; + }else{ + searched=ret+og.header_len+og.body_len; + } + } + + _seek_helper(vf,next); + ret=_get_next_page(vf,&og,-1); + if(ret==OV_EREAD)return(OV_EREAD); + + if(searched>=end || ret<0){ + vf->links=m+1; + vf->offsets=(ogg_int64_t*)_ogg_malloc((vf->links+1)*sizeof(*vf->offsets)); + vf->serialnos=(long*)_ogg_malloc(vf->links*sizeof(*vf->serialnos)); + vf->offsets[m+1]=searched; + }else{ + ret=_bisect_forward_serialno(vf,next,vf->offset, + end,ogg_page_serialno(&og),m+1); + if(ret==OV_EREAD)return(OV_EREAD); + } + + vf->offsets[m]=begin; + vf->serialnos[m]=currentno; + return(0); +} + +/* uses the local ogg_stream storage in vf; this is important for + non-streaming input sources */ +static int _fetch_headers(OggVorbis_File *vf,vorbis_info *vi,vorbis_comment *vc, + long *serialno,ogg_page *og_ptr){ + ogg_page og; + ogg_packet op; + int i,ret; + + if(!og_ptr){ + ogg_int64_t llret=_get_next_page(vf,&og,CHUNKSIZE); + if(llret==OV_EREAD)return(OV_EREAD); + if(llret<0)return OV_ENOTVORBIS; + og_ptr=&og; + } + + ogg_stream_reset_serialno(&vf->os,ogg_page_serialno(og_ptr)); + if(serialno)*serialno=vf->os.serialno; + vf->ready_state=STREAMSET; + + /* extract the initial header from the first page and verify that the + Ogg bitstream is in fact Vorbis data */ + + vorbis_info_init(vi); + vorbis_comment_init(vc); + + i=0; + while(i<3){ + ogg_stream_pagein(&vf->os,og_ptr); + while(i<3){ + int result=ogg_stream_packetout(&vf->os,&op); + if(result==0)break; + if(result==-1){ + ret=OV_EBADHEADER; + goto bail_header; + } + if((ret=vorbis_synthesis_headerin(vi,vc,&op))){ + goto bail_header; + } + i++; + } + if(i<3) + if(_get_next_page(vf,og_ptr,CHUNKSIZE)<0){ + ret=OV_EBADHEADER; + goto bail_header; + } + } + return 0; + + bail_header: + vorbis_info_clear(vi); + vorbis_comment_clear(vc); + vf->ready_state=OPENED; + + return ret; +} + +/* last step of the OggVorbis_File initialization; get all the + vorbis_info structs and PCM positions. Only called by the seekable + initialization (local stream storage is hacked slightly; pay + attention to how that's done) */ + +/* this is void and does not propogate errors up because we want to be + able to open and use damaged bitstreams as well as we can. Just + watch out for missing information for links in the OggVorbis_File + struct */ +static void _prefetch_all_headers(OggVorbis_File *vf, ogg_int64_t dataoffset){ + ogg_page og; + int i; + ogg_int64_t ret; + + vf->vi=(vorbis_info*) _ogg_realloc(vf->vi,vf->links*sizeof(*vf->vi)); + vf->vc=(vorbis_comment*) _ogg_realloc(vf->vc,vf->links*sizeof(*vf->vc)); + vf->dataoffsets=(ogg_int64_t*) _ogg_malloc(vf->links*sizeof(*vf->dataoffsets)); + vf->pcmlengths=(ogg_int64_t*) _ogg_malloc(vf->links*2*sizeof(*vf->pcmlengths)); + + for(i=0;ilinks;i++){ + if(i==0){ + /* we already grabbed the initial header earlier. Just set the offset */ + vf->dataoffsets[i]=dataoffset; + _seek_helper(vf,dataoffset); + + }else{ + + /* seek to the location of the initial header */ + + _seek_helper(vf,vf->offsets[i]); + if(_fetch_headers(vf,vf->vi+i,vf->vc+i,NULL,NULL)<0){ + vf->dataoffsets[i]=-1; + }else{ + vf->dataoffsets[i]=vf->offset; + } + } + + /* fetch beginning PCM offset */ + + if(vf->dataoffsets[i]!=-1){ + ogg_int64_t accumulated=0; + long lastblock=-1; + int result; + + ogg_stream_reset_serialno(&vf->os,vf->serialnos[i]); + + while(1){ + ogg_packet op; + + ret=_get_next_page(vf,&og,-1); + if(ret<0) + /* this should not be possible unless the file is + truncated/mangled */ + break; + + if(ogg_page_serialno(&og)!=vf->serialnos[i]) + break; + + /* count blocksizes of all frames in the page */ + ogg_stream_pagein(&vf->os,&og); + while((result=ogg_stream_packetout(&vf->os,&op))){ + if(result>0){ /* ignore holes */ + long thisblock=vorbis_packet_blocksize(vf->vi+i,&op); + if(lastblock!=-1) + accumulated+=(lastblock+thisblock)>>2; + lastblock=thisblock; + } + } + + if(ogg_page_granulepos(&og)!=-1){ + /* pcm offset of last packet on the first audio page */ + accumulated= ogg_page_granulepos(&og)-accumulated; + break; + } + } + + /* less than zero? This is a stream with samples trimmed off + the beginning, a normal occurrence; set the offset to zero */ + if(accumulated<0)accumulated=0; + + vf->pcmlengths[i*2]=accumulated; + } + + /* get the PCM length of this link. To do this, + get the last page of the stream */ + { + ogg_int64_t end=vf->offsets[i+1]; + _seek_helper(vf,end); + + while(1){ + ret=_get_prev_page(vf,&og); + if(ret<0){ + /* this should not be possible */ + vorbis_info_clear(vf->vi+i); + vorbis_comment_clear(vf->vc+i); + break; + } + if(ogg_page_granulepos(&og)!=-1){ + vf->pcmlengths[i*2+1]=ogg_page_granulepos(&og)-vf->pcmlengths[i*2]; + break; + } + vf->offset=ret; + } + } + } +} + +static int _make_decode_ready(OggVorbis_File *vf){ + if(vf->ready_state>STREAMSET)return 0; + if(vf->ready_stateseekable){ + if(vorbis_synthesis_init(&vf->vd,vf->vi+vf->current_link)) + return OV_EBADLINK; + }else{ + if(vorbis_synthesis_init(&vf->vd,vf->vi)) + return OV_EBADLINK; + } + vorbis_block_init(&vf->vd,&vf->vb); + vf->ready_state=INITSET; + vf->bittrack=0.f; + vf->samptrack=0.f; + return 0; +} + +static int _open_seekable2(OggVorbis_File *vf){ + long serialno=vf->current_serialno; + ogg_int64_t dataoffset=vf->offset, end; + ogg_page og; + + /* we're partially open and have a first link header state in + storage in vf */ + /* we can seek, so set out learning all about this file */ + (vf->callbacks.seek_func)(vf->datasource,0,SEEK_END); + vf->offset=vf->end=(vf->callbacks.tell_func)(vf->datasource); + + /* We get the offset for the last page of the physical bitstream. + Most OggVorbis files will contain a single logical bitstream */ + end=_get_prev_page(vf,&og); + if(end<0)return(end); + + /* more than one logical bitstream? */ + if(ogg_page_serialno(&og)!=serialno){ + + /* Chained bitstream. Bisect-search each logical bitstream + section. Do so based on serial number only */ + if(_bisect_forward_serialno(vf,0,0,end+1,serialno,0)<0)return(OV_EREAD); + + }else{ + + /* Only one logical bitstream */ + if(_bisect_forward_serialno(vf,0,end,end+1,serialno,0))return(OV_EREAD); + + } + + /* the initial header memory is referenced by vf after; don't free it */ + _prefetch_all_headers(vf,dataoffset); + return(ov_raw_seek(vf,0)); +} + +/* clear out the current logical bitstream decoder */ +static void _decode_clear(OggVorbis_File *vf){ + vorbis_dsp_clear(&vf->vd); + vorbis_block_clear(&vf->vb); + vf->ready_state=OPENED; +} + +/* fetch and process a packet. Handles the case where we're at a + bitstream boundary and dumps the decoding machine. If the decoding + machine is unloaded, it loads it. It also keeps pcm_offset up to + date (seek and read both use this. seek uses a special hack with + readp). + + return: <0) error, OV_HOLE (lost packet) or OV_EOF + 0) need more data (only if readp==0) + 1) got a packet +*/ + +static int _fetch_and_process_packet(OggVorbis_File *vf, + ogg_packet *op_in, + int readp, + int spanp){ + ogg_page og; + + /* handle one packet. Try to fetch it from current stream state */ + /* extract packets from page */ + while(1){ + + /* process a packet if we can. If the machine isn't loaded, + neither is a page */ + if(vf->ready_state==INITSET){ + while(1) { + ogg_packet op; + ogg_packet *op_ptr=(op_in?op_in:&op); + int result=ogg_stream_packetout(&vf->os,op_ptr); + ogg_int64_t granulepos; + + op_in=NULL; + if(result==-1)return(OV_HOLE); /* hole in the data. */ + if(result>0){ + /* got a packet. process it */ + granulepos=op_ptr->granulepos; + if(!vorbis_synthesis(&vf->vb,op_ptr)){ /* lazy check for lazy + header handling. The + header packets aren't + audio, so if/when we + submit them, + vorbis_synthesis will + reject them */ + + /* suck in the synthesis data and track bitrate */ + { + int oldsamples=vorbis_synthesis_pcmout(&vf->vd,NULL); + /* for proper use of libvorbis within libvorbisfile, + oldsamples will always be zero. */ + if(oldsamples)return(OV_EFAULT); + + vorbis_synthesis_blockin(&vf->vd,&vf->vb); + vf->samptrack+=vorbis_synthesis_pcmout(&vf->vd,NULL)-oldsamples; + vf->bittrack+=op_ptr->bytes*8; + } + + /* update the pcm offset. */ + if(granulepos!=-1 && !op_ptr->e_o_s){ + int link=(vf->seekable?vf->current_link:0); + int i,samples; + + /* this packet has a pcm_offset on it (the last packet + completed on a page carries the offset) After processing + (above), we know the pcm position of the *last* sample + ready to be returned. Find the offset of the *first* + + As an aside, this trick is inaccurate if we begin + reading anew right at the last page; the end-of-stream + granulepos declares the last frame in the stream, and the + last packet of the last page may be a partial frame. + So, we need a previous granulepos from an in-sequence page + to have a reference point. Thus the !op_ptr->e_o_s clause + above */ + + if(vf->seekable && link>0) + granulepos-=vf->pcmlengths[link*2]; + if(granulepos<0)granulepos=0; /* actually, this + shouldn't be possible + here unless the stream + is very broken */ + + samples=vorbis_synthesis_pcmout(&vf->vd,NULL); + + granulepos-=samples; + for(i=0;ipcmlengths[i*2+1]; + vf->pcm_offset=granulepos; + } + return(1); + } + } + else + break; + } + } + + if(vf->ready_state>=OPENED){ + ogg_int64_t ret; + if(!readp)return(0); + if((ret=_get_next_page(vf,&og,-1))<0){ + return(OV_EOF); /* eof. + leave unitialized */ + } + + /* bitrate tracking; add the header's bytes here, the body bytes + are done by packet above */ + vf->bittrack+=og.header_len*8; + + /* has our decoding just traversed a bitstream boundary? */ + if(vf->ready_state==INITSET){ + if(vf->current_serialno!=ogg_page_serialno(&og)){ + if(!spanp) + return(OV_EOF); + + _decode_clear(vf); + + if(!vf->seekable){ + vorbis_info_clear(vf->vi); + vorbis_comment_clear(vf->vc); + } + } + } + } + + /* Do we need to load a new machine before submitting the page? */ + /* This is different in the seekable and non-seekable cases. + + In the seekable case, we already have all the header + information loaded and cached; we just initialize the machine + with it and continue on our merry way. + + In the non-seekable (streaming) case, we'll only be at a + boundary if we just left the previous logical bitstream and + we're now nominally at the header of the next bitstream + */ + + if(vf->ready_state!=INITSET){ + int link; + + if(vf->ready_stateseekable){ + vf->current_serialno=ogg_page_serialno(&og); + + /* match the serialno to bitstream section. We use this rather than + offset positions to avoid problems near logical bitstream + boundaries */ + for(link=0;linklinks;link++) + if(vf->serialnos[link]==vf->current_serialno)break; + if(link==vf->links)return(OV_EBADLINK); /* sign of a bogus + stream. error out, + leave machine + uninitialized */ + + vf->current_link=link; + + ogg_stream_reset_serialno(&vf->os,vf->current_serialno); + vf->ready_state=STREAMSET; + + }else{ + /* we're streaming */ + /* fetch the three header packets, build the info struct */ + + int ret=_fetch_headers(vf,vf->vi,vf->vc,&vf->current_serialno,&og); + if(ret)return(ret); + vf->current_link++; + link=0; + } + } + + { + int ret=_make_decode_ready(vf); + if(ret<0)return ret; + } + } + ogg_stream_pagein(&vf->os,&og); + } +} + +/* if, eg, 64 bit stdio is configured by default, this will build with + fseek64 */ +static int _fseek64_wrap(FILE *f,ogg_int64_t off,int whence){ + if(f==NULL)return(-1); + return fseek(f,off,whence); +} + +static int _ov_open1(void *f,OggVorbis_File *vf,char *initial, + long ibytes, ov_callbacks callbacks){ + int offsettest=(f?callbacks.seek_func(f,0,SEEK_CUR):-1); + int ret; + + memset(vf,0,sizeof(*vf)); + vf->datasource=f; + vf->callbacks = callbacks; + + /* init the framing state */ + ogg_sync_init(&vf->oy); + + /* perhaps some data was previously read into a buffer for testing + against other stream types. Allow initialization from this + previously read data (as we may be reading from a non-seekable + stream) */ + if(initial){ + char *buffer=ogg_sync_buffer(&vf->oy,ibytes); + memcpy(buffer,initial,ibytes); + ogg_sync_wrote(&vf->oy,ibytes); + } + + /* can we seek? Stevens suggests the seek test was portable */ + if(offsettest!=-1)vf->seekable=1; + + /* No seeking yet; Set up a 'single' (current) logical bitstream + entry for partial open */ + vf->links=1; + vf->vi=(vorbis_info*) _ogg_calloc(vf->links,sizeof(*vf->vi)); + vf->vc=(vorbis_comment*) _ogg_calloc(vf->links,sizeof(*vf->vc)); + ogg_stream_init(&vf->os,-1); /* fill in the serialno later */ + + /* Try to fetch the headers, maintaining all the storage */ + if((ret=_fetch_headers(vf,vf->vi,vf->vc,&vf->current_serialno,NULL))<0){ + vf->datasource=NULL; + ov_clear(vf); + }else + vf->ready_state=PARTOPEN; + return(ret); +} + +static int _ov_open2(OggVorbis_File *vf){ + if(vf->ready_state != PARTOPEN) return OV_EINVAL; + vf->ready_state=OPENED; + if(vf->seekable){ + int ret=_open_seekable2(vf); + if(ret){ + vf->datasource=NULL; + ov_clear(vf); + } + return(ret); + }else + vf->ready_state=STREAMSET; + + return 0; +} + +/* clear out the OggVorbis_File struct */ +int ov_clear(OggVorbis_File *vf){ + if(vf){ + vorbis_block_clear(&vf->vb); + vorbis_dsp_clear(&vf->vd); + ogg_stream_clear(&vf->os); + + if(vf->vi && vf->links){ + int i; + for(i=0;ilinks;i++){ + vorbis_info_clear(vf->vi+i); + vorbis_comment_clear(vf->vc+i); + } + _ogg_free(vf->vi); + _ogg_free(vf->vc); + } + if(vf->dataoffsets)_ogg_free(vf->dataoffsets); + if(vf->pcmlengths)_ogg_free(vf->pcmlengths); + if(vf->serialnos)_ogg_free(vf->serialnos); + if(vf->offsets)_ogg_free(vf->offsets); + ogg_sync_clear(&vf->oy); + if(vf->datasource)(vf->callbacks.close_func)(vf->datasource); + memset(vf,0,sizeof(*vf)); + } +#ifdef DEBUG_LEAKS + _VDBG_dump(); +#endif + return(0); +} + +/* inspects the OggVorbis file and finds/documents all the logical + bitstreams contained in it. Tries to be tolerant of logical + bitstream sections that are truncated/woogie. + + return: -1) error + 0) OK +*/ + +int ov_open_callbacks(void *f,OggVorbis_File *vf,char *initial,long ibytes, + ov_callbacks callbacks){ + int ret=_ov_open1(f,vf,initial,ibytes,callbacks); + if(ret)return ret; + return _ov_open2(vf); +} + +int ov_open(FILE *f,OggVorbis_File *vf,char *initial,long ibytes){ + ov_callbacks callbacks = { + (size_t (*)(void *, size_t, size_t, void *)) fread, + (int (*)(void *, ogg_int64_t, int)) _fseek64_wrap, + (int (*)(void *)) fclose, + (long (*)(void *)) ftell + }; + + return ov_open_callbacks((void *)f, vf, initial, ibytes, callbacks); +} + +/* cheap hack for game usage where downsampling is desirable; there's + no need for SRC as we can just do it cheaply in libvorbis. */ + +int ov_halfrate(OggVorbis_File *vf,int flag){ + int i; + if(vf->vi==NULL)return OV_EINVAL; + if(!vf->seekable)return OV_EINVAL; + if(vf->ready_state>=STREAMSET) + _decode_clear(vf); /* clear out stream state; later on libvorbis + will be able to swap this on the fly, but + for now dumping the decode machine is needed + to reinit the MDCT lookups. 1.1 libvorbis + is planned to be able to switch on the fly */ + + for(i=0;ilinks;i++){ + if(vorbis_synthesis_halfrate(vf->vi+i,flag)){ + ov_halfrate(vf,0); + return OV_EINVAL; + } + } + return 0; +} + +int ov_halfrate_p(OggVorbis_File *vf){ + if(vf->vi==NULL)return OV_EINVAL; + return vorbis_synthesis_halfrate_p(vf->vi); +} + +/* Only partially open the vorbis file; test for Vorbisness, and load + the headers for the first chain. Do not seek (although test for + seekability). Use ov_test_open to finish opening the file, else + ov_clear to close/free it. Same return codes as open. */ + +int ov_test_callbacks(void *f,OggVorbis_File *vf,char *initial,long ibytes, + ov_callbacks callbacks) +{ + return _ov_open1(f,vf,initial,ibytes,callbacks); +} + +int ov_test(FILE *f,OggVorbis_File *vf,char *initial,long ibytes){ + ov_callbacks callbacks = { + (size_t (*)(void *, size_t, size_t, void *)) fread, + (int (*)(void *, ogg_int64_t, int)) _fseek64_wrap, + (int (*)(void *)) fclose, + (long (*)(void *)) ftell + }; + + return ov_test_callbacks((void *)f, vf, initial, ibytes, callbacks); +} + +int ov_test_open(OggVorbis_File *vf){ + if(vf->ready_state!=PARTOPEN)return(OV_EINVAL); + return _ov_open2(vf); +} + +/* How many logical bitstreams in this physical bitstream? */ +long ov_streams(OggVorbis_File *vf){ + return vf->links; +} + +/* Is the FILE * associated with vf seekable? */ +long ov_seekable(OggVorbis_File *vf){ + return vf->seekable; +} + +/* returns the bitrate for a given logical bitstream or the entire + physical bitstream. If the file is open for random access, it will + find the *actual* average bitrate. If the file is streaming, it + returns the nominal bitrate (if set) else the average of the + upper/lower bounds (if set) else -1 (unset). + + If you want the actual bitrate field settings, get them from the + vorbis_info structs */ + +long ov_bitrate(OggVorbis_File *vf,int i){ + if(vf->ready_state=vf->links)return(OV_EINVAL); + if(!vf->seekable && i!=0)return(ov_bitrate(vf,0)); + if(i<0){ + ogg_int64_t bits=0; + int i; + float br; + for(i=0;ilinks;i++) + bits+=(vf->offsets[i+1]-vf->dataoffsets[i])*8; + /* This once read: return(rint(bits/ov_time_total(vf,-1))); + * gcc 3.x on x86 miscompiled this at optimisation level 2 and above, + * so this is slightly transformed to make it work. + */ + br = bits/ov_time_total(vf,-1); + return(rint(br)); + }else{ + if(vf->seekable){ + /* return the actual bitrate */ + return(rint((vf->offsets[i+1]-vf->dataoffsets[i])*8/ov_time_total(vf,i))); + }else{ + /* return nominal if set */ + if(vf->vi[i].bitrate_nominal>0){ + return vf->vi[i].bitrate_nominal; + }else{ + if(vf->vi[i].bitrate_upper>0){ + if(vf->vi[i].bitrate_lower>0){ + return (vf->vi[i].bitrate_upper+vf->vi[i].bitrate_lower)/2; + }else{ + return vf->vi[i].bitrate_upper; + } + } + return(OV_FALSE); + } + } + } +} + +/* returns the actual bitrate since last call. returns -1 if no + additional data to offer since last call (or at beginning of stream), + EINVAL if stream is only partially open +*/ +long ov_bitrate_instant(OggVorbis_File *vf){ + int link=(vf->seekable?vf->current_link:0); + long ret; + if(vf->ready_statesamptrack==0)return(OV_FALSE); + ret=vf->bittrack/vf->samptrack*vf->vi[link].rate+.5; + vf->bittrack=0.f; + vf->samptrack=0.f; + return(ret); +} + +/* Guess */ +long ov_serialnumber(OggVorbis_File *vf,int i){ + if(i>=vf->links)return(ov_serialnumber(vf,vf->links-1)); + if(!vf->seekable && i>=0)return(ov_serialnumber(vf,-1)); + if(i<0){ + return(vf->current_serialno); + }else{ + return(vf->serialnos[i]); + } +} + +/* returns: total raw (compressed) length of content if i==-1 + raw (compressed) length of that logical bitstream for i==0 to n + OV_EINVAL if the stream is not seekable (we can't know the length) + or if stream is only partially open +*/ +ogg_int64_t ov_raw_total(OggVorbis_File *vf,int i){ + if(vf->ready_stateseekable || i>=vf->links)return(OV_EINVAL); + if(i<0){ + ogg_int64_t acc=0; + int i; + for(i=0;ilinks;i++) + acc+=ov_raw_total(vf,i); + return(acc); + }else{ + return(vf->offsets[i+1]-vf->offsets[i]); + } +} + +/* returns: total PCM length (samples) of content if i==-1 PCM length + (samples) of that logical bitstream for i==0 to n + OV_EINVAL if the stream is not seekable (we can't know the + length) or only partially open +*/ +ogg_int64_t ov_pcm_total(OggVorbis_File *vf,int i){ + if(vf->ready_stateseekable || i>=vf->links)return(OV_EINVAL); + if(i<0){ + ogg_int64_t acc=0; + int i; + for(i=0;ilinks;i++) + acc+=ov_pcm_total(vf,i); + return(acc); + }else{ + return(vf->pcmlengths[i*2+1]); + } +} + +/* returns: total seconds of content if i==-1 + seconds in that logical bitstream for i==0 to n + OV_EINVAL if the stream is not seekable (we can't know the + length) or only partially open +*/ +double ov_time_total(OggVorbis_File *vf,int i){ + if(vf->ready_stateseekable || i>=vf->links)return(OV_EINVAL); + if(i<0){ + double acc=0; + int i; + for(i=0;ilinks;i++) + acc+=ov_time_total(vf,i); + return(acc); + }else{ + return((double)(vf->pcmlengths[i*2+1])/vf->vi[i].rate); + } +} + +/* seek to an offset relative to the *compressed* data. This also + scans packets to update the PCM cursor. It will cross a logical + bitstream boundary, but only if it can't get any packets out of the + tail of the bitstream we seek to (so no surprises). + + returns zero on success, nonzero on failure */ + +int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos){ + ogg_stream_state work_os; + + if(vf->ready_stateseekable) + return(OV_ENOSEEK); /* don't dump machine if we can't seek */ + + if(pos<0 || pos>vf->end)return(OV_EINVAL); + + /* don't yet clear out decoding machine (if it's initialized), in + the case we're in the same link. Restart the decode lapping, and + let _fetch_and_process_packet deal with a potential bitstream + boundary */ + vf->pcm_offset=-1; + ogg_stream_reset_serialno(&vf->os, + vf->current_serialno); /* must set serialno */ + vorbis_synthesis_restart(&vf->vd); + + _seek_helper(vf,pos); + + /* we need to make sure the pcm_offset is set, but we don't want to + advance the raw cursor past good packets just to get to the first + with a granulepos. That's not equivalent behavior to beginning + decoding as immediately after the seek position as possible. + + So, a hack. We use two stream states; a local scratch state and + the shared vf->os stream state. We use the local state to + scan, and the shared state as a buffer for later decode. + + Unfortuantely, on the last page we still advance to last packet + because the granulepos on the last page is not necessarily on a + packet boundary, and we need to make sure the granpos is + correct. + */ + + { + ogg_page og; + ogg_packet op; + int lastblock=0; + int accblock=0; + int thisblock; + int eosflag; + + ogg_stream_init(&work_os,vf->current_serialno); /* get the memory ready */ + ogg_stream_reset(&work_os); /* eliminate the spurious OV_HOLE + return from not necessarily + starting from the beginning */ + + while(1){ + if(vf->ready_state>=STREAMSET){ + /* snarf/scan a packet if we can */ + int result=ogg_stream_packetout(&work_os,&op); + + if(result>0){ + + if(vf->vi[vf->current_link].codec_setup){ + thisblock=vorbis_packet_blocksize(vf->vi+vf->current_link,&op); + if(thisblock<0){ + ogg_stream_packetout(&vf->os,NULL); + thisblock=0; + }else{ + + if(eosflag) + ogg_stream_packetout(&vf->os,NULL); + else + if(lastblock)accblock+=(lastblock+thisblock)>>2; + } + + if(op.granulepos!=-1){ + int i,link=vf->current_link; + ogg_int64_t granulepos=op.granulepos-vf->pcmlengths[link*2]; + if(granulepos<0)granulepos=0; + + for(i=0;ipcmlengths[i*2+1]; + vf->pcm_offset=granulepos-accblock; + break; + } + lastblock=thisblock; + continue; + }else + ogg_stream_packetout(&vf->os,NULL); + } + } + + if(!lastblock){ + if(_get_next_page(vf,&og,-1)<0){ + vf->pcm_offset=ov_pcm_total(vf,-1); + break; + } + }else{ + /* huh? Bogus stream with packets but no granulepos */ + vf->pcm_offset=-1; + break; + } + + /* has our decoding just traversed a bitstream boundary? */ + if(vf->ready_state>=STREAMSET) + if(vf->current_serialno!=ogg_page_serialno(&og)){ + _decode_clear(vf); /* clear out stream state */ + ogg_stream_clear(&work_os); + } + + if(vf->ready_statecurrent_serialno=ogg_page_serialno(&og); + for(link=0;linklinks;link++) + if(vf->serialnos[link]==vf->current_serialno)break; + if(link==vf->links)goto seek_error; /* sign of a bogus stream. + error out, leave + machine uninitialized */ + vf->current_link=link; + + ogg_stream_reset_serialno(&vf->os,vf->current_serialno); + ogg_stream_reset_serialno(&work_os,vf->current_serialno); + vf->ready_state=STREAMSET; + + } + + ogg_stream_pagein(&vf->os,&og); + ogg_stream_pagein(&work_os,&og); + eosflag=ogg_page_eos(&og); + } + } + + ogg_stream_clear(&work_os); + vf->bittrack=0.f; + vf->samptrack=0.f; + return(0); + + seek_error: + /* dump the machine so we're in a known state */ + vf->pcm_offset=-1; + ogg_stream_clear(&work_os); + _decode_clear(vf); + return OV_EBADLINK; +} + +/* Page granularity seek (faster than sample granularity because we + don't do the last bit of decode to find a specific sample). + + Seek to the last [granule marked] page preceeding the specified pos + location, such that decoding past the returned point will quickly + arrive at the requested position. */ +int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos){ + int link=-1; + ogg_int64_t result=0; + ogg_int64_t total=ov_pcm_total(vf,-1); + + if(vf->ready_stateseekable)return(OV_ENOSEEK); + + if(pos<0 || pos>total)return(OV_EINVAL); + + /* which bitstream section does this pcm offset occur in? */ + for(link=vf->links-1;link>=0;link--){ + total-=vf->pcmlengths[link*2+1]; + if(pos>=total)break; + } + + /* search within the logical bitstream for the page with the highest + pcm_pos preceeding (or equal to) pos. There is a danger here; + missing pages or incorrect frame number information in the + bitstream could make our task impossible. Account for that (it + would be an error condition) */ + + /* new search algorithm by HB (Nicholas Vinen) */ + { + ogg_int64_t end=vf->offsets[link+1]; + ogg_int64_t begin=vf->offsets[link]; + ogg_int64_t begintime = vf->pcmlengths[link*2]; + ogg_int64_t endtime = vf->pcmlengths[link*2+1]+begintime; + ogg_int64_t target=pos-total+begintime; + ogg_int64_t best=begin; + + ogg_page og; + while(beginoffset); + if(result==OV_EREAD) goto seek_error; + if(result<0){ + if(bisect<=begin+1) + end=begin; /* found it */ + else{ + if(bisect==0) goto seek_error; + bisect-=CHUNKSIZE; + if(bisect<=begin)bisect=begin+1; + _seek_helper(vf,bisect); + } + }else{ + ogg_int64_t granulepos=ogg_page_granulepos(&og); + if(granulepos==-1)continue; + if(granuleposoffset; /* raw offset of next page */ + begintime=granulepos; + + if(target-begintime>44100)break; + bisect=begin; /* *not* begin + 1 */ + }else{ + if(bisect<=begin+1) + end=begin; /* found it */ + else{ + if(end==vf->offset){ /* we're pretty close - we'd be stuck in */ + end=result; + bisect-=CHUNKSIZE; /* an endless loop otherwise. */ + if(bisect<=begin)bisect=begin+1; + _seek_helper(vf,bisect); + }else{ + end=result; + endtime=granulepos; + break; + } + } + } + } + } + } + + /* found our page. seek to it, update pcm offset. Easier case than + raw_seek, don't keep packets preceeding granulepos. */ + { + ogg_page og; + ogg_packet op; + + /* seek */ + _seek_helper(vf,best); + vf->pcm_offset=-1; + + if(_get_next_page(vf,&og,-1)<0)return(OV_EOF); /* shouldn't happen */ + + if(link!=vf->current_link){ + /* Different link; dump entire decode machine */ + _decode_clear(vf); + + vf->current_link=link; + vf->current_serialno=ogg_page_serialno(&og); + vf->ready_state=STREAMSET; + + }else{ + vorbis_synthesis_restart(&vf->vd); + } + + ogg_stream_reset_serialno(&vf->os,vf->current_serialno); + ogg_stream_pagein(&vf->os,&og); + + /* pull out all but last packet; the one with granulepos */ + while(1){ + result=ogg_stream_packetpeek(&vf->os,&op); + if(result==0){ + /* !!! the packet finishing this page originated on a + preceeding page. Keep fetching previous pages until we + get one with a granulepos or without the 'continued' flag + set. Then just use raw_seek for simplicity. */ + + _seek_helper(vf,best); + + while(1){ + result=_get_prev_page(vf,&og); + if(result<0) goto seek_error; + if(ogg_page_granulepos(&og)>-1 || + !ogg_page_continued(&og)){ + return ov_raw_seek(vf,result); + } + vf->offset=result; + } + } + if(result<0){ + result = OV_EBADPACKET; + goto seek_error; + } + if(op.granulepos!=-1){ + vf->pcm_offset=op.granulepos-vf->pcmlengths[vf->current_link*2]; + if(vf->pcm_offset<0)vf->pcm_offset=0; + vf->pcm_offset+=total; + break; + }else + result=ogg_stream_packetout(&vf->os,NULL); + } + } + } + + /* verify result */ + if(vf->pcm_offset>pos || pos>ov_pcm_total(vf,-1)){ + result=OV_EFAULT; + goto seek_error; + } + vf->bittrack=0.f; + vf->samptrack=0.f; + return(0); + + seek_error: + /* dump machine so we're in a known state */ + vf->pcm_offset=-1; + _decode_clear(vf); + return (int)result; +} + +/* seek to a sample offset relative to the decompressed pcm stream + returns zero on success, nonzero on failure */ + +int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos){ + int thisblock,lastblock=0; + int ret=ov_pcm_seek_page(vf,pos); + if(ret<0)return(ret); + if((ret=_make_decode_ready(vf)))return ret; + + /* discard leading packets we don't need for the lapping of the + position we want; don't decode them */ + + while(1){ + ogg_packet op; + ogg_page og; + + int ret=ogg_stream_packetpeek(&vf->os,&op); + if(ret>0){ + thisblock=vorbis_packet_blocksize(vf->vi+vf->current_link,&op); + if(thisblock<0){ + ogg_stream_packetout(&vf->os,NULL); + continue; /* non audio packet */ + } + if(lastblock)vf->pcm_offset+=(lastblock+thisblock)>>2; + + if(vf->pcm_offset+((thisblock+ + vorbis_info_blocksize(vf->vi,1))>>2)>=pos)break; + + /* remove the packet from packet queue and track its granulepos */ + ogg_stream_packetout(&vf->os,NULL); + vorbis_synthesis_trackonly(&vf->vb,&op); /* set up a vb with + only tracking, no + pcm_decode */ + vorbis_synthesis_blockin(&vf->vd,&vf->vb); + + /* end of logical stream case is hard, especially with exact + length positioning. */ + + if(op.granulepos>-1){ + int i; + /* always believe the stream markers */ + vf->pcm_offset=op.granulepos-vf->pcmlengths[vf->current_link*2]; + if(vf->pcm_offset<0)vf->pcm_offset=0; + for(i=0;icurrent_link;i++) + vf->pcm_offset+=vf->pcmlengths[i*2+1]; + } + + lastblock=thisblock; + + }else{ + if(ret<0 && ret!=OV_HOLE)break; + + /* suck in a new page */ + if(_get_next_page(vf,&og,-1)<0)break; + if(vf->current_serialno!=ogg_page_serialno(&og))_decode_clear(vf); + + if(vf->ready_statecurrent_serialno=ogg_page_serialno(&og); + for(link=0;linklinks;link++) + if(vf->serialnos[link]==vf->current_serialno)break; + if(link==vf->links)return(OV_EBADLINK); + vf->current_link=link; + + ogg_stream_reset_serialno(&vf->os,vf->current_serialno); + vf->ready_state=STREAMSET; + ret=_make_decode_ready(vf); + if(ret)return ret; + lastblock=0; + } + + ogg_stream_pagein(&vf->os,&og); + } + } + + vf->bittrack=0.f; + vf->samptrack=0.f; + /* discard samples until we reach the desired position. Crossing a + logical bitstream boundary with abandon is OK. */ + while(vf->pcm_offsetpcm_offset; + long samples=vorbis_synthesis_pcmout(&vf->vd,NULL); + + if(samples>target)samples=target; + vorbis_synthesis_read(&vf->vd,samples); + vf->pcm_offset+=samples; + + if(samplespcm_offset=ov_pcm_total(vf,-1); /* eof */ + } + return 0; +} + +/* seek to a playback time relative to the decompressed pcm stream + returns zero on success, nonzero on failure */ +int ov_time_seek(OggVorbis_File *vf,double seconds){ + /* translate time to PCM position and call ov_pcm_seek */ + + int link=-1; + ogg_int64_t pcm_total=ov_pcm_total(vf,-1); + double time_total=ov_time_total(vf,-1); + + if(vf->ready_stateseekable)return(OV_ENOSEEK); + if(seconds<0 || seconds>time_total)return(OV_EINVAL); + + /* which bitstream section does this time offset occur in? */ + for(link=vf->links-1;link>=0;link--){ + pcm_total-=vf->pcmlengths[link*2+1]; + time_total-=ov_time_total(vf,link); + if(seconds>=time_total)break; + } + + /* enough information to convert time offset to pcm offset */ + { + ogg_int64_t target=pcm_total+(seconds-time_total)*vf->vi[link].rate; + return(ov_pcm_seek(vf,target)); + } +} + +/* page-granularity version of ov_time_seek + returns zero on success, nonzero on failure */ +int ov_time_seek_page(OggVorbis_File *vf,double seconds){ + /* translate time to PCM position and call ov_pcm_seek */ + + int link=-1; + ogg_int64_t pcm_total=ov_pcm_total(vf,-1); + double time_total=ov_time_total(vf,-1); + + if(vf->ready_stateseekable)return(OV_ENOSEEK); + if(seconds<0 || seconds>time_total)return(OV_EINVAL); + + /* which bitstream section does this time offset occur in? */ + for(link=vf->links-1;link>=0;link--){ + pcm_total-=vf->pcmlengths[link*2+1]; + time_total-=ov_time_total(vf,link); + if(seconds>=time_total)break; + } + + /* enough information to convert time offset to pcm offset */ + { + ogg_int64_t target=pcm_total+(seconds-time_total)*vf->vi[link].rate; + return(ov_pcm_seek_page(vf,target)); + } +} + +/* tell the current stream offset cursor. Note that seek followed by + tell will likely not give the set offset due to caching */ +ogg_int64_t ov_raw_tell(OggVorbis_File *vf){ + if(vf->ready_stateoffset); +} + +/* return PCM offset (sample) of next PCM sample to be read */ +ogg_int64_t ov_pcm_tell(OggVorbis_File *vf){ + if(vf->ready_statepcm_offset); +} + +/* return time offset (seconds) of next PCM sample to be read */ +double ov_time_tell(OggVorbis_File *vf){ + int link=0; + ogg_int64_t pcm_total=0; + double time_total=0.f; + + if(vf->ready_stateseekable){ + pcm_total=ov_pcm_total(vf,-1); + time_total=ov_time_total(vf,-1); + + /* which bitstream section does this time offset occur in? */ + for(link=vf->links-1;link>=0;link--){ + pcm_total-=vf->pcmlengths[link*2+1]; + time_total-=ov_time_total(vf,link); + if(vf->pcm_offset>=pcm_total)break; + } + } + + return((double)time_total+(double)(vf->pcm_offset-pcm_total)/vf->vi[link].rate); +} + +/* link: -1) return the vorbis_info struct for the bitstream section + currently being decoded + 0-n) to request information for a specific bitstream section + + In the case of a non-seekable bitstream, any call returns the + current bitstream. NULL in the case that the machine is not + initialized */ + +vorbis_info *ov_info(OggVorbis_File *vf,int link){ + if(vf->seekable){ + if(link<0) + if(vf->ready_state>=STREAMSET) + return vf->vi+vf->current_link; + else + return vf->vi; + else + if(link>=vf->links) + return NULL; + else + return vf->vi+link; + }else{ + return vf->vi; + } +} + +/* grr, strong typing, grr, no templates/inheritence, grr */ +vorbis_comment *ov_comment(OggVorbis_File *vf,int link){ + if(vf->seekable){ + if(link<0) + if(vf->ready_state>=STREAMSET) + return vf->vc+vf->current_link; + else + return vf->vc; + else + if(link>=vf->links) + return NULL; + else + return vf->vc+link; + }else{ + return vf->vc; + } +} + +static int host_is_big_endian() { + ogg_int32_t pattern = 0xfeedface; /* deadbeef */ + unsigned char *bytewise = (unsigned char *)&pattern; + if (bytewise[0] == 0xfe) return 1; + return 0; +} + +/* up to this point, everything could more or less hide the multiple + logical bitstream nature of chaining from the toplevel application + if the toplevel application didn't particularly care. However, at + the point that we actually read audio back, the multiple-section + nature must surface: Multiple bitstream sections do not necessarily + have to have the same number of channels or sampling rate. + + ov_read returns the sequential logical bitstream number currently + being decoded along with the PCM data in order that the toplevel + application can take action on channel/sample rate changes. This + number will be incremented even for streamed (non-seekable) streams + (for seekable streams, it represents the actual logical bitstream + index within the physical bitstream. Note that the accessor + functions above are aware of this dichotomy). + + input values: buffer) a buffer to hold packed PCM data for return + length) the byte length requested to be placed into buffer + bigendianp) should the data be packed LSB first (0) or + MSB first (1) + word) word size for output. currently 1 (byte) or + 2 (16 bit short) + + return values: <0) error/hole in data (OV_HOLE), partial open (OV_EINVAL) + 0) EOF + n) number of bytes of PCM actually returned. The + below works on a packet-by-packet basis, so the + return length is not related to the 'length' passed + in, just guaranteed to fit. + + *section) set to the logical bitstream number */ + +long ov_read(OggVorbis_File *vf,char *buffer,int length, + int bigendianp,int word,int sgned,int *bitstream){ + int i,j; + int host_endian = host_is_big_endian(); + + float **pcm; + long samples; + + if(vf->ready_stateready_state==INITSET){ + samples=vorbis_synthesis_pcmout(&vf->vd,&pcm); + if(samples)break; + } + + /* suck in another packet */ + { + int ret=_fetch_and_process_packet(vf,NULL,1,1); + if(ret==OV_EOF) + return(0); + if(ret<=0) + return(ret); + } + + } + + if(samples>0){ + + /* yay! proceed to pack data into the byte buffer */ + + long channels=ov_info(vf,-1)->channels; + long bytespersample=word * channels; + vorbis_fpu_control fpu; + (void) fpu; // (to avoid a warning about it being unused) + if(samples>length/bytespersample)samples=length/bytespersample; + + if(samples <= 0) + return OV_EINVAL; + + /* a tight loop to pack each size */ + { + int val; + if(word==1){ + int off=(sgned?0:128); + vorbis_fpu_setround(&fpu); + for(j=0;j127)val=127; + else if(val<-128)val=-128; + *buffer++=val+off; + } + vorbis_fpu_restore(fpu); + }else{ + int off=(sgned?0:32768); + + if(host_endian==bigendianp){ + if(sgned){ + + vorbis_fpu_setround(&fpu); + for(i=0;i32767)val=32767; + else if(val<-32768)val=-32768; + *dest=val; + dest+=channels; + } + } + vorbis_fpu_restore(fpu); + + }else{ + + vorbis_fpu_setround(&fpu); + for(i=0;i32767)val=32767; + else if(val<-32768)val=-32768; + *dest=val+off; + dest+=channels; + } + } + vorbis_fpu_restore(fpu); + + } + }else if(bigendianp){ + + vorbis_fpu_setround(&fpu); + for(j=0;j32767)val=32767; + else if(val<-32768)val=-32768; + val+=off; + *buffer++=(val>>8); + *buffer++=(val&0xff); + } + vorbis_fpu_restore(fpu); + + }else{ + int val; + vorbis_fpu_setround(&fpu); + for(j=0;j32767)val=32767; + else if(val<-32768)val=-32768; + val+=off; + *buffer++=(val&0xff); + *buffer++=(val>>8); + } + vorbis_fpu_restore(fpu); + + } + } + } + + vorbis_synthesis_read(&vf->vd,samples); + vf->pcm_offset+=samples; + if(bitstream)*bitstream=vf->current_link; + return(samples*bytespersample); + }else{ + return(samples); + } +} + +/* input values: pcm_channels) a float vector per channel of output + length) the sample length being read by the app + + return values: <0) error/hole in data (OV_HOLE), partial open (OV_EINVAL) + 0) EOF + n) number of samples of PCM actually returned. The + below works on a packet-by-packet basis, so the + return length is not related to the 'length' passed + in, just guaranteed to fit. + + *section) set to the logical bitstream number */ + +long ov_read_float(OggVorbis_File *vf,float ***pcm_channels,int length, + int *bitstream){ + + if(vf->ready_stateready_state==INITSET){ + float **pcm; + long samples=vorbis_synthesis_pcmout(&vf->vd,&pcm); + if(samples){ + if(pcm_channels)*pcm_channels=pcm; + if(samples>length)samples=length; + vorbis_synthesis_read(&vf->vd,samples); + vf->pcm_offset+=samples; + if(bitstream)*bitstream=vf->current_link; + return samples; + + } + } + + /* suck in another packet */ + { + int ret=_fetch_and_process_packet(vf,NULL,1,1); + if(ret==OV_EOF)return(0); + if(ret<=0)return(ret); + } + + } +} + +extern float *vorbis_window(vorbis_dsp_state *v,int W); +extern void _analysis_output_always(char *base,int i,float *v,int n,int bark,int dB, + ogg_int64_t off); + +static void _ov_splice(float **pcm,float **lappcm, + int n1, int n2, + int ch1, int ch2, + float *w1, float *w2){ + int i,j; + float *w=w1; + int n=n1; + + if(n1>n2){ + n=n2; + w=w2; + } + + /* splice */ + for(j=0;jready_state==INITSET)break; + /* suck in another packet */ + { + int ret=_fetch_and_process_packet(vf,NULL,1,0); + if(ret<0 && ret!=OV_HOLE)return(ret); + } + } + return 0; +} + +/* make sure vf is INITSET and that we have a primed buffer; if + we're crosslapping at a stream section boundary, this also makes + sure we're sanity checking against the right stream information */ +static int _ov_initprime(OggVorbis_File *vf){ + vorbis_dsp_state *vd=&vf->vd; + while(1){ + if(vf->ready_state==INITSET) + if(vorbis_synthesis_pcmout(vd,NULL))break; + + /* suck in another packet */ + { + int ret=_fetch_and_process_packet(vf,NULL,1,0); + if(ret<0 && ret!=OV_HOLE)return(ret); + } + } + return 0; +} + +/* grab enough data for lapping from vf; this may be in the form of + unreturned, already-decoded pcm, remaining PCM we will need to + decode, or synthetic postextrapolation from last packets. */ +static void _ov_getlap(OggVorbis_File *vf,vorbis_info *vi,vorbis_dsp_state *vd, + float **lappcm,int lapsize){ + int lapcount=0,i; + float **pcm; + + /* try first to decode the lapping data */ + while(lapcountlapsize-lapcount)samples=lapsize-lapcount; + for(i=0;ichannels;i++) + memcpy(lappcm[i]+lapcount,pcm[i],sizeof(**pcm)*samples); + lapcount+=samples; + vorbis_synthesis_read(vd,samples); + }else{ + /* suck in another packet */ + int ret=_fetch_and_process_packet(vf,NULL,1,0); /* do *not* span */ + if(ret==OV_EOF)break; + } + } + if(lapcountvd,&pcm); + if(samples==0){ + for(i=0;ichannels;i++) + memset(lappcm[i]+lapcount,0,sizeof(**pcm)*lapsize-lapcount); + lapcount=lapsize; + }else{ + if(samples>lapsize-lapcount)samples=lapsize-lapcount; + for(i=0;ichannels;i++) + memcpy(lappcm[i]+lapcount,pcm[i],sizeof(**pcm)*samples); + lapcount+=samples; + } + } +} + +/* this sets up crosslapping of a sample by using trailing data from + sample 1 and lapping it into the windowing buffer of sample 2 */ +int ov_crosslap(OggVorbis_File *vf1, OggVorbis_File *vf2){ + vorbis_info *vi1,*vi2; + float **lappcm; + float **pcm; + float *w1,*w2; + int n1,n2,i,ret,hs1,hs2; + + if(vf1==vf2)return(0); /* degenerate case */ + if(vf1->ready_stateready_statechannels); + n1=vorbis_info_blocksize(vi1,0)>>(1+hs1); + n2=vorbis_info_blocksize(vi2,0)>>(1+hs2); + w1=vorbis_window(&vf1->vd,0); + w2=vorbis_window(&vf2->vd,0); + + for(i=0;ichannels;i++) + lappcm[i]=(float*) alloca(sizeof(**lappcm)*n1); + + _ov_getlap(vf1,vi1,&vf1->vd,lappcm,n1); + + /* have a lapping buffer from vf1; now to splice it into the lapping + buffer of vf2 */ + /* consolidate and expose the buffer. */ + vorbis_synthesis_lapout(&vf2->vd,&pcm); + _analysis_output_always("pcmL",0,pcm[0],n1*2,0,0,0); + _analysis_output_always("pcmR",0,pcm[1],n1*2,0,0,0); + + /* splice */ + _ov_splice(pcm,lappcm,n1,n2,vi1->channels,vi2->channels,w1,w2); + + /* done */ + return(0); +} + +static int _ov_64_seek_lap(OggVorbis_File *vf,ogg_int64_t pos, + int (*localseek)(OggVorbis_File *,ogg_int64_t)){ + vorbis_info *vi; + float **lappcm; + float **pcm; + float *w1,*w2; + int n1,n2,ch1,ch2,hs; + int i,ret; + + if(vf->ready_statechannels; + n1=vorbis_info_blocksize(vi,0)>>(1+hs); + w1=vorbis_window(&vf->vd,0); /* window arrays from libvorbis are + persistent; even if the decode state + from this link gets dumped, this + window array continues to exist */ + + lappcm=(float**) alloca(sizeof(*lappcm)*ch1); + for(i=0;ivd,lappcm,n1); + + /* have lapping data; seek and prime the buffer */ + ret=localseek(vf,pos); + if(ret)return ret; + ret=_ov_initprime(vf); + if(ret)return(ret); + + /* Guard against cross-link changes; they're perfectly legal */ + vi=ov_info(vf,-1); + ch2=vi->channels; + n2=vorbis_info_blocksize(vi,0)>>(1+hs); + w2=vorbis_window(&vf->vd,0); + + /* consolidate and expose the buffer. */ + vorbis_synthesis_lapout(&vf->vd,&pcm); + + /* splice */ + _ov_splice(pcm,lappcm,n1,n2,ch1,ch2,w1,w2); + + /* done */ + return(0); +} + +int ov_raw_seek_lap(OggVorbis_File *vf,ogg_int64_t pos){ + return _ov_64_seek_lap(vf,pos,ov_raw_seek); +} + +int ov_pcm_seek_lap(OggVorbis_File *vf,ogg_int64_t pos){ + return _ov_64_seek_lap(vf,pos,ov_pcm_seek); +} + +int ov_pcm_seek_page_lap(OggVorbis_File *vf,ogg_int64_t pos){ + return _ov_64_seek_lap(vf,pos,ov_pcm_seek_page); +} + +static int _ov_d_seek_lap(OggVorbis_File *vf,double pos, + int (*localseek)(OggVorbis_File *,double)){ + vorbis_info *vi; + float **lappcm; + float **pcm; + float *w1,*w2; + int n1,n2,ch1,ch2,hs; + int i,ret; + + if(vf->ready_statechannels; + n1=vorbis_info_blocksize(vi,0)>>(1+hs); + w1=vorbis_window(&vf->vd,0); /* window arrays from libvorbis are + persistent; even if the decode state + from this link gets dumped, this + window array continues to exist */ + + lappcm=(float**) alloca(sizeof(*lappcm)*ch1); + for(i=0;ivd,lappcm,n1); + + /* have lapping data; seek and prime the buffer */ + ret=localseek(vf,pos); + if(ret)return ret; + ret=_ov_initprime(vf); + if(ret)return(ret); + + /* Guard against cross-link changes; they're perfectly legal */ + vi=ov_info(vf,-1); + ch2=vi->channels; + n2=vorbis_info_blocksize(vi,0)>>(1+hs); + w2=vorbis_window(&vf->vd,0); + + /* consolidate and expose the buffer. */ + vorbis_synthesis_lapout(&vf->vd,&pcm); + + /* splice */ + _ov_splice(pcm,lappcm,n1,n2,ch1,ch2,w1,w2); + + /* done */ + return(0); +} + +int ov_time_seek_lap(OggVorbis_File *vf,double pos){ + return _ov_d_seek_lap(vf,pos,ov_time_seek); +} + +int ov_time_seek_page_lap(OggVorbis_File *vf,double pos){ + return _ov_d_seek_lap(vf,pos,ov_time_seek_page); +} + +#endif +/********* End of inlined file: vorbisfile.c *********/ + +/********* Start of inlined file: window.c *********/ + +/********* Start of inlined file: juce_OggVorbisHeader.h *********/ +// This file is included at the start of each Ogg-Vorbis .c file, just to do a few housekeeping +// tasks.. + +#ifdef _MSC_VER + #pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4305 4189 4706) +#endif +/********* End of inlined file: juce_OggVorbisHeader.h *********/ + +#if JUCE_USE_OGGVORBIS + +#include +#include + +static float vwin64[32] = { + 0.0009460463F, 0.0085006468F, 0.0235352254F, 0.0458950567F, + 0.0753351908F, 0.1115073077F, 0.1539457973F, 0.2020557475F, + 0.2551056759F, 0.3122276645F, 0.3724270287F, 0.4346027792F, + 0.4975789974F, 0.5601459521F, 0.6211085051F, 0.6793382689F, + 0.7338252629F, 0.7837245849F, 0.8283939355F, 0.8674186656F, + 0.9006222429F, 0.9280614787F, 0.9500073081F, 0.9669131782F, + 0.9793740220F, 0.9880792941F, 0.9937636139F, 0.9971582668F, + 0.9989462667F, 0.9997230082F, 0.9999638688F, 0.9999995525F, +}; + +static float vwin128[64] = { + 0.0002365472F, 0.0021280687F, 0.0059065254F, 0.0115626550F, + 0.0190823442F, 0.0284463735F, 0.0396300935F, 0.0526030430F, + 0.0673285281F, 0.0837631763F, 0.1018564887F, 0.1215504095F, + 0.1427789367F, 0.1654677960F, 0.1895342001F, 0.2148867160F, + 0.2414252576F, 0.2690412240F, 0.2976177952F, 0.3270303960F, + 0.3571473350F, 0.3878306189F, 0.4189369387F, 0.4503188188F, + 0.4818259135F, 0.5133064334F, 0.5446086751F, 0.5755826278F, + 0.6060816248F, 0.6359640047F, 0.6650947483F, 0.6933470543F, + 0.7206038179F, 0.7467589810F, 0.7717187213F, 0.7954024542F, + 0.8177436264F, 0.8386902831F, 0.8582053981F, 0.8762669622F, + 0.8928678298F, 0.9080153310F, 0.9217306608F, 0.9340480615F, + 0.9450138200F, 0.9546851041F, 0.9631286621F, 0.9704194171F, + 0.9766389810F, 0.9818741197F, 0.9862151938F, 0.9897546035F, + 0.9925852598F, 0.9947991032F, 0.9964856900F, 0.9977308602F, + 0.9986155015F, 0.9992144193F, 0.9995953200F, 0.9998179155F, + 0.9999331503F, 0.9999825563F, 0.9999977357F, 0.9999999720F, +}; + +static float vwin256[128] = { + 0.0000591390F, 0.0005321979F, 0.0014780301F, 0.0028960636F, + 0.0047854363F, 0.0071449926F, 0.0099732775F, 0.0132685298F, + 0.0170286741F, 0.0212513119F, 0.0259337111F, 0.0310727950F, + 0.0366651302F, 0.0427069140F, 0.0491939614F, 0.0561216907F, + 0.0634851102F, 0.0712788035F, 0.0794969160F, 0.0881331402F, + 0.0971807028F, 0.1066323515F, 0.1164803426F, 0.1267164297F, + 0.1373318534F, 0.1483173323F, 0.1596630553F, 0.1713586755F, + 0.1833933062F, 0.1957555184F, 0.2084333404F, 0.2214142599F, + 0.2346852280F, 0.2482326664F, 0.2620424757F, 0.2761000481F, + 0.2903902813F, 0.3048975959F, 0.3196059553F, 0.3344988887F, + 0.3495595160F, 0.3647705766F, 0.3801144597F, 0.3955732382F, + 0.4111287047F, 0.4267624093F, 0.4424557009F, 0.4581897696F, + 0.4739456913F, 0.4897044744F, 0.5054471075F, 0.5211546088F, + 0.5368080763F, 0.5523887395F, 0.5678780103F, 0.5832575361F, + 0.5985092508F, 0.6136154277F, 0.6285587300F, 0.6433222619F, + 0.6578896175F, 0.6722449294F, 0.6863729144F, 0.7002589187F, + 0.7138889597F, 0.7272497662F, 0.7403288154F, 0.7531143679F, + 0.7655954985F, 0.7777621249F, 0.7896050322F, 0.8011158947F, + 0.8122872932F, 0.8231127294F, 0.8335866365F, 0.8437043850F, + 0.8534622861F, 0.8628575905F, 0.8718884835F, 0.8805540765F, + 0.8888543947F, 0.8967903616F, 0.9043637797F, 0.9115773078F, + 0.9184344360F, 0.9249394562F, 0.9310974312F, 0.9369141608F, + 0.9423961446F, 0.9475505439F, 0.9523851406F, 0.9569082947F, + 0.9611289005F, 0.9650563408F, 0.9687004405F, 0.9720714191F, + 0.9751798427F, 0.9780365753F, 0.9806527301F, 0.9830396204F, + 0.9852087111F, 0.9871715701F, 0.9889398207F, 0.9905250941F, + 0.9919389832F, 0.9931929973F, 0.9942985174F, 0.9952667537F, + 0.9961087037F, 0.9968351119F, 0.9974564312F, 0.9979827858F, + 0.9984239359F, 0.9987892441F, 0.9990876435F, 0.9993276081F, + 0.9995171241F, 0.9996636648F, 0.9997741654F, 0.9998550016F, + 0.9999119692F, 0.9999502656F, 0.9999744742F, 0.9999885497F, + 0.9999958064F, 0.9999989077F, 0.9999998584F, 0.9999999983F, +}; + +static float vwin512[256] = { + 0.0000147849F, 0.0001330607F, 0.0003695946F, 0.0007243509F, + 0.0011972759F, 0.0017882983F, 0.0024973285F, 0.0033242588F, + 0.0042689632F, 0.0053312973F, 0.0065110982F, 0.0078081841F, + 0.0092223540F, 0.0107533880F, 0.0124010466F, 0.0141650703F, + 0.0160451800F, 0.0180410758F, 0.0201524373F, 0.0223789233F, + 0.0247201710F, 0.0271757958F, 0.0297453914F, 0.0324285286F, + 0.0352247556F, 0.0381335972F, 0.0411545545F, 0.0442871045F, + 0.0475306997F, 0.0508847676F, 0.0543487103F, 0.0579219038F, + 0.0616036982F, 0.0653934164F, 0.0692903546F, 0.0732937809F, + 0.0774029356F, 0.0816170305F, 0.0859352485F, 0.0903567428F, + 0.0948806375F, 0.0995060259F, 0.1042319712F, 0.1090575056F, + 0.1139816300F, 0.1190033137F, 0.1241214941F, 0.1293350764F, + 0.1346429333F, 0.1400439046F, 0.1455367974F, 0.1511203852F, + 0.1567934083F, 0.1625545735F, 0.1684025537F, 0.1743359881F, + 0.1803534820F, 0.1864536069F, 0.1926349000F, 0.1988958650F, + 0.2052349715F, 0.2116506555F, 0.2181413191F, 0.2247053313F, + 0.2313410275F, 0.2380467105F, 0.2448206500F, 0.2516610835F, + 0.2585662164F, 0.2655342226F, 0.2725632448F, 0.2796513950F, + 0.2867967551F, 0.2939973773F, 0.3012512852F, 0.3085564739F, + 0.3159109111F, 0.3233125375F, 0.3307592680F, 0.3382489922F, + 0.3457795756F, 0.3533488602F, 0.3609546657F, 0.3685947904F, + 0.3762670121F, 0.3839690896F, 0.3916987634F, 0.3994537572F, + 0.4072317788F, 0.4150305215F, 0.4228476653F, 0.4306808783F, + 0.4385278181F, 0.4463861329F, 0.4542534630F, 0.4621274424F, + 0.4700057001F, 0.4778858615F, 0.4857655502F, 0.4936423891F, + 0.5015140023F, 0.5093780165F, 0.5172320626F, 0.5250737772F, + 0.5329008043F, 0.5407107971F, 0.5485014192F, 0.5562703465F, + 0.5640152688F, 0.5717338914F, 0.5794239366F, 0.5870831457F, + 0.5947092801F, 0.6023001235F, 0.6098534829F, 0.6173671907F, + 0.6248391059F, 0.6322671161F, 0.6396491384F, 0.6469831217F, + 0.6542670475F, 0.6614989319F, 0.6686768267F, 0.6757988210F, + 0.6828630426F, 0.6898676592F, 0.6968108799F, 0.7036909564F, + 0.7105061843F, 0.7172549043F, 0.7239355032F, 0.7305464154F, + 0.7370861235F, 0.7435531598F, 0.7499461068F, 0.7562635986F, + 0.7625043214F, 0.7686670148F, 0.7747504721F, 0.7807535410F, + 0.7866751247F, 0.7925141825F, 0.7982697296F, 0.8039408387F, + 0.8095266395F, 0.8150263196F, 0.8204391248F, 0.8257643590F, + 0.8310013848F, 0.8361496236F, 0.8412085555F, 0.8461777194F, + 0.8510567129F, 0.8558451924F, 0.8605428730F, 0.8651495278F, + 0.8696649882F, 0.8740891432F, 0.8784219392F, 0.8826633797F, + 0.8868135244F, 0.8908724888F, 0.8948404441F, 0.8987176157F, + 0.9025042831F, 0.9062007791F, 0.9098074886F, 0.9133248482F, + 0.9167533451F, 0.9200935163F, 0.9233459472F, 0.9265112712F, + 0.9295901680F, 0.9325833632F, 0.9354916263F, 0.9383157705F, + 0.9410566504F, 0.9437151618F, 0.9462922398F, 0.9487888576F, + 0.9512060252F, 0.9535447882F, 0.9558062262F, 0.9579914516F, + 0.9601016078F, 0.9621378683F, 0.9641014348F, 0.9659935361F, + 0.9678154261F, 0.9695683830F, 0.9712537071F, 0.9728727198F, + 0.9744267618F, 0.9759171916F, 0.9773453842F, 0.9787127293F, + 0.9800206298F, 0.9812705006F, 0.9824637665F, 0.9836018613F, + 0.9846862258F, 0.9857183066F, 0.9866995544F, 0.9876314227F, + 0.9885153662F, 0.9893528393F, 0.9901452948F, 0.9908941823F, + 0.9916009470F, 0.9922670279F, 0.9928938570F, 0.9934828574F, + 0.9940354423F, 0.9945530133F, 0.9950369595F, 0.9954886562F, + 0.9959094633F, 0.9963007242F, 0.9966637649F, 0.9969998925F, + 0.9973103939F, 0.9975965351F, 0.9978595598F, 0.9981006885F, + 0.9983211172F, 0.9985220166F, 0.9987045311F, 0.9988697776F, + 0.9990188449F, 0.9991527924F, 0.9992726499F, 0.9993794157F, + 0.9994740570F, 0.9995575079F, 0.9996306699F, 0.9996944099F, + 0.9997495605F, 0.9997969190F, 0.9998372465F, 0.9998712678F, + 0.9998996704F, 0.9999231041F, 0.9999421807F, 0.9999574732F, + 0.9999695157F, 0.9999788026F, 0.9999857885F, 0.9999908879F, + 0.9999944746F, 0.9999968817F, 0.9999984010F, 0.9999992833F, + 0.9999997377F, 0.9999999317F, 0.9999999911F, 0.9999999999F, +}; + +static float vwin1024[512] = { + 0.0000036962F, 0.0000332659F, 0.0000924041F, 0.0001811086F, + 0.0002993761F, 0.0004472021F, 0.0006245811F, 0.0008315063F, + 0.0010679699F, 0.0013339631F, 0.0016294757F, 0.0019544965F, + 0.0023090133F, 0.0026930125F, 0.0031064797F, 0.0035493989F, + 0.0040217533F, 0.0045235250F, 0.0050546946F, 0.0056152418F, + 0.0062051451F, 0.0068243817F, 0.0074729278F, 0.0081507582F, + 0.0088578466F, 0.0095941655F, 0.0103596863F, 0.0111543789F, + 0.0119782122F, 0.0128311538F, 0.0137131701F, 0.0146242260F, + 0.0155642855F, 0.0165333111F, 0.0175312640F, 0.0185581042F, + 0.0196137903F, 0.0206982797F, 0.0218115284F, 0.0229534910F, + 0.0241241208F, 0.0253233698F, 0.0265511886F, 0.0278075263F, + 0.0290923308F, 0.0304055484F, 0.0317471241F, 0.0331170013F, + 0.0345151222F, 0.0359414274F, 0.0373958560F, 0.0388783456F, + 0.0403888325F, 0.0419272511F, 0.0434935347F, 0.0450876148F, + 0.0467094213F, 0.0483588828F, 0.0500359261F, 0.0517404765F, + 0.0534724575F, 0.0552317913F, 0.0570183983F, 0.0588321971F, + 0.0606731048F, 0.0625410369F, 0.0644359070F, 0.0663576272F, + 0.0683061077F, 0.0702812571F, 0.0722829821F, 0.0743111878F, + 0.0763657775F, 0.0784466526F, 0.0805537129F, 0.0826868561F, + 0.0848459782F, 0.0870309736F, 0.0892417345F, 0.0914781514F, + 0.0937401128F, 0.0960275056F, 0.0983402145F, 0.1006781223F, + 0.1030411101F, 0.1054290568F, 0.1078418397F, 0.1102793336F, + 0.1127414119F, 0.1152279457F, 0.1177388042F, 0.1202738544F, + 0.1228329618F, 0.1254159892F, 0.1280227980F, 0.1306532471F, + 0.1333071937F, 0.1359844927F, 0.1386849970F, 0.1414085575F, + 0.1441550230F, 0.1469242403F, 0.1497160539F, 0.1525303063F, + 0.1553668381F, 0.1582254875F, 0.1611060909F, 0.1640084822F, + 0.1669324936F, 0.1698779549F, 0.1728446939F, 0.1758325362F, + 0.1788413055F, 0.1818708232F, 0.1849209084F, 0.1879913785F, + 0.1910820485F, 0.1941927312F, 0.1973232376F, 0.2004733764F, + 0.2036429541F, 0.2068317752F, 0.2100396421F, 0.2132663552F, + 0.2165117125F, 0.2197755102F, 0.2230575422F, 0.2263576007F, + 0.2296754753F, 0.2330109540F, 0.2363638225F, 0.2397338646F, + 0.2431208619F, 0.2465245941F, 0.2499448389F, 0.2533813719F, + 0.2568339669F, 0.2603023956F, 0.2637864277F, 0.2672858312F, + 0.2708003718F, 0.2743298135F, 0.2778739186F, 0.2814324472F, + 0.2850051576F, 0.2885918065F, 0.2921921485F, 0.2958059366F, + 0.2994329219F, 0.3030728538F, 0.3067254799F, 0.3103905462F, + 0.3140677969F, 0.3177569747F, 0.3214578205F, 0.3251700736F, + 0.3288934718F, 0.3326277513F, 0.3363726468F, 0.3401278914F, + 0.3438932168F, 0.3476683533F, 0.3514530297F, 0.3552469734F, + 0.3590499106F, 0.3628615659F, 0.3666816630F, 0.3705099239F, + 0.3743460698F, 0.3781898204F, 0.3820408945F, 0.3858990095F, + 0.3897638820F, 0.3936352274F, 0.3975127601F, 0.4013961936F, + 0.4052852405F, 0.4091796123F, 0.4130790198F, 0.4169831732F, + 0.4208917815F, 0.4248045534F, 0.4287211965F, 0.4326414181F, + 0.4365649248F, 0.4404914225F, 0.4444206167F, 0.4483522125F, + 0.4522859146F, 0.4562214270F, 0.4601584538F, 0.4640966984F, + 0.4680358644F, 0.4719756548F, 0.4759157726F, 0.4798559209F, + 0.4837958024F, 0.4877351199F, 0.4916735765F, 0.4956108751F, + 0.4995467188F, 0.5034808109F, 0.5074128550F, 0.5113425550F, + 0.5152696149F, 0.5191937395F, 0.5231146336F, 0.5270320028F, + 0.5309455530F, 0.5348549910F, 0.5387600239F, 0.5426603597F, + 0.5465557070F, 0.5504457754F, 0.5543302752F, 0.5582089175F, + 0.5620814145F, 0.5659474793F, 0.5698068262F, 0.5736591704F, + 0.5775042283F, 0.5813417176F, 0.5851713571F, 0.5889928670F, + 0.5928059689F, 0.5966103856F, 0.6004058415F, 0.6041920626F, + 0.6079687761F, 0.6117357113F, 0.6154925986F, 0.6192391705F, + 0.6229751612F, 0.6267003064F, 0.6304143441F, 0.6341170137F, + 0.6378080569F, 0.6414872173F, 0.6451542405F, 0.6488088741F, + 0.6524508681F, 0.6560799742F, 0.6596959469F, 0.6632985424F, + 0.6668875197F, 0.6704626398F, 0.6740236662F, 0.6775703649F, + 0.6811025043F, 0.6846198554F, 0.6881221916F, 0.6916092892F, + 0.6950809269F, 0.6985368861F, 0.7019769510F, 0.7054009085F, + 0.7088085484F, 0.7121996632F, 0.7155740484F, 0.7189315023F, + 0.7222718263F, 0.7255948245F, 0.7289003043F, 0.7321880760F, + 0.7354579530F, 0.7387097518F, 0.7419432921F, 0.7451583966F, + 0.7483548915F, 0.7515326059F, 0.7546913723F, 0.7578310265F, + 0.7609514077F, 0.7640523581F, 0.7671337237F, 0.7701953535F, + 0.7732371001F, 0.7762588195F, 0.7792603711F, 0.7822416178F, + 0.7852024259F, 0.7881426654F, 0.7910622097F, 0.7939609356F, + 0.7968387237F, 0.7996954579F, 0.8025310261F, 0.8053453193F, + 0.8081382324F, 0.8109096638F, 0.8136595156F, 0.8163876936F, + 0.8190941071F, 0.8217786690F, 0.8244412960F, 0.8270819086F, + 0.8297004305F, 0.8322967896F, 0.8348709171F, 0.8374227481F, + 0.8399522213F, 0.8424592789F, 0.8449438672F, 0.8474059356F, + 0.8498454378F, 0.8522623306F, 0.8546565748F, 0.8570281348F, + 0.8593769787F, 0.8617030779F, 0.8640064080F, 0.8662869477F, + 0.8685446796F, 0.8707795899F, 0.8729916682F, 0.8751809079F, + 0.8773473059F, 0.8794908626F, 0.8816115819F, 0.8837094713F, + 0.8857845418F, 0.8878368079F, 0.8898662874F, 0.8918730019F, + 0.8938569760F, 0.8958182380F, 0.8977568194F, 0.8996727552F, + 0.9015660837F, 0.9034368465F, 0.9052850885F, 0.9071108577F, + 0.9089142057F, 0.9106951869F, 0.9124538591F, 0.9141902832F, + 0.9159045233F, 0.9175966464F, 0.9192667228F, 0.9209148257F, + 0.9225410313F, 0.9241454187F, 0.9257280701F, 0.9272890704F, + 0.9288285075F, 0.9303464720F, 0.9318430576F, 0.9333183603F, + 0.9347724792F, 0.9362055158F, 0.9376175745F, 0.9390087622F, + 0.9403791881F, 0.9417289644F, 0.9430582055F, 0.9443670283F, + 0.9456555521F, 0.9469238986F, 0.9481721917F, 0.9494005577F, + 0.9506091252F, 0.9517980248F, 0.9529673894F, 0.9541173540F, + 0.9552480557F, 0.9563596334F, 0.9574522282F, 0.9585259830F, + 0.9595810428F, 0.9606175542F, 0.9616356656F, 0.9626355274F, + 0.9636172915F, 0.9645811114F, 0.9655271425F, 0.9664555414F, + 0.9673664664F, 0.9682600774F, 0.9691365355F, 0.9699960034F, + 0.9708386448F, 0.9716646250F, 0.9724741103F, 0.9732672685F, + 0.9740442683F, 0.9748052795F, 0.9755504729F, 0.9762800205F, + 0.9769940950F, 0.9776928703F, 0.9783765210F, 0.9790452223F, + 0.9796991504F, 0.9803384823F, 0.9809633954F, 0.9815740679F, + 0.9821706784F, 0.9827534063F, 0.9833224312F, 0.9838779332F, + 0.9844200928F, 0.9849490910F, 0.9854651087F, 0.9859683274F, + 0.9864589286F, 0.9869370940F, 0.9874030054F, 0.9878568447F, + 0.9882987937F, 0.9887290343F, 0.9891477481F, 0.9895551169F, + 0.9899513220F, 0.9903365446F, 0.9907109658F, 0.9910747662F, + 0.9914281260F, 0.9917712252F, 0.9921042433F, 0.9924273593F, + 0.9927407516F, 0.9930445982F, 0.9933390763F, 0.9936243626F, + 0.9939006331F, 0.9941680631F, 0.9944268269F, 0.9946770982F, + 0.9949190498F, 0.9951528537F, 0.9953786808F, 0.9955967011F, + 0.9958070836F, 0.9960099963F, 0.9962056061F, 0.9963940787F, + 0.9965755786F, 0.9967502693F, 0.9969183129F, 0.9970798704F, + 0.9972351013F, 0.9973841640F, 0.9975272151F, 0.9976644103F, + 0.9977959036F, 0.9979218476F, 0.9980423932F, 0.9981576901F, + 0.9982678862F, 0.9983731278F, 0.9984735596F, 0.9985693247F, + 0.9986605645F, 0.9987474186F, 0.9988300248F, 0.9989085193F, + 0.9989830364F, 0.9990537085F, 0.9991206662F, 0.9991840382F, + 0.9992439513F, 0.9993005303F, 0.9993538982F, 0.9994041757F, + 0.9994514817F, 0.9994959330F, 0.9995376444F, 0.9995767286F, + 0.9996132960F, 0.9996474550F, 0.9996793121F, 0.9997089710F, + 0.9997365339F, 0.9997621003F, 0.9997857677F, 0.9998076311F, + 0.9998277836F, 0.9998463156F, 0.9998633155F, 0.9998788692F, + 0.9998930603F, 0.9999059701F, 0.9999176774F, 0.9999282586F, + 0.9999377880F, 0.9999463370F, 0.9999539749F, 0.9999607685F, + 0.9999667820F, 0.9999720773F, 0.9999767136F, 0.9999807479F, + 0.9999842344F, 0.9999872249F, 0.9999897688F, 0.9999919127F, + 0.9999937009F, 0.9999951749F, 0.9999963738F, 0.9999973342F, + 0.9999980900F, 0.9999986724F, 0.9999991103F, 0.9999994297F, + 0.9999996543F, 0.9999998049F, 0.9999999000F, 0.9999999552F, + 0.9999999836F, 0.9999999957F, 0.9999999994F, 1.0000000000F, +}; + +static float vwin2048[1024] = { + 0.0000009241F, 0.0000083165F, 0.0000231014F, 0.0000452785F, + 0.0000748476F, 0.0001118085F, 0.0001561608F, 0.0002079041F, + 0.0002670379F, 0.0003335617F, 0.0004074748F, 0.0004887765F, + 0.0005774661F, 0.0006735427F, 0.0007770054F, 0.0008878533F, + 0.0010060853F, 0.0011317002F, 0.0012646969F, 0.0014050742F, + 0.0015528307F, 0.0017079650F, 0.0018704756F, 0.0020403610F, + 0.0022176196F, 0.0024022497F, 0.0025942495F, 0.0027936173F, + 0.0030003511F, 0.0032144490F, 0.0034359088F, 0.0036647286F, + 0.0039009061F, 0.0041444391F, 0.0043953253F, 0.0046535621F, + 0.0049191472F, 0.0051920781F, 0.0054723520F, 0.0057599664F, + 0.0060549184F, 0.0063572052F, 0.0066668239F, 0.0069837715F, + 0.0073080449F, 0.0076396410F, 0.0079785566F, 0.0083247884F, + 0.0086783330F, 0.0090391871F, 0.0094073470F, 0.0097828092F, + 0.0101655700F, 0.0105556258F, 0.0109529726F, 0.0113576065F, + 0.0117695237F, 0.0121887200F, 0.0126151913F, 0.0130489335F, + 0.0134899422F, 0.0139382130F, 0.0143937415F, 0.0148565233F, + 0.0153265536F, 0.0158038279F, 0.0162883413F, 0.0167800889F, + 0.0172790660F, 0.0177852675F, 0.0182986882F, 0.0188193231F, + 0.0193471668F, 0.0198822141F, 0.0204244594F, 0.0209738974F, + 0.0215305225F, 0.0220943289F, 0.0226653109F, 0.0232434627F, + 0.0238287784F, 0.0244212519F, 0.0250208772F, 0.0256276481F, + 0.0262415582F, 0.0268626014F, 0.0274907711F, 0.0281260608F, + 0.0287684638F, 0.0294179736F, 0.0300745833F, 0.0307382859F, + 0.0314090747F, 0.0320869424F, 0.0327718819F, 0.0334638860F, + 0.0341629474F, 0.0348690586F, 0.0355822122F, 0.0363024004F, + 0.0370296157F, 0.0377638502F, 0.0385050960F, 0.0392533451F, + 0.0400085896F, 0.0407708211F, 0.0415400315F, 0.0423162123F, + 0.0430993552F, 0.0438894515F, 0.0446864926F, 0.0454904698F, + 0.0463013742F, 0.0471191969F, 0.0479439288F, 0.0487755607F, + 0.0496140836F, 0.0504594879F, 0.0513117642F, 0.0521709031F, + 0.0530368949F, 0.0539097297F, 0.0547893979F, 0.0556758894F, + 0.0565691941F, 0.0574693019F, 0.0583762026F, 0.0592898858F, + 0.0602103410F, 0.0611375576F, 0.0620715250F, 0.0630122324F, + 0.0639596688F, 0.0649138234F, 0.0658746848F, 0.0668422421F, + 0.0678164838F, 0.0687973985F, 0.0697849746F, 0.0707792005F, + 0.0717800645F, 0.0727875547F, 0.0738016591F, 0.0748223656F, + 0.0758496620F, 0.0768835359F, 0.0779239751F, 0.0789709668F, + 0.0800244985F, 0.0810845574F, 0.0821511306F, 0.0832242052F, + 0.0843037679F, 0.0853898056F, 0.0864823050F, 0.0875812525F, + 0.0886866347F, 0.0897984378F, 0.0909166480F, 0.0920412513F, + 0.0931722338F, 0.0943095813F, 0.0954532795F, 0.0966033140F, + 0.0977596702F, 0.0989223336F, 0.1000912894F, 0.1012665227F, + 0.1024480185F, 0.1036357616F, 0.1048297369F, 0.1060299290F, + 0.1072363224F, 0.1084489014F, 0.1096676504F, 0.1108925534F, + 0.1121235946F, 0.1133607577F, 0.1146040267F, 0.1158533850F, + 0.1171088163F, 0.1183703040F, 0.1196378312F, 0.1209113812F, + 0.1221909370F, 0.1234764815F, 0.1247679974F, 0.1260654674F, + 0.1273688740F, 0.1286781995F, 0.1299934263F, 0.1313145365F, + 0.1326415121F, 0.1339743349F, 0.1353129866F, 0.1366574490F, + 0.1380077035F, 0.1393637315F, 0.1407255141F, 0.1420930325F, + 0.1434662677F, 0.1448452004F, 0.1462298115F, 0.1476200814F, + 0.1490159906F, 0.1504175195F, 0.1518246482F, 0.1532373569F, + 0.1546556253F, 0.1560794333F, 0.1575087606F, 0.1589435866F, + 0.1603838909F, 0.1618296526F, 0.1632808509F, 0.1647374648F, + 0.1661994731F, 0.1676668546F, 0.1691395880F, 0.1706176516F, + 0.1721010238F, 0.1735896829F, 0.1750836068F, 0.1765827736F, + 0.1780871610F, 0.1795967468F, 0.1811115084F, 0.1826314234F, + 0.1841564689F, 0.1856866221F, 0.1872218600F, 0.1887621595F, + 0.1903074974F, 0.1918578503F, 0.1934131947F, 0.1949735068F, + 0.1965387630F, 0.1981089393F, 0.1996840117F, 0.2012639560F, + 0.2028487479F, 0.2044383630F, 0.2060327766F, 0.2076319642F, + 0.2092359007F, 0.2108445614F, 0.2124579211F, 0.2140759545F, + 0.2156986364F, 0.2173259411F, 0.2189578432F, 0.2205943168F, + 0.2222353361F, 0.2238808751F, 0.2255309076F, 0.2271854073F, + 0.2288443480F, 0.2305077030F, 0.2321754457F, 0.2338475493F, + 0.2355239869F, 0.2372047315F, 0.2388897560F, 0.2405790329F, + 0.2422725350F, 0.2439702347F, 0.2456721043F, 0.2473781159F, + 0.2490882418F, 0.2508024539F, 0.2525207240F, 0.2542430237F, + 0.2559693248F, 0.2576995986F, 0.2594338166F, 0.2611719498F, + 0.2629139695F, 0.2646598466F, 0.2664095520F, 0.2681630564F, + 0.2699203304F, 0.2716813445F, 0.2734460691F, 0.2752144744F, + 0.2769865307F, 0.2787622079F, 0.2805414760F, 0.2823243047F, + 0.2841106637F, 0.2859005227F, 0.2876938509F, 0.2894906179F, + 0.2912907928F, 0.2930943447F, 0.2949012426F, 0.2967114554F, + 0.2985249520F, 0.3003417009F, 0.3021616708F, 0.3039848301F, + 0.3058111471F, 0.3076405901F, 0.3094731273F, 0.3113087266F, + 0.3131473560F, 0.3149889833F, 0.3168335762F, 0.3186811024F, + 0.3205315294F, 0.3223848245F, 0.3242409552F, 0.3260998886F, + 0.3279615918F, 0.3298260319F, 0.3316931758F, 0.3335629903F, + 0.3354354423F, 0.3373104982F, 0.3391881247F, 0.3410682882F, + 0.3429509551F, 0.3448360917F, 0.3467236642F, 0.3486136387F, + 0.3505059811F, 0.3524006575F, 0.3542976336F, 0.3561968753F, + 0.3580983482F, 0.3600020179F, 0.3619078499F, 0.3638158096F, + 0.3657258625F, 0.3676379737F, 0.3695521086F, 0.3714682321F, + 0.3733863094F, 0.3753063055F, 0.3772281852F, 0.3791519134F, + 0.3810774548F, 0.3830047742F, 0.3849338362F, 0.3868646053F, + 0.3887970459F, 0.3907311227F, 0.3926667998F, 0.3946040417F, + 0.3965428125F, 0.3984830765F, 0.4004247978F, 0.4023679403F, + 0.4043124683F, 0.4062583455F, 0.4082055359F, 0.4101540034F, + 0.4121037117F, 0.4140546246F, 0.4160067058F, 0.4179599190F, + 0.4199142277F, 0.4218695956F, 0.4238259861F, 0.4257833627F, + 0.4277416888F, 0.4297009279F, 0.4316610433F, 0.4336219983F, + 0.4355837562F, 0.4375462803F, 0.4395095337F, 0.4414734797F, + 0.4434380815F, 0.4454033021F, 0.4473691046F, 0.4493354521F, + 0.4513023078F, 0.4532696345F, 0.4552373954F, 0.4572055533F, + 0.4591740713F, 0.4611429123F, 0.4631120393F, 0.4650814151F, + 0.4670510028F, 0.4690207650F, 0.4709906649F, 0.4729606651F, + 0.4749307287F, 0.4769008185F, 0.4788708972F, 0.4808409279F, + 0.4828108732F, 0.4847806962F, 0.4867503597F, 0.4887198264F, + 0.4906890593F, 0.4926580213F, 0.4946266753F, 0.4965949840F, + 0.4985629105F, 0.5005304176F, 0.5024974683F, 0.5044640255F, + 0.5064300522F, 0.5083955114F, 0.5103603659F, 0.5123245790F, + 0.5142881136F, 0.5162509328F, 0.5182129997F, 0.5201742774F, + 0.5221347290F, 0.5240943178F, 0.5260530070F, 0.5280107598F, + 0.5299675395F, 0.5319233095F, 0.5338780330F, 0.5358316736F, + 0.5377841946F, 0.5397355596F, 0.5416857320F, 0.5436346755F, + 0.5455823538F, 0.5475287304F, 0.5494737691F, 0.5514174337F, + 0.5533596881F, 0.5553004962F, 0.5572398218F, 0.5591776291F, + 0.5611138821F, 0.5630485449F, 0.5649815818F, 0.5669129570F, + 0.5688426349F, 0.5707705799F, 0.5726967564F, 0.5746211290F, + 0.5765436624F, 0.5784643212F, 0.5803830702F, 0.5822998743F, + 0.5842146984F, 0.5861275076F, 0.5880382669F, 0.5899469416F, + 0.5918534968F, 0.5937578981F, 0.5956601107F, 0.5975601004F, + 0.5994578326F, 0.6013532732F, 0.6032463880F, 0.6051371429F, + 0.6070255039F, 0.6089114372F, 0.6107949090F, 0.6126758856F, + 0.6145543334F, 0.6164302191F, 0.6183035092F, 0.6201741706F, + 0.6220421700F, 0.6239074745F, 0.6257700513F, 0.6276298674F, + 0.6294868903F, 0.6313410873F, 0.6331924262F, 0.6350408745F, + 0.6368864001F, 0.6387289710F, 0.6405685552F, 0.6424051209F, + 0.6442386364F, 0.6460690702F, 0.6478963910F, 0.6497205673F, + 0.6515415682F, 0.6533593625F, 0.6551739194F, 0.6569852082F, + 0.6587931984F, 0.6605978593F, 0.6623991609F, 0.6641970728F, + 0.6659915652F, 0.6677826081F, 0.6695701718F, 0.6713542268F, + 0.6731347437F, 0.6749116932F, 0.6766850461F, 0.6784547736F, + 0.6802208469F, 0.6819832374F, 0.6837419164F, 0.6854968559F, + 0.6872480275F, 0.6889954034F, 0.6907389556F, 0.6924786566F, + 0.6942144788F, 0.6959463950F, 0.6976743780F, 0.6993984008F, + 0.7011184365F, 0.7028344587F, 0.7045464407F, 0.7062543564F, + 0.7079581796F, 0.7096578844F, 0.7113534450F, 0.7130448359F, + 0.7147320316F, 0.7164150070F, 0.7180937371F, 0.7197681970F, + 0.7214383620F, 0.7231042077F, 0.7247657098F, 0.7264228443F, + 0.7280755871F, 0.7297239147F, 0.7313678035F, 0.7330072301F, + 0.7346421715F, 0.7362726046F, 0.7378985069F, 0.7395198556F, + 0.7411366285F, 0.7427488034F, 0.7443563584F, 0.7459592717F, + 0.7475575218F, 0.7491510873F, 0.7507399471F, 0.7523240803F, + 0.7539034661F, 0.7554780839F, 0.7570479136F, 0.7586129349F, + 0.7601731279F, 0.7617284730F, 0.7632789506F, 0.7648245416F, + 0.7663652267F, 0.7679009872F, 0.7694318044F, 0.7709576599F, + 0.7724785354F, 0.7739944130F, 0.7755052749F, 0.7770111035F, + 0.7785118815F, 0.7800075916F, 0.7814982170F, 0.7829837410F, + 0.7844641472F, 0.7859394191F, 0.7874095408F, 0.7888744965F, + 0.7903342706F, 0.7917888476F, 0.7932382124F, 0.7946823501F, + 0.7961212460F, 0.7975548855F, 0.7989832544F, 0.8004063386F, + 0.8018241244F, 0.8032365981F, 0.8046437463F, 0.8060455560F, + 0.8074420141F, 0.8088331080F, 0.8102188253F, 0.8115991536F, + 0.8129740810F, 0.8143435957F, 0.8157076861F, 0.8170663409F, + 0.8184195489F, 0.8197672994F, 0.8211095817F, 0.8224463853F, + 0.8237777001F, 0.8251035161F, 0.8264238235F, 0.8277386129F, + 0.8290478750F, 0.8303516008F, 0.8316497814F, 0.8329424083F, + 0.8342294731F, 0.8355109677F, 0.8367868841F, 0.8380572148F, + 0.8393219523F, 0.8405810893F, 0.8418346190F, 0.8430825345F, + 0.8443248294F, 0.8455614974F, 0.8467925323F, 0.8480179285F, + 0.8492376802F, 0.8504517822F, 0.8516602292F, 0.8528630164F, + 0.8540601391F, 0.8552515928F, 0.8564373733F, 0.8576174766F, + 0.8587918990F, 0.8599606368F, 0.8611236868F, 0.8622810460F, + 0.8634327113F, 0.8645786802F, 0.8657189504F, 0.8668535195F, + 0.8679823857F, 0.8691055472F, 0.8702230025F, 0.8713347503F, + 0.8724407896F, 0.8735411194F, 0.8746357394F, 0.8757246489F, + 0.8768078479F, 0.8778853364F, 0.8789571146F, 0.8800231832F, + 0.8810835427F, 0.8821381942F, 0.8831871387F, 0.8842303777F, + 0.8852679127F, 0.8862997456F, 0.8873258784F, 0.8883463132F, + 0.8893610527F, 0.8903700994F, 0.8913734562F, 0.8923711263F, + 0.8933631129F, 0.8943494196F, 0.8953300500F, 0.8963050083F, + 0.8972742985F, 0.8982379249F, 0.8991958922F, 0.9001482052F, + 0.9010948688F, 0.9020358883F, 0.9029712690F, 0.9039010165F, + 0.9048251367F, 0.9057436357F, 0.9066565195F, 0.9075637946F, + 0.9084654678F, 0.9093615456F, 0.9102520353F, 0.9111369440F, + 0.9120162792F, 0.9128900484F, 0.9137582595F, 0.9146209204F, + 0.9154780394F, 0.9163296248F, 0.9171756853F, 0.9180162296F, + 0.9188512667F, 0.9196808057F, 0.9205048559F, 0.9213234270F, + 0.9221365285F, 0.9229441704F, 0.9237463629F, 0.9245431160F, + 0.9253344404F, 0.9261203465F, 0.9269008453F, 0.9276759477F, + 0.9284456648F, 0.9292100080F, 0.9299689889F, 0.9307226190F, + 0.9314709103F, 0.9322138747F, 0.9329515245F, 0.9336838721F, + 0.9344109300F, 0.9351327108F, 0.9358492275F, 0.9365604931F, + 0.9372665208F, 0.9379673239F, 0.9386629160F, 0.9393533107F, + 0.9400385220F, 0.9407185637F, 0.9413934501F, 0.9420631954F, + 0.9427278141F, 0.9433873208F, 0.9440417304F, 0.9446910576F, + 0.9453353176F, 0.9459745255F, 0.9466086968F, 0.9472378469F, + 0.9478619915F, 0.9484811463F, 0.9490953274F, 0.9497045506F, + 0.9503088323F, 0.9509081888F, 0.9515026365F, 0.9520921921F, + 0.9526768723F, 0.9532566940F, 0.9538316742F, 0.9544018300F, + 0.9549671786F, 0.9555277375F, 0.9560835241F, 0.9566345562F, + 0.9571808513F, 0.9577224275F, 0.9582593027F, 0.9587914949F, + 0.9593190225F, 0.9598419038F, 0.9603601571F, 0.9608738012F, + 0.9613828546F, 0.9618873361F, 0.9623872646F, 0.9628826591F, + 0.9633735388F, 0.9638599227F, 0.9643418303F, 0.9648192808F, + 0.9652922939F, 0.9657608890F, 0.9662250860F, 0.9666849046F, + 0.9671403646F, 0.9675914861F, 0.9680382891F, 0.9684807937F, + 0.9689190202F, 0.9693529890F, 0.9697827203F, 0.9702082347F, + 0.9706295529F, 0.9710466953F, 0.9714596828F, 0.9718685362F, + 0.9722732762F, 0.9726739240F, 0.9730705005F, 0.9734630267F, + 0.9738515239F, 0.9742360134F, 0.9746165163F, 0.9749930540F, + 0.9753656481F, 0.9757343198F, 0.9760990909F, 0.9764599829F, + 0.9768170175F, 0.9771702164F, 0.9775196013F, 0.9778651941F, + 0.9782070167F, 0.9785450909F, 0.9788794388F, 0.9792100824F, + 0.9795370437F, 0.9798603449F, 0.9801800080F, 0.9804960554F, + 0.9808085092F, 0.9811173916F, 0.9814227251F, 0.9817245318F, + 0.9820228343F, 0.9823176549F, 0.9826090160F, 0.9828969402F, + 0.9831814498F, 0.9834625674F, 0.9837403156F, 0.9840147169F, + 0.9842857939F, 0.9845535692F, 0.9848180654F, 0.9850793052F, + 0.9853373113F, 0.9855921062F, 0.9858437127F, 0.9860921535F, + 0.9863374512F, 0.9865796287F, 0.9868187085F, 0.9870547136F, + 0.9872876664F, 0.9875175899F, 0.9877445067F, 0.9879684396F, + 0.9881894112F, 0.9884074444F, 0.9886225619F, 0.9888347863F, + 0.9890441404F, 0.9892506468F, 0.9894543284F, 0.9896552077F, + 0.9898533074F, 0.9900486502F, 0.9902412587F, 0.9904311555F, + 0.9906183633F, 0.9908029045F, 0.9909848019F, 0.9911640779F, + 0.9913407550F, 0.9915148557F, 0.9916864025F, 0.9918554179F, + 0.9920219241F, 0.9921859437F, 0.9923474989F, 0.9925066120F, + 0.9926633054F, 0.9928176012F, 0.9929695218F, 0.9931190891F, + 0.9932663254F, 0.9934112527F, 0.9935538932F, 0.9936942686F, + 0.9938324012F, 0.9939683126F, 0.9941020248F, 0.9942335597F, + 0.9943629388F, 0.9944901841F, 0.9946153170F, 0.9947383593F, + 0.9948593325F, 0.9949782579F, 0.9950951572F, 0.9952100516F, + 0.9953229625F, 0.9954339111F, 0.9955429186F, 0.9956500062F, + 0.9957551948F, 0.9958585056F, 0.9959599593F, 0.9960595769F, + 0.9961573792F, 0.9962533869F, 0.9963476206F, 0.9964401009F, + 0.9965308483F, 0.9966198833F, 0.9967072261F, 0.9967928971F, + 0.9968769164F, 0.9969593041F, 0.9970400804F, 0.9971192651F, + 0.9971968781F, 0.9972729391F, 0.9973474680F, 0.9974204842F, + 0.9974920074F, 0.9975620569F, 0.9976306521F, 0.9976978122F, + 0.9977635565F, 0.9978279039F, 0.9978908736F, 0.9979524842F, + 0.9980127547F, 0.9980717037F, 0.9981293499F, 0.9981857116F, + 0.9982408073F, 0.9982946554F, 0.9983472739F, 0.9983986810F, + 0.9984488947F, 0.9984979328F, 0.9985458132F, 0.9985925534F, + 0.9986381711F, 0.9986826838F, 0.9987261086F, 0.9987684630F, + 0.9988097640F, 0.9988500286F, 0.9988892738F, 0.9989275163F, + 0.9989647727F, 0.9990010597F, 0.9990363938F, 0.9990707911F, + 0.9991042679F, 0.9991368404F, 0.9991685244F, 0.9991993358F, + 0.9992292905F, 0.9992584038F, 0.9992866914F, 0.9993141686F, + 0.9993408506F, 0.9993667526F, 0.9993918895F, 0.9994162761F, + 0.9994399273F, 0.9994628576F, 0.9994850815F, 0.9995066133F, + 0.9995274672F, 0.9995476574F, 0.9995671978F, 0.9995861021F, + 0.9996043841F, 0.9996220573F, 0.9996391352F, 0.9996556310F, + 0.9996715579F, 0.9996869288F, 0.9997017568F, 0.9997160543F, + 0.9997298342F, 0.9997431088F, 0.9997558905F, 0.9997681914F, + 0.9997800236F, 0.9997913990F, 0.9998023292F, 0.9998128261F, + 0.9998229009F, 0.9998325650F, 0.9998418296F, 0.9998507058F, + 0.9998592044F, 0.9998673362F, 0.9998751117F, 0.9998825415F, + 0.9998896358F, 0.9998964047F, 0.9999028584F, 0.9999090066F, + 0.9999148590F, 0.9999204253F, 0.9999257148F, 0.9999307368F, + 0.9999355003F, 0.9999400144F, 0.9999442878F, 0.9999483293F, + 0.9999521472F, 0.9999557499F, 0.9999591457F, 0.9999623426F, + 0.9999653483F, 0.9999681708F, 0.9999708175F, 0.9999732959F, + 0.9999756132F, 0.9999777765F, 0.9999797928F, 0.9999816688F, + 0.9999834113F, 0.9999850266F, 0.9999865211F, 0.9999879009F, + 0.9999891721F, 0.9999903405F, 0.9999914118F, 0.9999923914F, + 0.9999932849F, 0.9999940972F, 0.9999948336F, 0.9999954989F, + 0.9999960978F, 0.9999966349F, 0.9999971146F, 0.9999975411F, + 0.9999979185F, 0.9999982507F, 0.9999985414F, 0.9999987944F, + 0.9999990129F, 0.9999992003F, 0.9999993596F, 0.9999994939F, + 0.9999996059F, 0.9999996981F, 0.9999997732F, 0.9999998333F, + 0.9999998805F, 0.9999999170F, 0.9999999444F, 0.9999999643F, + 0.9999999784F, 0.9999999878F, 0.9999999937F, 0.9999999972F, + 0.9999999990F, 0.9999999997F, 1.0000000000F, 1.0000000000F, +}; + +static float vwin4096[2048] = { + 0.0000002310F, 0.0000020791F, 0.0000057754F, 0.0000113197F, + 0.0000187121F, 0.0000279526F, 0.0000390412F, 0.0000519777F, + 0.0000667623F, 0.0000833949F, 0.0001018753F, 0.0001222036F, + 0.0001443798F, 0.0001684037F, 0.0001942754F, 0.0002219947F, + 0.0002515616F, 0.0002829761F, 0.0003162380F, 0.0003513472F, + 0.0003883038F, 0.0004271076F, 0.0004677584F, 0.0005102563F, + 0.0005546011F, 0.0006007928F, 0.0006488311F, 0.0006987160F, + 0.0007504474F, 0.0008040251F, 0.0008594490F, 0.0009167191F, + 0.0009758351F, 0.0010367969F, 0.0010996044F, 0.0011642574F, + 0.0012307558F, 0.0012990994F, 0.0013692880F, 0.0014413216F, + 0.0015151998F, 0.0015909226F, 0.0016684898F, 0.0017479011F, + 0.0018291565F, 0.0019122556F, 0.0019971983F, 0.0020839845F, + 0.0021726138F, 0.0022630861F, 0.0023554012F, 0.0024495588F, + 0.0025455588F, 0.0026434008F, 0.0027430847F, 0.0028446103F, + 0.0029479772F, 0.0030531853F, 0.0031602342F, 0.0032691238F, + 0.0033798538F, 0.0034924239F, 0.0036068338F, 0.0037230833F, + 0.0038411721F, 0.0039610999F, 0.0040828664F, 0.0042064714F, + 0.0043319145F, 0.0044591954F, 0.0045883139F, 0.0047192696F, + 0.0048520622F, 0.0049866914F, 0.0051231569F, 0.0052614583F, + 0.0054015953F, 0.0055435676F, 0.0056873748F, 0.0058330166F, + 0.0059804926F, 0.0061298026F, 0.0062809460F, 0.0064339226F, + 0.0065887320F, 0.0067453738F, 0.0069038476F, 0.0070641531F, + 0.0072262899F, 0.0073902575F, 0.0075560556F, 0.0077236838F, + 0.0078931417F, 0.0080644288F, 0.0082375447F, 0.0084124891F, + 0.0085892615F, 0.0087678614F, 0.0089482885F, 0.0091305422F, + 0.0093146223F, 0.0095005281F, 0.0096882592F, 0.0098778153F, + 0.0100691958F, 0.0102624002F, 0.0104574281F, 0.0106542791F, + 0.0108529525F, 0.0110534480F, 0.0112557651F, 0.0114599032F, + 0.0116658618F, 0.0118736405F, 0.0120832387F, 0.0122946560F, + 0.0125078917F, 0.0127229454F, 0.0129398166F, 0.0131585046F, + 0.0133790090F, 0.0136013292F, 0.0138254647F, 0.0140514149F, + 0.0142791792F, 0.0145087572F, 0.0147401481F, 0.0149733515F, + 0.0152083667F, 0.0154451932F, 0.0156838304F, 0.0159242777F, + 0.0161665345F, 0.0164106001F, 0.0166564741F, 0.0169041557F, + 0.0171536443F, 0.0174049393F, 0.0176580401F, 0.0179129461F, + 0.0181696565F, 0.0184281708F, 0.0186884883F, 0.0189506084F, + 0.0192145303F, 0.0194802535F, 0.0197477772F, 0.0200171008F, + 0.0202882236F, 0.0205611449F, 0.0208358639F, 0.0211123801F, + 0.0213906927F, 0.0216708011F, 0.0219527043F, 0.0222364019F, + 0.0225218930F, 0.0228091769F, 0.0230982529F, 0.0233891203F, + 0.0236817782F, 0.0239762259F, 0.0242724628F, 0.0245704880F, + 0.0248703007F, 0.0251719002F, 0.0254752858F, 0.0257804565F, + 0.0260874117F, 0.0263961506F, 0.0267066722F, 0.0270189760F, + 0.0273330609F, 0.0276489263F, 0.0279665712F, 0.0282859949F, + 0.0286071966F, 0.0289301753F, 0.0292549303F, 0.0295814607F, + 0.0299097656F, 0.0302398442F, 0.0305716957F, 0.0309053191F, + 0.0312407135F, 0.0315778782F, 0.0319168122F, 0.0322575145F, + 0.0325999844F, 0.0329442209F, 0.0332902231F, 0.0336379900F, + 0.0339875208F, 0.0343388146F, 0.0346918703F, 0.0350466871F, + 0.0354032640F, 0.0357616000F, 0.0361216943F, 0.0364835458F, + 0.0368471535F, 0.0372125166F, 0.0375796339F, 0.0379485046F, + 0.0383191276F, 0.0386915020F, 0.0390656267F, 0.0394415008F, + 0.0398191231F, 0.0401984927F, 0.0405796086F, 0.0409624698F, + 0.0413470751F, 0.0417334235F, 0.0421215141F, 0.0425113457F, + 0.0429029172F, 0.0432962277F, 0.0436912760F, 0.0440880610F, + 0.0444865817F, 0.0448868370F, 0.0452888257F, 0.0456925468F, + 0.0460979992F, 0.0465051816F, 0.0469140931F, 0.0473247325F, + 0.0477370986F, 0.0481511902F, 0.0485670064F, 0.0489845458F, + 0.0494038074F, 0.0498247899F, 0.0502474922F, 0.0506719131F, + 0.0510980514F, 0.0515259060F, 0.0519554756F, 0.0523867590F, + 0.0528197550F, 0.0532544624F, 0.0536908800F, 0.0541290066F, + 0.0545688408F, 0.0550103815F, 0.0554536274F, 0.0558985772F, + 0.0563452297F, 0.0567935837F, 0.0572436377F, 0.0576953907F, + 0.0581488412F, 0.0586039880F, 0.0590608297F, 0.0595193651F, + 0.0599795929F, 0.0604415117F, 0.0609051202F, 0.0613704170F, + 0.0618374009F, 0.0623060704F, 0.0627764243F, 0.0632484611F, + 0.0637221795F, 0.0641975781F, 0.0646746555F, 0.0651534104F, + 0.0656338413F, 0.0661159469F, 0.0665997257F, 0.0670851763F, + 0.0675722973F, 0.0680610873F, 0.0685515448F, 0.0690436684F, + 0.0695374567F, 0.0700329081F, 0.0705300213F, 0.0710287947F, + 0.0715292269F, 0.0720313163F, 0.0725350616F, 0.0730404612F, + 0.0735475136F, 0.0740562172F, 0.0745665707F, 0.0750785723F, + 0.0755922207F, 0.0761075143F, 0.0766244515F, 0.0771430307F, + 0.0776632505F, 0.0781851092F, 0.0787086052F, 0.0792337371F, + 0.0797605032F, 0.0802889018F, 0.0808189315F, 0.0813505905F, + 0.0818838773F, 0.0824187903F, 0.0829553277F, 0.0834934881F, + 0.0840332697F, 0.0845746708F, 0.0851176899F, 0.0856623252F, + 0.0862085751F, 0.0867564379F, 0.0873059119F, 0.0878569954F, + 0.0884096867F, 0.0889639840F, 0.0895198858F, 0.0900773902F, + 0.0906364955F, 0.0911972000F, 0.0917595019F, 0.0923233995F, + 0.0928888909F, 0.0934559745F, 0.0940246485F, 0.0945949110F, + 0.0951667604F, 0.0957401946F, 0.0963152121F, 0.0968918109F, + 0.0974699893F, 0.0980497454F, 0.0986310773F, 0.0992139832F, + 0.0997984614F, 0.1003845098F, 0.1009721267F, 0.1015613101F, + 0.1021520582F, 0.1027443692F, 0.1033382410F, 0.1039336718F, + 0.1045306597F, 0.1051292027F, 0.1057292990F, 0.1063309466F, + 0.1069341435F, 0.1075388878F, 0.1081451776F, 0.1087530108F, + 0.1093623856F, 0.1099732998F, 0.1105857516F, 0.1111997389F, + 0.1118152597F, 0.1124323121F, 0.1130508939F, 0.1136710032F, + 0.1142926379F, 0.1149157960F, 0.1155404755F, 0.1161666742F, + 0.1167943901F, 0.1174236211F, 0.1180543652F, 0.1186866202F, + 0.1193203841F, 0.1199556548F, 0.1205924300F, 0.1212307078F, + 0.1218704860F, 0.1225117624F, 0.1231545349F, 0.1237988013F, + 0.1244445596F, 0.1250918074F, 0.1257405427F, 0.1263907632F, + 0.1270424667F, 0.1276956512F, 0.1283503142F, 0.1290064537F, + 0.1296640674F, 0.1303231530F, 0.1309837084F, 0.1316457312F, + 0.1323092193F, 0.1329741703F, 0.1336405820F, 0.1343084520F, + 0.1349777782F, 0.1356485582F, 0.1363207897F, 0.1369944704F, + 0.1376695979F, 0.1383461700F, 0.1390241842F, 0.1397036384F, + 0.1403845300F, 0.1410668567F, 0.1417506162F, 0.1424358061F, + 0.1431224240F, 0.1438104674F, 0.1444999341F, 0.1451908216F, + 0.1458831274F, 0.1465768492F, 0.1472719844F, 0.1479685308F, + 0.1486664857F, 0.1493658468F, 0.1500666115F, 0.1507687775F, + 0.1514723422F, 0.1521773031F, 0.1528836577F, 0.1535914035F, + 0.1543005380F, 0.1550110587F, 0.1557229631F, 0.1564362485F, + 0.1571509124F, 0.1578669524F, 0.1585843657F, 0.1593031499F, + 0.1600233024F, 0.1607448205F, 0.1614677017F, 0.1621919433F, + 0.1629175428F, 0.1636444975F, 0.1643728047F, 0.1651024619F, + 0.1658334665F, 0.1665658156F, 0.1672995067F, 0.1680345371F, + 0.1687709041F, 0.1695086050F, 0.1702476372F, 0.1709879978F, + 0.1717296843F, 0.1724726938F, 0.1732170237F, 0.1739626711F, + 0.1747096335F, 0.1754579079F, 0.1762074916F, 0.1769583819F, + 0.1777105760F, 0.1784640710F, 0.1792188642F, 0.1799749529F, + 0.1807323340F, 0.1814910049F, 0.1822509628F, 0.1830122046F, + 0.1837747277F, 0.1845385292F, 0.1853036062F, 0.1860699558F, + 0.1868375751F, 0.1876064613F, 0.1883766114F, 0.1891480226F, + 0.1899206919F, 0.1906946164F, 0.1914697932F, 0.1922462194F, + 0.1930238919F, 0.1938028079F, 0.1945829643F, 0.1953643583F, + 0.1961469868F, 0.1969308468F, 0.1977159353F, 0.1985022494F, + 0.1992897859F, 0.2000785420F, 0.2008685145F, 0.2016597005F, + 0.2024520968F, 0.2032457005F, 0.2040405084F, 0.2048365175F, + 0.2056337247F, 0.2064321269F, 0.2072317211F, 0.2080325041F, + 0.2088344727F, 0.2096376240F, 0.2104419547F, 0.2112474618F, + 0.2120541420F, 0.2128619923F, 0.2136710094F, 0.2144811902F, + 0.2152925315F, 0.2161050301F, 0.2169186829F, 0.2177334866F, + 0.2185494381F, 0.2193665340F, 0.2201847712F, 0.2210041465F, + 0.2218246565F, 0.2226462981F, 0.2234690680F, 0.2242929629F, + 0.2251179796F, 0.2259441147F, 0.2267713650F, 0.2275997272F, + 0.2284291979F, 0.2292597739F, 0.2300914518F, 0.2309242283F, + 0.2317581001F, 0.2325930638F, 0.2334291160F, 0.2342662534F, + 0.2351044727F, 0.2359437703F, 0.2367841431F, 0.2376255875F, + 0.2384681001F, 0.2393116776F, 0.2401563165F, 0.2410020134F, + 0.2418487649F, 0.2426965675F, 0.2435454178F, 0.2443953122F, + 0.2452462474F, 0.2460982199F, 0.2469512262F, 0.2478052628F, + 0.2486603262F, 0.2495164129F, 0.2503735194F, 0.2512316421F, + 0.2520907776F, 0.2529509222F, 0.2538120726F, 0.2546742250F, + 0.2555373760F, 0.2564015219F, 0.2572666593F, 0.2581327845F, + 0.2589998939F, 0.2598679840F, 0.2607370510F, 0.2616070916F, + 0.2624781019F, 0.2633500783F, 0.2642230173F, 0.2650969152F, + 0.2659717684F, 0.2668475731F, 0.2677243257F, 0.2686020226F, + 0.2694806601F, 0.2703602344F, 0.2712407419F, 0.2721221789F, + 0.2730045417F, 0.2738878265F, 0.2747720297F, 0.2756571474F, + 0.2765431760F, 0.2774301117F, 0.2783179508F, 0.2792066895F, + 0.2800963240F, 0.2809868505F, 0.2818782654F, 0.2827705647F, + 0.2836637447F, 0.2845578016F, 0.2854527315F, 0.2863485307F, + 0.2872451953F, 0.2881427215F, 0.2890411055F, 0.2899403433F, + 0.2908404312F, 0.2917413654F, 0.2926431418F, 0.2935457567F, + 0.2944492061F, 0.2953534863F, 0.2962585932F, 0.2971645230F, + 0.2980712717F, 0.2989788356F, 0.2998872105F, 0.3007963927F, + 0.3017063781F, 0.3026171629F, 0.3035287430F, 0.3044411145F, + 0.3053542736F, 0.3062682161F, 0.3071829381F, 0.3080984356F, + 0.3090147047F, 0.3099317413F, 0.3108495414F, 0.3117681011F, + 0.3126874163F, 0.3136074830F, 0.3145282972F, 0.3154498548F, + 0.3163721517F, 0.3172951841F, 0.3182189477F, 0.3191434385F, + 0.3200686525F, 0.3209945856F, 0.3219212336F, 0.3228485927F, + 0.3237766585F, 0.3247054271F, 0.3256348943F, 0.3265650560F, + 0.3274959081F, 0.3284274465F, 0.3293596671F, 0.3302925657F, + 0.3312261382F, 0.3321603804F, 0.3330952882F, 0.3340308574F, + 0.3349670838F, 0.3359039634F, 0.3368414919F, 0.3377796651F, + 0.3387184789F, 0.3396579290F, 0.3405980113F, 0.3415387216F, + 0.3424800556F, 0.3434220091F, 0.3443645779F, 0.3453077578F, + 0.3462515446F, 0.3471959340F, 0.3481409217F, 0.3490865036F, + 0.3500326754F, 0.3509794328F, 0.3519267715F, 0.3528746873F, + 0.3538231759F, 0.3547722330F, 0.3557218544F, 0.3566720357F, + 0.3576227727F, 0.3585740610F, 0.3595258964F, 0.3604782745F, + 0.3614311910F, 0.3623846417F, 0.3633386221F, 0.3642931280F, + 0.3652481549F, 0.3662036987F, 0.3671597548F, 0.3681163191F, + 0.3690733870F, 0.3700309544F, 0.3709890167F, 0.3719475696F, + 0.3729066089F, 0.3738661299F, 0.3748261285F, 0.3757866002F, + 0.3767475406F, 0.3777089453F, 0.3786708100F, 0.3796331302F, + 0.3805959014F, 0.3815591194F, 0.3825227796F, 0.3834868777F, + 0.3844514093F, 0.3854163698F, 0.3863817549F, 0.3873475601F, + 0.3883137810F, 0.3892804131F, 0.3902474521F, 0.3912148933F, + 0.3921827325F, 0.3931509650F, 0.3941195865F, 0.3950885925F, + 0.3960579785F, 0.3970277400F, 0.3979978725F, 0.3989683716F, + 0.3999392328F, 0.4009104516F, 0.4018820234F, 0.4028539438F, + 0.4038262084F, 0.4047988125F, 0.4057717516F, 0.4067450214F, + 0.4077186172F, 0.4086925345F, 0.4096667688F, 0.4106413155F, + 0.4116161703F, 0.4125913284F, 0.4135667854F, 0.4145425368F, + 0.4155185780F, 0.4164949044F, 0.4174715116F, 0.4184483949F, + 0.4194255498F, 0.4204029718F, 0.4213806563F, 0.4223585987F, + 0.4233367946F, 0.4243152392F, 0.4252939281F, 0.4262728566F, + 0.4272520202F, 0.4282314144F, 0.4292110345F, 0.4301908760F, + 0.4311709343F, 0.4321512047F, 0.4331316828F, 0.4341123639F, + 0.4350932435F, 0.4360743168F, 0.4370555794F, 0.4380370267F, + 0.4390186540F, 0.4400004567F, 0.4409824303F, 0.4419645701F, + 0.4429468716F, 0.4439293300F, 0.4449119409F, 0.4458946996F, + 0.4468776014F, 0.4478606418F, 0.4488438162F, 0.4498271199F, + 0.4508105483F, 0.4517940967F, 0.4527777607F, 0.4537615355F, + 0.4547454165F, 0.4557293991F, 0.4567134786F, 0.4576976505F, + 0.4586819101F, 0.4596662527F, 0.4606506738F, 0.4616351687F, + 0.4626197328F, 0.4636043614F, 0.4645890499F, 0.4655737936F, + 0.4665585880F, 0.4675434284F, 0.4685283101F, 0.4695132286F, + 0.4704981791F, 0.4714831570F, 0.4724681577F, 0.4734531766F, + 0.4744382089F, 0.4754232501F, 0.4764082956F, 0.4773933406F, + 0.4783783806F, 0.4793634108F, 0.4803484267F, 0.4813334237F, + 0.4823183969F, 0.4833033419F, 0.4842882540F, 0.4852731285F, + 0.4862579608F, 0.4872427462F, 0.4882274802F, 0.4892121580F, + 0.4901967751F, 0.4911813267F, 0.4921658083F, 0.4931502151F, + 0.4941345427F, 0.4951187863F, 0.4961029412F, 0.4970870029F, + 0.4980709667F, 0.4990548280F, 0.5000385822F, 0.5010222245F, + 0.5020057505F, 0.5029891553F, 0.5039724345F, 0.5049555834F, + 0.5059385973F, 0.5069214716F, 0.5079042018F, 0.5088867831F, + 0.5098692110F, 0.5108514808F, 0.5118335879F, 0.5128155277F, + 0.5137972956F, 0.5147788869F, 0.5157602971F, 0.5167415215F, + 0.5177225555F, 0.5187033945F, 0.5196840339F, 0.5206644692F, + 0.5216446956F, 0.5226247086F, 0.5236045035F, 0.5245840759F, + 0.5255634211F, 0.5265425344F, 0.5275214114F, 0.5285000474F, + 0.5294784378F, 0.5304565781F, 0.5314344637F, 0.5324120899F, + 0.5333894522F, 0.5343665461F, 0.5353433670F, 0.5363199102F, + 0.5372961713F, 0.5382721457F, 0.5392478287F, 0.5402232159F, + 0.5411983027F, 0.5421730845F, 0.5431475569F, 0.5441217151F, + 0.5450955548F, 0.5460690714F, 0.5470422602F, 0.5480151169F, + 0.5489876368F, 0.5499598155F, 0.5509316484F, 0.5519031310F, + 0.5528742587F, 0.5538450271F, 0.5548154317F, 0.5557854680F, + 0.5567551314F, 0.5577244174F, 0.5586933216F, 0.5596618395F, + 0.5606299665F, 0.5615976983F, 0.5625650302F, 0.5635319580F, + 0.5644984770F, 0.5654645828F, 0.5664302709F, 0.5673955370F, + 0.5683603765F, 0.5693247850F, 0.5702887580F, 0.5712522912F, + 0.5722153800F, 0.5731780200F, 0.5741402069F, 0.5751019362F, + 0.5760632034F, 0.5770240042F, 0.5779843341F, 0.5789441889F, + 0.5799035639F, 0.5808624549F, 0.5818208575F, 0.5827787673F, + 0.5837361800F, 0.5846930910F, 0.5856494961F, 0.5866053910F, + 0.5875607712F, 0.5885156324F, 0.5894699703F, 0.5904237804F, + 0.5913770586F, 0.5923298004F, 0.5932820016F, 0.5942336578F, + 0.5951847646F, 0.5961353179F, 0.5970853132F, 0.5980347464F, + 0.5989836131F, 0.5999319090F, 0.6008796298F, 0.6018267713F, + 0.6027733292F, 0.6037192993F, 0.6046646773F, 0.6056094589F, + 0.6065536400F, 0.6074972162F, 0.6084401833F, 0.6093825372F, + 0.6103242736F, 0.6112653884F, 0.6122058772F, 0.6131457359F, + 0.6140849604F, 0.6150235464F, 0.6159614897F, 0.6168987862F, + 0.6178354318F, 0.6187714223F, 0.6197067535F, 0.6206414213F, + 0.6215754215F, 0.6225087501F, 0.6234414028F, 0.6243733757F, + 0.6253046646F, 0.6262352654F, 0.6271651739F, 0.6280943862F, + 0.6290228982F, 0.6299507057F, 0.6308778048F, 0.6318041913F, + 0.6327298612F, 0.6336548105F, 0.6345790352F, 0.6355025312F, + 0.6364252945F, 0.6373473211F, 0.6382686070F, 0.6391891483F, + 0.6401089409F, 0.6410279808F, 0.6419462642F, 0.6428637869F, + 0.6437805452F, 0.6446965350F, 0.6456117524F, 0.6465261935F, + 0.6474398544F, 0.6483527311F, 0.6492648197F, 0.6501761165F, + 0.6510866174F, 0.6519963186F, 0.6529052162F, 0.6538133064F, + 0.6547205854F, 0.6556270492F, 0.6565326941F, 0.6574375162F, + 0.6583415117F, 0.6592446769F, 0.6601470079F, 0.6610485009F, + 0.6619491521F, 0.6628489578F, 0.6637479143F, 0.6646460177F, + 0.6655432643F, 0.6664396505F, 0.6673351724F, 0.6682298264F, + 0.6691236087F, 0.6700165157F, 0.6709085436F, 0.6717996889F, + 0.6726899478F, 0.6735793167F, 0.6744677918F, 0.6753553697F, + 0.6762420466F, 0.6771278190F, 0.6780126832F, 0.6788966357F, + 0.6797796728F, 0.6806617909F, 0.6815429866F, 0.6824232562F, + 0.6833025961F, 0.6841810030F, 0.6850584731F, 0.6859350031F, + 0.6868105894F, 0.6876852284F, 0.6885589168F, 0.6894316510F, + 0.6903034275F, 0.6911742430F, 0.6920440939F, 0.6929129769F, + 0.6937808884F, 0.6946478251F, 0.6955137837F, 0.6963787606F, + 0.6972427525F, 0.6981057560F, 0.6989677678F, 0.6998287845F, + 0.7006888028F, 0.7015478194F, 0.7024058309F, 0.7032628340F, + 0.7041188254F, 0.7049738019F, 0.7058277601F, 0.7066806969F, + 0.7075326089F, 0.7083834929F, 0.7092333457F, 0.7100821640F, + 0.7109299447F, 0.7117766846F, 0.7126223804F, 0.7134670291F, + 0.7143106273F, 0.7151531721F, 0.7159946602F, 0.7168350885F, + 0.7176744539F, 0.7185127534F, 0.7193499837F, 0.7201861418F, + 0.7210212247F, 0.7218552293F, 0.7226881526F, 0.7235199914F, + 0.7243507428F, 0.7251804039F, 0.7260089715F, 0.7268364426F, + 0.7276628144F, 0.7284880839F, 0.7293122481F, 0.7301353040F, + 0.7309572487F, 0.7317780794F, 0.7325977930F, 0.7334163868F, + 0.7342338579F, 0.7350502033F, 0.7358654202F, 0.7366795059F, + 0.7374924573F, 0.7383042718F, 0.7391149465F, 0.7399244787F, + 0.7407328655F, 0.7415401041F, 0.7423461920F, 0.7431511261F, + 0.7439549040F, 0.7447575227F, 0.7455589797F, 0.7463592723F, + 0.7471583976F, 0.7479563532F, 0.7487531363F, 0.7495487443F, + 0.7503431745F, 0.7511364244F, 0.7519284913F, 0.7527193726F, + 0.7535090658F, 0.7542975683F, 0.7550848776F, 0.7558709910F, + 0.7566559062F, 0.7574396205F, 0.7582221314F, 0.7590034366F, + 0.7597835334F, 0.7605624194F, 0.7613400923F, 0.7621165495F, + 0.7628917886F, 0.7636658072F, 0.7644386030F, 0.7652101735F, + 0.7659805164F, 0.7667496292F, 0.7675175098F, 0.7682841556F, + 0.7690495645F, 0.7698137341F, 0.7705766622F, 0.7713383463F, + 0.7720987844F, 0.7728579741F, 0.7736159132F, 0.7743725994F, + 0.7751280306F, 0.7758822046F, 0.7766351192F, 0.7773867722F, + 0.7781371614F, 0.7788862848F, 0.7796341401F, 0.7803807253F, + 0.7811260383F, 0.7818700769F, 0.7826128392F, 0.7833543230F, + 0.7840945263F, 0.7848334471F, 0.7855710833F, 0.7863074330F, + 0.7870424941F, 0.7877762647F, 0.7885087428F, 0.7892399264F, + 0.7899698137F, 0.7906984026F, 0.7914256914F, 0.7921516780F, + 0.7928763607F, 0.7935997375F, 0.7943218065F, 0.7950425661F, + 0.7957620142F, 0.7964801492F, 0.7971969692F, 0.7979124724F, + 0.7986266570F, 0.7993395214F, 0.8000510638F, 0.8007612823F, + 0.8014701754F, 0.8021777413F, 0.8028839784F, 0.8035888849F, + 0.8042924592F, 0.8049946997F, 0.8056956048F, 0.8063951727F, + 0.8070934020F, 0.8077902910F, 0.8084858381F, 0.8091800419F, + 0.8098729007F, 0.8105644130F, 0.8112545774F, 0.8119433922F, + 0.8126308561F, 0.8133169676F, 0.8140017251F, 0.8146851272F, + 0.8153671726F, 0.8160478598F, 0.8167271874F, 0.8174051539F, + 0.8180817582F, 0.8187569986F, 0.8194308741F, 0.8201033831F, + 0.8207745244F, 0.8214442966F, 0.8221126986F, 0.8227797290F, + 0.8234453865F, 0.8241096700F, 0.8247725781F, 0.8254341097F, + 0.8260942636F, 0.8267530385F, 0.8274104334F, 0.8280664470F, + 0.8287210782F, 0.8293743259F, 0.8300261889F, 0.8306766662F, + 0.8313257566F, 0.8319734591F, 0.8326197727F, 0.8332646963F, + 0.8339082288F, 0.8345503692F, 0.8351911167F, 0.8358304700F, + 0.8364684284F, 0.8371049907F, 0.8377401562F, 0.8383739238F, + 0.8390062927F, 0.8396372618F, 0.8402668305F, 0.8408949977F, + 0.8415217626F, 0.8421471245F, 0.8427710823F, 0.8433936354F, + 0.8440147830F, 0.8446345242F, 0.8452528582F, 0.8458697844F, + 0.8464853020F, 0.8470994102F, 0.8477121084F, 0.8483233958F, + 0.8489332718F, 0.8495417356F, 0.8501487866F, 0.8507544243F, + 0.8513586479F, 0.8519614568F, 0.8525628505F, 0.8531628283F, + 0.8537613897F, 0.8543585341F, 0.8549542611F, 0.8555485699F, + 0.8561414603F, 0.8567329315F, 0.8573229832F, 0.8579116149F, + 0.8584988262F, 0.8590846165F, 0.8596689855F, 0.8602519327F, + 0.8608334577F, 0.8614135603F, 0.8619922399F, 0.8625694962F, + 0.8631453289F, 0.8637197377F, 0.8642927222F, 0.8648642821F, + 0.8654344172F, 0.8660031272F, 0.8665704118F, 0.8671362708F, + 0.8677007039F, 0.8682637109F, 0.8688252917F, 0.8693854460F, + 0.8699441737F, 0.8705014745F, 0.8710573485F, 0.8716117953F, + 0.8721648150F, 0.8727164073F, 0.8732665723F, 0.8738153098F, + 0.8743626197F, 0.8749085021F, 0.8754529569F, 0.8759959840F, + 0.8765375835F, 0.8770777553F, 0.8776164996F, 0.8781538162F, + 0.8786897054F, 0.8792241670F, 0.8797572013F, 0.8802888082F, + 0.8808189880F, 0.8813477407F, 0.8818750664F, 0.8824009653F, + 0.8829254375F, 0.8834484833F, 0.8839701028F, 0.8844902961F, + 0.8850090636F, 0.8855264054F, 0.8860423218F, 0.8865568131F, + 0.8870698794F, 0.8875815212F, 0.8880917386F, 0.8886005319F, + 0.8891079016F, 0.8896138479F, 0.8901183712F, 0.8906214719F, + 0.8911231503F, 0.8916234067F, 0.8921222417F, 0.8926196556F, + 0.8931156489F, 0.8936102219F, 0.8941033752F, 0.8945951092F, + 0.8950854244F, 0.8955743212F, 0.8960618003F, 0.8965478621F, + 0.8970325071F, 0.8975157359F, 0.8979975490F, 0.8984779471F, + 0.8989569307F, 0.8994345004F, 0.8999106568F, 0.9003854005F, + 0.9008587323F, 0.9013306526F, 0.9018011623F, 0.9022702619F, + 0.9027379521F, 0.9032042337F, 0.9036691074F, 0.9041325739F, + 0.9045946339F, 0.9050552882F, 0.9055145376F, 0.9059723828F, + 0.9064288246F, 0.9068838638F, 0.9073375013F, 0.9077897379F, + 0.9082405743F, 0.9086900115F, 0.9091380503F, 0.9095846917F, + 0.9100299364F, 0.9104737854F, 0.9109162397F, 0.9113573001F, + 0.9117969675F, 0.9122352430F, 0.9126721275F, 0.9131076219F, + 0.9135417273F, 0.9139744447F, 0.9144057750F, 0.9148357194F, + 0.9152642787F, 0.9156914542F, 0.9161172468F, 0.9165416576F, + 0.9169646877F, 0.9173863382F, 0.9178066102F, 0.9182255048F, + 0.9186430232F, 0.9190591665F, 0.9194739359F, 0.9198873324F, + 0.9202993574F, 0.9207100120F, 0.9211192973F, 0.9215272147F, + 0.9219337653F, 0.9223389504F, 0.9227427713F, 0.9231452290F, + 0.9235463251F, 0.9239460607F, 0.9243444371F, 0.9247414557F, + 0.9251371177F, 0.9255314245F, 0.9259243774F, 0.9263159778F, + 0.9267062270F, 0.9270951264F, 0.9274826774F, 0.9278688814F, + 0.9282537398F, 0.9286372540F, 0.9290194254F, 0.9294002555F, + 0.9297797458F, 0.9301578976F, 0.9305347125F, 0.9309101919F, + 0.9312843373F, 0.9316571503F, 0.9320286323F, 0.9323987849F, + 0.9327676097F, 0.9331351080F, 0.9335012816F, 0.9338661320F, + 0.9342296607F, 0.9345918694F, 0.9349527596F, 0.9353123330F, + 0.9356705911F, 0.9360275357F, 0.9363831683F, 0.9367374905F, + 0.9370905042F, 0.9374422108F, 0.9377926122F, 0.9381417099F, + 0.9384895057F, 0.9388360014F, 0.9391811985F, 0.9395250989F, + 0.9398677043F, 0.9402090165F, 0.9405490371F, 0.9408877680F, + 0.9412252110F, 0.9415613678F, 0.9418962402F, 0.9422298301F, + 0.9425621392F, 0.9428931695F, 0.9432229226F, 0.9435514005F, + 0.9438786050F, 0.9442045381F, 0.9445292014F, 0.9448525971F, + 0.9451747268F, 0.9454955926F, 0.9458151963F, 0.9461335399F, + 0.9464506253F, 0.9467664545F, 0.9470810293F, 0.9473943517F, + 0.9477064238F, 0.9480172474F, 0.9483268246F, 0.9486351573F, + 0.9489422475F, 0.9492480973F, 0.9495527087F, 0.9498560837F, + 0.9501582243F, 0.9504591325F, 0.9507588105F, 0.9510572603F, + 0.9513544839F, 0.9516504834F, 0.9519452609F, 0.9522388186F, + 0.9525311584F, 0.9528222826F, 0.9531121932F, 0.9534008923F, + 0.9536883821F, 0.9539746647F, 0.9542597424F, 0.9545436171F, + 0.9548262912F, 0.9551077667F, 0.9553880459F, 0.9556671309F, + 0.9559450239F, 0.9562217272F, 0.9564972429F, 0.9567715733F, + 0.9570447206F, 0.9573166871F, 0.9575874749F, 0.9578570863F, + 0.9581255236F, 0.9583927890F, 0.9586588849F, 0.9589238134F, + 0.9591875769F, 0.9594501777F, 0.9597116180F, 0.9599719003F, + 0.9602310267F, 0.9604889995F, 0.9607458213F, 0.9610014942F, + 0.9612560206F, 0.9615094028F, 0.9617616433F, 0.9620127443F, + 0.9622627083F, 0.9625115376F, 0.9627592345F, 0.9630058016F, + 0.9632512411F, 0.9634955555F, 0.9637387471F, 0.9639808185F, + 0.9642217720F, 0.9644616100F, 0.9647003349F, 0.9649379493F, + 0.9651744556F, 0.9654098561F, 0.9656441534F, 0.9658773499F, + 0.9661094480F, 0.9663404504F, 0.9665703593F, 0.9667991774F, + 0.9670269071F, 0.9672535509F, 0.9674791114F, 0.9677035909F, + 0.9679269921F, 0.9681493174F, 0.9683705694F, 0.9685907506F, + 0.9688098636F, 0.9690279108F, 0.9692448948F, 0.9694608182F, + 0.9696756836F, 0.9698894934F, 0.9701022503F, 0.9703139569F, + 0.9705246156F, 0.9707342291F, 0.9709428000F, 0.9711503309F, + 0.9713568243F, 0.9715622829F, 0.9717667093F, 0.9719701060F, + 0.9721724757F, 0.9723738210F, 0.9725741446F, 0.9727734490F, + 0.9729717369F, 0.9731690109F, 0.9733652737F, 0.9735605279F, + 0.9737547762F, 0.9739480212F, 0.9741402656F, 0.9743315120F, + 0.9745217631F, 0.9747110216F, 0.9748992901F, 0.9750865714F, + 0.9752728681F, 0.9754581829F, 0.9756425184F, 0.9758258775F, + 0.9760082627F, 0.9761896768F, 0.9763701224F, 0.9765496024F, + 0.9767281193F, 0.9769056760F, 0.9770822751F, 0.9772579193F, + 0.9774326114F, 0.9776063542F, 0.9777791502F, 0.9779510023F, + 0.9781219133F, 0.9782918858F, 0.9784609226F, 0.9786290264F, + 0.9787962000F, 0.9789624461F, 0.9791277676F, 0.9792921671F, + 0.9794556474F, 0.9796182113F, 0.9797798615F, 0.9799406009F, + 0.9801004321F, 0.9802593580F, 0.9804173813F, 0.9805745049F, + 0.9807307314F, 0.9808860637F, 0.9810405046F, 0.9811940568F, + 0.9813467232F, 0.9814985065F, 0.9816494095F, 0.9817994351F, + 0.9819485860F, 0.9820968650F, 0.9822442750F, 0.9823908186F, + 0.9825364988F, 0.9826813184F, 0.9828252801F, 0.9829683868F, + 0.9831106413F, 0.9832520463F, 0.9833926048F, 0.9835323195F, + 0.9836711932F, 0.9838092288F, 0.9839464291F, 0.9840827969F, + 0.9842183351F, 0.9843530464F, 0.9844869337F, 0.9846199998F, + 0.9847522475F, 0.9848836798F, 0.9850142993F, 0.9851441090F, + 0.9852731117F, 0.9854013101F, 0.9855287073F, 0.9856553058F, + 0.9857811087F, 0.9859061188F, 0.9860303388F, 0.9861537717F, + 0.9862764202F, 0.9863982872F, 0.9865193756F, 0.9866396882F, + 0.9867592277F, 0.9868779972F, 0.9869959993F, 0.9871132370F, + 0.9872297131F, 0.9873454304F, 0.9874603918F, 0.9875746001F, + 0.9876880581F, 0.9878007688F, 0.9879127348F, 0.9880239592F, + 0.9881344447F, 0.9882441941F, 0.9883532104F, 0.9884614962F, + 0.9885690546F, 0.9886758883F, 0.9887820001F, 0.9888873930F, + 0.9889920697F, 0.9890960331F, 0.9891992859F, 0.9893018312F, + 0.9894036716F, 0.9895048100F, 0.9896052493F, 0.9897049923F, + 0.9898040418F, 0.9899024006F, 0.9900000717F, 0.9900970577F, + 0.9901933616F, 0.9902889862F, 0.9903839343F, 0.9904782087F, + 0.9905718122F, 0.9906647477F, 0.9907570180F, 0.9908486259F, + 0.9909395742F, 0.9910298658F, 0.9911195034F, 0.9912084899F, + 0.9912968281F, 0.9913845208F, 0.9914715708F, 0.9915579810F, + 0.9916437540F, 0.9917288928F, 0.9918134001F, 0.9918972788F, + 0.9919805316F, 0.9920631613F, 0.9921451707F, 0.9922265626F, + 0.9923073399F, 0.9923875052F, 0.9924670615F, 0.9925460114F, + 0.9926243577F, 0.9927021033F, 0.9927792508F, 0.9928558032F, + 0.9929317631F, 0.9930071333F, 0.9930819167F, 0.9931561158F, + 0.9932297337F, 0.9933027728F, 0.9933752362F, 0.9934471264F, + 0.9935184462F, 0.9935891985F, 0.9936593859F, 0.9937290112F, + 0.9937980771F, 0.9938665864F, 0.9939345418F, 0.9940019460F, + 0.9940688018F, 0.9941351118F, 0.9942008789F, 0.9942661057F, + 0.9943307950F, 0.9943949494F, 0.9944585717F, 0.9945216645F, + 0.9945842307F, 0.9946462728F, 0.9947077936F, 0.9947687957F, + 0.9948292820F, 0.9948892550F, 0.9949487174F, 0.9950076719F, + 0.9950661212F, 0.9951240679F, 0.9951815148F, 0.9952384645F, + 0.9952949196F, 0.9953508828F, 0.9954063568F, 0.9954613442F, + 0.9955158476F, 0.9955698697F, 0.9956234132F, 0.9956764806F, + 0.9957290746F, 0.9957811978F, 0.9958328528F, 0.9958840423F, + 0.9959347688F, 0.9959850351F, 0.9960348435F, 0.9960841969F, + 0.9961330977F, 0.9961815486F, 0.9962295521F, 0.9962771108F, + 0.9963242274F, 0.9963709043F, 0.9964171441F, 0.9964629494F, + 0.9965083228F, 0.9965532668F, 0.9965977840F, 0.9966418768F, + 0.9966855479F, 0.9967287998F, 0.9967716350F, 0.9968140559F, + 0.9968560653F, 0.9968976655F, 0.9969388591F, 0.9969796485F, + 0.9970200363F, 0.9970600250F, 0.9970996170F, 0.9971388149F, + 0.9971776211F, 0.9972160380F, 0.9972540683F, 0.9972917142F, + 0.9973289783F, 0.9973658631F, 0.9974023709F, 0.9974385042F, + 0.9974742655F, 0.9975096571F, 0.9975446816F, 0.9975793413F, + 0.9976136386F, 0.9976475759F, 0.9976811557F, 0.9977143803F, + 0.9977472521F, 0.9977797736F, 0.9978119470F, 0.9978437748F, + 0.9978752593F, 0.9979064029F, 0.9979372079F, 0.9979676768F, + 0.9979978117F, 0.9980276151F, 0.9980570893F, 0.9980862367F, + 0.9981150595F, 0.9981435600F, 0.9981717406F, 0.9981996035F, + 0.9982271511F, 0.9982543856F, 0.9982813093F, 0.9983079246F, + 0.9983342336F, 0.9983602386F, 0.9983859418F, 0.9984113456F, + 0.9984364522F, 0.9984612638F, 0.9984857825F, 0.9985100108F, + 0.9985339507F, 0.9985576044F, 0.9985809743F, 0.9986040624F, + 0.9986268710F, 0.9986494022F, 0.9986716583F, 0.9986936413F, + 0.9987153535F, 0.9987367969F, 0.9987579738F, 0.9987788864F, + 0.9987995366F, 0.9988199267F, 0.9988400587F, 0.9988599348F, + 0.9988795572F, 0.9988989278F, 0.9989180487F, 0.9989369222F, + 0.9989555501F, 0.9989739347F, 0.9989920780F, 0.9990099820F, + 0.9990276487F, 0.9990450803F, 0.9990622787F, 0.9990792460F, + 0.9990959841F, 0.9991124952F, 0.9991287812F, 0.9991448440F, + 0.9991606858F, 0.9991763084F, 0.9991917139F, 0.9992069042F, + 0.9992218813F, 0.9992366471F, 0.9992512035F, 0.9992655525F, + 0.9992796961F, 0.9992936361F, 0.9993073744F, 0.9993209131F, + 0.9993342538F, 0.9993473987F, 0.9993603494F, 0.9993731080F, + 0.9993856762F, 0.9993980559F, 0.9994102490F, 0.9994222573F, + 0.9994340827F, 0.9994457269F, 0.9994571918F, 0.9994684793F, + 0.9994795910F, 0.9994905288F, 0.9995012945F, 0.9995118898F, + 0.9995223165F, 0.9995325765F, 0.9995426713F, 0.9995526029F, + 0.9995623728F, 0.9995719829F, 0.9995814349F, 0.9995907304F, + 0.9995998712F, 0.9996088590F, 0.9996176954F, 0.9996263821F, + 0.9996349208F, 0.9996433132F, 0.9996515609F, 0.9996596656F, + 0.9996676288F, 0.9996754522F, 0.9996831375F, 0.9996906862F, + 0.9996981000F, 0.9997053804F, 0.9997125290F, 0.9997195474F, + 0.9997264371F, 0.9997331998F, 0.9997398369F, 0.9997463500F, + 0.9997527406F, 0.9997590103F, 0.9997651606F, 0.9997711930F, + 0.9997771089F, 0.9997829098F, 0.9997885973F, 0.9997941728F, + 0.9997996378F, 0.9998049936F, 0.9998102419F, 0.9998153839F, + 0.9998204211F, 0.9998253550F, 0.9998301868F, 0.9998349182F, + 0.9998395503F, 0.9998440847F, 0.9998485226F, 0.9998528654F, + 0.9998571146F, 0.9998612713F, 0.9998653370F, 0.9998693130F, + 0.9998732007F, 0.9998770012F, 0.9998807159F, 0.9998843461F, + 0.9998878931F, 0.9998913581F, 0.9998947424F, 0.9998980473F, + 0.9999012740F, 0.9999044237F, 0.9999074976F, 0.9999104971F, + 0.9999134231F, 0.9999162771F, 0.9999190601F, 0.9999217733F, + 0.9999244179F, 0.9999269950F, 0.9999295058F, 0.9999319515F, + 0.9999343332F, 0.9999366519F, 0.9999389088F, 0.9999411050F, + 0.9999432416F, 0.9999453196F, 0.9999473402F, 0.9999493044F, + 0.9999512132F, 0.9999530677F, 0.9999548690F, 0.9999566180F, + 0.9999583157F, 0.9999599633F, 0.9999615616F, 0.9999631116F, + 0.9999646144F, 0.9999660709F, 0.9999674820F, 0.9999688487F, + 0.9999701719F, 0.9999714526F, 0.9999726917F, 0.9999738900F, + 0.9999750486F, 0.9999761682F, 0.9999772497F, 0.9999782941F, + 0.9999793021F, 0.9999802747F, 0.9999812126F, 0.9999821167F, + 0.9999829878F, 0.9999838268F, 0.9999846343F, 0.9999854113F, + 0.9999861584F, 0.9999868765F, 0.9999875664F, 0.9999882287F, + 0.9999888642F, 0.9999894736F, 0.9999900577F, 0.9999906172F, + 0.9999911528F, 0.9999916651F, 0.9999921548F, 0.9999926227F, + 0.9999930693F, 0.9999934954F, 0.9999939015F, 0.9999942883F, + 0.9999946564F, 0.9999950064F, 0.9999953390F, 0.9999956547F, + 0.9999959541F, 0.9999962377F, 0.9999965062F, 0.9999967601F, + 0.9999969998F, 0.9999972260F, 0.9999974392F, 0.9999976399F, + 0.9999978285F, 0.9999980056F, 0.9999981716F, 0.9999983271F, + 0.9999984724F, 0.9999986081F, 0.9999987345F, 0.9999988521F, + 0.9999989613F, 0.9999990625F, 0.9999991562F, 0.9999992426F, + 0.9999993223F, 0.9999993954F, 0.9999994625F, 0.9999995239F, + 0.9999995798F, 0.9999996307F, 0.9999996768F, 0.9999997184F, + 0.9999997559F, 0.9999997895F, 0.9999998195F, 0.9999998462F, + 0.9999998698F, 0.9999998906F, 0.9999999088F, 0.9999999246F, + 0.9999999383F, 0.9999999500F, 0.9999999600F, 0.9999999684F, + 0.9999999754F, 0.9999999811F, 0.9999999858F, 0.9999999896F, + 0.9999999925F, 0.9999999948F, 0.9999999965F, 0.9999999978F, + 0.9999999986F, 0.9999999992F, 0.9999999996F, 0.9999999998F, + 0.9999999999F, 1.0000000000F, 1.0000000000F, 1.0000000000F, +}; + +static float vwin8192[4096] = { + 0.0000000578F, 0.0000005198F, 0.0000014438F, 0.0000028299F, + 0.0000046780F, 0.0000069882F, 0.0000097604F, 0.0000129945F, + 0.0000166908F, 0.0000208490F, 0.0000254692F, 0.0000305515F, + 0.0000360958F, 0.0000421021F, 0.0000485704F, 0.0000555006F, + 0.0000628929F, 0.0000707472F, 0.0000790635F, 0.0000878417F, + 0.0000970820F, 0.0001067842F, 0.0001169483F, 0.0001275744F, + 0.0001386625F, 0.0001502126F, 0.0001622245F, 0.0001746984F, + 0.0001876343F, 0.0002010320F, 0.0002148917F, 0.0002292132F, + 0.0002439967F, 0.0002592421F, 0.0002749493F, 0.0002911184F, + 0.0003077493F, 0.0003248421F, 0.0003423967F, 0.0003604132F, + 0.0003788915F, 0.0003978316F, 0.0004172335F, 0.0004370971F, + 0.0004574226F, 0.0004782098F, 0.0004994587F, 0.0005211694F, + 0.0005433418F, 0.0005659759F, 0.0005890717F, 0.0006126292F, + 0.0006366484F, 0.0006611292F, 0.0006860716F, 0.0007114757F, + 0.0007373414F, 0.0007636687F, 0.0007904576F, 0.0008177080F, + 0.0008454200F, 0.0008735935F, 0.0009022285F, 0.0009313250F, + 0.0009608830F, 0.0009909025F, 0.0010213834F, 0.0010523257F, + 0.0010837295F, 0.0011155946F, 0.0011479211F, 0.0011807090F, + 0.0012139582F, 0.0012476687F, 0.0012818405F, 0.0013164736F, + 0.0013515679F, 0.0013871235F, 0.0014231402F, 0.0014596182F, + 0.0014965573F, 0.0015339576F, 0.0015718190F, 0.0016101415F, + 0.0016489251F, 0.0016881698F, 0.0017278754F, 0.0017680421F, + 0.0018086698F, 0.0018497584F, 0.0018913080F, 0.0019333185F, + 0.0019757898F, 0.0020187221F, 0.0020621151F, 0.0021059690F, + 0.0021502837F, 0.0021950591F, 0.0022402953F, 0.0022859921F, + 0.0023321497F, 0.0023787679F, 0.0024258467F, 0.0024733861F, + 0.0025213861F, 0.0025698466F, 0.0026187676F, 0.0026681491F, + 0.0027179911F, 0.0027682935F, 0.0028190562F, 0.0028702794F, + 0.0029219628F, 0.0029741066F, 0.0030267107F, 0.0030797749F, + 0.0031332994F, 0.0031872841F, 0.0032417289F, 0.0032966338F, + 0.0033519988F, 0.0034078238F, 0.0034641089F, 0.0035208539F, + 0.0035780589F, 0.0036357237F, 0.0036938485F, 0.0037524331F, + 0.0038114775F, 0.0038709817F, 0.0039309456F, 0.0039913692F, + 0.0040522524F, 0.0041135953F, 0.0041753978F, 0.0042376599F, + 0.0043003814F, 0.0043635624F, 0.0044272029F, 0.0044913028F, + 0.0045558620F, 0.0046208806F, 0.0046863585F, 0.0047522955F, + 0.0048186919F, 0.0048855473F, 0.0049528619F, 0.0050206356F, + 0.0050888684F, 0.0051575601F, 0.0052267108F, 0.0052963204F, + 0.0053663890F, 0.0054369163F, 0.0055079025F, 0.0055793474F, + 0.0056512510F, 0.0057236133F, 0.0057964342F, 0.0058697137F, + 0.0059434517F, 0.0060176482F, 0.0060923032F, 0.0061674166F, + 0.0062429883F, 0.0063190183F, 0.0063955066F, 0.0064724532F, + 0.0065498579F, 0.0066277207F, 0.0067060416F, 0.0067848205F, + 0.0068640575F, 0.0069437523F, 0.0070239051F, 0.0071045157F, + 0.0071855840F, 0.0072671102F, 0.0073490940F, 0.0074315355F, + 0.0075144345F, 0.0075977911F, 0.0076816052F, 0.0077658768F, + 0.0078506057F, 0.0079357920F, 0.0080214355F, 0.0081075363F, + 0.0081940943F, 0.0082811094F, 0.0083685816F, 0.0084565108F, + 0.0085448970F, 0.0086337401F, 0.0087230401F, 0.0088127969F, + 0.0089030104F, 0.0089936807F, 0.0090848076F, 0.0091763911F, + 0.0092684311F, 0.0093609276F, 0.0094538805F, 0.0095472898F, + 0.0096411554F, 0.0097354772F, 0.0098302552F, 0.0099254894F, + 0.0100211796F, 0.0101173259F, 0.0102139281F, 0.0103109863F, + 0.0104085002F, 0.0105064700F, 0.0106048955F, 0.0107037766F, + 0.0108031133F, 0.0109029056F, 0.0110031534F, 0.0111038565F, + 0.0112050151F, 0.0113066289F, 0.0114086980F, 0.0115112222F, + 0.0116142015F, 0.0117176359F, 0.0118215252F, 0.0119258695F, + 0.0120306686F, 0.0121359225F, 0.0122416312F, 0.0123477944F, + 0.0124544123F, 0.0125614847F, 0.0126690116F, 0.0127769928F, + 0.0128854284F, 0.0129943182F, 0.0131036623F, 0.0132134604F, + 0.0133237126F, 0.0134344188F, 0.0135455790F, 0.0136571929F, + 0.0137692607F, 0.0138817821F, 0.0139947572F, 0.0141081859F, + 0.0142220681F, 0.0143364037F, 0.0144511927F, 0.0145664350F, + 0.0146821304F, 0.0147982791F, 0.0149148808F, 0.0150319355F, + 0.0151494431F, 0.0152674036F, 0.0153858168F, 0.0155046828F, + 0.0156240014F, 0.0157437726F, 0.0158639962F, 0.0159846723F, + 0.0161058007F, 0.0162273814F, 0.0163494142F, 0.0164718991F, + 0.0165948361F, 0.0167182250F, 0.0168420658F, 0.0169663584F, + 0.0170911027F, 0.0172162987F, 0.0173419462F, 0.0174680452F, + 0.0175945956F, 0.0177215974F, 0.0178490504F, 0.0179769545F, + 0.0181053098F, 0.0182341160F, 0.0183633732F, 0.0184930812F, + 0.0186232399F, 0.0187538494F, 0.0188849094F, 0.0190164200F, + 0.0191483809F, 0.0192807923F, 0.0194136539F, 0.0195469656F, + 0.0196807275F, 0.0198149394F, 0.0199496012F, 0.0200847128F, + 0.0202202742F, 0.0203562853F, 0.0204927460F, 0.0206296561F, + 0.0207670157F, 0.0209048245F, 0.0210430826F, 0.0211817899F, + 0.0213209462F, 0.0214605515F, 0.0216006057F, 0.0217411086F, + 0.0218820603F, 0.0220234605F, 0.0221653093F, 0.0223076066F, + 0.0224503521F, 0.0225935459F, 0.0227371879F, 0.0228812779F, + 0.0230258160F, 0.0231708018F, 0.0233162355F, 0.0234621169F, + 0.0236084459F, 0.0237552224F, 0.0239024462F, 0.0240501175F, + 0.0241982359F, 0.0243468015F, 0.0244958141F, 0.0246452736F, + 0.0247951800F, 0.0249455331F, 0.0250963329F, 0.0252475792F, + 0.0253992720F, 0.0255514111F, 0.0257039965F, 0.0258570281F, + 0.0260105057F, 0.0261644293F, 0.0263187987F, 0.0264736139F, + 0.0266288747F, 0.0267845811F, 0.0269407330F, 0.0270973302F, + 0.0272543727F, 0.0274118604F, 0.0275697930F, 0.0277281707F, + 0.0278869932F, 0.0280462604F, 0.0282059723F, 0.0283661287F, + 0.0285267295F, 0.0286877747F, 0.0288492641F, 0.0290111976F, + 0.0291735751F, 0.0293363965F, 0.0294996617F, 0.0296633706F, + 0.0298275231F, 0.0299921190F, 0.0301571583F, 0.0303226409F, + 0.0304885667F, 0.0306549354F, 0.0308217472F, 0.0309890017F, + 0.0311566989F, 0.0313248388F, 0.0314934211F, 0.0316624459F, + 0.0318319128F, 0.0320018220F, 0.0321721732F, 0.0323429663F, + 0.0325142013F, 0.0326858779F, 0.0328579962F, 0.0330305559F, + 0.0332035570F, 0.0333769994F, 0.0335508829F, 0.0337252074F, + 0.0338999728F, 0.0340751790F, 0.0342508259F, 0.0344269134F, + 0.0346034412F, 0.0347804094F, 0.0349578178F, 0.0351356663F, + 0.0353139548F, 0.0354926831F, 0.0356718511F, 0.0358514588F, + 0.0360315059F, 0.0362119924F, 0.0363929182F, 0.0365742831F, + 0.0367560870F, 0.0369383297F, 0.0371210113F, 0.0373041315F, + 0.0374876902F, 0.0376716873F, 0.0378561226F, 0.0380409961F, + 0.0382263077F, 0.0384120571F, 0.0385982443F, 0.0387848691F, + 0.0389719315F, 0.0391594313F, 0.0393473683F, 0.0395357425F, + 0.0397245537F, 0.0399138017F, 0.0401034866F, 0.0402936080F, + 0.0404841660F, 0.0406751603F, 0.0408665909F, 0.0410584576F, + 0.0412507603F, 0.0414434988F, 0.0416366731F, 0.0418302829F, + 0.0420243282F, 0.0422188088F, 0.0424137246F, 0.0426090755F, + 0.0428048613F, 0.0430010819F, 0.0431977371F, 0.0433948269F, + 0.0435923511F, 0.0437903095F, 0.0439887020F, 0.0441875285F, + 0.0443867889F, 0.0445864830F, 0.0447866106F, 0.0449871717F, + 0.0451881661F, 0.0453895936F, 0.0455914542F, 0.0457937477F, + 0.0459964738F, 0.0461996326F, 0.0464032239F, 0.0466072475F, + 0.0468117032F, 0.0470165910F, 0.0472219107F, 0.0474276622F, + 0.0476338452F, 0.0478404597F, 0.0480475056F, 0.0482549827F, + 0.0484628907F, 0.0486712297F, 0.0488799994F, 0.0490891998F, + 0.0492988306F, 0.0495088917F, 0.0497193830F, 0.0499303043F, + 0.0501416554F, 0.0503534363F, 0.0505656468F, 0.0507782867F, + 0.0509913559F, 0.0512048542F, 0.0514187815F, 0.0516331376F, + 0.0518479225F, 0.0520631358F, 0.0522787775F, 0.0524948475F, + 0.0527113455F, 0.0529282715F, 0.0531456252F, 0.0533634066F, + 0.0535816154F, 0.0538002515F, 0.0540193148F, 0.0542388051F, + 0.0544587222F, 0.0546790660F, 0.0548998364F, 0.0551210331F, + 0.0553426561F, 0.0555647051F, 0.0557871801F, 0.0560100807F, + 0.0562334070F, 0.0564571587F, 0.0566813357F, 0.0569059378F, + 0.0571309649F, 0.0573564168F, 0.0575822933F, 0.0578085942F, + 0.0580353195F, 0.0582624689F, 0.0584900423F, 0.0587180396F, + 0.0589464605F, 0.0591753049F, 0.0594045726F, 0.0596342635F, + 0.0598643774F, 0.0600949141F, 0.0603258735F, 0.0605572555F, + 0.0607890597F, 0.0610212862F, 0.0612539346F, 0.0614870049F, + 0.0617204968F, 0.0619544103F, 0.0621887451F, 0.0624235010F, + 0.0626586780F, 0.0628942758F, 0.0631302942F, 0.0633667331F, + 0.0636035923F, 0.0638408717F, 0.0640785710F, 0.0643166901F, + 0.0645552288F, 0.0647941870F, 0.0650335645F, 0.0652733610F, + 0.0655135765F, 0.0657542108F, 0.0659952636F, 0.0662367348F, + 0.0664786242F, 0.0667209316F, 0.0669636570F, 0.0672068000F, + 0.0674503605F, 0.0676943384F, 0.0679387334F, 0.0681835454F, + 0.0684287742F, 0.0686744196F, 0.0689204814F, 0.0691669595F, + 0.0694138536F, 0.0696611637F, 0.0699088894F, 0.0701570307F, + 0.0704055873F, 0.0706545590F, 0.0709039458F, 0.0711537473F, + 0.0714039634F, 0.0716545939F, 0.0719056387F, 0.0721570975F, + 0.0724089702F, 0.0726612565F, 0.0729139563F, 0.0731670694F, + 0.0734205956F, 0.0736745347F, 0.0739288866F, 0.0741836510F, + 0.0744388277F, 0.0746944166F, 0.0749504175F, 0.0752068301F, + 0.0754636543F, 0.0757208899F, 0.0759785367F, 0.0762365946F, + 0.0764950632F, 0.0767539424F, 0.0770132320F, 0.0772729319F, + 0.0775330418F, 0.0777935616F, 0.0780544909F, 0.0783158298F, + 0.0785775778F, 0.0788397349F, 0.0791023009F, 0.0793652755F, + 0.0796286585F, 0.0798924498F, 0.0801566492F, 0.0804212564F, + 0.0806862712F, 0.0809516935F, 0.0812175231F, 0.0814837597F, + 0.0817504031F, 0.0820174532F, 0.0822849097F, 0.0825527724F, + 0.0828210412F, 0.0830897158F, 0.0833587960F, 0.0836282816F, + 0.0838981724F, 0.0841684682F, 0.0844391688F, 0.0847102740F, + 0.0849817835F, 0.0852536973F, 0.0855260150F, 0.0857987364F, + 0.0860718614F, 0.0863453897F, 0.0866193211F, 0.0868936554F, + 0.0871683924F, 0.0874435319F, 0.0877190737F, 0.0879950175F, + 0.0882713632F, 0.0885481105F, 0.0888252592F, 0.0891028091F, + 0.0893807600F, 0.0896591117F, 0.0899378639F, 0.0902170165F, + 0.0904965692F, 0.0907765218F, 0.0910568740F, 0.0913376258F, + 0.0916187767F, 0.0919003268F, 0.0921822756F, 0.0924646230F, + 0.0927473687F, 0.0930305126F, 0.0933140545F, 0.0935979940F, + 0.0938823310F, 0.0941670653F, 0.0944521966F, 0.0947377247F, + 0.0950236494F, 0.0953099704F, 0.0955966876F, 0.0958838007F, + 0.0961713094F, 0.0964592136F, 0.0967475131F, 0.0970362075F, + 0.0973252967F, 0.0976147805F, 0.0979046585F, 0.0981949307F, + 0.0984855967F, 0.0987766563F, 0.0990681093F, 0.0993599555F, + 0.0996521945F, 0.0999448263F, 0.1002378506F, 0.1005312671F, + 0.1008250755F, 0.1011192757F, 0.1014138675F, 0.1017088505F, + 0.1020042246F, 0.1022999895F, 0.1025961450F, 0.1028926909F, + 0.1031896268F, 0.1034869526F, 0.1037846680F, 0.1040827729F, + 0.1043812668F, 0.1046801497F, 0.1049794213F, 0.1052790813F, + 0.1055791294F, 0.1058795656F, 0.1061803894F, 0.1064816006F, + 0.1067831991F, 0.1070851846F, 0.1073875568F, 0.1076903155F, + 0.1079934604F, 0.1082969913F, 0.1086009079F, 0.1089052101F, + 0.1092098975F, 0.1095149699F, 0.1098204270F, 0.1101262687F, + 0.1104324946F, 0.1107391045F, 0.1110460982F, 0.1113534754F, + 0.1116612359F, 0.1119693793F, 0.1122779055F, 0.1125868142F, + 0.1128961052F, 0.1132057781F, 0.1135158328F, 0.1138262690F, + 0.1141370863F, 0.1144482847F, 0.1147598638F, 0.1150718233F, + 0.1153841631F, 0.1156968828F, 0.1160099822F, 0.1163234610F, + 0.1166373190F, 0.1169515559F, 0.1172661714F, 0.1175811654F, + 0.1178965374F, 0.1182122874F, 0.1185284149F, 0.1188449198F, + 0.1191618018F, 0.1194790606F, 0.1197966960F, 0.1201147076F, + 0.1204330953F, 0.1207518587F, 0.1210709976F, 0.1213905118F, + 0.1217104009F, 0.1220306647F, 0.1223513029F, 0.1226723153F, + 0.1229937016F, 0.1233154615F, 0.1236375948F, 0.1239601011F, + 0.1242829803F, 0.1246062319F, 0.1249298559F, 0.1252538518F, + 0.1255782195F, 0.1259029586F, 0.1262280689F, 0.1265535501F, + 0.1268794019F, 0.1272056241F, 0.1275322163F, 0.1278591784F, + 0.1281865099F, 0.1285142108F, 0.1288422805F, 0.1291707190F, + 0.1294995259F, 0.1298287009F, 0.1301582437F, 0.1304881542F, + 0.1308184319F, 0.1311490766F, 0.1314800881F, 0.1318114660F, + 0.1321432100F, 0.1324753200F, 0.1328077955F, 0.1331406364F, + 0.1334738422F, 0.1338074129F, 0.1341413479F, 0.1344756472F, + 0.1348103103F, 0.1351453370F, 0.1354807270F, 0.1358164801F, + 0.1361525959F, 0.1364890741F, 0.1368259145F, 0.1371631167F, + 0.1375006805F, 0.1378386056F, 0.1381768917F, 0.1385155384F, + 0.1388545456F, 0.1391939129F, 0.1395336400F, 0.1398737266F, + 0.1402141724F, 0.1405549772F, 0.1408961406F, 0.1412376623F, + 0.1415795421F, 0.1419217797F, 0.1422643746F, 0.1426073268F, + 0.1429506358F, 0.1432943013F, 0.1436383231F, 0.1439827008F, + 0.1443274342F, 0.1446725229F, 0.1450179667F, 0.1453637652F, + 0.1457099181F, 0.1460564252F, 0.1464032861F, 0.1467505006F, + 0.1470980682F, 0.1474459888F, 0.1477942620F, 0.1481428875F, + 0.1484918651F, 0.1488411942F, 0.1491908748F, 0.1495409065F, + 0.1498912889F, 0.1502420218F, 0.1505931048F, 0.1509445376F, + 0.1512963200F, 0.1516484516F, 0.1520009321F, 0.1523537612F, + 0.1527069385F, 0.1530604638F, 0.1534143368F, 0.1537685571F, + 0.1541231244F, 0.1544780384F, 0.1548332987F, 0.1551889052F, + 0.1555448574F, 0.1559011550F, 0.1562577978F, 0.1566147853F, + 0.1569721173F, 0.1573297935F, 0.1576878135F, 0.1580461771F, + 0.1584048838F, 0.1587639334F, 0.1591233255F, 0.1594830599F, + 0.1598431361F, 0.1602035540F, 0.1605643131F, 0.1609254131F, + 0.1612868537F, 0.1616486346F, 0.1620107555F, 0.1623732160F, + 0.1627360158F, 0.1630991545F, 0.1634626319F, 0.1638264476F, + 0.1641906013F, 0.1645550926F, 0.1649199212F, 0.1652850869F, + 0.1656505892F, 0.1660164278F, 0.1663826024F, 0.1667491127F, + 0.1671159583F, 0.1674831388F, 0.1678506541F, 0.1682185036F, + 0.1685866872F, 0.1689552044F, 0.1693240549F, 0.1696932384F, + 0.1700627545F, 0.1704326029F, 0.1708027833F, 0.1711732952F, + 0.1715441385F, 0.1719153127F, 0.1722868175F, 0.1726586526F, + 0.1730308176F, 0.1734033121F, 0.1737761359F, 0.1741492886F, + 0.1745227698F, 0.1748965792F, 0.1752707164F, 0.1756451812F, + 0.1760199731F, 0.1763950918F, 0.1767705370F, 0.1771463083F, + 0.1775224054F, 0.1778988279F, 0.1782755754F, 0.1786526477F, + 0.1790300444F, 0.1794077651F, 0.1797858094F, 0.1801641771F, + 0.1805428677F, 0.1809218810F, 0.1813012165F, 0.1816808739F, + 0.1820608528F, 0.1824411530F, 0.1828217739F, 0.1832027154F, + 0.1835839770F, 0.1839655584F, 0.1843474592F, 0.1847296790F, + 0.1851122175F, 0.1854950744F, 0.1858782492F, 0.1862617417F, + 0.1866455514F, 0.1870296780F, 0.1874141211F, 0.1877988804F, + 0.1881839555F, 0.1885693461F, 0.1889550517F, 0.1893410721F, + 0.1897274068F, 0.1901140555F, 0.1905010178F, 0.1908882933F, + 0.1912758818F, 0.1916637828F, 0.1920519959F, 0.1924405208F, + 0.1928293571F, 0.1932185044F, 0.1936079625F, 0.1939977308F, + 0.1943878091F, 0.1947781969F, 0.1951688939F, 0.1955598998F, + 0.1959512141F, 0.1963428364F, 0.1967347665F, 0.1971270038F, + 0.1975195482F, 0.1979123990F, 0.1983055561F, 0.1986990190F, + 0.1990927873F, 0.1994868607F, 0.1998812388F, 0.2002759212F, + 0.2006709075F, 0.2010661974F, 0.2014617904F, 0.2018576862F, + 0.2022538844F, 0.2026503847F, 0.2030471865F, 0.2034442897F, + 0.2038416937F, 0.2042393982F, 0.2046374028F, 0.2050357071F, + 0.2054343107F, 0.2058332133F, 0.2062324145F, 0.2066319138F, + 0.2070317110F, 0.2074318055F, 0.2078321970F, 0.2082328852F, + 0.2086338696F, 0.2090351498F, 0.2094367255F, 0.2098385962F, + 0.2102407617F, 0.2106432213F, 0.2110459749F, 0.2114490220F, + 0.2118523621F, 0.2122559950F, 0.2126599202F, 0.2130641373F, + 0.2134686459F, 0.2138734456F, 0.2142785361F, 0.2146839168F, + 0.2150895875F, 0.2154955478F, 0.2159017972F, 0.2163083353F, + 0.2167151617F, 0.2171222761F, 0.2175296780F, 0.2179373670F, + 0.2183453428F, 0.2187536049F, 0.2191621529F, 0.2195709864F, + 0.2199801051F, 0.2203895085F, 0.2207991961F, 0.2212091677F, + 0.2216194228F, 0.2220299610F, 0.2224407818F, 0.2228518850F, + 0.2232632699F, 0.2236749364F, 0.2240868839F, 0.2244991121F, + 0.2249116204F, 0.2253244086F, 0.2257374763F, 0.2261508229F, + 0.2265644481F, 0.2269783514F, 0.2273925326F, 0.2278069911F, + 0.2282217265F, 0.2286367384F, 0.2290520265F, 0.2294675902F, + 0.2298834292F, 0.2302995431F, 0.2307159314F, 0.2311325937F, + 0.2315495297F, 0.2319667388F, 0.2323842207F, 0.2328019749F, + 0.2332200011F, 0.2336382988F, 0.2340568675F, 0.2344757070F, + 0.2348948166F, 0.2353141961F, 0.2357338450F, 0.2361537629F, + 0.2365739493F, 0.2369944038F, 0.2374151261F, 0.2378361156F, + 0.2382573720F, 0.2386788948F, 0.2391006836F, 0.2395227380F, + 0.2399450575F, 0.2403676417F, 0.2407904902F, 0.2412136026F, + 0.2416369783F, 0.2420606171F, 0.2424845185F, 0.2429086820F, + 0.2433331072F, 0.2437577936F, 0.2441827409F, 0.2446079486F, + 0.2450334163F, 0.2454591435F, 0.2458851298F, 0.2463113747F, + 0.2467378779F, 0.2471646389F, 0.2475916573F, 0.2480189325F, + 0.2484464643F, 0.2488742521F, 0.2493022955F, 0.2497305940F, + 0.2501591473F, 0.2505879549F, 0.2510170163F, 0.2514463311F, + 0.2518758989F, 0.2523057193F, 0.2527357916F, 0.2531661157F, + 0.2535966909F, 0.2540275169F, 0.2544585931F, 0.2548899193F, + 0.2553214948F, 0.2557533193F, 0.2561853924F, 0.2566177135F, + 0.2570502822F, 0.2574830981F, 0.2579161608F, 0.2583494697F, + 0.2587830245F, 0.2592168246F, 0.2596508697F, 0.2600851593F, + 0.2605196929F, 0.2609544701F, 0.2613894904F, 0.2618247534F, + 0.2622602586F, 0.2626960055F, 0.2631319938F, 0.2635682230F, + 0.2640046925F, 0.2644414021F, 0.2648783511F, 0.2653155391F, + 0.2657529657F, 0.2661906305F, 0.2666285329F, 0.2670666725F, + 0.2675050489F, 0.2679436616F, 0.2683825101F, 0.2688215940F, + 0.2692609127F, 0.2697004660F, 0.2701402532F, 0.2705802739F, + 0.2710205278F, 0.2714610142F, 0.2719017327F, 0.2723426830F, + 0.2727838644F, 0.2732252766F, 0.2736669191F, 0.2741087914F, + 0.2745508930F, 0.2749932235F, 0.2754357824F, 0.2758785693F, + 0.2763215837F, 0.2767648251F, 0.2772082930F, 0.2776519870F, + 0.2780959066F, 0.2785400513F, 0.2789844207F, 0.2794290143F, + 0.2798738316F, 0.2803188722F, 0.2807641355F, 0.2812096211F, + 0.2816553286F, 0.2821012574F, 0.2825474071F, 0.2829937773F, + 0.2834403673F, 0.2838871768F, 0.2843342053F, 0.2847814523F, + 0.2852289174F, 0.2856765999F, 0.2861244996F, 0.2865726159F, + 0.2870209482F, 0.2874694962F, 0.2879182594F, 0.2883672372F, + 0.2888164293F, 0.2892658350F, 0.2897154540F, 0.2901652858F, + 0.2906153298F, 0.2910655856F, 0.2915160527F, 0.2919667306F, + 0.2924176189F, 0.2928687171F, 0.2933200246F, 0.2937715409F, + 0.2942232657F, 0.2946751984F, 0.2951273386F, 0.2955796856F, + 0.2960322391F, 0.2964849986F, 0.2969379636F, 0.2973911335F, + 0.2978445080F, 0.2982980864F, 0.2987518684F, 0.2992058534F, + 0.2996600409F, 0.3001144305F, 0.3005690217F, 0.3010238139F, + 0.3014788067F, 0.3019339995F, 0.3023893920F, 0.3028449835F, + 0.3033007736F, 0.3037567618F, 0.3042129477F, 0.3046693306F, + 0.3051259102F, 0.3055826859F, 0.3060396572F, 0.3064968236F, + 0.3069541847F, 0.3074117399F, 0.3078694887F, 0.3083274307F, + 0.3087855653F, 0.3092438920F, 0.3097024104F, 0.3101611199F, + 0.3106200200F, 0.3110791103F, 0.3115383902F, 0.3119978592F, + 0.3124575169F, 0.3129173627F, 0.3133773961F, 0.3138376166F, + 0.3142980238F, 0.3147586170F, 0.3152193959F, 0.3156803598F, + 0.3161415084F, 0.3166028410F, 0.3170643573F, 0.3175260566F, + 0.3179879384F, 0.3184500023F, 0.3189122478F, 0.3193746743F, + 0.3198372814F, 0.3203000685F, 0.3207630351F, 0.3212261807F, + 0.3216895048F, 0.3221530069F, 0.3226166865F, 0.3230805430F, + 0.3235445760F, 0.3240087849F, 0.3244731693F, 0.3249377285F, + 0.3254024622F, 0.3258673698F, 0.3263324507F, 0.3267977045F, + 0.3272631306F, 0.3277287286F, 0.3281944978F, 0.3286604379F, + 0.3291265482F, 0.3295928284F, 0.3300592777F, 0.3305258958F, + 0.3309926821F, 0.3314596361F, 0.3319267573F, 0.3323940451F, + 0.3328614990F, 0.3333291186F, 0.3337969033F, 0.3342648525F, + 0.3347329658F, 0.3352012427F, 0.3356696825F, 0.3361382849F, + 0.3366070492F, 0.3370759749F, 0.3375450616F, 0.3380143087F, + 0.3384837156F, 0.3389532819F, 0.3394230071F, 0.3398928905F, + 0.3403629317F, 0.3408331302F, 0.3413034854F, 0.3417739967F, + 0.3422446638F, 0.3427154860F, 0.3431864628F, 0.3436575938F, + 0.3441288782F, 0.3446003158F, 0.3450719058F, 0.3455436478F, + 0.3460155412F, 0.3464875856F, 0.3469597804F, 0.3474321250F, + 0.3479046189F, 0.3483772617F, 0.3488500527F, 0.3493229914F, + 0.3497960774F, 0.3502693100F, 0.3507426887F, 0.3512162131F, + 0.3516898825F, 0.3521636965F, 0.3526376545F, 0.3531117559F, + 0.3535860003F, 0.3540603870F, 0.3545349157F, 0.3550095856F, + 0.3554843964F, 0.3559593474F, 0.3564344381F, 0.3569096680F, + 0.3573850366F, 0.3578605432F, 0.3583361875F, 0.3588119687F, + 0.3592878865F, 0.3597639402F, 0.3602401293F, 0.3607164533F, + 0.3611929117F, 0.3616695038F, 0.3621462292F, 0.3626230873F, + 0.3631000776F, 0.3635771995F, 0.3640544525F, 0.3645318360F, + 0.3650093496F, 0.3654869926F, 0.3659647645F, 0.3664426648F, + 0.3669206930F, 0.3673988484F, 0.3678771306F, 0.3683555390F, + 0.3688340731F, 0.3693127322F, 0.3697915160F, 0.3702704237F, + 0.3707494549F, 0.3712286091F, 0.3717078857F, 0.3721872840F, + 0.3726668037F, 0.3731464441F, 0.3736262047F, 0.3741060850F, + 0.3745860843F, 0.3750662023F, 0.3755464382F, 0.3760267915F, + 0.3765072618F, 0.3769878484F, 0.3774685509F, 0.3779493686F, + 0.3784303010F, 0.3789113475F, 0.3793925076F, 0.3798737809F, + 0.3803551666F, 0.3808366642F, 0.3813182733F, 0.3817999932F, + 0.3822818234F, 0.3827637633F, 0.3832458124F, 0.3837279702F, + 0.3842102360F, 0.3846926093F, 0.3851750897F, 0.3856576764F, + 0.3861403690F, 0.3866231670F, 0.3871060696F, 0.3875890765F, + 0.3880721870F, 0.3885554007F, 0.3890387168F, 0.3895221349F, + 0.3900056544F, 0.3904892748F, 0.3909729955F, 0.3914568160F, + 0.3919407356F, 0.3924247539F, 0.3929088702F, 0.3933930841F, + 0.3938773949F, 0.3943618021F, 0.3948463052F, 0.3953309035F, + 0.3958155966F, 0.3963003838F, 0.3967852646F, 0.3972702385F, + 0.3977553048F, 0.3982404631F, 0.3987257127F, 0.3992110531F, + 0.3996964838F, 0.4001820041F, 0.4006676136F, 0.4011533116F, + 0.4016390976F, 0.4021249710F, 0.4026109313F, 0.4030969779F, + 0.4035831102F, 0.4040693277F, 0.4045556299F, 0.4050420160F, + 0.4055284857F, 0.4060150383F, 0.4065016732F, 0.4069883899F, + 0.4074751879F, 0.4079620665F, 0.4084490252F, 0.4089360635F, + 0.4094231807F, 0.4099103763F, 0.4103976498F, 0.4108850005F, + 0.4113724280F, 0.4118599315F, 0.4123475107F, 0.4128351648F, + 0.4133228934F, 0.4138106959F, 0.4142985716F, 0.4147865201F, + 0.4152745408F, 0.4157626330F, 0.4162507963F, 0.4167390301F, + 0.4172273337F, 0.4177157067F, 0.4182041484F, 0.4186926583F, + 0.4191812359F, 0.4196698805F, 0.4201585915F, 0.4206473685F, + 0.4211362108F, 0.4216251179F, 0.4221140892F, 0.4226031241F, + 0.4230922221F, 0.4235813826F, 0.4240706050F, 0.4245598887F, + 0.4250492332F, 0.4255386379F, 0.4260281022F, 0.4265176256F, + 0.4270072075F, 0.4274968473F, 0.4279865445F, 0.4284762984F, + 0.4289661086F, 0.4294559743F, 0.4299458951F, 0.4304358704F, + 0.4309258996F, 0.4314159822F, 0.4319061175F, 0.4323963050F, + 0.4328865441F, 0.4333768342F, 0.4338671749F, 0.4343575654F, + 0.4348480052F, 0.4353384938F, 0.4358290306F, 0.4363196149F, + 0.4368102463F, 0.4373009241F, 0.4377916478F, 0.4382824168F, + 0.4387732305F, 0.4392640884F, 0.4397549899F, 0.4402459343F, + 0.4407369212F, 0.4412279499F, 0.4417190198F, 0.4422101305F, + 0.4427012813F, 0.4431924717F, 0.4436837010F, 0.4441749686F, + 0.4446662742F, 0.4451576169F, 0.4456489963F, 0.4461404118F, + 0.4466318628F, 0.4471233487F, 0.4476148690F, 0.4481064230F, + 0.4485980103F, 0.4490896302F, 0.4495812821F, 0.4500729654F, + 0.4505646797F, 0.4510564243F, 0.4515481986F, 0.4520400021F, + 0.4525318341F, 0.4530236942F, 0.4535155816F, 0.4540074959F, + 0.4544994365F, 0.4549914028F, 0.4554833941F, 0.4559754100F, + 0.4564674499F, 0.4569595131F, 0.4574515991F, 0.4579437074F, + 0.4584358372F, 0.4589279881F, 0.4594201595F, 0.4599123508F, + 0.4604045615F, 0.4608967908F, 0.4613890383F, 0.4618813034F, + 0.4623735855F, 0.4628658841F, 0.4633581984F, 0.4638505281F, + 0.4643428724F, 0.4648352308F, 0.4653276028F, 0.4658199877F, + 0.4663123849F, 0.4668047940F, 0.4672972143F, 0.4677896451F, + 0.4682820861F, 0.4687745365F, 0.4692669958F, 0.4697594634F, + 0.4702519387F, 0.4707444211F, 0.4712369102F, 0.4717294052F, + 0.4722219056F, 0.4727144109F, 0.4732069204F, 0.4736994336F, + 0.4741919498F, 0.4746844686F, 0.4751769893F, 0.4756695113F, + 0.4761620341F, 0.4766545571F, 0.4771470797F, 0.4776396013F, + 0.4781321213F, 0.4786246392F, 0.4791171544F, 0.4796096663F, + 0.4801021744F, 0.4805946779F, 0.4810871765F, 0.4815796694F, + 0.4820721561F, 0.4825646360F, 0.4830571086F, 0.4835495732F, + 0.4840420293F, 0.4845344763F, 0.4850269136F, 0.4855193407F, + 0.4860117569F, 0.4865041617F, 0.4869965545F, 0.4874889347F, + 0.4879813018F, 0.4884736551F, 0.4889659941F, 0.4894583182F, + 0.4899506268F, 0.4904429193F, 0.4909351952F, 0.4914274538F, + 0.4919196947F, 0.4924119172F, 0.4929041207F, 0.4933963046F, + 0.4938884685F, 0.4943806116F, 0.4948727335F, 0.4953648335F, + 0.4958569110F, 0.4963489656F, 0.4968409965F, 0.4973330032F, + 0.4978249852F, 0.4983169419F, 0.4988088726F, 0.4993007768F, + 0.4997926539F, 0.5002845034F, 0.5007763247F, 0.5012681171F, + 0.5017598801F, 0.5022516132F, 0.5027433157F, 0.5032349871F, + 0.5037266268F, 0.5042182341F, 0.5047098086F, 0.5052013497F, + 0.5056928567F, 0.5061843292F, 0.5066757664F, 0.5071671679F, + 0.5076585330F, 0.5081498613F, 0.5086411520F, 0.5091324047F, + 0.5096236187F, 0.5101147934F, 0.5106059284F, 0.5110970230F, + 0.5115880766F, 0.5120790887F, 0.5125700587F, 0.5130609860F, + 0.5135518700F, 0.5140427102F, 0.5145335059F, 0.5150242566F, + 0.5155149618F, 0.5160056208F, 0.5164962331F, 0.5169867980F, + 0.5174773151F, 0.5179677837F, 0.5184582033F, 0.5189485733F, + 0.5194388931F, 0.5199291621F, 0.5204193798F, 0.5209095455F, + 0.5213996588F, 0.5218897190F, 0.5223797256F, 0.5228696779F, + 0.5233595755F, 0.5238494177F, 0.5243392039F, 0.5248289337F, + 0.5253186063F, 0.5258082213F, 0.5262977781F, 0.5267872760F, + 0.5272767146F, 0.5277660932F, 0.5282554112F, 0.5287446682F, + 0.5292338635F, 0.5297229965F, 0.5302120667F, 0.5307010736F, + 0.5311900164F, 0.5316788947F, 0.5321677079F, 0.5326564554F, + 0.5331451366F, 0.5336337511F, 0.5341222981F, 0.5346107771F, + 0.5350991876F, 0.5355875290F, 0.5360758007F, 0.5365640021F, + 0.5370521327F, 0.5375401920F, 0.5380281792F, 0.5385160939F, + 0.5390039355F, 0.5394917034F, 0.5399793971F, 0.5404670159F, + 0.5409545594F, 0.5414420269F, 0.5419294179F, 0.5424167318F, + 0.5429039680F, 0.5433911261F, 0.5438782053F, 0.5443652051F, + 0.5448521250F, 0.5453389644F, 0.5458257228F, 0.5463123995F, + 0.5467989940F, 0.5472855057F, 0.5477719341F, 0.5482582786F, + 0.5487445387F, 0.5492307137F, 0.5497168031F, 0.5502028063F, + 0.5506887228F, 0.5511745520F, 0.5516602934F, 0.5521459463F, + 0.5526315103F, 0.5531169847F, 0.5536023690F, 0.5540876626F, + 0.5545728649F, 0.5550579755F, 0.5555429937F, 0.5560279189F, + 0.5565127507F, 0.5569974884F, 0.5574821315F, 0.5579666794F, + 0.5584511316F, 0.5589354875F, 0.5594197465F, 0.5599039080F, + 0.5603879716F, 0.5608719367F, 0.5613558026F, 0.5618395689F, + 0.5623232350F, 0.5628068002F, 0.5632902642F, 0.5637736262F, + 0.5642568858F, 0.5647400423F, 0.5652230953F, 0.5657060442F, + 0.5661888883F, 0.5666716272F, 0.5671542603F, 0.5676367870F, + 0.5681192069F, 0.5686015192F, 0.5690837235F, 0.5695658192F, + 0.5700478058F, 0.5705296827F, 0.5710114494F, 0.5714931052F, + 0.5719746497F, 0.5724560822F, 0.5729374023F, 0.5734186094F, + 0.5738997029F, 0.5743806823F, 0.5748615470F, 0.5753422965F, + 0.5758229301F, 0.5763034475F, 0.5767838480F, 0.5772641310F, + 0.5777442960F, 0.5782243426F, 0.5787042700F, 0.5791840778F, + 0.5796637654F, 0.5801433322F, 0.5806227778F, 0.5811021016F, + 0.5815813029F, 0.5820603814F, 0.5825393363F, 0.5830181673F, + 0.5834968737F, 0.5839754549F, 0.5844539105F, 0.5849322399F, + 0.5854104425F, 0.5858885179F, 0.5863664653F, 0.5868442844F, + 0.5873219746F, 0.5877995353F, 0.5882769660F, 0.5887542661F, + 0.5892314351F, 0.5897084724F, 0.5901853776F, 0.5906621500F, + 0.5911387892F, 0.5916152945F, 0.5920916655F, 0.5925679016F, + 0.5930440022F, 0.5935199669F, 0.5939957950F, 0.5944714861F, + 0.5949470396F, 0.5954224550F, 0.5958977317F, 0.5963728692F, + 0.5968478669F, 0.5973227244F, 0.5977974411F, 0.5982720163F, + 0.5987464497F, 0.5992207407F, 0.5996948887F, 0.6001688932F, + 0.6006427537F, 0.6011164696F, 0.6015900405F, 0.6020634657F, + 0.6025367447F, 0.6030098770F, 0.6034828621F, 0.6039556995F, + 0.6044283885F, 0.6049009288F, 0.6053733196F, 0.6058455606F, + 0.6063176512F, 0.6067895909F, 0.6072613790F, 0.6077330152F, + 0.6082044989F, 0.6086758295F, 0.6091470065F, 0.6096180294F, + 0.6100888977F, 0.6105596108F, 0.6110301682F, 0.6115005694F, + 0.6119708139F, 0.6124409011F, 0.6129108305F, 0.6133806017F, + 0.6138502139F, 0.6143196669F, 0.6147889599F, 0.6152580926F, + 0.6157270643F, 0.6161958746F, 0.6166645230F, 0.6171330088F, + 0.6176013317F, 0.6180694910F, 0.6185374863F, 0.6190053171F, + 0.6194729827F, 0.6199404828F, 0.6204078167F, 0.6208749841F, + 0.6213419842F, 0.6218088168F, 0.6222754811F, 0.6227419768F, + 0.6232083032F, 0.6236744600F, 0.6241404465F, 0.6246062622F, + 0.6250719067F, 0.6255373795F, 0.6260026799F, 0.6264678076F, + 0.6269327619F, 0.6273975425F, 0.6278621487F, 0.6283265800F, + 0.6287908361F, 0.6292549163F, 0.6297188201F, 0.6301825471F, + 0.6306460966F, 0.6311094683F, 0.6315726617F, 0.6320356761F, + 0.6324985111F, 0.6329611662F, 0.6334236410F, 0.6338859348F, + 0.6343480472F, 0.6348099777F, 0.6352717257F, 0.6357332909F, + 0.6361946726F, 0.6366558704F, 0.6371168837F, 0.6375777122F, + 0.6380383552F, 0.6384988123F, 0.6389590830F, 0.6394191668F, + 0.6398790631F, 0.6403387716F, 0.6407982916F, 0.6412576228F, + 0.6417167645F, 0.6421757163F, 0.6426344778F, 0.6430930483F, + 0.6435514275F, 0.6440096149F, 0.6444676098F, 0.6449254119F, + 0.6453830207F, 0.6458404356F, 0.6462976562F, 0.6467546820F, + 0.6472115125F, 0.6476681472F, 0.6481245856F, 0.6485808273F, + 0.6490368717F, 0.6494927183F, 0.6499483667F, 0.6504038164F, + 0.6508590670F, 0.6513141178F, 0.6517689684F, 0.6522236185F, + 0.6526780673F, 0.6531323146F, 0.6535863598F, 0.6540402024F, + 0.6544938419F, 0.6549472779F, 0.6554005099F, 0.6558535373F, + 0.6563063598F, 0.6567589769F, 0.6572113880F, 0.6576635927F, + 0.6581155906F, 0.6585673810F, 0.6590189637F, 0.6594703380F, + 0.6599215035F, 0.6603724598F, 0.6608232064F, 0.6612737427F, + 0.6617240684F, 0.6621741829F, 0.6626240859F, 0.6630737767F, + 0.6635232550F, 0.6639725202F, 0.6644215720F, 0.6648704098F, + 0.6653190332F, 0.6657674417F, 0.6662156348F, 0.6666636121F, + 0.6671113731F, 0.6675589174F, 0.6680062445F, 0.6684533538F, + 0.6689002450F, 0.6693469177F, 0.6697933712F, 0.6702396052F, + 0.6706856193F, 0.6711314129F, 0.6715769855F, 0.6720223369F, + 0.6724674664F, 0.6729123736F, 0.6733570581F, 0.6738015194F, + 0.6742457570F, 0.6746897706F, 0.6751335596F, 0.6755771236F, + 0.6760204621F, 0.6764635747F, 0.6769064609F, 0.6773491204F, + 0.6777915525F, 0.6782337570F, 0.6786757332F, 0.6791174809F, + 0.6795589995F, 0.6800002886F, 0.6804413477F, 0.6808821765F, + 0.6813227743F, 0.6817631409F, 0.6822032758F, 0.6826431785F, + 0.6830828485F, 0.6835222855F, 0.6839614890F, 0.6844004585F, + 0.6848391936F, 0.6852776939F, 0.6857159589F, 0.6861539883F, + 0.6865917815F, 0.6870293381F, 0.6874666576F, 0.6879037398F, + 0.6883405840F, 0.6887771899F, 0.6892135571F, 0.6896496850F, + 0.6900855733F, 0.6905212216F, 0.6909566294F, 0.6913917963F, + 0.6918267218F, 0.6922614055F, 0.6926958471F, 0.6931300459F, + 0.6935640018F, 0.6939977141F, 0.6944311825F, 0.6948644066F, + 0.6952973859F, 0.6957301200F, 0.6961626085F, 0.6965948510F, + 0.6970268470F, 0.6974585961F, 0.6978900980F, 0.6983213521F, + 0.6987523580F, 0.6991831154F, 0.6996136238F, 0.7000438828F, + 0.7004738921F, 0.7009036510F, 0.7013331594F, 0.7017624166F, + 0.7021914224F, 0.7026201763F, 0.7030486779F, 0.7034769268F, + 0.7039049226F, 0.7043326648F, 0.7047601531F, 0.7051873870F, + 0.7056143662F, 0.7060410902F, 0.7064675586F, 0.7068937711F, + 0.7073197271F, 0.7077454264F, 0.7081708684F, 0.7085960529F, + 0.7090209793F, 0.7094456474F, 0.7098700566F, 0.7102942066F, + 0.7107180970F, 0.7111417274F, 0.7115650974F, 0.7119882066F, + 0.7124110545F, 0.7128336409F, 0.7132559653F, 0.7136780272F, + 0.7140998264F, 0.7145213624F, 0.7149426348F, 0.7153636433F, + 0.7157843874F, 0.7162048668F, 0.7166250810F, 0.7170450296F, + 0.7174647124F, 0.7178841289F, 0.7183032786F, 0.7187221613F, + 0.7191407765F, 0.7195591239F, 0.7199772030F, 0.7203950135F, + 0.7208125550F, 0.7212298271F, 0.7216468294F, 0.7220635616F, + 0.7224800233F, 0.7228962140F, 0.7233121335F, 0.7237277813F, + 0.7241431571F, 0.7245582604F, 0.7249730910F, 0.7253876484F, + 0.7258019322F, 0.7262159422F, 0.7266296778F, 0.7270431388F, + 0.7274563247F, 0.7278692353F, 0.7282818700F, 0.7286942287F, + 0.7291063108F, 0.7295181160F, 0.7299296440F, 0.7303408944F, + 0.7307518669F, 0.7311625609F, 0.7315729763F, 0.7319831126F, + 0.7323929695F, 0.7328025466F, 0.7332118435F, 0.7336208600F, + 0.7340295955F, 0.7344380499F, 0.7348462226F, 0.7352541134F, + 0.7356617220F, 0.7360690478F, 0.7364760907F, 0.7368828502F, + 0.7372893259F, 0.7376955176F, 0.7381014249F, 0.7385070475F, + 0.7389123849F, 0.7393174368F, 0.7397222029F, 0.7401266829F, + 0.7405308763F, 0.7409347829F, 0.7413384023F, 0.7417417341F, + 0.7421447780F, 0.7425475338F, 0.7429500009F, 0.7433521791F, + 0.7437540681F, 0.7441556674F, 0.7445569769F, 0.7449579960F, + 0.7453587245F, 0.7457591621F, 0.7461593084F, 0.7465591631F, + 0.7469587259F, 0.7473579963F, 0.7477569741F, 0.7481556590F, + 0.7485540506F, 0.7489521486F, 0.7493499526F, 0.7497474623F, + 0.7501446775F, 0.7505415977F, 0.7509382227F, 0.7513345521F, + 0.7517305856F, 0.7521263229F, 0.7525217636F, 0.7529169074F, + 0.7533117541F, 0.7537063032F, 0.7541005545F, 0.7544945076F, + 0.7548881623F, 0.7552815182F, 0.7556745749F, 0.7560673323F, + 0.7564597899F, 0.7568519474F, 0.7572438046F, 0.7576353611F, + 0.7580266166F, 0.7584175708F, 0.7588082235F, 0.7591985741F, + 0.7595886226F, 0.7599783685F, 0.7603678116F, 0.7607569515F, + 0.7611457879F, 0.7615343206F, 0.7619225493F, 0.7623104735F, + 0.7626980931F, 0.7630854078F, 0.7634724171F, 0.7638591209F, + 0.7642455188F, 0.7646316106F, 0.7650173959F, 0.7654028744F, + 0.7657880459F, 0.7661729100F, 0.7665574664F, 0.7669417150F, + 0.7673256553F, 0.7677092871F, 0.7680926100F, 0.7684756239F, + 0.7688583284F, 0.7692407232F, 0.7696228080F, 0.7700045826F, + 0.7703860467F, 0.7707671999F, 0.7711480420F, 0.7715285728F, + 0.7719087918F, 0.7722886989F, 0.7726682938F, 0.7730475762F, + 0.7734265458F, 0.7738052023F, 0.7741835454F, 0.7745615750F, + 0.7749392906F, 0.7753166921F, 0.7756937791F, 0.7760705514F, + 0.7764470087F, 0.7768231508F, 0.7771989773F, 0.7775744880F, + 0.7779496827F, 0.7783245610F, 0.7786991227F, 0.7790733676F, + 0.7794472953F, 0.7798209056F, 0.7801941982F, 0.7805671729F, + 0.7809398294F, 0.7813121675F, 0.7816841869F, 0.7820558873F, + 0.7824272684F, 0.7827983301F, 0.7831690720F, 0.7835394940F, + 0.7839095957F, 0.7842793768F, 0.7846488373F, 0.7850179767F, + 0.7853867948F, 0.7857552914F, 0.7861234663F, 0.7864913191F, + 0.7868588497F, 0.7872260578F, 0.7875929431F, 0.7879595055F, + 0.7883257445F, 0.7886916601F, 0.7890572520F, 0.7894225198F, + 0.7897874635F, 0.7901520827F, 0.7905163772F, 0.7908803468F, + 0.7912439912F, 0.7916073102F, 0.7919703035F, 0.7923329710F, + 0.7926953124F, 0.7930573274F, 0.7934190158F, 0.7937803774F, + 0.7941414120F, 0.7945021193F, 0.7948624991F, 0.7952225511F, + 0.7955822752F, 0.7959416711F, 0.7963007387F, 0.7966594775F, + 0.7970178875F, 0.7973759685F, 0.7977337201F, 0.7980911422F, + 0.7984482346F, 0.7988049970F, 0.7991614292F, 0.7995175310F, + 0.7998733022F, 0.8002287426F, 0.8005838519F, 0.8009386299F, + 0.8012930765F, 0.8016471914F, 0.8020009744F, 0.8023544253F, + 0.8027075438F, 0.8030603298F, 0.8034127831F, 0.8037649035F, + 0.8041166906F, 0.8044681445F, 0.8048192647F, 0.8051700512F, + 0.8055205038F, 0.8058706222F, 0.8062204062F, 0.8065698556F, + 0.8069189702F, 0.8072677499F, 0.8076161944F, 0.8079643036F, + 0.8083120772F, 0.8086595151F, 0.8090066170F, 0.8093533827F, + 0.8096998122F, 0.8100459051F, 0.8103916613F, 0.8107370806F, + 0.8110821628F, 0.8114269077F, 0.8117713151F, 0.8121153849F, + 0.8124591169F, 0.8128025108F, 0.8131455666F, 0.8134882839F, + 0.8138306627F, 0.8141727027F, 0.8145144038F, 0.8148557658F, + 0.8151967886F, 0.8155374718F, 0.8158778154F, 0.8162178192F, + 0.8165574830F, 0.8168968067F, 0.8172357900F, 0.8175744328F, + 0.8179127349F, 0.8182506962F, 0.8185883164F, 0.8189255955F, + 0.8192625332F, 0.8195991295F, 0.8199353840F, 0.8202712967F, + 0.8206068673F, 0.8209420958F, 0.8212769820F, 0.8216115256F, + 0.8219457266F, 0.8222795848F, 0.8226131000F, 0.8229462721F, + 0.8232791009F, 0.8236115863F, 0.8239437280F, 0.8242755260F, + 0.8246069801F, 0.8249380901F, 0.8252688559F, 0.8255992774F, + 0.8259293544F, 0.8262590867F, 0.8265884741F, 0.8269175167F, + 0.8272462141F, 0.8275745663F, 0.8279025732F, 0.8282302344F, + 0.8285575501F, 0.8288845199F, 0.8292111437F, 0.8295374215F, + 0.8298633530F, 0.8301889382F, 0.8305141768F, 0.8308390688F, + 0.8311636141F, 0.8314878124F, 0.8318116637F, 0.8321351678F, + 0.8324583246F, 0.8327811340F, 0.8331035957F, 0.8334257098F, + 0.8337474761F, 0.8340688944F, 0.8343899647F, 0.8347106867F, + 0.8350310605F, 0.8353510857F, 0.8356707624F, 0.8359900904F, + 0.8363090696F, 0.8366276999F, 0.8369459811F, 0.8372639131F, + 0.8375814958F, 0.8378987292F, 0.8382156130F, 0.8385321472F, + 0.8388483316F, 0.8391641662F, 0.8394796508F, 0.8397947853F, + 0.8401095697F, 0.8404240037F, 0.8407380873F, 0.8410518204F, + 0.8413652029F, 0.8416782347F, 0.8419909156F, 0.8423032456F, + 0.8426152245F, 0.8429268523F, 0.8432381289F, 0.8435490541F, + 0.8438596279F, 0.8441698502F, 0.8444797208F, 0.8447892396F, + 0.8450984067F, 0.8454072218F, 0.8457156849F, 0.8460237959F, + 0.8463315547F, 0.8466389612F, 0.8469460154F, 0.8472527170F, + 0.8475590661F, 0.8478650625F, 0.8481707063F, 0.8484759971F, + 0.8487809351F, 0.8490855201F, 0.8493897521F, 0.8496936308F, + 0.8499971564F, 0.8503003286F, 0.8506031474F, 0.8509056128F, + 0.8512077246F, 0.8515094828F, 0.8518108872F, 0.8521119379F, + 0.8524126348F, 0.8527129777F, 0.8530129666F, 0.8533126015F, + 0.8536118822F, 0.8539108087F, 0.8542093809F, 0.8545075988F, + 0.8548054623F, 0.8551029712F, 0.8554001257F, 0.8556969255F, + 0.8559933707F, 0.8562894611F, 0.8565851968F, 0.8568805775F, + 0.8571756034F, 0.8574702743F, 0.8577645902F, 0.8580585509F, + 0.8583521566F, 0.8586454070F, 0.8589383021F, 0.8592308420F, + 0.8595230265F, 0.8598148556F, 0.8601063292F, 0.8603974473F, + 0.8606882098F, 0.8609786167F, 0.8612686680F, 0.8615583636F, + 0.8618477034F, 0.8621366874F, 0.8624253156F, 0.8627135878F, + 0.8630015042F, 0.8632890646F, 0.8635762690F, 0.8638631173F, + 0.8641496096F, 0.8644357457F, 0.8647215257F, 0.8650069495F, + 0.8652920171F, 0.8655767283F, 0.8658610833F, 0.8661450820F, + 0.8664287243F, 0.8667120102F, 0.8669949397F, 0.8672775127F, + 0.8675597293F, 0.8678415894F, 0.8681230929F, 0.8684042398F, + 0.8686850302F, 0.8689654640F, 0.8692455412F, 0.8695252617F, + 0.8698046255F, 0.8700836327F, 0.8703622831F, 0.8706405768F, + 0.8709185138F, 0.8711960940F, 0.8714733174F, 0.8717501840F, + 0.8720266939F, 0.8723028469F, 0.8725786430F, 0.8728540824F, + 0.8731291648F, 0.8734038905F, 0.8736782592F, 0.8739522711F, + 0.8742259261F, 0.8744992242F, 0.8747721653F, 0.8750447496F, + 0.8753169770F, 0.8755888475F, 0.8758603611F, 0.8761315177F, + 0.8764023175F, 0.8766727603F, 0.8769428462F, 0.8772125752F, + 0.8774819474F, 0.8777509626F, 0.8780196209F, 0.8782879224F, + 0.8785558669F, 0.8788234546F, 0.8790906854F, 0.8793575594F, + 0.8796240765F, 0.8798902368F, 0.8801560403F, 0.8804214870F, + 0.8806865768F, 0.8809513099F, 0.8812156863F, 0.8814797059F, + 0.8817433687F, 0.8820066749F, 0.8822696243F, 0.8825322171F, + 0.8827944532F, 0.8830563327F, 0.8833178556F, 0.8835790219F, + 0.8838398316F, 0.8841002848F, 0.8843603815F, 0.8846201217F, + 0.8848795054F, 0.8851385327F, 0.8853972036F, 0.8856555182F, + 0.8859134764F, 0.8861710783F, 0.8864283239F, 0.8866852133F, + 0.8869417464F, 0.8871979234F, 0.8874537443F, 0.8877092090F, + 0.8879643177F, 0.8882190704F, 0.8884734671F, 0.8887275078F, + 0.8889811927F, 0.8892345216F, 0.8894874948F, 0.8897401122F, + 0.8899923738F, 0.8902442798F, 0.8904958301F, 0.8907470248F, + 0.8909978640F, 0.8912483477F, 0.8914984759F, 0.8917482487F, + 0.8919976662F, 0.8922467284F, 0.8924954353F, 0.8927437871F, + 0.8929917837F, 0.8932394252F, 0.8934867118F, 0.8937336433F, + 0.8939802199F, 0.8942264417F, 0.8944723087F, 0.8947178210F, + 0.8949629785F, 0.8952077815F, 0.8954522299F, 0.8956963239F, + 0.8959400634F, 0.8961834486F, 0.8964264795F, 0.8966691561F, + 0.8969114786F, 0.8971534470F, 0.8973950614F, 0.8976363219F, + 0.8978772284F, 0.8981177812F, 0.8983579802F, 0.8985978256F, + 0.8988373174F, 0.8990764556F, 0.8993152405F, 0.8995536720F, + 0.8997917502F, 0.9000294751F, 0.9002668470F, 0.9005038658F, + 0.9007405317F, 0.9009768446F, 0.9012128048F, 0.9014484123F, + 0.9016836671F, 0.9019185693F, 0.9021531191F, 0.9023873165F, + 0.9026211616F, 0.9028546546F, 0.9030877954F, 0.9033205841F, + 0.9035530210F, 0.9037851059F, 0.9040168392F, 0.9042482207F, + 0.9044792507F, 0.9047099293F, 0.9049402564F, 0.9051702323F, + 0.9053998569F, 0.9056291305F, 0.9058580531F, 0.9060866248F, + 0.9063148457F, 0.9065427159F, 0.9067702355F, 0.9069974046F, + 0.9072242233F, 0.9074506917F, 0.9076768100F, 0.9079025782F, + 0.9081279964F, 0.9083530647F, 0.9085777833F, 0.9088021523F, + 0.9090261717F, 0.9092498417F, 0.9094731623F, 0.9096961338F, + 0.9099187561F, 0.9101410295F, 0.9103629540F, 0.9105845297F, + 0.9108057568F, 0.9110266354F, 0.9112471656F, 0.9114673475F, + 0.9116871812F, 0.9119066668F, 0.9121258046F, 0.9123445945F, + 0.9125630367F, 0.9127811314F, 0.9129988786F, 0.9132162785F, + 0.9134333312F, 0.9136500368F, 0.9138663954F, 0.9140824073F, + 0.9142980724F, 0.9145133910F, 0.9147283632F, 0.9149429890F, + 0.9151572687F, 0.9153712023F, 0.9155847900F, 0.9157980319F, + 0.9160109282F, 0.9162234790F, 0.9164356844F, 0.9166475445F, + 0.9168590595F, 0.9170702296F, 0.9172810548F, 0.9174915354F, + 0.9177016714F, 0.9179114629F, 0.9181209102F, 0.9183300134F, + 0.9185387726F, 0.9187471879F, 0.9189552595F, 0.9191629876F, + 0.9193703723F, 0.9195774136F, 0.9197841119F, 0.9199904672F, + 0.9201964797F, 0.9204021495F, 0.9206074767F, 0.9208124616F, + 0.9210171043F, 0.9212214049F, 0.9214253636F, 0.9216289805F, + 0.9218322558F, 0.9220351896F, 0.9222377821F, 0.9224400335F, + 0.9226419439F, 0.9228435134F, 0.9230447423F, 0.9232456307F, + 0.9234461787F, 0.9236463865F, 0.9238462543F, 0.9240457822F, + 0.9242449704F, 0.9244438190F, 0.9246423282F, 0.9248404983F, + 0.9250383293F, 0.9252358214F, 0.9254329747F, 0.9256297896F, + 0.9258262660F, 0.9260224042F, 0.9262182044F, 0.9264136667F, + 0.9266087913F, 0.9268035783F, 0.9269980280F, 0.9271921405F, + 0.9273859160F, 0.9275793546F, 0.9277724566F, 0.9279652221F, + 0.9281576513F, 0.9283497443F, 0.9285415014F, 0.9287329227F, + 0.9289240084F, 0.9291147586F, 0.9293051737F, 0.9294952536F, + 0.9296849987F, 0.9298744091F, 0.9300634850F, 0.9302522266F, + 0.9304406340F, 0.9306287074F, 0.9308164471F, 0.9310038532F, + 0.9311909259F, 0.9313776654F, 0.9315640719F, 0.9317501455F, + 0.9319358865F, 0.9321212951F, 0.9323063713F, 0.9324911155F, + 0.9326755279F, 0.9328596085F, 0.9330433577F, 0.9332267756F, + 0.9334098623F, 0.9335926182F, 0.9337750434F, 0.9339571380F, + 0.9341389023F, 0.9343203366F, 0.9345014409F, 0.9346822155F, + 0.9348626606F, 0.9350427763F, 0.9352225630F, 0.9354020207F, + 0.9355811498F, 0.9357599503F, 0.9359384226F, 0.9361165667F, + 0.9362943830F, 0.9364718716F, 0.9366490327F, 0.9368258666F, + 0.9370023733F, 0.9371785533F, 0.9373544066F, 0.9375299335F, + 0.9377051341F, 0.9378800087F, 0.9380545576F, 0.9382287809F, + 0.9384026787F, 0.9385762515F, 0.9387494993F, 0.9389224223F, + 0.9390950209F, 0.9392672951F, 0.9394392453F, 0.9396108716F, + 0.9397821743F, 0.9399531536F, 0.9401238096F, 0.9402941427F, + 0.9404641530F, 0.9406338407F, 0.9408032061F, 0.9409722495F, + 0.9411409709F, 0.9413093707F, 0.9414774491F, 0.9416452062F, + 0.9418126424F, 0.9419797579F, 0.9421465528F, 0.9423130274F, + 0.9424791819F, 0.9426450166F, 0.9428105317F, 0.9429757274F, + 0.9431406039F, 0.9433051616F, 0.9434694005F, 0.9436333209F, + 0.9437969232F, 0.9439602074F, 0.9441231739F, 0.9442858229F, + 0.9444481545F, 0.9446101691F, 0.9447718669F, 0.9449332481F, + 0.9450943129F, 0.9452550617F, 0.9454154945F, 0.9455756118F, + 0.9457354136F, 0.9458949003F, 0.9460540721F, 0.9462129292F, + 0.9463714719F, 0.9465297003F, 0.9466876149F, 0.9468452157F, + 0.9470025031F, 0.9471594772F, 0.9473161384F, 0.9474724869F, + 0.9476285229F, 0.9477842466F, 0.9479396584F, 0.9480947585F, + 0.9482495470F, 0.9484040243F, 0.9485581906F, 0.9487120462F, + 0.9488655913F, 0.9490188262F, 0.9491717511F, 0.9493243662F, + 0.9494766718F, 0.9496286683F, 0.9497803557F, 0.9499317345F, + 0.9500828047F, 0.9502335668F, 0.9503840209F, 0.9505341673F, + 0.9506840062F, 0.9508335380F, 0.9509827629F, 0.9511316810F, + 0.9512802928F, 0.9514285984F, 0.9515765982F, 0.9517242923F, + 0.9518716810F, 0.9520187646F, 0.9521655434F, 0.9523120176F, + 0.9524581875F, 0.9526040534F, 0.9527496154F, 0.9528948739F, + 0.9530398292F, 0.9531844814F, 0.9533288310F, 0.9534728780F, + 0.9536166229F, 0.9537600659F, 0.9539032071F, 0.9540460470F, + 0.9541885858F, 0.9543308237F, 0.9544727611F, 0.9546143981F, + 0.9547557351F, 0.9548967723F, 0.9550375100F, 0.9551779485F, + 0.9553180881F, 0.9554579290F, 0.9555974714F, 0.9557367158F, + 0.9558756623F, 0.9560143112F, 0.9561526628F, 0.9562907174F, + 0.9564284752F, 0.9565659366F, 0.9567031017F, 0.9568399710F, + 0.9569765446F, 0.9571128229F, 0.9572488061F, 0.9573844944F, + 0.9575198883F, 0.9576549879F, 0.9577897936F, 0.9579243056F, + 0.9580585242F, 0.9581924497F, 0.9583260824F, 0.9584594226F, + 0.9585924705F, 0.9587252264F, 0.9588576906F, 0.9589898634F, + 0.9591217452F, 0.9592533360F, 0.9593846364F, 0.9595156465F, + 0.9596463666F, 0.9597767971F, 0.9599069382F, 0.9600367901F, + 0.9601663533F, 0.9602956279F, 0.9604246143F, 0.9605533128F, + 0.9606817236F, 0.9608098471F, 0.9609376835F, 0.9610652332F, + 0.9611924963F, 0.9613194733F, 0.9614461644F, 0.9615725699F, + 0.9616986901F, 0.9618245253F, 0.9619500757F, 0.9620753418F, + 0.9622003238F, 0.9623250219F, 0.9624494365F, 0.9625735679F, + 0.9626974163F, 0.9628209821F, 0.9629442656F, 0.9630672671F, + 0.9631899868F, 0.9633124251F, 0.9634345822F, 0.9635564585F, + 0.9636780543F, 0.9637993699F, 0.9639204056F, 0.9640411616F, + 0.9641616383F, 0.9642818359F, 0.9644017549F, 0.9645213955F, + 0.9646407579F, 0.9647598426F, 0.9648786497F, 0.9649971797F, + 0.9651154328F, 0.9652334092F, 0.9653511095F, 0.9654685337F, + 0.9655856823F, 0.9657025556F, 0.9658191538F, 0.9659354773F, + 0.9660515263F, 0.9661673013F, 0.9662828024F, 0.9663980300F, + 0.9665129845F, 0.9666276660F, 0.9667420750F, 0.9668562118F, + 0.9669700766F, 0.9670836698F, 0.9671969917F, 0.9673100425F, + 0.9674228227F, 0.9675353325F, 0.9676475722F, 0.9677595422F, + 0.9678712428F, 0.9679826742F, 0.9680938368F, 0.9682047309F, + 0.9683153569F, 0.9684257150F, 0.9685358056F, 0.9686456289F, + 0.9687551853F, 0.9688644752F, 0.9689734987F, 0.9690822564F, + 0.9691907483F, 0.9692989750F, 0.9694069367F, 0.9695146337F, + 0.9696220663F, 0.9697292349F, 0.9698361398F, 0.9699427813F, + 0.9700491597F, 0.9701552754F, 0.9702611286F, 0.9703667197F, + 0.9704720490F, 0.9705771169F, 0.9706819236F, 0.9707864695F, + 0.9708907549F, 0.9709947802F, 0.9710985456F, 0.9712020514F, + 0.9713052981F, 0.9714082859F, 0.9715110151F, 0.9716134862F, + 0.9717156993F, 0.9718176549F, 0.9719193532F, 0.9720207946F, + 0.9721219794F, 0.9722229080F, 0.9723235806F, 0.9724239976F, + 0.9725241593F, 0.9726240661F, 0.9727237183F, 0.9728231161F, + 0.9729222601F, 0.9730211503F, 0.9731197873F, 0.9732181713F, + 0.9733163027F, 0.9734141817F, 0.9735118088F, 0.9736091842F, + 0.9737063083F, 0.9738031814F, 0.9738998039F, 0.9739961760F, + 0.9740922981F, 0.9741881706F, 0.9742837938F, 0.9743791680F, + 0.9744742935F, 0.9745691707F, 0.9746637999F, 0.9747581814F, + 0.9748523157F, 0.9749462029F, 0.9750398435F, 0.9751332378F, + 0.9752263861F, 0.9753192887F, 0.9754119461F, 0.9755043585F, + 0.9755965262F, 0.9756884496F, 0.9757801291F, 0.9758715650F, + 0.9759627575F, 0.9760537071F, 0.9761444141F, 0.9762348789F, + 0.9763251016F, 0.9764150828F, 0.9765048228F, 0.9765943218F, + 0.9766835802F, 0.9767725984F, 0.9768613767F, 0.9769499154F, + 0.9770382149F, 0.9771262755F, 0.9772140976F, 0.9773016815F, + 0.9773890275F, 0.9774761360F, 0.9775630073F, 0.9776496418F, + 0.9777360398F, 0.9778222016F, 0.9779081277F, 0.9779938182F, + 0.9780792736F, 0.9781644943F, 0.9782494805F, 0.9783342326F, + 0.9784187509F, 0.9785030359F, 0.9785870877F, 0.9786709069F, + 0.9787544936F, 0.9788378484F, 0.9789209714F, 0.9790038631F, + 0.9790865238F, 0.9791689538F, 0.9792511535F, 0.9793331232F, + 0.9794148633F, 0.9794963742F, 0.9795776561F, 0.9796587094F, + 0.9797395345F, 0.9798201316F, 0.9799005013F, 0.9799806437F, + 0.9800605593F, 0.9801402483F, 0.9802197112F, 0.9802989483F, + 0.9803779600F, 0.9804567465F, 0.9805353082F, 0.9806136455F, + 0.9806917587F, 0.9807696482F, 0.9808473143F, 0.9809247574F, + 0.9810019778F, 0.9810789759F, 0.9811557519F, 0.9812323064F, + 0.9813086395F, 0.9813847517F, 0.9814606433F, 0.9815363147F, + 0.9816117662F, 0.9816869981F, 0.9817620108F, 0.9818368047F, + 0.9819113801F, 0.9819857374F, 0.9820598769F, 0.9821337989F, + 0.9822075038F, 0.9822809920F, 0.9823542638F, 0.9824273195F, + 0.9825001596F, 0.9825727843F, 0.9826451940F, 0.9827173891F, + 0.9827893700F, 0.9828611368F, 0.9829326901F, 0.9830040302F, + 0.9830751574F, 0.9831460720F, 0.9832167745F, 0.9832872652F, + 0.9833575444F, 0.9834276124F, 0.9834974697F, 0.9835671166F, + 0.9836365535F, 0.9837057806F, 0.9837747983F, 0.9838436071F, + 0.9839122072F, 0.9839805990F, 0.9840487829F, 0.9841167591F, + 0.9841845282F, 0.9842520903F, 0.9843194459F, 0.9843865953F, + 0.9844535389F, 0.9845202771F, 0.9845868101F, 0.9846531383F, + 0.9847192622F, 0.9847851820F, 0.9848508980F, 0.9849164108F, + 0.9849817205F, 0.9850468276F, 0.9851117324F, 0.9851764352F, + 0.9852409365F, 0.9853052366F, 0.9853693358F, 0.9854332344F, + 0.9854969330F, 0.9855604317F, 0.9856237309F, 0.9856868310F, + 0.9857497325F, 0.9858124355F, 0.9858749404F, 0.9859372477F, + 0.9859993577F, 0.9860612707F, 0.9861229871F, 0.9861845072F, + 0.9862458315F, 0.9863069601F, 0.9863678936F, 0.9864286322F, + 0.9864891764F, 0.9865495264F, 0.9866096826F, 0.9866696454F, + 0.9867294152F, 0.9867889922F, 0.9868483769F, 0.9869075695F, + 0.9869665706F, 0.9870253803F, 0.9870839991F, 0.9871424273F, + 0.9872006653F, 0.9872587135F, 0.9873165721F, 0.9873742415F, + 0.9874317222F, 0.9874890144F, 0.9875461185F, 0.9876030348F, + 0.9876597638F, 0.9877163057F, 0.9877726610F, 0.9878288300F, + 0.9878848130F, 0.9879406104F, 0.9879962225F, 0.9880516497F, + 0.9881068924F, 0.9881619509F, 0.9882168256F, 0.9882715168F, + 0.9883260249F, 0.9883803502F, 0.9884344931F, 0.9884884539F, + 0.9885422331F, 0.9885958309F, 0.9886492477F, 0.9887024838F, + 0.9887555397F, 0.9888084157F, 0.9888611120F, 0.9889136292F, + 0.9889659675F, 0.9890181273F, 0.9890701089F, 0.9891219128F, + 0.9891735392F, 0.9892249885F, 0.9892762610F, 0.9893273572F, + 0.9893782774F, 0.9894290219F, 0.9894795911F, 0.9895299853F, + 0.9895802049F, 0.9896302502F, 0.9896801217F, 0.9897298196F, + 0.9897793443F, 0.9898286961F, 0.9898778755F, 0.9899268828F, + 0.9899757183F, 0.9900243823F, 0.9900728753F, 0.9901211976F, + 0.9901693495F, 0.9902173314F, 0.9902651436F, 0.9903127865F, + 0.9903602605F, 0.9904075659F, 0.9904547031F, 0.9905016723F, + 0.9905484740F, 0.9905951086F, 0.9906415763F, 0.9906878775F, + 0.9907340126F, 0.9907799819F, 0.9908257858F, 0.9908714247F, + 0.9909168988F, 0.9909622086F, 0.9910073543F, 0.9910523364F, + 0.9910971552F, 0.9911418110F, 0.9911863042F, 0.9912306351F, + 0.9912748042F, 0.9913188117F, 0.9913626580F, 0.9914063435F, + 0.9914498684F, 0.9914932333F, 0.9915364383F, 0.9915794839F, + 0.9916223703F, 0.9916650981F, 0.9917076674F, 0.9917500787F, + 0.9917923323F, 0.9918344286F, 0.9918763679F, 0.9919181505F, + 0.9919597769F, 0.9920012473F, 0.9920425621F, 0.9920837217F, + 0.9921247263F, 0.9921655765F, 0.9922062724F, 0.9922468145F, + 0.9922872030F, 0.9923274385F, 0.9923675211F, 0.9924074513F, + 0.9924472294F, 0.9924868557F, 0.9925263306F, 0.9925656544F, + 0.9926048275F, 0.9926438503F, 0.9926827230F, 0.9927214461F, + 0.9927600199F, 0.9927984446F, 0.9928367208F, 0.9928748486F, + 0.9929128285F, 0.9929506608F, 0.9929883459F, 0.9930258841F, + 0.9930632757F, 0.9931005211F, 0.9931376207F, 0.9931745747F, + 0.9932113836F, 0.9932480476F, 0.9932845671F, 0.9933209425F, + 0.9933571742F, 0.9933932623F, 0.9934292074F, 0.9934650097F, + 0.9935006696F, 0.9935361874F, 0.9935715635F, 0.9936067982F, + 0.9936418919F, 0.9936768448F, 0.9937116574F, 0.9937463300F, + 0.9937808629F, 0.9938152565F, 0.9938495111F, 0.9938836271F, + 0.9939176047F, 0.9939514444F, 0.9939851465F, 0.9940187112F, + 0.9940521391F, 0.9940854303F, 0.9941185853F, 0.9941516044F, + 0.9941844879F, 0.9942172361F, 0.9942498495F, 0.9942823283F, + 0.9943146729F, 0.9943468836F, 0.9943789608F, 0.9944109047F, + 0.9944427158F, 0.9944743944F, 0.9945059408F, 0.9945373553F, + 0.9945686384F, 0.9945997902F, 0.9946308112F, 0.9946617017F, + 0.9946924621F, 0.9947230926F, 0.9947535937F, 0.9947839656F, + 0.9948142086F, 0.9948443232F, 0.9948743097F, 0.9949041683F, + 0.9949338995F, 0.9949635035F, 0.9949929807F, 0.9950223315F, + 0.9950515561F, 0.9950806549F, 0.9951096282F, 0.9951384764F, + 0.9951671998F, 0.9951957987F, 0.9952242735F, 0.9952526245F, + 0.9952808520F, 0.9953089564F, 0.9953369380F, 0.9953647971F, + 0.9953925340F, 0.9954201491F, 0.9954476428F, 0.9954750153F, + 0.9955022670F, 0.9955293981F, 0.9955564092F, 0.9955833003F, + 0.9956100720F, 0.9956367245F, 0.9956632582F, 0.9956896733F, + 0.9957159703F, 0.9957421494F, 0.9957682110F, 0.9957941553F, + 0.9958199828F, 0.9958456937F, 0.9958712884F, 0.9958967672F, + 0.9959221305F, 0.9959473784F, 0.9959725115F, 0.9959975300F, + 0.9960224342F, 0.9960472244F, 0.9960719011F, 0.9960964644F, + 0.9961209148F, 0.9961452525F, 0.9961694779F, 0.9961935913F, + 0.9962175930F, 0.9962414834F, 0.9962652627F, 0.9962889313F, + 0.9963124895F, 0.9963359377F, 0.9963592761F, 0.9963825051F, + 0.9964056250F, 0.9964286361F, 0.9964515387F, 0.9964743332F, + 0.9964970198F, 0.9965195990F, 0.9965420709F, 0.9965644360F, + 0.9965866946F, 0.9966088469F, 0.9966308932F, 0.9966528340F, + 0.9966746695F, 0.9966964001F, 0.9967180260F, 0.9967395475F, + 0.9967609651F, 0.9967822789F, 0.9968034894F, 0.9968245968F, + 0.9968456014F, 0.9968665036F, 0.9968873037F, 0.9969080019F, + 0.9969285987F, 0.9969490942F, 0.9969694889F, 0.9969897830F, + 0.9970099769F, 0.9970300708F, 0.9970500651F, 0.9970699601F, + 0.9970897561F, 0.9971094533F, 0.9971290522F, 0.9971485531F, + 0.9971679561F, 0.9971872617F, 0.9972064702F, 0.9972255818F, + 0.9972445968F, 0.9972635157F, 0.9972823386F, 0.9973010659F, + 0.9973196980F, 0.9973382350F, 0.9973566773F, 0.9973750253F, + 0.9973932791F, 0.9974114392F, 0.9974295059F, 0.9974474793F, + 0.9974653599F, 0.9974831480F, 0.9975008438F, 0.9975184476F, + 0.9975359598F, 0.9975533806F, 0.9975707104F, 0.9975879495F, + 0.9976050981F, 0.9976221566F, 0.9976391252F, 0.9976560043F, + 0.9976727941F, 0.9976894950F, 0.9977061073F, 0.9977226312F, + 0.9977390671F, 0.9977554152F, 0.9977716759F, 0.9977878495F, + 0.9978039361F, 0.9978199363F, 0.9978358501F, 0.9978516780F, + 0.9978674202F, 0.9978830771F, 0.9978986488F, 0.9979141358F, + 0.9979295383F, 0.9979448566F, 0.9979600909F, 0.9979752417F, + 0.9979903091F, 0.9980052936F, 0.9980201952F, 0.9980350145F, + 0.9980497515F, 0.9980644067F, 0.9980789804F, 0.9980934727F, + 0.9981078841F, 0.9981222147F, 0.9981364649F, 0.9981506350F, + 0.9981647253F, 0.9981787360F, 0.9981926674F, 0.9982065199F, + 0.9982202936F, 0.9982339890F, 0.9982476062F, 0.9982611456F, + 0.9982746074F, 0.9982879920F, 0.9983012996F, 0.9983145304F, + 0.9983276849F, 0.9983407632F, 0.9983537657F, 0.9983666926F, + 0.9983795442F, 0.9983923208F, 0.9984050226F, 0.9984176501F, + 0.9984302033F, 0.9984426827F, 0.9984550884F, 0.9984674208F, + 0.9984796802F, 0.9984918667F, 0.9985039808F, 0.9985160227F, + 0.9985279926F, 0.9985398909F, 0.9985517177F, 0.9985634734F, + 0.9985751583F, 0.9985867727F, 0.9985983167F, 0.9986097907F, + 0.9986211949F, 0.9986325297F, 0.9986437953F, 0.9986549919F, + 0.9986661199F, 0.9986771795F, 0.9986881710F, 0.9986990946F, + 0.9987099507F, 0.9987207394F, 0.9987314611F, 0.9987421161F, + 0.9987527045F, 0.9987632267F, 0.9987736829F, 0.9987840734F, + 0.9987943985F, 0.9988046584F, 0.9988148534F, 0.9988249838F, + 0.9988350498F, 0.9988450516F, 0.9988549897F, 0.9988648641F, + 0.9988746753F, 0.9988844233F, 0.9988941086F, 0.9989037313F, + 0.9989132918F, 0.9989227902F, 0.9989322269F, 0.9989416021F, + 0.9989509160F, 0.9989601690F, 0.9989693613F, 0.9989784931F, + 0.9989875647F, 0.9989965763F, 0.9990055283F, 0.9990144208F, + 0.9990232541F, 0.9990320286F, 0.9990407443F, 0.9990494016F, + 0.9990580008F, 0.9990665421F, 0.9990750257F, 0.9990834519F, + 0.9990918209F, 0.9991001331F, 0.9991083886F, 0.9991165877F, + 0.9991247307F, 0.9991328177F, 0.9991408491F, 0.9991488251F, + 0.9991567460F, 0.9991646119F, 0.9991724232F, 0.9991801801F, + 0.9991878828F, 0.9991955316F, 0.9992031267F, 0.9992106684F, + 0.9992181569F, 0.9992255925F, 0.9992329753F, 0.9992403057F, + 0.9992475839F, 0.9992548101F, 0.9992619846F, 0.9992691076F, + 0.9992761793F, 0.9992832001F, 0.9992901701F, 0.9992970895F, + 0.9993039587F, 0.9993107777F, 0.9993175470F, 0.9993242667F, + 0.9993309371F, 0.9993375583F, 0.9993441307F, 0.9993506545F, + 0.9993571298F, 0.9993635570F, 0.9993699362F, 0.9993762678F, + 0.9993825519F, 0.9993887887F, 0.9993949785F, 0.9994011216F, + 0.9994072181F, 0.9994132683F, 0.9994192725F, 0.9994252307F, + 0.9994311434F, 0.9994370107F, 0.9994428327F, 0.9994486099F, + 0.9994543423F, 0.9994600303F, 0.9994656739F, 0.9994712736F, + 0.9994768294F, 0.9994823417F, 0.9994878105F, 0.9994932363F, + 0.9994986191F, 0.9995039592F, 0.9995092568F, 0.9995145122F, + 0.9995197256F, 0.9995248971F, 0.9995300270F, 0.9995351156F, + 0.9995401630F, 0.9995451695F, 0.9995501352F, 0.9995550604F, + 0.9995599454F, 0.9995647903F, 0.9995695953F, 0.9995743607F, + 0.9995790866F, 0.9995837734F, 0.9995884211F, 0.9995930300F, + 0.9995976004F, 0.9996021324F, 0.9996066263F, 0.9996110822F, + 0.9996155004F, 0.9996198810F, 0.9996242244F, 0.9996285306F, + 0.9996327999F, 0.9996370326F, 0.9996412287F, 0.9996453886F, + 0.9996495125F, 0.9996536004F, 0.9996576527F, 0.9996616696F, + 0.9996656512F, 0.9996695977F, 0.9996735094F, 0.9996773865F, + 0.9996812291F, 0.9996850374F, 0.9996888118F, 0.9996925523F, + 0.9996962591F, 0.9996999325F, 0.9997035727F, 0.9997071798F, + 0.9997107541F, 0.9997142957F, 0.9997178049F, 0.9997212818F, + 0.9997247266F, 0.9997281396F, 0.9997315209F, 0.9997348708F, + 0.9997381893F, 0.9997414767F, 0.9997447333F, 0.9997479591F, + 0.9997511544F, 0.9997543194F, 0.9997574542F, 0.9997605591F, + 0.9997636342F, 0.9997666797F, 0.9997696958F, 0.9997726828F, + 0.9997756407F, 0.9997785698F, 0.9997814703F, 0.9997843423F, + 0.9997871860F, 0.9997900016F, 0.9997927894F, 0.9997955494F, + 0.9997982818F, 0.9998009869F, 0.9998036648F, 0.9998063157F, + 0.9998089398F, 0.9998115373F, 0.9998141082F, 0.9998166529F, + 0.9998191715F, 0.9998216642F, 0.9998241311F, 0.9998265724F, + 0.9998289884F, 0.9998313790F, 0.9998337447F, 0.9998360854F, + 0.9998384015F, 0.9998406930F, 0.9998429602F, 0.9998452031F, + 0.9998474221F, 0.9998496171F, 0.9998517885F, 0.9998539364F, + 0.9998560610F, 0.9998581624F, 0.9998602407F, 0.9998622962F, + 0.9998643291F, 0.9998663394F, 0.9998683274F, 0.9998702932F, + 0.9998722370F, 0.9998741589F, 0.9998760591F, 0.9998779378F, + 0.9998797952F, 0.9998816313F, 0.9998834464F, 0.9998852406F, + 0.9998870141F, 0.9998887670F, 0.9998904995F, 0.9998922117F, + 0.9998939039F, 0.9998955761F, 0.9998972285F, 0.9998988613F, + 0.9999004746F, 0.9999020686F, 0.9999036434F, 0.9999051992F, + 0.9999067362F, 0.9999082544F, 0.9999097541F, 0.9999112354F, + 0.9999126984F, 0.9999141433F, 0.9999155703F, 0.9999169794F, + 0.9999183709F, 0.9999197449F, 0.9999211014F, 0.9999224408F, + 0.9999237631F, 0.9999250684F, 0.9999263570F, 0.9999276289F, + 0.9999288843F, 0.9999301233F, 0.9999313461F, 0.9999325529F, + 0.9999337437F, 0.9999349187F, 0.9999360780F, 0.9999372218F, + 0.9999383503F, 0.9999394635F, 0.9999405616F, 0.9999416447F, + 0.9999427129F, 0.9999437665F, 0.9999448055F, 0.9999458301F, + 0.9999468404F, 0.9999478365F, 0.9999488185F, 0.9999497867F, + 0.9999507411F, 0.9999516819F, 0.9999526091F, 0.9999535230F, + 0.9999544236F, 0.9999553111F, 0.9999561856F, 0.9999570472F, + 0.9999578960F, 0.9999587323F, 0.9999595560F, 0.9999603674F, + 0.9999611666F, 0.9999619536F, 0.9999627286F, 0.9999634917F, + 0.9999642431F, 0.9999649828F, 0.9999657110F, 0.9999664278F, + 0.9999671334F, 0.9999678278F, 0.9999685111F, 0.9999691835F, + 0.9999698451F, 0.9999704960F, 0.9999711364F, 0.9999717662F, + 0.9999723858F, 0.9999729950F, 0.9999735942F, 0.9999741834F, + 0.9999747626F, 0.9999753321F, 0.9999758919F, 0.9999764421F, + 0.9999769828F, 0.9999775143F, 0.9999780364F, 0.9999785495F, + 0.9999790535F, 0.9999795485F, 0.9999800348F, 0.9999805124F, + 0.9999809813F, 0.9999814417F, 0.9999818938F, 0.9999823375F, + 0.9999827731F, 0.9999832005F, 0.9999836200F, 0.9999840316F, + 0.9999844353F, 0.9999848314F, 0.9999852199F, 0.9999856008F, + 0.9999859744F, 0.9999863407F, 0.9999866997F, 0.9999870516F, + 0.9999873965F, 0.9999877345F, 0.9999880656F, 0.9999883900F, + 0.9999887078F, 0.9999890190F, 0.9999893237F, 0.9999896220F, + 0.9999899140F, 0.9999901999F, 0.9999904796F, 0.9999907533F, + 0.9999910211F, 0.9999912830F, 0.9999915391F, 0.9999917896F, + 0.9999920345F, 0.9999922738F, 0.9999925077F, 0.9999927363F, + 0.9999929596F, 0.9999931777F, 0.9999933907F, 0.9999935987F, + 0.9999938018F, 0.9999940000F, 0.9999941934F, 0.9999943820F, + 0.9999945661F, 0.9999947456F, 0.9999949206F, 0.9999950912F, + 0.9999952575F, 0.9999954195F, 0.9999955773F, 0.9999957311F, + 0.9999958807F, 0.9999960265F, 0.9999961683F, 0.9999963063F, + 0.9999964405F, 0.9999965710F, 0.9999966979F, 0.9999968213F, + 0.9999969412F, 0.9999970576F, 0.9999971707F, 0.9999972805F, + 0.9999973871F, 0.9999974905F, 0.9999975909F, 0.9999976881F, + 0.9999977824F, 0.9999978738F, 0.9999979624F, 0.9999980481F, + 0.9999981311F, 0.9999982115F, 0.9999982892F, 0.9999983644F, + 0.9999984370F, 0.9999985072F, 0.9999985750F, 0.9999986405F, + 0.9999987037F, 0.9999987647F, 0.9999988235F, 0.9999988802F, + 0.9999989348F, 0.9999989873F, 0.9999990379F, 0.9999990866F, + 0.9999991334F, 0.9999991784F, 0.9999992217F, 0.9999992632F, + 0.9999993030F, 0.9999993411F, 0.9999993777F, 0.9999994128F, + 0.9999994463F, 0.9999994784F, 0.9999995091F, 0.9999995384F, + 0.9999995663F, 0.9999995930F, 0.9999996184F, 0.9999996426F, + 0.9999996657F, 0.9999996876F, 0.9999997084F, 0.9999997282F, + 0.9999997469F, 0.9999997647F, 0.9999997815F, 0.9999997973F, + 0.9999998123F, 0.9999998265F, 0.9999998398F, 0.9999998524F, + 0.9999998642F, 0.9999998753F, 0.9999998857F, 0.9999998954F, + 0.9999999045F, 0.9999999130F, 0.9999999209F, 0.9999999282F, + 0.9999999351F, 0.9999999414F, 0.9999999472F, 0.9999999526F, + 0.9999999576F, 0.9999999622F, 0.9999999664F, 0.9999999702F, + 0.9999999737F, 0.9999999769F, 0.9999999798F, 0.9999999824F, + 0.9999999847F, 0.9999999868F, 0.9999999887F, 0.9999999904F, + 0.9999999919F, 0.9999999932F, 0.9999999943F, 0.9999999953F, + 0.9999999961F, 0.9999999969F, 0.9999999975F, 0.9999999980F, + 0.9999999985F, 0.9999999988F, 0.9999999991F, 0.9999999993F, + 0.9999999995F, 0.9999999997F, 0.9999999998F, 0.9999999999F, + 0.9999999999F, 1.0000000000F, 1.0000000000F, 1.0000000000F, + 1.0000000000F, 1.0000000000F, 1.0000000000F, 1.0000000000F, +}; + +static float *vwin[8] = { + vwin64, + vwin128, + vwin256, + vwin512, + vwin1024, + vwin2048, + vwin4096, + vwin8192, +}; + +float *_vorbis_window_get(int n){ + return vwin[n]; +} + +void _vorbis_apply_window(float *d,int *winno,long *blocksizes, + int lW,int W,int nW){ + lW=(W?lW:0); + nW=(W?nW:0); + + { + float *windowLW=vwin[winno[lW]]; + float *windowNW=vwin[winno[nW]]; + + long n=blocksizes[W]; + long ln=blocksizes[lW]; + long rn=blocksizes[nW]; + + long leftbegin=n/4-ln/4; + long leftend=leftbegin+ln/2; + + long rightbegin=n/2+n/4-rn/4; + long rightend=rightbegin+rn/2; + + int i,p; + + for(i=0;ichannels; + bitsPerSample = 16; + sampleRate = info->rate; + + reservoir.setSize (numChannels, + (int) jmin (lengthInSamples, (int64) reservoir.getNumSamples())); + } + } + + ~OggReader() + { + ov_clear (&ovFile); + } + + bool read (int** destSamples, + int64 startSampleInFile, + int numSamples) + { + if (startSampleInFile < reservoirStart + || startSampleInFile + numSamples > reservoirStart + samplesInReservoir) + { + // buffer miss, so refill the reservoir + int bitStream = 0; + + reservoirStart = (int) jmax ((int64) 0, startSampleInFile - 32); + samplesInReservoir = jmax (numSamples + 32, reservoir.getNumSamples()); + reservoir.setSize (numChannels, samplesInReservoir, false, false, true); + + if (reservoirStart != (int) ov_pcm_tell (&ovFile)) + ov_pcm_seek (&ovFile, reservoirStart); + + int offset = 0; + int numToRead = samplesInReservoir; + + while (numToRead > 0) + { + float** dataIn = 0; + + const int samps = ov_read_float (&ovFile, &dataIn, numToRead, &bitStream); + if (samps == 0) + break; + + jassert (samps <= numToRead); + + for (int i = jmin (numChannels, reservoir.getNumChannels()); --i >= 0;) + { + memcpy (reservoir.getSampleData (i, offset), + dataIn[i], + sizeof (float) * samps); + } + + numToRead -= samps; + offset += samps; + } + + if (numToRead > 0) + reservoir.clear (offset, numToRead); + } + + if (numSamples > 0) + { + for (unsigned int i = 0; i < numChannels; ++i) + { + if (destSamples[i] == 0) + break; + + memcpy (destSamples[i], + reservoir.getSampleData (jmin (i, reservoir.getNumChannels()), + (int) (startSampleInFile - reservoirStart)), + sizeof (float) * numSamples); + } + } + + return true; + } + + static size_t oggReadCallback (void* ptr, size_t size, size_t nmemb, void* datasource) + { + return (size_t) (((InputStream*) datasource)->read (ptr, (int) (size * nmemb)) / size); + } + + static int oggSeekCallback (void* datasource, ogg_int64_t offset, int whence) + { + InputStream* const in = (InputStream*) datasource; + + if (whence == SEEK_CUR) + offset += in->getPosition(); + else if (whence == SEEK_END) + offset += in->getTotalLength(); + + in->setPosition (offset); + return 0; + } + + static int oggCloseCallback (void*) + { + return 0; + } + + static long oggTellCallback (void* datasource) + { + return (long) ((InputStream*) datasource)->getPosition(); + } + + juce_UseDebuggingNewOperator +}; + +class OggWriter : public AudioFormatWriter +{ + ogg_stream_state os; + ogg_page og; + ogg_packet op; + vorbis_info vi; + vorbis_comment vc; + vorbis_dsp_state vd; + vorbis_block vb; + +public: + bool ok; + + OggWriter (OutputStream* const out, + const double sampleRate, + const int numChannels, + const int bitsPerSample, + const int qualityIndex) + : AudioFormatWriter (out, oggFormatName, + sampleRate, + numChannels, + bitsPerSample) + { + ok = false; + + vorbis_info_init (&vi); + + if (vorbis_encode_init_vbr (&vi, + numChannels, + (int) sampleRate, + jlimit (0.0f, 1.0f, qualityIndex * 0.5f)) == 0) + { + vorbis_comment_init (&vc); + + if (JUCEApplication::getInstance() != 0) + vorbis_comment_add_tag (&vc, "ENCODER", + (char*) (const char*) JUCEApplication::getInstance()->getApplicationName()); + + vorbis_analysis_init (&vd, &vi); + vorbis_block_init (&vd, &vb); + + ogg_stream_init (&os, Random::getSystemRandom().nextInt()); + + ogg_packet header; + ogg_packet header_comm; + ogg_packet header_code; + + vorbis_analysis_headerout (&vd, &vc, &header, &header_comm, &header_code); + + ogg_stream_packetin (&os, &header); + ogg_stream_packetin (&os, &header_comm); + ogg_stream_packetin (&os, &header_code); + + for (;;) + { + if (ogg_stream_flush (&os, &og) == 0) + break; + + output->write (og.header, og.header_len); + output->write (og.body, og.body_len); + } + + ok = true; + } + } + + ~OggWriter() + { + if (ok) + { + ogg_stream_clear (&os); + vorbis_block_clear (&vb); + vorbis_dsp_clear (&vd); + vorbis_comment_clear (&vc); + + vorbis_info_clear (&vi); + output->flush(); + } + else + { + vorbis_info_clear (&vi); + output = 0; // to stop the base class deleting this, as it needs to be returned + // to the caller of createWriter() + } + } + + bool write (const int** samplesToWrite, int numSamples) + { + if (! ok) + return false; + + if (numSamples > 0) + { + const double gain = 1.0 / 0x80000000u; + float** const vorbisBuffer = vorbis_analysis_buffer (&vd, numSamples); + + for (int i = numChannels; --i >= 0;) + { + float* const dst = vorbisBuffer[i]; + const int* const src = samplesToWrite [i]; + + if (src != 0 && dst != 0) + { + for (int j = 0; j < numSamples; ++j) + dst[j] = (float) (src[j] * gain); + } + } + } + + vorbis_analysis_wrote (&vd, numSamples); + + while (vorbis_analysis_blockout (&vd, &vb) == 1) + { + vorbis_analysis (&vb, 0); + vorbis_bitrate_addblock (&vb); + + while (vorbis_bitrate_flushpacket (&vd, &op)) + { + ogg_stream_packetin (&os, &op); + + for (;;) + { + if (ogg_stream_pageout (&os, &og) == 0) + break; + + output->write (og.header, og.header_len); + output->write (og.body, og.body_len); + + if (ogg_page_eos (&og)) + break; + } + } + } + + return true; + } + + juce_UseDebuggingNewOperator +}; + +OggVorbisAudioFormat::OggVorbisAudioFormat() + : AudioFormat (oggFormatName, (const tchar**) oggExtensions) +{ +} + +OggVorbisAudioFormat::~OggVorbisAudioFormat() +{ +} + +const Array OggVorbisAudioFormat::getPossibleSampleRates() +{ + const int rates[] = { 22050, 32000, 44100, 48000, 0 }; + return Array (rates); +} + +const Array OggVorbisAudioFormat::getPossibleBitDepths() +{ + Array depths; + depths.add (32); + return depths; +} + +bool OggVorbisAudioFormat::canDoStereo() +{ + return true; +} + +bool OggVorbisAudioFormat::canDoMono() +{ + return true; +} + +AudioFormatReader* OggVorbisAudioFormat::createReaderFor (InputStream* in, + const bool deleteStreamIfOpeningFails) +{ + OggReader* r = new OggReader (in); + + if (r->sampleRate == 0) + { + if (! deleteStreamIfOpeningFails) + r->input = 0; + + deleteAndZero (r); + } + + return r; +} + +AudioFormatWriter* OggVorbisAudioFormat::createWriterFor (OutputStream* out, + double sampleRate, + unsigned int numChannels, + int bitsPerSample, + const StringPairArray& /*metadataValues*/, + int qualityOptionIndex) +{ + OggWriter* w = new OggWriter (out, + sampleRate, + numChannels, + bitsPerSample, + qualityOptionIndex); + + if (! w->ok) + deleteAndZero (w); + + return w; +} + +bool OggVorbisAudioFormat::isCompressed() +{ + return true; +} + +const StringArray OggVorbisAudioFormat::getQualityOptions() +{ + StringArray s; + s.add ("Low Quality"); + s.add ("Medium Quality"); + s.add ("High Quality"); + return s; +} + +int OggVorbisAudioFormat::estimateOggFileQuality (const File& source) +{ + FileInputStream* const in = source.createInputStream(); + + if (in != 0) + { + AudioFormatReader* const r = createReaderFor (in, true); + + if (r != 0) + { + const int64 numSamps = r->lengthInSamples; + delete r; + + const int64 fileNumSamps = source.getSize() / 4; + const double ratio = numSamps / (double) fileNumSamps; + + if (ratio > 12.0) + return 0; + else if (ratio > 6.0) + return 1; + else + return 2; + } + } + + return 1; +} + +END_JUCE_NAMESPACE + +#endif +/********* End of inlined file: juce_OggVorbisAudioFormat.cpp *********/ + +/********* Start of inlined file: juce_JPEGLoader.cpp *********/ + +#if JUCE_MSVC + #pragma warning (push) +#endif + +namespace jpeglibNamespace +{ + extern "C" + { + #define JPEG_INTERNALS + #undef FAR + +/********* Start of inlined file: jpeglib.h *********/ +#ifndef JPEGLIB_H +#define JPEGLIB_H + +/* + * First we include the configuration files that record how this + * installation of the JPEG library is set up. jconfig.h can be + * generated automatically for many systems. jmorecfg.h contains + * manual configuration options that most people need not worry about. + */ + +#ifndef JCONFIG_INCLUDED /* in case jinclude.h already did */ + +/********* Start of inlined file: jconfig.h *********/ +/* see jconfig.doc for explanations */ + +// disable all the warnings under MSVC +#ifdef _MSC_VER +#pragma warning (disable: 4996 4267 4100 4127 4702 4244) +#endif + +#ifdef __BORLANDC__ +#pragma warn -8057 +#pragma warn -8019 +#pragma warn -8004 +#pragma warn -8008 +#endif + +#define HAVE_PROTOTYPES +#define HAVE_UNSIGNED_CHAR +#define HAVE_UNSIGNED_SHORT +/* #define void char */ +/* #define const */ +#undef CHAR_IS_UNSIGNED +#define HAVE_STDDEF_H +#define HAVE_STDLIB_H +#undef NEED_BSD_STRINGS +#undef NEED_SYS_TYPES_H +#undef NEED_FAR_POINTERS /* we presume a 32-bit flat memory model */ +#undef NEED_SHORT_EXTERNAL_NAMES +#undef INCOMPLETE_TYPES_BROKEN + +/* Define "boolean" as unsigned char, not int, per Windows custom */ +#ifndef __RPCNDR_H__ /* don't conflict if rpcndr.h already read */ +typedef unsigned char boolean; +#endif +#define HAVE_BOOLEAN /* prevent jmorecfg.h from redefining it */ + +#ifdef JPEG_INTERNALS + +#undef RIGHT_SHIFT_IS_UNSIGNED + +#endif /* JPEG_INTERNALS */ + +#ifdef JPEG_CJPEG_DJPEG + +#define BMP_SUPPORTED /* BMP image file format */ +#define GIF_SUPPORTED /* GIF image file format */ +#define PPM_SUPPORTED /* PBMPLUS PPM/PGM image file format */ +#undef RLE_SUPPORTED /* Utah RLE image file format */ +#define TARGA_SUPPORTED /* Targa image file format */ + +#define TWO_FILE_COMMANDLINE /* optional */ +#define USE_SETMODE /* Microsoft has setmode() */ +#undef NEED_SIGNAL_CATCHER +#undef DONT_USE_B_MODE +#undef PROGRESS_REPORT /* optional */ + +#endif /* JPEG_CJPEG_DJPEG */ +/********* End of inlined file: jconfig.h *********/ + + /* widely used configuration options */ +#endif + +/********* Start of inlined file: jmorecfg.h *********/ +/* + * Define BITS_IN_JSAMPLE as either + * 8 for 8-bit sample values (the usual setting) + * 12 for 12-bit sample values + * Only 8 and 12 are legal data precisions for lossy JPEG according to the + * JPEG standard, and the IJG code does not support anything else! + * We do not support run-time selection of data precision, sorry. + */ + +#define BITS_IN_JSAMPLE 8 /* use 8 or 12 */ + +/* + * Maximum number of components (color channels) allowed in JPEG image. + * To meet the letter of the JPEG spec, set this to 255. However, darn + * few applications need more than 4 channels (maybe 5 for CMYK + alpha + * mask). We recommend 10 as a reasonable compromise; use 4 if you are + * really short on memory. (Each allowed component costs a hundred or so + * bytes of storage, whether actually used in an image or not.) + */ + +#define MAX_COMPONENTS 10 /* maximum number of image components */ + +/* + * Basic data types. + * You may need to change these if you have a machine with unusual data + * type sizes; for example, "char" not 8 bits, "short" not 16 bits, + * or "long" not 32 bits. We don't care whether "int" is 16 or 32 bits, + * but it had better be at least 16. + */ + +/* Representation of a single sample (pixel element value). + * We frequently allocate large arrays of these, so it's important to keep + * them small. But if you have memory to burn and access to char or short + * arrays is very slow on your hardware, you might want to change these. + */ + +#if BITS_IN_JSAMPLE == 8 +/* JSAMPLE should be the smallest type that will hold the values 0..255. + * You can use a signed char by having GETJSAMPLE mask it with 0xFF. + */ + +#ifdef HAVE_UNSIGNED_CHAR + +typedef unsigned char JSAMPLE; +#define GETJSAMPLE(value) ((int) (value)) + +#else /* not HAVE_UNSIGNED_CHAR */ + +typedef char JSAMPLE; +#ifdef CHAR_IS_UNSIGNED +#define GETJSAMPLE(value) ((int) (value)) +#else +#define GETJSAMPLE(value) ((int) (value) & 0xFF) +#endif /* CHAR_IS_UNSIGNED */ + +#endif /* HAVE_UNSIGNED_CHAR */ + +#define MAXJSAMPLE 255 +#define CENTERJSAMPLE 128 + +#endif /* BITS_IN_JSAMPLE == 8 */ + +#if BITS_IN_JSAMPLE == 12 +/* JSAMPLE should be the smallest type that will hold the values 0..4095. + * On nearly all machines "short" will do nicely. + */ + +typedef short JSAMPLE; +#define GETJSAMPLE(value) ((int) (value)) + +#define MAXJSAMPLE 4095 +#define CENTERJSAMPLE 2048 + +#endif /* BITS_IN_JSAMPLE == 12 */ + +/* Representation of a DCT frequency coefficient. + * This should be a signed value of at least 16 bits; "short" is usually OK. + * Again, we allocate large arrays of these, but you can change to int + * if you have memory to burn and "short" is really slow. + */ + +typedef short JCOEF; + +/* Compressed datastreams are represented as arrays of JOCTET. + * These must be EXACTLY 8 bits wide, at least once they are written to + * external storage. Note that when using the stdio data source/destination + * managers, this is also the data type passed to fread/fwrite. + */ + +#ifdef HAVE_UNSIGNED_CHAR + +typedef unsigned char JOCTET; +#define GETJOCTET(value) (value) + +#else /* not HAVE_UNSIGNED_CHAR */ + +typedef char JOCTET; +#ifdef CHAR_IS_UNSIGNED +#define GETJOCTET(value) (value) +#else +#define GETJOCTET(value) ((value) & 0xFF) +#endif /* CHAR_IS_UNSIGNED */ + +#endif /* HAVE_UNSIGNED_CHAR */ + +/* These typedefs are used for various table entries and so forth. + * They must be at least as wide as specified; but making them too big + * won't cost a huge amount of memory, so we don't provide special + * extraction code like we did for JSAMPLE. (In other words, these + * typedefs live at a different point on the speed/space tradeoff curve.) + */ + +/* UINT8 must hold at least the values 0..255. */ + +#ifdef HAVE_UNSIGNED_CHAR +typedef unsigned char UINT8; +#else /* not HAVE_UNSIGNED_CHAR */ +#ifdef CHAR_IS_UNSIGNED +typedef char UINT8; +#else /* not CHAR_IS_UNSIGNED */ +typedef short UINT8; +#endif /* CHAR_IS_UNSIGNED */ +#endif /* HAVE_UNSIGNED_CHAR */ + +/* UINT16 must hold at least the values 0..65535. */ + +#ifdef HAVE_UNSIGNED_SHORT +typedef unsigned short UINT16; +#else /* not HAVE_UNSIGNED_SHORT */ +typedef unsigned int UINT16; +#endif /* HAVE_UNSIGNED_SHORT */ + +/* INT16 must hold at least the values -32768..32767. */ + +#ifndef XMD_H /* X11/xmd.h correctly defines INT16 */ +typedef short INT16; +#endif + +/* INT32 must hold at least signed 32-bit values. */ + +#ifndef XMD_H /* X11/xmd.h correctly defines INT32 */ +typedef long INT32; +#endif + +/* Datatype used for image dimensions. The JPEG standard only supports + * images up to 64K*64K due to 16-bit fields in SOF markers. Therefore + * "unsigned int" is sufficient on all machines. However, if you need to + * handle larger images and you don't mind deviating from the spec, you + * can change this datatype. + */ + +typedef unsigned int JDIMENSION; + +#define JPEG_MAX_DIMENSION 65500L /* a tad under 64K to prevent overflows */ + +/* These macros are used in all function definitions and extern declarations. + * You could modify them if you need to change function linkage conventions; + * in particular, you'll need to do that to make the library a Windows DLL. + * Another application is to make all functions global for use with debuggers + * or code profilers that require it. + */ + +/* a function called through method pointers: */ +#define METHODDEF(type) static type +/* a function used only in its module: */ +#define LOCAL(type) static type +/* a function referenced thru EXTERNs: */ +#define GLOBAL(type) type +/* a reference to a GLOBAL function: */ +#define EXTERN(type) extern type + +/* This macro is used to declare a "method", that is, a function pointer. + * We want to supply prototype parameters if the compiler can cope. + * Note that the arglist parameter must be parenthesized! + * Again, you can customize this if you need special linkage keywords. + */ + +#ifdef HAVE_PROTOTYPES +#define JMETHOD(type,methodname,arglist) type (*methodname) arglist +#else +#define JMETHOD(type,methodname,arglist) type (*methodname) () +#endif + +/* Here is the pseudo-keyword for declaring pointers that must be "far" + * on 80x86 machines. Most of the specialized coding for 80x86 is handled + * by just saying "FAR *" where such a pointer is needed. In a few places + * explicit coding is needed; see uses of the NEED_FAR_POINTERS symbol. + */ + +#ifdef NEED_FAR_POINTERS +#define FAR far +#else +#define FAR +#endif + +/* + * On a few systems, type boolean and/or its values FALSE, TRUE may appear + * in standard header files. Or you may have conflicts with application- + * specific header files that you want to include together with these files. + * Defining HAVE_BOOLEAN before including jpeglib.h should make it work. + */ + +#ifndef HAVE_BOOLEAN +typedef int boolean; +#endif +#ifndef FALSE /* in case these macros already exist */ +#define FALSE 0 /* values of boolean */ +#endif +#ifndef TRUE +#define TRUE 1 +#endif + +/* + * The remaining options affect code selection within the JPEG library, + * but they don't need to be visible to most applications using the library. + * To minimize application namespace pollution, the symbols won't be + * defined unless JPEG_INTERNALS or JPEG_INTERNAL_OPTIONS has been defined. + */ + +#ifdef JPEG_INTERNALS +#define JPEG_INTERNAL_OPTIONS +#endif + +#ifdef JPEG_INTERNAL_OPTIONS + +/* + * These defines indicate whether to include various optional functions. + * Undefining some of these symbols will produce a smaller but less capable + * library. Note that you can leave certain source files out of the + * compilation/linking process if you've #undef'd the corresponding symbols. + * (You may HAVE to do that if your compiler doesn't like null source files.) + */ + +/* Arithmetic coding is unsupported for legal reasons. Complaints to IBM. */ + +/* Capability options common to encoder and decoder: */ + +#define DCT_ISLOW_SUPPORTED /* slow but accurate integer algorithm */ +#define DCT_IFAST_SUPPORTED /* faster, less accurate integer method */ +#define DCT_FLOAT_SUPPORTED /* floating-point: accurate, fast on fast HW */ + +/* Encoder capability options: */ + +#undef C_ARITH_CODING_SUPPORTED /* Arithmetic coding back end? */ +#define C_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ +#define C_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ +#define ENTROPY_OPT_SUPPORTED /* Optimization of entropy coding parms? */ +/* Note: if you selected 12-bit data precision, it is dangerous to turn off + * ENTROPY_OPT_SUPPORTED. The standard Huffman tables are only good for 8-bit + * precision, so jchuff.c normally uses entropy optimization to compute + * usable tables for higher precision. If you don't want to do optimization, + * you'll have to supply different default Huffman tables. + * The exact same statements apply for progressive JPEG: the default tables + * don't work for progressive mode. (This may get fixed, however.) + */ +#define INPUT_SMOOTHING_SUPPORTED /* Input image smoothing option? */ + +/* Decoder capability options: */ + +#undef D_ARITH_CODING_SUPPORTED /* Arithmetic coding back end? */ +#define D_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ +#define D_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ +#define SAVE_MARKERS_SUPPORTED /* jpeg_save_markers() needed? */ +#define BLOCK_SMOOTHING_SUPPORTED /* Block smoothing? (Progressive only) */ +#define IDCT_SCALING_SUPPORTED /* Output rescaling via IDCT? */ +#undef UPSAMPLE_SCALING_SUPPORTED /* Output rescaling at upsample stage? */ +#define UPSAMPLE_MERGING_SUPPORTED /* Fast path for sloppy upsampling? */ +#define QUANT_1PASS_SUPPORTED /* 1-pass color quantization? */ +#define QUANT_2PASS_SUPPORTED /* 2-pass color quantization? */ + +/* more capability options later, no doubt */ + +/* + * Ordering of RGB data in scanlines passed to or from the application. + * If your application wants to deal with data in the order B,G,R, just + * change these macros. You can also deal with formats such as R,G,B,X + * (one extra byte per pixel) by changing RGB_PIXELSIZE. Note that changing + * the offsets will also change the order in which colormap data is organized. + * RESTRICTIONS: + * 1. The sample applications cjpeg,djpeg do NOT support modified RGB formats. + * 2. These macros only affect RGB<=>YCbCr color conversion, so they are not + * useful if you are using JPEG color spaces other than YCbCr or grayscale. + * 3. The color quantizer modules will not behave desirably if RGB_PIXELSIZE + * is not 3 (they don't understand about dummy color components!). So you + * can't use color quantization if you change that value. + */ + +#define RGB_RED 0 /* Offset of Red in an RGB scanline element */ +#define RGB_GREEN 1 /* Offset of Green */ +#define RGB_BLUE 2 /* Offset of Blue */ +#define RGB_PIXELSIZE 3 /* JSAMPLEs per RGB scanline element */ + +/* Definitions for speed-related optimizations. */ + +/* If your compiler supports inline functions, define INLINE + * as the inline keyword; otherwise define it as empty. + */ + +#ifndef INLINE +#ifdef __GNUC__ /* for instance, GNU C knows about inline */ +#define INLINE __inline__ +#endif +#ifndef INLINE +#define INLINE /* default is to define it as empty */ +#endif +#endif + +/* On some machines (notably 68000 series) "int" is 32 bits, but multiplying + * two 16-bit shorts is faster than multiplying two ints. Define MULTIPLIER + * as short on such a machine. MULTIPLIER must be at least 16 bits wide. + */ + +#ifndef MULTIPLIER +#define MULTIPLIER int /* type for fastest integer multiply */ +#endif + +/* FAST_FLOAT should be either float or double, whichever is done faster + * by your compiler. (Note that this type is only used in the floating point + * DCT routines, so it only matters if you've defined DCT_FLOAT_SUPPORTED.) + * Typically, float is faster in ANSI C compilers, while double is faster in + * pre-ANSI compilers (because they insist on converting to double anyway). + * The code below therefore chooses float if we have ANSI-style prototypes. + */ + +#ifndef FAST_FLOAT +#ifdef HAVE_PROTOTYPES +#define FAST_FLOAT float +#else +#define FAST_FLOAT double +#endif +#endif + +#endif /* JPEG_INTERNAL_OPTIONS */ +/********* End of inlined file: jmorecfg.h *********/ + + /* seldom changed options */ + +/* Version ID for the JPEG library. + * Might be useful for tests like "#if JPEG_LIB_VERSION >= 60". + */ + +#define JPEG_LIB_VERSION 62 /* Version 6b */ + +/* Various constants determining the sizes of things. + * All of these are specified by the JPEG standard, so don't change them + * if you want to be compatible. + */ + +#define DCTSIZE 8 /* The basic DCT block is 8x8 samples */ +#define DCTSIZE2 64 /* DCTSIZE squared; # of elements in a block */ +#define NUM_QUANT_TBLS 4 /* Quantization tables are numbered 0..3 */ +#define NUM_HUFF_TBLS 4 /* Huffman tables are numbered 0..3 */ +#define NUM_ARITH_TBLS 16 /* Arith-coding tables are numbered 0..15 */ +#define MAX_COMPS_IN_SCAN 4 /* JPEG limit on # of components in one scan */ +#define MAX_SAMP_FACTOR 4 /* JPEG limit on sampling factors */ +/* Unfortunately, some bozo at Adobe saw no reason to be bound by the standard; + * the PostScript DCT filter can emit files with many more than 10 blocks/MCU. + * If you happen to run across such a file, you can up D_MAX_BLOCKS_IN_MCU + * to handle it. We even let you do this from the jconfig.h file. However, + * we strongly discourage changing C_MAX_BLOCKS_IN_MCU; just because Adobe + * sometimes emits noncompliant files doesn't mean you should too. + */ +#define C_MAX_BLOCKS_IN_MCU 10 /* compressor's limit on blocks per MCU */ +#ifndef D_MAX_BLOCKS_IN_MCU +#define D_MAX_BLOCKS_IN_MCU 10 /* decompressor's limit on blocks per MCU */ +#endif + +/* Data structures for images (arrays of samples and of DCT coefficients). + * On 80x86 machines, the image arrays are too big for near pointers, + * but the pointer arrays can fit in near memory. + */ + +typedef JSAMPLE FAR *JSAMPROW; /* ptr to one image row of pixel samples. */ +typedef JSAMPROW *JSAMPARRAY; /* ptr to some rows (a 2-D sample array) */ +typedef JSAMPARRAY *JSAMPIMAGE; /* a 3-D sample array: top index is color */ + +typedef JCOEF JBLOCK[DCTSIZE2]; /* one block of coefficients */ +typedef JBLOCK FAR *JBLOCKROW; /* pointer to one row of coefficient blocks */ +typedef JBLOCKROW *JBLOCKARRAY; /* a 2-D array of coefficient blocks */ +typedef JBLOCKARRAY *JBLOCKIMAGE; /* a 3-D array of coefficient blocks */ + +typedef JCOEF FAR *JCOEFPTR; /* useful in a couple of places */ + +/* Types for JPEG compression parameters and working tables. */ + +/* DCT coefficient quantization tables. */ + +typedef struct { + /* This array gives the coefficient quantizers in natural array order + * (not the zigzag order in which they are stored in a JPEG DQT marker). + * CAUTION: IJG versions prior to v6a kept this array in zigzag order. + */ + UINT16 quantval[DCTSIZE2]; /* quantization step for each coefficient */ + /* This field is used only during compression. It's initialized FALSE when + * the table is created, and set TRUE when it's been output to the file. + * You could suppress output of a table by setting this to TRUE. + * (See jpeg_suppress_tables for an example.) + */ + boolean sent_table; /* TRUE when table has been output */ +} JQUANT_TBL; + +/* Huffman coding tables. */ + +typedef struct { + /* These two fields directly represent the contents of a JPEG DHT marker */ + UINT8 bits[17]; /* bits[k] = # of symbols with codes of */ + /* length k bits; bits[0] is unused */ + UINT8 huffval[256]; /* The symbols, in order of incr code length */ + /* This field is used only during compression. It's initialized FALSE when + * the table is created, and set TRUE when it's been output to the file. + * You could suppress output of a table by setting this to TRUE. + * (See jpeg_suppress_tables for an example.) + */ + boolean sent_table; /* TRUE when table has been output */ +} JHUFF_TBL; + +/* Basic info about one component (color channel). */ + +typedef struct { + /* These values are fixed over the whole image. */ + /* For compression, they must be supplied by parameter setup; */ + /* for decompression, they are read from the SOF marker. */ + int component_id; /* identifier for this component (0..255) */ + int component_index; /* its index in SOF or cinfo->comp_info[] */ + int h_samp_factor; /* horizontal sampling factor (1..4) */ + int v_samp_factor; /* vertical sampling factor (1..4) */ + int quant_tbl_no; /* quantization table selector (0..3) */ + /* These values may vary between scans. */ + /* For compression, they must be supplied by parameter setup; */ + /* for decompression, they are read from the SOS marker. */ + /* The decompressor output side may not use these variables. */ + int dc_tbl_no; /* DC entropy table selector (0..3) */ + int ac_tbl_no; /* AC entropy table selector (0..3) */ + + /* Remaining fields should be treated as private by applications. */ + + /* These values are computed during compression or decompression startup: */ + /* Component's size in DCT blocks. + * Any dummy blocks added to complete an MCU are not counted; therefore + * these values do not depend on whether a scan is interleaved or not. + */ + JDIMENSION width_in_blocks; + JDIMENSION height_in_blocks; + /* Size of a DCT block in samples. Always DCTSIZE for compression. + * For decompression this is the size of the output from one DCT block, + * reflecting any scaling we choose to apply during the IDCT step. + * Values of 1,2,4,8 are likely to be supported. Note that different + * components may receive different IDCT scalings. + */ + int DCT_scaled_size; + /* The downsampled dimensions are the component's actual, unpadded number + * of samples at the main buffer (preprocessing/compression interface), thus + * downsampled_width = ceil(image_width * Hi/Hmax) + * and similarly for height. For decompression, IDCT scaling is included, so + * downsampled_width = ceil(image_width * Hi/Hmax * DCT_scaled_size/DCTSIZE) + */ + JDIMENSION downsampled_width; /* actual width in samples */ + JDIMENSION downsampled_height; /* actual height in samples */ + /* This flag is used only for decompression. In cases where some of the + * components will be ignored (eg grayscale output from YCbCr image), + * we can skip most computations for the unused components. + */ + boolean component_needed; /* do we need the value of this component? */ + + /* These values are computed before starting a scan of the component. */ + /* The decompressor output side may not use these variables. */ + int MCU_width; /* number of blocks per MCU, horizontally */ + int MCU_height; /* number of blocks per MCU, vertically */ + int MCU_blocks; /* MCU_width * MCU_height */ + int MCU_sample_width; /* MCU width in samples, MCU_width*DCT_scaled_size */ + int last_col_width; /* # of non-dummy blocks across in last MCU */ + int last_row_height; /* # of non-dummy blocks down in last MCU */ + + /* Saved quantization table for component; NULL if none yet saved. + * See jdinput.c comments about the need for this information. + * This field is currently used only for decompression. + */ + JQUANT_TBL * quant_table; + + /* Private per-component storage for DCT or IDCT subsystem. */ + void * dct_table; +} jpeg_component_info; + +/* The script for encoding a multiple-scan file is an array of these: */ + +typedef struct { + int comps_in_scan; /* number of components encoded in this scan */ + int component_index[MAX_COMPS_IN_SCAN]; /* their SOF/comp_info[] indexes */ + int Ss, Se; /* progressive JPEG spectral selection parms */ + int Ah, Al; /* progressive JPEG successive approx. parms */ +} jpeg_scan_info; + +/* The decompressor can save APPn and COM markers in a list of these: */ + +typedef struct jpeg_marker_struct FAR * jpeg_saved_marker_ptr; + +struct jpeg_marker_struct { + jpeg_saved_marker_ptr next; /* next in list, or NULL */ + UINT8 marker; /* marker code: JPEG_COM, or JPEG_APP0+n */ + unsigned int original_length; /* # bytes of data in the file */ + unsigned int data_length; /* # bytes of data saved at data[] */ + JOCTET FAR * data; /* the data contained in the marker */ + /* the marker length word is not counted in data_length or original_length */ +}; + +/* Known color spaces. */ + +typedef enum { + JCS_UNKNOWN, /* error/unspecified */ + JCS_GRAYSCALE, /* monochrome */ + JCS_RGB, /* red/green/blue */ + JCS_YCbCr, /* Y/Cb/Cr (also known as YUV) */ + JCS_CMYK, /* C/M/Y/K */ + JCS_YCCK /* Y/Cb/Cr/K */ +} J_COLOR_SPACE; + +/* DCT/IDCT algorithm options. */ + +typedef enum { + JDCT_ISLOW, /* slow but accurate integer algorithm */ + JDCT_IFAST, /* faster, less accurate integer method */ + JDCT_FLOAT /* floating-point: accurate, fast on fast HW */ +} J_DCT_METHOD; + +#ifndef JDCT_DEFAULT /* may be overridden in jconfig.h */ +#define JDCT_DEFAULT JDCT_ISLOW +#endif +#ifndef JDCT_FASTEST /* may be overridden in jconfig.h */ +#define JDCT_FASTEST JDCT_IFAST +#endif + +/* Dithering options for decompression. */ + +typedef enum { + JDITHER_NONE, /* no dithering */ + JDITHER_ORDERED, /* simple ordered dither */ + JDITHER_FS /* Floyd-Steinberg error diffusion dither */ +} J_DITHER_MODE; + +/* Common fields between JPEG compression and decompression master structs. */ + +#define jpeg_common_fields \ + struct jpeg_error_mgr * err; /* Error handler module */\ + struct jpeg_memory_mgr * mem; /* Memory manager module */\ + struct jpeg_progress_mgr * progress; /* Progress monitor, or NULL if none */\ + void * client_data; /* Available for use by application */\ + boolean is_decompressor; /* So common code can tell which is which */\ + int global_state /* For checking call sequence validity */ + +/* Routines that are to be used by both halves of the library are declared + * to receive a pointer to this structure. There are no actual instances of + * jpeg_common_struct, only of jpeg_compress_struct and jpeg_decompress_struct. + */ +struct jpeg_common_struct { + jpeg_common_fields; /* Fields common to both master struct types */ + /* Additional fields follow in an actual jpeg_compress_struct or + * jpeg_decompress_struct. All three structs must agree on these + * initial fields! (This would be a lot cleaner in C++.) + */ +}; + +typedef struct jpeg_common_struct * j_common_ptr; +typedef struct jpeg_compress_struct * j_compress_ptr; +typedef struct jpeg_decompress_struct * j_decompress_ptr; + +/* Master record for a compression instance */ + +struct jpeg_compress_struct { + jpeg_common_fields; /* Fields shared with jpeg_decompress_struct */ + + /* Destination for compressed data */ + struct jpeg_destination_mgr * dest; + + /* Description of source image --- these fields must be filled in by + * outer application before starting compression. in_color_space must + * be correct before you can even call jpeg_set_defaults(). + */ + + JDIMENSION image_width; /* input image width */ + JDIMENSION image_height; /* input image height */ + int input_components; /* # of color components in input image */ + J_COLOR_SPACE in_color_space; /* colorspace of input image */ + + double input_gamma; /* image gamma of input image */ + + /* Compression parameters --- these fields must be set before calling + * jpeg_start_compress(). We recommend calling jpeg_set_defaults() to + * initialize everything to reasonable defaults, then changing anything + * the application specifically wants to change. That way you won't get + * burnt when new parameters are added. Also note that there are several + * helper routines to simplify changing parameters. + */ + + int data_precision; /* bits of precision in image data */ + + int num_components; /* # of color components in JPEG image */ + J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ + + jpeg_component_info * comp_info; + /* comp_info[i] describes component that appears i'th in SOF */ + + JQUANT_TBL * quant_tbl_ptrs[NUM_QUANT_TBLS]; + /* ptrs to coefficient quantization tables, or NULL if not defined */ + + JHUFF_TBL * dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + /* ptrs to Huffman coding tables, or NULL if not defined */ + + UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ + UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */ + UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ + + int num_scans; /* # of entries in scan_info array */ + const jpeg_scan_info * scan_info; /* script for multi-scan file, or NULL */ + /* The default value of scan_info is NULL, which causes a single-scan + * sequential JPEG file to be emitted. To create a multi-scan file, + * set num_scans and scan_info to point to an array of scan definitions. + */ + + boolean raw_data_in; /* TRUE=caller supplies downsampled data */ + boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ + boolean optimize_coding; /* TRUE=optimize entropy encoding parms */ + boolean CCIR601_sampling; /* TRUE=first samples are cosited */ + int smoothing_factor; /* 1..100, or 0 for no input smoothing */ + J_DCT_METHOD dct_method; /* DCT algorithm selector */ + + /* The restart interval can be specified in absolute MCUs by setting + * restart_interval, or in MCU rows by setting restart_in_rows + * (in which case the correct restart_interval will be figured + * for each scan). + */ + unsigned int restart_interval; /* MCUs per restart, or 0 for no restart */ + int restart_in_rows; /* if > 0, MCU rows per restart interval */ + + /* Parameters controlling emission of special markers. */ + + boolean write_JFIF_header; /* should a JFIF marker be written? */ + UINT8 JFIF_major_version; /* What to write for the JFIF version number */ + UINT8 JFIF_minor_version; + /* These three values are not used by the JPEG code, merely copied */ + /* into the JFIF APP0 marker. density_unit can be 0 for unknown, */ + /* 1 for dots/inch, or 2 for dots/cm. Note that the pixel aspect */ + /* ratio is defined by X_density/Y_density even when density_unit=0. */ + UINT8 density_unit; /* JFIF code for pixel size units */ + UINT16 X_density; /* Horizontal pixel density */ + UINT16 Y_density; /* Vertical pixel density */ + boolean write_Adobe_marker; /* should an Adobe marker be written? */ + + /* State variable: index of next scanline to be written to + * jpeg_write_scanlines(). Application may use this to control its + * processing loop, e.g., "while (next_scanline < image_height)". + */ + + JDIMENSION next_scanline; /* 0 .. image_height-1 */ + + /* Remaining fields are known throughout compressor, but generally + * should not be touched by a surrounding application. + */ + + /* + * These fields are computed during compression startup + */ + boolean progressive_mode; /* TRUE if scan script uses progressive mode */ + int max_h_samp_factor; /* largest h_samp_factor */ + int max_v_samp_factor; /* largest v_samp_factor */ + + JDIMENSION total_iMCU_rows; /* # of iMCU rows to be input to coef ctlr */ + /* The coefficient controller receives data in units of MCU rows as defined + * for fully interleaved scans (whether the JPEG file is interleaved or not). + * There are v_samp_factor * DCTSIZE sample rows of each component in an + * "iMCU" (interleaved MCU) row. + */ + + /* + * These fields are valid during any one scan. + * They describe the components and MCUs actually appearing in the scan. + */ + int comps_in_scan; /* # of JPEG components in this scan */ + jpeg_component_info * cur_comp_info[MAX_COMPS_IN_SCAN]; + /* *cur_comp_info[i] describes component that appears i'th in SOS */ + + JDIMENSION MCUs_per_row; /* # of MCUs across the image */ + JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ + + int blocks_in_MCU; /* # of DCT blocks per MCU */ + int MCU_membership[C_MAX_BLOCKS_IN_MCU]; + /* MCU_membership[i] is index in cur_comp_info of component owning */ + /* i'th block in an MCU */ + + int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + + /* + * Links to compression subobjects (methods and private variables of modules) + */ + struct jpeg_comp_master * master; + struct jpeg_c_main_controller * main; + struct jpeg_c_prep_controller * prep; + struct jpeg_c_coef_controller * coef; + struct jpeg_marker_writer * marker; + struct jpeg_color_converter * cconvert; + struct jpeg_downsampler * downsample; + struct jpeg_forward_dct * fdct; + struct jpeg_entropy_encoder * entropy; + jpeg_scan_info * script_space; /* workspace for jpeg_simple_progression */ + int script_space_size; +}; + +/* Master record for a decompression instance */ + +struct jpeg_decompress_struct { + jpeg_common_fields; /* Fields shared with jpeg_compress_struct */ + + /* Source of compressed data */ + struct jpeg_source_mgr * src; + + /* Basic description of image --- filled in by jpeg_read_header(). */ + /* Application may inspect these values to decide how to process image. */ + + JDIMENSION image_width; /* nominal image width (from SOF marker) */ + JDIMENSION image_height; /* nominal image height */ + int num_components; /* # of color components in JPEG image */ + J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ + + /* Decompression processing parameters --- these fields must be set before + * calling jpeg_start_decompress(). Note that jpeg_read_header() initializes + * them to default values. + */ + + J_COLOR_SPACE out_color_space; /* colorspace for output */ + + unsigned int scale_num, scale_denom; /* fraction by which to scale image */ + + double output_gamma; /* image gamma wanted in output */ + + boolean buffered_image; /* TRUE=multiple output passes */ + boolean raw_data_out; /* TRUE=downsampled data wanted */ + + J_DCT_METHOD dct_method; /* IDCT algorithm selector */ + boolean do_fancy_upsampling; /* TRUE=apply fancy upsampling */ + boolean do_block_smoothing; /* TRUE=apply interblock smoothing */ + + boolean quantize_colors; /* TRUE=colormapped output wanted */ + /* the following are ignored if not quantize_colors: */ + J_DITHER_MODE dither_mode; /* type of color dithering to use */ + boolean two_pass_quantize; /* TRUE=use two-pass color quantization */ + int desired_number_of_colors; /* max # colors to use in created colormap */ + /* these are significant only in buffered-image mode: */ + boolean enable_1pass_quant; /* enable future use of 1-pass quantizer */ + boolean enable_external_quant;/* enable future use of external colormap */ + boolean enable_2pass_quant; /* enable future use of 2-pass quantizer */ + + /* Description of actual output image that will be returned to application. + * These fields are computed by jpeg_start_decompress(). + * You can also use jpeg_calc_output_dimensions() to determine these values + * in advance of calling jpeg_start_decompress(). + */ + + JDIMENSION output_width; /* scaled image width */ + JDIMENSION output_height; /* scaled image height */ + int out_color_components; /* # of color components in out_color_space */ + int output_components; /* # of color components returned */ + /* output_components is 1 (a colormap index) when quantizing colors; + * otherwise it equals out_color_components. + */ + int rec_outbuf_height; /* min recommended height of scanline buffer */ + /* If the buffer passed to jpeg_read_scanlines() is less than this many rows + * high, space and time will be wasted due to unnecessary data copying. + * Usually rec_outbuf_height will be 1 or 2, at most 4. + */ + + /* When quantizing colors, the output colormap is described by these fields. + * The application can supply a colormap by setting colormap non-NULL before + * calling jpeg_start_decompress; otherwise a colormap is created during + * jpeg_start_decompress or jpeg_start_output. + * The map has out_color_components rows and actual_number_of_colors columns. + */ + int actual_number_of_colors; /* number of entries in use */ + JSAMPARRAY colormap; /* The color map as a 2-D pixel array */ + + /* State variables: these variables indicate the progress of decompression. + * The application may examine these but must not modify them. + */ + + /* Row index of next scanline to be read from jpeg_read_scanlines(). + * Application may use this to control its processing loop, e.g., + * "while (output_scanline < output_height)". + */ + JDIMENSION output_scanline; /* 0 .. output_height-1 */ + + /* Current input scan number and number of iMCU rows completed in scan. + * These indicate the progress of the decompressor input side. + */ + int input_scan_number; /* Number of SOS markers seen so far */ + JDIMENSION input_iMCU_row; /* Number of iMCU rows completed */ + + /* The "output scan number" is the notional scan being displayed by the + * output side. The decompressor will not allow output scan/row number + * to get ahead of input scan/row, but it can fall arbitrarily far behind. + */ + int output_scan_number; /* Nominal scan number being displayed */ + JDIMENSION output_iMCU_row; /* Number of iMCU rows read */ + + /* Current progression status. coef_bits[c][i] indicates the precision + * with which component c's DCT coefficient i (in zigzag order) is known. + * It is -1 when no data has yet been received, otherwise it is the point + * transform (shift) value for the most recent scan of the coefficient + * (thus, 0 at completion of the progression). + * This pointer is NULL when reading a non-progressive file. + */ + int (*coef_bits)[DCTSIZE2]; /* -1 or current Al value for each coef */ + + /* Internal JPEG parameters --- the application usually need not look at + * these fields. Note that the decompressor output side may not use + * any parameters that can change between scans. + */ + + /* Quantization and Huffman tables are carried forward across input + * datastreams when processing abbreviated JPEG datastreams. + */ + + JQUANT_TBL * quant_tbl_ptrs[NUM_QUANT_TBLS]; + /* ptrs to coefficient quantization tables, or NULL if not defined */ + + JHUFF_TBL * dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + /* ptrs to Huffman coding tables, or NULL if not defined */ + + /* These parameters are never carried across datastreams, since they + * are given in SOF/SOS markers or defined to be reset by SOI. + */ + + int data_precision; /* bits of precision in image data */ + + jpeg_component_info * comp_info; + /* comp_info[i] describes component that appears i'th in SOF */ + + boolean progressive_mode; /* TRUE if SOFn specifies progressive mode */ + boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ + + UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ + UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */ + UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ + + unsigned int restart_interval; /* MCUs per restart interval, or 0 for no restart */ + + /* These fields record data obtained from optional markers recognized by + * the JPEG library. + */ + boolean saw_JFIF_marker; /* TRUE iff a JFIF APP0 marker was found */ + /* Data copied from JFIF marker; only valid if saw_JFIF_marker is TRUE: */ + UINT8 JFIF_major_version; /* JFIF version number */ + UINT8 JFIF_minor_version; + UINT8 density_unit; /* JFIF code for pixel size units */ + UINT16 X_density; /* Horizontal pixel density */ + UINT16 Y_density; /* Vertical pixel density */ + boolean saw_Adobe_marker; /* TRUE iff an Adobe APP14 marker was found */ + UINT8 Adobe_transform; /* Color transform code from Adobe marker */ + + boolean CCIR601_sampling; /* TRUE=first samples are cosited */ + + /* Aside from the specific data retained from APPn markers known to the + * library, the uninterpreted contents of any or all APPn and COM markers + * can be saved in a list for examination by the application. + */ + jpeg_saved_marker_ptr marker_list; /* Head of list of saved markers */ + + /* Remaining fields are known throughout decompressor, but generally + * should not be touched by a surrounding application. + */ + + /* + * These fields are computed during decompression startup + */ + int max_h_samp_factor; /* largest h_samp_factor */ + int max_v_samp_factor; /* largest v_samp_factor */ + + int min_DCT_scaled_size; /* smallest DCT_scaled_size of any component */ + + JDIMENSION total_iMCU_rows; /* # of iMCU rows in image */ + /* The coefficient controller's input and output progress is measured in + * units of "iMCU" (interleaved MCU) rows. These are the same as MCU rows + * in fully interleaved JPEG scans, but are used whether the scan is + * interleaved or not. We define an iMCU row as v_samp_factor DCT block + * rows of each component. Therefore, the IDCT output contains + * v_samp_factor*DCT_scaled_size sample rows of a component per iMCU row. + */ + + JSAMPLE * sample_range_limit; /* table for fast range-limiting */ + + /* + * These fields are valid during any one scan. + * They describe the components and MCUs actually appearing in the scan. + * Note that the decompressor output side must not use these fields. + */ + int comps_in_scan; /* # of JPEG components in this scan */ + jpeg_component_info * cur_comp_info[MAX_COMPS_IN_SCAN]; + /* *cur_comp_info[i] describes component that appears i'th in SOS */ + + JDIMENSION MCUs_per_row; /* # of MCUs across the image */ + JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ + + int blocks_in_MCU; /* # of DCT blocks per MCU */ + int MCU_membership[D_MAX_BLOCKS_IN_MCU]; + /* MCU_membership[i] is index in cur_comp_info of component owning */ + /* i'th block in an MCU */ + + int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + + /* This field is shared between entropy decoder and marker parser. + * It is either zero or the code of a JPEG marker that has been + * read from the data source, but has not yet been processed. + */ + int unread_marker; + + /* + * Links to decompression subobjects (methods, private variables of modules) + */ + struct jpeg_decomp_master * master; + struct jpeg_d_main_controller * main; + struct jpeg_d_coef_controller * coef; + struct jpeg_d_post_controller * post; + struct jpeg_input_controller * inputctl; + struct jpeg_marker_reader * marker; + struct jpeg_entropy_decoder * entropy; + struct jpeg_inverse_dct * idct; + struct jpeg_upsampler * upsample; + struct jpeg_color_deconverter * cconvert; + struct jpeg_color_quantizer * cquantize; +}; + +/* "Object" declarations for JPEG modules that may be supplied or called + * directly by the surrounding application. + * As with all objects in the JPEG library, these structs only define the + * publicly visible methods and state variables of a module. Additional + * private fields may exist after the public ones. + */ + +/* Error handler object */ + +struct jpeg_error_mgr { + /* Error exit handler: does not return to caller */ + JMETHOD(void, error_exit, (j_common_ptr cinfo)); + /* Conditionally emit a trace or warning message */ + JMETHOD(void, emit_message, (j_common_ptr cinfo, int msg_level)); + /* Routine that actually outputs a trace or error message */ + JMETHOD(void, output_message, (j_common_ptr cinfo)); + /* Format a message string for the most recent JPEG error or message */ + JMETHOD(void, format_message, (j_common_ptr cinfo, char * buffer)); +#define JMSG_LENGTH_MAX 200 /* recommended size of format_message buffer */ + /* Reset error state variables at start of a new image */ + JMETHOD(void, reset_error_mgr, (j_common_ptr cinfo)); + + /* The message ID code and any parameters are saved here. + * A message can have one string parameter or up to 8 int parameters. + */ + int msg_code; +#define JMSG_STR_PARM_MAX 80 + union { + int i[8]; + char s[JMSG_STR_PARM_MAX]; + } msg_parm; + + /* Standard state variables for error facility */ + + int trace_level; /* max msg_level that will be displayed */ + + /* For recoverable corrupt-data errors, we emit a warning message, + * but keep going unless emit_message chooses to abort. emit_message + * should count warnings in num_warnings. The surrounding application + * can check for bad data by seeing if num_warnings is nonzero at the + * end of processing. + */ + long num_warnings; /* number of corrupt-data warnings */ + + /* These fields point to the table(s) of error message strings. + * An application can change the table pointer to switch to a different + * message list (typically, to change the language in which errors are + * reported). Some applications may wish to add additional error codes + * that will be handled by the JPEG library error mechanism; the second + * table pointer is used for this purpose. + * + * First table includes all errors generated by JPEG library itself. + * Error code 0 is reserved for a "no such error string" message. + */ + const char * const * jpeg_message_table; /* Library errors */ + int last_jpeg_message; /* Table contains strings 0..last_jpeg_message */ + /* Second table can be added by application (see cjpeg/djpeg for example). + * It contains strings numbered first_addon_message..last_addon_message. + */ + const char * const * addon_message_table; /* Non-library errors */ + int first_addon_message; /* code for first string in addon table */ + int last_addon_message; /* code for last string in addon table */ +}; + +/* Progress monitor object */ + +struct jpeg_progress_mgr { + JMETHOD(void, progress_monitor, (j_common_ptr cinfo)); + + long pass_counter; /* work units completed in this pass */ + long pass_limit; /* total number of work units in this pass */ + int completed_passes; /* passes completed so far */ + int total_passes; /* total number of passes expected */ +}; + +/* Data destination object for compression */ + +struct jpeg_destination_mgr { + JOCTET * next_output_byte; /* => next byte to write in buffer */ + size_t free_in_buffer; /* # of byte spaces remaining in buffer */ + + JMETHOD(void, init_destination, (j_compress_ptr cinfo)); + JMETHOD(boolean, empty_output_buffer, (j_compress_ptr cinfo)); + JMETHOD(void, term_destination, (j_compress_ptr cinfo)); +}; + +/* Data source object for decompression */ + +struct jpeg_source_mgr { + const JOCTET * next_input_byte; /* => next byte to read from buffer */ + size_t bytes_in_buffer; /* # of bytes remaining in buffer */ + + JMETHOD(void, init_source, (j_decompress_ptr cinfo)); + JMETHOD(boolean, fill_input_buffer, (j_decompress_ptr cinfo)); + JMETHOD(void, skip_input_data, (j_decompress_ptr cinfo, long num_bytes)); + JMETHOD(boolean, resync_to_restart, (j_decompress_ptr cinfo, int desired)); + JMETHOD(void, term_source, (j_decompress_ptr cinfo)); +}; + +/* Memory manager object. + * Allocates "small" objects (a few K total), "large" objects (tens of K), + * and "really big" objects (virtual arrays with backing store if needed). + * The memory manager does not allow individual objects to be freed; rather, + * each created object is assigned to a pool, and whole pools can be freed + * at once. This is faster and more convenient than remembering exactly what + * to free, especially where malloc()/free() are not too speedy. + * NB: alloc routines never return NULL. They exit to error_exit if not + * successful. + */ + +#define JPOOL_PERMANENT 0 /* lasts until master record is destroyed */ +#define JPOOL_IMAGE 1 /* lasts until done with image/datastream */ +#define JPOOL_NUMPOOLS 2 + +typedef struct jvirt_sarray_control * jvirt_sarray_ptr; +typedef struct jvirt_barray_control * jvirt_barray_ptr; + +struct jpeg_memory_mgr { + /* Method pointers */ + JMETHOD(void *, alloc_small, (j_common_ptr cinfo, int pool_id, + size_t sizeofobject)); + JMETHOD(void FAR *, alloc_large, (j_common_ptr cinfo, int pool_id, + size_t sizeofobject)); + JMETHOD(JSAMPARRAY, alloc_sarray, (j_common_ptr cinfo, int pool_id, + JDIMENSION samplesperrow, + JDIMENSION numrows)); + JMETHOD(JBLOCKARRAY, alloc_barray, (j_common_ptr cinfo, int pool_id, + JDIMENSION blocksperrow, + JDIMENSION numrows)); + JMETHOD(jvirt_sarray_ptr, request_virt_sarray, (j_common_ptr cinfo, + int pool_id, + boolean pre_zero, + JDIMENSION samplesperrow, + JDIMENSION numrows, + JDIMENSION maxaccess)); + JMETHOD(jvirt_barray_ptr, request_virt_barray, (j_common_ptr cinfo, + int pool_id, + boolean pre_zero, + JDIMENSION blocksperrow, + JDIMENSION numrows, + JDIMENSION maxaccess)); + JMETHOD(void, realize_virt_arrays, (j_common_ptr cinfo)); + JMETHOD(JSAMPARRAY, access_virt_sarray, (j_common_ptr cinfo, + jvirt_sarray_ptr ptr, + JDIMENSION start_row, + JDIMENSION num_rows, + boolean writable)); + JMETHOD(JBLOCKARRAY, access_virt_barray, (j_common_ptr cinfo, + jvirt_barray_ptr ptr, + JDIMENSION start_row, + JDIMENSION num_rows, + boolean writable)); + JMETHOD(void, free_pool, (j_common_ptr cinfo, int pool_id)); + JMETHOD(void, self_destruct, (j_common_ptr cinfo)); + + /* Limit on memory allocation for this JPEG object. (Note that this is + * merely advisory, not a guaranteed maximum; it only affects the space + * used for virtual-array buffers.) May be changed by outer application + * after creating the JPEG object. + */ + long max_memory_to_use; + + /* Maximum allocation request accepted by alloc_large. */ + long max_alloc_chunk; +}; + +/* Routine signature for application-supplied marker processing methods. + * Need not pass marker code since it is stored in cinfo->unread_marker. + */ +typedef JMETHOD(boolean, jpeg_marker_parser_method, (j_decompress_ptr cinfo)); + +/* Declarations for routines called by application. + * The JPP macro hides prototype parameters from compilers that can't cope. + * Note JPP requires double parentheses. + */ + +#ifdef HAVE_PROTOTYPES +#define JPP(arglist) arglist +#else +#define JPP(arglist) () +#endif + +/* Short forms of external names for systems with brain-damaged linkers. + * We shorten external names to be unique in the first six letters, which + * is good enough for all known systems. + * (If your compiler itself needs names to be unique in less than 15 + * characters, you are out of luck. Get a better compiler.) + */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_std_error jStdError +#define jpeg_CreateCompress jCreaCompress +#define jpeg_CreateDecompress jCreaDecompress +#define jpeg_destroy_compress jDestCompress +#define jpeg_destroy_decompress jDestDecompress +#define jpeg_stdio_dest jStdDest +#define jpeg_stdio_src jStdSrc +#define jpeg_set_defaults jSetDefaults +#define jpeg_set_colorspace jSetColorspace +#define jpeg_default_colorspace jDefColorspace +#define jpeg_set_quality jSetQuality +#define jpeg_set_linear_quality jSetLQuality +#define jpeg_add_quant_table jAddQuantTable +#define jpeg_quality_scaling jQualityScaling +#define jpeg_simple_progression jSimProgress +#define jpeg_suppress_tables jSuppressTables +#define jpeg_alloc_quant_table jAlcQTable +#define jpeg_alloc_huff_table jAlcHTable +#define jpeg_start_compress jStrtCompress +#define jpeg_write_scanlines jWrtScanlines +#define jpeg_finish_compress jFinCompress +#define jpeg_write_raw_data jWrtRawData +#define jpeg_write_marker jWrtMarker +#define jpeg_write_m_header jWrtMHeader +#define jpeg_write_m_byte jWrtMByte +#define jpeg_write_tables jWrtTables +#define jpeg_read_header jReadHeader +#define jpeg_start_decompress jStrtDecompress +#define jpeg_read_scanlines jReadScanlines +#define jpeg_finish_decompress jFinDecompress +#define jpeg_read_raw_data jReadRawData +#define jpeg_has_multiple_scans jHasMultScn +#define jpeg_start_output jStrtOutput +#define jpeg_finish_output jFinOutput +#define jpeg_input_complete jInComplete +#define jpeg_new_colormap jNewCMap +#define jpeg_consume_input jConsumeInput +#define jpeg_calc_output_dimensions jCalcDimensions +#define jpeg_save_markers jSaveMarkers +#define jpeg_set_marker_processor jSetMarker +#define jpeg_read_coefficients jReadCoefs +#define jpeg_write_coefficients jWrtCoefs +#define jpeg_copy_critical_parameters jCopyCrit +#define jpeg_abort_compress jAbrtCompress +#define jpeg_abort_decompress jAbrtDecompress +#define jpeg_abort jAbort +#define jpeg_destroy jDestroy +#define jpeg_resync_to_restart jResyncRestart +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + +/* Default error-management setup */ +EXTERN(struct jpeg_error_mgr *) jpeg_std_error + JPP((struct jpeg_error_mgr * err)); + +/* Initialization of JPEG compression objects. + * jpeg_create_compress() and jpeg_create_decompress() are the exported + * names that applications should call. These expand to calls on + * jpeg_CreateCompress and jpeg_CreateDecompress with additional information + * passed for version mismatch checking. + * NB: you must set up the error-manager BEFORE calling jpeg_create_xxx. + */ +#define jpeg_create_compress(cinfo) \ + jpeg_CreateCompress((cinfo), JPEG_LIB_VERSION, \ + (size_t) sizeof(struct jpeg_compress_struct)) +#define jpeg_create_decompress(cinfo) \ + jpeg_CreateDecompress((cinfo), JPEG_LIB_VERSION, \ + (size_t) sizeof(struct jpeg_decompress_struct)) +EXTERN(void) jpeg_CreateCompress JPP((j_compress_ptr cinfo, + int version, size_t structsize)); +EXTERN(void) jpeg_CreateDecompress JPP((j_decompress_ptr cinfo, + int version, size_t structsize)); +/* Destruction of JPEG compression objects */ +EXTERN(void) jpeg_destroy_compress JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_destroy_decompress JPP((j_decompress_ptr cinfo)); + +/* Standard data source and destination managers: stdio streams. */ +/* Caller is responsible for opening the file before and closing after. */ +EXTERN(void) jpeg_stdio_dest JPP((j_compress_ptr cinfo, FILE * outfile)); +EXTERN(void) jpeg_stdio_src JPP((j_decompress_ptr cinfo, FILE * infile)); + +/* Default parameter setup for compression */ +EXTERN(void) jpeg_set_defaults JPP((j_compress_ptr cinfo)); +/* Compression parameter setup aids */ +EXTERN(void) jpeg_set_colorspace JPP((j_compress_ptr cinfo, + J_COLOR_SPACE colorspace)); +EXTERN(void) jpeg_default_colorspace JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_set_quality JPP((j_compress_ptr cinfo, int quality, + boolean force_baseline)); +EXTERN(void) jpeg_set_linear_quality JPP((j_compress_ptr cinfo, + int scale_factor, + boolean force_baseline)); +EXTERN(void) jpeg_add_quant_table JPP((j_compress_ptr cinfo, int which_tbl, + const unsigned int *basic_table, + int scale_factor, + boolean force_baseline)); +EXTERN(int) jpeg_quality_scaling JPP((int quality)); +EXTERN(void) jpeg_simple_progression JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_suppress_tables JPP((j_compress_ptr cinfo, + boolean suppress)); +EXTERN(JQUANT_TBL *) jpeg_alloc_quant_table JPP((j_common_ptr cinfo)); +EXTERN(JHUFF_TBL *) jpeg_alloc_huff_table JPP((j_common_ptr cinfo)); + +/* Main entry points for compression */ +EXTERN(void) jpeg_start_compress JPP((j_compress_ptr cinfo, + boolean write_all_tables)); +EXTERN(JDIMENSION) jpeg_write_scanlines JPP((j_compress_ptr cinfo, + JSAMPARRAY scanlines, + JDIMENSION num_lines)); +EXTERN(void) jpeg_finish_compress JPP((j_compress_ptr cinfo)); + +/* Replaces jpeg_write_scanlines when writing raw downsampled data. */ +EXTERN(JDIMENSION) jpeg_write_raw_data JPP((j_compress_ptr cinfo, + JSAMPIMAGE data, + JDIMENSION num_lines)); + +/* Write a special marker. See libjpeg.doc concerning safe usage. */ +EXTERN(void) jpeg_write_marker + JPP((j_compress_ptr cinfo, int marker, + const JOCTET * dataptr, unsigned int datalen)); +/* Same, but piecemeal. */ +EXTERN(void) jpeg_write_m_header + JPP((j_compress_ptr cinfo, int marker, unsigned int datalen)); +EXTERN(void) jpeg_write_m_byte + JPP((j_compress_ptr cinfo, int val)); + +/* Alternate compression function: just write an abbreviated table file */ +EXTERN(void) jpeg_write_tables JPP((j_compress_ptr cinfo)); + +/* Decompression startup: read start of JPEG datastream to see what's there */ +EXTERN(int) jpeg_read_header JPP((j_decompress_ptr cinfo, + boolean require_image)); +/* Return value is one of: */ +#define JPEG_SUSPENDED 0 /* Suspended due to lack of input data */ +#define JPEG_HEADER_OK 1 /* Found valid image datastream */ +#define JPEG_HEADER_TABLES_ONLY 2 /* Found valid table-specs-only datastream */ +/* If you pass require_image = TRUE (normal case), you need not check for + * a TABLES_ONLY return code; an abbreviated file will cause an error exit. + * JPEG_SUSPENDED is only possible if you use a data source module that can + * give a suspension return (the stdio source module doesn't). + */ + +/* Main entry points for decompression */ +EXTERN(boolean) jpeg_start_decompress JPP((j_decompress_ptr cinfo)); +EXTERN(JDIMENSION) jpeg_read_scanlines JPP((j_decompress_ptr cinfo, + JSAMPARRAY scanlines, + JDIMENSION max_lines)); +EXTERN(boolean) jpeg_finish_decompress JPP((j_decompress_ptr cinfo)); + +/* Replaces jpeg_read_scanlines when reading raw downsampled data. */ +EXTERN(JDIMENSION) jpeg_read_raw_data JPP((j_decompress_ptr cinfo, + JSAMPIMAGE data, + JDIMENSION max_lines)); + +/* Additional entry points for buffered-image mode. */ +EXTERN(boolean) jpeg_has_multiple_scans JPP((j_decompress_ptr cinfo)); +EXTERN(boolean) jpeg_start_output JPP((j_decompress_ptr cinfo, + int scan_number)); +EXTERN(boolean) jpeg_finish_output JPP((j_decompress_ptr cinfo)); +EXTERN(boolean) jpeg_input_complete JPP((j_decompress_ptr cinfo)); +EXTERN(void) jpeg_new_colormap JPP((j_decompress_ptr cinfo)); +EXTERN(int) jpeg_consume_input JPP((j_decompress_ptr cinfo)); +/* Return value is one of: */ +/* #define JPEG_SUSPENDED 0 Suspended due to lack of input data */ +#define JPEG_REACHED_SOS 1 /* Reached start of new scan */ +#define JPEG_REACHED_EOI 2 /* Reached end of image */ +#define JPEG_ROW_COMPLETED 3 /* Completed one iMCU row */ +#define JPEG_SCAN_COMPLETED 4 /* Completed last iMCU row of a scan */ + +/* Precalculate output dimensions for current decompression parameters. */ +EXTERN(void) jpeg_calc_output_dimensions JPP((j_decompress_ptr cinfo)); + +/* Control saving of COM and APPn markers into marker_list. */ +EXTERN(void) jpeg_save_markers + JPP((j_decompress_ptr cinfo, int marker_code, + unsigned int length_limit)); + +/* Install a special processing method for COM or APPn markers. */ +EXTERN(void) jpeg_set_marker_processor + JPP((j_decompress_ptr cinfo, int marker_code, + jpeg_marker_parser_method routine)); + +/* Read or write raw DCT coefficients --- useful for lossless transcoding. */ +EXTERN(jvirt_barray_ptr *) jpeg_read_coefficients JPP((j_decompress_ptr cinfo)); +EXTERN(void) jpeg_write_coefficients JPP((j_compress_ptr cinfo, + jvirt_barray_ptr * coef_arrays)); +EXTERN(void) jpeg_copy_critical_parameters JPP((j_decompress_ptr srcinfo, + j_compress_ptr dstinfo)); + +/* If you choose to abort compression or decompression before completing + * jpeg_finish_(de)compress, then you need to clean up to release memory, + * temporary files, etc. You can just call jpeg_destroy_(de)compress + * if you're done with the JPEG object, but if you want to clean it up and + * reuse it, call this: + */ +EXTERN(void) jpeg_abort_compress JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_abort_decompress JPP((j_decompress_ptr cinfo)); + +/* Generic versions of jpeg_abort and jpeg_destroy that work on either + * flavor of JPEG object. These may be more convenient in some places. + */ +EXTERN(void) jpeg_abort JPP((j_common_ptr cinfo)); +EXTERN(void) jpeg_destroy JPP((j_common_ptr cinfo)); + +/* Default restart-marker-resync procedure for use by data source modules */ +EXTERN(boolean) jpeg_resync_to_restart JPP((j_decompress_ptr cinfo, + int desired)); + +/* These marker codes are exported since applications and data source modules + * are likely to want to use them. + */ + +#define JPEG_RST0 0xD0 /* RST0 marker code */ +#define JPEG_EOI 0xD9 /* EOI marker code */ +#define JPEG_APP0 0xE0 /* APP0 marker code */ +#define JPEG_COM 0xFE /* COM marker code */ + +/* If we have a brain-damaged compiler that emits warnings (or worse, errors) + * for structure definitions that are never filled in, keep it quiet by + * supplying dummy definitions for the various substructures. + */ + +#ifdef INCOMPLETE_TYPES_BROKEN +#ifndef JPEG_INTERNALS /* will be defined in jpegint.h */ +struct jvirt_sarray_control { long dummy; }; +struct jvirt_barray_control { long dummy; }; +struct jpeg_comp_master { long dummy; }; +struct jpeg_c_main_controller { long dummy; }; +struct jpeg_c_prep_controller { long dummy; }; +struct jpeg_c_coef_controller { long dummy; }; +struct jpeg_marker_writer { long dummy; }; +struct jpeg_color_converter { long dummy; }; +struct jpeg_downsampler { long dummy; }; +struct jpeg_forward_dct { long dummy; }; +struct jpeg_entropy_encoder { long dummy; }; +struct jpeg_decomp_master { long dummy; }; +struct jpeg_d_main_controller { long dummy; }; +struct jpeg_d_coef_controller { long dummy; }; +struct jpeg_d_post_controller { long dummy; }; +struct jpeg_input_controller { long dummy; }; +struct jpeg_marker_reader { long dummy; }; +struct jpeg_entropy_decoder { long dummy; }; +struct jpeg_inverse_dct { long dummy; }; +struct jpeg_upsampler { long dummy; }; +struct jpeg_color_deconverter { long dummy; }; +struct jpeg_color_quantizer { long dummy; }; +#endif /* JPEG_INTERNALS */ +#endif /* INCOMPLETE_TYPES_BROKEN */ + +/* + * The JPEG library modules define JPEG_INTERNALS before including this file. + * The internal structure declarations are read only when that is true. + * Applications using the library should not include jpegint.h, but may wish + * to include jerror.h. + */ + +#ifdef JPEG_INTERNALS + +/********* Start of inlined file: jpegint.h *********/ +/* Declarations for both compression & decompression */ + +typedef enum { /* Operating modes for buffer controllers */ + JBUF_PASS_THRU, /* Plain stripwise operation */ + /* Remaining modes require a full-image buffer to have been created */ + JBUF_SAVE_SOURCE, /* Run source subobject only, save output */ + JBUF_CRANK_DEST, /* Run dest subobject only, using saved data */ + JBUF_SAVE_AND_PASS /* Run both subobjects, save output */ +} J_BUF_MODE; + +/* Values of global_state field (jdapi.c has some dependencies on ordering!) */ +#define CSTATE_START 100 /* after create_compress */ +#define CSTATE_SCANNING 101 /* start_compress done, write_scanlines OK */ +#define CSTATE_RAW_OK 102 /* start_compress done, write_raw_data OK */ +#define CSTATE_WRCOEFS 103 /* jpeg_write_coefficients done */ +#define DSTATE_START 200 /* after create_decompress */ +#define DSTATE_INHEADER 201 /* reading header markers, no SOS yet */ +#define DSTATE_READY 202 /* found SOS, ready for start_decompress */ +#define DSTATE_PRELOAD 203 /* reading multiscan file in start_decompress*/ +#define DSTATE_PRESCAN 204 /* performing dummy pass for 2-pass quant */ +#define DSTATE_SCANNING 205 /* start_decompress done, read_scanlines OK */ +#define DSTATE_RAW_OK 206 /* start_decompress done, read_raw_data OK */ +#define DSTATE_BUFIMAGE 207 /* expecting jpeg_start_output */ +#define DSTATE_BUFPOST 208 /* looking for SOS/EOI in jpeg_finish_output */ +#define DSTATE_RDCOEFS 209 /* reading file in jpeg_read_coefficients */ +#define DSTATE_STOPPING 210 /* looking for EOI in jpeg_finish_decompress */ + +/* Declarations for compression modules */ + +/* Master control module */ +struct jpeg_comp_master { + JMETHOD(void, prepare_for_pass, (j_compress_ptr cinfo)); + JMETHOD(void, pass_startup, (j_compress_ptr cinfo)); + JMETHOD(void, finish_pass, (j_compress_ptr cinfo)); + + /* State variables made visible to other modules */ + boolean call_pass_startup; /* True if pass_startup must be called */ + boolean is_last_pass; /* True during last pass */ +}; + +/* Main buffer control (downsampled-data buffer) */ +struct jpeg_c_main_controller { + JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(void, process_data, (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JDIMENSION *in_row_ctr, + JDIMENSION in_rows_avail)); +}; + +/* Compression preprocessing (downsampling input buffer control) */ +struct jpeg_c_prep_controller { + JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(void, pre_process_data, (j_compress_ptr cinfo, + JSAMPARRAY input_buf, + JDIMENSION *in_row_ctr, + JDIMENSION in_rows_avail, + JSAMPIMAGE output_buf, + JDIMENSION *out_row_group_ctr, + JDIMENSION out_row_groups_avail)); +}; + +/* Coefficient buffer control */ +struct jpeg_c_coef_controller { + JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(boolean, compress_data, (j_compress_ptr cinfo, + JSAMPIMAGE input_buf)); +}; + +/* Colorspace conversion */ +struct jpeg_color_converter { + JMETHOD(void, start_pass, (j_compress_ptr cinfo)); + JMETHOD(void, color_convert, (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows)); +}; + +/* Downsampling */ +struct jpeg_downsampler { + JMETHOD(void, start_pass, (j_compress_ptr cinfo)); + JMETHOD(void, downsample, (j_compress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION in_row_index, + JSAMPIMAGE output_buf, + JDIMENSION out_row_group_index)); + + boolean need_context_rows; /* TRUE if need rows above & below */ +}; + +/* Forward DCT (also controls coefficient quantization) */ +struct jpeg_forward_dct { + JMETHOD(void, start_pass, (j_compress_ptr cinfo)); + /* perhaps this should be an array??? */ + JMETHOD(void, forward_DCT, (j_compress_ptr cinfo, + jpeg_component_info * compptr, + JSAMPARRAY sample_data, JBLOCKROW coef_blocks, + JDIMENSION start_row, JDIMENSION start_col, + JDIMENSION num_blocks)); +}; + +/* Entropy encoding */ +struct jpeg_entropy_encoder { + JMETHOD(void, start_pass, (j_compress_ptr cinfo, boolean gather_statistics)); + JMETHOD(boolean, encode_mcu, (j_compress_ptr cinfo, JBLOCKROW *MCU_data)); + JMETHOD(void, finish_pass, (j_compress_ptr cinfo)); +}; + +/* Marker writing */ +struct jpeg_marker_writer { + JMETHOD(void, write_file_header, (j_compress_ptr cinfo)); + JMETHOD(void, write_frame_header, (j_compress_ptr cinfo)); + JMETHOD(void, write_scan_header, (j_compress_ptr cinfo)); + JMETHOD(void, write_file_trailer, (j_compress_ptr cinfo)); + JMETHOD(void, write_tables_only, (j_compress_ptr cinfo)); + /* These routines are exported to allow insertion of extra markers */ + /* Probably only COM and APPn markers should be written this way */ + JMETHOD(void, write_marker_header, (j_compress_ptr cinfo, int marker, + unsigned int datalen)); + JMETHOD(void, write_marker_byte, (j_compress_ptr cinfo, int val)); +}; + +/* Declarations for decompression modules */ + +/* Master control module */ +struct jpeg_decomp_master { + JMETHOD(void, prepare_for_output_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, finish_output_pass, (j_decompress_ptr cinfo)); + + /* State variables made visible to other modules */ + boolean is_dummy_pass; /* True during 1st pass for 2-pass quant */ +}; + +/* Input control module */ +struct jpeg_input_controller { + JMETHOD(int, consume_input, (j_decompress_ptr cinfo)); + JMETHOD(void, reset_input_controller, (j_decompress_ptr cinfo)); + JMETHOD(void, start_input_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, finish_input_pass, (j_decompress_ptr cinfo)); + + /* State variables made visible to other modules */ + boolean has_multiple_scans; /* True if file has multiple scans */ + boolean eoi_reached; /* True when EOI has been consumed */ +}; + +/* Main buffer control (downsampled-data buffer) */ +struct jpeg_d_main_controller { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(void, process_data, (j_decompress_ptr cinfo, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); +}; + +/* Coefficient buffer control */ +struct jpeg_d_coef_controller { + JMETHOD(void, start_input_pass, (j_decompress_ptr cinfo)); + JMETHOD(int, consume_data, (j_decompress_ptr cinfo)); + JMETHOD(void, start_output_pass, (j_decompress_ptr cinfo)); + JMETHOD(int, decompress_data, (j_decompress_ptr cinfo, + JSAMPIMAGE output_buf)); + /* Pointer to array of coefficient virtual arrays, or NULL if none */ + jvirt_barray_ptr *coef_arrays; +}; + +/* Decompression postprocessing (color quantization buffer control) */ +struct jpeg_d_post_controller { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(void, post_process_data, (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, + JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, + JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); +}; + +/* Marker reading & parsing */ +struct jpeg_marker_reader { + JMETHOD(void, reset_marker_reader, (j_decompress_ptr cinfo)); + /* Read markers until SOS or EOI. + * Returns same codes as are defined for jpeg_consume_input: + * JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI. + */ + JMETHOD(int, read_markers, (j_decompress_ptr cinfo)); + /* Read a restart marker --- exported for use by entropy decoder only */ + jpeg_marker_parser_method read_restart_marker; + + /* State of marker reader --- nominally internal, but applications + * supplying COM or APPn handlers might like to know the state. + */ + boolean saw_SOI; /* found SOI? */ + boolean saw_SOF; /* found SOF? */ + int next_restart_num; /* next restart number expected (0-7) */ + unsigned int discarded_bytes; /* # of bytes skipped looking for a marker */ +}; + +/* Entropy decoding */ +struct jpeg_entropy_decoder { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + JMETHOD(boolean, decode_mcu, (j_decompress_ptr cinfo, + JBLOCKROW *MCU_data)); + + /* This is here to share code between baseline and progressive decoders; */ + /* other modules probably should not use it */ + boolean insufficient_data; /* set TRUE after emitting warning */ +}; + +/* Inverse DCT (also performs dequantization) */ +typedef JMETHOD(void, inverse_DCT_method_ptr, + (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col)); + +struct jpeg_inverse_dct { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + /* It is useful to allow each component to have a separate IDCT method. */ + inverse_DCT_method_ptr inverse_DCT[MAX_COMPONENTS]; +}; + +/* Upsampling (note that upsampler must also call color converter) */ +struct jpeg_upsampler { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, upsample, (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, + JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, + JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); + + boolean need_context_rows; /* TRUE if need rows above & below */ +}; + +/* Colorspace conversion */ +struct jpeg_color_deconverter { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, color_convert, (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows)); +}; + +/* Color quantization or color precision reduction */ +struct jpeg_color_quantizer { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo, boolean is_pre_scan)); + JMETHOD(void, color_quantize, (j_decompress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPARRAY output_buf, + int num_rows)); + JMETHOD(void, finish_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, new_color_map, (j_decompress_ptr cinfo)); +}; + +/* Miscellaneous useful macros */ + +#undef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#undef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) + +/* We assume that right shift corresponds to signed division by 2 with + * rounding towards minus infinity. This is correct for typical "arithmetic + * shift" instructions that shift in copies of the sign bit. But some + * C compilers implement >> with an unsigned shift. For these machines you + * must define RIGHT_SHIFT_IS_UNSIGNED. + * RIGHT_SHIFT provides a proper signed right shift of an INT32 quantity. + * It is only applied with constant shift counts. SHIFT_TEMPS must be + * included in the variables of any routine using RIGHT_SHIFT. + */ + +#ifdef RIGHT_SHIFT_IS_UNSIGNED +#define SHIFT_TEMPS INT32 shift_temp; +#define RIGHT_SHIFT(x,shft) \ + ((shift_temp = (x)) < 0 ? \ + (shift_temp >> (shft)) | ((~((INT32) 0)) << (32-(shft))) : \ + (shift_temp >> (shft))) +#else +#define SHIFT_TEMPS +#define RIGHT_SHIFT(x,shft) ((x) >> (shft)) +#endif + +/* Short forms of external names for systems with brain-damaged linkers. */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jinit_compress_master jICompress +#define jinit_c_master_control jICMaster +#define jinit_c_main_controller jICMainC +#define jinit_c_prep_controller jICPrepC +#define jinit_c_coef_controller jICCoefC +#define jinit_color_converter jICColor +#define jinit_downsampler jIDownsampler +#define jinit_forward_dct jIFDCT +#define jinit_huff_encoder jIHEncoder +#define jinit_phuff_encoder jIPHEncoder +#define jinit_marker_writer jIMWriter +#define jinit_master_decompress jIDMaster +#define jinit_d_main_controller jIDMainC +#define jinit_d_coef_controller jIDCoefC +#define jinit_d_post_controller jIDPostC +#define jinit_input_controller jIInCtlr +#define jinit_marker_reader jIMReader +#define jinit_huff_decoder jIHDecoder +#define jinit_phuff_decoder jIPHDecoder +#define jinit_inverse_dct jIIDCT +#define jinit_upsampler jIUpsampler +#define jinit_color_deconverter jIDColor +#define jinit_1pass_quantizer jI1Quant +#define jinit_2pass_quantizer jI2Quant +#define jinit_merged_upsampler jIMUpsampler +#define jinit_memory_mgr jIMemMgr +#define jdiv_round_up jDivRound +#define jround_up jRound +#define jcopy_sample_rows jCopySamples +#define jcopy_block_row jCopyBlocks +#define jzero_far jZeroFar +#define jpeg_zigzag_order jZIGTable +#define jpeg_natural_order jZAGTable +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + +/* Compression module initialization routines */ +EXTERN(void) jinit_compress_master JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_c_master_control JPP((j_compress_ptr cinfo, + boolean transcode_only)); +EXTERN(void) jinit_c_main_controller JPP((j_compress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_c_prep_controller JPP((j_compress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_c_coef_controller JPP((j_compress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_color_converter JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_downsampler JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_forward_dct JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_huff_encoder JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_phuff_encoder JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_marker_writer JPP((j_compress_ptr cinfo)); +/* Decompression module initialization routines */ +EXTERN(void) jinit_master_decompress JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_d_main_controller JPP((j_decompress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_d_coef_controller JPP((j_decompress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_d_post_controller JPP((j_decompress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_input_controller JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_marker_reader JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_huff_decoder JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_phuff_decoder JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_inverse_dct JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_upsampler JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_color_deconverter JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_1pass_quantizer JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_2pass_quantizer JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_merged_upsampler JPP((j_decompress_ptr cinfo)); +/* Memory manager initialization */ +EXTERN(void) jinit_memory_mgr JPP((j_common_ptr cinfo)); + +/* Utility routines in jutils.c */ +EXTERN(long) jdiv_round_up JPP((long a, long b)); +EXTERN(long) jround_up JPP((long a, long b)); +EXTERN(void) jcopy_sample_rows JPP((JSAMPARRAY input_array, int source_row, + JSAMPARRAY output_array, int dest_row, + int num_rows, JDIMENSION num_cols)); +EXTERN(void) jcopy_block_row JPP((JBLOCKROW input_row, JBLOCKROW output_row, + JDIMENSION num_blocks)); +EXTERN(void) jzero_far JPP((void FAR * target, size_t bytestozero)); +/* Constant tables in jutils.c */ +#if 0 /* This table is not actually needed in v6a */ +extern const int jpeg_zigzag_order[]; /* natural coef order to zigzag order */ +#endif +extern const int jpeg_natural_order[]; /* zigzag coef order to natural order */ + +/* Suppress undefined-structure complaints if necessary. */ + +#ifdef INCOMPLETE_TYPES_BROKEN +#ifndef AM_MEMORY_MANAGER /* only jmemmgr.c defines these */ +struct jvirt_sarray_control { long dummy; }; +struct jvirt_barray_control { long dummy; }; +#endif +#endif /* INCOMPLETE_TYPES_BROKEN */ +/********* End of inlined file: jpegint.h *********/ + + /* fetch private declarations */ + +/********* Start of inlined file: jerror.h *********/ +/* + * To define the enum list of message codes, include this file without + * defining macro JMESSAGE. To create a message string table, include it + * again with a suitable JMESSAGE definition (see jerror.c for an example). + */ +#ifndef JMESSAGE +#ifndef JERROR_H +/* First time through, define the enum list */ +#define JMAKE_ENUM_LIST +#else +/* Repeated inclusions of this file are no-ops unless JMESSAGE is defined */ +#define JMESSAGE(code,string) +#endif /* JERROR_H */ +#endif /* JMESSAGE */ + +#ifdef JMAKE_ENUM_LIST + +typedef enum { + +#define JMESSAGE(code,string) code , + +#endif /* JMAKE_ENUM_LIST */ + +JMESSAGE(JMSG_NOMESSAGE, "Bogus message code %d") /* Must be first entry! */ + +/* For maintenance convenience, list is alphabetical by message code name */ +JMESSAGE(JERR_ARITH_NOTIMPL, + "Sorry, there are legal restrictions on arithmetic coding") +JMESSAGE(JERR_BAD_ALIGN_TYPE, "ALIGN_TYPE is wrong, please fix") +JMESSAGE(JERR_BAD_ALLOC_CHUNK, "MAX_ALLOC_CHUNK is wrong, please fix") +JMESSAGE(JERR_BAD_BUFFER_MODE, "Bogus buffer control mode") +JMESSAGE(JERR_BAD_COMPONENT_ID, "Invalid component ID %d in SOS") +JMESSAGE(JERR_BAD_DCT_COEF, "DCT coefficient out of range") +JMESSAGE(JERR_BAD_DCTSIZE, "IDCT output block size %d not supported") +JMESSAGE(JERR_BAD_HUFF_TABLE, "Bogus Huffman table definition") +JMESSAGE(JERR_BAD_IN_COLORSPACE, "Bogus input colorspace") +JMESSAGE(JERR_BAD_J_COLORSPACE, "Bogus JPEG colorspace") +JMESSAGE(JERR_BAD_LENGTH, "Bogus marker length") +JMESSAGE(JERR_BAD_LIB_VERSION, + "Wrong JPEG library version: library is %d, caller expects %d") +JMESSAGE(JERR_BAD_MCU_SIZE, "Sampling factors too large for interleaved scan") +JMESSAGE(JERR_BAD_POOL_ID, "Invalid memory pool code %d") +JMESSAGE(JERR_BAD_PRECISION, "Unsupported JPEG data precision %d") +JMESSAGE(JERR_BAD_PROGRESSION, + "Invalid progressive parameters Ss=%d Se=%d Ah=%d Al=%d") +JMESSAGE(JERR_BAD_PROG_SCRIPT, + "Invalid progressive parameters at scan script entry %d") +JMESSAGE(JERR_BAD_SAMPLING, "Bogus sampling factors") +JMESSAGE(JERR_BAD_SCAN_SCRIPT, "Invalid scan script at entry %d") +JMESSAGE(JERR_BAD_STATE, "Improper call to JPEG library in state %d") +JMESSAGE(JERR_BAD_STRUCT_SIZE, + "JPEG parameter struct mismatch: library thinks size is %u, caller expects %u") +JMESSAGE(JERR_BAD_VIRTUAL_ACCESS, "Bogus virtual array access") +JMESSAGE(JERR_BUFFER_SIZE, "Buffer passed to JPEG library is too small") +JMESSAGE(JERR_CANT_SUSPEND, "Suspension not allowed here") +JMESSAGE(JERR_CCIR601_NOTIMPL, "CCIR601 sampling not implemented yet") +JMESSAGE(JERR_COMPONENT_COUNT, "Too many color components: %d, max %d") +JMESSAGE(JERR_CONVERSION_NOTIMPL, "Unsupported color conversion request") +JMESSAGE(JERR_DAC_INDEX, "Bogus DAC index %d") +JMESSAGE(JERR_DAC_VALUE, "Bogus DAC value 0x%x") +JMESSAGE(JERR_DHT_INDEX, "Bogus DHT index %d") +JMESSAGE(JERR_DQT_INDEX, "Bogus DQT index %d") +JMESSAGE(JERR_EMPTY_IMAGE, "Empty JPEG image (DNL not supported)") +JMESSAGE(JERR_EMS_READ, "Read from EMS failed") +JMESSAGE(JERR_EMS_WRITE, "Write to EMS failed") +JMESSAGE(JERR_EOI_EXPECTED, "Didn't expect more than one scan") +JMESSAGE(JERR_FILE_READ, "Input file read error") +JMESSAGE(JERR_FILE_WRITE, "Output file write error --- out of disk space?") +JMESSAGE(JERR_FRACT_SAMPLE_NOTIMPL, "Fractional sampling not implemented yet") +JMESSAGE(JERR_HUFF_CLEN_OVERFLOW, "Huffman code size table overflow") +JMESSAGE(JERR_HUFF_MISSING_CODE, "Missing Huffman code table entry") +JMESSAGE(JERR_IMAGE_TOO_BIG, "Maximum supported image dimension is %u pixels") +JMESSAGE(JERR_INPUT_EMPTY, "Empty input file") +JMESSAGE(JERR_INPUT_EOF, "Premature end of input file") +JMESSAGE(JERR_MISMATCHED_QUANT_TABLE, + "Cannot transcode due to multiple use of quantization table %d") +JMESSAGE(JERR_MISSING_DATA, "Scan script does not transmit all data") +JMESSAGE(JERR_MODE_CHANGE, "Invalid color quantization mode change") +JMESSAGE(JERR_NOTIMPL, "Not implemented yet") +JMESSAGE(JERR_NOT_COMPILED, "Requested feature was omitted at compile time") +JMESSAGE(JERR_NO_BACKING_STORE, "Backing store not supported") +JMESSAGE(JERR_NO_HUFF_TABLE, "Huffman table 0x%02x was not defined") +JMESSAGE(JERR_NO_IMAGE, "JPEG datastream contains no image") +JMESSAGE(JERR_NO_QUANT_TABLE, "Quantization table 0x%02x was not defined") +JMESSAGE(JERR_NO_SOI, "Not a JPEG file: starts with 0x%02x 0x%02x") +JMESSAGE(JERR_OUT_OF_MEMORY, "Insufficient memory (case %d)") +JMESSAGE(JERR_QUANT_COMPONENTS, + "Cannot quantize more than %d color components") +JMESSAGE(JERR_QUANT_FEW_COLORS, "Cannot quantize to fewer than %d colors") +JMESSAGE(JERR_QUANT_MANY_COLORS, "Cannot quantize to more than %d colors") +JMESSAGE(JERR_SOF_DUPLICATE, "Invalid JPEG file structure: two SOF markers") +JMESSAGE(JERR_SOF_NO_SOS, "Invalid JPEG file structure: missing SOS marker") +JMESSAGE(JERR_SOF_UNSUPPORTED, "Unsupported JPEG process: SOF type 0x%02x") +JMESSAGE(JERR_SOI_DUPLICATE, "Invalid JPEG file structure: two SOI markers") +JMESSAGE(JERR_SOS_NO_SOF, "Invalid JPEG file structure: SOS before SOF") +JMESSAGE(JERR_TFILE_CREATE, "Failed to create temporary file %s") +JMESSAGE(JERR_TFILE_READ, "Read failed on temporary file") +JMESSAGE(JERR_TFILE_SEEK, "Seek failed on temporary file") +JMESSAGE(JERR_TFILE_WRITE, + "Write failed on temporary file --- out of disk space?") +JMESSAGE(JERR_TOO_LITTLE_DATA, "Application transferred too few scanlines") +JMESSAGE(JERR_UNKNOWN_MARKER, "Unsupported marker type 0x%02x") +JMESSAGE(JERR_VIRTUAL_BUG, "Virtual array controller messed up") +JMESSAGE(JERR_WIDTH_OVERFLOW, "Image too wide for this implementation") +JMESSAGE(JERR_XMS_READ, "Read from XMS failed") +JMESSAGE(JERR_XMS_WRITE, "Write to XMS failed") +JMESSAGE(JMSG_COPYRIGHT, JCOPYRIGHT) +JMESSAGE(JMSG_VERSION, JVERSION) +JMESSAGE(JTRC_16BIT_TABLES, + "Caution: quantization tables are too coarse for baseline JPEG") +JMESSAGE(JTRC_ADOBE, + "Adobe APP14 marker: version %d, flags 0x%04x 0x%04x, transform %d") +JMESSAGE(JTRC_APP0, "Unknown APP0 marker (not JFIF), length %u") +JMESSAGE(JTRC_APP14, "Unknown APP14 marker (not Adobe), length %u") +JMESSAGE(JTRC_DAC, "Define Arithmetic Table 0x%02x: 0x%02x") +JMESSAGE(JTRC_DHT, "Define Huffman Table 0x%02x") +JMESSAGE(JTRC_DQT, "Define Quantization Table %d precision %d") +JMESSAGE(JTRC_DRI, "Define Restart Interval %u") +JMESSAGE(JTRC_EMS_CLOSE, "Freed EMS handle %u") +JMESSAGE(JTRC_EMS_OPEN, "Obtained EMS handle %u") +JMESSAGE(JTRC_EOI, "End Of Image") +JMESSAGE(JTRC_HUFFBITS, " %3d %3d %3d %3d %3d %3d %3d %3d") +JMESSAGE(JTRC_JFIF, "JFIF APP0 marker: version %d.%02d, density %dx%d %d") +JMESSAGE(JTRC_JFIF_BADTHUMBNAILSIZE, + "Warning: thumbnail image size does not match data length %u") +JMESSAGE(JTRC_JFIF_EXTENSION, + "JFIF extension marker: type 0x%02x, length %u") +JMESSAGE(JTRC_JFIF_THUMBNAIL, " with %d x %d thumbnail image") +JMESSAGE(JTRC_MISC_MARKER, "Miscellaneous marker 0x%02x, length %u") +JMESSAGE(JTRC_PARMLESS_MARKER, "Unexpected marker 0x%02x") +JMESSAGE(JTRC_QUANTVALS, " %4u %4u %4u %4u %4u %4u %4u %4u") +JMESSAGE(JTRC_QUANT_3_NCOLORS, "Quantizing to %d = %d*%d*%d colors") +JMESSAGE(JTRC_QUANT_NCOLORS, "Quantizing to %d colors") +JMESSAGE(JTRC_QUANT_SELECTED, "Selected %d colors for quantization") +JMESSAGE(JTRC_RECOVERY_ACTION, "At marker 0x%02x, recovery action %d") +JMESSAGE(JTRC_RST, "RST%d") +JMESSAGE(JTRC_SMOOTH_NOTIMPL, + "Smoothing not supported with nonstandard sampling ratios") +JMESSAGE(JTRC_SOF, "Start Of Frame 0x%02x: width=%u, height=%u, components=%d") +JMESSAGE(JTRC_SOF_COMPONENT, " Component %d: %dhx%dv q=%d") +JMESSAGE(JTRC_SOI, "Start of Image") +JMESSAGE(JTRC_SOS, "Start Of Scan: %d components") +JMESSAGE(JTRC_SOS_COMPONENT, " Component %d: dc=%d ac=%d") +JMESSAGE(JTRC_SOS_PARAMS, " Ss=%d, Se=%d, Ah=%d, Al=%d") +JMESSAGE(JTRC_TFILE_CLOSE, "Closed temporary file %s") +JMESSAGE(JTRC_TFILE_OPEN, "Opened temporary file %s") +JMESSAGE(JTRC_THUMB_JPEG, + "JFIF extension marker: JPEG-compressed thumbnail image, length %u") +JMESSAGE(JTRC_THUMB_PALETTE, + "JFIF extension marker: palette thumbnail image, length %u") +JMESSAGE(JTRC_THUMB_RGB, + "JFIF extension marker: RGB thumbnail image, length %u") +JMESSAGE(JTRC_UNKNOWN_IDS, + "Unrecognized component IDs %d %d %d, assuming YCbCr") +JMESSAGE(JTRC_XMS_CLOSE, "Freed XMS handle %u") +JMESSAGE(JTRC_XMS_OPEN, "Obtained XMS handle %u") +JMESSAGE(JWRN_ADOBE_XFORM, "Unknown Adobe color transform code %d") +JMESSAGE(JWRN_BOGUS_PROGRESSION, + "Inconsistent progression sequence for component %d coefficient %d") +JMESSAGE(JWRN_EXTRANEOUS_DATA, + "Corrupt JPEG data: %u extraneous bytes before marker 0x%02x") +JMESSAGE(JWRN_HIT_MARKER, "Corrupt JPEG data: premature end of data segment") +JMESSAGE(JWRN_HUFF_BAD_CODE, "Corrupt JPEG data: bad Huffman code") +JMESSAGE(JWRN_JFIF_MAJOR, "Warning: unknown JFIF revision number %d.%02d") +JMESSAGE(JWRN_JPEG_EOF, "Premature end of JPEG file") +JMESSAGE(JWRN_MUST_RESYNC, + "Corrupt JPEG data: found marker 0x%02x instead of RST%d") +JMESSAGE(JWRN_NOT_SEQUENTIAL, "Invalid SOS parameters for sequential JPEG") +JMESSAGE(JWRN_TOO_MUCH_DATA, "Application transferred too many scanlines") + +#ifdef JMAKE_ENUM_LIST + + JMSG_LASTMSGCODE +} J_MESSAGE_CODE; + +#undef JMAKE_ENUM_LIST +#endif /* JMAKE_ENUM_LIST */ + +/* Zap JMESSAGE macro so that future re-inclusions do nothing by default */ +#undef JMESSAGE + +#ifndef JERROR_H +#define JERROR_H + +/* Macros to simplify using the error and trace message stuff */ +/* The first parameter is either type of cinfo pointer */ + +/* Fatal errors (print message and exit) */ +#define ERREXIT(cinfo,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT1(cinfo,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT2(cinfo,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT3(cinfo,code,p1,p2,p3) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (cinfo)->err->msg_parm.i[2] = (p3), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT4(cinfo,code,p1,p2,p3,p4) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (cinfo)->err->msg_parm.i[2] = (p3), \ + (cinfo)->err->msg_parm.i[3] = (p4), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXITS(cinfo,code,str) \ + ((cinfo)->err->msg_code = (code), \ + strncpy((cinfo)->err->msg_parm.s, (str), JMSG_STR_PARM_MAX), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) + +#define MAKESTMT(stuff) do { stuff } while (0) + +/* Nonfatal errors (we can keep going, but the data is probably corrupt) */ +#define WARNMS(cinfo,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) +#define WARNMS1(cinfo,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) +#define WARNMS2(cinfo,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) + +/* Informational/debugging messages */ +#define TRACEMS(cinfo,lvl,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS1(cinfo,lvl,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS2(cinfo,lvl,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS3(cinfo,lvl,code,p1,p2,p3) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMS4(cinfo,lvl,code,p1,p2,p3,p4) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMS5(cinfo,lvl,code,p1,p2,p3,p4,p5) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + _mp[4] = (p5); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMS8(cinfo,lvl,code,p1,p2,p3,p4,p5,p6,p7,p8) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + _mp[4] = (p5); _mp[5] = (p6); _mp[6] = (p7); _mp[7] = (p8); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMSS(cinfo,lvl,code,str) \ + ((cinfo)->err->msg_code = (code), \ + strncpy((cinfo)->err->msg_parm.s, (str), JMSG_STR_PARM_MAX), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) + +#endif /* JERROR_H */ +/********* End of inlined file: jerror.h *********/ + + /* fetch error codes too */ +#endif + +#endif /* JPEGLIB_H */ +/********* End of inlined file: jpeglib.h *********/ + +/********* Start of inlined file: jcapimin.c *********/ +#define JPEG_INTERNALS + +/********* Start of inlined file: jinclude.h *********/ +/* Include auto-config file to find out which system include files we need. */ + +#ifndef __jinclude_h__ +#define __jinclude_h__ + +/********* Start of inlined file: jconfig.h *********/ +/* see jconfig.doc for explanations */ + +// disable all the warnings under MSVC +#ifdef _MSC_VER +#pragma warning (disable: 4996 4267 4100 4127 4702 4244) +#endif + +#ifdef __BORLANDC__ +#pragma warn -8057 +#pragma warn -8019 +#pragma warn -8004 +#pragma warn -8008 +#endif + +#define HAVE_PROTOTYPES +#define HAVE_UNSIGNED_CHAR +#define HAVE_UNSIGNED_SHORT +/* #define void char */ +/* #define const */ +#undef CHAR_IS_UNSIGNED +#define HAVE_STDDEF_H +#define HAVE_STDLIB_H +#undef NEED_BSD_STRINGS +#undef NEED_SYS_TYPES_H +#undef NEED_FAR_POINTERS /* we presume a 32-bit flat memory model */ +#undef NEED_SHORT_EXTERNAL_NAMES +#undef INCOMPLETE_TYPES_BROKEN + +/* Define "boolean" as unsigned char, not int, per Windows custom */ +#ifndef __RPCNDR_H__ /* don't conflict if rpcndr.h already read */ +typedef unsigned char boolean; +#endif +#define HAVE_BOOLEAN /* prevent jmorecfg.h from redefining it */ + +#ifdef JPEG_INTERNALS + +#undef RIGHT_SHIFT_IS_UNSIGNED + +#endif /* JPEG_INTERNALS */ + +#ifdef JPEG_CJPEG_DJPEG + +#define BMP_SUPPORTED /* BMP image file format */ +#define GIF_SUPPORTED /* GIF image file format */ +#define PPM_SUPPORTED /* PBMPLUS PPM/PGM image file format */ +#undef RLE_SUPPORTED /* Utah RLE image file format */ +#define TARGA_SUPPORTED /* Targa image file format */ + +#define TWO_FILE_COMMANDLINE /* optional */ +#define USE_SETMODE /* Microsoft has setmode() */ +#undef NEED_SIGNAL_CATCHER +#undef DONT_USE_B_MODE +#undef PROGRESS_REPORT /* optional */ + +#endif /* JPEG_CJPEG_DJPEG */ +/********* End of inlined file: jconfig.h *********/ + + /* auto configuration options */ +#define JCONFIG_INCLUDED /* so that jpeglib.h doesn't do it again */ + +/* + * We need the NULL macro and size_t typedef. + * On an ANSI-conforming system it is sufficient to include . + * Otherwise, we get them from or ; we may have to + * pull in as well. + * Note that the core JPEG library does not require ; + * only the default error handler and data source/destination modules do. + * But we must pull it in because of the references to FILE in jpeglib.h. + * You can remove those references if you want to compile without . + */ + +#ifdef HAVE_STDDEF_H +#include +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif + +#ifdef NEED_SYS_TYPES_H +#include +#endif + +#include + +/* + * We need memory copying and zeroing functions, plus strncpy(). + * ANSI and System V implementations declare these in . + * BSD doesn't have the mem() functions, but it does have bcopy()/bzero(). + * Some systems may declare memset and memcpy in . + * + * NOTE: we assume the size parameters to these functions are of type size_t. + * Change the casts in these macros if not! + */ + +#ifdef NEED_BSD_STRINGS + +#include +#define MEMZERO(target,size) bzero((void *)(target), (size_t)(size)) +#define MEMCOPY(dest,src,size) bcopy((const void *)(src), (void *)(dest), (size_t)(size)) + +#else /* not BSD, assume ANSI/SysV string lib */ + +#include +#define MEMZERO(target,size) memset((void *)(target), 0, (size_t)(size)) +#define MEMCOPY(dest,src,size) memcpy((void *)(dest), (const void *)(src), (size_t)(size)) + +#endif + +/* + * In ANSI C, and indeed any rational implementation, size_t is also the + * type returned by sizeof(). However, it seems there are some irrational + * implementations out there, in which sizeof() returns an int even though + * size_t is defined as long or unsigned long. To ensure consistent results + * we always use this SIZEOF() macro in place of using sizeof() directly. + */ + +#define SIZEOF(object) ((size_t) sizeof(object)) + +/* + * The modules that use fread() and fwrite() always invoke them through + * these macros. On some systems you may need to twiddle the argument casts. + * CAUTION: argument order is different from underlying functions! + */ + +#define JFREAD(file,buf,sizeofbuf) \ + ((size_t) fread((void *) (buf), (size_t) 1, (size_t) (sizeofbuf), (file))) +#define JFWRITE(file,buf,sizeofbuf) \ + ((size_t) fwrite((const void *) (buf), (size_t) 1, (size_t) (sizeofbuf), (file))) + +typedef enum { /* JPEG marker codes */ + M_SOF0 = 0xc0, + M_SOF1 = 0xc1, + M_SOF2 = 0xc2, + M_SOF3 = 0xc3, + + M_SOF5 = 0xc5, + M_SOF6 = 0xc6, + M_SOF7 = 0xc7, + + M_JPG = 0xc8, + M_SOF9 = 0xc9, + M_SOF10 = 0xca, + M_SOF11 = 0xcb, + + M_SOF13 = 0xcd, + M_SOF14 = 0xce, + M_SOF15 = 0xcf, + + M_DHT = 0xc4, + + M_DAC = 0xcc, + + M_RST0 = 0xd0, + M_RST1 = 0xd1, + M_RST2 = 0xd2, + M_RST3 = 0xd3, + M_RST4 = 0xd4, + M_RST5 = 0xd5, + M_RST6 = 0xd6, + M_RST7 = 0xd7, + + M_SOI = 0xd8, + M_EOI = 0xd9, + M_SOS = 0xda, + M_DQT = 0xdb, + M_DNL = 0xdc, + M_DRI = 0xdd, + M_DHP = 0xde, + M_EXP = 0xdf, + + M_APP0 = 0xe0, + M_APP1 = 0xe1, + M_APP2 = 0xe2, + M_APP3 = 0xe3, + M_APP4 = 0xe4, + M_APP5 = 0xe5, + M_APP6 = 0xe6, + M_APP7 = 0xe7, + M_APP8 = 0xe8, + M_APP9 = 0xe9, + M_APP10 = 0xea, + M_APP11 = 0xeb, + M_APP12 = 0xec, + M_APP13 = 0xed, + M_APP14 = 0xee, + M_APP15 = 0xef, + + M_JPG0 = 0xf0, + M_JPG13 = 0xfd, + M_COM = 0xfe, + + M_TEM = 0x01, + + M_ERROR = 0x100 +} JPEG_MARKER; + +/* + * Figure F.12: extend sign bit. + * On some machines, a shift and add will be faster than a table lookup. + */ + +#ifdef AVOID_TABLES + +#define HUFF_EXTEND(x,s) ((x) < (1<<((s)-1)) ? (x) + (((-1)<<(s)) + 1) : (x)) + +#else + +#define HUFF_EXTEND(x,s) ((x) < extend_test[s] ? (x) + extend_offset[s] : (x)) + +static const int extend_test[16] = /* entry n is 2**(n-1) */ + { 0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, + 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000 }; + +static const int extend_offset[16] = /* entry n is (-1 << n) + 1 */ + { 0, ((-1)<<1) + 1, ((-1)<<2) + 1, ((-1)<<3) + 1, ((-1)<<4) + 1, + ((-1)<<5) + 1, ((-1)<<6) + 1, ((-1)<<7) + 1, ((-1)<<8) + 1, + ((-1)<<9) + 1, ((-1)<<10) + 1, ((-1)<<11) + 1, ((-1)<<12) + 1, + ((-1)<<13) + 1, ((-1)<<14) + 1, ((-1)<<15) + 1 }; + +#endif /* AVOID_TABLES */ + +#endif +/********* End of inlined file: jinclude.h *********/ + +/* + * Initialization of a JPEG compression object. + * The error manager must already be set up (in case memory manager fails). + */ + +GLOBAL(void) +jpeg_CreateCompress (j_compress_ptr cinfo, int version, size_t structsize) +{ + int i; + + /* Guard against version mismatches between library and caller. */ + cinfo->mem = NULL; /* so jpeg_destroy knows mem mgr not called */ + if (version != JPEG_LIB_VERSION) + ERREXIT2(cinfo, JERR_BAD_LIB_VERSION, JPEG_LIB_VERSION, version); + if (structsize != SIZEOF(struct jpeg_compress_struct)) + ERREXIT2(cinfo, JERR_BAD_STRUCT_SIZE, + (int) SIZEOF(struct jpeg_compress_struct), (int) structsize); + + /* For debugging purposes, we zero the whole master structure. + * But the application has already set the err pointer, and may have set + * client_data, so we have to save and restore those fields. + * Note: if application hasn't set client_data, tools like Purify may + * complain here. + */ + { + struct jpeg_error_mgr * err = cinfo->err; + void * client_data = cinfo->client_data; /* ignore Purify complaint here */ + MEMZERO(cinfo, SIZEOF(struct jpeg_compress_struct)); + cinfo->err = err; + cinfo->client_data = client_data; + } + cinfo->is_decompressor = FALSE; + + /* Initialize a memory manager instance for this object */ + jinit_memory_mgr((j_common_ptr) cinfo); + + /* Zero out pointers to permanent structures. */ + cinfo->progress = NULL; + cinfo->dest = NULL; + + cinfo->comp_info = NULL; + + for (i = 0; i < NUM_QUANT_TBLS; i++) + cinfo->quant_tbl_ptrs[i] = NULL; + + for (i = 0; i < NUM_HUFF_TBLS; i++) { + cinfo->dc_huff_tbl_ptrs[i] = NULL; + cinfo->ac_huff_tbl_ptrs[i] = NULL; + } + + cinfo->script_space = NULL; + + cinfo->input_gamma = 1.0; /* in case application forgets */ + + /* OK, I'm ready */ + cinfo->global_state = CSTATE_START; +} + +/* + * Destruction of a JPEG compression object + */ + +GLOBAL(void) +jpeg_destroy_compress (j_compress_ptr cinfo) +{ + jpeg_destroy((j_common_ptr) cinfo); /* use common routine */ +} + +/* + * Abort processing of a JPEG compression operation, + * but don't destroy the object itself. + */ + +GLOBAL(void) +jpeg_abort_compress (j_compress_ptr cinfo) +{ + jpeg_abort((j_common_ptr) cinfo); /* use common routine */ +} + +/* + * Forcibly suppress or un-suppress all quantization and Huffman tables. + * Marks all currently defined tables as already written (if suppress) + * or not written (if !suppress). This will control whether they get emitted + * by a subsequent jpeg_start_compress call. + * + * This routine is exported for use by applications that want to produce + * abbreviated JPEG datastreams. It logically belongs in jcparam.c, but + * since it is called by jpeg_start_compress, we put it here --- otherwise + * jcparam.o would be linked whether the application used it or not. + */ + +GLOBAL(void) +jpeg_suppress_tables (j_compress_ptr cinfo, boolean suppress) +{ + int i; + JQUANT_TBL * qtbl; + JHUFF_TBL * htbl; + + for (i = 0; i < NUM_QUANT_TBLS; i++) { + if ((qtbl = cinfo->quant_tbl_ptrs[i]) != NULL) + qtbl->sent_table = suppress; + } + + for (i = 0; i < NUM_HUFF_TBLS; i++) { + if ((htbl = cinfo->dc_huff_tbl_ptrs[i]) != NULL) + htbl->sent_table = suppress; + if ((htbl = cinfo->ac_huff_tbl_ptrs[i]) != NULL) + htbl->sent_table = suppress; + } +} + +/* + * Finish JPEG compression. + * + * If a multipass operating mode was selected, this may do a great deal of + * work including most of the actual output. + */ + +GLOBAL(void) +jpeg_finish_compress (j_compress_ptr cinfo) +{ + JDIMENSION iMCU_row; + + if (cinfo->global_state == CSTATE_SCANNING || + cinfo->global_state == CSTATE_RAW_OK) { + /* Terminate first pass */ + if (cinfo->next_scanline < cinfo->image_height) + ERREXIT(cinfo, JERR_TOO_LITTLE_DATA); + (*cinfo->master->finish_pass) (cinfo); + } else if (cinfo->global_state != CSTATE_WRCOEFS) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + /* Perform any remaining passes */ + while (! cinfo->master->is_last_pass) { + (*cinfo->master->prepare_for_pass) (cinfo); + for (iMCU_row = 0; iMCU_row < cinfo->total_iMCU_rows; iMCU_row++) { + if (cinfo->progress != NULL) { + cinfo->progress->pass_counter = (long) iMCU_row; + cinfo->progress->pass_limit = (long) cinfo->total_iMCU_rows; + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + } + /* We bypass the main controller and invoke coef controller directly; + * all work is being done from the coefficient buffer. + */ + if (! (*cinfo->coef->compress_data) (cinfo, (JSAMPIMAGE) NULL)) + ERREXIT(cinfo, JERR_CANT_SUSPEND); + } + (*cinfo->master->finish_pass) (cinfo); + } + /* Write EOI, do final cleanup */ + (*cinfo->marker->write_file_trailer) (cinfo); + (*cinfo->dest->term_destination) (cinfo); + /* We can use jpeg_abort to release memory and reset global_state */ + jpeg_abort((j_common_ptr) cinfo); +} + +/* + * Write a special marker. + * This is only recommended for writing COM or APPn markers. + * Must be called after jpeg_start_compress() and before + * first call to jpeg_write_scanlines() or jpeg_write_raw_data(). + */ + +GLOBAL(void) +jpeg_write_marker (j_compress_ptr cinfo, int marker, + const JOCTET *dataptr, unsigned int datalen) +{ + JMETHOD(void, write_marker_byte, (j_compress_ptr info, int val)); + + if (cinfo->next_scanline != 0 || + (cinfo->global_state != CSTATE_SCANNING && + cinfo->global_state != CSTATE_RAW_OK && + cinfo->global_state != CSTATE_WRCOEFS)) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + (*cinfo->marker->write_marker_header) (cinfo, marker, datalen); + write_marker_byte = cinfo->marker->write_marker_byte; /* copy for speed */ + while (datalen--) { + (*write_marker_byte) (cinfo, *dataptr); + dataptr++; + } +} + +/* Same, but piecemeal. */ + +GLOBAL(void) +jpeg_write_m_header (j_compress_ptr cinfo, int marker, unsigned int datalen) +{ + if (cinfo->next_scanline != 0 || + (cinfo->global_state != CSTATE_SCANNING && + cinfo->global_state != CSTATE_RAW_OK && + cinfo->global_state != CSTATE_WRCOEFS)) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + (*cinfo->marker->write_marker_header) (cinfo, marker, datalen); +} + +GLOBAL(void) +jpeg_write_m_byte (j_compress_ptr cinfo, int val) +{ + (*cinfo->marker->write_marker_byte) (cinfo, val); +} + +/* + * Alternate compression function: just write an abbreviated table file. + * Before calling this, all parameters and a data destination must be set up. + * + * To produce a pair of files containing abbreviated tables and abbreviated + * image data, one would proceed as follows: + * + * initialize JPEG object + * set JPEG parameters + * set destination to table file + * jpeg_write_tables(cinfo); + * set destination to image file + * jpeg_start_compress(cinfo, FALSE); + * write data... + * jpeg_finish_compress(cinfo); + * + * jpeg_write_tables has the side effect of marking all tables written + * (same as jpeg_suppress_tables(..., TRUE)). Thus a subsequent start_compress + * will not re-emit the tables unless it is passed write_all_tables=TRUE. + */ + +GLOBAL(void) +jpeg_write_tables (j_compress_ptr cinfo) +{ + if (cinfo->global_state != CSTATE_START) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + /* (Re)initialize error mgr and destination modules */ + (*cinfo->err->reset_error_mgr) ((j_common_ptr) cinfo); + (*cinfo->dest->init_destination) (cinfo); + /* Initialize the marker writer ... bit of a crock to do it here. */ + jinit_marker_writer(cinfo); + /* Write them tables! */ + (*cinfo->marker->write_tables_only) (cinfo); + /* And clean up. */ + (*cinfo->dest->term_destination) (cinfo); + /* + * In library releases up through v6a, we called jpeg_abort() here to free + * any working memory allocated by the destination manager and marker + * writer. Some applications had a problem with that: they allocated space + * of their own from the library memory manager, and didn't want it to go + * away during write_tables. So now we do nothing. This will cause a + * memory leak if an app calls write_tables repeatedly without doing a full + * compression cycle or otherwise resetting the JPEG object. However, that + * seems less bad than unexpectedly freeing memory in the normal case. + * An app that prefers the old behavior can call jpeg_abort for itself after + * each call to jpeg_write_tables(). + */ +} +/********* End of inlined file: jcapimin.c *********/ + +/********* Start of inlined file: jcapistd.c *********/ +#define JPEG_INTERNALS + +/* + * Compression initialization. + * Before calling this, all parameters and a data destination must be set up. + * + * We require a write_all_tables parameter as a failsafe check when writing + * multiple datastreams from the same compression object. Since prior runs + * will have left all the tables marked sent_table=TRUE, a subsequent run + * would emit an abbreviated stream (no tables) by default. This may be what + * is wanted, but for safety's sake it should not be the default behavior: + * programmers should have to make a deliberate choice to emit abbreviated + * images. Therefore the documentation and examples should encourage people + * to pass write_all_tables=TRUE; then it will take active thought to do the + * wrong thing. + */ + +GLOBAL(void) +jpeg_start_compress (j_compress_ptr cinfo, boolean write_all_tables) +{ + if (cinfo->global_state != CSTATE_START) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + if (write_all_tables) + jpeg_suppress_tables(cinfo, FALSE); /* mark all tables to be written */ + + /* (Re)initialize error mgr and destination modules */ + (*cinfo->err->reset_error_mgr) ((j_common_ptr) cinfo); + (*cinfo->dest->init_destination) (cinfo); + /* Perform master selection of active modules */ + jinit_compress_master(cinfo); + /* Set up for the first pass */ + (*cinfo->master->prepare_for_pass) (cinfo); + /* Ready for application to drive first pass through jpeg_write_scanlines + * or jpeg_write_raw_data. + */ + cinfo->next_scanline = 0; + cinfo->global_state = (cinfo->raw_data_in ? CSTATE_RAW_OK : CSTATE_SCANNING); +} + +/* + * Write some scanlines of data to the JPEG compressor. + * + * The return value will be the number of lines actually written. + * This should be less than the supplied num_lines only in case that + * the data destination module has requested suspension of the compressor, + * or if more than image_height scanlines are passed in. + * + * Note: we warn about excess calls to jpeg_write_scanlines() since + * this likely signals an application programmer error. However, + * excess scanlines passed in the last valid call are *silently* ignored, + * so that the application need not adjust num_lines for end-of-image + * when using a multiple-scanline buffer. + */ + +GLOBAL(JDIMENSION) +jpeg_write_scanlines (j_compress_ptr cinfo, JSAMPARRAY scanlines, + JDIMENSION num_lines) +{ + JDIMENSION row_ctr, rows_left; + + if (cinfo->global_state != CSTATE_SCANNING) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + if (cinfo->next_scanline >= cinfo->image_height) + WARNMS(cinfo, JWRN_TOO_MUCH_DATA); + + /* Call progress monitor hook if present */ + if (cinfo->progress != NULL) { + cinfo->progress->pass_counter = (long) cinfo->next_scanline; + cinfo->progress->pass_limit = (long) cinfo->image_height; + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + } + + /* Give master control module another chance if this is first call to + * jpeg_write_scanlines. This lets output of the frame/scan headers be + * delayed so that application can write COM, etc, markers between + * jpeg_start_compress and jpeg_write_scanlines. + */ + if (cinfo->master->call_pass_startup) + (*cinfo->master->pass_startup) (cinfo); + + /* Ignore any extra scanlines at bottom of image. */ + rows_left = cinfo->image_height - cinfo->next_scanline; + if (num_lines > rows_left) + num_lines = rows_left; + + row_ctr = 0; + (*cinfo->main->process_data) (cinfo, scanlines, &row_ctr, num_lines); + cinfo->next_scanline += row_ctr; + return row_ctr; +} + +/* + * Alternate entry point to write raw data. + * Processes exactly one iMCU row per call, unless suspended. + */ + +GLOBAL(JDIMENSION) +jpeg_write_raw_data (j_compress_ptr cinfo, JSAMPIMAGE data, + JDIMENSION num_lines) +{ + JDIMENSION lines_per_iMCU_row; + + if (cinfo->global_state != CSTATE_RAW_OK) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + if (cinfo->next_scanline >= cinfo->image_height) { + WARNMS(cinfo, JWRN_TOO_MUCH_DATA); + return 0; + } + + /* Call progress monitor hook if present */ + if (cinfo->progress != NULL) { + cinfo->progress->pass_counter = (long) cinfo->next_scanline; + cinfo->progress->pass_limit = (long) cinfo->image_height; + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + } + + /* Give master control module another chance if this is first call to + * jpeg_write_raw_data. This lets output of the frame/scan headers be + * delayed so that application can write COM, etc, markers between + * jpeg_start_compress and jpeg_write_raw_data. + */ + if (cinfo->master->call_pass_startup) + (*cinfo->master->pass_startup) (cinfo); + + /* Verify that at least one iMCU row has been passed. */ + lines_per_iMCU_row = cinfo->max_v_samp_factor * DCTSIZE; + if (num_lines < lines_per_iMCU_row) + ERREXIT(cinfo, JERR_BUFFER_SIZE); + + /* Directly compress the row. */ + if (! (*cinfo->coef->compress_data) (cinfo, data)) { + /* If compressor did not consume the whole row, suspend processing. */ + return 0; + } + + /* OK, we processed one iMCU row. */ + cinfo->next_scanline += lines_per_iMCU_row; + return lines_per_iMCU_row; +} +/********* End of inlined file: jcapistd.c *********/ + +/********* Start of inlined file: jccoefct.c *********/ +#define JPEG_INTERNALS + +/* We use a full-image coefficient buffer when doing Huffman optimization, + * and also for writing multiple-scan JPEG files. In all cases, the DCT + * step is run during the first pass, and subsequent passes need only read + * the buffered coefficients. + */ +#ifdef ENTROPY_OPT_SUPPORTED +#define FULL_COEF_BUFFER_SUPPORTED +#else +#ifdef C_MULTISCAN_FILES_SUPPORTED +#define FULL_COEF_BUFFER_SUPPORTED +#endif +#endif + +/* Private buffer controller object */ + +typedef struct { + struct jpeg_c_coef_controller pub; /* public fields */ + + JDIMENSION iMCU_row_num; /* iMCU row # within image */ + JDIMENSION mcu_ctr; /* counts MCUs processed in current row */ + int MCU_vert_offset; /* counts MCU rows within iMCU row */ + int MCU_rows_per_iMCU_row; /* number of such rows needed */ + + /* For single-pass compression, it's sufficient to buffer just one MCU + * (although this may prove a bit slow in practice). We allocate a + * workspace of C_MAX_BLOCKS_IN_MCU coefficient blocks, and reuse it for each + * MCU constructed and sent. (On 80x86, the workspace is FAR even though + * it's not really very big; this is to keep the module interfaces unchanged + * when a large coefficient buffer is necessary.) + * In multi-pass modes, this array points to the current MCU's blocks + * within the virtual arrays. + */ + JBLOCKROW MCU_buffer[C_MAX_BLOCKS_IN_MCU]; + + /* In multi-pass modes, we need a virtual block array for each component. */ + jvirt_barray_ptr whole_image[MAX_COMPONENTS]; +} my_coef_controller; + +typedef my_coef_controller * my_coef_ptr; + +/* Forward declarations */ +METHODDEF(boolean) compress_data + JPP((j_compress_ptr cinfo, JSAMPIMAGE input_buf)); +#ifdef FULL_COEF_BUFFER_SUPPORTED +METHODDEF(boolean) compress_first_pass + JPP((j_compress_ptr cinfo, JSAMPIMAGE input_buf)); +METHODDEF(boolean) compress_output + JPP((j_compress_ptr cinfo, JSAMPIMAGE input_buf)); +#endif + +LOCAL(void) +start_iMCU_row (j_compress_ptr cinfo) +/* Reset within-iMCU-row counters for a new row */ +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + /* In an interleaved scan, an MCU row is the same as an iMCU row. + * In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. + * But at the bottom of the image, process only what's left. + */ + if (cinfo->comps_in_scan > 1) { + coef->MCU_rows_per_iMCU_row = 1; + } else { + if (coef->iMCU_row_num < (cinfo->total_iMCU_rows-1)) + coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->v_samp_factor; + else + coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->last_row_height; + } + + coef->mcu_ctr = 0; + coef->MCU_vert_offset = 0; +} + +/* + * Initialize for a processing pass. + */ + +METHODDEF(void) +start_pass_coef (j_compress_ptr cinfo, J_BUF_MODE pass_mode) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + coef->iMCU_row_num = 0; + start_iMCU_row(cinfo); + + switch (pass_mode) { + case JBUF_PASS_THRU: + if (coef->whole_image[0] != NULL) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + coef->pub.compress_data = compress_data; + break; +#ifdef FULL_COEF_BUFFER_SUPPORTED + case JBUF_SAVE_AND_PASS: + if (coef->whole_image[0] == NULL) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + coef->pub.compress_data = compress_first_pass; + break; + case JBUF_CRANK_DEST: + if (coef->whole_image[0] == NULL) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + coef->pub.compress_data = compress_output; + break; +#endif + default: + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + break; + } +} + +/* + * Process some data in the single-pass case. + * We process the equivalent of one fully interleaved MCU row ("iMCU" row) + * per call, ie, v_samp_factor block rows for each component in the image. + * Returns TRUE if the iMCU row is completed, FALSE if suspended. + * + * NB: input_buf contains a plane for each component in image, + * which we index according to the component's SOF position. + */ + +METHODDEF(boolean) +compress_data (j_compress_ptr cinfo, JSAMPIMAGE input_buf) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + JDIMENSION MCU_col_num; /* index of current MCU within row */ + JDIMENSION last_MCU_col = cinfo->MCUs_per_row - 1; + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + int blkn, bi, ci, yindex, yoffset, blockcnt; + JDIMENSION ypos, xpos; + jpeg_component_info *compptr; + + /* Loop to write as much as one whole iMCU row */ + for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row; + yoffset++) { + for (MCU_col_num = coef->mcu_ctr; MCU_col_num <= last_MCU_col; + MCU_col_num++) { + /* Determine where data comes from in input_buf and do the DCT thing. + * Each call on forward_DCT processes a horizontal row of DCT blocks + * as wide as an MCU; we rely on having allocated the MCU_buffer[] blocks + * sequentially. Dummy blocks at the right or bottom edge are filled in + * specially. The data in them does not matter for image reconstruction, + * so we fill them with values that will encode to the smallest amount of + * data, viz: all zeroes in the AC entries, DC entries equal to previous + * block's DC value. (Thanks to Thomas Kinsman for this idea.) + */ + blkn = 0; + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + blockcnt = (MCU_col_num < last_MCU_col) ? compptr->MCU_width + : compptr->last_col_width; + xpos = MCU_col_num * compptr->MCU_sample_width; + ypos = yoffset * DCTSIZE; /* ypos == (yoffset+yindex) * DCTSIZE */ + for (yindex = 0; yindex < compptr->MCU_height; yindex++) { + if (coef->iMCU_row_num < last_iMCU_row || + yoffset+yindex < compptr->last_row_height) { + (*cinfo->fdct->forward_DCT) (cinfo, compptr, + input_buf[compptr->component_index], + coef->MCU_buffer[blkn], + ypos, xpos, (JDIMENSION) blockcnt); + if (blockcnt < compptr->MCU_width) { + /* Create some dummy blocks at the right edge of the image. */ + jzero_far((void FAR *) coef->MCU_buffer[blkn + blockcnt], + (compptr->MCU_width - blockcnt) * SIZEOF(JBLOCK)); + for (bi = blockcnt; bi < compptr->MCU_width; bi++) { + coef->MCU_buffer[blkn+bi][0][0] = coef->MCU_buffer[blkn+bi-1][0][0]; + } + } + } else { + /* Create a row of dummy blocks at the bottom of the image. */ + jzero_far((void FAR *) coef->MCU_buffer[blkn], + compptr->MCU_width * SIZEOF(JBLOCK)); + for (bi = 0; bi < compptr->MCU_width; bi++) { + coef->MCU_buffer[blkn+bi][0][0] = coef->MCU_buffer[blkn-1][0][0]; + } + } + blkn += compptr->MCU_width; + ypos += DCTSIZE; + } + } + /* Try to write the MCU. In event of a suspension failure, we will + * re-DCT the MCU on restart (a bit inefficient, could be fixed...) + */ + if (! (*cinfo->entropy->encode_mcu) (cinfo, coef->MCU_buffer)) { + /* Suspension forced; update state counters and exit */ + coef->MCU_vert_offset = yoffset; + coef->mcu_ctr = MCU_col_num; + return FALSE; + } + } + /* Completed an MCU row, but perhaps not an iMCU row */ + coef->mcu_ctr = 0; + } + /* Completed the iMCU row, advance counters for next one */ + coef->iMCU_row_num++; + start_iMCU_row(cinfo); + return TRUE; +} + +#ifdef FULL_COEF_BUFFER_SUPPORTED + +/* + * Process some data in the first pass of a multi-pass case. + * We process the equivalent of one fully interleaved MCU row ("iMCU" row) + * per call, ie, v_samp_factor block rows for each component in the image. + * This amount of data is read from the source buffer, DCT'd and quantized, + * and saved into the virtual arrays. We also generate suitable dummy blocks + * as needed at the right and lower edges. (The dummy blocks are constructed + * in the virtual arrays, which have been padded appropriately.) This makes + * it possible for subsequent passes not to worry about real vs. dummy blocks. + * + * We must also emit the data to the entropy encoder. This is conveniently + * done by calling compress_output() after we've loaded the current strip + * of the virtual arrays. + * + * NB: input_buf contains a plane for each component in image. All + * components are DCT'd and loaded into the virtual arrays in this pass. + * However, it may be that only a subset of the components are emitted to + * the entropy encoder during this first pass; be careful about looking + * at the scan-dependent variables (MCU dimensions, etc). + */ + +METHODDEF(boolean) +compress_first_pass (j_compress_ptr cinfo, JSAMPIMAGE input_buf) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + JDIMENSION blocks_across, MCUs_across, MCUindex; + int bi, ci, h_samp_factor, block_row, block_rows, ndummy; + JCOEF lastDC; + jpeg_component_info *compptr; + JBLOCKARRAY buffer; + JBLOCKROW thisblockrow, lastblockrow; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Align the virtual buffer for this component. */ + buffer = (*cinfo->mem->access_virt_barray) + ((j_common_ptr) cinfo, coef->whole_image[ci], + coef->iMCU_row_num * compptr->v_samp_factor, + (JDIMENSION) compptr->v_samp_factor, TRUE); + /* Count non-dummy DCT block rows in this iMCU row. */ + if (coef->iMCU_row_num < last_iMCU_row) + block_rows = compptr->v_samp_factor; + else { + /* NB: can't use last_row_height here, since may not be set! */ + block_rows = (int) (compptr->height_in_blocks % compptr->v_samp_factor); + if (block_rows == 0) block_rows = compptr->v_samp_factor; + } + blocks_across = compptr->width_in_blocks; + h_samp_factor = compptr->h_samp_factor; + /* Count number of dummy blocks to be added at the right margin. */ + ndummy = (int) (blocks_across % h_samp_factor); + if (ndummy > 0) + ndummy = h_samp_factor - ndummy; + /* Perform DCT for all non-dummy blocks in this iMCU row. Each call + * on forward_DCT processes a complete horizontal row of DCT blocks. + */ + for (block_row = 0; block_row < block_rows; block_row++) { + thisblockrow = buffer[block_row]; + (*cinfo->fdct->forward_DCT) (cinfo, compptr, + input_buf[ci], thisblockrow, + (JDIMENSION) (block_row * DCTSIZE), + (JDIMENSION) 0, blocks_across); + if (ndummy > 0) { + /* Create dummy blocks at the right edge of the image. */ + thisblockrow += blocks_across; /* => first dummy block */ + jzero_far((void FAR *) thisblockrow, ndummy * SIZEOF(JBLOCK)); + lastDC = thisblockrow[-1][0]; + for (bi = 0; bi < ndummy; bi++) { + thisblockrow[bi][0] = lastDC; + } + } + } + /* If at end of image, create dummy block rows as needed. + * The tricky part here is that within each MCU, we want the DC values + * of the dummy blocks to match the last real block's DC value. + * This squeezes a few more bytes out of the resulting file... + */ + if (coef->iMCU_row_num == last_iMCU_row) { + blocks_across += ndummy; /* include lower right corner */ + MCUs_across = blocks_across / h_samp_factor; + for (block_row = block_rows; block_row < compptr->v_samp_factor; + block_row++) { + thisblockrow = buffer[block_row]; + lastblockrow = buffer[block_row-1]; + jzero_far((void FAR *) thisblockrow, + (size_t) (blocks_across * SIZEOF(JBLOCK))); + for (MCUindex = 0; MCUindex < MCUs_across; MCUindex++) { + lastDC = lastblockrow[h_samp_factor-1][0]; + for (bi = 0; bi < h_samp_factor; bi++) { + thisblockrow[bi][0] = lastDC; + } + thisblockrow += h_samp_factor; /* advance to next MCU in row */ + lastblockrow += h_samp_factor; + } + } + } + } + /* NB: compress_output will increment iMCU_row_num if successful. + * A suspension return will result in redoing all the work above next time. + */ + + /* Emit data to the entropy encoder, sharing code with subsequent passes */ + return compress_output(cinfo, input_buf); +} + +/* + * Process some data in subsequent passes of a multi-pass case. + * We process the equivalent of one fully interleaved MCU row ("iMCU" row) + * per call, ie, v_samp_factor block rows for each component in the scan. + * The data is obtained from the virtual arrays and fed to the entropy coder. + * Returns TRUE if the iMCU row is completed, FALSE if suspended. + * + * NB: input_buf is ignored; it is likely to be a NULL pointer. + */ + +METHODDEF(boolean) +compress_output (j_compress_ptr cinfo, JSAMPIMAGE input_buf) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + JDIMENSION MCU_col_num; /* index of current MCU within row */ + int blkn, ci, xindex, yindex, yoffset; + JDIMENSION start_col; + JBLOCKARRAY buffer[MAX_COMPS_IN_SCAN]; + JBLOCKROW buffer_ptr; + jpeg_component_info *compptr; + + /* Align the virtual buffers for the components used in this scan. + * NB: during first pass, this is safe only because the buffers will + * already be aligned properly, so jmemmgr.c won't need to do any I/O. + */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + buffer[ci] = (*cinfo->mem->access_virt_barray) + ((j_common_ptr) cinfo, coef->whole_image[compptr->component_index], + coef->iMCU_row_num * compptr->v_samp_factor, + (JDIMENSION) compptr->v_samp_factor, FALSE); + } + + /* Loop to process one whole iMCU row */ + for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row; + yoffset++) { + for (MCU_col_num = coef->mcu_ctr; MCU_col_num < cinfo->MCUs_per_row; + MCU_col_num++) { + /* Construct list of pointers to DCT blocks belonging to this MCU */ + blkn = 0; /* index of current DCT block within MCU */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + start_col = MCU_col_num * compptr->MCU_width; + for (yindex = 0; yindex < compptr->MCU_height; yindex++) { + buffer_ptr = buffer[ci][yindex+yoffset] + start_col; + for (xindex = 0; xindex < compptr->MCU_width; xindex++) { + coef->MCU_buffer[blkn++] = buffer_ptr++; + } + } + } + /* Try to write the MCU. */ + if (! (*cinfo->entropy->encode_mcu) (cinfo, coef->MCU_buffer)) { + /* Suspension forced; update state counters and exit */ + coef->MCU_vert_offset = yoffset; + coef->mcu_ctr = MCU_col_num; + return FALSE; + } + } + /* Completed an MCU row, but perhaps not an iMCU row */ + coef->mcu_ctr = 0; + } + /* Completed the iMCU row, advance counters for next one */ + coef->iMCU_row_num++; + start_iMCU_row(cinfo); + return TRUE; +} + +#endif /* FULL_COEF_BUFFER_SUPPORTED */ + +/* + * Initialize coefficient buffer controller. + */ + +GLOBAL(void) +jinit_c_coef_controller (j_compress_ptr cinfo, boolean need_full_buffer) +{ + my_coef_ptr coef; + + coef = (my_coef_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_coef_controller)); + cinfo->coef = (struct jpeg_c_coef_controller *) coef; + coef->pub.start_pass = start_pass_coef; + + /* Create the coefficient buffer. */ + if (need_full_buffer) { +#ifdef FULL_COEF_BUFFER_SUPPORTED + /* Allocate a full-image virtual array for each component, */ + /* padded to a multiple of samp_factor DCT blocks in each direction. */ + int ci; + jpeg_component_info *compptr; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + coef->whole_image[ci] = (*cinfo->mem->request_virt_barray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, + (JDIMENSION) jround_up((long) compptr->width_in_blocks, + (long) compptr->h_samp_factor), + (JDIMENSION) jround_up((long) compptr->height_in_blocks, + (long) compptr->v_samp_factor), + (JDIMENSION) compptr->v_samp_factor); + } +#else + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); +#endif + } else { + /* We only need a single-MCU buffer. */ + JBLOCKROW buffer; + int i; + + buffer = (JBLOCKROW) + (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE, + C_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK)); + for (i = 0; i < C_MAX_BLOCKS_IN_MCU; i++) { + coef->MCU_buffer[i] = buffer + i; + } + coef->whole_image[0] = NULL; /* flag for no virtual arrays */ + } +} +/********* End of inlined file: jccoefct.c *********/ + +/********* Start of inlined file: jccolor.c *********/ +#define JPEG_INTERNALS + +/* Private subobject */ + +typedef struct { + struct jpeg_color_converter pub; /* public fields */ + + /* Private state for RGB->YCC conversion */ + INT32 * rgb_ycc_tab; /* => table for RGB to YCbCr conversion */ +} my_color_converter; + +typedef my_color_converter * my_cconvert_ptr; + +/**************** RGB -> YCbCr conversion: most common case **************/ + +/* + * YCbCr is defined per CCIR 601-1, except that Cb and Cr are + * normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5. + * The conversion equations to be implemented are therefore + * Y = 0.29900 * R + 0.58700 * G + 0.11400 * B + * Cb = -0.16874 * R - 0.33126 * G + 0.50000 * B + CENTERJSAMPLE + * Cr = 0.50000 * R - 0.41869 * G - 0.08131 * B + CENTERJSAMPLE + * (These numbers are derived from TIFF 6.0 section 21, dated 3-June-92.) + * Note: older versions of the IJG code used a zero offset of MAXJSAMPLE/2, + * rather than CENTERJSAMPLE, for Cb and Cr. This gave equal positive and + * negative swings for Cb/Cr, but meant that grayscale values (Cb=Cr=0) + * were not represented exactly. Now we sacrifice exact representation of + * maximum red and maximum blue in order to get exact grayscales. + * + * To avoid floating-point arithmetic, we represent the fractional constants + * as integers scaled up by 2^16 (about 4 digits precision); we have to divide + * the products by 2^16, with appropriate rounding, to get the correct answer. + * + * For even more speed, we avoid doing any multiplications in the inner loop + * by precalculating the constants times R,G,B for all possible values. + * For 8-bit JSAMPLEs this is very reasonable (only 256 entries per table); + * for 12-bit samples it is still acceptable. It's not very reasonable for + * 16-bit samples, but if you want lossless storage you shouldn't be changing + * colorspace anyway. + * The CENTERJSAMPLE offsets and the rounding fudge-factor of 0.5 are included + * in the tables to save adding them separately in the inner loop. + */ + +#define SCALEBITS 16 /* speediest right-shift on some machines */ +#define CBCR_OFFSET ((INT32) CENTERJSAMPLE << SCALEBITS) +#define ONE_HALF ((INT32) 1 << (SCALEBITS-1)) +#define FIX(x) ((INT32) ((x) * (1L< Y section */ +#define G_Y_OFF (1*(MAXJSAMPLE+1)) /* offset to G => Y section */ +#define B_Y_OFF (2*(MAXJSAMPLE+1)) /* etc. */ +#define R_CB_OFF (3*(MAXJSAMPLE+1)) +#define G_CB_OFF (4*(MAXJSAMPLE+1)) +#define B_CB_OFF (5*(MAXJSAMPLE+1)) +#define R_CR_OFF B_CB_OFF /* B=>Cb, R=>Cr are the same */ +#define G_CR_OFF (6*(MAXJSAMPLE+1)) +#define B_CR_OFF (7*(MAXJSAMPLE+1)) +#define TABLE_SIZE (8*(MAXJSAMPLE+1)) + +/* + * Initialize for RGB->YCC colorspace conversion. + */ + +METHODDEF(void) +rgb_ycc_start (j_compress_ptr cinfo) +{ + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + INT32 * rgb_ycc_tab; + INT32 i; + + /* Allocate and fill in the conversion tables. */ + cconvert->rgb_ycc_tab = rgb_ycc_tab = (INT32 *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (TABLE_SIZE * SIZEOF(INT32))); + + for (i = 0; i <= MAXJSAMPLE; i++) { + rgb_ycc_tab[i+R_Y_OFF] = FIX(0.29900) * i; + rgb_ycc_tab[i+G_Y_OFF] = FIX(0.58700) * i; + rgb_ycc_tab[i+B_Y_OFF] = FIX(0.11400) * i + ONE_HALF; + rgb_ycc_tab[i+R_CB_OFF] = (-FIX(0.16874)) * i; + rgb_ycc_tab[i+G_CB_OFF] = (-FIX(0.33126)) * i; + /* We use a rounding fudge-factor of 0.5-epsilon for Cb and Cr. + * This ensures that the maximum output will round to MAXJSAMPLE + * not MAXJSAMPLE+1, and thus that we don't have to range-limit. + */ + rgb_ycc_tab[i+B_CB_OFF] = FIX(0.50000) * i + CBCR_OFFSET + ONE_HALF-1; +/* B=>Cb and R=>Cr tables are the same + rgb_ycc_tab[i+R_CR_OFF] = FIX(0.50000) * i + CBCR_OFFSET + ONE_HALF-1; +*/ + rgb_ycc_tab[i+G_CR_OFF] = (-FIX(0.41869)) * i; + rgb_ycc_tab[i+B_CR_OFF] = (-FIX(0.08131)) * i; + } +} + +/* + * Convert some rows of samples to the JPEG colorspace. + * + * Note that we change from the application's interleaved-pixel format + * to our internal noninterleaved, one-plane-per-component format. + * The input buffer is therefore three times as wide as the output buffer. + * + * A starting row offset is provided only for the output buffer. The caller + * can easily adjust the passed input_buf value to accommodate any row + * offset required on that side. + */ + +METHODDEF(void) +rgb_ycc_convert (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows) +{ + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + register int r, g, b; + register INT32 * ctab = cconvert->rgb_ycc_tab; + register JSAMPROW inptr; + register JSAMPROW outptr0, outptr1, outptr2; + register JDIMENSION col; + JDIMENSION num_cols = cinfo->image_width; + + while (--num_rows >= 0) { + inptr = *input_buf++; + outptr0 = output_buf[0][output_row]; + outptr1 = output_buf[1][output_row]; + outptr2 = output_buf[2][output_row]; + output_row++; + for (col = 0; col < num_cols; col++) { + r = GETJSAMPLE(inptr[RGB_RED]); + g = GETJSAMPLE(inptr[RGB_GREEN]); + b = GETJSAMPLE(inptr[RGB_BLUE]); + inptr += RGB_PIXELSIZE; + /* If the inputs are 0..MAXJSAMPLE, the outputs of these equations + * must be too; we do not need an explicit range-limiting operation. + * Hence the value being shifted is never negative, and we don't + * need the general RIGHT_SHIFT macro. + */ + /* Y */ + outptr0[col] = (JSAMPLE) + ((ctab[r+R_Y_OFF] + ctab[g+G_Y_OFF] + ctab[b+B_Y_OFF]) + >> SCALEBITS); + /* Cb */ + outptr1[col] = (JSAMPLE) + ((ctab[r+R_CB_OFF] + ctab[g+G_CB_OFF] + ctab[b+B_CB_OFF]) + >> SCALEBITS); + /* Cr */ + outptr2[col] = (JSAMPLE) + ((ctab[r+R_CR_OFF] + ctab[g+G_CR_OFF] + ctab[b+B_CR_OFF]) + >> SCALEBITS); + } + } +} + +/**************** Cases other than RGB -> YCbCr **************/ + +/* + * Convert some rows of samples to the JPEG colorspace. + * This version handles RGB->grayscale conversion, which is the same + * as the RGB->Y portion of RGB->YCbCr. + * We assume rgb_ycc_start has been called (we only use the Y tables). + */ + +METHODDEF(void) +rgb_gray_convert (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows) +{ + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + register int r, g, b; + register INT32 * ctab = cconvert->rgb_ycc_tab; + register JSAMPROW inptr; + register JSAMPROW outptr; + register JDIMENSION col; + JDIMENSION num_cols = cinfo->image_width; + + while (--num_rows >= 0) { + inptr = *input_buf++; + outptr = output_buf[0][output_row]; + output_row++; + for (col = 0; col < num_cols; col++) { + r = GETJSAMPLE(inptr[RGB_RED]); + g = GETJSAMPLE(inptr[RGB_GREEN]); + b = GETJSAMPLE(inptr[RGB_BLUE]); + inptr += RGB_PIXELSIZE; + /* Y */ + outptr[col] = (JSAMPLE) + ((ctab[r+R_Y_OFF] + ctab[g+G_Y_OFF] + ctab[b+B_Y_OFF]) + >> SCALEBITS); + } + } +} + +/* + * Convert some rows of samples to the JPEG colorspace. + * This version handles Adobe-style CMYK->YCCK conversion, + * where we convert R=1-C, G=1-M, and B=1-Y to YCbCr using the same + * conversion as above, while passing K (black) unchanged. + * We assume rgb_ycc_start has been called. + */ + +METHODDEF(void) +cmyk_ycck_convert (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows) +{ + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + register int r, g, b; + register INT32 * ctab = cconvert->rgb_ycc_tab; + register JSAMPROW inptr; + register JSAMPROW outptr0, outptr1, outptr2, outptr3; + register JDIMENSION col; + JDIMENSION num_cols = cinfo->image_width; + + while (--num_rows >= 0) { + inptr = *input_buf++; + outptr0 = output_buf[0][output_row]; + outptr1 = output_buf[1][output_row]; + outptr2 = output_buf[2][output_row]; + outptr3 = output_buf[3][output_row]; + output_row++; + for (col = 0; col < num_cols; col++) { + r = MAXJSAMPLE - GETJSAMPLE(inptr[0]); + g = MAXJSAMPLE - GETJSAMPLE(inptr[1]); + b = MAXJSAMPLE - GETJSAMPLE(inptr[2]); + /* K passes through as-is */ + outptr3[col] = inptr[3]; /* don't need GETJSAMPLE here */ + inptr += 4; + /* If the inputs are 0..MAXJSAMPLE, the outputs of these equations + * must be too; we do not need an explicit range-limiting operation. + * Hence the value being shifted is never negative, and we don't + * need the general RIGHT_SHIFT macro. + */ + /* Y */ + outptr0[col] = (JSAMPLE) + ((ctab[r+R_Y_OFF] + ctab[g+G_Y_OFF] + ctab[b+B_Y_OFF]) + >> SCALEBITS); + /* Cb */ + outptr1[col] = (JSAMPLE) + ((ctab[r+R_CB_OFF] + ctab[g+G_CB_OFF] + ctab[b+B_CB_OFF]) + >> SCALEBITS); + /* Cr */ + outptr2[col] = (JSAMPLE) + ((ctab[r+R_CR_OFF] + ctab[g+G_CR_OFF] + ctab[b+B_CR_OFF]) + >> SCALEBITS); + } + } +} + +/* + * Convert some rows of samples to the JPEG colorspace. + * This version handles grayscale output with no conversion. + * The source can be either plain grayscale or YCbCr (since Y == gray). + */ + +METHODDEF(void) +grayscale_convert (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows) +{ + register JSAMPROW inptr; + register JSAMPROW outptr; + register JDIMENSION col; + JDIMENSION num_cols = cinfo->image_width; + int instride = cinfo->input_components; + + while (--num_rows >= 0) { + inptr = *input_buf++; + outptr = output_buf[0][output_row]; + output_row++; + for (col = 0; col < num_cols; col++) { + outptr[col] = inptr[0]; /* don't need GETJSAMPLE() here */ + inptr += instride; + } + } +} + +/* + * Convert some rows of samples to the JPEG colorspace. + * This version handles multi-component colorspaces without conversion. + * We assume input_components == num_components. + */ + +METHODDEF(void) +null_convert (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows) +{ + register JSAMPROW inptr; + register JSAMPROW outptr; + register JDIMENSION col; + register int ci; + int nc = cinfo->num_components; + JDIMENSION num_cols = cinfo->image_width; + + while (--num_rows >= 0) { + /* It seems fastest to make a separate pass for each component. */ + for (ci = 0; ci < nc; ci++) { + inptr = *input_buf; + outptr = output_buf[ci][output_row]; + for (col = 0; col < num_cols; col++) { + outptr[col] = inptr[ci]; /* don't need GETJSAMPLE() here */ + inptr += nc; + } + } + input_buf++; + output_row++; + } +} + +/* + * Empty method for start_pass. + */ + +METHODDEF(void) +null_method (j_compress_ptr cinfo) +{ + /* no work needed */ +} + +/* + * Module initialization routine for input colorspace conversion. + */ + +GLOBAL(void) +jinit_color_converter (j_compress_ptr cinfo) +{ + my_cconvert_ptr cconvert; + + cconvert = (my_cconvert_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_color_converter)); + cinfo->cconvert = (struct jpeg_color_converter *) cconvert; + /* set start_pass to null method until we find out differently */ + cconvert->pub.start_pass = null_method; + + /* Make sure input_components agrees with in_color_space */ + switch (cinfo->in_color_space) { + case JCS_GRAYSCALE: + if (cinfo->input_components != 1) + ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE); + break; + + case JCS_RGB: +#if RGB_PIXELSIZE != 3 + if (cinfo->input_components != RGB_PIXELSIZE) + ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE); + break; +#endif /* else share code with YCbCr */ + + case JCS_YCbCr: + if (cinfo->input_components != 3) + ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE); + break; + + case JCS_CMYK: + case JCS_YCCK: + if (cinfo->input_components != 4) + ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE); + break; + + default: /* JCS_UNKNOWN can be anything */ + if (cinfo->input_components < 1) + ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE); + break; + } + + /* Check num_components, set conversion method based on requested space */ + switch (cinfo->jpeg_color_space) { + case JCS_GRAYSCALE: + if (cinfo->num_components != 1) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + if (cinfo->in_color_space == JCS_GRAYSCALE) + cconvert->pub.color_convert = grayscale_convert; + else if (cinfo->in_color_space == JCS_RGB) { + cconvert->pub.start_pass = rgb_ycc_start; + cconvert->pub.color_convert = rgb_gray_convert; + } else if (cinfo->in_color_space == JCS_YCbCr) + cconvert->pub.color_convert = grayscale_convert; + else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + + case JCS_RGB: + if (cinfo->num_components != 3) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + if (cinfo->in_color_space == JCS_RGB && RGB_PIXELSIZE == 3) + cconvert->pub.color_convert = null_convert; + else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + + case JCS_YCbCr: + if (cinfo->num_components != 3) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + if (cinfo->in_color_space == JCS_RGB) { + cconvert->pub.start_pass = rgb_ycc_start; + cconvert->pub.color_convert = rgb_ycc_convert; + } else if (cinfo->in_color_space == JCS_YCbCr) + cconvert->pub.color_convert = null_convert; + else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + + case JCS_CMYK: + if (cinfo->num_components != 4) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + if (cinfo->in_color_space == JCS_CMYK) + cconvert->pub.color_convert = null_convert; + else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + + case JCS_YCCK: + if (cinfo->num_components != 4) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + if (cinfo->in_color_space == JCS_CMYK) { + cconvert->pub.start_pass = rgb_ycc_start; + cconvert->pub.color_convert = cmyk_ycck_convert; + } else if (cinfo->in_color_space == JCS_YCCK) + cconvert->pub.color_convert = null_convert; + else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + + default: /* allow null conversion of JCS_UNKNOWN */ + if (cinfo->jpeg_color_space != cinfo->in_color_space || + cinfo->num_components != cinfo->input_components) + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + cconvert->pub.color_convert = null_convert; + break; + } +} +/********* End of inlined file: jccolor.c *********/ + + #undef FIX + +/********* Start of inlined file: jcdctmgr.c *********/ +#define JPEG_INTERNALS + +/********* Start of inlined file: jdct.h *********/ +/* + * A forward DCT routine is given a pointer to a work area of type DCTELEM[]; + * the DCT is to be performed in-place in that buffer. Type DCTELEM is int + * for 8-bit samples, INT32 for 12-bit samples. (NOTE: Floating-point DCT + * implementations use an array of type FAST_FLOAT, instead.) + * The DCT inputs are expected to be signed (range +-CENTERJSAMPLE). + * The DCT outputs are returned scaled up by a factor of 8; they therefore + * have a range of +-8K for 8-bit data, +-128K for 12-bit data. This + * convention improves accuracy in integer implementations and saves some + * work in floating-point ones. + * Quantization of the output coefficients is done by jcdctmgr.c. + */ + +#ifndef __jdct_h__ +#define __jdct_h__ + +#if BITS_IN_JSAMPLE == 8 +typedef int DCTELEM; /* 16 or 32 bits is fine */ +#else +typedef INT32 DCTELEM; /* must have 32 bits */ +#endif + +typedef JMETHOD(void, forward_DCT_method_ptr, (DCTELEM * data)); +typedef JMETHOD(void, float_DCT_method_ptr, (FAST_FLOAT * data)); + +/* + * An inverse DCT routine is given a pointer to the input JBLOCK and a pointer + * to an output sample array. The routine must dequantize the input data as + * well as perform the IDCT; for dequantization, it uses the multiplier table + * pointed to by compptr->dct_table. The output data is to be placed into the + * sample array starting at a specified column. (Any row offset needed will + * be applied to the array pointer before it is passed to the IDCT code.) + * Note that the number of samples emitted by the IDCT routine is + * DCT_scaled_size * DCT_scaled_size. + */ + +/* typedef inverse_DCT_method_ptr is declared in jpegint.h */ + +/* + * Each IDCT routine has its own ideas about the best dct_table element type. + */ + +typedef MULTIPLIER ISLOW_MULT_TYPE; /* short or int, whichever is faster */ +#if BITS_IN_JSAMPLE == 8 +typedef MULTIPLIER IFAST_MULT_TYPE; /* 16 bits is OK, use short if faster */ +#define IFAST_SCALE_BITS 2 /* fractional bits in scale factors */ +#else +typedef INT32 IFAST_MULT_TYPE; /* need 32 bits for scaled quantizers */ +#define IFAST_SCALE_BITS 13 /* fractional bits in scale factors */ +#endif +typedef FAST_FLOAT FLOAT_MULT_TYPE; /* preferred floating type */ + +/* + * Each IDCT routine is responsible for range-limiting its results and + * converting them to unsigned form (0..MAXJSAMPLE). The raw outputs could + * be quite far out of range if the input data is corrupt, so a bulletproof + * range-limiting step is required. We use a mask-and-table-lookup method + * to do the combined operations quickly. See the comments with + * prepare_range_limit_table (in jdmaster.c) for more info. + */ + +#define IDCT_range_limit(cinfo) ((cinfo)->sample_range_limit + CENTERJSAMPLE) + +#define RANGE_MASK (MAXJSAMPLE * 4 + 3) /* 2 bits wider than legal samples */ + +/* Short forms of external names for systems with brain-damaged linkers. */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_fdct_islow jFDislow +#define jpeg_fdct_ifast jFDifast +#define jpeg_fdct_float jFDfloat +#define jpeg_idct_islow jRDislow +#define jpeg_idct_ifast jRDifast +#define jpeg_idct_float jRDfloat +#define jpeg_idct_4x4 jRD4x4 +#define jpeg_idct_2x2 jRD2x2 +#define jpeg_idct_1x1 jRD1x1 +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + +/* Extern declarations for the forward and inverse DCT routines. */ + +EXTERN(void) jpeg_fdct_islow JPP((DCTELEM * data)); +EXTERN(void) jpeg_fdct_ifast JPP((DCTELEM * data)); +EXTERN(void) jpeg_fdct_float JPP((FAST_FLOAT * data)); + +EXTERN(void) jpeg_idct_islow + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_ifast + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_float + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_4x4 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_2x2 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_1x1 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); + +/* + * Macros for handling fixed-point arithmetic; these are used by many + * but not all of the DCT/IDCT modules. + * + * All values are expected to be of type INT32. + * Fractional constants are scaled left by CONST_BITS bits. + * CONST_BITS is defined within each module using these macros, + * and may differ from one module to the next. + */ + +#define ONE ((INT32) 1) +#define CONST_SCALE (ONE << CONST_BITS) + +/* Convert a positive real constant to an integer scaled by CONST_SCALE. + * Caution: some C compilers fail to reduce "FIX(constant)" at compile time, + * thus causing a lot of useless floating-point operations at run time. + */ + +#define FIX(x) ((INT32) ((x) * CONST_SCALE + 0.5)) + +/* Descale and correctly round an INT32 value that's scaled by N bits. + * We assume RIGHT_SHIFT rounds towards minus infinity, so adding + * the fudge factor is correct for either sign of X. + */ + +#define DESCALE(x,n) RIGHT_SHIFT((x) + (ONE << ((n)-1)), n) + +/* Multiply an INT32 variable by an INT32 constant to yield an INT32 result. + * This macro is used only when the two inputs will actually be no more than + * 16 bits wide, so that a 16x16->32 bit multiply can be used instead of a + * full 32x32 multiply. This provides a useful speedup on many machines. + * Unfortunately there is no way to specify a 16x16->32 multiply portably + * in C, but some C compilers will do the right thing if you provide the + * correct combination of casts. + */ + +#ifdef SHORTxSHORT_32 /* may work if 'int' is 32 bits */ +#define MULTIPLY16C16(var,const) (((INT16) (var)) * ((INT16) (const))) +#endif +#ifdef SHORTxLCONST_32 /* known to work with Microsoft C 6.0 */ +#define MULTIPLY16C16(var,const) (((INT16) (var)) * ((INT32) (const))) +#endif + +#ifndef MULTIPLY16C16 /* default definition */ +#define MULTIPLY16C16(var,const) ((var) * (const)) +#endif + +/* Same except both inputs are variables. */ + +#ifdef SHORTxSHORT_32 /* may work if 'int' is 32 bits */ +#define MULTIPLY16V16(var1,var2) (((INT16) (var1)) * ((INT16) (var2))) +#endif + +#ifndef MULTIPLY16V16 /* default definition */ +#define MULTIPLY16V16(var1,var2) ((var1) * (var2)) +#endif + +#endif +/********* End of inlined file: jdct.h *********/ + + /* Private declarations for DCT subsystem */ + +/* Private subobject for this module */ + +typedef struct { + struct jpeg_forward_dct pub; /* public fields */ + + /* Pointer to the DCT routine actually in use */ + forward_DCT_method_ptr do_dct; + + /* The actual post-DCT divisors --- not identical to the quant table + * entries, because of scaling (especially for an unnormalized DCT). + * Each table is given in normal array order. + */ + DCTELEM * divisors[NUM_QUANT_TBLS]; + +#ifdef DCT_FLOAT_SUPPORTED + /* Same as above for the floating-point case. */ + float_DCT_method_ptr do_float_dct; + FAST_FLOAT * float_divisors[NUM_QUANT_TBLS]; +#endif +} my_fdct_controller; + +typedef my_fdct_controller * my_fdct_ptr; + +/* + * Initialize for a processing pass. + * Verify that all referenced Q-tables are present, and set up + * the divisor table for each one. + * In the current implementation, DCT of all components is done during + * the first pass, even if only some components will be output in the + * first scan. Hence all components should be examined here. + */ + +METHODDEF(void) +start_pass_fdctmgr (j_compress_ptr cinfo) +{ + my_fdct_ptr fdct = (my_fdct_ptr) cinfo->fdct; + int ci, qtblno, i; + jpeg_component_info *compptr; + JQUANT_TBL * qtbl; + DCTELEM * dtbl; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + qtblno = compptr->quant_tbl_no; + /* Make sure specified quantization table is present */ + if (qtblno < 0 || qtblno >= NUM_QUANT_TBLS || + cinfo->quant_tbl_ptrs[qtblno] == NULL) + ERREXIT1(cinfo, JERR_NO_QUANT_TABLE, qtblno); + qtbl = cinfo->quant_tbl_ptrs[qtblno]; + /* Compute divisors for this quant table */ + /* We may do this more than once for same table, but it's not a big deal */ + switch (cinfo->dct_method) { +#ifdef DCT_ISLOW_SUPPORTED + case JDCT_ISLOW: + /* For LL&M IDCT method, divisors are equal to raw quantization + * coefficients multiplied by 8 (to counteract scaling). + */ + if (fdct->divisors[qtblno] == NULL) { + fdct->divisors[qtblno] = (DCTELEM *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + DCTSIZE2 * SIZEOF(DCTELEM)); + } + dtbl = fdct->divisors[qtblno]; + for (i = 0; i < DCTSIZE2; i++) { + dtbl[i] = ((DCTELEM) qtbl->quantval[i]) << 3; + } + break; +#endif +#ifdef DCT_IFAST_SUPPORTED + case JDCT_IFAST: + { + /* For AA&N IDCT method, divisors are equal to quantization + * coefficients scaled by scalefactor[row]*scalefactor[col], where + * scalefactor[0] = 1 + * scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 + * We apply a further scale factor of 8. + */ +#define CONST_BITS 14 + static const INT16 aanscales[DCTSIZE2] = { + /* precomputed values scaled up by 14 bits */ + 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, + 22725, 31521, 29692, 26722, 22725, 17855, 12299, 6270, + 21407, 29692, 27969, 25172, 21407, 16819, 11585, 5906, + 19266, 26722, 25172, 22654, 19266, 15137, 10426, 5315, + 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, + 12873, 17855, 16819, 15137, 12873, 10114, 6967, 3552, + 8867, 12299, 11585, 10426, 8867, 6967, 4799, 2446, + 4520, 6270, 5906, 5315, 4520, 3552, 2446, 1247 + }; + SHIFT_TEMPS + + if (fdct->divisors[qtblno] == NULL) { + fdct->divisors[qtblno] = (DCTELEM *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + DCTSIZE2 * SIZEOF(DCTELEM)); + } + dtbl = fdct->divisors[qtblno]; + for (i = 0; i < DCTSIZE2; i++) { + dtbl[i] = (DCTELEM) + DESCALE(MULTIPLY16V16((INT32) qtbl->quantval[i], + (INT32) aanscales[i]), + CONST_BITS-3); + } + } + break; +#endif +#ifdef DCT_FLOAT_SUPPORTED + case JDCT_FLOAT: + { + /* For float AA&N IDCT method, divisors are equal to quantization + * coefficients scaled by scalefactor[row]*scalefactor[col], where + * scalefactor[0] = 1 + * scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 + * We apply a further scale factor of 8. + * What's actually stored is 1/divisor so that the inner loop can + * use a multiplication rather than a division. + */ + FAST_FLOAT * fdtbl; + int row, col; + static const double aanscalefactor[DCTSIZE] = { + 1.0, 1.387039845, 1.306562965, 1.175875602, + 1.0, 0.785694958, 0.541196100, 0.275899379 + }; + + if (fdct->float_divisors[qtblno] == NULL) { + fdct->float_divisors[qtblno] = (FAST_FLOAT *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + DCTSIZE2 * SIZEOF(FAST_FLOAT)); + } + fdtbl = fdct->float_divisors[qtblno]; + i = 0; + for (row = 0; row < DCTSIZE; row++) { + for (col = 0; col < DCTSIZE; col++) { + fdtbl[i] = (FAST_FLOAT) + (1.0 / (((double) qtbl->quantval[i] * + aanscalefactor[row] * aanscalefactor[col] * 8.0))); + i++; + } + } + } + break; +#endif + default: + ERREXIT(cinfo, JERR_NOT_COMPILED); + break; + } + } +} + +/* + * Perform forward DCT on one or more blocks of a component. + * + * The input samples are taken from the sample_data[] array starting at + * position start_row/start_col, and moving to the right for any additional + * blocks. The quantized coefficients are returned in coef_blocks[]. + */ + +METHODDEF(void) +forward_DCT (j_compress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY sample_data, JBLOCKROW coef_blocks, + JDIMENSION start_row, JDIMENSION start_col, + JDIMENSION num_blocks) +/* This version is used for integer DCT implementations. */ +{ + /* This routine is heavily used, so it's worth coding it tightly. */ + my_fdct_ptr fdct = (my_fdct_ptr) cinfo->fdct; + forward_DCT_method_ptr do_dct = fdct->do_dct; + DCTELEM * divisors = fdct->divisors[compptr->quant_tbl_no]; + DCTELEM workspace[DCTSIZE2]; /* work area for FDCT subroutine */ + JDIMENSION bi; + + sample_data += start_row; /* fold in the vertical offset once */ + + for (bi = 0; bi < num_blocks; bi++, start_col += DCTSIZE) { + /* Load data into workspace, applying unsigned->signed conversion */ + { register DCTELEM *workspaceptr; + register JSAMPROW elemptr; + register int elemr; + + workspaceptr = workspace; + for (elemr = 0; elemr < DCTSIZE; elemr++) { + elemptr = sample_data[elemr] + start_col; +#if DCTSIZE == 8 /* unroll the inner loop */ + *workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE; + *workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE; + *workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE; + *workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE; + *workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE; + *workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE; + *workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE; + *workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE; +#else + { register int elemc; + for (elemc = DCTSIZE; elemc > 0; elemc--) { + *workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE; + } + } +#endif + } + } + + /* Perform the DCT */ + (*do_dct) (workspace); + + /* Quantize/descale the coefficients, and store into coef_blocks[] */ + { register DCTELEM temp, qval; + register int i; + register JCOEFPTR output_ptr = coef_blocks[bi]; + + for (i = 0; i < DCTSIZE2; i++) { + qval = divisors[i]; + temp = workspace[i]; + /* Divide the coefficient value by qval, ensuring proper rounding. + * Since C does not specify the direction of rounding for negative + * quotients, we have to force the dividend positive for portability. + * + * In most files, at least half of the output values will be zero + * (at default quantization settings, more like three-quarters...) + * so we should ensure that this case is fast. On many machines, + * a comparison is enough cheaper than a divide to make a special test + * a win. Since both inputs will be nonnegative, we need only test + * for a < b to discover whether a/b is 0. + * If your machine's division is fast enough, define FAST_DIVIDE. + */ +#ifdef FAST_DIVIDE +#define DIVIDE_BY(a,b) a /= b +#else +#define DIVIDE_BY(a,b) if (a >= b) a /= b; else a = 0 +#endif + if (temp < 0) { + temp = -temp; + temp += qval>>1; /* for rounding */ + DIVIDE_BY(temp, qval); + temp = -temp; + } else { + temp += qval>>1; /* for rounding */ + DIVIDE_BY(temp, qval); + } + output_ptr[i] = (JCOEF) temp; + } + } + } +} + +#ifdef DCT_FLOAT_SUPPORTED + +METHODDEF(void) +forward_DCT_float (j_compress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY sample_data, JBLOCKROW coef_blocks, + JDIMENSION start_row, JDIMENSION start_col, + JDIMENSION num_blocks) +/* This version is used for floating-point DCT implementations. */ +{ + /* This routine is heavily used, so it's worth coding it tightly. */ + my_fdct_ptr fdct = (my_fdct_ptr) cinfo->fdct; + float_DCT_method_ptr do_dct = fdct->do_float_dct; + FAST_FLOAT * divisors = fdct->float_divisors[compptr->quant_tbl_no]; + FAST_FLOAT workspace[DCTSIZE2]; /* work area for FDCT subroutine */ + JDIMENSION bi; + + sample_data += start_row; /* fold in the vertical offset once */ + + for (bi = 0; bi < num_blocks; bi++, start_col += DCTSIZE) { + /* Load data into workspace, applying unsigned->signed conversion */ + { register FAST_FLOAT *workspaceptr; + register JSAMPROW elemptr; + register int elemr; + + workspaceptr = workspace; + for (elemr = 0; elemr < DCTSIZE; elemr++) { + elemptr = sample_data[elemr] + start_col; +#if DCTSIZE == 8 /* unroll the inner loop */ + *workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE); + *workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE); + *workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE); + *workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE); + *workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE); + *workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE); + *workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE); + *workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE); +#else + { register int elemc; + for (elemc = DCTSIZE; elemc > 0; elemc--) { + *workspaceptr++ = (FAST_FLOAT) + (GETJSAMPLE(*elemptr++) - CENTERJSAMPLE); + } + } +#endif + } + } + + /* Perform the DCT */ + (*do_dct) (workspace); + + /* Quantize/descale the coefficients, and store into coef_blocks[] */ + { register FAST_FLOAT temp; + register int i; + register JCOEFPTR output_ptr = coef_blocks[bi]; + + for (i = 0; i < DCTSIZE2; i++) { + /* Apply the quantization and scaling factor */ + temp = workspace[i] * divisors[i]; + /* Round to nearest integer. + * Since C does not specify the direction of rounding for negative + * quotients, we have to force the dividend positive for portability. + * The maximum coefficient size is +-16K (for 12-bit data), so this + * code should work for either 16-bit or 32-bit ints. + */ + output_ptr[i] = (JCOEF) ((int) (temp + (FAST_FLOAT) 16384.5) - 16384); + } + } + } +} + +#endif /* DCT_FLOAT_SUPPORTED */ + +/* + * Initialize FDCT manager. + */ + +GLOBAL(void) +jinit_forward_dct (j_compress_ptr cinfo) +{ + my_fdct_ptr fdct; + int i; + + fdct = (my_fdct_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_fdct_controller)); + cinfo->fdct = (struct jpeg_forward_dct *) fdct; + fdct->pub.start_pass = start_pass_fdctmgr; + + switch (cinfo->dct_method) { +#ifdef DCT_ISLOW_SUPPORTED + case JDCT_ISLOW: + fdct->pub.forward_DCT = forward_DCT; + fdct->do_dct = jpeg_fdct_islow; + break; +#endif +#ifdef DCT_IFAST_SUPPORTED + case JDCT_IFAST: + fdct->pub.forward_DCT = forward_DCT; + fdct->do_dct = jpeg_fdct_ifast; + break; +#endif +#ifdef DCT_FLOAT_SUPPORTED + case JDCT_FLOAT: + fdct->pub.forward_DCT = forward_DCT_float; + fdct->do_float_dct = jpeg_fdct_float; + break; +#endif + default: + ERREXIT(cinfo, JERR_NOT_COMPILED); + break; + } + + /* Mark divisor tables unallocated */ + for (i = 0; i < NUM_QUANT_TBLS; i++) { + fdct->divisors[i] = NULL; +#ifdef DCT_FLOAT_SUPPORTED + fdct->float_divisors[i] = NULL; +#endif + } +} +/********* End of inlined file: jcdctmgr.c *********/ + + #undef CONST_BITS + +/********* Start of inlined file: jchuff.c *********/ +#define JPEG_INTERNALS + +/********* Start of inlined file: jchuff.h *********/ +/* The legal range of a DCT coefficient is + * -1024 .. +1023 for 8-bit data; + * -16384 .. +16383 for 12-bit data. + * Hence the magnitude should always fit in 10 or 14 bits respectively. + */ + +#ifndef _jchuff_h_ +#define _jchuff_h_ + +#if BITS_IN_JSAMPLE == 8 +#define MAX_COEF_BITS 10 +#else +#define MAX_COEF_BITS 14 +#endif + +/* Derived data constructed for each Huffman table */ + +typedef struct { + unsigned int ehufco[256]; /* code for each symbol */ + char ehufsi[256]; /* length of code for each symbol */ + /* If no code has been allocated for a symbol S, ehufsi[S] contains 0 */ +} c_derived_tbl; + +/* Short forms of external names for systems with brain-damaged linkers. */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_make_c_derived_tbl jMkCDerived +#define jpeg_gen_optimal_table jGenOptTbl +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + +/* Expand a Huffman table definition into the derived format */ +EXTERN(void) jpeg_make_c_derived_tbl + JPP((j_compress_ptr cinfo, boolean isDC, int tblno, + c_derived_tbl ** pdtbl)); + +/* Generate an optimal table definition given the specified counts */ +EXTERN(void) jpeg_gen_optimal_table + JPP((j_compress_ptr cinfo, JHUFF_TBL * htbl, long freq[])); + +#endif +/********* End of inlined file: jchuff.h *********/ + + /* Declarations shared with jcphuff.c */ + +/* Expanded entropy encoder object for Huffman encoding. + * + * The savable_state subrecord contains fields that change within an MCU, + * but must not be updated permanently until we complete the MCU. + */ + +typedef struct { + INT32 put_buffer; /* current bit-accumulation buffer */ + int put_bits; /* # of bits now in it */ + int last_dc_val[MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ +} savable_state; + +/* This macro is to work around compilers with missing or broken + * structure assignment. You'll need to fix this code if you have + * such a compiler and you change MAX_COMPS_IN_SCAN. + */ + +#ifndef NO_STRUCT_ASSIGN +#define ASSIGN_STATE(dest,src) ((dest) = (src)) +#else +#if MAX_COMPS_IN_SCAN == 4 +#define ASSIGN_STATE(dest,src) \ + ((dest).put_buffer = (src).put_buffer, \ + (dest).put_bits = (src).put_bits, \ + (dest).last_dc_val[0] = (src).last_dc_val[0], \ + (dest).last_dc_val[1] = (src).last_dc_val[1], \ + (dest).last_dc_val[2] = (src).last_dc_val[2], \ + (dest).last_dc_val[3] = (src).last_dc_val[3]) +#endif +#endif + +typedef struct { + struct jpeg_entropy_encoder pub; /* public fields */ + + savable_state saved; /* Bit buffer & DC state at start of MCU */ + + /* These fields are NOT loaded into local working state. */ + unsigned int restarts_to_go; /* MCUs left in this restart interval */ + int next_restart_num; /* next restart number to write (0-7) */ + + /* Pointers to derived tables (these workspaces have image lifespan) */ + c_derived_tbl * dc_derived_tbls[NUM_HUFF_TBLS]; + c_derived_tbl * ac_derived_tbls[NUM_HUFF_TBLS]; + +#ifdef ENTROPY_OPT_SUPPORTED /* Statistics tables for optimization */ + long * dc_count_ptrs[NUM_HUFF_TBLS]; + long * ac_count_ptrs[NUM_HUFF_TBLS]; +#endif +} huff_entropy_encoder; + +typedef huff_entropy_encoder * huff_entropy_ptr; + +/* Working state while writing an MCU. + * This struct contains all the fields that are needed by subroutines. + */ + +typedef struct { + JOCTET * next_output_byte; /* => next byte to write in buffer */ + size_t free_in_buffer; /* # of byte spaces remaining in buffer */ + savable_state cur; /* Current bit buffer & DC state */ + j_compress_ptr cinfo; /* dump_buffer needs access to this */ +} working_state; + +/* Forward declarations */ +METHODDEF(boolean) encode_mcu_huff JPP((j_compress_ptr cinfo, + JBLOCKROW *MCU_data)); +METHODDEF(void) finish_pass_huff JPP((j_compress_ptr cinfo)); +#ifdef ENTROPY_OPT_SUPPORTED +METHODDEF(boolean) encode_mcu_gather JPP((j_compress_ptr cinfo, + JBLOCKROW *MCU_data)); +METHODDEF(void) finish_pass_gather JPP((j_compress_ptr cinfo)); +#endif + +/* + * Initialize for a Huffman-compressed scan. + * If gather_statistics is TRUE, we do not output anything during the scan, + * just count the Huffman symbols used and generate Huffman code tables. + */ + +METHODDEF(void) +start_pass_huff (j_compress_ptr cinfo, boolean gather_statistics) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + int ci, dctbl, actbl; + jpeg_component_info * compptr; + + if (gather_statistics) { +#ifdef ENTROPY_OPT_SUPPORTED + entropy->pub.encode_mcu = encode_mcu_gather; + entropy->pub.finish_pass = finish_pass_gather; +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else { + entropy->pub.encode_mcu = encode_mcu_huff; + entropy->pub.finish_pass = finish_pass_huff; + } + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + dctbl = compptr->dc_tbl_no; + actbl = compptr->ac_tbl_no; + if (gather_statistics) { +#ifdef ENTROPY_OPT_SUPPORTED + /* Check for invalid table indexes */ + /* (make_c_derived_tbl does this in the other path) */ + if (dctbl < 0 || dctbl >= NUM_HUFF_TBLS) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, dctbl); + if (actbl < 0 || actbl >= NUM_HUFF_TBLS) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, actbl); + /* Allocate and zero the statistics tables */ + /* Note that jpeg_gen_optimal_table expects 257 entries in each table! */ + if (entropy->dc_count_ptrs[dctbl] == NULL) + entropy->dc_count_ptrs[dctbl] = (long *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + 257 * SIZEOF(long)); + MEMZERO(entropy->dc_count_ptrs[dctbl], 257 * SIZEOF(long)); + if (entropy->ac_count_ptrs[actbl] == NULL) + entropy->ac_count_ptrs[actbl] = (long *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + 257 * SIZEOF(long)); + MEMZERO(entropy->ac_count_ptrs[actbl], 257 * SIZEOF(long)); +#endif + } else { + /* Compute derived values for Huffman tables */ + /* We may do this more than once for a table, but it's not expensive */ + jpeg_make_c_derived_tbl(cinfo, TRUE, dctbl, + & entropy->dc_derived_tbls[dctbl]); + jpeg_make_c_derived_tbl(cinfo, FALSE, actbl, + & entropy->ac_derived_tbls[actbl]); + } + /* Initialize DC predictions to 0 */ + entropy->saved.last_dc_val[ci] = 0; + } + + /* Initialize bit buffer to empty */ + entropy->saved.put_buffer = 0; + entropy->saved.put_bits = 0; + + /* Initialize restart stuff */ + entropy->restarts_to_go = cinfo->restart_interval; + entropy->next_restart_num = 0; +} + +/* + * Compute the derived values for a Huffman table. + * This routine also performs some validation checks on the table. + * + * Note this is also used by jcphuff.c. + */ + +GLOBAL(void) +jpeg_make_c_derived_tbl (j_compress_ptr cinfo, boolean isDC, int tblno, + c_derived_tbl ** pdtbl) +{ + JHUFF_TBL *htbl; + c_derived_tbl *dtbl; + int p, i, l, lastp, si, maxsymbol; + char huffsize[257]; + unsigned int huffcode[257]; + unsigned int code; + + /* Note that huffsize[] and huffcode[] are filled in code-length order, + * paralleling the order of the symbols themselves in htbl->huffval[]. + */ + + /* Find the input Huffman table */ + if (tblno < 0 || tblno >= NUM_HUFF_TBLS) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, tblno); + htbl = + isDC ? cinfo->dc_huff_tbl_ptrs[tblno] : cinfo->ac_huff_tbl_ptrs[tblno]; + if (htbl == NULL) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, tblno); + + /* Allocate a workspace if we haven't already done so. */ + if (*pdtbl == NULL) + *pdtbl = (c_derived_tbl *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(c_derived_tbl)); + dtbl = *pdtbl; + + /* Figure C.1: make table of Huffman code length for each symbol */ + + p = 0; + for (l = 1; l <= 16; l++) { + i = (int) htbl->bits[l]; + if (i < 0 || p + i > 256) /* protect against table overrun */ + ERREXIT(cinfo, JERR_BAD_HUFF_TABLE); + while (i--) + huffsize[p++] = (char) l; + } + huffsize[p] = 0; + lastp = p; + + /* Figure C.2: generate the codes themselves */ + /* We also validate that the counts represent a legal Huffman code tree. */ + + code = 0; + si = huffsize[0]; + p = 0; + while (huffsize[p]) { + while (((int) huffsize[p]) == si) { + huffcode[p++] = code; + code++; + } + /* code is now 1 more than the last code used for codelength si; but + * it must still fit in si bits, since no code is allowed to be all ones. + */ + if (((INT32) code) >= (((INT32) 1) << si)) + ERREXIT(cinfo, JERR_BAD_HUFF_TABLE); + code <<= 1; + si++; + } + + /* Figure C.3: generate encoding tables */ + /* These are code and size indexed by symbol value */ + + /* Set all codeless symbols to have code length 0; + * this lets us detect duplicate VAL entries here, and later + * allows emit_bits to detect any attempt to emit such symbols. + */ + MEMZERO(dtbl->ehufsi, SIZEOF(dtbl->ehufsi)); + + /* This is also a convenient place to check for out-of-range + * and duplicated VAL entries. We allow 0..255 for AC symbols + * but only 0..15 for DC. (We could constrain them further + * based on data depth and mode, but this seems enough.) + */ + maxsymbol = isDC ? 15 : 255; + + for (p = 0; p < lastp; p++) { + i = htbl->huffval[p]; + if (i < 0 || i > maxsymbol || dtbl->ehufsi[i]) + ERREXIT(cinfo, JERR_BAD_HUFF_TABLE); + dtbl->ehufco[i] = huffcode[p]; + dtbl->ehufsi[i] = huffsize[p]; + } +} + +/* Outputting bytes to the file */ + +/* Emit a byte, taking 'action' if must suspend. */ +#define emit_byte(state,val,action) \ + { *(state)->next_output_byte++ = (JOCTET) (val); \ + if (--(state)->free_in_buffer == 0) \ + if (! dump_buffer(state)) \ + { action; } } + +LOCAL(boolean) +dump_buffer (working_state * state) +/* Empty the output buffer; return TRUE if successful, FALSE if must suspend */ +{ + struct jpeg_destination_mgr * dest = state->cinfo->dest; + + if (! (*dest->empty_output_buffer) (state->cinfo)) + return FALSE; + /* After a successful buffer dump, must reset buffer pointers */ + state->next_output_byte = dest->next_output_byte; + state->free_in_buffer = dest->free_in_buffer; + return TRUE; +} + +/* Outputting bits to the file */ + +/* Only the right 24 bits of put_buffer are used; the valid bits are + * left-justified in this part. At most 16 bits can be passed to emit_bits + * in one call, and we never retain more than 7 bits in put_buffer + * between calls, so 24 bits are sufficient. + */ + +INLINE +LOCAL(boolean) +emit_bits (working_state * state, unsigned int code, int size) +/* Emit some bits; return TRUE if successful, FALSE if must suspend */ +{ + /* This routine is heavily used, so it's worth coding tightly. */ + register INT32 put_buffer = (INT32) code; + register int put_bits = state->cur.put_bits; + + /* if size is 0, caller used an invalid Huffman table entry */ + if (size == 0) + ERREXIT(state->cinfo, JERR_HUFF_MISSING_CODE); + + put_buffer &= (((INT32) 1)<cur.put_buffer; /* and merge with old buffer contents */ + + while (put_bits >= 8) { + int c = (int) ((put_buffer >> 16) & 0xFF); + + emit_byte(state, c, return FALSE); + if (c == 0xFF) { /* need to stuff a zero byte? */ + emit_byte(state, 0, return FALSE); + } + put_buffer <<= 8; + put_bits -= 8; + } + + state->cur.put_buffer = put_buffer; /* update state variables */ + state->cur.put_bits = put_bits; + + return TRUE; +} + +LOCAL(boolean) +flush_bits (working_state * state) +{ + if (! emit_bits(state, 0x7F, 7)) /* fill any partial byte with ones */ + return FALSE; + state->cur.put_buffer = 0; /* and reset bit-buffer to empty */ + state->cur.put_bits = 0; + return TRUE; +} + +/* Encode a single block's worth of coefficients */ + +LOCAL(boolean) +encode_one_block (working_state * state, JCOEFPTR block, int last_dc_val, + c_derived_tbl *dctbl, c_derived_tbl *actbl) +{ + register int temp, temp2; + register int nbits; + register int k, r, i; + + /* Encode the DC coefficient difference per section F.1.2.1 */ + + temp = temp2 = block[0] - last_dc_val; + + if (temp < 0) { + temp = -temp; /* temp is abs value of input */ + /* For a negative input, want temp2 = bitwise complement of abs(input) */ + /* This code assumes we are on a two's complement machine */ + temp2--; + } + + /* Find the number of bits needed for the magnitude of the coefficient */ + nbits = 0; + while (temp) { + nbits++; + temp >>= 1; + } + /* Check for out-of-range coefficient values. + * Since we're encoding a difference, the range limit is twice as much. + */ + if (nbits > MAX_COEF_BITS+1) + ERREXIT(state->cinfo, JERR_BAD_DCT_COEF); + + /* Emit the Huffman-coded symbol for the number of bits */ + if (! emit_bits(state, dctbl->ehufco[nbits], dctbl->ehufsi[nbits])) + return FALSE; + + /* Emit that number of bits of the value, if positive, */ + /* or the complement of its magnitude, if negative. */ + if (nbits) /* emit_bits rejects calls with size 0 */ + if (! emit_bits(state, (unsigned int) temp2, nbits)) + return FALSE; + + /* Encode the AC coefficients per section F.1.2.2 */ + + r = 0; /* r = run length of zeros */ + + for (k = 1; k < DCTSIZE2; k++) { + if ((temp = block[jpeg_natural_order[k]]) == 0) { + r++; + } else { + /* if run length > 15, must emit special run-length-16 codes (0xF0) */ + while (r > 15) { + if (! emit_bits(state, actbl->ehufco[0xF0], actbl->ehufsi[0xF0])) + return FALSE; + r -= 16; + } + + temp2 = temp; + if (temp < 0) { + temp = -temp; /* temp is abs value of input */ + /* This code assumes we are on a two's complement machine */ + temp2--; + } + + /* Find the number of bits needed for the magnitude of the coefficient */ + nbits = 1; /* there must be at least one 1 bit */ + while ((temp >>= 1)) + nbits++; + /* Check for out-of-range coefficient values */ + if (nbits > MAX_COEF_BITS) + ERREXIT(state->cinfo, JERR_BAD_DCT_COEF); + + /* Emit Huffman symbol for run length / number of bits */ + i = (r << 4) + nbits; + if (! emit_bits(state, actbl->ehufco[i], actbl->ehufsi[i])) + return FALSE; + + /* Emit that number of bits of the value, if positive, */ + /* or the complement of its magnitude, if negative. */ + if (! emit_bits(state, (unsigned int) temp2, nbits)) + return FALSE; + + r = 0; + } + } + + /* If the last coef(s) were zero, emit an end-of-block code */ + if (r > 0) + if (! emit_bits(state, actbl->ehufco[0], actbl->ehufsi[0])) + return FALSE; + + return TRUE; +} + +/* + * Emit a restart marker & resynchronize predictions. + */ + +LOCAL(boolean) +emit_restart (working_state * state, int restart_num) +{ + int ci; + + if (! flush_bits(state)) + return FALSE; + + emit_byte(state, 0xFF, return FALSE); + emit_byte(state, JPEG_RST0 + restart_num, return FALSE); + + /* Re-initialize DC predictions to 0 */ + for (ci = 0; ci < state->cinfo->comps_in_scan; ci++) + state->cur.last_dc_val[ci] = 0; + + /* The restart counter is not updated until we successfully write the MCU. */ + + return TRUE; +} + +/* + * Encode and output one MCU's worth of Huffman-compressed coefficients. + */ + +METHODDEF(boolean) +encode_mcu_huff (j_compress_ptr cinfo, JBLOCKROW *MCU_data) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + working_state state; + int blkn, ci; + jpeg_component_info * compptr; + + /* Load up working state */ + state.next_output_byte = cinfo->dest->next_output_byte; + state.free_in_buffer = cinfo->dest->free_in_buffer; + ASSIGN_STATE(state.cur, entropy->saved); + state.cinfo = cinfo; + + /* Emit restart marker if needed */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) + if (! emit_restart(&state, entropy->next_restart_num)) + return FALSE; + } + + /* Encode the MCU data blocks */ + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + ci = cinfo->MCU_membership[blkn]; + compptr = cinfo->cur_comp_info[ci]; + if (! encode_one_block(&state, + MCU_data[blkn][0], state.cur.last_dc_val[ci], + entropy->dc_derived_tbls[compptr->dc_tbl_no], + entropy->ac_derived_tbls[compptr->ac_tbl_no])) + return FALSE; + /* Update last_dc_val */ + state.cur.last_dc_val[ci] = MCU_data[blkn][0][0]; + } + + /* Completed MCU, so update state */ + cinfo->dest->next_output_byte = state.next_output_byte; + cinfo->dest->free_in_buffer = state.free_in_buffer; + ASSIGN_STATE(entropy->saved, state.cur); + + /* Update restart-interval state too */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) { + entropy->restarts_to_go = cinfo->restart_interval; + entropy->next_restart_num++; + entropy->next_restart_num &= 7; + } + entropy->restarts_to_go--; + } + + return TRUE; +} + +/* + * Finish up at the end of a Huffman-compressed scan. + */ + +METHODDEF(void) +finish_pass_huff (j_compress_ptr cinfo) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + working_state state; + + /* Load up working state ... flush_bits needs it */ + state.next_output_byte = cinfo->dest->next_output_byte; + state.free_in_buffer = cinfo->dest->free_in_buffer; + ASSIGN_STATE(state.cur, entropy->saved); + state.cinfo = cinfo; + + /* Flush out the last data */ + if (! flush_bits(&state)) + ERREXIT(cinfo, JERR_CANT_SUSPEND); + + /* Update state */ + cinfo->dest->next_output_byte = state.next_output_byte; + cinfo->dest->free_in_buffer = state.free_in_buffer; + ASSIGN_STATE(entropy->saved, state.cur); +} + +/* + * Huffman coding optimization. + * + * We first scan the supplied data and count the number of uses of each symbol + * that is to be Huffman-coded. (This process MUST agree with the code above.) + * Then we build a Huffman coding tree for the observed counts. + * Symbols which are not needed at all for the particular image are not + * assigned any code, which saves space in the DHT marker as well as in + * the compressed data. + */ + +#ifdef ENTROPY_OPT_SUPPORTED + +/* Process a single block's worth of coefficients */ + +LOCAL(void) +htest_one_block (j_compress_ptr cinfo, JCOEFPTR block, int last_dc_val, + long dc_counts[], long ac_counts[]) +{ + register int temp; + register int nbits; + register int k, r; + + /* Encode the DC coefficient difference per section F.1.2.1 */ + + temp = block[0] - last_dc_val; + if (temp < 0) + temp = -temp; + + /* Find the number of bits needed for the magnitude of the coefficient */ + nbits = 0; + while (temp) { + nbits++; + temp >>= 1; + } + /* Check for out-of-range coefficient values. + * Since we're encoding a difference, the range limit is twice as much. + */ + if (nbits > MAX_COEF_BITS+1) + ERREXIT(cinfo, JERR_BAD_DCT_COEF); + + /* Count the Huffman symbol for the number of bits */ + dc_counts[nbits]++; + + /* Encode the AC coefficients per section F.1.2.2 */ + + r = 0; /* r = run length of zeros */ + + for (k = 1; k < DCTSIZE2; k++) { + if ((temp = block[jpeg_natural_order[k]]) == 0) { + r++; + } else { + /* if run length > 15, must emit special run-length-16 codes (0xF0) */ + while (r > 15) { + ac_counts[0xF0]++; + r -= 16; + } + + /* Find the number of bits needed for the magnitude of the coefficient */ + if (temp < 0) + temp = -temp; + + /* Find the number of bits needed for the magnitude of the coefficient */ + nbits = 1; /* there must be at least one 1 bit */ + while ((temp >>= 1)) + nbits++; + /* Check for out-of-range coefficient values */ + if (nbits > MAX_COEF_BITS) + ERREXIT(cinfo, JERR_BAD_DCT_COEF); + + /* Count Huffman symbol for run length / number of bits */ + ac_counts[(r << 4) + nbits]++; + + r = 0; + } + } + + /* If the last coef(s) were zero, emit an end-of-block code */ + if (r > 0) + ac_counts[0]++; +} + +/* + * Trial-encode one MCU's worth of Huffman-compressed coefficients. + * No data is actually output, so no suspension return is possible. + */ + +METHODDEF(boolean) +encode_mcu_gather (j_compress_ptr cinfo, JBLOCKROW *MCU_data) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + int blkn, ci; + jpeg_component_info * compptr; + + /* Take care of restart intervals if needed */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) { + /* Re-initialize DC predictions to 0 */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) + entropy->saved.last_dc_val[ci] = 0; + /* Update restart state */ + entropy->restarts_to_go = cinfo->restart_interval; + } + entropy->restarts_to_go--; + } + + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + ci = cinfo->MCU_membership[blkn]; + compptr = cinfo->cur_comp_info[ci]; + htest_one_block(cinfo, MCU_data[blkn][0], entropy->saved.last_dc_val[ci], + entropy->dc_count_ptrs[compptr->dc_tbl_no], + entropy->ac_count_ptrs[compptr->ac_tbl_no]); + entropy->saved.last_dc_val[ci] = MCU_data[blkn][0][0]; + } + + return TRUE; +} + +/* + * Generate the best Huffman code table for the given counts, fill htbl. + * Note this is also used by jcphuff.c. + * + * The JPEG standard requires that no symbol be assigned a codeword of all + * one bits (so that padding bits added at the end of a compressed segment + * can't look like a valid code). Because of the canonical ordering of + * codewords, this just means that there must be an unused slot in the + * longest codeword length category. Section K.2 of the JPEG spec suggests + * reserving such a slot by pretending that symbol 256 is a valid symbol + * with count 1. In theory that's not optimal; giving it count zero but + * including it in the symbol set anyway should give a better Huffman code. + * But the theoretically better code actually seems to come out worse in + * practice, because it produces more all-ones bytes (which incur stuffed + * zero bytes in the final file). In any case the difference is tiny. + * + * The JPEG standard requires Huffman codes to be no more than 16 bits long. + * If some symbols have a very small but nonzero probability, the Huffman tree + * must be adjusted to meet the code length restriction. We currently use + * the adjustment method suggested in JPEG section K.2. This method is *not* + * optimal; it may not choose the best possible limited-length code. But + * typically only very-low-frequency symbols will be given less-than-optimal + * lengths, so the code is almost optimal. Experimental comparisons against + * an optimal limited-length-code algorithm indicate that the difference is + * microscopic --- usually less than a hundredth of a percent of total size. + * So the extra complexity of an optimal algorithm doesn't seem worthwhile. + */ + +GLOBAL(void) +jpeg_gen_optimal_table (j_compress_ptr cinfo, JHUFF_TBL * htbl, long freq[]) +{ +#define MAX_CLEN 32 /* assumed maximum initial code length */ + UINT8 bits[MAX_CLEN+1]; /* bits[k] = # of symbols with code length k */ + int codesize[257]; /* codesize[k] = code length of symbol k */ + int others[257]; /* next symbol in current branch of tree */ + int c1, c2; + int p, i, j; + long v; + + /* This algorithm is explained in section K.2 of the JPEG standard */ + + MEMZERO(bits, SIZEOF(bits)); + MEMZERO(codesize, SIZEOF(codesize)); + for (i = 0; i < 257; i++) + others[i] = -1; /* init links to empty */ + + freq[256] = 1; /* make sure 256 has a nonzero count */ + /* Including the pseudo-symbol 256 in the Huffman procedure guarantees + * that no real symbol is given code-value of all ones, because 256 + * will be placed last in the largest codeword category. + */ + + /* Huffman's basic algorithm to assign optimal code lengths to symbols */ + + for (;;) { + /* Find the smallest nonzero frequency, set c1 = its symbol */ + /* In case of ties, take the larger symbol number */ + c1 = -1; + v = 1000000000L; + for (i = 0; i <= 256; i++) { + if (freq[i] && freq[i] <= v) { + v = freq[i]; + c1 = i; + } + } + + /* Find the next smallest nonzero frequency, set c2 = its symbol */ + /* In case of ties, take the larger symbol number */ + c2 = -1; + v = 1000000000L; + for (i = 0; i <= 256; i++) { + if (freq[i] && freq[i] <= v && i != c1) { + v = freq[i]; + c2 = i; + } + } + + /* Done if we've merged everything into one frequency */ + if (c2 < 0) + break; + + /* Else merge the two counts/trees */ + freq[c1] += freq[c2]; + freq[c2] = 0; + + /* Increment the codesize of everything in c1's tree branch */ + codesize[c1]++; + while (others[c1] >= 0) { + c1 = others[c1]; + codesize[c1]++; + } + + others[c1] = c2; /* chain c2 onto c1's tree branch */ + + /* Increment the codesize of everything in c2's tree branch */ + codesize[c2]++; + while (others[c2] >= 0) { + c2 = others[c2]; + codesize[c2]++; + } + } + + /* Now count the number of symbols of each code length */ + for (i = 0; i <= 256; i++) { + if (codesize[i]) { + /* The JPEG standard seems to think that this can't happen, */ + /* but I'm paranoid... */ + if (codesize[i] > MAX_CLEN) + ERREXIT(cinfo, JERR_HUFF_CLEN_OVERFLOW); + + bits[codesize[i]]++; + } + } + + /* JPEG doesn't allow symbols with code lengths over 16 bits, so if the pure + * Huffman procedure assigned any such lengths, we must adjust the coding. + * Here is what the JPEG spec says about how this next bit works: + * Since symbols are paired for the longest Huffman code, the symbols are + * removed from this length category two at a time. The prefix for the pair + * (which is one bit shorter) is allocated to one of the pair; then, + * skipping the BITS entry for that prefix length, a code word from the next + * shortest nonzero BITS entry is converted into a prefix for two code words + * one bit longer. + */ + + for (i = MAX_CLEN; i > 16; i--) { + while (bits[i] > 0) { + j = i - 2; /* find length of new prefix to be used */ + while (bits[j] == 0) + j--; + + bits[i] -= 2; /* remove two symbols */ + bits[i-1]++; /* one goes in this length */ + bits[j+1] += 2; /* two new symbols in this length */ + bits[j]--; /* symbol of this length is now a prefix */ + } + } + + /* Remove the count for the pseudo-symbol 256 from the largest codelength */ + while (bits[i] == 0) /* find largest codelength still in use */ + i--; + bits[i]--; + + /* Return final symbol counts (only for lengths 0..16) */ + MEMCOPY(htbl->bits, bits, SIZEOF(htbl->bits)); + + /* Return a list of the symbols sorted by code length */ + /* It's not real clear to me why we don't need to consider the codelength + * changes made above, but the JPEG spec seems to think this works. + */ + p = 0; + for (i = 1; i <= MAX_CLEN; i++) { + for (j = 0; j <= 255; j++) { + if (codesize[j] == i) { + htbl->huffval[p] = (UINT8) j; + p++; + } + } + } + + /* Set sent_table FALSE so updated table will be written to JPEG file. */ + htbl->sent_table = FALSE; +} + +/* + * Finish up a statistics-gathering pass and create the new Huffman tables. + */ + +METHODDEF(void) +finish_pass_gather (j_compress_ptr cinfo) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + int ci, dctbl, actbl; + jpeg_component_info * compptr; + JHUFF_TBL **htblptr; + boolean did_dc[NUM_HUFF_TBLS]; + boolean did_ac[NUM_HUFF_TBLS]; + + /* It's important not to apply jpeg_gen_optimal_table more than once + * per table, because it clobbers the input frequency counts! + */ + MEMZERO(did_dc, SIZEOF(did_dc)); + MEMZERO(did_ac, SIZEOF(did_ac)); + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + dctbl = compptr->dc_tbl_no; + actbl = compptr->ac_tbl_no; + if (! did_dc[dctbl]) { + htblptr = & cinfo->dc_huff_tbl_ptrs[dctbl]; + if (*htblptr == NULL) + *htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo); + jpeg_gen_optimal_table(cinfo, *htblptr, entropy->dc_count_ptrs[dctbl]); + did_dc[dctbl] = TRUE; + } + if (! did_ac[actbl]) { + htblptr = & cinfo->ac_huff_tbl_ptrs[actbl]; + if (*htblptr == NULL) + *htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo); + jpeg_gen_optimal_table(cinfo, *htblptr, entropy->ac_count_ptrs[actbl]); + did_ac[actbl] = TRUE; + } + } +} + +#endif /* ENTROPY_OPT_SUPPORTED */ + +/* + * Module initialization routine for Huffman entropy encoding. + */ + +GLOBAL(void) +jinit_huff_encoder (j_compress_ptr cinfo) +{ + huff_entropy_ptr entropy; + int i; + + entropy = (huff_entropy_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(huff_entropy_encoder)); + cinfo->entropy = (struct jpeg_entropy_encoder *) entropy; + entropy->pub.start_pass = start_pass_huff; + + /* Mark tables unallocated */ + for (i = 0; i < NUM_HUFF_TBLS; i++) { + entropy->dc_derived_tbls[i] = entropy->ac_derived_tbls[i] = NULL; +#ifdef ENTROPY_OPT_SUPPORTED + entropy->dc_count_ptrs[i] = entropy->ac_count_ptrs[i] = NULL; +#endif + } +} +/********* End of inlined file: jchuff.c *********/ + + #undef emit_byte + +/********* Start of inlined file: jcinit.c *********/ +#define JPEG_INTERNALS + +/* + * Master selection of compression modules. + * This is done once at the start of processing an image. We determine + * which modules will be used and give them appropriate initialization calls. + */ + +GLOBAL(void) +jinit_compress_master (j_compress_ptr cinfo) +{ + /* Initialize master control (includes parameter checking/processing) */ + jinit_c_master_control(cinfo, FALSE /* full compression */); + + /* Preprocessing */ + if (! cinfo->raw_data_in) { + jinit_color_converter(cinfo); + jinit_downsampler(cinfo); + jinit_c_prep_controller(cinfo, FALSE /* never need full buffer here */); + } + /* Forward DCT */ + jinit_forward_dct(cinfo); + /* Entropy encoding: either Huffman or arithmetic coding. */ + if (cinfo->arith_code) { + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); + } else { + if (cinfo->progressive_mode) { +#ifdef C_PROGRESSIVE_SUPPORTED + jinit_phuff_encoder(cinfo); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else + jinit_huff_encoder(cinfo); + } + + /* Need a full-image coefficient buffer in any multi-pass mode. */ + jinit_c_coef_controller(cinfo, + (boolean) (cinfo->num_scans > 1 || cinfo->optimize_coding)); + jinit_c_main_controller(cinfo, FALSE /* never need full buffer here */); + + jinit_marker_writer(cinfo); + + /* We can now tell the memory manager to allocate virtual arrays. */ + (*cinfo->mem->realize_virt_arrays) ((j_common_ptr) cinfo); + + /* Write the datastream header (SOI) immediately. + * Frame and scan headers are postponed till later. + * This lets application insert special markers after the SOI. + */ + (*cinfo->marker->write_file_header) (cinfo); +} +/********* End of inlined file: jcinit.c *********/ + +/********* Start of inlined file: jcmainct.c *********/ +#define JPEG_INTERNALS + +/* Note: currently, there is no operating mode in which a full-image buffer + * is needed at this step. If there were, that mode could not be used with + * "raw data" input, since this module is bypassed in that case. However, + * we've left the code here for possible use in special applications. + */ +#undef FULL_MAIN_BUFFER_SUPPORTED + +/* Private buffer controller object */ + +typedef struct { + struct jpeg_c_main_controller pub; /* public fields */ + + JDIMENSION cur_iMCU_row; /* number of current iMCU row */ + JDIMENSION rowgroup_ctr; /* counts row groups received in iMCU row */ + boolean suspended; /* remember if we suspended output */ + J_BUF_MODE pass_mode; /* current operating mode */ + + /* If using just a strip buffer, this points to the entire set of buffers + * (we allocate one for each component). In the full-image case, this + * points to the currently accessible strips of the virtual arrays. + */ + JSAMPARRAY buffer[MAX_COMPONENTS]; + +#ifdef FULL_MAIN_BUFFER_SUPPORTED + /* If using full-image storage, this array holds pointers to virtual-array + * control blocks for each component. Unused if not full-image storage. + */ + jvirt_sarray_ptr whole_image[MAX_COMPONENTS]; +#endif +} my_main_controller; + +typedef my_main_controller * my_main_ptr; + +/* Forward declarations */ +METHODDEF(void) process_data_simple_main + JPP((j_compress_ptr cinfo, JSAMPARRAY input_buf, + JDIMENSION *in_row_ctr, JDIMENSION in_rows_avail)); +#ifdef FULL_MAIN_BUFFER_SUPPORTED +METHODDEF(void) process_data_buffer_main + JPP((j_compress_ptr cinfo, JSAMPARRAY input_buf, + JDIMENSION *in_row_ctr, JDIMENSION in_rows_avail)); +#endif + +/* + * Initialize for a processing pass. + */ + +METHODDEF(void) +start_pass_main (j_compress_ptr cinfo, J_BUF_MODE pass_mode) +{ + my_main_ptr main_ = (my_main_ptr) cinfo->main; + + /* Do nothing in raw-data mode. */ + if (cinfo->raw_data_in) + return; + + main_->cur_iMCU_row = 0; /* initialize counters */ + main_->rowgroup_ctr = 0; + main_->suspended = FALSE; + main_->pass_mode = pass_mode; /* save mode for use by process_data */ + + switch (pass_mode) { + case JBUF_PASS_THRU: +#ifdef FULL_MAIN_BUFFER_SUPPORTED + if (main_->whole_image[0] != NULL) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); +#endif + main_->pub.process_data = process_data_simple_main; + break; +#ifdef FULL_MAIN_BUFFER_SUPPORTED + case JBUF_SAVE_SOURCE: + case JBUF_CRANK_DEST: + case JBUF_SAVE_AND_PASS: + if (main_->whole_image[0] == NULL) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + main_->pub.process_data = process_data_buffer_main; + break; +#endif + default: + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + break; + } +} + +/* + * Process some data. + * This routine handles the simple pass-through mode, + * where we have only a strip buffer. + */ + +METHODDEF(void) +process_data_simple_main (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JDIMENSION *in_row_ctr, + JDIMENSION in_rows_avail) +{ + my_main_ptr main_ = (my_main_ptr) cinfo->main; + + while (main_->cur_iMCU_row < cinfo->total_iMCU_rows) { + /* Read input data if we haven't filled the main buffer yet */ + if (main_->rowgroup_ctr < DCTSIZE) + (*cinfo->prep->pre_process_data) (cinfo, + input_buf, in_row_ctr, in_rows_avail, + main_->buffer, &main_->rowgroup_ctr, + (JDIMENSION) DCTSIZE); + + /* If we don't have a full iMCU row buffered, return to application for + * more data. Note that preprocessor will always pad to fill the iMCU row + * at the bottom of the image. + */ + if (main_->rowgroup_ctr != DCTSIZE) + return; + + /* Send the completed row to the compressor */ + if (! (*cinfo->coef->compress_data) (cinfo, main_->buffer)) { + /* If compressor did not consume the whole row, then we must need to + * suspend processing and return to the application. In this situation + * we pretend we didn't yet consume the last input row; otherwise, if + * it happened to be the last row of the image, the application would + * think we were done. + */ + if (! main_->suspended) { + (*in_row_ctr)--; + main_->suspended = TRUE; + } + return; + } + /* We did finish the row. Undo our little suspension hack if a previous + * call suspended; then mark the main buffer empty. + */ + if (main_->suspended) { + (*in_row_ctr)++; + main_->suspended = FALSE; + } + main_->rowgroup_ctr = 0; + main_->cur_iMCU_row++; + } +} + +#ifdef FULL_MAIN_BUFFER_SUPPORTED + +/* + * Process some data. + * This routine handles all of the modes that use a full-size buffer. + */ + +METHODDEF(void) +process_data_buffer_main (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JDIMENSION *in_row_ctr, + JDIMENSION in_rows_avail) +{ + my_main_ptr main = (my_main_ptr) cinfo->main; + int ci; + jpeg_component_info *compptr; + boolean writing = (main->pass_mode != JBUF_CRANK_DEST); + + while (main->cur_iMCU_row < cinfo->total_iMCU_rows) { + /* Realign the virtual buffers if at the start of an iMCU row. */ + if (main->rowgroup_ctr == 0) { + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + main->buffer[ci] = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, main->whole_image[ci], + main->cur_iMCU_row * (compptr->v_samp_factor * DCTSIZE), + (JDIMENSION) (compptr->v_samp_factor * DCTSIZE), writing); + } + /* In a read pass, pretend we just read some source data. */ + if (! writing) { + *in_row_ctr += cinfo->max_v_samp_factor * DCTSIZE; + main->rowgroup_ctr = DCTSIZE; + } + } + + /* If a write pass, read input data until the current iMCU row is full. */ + /* Note: preprocessor will pad if necessary to fill the last iMCU row. */ + if (writing) { + (*cinfo->prep->pre_process_data) (cinfo, + input_buf, in_row_ctr, in_rows_avail, + main->buffer, &main->rowgroup_ctr, + (JDIMENSION) DCTSIZE); + /* Return to application if we need more data to fill the iMCU row. */ + if (main->rowgroup_ctr < DCTSIZE) + return; + } + + /* Emit data, unless this is a sink-only pass. */ + if (main->pass_mode != JBUF_SAVE_SOURCE) { + if (! (*cinfo->coef->compress_data) (cinfo, main->buffer)) { + /* If compressor did not consume the whole row, then we must need to + * suspend processing and return to the application. In this situation + * we pretend we didn't yet consume the last input row; otherwise, if + * it happened to be the last row of the image, the application would + * think we were done. + */ + if (! main->suspended) { + (*in_row_ctr)--; + main->suspended = TRUE; + } + return; + } + /* We did finish the row. Undo our little suspension hack if a previous + * call suspended; then mark the main buffer empty. + */ + if (main->suspended) { + (*in_row_ctr)++; + main->suspended = FALSE; + } + } + + /* If get here, we are done with this iMCU row. Mark buffer empty. */ + main->rowgroup_ctr = 0; + main->cur_iMCU_row++; + } +} + +#endif /* FULL_MAIN_BUFFER_SUPPORTED */ + +/* + * Initialize main buffer controller. + */ + +GLOBAL(void) +jinit_c_main_controller (j_compress_ptr cinfo, boolean need_full_buffer) +{ + my_main_ptr main_; + int ci; + jpeg_component_info *compptr; + + main_ = (my_main_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_main_controller)); + cinfo->main = (struct jpeg_c_main_controller *) main_; + main_->pub.start_pass = start_pass_main; + + /* We don't need to create a buffer in raw-data mode. */ + if (cinfo->raw_data_in) + return; + + /* Create the buffer. It holds downsampled data, so each component + * may be of a different size. + */ + if (need_full_buffer) { +#ifdef FULL_MAIN_BUFFER_SUPPORTED + /* Allocate a full-image virtual array for each component */ + /* Note we pad the bottom to a multiple of the iMCU height */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + main->whole_image[ci] = (*cinfo->mem->request_virt_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, + compptr->width_in_blocks * DCTSIZE, + (JDIMENSION) jround_up((long) compptr->height_in_blocks, + (long) compptr->v_samp_factor) * DCTSIZE, + (JDIMENSION) (compptr->v_samp_factor * DCTSIZE)); + } +#else + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); +#endif + } else { +#ifdef FULL_MAIN_BUFFER_SUPPORTED + main_->whole_image[0] = NULL; /* flag for no virtual arrays */ +#endif + /* Allocate a strip buffer for each component */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + main_->buffer[ci] = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + compptr->width_in_blocks * DCTSIZE, + (JDIMENSION) (compptr->v_samp_factor * DCTSIZE)); + } + } +} +/********* End of inlined file: jcmainct.c *********/ + +/********* Start of inlined file: jcmarker.c *********/ +#define JPEG_INTERNALS + +/* Private state */ + +typedef struct { + struct jpeg_marker_writer pub; /* public fields */ + + unsigned int last_restart_interval; /* last DRI value emitted; 0 after SOI */ +} my_marker_writer; + +typedef my_marker_writer * my_marker_ptr; + +/* + * Basic output routines. + * + * Note that we do not support suspension while writing a marker. + * Therefore, an application using suspension must ensure that there is + * enough buffer space for the initial markers (typ. 600-700 bytes) before + * calling jpeg_start_compress, and enough space to write the trailing EOI + * (a few bytes) before calling jpeg_finish_compress. Multipass compression + * modes are not supported at all with suspension, so those two are the only + * points where markers will be written. + */ + +LOCAL(void) +emit_byte (j_compress_ptr cinfo, int val) +/* Emit a byte */ +{ + struct jpeg_destination_mgr * dest = cinfo->dest; + + *(dest->next_output_byte)++ = (JOCTET) val; + if (--dest->free_in_buffer == 0) { + if (! (*dest->empty_output_buffer) (cinfo)) + ERREXIT(cinfo, JERR_CANT_SUSPEND); + } +} + +LOCAL(void) +emit_marker (j_compress_ptr cinfo, JPEG_MARKER mark) +/* Emit a marker code */ +{ + emit_byte(cinfo, 0xFF); + emit_byte(cinfo, (int) mark); +} + +LOCAL(void) +emit_2bytes (j_compress_ptr cinfo, int value) +/* Emit a 2-byte integer; these are always MSB first in JPEG files */ +{ + emit_byte(cinfo, (value >> 8) & 0xFF); + emit_byte(cinfo, value & 0xFF); +} + +/* + * Routines to write specific marker types. + */ + +LOCAL(int) +emit_dqt (j_compress_ptr cinfo, int index) +/* Emit a DQT marker */ +/* Returns the precision used (0 = 8bits, 1 = 16bits) for baseline checking */ +{ + JQUANT_TBL * qtbl = cinfo->quant_tbl_ptrs[index]; + int prec; + int i; + + if (qtbl == NULL) + ERREXIT1(cinfo, JERR_NO_QUANT_TABLE, index); + + prec = 0; + for (i = 0; i < DCTSIZE2; i++) { + if (qtbl->quantval[i] > 255) + prec = 1; + } + + if (! qtbl->sent_table) { + emit_marker(cinfo, M_DQT); + + emit_2bytes(cinfo, prec ? DCTSIZE2*2 + 1 + 2 : DCTSIZE2 + 1 + 2); + + emit_byte(cinfo, index + (prec<<4)); + + for (i = 0; i < DCTSIZE2; i++) { + /* The table entries must be emitted in zigzag order. */ + unsigned int qval = qtbl->quantval[jpeg_natural_order[i]]; + if (prec) + emit_byte(cinfo, (int) (qval >> 8)); + emit_byte(cinfo, (int) (qval & 0xFF)); + } + + qtbl->sent_table = TRUE; + } + + return prec; +} + +LOCAL(void) +emit_dht (j_compress_ptr cinfo, int index, boolean is_ac) +/* Emit a DHT marker */ +{ + JHUFF_TBL * htbl; + int length, i; + + if (is_ac) { + htbl = cinfo->ac_huff_tbl_ptrs[index]; + index += 0x10; /* output index has AC bit set */ + } else { + htbl = cinfo->dc_huff_tbl_ptrs[index]; + } + + if (htbl == NULL) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, index); + + if (! htbl->sent_table) { + emit_marker(cinfo, M_DHT); + + length = 0; + for (i = 1; i <= 16; i++) + length += htbl->bits[i]; + + emit_2bytes(cinfo, length + 2 + 1 + 16); + emit_byte(cinfo, index); + + for (i = 1; i <= 16; i++) + emit_byte(cinfo, htbl->bits[i]); + + for (i = 0; i < length; i++) + emit_byte(cinfo, htbl->huffval[i]); + + htbl->sent_table = TRUE; + } +} + +LOCAL(void) +emit_dac (j_compress_ptr cinfo) +/* Emit a DAC marker */ +/* Since the useful info is so small, we want to emit all the tables in */ +/* one DAC marker. Therefore this routine does its own scan of the table. */ +{ +#ifdef C_ARITH_CODING_SUPPORTED + char dc_in_use[NUM_ARITH_TBLS]; + char ac_in_use[NUM_ARITH_TBLS]; + int length, i; + jpeg_component_info *compptr; + + for (i = 0; i < NUM_ARITH_TBLS; i++) + dc_in_use[i] = ac_in_use[i] = 0; + + for (i = 0; i < cinfo->comps_in_scan; i++) { + compptr = cinfo->cur_comp_info[i]; + dc_in_use[compptr->dc_tbl_no] = 1; + ac_in_use[compptr->ac_tbl_no] = 1; + } + + length = 0; + for (i = 0; i < NUM_ARITH_TBLS; i++) + length += dc_in_use[i] + ac_in_use[i]; + + emit_marker(cinfo, M_DAC); + + emit_2bytes(cinfo, length*2 + 2); + + for (i = 0; i < NUM_ARITH_TBLS; i++) { + if (dc_in_use[i]) { + emit_byte(cinfo, i); + emit_byte(cinfo, cinfo->arith_dc_L[i] + (cinfo->arith_dc_U[i]<<4)); + } + if (ac_in_use[i]) { + emit_byte(cinfo, i + 0x10); + emit_byte(cinfo, cinfo->arith_ac_K[i]); + } + } +#endif /* C_ARITH_CODING_SUPPORTED */ +} + +LOCAL(void) +emit_dri (j_compress_ptr cinfo) +/* Emit a DRI marker */ +{ + emit_marker(cinfo, M_DRI); + + emit_2bytes(cinfo, 4); /* fixed length */ + + emit_2bytes(cinfo, (int) cinfo->restart_interval); +} + +LOCAL(void) +emit_sof (j_compress_ptr cinfo, JPEG_MARKER code) +/* Emit a SOF marker */ +{ + int ci; + jpeg_component_info *compptr; + + emit_marker(cinfo, code); + + emit_2bytes(cinfo, 3 * cinfo->num_components + 2 + 5 + 1); /* length */ + + /* Make sure image isn't bigger than SOF field can handle */ + if ((long) cinfo->image_height > 65535L || + (long) cinfo->image_width > 65535L) + ERREXIT1(cinfo, JERR_IMAGE_TOO_BIG, (unsigned int) 65535); + + emit_byte(cinfo, cinfo->data_precision); + emit_2bytes(cinfo, (int) cinfo->image_height); + emit_2bytes(cinfo, (int) cinfo->image_width); + + emit_byte(cinfo, cinfo->num_components); + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + emit_byte(cinfo, compptr->component_id); + emit_byte(cinfo, (compptr->h_samp_factor << 4) + compptr->v_samp_factor); + emit_byte(cinfo, compptr->quant_tbl_no); + } +} + +LOCAL(void) +emit_sos (j_compress_ptr cinfo) +/* Emit a SOS marker */ +{ + int i, td, ta; + jpeg_component_info *compptr; + + emit_marker(cinfo, M_SOS); + + emit_2bytes(cinfo, 2 * cinfo->comps_in_scan + 2 + 1 + 3); /* length */ + + emit_byte(cinfo, cinfo->comps_in_scan); + + for (i = 0; i < cinfo->comps_in_scan; i++) { + compptr = cinfo->cur_comp_info[i]; + emit_byte(cinfo, compptr->component_id); + td = compptr->dc_tbl_no; + ta = compptr->ac_tbl_no; + if (cinfo->progressive_mode) { + /* Progressive mode: only DC or only AC tables are used in one scan; + * furthermore, Huffman coding of DC refinement uses no table at all. + * We emit 0 for unused field(s); this is recommended by the P&M text + * but does not seem to be specified in the standard. + */ + if (cinfo->Ss == 0) { + ta = 0; /* DC scan */ + if (cinfo->Ah != 0 && !cinfo->arith_code) + td = 0; /* no DC table either */ + } else { + td = 0; /* AC scan */ + } + } + emit_byte(cinfo, (td << 4) + ta); + } + + emit_byte(cinfo, cinfo->Ss); + emit_byte(cinfo, cinfo->Se); + emit_byte(cinfo, (cinfo->Ah << 4) + cinfo->Al); +} + +LOCAL(void) +emit_jfif_app0 (j_compress_ptr cinfo) +/* Emit a JFIF-compliant APP0 marker */ +{ + /* + * Length of APP0 block (2 bytes) + * Block ID (4 bytes - ASCII "JFIF") + * Zero byte (1 byte to terminate the ID string) + * Version Major, Minor (2 bytes - major first) + * Units (1 byte - 0x00 = none, 0x01 = inch, 0x02 = cm) + * Xdpu (2 bytes - dots per unit horizontal) + * Ydpu (2 bytes - dots per unit vertical) + * Thumbnail X size (1 byte) + * Thumbnail Y size (1 byte) + */ + + emit_marker(cinfo, M_APP0); + + emit_2bytes(cinfo, 2 + 4 + 1 + 2 + 1 + 2 + 2 + 1 + 1); /* length */ + + emit_byte(cinfo, 0x4A); /* Identifier: ASCII "JFIF" */ + emit_byte(cinfo, 0x46); + emit_byte(cinfo, 0x49); + emit_byte(cinfo, 0x46); + emit_byte(cinfo, 0); + emit_byte(cinfo, cinfo->JFIF_major_version); /* Version fields */ + emit_byte(cinfo, cinfo->JFIF_minor_version); + emit_byte(cinfo, cinfo->density_unit); /* Pixel size information */ + emit_2bytes(cinfo, (int) cinfo->X_density); + emit_2bytes(cinfo, (int) cinfo->Y_density); + emit_byte(cinfo, 0); /* No thumbnail image */ + emit_byte(cinfo, 0); +} + +LOCAL(void) +emit_adobe_app14 (j_compress_ptr cinfo) +/* Emit an Adobe APP14 marker */ +{ + /* + * Length of APP14 block (2 bytes) + * Block ID (5 bytes - ASCII "Adobe") + * Version Number (2 bytes - currently 100) + * Flags0 (2 bytes - currently 0) + * Flags1 (2 bytes - currently 0) + * Color transform (1 byte) + * + * Although Adobe TN 5116 mentions Version = 101, all the Adobe files + * now in circulation seem to use Version = 100, so that's what we write. + * + * We write the color transform byte as 1 if the JPEG color space is + * YCbCr, 2 if it's YCCK, 0 otherwise. Adobe's definition has to do with + * whether the encoder performed a transformation, which is pretty useless. + */ + + emit_marker(cinfo, M_APP14); + + emit_2bytes(cinfo, 2 + 5 + 2 + 2 + 2 + 1); /* length */ + + emit_byte(cinfo, 0x41); /* Identifier: ASCII "Adobe" */ + emit_byte(cinfo, 0x64); + emit_byte(cinfo, 0x6F); + emit_byte(cinfo, 0x62); + emit_byte(cinfo, 0x65); + emit_2bytes(cinfo, 100); /* Version */ + emit_2bytes(cinfo, 0); /* Flags0 */ + emit_2bytes(cinfo, 0); /* Flags1 */ + switch (cinfo->jpeg_color_space) { + case JCS_YCbCr: + emit_byte(cinfo, 1); /* Color transform = 1 */ + break; + case JCS_YCCK: + emit_byte(cinfo, 2); /* Color transform = 2 */ + break; + default: + emit_byte(cinfo, 0); /* Color transform = 0 */ + break; + } +} + +/* + * These routines allow writing an arbitrary marker with parameters. + * The only intended use is to emit COM or APPn markers after calling + * write_file_header and before calling write_frame_header. + * Other uses are not guaranteed to produce desirable results. + * Counting the parameter bytes properly is the caller's responsibility. + */ + +METHODDEF(void) +write_marker_header (j_compress_ptr cinfo, int marker, unsigned int datalen) +/* Emit an arbitrary marker header */ +{ + if (datalen > (unsigned int) 65533) /* safety check */ + ERREXIT(cinfo, JERR_BAD_LENGTH); + + emit_marker(cinfo, (JPEG_MARKER) marker); + + emit_2bytes(cinfo, (int) (datalen + 2)); /* total length */ +} + +METHODDEF(void) +write_marker_byte (j_compress_ptr cinfo, int val) +/* Emit one byte of marker parameters following write_marker_header */ +{ + emit_byte(cinfo, val); +} + +/* + * Write datastream header. + * This consists of an SOI and optional APPn markers. + * We recommend use of the JFIF marker, but not the Adobe marker, + * when using YCbCr or grayscale data. The JFIF marker should NOT + * be used for any other JPEG colorspace. The Adobe marker is helpful + * to distinguish RGB, CMYK, and YCCK colorspaces. + * Note that an application can write additional header markers after + * jpeg_start_compress returns. + */ + +METHODDEF(void) +write_file_header (j_compress_ptr cinfo) +{ + my_marker_ptr marker = (my_marker_ptr) cinfo->marker; + + emit_marker(cinfo, M_SOI); /* first the SOI */ + + /* SOI is defined to reset restart interval to 0 */ + marker->last_restart_interval = 0; + + if (cinfo->write_JFIF_header) /* next an optional JFIF APP0 */ + emit_jfif_app0(cinfo); + if (cinfo->write_Adobe_marker) /* next an optional Adobe APP14 */ + emit_adobe_app14(cinfo); +} + +/* + * Write frame header. + * This consists of DQT and SOFn markers. + * Note that we do not emit the SOF until we have emitted the DQT(s). + * This avoids compatibility problems with incorrect implementations that + * try to error-check the quant table numbers as soon as they see the SOF. + */ + +METHODDEF(void) +write_frame_header (j_compress_ptr cinfo) +{ + int ci, prec; + boolean is_baseline; + jpeg_component_info *compptr; + + /* Emit DQT for each quantization table. + * Note that emit_dqt() suppresses any duplicate tables. + */ + prec = 0; + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + prec += emit_dqt(cinfo, compptr->quant_tbl_no); + } + /* now prec is nonzero iff there are any 16-bit quant tables. */ + + /* Check for a non-baseline specification. + * Note we assume that Huffman table numbers won't be changed later. + */ + if (cinfo->arith_code || cinfo->progressive_mode || + cinfo->data_precision != 8) { + is_baseline = FALSE; + } else { + is_baseline = TRUE; + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + if (compptr->dc_tbl_no > 1 || compptr->ac_tbl_no > 1) + is_baseline = FALSE; + } + if (prec && is_baseline) { + is_baseline = FALSE; + /* If it's baseline except for quantizer size, warn the user */ + TRACEMS(cinfo, 0, JTRC_16BIT_TABLES); + } + } + + /* Emit the proper SOF marker */ + if (cinfo->arith_code) { + emit_sof(cinfo, M_SOF9); /* SOF code for arithmetic coding */ + } else { + if (cinfo->progressive_mode) + emit_sof(cinfo, M_SOF2); /* SOF code for progressive Huffman */ + else if (is_baseline) + emit_sof(cinfo, M_SOF0); /* SOF code for baseline implementation */ + else + emit_sof(cinfo, M_SOF1); /* SOF code for non-baseline Huffman file */ + } +} + +/* + * Write scan header. + * This consists of DHT or DAC markers, optional DRI, and SOS. + * Compressed data will be written following the SOS. + */ + +METHODDEF(void) +write_scan_header (j_compress_ptr cinfo) +{ + my_marker_ptr marker = (my_marker_ptr) cinfo->marker; + int i; + jpeg_component_info *compptr; + + if (cinfo->arith_code) { + /* Emit arith conditioning info. We may have some duplication + * if the file has multiple scans, but it's so small it's hardly + * worth worrying about. + */ + emit_dac(cinfo); + } else { + /* Emit Huffman tables. + * Note that emit_dht() suppresses any duplicate tables. + */ + for (i = 0; i < cinfo->comps_in_scan; i++) { + compptr = cinfo->cur_comp_info[i]; + if (cinfo->progressive_mode) { + /* Progressive mode: only DC or only AC tables are used in one scan */ + if (cinfo->Ss == 0) { + if (cinfo->Ah == 0) /* DC needs no table for refinement scan */ + emit_dht(cinfo, compptr->dc_tbl_no, FALSE); + } else { + emit_dht(cinfo, compptr->ac_tbl_no, TRUE); + } + } else { + /* Sequential mode: need both DC and AC tables */ + emit_dht(cinfo, compptr->dc_tbl_no, FALSE); + emit_dht(cinfo, compptr->ac_tbl_no, TRUE); + } + } + } + + /* Emit DRI if required --- note that DRI value could change for each scan. + * We avoid wasting space with unnecessary DRIs, however. + */ + if (cinfo->restart_interval != marker->last_restart_interval) { + emit_dri(cinfo); + marker->last_restart_interval = cinfo->restart_interval; + } + + emit_sos(cinfo); +} + +/* + * Write datastream trailer. + */ + +METHODDEF(void) +write_file_trailer (j_compress_ptr cinfo) +{ + emit_marker(cinfo, M_EOI); +} + +/* + * Write an abbreviated table-specification datastream. + * This consists of SOI, DQT and DHT tables, and EOI. + * Any table that is defined and not marked sent_table = TRUE will be + * emitted. Note that all tables will be marked sent_table = TRUE at exit. + */ + +METHODDEF(void) +write_tables_only (j_compress_ptr cinfo) +{ + int i; + + emit_marker(cinfo, M_SOI); + + for (i = 0; i < NUM_QUANT_TBLS; i++) { + if (cinfo->quant_tbl_ptrs[i] != NULL) + (void) emit_dqt(cinfo, i); + } + + if (! cinfo->arith_code) { + for (i = 0; i < NUM_HUFF_TBLS; i++) { + if (cinfo->dc_huff_tbl_ptrs[i] != NULL) + emit_dht(cinfo, i, FALSE); + if (cinfo->ac_huff_tbl_ptrs[i] != NULL) + emit_dht(cinfo, i, TRUE); + } + } + + emit_marker(cinfo, M_EOI); +} + +/* + * Initialize the marker writer module. + */ + +GLOBAL(void) +jinit_marker_writer (j_compress_ptr cinfo) +{ + my_marker_ptr marker; + + /* Create the subobject */ + marker = (my_marker_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_marker_writer)); + cinfo->marker = (struct jpeg_marker_writer *) marker; + /* Initialize method pointers */ + marker->pub.write_file_header = write_file_header; + marker->pub.write_frame_header = write_frame_header; + marker->pub.write_scan_header = write_scan_header; + marker->pub.write_file_trailer = write_file_trailer; + marker->pub.write_tables_only = write_tables_only; + marker->pub.write_marker_header = write_marker_header; + marker->pub.write_marker_byte = write_marker_byte; + /* Initialize private state */ + marker->last_restart_interval = 0; +} +/********* End of inlined file: jcmarker.c *********/ + +/********* Start of inlined file: jcmaster.c *********/ +#define JPEG_INTERNALS + +/* Private state */ + +typedef enum { + main_pass, /* input data, also do first output step */ + huff_opt_pass, /* Huffman code optimization pass */ + output_pass /* data output pass */ +} c_pass_type; + +typedef struct { + struct jpeg_comp_master pub; /* public fields */ + + c_pass_type pass_type; /* the type of the current pass */ + + int pass_number; /* # of passes completed */ + int total_passes; /* total # of passes needed */ + + int scan_number; /* current index in scan_info[] */ +} my_comp_master; + +typedef my_comp_master * my_master_ptr; + +/* + * Support routines that do various essential calculations. + */ + +LOCAL(void) +initial_setup (j_compress_ptr cinfo) +/* Do computations that are needed before master selection phase */ +{ + int ci; + jpeg_component_info *compptr; + long samplesperrow; + JDIMENSION jd_samplesperrow; + + /* Sanity check on image dimensions */ + if (cinfo->image_height <= 0 || cinfo->image_width <= 0 + || cinfo->num_components <= 0 || cinfo->input_components <= 0) + ERREXIT(cinfo, JERR_EMPTY_IMAGE); + + /* Make sure image isn't bigger than I can handle */ + if ((long) cinfo->image_height > (long) JPEG_MAX_DIMENSION || + (long) cinfo->image_width > (long) JPEG_MAX_DIMENSION) + ERREXIT1(cinfo, JERR_IMAGE_TOO_BIG, (unsigned int) JPEG_MAX_DIMENSION); + + /* Width of an input scanline must be representable as JDIMENSION. */ + samplesperrow = (long) cinfo->image_width * (long) cinfo->input_components; + jd_samplesperrow = (JDIMENSION) samplesperrow; + if ((long) jd_samplesperrow != samplesperrow) + ERREXIT(cinfo, JERR_WIDTH_OVERFLOW); + + /* For now, precision must match compiled-in value... */ + if (cinfo->data_precision != BITS_IN_JSAMPLE) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + + /* Check that number of components won't exceed internal array sizes */ + if (cinfo->num_components > MAX_COMPONENTS) + ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->num_components, + MAX_COMPONENTS); + + /* Compute maximum sampling factors; check factor validity */ + cinfo->max_h_samp_factor = 1; + cinfo->max_v_samp_factor = 1; + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + if (compptr->h_samp_factor<=0 || compptr->h_samp_factor>MAX_SAMP_FACTOR || + compptr->v_samp_factor<=0 || compptr->v_samp_factor>MAX_SAMP_FACTOR) + ERREXIT(cinfo, JERR_BAD_SAMPLING); + cinfo->max_h_samp_factor = MAX(cinfo->max_h_samp_factor, + compptr->h_samp_factor); + cinfo->max_v_samp_factor = MAX(cinfo->max_v_samp_factor, + compptr->v_samp_factor); + } + + /* Compute dimensions of components */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Fill in the correct component_index value; don't rely on application */ + compptr->component_index = ci; + /* For compression, we never do DCT scaling. */ + compptr->DCT_scaled_size = DCTSIZE; + /* Size in DCT blocks */ + compptr->width_in_blocks = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * (long) compptr->h_samp_factor, + (long) (cinfo->max_h_samp_factor * DCTSIZE)); + compptr->height_in_blocks = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * (long) compptr->v_samp_factor, + (long) (cinfo->max_v_samp_factor * DCTSIZE)); + /* Size in samples */ + compptr->downsampled_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * (long) compptr->h_samp_factor, + (long) cinfo->max_h_samp_factor); + compptr->downsampled_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * (long) compptr->v_samp_factor, + (long) cinfo->max_v_samp_factor); + /* Mark component needed (this flag isn't actually used for compression) */ + compptr->component_needed = TRUE; + } + + /* Compute number of fully interleaved MCU rows (number of times that + * main controller will call coefficient controller). + */ + cinfo->total_iMCU_rows = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, + (long) (cinfo->max_v_samp_factor*DCTSIZE)); +} + +#ifdef C_MULTISCAN_FILES_SUPPORTED + +LOCAL(void) +validate_script (j_compress_ptr cinfo) +/* Verify that the scan script in cinfo->scan_info[] is valid; also + * determine whether it uses progressive JPEG, and set cinfo->progressive_mode. + */ +{ + const jpeg_scan_info * scanptr; + int scanno, ncomps, ci, coefi, thisi; + int Ss, Se, Ah, Al; + boolean component_sent[MAX_COMPONENTS]; +#ifdef C_PROGRESSIVE_SUPPORTED + int * last_bitpos_ptr; + int last_bitpos[MAX_COMPONENTS][DCTSIZE2]; + /* -1 until that coefficient has been seen; then last Al for it */ +#endif + + if (cinfo->num_scans <= 0) + ERREXIT1(cinfo, JERR_BAD_SCAN_SCRIPT, 0); + + /* For sequential JPEG, all scans must have Ss=0, Se=DCTSIZE2-1; + * for progressive JPEG, no scan can have this. + */ + scanptr = cinfo->scan_info; + if (scanptr->Ss != 0 || scanptr->Se != DCTSIZE2-1) { +#ifdef C_PROGRESSIVE_SUPPORTED + cinfo->progressive_mode = TRUE; + last_bitpos_ptr = & last_bitpos[0][0]; + for (ci = 0; ci < cinfo->num_components; ci++) + for (coefi = 0; coefi < DCTSIZE2; coefi++) + *last_bitpos_ptr++ = -1; +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else { + cinfo->progressive_mode = FALSE; + for (ci = 0; ci < cinfo->num_components; ci++) + component_sent[ci] = FALSE; + } + + for (scanno = 1; scanno <= cinfo->num_scans; scanptr++, scanno++) { + /* Validate component indexes */ + ncomps = scanptr->comps_in_scan; + if (ncomps <= 0 || ncomps > MAX_COMPS_IN_SCAN) + ERREXIT2(cinfo, JERR_COMPONENT_COUNT, ncomps, MAX_COMPS_IN_SCAN); + for (ci = 0; ci < ncomps; ci++) { + thisi = scanptr->component_index[ci]; + if (thisi < 0 || thisi >= cinfo->num_components) + ERREXIT1(cinfo, JERR_BAD_SCAN_SCRIPT, scanno); + /* Components must appear in SOF order within each scan */ + if (ci > 0 && thisi <= scanptr->component_index[ci-1]) + ERREXIT1(cinfo, JERR_BAD_SCAN_SCRIPT, scanno); + } + /* Validate progression parameters */ + Ss = scanptr->Ss; + Se = scanptr->Se; + Ah = scanptr->Ah; + Al = scanptr->Al; + if (cinfo->progressive_mode) { +#ifdef C_PROGRESSIVE_SUPPORTED + /* The JPEG spec simply gives the ranges 0..13 for Ah and Al, but that + * seems wrong: the upper bound ought to depend on data precision. + * Perhaps they really meant 0..N+1 for N-bit precision. + * Here we allow 0..10 for 8-bit data; Al larger than 10 results in + * out-of-range reconstructed DC values during the first DC scan, + * which might cause problems for some decoders. + */ +#if BITS_IN_JSAMPLE == 8 +#define MAX_AH_AL 10 +#else +#define MAX_AH_AL 13 +#endif + if (Ss < 0 || Ss >= DCTSIZE2 || Se < Ss || Se >= DCTSIZE2 || + Ah < 0 || Ah > MAX_AH_AL || Al < 0 || Al > MAX_AH_AL) + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + if (Ss == 0) { + if (Se != 0) /* DC and AC together not OK */ + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + } else { + if (ncomps != 1) /* AC scans must be for only one component */ + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + } + for (ci = 0; ci < ncomps; ci++) { + last_bitpos_ptr = & last_bitpos[scanptr->component_index[ci]][0]; + if (Ss != 0 && last_bitpos_ptr[0] < 0) /* AC without prior DC scan */ + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + for (coefi = Ss; coefi <= Se; coefi++) { + if (last_bitpos_ptr[coefi] < 0) { + /* first scan of this coefficient */ + if (Ah != 0) + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + } else { + /* not first scan */ + if (Ah != last_bitpos_ptr[coefi] || Al != Ah-1) + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + } + last_bitpos_ptr[coefi] = Al; + } + } +#endif + } else { + /* For sequential JPEG, all progression parameters must be these: */ + if (Ss != 0 || Se != DCTSIZE2-1 || Ah != 0 || Al != 0) + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + /* Make sure components are not sent twice */ + for (ci = 0; ci < ncomps; ci++) { + thisi = scanptr->component_index[ci]; + if (component_sent[thisi]) + ERREXIT1(cinfo, JERR_BAD_SCAN_SCRIPT, scanno); + component_sent[thisi] = TRUE; + } + } + } + + /* Now verify that everything got sent. */ + if (cinfo->progressive_mode) { +#ifdef C_PROGRESSIVE_SUPPORTED + /* For progressive mode, we only check that at least some DC data + * got sent for each component; the spec does not require that all bits + * of all coefficients be transmitted. Would it be wiser to enforce + * transmission of all coefficient bits?? + */ + for (ci = 0; ci < cinfo->num_components; ci++) { + if (last_bitpos[ci][0] < 0) + ERREXIT(cinfo, JERR_MISSING_DATA); + } +#endif + } else { + for (ci = 0; ci < cinfo->num_components; ci++) { + if (! component_sent[ci]) + ERREXIT(cinfo, JERR_MISSING_DATA); + } + } +} + +#endif /* C_MULTISCAN_FILES_SUPPORTED */ + +LOCAL(void) +select_scan_parameters (j_compress_ptr cinfo) +/* Set up the scan parameters for the current scan */ +{ + int ci; + +#ifdef C_MULTISCAN_FILES_SUPPORTED + if (cinfo->scan_info != NULL) { + /* Prepare for current scan --- the script is already validated */ + my_master_ptr master = (my_master_ptr) cinfo->master; + const jpeg_scan_info * scanptr = cinfo->scan_info + master->scan_number; + + cinfo->comps_in_scan = scanptr->comps_in_scan; + for (ci = 0; ci < scanptr->comps_in_scan; ci++) { + cinfo->cur_comp_info[ci] = + &cinfo->comp_info[scanptr->component_index[ci]]; + } + cinfo->Ss = scanptr->Ss; + cinfo->Se = scanptr->Se; + cinfo->Ah = scanptr->Ah; + cinfo->Al = scanptr->Al; + } + else +#endif + { + /* Prepare for single sequential-JPEG scan containing all components */ + if (cinfo->num_components > MAX_COMPS_IN_SCAN) + ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->num_components, + MAX_COMPS_IN_SCAN); + cinfo->comps_in_scan = cinfo->num_components; + for (ci = 0; ci < cinfo->num_components; ci++) { + cinfo->cur_comp_info[ci] = &cinfo->comp_info[ci]; + } + cinfo->Ss = 0; + cinfo->Se = DCTSIZE2-1; + cinfo->Ah = 0; + cinfo->Al = 0; + } +} + +LOCAL(void) +per_scan_setup (j_compress_ptr cinfo) +/* Do computations that are needed before processing a JPEG scan */ +/* cinfo->comps_in_scan and cinfo->cur_comp_info[] are already set */ +{ + int ci, mcublks, tmp; + jpeg_component_info *compptr; + + if (cinfo->comps_in_scan == 1) { + + /* Noninterleaved (single-component) scan */ + compptr = cinfo->cur_comp_info[0]; + + /* Overall image size in MCUs */ + cinfo->MCUs_per_row = compptr->width_in_blocks; + cinfo->MCU_rows_in_scan = compptr->height_in_blocks; + + /* For noninterleaved scan, always one block per MCU */ + compptr->MCU_width = 1; + compptr->MCU_height = 1; + compptr->MCU_blocks = 1; + compptr->MCU_sample_width = DCTSIZE; + compptr->last_col_width = 1; + /* For noninterleaved scans, it is convenient to define last_row_height + * as the number of block rows present in the last iMCU row. + */ + tmp = (int) (compptr->height_in_blocks % compptr->v_samp_factor); + if (tmp == 0) tmp = compptr->v_samp_factor; + compptr->last_row_height = tmp; + + /* Prepare array describing MCU composition */ + cinfo->blocks_in_MCU = 1; + cinfo->MCU_membership[0] = 0; + + } else { + + /* Interleaved (multi-component) scan */ + if (cinfo->comps_in_scan <= 0 || cinfo->comps_in_scan > MAX_COMPS_IN_SCAN) + ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->comps_in_scan, + MAX_COMPS_IN_SCAN); + + /* Overall image size in MCUs */ + cinfo->MCUs_per_row = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width, + (long) (cinfo->max_h_samp_factor*DCTSIZE)); + cinfo->MCU_rows_in_scan = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, + (long) (cinfo->max_v_samp_factor*DCTSIZE)); + + cinfo->blocks_in_MCU = 0; + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + /* Sampling factors give # of blocks of component in each MCU */ + compptr->MCU_width = compptr->h_samp_factor; + compptr->MCU_height = compptr->v_samp_factor; + compptr->MCU_blocks = compptr->MCU_width * compptr->MCU_height; + compptr->MCU_sample_width = compptr->MCU_width * DCTSIZE; + /* Figure number of non-dummy blocks in last MCU column & row */ + tmp = (int) (compptr->width_in_blocks % compptr->MCU_width); + if (tmp == 0) tmp = compptr->MCU_width; + compptr->last_col_width = tmp; + tmp = (int) (compptr->height_in_blocks % compptr->MCU_height); + if (tmp == 0) tmp = compptr->MCU_height; + compptr->last_row_height = tmp; + /* Prepare array describing MCU composition */ + mcublks = compptr->MCU_blocks; + if (cinfo->blocks_in_MCU + mcublks > C_MAX_BLOCKS_IN_MCU) + ERREXIT(cinfo, JERR_BAD_MCU_SIZE); + while (mcublks-- > 0) { + cinfo->MCU_membership[cinfo->blocks_in_MCU++] = ci; + } + } + + } + + /* Convert restart specified in rows to actual MCU count. */ + /* Note that count must fit in 16 bits, so we provide limiting. */ + if (cinfo->restart_in_rows > 0) { + long nominal = (long) cinfo->restart_in_rows * (long) cinfo->MCUs_per_row; + cinfo->restart_interval = (unsigned int) MIN(nominal, 65535L); + } +} + +/* + * Per-pass setup. + * This is called at the beginning of each pass. We determine which modules + * will be active during this pass and give them appropriate start_pass calls. + * We also set is_last_pass to indicate whether any more passes will be + * required. + */ + +METHODDEF(void) +prepare_for_pass (j_compress_ptr cinfo) +{ + my_master_ptr master = (my_master_ptr) cinfo->master; + + switch (master->pass_type) { + case main_pass: + /* Initial pass: will collect input data, and do either Huffman + * optimization or data output for the first scan. + */ + select_scan_parameters(cinfo); + per_scan_setup(cinfo); + if (! cinfo->raw_data_in) { + (*cinfo->cconvert->start_pass) (cinfo); + (*cinfo->downsample->start_pass) (cinfo); + (*cinfo->prep->start_pass) (cinfo, JBUF_PASS_THRU); + } + (*cinfo->fdct->start_pass) (cinfo); + (*cinfo->entropy->start_pass) (cinfo, cinfo->optimize_coding); + (*cinfo->coef->start_pass) (cinfo, + (master->total_passes > 1 ? + JBUF_SAVE_AND_PASS : JBUF_PASS_THRU)); + (*cinfo->main->start_pass) (cinfo, JBUF_PASS_THRU); + if (cinfo->optimize_coding) { + /* No immediate data output; postpone writing frame/scan headers */ + master->pub.call_pass_startup = FALSE; + } else { + /* Will write frame/scan headers at first jpeg_write_scanlines call */ + master->pub.call_pass_startup = TRUE; + } + break; +#ifdef ENTROPY_OPT_SUPPORTED + case huff_opt_pass: + /* Do Huffman optimization for a scan after the first one. */ + select_scan_parameters(cinfo); + per_scan_setup(cinfo); + if (cinfo->Ss != 0 || cinfo->Ah == 0 || cinfo->arith_code) { + (*cinfo->entropy->start_pass) (cinfo, TRUE); + (*cinfo->coef->start_pass) (cinfo, JBUF_CRANK_DEST); + master->pub.call_pass_startup = FALSE; + break; + } + /* Special case: Huffman DC refinement scans need no Huffman table + * and therefore we can skip the optimization pass for them. + */ + master->pass_type = output_pass; + master->pass_number++; + /*FALLTHROUGH*/ +#endif + case output_pass: + /* Do a data-output pass. */ + /* We need not repeat per-scan setup if prior optimization pass did it. */ + if (! cinfo->optimize_coding) { + select_scan_parameters(cinfo); + per_scan_setup(cinfo); + } + (*cinfo->entropy->start_pass) (cinfo, FALSE); + (*cinfo->coef->start_pass) (cinfo, JBUF_CRANK_DEST); + /* We emit frame/scan headers now */ + if (master->scan_number == 0) + (*cinfo->marker->write_frame_header) (cinfo); + (*cinfo->marker->write_scan_header) (cinfo); + master->pub.call_pass_startup = FALSE; + break; + default: + ERREXIT(cinfo, JERR_NOT_COMPILED); + } + + master->pub.is_last_pass = (master->pass_number == master->total_passes-1); + + /* Set up progress monitor's pass info if present */ + if (cinfo->progress != NULL) { + cinfo->progress->completed_passes = master->pass_number; + cinfo->progress->total_passes = master->total_passes; + } +} + +/* + * Special start-of-pass hook. + * This is called by jpeg_write_scanlines if call_pass_startup is TRUE. + * In single-pass processing, we need this hook because we don't want to + * write frame/scan headers during jpeg_start_compress; we want to let the + * application write COM markers etc. between jpeg_start_compress and the + * jpeg_write_scanlines loop. + * In multi-pass processing, this routine is not used. + */ + +METHODDEF(void) +pass_startup (j_compress_ptr cinfo) +{ + cinfo->master->call_pass_startup = FALSE; /* reset flag so call only once */ + + (*cinfo->marker->write_frame_header) (cinfo); + (*cinfo->marker->write_scan_header) (cinfo); +} + +/* + * Finish up at end of pass. + */ + +METHODDEF(void) +finish_pass_master (j_compress_ptr cinfo) +{ + my_master_ptr master = (my_master_ptr) cinfo->master; + + /* The entropy coder always needs an end-of-pass call, + * either to analyze statistics or to flush its output buffer. + */ + (*cinfo->entropy->finish_pass) (cinfo); + + /* Update state for next pass */ + switch (master->pass_type) { + case main_pass: + /* next pass is either output of scan 0 (after optimization) + * or output of scan 1 (if no optimization). + */ + master->pass_type = output_pass; + if (! cinfo->optimize_coding) + master->scan_number++; + break; + case huff_opt_pass: + /* next pass is always output of current scan */ + master->pass_type = output_pass; + break; + case output_pass: + /* next pass is either optimization or output of next scan */ + if (cinfo->optimize_coding) + master->pass_type = huff_opt_pass; + master->scan_number++; + break; + } + + master->pass_number++; +} + +/* + * Initialize master compression control. + */ + +GLOBAL(void) +jinit_c_master_control (j_compress_ptr cinfo, boolean transcode_only) +{ + my_master_ptr master; + + master = (my_master_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_comp_master)); + cinfo->master = (struct jpeg_comp_master *) master; + master->pub.prepare_for_pass = prepare_for_pass; + master->pub.pass_startup = pass_startup; + master->pub.finish_pass = finish_pass_master; + master->pub.is_last_pass = FALSE; + + /* Validate parameters, determine derived values */ + initial_setup(cinfo); + + if (cinfo->scan_info != NULL) { +#ifdef C_MULTISCAN_FILES_SUPPORTED + validate_script(cinfo); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else { + cinfo->progressive_mode = FALSE; + cinfo->num_scans = 1; + } + + if (cinfo->progressive_mode) /* TEMPORARY HACK ??? */ + cinfo->optimize_coding = TRUE; /* assume default tables no good for progressive mode */ + + /* Initialize my private state */ + if (transcode_only) { + /* no main pass in transcoding */ + if (cinfo->optimize_coding) + master->pass_type = huff_opt_pass; + else + master->pass_type = output_pass; + } else { + /* for normal compression, first pass is always this type: */ + master->pass_type = main_pass; + } + master->scan_number = 0; + master->pass_number = 0; + if (cinfo->optimize_coding) + master->total_passes = cinfo->num_scans * 2; + else + master->total_passes = cinfo->num_scans; +} +/********* End of inlined file: jcmaster.c *********/ + +/********* Start of inlined file: jcomapi.c *********/ +#define JPEG_INTERNALS + +/* + * Abort processing of a JPEG compression or decompression operation, + * but don't destroy the object itself. + * + * For this, we merely clean up all the nonpermanent memory pools. + * Note that temp files (virtual arrays) are not allowed to belong to + * the permanent pool, so we will be able to close all temp files here. + * Closing a data source or destination, if necessary, is the application's + * responsibility. + */ + +GLOBAL(void) +jpeg_abort (j_common_ptr cinfo) +{ + int pool; + + /* Do nothing if called on a not-initialized or destroyed JPEG object. */ + if (cinfo->mem == NULL) + return; + + /* Releasing pools in reverse order might help avoid fragmentation + * with some (brain-damaged) malloc libraries. + */ + for (pool = JPOOL_NUMPOOLS-1; pool > JPOOL_PERMANENT; pool--) { + (*cinfo->mem->free_pool) (cinfo, pool); + } + + /* Reset overall state for possible reuse of object */ + if (cinfo->is_decompressor) { + cinfo->global_state = DSTATE_START; + /* Try to keep application from accessing now-deleted marker list. + * A bit kludgy to do it here, but this is the most central place. + */ + ((j_decompress_ptr) cinfo)->marker_list = NULL; + } else { + cinfo->global_state = CSTATE_START; + } +} + +/* + * Destruction of a JPEG object. + * + * Everything gets deallocated except the master jpeg_compress_struct itself + * and the error manager struct. Both of these are supplied by the application + * and must be freed, if necessary, by the application. (Often they are on + * the stack and so don't need to be freed anyway.) + * Closing a data source or destination, if necessary, is the application's + * responsibility. + */ + +GLOBAL(void) +jpeg_destroy (j_common_ptr cinfo) +{ + /* We need only tell the memory manager to release everything. */ + /* NB: mem pointer is NULL if memory mgr failed to initialize. */ + if (cinfo->mem != NULL) + (*cinfo->mem->self_destruct) (cinfo); + cinfo->mem = NULL; /* be safe if jpeg_destroy is called twice */ + cinfo->global_state = 0; /* mark it destroyed */ +} + +/* + * Convenience routines for allocating quantization and Huffman tables. + * (Would jutils.c be a more reasonable place to put these?) + */ + +GLOBAL(JQUANT_TBL *) +jpeg_alloc_quant_table (j_common_ptr cinfo) +{ + JQUANT_TBL *tbl; + + tbl = (JQUANT_TBL *) + (*cinfo->mem->alloc_small) (cinfo, JPOOL_PERMANENT, SIZEOF(JQUANT_TBL)); + tbl->sent_table = FALSE; /* make sure this is false in any new table */ + return tbl; +} + +GLOBAL(JHUFF_TBL *) +jpeg_alloc_huff_table (j_common_ptr cinfo) +{ + JHUFF_TBL *tbl; + + tbl = (JHUFF_TBL *) + (*cinfo->mem->alloc_small) (cinfo, JPOOL_PERMANENT, SIZEOF(JHUFF_TBL)); + tbl->sent_table = FALSE; /* make sure this is false in any new table */ + return tbl; +} +/********* End of inlined file: jcomapi.c *********/ + +/********* Start of inlined file: jcparam.c *********/ +#define JPEG_INTERNALS + +/* + * Quantization table setup routines + */ + +GLOBAL(void) +jpeg_add_quant_table (j_compress_ptr cinfo, int which_tbl, + const unsigned int *basic_table, + int scale_factor, boolean force_baseline) +/* Define a quantization table equal to the basic_table times + * a scale factor (given as a percentage). + * If force_baseline is TRUE, the computed quantization table entries + * are limited to 1..255 for JPEG baseline compatibility. + */ +{ + JQUANT_TBL ** qtblptr; + int i; + long temp; + + /* Safety check to ensure start_compress not called yet. */ + if (cinfo->global_state != CSTATE_START) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + if (which_tbl < 0 || which_tbl >= NUM_QUANT_TBLS) + ERREXIT1(cinfo, JERR_DQT_INDEX, which_tbl); + + qtblptr = & cinfo->quant_tbl_ptrs[which_tbl]; + + if (*qtblptr == NULL) + *qtblptr = jpeg_alloc_quant_table((j_common_ptr) cinfo); + + for (i = 0; i < DCTSIZE2; i++) { + temp = ((long) basic_table[i] * scale_factor + 50L) / 100L; + /* limit the values to the valid range */ + if (temp <= 0L) temp = 1L; + if (temp > 32767L) temp = 32767L; /* max quantizer needed for 12 bits */ + if (force_baseline && temp > 255L) + temp = 255L; /* limit to baseline range if requested */ + (*qtblptr)->quantval[i] = (UINT16) temp; + } + + /* Initialize sent_table FALSE so table will be written to JPEG file. */ + (*qtblptr)->sent_table = FALSE; +} + +GLOBAL(void) +jpeg_set_linear_quality (j_compress_ptr cinfo, int scale_factor, + boolean force_baseline) +/* Set or change the 'quality' (quantization) setting, using default tables + * and a straight percentage-scaling quality scale. In most cases it's better + * to use jpeg_set_quality (below); this entry point is provided for + * applications that insist on a linear percentage scaling. + */ +{ + /* These are the sample quantization tables given in JPEG spec section K.1. + * The spec says that the values given produce "good" quality, and + * when divided by 2, "very good" quality. + */ + static const unsigned int std_luminance_quant_tbl[DCTSIZE2] = { + 16, 11, 10, 16, 24, 40, 51, 61, + 12, 12, 14, 19, 26, 58, 60, 55, + 14, 13, 16, 24, 40, 57, 69, 56, + 14, 17, 22, 29, 51, 87, 80, 62, + 18, 22, 37, 56, 68, 109, 103, 77, + 24, 35, 55, 64, 81, 104, 113, 92, + 49, 64, 78, 87, 103, 121, 120, 101, + 72, 92, 95, 98, 112, 100, 103, 99 + }; + static const unsigned int std_chrominance_quant_tbl[DCTSIZE2] = { + 17, 18, 24, 47, 99, 99, 99, 99, + 18, 21, 26, 66, 99, 99, 99, 99, + 24, 26, 56, 99, 99, 99, 99, 99, + 47, 66, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99 + }; + + /* Set up two quantization tables using the specified scaling */ + jpeg_add_quant_table(cinfo, 0, std_luminance_quant_tbl, + scale_factor, force_baseline); + jpeg_add_quant_table(cinfo, 1, std_chrominance_quant_tbl, + scale_factor, force_baseline); +} + +GLOBAL(int) +jpeg_quality_scaling (int quality) +/* Convert a user-specified quality rating to a percentage scaling factor + * for an underlying quantization table, using our recommended scaling curve. + * The input 'quality' factor should be 0 (terrible) to 100 (very good). + */ +{ + /* Safety limit on quality factor. Convert 0 to 1 to avoid zero divide. */ + if (quality <= 0) quality = 1; + if (quality > 100) quality = 100; + + /* The basic table is used as-is (scaling 100) for a quality of 50. + * Qualities 50..100 are converted to scaling percentage 200 - 2*Q; + * note that at Q=100 the scaling is 0, which will cause jpeg_add_quant_table + * to make all the table entries 1 (hence, minimum quantization loss). + * Qualities 1..50 are converted to scaling percentage 5000/Q. + */ + if (quality < 50) + quality = 5000 / quality; + else + quality = 200 - quality*2; + + return quality; +} + +GLOBAL(void) +jpeg_set_quality (j_compress_ptr cinfo, int quality, boolean force_baseline) +/* Set or change the 'quality' (quantization) setting, using default tables. + * This is the standard quality-adjusting entry point for typical user + * interfaces; only those who want detailed control over quantization tables + * would use the preceding three routines directly. + */ +{ + /* Convert user 0-100 rating to percentage scaling */ + quality = jpeg_quality_scaling(quality); + + /* Set up standard quality tables */ + jpeg_set_linear_quality(cinfo, quality, force_baseline); +} + +/* + * Huffman table setup routines + */ + +LOCAL(void) +add_huff_table (j_compress_ptr cinfo, + JHUFF_TBL **htblptr, const UINT8 *bits, const UINT8 *val) +/* Define a Huffman table */ +{ + int nsymbols, len; + + if (*htblptr == NULL) + *htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo); + + /* Copy the number-of-symbols-of-each-code-length counts */ + MEMCOPY((*htblptr)->bits, bits, SIZEOF((*htblptr)->bits)); + + /* Validate the counts. We do this here mainly so we can copy the right + * number of symbols from the val[] array, without risking marching off + * the end of memory. jchuff.c will do a more thorough test later. + */ + nsymbols = 0; + for (len = 1; len <= 16; len++) + nsymbols += bits[len]; + if (nsymbols < 1 || nsymbols > 256) + ERREXIT(cinfo, JERR_BAD_HUFF_TABLE); + + MEMCOPY((*htblptr)->huffval, val, nsymbols * SIZEOF(UINT8)); + + /* Initialize sent_table FALSE so table will be written to JPEG file. */ + (*htblptr)->sent_table = FALSE; +} + +LOCAL(void) +std_huff_tables (j_compress_ptr cinfo) +/* Set up the standard Huffman tables (cf. JPEG standard section K.3) */ +/* IMPORTANT: these are only valid for 8-bit data precision! */ +{ + static const UINT8 bits_dc_luminance[17] = + { /* 0-base */ 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 }; + static const UINT8 val_dc_luminance[] = + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; + + static const UINT8 bits_dc_chrominance[17] = + { /* 0-base */ 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 }; + static const UINT8 val_dc_chrominance[] = + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; + + static const UINT8 bits_ac_luminance[17] = + { /* 0-base */ 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d }; + static const UINT8 val_ac_luminance[] = + { 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, + 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, + 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, + 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, + 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, + 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, + 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, + 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, + 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, + 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, + 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, + 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, + 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa }; + + static const UINT8 bits_ac_chrominance[17] = + { /* 0-base */ 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77 }; + static const UINT8 val_ac_chrominance[] = + { 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, + 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, + 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, + 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, + 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, + 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, + 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, + 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, + 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, + 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, + 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, + 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, + 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa }; + + add_huff_table(cinfo, &cinfo->dc_huff_tbl_ptrs[0], + bits_dc_luminance, val_dc_luminance); + add_huff_table(cinfo, &cinfo->ac_huff_tbl_ptrs[0], + bits_ac_luminance, val_ac_luminance); + add_huff_table(cinfo, &cinfo->dc_huff_tbl_ptrs[1], + bits_dc_chrominance, val_dc_chrominance); + add_huff_table(cinfo, &cinfo->ac_huff_tbl_ptrs[1], + bits_ac_chrominance, val_ac_chrominance); +} + +/* + * Default parameter setup for compression. + * + * Applications that don't choose to use this routine must do their + * own setup of all these parameters. Alternately, you can call this + * to establish defaults and then alter parameters selectively. This + * is the recommended approach since, if we add any new parameters, + * your code will still work (they'll be set to reasonable defaults). + */ + +GLOBAL(void) +jpeg_set_defaults (j_compress_ptr cinfo) +{ + int i; + + /* Safety check to ensure start_compress not called yet. */ + if (cinfo->global_state != CSTATE_START) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + /* Allocate comp_info array large enough for maximum component count. + * Array is made permanent in case application wants to compress + * multiple images at same param settings. + */ + if (cinfo->comp_info == NULL) + cinfo->comp_info = (jpeg_component_info *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + MAX_COMPONENTS * SIZEOF(jpeg_component_info)); + + /* Initialize everything not dependent on the color space */ + + cinfo->data_precision = BITS_IN_JSAMPLE; + /* Set up two quantization tables using default quality of 75 */ + jpeg_set_quality(cinfo, 75, TRUE); + /* Set up two Huffman tables */ + std_huff_tables(cinfo); + + /* Initialize default arithmetic coding conditioning */ + for (i = 0; i < NUM_ARITH_TBLS; i++) { + cinfo->arith_dc_L[i] = 0; + cinfo->arith_dc_U[i] = 1; + cinfo->arith_ac_K[i] = 5; + } + + /* Default is no multiple-scan output */ + cinfo->scan_info = NULL; + cinfo->num_scans = 0; + + /* Expect normal source image, not raw downsampled data */ + cinfo->raw_data_in = FALSE; + + /* Use Huffman coding, not arithmetic coding, by default */ + cinfo->arith_code = FALSE; + + /* By default, don't do extra passes to optimize entropy coding */ + cinfo->optimize_coding = FALSE; + /* The standard Huffman tables are only valid for 8-bit data precision. + * If the precision is higher, force optimization on so that usable + * tables will be computed. This test can be removed if default tables + * are supplied that are valid for the desired precision. + */ + if (cinfo->data_precision > 8) + cinfo->optimize_coding = TRUE; + + /* By default, use the simpler non-cosited sampling alignment */ + cinfo->CCIR601_sampling = FALSE; + + /* No input smoothing */ + cinfo->smoothing_factor = 0; + + /* DCT algorithm preference */ + cinfo->dct_method = JDCT_DEFAULT; + + /* No restart markers */ + cinfo->restart_interval = 0; + cinfo->restart_in_rows = 0; + + /* Fill in default JFIF marker parameters. Note that whether the marker + * will actually be written is determined by jpeg_set_colorspace. + * + * By default, the library emits JFIF version code 1.01. + * An application that wants to emit JFIF 1.02 extension markers should set + * JFIF_minor_version to 2. We could probably get away with just defaulting + * to 1.02, but there may still be some decoders in use that will complain + * about that; saying 1.01 should minimize compatibility problems. + */ + cinfo->JFIF_major_version = 1; /* Default JFIF version = 1.01 */ + cinfo->JFIF_minor_version = 1; + cinfo->density_unit = 0; /* Pixel size is unknown by default */ + cinfo->X_density = 1; /* Pixel aspect ratio is square by default */ + cinfo->Y_density = 1; + + /* Choose JPEG colorspace based on input space, set defaults accordingly */ + + jpeg_default_colorspace(cinfo); +} + +/* + * Select an appropriate JPEG colorspace for in_color_space. + */ + +GLOBAL(void) +jpeg_default_colorspace (j_compress_ptr cinfo) +{ + switch (cinfo->in_color_space) { + case JCS_GRAYSCALE: + jpeg_set_colorspace(cinfo, JCS_GRAYSCALE); + break; + case JCS_RGB: + jpeg_set_colorspace(cinfo, JCS_YCbCr); + break; + case JCS_YCbCr: + jpeg_set_colorspace(cinfo, JCS_YCbCr); + break; + case JCS_CMYK: + jpeg_set_colorspace(cinfo, JCS_CMYK); /* By default, no translation */ + break; + case JCS_YCCK: + jpeg_set_colorspace(cinfo, JCS_YCCK); + break; + case JCS_UNKNOWN: + jpeg_set_colorspace(cinfo, JCS_UNKNOWN); + break; + default: + ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE); + } +} + +/* + * Set the JPEG colorspace, and choose colorspace-dependent default values. + */ + +GLOBAL(void) +jpeg_set_colorspace (j_compress_ptr cinfo, J_COLOR_SPACE colorspace) +{ + jpeg_component_info * compptr; + int ci; + +#define SET_COMP(index,id,hsamp,vsamp,quant,dctbl,actbl) \ + (compptr = &cinfo->comp_info[index], \ + compptr->component_id = (id), \ + compptr->h_samp_factor = (hsamp), \ + compptr->v_samp_factor = (vsamp), \ + compptr->quant_tbl_no = (quant), \ + compptr->dc_tbl_no = (dctbl), \ + compptr->ac_tbl_no = (actbl) ) + + /* Safety check to ensure start_compress not called yet. */ + if (cinfo->global_state != CSTATE_START) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + /* For all colorspaces, we use Q and Huff tables 0 for luminance components, + * tables 1 for chrominance components. + */ + + cinfo->jpeg_color_space = colorspace; + + cinfo->write_JFIF_header = FALSE; /* No marker for non-JFIF colorspaces */ + cinfo->write_Adobe_marker = FALSE; /* write no Adobe marker by default */ + + switch (colorspace) { + case JCS_GRAYSCALE: + cinfo->write_JFIF_header = TRUE; /* Write a JFIF marker */ + cinfo->num_components = 1; + /* JFIF specifies component ID 1 */ + SET_COMP(0, 1, 1,1, 0, 0,0); + break; + case JCS_RGB: + cinfo->write_Adobe_marker = TRUE; /* write Adobe marker to flag RGB */ + cinfo->num_components = 3; + SET_COMP(0, 0x52 /* 'R' */, 1,1, 0, 0,0); + SET_COMP(1, 0x47 /* 'G' */, 1,1, 0, 0,0); + SET_COMP(2, 0x42 /* 'B' */, 1,1, 0, 0,0); + break; + case JCS_YCbCr: + cinfo->write_JFIF_header = TRUE; /* Write a JFIF marker */ + cinfo->num_components = 3; + /* JFIF specifies component IDs 1,2,3 */ + /* We default to 2x2 subsamples of chrominance */ + SET_COMP(0, 1, 2,2, 0, 0,0); + SET_COMP(1, 2, 1,1, 1, 1,1); + SET_COMP(2, 3, 1,1, 1, 1,1); + break; + case JCS_CMYK: + cinfo->write_Adobe_marker = TRUE; /* write Adobe marker to flag CMYK */ + cinfo->num_components = 4; + SET_COMP(0, 0x43 /* 'C' */, 1,1, 0, 0,0); + SET_COMP(1, 0x4D /* 'M' */, 1,1, 0, 0,0); + SET_COMP(2, 0x59 /* 'Y' */, 1,1, 0, 0,0); + SET_COMP(3, 0x4B /* 'K' */, 1,1, 0, 0,0); + break; + case JCS_YCCK: + cinfo->write_Adobe_marker = TRUE; /* write Adobe marker to flag YCCK */ + cinfo->num_components = 4; + SET_COMP(0, 1, 2,2, 0, 0,0); + SET_COMP(1, 2, 1,1, 1, 1,1); + SET_COMP(2, 3, 1,1, 1, 1,1); + SET_COMP(3, 4, 2,2, 0, 0,0); + break; + case JCS_UNKNOWN: + cinfo->num_components = cinfo->input_components; + if (cinfo->num_components < 1 || cinfo->num_components > MAX_COMPONENTS) + ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->num_components, + MAX_COMPONENTS); + for (ci = 0; ci < cinfo->num_components; ci++) { + SET_COMP(ci, ci, 1,1, 0, 0,0); + } + break; + default: + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + } +} + +#ifdef C_PROGRESSIVE_SUPPORTED + +LOCAL(jpeg_scan_info *) +fill_a_scan (jpeg_scan_info * scanptr, int ci, + int Ss, int Se, int Ah, int Al) +/* Support routine: generate one scan for specified component */ +{ + scanptr->comps_in_scan = 1; + scanptr->component_index[0] = ci; + scanptr->Ss = Ss; + scanptr->Se = Se; + scanptr->Ah = Ah; + scanptr->Al = Al; + scanptr++; + return scanptr; +} + +LOCAL(jpeg_scan_info *) +fill_scans (jpeg_scan_info * scanptr, int ncomps, + int Ss, int Se, int Ah, int Al) +/* Support routine: generate one scan for each component */ +{ + int ci; + + for (ci = 0; ci < ncomps; ci++) { + scanptr->comps_in_scan = 1; + scanptr->component_index[0] = ci; + scanptr->Ss = Ss; + scanptr->Se = Se; + scanptr->Ah = Ah; + scanptr->Al = Al; + scanptr++; + } + return scanptr; +} + +LOCAL(jpeg_scan_info *) +fill_dc_scans (jpeg_scan_info * scanptr, int ncomps, int Ah, int Al) +/* Support routine: generate interleaved DC scan if possible, else N scans */ +{ + int ci; + + if (ncomps <= MAX_COMPS_IN_SCAN) { + /* Single interleaved DC scan */ + scanptr->comps_in_scan = ncomps; + for (ci = 0; ci < ncomps; ci++) + scanptr->component_index[ci] = ci; + scanptr->Ss = scanptr->Se = 0; + scanptr->Ah = Ah; + scanptr->Al = Al; + scanptr++; + } else { + /* Noninterleaved DC scan for each component */ + scanptr = fill_scans(scanptr, ncomps, 0, 0, Ah, Al); + } + return scanptr; +} + +/* + * Create a recommended progressive-JPEG script. + * cinfo->num_components and cinfo->jpeg_color_space must be correct. + */ + +GLOBAL(void) +jpeg_simple_progression (j_compress_ptr cinfo) +{ + int ncomps = cinfo->num_components; + int nscans; + jpeg_scan_info * scanptr; + + /* Safety check to ensure start_compress not called yet. */ + if (cinfo->global_state != CSTATE_START) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + /* Figure space needed for script. Calculation must match code below! */ + if (ncomps == 3 && cinfo->jpeg_color_space == JCS_YCbCr) { + /* Custom script for YCbCr color images. */ + nscans = 10; + } else { + /* All-purpose script for other color spaces. */ + if (ncomps > MAX_COMPS_IN_SCAN) + nscans = 6 * ncomps; /* 2 DC + 4 AC scans per component */ + else + nscans = 2 + 4 * ncomps; /* 2 DC scans; 4 AC scans per component */ + } + + /* Allocate space for script. + * We need to put it in the permanent pool in case the application performs + * multiple compressions without changing the settings. To avoid a memory + * leak if jpeg_simple_progression is called repeatedly for the same JPEG + * object, we try to re-use previously allocated space, and we allocate + * enough space to handle YCbCr even if initially asked for grayscale. + */ + if (cinfo->script_space == NULL || cinfo->script_space_size < nscans) { + cinfo->script_space_size = MAX(nscans, 10); + cinfo->script_space = (jpeg_scan_info *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + cinfo->script_space_size * SIZEOF(jpeg_scan_info)); + } + scanptr = cinfo->script_space; + cinfo->scan_info = scanptr; + cinfo->num_scans = nscans; + + if (ncomps == 3 && cinfo->jpeg_color_space == JCS_YCbCr) { + /* Custom script for YCbCr color images. */ + /* Initial DC scan */ + scanptr = fill_dc_scans(scanptr, ncomps, 0, 1); + /* Initial AC scan: get some luma data out in a hurry */ + scanptr = fill_a_scan(scanptr, 0, 1, 5, 0, 2); + /* Chroma data is too small to be worth expending many scans on */ + scanptr = fill_a_scan(scanptr, 2, 1, 63, 0, 1); + scanptr = fill_a_scan(scanptr, 1, 1, 63, 0, 1); + /* Complete spectral selection for luma AC */ + scanptr = fill_a_scan(scanptr, 0, 6, 63, 0, 2); + /* Refine next bit of luma AC */ + scanptr = fill_a_scan(scanptr, 0, 1, 63, 2, 1); + /* Finish DC successive approximation */ + scanptr = fill_dc_scans(scanptr, ncomps, 1, 0); + /* Finish AC successive approximation */ + scanptr = fill_a_scan(scanptr, 2, 1, 63, 1, 0); + scanptr = fill_a_scan(scanptr, 1, 1, 63, 1, 0); + /* Luma bottom bit comes last since it's usually largest scan */ + scanptr = fill_a_scan(scanptr, 0, 1, 63, 1, 0); + } else { + /* All-purpose script for other color spaces. */ + /* Successive approximation first pass */ + scanptr = fill_dc_scans(scanptr, ncomps, 0, 1); + scanptr = fill_scans(scanptr, ncomps, 1, 5, 0, 2); + scanptr = fill_scans(scanptr, ncomps, 6, 63, 0, 2); + /* Successive approximation second pass */ + scanptr = fill_scans(scanptr, ncomps, 1, 63, 2, 1); + /* Successive approximation final pass */ + scanptr = fill_dc_scans(scanptr, ncomps, 1, 0); + scanptr = fill_scans(scanptr, ncomps, 1, 63, 1, 0); + } +} + +#endif /* C_PROGRESSIVE_SUPPORTED */ +/********* End of inlined file: jcparam.c *********/ + +/********* Start of inlined file: jcphuff.c *********/ +#define JPEG_INTERNALS + +#ifdef C_PROGRESSIVE_SUPPORTED + +/* Expanded entropy encoder object for progressive Huffman encoding. */ + +typedef struct { + struct jpeg_entropy_encoder pub; /* public fields */ + + /* Mode flag: TRUE for optimization, FALSE for actual data output */ + boolean gather_statistics; + + /* Bit-level coding status. + * next_output_byte/free_in_buffer are local copies of cinfo->dest fields. + */ + JOCTET * next_output_byte; /* => next byte to write in buffer */ + size_t free_in_buffer; /* # of byte spaces remaining in buffer */ + INT32 put_buffer; /* current bit-accumulation buffer */ + int put_bits; /* # of bits now in it */ + j_compress_ptr cinfo; /* link to cinfo (needed for dump_buffer) */ + + /* Coding status for DC components */ + int last_dc_val[MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ + + /* Coding status for AC components */ + int ac_tbl_no; /* the table number of the single component */ + unsigned int EOBRUN; /* run length of EOBs */ + unsigned int BE; /* # of buffered correction bits before MCU */ + char * bit_buffer; /* buffer for correction bits (1 per char) */ + /* packing correction bits tightly would save some space but cost time... */ + + unsigned int restarts_to_go; /* MCUs left in this restart interval */ + int next_restart_num; /* next restart number to write (0-7) */ + + /* Pointers to derived tables (these workspaces have image lifespan). + * Since any one scan codes only DC or only AC, we only need one set + * of tables, not one for DC and one for AC. + */ + c_derived_tbl * derived_tbls[NUM_HUFF_TBLS]; + + /* Statistics tables for optimization; again, one set is enough */ + long * count_ptrs[NUM_HUFF_TBLS]; +} phuff_entropy_encoder; + +typedef phuff_entropy_encoder * phuff_entropy_ptr; + +/* MAX_CORR_BITS is the number of bits the AC refinement correction-bit + * buffer can hold. Larger sizes may slightly improve compression, but + * 1000 is already well into the realm of overkill. + * The minimum safe size is 64 bits. + */ + +#define MAX_CORR_BITS 1000 /* Max # of correction bits I can buffer */ + +/* IRIGHT_SHIFT is like RIGHT_SHIFT, but works on int rather than INT32. + * We assume that int right shift is unsigned if INT32 right shift is, + * which should be safe. + */ + +#ifdef RIGHT_SHIFT_IS_UNSIGNED +#define ISHIFT_TEMPS int ishift_temp; +#define IRIGHT_SHIFT(x,shft) \ + ((ishift_temp = (x)) < 0 ? \ + (ishift_temp >> (shft)) | ((~0) << (16-(shft))) : \ + (ishift_temp >> (shft))) +#else +#define ISHIFT_TEMPS +#define IRIGHT_SHIFT(x,shft) ((x) >> (shft)) +#endif + +/* Forward declarations */ +METHODDEF(boolean) encode_mcu_DC_first JPP((j_compress_ptr cinfo, + JBLOCKROW *MCU_data)); +METHODDEF(boolean) encode_mcu_AC_first JPP((j_compress_ptr cinfo, + JBLOCKROW *MCU_data)); +METHODDEF(boolean) encode_mcu_DC_refine JPP((j_compress_ptr cinfo, + JBLOCKROW *MCU_data)); +METHODDEF(boolean) encode_mcu_AC_refine JPP((j_compress_ptr cinfo, + JBLOCKROW *MCU_data)); +METHODDEF(void) finish_pass_phuff JPP((j_compress_ptr cinfo)); +METHODDEF(void) finish_pass_gather_phuff JPP((j_compress_ptr cinfo)); + +/* + * Initialize for a Huffman-compressed scan using progressive JPEG. + */ + +METHODDEF(void) +start_pass_phuff (j_compress_ptr cinfo, boolean gather_statistics) +{ + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + boolean is_DC_band; + int ci, tbl; + jpeg_component_info * compptr; + + entropy->cinfo = cinfo; + entropy->gather_statistics = gather_statistics; + + is_DC_band = (cinfo->Ss == 0); + + /* We assume jcmaster.c already validated the scan parameters. */ + + /* Select execution routines */ + if (cinfo->Ah == 0) { + if (is_DC_band) + entropy->pub.encode_mcu = encode_mcu_DC_first; + else + entropy->pub.encode_mcu = encode_mcu_AC_first; + } else { + if (is_DC_band) + entropy->pub.encode_mcu = encode_mcu_DC_refine; + else { + entropy->pub.encode_mcu = encode_mcu_AC_refine; + /* AC refinement needs a correction bit buffer */ + if (entropy->bit_buffer == NULL) + entropy->bit_buffer = (char *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + MAX_CORR_BITS * SIZEOF(char)); + } + } + if (gather_statistics) + entropy->pub.finish_pass = finish_pass_gather_phuff; + else + entropy->pub.finish_pass = finish_pass_phuff; + + /* Only DC coefficients may be interleaved, so cinfo->comps_in_scan = 1 + * for AC coefficients. + */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + /* Initialize DC predictions to 0 */ + entropy->last_dc_val[ci] = 0; + /* Get table index */ + if (is_DC_band) { + if (cinfo->Ah != 0) /* DC refinement needs no table */ + continue; + tbl = compptr->dc_tbl_no; + } else { + entropy->ac_tbl_no = tbl = compptr->ac_tbl_no; + } + if (gather_statistics) { + /* Check for invalid table index */ + /* (make_c_derived_tbl does this in the other path) */ + if (tbl < 0 || tbl >= NUM_HUFF_TBLS) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, tbl); + /* Allocate and zero the statistics tables */ + /* Note that jpeg_gen_optimal_table expects 257 entries in each table! */ + if (entropy->count_ptrs[tbl] == NULL) + entropy->count_ptrs[tbl] = (long *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + 257 * SIZEOF(long)); + MEMZERO(entropy->count_ptrs[tbl], 257 * SIZEOF(long)); + } else { + /* Compute derived values for Huffman table */ + /* We may do this more than once for a table, but it's not expensive */ + jpeg_make_c_derived_tbl(cinfo, is_DC_band, tbl, + & entropy->derived_tbls[tbl]); + } + } + + /* Initialize AC stuff */ + entropy->EOBRUN = 0; + entropy->BE = 0; + + /* Initialize bit buffer to empty */ + entropy->put_buffer = 0; + entropy->put_bits = 0; + + /* Initialize restart stuff */ + entropy->restarts_to_go = cinfo->restart_interval; + entropy->next_restart_num = 0; +} + +/* Outputting bytes to the file. + * NB: these must be called only when actually outputting, + * that is, entropy->gather_statistics == FALSE. + */ + +/* Emit a byte */ +#define emit_byte(entropy,val) \ + { *(entropy)->next_output_byte++ = (JOCTET) (val); \ + if (--(entropy)->free_in_buffer == 0) \ + dump_buffer_p(entropy); } + +LOCAL(void) +dump_buffer_p (phuff_entropy_ptr entropy) +/* Empty the output buffer; we do not support suspension in this module. */ +{ + struct jpeg_destination_mgr * dest = entropy->cinfo->dest; + + if (! (*dest->empty_output_buffer) (entropy->cinfo)) + ERREXIT(entropy->cinfo, JERR_CANT_SUSPEND); + /* After a successful buffer dump, must reset buffer pointers */ + entropy->next_output_byte = dest->next_output_byte; + entropy->free_in_buffer = dest->free_in_buffer; +} + +/* Outputting bits to the file */ + +/* Only the right 24 bits of put_buffer are used; the valid bits are + * left-justified in this part. At most 16 bits can be passed to emit_bits + * in one call, and we never retain more than 7 bits in put_buffer + * between calls, so 24 bits are sufficient. + */ + +INLINE +LOCAL(void) +emit_bits_p (phuff_entropy_ptr entropy, unsigned int code, int size) +/* Emit some bits, unless we are in gather mode */ +{ + /* This routine is heavily used, so it's worth coding tightly. */ + register INT32 put_buffer = (INT32) code; + register int put_bits = entropy->put_bits; + + /* if size is 0, caller used an invalid Huffman table entry */ + if (size == 0) + ERREXIT(entropy->cinfo, JERR_HUFF_MISSING_CODE); + + if (entropy->gather_statistics) + return; /* do nothing if we're only getting stats */ + + put_buffer &= (((INT32) 1)<put_buffer; /* and merge with old buffer contents */ + + while (put_bits >= 8) { + int c = (int) ((put_buffer >> 16) & 0xFF); + + emit_byte(entropy, c); + if (c == 0xFF) { /* need to stuff a zero byte? */ + emit_byte(entropy, 0); + } + put_buffer <<= 8; + put_bits -= 8; + } + + entropy->put_buffer = put_buffer; /* update variables */ + entropy->put_bits = put_bits; +} + +LOCAL(void) +flush_bits_p (phuff_entropy_ptr entropy) +{ + emit_bits_p(entropy, 0x7F, 7); /* fill any partial byte with ones */ + entropy->put_buffer = 0; /* and reset bit-buffer to empty */ + entropy->put_bits = 0; +} + +/* + * Emit (or just count) a Huffman symbol. + */ + +INLINE +LOCAL(void) +emit_symbol (phuff_entropy_ptr entropy, int tbl_no, int symbol) +{ + if (entropy->gather_statistics) + entropy->count_ptrs[tbl_no][symbol]++; + else { + c_derived_tbl * tbl = entropy->derived_tbls[tbl_no]; + emit_bits_p(entropy, tbl->ehufco[symbol], tbl->ehufsi[symbol]); + } +} + +/* + * Emit bits from a correction bit buffer. + */ + +LOCAL(void) +emit_buffered_bits (phuff_entropy_ptr entropy, char * bufstart, + unsigned int nbits) +{ + if (entropy->gather_statistics) + return; /* no real work */ + + while (nbits > 0) { + emit_bits_p(entropy, (unsigned int) (*bufstart), 1); + bufstart++; + nbits--; + } +} + +/* + * Emit any pending EOBRUN symbol. + */ + +LOCAL(void) +emit_eobrun (phuff_entropy_ptr entropy) +{ + register int temp, nbits; + + if (entropy->EOBRUN > 0) { /* if there is any pending EOBRUN */ + temp = entropy->EOBRUN; + nbits = 0; + while ((temp >>= 1)) + nbits++; + /* safety check: shouldn't happen given limited correction-bit buffer */ + if (nbits > 14) + ERREXIT(entropy->cinfo, JERR_HUFF_MISSING_CODE); + + emit_symbol(entropy, entropy->ac_tbl_no, nbits << 4); + if (nbits) + emit_bits_p(entropy, entropy->EOBRUN, nbits); + + entropy->EOBRUN = 0; + + /* Emit any buffered correction bits */ + emit_buffered_bits(entropy, entropy->bit_buffer, entropy->BE); + entropy->BE = 0; + } +} + +/* + * Emit a restart marker & resynchronize predictions. + */ + +LOCAL(void) +emit_restart_p (phuff_entropy_ptr entropy, int restart_num) +{ + int ci; + + emit_eobrun(entropy); + + if (! entropy->gather_statistics) { + flush_bits_p(entropy); + emit_byte(entropy, 0xFF); + emit_byte(entropy, JPEG_RST0 + restart_num); + } + + if (entropy->cinfo->Ss == 0) { + /* Re-initialize DC predictions to 0 */ + for (ci = 0; ci < entropy->cinfo->comps_in_scan; ci++) + entropy->last_dc_val[ci] = 0; + } else { + /* Re-initialize all AC-related fields to 0 */ + entropy->EOBRUN = 0; + entropy->BE = 0; + } +} + +/* + * MCU encoding for DC initial scan (either spectral selection, + * or first pass of successive approximation). + */ + +METHODDEF(boolean) +encode_mcu_DC_first (j_compress_ptr cinfo, JBLOCKROW *MCU_data) +{ + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + register int temp, temp2; + register int nbits; + int blkn, ci; + int Al = cinfo->Al; + JBLOCKROW block; + jpeg_component_info * compptr; + ISHIFT_TEMPS + + entropy->next_output_byte = cinfo->dest->next_output_byte; + entropy->free_in_buffer = cinfo->dest->free_in_buffer; + + /* Emit restart marker if needed */ + if (cinfo->restart_interval) + if (entropy->restarts_to_go == 0) + emit_restart_p(entropy, entropy->next_restart_num); + + /* Encode the MCU data blocks */ + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + block = MCU_data[blkn]; + ci = cinfo->MCU_membership[blkn]; + compptr = cinfo->cur_comp_info[ci]; + + /* Compute the DC value after the required point transform by Al. + * This is simply an arithmetic right shift. + */ + temp2 = IRIGHT_SHIFT((int) ((*block)[0]), Al); + + /* DC differences are figured on the point-transformed values. */ + temp = temp2 - entropy->last_dc_val[ci]; + entropy->last_dc_val[ci] = temp2; + + /* Encode the DC coefficient difference per section G.1.2.1 */ + temp2 = temp; + if (temp < 0) { + temp = -temp; /* temp is abs value of input */ + /* For a negative input, want temp2 = bitwise complement of abs(input) */ + /* This code assumes we are on a two's complement machine */ + temp2--; + } + + /* Find the number of bits needed for the magnitude of the coefficient */ + nbits = 0; + while (temp) { + nbits++; + temp >>= 1; + } + /* Check for out-of-range coefficient values. + * Since we're encoding a difference, the range limit is twice as much. + */ + if (nbits > MAX_COEF_BITS+1) + ERREXIT(cinfo, JERR_BAD_DCT_COEF); + + /* Count/emit the Huffman-coded symbol for the number of bits */ + emit_symbol(entropy, compptr->dc_tbl_no, nbits); + + /* Emit that number of bits of the value, if positive, */ + /* or the complement of its magnitude, if negative. */ + if (nbits) /* emit_bits rejects calls with size 0 */ + emit_bits_p(entropy, (unsigned int) temp2, nbits); + } + + cinfo->dest->next_output_byte = entropy->next_output_byte; + cinfo->dest->free_in_buffer = entropy->free_in_buffer; + + /* Update restart-interval state too */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) { + entropy->restarts_to_go = cinfo->restart_interval; + entropy->next_restart_num++; + entropy->next_restart_num &= 7; + } + entropy->restarts_to_go--; + } + + return TRUE; +} + +/* + * MCU encoding for AC initial scan (either spectral selection, + * or first pass of successive approximation). + */ + +METHODDEF(boolean) +encode_mcu_AC_first (j_compress_ptr cinfo, JBLOCKROW *MCU_data) +{ + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + register int temp, temp2; + register int nbits; + register int r, k; + int Se = cinfo->Se; + int Al = cinfo->Al; + JBLOCKROW block; + + entropy->next_output_byte = cinfo->dest->next_output_byte; + entropy->free_in_buffer = cinfo->dest->free_in_buffer; + + /* Emit restart marker if needed */ + if (cinfo->restart_interval) + if (entropy->restarts_to_go == 0) + emit_restart_p(entropy, entropy->next_restart_num); + + /* Encode the MCU data block */ + block = MCU_data[0]; + + /* Encode the AC coefficients per section G.1.2.2, fig. G.3 */ + + r = 0; /* r = run length of zeros */ + + for (k = cinfo->Ss; k <= Se; k++) { + if ((temp = (*block)[jpeg_natural_order[k]]) == 0) { + r++; + continue; + } + /* We must apply the point transform by Al. For AC coefficients this + * is an integer division with rounding towards 0. To do this portably + * in C, we shift after obtaining the absolute value; so the code is + * interwoven with finding the abs value (temp) and output bits (temp2). + */ + if (temp < 0) { + temp = -temp; /* temp is abs value of input */ + temp >>= Al; /* apply the point transform */ + /* For a negative coef, want temp2 = bitwise complement of abs(coef) */ + temp2 = ~temp; + } else { + temp >>= Al; /* apply the point transform */ + temp2 = temp; + } + /* Watch out for case that nonzero coef is zero after point transform */ + if (temp == 0) { + r++; + continue; + } + + /* Emit any pending EOBRUN */ + if (entropy->EOBRUN > 0) + emit_eobrun(entropy); + /* if run length > 15, must emit special run-length-16 codes (0xF0) */ + while (r > 15) { + emit_symbol(entropy, entropy->ac_tbl_no, 0xF0); + r -= 16; + } + + /* Find the number of bits needed for the magnitude of the coefficient */ + nbits = 1; /* there must be at least one 1 bit */ + while ((temp >>= 1)) + nbits++; + /* Check for out-of-range coefficient values */ + if (nbits > MAX_COEF_BITS) + ERREXIT(cinfo, JERR_BAD_DCT_COEF); + + /* Count/emit Huffman symbol for run length / number of bits */ + emit_symbol(entropy, entropy->ac_tbl_no, (r << 4) + nbits); + + /* Emit that number of bits of the value, if positive, */ + /* or the complement of its magnitude, if negative. */ + emit_bits_p(entropy, (unsigned int) temp2, nbits); + + r = 0; /* reset zero run length */ + } + + if (r > 0) { /* If there are trailing zeroes, */ + entropy->EOBRUN++; /* count an EOB */ + if (entropy->EOBRUN == 0x7FFF) + emit_eobrun(entropy); /* force it out to avoid overflow */ + } + + cinfo->dest->next_output_byte = entropy->next_output_byte; + cinfo->dest->free_in_buffer = entropy->free_in_buffer; + + /* Update restart-interval state too */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) { + entropy->restarts_to_go = cinfo->restart_interval; + entropy->next_restart_num++; + entropy->next_restart_num &= 7; + } + entropy->restarts_to_go--; + } + + return TRUE; +} + +/* + * MCU encoding for DC successive approximation refinement scan. + * Note: we assume such scans can be multi-component, although the spec + * is not very clear on the point. + */ + +METHODDEF(boolean) +encode_mcu_DC_refine (j_compress_ptr cinfo, JBLOCKROW *MCU_data) +{ + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + register int temp; + int blkn; + int Al = cinfo->Al; + JBLOCKROW block; + + entropy->next_output_byte = cinfo->dest->next_output_byte; + entropy->free_in_buffer = cinfo->dest->free_in_buffer; + + /* Emit restart marker if needed */ + if (cinfo->restart_interval) + if (entropy->restarts_to_go == 0) + emit_restart_p(entropy, entropy->next_restart_num); + + /* Encode the MCU data blocks */ + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + block = MCU_data[blkn]; + + /* We simply emit the Al'th bit of the DC coefficient value. */ + temp = (*block)[0]; + emit_bits_p(entropy, (unsigned int) (temp >> Al), 1); + } + + cinfo->dest->next_output_byte = entropy->next_output_byte; + cinfo->dest->free_in_buffer = entropy->free_in_buffer; + + /* Update restart-interval state too */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) { + entropy->restarts_to_go = cinfo->restart_interval; + entropy->next_restart_num++; + entropy->next_restart_num &= 7; + } + entropy->restarts_to_go--; + } + + return TRUE; +} + +/* + * MCU encoding for AC successive approximation refinement scan. + */ + +METHODDEF(boolean) +encode_mcu_AC_refine (j_compress_ptr cinfo, JBLOCKROW *MCU_data) +{ + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + register int temp; + register int r, k; + int EOB; + char *BR_buffer; + unsigned int BR; + int Se = cinfo->Se; + int Al = cinfo->Al; + JBLOCKROW block; + int absvalues[DCTSIZE2]; + + entropy->next_output_byte = cinfo->dest->next_output_byte; + entropy->free_in_buffer = cinfo->dest->free_in_buffer; + + /* Emit restart marker if needed */ + if (cinfo->restart_interval) + if (entropy->restarts_to_go == 0) + emit_restart_p(entropy, entropy->next_restart_num); + + /* Encode the MCU data block */ + block = MCU_data[0]; + + /* It is convenient to make a pre-pass to determine the transformed + * coefficients' absolute values and the EOB position. + */ + EOB = 0; + for (k = cinfo->Ss; k <= Se; k++) { + temp = (*block)[jpeg_natural_order[k]]; + /* We must apply the point transform by Al. For AC coefficients this + * is an integer division with rounding towards 0. To do this portably + * in C, we shift after obtaining the absolute value. + */ + if (temp < 0) + temp = -temp; /* temp is abs value of input */ + temp >>= Al; /* apply the point transform */ + absvalues[k] = temp; /* save abs value for main pass */ + if (temp == 1) + EOB = k; /* EOB = index of last newly-nonzero coef */ + } + + /* Encode the AC coefficients per section G.1.2.3, fig. G.7 */ + + r = 0; /* r = run length of zeros */ + BR = 0; /* BR = count of buffered bits added now */ + BR_buffer = entropy->bit_buffer + entropy->BE; /* Append bits to buffer */ + + for (k = cinfo->Ss; k <= Se; k++) { + if ((temp = absvalues[k]) == 0) { + r++; + continue; + } + + /* Emit any required ZRLs, but not if they can be folded into EOB */ + while (r > 15 && k <= EOB) { + /* emit any pending EOBRUN and the BE correction bits */ + emit_eobrun(entropy); + /* Emit ZRL */ + emit_symbol(entropy, entropy->ac_tbl_no, 0xF0); + r -= 16; + /* Emit buffered correction bits that must be associated with ZRL */ + emit_buffered_bits(entropy, BR_buffer, BR); + BR_buffer = entropy->bit_buffer; /* BE bits are gone now */ + BR = 0; + } + + /* If the coef was previously nonzero, it only needs a correction bit. + * NOTE: a straight translation of the spec's figure G.7 would suggest + * that we also need to test r > 15. But if r > 15, we can only get here + * if k > EOB, which implies that this coefficient is not 1. + */ + if (temp > 1) { + /* The correction bit is the next bit of the absolute value. */ + BR_buffer[BR++] = (char) (temp & 1); + continue; + } + + /* Emit any pending EOBRUN and the BE correction bits */ + emit_eobrun(entropy); + + /* Count/emit Huffman symbol for run length / number of bits */ + emit_symbol(entropy, entropy->ac_tbl_no, (r << 4) + 1); + + /* Emit output bit for newly-nonzero coef */ + temp = ((*block)[jpeg_natural_order[k]] < 0) ? 0 : 1; + emit_bits_p(entropy, (unsigned int) temp, 1); + + /* Emit buffered correction bits that must be associated with this code */ + emit_buffered_bits(entropy, BR_buffer, BR); + BR_buffer = entropy->bit_buffer; /* BE bits are gone now */ + BR = 0; + r = 0; /* reset zero run length */ + } + + if (r > 0 || BR > 0) { /* If there are trailing zeroes, */ + entropy->EOBRUN++; /* count an EOB */ + entropy->BE += BR; /* concat my correction bits to older ones */ + /* We force out the EOB if we risk either: + * 1. overflow of the EOB counter; + * 2. overflow of the correction bit buffer during the next MCU. + */ + if (entropy->EOBRUN == 0x7FFF || entropy->BE > (MAX_CORR_BITS-DCTSIZE2+1)) + emit_eobrun(entropy); + } + + cinfo->dest->next_output_byte = entropy->next_output_byte; + cinfo->dest->free_in_buffer = entropy->free_in_buffer; + + /* Update restart-interval state too */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) { + entropy->restarts_to_go = cinfo->restart_interval; + entropy->next_restart_num++; + entropy->next_restart_num &= 7; + } + entropy->restarts_to_go--; + } + + return TRUE; +} + +/* + * Finish up at the end of a Huffman-compressed progressive scan. + */ + +METHODDEF(void) +finish_pass_phuff (j_compress_ptr cinfo) +{ + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + + entropy->next_output_byte = cinfo->dest->next_output_byte; + entropy->free_in_buffer = cinfo->dest->free_in_buffer; + + /* Flush out any buffered data */ + emit_eobrun(entropy); + flush_bits_p(entropy); + + cinfo->dest->next_output_byte = entropy->next_output_byte; + cinfo->dest->free_in_buffer = entropy->free_in_buffer; +} + +/* + * Finish up a statistics-gathering pass and create the new Huffman tables. + */ + +METHODDEF(void) +finish_pass_gather_phuff (j_compress_ptr cinfo) +{ + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + boolean is_DC_band; + int ci, tbl; + jpeg_component_info * compptr; + JHUFF_TBL **htblptr; + boolean did[NUM_HUFF_TBLS]; + + /* Flush out buffered data (all we care about is counting the EOB symbol) */ + emit_eobrun(entropy); + + is_DC_band = (cinfo->Ss == 0); + + /* It's important not to apply jpeg_gen_optimal_table more than once + * per table, because it clobbers the input frequency counts! + */ + MEMZERO(did, SIZEOF(did)); + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + if (is_DC_band) { + if (cinfo->Ah != 0) /* DC refinement needs no table */ + continue; + tbl = compptr->dc_tbl_no; + } else { + tbl = compptr->ac_tbl_no; + } + if (! did[tbl]) { + if (is_DC_band) + htblptr = & cinfo->dc_huff_tbl_ptrs[tbl]; + else + htblptr = & cinfo->ac_huff_tbl_ptrs[tbl]; + if (*htblptr == NULL) + *htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo); + jpeg_gen_optimal_table(cinfo, *htblptr, entropy->count_ptrs[tbl]); + did[tbl] = TRUE; + } + } +} + +/* + * Module initialization routine for progressive Huffman entropy encoding. + */ + +GLOBAL(void) +jinit_phuff_encoder (j_compress_ptr cinfo) +{ + phuff_entropy_ptr entropy; + int i; + + entropy = (phuff_entropy_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(phuff_entropy_encoder)); + cinfo->entropy = (struct jpeg_entropy_encoder *) entropy; + entropy->pub.start_pass = start_pass_phuff; + + /* Mark tables unallocated */ + for (i = 0; i < NUM_HUFF_TBLS; i++) { + entropy->derived_tbls[i] = NULL; + entropy->count_ptrs[i] = NULL; + } + entropy->bit_buffer = NULL; /* needed only in AC refinement scan */ +} + +#endif /* C_PROGRESSIVE_SUPPORTED */ +/********* End of inlined file: jcphuff.c *********/ + +/********* Start of inlined file: jcprepct.c *********/ +#define JPEG_INTERNALS + +/* At present, jcsample.c can request context rows only for smoothing. + * In the future, we might also need context rows for CCIR601 sampling + * or other more-complex downsampling procedures. The code to support + * context rows should be compiled only if needed. + */ +#ifdef INPUT_SMOOTHING_SUPPORTED +#define CONTEXT_ROWS_SUPPORTED +#endif + +/* + * For the simple (no-context-row) case, we just need to buffer one + * row group's worth of pixels for the downsampling step. At the bottom of + * the image, we pad to a full row group by replicating the last pixel row. + * The downsampler's last output row is then replicated if needed to pad + * out to a full iMCU row. + * + * When providing context rows, we must buffer three row groups' worth of + * pixels. Three row groups are physically allocated, but the row pointer + * arrays are made five row groups high, with the extra pointers above and + * below "wrapping around" to point to the last and first real row groups. + * This allows the downsampler to access the proper context rows. + * At the top and bottom of the image, we create dummy context rows by + * copying the first or last real pixel row. This copying could be avoided + * by pointer hacking as is done in jdmainct.c, but it doesn't seem worth the + * trouble on the compression side. + */ + +/* Private buffer controller object */ + +typedef struct { + struct jpeg_c_prep_controller pub; /* public fields */ + + /* Downsampling input buffer. This buffer holds color-converted data + * until we have enough to do a downsample step. + */ + JSAMPARRAY color_buf[MAX_COMPONENTS]; + + JDIMENSION rows_to_go; /* counts rows remaining in source image */ + int next_buf_row; /* index of next row to store in color_buf */ + +#ifdef CONTEXT_ROWS_SUPPORTED /* only needed for context case */ + int this_row_group; /* starting row index of group to process */ + int next_buf_stop; /* downsample when we reach this index */ +#endif +} my_prep_controller; + +typedef my_prep_controller * my_prep_ptr; + +/* + * Initialize for a processing pass. + */ + +METHODDEF(void) +start_pass_prep (j_compress_ptr cinfo, J_BUF_MODE pass_mode) +{ + my_prep_ptr prep = (my_prep_ptr) cinfo->prep; + + if (pass_mode != JBUF_PASS_THRU) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + + /* Initialize total-height counter for detecting bottom of image */ + prep->rows_to_go = cinfo->image_height; + /* Mark the conversion buffer empty */ + prep->next_buf_row = 0; +#ifdef CONTEXT_ROWS_SUPPORTED + /* Preset additional state variables for context mode. + * These aren't used in non-context mode, so we needn't test which mode. + */ + prep->this_row_group = 0; + /* Set next_buf_stop to stop after two row groups have been read in. */ + prep->next_buf_stop = 2 * cinfo->max_v_samp_factor; +#endif +} + +/* + * Expand an image vertically from height input_rows to height output_rows, + * by duplicating the bottom row. + */ + +LOCAL(void) +expand_bottom_edge (JSAMPARRAY image_data, JDIMENSION num_cols, + int input_rows, int output_rows) +{ + register int row; + + for (row = input_rows; row < output_rows; row++) { + jcopy_sample_rows(image_data, input_rows-1, image_data, row, + 1, num_cols); + } +} + +/* + * Process some data in the simple no-context case. + * + * Preprocessor output data is counted in "row groups". A row group + * is defined to be v_samp_factor sample rows of each component. + * Downsampling will produce this much data from each max_v_samp_factor + * input rows. + */ + +METHODDEF(void) +pre_process_data (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JDIMENSION *in_row_ctr, + JDIMENSION in_rows_avail, + JSAMPIMAGE output_buf, JDIMENSION *out_row_group_ctr, + JDIMENSION out_row_groups_avail) +{ + my_prep_ptr prep = (my_prep_ptr) cinfo->prep; + int numrows, ci; + JDIMENSION inrows; + jpeg_component_info * compptr; + + while (*in_row_ctr < in_rows_avail && + *out_row_group_ctr < out_row_groups_avail) { + /* Do color conversion to fill the conversion buffer. */ + inrows = in_rows_avail - *in_row_ctr; + numrows = cinfo->max_v_samp_factor - prep->next_buf_row; + numrows = (int) MIN((JDIMENSION) numrows, inrows); + (*cinfo->cconvert->color_convert) (cinfo, input_buf + *in_row_ctr, + prep->color_buf, + (JDIMENSION) prep->next_buf_row, + numrows); + *in_row_ctr += numrows; + prep->next_buf_row += numrows; + prep->rows_to_go -= numrows; + /* If at bottom of image, pad to fill the conversion buffer. */ + if (prep->rows_to_go == 0 && + prep->next_buf_row < cinfo->max_v_samp_factor) { + for (ci = 0; ci < cinfo->num_components; ci++) { + expand_bottom_edge(prep->color_buf[ci], cinfo->image_width, + prep->next_buf_row, cinfo->max_v_samp_factor); + } + prep->next_buf_row = cinfo->max_v_samp_factor; + } + /* If we've filled the conversion buffer, empty it. */ + if (prep->next_buf_row == cinfo->max_v_samp_factor) { + (*cinfo->downsample->downsample) (cinfo, + prep->color_buf, (JDIMENSION) 0, + output_buf, *out_row_group_ctr); + prep->next_buf_row = 0; + (*out_row_group_ctr)++; + } + /* If at bottom of image, pad the output to a full iMCU height. + * Note we assume the caller is providing a one-iMCU-height output buffer! + */ + if (prep->rows_to_go == 0 && + *out_row_group_ctr < out_row_groups_avail) { + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + expand_bottom_edge(output_buf[ci], + compptr->width_in_blocks * DCTSIZE, + (int) (*out_row_group_ctr * compptr->v_samp_factor), + (int) (out_row_groups_avail * compptr->v_samp_factor)); + } + *out_row_group_ctr = out_row_groups_avail; + break; /* can exit outer loop without test */ + } + } +} + +#ifdef CONTEXT_ROWS_SUPPORTED + +/* + * Process some data in the context case. + */ + +METHODDEF(void) +pre_process_context (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JDIMENSION *in_row_ctr, + JDIMENSION in_rows_avail, + JSAMPIMAGE output_buf, JDIMENSION *out_row_group_ctr, + JDIMENSION out_row_groups_avail) +{ + my_prep_ptr prep = (my_prep_ptr) cinfo->prep; + int numrows, ci; + int buf_height = cinfo->max_v_samp_factor * 3; + JDIMENSION inrows; + + while (*out_row_group_ctr < out_row_groups_avail) { + if (*in_row_ctr < in_rows_avail) { + /* Do color conversion to fill the conversion buffer. */ + inrows = in_rows_avail - *in_row_ctr; + numrows = prep->next_buf_stop - prep->next_buf_row; + numrows = (int) MIN((JDIMENSION) numrows, inrows); + (*cinfo->cconvert->color_convert) (cinfo, input_buf + *in_row_ctr, + prep->color_buf, + (JDIMENSION) prep->next_buf_row, + numrows); + /* Pad at top of image, if first time through */ + if (prep->rows_to_go == cinfo->image_height) { + for (ci = 0; ci < cinfo->num_components; ci++) { + int row; + for (row = 1; row <= cinfo->max_v_samp_factor; row++) { + jcopy_sample_rows(prep->color_buf[ci], 0, + prep->color_buf[ci], -row, + 1, cinfo->image_width); + } + } + } + *in_row_ctr += numrows; + prep->next_buf_row += numrows; + prep->rows_to_go -= numrows; + } else { + /* Return for more data, unless we are at the bottom of the image. */ + if (prep->rows_to_go != 0) + break; + /* When at bottom of image, pad to fill the conversion buffer. */ + if (prep->next_buf_row < prep->next_buf_stop) { + for (ci = 0; ci < cinfo->num_components; ci++) { + expand_bottom_edge(prep->color_buf[ci], cinfo->image_width, + prep->next_buf_row, prep->next_buf_stop); + } + prep->next_buf_row = prep->next_buf_stop; + } + } + /* If we've gotten enough data, downsample a row group. */ + if (prep->next_buf_row == prep->next_buf_stop) { + (*cinfo->downsample->downsample) (cinfo, + prep->color_buf, + (JDIMENSION) prep->this_row_group, + output_buf, *out_row_group_ctr); + (*out_row_group_ctr)++; + /* Advance pointers with wraparound as necessary. */ + prep->this_row_group += cinfo->max_v_samp_factor; + if (prep->this_row_group >= buf_height) + prep->this_row_group = 0; + if (prep->next_buf_row >= buf_height) + prep->next_buf_row = 0; + prep->next_buf_stop = prep->next_buf_row + cinfo->max_v_samp_factor; + } + } +} + +/* + * Create the wrapped-around downsampling input buffer needed for context mode. + */ + +LOCAL(void) +create_context_buffer (j_compress_ptr cinfo) +{ + my_prep_ptr prep = (my_prep_ptr) cinfo->prep; + int rgroup_height = cinfo->max_v_samp_factor; + int ci, i; + jpeg_component_info * compptr; + JSAMPARRAY true_buffer, fake_buffer; + + /* Grab enough space for fake row pointers for all the components; + * we need five row groups' worth of pointers for each component. + */ + fake_buffer = (JSAMPARRAY) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (cinfo->num_components * 5 * rgroup_height) * + SIZEOF(JSAMPROW)); + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Allocate the actual buffer space (3 row groups) for this component. + * We make the buffer wide enough to allow the downsampler to edge-expand + * horizontally within the buffer, if it so chooses. + */ + true_buffer = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + (JDIMENSION) (((long) compptr->width_in_blocks * DCTSIZE * + cinfo->max_h_samp_factor) / compptr->h_samp_factor), + (JDIMENSION) (3 * rgroup_height)); + /* Copy true buffer row pointers into the middle of the fake row array */ + MEMCOPY(fake_buffer + rgroup_height, true_buffer, + 3 * rgroup_height * SIZEOF(JSAMPROW)); + /* Fill in the above and below wraparound pointers */ + for (i = 0; i < rgroup_height; i++) { + fake_buffer[i] = true_buffer[2 * rgroup_height + i]; + fake_buffer[4 * rgroup_height + i] = true_buffer[i]; + } + prep->color_buf[ci] = fake_buffer + rgroup_height; + fake_buffer += 5 * rgroup_height; /* point to space for next component */ + } +} + +#endif /* CONTEXT_ROWS_SUPPORTED */ + +/* + * Initialize preprocessing controller. + */ + +GLOBAL(void) +jinit_c_prep_controller (j_compress_ptr cinfo, boolean need_full_buffer) +{ + my_prep_ptr prep; + int ci; + jpeg_component_info * compptr; + + if (need_full_buffer) /* safety check */ + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + + prep = (my_prep_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_prep_controller)); + cinfo->prep = (struct jpeg_c_prep_controller *) prep; + prep->pub.start_pass = start_pass_prep; + + /* Allocate the color conversion buffer. + * We make the buffer wide enough to allow the downsampler to edge-expand + * horizontally within the buffer, if it so chooses. + */ + if (cinfo->downsample->need_context_rows) { + /* Set up to provide context rows */ +#ifdef CONTEXT_ROWS_SUPPORTED + prep->pub.pre_process_data = pre_process_context; + create_context_buffer(cinfo); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else { + /* No context, just make it tall enough for one row group */ + prep->pub.pre_process_data = pre_process_data; + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + prep->color_buf[ci] = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + (JDIMENSION) (((long) compptr->width_in_blocks * DCTSIZE * + cinfo->max_h_samp_factor) / compptr->h_samp_factor), + (JDIMENSION) cinfo->max_v_samp_factor); + } + } +} +/********* End of inlined file: jcprepct.c *********/ + +/********* Start of inlined file: jcsample.c *********/ +#define JPEG_INTERNALS + +/* Pointer to routine to downsample a single component */ +typedef JMETHOD(void, downsample1_ptr, + (j_compress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY output_data)); + +/* Private subobject */ + +typedef struct { + struct jpeg_downsampler pub; /* public fields */ + + /* Downsampling method pointers, one per component */ + downsample1_ptr methods[MAX_COMPONENTS]; +} my_downsampler; + +typedef my_downsampler * my_downsample_ptr; + +/* + * Initialize for a downsampling pass. + */ + +METHODDEF(void) +start_pass_downsample (j_compress_ptr cinfo) +{ + /* no work for now */ +} + +/* + * Expand a component horizontally from width input_cols to width output_cols, + * by duplicating the rightmost samples. + */ + +LOCAL(void) +expand_right_edge (JSAMPARRAY image_data, int num_rows, + JDIMENSION input_cols, JDIMENSION output_cols) +{ + register JSAMPROW ptr; + register JSAMPLE pixval; + register int count; + int row; + int numcols = (int) (output_cols - input_cols); + + if (numcols > 0) { + for (row = 0; row < num_rows; row++) { + ptr = image_data[row] + input_cols; + pixval = ptr[-1]; /* don't need GETJSAMPLE() here */ + for (count = numcols; count > 0; count--) + *ptr++ = pixval; + } + } +} + +/* + * Do downsampling for a whole row group (all components). + * + * In this version we simply downsample each component independently. + */ + +METHODDEF(void) +sep_downsample (j_compress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION in_row_index, + JSAMPIMAGE output_buf, JDIMENSION out_row_group_index) +{ + my_downsample_ptr downsample = (my_downsample_ptr) cinfo->downsample; + int ci; + jpeg_component_info * compptr; + JSAMPARRAY in_ptr, out_ptr; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + in_ptr = input_buf[ci] + in_row_index; + out_ptr = output_buf[ci] + (out_row_group_index * compptr->v_samp_factor); + (*downsample->methods[ci]) (cinfo, compptr, in_ptr, out_ptr); + } +} + +/* + * Downsample pixel values of a single component. + * One row group is processed per call. + * This version handles arbitrary integral sampling ratios, without smoothing. + * Note that this version is not actually used for customary sampling ratios. + */ + +METHODDEF(void) +int_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY output_data) +{ + int inrow, outrow, h_expand, v_expand, numpix, numpix2, h, v; + JDIMENSION outcol, outcol_h; /* outcol_h == outcol*h_expand */ + JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE; + JSAMPROW inptr, outptr; + INT32 outvalue; + + h_expand = cinfo->max_h_samp_factor / compptr->h_samp_factor; + v_expand = cinfo->max_v_samp_factor / compptr->v_samp_factor; + numpix = h_expand * v_expand; + numpix2 = numpix/2; + + /* Expand input data enough to let all the output samples be generated + * by the standard loop. Special-casing padded output would be more + * efficient. + */ + expand_right_edge(input_data, cinfo->max_v_samp_factor, + cinfo->image_width, output_cols * h_expand); + + inrow = 0; + for (outrow = 0; outrow < compptr->v_samp_factor; outrow++) { + outptr = output_data[outrow]; + for (outcol = 0, outcol_h = 0; outcol < output_cols; + outcol++, outcol_h += h_expand) { + outvalue = 0; + for (v = 0; v < v_expand; v++) { + inptr = input_data[inrow+v] + outcol_h; + for (h = 0; h < h_expand; h++) { + outvalue += (INT32) GETJSAMPLE(*inptr++); + } + } + *outptr++ = (JSAMPLE) ((outvalue + numpix2) / numpix); + } + inrow += v_expand; + } +} + +/* + * Downsample pixel values of a single component. + * This version handles the special case of a full-size component, + * without smoothing. + */ + +METHODDEF(void) +fullsize_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY output_data) +{ + /* Copy the data */ + jcopy_sample_rows(input_data, 0, output_data, 0, + cinfo->max_v_samp_factor, cinfo->image_width); + /* Edge-expand */ + expand_right_edge(output_data, cinfo->max_v_samp_factor, + cinfo->image_width, compptr->width_in_blocks * DCTSIZE); +} + +/* + * Downsample pixel values of a single component. + * This version handles the common case of 2:1 horizontal and 1:1 vertical, + * without smoothing. + * + * A note about the "bias" calculations: when rounding fractional values to + * integer, we do not want to always round 0.5 up to the next integer. + * If we did that, we'd introduce a noticeable bias towards larger values. + * Instead, this code is arranged so that 0.5 will be rounded up or down at + * alternate pixel locations (a simple ordered dither pattern). + */ + +METHODDEF(void) +h2v1_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY output_data) +{ + int outrow; + JDIMENSION outcol; + JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE; + register JSAMPROW inptr, outptr; + register int bias; + + /* Expand input data enough to let all the output samples be generated + * by the standard loop. Special-casing padded output would be more + * efficient. + */ + expand_right_edge(input_data, cinfo->max_v_samp_factor, + cinfo->image_width, output_cols * 2); + + for (outrow = 0; outrow < compptr->v_samp_factor; outrow++) { + outptr = output_data[outrow]; + inptr = input_data[outrow]; + bias = 0; /* bias = 0,1,0,1,... for successive samples */ + for (outcol = 0; outcol < output_cols; outcol++) { + *outptr++ = (JSAMPLE) ((GETJSAMPLE(*inptr) + GETJSAMPLE(inptr[1]) + + bias) >> 1); + bias ^= 1; /* 0=>1, 1=>0 */ + inptr += 2; + } + } +} + +/* + * Downsample pixel values of a single component. + * This version handles the standard case of 2:1 horizontal and 2:1 vertical, + * without smoothing. + */ + +METHODDEF(void) +h2v2_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY output_data) +{ + int inrow, outrow; + JDIMENSION outcol; + JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE; + register JSAMPROW inptr0, inptr1, outptr; + register int bias; + + /* Expand input data enough to let all the output samples be generated + * by the standard loop. Special-casing padded output would be more + * efficient. + */ + expand_right_edge(input_data, cinfo->max_v_samp_factor, + cinfo->image_width, output_cols * 2); + + inrow = 0; + for (outrow = 0; outrow < compptr->v_samp_factor; outrow++) { + outptr = output_data[outrow]; + inptr0 = input_data[inrow]; + inptr1 = input_data[inrow+1]; + bias = 1; /* bias = 1,2,1,2,... for successive samples */ + for (outcol = 0; outcol < output_cols; outcol++) { + *outptr++ = (JSAMPLE) ((GETJSAMPLE(*inptr0) + GETJSAMPLE(inptr0[1]) + + GETJSAMPLE(*inptr1) + GETJSAMPLE(inptr1[1]) + + bias) >> 2); + bias ^= 3; /* 1=>2, 2=>1 */ + inptr0 += 2; inptr1 += 2; + } + inrow += 2; + } +} + +#ifdef INPUT_SMOOTHING_SUPPORTED + +/* + * Downsample pixel values of a single component. + * This version handles the standard case of 2:1 horizontal and 2:1 vertical, + * with smoothing. One row of context is required. + */ + +METHODDEF(void) +h2v2_smooth_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY output_data) +{ + int inrow, outrow; + JDIMENSION colctr; + JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE; + register JSAMPROW inptr0, inptr1, above_ptr, below_ptr, outptr; + INT32 membersum, neighsum, memberscale, neighscale; + + /* Expand input data enough to let all the output samples be generated + * by the standard loop. Special-casing padded output would be more + * efficient. + */ + expand_right_edge(input_data - 1, cinfo->max_v_samp_factor + 2, + cinfo->image_width, output_cols * 2); + + /* We don't bother to form the individual "smoothed" input pixel values; + * we can directly compute the output which is the average of the four + * smoothed values. Each of the four member pixels contributes a fraction + * (1-8*SF) to its own smoothed image and a fraction SF to each of the three + * other smoothed pixels, therefore a total fraction (1-5*SF)/4 to the final + * output. The four corner-adjacent neighbor pixels contribute a fraction + * SF to just one smoothed pixel, or SF/4 to the final output; while the + * eight edge-adjacent neighbors contribute SF to each of two smoothed + * pixels, or SF/2 overall. In order to use integer arithmetic, these + * factors are scaled by 2^16 = 65536. + * Also recall that SF = smoothing_factor / 1024. + */ + + memberscale = 16384 - cinfo->smoothing_factor * 80; /* scaled (1-5*SF)/4 */ + neighscale = cinfo->smoothing_factor * 16; /* scaled SF/4 */ + + inrow = 0; + for (outrow = 0; outrow < compptr->v_samp_factor; outrow++) { + outptr = output_data[outrow]; + inptr0 = input_data[inrow]; + inptr1 = input_data[inrow+1]; + above_ptr = input_data[inrow-1]; + below_ptr = input_data[inrow+2]; + + /* Special case for first column: pretend column -1 is same as column 0 */ + membersum = GETJSAMPLE(*inptr0) + GETJSAMPLE(inptr0[1]) + + GETJSAMPLE(*inptr1) + GETJSAMPLE(inptr1[1]); + neighsum = GETJSAMPLE(*above_ptr) + GETJSAMPLE(above_ptr[1]) + + GETJSAMPLE(*below_ptr) + GETJSAMPLE(below_ptr[1]) + + GETJSAMPLE(*inptr0) + GETJSAMPLE(inptr0[2]) + + GETJSAMPLE(*inptr1) + GETJSAMPLE(inptr1[2]); + neighsum += neighsum; + neighsum += GETJSAMPLE(*above_ptr) + GETJSAMPLE(above_ptr[2]) + + GETJSAMPLE(*below_ptr) + GETJSAMPLE(below_ptr[2]); + membersum = membersum * memberscale + neighsum * neighscale; + *outptr++ = (JSAMPLE) ((membersum + 32768) >> 16); + inptr0 += 2; inptr1 += 2; above_ptr += 2; below_ptr += 2; + + for (colctr = output_cols - 2; colctr > 0; colctr--) { + /* sum of pixels directly mapped to this output element */ + membersum = GETJSAMPLE(*inptr0) + GETJSAMPLE(inptr0[1]) + + GETJSAMPLE(*inptr1) + GETJSAMPLE(inptr1[1]); + /* sum of edge-neighbor pixels */ + neighsum = GETJSAMPLE(*above_ptr) + GETJSAMPLE(above_ptr[1]) + + GETJSAMPLE(*below_ptr) + GETJSAMPLE(below_ptr[1]) + + GETJSAMPLE(inptr0[-1]) + GETJSAMPLE(inptr0[2]) + + GETJSAMPLE(inptr1[-1]) + GETJSAMPLE(inptr1[2]); + /* The edge-neighbors count twice as much as corner-neighbors */ + neighsum += neighsum; + /* Add in the corner-neighbors */ + neighsum += GETJSAMPLE(above_ptr[-1]) + GETJSAMPLE(above_ptr[2]) + + GETJSAMPLE(below_ptr[-1]) + GETJSAMPLE(below_ptr[2]); + /* form final output scaled up by 2^16 */ + membersum = membersum * memberscale + neighsum * neighscale; + /* round, descale and output it */ + *outptr++ = (JSAMPLE) ((membersum + 32768) >> 16); + inptr0 += 2; inptr1 += 2; above_ptr += 2; below_ptr += 2; + } + + /* Special case for last column */ + membersum = GETJSAMPLE(*inptr0) + GETJSAMPLE(inptr0[1]) + + GETJSAMPLE(*inptr1) + GETJSAMPLE(inptr1[1]); + neighsum = GETJSAMPLE(*above_ptr) + GETJSAMPLE(above_ptr[1]) + + GETJSAMPLE(*below_ptr) + GETJSAMPLE(below_ptr[1]) + + GETJSAMPLE(inptr0[-1]) + GETJSAMPLE(inptr0[1]) + + GETJSAMPLE(inptr1[-1]) + GETJSAMPLE(inptr1[1]); + neighsum += neighsum; + neighsum += GETJSAMPLE(above_ptr[-1]) + GETJSAMPLE(above_ptr[1]) + + GETJSAMPLE(below_ptr[-1]) + GETJSAMPLE(below_ptr[1]); + membersum = membersum * memberscale + neighsum * neighscale; + *outptr = (JSAMPLE) ((membersum + 32768) >> 16); + + inrow += 2; + } +} + +/* + * Downsample pixel values of a single component. + * This version handles the special case of a full-size component, + * with smoothing. One row of context is required. + */ + +METHODDEF(void) +fullsize_smooth_downsample (j_compress_ptr cinfo, jpeg_component_info *compptr, + JSAMPARRAY input_data, JSAMPARRAY output_data) +{ + int outrow; + JDIMENSION colctr; + JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE; + register JSAMPROW inptr, above_ptr, below_ptr, outptr; + INT32 membersum, neighsum, memberscale, neighscale; + int colsum, lastcolsum, nextcolsum; + + /* Expand input data enough to let all the output samples be generated + * by the standard loop. Special-casing padded output would be more + * efficient. + */ + expand_right_edge(input_data - 1, cinfo->max_v_samp_factor + 2, + cinfo->image_width, output_cols); + + /* Each of the eight neighbor pixels contributes a fraction SF to the + * smoothed pixel, while the main pixel contributes (1-8*SF). In order + * to use integer arithmetic, these factors are multiplied by 2^16 = 65536. + * Also recall that SF = smoothing_factor / 1024. + */ + + memberscale = 65536L - cinfo->smoothing_factor * 512L; /* scaled 1-8*SF */ + neighscale = cinfo->smoothing_factor * 64; /* scaled SF */ + + for (outrow = 0; outrow < compptr->v_samp_factor; outrow++) { + outptr = output_data[outrow]; + inptr = input_data[outrow]; + above_ptr = input_data[outrow-1]; + below_ptr = input_data[outrow+1]; + + /* Special case for first column */ + colsum = GETJSAMPLE(*above_ptr++) + GETJSAMPLE(*below_ptr++) + + GETJSAMPLE(*inptr); + membersum = GETJSAMPLE(*inptr++); + nextcolsum = GETJSAMPLE(*above_ptr) + GETJSAMPLE(*below_ptr) + + GETJSAMPLE(*inptr); + neighsum = colsum + (colsum - membersum) + nextcolsum; + membersum = membersum * memberscale + neighsum * neighscale; + *outptr++ = (JSAMPLE) ((membersum + 32768) >> 16); + lastcolsum = colsum; colsum = nextcolsum; + + for (colctr = output_cols - 2; colctr > 0; colctr--) { + membersum = GETJSAMPLE(*inptr++); + above_ptr++; below_ptr++; + nextcolsum = GETJSAMPLE(*above_ptr) + GETJSAMPLE(*below_ptr) + + GETJSAMPLE(*inptr); + neighsum = lastcolsum + (colsum - membersum) + nextcolsum; + membersum = membersum * memberscale + neighsum * neighscale; + *outptr++ = (JSAMPLE) ((membersum + 32768) >> 16); + lastcolsum = colsum; colsum = nextcolsum; + } + + /* Special case for last column */ + membersum = GETJSAMPLE(*inptr); + neighsum = lastcolsum + (colsum - membersum) + colsum; + membersum = membersum * memberscale + neighsum * neighscale; + *outptr = (JSAMPLE) ((membersum + 32768) >> 16); + + } +} + +#endif /* INPUT_SMOOTHING_SUPPORTED */ + +/* + * Module initialization routine for downsampling. + * Note that we must select a routine for each component. + */ + +GLOBAL(void) +jinit_downsampler (j_compress_ptr cinfo) +{ + my_downsample_ptr downsample; + int ci; + jpeg_component_info * compptr; + boolean smoothok = TRUE; + + downsample = (my_downsample_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_downsampler)); + cinfo->downsample = (struct jpeg_downsampler *) downsample; + downsample->pub.start_pass = start_pass_downsample; + downsample->pub.downsample = sep_downsample; + downsample->pub.need_context_rows = FALSE; + + if (cinfo->CCIR601_sampling) + ERREXIT(cinfo, JERR_CCIR601_NOTIMPL); + + /* Verify we can handle the sampling factors, and set up method pointers */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + if (compptr->h_samp_factor == cinfo->max_h_samp_factor && + compptr->v_samp_factor == cinfo->max_v_samp_factor) { +#ifdef INPUT_SMOOTHING_SUPPORTED + if (cinfo->smoothing_factor) { + downsample->methods[ci] = fullsize_smooth_downsample; + downsample->pub.need_context_rows = TRUE; + } else +#endif + downsample->methods[ci] = fullsize_downsample; + } else if (compptr->h_samp_factor * 2 == cinfo->max_h_samp_factor && + compptr->v_samp_factor == cinfo->max_v_samp_factor) { + smoothok = FALSE; + downsample->methods[ci] = h2v1_downsample; + } else if (compptr->h_samp_factor * 2 == cinfo->max_h_samp_factor && + compptr->v_samp_factor * 2 == cinfo->max_v_samp_factor) { +#ifdef INPUT_SMOOTHING_SUPPORTED + if (cinfo->smoothing_factor) { + downsample->methods[ci] = h2v2_smooth_downsample; + downsample->pub.need_context_rows = TRUE; + } else +#endif + downsample->methods[ci] = h2v2_downsample; + } else if ((cinfo->max_h_samp_factor % compptr->h_samp_factor) == 0 && + (cinfo->max_v_samp_factor % compptr->v_samp_factor) == 0) { + smoothok = FALSE; + downsample->methods[ci] = int_downsample; + } else + ERREXIT(cinfo, JERR_FRACT_SAMPLE_NOTIMPL); + } + +#ifdef INPUT_SMOOTHING_SUPPORTED + if (cinfo->smoothing_factor && !smoothok) + TRACEMS(cinfo, 0, JTRC_SMOOTH_NOTIMPL); +#endif +} +/********* End of inlined file: jcsample.c *********/ + +/********* Start of inlined file: jctrans.c *********/ +#define JPEG_INTERNALS + +/* Forward declarations */ +LOCAL(void) transencode_master_selection + JPP((j_compress_ptr cinfo, jvirt_barray_ptr * coef_arrays)); +LOCAL(void) transencode_coef_controller + JPP((j_compress_ptr cinfo, jvirt_barray_ptr * coef_arrays)); + +/* + * Compression initialization for writing raw-coefficient data. + * Before calling this, all parameters and a data destination must be set up. + * Call jpeg_finish_compress() to actually write the data. + * + * The number of passed virtual arrays must match cinfo->num_components. + * Note that the virtual arrays need not be filled or even realized at + * the time write_coefficients is called; indeed, if the virtual arrays + * were requested from this compression object's memory manager, they + * typically will be realized during this routine and filled afterwards. + */ + +GLOBAL(void) +jpeg_write_coefficients (j_compress_ptr cinfo, jvirt_barray_ptr * coef_arrays) +{ + if (cinfo->global_state != CSTATE_START) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + /* Mark all tables to be written */ + jpeg_suppress_tables(cinfo, FALSE); + /* (Re)initialize error mgr and destination modules */ + (*cinfo->err->reset_error_mgr) ((j_common_ptr) cinfo); + (*cinfo->dest->init_destination) (cinfo); + /* Perform master selection of active modules */ + transencode_master_selection(cinfo, coef_arrays); + /* Wait for jpeg_finish_compress() call */ + cinfo->next_scanline = 0; /* so jpeg_write_marker works */ + cinfo->global_state = CSTATE_WRCOEFS; +} + +/* + * Initialize the compression object with default parameters, + * then copy from the source object all parameters needed for lossless + * transcoding. Parameters that can be varied without loss (such as + * scan script and Huffman optimization) are left in their default states. + */ + +GLOBAL(void) +jpeg_copy_critical_parameters (j_decompress_ptr srcinfo, + j_compress_ptr dstinfo) +{ + JQUANT_TBL ** qtblptr; + jpeg_component_info *incomp, *outcomp; + JQUANT_TBL *c_quant, *slot_quant; + int tblno, ci, coefi; + + /* Safety check to ensure start_compress not called yet. */ + if (dstinfo->global_state != CSTATE_START) + ERREXIT1(dstinfo, JERR_BAD_STATE, dstinfo->global_state); + /* Copy fundamental image dimensions */ + dstinfo->image_width = srcinfo->image_width; + dstinfo->image_height = srcinfo->image_height; + dstinfo->input_components = srcinfo->num_components; + dstinfo->in_color_space = srcinfo->jpeg_color_space; + /* Initialize all parameters to default values */ + jpeg_set_defaults(dstinfo); + /* jpeg_set_defaults may choose wrong colorspace, eg YCbCr if input is RGB. + * Fix it to get the right header markers for the image colorspace. + */ + jpeg_set_colorspace(dstinfo, srcinfo->jpeg_color_space); + dstinfo->data_precision = srcinfo->data_precision; + dstinfo->CCIR601_sampling = srcinfo->CCIR601_sampling; + /* Copy the source's quantization tables. */ + for (tblno = 0; tblno < NUM_QUANT_TBLS; tblno++) { + if (srcinfo->quant_tbl_ptrs[tblno] != NULL) { + qtblptr = & dstinfo->quant_tbl_ptrs[tblno]; + if (*qtblptr == NULL) + *qtblptr = jpeg_alloc_quant_table((j_common_ptr) dstinfo); + MEMCOPY((*qtblptr)->quantval, + srcinfo->quant_tbl_ptrs[tblno]->quantval, + SIZEOF((*qtblptr)->quantval)); + (*qtblptr)->sent_table = FALSE; + } + } + /* Copy the source's per-component info. + * Note we assume jpeg_set_defaults has allocated the dest comp_info array. + */ + dstinfo->num_components = srcinfo->num_components; + if (dstinfo->num_components < 1 || dstinfo->num_components > MAX_COMPONENTS) + ERREXIT2(dstinfo, JERR_COMPONENT_COUNT, dstinfo->num_components, + MAX_COMPONENTS); + for (ci = 0, incomp = srcinfo->comp_info, outcomp = dstinfo->comp_info; + ci < dstinfo->num_components; ci++, incomp++, outcomp++) { + outcomp->component_id = incomp->component_id; + outcomp->h_samp_factor = incomp->h_samp_factor; + outcomp->v_samp_factor = incomp->v_samp_factor; + outcomp->quant_tbl_no = incomp->quant_tbl_no; + /* Make sure saved quantization table for component matches the qtable + * slot. If not, the input file re-used this qtable slot. + * IJG encoder currently cannot duplicate this. + */ + tblno = outcomp->quant_tbl_no; + if (tblno < 0 || tblno >= NUM_QUANT_TBLS || + srcinfo->quant_tbl_ptrs[tblno] == NULL) + ERREXIT1(dstinfo, JERR_NO_QUANT_TABLE, tblno); + slot_quant = srcinfo->quant_tbl_ptrs[tblno]; + c_quant = incomp->quant_table; + if (c_quant != NULL) { + for (coefi = 0; coefi < DCTSIZE2; coefi++) { + if (c_quant->quantval[coefi] != slot_quant->quantval[coefi]) + ERREXIT1(dstinfo, JERR_MISMATCHED_QUANT_TABLE, tblno); + } + } + /* Note: we do not copy the source's Huffman table assignments; + * instead we rely on jpeg_set_colorspace to have made a suitable choice. + */ + } + /* Also copy JFIF version and resolution information, if available. + * Strictly speaking this isn't "critical" info, but it's nearly + * always appropriate to copy it if available. In particular, + * if the application chooses to copy JFIF 1.02 extension markers from + * the source file, we need to copy the version to make sure we don't + * emit a file that has 1.02 extensions but a claimed version of 1.01. + * We will *not*, however, copy version info from mislabeled "2.01" files. + */ + if (srcinfo->saw_JFIF_marker) { + if (srcinfo->JFIF_major_version == 1) { + dstinfo->JFIF_major_version = srcinfo->JFIF_major_version; + dstinfo->JFIF_minor_version = srcinfo->JFIF_minor_version; + } + dstinfo->density_unit = srcinfo->density_unit; + dstinfo->X_density = srcinfo->X_density; + dstinfo->Y_density = srcinfo->Y_density; + } +} + +/* + * Master selection of compression modules for transcoding. + * This substitutes for jcinit.c's initialization of the full compressor. + */ + +LOCAL(void) +transencode_master_selection (j_compress_ptr cinfo, + jvirt_barray_ptr * coef_arrays) +{ + /* Although we don't actually use input_components for transcoding, + * jcmaster.c's initial_setup will complain if input_components is 0. + */ + cinfo->input_components = 1; + /* Initialize master control (includes parameter checking/processing) */ + jinit_c_master_control(cinfo, TRUE /* transcode only */); + + /* Entropy encoding: either Huffman or arithmetic coding. */ + if (cinfo->arith_code) { + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); + } else { + if (cinfo->progressive_mode) { +#ifdef C_PROGRESSIVE_SUPPORTED + jinit_phuff_encoder(cinfo); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else + jinit_huff_encoder(cinfo); + } + + /* We need a special coefficient buffer controller. */ + transencode_coef_controller(cinfo, coef_arrays); + + jinit_marker_writer(cinfo); + + /* We can now tell the memory manager to allocate virtual arrays. */ + (*cinfo->mem->realize_virt_arrays) ((j_common_ptr) cinfo); + + /* Write the datastream header (SOI, JFIF) immediately. + * Frame and scan headers are postponed till later. + * This lets application insert special markers after the SOI. + */ + (*cinfo->marker->write_file_header) (cinfo); +} + +/* + * The rest of this file is a special implementation of the coefficient + * buffer controller. This is similar to jccoefct.c, but it handles only + * output from presupplied virtual arrays. Furthermore, we generate any + * dummy padding blocks on-the-fly rather than expecting them to be present + * in the arrays. + */ + +/* Private buffer controller object */ + +typedef struct { + struct jpeg_c_coef_controller pub; /* public fields */ + + JDIMENSION iMCU_row_num; /* iMCU row # within image */ + JDIMENSION mcu_ctr; /* counts MCUs processed in current row */ + int MCU_vert_offset; /* counts MCU rows within iMCU row */ + int MCU_rows_per_iMCU_row; /* number of such rows needed */ + + /* Virtual block array for each component. */ + jvirt_barray_ptr * whole_image; + + /* Workspace for constructing dummy blocks at right/bottom edges. */ + JBLOCKROW dummy_buffer[C_MAX_BLOCKS_IN_MCU]; +} my_coef_controller2; + +typedef my_coef_controller2 * my_coef_ptr2; + +LOCAL(void) +start_iMCU_row2 (j_compress_ptr cinfo) +/* Reset within-iMCU-row counters for a new row */ +{ + my_coef_ptr2 coef = (my_coef_ptr2) cinfo->coef; + + /* In an interleaved scan, an MCU row is the same as an iMCU row. + * In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. + * But at the bottom of the image, process only what's left. + */ + if (cinfo->comps_in_scan > 1) { + coef->MCU_rows_per_iMCU_row = 1; + } else { + if (coef->iMCU_row_num < (cinfo->total_iMCU_rows-1)) + coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->v_samp_factor; + else + coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->last_row_height; + } + + coef->mcu_ctr = 0; + coef->MCU_vert_offset = 0; +} + +/* + * Initialize for a processing pass. + */ + +METHODDEF(void) +start_pass_coef2 (j_compress_ptr cinfo, J_BUF_MODE pass_mode) +{ + my_coef_ptr2 coef = (my_coef_ptr2) cinfo->coef; + + if (pass_mode != JBUF_CRANK_DEST) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + + coef->iMCU_row_num = 0; + start_iMCU_row2(cinfo); +} + +/* + * Process some data. + * We process the equivalent of one fully interleaved MCU row ("iMCU" row) + * per call, ie, v_samp_factor block rows for each component in the scan. + * The data is obtained from the virtual arrays and fed to the entropy coder. + * Returns TRUE if the iMCU row is completed, FALSE if suspended. + * + * NB: input_buf is ignored; it is likely to be a NULL pointer. + */ + +METHODDEF(boolean) +compress_output2 (j_compress_ptr cinfo, JSAMPIMAGE input_buf) +{ + my_coef_ptr2 coef = (my_coef_ptr2) cinfo->coef; + JDIMENSION MCU_col_num; /* index of current MCU within row */ + JDIMENSION last_MCU_col = cinfo->MCUs_per_row - 1; + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + int blkn, ci, xindex, yindex, yoffset, blockcnt; + JDIMENSION start_col; + JBLOCKARRAY buffer[MAX_COMPS_IN_SCAN]; + JBLOCKROW MCU_buffer[C_MAX_BLOCKS_IN_MCU]; + JBLOCKROW buffer_ptr; + jpeg_component_info *compptr; + + /* Align the virtual buffers for the components used in this scan. */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + buffer[ci] = (*cinfo->mem->access_virt_barray) + ((j_common_ptr) cinfo, coef->whole_image[compptr->component_index], + coef->iMCU_row_num * compptr->v_samp_factor, + (JDIMENSION) compptr->v_samp_factor, FALSE); + } + + /* Loop to process one whole iMCU row */ + for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row; + yoffset++) { + for (MCU_col_num = coef->mcu_ctr; MCU_col_num < cinfo->MCUs_per_row; + MCU_col_num++) { + /* Construct list of pointers to DCT blocks belonging to this MCU */ + blkn = 0; /* index of current DCT block within MCU */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + start_col = MCU_col_num * compptr->MCU_width; + blockcnt = (MCU_col_num < last_MCU_col) ? compptr->MCU_width + : compptr->last_col_width; + for (yindex = 0; yindex < compptr->MCU_height; yindex++) { + if (coef->iMCU_row_num < last_iMCU_row || + yindex+yoffset < compptr->last_row_height) { + /* Fill in pointers to real blocks in this row */ + buffer_ptr = buffer[ci][yindex+yoffset] + start_col; + for (xindex = 0; xindex < blockcnt; xindex++) + MCU_buffer[blkn++] = buffer_ptr++; + } else { + /* At bottom of image, need a whole row of dummy blocks */ + xindex = 0; + } + /* Fill in any dummy blocks needed in this row. + * Dummy blocks are filled in the same way as in jccoefct.c: + * all zeroes in the AC entries, DC entries equal to previous + * block's DC value. The init routine has already zeroed the + * AC entries, so we need only set the DC entries correctly. + */ + for (; xindex < compptr->MCU_width; xindex++) { + MCU_buffer[blkn] = coef->dummy_buffer[blkn]; + MCU_buffer[blkn][0][0] = MCU_buffer[blkn-1][0][0]; + blkn++; + } + } + } + /* Try to write the MCU. */ + if (! (*cinfo->entropy->encode_mcu) (cinfo, MCU_buffer)) { + /* Suspension forced; update state counters and exit */ + coef->MCU_vert_offset = yoffset; + coef->mcu_ctr = MCU_col_num; + return FALSE; + } + } + /* Completed an MCU row, but perhaps not an iMCU row */ + coef->mcu_ctr = 0; + } + /* Completed the iMCU row, advance counters for next one */ + coef->iMCU_row_num++; + start_iMCU_row2(cinfo); + return TRUE; +} + +/* + * Initialize coefficient buffer controller. + * + * Each passed coefficient array must be the right size for that + * coefficient: width_in_blocks wide and height_in_blocks high, + * with unitheight at least v_samp_factor. + */ + +LOCAL(void) +transencode_coef_controller (j_compress_ptr cinfo, + jvirt_barray_ptr * coef_arrays) +{ + my_coef_ptr2 coef; + JBLOCKROW buffer; + int i; + + coef = (my_coef_ptr2) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_coef_controller2)); + cinfo->coef = (struct jpeg_c_coef_controller *) coef; + coef->pub.start_pass = start_pass_coef2; + coef->pub.compress_data = compress_output2; + + /* Save pointer to virtual arrays */ + coef->whole_image = coef_arrays; + + /* Allocate and pre-zero space for dummy DCT blocks. */ + buffer = (JBLOCKROW) + (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE, + C_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK)); + jzero_far((void FAR *) buffer, C_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK)); + for (i = 0; i < C_MAX_BLOCKS_IN_MCU; i++) { + coef->dummy_buffer[i] = buffer + i; + } +} +/********* End of inlined file: jctrans.c *********/ + +/********* Start of inlined file: jdapistd.c *********/ +#define JPEG_INTERNALS + +/* Forward declarations */ +LOCAL(boolean) output_pass_setup JPP((j_decompress_ptr cinfo)); + +/* + * Decompression initialization. + * jpeg_read_header must be completed before calling this. + * + * If a multipass operating mode was selected, this will do all but the + * last pass, and thus may take a great deal of time. + * + * Returns FALSE if suspended. The return value need be inspected only if + * a suspending data source is used. + */ + +GLOBAL(boolean) +jpeg_start_decompress (j_decompress_ptr cinfo) +{ + if (cinfo->global_state == DSTATE_READY) { + /* First call: initialize master control, select active modules */ + jinit_master_decompress(cinfo); + if (cinfo->buffered_image) { + /* No more work here; expecting jpeg_start_output next */ + cinfo->global_state = DSTATE_BUFIMAGE; + return TRUE; + } + cinfo->global_state = DSTATE_PRELOAD; + } + if (cinfo->global_state == DSTATE_PRELOAD) { + /* If file has multiple scans, absorb them all into the coef buffer */ + if (cinfo->inputctl->has_multiple_scans) { +#ifdef D_MULTISCAN_FILES_SUPPORTED + for (;;) { + int retcode; + /* Call progress monitor hook if present */ + if (cinfo->progress != NULL) + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + /* Absorb some more input */ + retcode = (*cinfo->inputctl->consume_input) (cinfo); + if (retcode == JPEG_SUSPENDED) + return FALSE; + if (retcode == JPEG_REACHED_EOI) + break; + /* Advance progress counter if appropriate */ + if (cinfo->progress != NULL && + (retcode == JPEG_ROW_COMPLETED || retcode == JPEG_REACHED_SOS)) { + if (++cinfo->progress->pass_counter >= cinfo->progress->pass_limit) { + /* jdmaster underestimated number of scans; ratchet up one scan */ + cinfo->progress->pass_limit += (long) cinfo->total_iMCU_rows; + } + } + } +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif /* D_MULTISCAN_FILES_SUPPORTED */ + } + cinfo->output_scan_number = cinfo->input_scan_number; + } else if (cinfo->global_state != DSTATE_PRESCAN) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + /* Perform any dummy output passes, and set up for the final pass */ + return output_pass_setup(cinfo); +} + +/* + * Set up for an output pass, and perform any dummy pass(es) needed. + * Common subroutine for jpeg_start_decompress and jpeg_start_output. + * Entry: global_state = DSTATE_PRESCAN only if previously suspended. + * Exit: If done, returns TRUE and sets global_state for proper output mode. + * If suspended, returns FALSE and sets global_state = DSTATE_PRESCAN. + */ + +LOCAL(boolean) +output_pass_setup (j_decompress_ptr cinfo) +{ + if (cinfo->global_state != DSTATE_PRESCAN) { + /* First call: do pass setup */ + (*cinfo->master->prepare_for_output_pass) (cinfo); + cinfo->output_scanline = 0; + cinfo->global_state = DSTATE_PRESCAN; + } + /* Loop over any required dummy passes */ + while (cinfo->master->is_dummy_pass) { +#ifdef QUANT_2PASS_SUPPORTED + /* Crank through the dummy pass */ + while (cinfo->output_scanline < cinfo->output_height) { + JDIMENSION last_scanline; + /* Call progress monitor hook if present */ + if (cinfo->progress != NULL) { + cinfo->progress->pass_counter = (long) cinfo->output_scanline; + cinfo->progress->pass_limit = (long) cinfo->output_height; + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + } + /* Process some data */ + last_scanline = cinfo->output_scanline; + (*cinfo->main->process_data) (cinfo, (JSAMPARRAY) NULL, + &cinfo->output_scanline, (JDIMENSION) 0); + if (cinfo->output_scanline == last_scanline) + return FALSE; /* No progress made, must suspend */ + } + /* Finish up dummy pass, and set up for another one */ + (*cinfo->master->finish_output_pass) (cinfo); + (*cinfo->master->prepare_for_output_pass) (cinfo); + cinfo->output_scanline = 0; +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif /* QUANT_2PASS_SUPPORTED */ + } + /* Ready for application to drive output pass through + * jpeg_read_scanlines or jpeg_read_raw_data. + */ + cinfo->global_state = cinfo->raw_data_out ? DSTATE_RAW_OK : DSTATE_SCANNING; + return TRUE; +} + +/* + * Read some scanlines of data from the JPEG decompressor. + * + * The return value will be the number of lines actually read. + * This may be less than the number requested in several cases, + * including bottom of image, data source suspension, and operating + * modes that emit multiple scanlines at a time. + * + * Note: we warn about excess calls to jpeg_read_scanlines() since + * this likely signals an application programmer error. However, + * an oversize buffer (max_lines > scanlines remaining) is not an error. + */ + +GLOBAL(JDIMENSION) +jpeg_read_scanlines (j_decompress_ptr cinfo, JSAMPARRAY scanlines, + JDIMENSION max_lines) +{ + JDIMENSION row_ctr; + + if (cinfo->global_state != DSTATE_SCANNING) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + if (cinfo->output_scanline >= cinfo->output_height) { + WARNMS(cinfo, JWRN_TOO_MUCH_DATA); + return 0; + } + + /* Call progress monitor hook if present */ + if (cinfo->progress != NULL) { + cinfo->progress->pass_counter = (long) cinfo->output_scanline; + cinfo->progress->pass_limit = (long) cinfo->output_height; + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + } + + /* Process some data */ + row_ctr = 0; + (*cinfo->main->process_data) (cinfo, scanlines, &row_ctr, max_lines); + cinfo->output_scanline += row_ctr; + return row_ctr; +} + +/* + * Alternate entry point to read raw data. + * Processes exactly one iMCU row per call, unless suspended. + */ + +GLOBAL(JDIMENSION) +jpeg_read_raw_data (j_decompress_ptr cinfo, JSAMPIMAGE data, + JDIMENSION max_lines) +{ + JDIMENSION lines_per_iMCU_row; + + if (cinfo->global_state != DSTATE_RAW_OK) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + if (cinfo->output_scanline >= cinfo->output_height) { + WARNMS(cinfo, JWRN_TOO_MUCH_DATA); + return 0; + } + + /* Call progress monitor hook if present */ + if (cinfo->progress != NULL) { + cinfo->progress->pass_counter = (long) cinfo->output_scanline; + cinfo->progress->pass_limit = (long) cinfo->output_height; + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + } + + /* Verify that at least one iMCU row can be returned. */ + lines_per_iMCU_row = cinfo->max_v_samp_factor * cinfo->min_DCT_scaled_size; + if (max_lines < lines_per_iMCU_row) + ERREXIT(cinfo, JERR_BUFFER_SIZE); + + /* Decompress directly into user's buffer. */ + if (! (*cinfo->coef->decompress_data) (cinfo, data)) + return 0; /* suspension forced, can do nothing more */ + + /* OK, we processed one iMCU row. */ + cinfo->output_scanline += lines_per_iMCU_row; + return lines_per_iMCU_row; +} + +/* Additional entry points for buffered-image mode. */ + +#ifdef D_MULTISCAN_FILES_SUPPORTED + +/* + * Initialize for an output pass in buffered-image mode. + */ + +GLOBAL(boolean) +jpeg_start_output (j_decompress_ptr cinfo, int scan_number) +{ + if (cinfo->global_state != DSTATE_BUFIMAGE && + cinfo->global_state != DSTATE_PRESCAN) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + /* Limit scan number to valid range */ + if (scan_number <= 0) + scan_number = 1; + if (cinfo->inputctl->eoi_reached && + scan_number > cinfo->input_scan_number) + scan_number = cinfo->input_scan_number; + cinfo->output_scan_number = scan_number; + /* Perform any dummy output passes, and set up for the real pass */ + return output_pass_setup(cinfo); +} + +/* + * Finish up after an output pass in buffered-image mode. + * + * Returns FALSE if suspended. The return value need be inspected only if + * a suspending data source is used. + */ + +GLOBAL(boolean) +jpeg_finish_output (j_decompress_ptr cinfo) +{ + if ((cinfo->global_state == DSTATE_SCANNING || + cinfo->global_state == DSTATE_RAW_OK) && cinfo->buffered_image) { + /* Terminate this pass. */ + /* We do not require the whole pass to have been completed. */ + (*cinfo->master->finish_output_pass) (cinfo); + cinfo->global_state = DSTATE_BUFPOST; + } else if (cinfo->global_state != DSTATE_BUFPOST) { + /* BUFPOST = repeat call after a suspension, anything else is error */ + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + } + /* Read markers looking for SOS or EOI */ + while (cinfo->input_scan_number <= cinfo->output_scan_number && + ! cinfo->inputctl->eoi_reached) { + if ((*cinfo->inputctl->consume_input) (cinfo) == JPEG_SUSPENDED) + return FALSE; /* Suspend, come back later */ + } + cinfo->global_state = DSTATE_BUFIMAGE; + return TRUE; +} + +#endif /* D_MULTISCAN_FILES_SUPPORTED */ +/********* End of inlined file: jdapistd.c *********/ + +/********* Start of inlined file: jdapimin.c *********/ +#define JPEG_INTERNALS + +/* + * Initialization of a JPEG decompression object. + * The error manager must already be set up (in case memory manager fails). + */ + +GLOBAL(void) +jpeg_CreateDecompress (j_decompress_ptr cinfo, int version, size_t structsize) +{ + int i; + + /* Guard against version mismatches between library and caller. */ + cinfo->mem = NULL; /* so jpeg_destroy knows mem mgr not called */ + if (version != JPEG_LIB_VERSION) + ERREXIT2(cinfo, JERR_BAD_LIB_VERSION, JPEG_LIB_VERSION, version); + if (structsize != SIZEOF(struct jpeg_decompress_struct)) + ERREXIT2(cinfo, JERR_BAD_STRUCT_SIZE, + (int) SIZEOF(struct jpeg_decompress_struct), (int) structsize); + + /* For debugging purposes, we zero the whole master structure. + * But the application has already set the err pointer, and may have set + * client_data, so we have to save and restore those fields. + * Note: if application hasn't set client_data, tools like Purify may + * complain here. + */ + { + struct jpeg_error_mgr * err = cinfo->err; + void * client_data = cinfo->client_data; /* ignore Purify complaint here */ + MEMZERO(cinfo, SIZEOF(struct jpeg_decompress_struct)); + cinfo->err = err; + cinfo->client_data = client_data; + } + cinfo->is_decompressor = TRUE; + + /* Initialize a memory manager instance for this object */ + jinit_memory_mgr((j_common_ptr) cinfo); + + /* Zero out pointers to permanent structures. */ + cinfo->progress = NULL; + cinfo->src = NULL; + + for (i = 0; i < NUM_QUANT_TBLS; i++) + cinfo->quant_tbl_ptrs[i] = NULL; + + for (i = 0; i < NUM_HUFF_TBLS; i++) { + cinfo->dc_huff_tbl_ptrs[i] = NULL; + cinfo->ac_huff_tbl_ptrs[i] = NULL; + } + + /* Initialize marker processor so application can override methods + * for COM, APPn markers before calling jpeg_read_header. + */ + cinfo->marker_list = NULL; + jinit_marker_reader(cinfo); + + /* And initialize the overall input controller. */ + jinit_input_controller(cinfo); + + /* OK, I'm ready */ + cinfo->global_state = DSTATE_START; +} + +/* + * Destruction of a JPEG decompression object + */ + +GLOBAL(void) +jpeg_destroy_decompress (j_decompress_ptr cinfo) +{ + jpeg_destroy((j_common_ptr) cinfo); /* use common routine */ +} + +/* + * Abort processing of a JPEG decompression operation, + * but don't destroy the object itself. + */ + +GLOBAL(void) +jpeg_abort_decompress (j_decompress_ptr cinfo) +{ + jpeg_abort((j_common_ptr) cinfo); /* use common routine */ +} + +/* + * Set default decompression parameters. + */ + +LOCAL(void) +default_decompress_parms (j_decompress_ptr cinfo) +{ + /* Guess the input colorspace, and set output colorspace accordingly. */ + /* (Wish JPEG committee had provided a real way to specify this...) */ + /* Note application may override our guesses. */ + switch (cinfo->num_components) { + case 1: + cinfo->jpeg_color_space = JCS_GRAYSCALE; + cinfo->out_color_space = JCS_GRAYSCALE; + break; + + case 3: + if (cinfo->saw_JFIF_marker) { + cinfo->jpeg_color_space = JCS_YCbCr; /* JFIF implies YCbCr */ + } else if (cinfo->saw_Adobe_marker) { + switch (cinfo->Adobe_transform) { + case 0: + cinfo->jpeg_color_space = JCS_RGB; + break; + case 1: + cinfo->jpeg_color_space = JCS_YCbCr; + break; + default: + WARNMS1(cinfo, JWRN_ADOBE_XFORM, cinfo->Adobe_transform); + cinfo->jpeg_color_space = JCS_YCbCr; /* assume it's YCbCr */ + break; + } + } else { + /* Saw no special markers, try to guess from the component IDs */ + int cid0 = cinfo->comp_info[0].component_id; + int cid1 = cinfo->comp_info[1].component_id; + int cid2 = cinfo->comp_info[2].component_id; + + if (cid0 == 1 && cid1 == 2 && cid2 == 3) + cinfo->jpeg_color_space = JCS_YCbCr; /* assume JFIF w/out marker */ + else if (cid0 == 82 && cid1 == 71 && cid2 == 66) + cinfo->jpeg_color_space = JCS_RGB; /* ASCII 'R', 'G', 'B' */ + else { + TRACEMS3(cinfo, 1, JTRC_UNKNOWN_IDS, cid0, cid1, cid2); + cinfo->jpeg_color_space = JCS_YCbCr; /* assume it's YCbCr */ + } + } + /* Always guess RGB is proper output colorspace. */ + cinfo->out_color_space = JCS_RGB; + break; + + case 4: + if (cinfo->saw_Adobe_marker) { + switch (cinfo->Adobe_transform) { + case 0: + cinfo->jpeg_color_space = JCS_CMYK; + break; + case 2: + cinfo->jpeg_color_space = JCS_YCCK; + break; + default: + WARNMS1(cinfo, JWRN_ADOBE_XFORM, cinfo->Adobe_transform); + cinfo->jpeg_color_space = JCS_YCCK; /* assume it's YCCK */ + break; + } + } else { + /* No special markers, assume straight CMYK. */ + cinfo->jpeg_color_space = JCS_CMYK; + } + cinfo->out_color_space = JCS_CMYK; + break; + + default: + cinfo->jpeg_color_space = JCS_UNKNOWN; + cinfo->out_color_space = JCS_UNKNOWN; + break; + } + + /* Set defaults for other decompression parameters. */ + cinfo->scale_num = 1; /* 1:1 scaling */ + cinfo->scale_denom = 1; + cinfo->output_gamma = 1.0; + cinfo->buffered_image = FALSE; + cinfo->raw_data_out = FALSE; + cinfo->dct_method = JDCT_DEFAULT; + cinfo->do_fancy_upsampling = TRUE; + cinfo->do_block_smoothing = TRUE; + cinfo->quantize_colors = FALSE; + /* We set these in case application only sets quantize_colors. */ + cinfo->dither_mode = JDITHER_FS; +#ifdef QUANT_2PASS_SUPPORTED + cinfo->two_pass_quantize = TRUE; +#else + cinfo->two_pass_quantize = FALSE; +#endif + cinfo->desired_number_of_colors = 256; + cinfo->colormap = NULL; + /* Initialize for no mode change in buffered-image mode. */ + cinfo->enable_1pass_quant = FALSE; + cinfo->enable_external_quant = FALSE; + cinfo->enable_2pass_quant = FALSE; +} + +/* + * Decompression startup: read start of JPEG datastream to see what's there. + * Need only initialize JPEG object and supply a data source before calling. + * + * This routine will read as far as the first SOS marker (ie, actual start of + * compressed data), and will save all tables and parameters in the JPEG + * object. It will also initialize the decompression parameters to default + * values, and finally return JPEG_HEADER_OK. On return, the application may + * adjust the decompression parameters and then call jpeg_start_decompress. + * (Or, if the application only wanted to determine the image parameters, + * the data need not be decompressed. In that case, call jpeg_abort or + * jpeg_destroy to release any temporary space.) + * If an abbreviated (tables only) datastream is presented, the routine will + * return JPEG_HEADER_TABLES_ONLY upon reaching EOI. The application may then + * re-use the JPEG object to read the abbreviated image datastream(s). + * It is unnecessary (but OK) to call jpeg_abort in this case. + * The JPEG_SUSPENDED return code only occurs if the data source module + * requests suspension of the decompressor. In this case the application + * should load more source data and then re-call jpeg_read_header to resume + * processing. + * If a non-suspending data source is used and require_image is TRUE, then the + * return code need not be inspected since only JPEG_HEADER_OK is possible. + * + * This routine is now just a front end to jpeg_consume_input, with some + * extra error checking. + */ + +GLOBAL(int) +jpeg_read_header (j_decompress_ptr cinfo, boolean require_image) +{ + int retcode; + + if (cinfo->global_state != DSTATE_START && + cinfo->global_state != DSTATE_INHEADER) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + retcode = jpeg_consume_input(cinfo); + + switch (retcode) { + case JPEG_REACHED_SOS: + retcode = JPEG_HEADER_OK; + break; + case JPEG_REACHED_EOI: + if (require_image) /* Complain if application wanted an image */ + ERREXIT(cinfo, JERR_NO_IMAGE); + /* Reset to start state; it would be safer to require the application to + * call jpeg_abort, but we can't change it now for compatibility reasons. + * A side effect is to free any temporary memory (there shouldn't be any). + */ + jpeg_abort((j_common_ptr) cinfo); /* sets state = DSTATE_START */ + retcode = JPEG_HEADER_TABLES_ONLY; + break; + case JPEG_SUSPENDED: + /* no work */ + break; + } + + return retcode; +} + +/* + * Consume data in advance of what the decompressor requires. + * This can be called at any time once the decompressor object has + * been created and a data source has been set up. + * + * This routine is essentially a state machine that handles a couple + * of critical state-transition actions, namely initial setup and + * transition from header scanning to ready-for-start_decompress. + * All the actual input is done via the input controller's consume_input + * method. + */ + +GLOBAL(int) +jpeg_consume_input (j_decompress_ptr cinfo) +{ + int retcode = JPEG_SUSPENDED; + + /* NB: every possible DSTATE value should be listed in this switch */ + switch (cinfo->global_state) { + case DSTATE_START: + /* Start-of-datastream actions: reset appropriate modules */ + (*cinfo->inputctl->reset_input_controller) (cinfo); + /* Initialize application's data source module */ + (*cinfo->src->init_source) (cinfo); + cinfo->global_state = DSTATE_INHEADER; + /*FALLTHROUGH*/ + case DSTATE_INHEADER: + retcode = (*cinfo->inputctl->consume_input) (cinfo); + if (retcode == JPEG_REACHED_SOS) { /* Found SOS, prepare to decompress */ + /* Set up default parameters based on header data */ + default_decompress_parms(cinfo); + /* Set global state: ready for start_decompress */ + cinfo->global_state = DSTATE_READY; + } + break; + case DSTATE_READY: + /* Can't advance past first SOS until start_decompress is called */ + retcode = JPEG_REACHED_SOS; + break; + case DSTATE_PRELOAD: + case DSTATE_PRESCAN: + case DSTATE_SCANNING: + case DSTATE_RAW_OK: + case DSTATE_BUFIMAGE: + case DSTATE_BUFPOST: + case DSTATE_STOPPING: + retcode = (*cinfo->inputctl->consume_input) (cinfo); + break; + default: + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + } + return retcode; +} + +/* + * Have we finished reading the input file? + */ + +GLOBAL(boolean) +jpeg_input_complete (j_decompress_ptr cinfo) +{ + /* Check for valid jpeg object */ + if (cinfo->global_state < DSTATE_START || + cinfo->global_state > DSTATE_STOPPING) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + return cinfo->inputctl->eoi_reached; +} + +/* + * Is there more than one scan? + */ + +GLOBAL(boolean) +jpeg_has_multiple_scans (j_decompress_ptr cinfo) +{ + /* Only valid after jpeg_read_header completes */ + if (cinfo->global_state < DSTATE_READY || + cinfo->global_state > DSTATE_STOPPING) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + return cinfo->inputctl->has_multiple_scans; +} + +/* + * Finish JPEG decompression. + * + * This will normally just verify the file trailer and release temp storage. + * + * Returns FALSE if suspended. The return value need be inspected only if + * a suspending data source is used. + */ + +GLOBAL(boolean) +jpeg_finish_decompress (j_decompress_ptr cinfo) +{ + if ((cinfo->global_state == DSTATE_SCANNING || + cinfo->global_state == DSTATE_RAW_OK) && ! cinfo->buffered_image) { + /* Terminate final pass of non-buffered mode */ + if (cinfo->output_scanline < cinfo->output_height) + ERREXIT(cinfo, JERR_TOO_LITTLE_DATA); + (*cinfo->master->finish_output_pass) (cinfo); + cinfo->global_state = DSTATE_STOPPING; + } else if (cinfo->global_state == DSTATE_BUFIMAGE) { + /* Finishing after a buffered-image operation */ + cinfo->global_state = DSTATE_STOPPING; + } else if (cinfo->global_state != DSTATE_STOPPING) { + /* STOPPING = repeat call after a suspension, anything else is error */ + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + } + /* Read until EOI */ + while (! cinfo->inputctl->eoi_reached) { + if ((*cinfo->inputctl->consume_input) (cinfo) == JPEG_SUSPENDED) + return FALSE; /* Suspend, come back later */ + } + /* Do final cleanup */ + (*cinfo->src->term_source) (cinfo); + /* We can use jpeg_abort to release memory and reset global_state */ + jpeg_abort((j_common_ptr) cinfo); + return TRUE; +} +/********* End of inlined file: jdapimin.c *********/ + +/********* Start of inlined file: jdatasrc.c *********/ +/* this is not a core library module, so it doesn't define JPEG_INTERNALS */ + +/********* Start of inlined file: jerror.h *********/ +/* + * To define the enum list of message codes, include this file without + * defining macro JMESSAGE. To create a message string table, include it + * again with a suitable JMESSAGE definition (see jerror.c for an example). + */ +#ifndef JMESSAGE +#ifndef JERROR_H +/* First time through, define the enum list */ +#define JMAKE_ENUM_LIST +#else +/* Repeated inclusions of this file are no-ops unless JMESSAGE is defined */ +#define JMESSAGE(code,string) +#endif /* JERROR_H */ +#endif /* JMESSAGE */ + +#ifdef JMAKE_ENUM_LIST + +typedef enum { + +#define JMESSAGE(code,string) code , + +#endif /* JMAKE_ENUM_LIST */ + +JMESSAGE(JMSG_NOMESSAGE, "Bogus message code %d") /* Must be first entry! */ + +/* For maintenance convenience, list is alphabetical by message code name */ +JMESSAGE(JERR_ARITH_NOTIMPL, + "Sorry, there are legal restrictions on arithmetic coding") +JMESSAGE(JERR_BAD_ALIGN_TYPE, "ALIGN_TYPE is wrong, please fix") +JMESSAGE(JERR_BAD_ALLOC_CHUNK, "MAX_ALLOC_CHUNK is wrong, please fix") +JMESSAGE(JERR_BAD_BUFFER_MODE, "Bogus buffer control mode") +JMESSAGE(JERR_BAD_COMPONENT_ID, "Invalid component ID %d in SOS") +JMESSAGE(JERR_BAD_DCT_COEF, "DCT coefficient out of range") +JMESSAGE(JERR_BAD_DCTSIZE, "IDCT output block size %d not supported") +JMESSAGE(JERR_BAD_HUFF_TABLE, "Bogus Huffman table definition") +JMESSAGE(JERR_BAD_IN_COLORSPACE, "Bogus input colorspace") +JMESSAGE(JERR_BAD_J_COLORSPACE, "Bogus JPEG colorspace") +JMESSAGE(JERR_BAD_LENGTH, "Bogus marker length") +JMESSAGE(JERR_BAD_LIB_VERSION, + "Wrong JPEG library version: library is %d, caller expects %d") +JMESSAGE(JERR_BAD_MCU_SIZE, "Sampling factors too large for interleaved scan") +JMESSAGE(JERR_BAD_POOL_ID, "Invalid memory pool code %d") +JMESSAGE(JERR_BAD_PRECISION, "Unsupported JPEG data precision %d") +JMESSAGE(JERR_BAD_PROGRESSION, + "Invalid progressive parameters Ss=%d Se=%d Ah=%d Al=%d") +JMESSAGE(JERR_BAD_PROG_SCRIPT, + "Invalid progressive parameters at scan script entry %d") +JMESSAGE(JERR_BAD_SAMPLING, "Bogus sampling factors") +JMESSAGE(JERR_BAD_SCAN_SCRIPT, "Invalid scan script at entry %d") +JMESSAGE(JERR_BAD_STATE, "Improper call to JPEG library in state %d") +JMESSAGE(JERR_BAD_STRUCT_SIZE, + "JPEG parameter struct mismatch: library thinks size is %u, caller expects %u") +JMESSAGE(JERR_BAD_VIRTUAL_ACCESS, "Bogus virtual array access") +JMESSAGE(JERR_BUFFER_SIZE, "Buffer passed to JPEG library is too small") +JMESSAGE(JERR_CANT_SUSPEND, "Suspension not allowed here") +JMESSAGE(JERR_CCIR601_NOTIMPL, "CCIR601 sampling not implemented yet") +JMESSAGE(JERR_COMPONENT_COUNT, "Too many color components: %d, max %d") +JMESSAGE(JERR_CONVERSION_NOTIMPL, "Unsupported color conversion request") +JMESSAGE(JERR_DAC_INDEX, "Bogus DAC index %d") +JMESSAGE(JERR_DAC_VALUE, "Bogus DAC value 0x%x") +JMESSAGE(JERR_DHT_INDEX, "Bogus DHT index %d") +JMESSAGE(JERR_DQT_INDEX, "Bogus DQT index %d") +JMESSAGE(JERR_EMPTY_IMAGE, "Empty JPEG image (DNL not supported)") +JMESSAGE(JERR_EMS_READ, "Read from EMS failed") +JMESSAGE(JERR_EMS_WRITE, "Write to EMS failed") +JMESSAGE(JERR_EOI_EXPECTED, "Didn't expect more than one scan") +JMESSAGE(JERR_FILE_READ, "Input file read error") +JMESSAGE(JERR_FILE_WRITE, "Output file write error --- out of disk space?") +JMESSAGE(JERR_FRACT_SAMPLE_NOTIMPL, "Fractional sampling not implemented yet") +JMESSAGE(JERR_HUFF_CLEN_OVERFLOW, "Huffman code size table overflow") +JMESSAGE(JERR_HUFF_MISSING_CODE, "Missing Huffman code table entry") +JMESSAGE(JERR_IMAGE_TOO_BIG, "Maximum supported image dimension is %u pixels") +JMESSAGE(JERR_INPUT_EMPTY, "Empty input file") +JMESSAGE(JERR_INPUT_EOF, "Premature end of input file") +JMESSAGE(JERR_MISMATCHED_QUANT_TABLE, + "Cannot transcode due to multiple use of quantization table %d") +JMESSAGE(JERR_MISSING_DATA, "Scan script does not transmit all data") +JMESSAGE(JERR_MODE_CHANGE, "Invalid color quantization mode change") +JMESSAGE(JERR_NOTIMPL, "Not implemented yet") +JMESSAGE(JERR_NOT_COMPILED, "Requested feature was omitted at compile time") +JMESSAGE(JERR_NO_BACKING_STORE, "Backing store not supported") +JMESSAGE(JERR_NO_HUFF_TABLE, "Huffman table 0x%02x was not defined") +JMESSAGE(JERR_NO_IMAGE, "JPEG datastream contains no image") +JMESSAGE(JERR_NO_QUANT_TABLE, "Quantization table 0x%02x was not defined") +JMESSAGE(JERR_NO_SOI, "Not a JPEG file: starts with 0x%02x 0x%02x") +JMESSAGE(JERR_OUT_OF_MEMORY, "Insufficient memory (case %d)") +JMESSAGE(JERR_QUANT_COMPONENTS, + "Cannot quantize more than %d color components") +JMESSAGE(JERR_QUANT_FEW_COLORS, "Cannot quantize to fewer than %d colors") +JMESSAGE(JERR_QUANT_MANY_COLORS, "Cannot quantize to more than %d colors") +JMESSAGE(JERR_SOF_DUPLICATE, "Invalid JPEG file structure: two SOF markers") +JMESSAGE(JERR_SOF_NO_SOS, "Invalid JPEG file structure: missing SOS marker") +JMESSAGE(JERR_SOF_UNSUPPORTED, "Unsupported JPEG process: SOF type 0x%02x") +JMESSAGE(JERR_SOI_DUPLICATE, "Invalid JPEG file structure: two SOI markers") +JMESSAGE(JERR_SOS_NO_SOF, "Invalid JPEG file structure: SOS before SOF") +JMESSAGE(JERR_TFILE_CREATE, "Failed to create temporary file %s") +JMESSAGE(JERR_TFILE_READ, "Read failed on temporary file") +JMESSAGE(JERR_TFILE_SEEK, "Seek failed on temporary file") +JMESSAGE(JERR_TFILE_WRITE, + "Write failed on temporary file --- out of disk space?") +JMESSAGE(JERR_TOO_LITTLE_DATA, "Application transferred too few scanlines") +JMESSAGE(JERR_UNKNOWN_MARKER, "Unsupported marker type 0x%02x") +JMESSAGE(JERR_VIRTUAL_BUG, "Virtual array controller messed up") +JMESSAGE(JERR_WIDTH_OVERFLOW, "Image too wide for this implementation") +JMESSAGE(JERR_XMS_READ, "Read from XMS failed") +JMESSAGE(JERR_XMS_WRITE, "Write to XMS failed") +JMESSAGE(JMSG_COPYRIGHT, JCOPYRIGHT) +JMESSAGE(JMSG_VERSION, JVERSION) +JMESSAGE(JTRC_16BIT_TABLES, + "Caution: quantization tables are too coarse for baseline JPEG") +JMESSAGE(JTRC_ADOBE, + "Adobe APP14 marker: version %d, flags 0x%04x 0x%04x, transform %d") +JMESSAGE(JTRC_APP0, "Unknown APP0 marker (not JFIF), length %u") +JMESSAGE(JTRC_APP14, "Unknown APP14 marker (not Adobe), length %u") +JMESSAGE(JTRC_DAC, "Define Arithmetic Table 0x%02x: 0x%02x") +JMESSAGE(JTRC_DHT, "Define Huffman Table 0x%02x") +JMESSAGE(JTRC_DQT, "Define Quantization Table %d precision %d") +JMESSAGE(JTRC_DRI, "Define Restart Interval %u") +JMESSAGE(JTRC_EMS_CLOSE, "Freed EMS handle %u") +JMESSAGE(JTRC_EMS_OPEN, "Obtained EMS handle %u") +JMESSAGE(JTRC_EOI, "End Of Image") +JMESSAGE(JTRC_HUFFBITS, " %3d %3d %3d %3d %3d %3d %3d %3d") +JMESSAGE(JTRC_JFIF, "JFIF APP0 marker: version %d.%02d, density %dx%d %d") +JMESSAGE(JTRC_JFIF_BADTHUMBNAILSIZE, + "Warning: thumbnail image size does not match data length %u") +JMESSAGE(JTRC_JFIF_EXTENSION, + "JFIF extension marker: type 0x%02x, length %u") +JMESSAGE(JTRC_JFIF_THUMBNAIL, " with %d x %d thumbnail image") +JMESSAGE(JTRC_MISC_MARKER, "Miscellaneous marker 0x%02x, length %u") +JMESSAGE(JTRC_PARMLESS_MARKER, "Unexpected marker 0x%02x") +JMESSAGE(JTRC_QUANTVALS, " %4u %4u %4u %4u %4u %4u %4u %4u") +JMESSAGE(JTRC_QUANT_3_NCOLORS, "Quantizing to %d = %d*%d*%d colors") +JMESSAGE(JTRC_QUANT_NCOLORS, "Quantizing to %d colors") +JMESSAGE(JTRC_QUANT_SELECTED, "Selected %d colors for quantization") +JMESSAGE(JTRC_RECOVERY_ACTION, "At marker 0x%02x, recovery action %d") +JMESSAGE(JTRC_RST, "RST%d") +JMESSAGE(JTRC_SMOOTH_NOTIMPL, + "Smoothing not supported with nonstandard sampling ratios") +JMESSAGE(JTRC_SOF, "Start Of Frame 0x%02x: width=%u, height=%u, components=%d") +JMESSAGE(JTRC_SOF_COMPONENT, " Component %d: %dhx%dv q=%d") +JMESSAGE(JTRC_SOI, "Start of Image") +JMESSAGE(JTRC_SOS, "Start Of Scan: %d components") +JMESSAGE(JTRC_SOS_COMPONENT, " Component %d: dc=%d ac=%d") +JMESSAGE(JTRC_SOS_PARAMS, " Ss=%d, Se=%d, Ah=%d, Al=%d") +JMESSAGE(JTRC_TFILE_CLOSE, "Closed temporary file %s") +JMESSAGE(JTRC_TFILE_OPEN, "Opened temporary file %s") +JMESSAGE(JTRC_THUMB_JPEG, + "JFIF extension marker: JPEG-compressed thumbnail image, length %u") +JMESSAGE(JTRC_THUMB_PALETTE, + "JFIF extension marker: palette thumbnail image, length %u") +JMESSAGE(JTRC_THUMB_RGB, + "JFIF extension marker: RGB thumbnail image, length %u") +JMESSAGE(JTRC_UNKNOWN_IDS, + "Unrecognized component IDs %d %d %d, assuming YCbCr") +JMESSAGE(JTRC_XMS_CLOSE, "Freed XMS handle %u") +JMESSAGE(JTRC_XMS_OPEN, "Obtained XMS handle %u") +JMESSAGE(JWRN_ADOBE_XFORM, "Unknown Adobe color transform code %d") +JMESSAGE(JWRN_BOGUS_PROGRESSION, + "Inconsistent progression sequence for component %d coefficient %d") +JMESSAGE(JWRN_EXTRANEOUS_DATA, + "Corrupt JPEG data: %u extraneous bytes before marker 0x%02x") +JMESSAGE(JWRN_HIT_MARKER, "Corrupt JPEG data: premature end of data segment") +JMESSAGE(JWRN_HUFF_BAD_CODE, "Corrupt JPEG data: bad Huffman code") +JMESSAGE(JWRN_JFIF_MAJOR, "Warning: unknown JFIF revision number %d.%02d") +JMESSAGE(JWRN_JPEG_EOF, "Premature end of JPEG file") +JMESSAGE(JWRN_MUST_RESYNC, + "Corrupt JPEG data: found marker 0x%02x instead of RST%d") +JMESSAGE(JWRN_NOT_SEQUENTIAL, "Invalid SOS parameters for sequential JPEG") +JMESSAGE(JWRN_TOO_MUCH_DATA, "Application transferred too many scanlines") + +#ifdef JMAKE_ENUM_LIST + + JMSG_LASTMSGCODE +} J_MESSAGE_CODE; + +#undef JMAKE_ENUM_LIST +#endif /* JMAKE_ENUM_LIST */ + +/* Zap JMESSAGE macro so that future re-inclusions do nothing by default */ +#undef JMESSAGE + +#ifndef JERROR_H +#define JERROR_H + +/* Macros to simplify using the error and trace message stuff */ +/* The first parameter is either type of cinfo pointer */ + +/* Fatal errors (print message and exit) */ +#define ERREXIT(cinfo,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT1(cinfo,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT2(cinfo,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT3(cinfo,code,p1,p2,p3) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (cinfo)->err->msg_parm.i[2] = (p3), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT4(cinfo,code,p1,p2,p3,p4) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (cinfo)->err->msg_parm.i[2] = (p3), \ + (cinfo)->err->msg_parm.i[3] = (p4), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXITS(cinfo,code,str) \ + ((cinfo)->err->msg_code = (code), \ + strncpy((cinfo)->err->msg_parm.s, (str), JMSG_STR_PARM_MAX), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) + +#define MAKESTMT(stuff) do { stuff } while (0) + +/* Nonfatal errors (we can keep going, but the data is probably corrupt) */ +#define WARNMS(cinfo,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) +#define WARNMS1(cinfo,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) +#define WARNMS2(cinfo,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) + +/* Informational/debugging messages */ +#define TRACEMS(cinfo,lvl,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS1(cinfo,lvl,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS2(cinfo,lvl,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS3(cinfo,lvl,code,p1,p2,p3) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMS4(cinfo,lvl,code,p1,p2,p3,p4) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMS5(cinfo,lvl,code,p1,p2,p3,p4,p5) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + _mp[4] = (p5); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMS8(cinfo,lvl,code,p1,p2,p3,p4,p5,p6,p7,p8) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + _mp[4] = (p5); _mp[5] = (p6); _mp[6] = (p7); _mp[7] = (p8); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMSS(cinfo,lvl,code,str) \ + ((cinfo)->err->msg_code = (code), \ + strncpy((cinfo)->err->msg_parm.s, (str), JMSG_STR_PARM_MAX), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) + +#endif /* JERROR_H */ +/********* End of inlined file: jerror.h *********/ + +/* Expanded data source object for stdio input */ + +typedef struct { + struct jpeg_source_mgr pub; /* public fields */ + + FILE * infile; /* source stream */ + JOCTET * buffer; /* start of buffer */ + boolean start_of_file; /* have we gotten any data yet? */ +} my_source_mgr; + +typedef my_source_mgr * my_src_ptr; + +#define INPUT_BUF_SIZE 4096 /* choose an efficiently fread'able size */ + +/* + * Initialize source --- called by jpeg_read_header + * before any data is actually read. + */ + +METHODDEF(void) +init_source (j_decompress_ptr cinfo) +{ + my_src_ptr src = (my_src_ptr) cinfo->src; + + /* We reset the empty-input-file flag for each image, + * but we don't clear the input buffer. + * This is correct behavior for reading a series of images from one source. + */ + src->start_of_file = TRUE; +} + +/* + * Fill the input buffer --- called whenever buffer is emptied. + * + * In typical applications, this should read fresh data into the buffer + * (ignoring the current state of next_input_byte & bytes_in_buffer), + * reset the pointer & count to the start of the buffer, and return TRUE + * indicating that the buffer has been reloaded. It is not necessary to + * fill the buffer entirely, only to obtain at least one more byte. + * + * There is no such thing as an EOF return. If the end of the file has been + * reached, the routine has a choice of ERREXIT() or inserting fake data into + * the buffer. In most cases, generating a warning message and inserting a + * fake EOI marker is the best course of action --- this will allow the + * decompressor to output however much of the image is there. However, + * the resulting error message is misleading if the real problem is an empty + * input file, so we handle that case specially. + * + * In applications that need to be able to suspend compression due to input + * not being available yet, a FALSE return indicates that no more data can be + * obtained right now, but more may be forthcoming later. In this situation, + * the decompressor will return to its caller (with an indication of the + * number of scanlines it has read, if any). The application should resume + * decompression after it has loaded more data into the input buffer. Note + * that there are substantial restrictions on the use of suspension --- see + * the documentation. + * + * When suspending, the decompressor will back up to a convenient restart point + * (typically the start of the current MCU). next_input_byte & bytes_in_buffer + * indicate where the restart point will be if the current call returns FALSE. + * Data beyond this point must be rescanned after resumption, so move it to + * the front of the buffer rather than discarding it. + */ + +METHODDEF(boolean) +fill_input_buffer (j_decompress_ptr cinfo) +{ + my_src_ptr src = (my_src_ptr) cinfo->src; + size_t nbytes; + + nbytes = JFREAD(src->infile, src->buffer, INPUT_BUF_SIZE); + + if (nbytes <= 0) { + if (src->start_of_file) /* Treat empty input file as fatal error */ + ERREXIT(cinfo, JERR_INPUT_EMPTY); + WARNMS(cinfo, JWRN_JPEG_EOF); + /* Insert a fake EOI marker */ + src->buffer[0] = (JOCTET) 0xFF; + src->buffer[1] = (JOCTET) JPEG_EOI; + nbytes = 2; + } + + src->pub.next_input_byte = src->buffer; + src->pub.bytes_in_buffer = nbytes; + src->start_of_file = FALSE; + + return TRUE; +} + +/* + * Skip data --- used to skip over a potentially large amount of + * uninteresting data (such as an APPn marker). + * + * Writers of suspendable-input applications must note that skip_input_data + * is not granted the right to give a suspension return. If the skip extends + * beyond the data currently in the buffer, the buffer can be marked empty so + * that the next read will cause a fill_input_buffer call that can suspend. + * Arranging for additional bytes to be discarded before reloading the input + * buffer is the application writer's problem. + */ + +METHODDEF(void) +skip_input_data (j_decompress_ptr cinfo, long num_bytes) +{ + my_src_ptr src = (my_src_ptr) cinfo->src; + + /* Just a dumb implementation for now. Could use fseek() except + * it doesn't work on pipes. Not clear that being smart is worth + * any trouble anyway --- large skips are infrequent. + */ + if (num_bytes > 0) { + while (num_bytes > (long) src->pub.bytes_in_buffer) { + num_bytes -= (long) src->pub.bytes_in_buffer; + (void) fill_input_buffer(cinfo); + /* note we assume that fill_input_buffer will never return FALSE, + * so suspension need not be handled. + */ + } + src->pub.next_input_byte += (size_t) num_bytes; + src->pub.bytes_in_buffer -= (size_t) num_bytes; + } +} + +/* + * An additional method that can be provided by data source modules is the + * resync_to_restart method for error recovery in the presence of RST markers. + * For the moment, this source module just uses the default resync method + * provided by the JPEG library. That method assumes that no backtracking + * is possible. + */ + +/* + * Terminate source --- called by jpeg_finish_decompress + * after all data has been read. Often a no-op. + * + * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding + * application must deal with any cleanup that should happen even + * for error exit. + */ + +METHODDEF(void) +term_source (j_decompress_ptr cinfo) +{ + /* no work necessary here */ +} + +/* + * Prepare for input from a stdio stream. + * The caller must have already opened the stream, and is responsible + * for closing it after finishing decompression. + */ + +GLOBAL(void) +jpeg_stdio_src (j_decompress_ptr cinfo, FILE * infile) +{ + my_src_ptr src; + + /* The source object and input buffer are made permanent so that a series + * of JPEG images can be read from the same file by calling jpeg_stdio_src + * only before the first one. (If we discarded the buffer at the end of + * one image, we'd likely lose the start of the next one.) + * This makes it unsafe to use this manager and a different source + * manager serially with the same JPEG object. Caveat programmer. + */ + if (cinfo->src == NULL) { /* first time for this JPEG object? */ + cinfo->src = (struct jpeg_source_mgr *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + SIZEOF(my_source_mgr)); + src = (my_src_ptr) cinfo->src; + src->buffer = (JOCTET *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + INPUT_BUF_SIZE * SIZEOF(JOCTET)); + } + + src = (my_src_ptr) cinfo->src; + src->pub.init_source = init_source; + src->pub.fill_input_buffer = fill_input_buffer; + src->pub.skip_input_data = skip_input_data; + src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */ + src->pub.term_source = term_source; + src->infile = infile; + src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */ + src->pub.next_input_byte = NULL; /* until buffer loaded */ +} +/********* End of inlined file: jdatasrc.c *********/ + +/********* Start of inlined file: jdcoefct.c *********/ +#define JPEG_INTERNALS + +/* Block smoothing is only applicable for progressive JPEG, so: */ +#ifndef D_PROGRESSIVE_SUPPORTED +#undef BLOCK_SMOOTHING_SUPPORTED +#endif + +/* Private buffer controller object */ + +typedef struct { + struct jpeg_d_coef_controller pub; /* public fields */ + + /* These variables keep track of the current location of the input side. */ + /* cinfo->input_iMCU_row is also used for this. */ + JDIMENSION MCU_ctr; /* counts MCUs processed in current row */ + int MCU_vert_offset; /* counts MCU rows within iMCU row */ + int MCU_rows_per_iMCU_row; /* number of such rows needed */ + + /* The output side's location is represented by cinfo->output_iMCU_row. */ + + /* In single-pass modes, it's sufficient to buffer just one MCU. + * We allocate a workspace of D_MAX_BLOCKS_IN_MCU coefficient blocks, + * and let the entropy decoder write into that workspace each time. + * (On 80x86, the workspace is FAR even though it's not really very big; + * this is to keep the module interfaces unchanged when a large coefficient + * buffer is necessary.) + * In multi-pass modes, this array points to the current MCU's blocks + * within the virtual arrays; it is used only by the input side. + */ + JBLOCKROW MCU_buffer[D_MAX_BLOCKS_IN_MCU]; + +#ifdef D_MULTISCAN_FILES_SUPPORTED + /* In multi-pass modes, we need a virtual block array for each component. */ + jvirt_barray_ptr whole_image[MAX_COMPONENTS]; +#endif + +#ifdef BLOCK_SMOOTHING_SUPPORTED + /* When doing block smoothing, we latch coefficient Al values here */ + int * coef_bits_latch; +#define SAVED_COEFS 6 /* we save coef_bits[0..5] */ +#endif +} my_coef_controller3; + +typedef my_coef_controller3 * my_coef_ptr3; + +/* Forward declarations */ +METHODDEF(int) decompress_onepass + JPP((j_decompress_ptr cinfo, JSAMPIMAGE output_buf)); +#ifdef D_MULTISCAN_FILES_SUPPORTED +METHODDEF(int) decompress_data + JPP((j_decompress_ptr cinfo, JSAMPIMAGE output_buf)); +#endif +#ifdef BLOCK_SMOOTHING_SUPPORTED +LOCAL(boolean) smoothing_ok JPP((j_decompress_ptr cinfo)); +METHODDEF(int) decompress_smooth_data + JPP((j_decompress_ptr cinfo, JSAMPIMAGE output_buf)); +#endif + +LOCAL(void) +start_iMCU_row3 (j_decompress_ptr cinfo) +/* Reset within-iMCU-row counters for a new row (input side) */ +{ + my_coef_ptr3 coef = (my_coef_ptr3) cinfo->coef; + + /* In an interleaved scan, an MCU row is the same as an iMCU row. + * In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. + * But at the bottom of the image, process only what's left. + */ + if (cinfo->comps_in_scan > 1) { + coef->MCU_rows_per_iMCU_row = 1; + } else { + if (cinfo->input_iMCU_row < (cinfo->total_iMCU_rows-1)) + coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->v_samp_factor; + else + coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->last_row_height; + } + + coef->MCU_ctr = 0; + coef->MCU_vert_offset = 0; +} + +/* + * Initialize for an input processing pass. + */ + +METHODDEF(void) +start_input_pass (j_decompress_ptr cinfo) +{ + cinfo->input_iMCU_row = 0; + start_iMCU_row3(cinfo); +} + +/* + * Initialize for an output processing pass. + */ + +METHODDEF(void) +start_output_pass (j_decompress_ptr cinfo) +{ +#ifdef BLOCK_SMOOTHING_SUPPORTED + my_coef_ptr3 coef = (my_coef_ptr3) cinfo->coef; + + /* If multipass, check to see whether to use block smoothing on this pass */ + if (coef->pub.coef_arrays != NULL) { + if (cinfo->do_block_smoothing && smoothing_ok(cinfo)) + coef->pub.decompress_data = decompress_smooth_data; + else + coef->pub.decompress_data = decompress_data; + } +#endif + cinfo->output_iMCU_row = 0; +} + +/* + * Decompress and return some data in the single-pass case. + * Always attempts to emit one fully interleaved MCU row ("iMCU" row). + * Input and output must run in lockstep since we have only a one-MCU buffer. + * Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED. + * + * NB: output_buf contains a plane for each component in image, + * which we index according to the component's SOF position. + */ + +METHODDEF(int) +decompress_onepass (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) +{ + my_coef_ptr3 coef = (my_coef_ptr3) cinfo->coef; + JDIMENSION MCU_col_num; /* index of current MCU within row */ + JDIMENSION last_MCU_col = cinfo->MCUs_per_row - 1; + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + int blkn, ci, xindex, yindex, yoffset, useful_width; + JSAMPARRAY output_ptr; + JDIMENSION start_col, output_col; + jpeg_component_info *compptr; + inverse_DCT_method_ptr inverse_DCT; + + /* Loop to process as much as one whole iMCU row */ + for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row; + yoffset++) { + for (MCU_col_num = coef->MCU_ctr; MCU_col_num <= last_MCU_col; + MCU_col_num++) { + /* Try to fetch an MCU. Entropy decoder expects buffer to be zeroed. */ + jzero_far((void FAR *) coef->MCU_buffer[0], + (size_t) (cinfo->blocks_in_MCU * SIZEOF(JBLOCK))); + if (! (*cinfo->entropy->decode_mcu) (cinfo, coef->MCU_buffer)) { + /* Suspension forced; update state counters and exit */ + coef->MCU_vert_offset = yoffset; + coef->MCU_ctr = MCU_col_num; + return JPEG_SUSPENDED; + } + /* Determine where data should go in output_buf and do the IDCT thing. + * We skip dummy blocks at the right and bottom edges (but blkn gets + * incremented past them!). Note the inner loop relies on having + * allocated the MCU_buffer[] blocks sequentially. + */ + blkn = 0; /* index of current DCT block within MCU */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + /* Don't bother to IDCT an uninteresting component. */ + if (! compptr->component_needed) { + blkn += compptr->MCU_blocks; + continue; + } + inverse_DCT = cinfo->idct->inverse_DCT[compptr->component_index]; + useful_width = (MCU_col_num < last_MCU_col) ? compptr->MCU_width + : compptr->last_col_width; + output_ptr = output_buf[compptr->component_index] + + yoffset * compptr->DCT_scaled_size; + start_col = MCU_col_num * compptr->MCU_sample_width; + for (yindex = 0; yindex < compptr->MCU_height; yindex++) { + if (cinfo->input_iMCU_row < last_iMCU_row || + yoffset+yindex < compptr->last_row_height) { + output_col = start_col; + for (xindex = 0; xindex < useful_width; xindex++) { + (*inverse_DCT) (cinfo, compptr, + (JCOEFPTR) coef->MCU_buffer[blkn+xindex], + output_ptr, output_col); + output_col += compptr->DCT_scaled_size; + } + } + blkn += compptr->MCU_width; + output_ptr += compptr->DCT_scaled_size; + } + } + } + /* Completed an MCU row, but perhaps not an iMCU row */ + coef->MCU_ctr = 0; + } + /* Completed the iMCU row, advance counters for next one */ + cinfo->output_iMCU_row++; + if (++(cinfo->input_iMCU_row) < cinfo->total_iMCU_rows) { + start_iMCU_row3(cinfo); + return JPEG_ROW_COMPLETED; + } + /* Completed the scan */ + (*cinfo->inputctl->finish_input_pass) (cinfo); + return JPEG_SCAN_COMPLETED; +} + +/* + * Dummy consume-input routine for single-pass operation. + */ + +METHODDEF(int) +dummy_consume_data (j_decompress_ptr cinfo) +{ + return JPEG_SUSPENDED; /* Always indicate nothing was done */ +} + +#ifdef D_MULTISCAN_FILES_SUPPORTED + +/* + * Consume input data and store it in the full-image coefficient buffer. + * We read as much as one fully interleaved MCU row ("iMCU" row) per call, + * ie, v_samp_factor block rows for each component in the scan. + * Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED. + */ + +METHODDEF(int) +consume_data (j_decompress_ptr cinfo) +{ + my_coef_ptr3 coef = (my_coef_ptr3) cinfo->coef; + JDIMENSION MCU_col_num; /* index of current MCU within row */ + int blkn, ci, xindex, yindex, yoffset; + JDIMENSION start_col; + JBLOCKARRAY buffer[MAX_COMPS_IN_SCAN]; + JBLOCKROW buffer_ptr; + jpeg_component_info *compptr; + + /* Align the virtual buffers for the components used in this scan. */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + buffer[ci] = (*cinfo->mem->access_virt_barray) + ((j_common_ptr) cinfo, coef->whole_image[compptr->component_index], + cinfo->input_iMCU_row * compptr->v_samp_factor, + (JDIMENSION) compptr->v_samp_factor, TRUE); + /* Note: entropy decoder expects buffer to be zeroed, + * but this is handled automatically by the memory manager + * because we requested a pre-zeroed array. + */ + } + + /* Loop to process one whole iMCU row */ + for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row; + yoffset++) { + for (MCU_col_num = coef->MCU_ctr; MCU_col_num < cinfo->MCUs_per_row; + MCU_col_num++) { + /* Construct list of pointers to DCT blocks belonging to this MCU */ + blkn = 0; /* index of current DCT block within MCU */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + start_col = MCU_col_num * compptr->MCU_width; + for (yindex = 0; yindex < compptr->MCU_height; yindex++) { + buffer_ptr = buffer[ci][yindex+yoffset] + start_col; + for (xindex = 0; xindex < compptr->MCU_width; xindex++) { + coef->MCU_buffer[blkn++] = buffer_ptr++; + } + } + } + /* Try to fetch the MCU. */ + if (! (*cinfo->entropy->decode_mcu) (cinfo, coef->MCU_buffer)) { + /* Suspension forced; update state counters and exit */ + coef->MCU_vert_offset = yoffset; + coef->MCU_ctr = MCU_col_num; + return JPEG_SUSPENDED; + } + } + /* Completed an MCU row, but perhaps not an iMCU row */ + coef->MCU_ctr = 0; + } + /* Completed the iMCU row, advance counters for next one */ + if (++(cinfo->input_iMCU_row) < cinfo->total_iMCU_rows) { + start_iMCU_row3(cinfo); + return JPEG_ROW_COMPLETED; + } + /* Completed the scan */ + (*cinfo->inputctl->finish_input_pass) (cinfo); + return JPEG_SCAN_COMPLETED; +} + +/* + * Decompress and return some data in the multi-pass case. + * Always attempts to emit one fully interleaved MCU row ("iMCU" row). + * Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED. + * + * NB: output_buf contains a plane for each component in image. + */ + +METHODDEF(int) +decompress_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) +{ + my_coef_ptr3 coef = (my_coef_ptr3) cinfo->coef; + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + JDIMENSION block_num; + int ci, block_row, block_rows; + JBLOCKARRAY buffer; + JBLOCKROW buffer_ptr; + JSAMPARRAY output_ptr; + JDIMENSION output_col; + jpeg_component_info *compptr; + inverse_DCT_method_ptr inverse_DCT; + + /* Force some input to be done if we are getting ahead of the input. */ + while (cinfo->input_scan_number < cinfo->output_scan_number || + (cinfo->input_scan_number == cinfo->output_scan_number && + cinfo->input_iMCU_row <= cinfo->output_iMCU_row)) { + if ((*cinfo->inputctl->consume_input)(cinfo) == JPEG_SUSPENDED) + return JPEG_SUSPENDED; + } + + /* OK, output from the virtual arrays. */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Don't bother to IDCT an uninteresting component. */ + if (! compptr->component_needed) + continue; + /* Align the virtual buffer for this component. */ + buffer = (*cinfo->mem->access_virt_barray) + ((j_common_ptr) cinfo, coef->whole_image[ci], + cinfo->output_iMCU_row * compptr->v_samp_factor, + (JDIMENSION) compptr->v_samp_factor, FALSE); + /* Count non-dummy DCT block rows in this iMCU row. */ + if (cinfo->output_iMCU_row < last_iMCU_row) + block_rows = compptr->v_samp_factor; + else { + /* NB: can't use last_row_height here; it is input-side-dependent! */ + block_rows = (int) (compptr->height_in_blocks % compptr->v_samp_factor); + if (block_rows == 0) block_rows = compptr->v_samp_factor; + } + inverse_DCT = cinfo->idct->inverse_DCT[ci]; + output_ptr = output_buf[ci]; + /* Loop over all DCT blocks to be processed. */ + for (block_row = 0; block_row < block_rows; block_row++) { + buffer_ptr = buffer[block_row]; + output_col = 0; + for (block_num = 0; block_num < compptr->width_in_blocks; block_num++) { + (*inverse_DCT) (cinfo, compptr, (JCOEFPTR) buffer_ptr, + output_ptr, output_col); + buffer_ptr++; + output_col += compptr->DCT_scaled_size; + } + output_ptr += compptr->DCT_scaled_size; + } + } + + if (++(cinfo->output_iMCU_row) < cinfo->total_iMCU_rows) + return JPEG_ROW_COMPLETED; + return JPEG_SCAN_COMPLETED; +} + +#endif /* D_MULTISCAN_FILES_SUPPORTED */ + +#ifdef BLOCK_SMOOTHING_SUPPORTED + +/* + * This code applies interblock smoothing as described by section K.8 + * of the JPEG standard: the first 5 AC coefficients are estimated from + * the DC values of a DCT block and its 8 neighboring blocks. + * We apply smoothing only for progressive JPEG decoding, and only if + * the coefficients it can estimate are not yet known to full precision. + */ + +/* Natural-order array positions of the first 5 zigzag-order coefficients */ +#define Q01_POS 1 +#define Q10_POS 8 +#define Q20_POS 16 +#define Q11_POS 9 +#define Q02_POS 2 + +/* + * Determine whether block smoothing is applicable and safe. + * We also latch the current states of the coef_bits[] entries for the + * AC coefficients; otherwise, if the input side of the decompressor + * advances into a new scan, we might think the coefficients are known + * more accurately than they really are. + */ + +LOCAL(boolean) +smoothing_ok (j_decompress_ptr cinfo) +{ + my_coef_ptr3 coef = (my_coef_ptr3) cinfo->coef; + boolean smoothing_useful = FALSE; + int ci, coefi; + jpeg_component_info *compptr; + JQUANT_TBL * qtable; + int * coef_bits; + int * coef_bits_latch; + + if (! cinfo->progressive_mode || cinfo->coef_bits == NULL) + return FALSE; + + /* Allocate latch area if not already done */ + if (coef->coef_bits_latch == NULL) + coef->coef_bits_latch = (int *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + cinfo->num_components * + (SAVED_COEFS * SIZEOF(int))); + coef_bits_latch = coef->coef_bits_latch; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* All components' quantization values must already be latched. */ + if ((qtable = compptr->quant_table) == NULL) + return FALSE; + /* Verify DC & first 5 AC quantizers are nonzero to avoid zero-divide. */ + if (qtable->quantval[0] == 0 || + qtable->quantval[Q01_POS] == 0 || + qtable->quantval[Q10_POS] == 0 || + qtable->quantval[Q20_POS] == 0 || + qtable->quantval[Q11_POS] == 0 || + qtable->quantval[Q02_POS] == 0) + return FALSE; + /* DC values must be at least partly known for all components. */ + coef_bits = cinfo->coef_bits[ci]; + if (coef_bits[0] < 0) + return FALSE; + /* Block smoothing is helpful if some AC coefficients remain inaccurate. */ + for (coefi = 1; coefi <= 5; coefi++) { + coef_bits_latch[coefi] = coef_bits[coefi]; + if (coef_bits[coefi] != 0) + smoothing_useful = TRUE; + } + coef_bits_latch += SAVED_COEFS; + } + + return smoothing_useful; +} + +/* + * Variant of decompress_data for use when doing block smoothing. + */ + +METHODDEF(int) +decompress_smooth_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) +{ + my_coef_ptr3 coef = (my_coef_ptr3) cinfo->coef; + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + JDIMENSION block_num, last_block_column; + int ci, block_row, block_rows, access_rows; + JBLOCKARRAY buffer; + JBLOCKROW buffer_ptr, prev_block_row, next_block_row; + JSAMPARRAY output_ptr; + JDIMENSION output_col; + jpeg_component_info *compptr; + inverse_DCT_method_ptr inverse_DCT; + boolean first_row, last_row; + JBLOCK workspace; + int *coef_bits; + JQUANT_TBL *quanttbl; + INT32 Q00,Q01,Q02,Q10,Q11,Q20, num; + int DC1,DC2,DC3,DC4,DC5,DC6,DC7,DC8,DC9; + int Al, pred; + + /* Force some input to be done if we are getting ahead of the input. */ + while (cinfo->input_scan_number <= cinfo->output_scan_number && + ! cinfo->inputctl->eoi_reached) { + if (cinfo->input_scan_number == cinfo->output_scan_number) { + /* If input is working on current scan, we ordinarily want it to + * have completed the current row. But if input scan is DC, + * we want it to keep one row ahead so that next block row's DC + * values are up to date. + */ + JDIMENSION delta = (cinfo->Ss == 0) ? 1 : 0; + if (cinfo->input_iMCU_row > cinfo->output_iMCU_row+delta) + break; + } + if ((*cinfo->inputctl->consume_input)(cinfo) == JPEG_SUSPENDED) + return JPEG_SUSPENDED; + } + + /* OK, output from the virtual arrays. */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Don't bother to IDCT an uninteresting component. */ + if (! compptr->component_needed) + continue; + /* Count non-dummy DCT block rows in this iMCU row. */ + if (cinfo->output_iMCU_row < last_iMCU_row) { + block_rows = compptr->v_samp_factor; + access_rows = block_rows * 2; /* this and next iMCU row */ + last_row = FALSE; + } else { + /* NB: can't use last_row_height here; it is input-side-dependent! */ + block_rows = (int) (compptr->height_in_blocks % compptr->v_samp_factor); + if (block_rows == 0) block_rows = compptr->v_samp_factor; + access_rows = block_rows; /* this iMCU row only */ + last_row = TRUE; + } + /* Align the virtual buffer for this component. */ + if (cinfo->output_iMCU_row > 0) { + access_rows += compptr->v_samp_factor; /* prior iMCU row too */ + buffer = (*cinfo->mem->access_virt_barray) + ((j_common_ptr) cinfo, coef->whole_image[ci], + (cinfo->output_iMCU_row - 1) * compptr->v_samp_factor, + (JDIMENSION) access_rows, FALSE); + buffer += compptr->v_samp_factor; /* point to current iMCU row */ + first_row = FALSE; + } else { + buffer = (*cinfo->mem->access_virt_barray) + ((j_common_ptr) cinfo, coef->whole_image[ci], + (JDIMENSION) 0, (JDIMENSION) access_rows, FALSE); + first_row = TRUE; + } + /* Fetch component-dependent info */ + coef_bits = coef->coef_bits_latch + (ci * SAVED_COEFS); + quanttbl = compptr->quant_table; + Q00 = quanttbl->quantval[0]; + Q01 = quanttbl->quantval[Q01_POS]; + Q10 = quanttbl->quantval[Q10_POS]; + Q20 = quanttbl->quantval[Q20_POS]; + Q11 = quanttbl->quantval[Q11_POS]; + Q02 = quanttbl->quantval[Q02_POS]; + inverse_DCT = cinfo->idct->inverse_DCT[ci]; + output_ptr = output_buf[ci]; + /* Loop over all DCT blocks to be processed. */ + for (block_row = 0; block_row < block_rows; block_row++) { + buffer_ptr = buffer[block_row]; + if (first_row && block_row == 0) + prev_block_row = buffer_ptr; + else + prev_block_row = buffer[block_row-1]; + if (last_row && block_row == block_rows-1) + next_block_row = buffer_ptr; + else + next_block_row = buffer[block_row+1]; + /* We fetch the surrounding DC values using a sliding-register approach. + * Initialize all nine here so as to do the right thing on narrow pics. + */ + DC1 = DC2 = DC3 = (int) prev_block_row[0][0]; + DC4 = DC5 = DC6 = (int) buffer_ptr[0][0]; + DC7 = DC8 = DC9 = (int) next_block_row[0][0]; + output_col = 0; + last_block_column = compptr->width_in_blocks - 1; + for (block_num = 0; block_num <= last_block_column; block_num++) { + /* Fetch current DCT block into workspace so we can modify it. */ + jcopy_block_row(buffer_ptr, (JBLOCKROW) workspace, (JDIMENSION) 1); + /* Update DC values */ + if (block_num < last_block_column) { + DC3 = (int) prev_block_row[1][0]; + DC6 = (int) buffer_ptr[1][0]; + DC9 = (int) next_block_row[1][0]; + } + /* Compute coefficient estimates per K.8. + * An estimate is applied only if coefficient is still zero, + * and is not known to be fully accurate. + */ + /* AC01 */ + if ((Al=coef_bits[1]) != 0 && workspace[1] == 0) { + num = 36 * Q00 * (DC4 - DC6); + if (num >= 0) { + pred = (int) (((Q01<<7) + num) / (Q01<<8)); + if (Al > 0 && pred >= (1< 0 && pred >= (1<= 0) { + pred = (int) (((Q10<<7) + num) / (Q10<<8)); + if (Al > 0 && pred >= (1< 0 && pred >= (1<= 0) { + pred = (int) (((Q20<<7) + num) / (Q20<<8)); + if (Al > 0 && pred >= (1< 0 && pred >= (1<= 0) { + pred = (int) (((Q11<<7) + num) / (Q11<<8)); + if (Al > 0 && pred >= (1< 0 && pred >= (1<= 0) { + pred = (int) (((Q02<<7) + num) / (Q02<<8)); + if (Al > 0 && pred >= (1< 0 && pred >= (1<DCT_scaled_size; + } + output_ptr += compptr->DCT_scaled_size; + } + } + + if (++(cinfo->output_iMCU_row) < cinfo->total_iMCU_rows) + return JPEG_ROW_COMPLETED; + return JPEG_SCAN_COMPLETED; +} + +#endif /* BLOCK_SMOOTHING_SUPPORTED */ + +/* + * Initialize coefficient buffer controller. + */ + +GLOBAL(void) +jinit_d_coef_controller (j_decompress_ptr cinfo, boolean need_full_buffer) +{ + my_coef_ptr3 coef; + + coef = (my_coef_ptr3) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_coef_controller3)); + cinfo->coef = (struct jpeg_d_coef_controller *) coef; + coef->pub.start_input_pass = start_input_pass; + coef->pub.start_output_pass = start_output_pass; +#ifdef BLOCK_SMOOTHING_SUPPORTED + coef->coef_bits_latch = NULL; +#endif + + /* Create the coefficient buffer. */ + if (need_full_buffer) { +#ifdef D_MULTISCAN_FILES_SUPPORTED + /* Allocate a full-image virtual array for each component, */ + /* padded to a multiple of samp_factor DCT blocks in each direction. */ + /* Note we ask for a pre-zeroed array. */ + int ci, access_rows; + jpeg_component_info *compptr; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + access_rows = compptr->v_samp_factor; +#ifdef BLOCK_SMOOTHING_SUPPORTED + /* If block smoothing could be used, need a bigger window */ + if (cinfo->progressive_mode) + access_rows *= 3; +#endif + coef->whole_image[ci] = (*cinfo->mem->request_virt_barray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, TRUE, + (JDIMENSION) jround_up((long) compptr->width_in_blocks, + (long) compptr->h_samp_factor), + (JDIMENSION) jround_up((long) compptr->height_in_blocks, + (long) compptr->v_samp_factor), + (JDIMENSION) access_rows); + } + coef->pub.consume_data = consume_data; + coef->pub.decompress_data = decompress_data; + coef->pub.coef_arrays = coef->whole_image; /* link to virtual arrays */ +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else { + /* We only need a single-MCU buffer. */ + JBLOCKROW buffer; + int i; + + buffer = (JBLOCKROW) + (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE, + D_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK)); + for (i = 0; i < D_MAX_BLOCKS_IN_MCU; i++) { + coef->MCU_buffer[i] = buffer + i; + } + coef->pub.consume_data = dummy_consume_data; + coef->pub.decompress_data = decompress_onepass; + coef->pub.coef_arrays = NULL; /* flag for no virtual arrays */ + } +} +/********* End of inlined file: jdcoefct.c *********/ + + #undef FIX + +/********* Start of inlined file: jdcolor.c *********/ +#define JPEG_INTERNALS + +/* Private subobject */ + +typedef struct { + struct jpeg_color_deconverter pub; /* public fields */ + + /* Private state for YCC->RGB conversion */ + int * Cr_r_tab; /* => table for Cr to R conversion */ + int * Cb_b_tab; /* => table for Cb to B conversion */ + INT32 * Cr_g_tab; /* => table for Cr to G conversion */ + INT32 * Cb_g_tab; /* => table for Cb to G conversion */ +} my_color_deconverter2; + +typedef my_color_deconverter2 * my_cconvert_ptr2; + +/**************** YCbCr -> RGB conversion: most common case **************/ + +/* + * YCbCr is defined per CCIR 601-1, except that Cb and Cr are + * normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5. + * The conversion equations to be implemented are therefore + * R = Y + 1.40200 * Cr + * G = Y - 0.34414 * Cb - 0.71414 * Cr + * B = Y + 1.77200 * Cb + * where Cb and Cr represent the incoming values less CENTERJSAMPLE. + * (These numbers are derived from TIFF 6.0 section 21, dated 3-June-92.) + * + * To avoid floating-point arithmetic, we represent the fractional constants + * as integers scaled up by 2^16 (about 4 digits precision); we have to divide + * the products by 2^16, with appropriate rounding, to get the correct answer. + * Notice that Y, being an integral input, does not contribute any fraction + * so it need not participate in the rounding. + * + * For even more speed, we avoid doing any multiplications in the inner loop + * by precalculating the constants times Cb and Cr for all possible values. + * For 8-bit JSAMPLEs this is very reasonable (only 256 entries per table); + * for 12-bit samples it is still acceptable. It's not very reasonable for + * 16-bit samples, but if you want lossless storage you shouldn't be changing + * colorspace anyway. + * The Cr=>R and Cb=>B values can be rounded to integers in advance; the + * values for the G calculation are left scaled up, since we must add them + * together before rounding. + */ + +#define SCALEBITS 16 /* speediest right-shift on some machines */ +#define ONE_HALF ((INT32) 1 << (SCALEBITS-1)) +#define FIX(x) ((INT32) ((x) * (1L<RGB colorspace conversion. + */ + +LOCAL(void) +build_ycc_rgb_table (j_decompress_ptr cinfo) +{ + my_cconvert_ptr2 cconvert = (my_cconvert_ptr2) cinfo->cconvert; + int i; + INT32 x; + SHIFT_TEMPS + + cconvert->Cr_r_tab = (int *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(int)); + cconvert->Cb_b_tab = (int *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(int)); + cconvert->Cr_g_tab = (INT32 *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(INT32)); + cconvert->Cb_g_tab = (INT32 *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(INT32)); + + for (i = 0, x = -CENTERJSAMPLE; i <= MAXJSAMPLE; i++, x++) { + /* i is the actual input pixel value, in the range 0..MAXJSAMPLE */ + /* The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE */ + /* Cr=>R value is nearest int to 1.40200 * x */ + cconvert->Cr_r_tab[i] = (int) + RIGHT_SHIFT(FIX(1.40200) * x + ONE_HALF, SCALEBITS); + /* Cb=>B value is nearest int to 1.77200 * x */ + cconvert->Cb_b_tab[i] = (int) + RIGHT_SHIFT(FIX(1.77200) * x + ONE_HALF, SCALEBITS); + /* Cr=>G value is scaled-up -0.71414 * x */ + cconvert->Cr_g_tab[i] = (- FIX(0.71414)) * x; + /* Cb=>G value is scaled-up -0.34414 * x */ + /* We also add in ONE_HALF so that need not do it in inner loop */ + cconvert->Cb_g_tab[i] = (- FIX(0.34414)) * x + ONE_HALF; + } +} + +/* + * Convert some rows of samples to the output colorspace. + * + * Note that we change from noninterleaved, one-plane-per-component format + * to interleaved-pixel format. The output buffer is therefore three times + * as wide as the input buffer. + * A starting row offset is provided only for the input buffer. The caller + * can easily adjust the passed output_buf value to accommodate any row + * offset required on that side. + */ + +METHODDEF(void) +ycc_rgb_convert (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows) +{ + my_cconvert_ptr2 cconvert = (my_cconvert_ptr2) cinfo->cconvert; + register int y, cb, cr; + register JSAMPROW outptr; + register JSAMPROW inptr0, inptr1, inptr2; + register JDIMENSION col; + JDIMENSION num_cols = cinfo->output_width; + /* copy these pointers into registers if possible */ + register JSAMPLE * range_limit = cinfo->sample_range_limit; + register int * Crrtab = cconvert->Cr_r_tab; + register int * Cbbtab = cconvert->Cb_b_tab; + register INT32 * Crgtab = cconvert->Cr_g_tab; + register INT32 * Cbgtab = cconvert->Cb_g_tab; + SHIFT_TEMPS + + while (--num_rows >= 0) { + inptr0 = input_buf[0][input_row]; + inptr1 = input_buf[1][input_row]; + inptr2 = input_buf[2][input_row]; + input_row++; + outptr = *output_buf++; + for (col = 0; col < num_cols; col++) { + y = GETJSAMPLE(inptr0[col]); + cb = GETJSAMPLE(inptr1[col]); + cr = GETJSAMPLE(inptr2[col]); + /* Range-limiting is essential due to noise introduced by DCT losses. */ + outptr[RGB_RED] = range_limit[y + Crrtab[cr]]; + outptr[RGB_GREEN] = range_limit[y + + ((int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], + SCALEBITS))]; + outptr[RGB_BLUE] = range_limit[y + Cbbtab[cb]]; + outptr += RGB_PIXELSIZE; + } + } +} + +/**************** Cases other than YCbCr -> RGB **************/ + +/* + * Color conversion for no colorspace change: just copy the data, + * converting from separate-planes to interleaved representation. + */ + +METHODDEF(void) +null_convert2 (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows) +{ + register JSAMPROW inptr, outptr; + register JDIMENSION count; + register int num_components = cinfo->num_components; + JDIMENSION num_cols = cinfo->output_width; + int ci; + + while (--num_rows >= 0) { + for (ci = 0; ci < num_components; ci++) { + inptr = input_buf[ci][input_row]; + outptr = output_buf[0] + ci; + for (count = num_cols; count > 0; count--) { + *outptr = *inptr++; /* needn't bother with GETJSAMPLE() here */ + outptr += num_components; + } + } + input_row++; + output_buf++; + } +} + +/* + * Color conversion for grayscale: just copy the data. + * This also works for YCbCr -> grayscale conversion, in which + * we just copy the Y (luminance) component and ignore chrominance. + */ + +METHODDEF(void) +grayscale_convert2 (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows) +{ + jcopy_sample_rows(input_buf[0], (int) input_row, output_buf, 0, + num_rows, cinfo->output_width); +} + +/* + * Convert grayscale to RGB: just duplicate the graylevel three times. + * This is provided to support applications that don't want to cope + * with grayscale as a separate case. + */ + +METHODDEF(void) +gray_rgb_convert (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows) +{ + register JSAMPROW inptr, outptr; + register JDIMENSION col; + JDIMENSION num_cols = cinfo->output_width; + + while (--num_rows >= 0) { + inptr = input_buf[0][input_row++]; + outptr = *output_buf++; + for (col = 0; col < num_cols; col++) { + /* We can dispense with GETJSAMPLE() here */ + outptr[RGB_RED] = outptr[RGB_GREEN] = outptr[RGB_BLUE] = inptr[col]; + outptr += RGB_PIXELSIZE; + } + } +} + +/* + * Adobe-style YCCK->CMYK conversion. + * We convert YCbCr to R=1-C, G=1-M, and B=1-Y using the same + * conversion as above, while passing K (black) unchanged. + * We assume build_ycc_rgb_table has been called. + */ + +METHODDEF(void) +ycck_cmyk_convert (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows) +{ + my_cconvert_ptr2 cconvert = (my_cconvert_ptr2) cinfo->cconvert; + register int y, cb, cr; + register JSAMPROW outptr; + register JSAMPROW inptr0, inptr1, inptr2, inptr3; + register JDIMENSION col; + JDIMENSION num_cols = cinfo->output_width; + /* copy these pointers into registers if possible */ + register JSAMPLE * range_limit = cinfo->sample_range_limit; + register int * Crrtab = cconvert->Cr_r_tab; + register int * Cbbtab = cconvert->Cb_b_tab; + register INT32 * Crgtab = cconvert->Cr_g_tab; + register INT32 * Cbgtab = cconvert->Cb_g_tab; + SHIFT_TEMPS + + while (--num_rows >= 0) { + inptr0 = input_buf[0][input_row]; + inptr1 = input_buf[1][input_row]; + inptr2 = input_buf[2][input_row]; + inptr3 = input_buf[3][input_row]; + input_row++; + outptr = *output_buf++; + for (col = 0; col < num_cols; col++) { + y = GETJSAMPLE(inptr0[col]); + cb = GETJSAMPLE(inptr1[col]); + cr = GETJSAMPLE(inptr2[col]); + /* Range-limiting is essential due to noise introduced by DCT losses. */ + outptr[0] = range_limit[MAXJSAMPLE - (y + Crrtab[cr])]; /* red */ + outptr[1] = range_limit[MAXJSAMPLE - (y + /* green */ + ((int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], + SCALEBITS)))]; + outptr[2] = range_limit[MAXJSAMPLE - (y + Cbbtab[cb])]; /* blue */ + /* K passes through unchanged */ + outptr[3] = inptr3[col]; /* don't need GETJSAMPLE here */ + outptr += 4; + } + } +} + +/* + * Empty method for start_pass. + */ + +METHODDEF(void) +start_pass_dcolor (j_decompress_ptr cinfo) +{ + /* no work needed */ +} + +/* + * Module initialization routine for output colorspace conversion. + */ + +GLOBAL(void) +jinit_color_deconverter (j_decompress_ptr cinfo) +{ + my_cconvert_ptr2 cconvert; + int ci; + + cconvert = (my_cconvert_ptr2) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_color_deconverter2)); + cinfo->cconvert = (struct jpeg_color_deconverter *) cconvert; + cconvert->pub.start_pass = start_pass_dcolor; + + /* Make sure num_components agrees with jpeg_color_space */ + switch (cinfo->jpeg_color_space) { + case JCS_GRAYSCALE: + if (cinfo->num_components != 1) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + break; + + case JCS_RGB: + case JCS_YCbCr: + if (cinfo->num_components != 3) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + break; + + case JCS_CMYK: + case JCS_YCCK: + if (cinfo->num_components != 4) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + break; + + default: /* JCS_UNKNOWN can be anything */ + if (cinfo->num_components < 1) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + break; + } + + /* Set out_color_components and conversion method based on requested space. + * Also clear the component_needed flags for any unused components, + * so that earlier pipeline stages can avoid useless computation. + */ + + switch (cinfo->out_color_space) { + case JCS_GRAYSCALE: + cinfo->out_color_components = 1; + if (cinfo->jpeg_color_space == JCS_GRAYSCALE || + cinfo->jpeg_color_space == JCS_YCbCr) { + cconvert->pub.color_convert = grayscale_convert2; + /* For color->grayscale conversion, only the Y (0) component is needed */ + for (ci = 1; ci < cinfo->num_components; ci++) + cinfo->comp_info[ci].component_needed = FALSE; + } else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + + case JCS_RGB: + cinfo->out_color_components = RGB_PIXELSIZE; + if (cinfo->jpeg_color_space == JCS_YCbCr) { + cconvert->pub.color_convert = ycc_rgb_convert; + build_ycc_rgb_table(cinfo); + } else if (cinfo->jpeg_color_space == JCS_GRAYSCALE) { + cconvert->pub.color_convert = gray_rgb_convert; + } else if (cinfo->jpeg_color_space == JCS_RGB && RGB_PIXELSIZE == 3) { + cconvert->pub.color_convert = null_convert2; + } else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + + case JCS_CMYK: + cinfo->out_color_components = 4; + if (cinfo->jpeg_color_space == JCS_YCCK) { + cconvert->pub.color_convert = ycck_cmyk_convert; + build_ycc_rgb_table(cinfo); + } else if (cinfo->jpeg_color_space == JCS_CMYK) { + cconvert->pub.color_convert = null_convert2; + } else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + + default: + /* Permit null conversion to same output space */ + if (cinfo->out_color_space == cinfo->jpeg_color_space) { + cinfo->out_color_components = cinfo->num_components; + cconvert->pub.color_convert = null_convert2; + } else /* unsupported non-null conversion */ + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + } + + if (cinfo->quantize_colors) + cinfo->output_components = 1; /* single colormapped output component */ + else + cinfo->output_components = cinfo->out_color_components; +} +/********* End of inlined file: jdcolor.c *********/ + + #undef FIX + +/********* Start of inlined file: jddctmgr.c *********/ +#define JPEG_INTERNALS + +/* + * The decompressor input side (jdinput.c) saves away the appropriate + * quantization table for each component at the start of the first scan + * involving that component. (This is necessary in order to correctly + * decode files that reuse Q-table slots.) + * When we are ready to make an output pass, the saved Q-table is converted + * to a multiplier table that will actually be used by the IDCT routine. + * The multiplier table contents are IDCT-method-dependent. To support + * application changes in IDCT method between scans, we can remake the + * multiplier tables if necessary. + * In buffered-image mode, the first output pass may occur before any data + * has been seen for some components, and thus before their Q-tables have + * been saved away. To handle this case, multiplier tables are preset + * to zeroes; the result of the IDCT will be a neutral gray level. + */ + +/* Private subobject for this module */ + +typedef struct { + struct jpeg_inverse_dct pub; /* public fields */ + + /* This array contains the IDCT method code that each multiplier table + * is currently set up for, or -1 if it's not yet set up. + * The actual multiplier tables are pointed to by dct_table in the + * per-component comp_info structures. + */ + int cur_method[MAX_COMPONENTS]; +} my_idct_controller; + +typedef my_idct_controller * my_idct_ptr; + +/* Allocated multiplier tables: big enough for any supported variant */ + +typedef union { + ISLOW_MULT_TYPE islow_array[DCTSIZE2]; +#ifdef DCT_IFAST_SUPPORTED + IFAST_MULT_TYPE ifast_array[DCTSIZE2]; +#endif +#ifdef DCT_FLOAT_SUPPORTED + FLOAT_MULT_TYPE float_array[DCTSIZE2]; +#endif +} multiplier_table; + +/* The current scaled-IDCT routines require ISLOW-style multiplier tables, + * so be sure to compile that code if either ISLOW or SCALING is requested. + */ +#ifdef DCT_ISLOW_SUPPORTED +#define PROVIDE_ISLOW_TABLES +#else +#ifdef IDCT_SCALING_SUPPORTED +#define PROVIDE_ISLOW_TABLES +#endif +#endif + +/* + * Prepare for an output pass. + * Here we select the proper IDCT routine for each component and build + * a matching multiplier table. + */ + +METHODDEF(void) +start_pass (j_decompress_ptr cinfo) +{ + my_idct_ptr idct = (my_idct_ptr) cinfo->idct; + int ci, i; + jpeg_component_info *compptr; + int method = 0; + inverse_DCT_method_ptr method_ptr = NULL; + JQUANT_TBL * qtbl; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Select the proper IDCT routine for this component's scaling */ + switch (compptr->DCT_scaled_size) { +#ifdef IDCT_SCALING_SUPPORTED + case 1: + method_ptr = jpeg_idct_1x1; + method = JDCT_ISLOW; /* jidctred uses islow-style table */ + break; + case 2: + method_ptr = jpeg_idct_2x2; + method = JDCT_ISLOW; /* jidctred uses islow-style table */ + break; + case 4: + method_ptr = jpeg_idct_4x4; + method = JDCT_ISLOW; /* jidctred uses islow-style table */ + break; +#endif + case DCTSIZE: + switch (cinfo->dct_method) { +#ifdef DCT_ISLOW_SUPPORTED + case JDCT_ISLOW: + method_ptr = jpeg_idct_islow; + method = JDCT_ISLOW; + break; +#endif +#ifdef DCT_IFAST_SUPPORTED + case JDCT_IFAST: + method_ptr = jpeg_idct_ifast; + method = JDCT_IFAST; + break; +#endif +#ifdef DCT_FLOAT_SUPPORTED + case JDCT_FLOAT: + method_ptr = jpeg_idct_float; + method = JDCT_FLOAT; + break; +#endif + default: + ERREXIT(cinfo, JERR_NOT_COMPILED); + break; + } + break; + default: + ERREXIT1(cinfo, JERR_BAD_DCTSIZE, compptr->DCT_scaled_size); + break; + } + idct->pub.inverse_DCT[ci] = method_ptr; + /* Create multiplier table from quant table. + * However, we can skip this if the component is uninteresting + * or if we already built the table. Also, if no quant table + * has yet been saved for the component, we leave the + * multiplier table all-zero; we'll be reading zeroes from the + * coefficient controller's buffer anyway. + */ + if (! compptr->component_needed || idct->cur_method[ci] == method) + continue; + qtbl = compptr->quant_table; + if (qtbl == NULL) /* happens if no data yet for component */ + continue; + idct->cur_method[ci] = method; + switch (method) { +#ifdef PROVIDE_ISLOW_TABLES + case JDCT_ISLOW: + { + /* For LL&M IDCT method, multipliers are equal to raw quantization + * coefficients, but are stored as ints to ensure access efficiency. + */ + ISLOW_MULT_TYPE * ismtbl = (ISLOW_MULT_TYPE *) compptr->dct_table; + for (i = 0; i < DCTSIZE2; i++) { + ismtbl[i] = (ISLOW_MULT_TYPE) qtbl->quantval[i]; + } + } + break; +#endif +#ifdef DCT_IFAST_SUPPORTED + case JDCT_IFAST: + { + /* For AA&N IDCT method, multipliers are equal to quantization + * coefficients scaled by scalefactor[row]*scalefactor[col], where + * scalefactor[0] = 1 + * scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 + * For integer operation, the multiplier table is to be scaled by + * IFAST_SCALE_BITS. + */ + IFAST_MULT_TYPE * ifmtbl = (IFAST_MULT_TYPE *) compptr->dct_table; +#define CONST_BITS 14 + static const INT16 aanscales[DCTSIZE2] = { + /* precomputed values scaled up by 14 bits */ + 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, + 22725, 31521, 29692, 26722, 22725, 17855, 12299, 6270, + 21407, 29692, 27969, 25172, 21407, 16819, 11585, 5906, + 19266, 26722, 25172, 22654, 19266, 15137, 10426, 5315, + 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, + 12873, 17855, 16819, 15137, 12873, 10114, 6967, 3552, + 8867, 12299, 11585, 10426, 8867, 6967, 4799, 2446, + 4520, 6270, 5906, 5315, 4520, 3552, 2446, 1247 + }; + SHIFT_TEMPS + + for (i = 0; i < DCTSIZE2; i++) { + ifmtbl[i] = (IFAST_MULT_TYPE) + DESCALE(MULTIPLY16V16((INT32) qtbl->quantval[i], + (INT32) aanscales[i]), + CONST_BITS-IFAST_SCALE_BITS); + } + } + break; +#endif +#ifdef DCT_FLOAT_SUPPORTED + case JDCT_FLOAT: + { + /* For float AA&N IDCT method, multipliers are equal to quantization + * coefficients scaled by scalefactor[row]*scalefactor[col], where + * scalefactor[0] = 1 + * scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 + */ + FLOAT_MULT_TYPE * fmtbl = (FLOAT_MULT_TYPE *) compptr->dct_table; + int row, col; + static const double aanscalefactor[DCTSIZE] = { + 1.0, 1.387039845, 1.306562965, 1.175875602, + 1.0, 0.785694958, 0.541196100, 0.275899379 + }; + + i = 0; + for (row = 0; row < DCTSIZE; row++) { + for (col = 0; col < DCTSIZE; col++) { + fmtbl[i] = (FLOAT_MULT_TYPE) + ((double) qtbl->quantval[i] * + aanscalefactor[row] * aanscalefactor[col]); + i++; + } + } + } + break; +#endif + default: + ERREXIT(cinfo, JERR_NOT_COMPILED); + break; + } + } +} + +/* + * Initialize IDCT manager. + */ + +GLOBAL(void) +jinit_inverse_dct (j_decompress_ptr cinfo) +{ + my_idct_ptr idct; + int ci; + jpeg_component_info *compptr; + + idct = (my_idct_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_idct_controller)); + cinfo->idct = (struct jpeg_inverse_dct *) idct; + idct->pub.start_pass = start_pass; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Allocate and pre-zero a multiplier table for each component */ + compptr->dct_table = + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(multiplier_table)); + MEMZERO(compptr->dct_table, SIZEOF(multiplier_table)); + /* Mark multiplier table not yet set up for any method */ + idct->cur_method[ci] = -1; + } +} +/********* End of inlined file: jddctmgr.c *********/ + + #undef CONST_BITS + #undef ASSIGN_STATE + +/********* Start of inlined file: jdhuff.c *********/ +#define JPEG_INTERNALS + +/********* Start of inlined file: jdhuff.h *********/ +/* Short forms of external names for systems with brain-damaged linkers. */ + +#ifndef __jdhuff_h__ +#define __jdhuff_h__ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_make_d_derived_tbl jMkDDerived +#define jpeg_fill_bit_buffer jFilBitBuf +#define jpeg_huff_decode jHufDecode +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + +/* Derived data constructed for each Huffman table */ + +#define HUFF_LOOKAHEAD 8 /* # of bits of lookahead */ + +typedef struct { + /* Basic tables: (element [0] of each array is unused) */ + INT32 maxcode[18]; /* largest code of length k (-1 if none) */ + /* (maxcode[17] is a sentinel to ensure jpeg_huff_decode terminates) */ + INT32 valoffset[17]; /* huffval[] offset for codes of length k */ + /* valoffset[k] = huffval[] index of 1st symbol of code length k, less + * the smallest code of length k; so given a code of length k, the + * corresponding symbol is huffval[code + valoffset[k]] + */ + + /* Link to public Huffman table (needed only in jpeg_huff_decode) */ + JHUFF_TBL *pub; + + /* Lookahead tables: indexed by the next HUFF_LOOKAHEAD bits of + * the input data stream. If the next Huffman code is no more + * than HUFF_LOOKAHEAD bits long, we can obtain its length and + * the corresponding symbol directly from these tables. + */ + int look_nbits[1< 32 bits on your machine, and shifting/masking longs is + * reasonably fast, making bit_buf_type be long and setting BIT_BUF_SIZE + * appropriately should be a win. Unfortunately we can't define the size + * with something like #define BIT_BUF_SIZE (sizeof(bit_buf_type)*8) + * because not all machines measure sizeof in 8-bit bytes. + */ + +typedef struct { /* Bitreading state saved across MCUs */ + bit_buf_type get_buffer; /* current bit-extraction buffer */ + int bits_left; /* # of unused bits in it */ +} bitread_perm_state; + +typedef struct { /* Bitreading working state within an MCU */ + /* Current data source location */ + /* We need a copy, rather than munging the original, in case of suspension */ + const JOCTET * next_input_byte; /* => next byte to read from source */ + size_t bytes_in_buffer; /* # of bytes remaining in source buffer */ + /* Bit input buffer --- note these values are kept in register variables, + * not in this struct, inside the inner loops. + */ + bit_buf_type get_buffer; /* current bit-extraction buffer */ + int bits_left; /* # of unused bits in it */ + /* Pointer needed by jpeg_fill_bit_buffer. */ + j_decompress_ptr cinfo; /* back link to decompress master record */ +} bitread_working_state; + +/* Macros to declare and load/save bitread local variables. */ +#define BITREAD_STATE_VARS \ + register bit_buf_type get_buffer; \ + register int bits_left; \ + bitread_working_state br_state + +#define BITREAD_LOAD_STATE(cinfop,permstate) \ + br_state.cinfo = cinfop; \ + br_state.next_input_byte = cinfop->src->next_input_byte; \ + br_state.bytes_in_buffer = cinfop->src->bytes_in_buffer; \ + get_buffer = permstate.get_buffer; \ + bits_left = permstate.bits_left; + +#define BITREAD_SAVE_STATE(cinfop,permstate) \ + cinfop->src->next_input_byte = br_state.next_input_byte; \ + cinfop->src->bytes_in_buffer = br_state.bytes_in_buffer; \ + permstate.get_buffer = get_buffer; \ + permstate.bits_left = bits_left + +/* + * These macros provide the in-line portion of bit fetching. + * Use CHECK_BIT_BUFFER to ensure there are N bits in get_buffer + * before using GET_BITS, PEEK_BITS, or DROP_BITS. + * The variables get_buffer and bits_left are assumed to be locals, + * but the state struct might not be (jpeg_huff_decode needs this). + * CHECK_BIT_BUFFER(state,n,action); + * Ensure there are N bits in get_buffer; if suspend, take action. + * val = GET_BITS(n); + * Fetch next N bits. + * val = PEEK_BITS(n); + * Fetch next N bits without removing them from the buffer. + * DROP_BITS(n); + * Discard next N bits. + * The value N should be a simple variable, not an expression, because it + * is evaluated multiple times. + */ + +#define CHECK_BIT_BUFFER(state,nbits,action) \ + { if (bits_left < (nbits)) { \ + if (! jpeg_fill_bit_buffer(&(state),get_buffer,bits_left,nbits)) \ + { action; } \ + get_buffer = (state).get_buffer; bits_left = (state).bits_left; } } + +#define GET_BITS(nbits) \ + (((int) (get_buffer >> (bits_left -= (nbits)))) & ((1<<(nbits))-1)) + +#define PEEK_BITS(nbits) \ + (((int) (get_buffer >> (bits_left - (nbits)))) & ((1<<(nbits))-1)) + +#define DROP_BITS(nbits) \ + (bits_left -= (nbits)) + +/* Load up the bit buffer to a depth of at least nbits */ +EXTERN(boolean) jpeg_fill_bit_buffer + JPP((bitread_working_state * state, register bit_buf_type get_buffer, + register int bits_left, int nbits)); + +/* + * Code for extracting next Huffman-coded symbol from input bit stream. + * Again, this is time-critical and we make the main paths be macros. + * + * We use a lookahead table to process codes of up to HUFF_LOOKAHEAD bits + * without looping. Usually, more than 95% of the Huffman codes will be 8 + * or fewer bits long. The few overlength codes are handled with a loop, + * which need not be inline code. + * + * Notes about the HUFF_DECODE macro: + * 1. Near the end of the data segment, we may fail to get enough bits + * for a lookahead. In that case, we do it the hard way. + * 2. If the lookahead table contains no entry, the next code must be + * more than HUFF_LOOKAHEAD bits long. + * 3. jpeg_huff_decode returns -1 if forced to suspend. + */ + +#define HUFF_DECODE(result,state,htbl,failaction,slowlabel) \ +{ register int nb, look; \ + if (bits_left < HUFF_LOOKAHEAD) { \ + if (! jpeg_fill_bit_buffer(&state,get_buffer,bits_left, 0)) {failaction;} \ + get_buffer = state.get_buffer; bits_left = state.bits_left; \ + if (bits_left < HUFF_LOOKAHEAD) { \ + nb = 1; goto slowlabel; \ + } \ + } \ + look = PEEK_BITS(HUFF_LOOKAHEAD); \ + if ((nb = htbl->look_nbits[look]) != 0) { \ + DROP_BITS(nb); \ + result = htbl->look_sym[look]; \ + } else { \ + nb = HUFF_LOOKAHEAD+1; \ +slowlabel: \ + if ((result=jpeg_huff_decode(&state,get_buffer,bits_left,htbl,nb)) < 0) \ + { failaction; } \ + get_buffer = state.get_buffer; bits_left = state.bits_left; \ + } \ +} + +/* Out-of-line case for Huffman code fetching */ +EXTERN(int) jpeg_huff_decode + JPP((bitread_working_state * state, register bit_buf_type get_buffer, + register int bits_left, d_derived_tbl * htbl, int min_bits)); + +#endif +/********* End of inlined file: jdhuff.h *********/ + + /* Declarations shared with jdphuff.c */ + +/* + * Expanded entropy decoder object for Huffman decoding. + * + * The savable_state subrecord contains fields that change within an MCU, + * but must not be updated permanently until we complete the MCU. + */ + +typedef struct { + int last_dc_val[MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ +} savable_state2; + +/* This macro is to work around compilers with missing or broken + * structure assignment. You'll need to fix this code if you have + * such a compiler and you change MAX_COMPS_IN_SCAN. + */ + +#ifndef NO_STRUCT_ASSIGN +#define ASSIGN_STATE(dest,src) ((dest) = (src)) +#else +#if MAX_COMPS_IN_SCAN == 4 +#define ASSIGN_STATE(dest,src) \ + ((dest).last_dc_val[0] = (src).last_dc_val[0], \ + (dest).last_dc_val[1] = (src).last_dc_val[1], \ + (dest).last_dc_val[2] = (src).last_dc_val[2], \ + (dest).last_dc_val[3] = (src).last_dc_val[3]) +#endif +#endif + +typedef struct { + struct jpeg_entropy_decoder pub; /* public fields */ + + /* These fields are loaded into local variables at start of each MCU. + * In case of suspension, we exit WITHOUT updating them. + */ + bitread_perm_state bitstate; /* Bit buffer at start of MCU */ + savable_state2 saved; /* Other state at start of MCU */ + + /* These fields are NOT loaded into local working state. */ + unsigned int restarts_to_go; /* MCUs left in this restart interval */ + + /* Pointers to derived tables (these workspaces have image lifespan) */ + d_derived_tbl * dc_derived_tbls[NUM_HUFF_TBLS]; + d_derived_tbl * ac_derived_tbls[NUM_HUFF_TBLS]; + + /* Precalculated info set up by start_pass for use in decode_mcu: */ + + /* Pointers to derived tables to be used for each block within an MCU */ + d_derived_tbl * dc_cur_tbls[D_MAX_BLOCKS_IN_MCU]; + d_derived_tbl * ac_cur_tbls[D_MAX_BLOCKS_IN_MCU]; + /* Whether we care about the DC and AC coefficient values for each block */ + boolean dc_needed[D_MAX_BLOCKS_IN_MCU]; + boolean ac_needed[D_MAX_BLOCKS_IN_MCU]; +} huff_entropy_decoder2; + +typedef huff_entropy_decoder2 * huff_entropy_ptr2; + +/* + * Initialize for a Huffman-compressed scan. + */ + +METHODDEF(void) +start_pass_huff_decoder (j_decompress_ptr cinfo) +{ + huff_entropy_ptr2 entropy = (huff_entropy_ptr2) cinfo->entropy; + int ci, blkn, dctbl, actbl; + jpeg_component_info * compptr; + + /* Check that the scan parameters Ss, Se, Ah/Al are OK for sequential JPEG. + * This ought to be an error condition, but we make it a warning because + * there are some baseline files out there with all zeroes in these bytes. + */ + if (cinfo->Ss != 0 || cinfo->Se != DCTSIZE2-1 || + cinfo->Ah != 0 || cinfo->Al != 0) + WARNMS(cinfo, JWRN_NOT_SEQUENTIAL); + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + dctbl = compptr->dc_tbl_no; + actbl = compptr->ac_tbl_no; + /* Compute derived values for Huffman tables */ + /* We may do this more than once for a table, but it's not expensive */ + jpeg_make_d_derived_tbl(cinfo, TRUE, dctbl, + & entropy->dc_derived_tbls[dctbl]); + jpeg_make_d_derived_tbl(cinfo, FALSE, actbl, + & entropy->ac_derived_tbls[actbl]); + /* Initialize DC predictions to 0 */ + entropy->saved.last_dc_val[ci] = 0; + } + + /* Precalculate decoding info for each block in an MCU of this scan */ + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + ci = cinfo->MCU_membership[blkn]; + compptr = cinfo->cur_comp_info[ci]; + /* Precalculate which table to use for each block */ + entropy->dc_cur_tbls[blkn] = entropy->dc_derived_tbls[compptr->dc_tbl_no]; + entropy->ac_cur_tbls[blkn] = entropy->ac_derived_tbls[compptr->ac_tbl_no]; + /* Decide whether we really care about the coefficient values */ + if (compptr->component_needed) { + entropy->dc_needed[blkn] = TRUE; + /* we don't need the ACs if producing a 1/8th-size image */ + entropy->ac_needed[blkn] = (compptr->DCT_scaled_size > 1); + } else { + entropy->dc_needed[blkn] = entropy->ac_needed[blkn] = FALSE; + } + } + + /* Initialize bitread state variables */ + entropy->bitstate.bits_left = 0; + entropy->bitstate.get_buffer = 0; /* unnecessary, but keeps Purify quiet */ + entropy->pub.insufficient_data = FALSE; + + /* Initialize restart counter */ + entropy->restarts_to_go = cinfo->restart_interval; +} + +/* + * Compute the derived values for a Huffman table. + * This routine also performs some validation checks on the table. + * + * Note this is also used by jdphuff.c. + */ + +GLOBAL(void) +jpeg_make_d_derived_tbl (j_decompress_ptr cinfo, boolean isDC, int tblno, + d_derived_tbl ** pdtbl) +{ + JHUFF_TBL *htbl; + d_derived_tbl *dtbl; + int p, i, l, si, numsymbols; + int lookbits, ctr; + char huffsize[257]; + unsigned int huffcode[257]; + unsigned int code; + + /* Note that huffsize[] and huffcode[] are filled in code-length order, + * paralleling the order of the symbols themselves in htbl->huffval[]. + */ + + /* Find the input Huffman table */ + if (tblno < 0 || tblno >= NUM_HUFF_TBLS) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, tblno); + htbl = + isDC ? cinfo->dc_huff_tbl_ptrs[tblno] : cinfo->ac_huff_tbl_ptrs[tblno]; + if (htbl == NULL) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, tblno); + + /* Allocate a workspace if we haven't already done so. */ + if (*pdtbl == NULL) + *pdtbl = (d_derived_tbl *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(d_derived_tbl)); + dtbl = *pdtbl; + dtbl->pub = htbl; /* fill in back link */ + + /* Figure C.1: make table of Huffman code length for each symbol */ + + p = 0; + for (l = 1; l <= 16; l++) { + i = (int) htbl->bits[l]; + if (i < 0 || p + i > 256) /* protect against table overrun */ + ERREXIT(cinfo, JERR_BAD_HUFF_TABLE); + while (i--) + huffsize[p++] = (char) l; + } + huffsize[p] = 0; + numsymbols = p; + + /* Figure C.2: generate the codes themselves */ + /* We also validate that the counts represent a legal Huffman code tree. */ + + code = 0; + si = huffsize[0]; + p = 0; + while (huffsize[p]) { + while (((int) huffsize[p]) == si) { + huffcode[p++] = code; + code++; + } + /* code is now 1 more than the last code used for codelength si; but + * it must still fit in si bits, since no code is allowed to be all ones. + */ + if (((INT32) code) >= (((INT32) 1) << si)) + ERREXIT(cinfo, JERR_BAD_HUFF_TABLE); + code <<= 1; + si++; + } + + /* Figure F.15: generate decoding tables for bit-sequential decoding */ + + p = 0; + for (l = 1; l <= 16; l++) { + if (htbl->bits[l]) { + /* valoffset[l] = huffval[] index of 1st symbol of code length l, + * minus the minimum code of length l + */ + dtbl->valoffset[l] = (INT32) p - (INT32) huffcode[p]; + p += htbl->bits[l]; + dtbl->maxcode[l] = huffcode[p-1]; /* maximum code of length l */ + } else { + dtbl->maxcode[l] = -1; /* -1 if no codes of this length */ + } + } + dtbl->maxcode[17] = 0xFFFFFL; /* ensures jpeg_huff_decode terminates */ + + /* Compute lookahead tables to speed up decoding. + * First we set all the table entries to 0, indicating "too long"; + * then we iterate through the Huffman codes that are short enough and + * fill in all the entries that correspond to bit sequences starting + * with that code. + */ + + MEMZERO(dtbl->look_nbits, SIZEOF(dtbl->look_nbits)); + + p = 0; + for (l = 1; l <= HUFF_LOOKAHEAD; l++) { + for (i = 1; i <= (int) htbl->bits[l]; i++, p++) { + /* l = current code's length, p = its index in huffcode[] & huffval[]. */ + /* Generate left-justified code followed by all possible bit sequences */ + lookbits = huffcode[p] << (HUFF_LOOKAHEAD-l); + for (ctr = 1 << (HUFF_LOOKAHEAD-l); ctr > 0; ctr--) { + dtbl->look_nbits[lookbits] = l; + dtbl->look_sym[lookbits] = htbl->huffval[p]; + lookbits++; + } + } + } + + /* Validate symbols as being reasonable. + * For AC tables, we make no check, but accept all byte values 0..255. + * For DC tables, we require the symbols to be in range 0..15. + * (Tighter bounds could be applied depending on the data depth and mode, + * but this is sufficient to ensure safe decoding.) + */ + if (isDC) { + for (i = 0; i < numsymbols; i++) { + int sym = htbl->huffval[i]; + if (sym < 0 || sym > 15) + ERREXIT(cinfo, JERR_BAD_HUFF_TABLE); + } + } +} + +/* + * Out-of-line code for bit fetching (shared with jdphuff.c). + * See jdhuff.h for info about usage. + * Note: current values of get_buffer and bits_left are passed as parameters, + * but are returned in the corresponding fields of the state struct. + * + * On most machines MIN_GET_BITS should be 25 to allow the full 32-bit width + * of get_buffer to be used. (On machines with wider words, an even larger + * buffer could be used.) However, on some machines 32-bit shifts are + * quite slow and take time proportional to the number of places shifted. + * (This is true with most PC compilers, for instance.) In this case it may + * be a win to set MIN_GET_BITS to the minimum value of 15. This reduces the + * average shift distance at the cost of more calls to jpeg_fill_bit_buffer. + */ + +#ifdef SLOW_SHIFT_32 +#define MIN_GET_BITS 15 /* minimum allowable value */ +#else +#define MIN_GET_BITS (BIT_BUF_SIZE-7) +#endif + +GLOBAL(boolean) +jpeg_fill_bit_buffer (bitread_working_state * state, + register bit_buf_type get_buffer, register int bits_left, + int nbits) +/* Load up the bit buffer to a depth of at least nbits */ +{ + /* Copy heavily used state fields into locals (hopefully registers) */ + register const JOCTET * next_input_byte = state->next_input_byte; + register size_t bytes_in_buffer = state->bytes_in_buffer; + j_decompress_ptr cinfo = state->cinfo; + + /* Attempt to load at least MIN_GET_BITS bits into get_buffer. */ + /* (It is assumed that no request will be for more than that many bits.) */ + /* We fail to do so only if we hit a marker or are forced to suspend. */ + + if (cinfo->unread_marker == 0) { /* cannot advance past a marker */ + while (bits_left < MIN_GET_BITS) { + register int c; + + /* Attempt to read a byte */ + if (bytes_in_buffer == 0) { + if (! (*cinfo->src->fill_input_buffer) (cinfo)) + return FALSE; + next_input_byte = cinfo->src->next_input_byte; + bytes_in_buffer = cinfo->src->bytes_in_buffer; + } + bytes_in_buffer--; + c = GETJOCTET(*next_input_byte++); + + /* If it's 0xFF, check and discard stuffed zero byte */ + if (c == 0xFF) { + /* Loop here to discard any padding FF's on terminating marker, + * so that we can save a valid unread_marker value. NOTE: we will + * accept multiple FF's followed by a 0 as meaning a single FF data + * byte. This data pattern is not valid according to the standard. + */ + do { + if (bytes_in_buffer == 0) { + if (! (*cinfo->src->fill_input_buffer) (cinfo)) + return FALSE; + next_input_byte = cinfo->src->next_input_byte; + bytes_in_buffer = cinfo->src->bytes_in_buffer; + } + bytes_in_buffer--; + c = GETJOCTET(*next_input_byte++); + } while (c == 0xFF); + + if (c == 0) { + /* Found FF/00, which represents an FF data byte */ + c = 0xFF; + } else { + /* Oops, it's actually a marker indicating end of compressed data. + * Save the marker code for later use. + * Fine point: it might appear that we should save the marker into + * bitread working state, not straight into permanent state. But + * once we have hit a marker, we cannot need to suspend within the + * current MCU, because we will read no more bytes from the data + * source. So it is OK to update permanent state right away. + */ + cinfo->unread_marker = c; + /* See if we need to insert some fake zero bits. */ + goto no_more_bytes; + } + } + + /* OK, load c into get_buffer */ + get_buffer = (get_buffer << 8) | c; + bits_left += 8; + } /* end while */ + } else { + no_more_bytes: + /* We get here if we've read the marker that terminates the compressed + * data segment. There should be enough bits in the buffer register + * to satisfy the request; if so, no problem. + */ + if (nbits > bits_left) { + /* Uh-oh. Report corrupted data to user and stuff zeroes into + * the data stream, so that we can produce some kind of image. + * We use a nonvolatile flag to ensure that only one warning message + * appears per data segment. + */ + if (! cinfo->entropy->insufficient_data) { + WARNMS(cinfo, JWRN_HIT_MARKER); + cinfo->entropy->insufficient_data = TRUE; + } + /* Fill the buffer with zero bits */ + get_buffer <<= MIN_GET_BITS - bits_left; + bits_left = MIN_GET_BITS; + } + } + + /* Unload the local registers */ + state->next_input_byte = next_input_byte; + state->bytes_in_buffer = bytes_in_buffer; + state->get_buffer = get_buffer; + state->bits_left = bits_left; + + return TRUE; +} + +/* + * Out-of-line code for Huffman code decoding. + * See jdhuff.h for info about usage. + */ + +GLOBAL(int) +jpeg_huff_decode (bitread_working_state * state, + register bit_buf_type get_buffer, register int bits_left, + d_derived_tbl * htbl, int min_bits) +{ + register int l = min_bits; + register INT32 code; + + /* HUFF_DECODE has determined that the code is at least min_bits */ + /* bits long, so fetch that many bits in one swoop. */ + + CHECK_BIT_BUFFER(*state, l, return -1); + code = GET_BITS(l); + + /* Collect the rest of the Huffman code one bit at a time. */ + /* This is per Figure F.16 in the JPEG spec. */ + + while (code > htbl->maxcode[l]) { + code <<= 1; + CHECK_BIT_BUFFER(*state, 1, return -1); + code |= GET_BITS(1); + l++; + } + + /* Unload the local registers */ + state->get_buffer = get_buffer; + state->bits_left = bits_left; + + /* With garbage input we may reach the sentinel value l = 17. */ + + if (l > 16) { + WARNMS(state->cinfo, JWRN_HUFF_BAD_CODE); + return 0; /* fake a zero as the safest result */ + } + + return htbl->pub->huffval[ (int) (code + htbl->valoffset[l]) ]; +} + +/* + * Check for a restart marker & resynchronize decoder. + * Returns FALSE if must suspend. + */ + +LOCAL(boolean) +process_restart (j_decompress_ptr cinfo) +{ + huff_entropy_ptr2 entropy = (huff_entropy_ptr2) cinfo->entropy; + int ci; + + /* Throw away any unused bits remaining in bit buffer; */ + /* include any full bytes in next_marker's count of discarded bytes */ + cinfo->marker->discarded_bytes += entropy->bitstate.bits_left / 8; + entropy->bitstate.bits_left = 0; + + /* Advance past the RSTn marker */ + if (! (*cinfo->marker->read_restart_marker) (cinfo)) + return FALSE; + + /* Re-initialize DC predictions to 0 */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) + entropy->saved.last_dc_val[ci] = 0; + + /* Reset restart counter */ + entropy->restarts_to_go = cinfo->restart_interval; + + /* Reset out-of-data flag, unless read_restart_marker left us smack up + * against a marker. In that case we will end up treating the next data + * segment as empty, and we can avoid producing bogus output pixels by + * leaving the flag set. + */ + if (cinfo->unread_marker == 0) + entropy->pub.insufficient_data = FALSE; + + return TRUE; +} + +/* + * Decode and return one MCU's worth of Huffman-compressed coefficients. + * The coefficients are reordered from zigzag order into natural array order, + * but are not dequantized. + * + * The i'th block of the MCU is stored into the block pointed to by + * MCU_data[i]. WE ASSUME THIS AREA HAS BEEN ZEROED BY THE CALLER. + * (Wholesale zeroing is usually a little faster than retail...) + * + * Returns FALSE if data source requested suspension. In that case no + * changes have been made to permanent state. (Exception: some output + * coefficients may already have been assigned. This is harmless for + * this module, since we'll just re-assign them on the next call.) + */ + +METHODDEF(boolean) +decode_mcu (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) +{ + huff_entropy_ptr2 entropy = (huff_entropy_ptr2) cinfo->entropy; + int blkn; + BITREAD_STATE_VARS; + savable_state2 state; + + /* Process restart marker if needed; may have to suspend */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) + if (! process_restart(cinfo)) + return FALSE; + } + + /* If we've run out of data, just leave the MCU set to zeroes. + * This way, we return uniform gray for the remainder of the segment. + */ + if (! entropy->pub.insufficient_data) { + + /* Load up working state */ + BITREAD_LOAD_STATE(cinfo,entropy->bitstate); + ASSIGN_STATE(state, entropy->saved); + + /* Outer loop handles each block in the MCU */ + + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + JBLOCKROW block = MCU_data[blkn]; + d_derived_tbl * dctbl = entropy->dc_cur_tbls[blkn]; + d_derived_tbl * actbl = entropy->ac_cur_tbls[blkn]; + register int s, k, r; + + /* Decode a single block's worth of coefficients */ + + /* Section F.2.2.1: decode the DC coefficient difference */ + HUFF_DECODE(s, br_state, dctbl, return FALSE, label1); + if (s) { + CHECK_BIT_BUFFER(br_state, s, return FALSE); + r = GET_BITS(s); + s = HUFF_EXTEND(r, s); + } + + if (entropy->dc_needed[blkn]) { + /* Convert DC difference to actual value, update last_dc_val */ + int ci = cinfo->MCU_membership[blkn]; + s += state.last_dc_val[ci]; + state.last_dc_val[ci] = s; + /* Output the DC coefficient (assumes jpeg_natural_order[0] = 0) */ + (*block)[0] = (JCOEF) s; + } + + if (entropy->ac_needed[blkn]) { + + /* Section F.2.2.2: decode the AC coefficients */ + /* Since zeroes are skipped, output area must be cleared beforehand */ + for (k = 1; k < DCTSIZE2; k++) { + HUFF_DECODE(s, br_state, actbl, return FALSE, label2); + + r = s >> 4; + s &= 15; + + if (s) { + k += r; + CHECK_BIT_BUFFER(br_state, s, return FALSE); + r = GET_BITS(s); + s = HUFF_EXTEND(r, s); + /* Output coefficient in natural (dezigzagged) order. + * Note: the extra entries in jpeg_natural_order[] will save us + * if k >= DCTSIZE2, which could happen if the data is corrupted. + */ + (*block)[jpeg_natural_order[k]] = (JCOEF) s; + } else { + if (r != 15) + break; + k += 15; + } + } + + } else { + + /* Section F.2.2.2: decode the AC coefficients */ + /* In this path we just discard the values */ + for (k = 1; k < DCTSIZE2; k++) { + HUFF_DECODE(s, br_state, actbl, return FALSE, label3); + + r = s >> 4; + s &= 15; + + if (s) { + k += r; + CHECK_BIT_BUFFER(br_state, s, return FALSE); + DROP_BITS(s); + } else { + if (r != 15) + break; + k += 15; + } + } + + } + } + + /* Completed MCU, so update state */ + BITREAD_SAVE_STATE(cinfo,entropy->bitstate); + ASSIGN_STATE(entropy->saved, state); + } + + /* Account for restart interval (no-op if not using restarts) */ + entropy->restarts_to_go--; + + return TRUE; +} + +/* + * Module initialization routine for Huffman entropy decoding. + */ + +GLOBAL(void) +jinit_huff_decoder (j_decompress_ptr cinfo) +{ + huff_entropy_ptr2 entropy; + int i; + + entropy = (huff_entropy_ptr2) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(huff_entropy_decoder2)); + cinfo->entropy = (struct jpeg_entropy_decoder *) entropy; + entropy->pub.start_pass = start_pass_huff_decoder; + entropy->pub.decode_mcu = decode_mcu; + + /* Mark tables unallocated */ + for (i = 0; i < NUM_HUFF_TBLS; i++) { + entropy->dc_derived_tbls[i] = entropy->ac_derived_tbls[i] = NULL; + } +} +/********* End of inlined file: jdhuff.c *********/ + +/********* Start of inlined file: jdinput.c *********/ +#define JPEG_INTERNALS + +/* Private state */ + +typedef struct { + struct jpeg_input_controller pub; /* public fields */ + + boolean inheaders; /* TRUE until first SOS is reached */ +} my_input_controller; + +typedef my_input_controller * my_inputctl_ptr; + +/* Forward declarations */ +METHODDEF(int) consume_markers JPP((j_decompress_ptr cinfo)); + +/* + * Routines to calculate various quantities related to the size of the image. + */ + +LOCAL(void) +initial_setup2 (j_decompress_ptr cinfo) +/* Called once, when first SOS marker is reached */ +{ + int ci; + jpeg_component_info *compptr; + + /* Make sure image isn't bigger than I can handle */ + if ((long) cinfo->image_height > (long) JPEG_MAX_DIMENSION || + (long) cinfo->image_width > (long) JPEG_MAX_DIMENSION) + ERREXIT1(cinfo, JERR_IMAGE_TOO_BIG, (unsigned int) JPEG_MAX_DIMENSION); + + /* For now, precision must match compiled-in value... */ + if (cinfo->data_precision != BITS_IN_JSAMPLE) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + + /* Check that number of components won't exceed internal array sizes */ + if (cinfo->num_components > MAX_COMPONENTS) + ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->num_components, + MAX_COMPONENTS); + + /* Compute maximum sampling factors; check factor validity */ + cinfo->max_h_samp_factor = 1; + cinfo->max_v_samp_factor = 1; + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + if (compptr->h_samp_factor<=0 || compptr->h_samp_factor>MAX_SAMP_FACTOR || + compptr->v_samp_factor<=0 || compptr->v_samp_factor>MAX_SAMP_FACTOR) + ERREXIT(cinfo, JERR_BAD_SAMPLING); + cinfo->max_h_samp_factor = MAX(cinfo->max_h_samp_factor, + compptr->h_samp_factor); + cinfo->max_v_samp_factor = MAX(cinfo->max_v_samp_factor, + compptr->v_samp_factor); + } + + /* We initialize DCT_scaled_size and min_DCT_scaled_size to DCTSIZE. + * In the full decompressor, this will be overridden by jdmaster.c; + * but in the transcoder, jdmaster.c is not used, so we must do it here. + */ + cinfo->min_DCT_scaled_size = DCTSIZE; + + /* Compute dimensions of components */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + compptr->DCT_scaled_size = DCTSIZE; + /* Size in DCT blocks */ + compptr->width_in_blocks = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * (long) compptr->h_samp_factor, + (long) (cinfo->max_h_samp_factor * DCTSIZE)); + compptr->height_in_blocks = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * (long) compptr->v_samp_factor, + (long) (cinfo->max_v_samp_factor * DCTSIZE)); + /* downsampled_width and downsampled_height will also be overridden by + * jdmaster.c if we are doing full decompression. The transcoder library + * doesn't use these values, but the calling application might. + */ + /* Size in samples */ + compptr->downsampled_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * (long) compptr->h_samp_factor, + (long) cinfo->max_h_samp_factor); + compptr->downsampled_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * (long) compptr->v_samp_factor, + (long) cinfo->max_v_samp_factor); + /* Mark component needed, until color conversion says otherwise */ + compptr->component_needed = TRUE; + /* Mark no quantization table yet saved for component */ + compptr->quant_table = NULL; + } + + /* Compute number of fully interleaved MCU rows. */ + cinfo->total_iMCU_rows = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, + (long) (cinfo->max_v_samp_factor*DCTSIZE)); + + /* Decide whether file contains multiple scans */ + if (cinfo->comps_in_scan < cinfo->num_components || cinfo->progressive_mode) + cinfo->inputctl->has_multiple_scans = TRUE; + else + cinfo->inputctl->has_multiple_scans = FALSE; +} + +LOCAL(void) +per_scan_setup2 (j_decompress_ptr cinfo) +/* Do computations that are needed before processing a JPEG scan */ +/* cinfo->comps_in_scan and cinfo->cur_comp_info[] were set from SOS marker */ +{ + int ci, mcublks, tmp; + jpeg_component_info *compptr; + + if (cinfo->comps_in_scan == 1) { + + /* Noninterleaved (single-component) scan */ + compptr = cinfo->cur_comp_info[0]; + + /* Overall image size in MCUs */ + cinfo->MCUs_per_row = compptr->width_in_blocks; + cinfo->MCU_rows_in_scan = compptr->height_in_blocks; + + /* For noninterleaved scan, always one block per MCU */ + compptr->MCU_width = 1; + compptr->MCU_height = 1; + compptr->MCU_blocks = 1; + compptr->MCU_sample_width = compptr->DCT_scaled_size; + compptr->last_col_width = 1; + /* For noninterleaved scans, it is convenient to define last_row_height + * as the number of block rows present in the last iMCU row. + */ + tmp = (int) (compptr->height_in_blocks % compptr->v_samp_factor); + if (tmp == 0) tmp = compptr->v_samp_factor; + compptr->last_row_height = tmp; + + /* Prepare array describing MCU composition */ + cinfo->blocks_in_MCU = 1; + cinfo->MCU_membership[0] = 0; + + } else { + + /* Interleaved (multi-component) scan */ + if (cinfo->comps_in_scan <= 0 || cinfo->comps_in_scan > MAX_COMPS_IN_SCAN) + ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->comps_in_scan, + MAX_COMPS_IN_SCAN); + + /* Overall image size in MCUs */ + cinfo->MCUs_per_row = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width, + (long) (cinfo->max_h_samp_factor*DCTSIZE)); + cinfo->MCU_rows_in_scan = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, + (long) (cinfo->max_v_samp_factor*DCTSIZE)); + + cinfo->blocks_in_MCU = 0; + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + /* Sampling factors give # of blocks of component in each MCU */ + compptr->MCU_width = compptr->h_samp_factor; + compptr->MCU_height = compptr->v_samp_factor; + compptr->MCU_blocks = compptr->MCU_width * compptr->MCU_height; + compptr->MCU_sample_width = compptr->MCU_width * compptr->DCT_scaled_size; + /* Figure number of non-dummy blocks in last MCU column & row */ + tmp = (int) (compptr->width_in_blocks % compptr->MCU_width); + if (tmp == 0) tmp = compptr->MCU_width; + compptr->last_col_width = tmp; + tmp = (int) (compptr->height_in_blocks % compptr->MCU_height); + if (tmp == 0) tmp = compptr->MCU_height; + compptr->last_row_height = tmp; + /* Prepare array describing MCU composition */ + mcublks = compptr->MCU_blocks; + if (cinfo->blocks_in_MCU + mcublks > D_MAX_BLOCKS_IN_MCU) + ERREXIT(cinfo, JERR_BAD_MCU_SIZE); + while (mcublks-- > 0) { + cinfo->MCU_membership[cinfo->blocks_in_MCU++] = ci; + } + } + + } +} + +/* + * Save away a copy of the Q-table referenced by each component present + * in the current scan, unless already saved during a prior scan. + * + * In a multiple-scan JPEG file, the encoder could assign different components + * the same Q-table slot number, but change table definitions between scans + * so that each component uses a different Q-table. (The IJG encoder is not + * currently capable of doing this, but other encoders might.) Since we want + * to be able to dequantize all the components at the end of the file, this + * means that we have to save away the table actually used for each component. + * We do this by copying the table at the start of the first scan containing + * the component. + * The JPEG spec prohibits the encoder from changing the contents of a Q-table + * slot between scans of a component using that slot. If the encoder does so + * anyway, this decoder will simply use the Q-table values that were current + * at the start of the first scan for the component. + * + * The decompressor output side looks only at the saved quant tables, + * not at the current Q-table slots. + */ + +LOCAL(void) +latch_quant_tables (j_decompress_ptr cinfo) +{ + int ci, qtblno; + jpeg_component_info *compptr; + JQUANT_TBL * qtbl; + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + /* No work if we already saved Q-table for this component */ + if (compptr->quant_table != NULL) + continue; + /* Make sure specified quantization table is present */ + qtblno = compptr->quant_tbl_no; + if (qtblno < 0 || qtblno >= NUM_QUANT_TBLS || + cinfo->quant_tbl_ptrs[qtblno] == NULL) + ERREXIT1(cinfo, JERR_NO_QUANT_TABLE, qtblno); + /* OK, save away the quantization table */ + qtbl = (JQUANT_TBL *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(JQUANT_TBL)); + MEMCOPY(qtbl, cinfo->quant_tbl_ptrs[qtblno], SIZEOF(JQUANT_TBL)); + compptr->quant_table = qtbl; + } +} + +/* + * Initialize the input modules to read a scan of compressed data. + * The first call to this is done by jdmaster.c after initializing + * the entire decompressor (during jpeg_start_decompress). + * Subsequent calls come from consume_markers, below. + */ + +METHODDEF(void) +start_input_pass2 (j_decompress_ptr cinfo) +{ + per_scan_setup2(cinfo); + latch_quant_tables(cinfo); + (*cinfo->entropy->start_pass) (cinfo); + (*cinfo->coef->start_input_pass) (cinfo); + cinfo->inputctl->consume_input = cinfo->coef->consume_data; +} + +/* + * Finish up after inputting a compressed-data scan. + * This is called by the coefficient controller after it's read all + * the expected data of the scan. + */ + +METHODDEF(void) +finish_input_pass (j_decompress_ptr cinfo) +{ + cinfo->inputctl->consume_input = consume_markers; +} + +/* + * Read JPEG markers before, between, or after compressed-data scans. + * Change state as necessary when a new scan is reached. + * Return value is JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI. + * + * The consume_input method pointer points either here or to the + * coefficient controller's consume_data routine, depending on whether + * we are reading a compressed data segment or inter-segment markers. + */ + +METHODDEF(int) +consume_markers (j_decompress_ptr cinfo) +{ + my_inputctl_ptr inputctl = (my_inputctl_ptr) cinfo->inputctl; + int val; + + if (inputctl->pub.eoi_reached) /* After hitting EOI, read no further */ + return JPEG_REACHED_EOI; + + val = (*cinfo->marker->read_markers) (cinfo); + + switch (val) { + case JPEG_REACHED_SOS: /* Found SOS */ + if (inputctl->inheaders) { /* 1st SOS */ + initial_setup2(cinfo); + inputctl->inheaders = FALSE; + /* Note: start_input_pass must be called by jdmaster.c + * before any more input can be consumed. jdapimin.c is + * responsible for enforcing this sequencing. + */ + } else { /* 2nd or later SOS marker */ + if (! inputctl->pub.has_multiple_scans) + ERREXIT(cinfo, JERR_EOI_EXPECTED); /* Oops, I wasn't expecting this! */ + start_input_pass2(cinfo); + } + break; + case JPEG_REACHED_EOI: /* Found EOI */ + inputctl->pub.eoi_reached = TRUE; + if (inputctl->inheaders) { /* Tables-only datastream, apparently */ + if (cinfo->marker->saw_SOF) + ERREXIT(cinfo, JERR_SOF_NO_SOS); + } else { + /* Prevent infinite loop in coef ctlr's decompress_data routine + * if user set output_scan_number larger than number of scans. + */ + if (cinfo->output_scan_number > cinfo->input_scan_number) + cinfo->output_scan_number = cinfo->input_scan_number; + } + break; + case JPEG_SUSPENDED: + break; + } + + return val; +} + +/* + * Reset state to begin a fresh datastream. + */ + +METHODDEF(void) +reset_input_controller (j_decompress_ptr cinfo) +{ + my_inputctl_ptr inputctl = (my_inputctl_ptr) cinfo->inputctl; + + inputctl->pub.consume_input = consume_markers; + inputctl->pub.has_multiple_scans = FALSE; /* "unknown" would be better */ + inputctl->pub.eoi_reached = FALSE; + inputctl->inheaders = TRUE; + /* Reset other modules */ + (*cinfo->err->reset_error_mgr) ((j_common_ptr) cinfo); + (*cinfo->marker->reset_marker_reader) (cinfo); + /* Reset progression state -- would be cleaner if entropy decoder did this */ + cinfo->coef_bits = NULL; +} + +/* + * Initialize the input controller module. + * This is called only once, when the decompression object is created. + */ + +GLOBAL(void) +jinit_input_controller (j_decompress_ptr cinfo) +{ + my_inputctl_ptr inputctl; + + /* Create subobject in permanent pool */ + inputctl = (my_inputctl_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + SIZEOF(my_input_controller)); + cinfo->inputctl = (struct jpeg_input_controller *) inputctl; + /* Initialize method pointers */ + inputctl->pub.consume_input = consume_markers; + inputctl->pub.reset_input_controller = reset_input_controller; + inputctl->pub.start_input_pass = start_input_pass2; + inputctl->pub.finish_input_pass = finish_input_pass; + /* Initialize state: can't use reset_input_controller since we don't + * want to try to reset other modules yet. + */ + inputctl->pub.has_multiple_scans = FALSE; /* "unknown" would be better */ + inputctl->pub.eoi_reached = FALSE; + inputctl->inheaders = TRUE; +} +/********* End of inlined file: jdinput.c *********/ + +/********* Start of inlined file: jdmainct.c *********/ +#define JPEG_INTERNALS + +/* + * In the current system design, the main buffer need never be a full-image + * buffer; any full-height buffers will be found inside the coefficient or + * postprocessing controllers. Nonetheless, the main controller is not + * trivial. Its responsibility is to provide context rows for upsampling/ + * rescaling, and doing this in an efficient fashion is a bit tricky. + * + * Postprocessor input data is counted in "row groups". A row group + * is defined to be (v_samp_factor * DCT_scaled_size / min_DCT_scaled_size) + * sample rows of each component. (We require DCT_scaled_size values to be + * chosen such that these numbers are integers. In practice DCT_scaled_size + * values will likely be powers of two, so we actually have the stronger + * condition that DCT_scaled_size / min_DCT_scaled_size is an integer.) + * Upsampling will typically produce max_v_samp_factor pixel rows from each + * row group (times any additional scale factor that the upsampler is + * applying). + * + * The coefficient controller will deliver data to us one iMCU row at a time; + * each iMCU row contains v_samp_factor * DCT_scaled_size sample rows, or + * exactly min_DCT_scaled_size row groups. (This amount of data corresponds + * to one row of MCUs when the image is fully interleaved.) Note that the + * number of sample rows varies across components, but the number of row + * groups does not. Some garbage sample rows may be included in the last iMCU + * row at the bottom of the image. + * + * Depending on the vertical scaling algorithm used, the upsampler may need + * access to the sample row(s) above and below its current input row group. + * The upsampler is required to set need_context_rows TRUE at global selection + * time if so. When need_context_rows is FALSE, this controller can simply + * obtain one iMCU row at a time from the coefficient controller and dole it + * out as row groups to the postprocessor. + * + * When need_context_rows is TRUE, this controller guarantees that the buffer + * passed to postprocessing contains at least one row group's worth of samples + * above and below the row group(s) being processed. Note that the context + * rows "above" the first passed row group appear at negative row offsets in + * the passed buffer. At the top and bottom of the image, the required + * context rows are manufactured by duplicating the first or last real sample + * row; this avoids having special cases in the upsampling inner loops. + * + * The amount of context is fixed at one row group just because that's a + * convenient number for this controller to work with. The existing + * upsamplers really only need one sample row of context. An upsampler + * supporting arbitrary output rescaling might wish for more than one row + * group of context when shrinking the image; tough, we don't handle that. + * (This is justified by the assumption that downsizing will be handled mostly + * by adjusting the DCT_scaled_size values, so that the actual scale factor at + * the upsample step needn't be much less than one.) + * + * To provide the desired context, we have to retain the last two row groups + * of one iMCU row while reading in the next iMCU row. (The last row group + * can't be processed until we have another row group for its below-context, + * and so we have to save the next-to-last group too for its above-context.) + * We could do this most simply by copying data around in our buffer, but + * that'd be very slow. We can avoid copying any data by creating a rather + * strange pointer structure. Here's how it works. We allocate a workspace + * consisting of M+2 row groups (where M = min_DCT_scaled_size is the number + * of row groups per iMCU row). We create two sets of redundant pointers to + * the workspace. Labeling the physical row groups 0 to M+1, the synthesized + * pointer lists look like this: + * M+1 M-1 + * master pointer --> 0 master pointer --> 0 + * 1 1 + * ... ... + * M-3 M-3 + * M-2 M + * M-1 M+1 + * M M-2 + * M+1 M-1 + * 0 0 + * We read alternate iMCU rows using each master pointer; thus the last two + * row groups of the previous iMCU row remain un-overwritten in the workspace. + * The pointer lists are set up so that the required context rows appear to + * be adjacent to the proper places when we pass the pointer lists to the + * upsampler. + * + * The above pictures describe the normal state of the pointer lists. + * At top and bottom of the image, we diddle the pointer lists to duplicate + * the first or last sample row as necessary (this is cheaper than copying + * sample rows around). + * + * This scheme breaks down if M < 2, ie, min_DCT_scaled_size is 1. In that + * situation each iMCU row provides only one row group so the buffering logic + * must be different (eg, we must read two iMCU rows before we can emit the + * first row group). For now, we simply do not support providing context + * rows when min_DCT_scaled_size is 1. That combination seems unlikely to + * be worth providing --- if someone wants a 1/8th-size preview, they probably + * want it quick and dirty, so a context-free upsampler is sufficient. + */ + +/* Private buffer controller object */ + +typedef struct { + struct jpeg_d_main_controller pub; /* public fields */ + + /* Pointer to allocated workspace (M or M+2 row groups). */ + JSAMPARRAY buffer[MAX_COMPONENTS]; + + boolean buffer_full; /* Have we gotten an iMCU row from decoder? */ + JDIMENSION rowgroup_ctr; /* counts row groups output to postprocessor */ + + /* Remaining fields are only used in the context case. */ + + /* These are the master pointers to the funny-order pointer lists. */ + JSAMPIMAGE xbuffer[2]; /* pointers to weird pointer lists */ + + int whichptr; /* indicates which pointer set is now in use */ + int context_state; /* process_data state machine status */ + JDIMENSION rowgroups_avail; /* row groups available to postprocessor */ + JDIMENSION iMCU_row_ctr; /* counts iMCU rows to detect image top/bot */ +} my_main_controller4; + +typedef my_main_controller4 * my_main_ptr4; + +/* context_state values: */ +#define CTX_PREPARE_FOR_IMCU 0 /* need to prepare for MCU row */ +#define CTX_PROCESS_IMCU 1 /* feeding iMCU to postprocessor */ +#define CTX_POSTPONED_ROW 2 /* feeding postponed row group */ + +/* Forward declarations */ +METHODDEF(void) process_data_simple_main2 + JPP((j_decompress_ptr cinfo, JSAMPARRAY output_buf, + JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail)); +METHODDEF(void) process_data_context_main + JPP((j_decompress_ptr cinfo, JSAMPARRAY output_buf, + JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail)); +#ifdef QUANT_2PASS_SUPPORTED +METHODDEF(void) process_data_crank_post + JPP((j_decompress_ptr cinfo, JSAMPARRAY output_buf, + JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail)); +#endif + +LOCAL(void) +alloc_funny_pointers (j_decompress_ptr cinfo) +/* Allocate space for the funny pointer lists. + * This is done only once, not once per pass. + */ +{ + my_main_ptr4 main_ = (my_main_ptr4) cinfo->main; + int ci, rgroup; + int M = cinfo->min_DCT_scaled_size; + jpeg_component_info *compptr; + JSAMPARRAY xbuf; + + /* Get top-level space for component array pointers. + * We alloc both arrays with one call to save a few cycles. + */ + main_->xbuffer[0] = (JSAMPIMAGE) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + cinfo->num_components * 2 * SIZEOF(JSAMPARRAY)); + main_->xbuffer[1] = main_->xbuffer[0] + cinfo->num_components; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + cinfo->min_DCT_scaled_size; /* height of a row group of component */ + /* Get space for pointer lists --- M+4 row groups in each list. + * We alloc both pointer lists with one call to save a few cycles. + */ + xbuf = (JSAMPARRAY) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + 2 * (rgroup * (M + 4)) * SIZEOF(JSAMPROW)); + xbuf += rgroup; /* want one row group at negative offsets */ + main_->xbuffer[0][ci] = xbuf; + xbuf += rgroup * (M + 4); + main_->xbuffer[1][ci] = xbuf; + } +} + +LOCAL(void) +make_funny_pointers (j_decompress_ptr cinfo) +/* Create the funny pointer lists discussed in the comments above. + * The actual workspace is already allocated (in main->buffer), + * and the space for the pointer lists is allocated too. + * This routine just fills in the curiously ordered lists. + * This will be repeated at the beginning of each pass. + */ +{ + my_main_ptr4 main_ = (my_main_ptr4) cinfo->main; + int ci, i, rgroup; + int M = cinfo->min_DCT_scaled_size; + jpeg_component_info *compptr; + JSAMPARRAY buf, xbuf0, xbuf1; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + cinfo->min_DCT_scaled_size; /* height of a row group of component */ + xbuf0 = main_->xbuffer[0][ci]; + xbuf1 = main_->xbuffer[1][ci]; + /* First copy the workspace pointers as-is */ + buf = main_->buffer[ci]; + for (i = 0; i < rgroup * (M + 2); i++) { + xbuf0[i] = xbuf1[i] = buf[i]; + } + /* In the second list, put the last four row groups in swapped order */ + for (i = 0; i < rgroup * 2; i++) { + xbuf1[rgroup*(M-2) + i] = buf[rgroup*M + i]; + xbuf1[rgroup*M + i] = buf[rgroup*(M-2) + i]; + } + /* The wraparound pointers at top and bottom will be filled later + * (see set_wraparound_pointers, below). Initially we want the "above" + * pointers to duplicate the first actual data line. This only needs + * to happen in xbuffer[0]. + */ + for (i = 0; i < rgroup; i++) { + xbuf0[i - rgroup] = xbuf0[0]; + } + } +} + +LOCAL(void) +set_wraparound_pointers (j_decompress_ptr cinfo) +/* Set up the "wraparound" pointers at top and bottom of the pointer lists. + * This changes the pointer list state from top-of-image to the normal state. + */ +{ + my_main_ptr4 main_ = (my_main_ptr4) cinfo->main; + int ci, i, rgroup; + int M = cinfo->min_DCT_scaled_size; + jpeg_component_info *compptr; + JSAMPARRAY xbuf0, xbuf1; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + cinfo->min_DCT_scaled_size; /* height of a row group of component */ + xbuf0 = main_->xbuffer[0][ci]; + xbuf1 = main_->xbuffer[1][ci]; + for (i = 0; i < rgroup; i++) { + xbuf0[i - rgroup] = xbuf0[rgroup*(M+1) + i]; + xbuf1[i - rgroup] = xbuf1[rgroup*(M+1) + i]; + xbuf0[rgroup*(M+2) + i] = xbuf0[i]; + xbuf1[rgroup*(M+2) + i] = xbuf1[i]; + } + } +} + +LOCAL(void) +set_bottom_pointers (j_decompress_ptr cinfo) +/* Change the pointer lists to duplicate the last sample row at the bottom + * of the image. whichptr indicates which xbuffer holds the final iMCU row. + * Also sets rowgroups_avail to indicate number of nondummy row groups in row. + */ +{ + my_main_ptr4 main_ = (my_main_ptr4) cinfo->main; + int ci, i, rgroup, iMCUheight, rows_left; + jpeg_component_info *compptr; + JSAMPARRAY xbuf; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Count sample rows in one iMCU row and in one row group */ + iMCUheight = compptr->v_samp_factor * compptr->DCT_scaled_size; + rgroup = iMCUheight / cinfo->min_DCT_scaled_size; + /* Count nondummy sample rows remaining for this component */ + rows_left = (int) (compptr->downsampled_height % (JDIMENSION) iMCUheight); + if (rows_left == 0) rows_left = iMCUheight; + /* Count nondummy row groups. Should get same answer for each component, + * so we need only do it once. + */ + if (ci == 0) { + main_->rowgroups_avail = (JDIMENSION) ((rows_left-1) / rgroup + 1); + } + /* Duplicate the last real sample row rgroup*2 times; this pads out the + * last partial rowgroup and ensures at least one full rowgroup of context. + */ + xbuf = main_->xbuffer[main_->whichptr][ci]; + for (i = 0; i < rgroup * 2; i++) { + xbuf[rows_left + i] = xbuf[rows_left-1]; + } + } +} + +/* + * Initialize for a processing pass. + */ + +METHODDEF(void) +start_pass_main2 (j_decompress_ptr cinfo, J_BUF_MODE pass_mode) +{ + my_main_ptr4 main_ = (my_main_ptr4) cinfo->main; + + switch (pass_mode) { + case JBUF_PASS_THRU: + if (cinfo->upsample->need_context_rows) { + main_->pub.process_data = process_data_context_main; + make_funny_pointers(cinfo); /* Create the xbuffer[] lists */ + main_->whichptr = 0; /* Read first iMCU row into xbuffer[0] */ + main_->context_state = CTX_PREPARE_FOR_IMCU; + main_->iMCU_row_ctr = 0; + } else { + /* Simple case with no context needed */ + main_->pub.process_data = process_data_simple_main2; + } + main_->buffer_full = FALSE; /* Mark buffer empty */ + main_->rowgroup_ctr = 0; + break; +#ifdef QUANT_2PASS_SUPPORTED + case JBUF_CRANK_DEST: + /* For last pass of 2-pass quantization, just crank the postprocessor */ + main_->pub.process_data = process_data_crank_post; + break; +#endif + default: + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + break; + } +} + +/* + * Process some data. + * This handles the simple case where no context is required. + */ + +METHODDEF(void) +process_data_simple_main2 (j_decompress_ptr cinfo, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +{ + my_main_ptr4 main_ = (my_main_ptr4) cinfo->main; + JDIMENSION rowgroups_avail; + + /* Read input data if we haven't filled the main buffer yet */ + if (! main_->buffer_full) { + if (! (*cinfo->coef->decompress_data) (cinfo, main_->buffer)) + return; /* suspension forced, can do nothing more */ + main_->buffer_full = TRUE; /* OK, we have an iMCU row to work with */ + } + + /* There are always min_DCT_scaled_size row groups in an iMCU row. */ + rowgroups_avail = (JDIMENSION) cinfo->min_DCT_scaled_size; + /* Note: at the bottom of the image, we may pass extra garbage row groups + * to the postprocessor. The postprocessor has to check for bottom + * of image anyway (at row resolution), so no point in us doing it too. + */ + + /* Feed the postprocessor */ + (*cinfo->post->post_process_data) (cinfo, main_->buffer, + &main_->rowgroup_ctr, rowgroups_avail, + output_buf, out_row_ctr, out_rows_avail); + + /* Has postprocessor consumed all the data yet? If so, mark buffer empty */ + if (main_->rowgroup_ctr >= rowgroups_avail) { + main_->buffer_full = FALSE; + main_->rowgroup_ctr = 0; + } +} + +/* + * Process some data. + * This handles the case where context rows must be provided. + */ + +METHODDEF(void) +process_data_context_main (j_decompress_ptr cinfo, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +{ + my_main_ptr4 main_ = (my_main_ptr4) cinfo->main; + + /* Read input data if we haven't filled the main buffer yet */ + if (! main_->buffer_full) { + if (! (*cinfo->coef->decompress_data) (cinfo, + main_->xbuffer[main_->whichptr])) + return; /* suspension forced, can do nothing more */ + main_->buffer_full = TRUE; /* OK, we have an iMCU row to work with */ + main_->iMCU_row_ctr++; /* count rows received */ + } + + /* Postprocessor typically will not swallow all the input data it is handed + * in one call (due to filling the output buffer first). Must be prepared + * to exit and restart. This switch lets us keep track of how far we got. + * Note that each case falls through to the next on successful completion. + */ + switch (main_->context_state) { + case CTX_POSTPONED_ROW: + /* Call postprocessor using previously set pointers for postponed row */ + (*cinfo->post->post_process_data) (cinfo, main_->xbuffer[main_->whichptr], + &main_->rowgroup_ctr, main_->rowgroups_avail, + output_buf, out_row_ctr, out_rows_avail); + if (main_->rowgroup_ctr < main_->rowgroups_avail) + return; /* Need to suspend */ + main_->context_state = CTX_PREPARE_FOR_IMCU; + if (*out_row_ctr >= out_rows_avail) + return; /* Postprocessor exactly filled output buf */ + /*FALLTHROUGH*/ + case CTX_PREPARE_FOR_IMCU: + /* Prepare to process first M-1 row groups of this iMCU row */ + main_->rowgroup_ctr = 0; + main_->rowgroups_avail = (JDIMENSION) (cinfo->min_DCT_scaled_size - 1); + /* Check for bottom of image: if so, tweak pointers to "duplicate" + * the last sample row, and adjust rowgroups_avail to ignore padding rows. + */ + if (main_->iMCU_row_ctr == cinfo->total_iMCU_rows) + set_bottom_pointers(cinfo); + main_->context_state = CTX_PROCESS_IMCU; + /*FALLTHROUGH*/ + case CTX_PROCESS_IMCU: + /* Call postprocessor using previously set pointers */ + (*cinfo->post->post_process_data) (cinfo, main_->xbuffer[main_->whichptr], + &main_->rowgroup_ctr, main_->rowgroups_avail, + output_buf, out_row_ctr, out_rows_avail); + if (main_->rowgroup_ctr < main_->rowgroups_avail) + return; /* Need to suspend */ + /* After the first iMCU, change wraparound pointers to normal state */ + if (main_->iMCU_row_ctr == 1) + set_wraparound_pointers(cinfo); + /* Prepare to load new iMCU row using other xbuffer list */ + main_->whichptr ^= 1; /* 0=>1 or 1=>0 */ + main_->buffer_full = FALSE; + /* Still need to process last row group of this iMCU row, */ + /* which is saved at index M+1 of the other xbuffer */ + main_->rowgroup_ctr = (JDIMENSION) (cinfo->min_DCT_scaled_size + 1); + main_->rowgroups_avail = (JDIMENSION) (cinfo->min_DCT_scaled_size + 2); + main_->context_state = CTX_POSTPONED_ROW; + } +} + +/* + * Process some data. + * Final pass of two-pass quantization: just call the postprocessor. + * Source data will be the postprocessor controller's internal buffer. + */ + +#ifdef QUANT_2PASS_SUPPORTED + +METHODDEF(void) +process_data_crank_post (j_decompress_ptr cinfo, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +{ + (*cinfo->post->post_process_data) (cinfo, (JSAMPIMAGE) NULL, + (JDIMENSION *) NULL, (JDIMENSION) 0, + output_buf, out_row_ctr, out_rows_avail); +} + +#endif /* QUANT_2PASS_SUPPORTED */ + +/* + * Initialize main buffer controller. + */ + +GLOBAL(void) +jinit_d_main_controller (j_decompress_ptr cinfo, boolean need_full_buffer) +{ + my_main_ptr4 main_; + int ci, rgroup, ngroups; + jpeg_component_info *compptr; + + main_ = (my_main_ptr4) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_main_controller4)); + cinfo->main = (struct jpeg_d_main_controller *) main_; + main_->pub.start_pass = start_pass_main2; + + if (need_full_buffer) /* shouldn't happen */ + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + + /* Allocate the workspace. + * ngroups is the number of row groups we need. + */ + if (cinfo->upsample->need_context_rows) { + if (cinfo->min_DCT_scaled_size < 2) /* unsupported, see comments above */ + ERREXIT(cinfo, JERR_NOTIMPL); + alloc_funny_pointers(cinfo); /* Alloc space for xbuffer[] lists */ + ngroups = cinfo->min_DCT_scaled_size + 2; + } else { + ngroups = cinfo->min_DCT_scaled_size; + } + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + cinfo->min_DCT_scaled_size; /* height of a row group of component */ + main_->buffer[ci] = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + compptr->width_in_blocks * compptr->DCT_scaled_size, + (JDIMENSION) (rgroup * ngroups)); + } +} +/********* End of inlined file: jdmainct.c *********/ + +/********* Start of inlined file: jdmarker.c *********/ +#define JPEG_INTERNALS + +/* Private state */ + +typedef struct { + struct jpeg_marker_reader pub; /* public fields */ + + /* Application-overridable marker processing methods */ + jpeg_marker_parser_method process_COM; + jpeg_marker_parser_method process_APPn[16]; + + /* Limit on marker data length to save for each marker type */ + unsigned int length_limit_COM; + unsigned int length_limit_APPn[16]; + + /* Status of COM/APPn marker saving */ + jpeg_saved_marker_ptr cur_marker; /* NULL if not processing a marker */ + unsigned int bytes_read; /* data bytes read so far in marker */ + /* Note: cur_marker is not linked into marker_list until it's all read. */ +} my_marker_reader; + +typedef my_marker_reader * my_marker_ptr2; + +/* + * Macros for fetching data from the data source module. + * + * At all times, cinfo->src->next_input_byte and ->bytes_in_buffer reflect + * the current restart point; we update them only when we have reached a + * suitable place to restart if a suspension occurs. + */ + +/* Declare and initialize local copies of input pointer/count */ +#define INPUT_VARS(cinfo) \ + struct jpeg_source_mgr * datasrc = (cinfo)->src; \ + const JOCTET * next_input_byte = datasrc->next_input_byte; \ + size_t bytes_in_buffer = datasrc->bytes_in_buffer + +/* Unload the local copies --- do this only at a restart boundary */ +#define INPUT_SYNC(cinfo) \ + ( datasrc->next_input_byte = next_input_byte, \ + datasrc->bytes_in_buffer = bytes_in_buffer ) + +/* Reload the local copies --- used only in MAKE_BYTE_AVAIL */ +#define INPUT_RELOAD(cinfo) \ + ( next_input_byte = datasrc->next_input_byte, \ + bytes_in_buffer = datasrc->bytes_in_buffer ) + +/* Internal macro for INPUT_BYTE and INPUT_2BYTES: make a byte available. + * Note we do *not* do INPUT_SYNC before calling fill_input_buffer, + * but we must reload the local copies after a successful fill. + */ +#define MAKE_BYTE_AVAIL(cinfo,action) \ + if (bytes_in_buffer == 0) { \ + if (! (*datasrc->fill_input_buffer) (cinfo)) \ + { action; } \ + INPUT_RELOAD(cinfo); \ + } + +/* Read a byte into variable V. + * If must suspend, take the specified action (typically "return FALSE"). + */ +#define INPUT_BYTE(cinfo,V,action) \ + MAKESTMT( MAKE_BYTE_AVAIL(cinfo,action); \ + bytes_in_buffer--; \ + V = GETJOCTET(*next_input_byte++); ) + +/* As above, but read two bytes interpreted as an unsigned 16-bit integer. + * V should be declared unsigned int or perhaps INT32. + */ +#define INPUT_2BYTES(cinfo,V,action) \ + MAKESTMT( MAKE_BYTE_AVAIL(cinfo,action); \ + bytes_in_buffer--; \ + V = ((unsigned int) GETJOCTET(*next_input_byte++)) << 8; \ + MAKE_BYTE_AVAIL(cinfo,action); \ + bytes_in_buffer--; \ + V += GETJOCTET(*next_input_byte++); ) + +/* + * Routines to process JPEG markers. + * + * Entry condition: JPEG marker itself has been read and its code saved + * in cinfo->unread_marker; input restart point is just after the marker. + * + * Exit: if return TRUE, have read and processed any parameters, and have + * updated the restart point to point after the parameters. + * If return FALSE, was forced to suspend before reaching end of + * marker parameters; restart point has not been moved. Same routine + * will be called again after application supplies more input data. + * + * This approach to suspension assumes that all of a marker's parameters + * can fit into a single input bufferload. This should hold for "normal" + * markers. Some COM/APPn markers might have large parameter segments + * that might not fit. If we are simply dropping such a marker, we use + * skip_input_data to get past it, and thereby put the problem on the + * source manager's shoulders. If we are saving the marker's contents + * into memory, we use a slightly different convention: when forced to + * suspend, the marker processor updates the restart point to the end of + * what it's consumed (ie, the end of the buffer) before returning FALSE. + * On resumption, cinfo->unread_marker still contains the marker code, + * but the data source will point to the next chunk of marker data. + * The marker processor must retain internal state to deal with this. + * + * Note that we don't bother to avoid duplicate trace messages if a + * suspension occurs within marker parameters. Other side effects + * require more care. + */ + +LOCAL(boolean) +get_soi (j_decompress_ptr cinfo) +/* Process an SOI marker */ +{ + int i; + + TRACEMS(cinfo, 1, JTRC_SOI); + + if (cinfo->marker->saw_SOI) + ERREXIT(cinfo, JERR_SOI_DUPLICATE); + + /* Reset all parameters that are defined to be reset by SOI */ + + for (i = 0; i < NUM_ARITH_TBLS; i++) { + cinfo->arith_dc_L[i] = 0; + cinfo->arith_dc_U[i] = 1; + cinfo->arith_ac_K[i] = 5; + } + cinfo->restart_interval = 0; + + /* Set initial assumptions for colorspace etc */ + + cinfo->jpeg_color_space = JCS_UNKNOWN; + cinfo->CCIR601_sampling = FALSE; /* Assume non-CCIR sampling??? */ + + cinfo->saw_JFIF_marker = FALSE; + cinfo->JFIF_major_version = 1; /* set default JFIF APP0 values */ + cinfo->JFIF_minor_version = 1; + cinfo->density_unit = 0; + cinfo->X_density = 1; + cinfo->Y_density = 1; + cinfo->saw_Adobe_marker = FALSE; + cinfo->Adobe_transform = 0; + + cinfo->marker->saw_SOI = TRUE; + + return TRUE; +} + +LOCAL(boolean) +get_sof (j_decompress_ptr cinfo, boolean is_prog, boolean is_arith) +/* Process a SOFn marker */ +{ + INT32 length; + int c, ci; + jpeg_component_info * compptr; + INPUT_VARS(cinfo); + + cinfo->progressive_mode = is_prog; + cinfo->arith_code = is_arith; + + INPUT_2BYTES(cinfo, length, return FALSE); + + INPUT_BYTE(cinfo, cinfo->data_precision, return FALSE); + INPUT_2BYTES(cinfo, cinfo->image_height, return FALSE); + INPUT_2BYTES(cinfo, cinfo->image_width, return FALSE); + INPUT_BYTE(cinfo, cinfo->num_components, return FALSE); + + length -= 8; + + TRACEMS4(cinfo, 1, JTRC_SOF, cinfo->unread_marker, + (int) cinfo->image_width, (int) cinfo->image_height, + cinfo->num_components); + + if (cinfo->marker->saw_SOF) + ERREXIT(cinfo, JERR_SOF_DUPLICATE); + + /* We don't support files in which the image height is initially specified */ + /* as 0 and is later redefined by DNL. As long as we have to check that, */ + /* might as well have a general sanity check. */ + if (cinfo->image_height <= 0 || cinfo->image_width <= 0 + || cinfo->num_components <= 0) + ERREXIT(cinfo, JERR_EMPTY_IMAGE); + + if (length != (cinfo->num_components * 3)) + ERREXIT(cinfo, JERR_BAD_LENGTH); + + if (cinfo->comp_info == NULL) /* do only once, even if suspend */ + cinfo->comp_info = (jpeg_component_info *) (*cinfo->mem->alloc_small) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + cinfo->num_components * SIZEOF(jpeg_component_info)); + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + compptr->component_index = ci; + INPUT_BYTE(cinfo, compptr->component_id, return FALSE); + INPUT_BYTE(cinfo, c, return FALSE); + compptr->h_samp_factor = (c >> 4) & 15; + compptr->v_samp_factor = (c ) & 15; + INPUT_BYTE(cinfo, compptr->quant_tbl_no, return FALSE); + + TRACEMS4(cinfo, 1, JTRC_SOF_COMPONENT, + compptr->component_id, compptr->h_samp_factor, + compptr->v_samp_factor, compptr->quant_tbl_no); + } + + cinfo->marker->saw_SOF = TRUE; + + INPUT_SYNC(cinfo); + return TRUE; +} + +LOCAL(boolean) +get_sos (j_decompress_ptr cinfo) +/* Process a SOS marker */ +{ + INT32 length; + int i, ci, n, c, cc; + jpeg_component_info * compptr; + INPUT_VARS(cinfo); + + if (! cinfo->marker->saw_SOF) + ERREXIT(cinfo, JERR_SOS_NO_SOF); + + INPUT_2BYTES(cinfo, length, return FALSE); + + INPUT_BYTE(cinfo, n, return FALSE); /* Number of components */ + + TRACEMS1(cinfo, 1, JTRC_SOS, n); + + if (length != (n * 2 + 6) || n < 1 || n > MAX_COMPS_IN_SCAN) + ERREXIT(cinfo, JERR_BAD_LENGTH); + + cinfo->comps_in_scan = n; + + /* Collect the component-spec parameters */ + + for (i = 0; i < n; i++) { + INPUT_BYTE(cinfo, cc, return FALSE); + INPUT_BYTE(cinfo, c, return FALSE); + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + if (cc == compptr->component_id) + goto id_found; + } + + ERREXIT1(cinfo, JERR_BAD_COMPONENT_ID, cc); + + id_found: + + cinfo->cur_comp_info[i] = compptr; + compptr->dc_tbl_no = (c >> 4) & 15; + compptr->ac_tbl_no = (c ) & 15; + + TRACEMS3(cinfo, 1, JTRC_SOS_COMPONENT, cc, + compptr->dc_tbl_no, compptr->ac_tbl_no); + } + + /* Collect the additional scan parameters Ss, Se, Ah/Al. */ + INPUT_BYTE(cinfo, c, return FALSE); + cinfo->Ss = c; + INPUT_BYTE(cinfo, c, return FALSE); + cinfo->Se = c; + INPUT_BYTE(cinfo, c, return FALSE); + cinfo->Ah = (c >> 4) & 15; + cinfo->Al = (c ) & 15; + + TRACEMS4(cinfo, 1, JTRC_SOS_PARAMS, cinfo->Ss, cinfo->Se, + cinfo->Ah, cinfo->Al); + + /* Prepare to scan data & restart markers */ + cinfo->marker->next_restart_num = 0; + + /* Count another SOS marker */ + cinfo->input_scan_number++; + + INPUT_SYNC(cinfo); + return TRUE; +} + +#ifdef D_ARITH_CODING_SUPPORTED + +LOCAL(boolean) +get_dac (j_decompress_ptr cinfo) +/* Process a DAC marker */ +{ + INT32 length; + int index, val; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + length -= 2; + + while (length > 0) { + INPUT_BYTE(cinfo, index, return FALSE); + INPUT_BYTE(cinfo, val, return FALSE); + + length -= 2; + + TRACEMS2(cinfo, 1, JTRC_DAC, index, val); + + if (index < 0 || index >= (2*NUM_ARITH_TBLS)) + ERREXIT1(cinfo, JERR_DAC_INDEX, index); + + if (index >= NUM_ARITH_TBLS) { /* define AC table */ + cinfo->arith_ac_K[index-NUM_ARITH_TBLS] = (UINT8) val; + } else { /* define DC table */ + cinfo->arith_dc_L[index] = (UINT8) (val & 0x0F); + cinfo->arith_dc_U[index] = (UINT8) (val >> 4); + if (cinfo->arith_dc_L[index] > cinfo->arith_dc_U[index]) + ERREXIT1(cinfo, JERR_DAC_VALUE, val); + } + } + + if (length != 0) + ERREXIT(cinfo, JERR_BAD_LENGTH); + + INPUT_SYNC(cinfo); + return TRUE; +} + +#else /* ! D_ARITH_CODING_SUPPORTED */ + +#define get_dac(cinfo) skip_variable(cinfo) + +#endif /* D_ARITH_CODING_SUPPORTED */ + +LOCAL(boolean) +get_dht (j_decompress_ptr cinfo) +/* Process a DHT marker */ +{ + INT32 length; + UINT8 bits[17]; + UINT8 huffval[256]; + int i, index, count; + JHUFF_TBL **htblptr; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + length -= 2; + + while (length > 16) { + INPUT_BYTE(cinfo, index, return FALSE); + + TRACEMS1(cinfo, 1, JTRC_DHT, index); + + bits[0] = 0; + count = 0; + for (i = 1; i <= 16; i++) { + INPUT_BYTE(cinfo, bits[i], return FALSE); + count += bits[i]; + } + + length -= 1 + 16; + + TRACEMS8(cinfo, 2, JTRC_HUFFBITS, + bits[1], bits[2], bits[3], bits[4], + bits[5], bits[6], bits[7], bits[8]); + TRACEMS8(cinfo, 2, JTRC_HUFFBITS, + bits[9], bits[10], bits[11], bits[12], + bits[13], bits[14], bits[15], bits[16]); + + /* Here we just do minimal validation of the counts to avoid walking + * off the end of our table space. jdhuff.c will check more carefully. + */ + if (count > 256 || ((INT32) count) > length) + ERREXIT(cinfo, JERR_BAD_HUFF_TABLE); + + for (i = 0; i < count; i++) + INPUT_BYTE(cinfo, huffval[i], return FALSE); + + length -= count; + + if (index & 0x10) { /* AC table definition */ + index -= 0x10; + htblptr = &cinfo->ac_huff_tbl_ptrs[index]; + } else { /* DC table definition */ + htblptr = &cinfo->dc_huff_tbl_ptrs[index]; + } + + if (index < 0 || index >= NUM_HUFF_TBLS) + ERREXIT1(cinfo, JERR_DHT_INDEX, index); + + if (*htblptr == NULL) + *htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo); + + MEMCOPY((*htblptr)->bits, bits, SIZEOF((*htblptr)->bits)); + MEMCOPY((*htblptr)->huffval, huffval, SIZEOF((*htblptr)->huffval)); + } + + if (length != 0) + ERREXIT(cinfo, JERR_BAD_LENGTH); + + INPUT_SYNC(cinfo); + return TRUE; +} + +LOCAL(boolean) +get_dqt (j_decompress_ptr cinfo) +/* Process a DQT marker */ +{ + INT32 length; + int n, i, prec; + unsigned int tmp; + JQUANT_TBL *quant_ptr; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + length -= 2; + + while (length > 0) { + INPUT_BYTE(cinfo, n, return FALSE); + prec = n >> 4; + n &= 0x0F; + + TRACEMS2(cinfo, 1, JTRC_DQT, n, prec); + + if (n >= NUM_QUANT_TBLS) + ERREXIT1(cinfo, JERR_DQT_INDEX, n); + + if (cinfo->quant_tbl_ptrs[n] == NULL) + cinfo->quant_tbl_ptrs[n] = jpeg_alloc_quant_table((j_common_ptr) cinfo); + quant_ptr = cinfo->quant_tbl_ptrs[n]; + + for (i = 0; i < DCTSIZE2; i++) { + if (prec) + INPUT_2BYTES(cinfo, tmp, return FALSE); + else + INPUT_BYTE(cinfo, tmp, return FALSE); + /* We convert the zigzag-order table to natural array order. */ + quant_ptr->quantval[jpeg_natural_order[i]] = (UINT16) tmp; + } + + if (cinfo->err->trace_level >= 2) { + for (i = 0; i < DCTSIZE2; i += 8) { + TRACEMS8(cinfo, 2, JTRC_QUANTVALS, + quant_ptr->quantval[i], quant_ptr->quantval[i+1], + quant_ptr->quantval[i+2], quant_ptr->quantval[i+3], + quant_ptr->quantval[i+4], quant_ptr->quantval[i+5], + quant_ptr->quantval[i+6], quant_ptr->quantval[i+7]); + } + } + + length -= DCTSIZE2+1; + if (prec) length -= DCTSIZE2; + } + + if (length != 0) + ERREXIT(cinfo, JERR_BAD_LENGTH); + + INPUT_SYNC(cinfo); + return TRUE; +} + +LOCAL(boolean) +get_dri (j_decompress_ptr cinfo) +/* Process a DRI marker */ +{ + INT32 length; + unsigned int tmp; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + + if (length != 4) + ERREXIT(cinfo, JERR_BAD_LENGTH); + + INPUT_2BYTES(cinfo, tmp, return FALSE); + + TRACEMS1(cinfo, 1, JTRC_DRI, tmp); + + cinfo->restart_interval = tmp; + + INPUT_SYNC(cinfo); + return TRUE; +} + +/* + * Routines for processing APPn and COM markers. + * These are either saved in memory or discarded, per application request. + * APP0 and APP14 are specially checked to see if they are + * JFIF and Adobe markers, respectively. + */ + +#define APP0_DATA_LEN 14 /* Length of interesting data in APP0 */ +#define APP14_DATA_LEN 12 /* Length of interesting data in APP14 */ +#define APPN_DATA_LEN 14 /* Must be the largest of the above!! */ + +LOCAL(void) +examine_app0 (j_decompress_ptr cinfo, JOCTET FAR * data, + unsigned int datalen, INT32 remaining) +/* Examine first few bytes from an APP0. + * Take appropriate action if it is a JFIF marker. + * datalen is # of bytes at data[], remaining is length of rest of marker data. + */ +{ + INT32 totallen = (INT32) datalen + remaining; + + if (datalen >= APP0_DATA_LEN && + GETJOCTET(data[0]) == 0x4A && + GETJOCTET(data[1]) == 0x46 && + GETJOCTET(data[2]) == 0x49 && + GETJOCTET(data[3]) == 0x46 && + GETJOCTET(data[4]) == 0) { + /* Found JFIF APP0 marker: save info */ + cinfo->saw_JFIF_marker = TRUE; + cinfo->JFIF_major_version = GETJOCTET(data[5]); + cinfo->JFIF_minor_version = GETJOCTET(data[6]); + cinfo->density_unit = GETJOCTET(data[7]); + cinfo->X_density = (GETJOCTET(data[8]) << 8) + GETJOCTET(data[9]); + cinfo->Y_density = (GETJOCTET(data[10]) << 8) + GETJOCTET(data[11]); + /* Check version. + * Major version must be 1, anything else signals an incompatible change. + * (We used to treat this as an error, but now it's a nonfatal warning, + * because some bozo at Hijaak couldn't read the spec.) + * Minor version should be 0..2, but process anyway if newer. + */ + if (cinfo->JFIF_major_version != 1) + WARNMS2(cinfo, JWRN_JFIF_MAJOR, + cinfo->JFIF_major_version, cinfo->JFIF_minor_version); + /* Generate trace messages */ + TRACEMS5(cinfo, 1, JTRC_JFIF, + cinfo->JFIF_major_version, cinfo->JFIF_minor_version, + cinfo->X_density, cinfo->Y_density, cinfo->density_unit); + /* Validate thumbnail dimensions and issue appropriate messages */ + if (GETJOCTET(data[12]) | GETJOCTET(data[13])) + TRACEMS2(cinfo, 1, JTRC_JFIF_THUMBNAIL, + GETJOCTET(data[12]), GETJOCTET(data[13])); + totallen -= APP0_DATA_LEN; + if (totallen != + ((INT32)GETJOCTET(data[12]) * (INT32)GETJOCTET(data[13]) * (INT32) 3)) + TRACEMS1(cinfo, 1, JTRC_JFIF_BADTHUMBNAILSIZE, (int) totallen); + } else if (datalen >= 6 && + GETJOCTET(data[0]) == 0x4A && + GETJOCTET(data[1]) == 0x46 && + GETJOCTET(data[2]) == 0x58 && + GETJOCTET(data[3]) == 0x58 && + GETJOCTET(data[4]) == 0) { + /* Found JFIF "JFXX" extension APP0 marker */ + /* The library doesn't actually do anything with these, + * but we try to produce a helpful trace message. + */ + switch (GETJOCTET(data[5])) { + case 0x10: + TRACEMS1(cinfo, 1, JTRC_THUMB_JPEG, (int) totallen); + break; + case 0x11: + TRACEMS1(cinfo, 1, JTRC_THUMB_PALETTE, (int) totallen); + break; + case 0x13: + TRACEMS1(cinfo, 1, JTRC_THUMB_RGB, (int) totallen); + break; + default: + TRACEMS2(cinfo, 1, JTRC_JFIF_EXTENSION, + GETJOCTET(data[5]), (int) totallen); + break; + } + } else { + /* Start of APP0 does not match "JFIF" or "JFXX", or too short */ + TRACEMS1(cinfo, 1, JTRC_APP0, (int) totallen); + } +} + +LOCAL(void) +examine_app14 (j_decompress_ptr cinfo, JOCTET FAR * data, + unsigned int datalen, INT32 remaining) +/* Examine first few bytes from an APP14. + * Take appropriate action if it is an Adobe marker. + * datalen is # of bytes at data[], remaining is length of rest of marker data. + */ +{ + unsigned int version, flags0, flags1, transform; + + if (datalen >= APP14_DATA_LEN && + GETJOCTET(data[0]) == 0x41 && + GETJOCTET(data[1]) == 0x64 && + GETJOCTET(data[2]) == 0x6F && + GETJOCTET(data[3]) == 0x62 && + GETJOCTET(data[4]) == 0x65) { + /* Found Adobe APP14 marker */ + version = (GETJOCTET(data[5]) << 8) + GETJOCTET(data[6]); + flags0 = (GETJOCTET(data[7]) << 8) + GETJOCTET(data[8]); + flags1 = (GETJOCTET(data[9]) << 8) + GETJOCTET(data[10]); + transform = GETJOCTET(data[11]); + TRACEMS4(cinfo, 1, JTRC_ADOBE, version, flags0, flags1, transform); + cinfo->saw_Adobe_marker = TRUE; + cinfo->Adobe_transform = (UINT8) transform; + } else { + /* Start of APP14 does not match "Adobe", or too short */ + TRACEMS1(cinfo, 1, JTRC_APP14, (int) (datalen + remaining)); + } +} + +METHODDEF(boolean) +get_interesting_appn (j_decompress_ptr cinfo) +/* Process an APP0 or APP14 marker without saving it */ +{ + INT32 length; + JOCTET b[APPN_DATA_LEN]; + unsigned int i, numtoread; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + length -= 2; + + /* get the interesting part of the marker data */ + if (length >= APPN_DATA_LEN) + numtoread = APPN_DATA_LEN; + else if (length > 0) + numtoread = (unsigned int) length; + else + numtoread = 0; + for (i = 0; i < numtoread; i++) + INPUT_BYTE(cinfo, b[i], return FALSE); + length -= numtoread; + + /* process it */ + switch (cinfo->unread_marker) { + case M_APP0: + examine_app0(cinfo, (JOCTET FAR *) b, numtoread, length); + break; + case M_APP14: + examine_app14(cinfo, (JOCTET FAR *) b, numtoread, length); + break; + default: + /* can't get here unless jpeg_save_markers chooses wrong processor */ + ERREXIT1(cinfo, JERR_UNKNOWN_MARKER, cinfo->unread_marker); + break; + } + + /* skip any remaining data -- could be lots */ + INPUT_SYNC(cinfo); + if (length > 0) + (*cinfo->src->skip_input_data) (cinfo, (long) length); + + return TRUE; +} + +#ifdef SAVE_MARKERS_SUPPORTED + +METHODDEF(boolean) +save_marker (j_decompress_ptr cinfo) +/* Save an APPn or COM marker into the marker list */ +{ + my_marker_ptr2 marker = (my_marker_ptr2) cinfo->marker; + jpeg_saved_marker_ptr cur_marker = marker->cur_marker; + unsigned int bytes_read, data_length; + JOCTET FAR * data; + INT32 length = 0; + INPUT_VARS(cinfo); + + if (cur_marker == NULL) { + /* begin reading a marker */ + INPUT_2BYTES(cinfo, length, return FALSE); + length -= 2; + if (length >= 0) { /* watch out for bogus length word */ + /* figure out how much we want to save */ + unsigned int limit; + if (cinfo->unread_marker == (int) M_COM) + limit = marker->length_limit_COM; + else + limit = marker->length_limit_APPn[cinfo->unread_marker - (int) M_APP0]; + if ((unsigned int) length < limit) + limit = (unsigned int) length; + /* allocate and initialize the marker item */ + cur_marker = (jpeg_saved_marker_ptr) + (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(struct jpeg_marker_struct) + limit); + cur_marker->next = NULL; + cur_marker->marker = (UINT8) cinfo->unread_marker; + cur_marker->original_length = (unsigned int) length; + cur_marker->data_length = limit; + /* data area is just beyond the jpeg_marker_struct */ + data = cur_marker->data = (JOCTET FAR *) (cur_marker + 1); + marker->cur_marker = cur_marker; + marker->bytes_read = 0; + bytes_read = 0; + data_length = limit; + } else { + /* deal with bogus length word */ + bytes_read = data_length = 0; + data = NULL; + } + } else { + /* resume reading a marker */ + bytes_read = marker->bytes_read; + data_length = cur_marker->data_length; + data = cur_marker->data + bytes_read; + } + + while (bytes_read < data_length) { + INPUT_SYNC(cinfo); /* move the restart point to here */ + marker->bytes_read = bytes_read; + /* If there's not at least one byte in buffer, suspend */ + MAKE_BYTE_AVAIL(cinfo, return FALSE); + /* Copy bytes with reasonable rapidity */ + while (bytes_read < data_length && bytes_in_buffer > 0) { + *data++ = *next_input_byte++; + bytes_in_buffer--; + bytes_read++; + } + } + + /* Done reading what we want to read */ + if (cur_marker != NULL) { /* will be NULL if bogus length word */ + /* Add new marker to end of list */ + if (cinfo->marker_list == NULL) { + cinfo->marker_list = cur_marker; + } else { + jpeg_saved_marker_ptr prev = cinfo->marker_list; + while (prev->next != NULL) + prev = prev->next; + prev->next = cur_marker; + } + /* Reset pointer & calc remaining data length */ + data = cur_marker->data; + length = cur_marker->original_length - data_length; + } + /* Reset to initial state for next marker */ + marker->cur_marker = NULL; + + /* Process the marker if interesting; else just make a generic trace msg */ + switch (cinfo->unread_marker) { + case M_APP0: + examine_app0(cinfo, data, data_length, length); + break; + case M_APP14: + examine_app14(cinfo, data, data_length, length); + break; + default: + TRACEMS2(cinfo, 1, JTRC_MISC_MARKER, cinfo->unread_marker, + (int) (data_length + length)); + break; + } + + /* skip any remaining data -- could be lots */ + INPUT_SYNC(cinfo); /* do before skip_input_data */ + if (length > 0) + (*cinfo->src->skip_input_data) (cinfo, (long) length); + + return TRUE; +} + +#endif /* SAVE_MARKERS_SUPPORTED */ + +METHODDEF(boolean) +skip_variable (j_decompress_ptr cinfo) +/* Skip over an unknown or uninteresting variable-length marker */ +{ + INT32 length; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + length -= 2; + + TRACEMS2(cinfo, 1, JTRC_MISC_MARKER, cinfo->unread_marker, (int) length); + + INPUT_SYNC(cinfo); /* do before skip_input_data */ + if (length > 0) + (*cinfo->src->skip_input_data) (cinfo, (long) length); + + return TRUE; +} + +/* + * Find the next JPEG marker, save it in cinfo->unread_marker. + * Returns FALSE if had to suspend before reaching a marker; + * in that case cinfo->unread_marker is unchanged. + * + * Note that the result might not be a valid marker code, + * but it will never be 0 or FF. + */ + +LOCAL(boolean) +next_marker (j_decompress_ptr cinfo) +{ + int c; + INPUT_VARS(cinfo); + + for (;;) { + INPUT_BYTE(cinfo, c, return FALSE); + /* Skip any non-FF bytes. + * This may look a bit inefficient, but it will not occur in a valid file. + * We sync after each discarded byte so that a suspending data source + * can discard the byte from its buffer. + */ + while (c != 0xFF) { + cinfo->marker->discarded_bytes++; + INPUT_SYNC(cinfo); + INPUT_BYTE(cinfo, c, return FALSE); + } + /* This loop swallows any duplicate FF bytes. Extra FFs are legal as + * pad bytes, so don't count them in discarded_bytes. We assume there + * will not be so many consecutive FF bytes as to overflow a suspending + * data source's input buffer. + */ + do { + INPUT_BYTE(cinfo, c, return FALSE); + } while (c == 0xFF); + if (c != 0) + break; /* found a valid marker, exit loop */ + /* Reach here if we found a stuffed-zero data sequence (FF/00). + * Discard it and loop back to try again. + */ + cinfo->marker->discarded_bytes += 2; + INPUT_SYNC(cinfo); + } + + if (cinfo->marker->discarded_bytes != 0) { + WARNMS2(cinfo, JWRN_EXTRANEOUS_DATA, cinfo->marker->discarded_bytes, c); + cinfo->marker->discarded_bytes = 0; + } + + cinfo->unread_marker = c; + + INPUT_SYNC(cinfo); + return TRUE; +} + +LOCAL(boolean) +first_marker (j_decompress_ptr cinfo) +/* Like next_marker, but used to obtain the initial SOI marker. */ +/* For this marker, we do not allow preceding garbage or fill; otherwise, + * we might well scan an entire input file before realizing it ain't JPEG. + * If an application wants to process non-JFIF files, it must seek to the + * SOI before calling the JPEG library. + */ +{ + int c, c2; + INPUT_VARS(cinfo); + + INPUT_BYTE(cinfo, c, return FALSE); + INPUT_BYTE(cinfo, c2, return FALSE); + if (c != 0xFF || c2 != (int) M_SOI) + ERREXIT2(cinfo, JERR_NO_SOI, c, c2); + + cinfo->unread_marker = c2; + + INPUT_SYNC(cinfo); + return TRUE; +} + +/* + * Read markers until SOS or EOI. + * + * Returns same codes as are defined for jpeg_consume_input: + * JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI. + */ + +METHODDEF(int) +read_markers (j_decompress_ptr cinfo) +{ + /* Outer loop repeats once for each marker. */ + for (;;) { + /* Collect the marker proper, unless we already did. */ + /* NB: first_marker() enforces the requirement that SOI appear first. */ + if (cinfo->unread_marker == 0) { + if (! cinfo->marker->saw_SOI) { + if (! first_marker(cinfo)) + return JPEG_SUSPENDED; + } else { + if (! next_marker(cinfo)) + return JPEG_SUSPENDED; + } + } + /* At this point cinfo->unread_marker contains the marker code and the + * input point is just past the marker proper, but before any parameters. + * A suspension will cause us to return with this state still true. + */ + switch (cinfo->unread_marker) { + case M_SOI: + if (! get_soi(cinfo)) + return JPEG_SUSPENDED; + break; + + case M_SOF0: /* Baseline */ + case M_SOF1: /* Extended sequential, Huffman */ + if (! get_sof(cinfo, FALSE, FALSE)) + return JPEG_SUSPENDED; + break; + + case M_SOF2: /* Progressive, Huffman */ + if (! get_sof(cinfo, TRUE, FALSE)) + return JPEG_SUSPENDED; + break; + + case M_SOF9: /* Extended sequential, arithmetic */ + if (! get_sof(cinfo, FALSE, TRUE)) + return JPEG_SUSPENDED; + break; + + case M_SOF10: /* Progressive, arithmetic */ + if (! get_sof(cinfo, TRUE, TRUE)) + return JPEG_SUSPENDED; + break; + + /* Currently unsupported SOFn types */ + case M_SOF3: /* Lossless, Huffman */ + case M_SOF5: /* Differential sequential, Huffman */ + case M_SOF6: /* Differential progressive, Huffman */ + case M_SOF7: /* Differential lossless, Huffman */ + case M_JPG: /* Reserved for JPEG extensions */ + case M_SOF11: /* Lossless, arithmetic */ + case M_SOF13: /* Differential sequential, arithmetic */ + case M_SOF14: /* Differential progressive, arithmetic */ + case M_SOF15: /* Differential lossless, arithmetic */ + ERREXIT1(cinfo, JERR_SOF_UNSUPPORTED, cinfo->unread_marker); + break; + + case M_SOS: + if (! get_sos(cinfo)) + return JPEG_SUSPENDED; + cinfo->unread_marker = 0; /* processed the marker */ + return JPEG_REACHED_SOS; + + case M_EOI: + TRACEMS(cinfo, 1, JTRC_EOI); + cinfo->unread_marker = 0; /* processed the marker */ + return JPEG_REACHED_EOI; + + case M_DAC: + if (! get_dac(cinfo)) + return JPEG_SUSPENDED; + break; + + case M_DHT: + if (! get_dht(cinfo)) + return JPEG_SUSPENDED; + break; + + case M_DQT: + if (! get_dqt(cinfo)) + return JPEG_SUSPENDED; + break; + + case M_DRI: + if (! get_dri(cinfo)) + return JPEG_SUSPENDED; + break; + + case M_APP0: + case M_APP1: + case M_APP2: + case M_APP3: + case M_APP4: + case M_APP5: + case M_APP6: + case M_APP7: + case M_APP8: + case M_APP9: + case M_APP10: + case M_APP11: + case M_APP12: + case M_APP13: + case M_APP14: + case M_APP15: + if (! (*((my_marker_ptr2) cinfo->marker)->process_APPn[ + cinfo->unread_marker - (int) M_APP0]) (cinfo)) + return JPEG_SUSPENDED; + break; + + case M_COM: + if (! (*((my_marker_ptr2) cinfo->marker)->process_COM) (cinfo)) + return JPEG_SUSPENDED; + break; + + case M_RST0: /* these are all parameterless */ + case M_RST1: + case M_RST2: + case M_RST3: + case M_RST4: + case M_RST5: + case M_RST6: + case M_RST7: + case M_TEM: + TRACEMS1(cinfo, 1, JTRC_PARMLESS_MARKER, cinfo->unread_marker); + break; + + case M_DNL: /* Ignore DNL ... perhaps the wrong thing */ + if (! skip_variable(cinfo)) + return JPEG_SUSPENDED; + break; + + default: /* must be DHP, EXP, JPGn, or RESn */ + /* For now, we treat the reserved markers as fatal errors since they are + * likely to be used to signal incompatible JPEG Part 3 extensions. + * Once the JPEG 3 version-number marker is well defined, this code + * ought to change! + */ + ERREXIT1(cinfo, JERR_UNKNOWN_MARKER, cinfo->unread_marker); + break; + } + /* Successfully processed marker, so reset state variable */ + cinfo->unread_marker = 0; + } /* end loop */ +} + +/* + * Read a restart marker, which is expected to appear next in the datastream; + * if the marker is not there, take appropriate recovery action. + * Returns FALSE if suspension is required. + * + * This is called by the entropy decoder after it has read an appropriate + * number of MCUs. cinfo->unread_marker may be nonzero if the entropy decoder + * has already read a marker from the data source. Under normal conditions + * cinfo->unread_marker will be reset to 0 before returning; if not reset, + * it holds a marker which the decoder will be unable to read past. + */ + +METHODDEF(boolean) +read_restart_marker (j_decompress_ptr cinfo) +{ + /* Obtain a marker unless we already did. */ + /* Note that next_marker will complain if it skips any data. */ + if (cinfo->unread_marker == 0) { + if (! next_marker(cinfo)) + return FALSE; + } + + if (cinfo->unread_marker == + ((int) M_RST0 + cinfo->marker->next_restart_num)) { + /* Normal case --- swallow the marker and let entropy decoder continue */ + TRACEMS1(cinfo, 3, JTRC_RST, cinfo->marker->next_restart_num); + cinfo->unread_marker = 0; + } else { + /* Uh-oh, the restart markers have been messed up. */ + /* Let the data source manager determine how to resync. */ + if (! (*cinfo->src->resync_to_restart) (cinfo, + cinfo->marker->next_restart_num)) + return FALSE; + } + + /* Update next-restart state */ + cinfo->marker->next_restart_num = (cinfo->marker->next_restart_num + 1) & 7; + + return TRUE; +} + +/* + * This is the default resync_to_restart method for data source managers + * to use if they don't have any better approach. Some data source managers + * may be able to back up, or may have additional knowledge about the data + * which permits a more intelligent recovery strategy; such managers would + * presumably supply their own resync method. + * + * read_restart_marker calls resync_to_restart if it finds a marker other than + * the restart marker it was expecting. (This code is *not* used unless + * a nonzero restart interval has been declared.) cinfo->unread_marker is + * the marker code actually found (might be anything, except 0 or FF). + * The desired restart marker number (0..7) is passed as a parameter. + * This routine is supposed to apply whatever error recovery strategy seems + * appropriate in order to position the input stream to the next data segment. + * Note that cinfo->unread_marker is treated as a marker appearing before + * the current data-source input point; usually it should be reset to zero + * before returning. + * Returns FALSE if suspension is required. + * + * This implementation is substantially constrained by wanting to treat the + * input as a data stream; this means we can't back up. Therefore, we have + * only the following actions to work with: + * 1. Simply discard the marker and let the entropy decoder resume at next + * byte of file. + * 2. Read forward until we find another marker, discarding intervening + * data. (In theory we could look ahead within the current bufferload, + * without having to discard data if we don't find the desired marker. + * This idea is not implemented here, in part because it makes behavior + * dependent on buffer size and chance buffer-boundary positions.) + * 3. Leave the marker unread (by failing to zero cinfo->unread_marker). + * This will cause the entropy decoder to process an empty data segment, + * inserting dummy zeroes, and then we will reprocess the marker. + * + * #2 is appropriate if we think the desired marker lies ahead, while #3 is + * appropriate if the found marker is a future restart marker (indicating + * that we have missed the desired restart marker, probably because it got + * corrupted). + * We apply #2 or #3 if the found marker is a restart marker no more than + * two counts behind or ahead of the expected one. We also apply #2 if the + * found marker is not a legal JPEG marker code (it's certainly bogus data). + * If the found marker is a restart marker more than 2 counts away, we do #1 + * (too much risk that the marker is erroneous; with luck we will be able to + * resync at some future point). + * For any valid non-restart JPEG marker, we apply #3. This keeps us from + * overrunning the end of a scan. An implementation limited to single-scan + * files might find it better to apply #2 for markers other than EOI, since + * any other marker would have to be bogus data in that case. + */ + +GLOBAL(boolean) +jpeg_resync_to_restart (j_decompress_ptr cinfo, int desired) +{ + int marker = cinfo->unread_marker; + int action = 1; + + /* Always put up a warning. */ + WARNMS2(cinfo, JWRN_MUST_RESYNC, marker, desired); + + /* Outer loop handles repeated decision after scanning forward. */ + for (;;) { + if (marker < (int) M_SOF0) + action = 2; /* invalid marker */ + else if (marker < (int) M_RST0 || marker > (int) M_RST7) + action = 3; /* valid non-restart marker */ + else { + if (marker == ((int) M_RST0 + ((desired+1) & 7)) || + marker == ((int) M_RST0 + ((desired+2) & 7))) + action = 3; /* one of the next two expected restarts */ + else if (marker == ((int) M_RST0 + ((desired-1) & 7)) || + marker == ((int) M_RST0 + ((desired-2) & 7))) + action = 2; /* a prior restart, so advance */ + else + action = 1; /* desired restart or too far away */ + } + TRACEMS2(cinfo, 4, JTRC_RECOVERY_ACTION, marker, action); + switch (action) { + case 1: + /* Discard marker and let entropy decoder resume processing. */ + cinfo->unread_marker = 0; + return TRUE; + case 2: + /* Scan to the next marker, and repeat the decision loop. */ + if (! next_marker(cinfo)) + return FALSE; + marker = cinfo->unread_marker; + break; + case 3: + /* Return without advancing past this marker. */ + /* Entropy decoder will be forced to process an empty segment. */ + return TRUE; + } + } /* end loop */ +} + +/* + * Reset marker processing state to begin a fresh datastream. + */ + +METHODDEF(void) +reset_marker_reader (j_decompress_ptr cinfo) +{ + my_marker_ptr2 marker = (my_marker_ptr2) cinfo->marker; + + cinfo->comp_info = NULL; /* until allocated by get_sof */ + cinfo->input_scan_number = 0; /* no SOS seen yet */ + cinfo->unread_marker = 0; /* no pending marker */ + marker->pub.saw_SOI = FALSE; /* set internal state too */ + marker->pub.saw_SOF = FALSE; + marker->pub.discarded_bytes = 0; + marker->cur_marker = NULL; +} + +/* + * Initialize the marker reader module. + * This is called only once, when the decompression object is created. + */ + +GLOBAL(void) +jinit_marker_reader (j_decompress_ptr cinfo) +{ + my_marker_ptr2 marker; + int i; + + /* Create subobject in permanent pool */ + marker = (my_marker_ptr2) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + SIZEOF(my_marker_reader)); + cinfo->marker = (struct jpeg_marker_reader *) marker; + /* Initialize public method pointers */ + marker->pub.reset_marker_reader = reset_marker_reader; + marker->pub.read_markers = read_markers; + marker->pub.read_restart_marker = read_restart_marker; + /* Initialize COM/APPn processing. + * By default, we examine and then discard APP0 and APP14, + * but simply discard COM and all other APPn. + */ + marker->process_COM = skip_variable; + marker->length_limit_COM = 0; + for (i = 0; i < 16; i++) { + marker->process_APPn[i] = skip_variable; + marker->length_limit_APPn[i] = 0; + } + marker->process_APPn[0] = get_interesting_appn; + marker->process_APPn[14] = get_interesting_appn; + /* Reset marker processing state */ + reset_marker_reader(cinfo); +} + +/* + * Control saving of COM and APPn markers into marker_list. + */ + +#ifdef SAVE_MARKERS_SUPPORTED + +GLOBAL(void) +jpeg_save_markers (j_decompress_ptr cinfo, int marker_code, + unsigned int length_limit) +{ + my_marker_ptr2 marker = (my_marker_ptr2) cinfo->marker; + long maxlength; + jpeg_marker_parser_method processor; + + /* Length limit mustn't be larger than what we can allocate + * (should only be a concern in a 16-bit environment). + */ + maxlength = cinfo->mem->max_alloc_chunk - SIZEOF(struct jpeg_marker_struct); + if (((long) length_limit) > maxlength) + length_limit = (unsigned int) maxlength; + + /* Choose processor routine to use. + * APP0/APP14 have special requirements. + */ + if (length_limit) { + processor = save_marker; + /* If saving APP0/APP14, save at least enough for our internal use. */ + if (marker_code == (int) M_APP0 && length_limit < APP0_DATA_LEN) + length_limit = APP0_DATA_LEN; + else if (marker_code == (int) M_APP14 && length_limit < APP14_DATA_LEN) + length_limit = APP14_DATA_LEN; + } else { + processor = skip_variable; + /* If discarding APP0/APP14, use our regular on-the-fly processor. */ + if (marker_code == (int) M_APP0 || marker_code == (int) M_APP14) + processor = get_interesting_appn; + } + + if (marker_code == (int) M_COM) { + marker->process_COM = processor; + marker->length_limit_COM = length_limit; + } else if (marker_code >= (int) M_APP0 && marker_code <= (int) M_APP15) { + marker->process_APPn[marker_code - (int) M_APP0] = processor; + marker->length_limit_APPn[marker_code - (int) M_APP0] = length_limit; + } else + ERREXIT1(cinfo, JERR_UNKNOWN_MARKER, marker_code); +} + +#endif /* SAVE_MARKERS_SUPPORTED */ + +/* + * Install a special processing method for COM or APPn markers. + */ + +GLOBAL(void) +jpeg_set_marker_processor (j_decompress_ptr cinfo, int marker_code, + jpeg_marker_parser_method routine) +{ + my_marker_ptr2 marker = (my_marker_ptr2) cinfo->marker; + + if (marker_code == (int) M_COM) + marker->process_COM = routine; + else if (marker_code >= (int) M_APP0 && marker_code <= (int) M_APP15) + marker->process_APPn[marker_code - (int) M_APP0] = routine; + else + ERREXIT1(cinfo, JERR_UNKNOWN_MARKER, marker_code); +} +/********* End of inlined file: jdmarker.c *********/ + +/********* Start of inlined file: jdmaster.c *********/ +#define JPEG_INTERNALS + +/* Private state */ + +typedef struct { + struct jpeg_decomp_master pub; /* public fields */ + + int pass_number; /* # of passes completed */ + + boolean using_merged_upsample; /* TRUE if using merged upsample/cconvert */ + + /* Saved references to initialized quantizer modules, + * in case we need to switch modes. + */ + struct jpeg_color_quantizer * quantizer_1pass; + struct jpeg_color_quantizer * quantizer_2pass; +} my_decomp_master; + +typedef my_decomp_master * my_master_ptr6; + +/* + * Determine whether merged upsample/color conversion should be used. + * CRUCIAL: this must match the actual capabilities of jdmerge.c! + */ + +LOCAL(boolean) +use_merged_upsample (j_decompress_ptr cinfo) +{ +#ifdef UPSAMPLE_MERGING_SUPPORTED + /* Merging is the equivalent of plain box-filter upsampling */ + if (cinfo->do_fancy_upsampling || cinfo->CCIR601_sampling) + return FALSE; + /* jdmerge.c only supports YCC=>RGB color conversion */ + if (cinfo->jpeg_color_space != JCS_YCbCr || cinfo->num_components != 3 || + cinfo->out_color_space != JCS_RGB || + cinfo->out_color_components != RGB_PIXELSIZE) + return FALSE; + /* and it only handles 2h1v or 2h2v sampling ratios */ + if (cinfo->comp_info[0].h_samp_factor != 2 || + cinfo->comp_info[1].h_samp_factor != 1 || + cinfo->comp_info[2].h_samp_factor != 1 || + cinfo->comp_info[0].v_samp_factor > 2 || + cinfo->comp_info[1].v_samp_factor != 1 || + cinfo->comp_info[2].v_samp_factor != 1) + return FALSE; + /* furthermore, it doesn't work if we've scaled the IDCTs differently */ + if (cinfo->comp_info[0].DCT_scaled_size != cinfo->min_DCT_scaled_size || + cinfo->comp_info[1].DCT_scaled_size != cinfo->min_DCT_scaled_size || + cinfo->comp_info[2].DCT_scaled_size != cinfo->min_DCT_scaled_size) + return FALSE; + /* ??? also need to test for upsample-time rescaling, when & if supported */ + return TRUE; /* by golly, it'll work... */ +#else + return FALSE; +#endif +} + +/* + * Compute output image dimensions and related values. + * NOTE: this is exported for possible use by application. + * Hence it mustn't do anything that can't be done twice. + * Also note that it may be called before the master module is initialized! + */ + +GLOBAL(void) +jpeg_calc_output_dimensions (j_decompress_ptr cinfo) +/* Do computations that are needed before master selection phase */ +{ +#ifdef IDCT_SCALING_SUPPORTED + int ci; + jpeg_component_info *compptr; +#endif + + /* Prevent application from calling me at wrong times */ + if (cinfo->global_state != DSTATE_READY) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + +#ifdef IDCT_SCALING_SUPPORTED + + /* Compute actual output image dimensions and DCT scaling choices. */ + if (cinfo->scale_num * 8 <= cinfo->scale_denom) { + /* Provide 1/8 scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width, 8L); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, 8L); + cinfo->min_DCT_scaled_size = 1; + } else if (cinfo->scale_num * 4 <= cinfo->scale_denom) { + /* Provide 1/4 scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width, 4L); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, 4L); + cinfo->min_DCT_scaled_size = 2; + } else if (cinfo->scale_num * 2 <= cinfo->scale_denom) { + /* Provide 1/2 scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width, 2L); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, 2L); + cinfo->min_DCT_scaled_size = 4; + } else { + /* Provide 1/1 scaling */ + cinfo->output_width = cinfo->image_width; + cinfo->output_height = cinfo->image_height; + cinfo->min_DCT_scaled_size = DCTSIZE; + } + /* In selecting the actual DCT scaling for each component, we try to + * scale up the chroma components via IDCT scaling rather than upsampling. + * This saves time if the upsampler gets to use 1:1 scaling. + * Note this code assumes that the supported DCT scalings are powers of 2. + */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + int ssize = cinfo->min_DCT_scaled_size; + while (ssize < DCTSIZE && + (compptr->h_samp_factor * ssize * 2 <= + cinfo->max_h_samp_factor * cinfo->min_DCT_scaled_size) && + (compptr->v_samp_factor * ssize * 2 <= + cinfo->max_v_samp_factor * cinfo->min_DCT_scaled_size)) { + ssize = ssize * 2; + } + compptr->DCT_scaled_size = ssize; + } + + /* Recompute downsampled dimensions of components; + * application needs to know these if using raw downsampled data. + */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Size in samples, after IDCT scaling */ + compptr->downsampled_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * + (long) (compptr->h_samp_factor * compptr->DCT_scaled_size), + (long) (cinfo->max_h_samp_factor * DCTSIZE)); + compptr->downsampled_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * + (long) (compptr->v_samp_factor * compptr->DCT_scaled_size), + (long) (cinfo->max_v_samp_factor * DCTSIZE)); + } + +#else /* !IDCT_SCALING_SUPPORTED */ + + /* Hardwire it to "no scaling" */ + cinfo->output_width = cinfo->image_width; + cinfo->output_height = cinfo->image_height; + /* jdinput.c has already initialized DCT_scaled_size to DCTSIZE, + * and has computed unscaled downsampled_width and downsampled_height. + */ + +#endif /* IDCT_SCALING_SUPPORTED */ + + /* Report number of components in selected colorspace. */ + /* Probably this should be in the color conversion module... */ + switch (cinfo->out_color_space) { + case JCS_GRAYSCALE: + cinfo->out_color_components = 1; + break; + case JCS_RGB: +#if RGB_PIXELSIZE != 3 + cinfo->out_color_components = RGB_PIXELSIZE; + break; +#endif /* else share code with YCbCr */ + case JCS_YCbCr: + cinfo->out_color_components = 3; + break; + case JCS_CMYK: + case JCS_YCCK: + cinfo->out_color_components = 4; + break; + default: /* else must be same colorspace as in file */ + cinfo->out_color_components = cinfo->num_components; + break; + } + cinfo->output_components = (cinfo->quantize_colors ? 1 : + cinfo->out_color_components); + + /* See if upsampler will want to emit more than one row at a time */ + if (use_merged_upsample(cinfo)) + cinfo->rec_outbuf_height = cinfo->max_v_samp_factor; + else + cinfo->rec_outbuf_height = 1; +} + +/* + * Several decompression processes need to range-limit values to the range + * 0..MAXJSAMPLE; the input value may fall somewhat outside this range + * due to noise introduced by quantization, roundoff error, etc. These + * processes are inner loops and need to be as fast as possible. On most + * machines, particularly CPUs with pipelines or instruction prefetch, + * a (subscript-check-less) C table lookup + * x = sample_range_limit[x]; + * is faster than explicit tests + * if (x < 0) x = 0; + * else if (x > MAXJSAMPLE) x = MAXJSAMPLE; + * These processes all use a common table prepared by the routine below. + * + * For most steps we can mathematically guarantee that the initial value + * of x is within MAXJSAMPLE+1 of the legal range, so a table running from + * -(MAXJSAMPLE+1) to 2*MAXJSAMPLE+1 is sufficient. But for the initial + * limiting step (just after the IDCT), a wildly out-of-range value is + * possible if the input data is corrupt. To avoid any chance of indexing + * off the end of memory and getting a bad-pointer trap, we perform the + * post-IDCT limiting thus: + * x = range_limit[x & MASK]; + * where MASK is 2 bits wider than legal sample data, ie 10 bits for 8-bit + * samples. Under normal circumstances this is more than enough range and + * a correct output will be generated; with bogus input data the mask will + * cause wraparound, and we will safely generate a bogus-but-in-range output. + * For the post-IDCT step, we want to convert the data from signed to unsigned + * representation by adding CENTERJSAMPLE at the same time that we limit it. + * So the post-IDCT limiting table ends up looking like this: + * CENTERJSAMPLE,CENTERJSAMPLE+1,...,MAXJSAMPLE, + * MAXJSAMPLE (repeat 2*(MAXJSAMPLE+1)-CENTERJSAMPLE times), + * 0 (repeat 2*(MAXJSAMPLE+1)-CENTERJSAMPLE times), + * 0,1,...,CENTERJSAMPLE-1 + * Negative inputs select values from the upper half of the table after + * masking. + * + * We can save some space by overlapping the start of the post-IDCT table + * with the simpler range limiting table. The post-IDCT table begins at + * sample_range_limit + CENTERJSAMPLE. + * + * Note that the table is allocated in near data space on PCs; it's small + * enough and used often enough to justify this. + */ + +LOCAL(void) +prepare_range_limit_table (j_decompress_ptr cinfo) +/* Allocate and fill in the sample_range_limit table */ +{ + JSAMPLE * table; + int i; + + table = (JSAMPLE *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (5 * (MAXJSAMPLE+1) + CENTERJSAMPLE) * SIZEOF(JSAMPLE)); + table += (MAXJSAMPLE+1); /* allow negative subscripts of simple table */ + cinfo->sample_range_limit = table; + /* First segment of "simple" table: limit[x] = 0 for x < 0 */ + MEMZERO(table - (MAXJSAMPLE+1), (MAXJSAMPLE+1) * SIZEOF(JSAMPLE)); + /* Main part of "simple" table: limit[x] = x */ + for (i = 0; i <= MAXJSAMPLE; i++) + table[i] = (JSAMPLE) i; + table += CENTERJSAMPLE; /* Point to where post-IDCT table starts */ + /* End of simple table, rest of first half of post-IDCT table */ + for (i = CENTERJSAMPLE; i < 2*(MAXJSAMPLE+1); i++) + table[i] = MAXJSAMPLE; + /* Second half of post-IDCT table */ + MEMZERO(table + (2 * (MAXJSAMPLE+1)), + (2 * (MAXJSAMPLE+1) - CENTERJSAMPLE) * SIZEOF(JSAMPLE)); + MEMCOPY(table + (4 * (MAXJSAMPLE+1) - CENTERJSAMPLE), + cinfo->sample_range_limit, CENTERJSAMPLE * SIZEOF(JSAMPLE)); +} + +/* + * Master selection of decompression modules. + * This is done once at jpeg_start_decompress time. We determine + * which modules will be used and give them appropriate initialization calls. + * We also initialize the decompressor input side to begin consuming data. + * + * Since jpeg_read_header has finished, we know what is in the SOF + * and (first) SOS markers. We also have all the application parameter + * settings. + */ + +LOCAL(void) +master_selection (j_decompress_ptr cinfo) +{ + my_master_ptr6 master = (my_master_ptr6) cinfo->master; + boolean use_c_buffer; + long samplesperrow; + JDIMENSION jd_samplesperrow; + + /* Initialize dimensions and other stuff */ + jpeg_calc_output_dimensions(cinfo); + prepare_range_limit_table(cinfo); + + /* Width of an output scanline must be representable as JDIMENSION. */ + samplesperrow = (long) cinfo->output_width * (long) cinfo->out_color_components; + jd_samplesperrow = (JDIMENSION) samplesperrow; + if ((long) jd_samplesperrow != samplesperrow) + ERREXIT(cinfo, JERR_WIDTH_OVERFLOW); + + /* Initialize my private state */ + master->pass_number = 0; + master->using_merged_upsample = use_merged_upsample(cinfo); + + /* Color quantizer selection */ + master->quantizer_1pass = NULL; + master->quantizer_2pass = NULL; + /* No mode changes if not using buffered-image mode. */ + if (! cinfo->quantize_colors || ! cinfo->buffered_image) { + cinfo->enable_1pass_quant = FALSE; + cinfo->enable_external_quant = FALSE; + cinfo->enable_2pass_quant = FALSE; + } + if (cinfo->quantize_colors) { + if (cinfo->raw_data_out) + ERREXIT(cinfo, JERR_NOTIMPL); + /* 2-pass quantizer only works in 3-component color space. */ + if (cinfo->out_color_components != 3) { + cinfo->enable_1pass_quant = TRUE; + cinfo->enable_external_quant = FALSE; + cinfo->enable_2pass_quant = FALSE; + cinfo->colormap = NULL; + } else if (cinfo->colormap != NULL) { + cinfo->enable_external_quant = TRUE; + } else if (cinfo->two_pass_quantize) { + cinfo->enable_2pass_quant = TRUE; + } else { + cinfo->enable_1pass_quant = TRUE; + } + + if (cinfo->enable_1pass_quant) { +#ifdef QUANT_1PASS_SUPPORTED + jinit_1pass_quantizer(cinfo); + master->quantizer_1pass = cinfo->cquantize; +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } + + /* We use the 2-pass code to map to external colormaps. */ + if (cinfo->enable_2pass_quant || cinfo->enable_external_quant) { +#ifdef QUANT_2PASS_SUPPORTED + jinit_2pass_quantizer(cinfo); + master->quantizer_2pass = cinfo->cquantize; +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } + /* If both quantizers are initialized, the 2-pass one is left active; + * this is necessary for starting with quantization to an external map. + */ + } + + /* Post-processing: in particular, color conversion first */ + if (! cinfo->raw_data_out) { + if (master->using_merged_upsample) { +#ifdef UPSAMPLE_MERGING_SUPPORTED + jinit_merged_upsampler(cinfo); /* does color conversion too */ +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else { + jinit_color_deconverter(cinfo); + jinit_upsampler(cinfo); + } + jinit_d_post_controller(cinfo, cinfo->enable_2pass_quant); + } + /* Inverse DCT */ + jinit_inverse_dct(cinfo); + /* Entropy decoding: either Huffman or arithmetic coding. */ + if (cinfo->arith_code) { + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); + } else { + if (cinfo->progressive_mode) { +#ifdef D_PROGRESSIVE_SUPPORTED + jinit_phuff_decoder(cinfo); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else + jinit_huff_decoder(cinfo); + } + + /* Initialize principal buffer controllers. */ + use_c_buffer = cinfo->inputctl->has_multiple_scans || cinfo->buffered_image; + jinit_d_coef_controller(cinfo, use_c_buffer); + + if (! cinfo->raw_data_out) + jinit_d_main_controller(cinfo, FALSE /* never need full buffer here */); + + /* We can now tell the memory manager to allocate virtual arrays. */ + (*cinfo->mem->realize_virt_arrays) ((j_common_ptr) cinfo); + + /* Initialize input side of decompressor to consume first scan. */ + (*cinfo->inputctl->start_input_pass) (cinfo); + +#ifdef D_MULTISCAN_FILES_SUPPORTED + /* If jpeg_start_decompress will read the whole file, initialize + * progress monitoring appropriately. The input step is counted + * as one pass. + */ + if (cinfo->progress != NULL && ! cinfo->buffered_image && + cinfo->inputctl->has_multiple_scans) { + int nscans; + /* Estimate number of scans to set pass_limit. */ + if (cinfo->progressive_mode) { + /* Arbitrarily estimate 2 interleaved DC scans + 3 AC scans/component. */ + nscans = 2 + 3 * cinfo->num_components; + } else { + /* For a nonprogressive multiscan file, estimate 1 scan per component. */ + nscans = cinfo->num_components; + } + cinfo->progress->pass_counter = 0L; + cinfo->progress->pass_limit = (long) cinfo->total_iMCU_rows * nscans; + cinfo->progress->completed_passes = 0; + cinfo->progress->total_passes = (cinfo->enable_2pass_quant ? 3 : 2); + /* Count the input pass as done */ + master->pass_number++; + } +#endif /* D_MULTISCAN_FILES_SUPPORTED */ +} + +/* + * Per-pass setup. + * This is called at the beginning of each output pass. We determine which + * modules will be active during this pass and give them appropriate + * start_pass calls. We also set is_dummy_pass to indicate whether this + * is a "real" output pass or a dummy pass for color quantization. + * (In the latter case, jdapistd.c will crank the pass to completion.) + */ + +METHODDEF(void) +prepare_for_output_pass (j_decompress_ptr cinfo) +{ + my_master_ptr6 master = (my_master_ptr6) cinfo->master; + + if (master->pub.is_dummy_pass) { +#ifdef QUANT_2PASS_SUPPORTED + /* Final pass of 2-pass quantization */ + master->pub.is_dummy_pass = FALSE; + (*cinfo->cquantize->start_pass) (cinfo, FALSE); + (*cinfo->post->start_pass) (cinfo, JBUF_CRANK_DEST); + (*cinfo->main->start_pass) (cinfo, JBUF_CRANK_DEST); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif /* QUANT_2PASS_SUPPORTED */ + } else { + if (cinfo->quantize_colors && cinfo->colormap == NULL) { + /* Select new quantization method */ + if (cinfo->two_pass_quantize && cinfo->enable_2pass_quant) { + cinfo->cquantize = master->quantizer_2pass; + master->pub.is_dummy_pass = TRUE; + } else if (cinfo->enable_1pass_quant) { + cinfo->cquantize = master->quantizer_1pass; + } else { + ERREXIT(cinfo, JERR_MODE_CHANGE); + } + } + (*cinfo->idct->start_pass) (cinfo); + (*cinfo->coef->start_output_pass) (cinfo); + if (! cinfo->raw_data_out) { + if (! master->using_merged_upsample) + (*cinfo->cconvert->start_pass) (cinfo); + (*cinfo->upsample->start_pass) (cinfo); + if (cinfo->quantize_colors) + (*cinfo->cquantize->start_pass) (cinfo, master->pub.is_dummy_pass); + (*cinfo->post->start_pass) (cinfo, + (master->pub.is_dummy_pass ? JBUF_SAVE_AND_PASS : JBUF_PASS_THRU)); + (*cinfo->main->start_pass) (cinfo, JBUF_PASS_THRU); + } + } + + /* Set up progress monitor's pass info if present */ + if (cinfo->progress != NULL) { + cinfo->progress->completed_passes = master->pass_number; + cinfo->progress->total_passes = master->pass_number + + (master->pub.is_dummy_pass ? 2 : 1); + /* In buffered-image mode, we assume one more output pass if EOI not + * yet reached, but no more passes if EOI has been reached. + */ + if (cinfo->buffered_image && ! cinfo->inputctl->eoi_reached) { + cinfo->progress->total_passes += (cinfo->enable_2pass_quant ? 2 : 1); + } + } +} + +/* + * Finish up at end of an output pass. + */ + +METHODDEF(void) +finish_output_pass (j_decompress_ptr cinfo) +{ + my_master_ptr6 master = (my_master_ptr6) cinfo->master; + + if (cinfo->quantize_colors) + (*cinfo->cquantize->finish_pass) (cinfo); + master->pass_number++; +} + +#ifdef D_MULTISCAN_FILES_SUPPORTED + +/* + * Switch to a new external colormap between output passes. + */ + +GLOBAL(void) +jpeg_new_colormap (j_decompress_ptr cinfo) +{ + my_master_ptr6 master = (my_master_ptr6) cinfo->master; + + /* Prevent application from calling me at wrong times */ + if (cinfo->global_state != DSTATE_BUFIMAGE) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + if (cinfo->quantize_colors && cinfo->enable_external_quant && + cinfo->colormap != NULL) { + /* Select 2-pass quantizer for external colormap use */ + cinfo->cquantize = master->quantizer_2pass; + /* Notify quantizer of colormap change */ + (*cinfo->cquantize->new_color_map) (cinfo); + master->pub.is_dummy_pass = FALSE; /* just in case */ + } else + ERREXIT(cinfo, JERR_MODE_CHANGE); +} + +#endif /* D_MULTISCAN_FILES_SUPPORTED */ + +/* + * Initialize master decompression control and select active modules. + * This is performed at the start of jpeg_start_decompress. + */ + +GLOBAL(void) +jinit_master_decompress (j_decompress_ptr cinfo) +{ + my_master_ptr6 master; + + master = (my_master_ptr6) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_decomp_master)); + cinfo->master = (struct jpeg_decomp_master *) master; + master->pub.prepare_for_output_pass = prepare_for_output_pass; + master->pub.finish_output_pass = finish_output_pass; + + master->pub.is_dummy_pass = FALSE; + + master_selection(cinfo); +} +/********* End of inlined file: jdmaster.c *********/ + + #undef FIX + +/********* Start of inlined file: jdmerge.c *********/ +#define JPEG_INTERNALS + +#ifdef UPSAMPLE_MERGING_SUPPORTED + +/* Private subobject */ + +typedef struct { + struct jpeg_upsampler pub; /* public fields */ + + /* Pointer to routine to do actual upsampling/conversion of one row group */ + JMETHOD(void, upmethod, (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION in_row_group_ctr, + JSAMPARRAY output_buf)); + + /* Private state for YCC->RGB conversion */ + int * Cr_r_tab; /* => table for Cr to R conversion */ + int * Cb_b_tab; /* => table for Cb to B conversion */ + INT32 * Cr_g_tab; /* => table for Cr to G conversion */ + INT32 * Cb_g_tab; /* => table for Cb to G conversion */ + + /* For 2:1 vertical sampling, we produce two output rows at a time. + * We need a "spare" row buffer to hold the second output row if the + * application provides just a one-row buffer; we also use the spare + * to discard the dummy last row if the image height is odd. + */ + JSAMPROW spare_row; + boolean spare_full; /* T if spare buffer is occupied */ + + JDIMENSION out_row_width; /* samples per output row */ + JDIMENSION rows_to_go; /* counts rows remaining in image */ +} my_upsampler; + +typedef my_upsampler * my_upsample_ptr; + +#define SCALEBITS 16 /* speediest right-shift on some machines */ +#define ONE_HALF ((INT32) 1 << (SCALEBITS-1)) +#define FIX(x) ((INT32) ((x) * (1L<RGB colorspace conversion. + * This is taken directly from jdcolor.c; see that file for more info. + */ + +LOCAL(void) +build_ycc_rgb_table2 (j_decompress_ptr cinfo) +{ + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + int i; + INT32 x; + SHIFT_TEMPS + + upsample->Cr_r_tab = (int *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(int)); + upsample->Cb_b_tab = (int *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(int)); + upsample->Cr_g_tab = (INT32 *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(INT32)); + upsample->Cb_g_tab = (INT32 *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(INT32)); + + for (i = 0, x = -CENTERJSAMPLE; i <= MAXJSAMPLE; i++, x++) { + /* i is the actual input pixel value, in the range 0..MAXJSAMPLE */ + /* The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE */ + /* Cr=>R value is nearest int to 1.40200 * x */ + upsample->Cr_r_tab[i] = (int) + RIGHT_SHIFT(FIX(1.40200) * x + ONE_HALF, SCALEBITS); + /* Cb=>B value is nearest int to 1.77200 * x */ + upsample->Cb_b_tab[i] = (int) + RIGHT_SHIFT(FIX(1.77200) * x + ONE_HALF, SCALEBITS); + /* Cr=>G value is scaled-up -0.71414 * x */ + upsample->Cr_g_tab[i] = (- FIX(0.71414)) * x; + /* Cb=>G value is scaled-up -0.34414 * x */ + /* We also add in ONE_HALF so that need not do it in inner loop */ + upsample->Cb_g_tab[i] = (- FIX(0.34414)) * x + ONE_HALF; + } +} + +/* + * Initialize for an upsampling pass. + */ + +METHODDEF(void) +start_pass_merged_upsample (j_decompress_ptr cinfo) +{ + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + + /* Mark the spare buffer empty */ + upsample->spare_full = FALSE; + /* Initialize total-height counter for detecting bottom of image */ + upsample->rows_to_go = cinfo->output_height; +} + +/* + * Control routine to do upsampling (and color conversion). + * + * The control routine just handles the row buffering considerations. + */ + +METHODDEF(void) +merged_2v_upsample (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +/* 2:1 vertical sampling case: may need a spare row. */ +{ + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + JSAMPROW work_ptrs[2]; + JDIMENSION num_rows; /* number of rows returned to caller */ + + if (upsample->spare_full) { + /* If we have a spare row saved from a previous cycle, just return it. */ + jcopy_sample_rows(& upsample->spare_row, 0, output_buf + *out_row_ctr, 0, + 1, upsample->out_row_width); + num_rows = 1; + upsample->spare_full = FALSE; + } else { + /* Figure number of rows to return to caller. */ + num_rows = 2; + /* Not more than the distance to the end of the image. */ + if (num_rows > upsample->rows_to_go) + num_rows = upsample->rows_to_go; + /* And not more than what the client can accept: */ + out_rows_avail -= *out_row_ctr; + if (num_rows > out_rows_avail) + num_rows = out_rows_avail; + /* Create output pointer array for upsampler. */ + work_ptrs[0] = output_buf[*out_row_ctr]; + if (num_rows > 1) { + work_ptrs[1] = output_buf[*out_row_ctr + 1]; + } else { + work_ptrs[1] = upsample->spare_row; + upsample->spare_full = TRUE; + } + /* Now do the upsampling. */ + (*upsample->upmethod) (cinfo, input_buf, *in_row_group_ctr, work_ptrs); + } + + /* Adjust counts */ + *out_row_ctr += num_rows; + upsample->rows_to_go -= num_rows; + /* When the buffer is emptied, declare this input row group consumed */ + if (! upsample->spare_full) + (*in_row_group_ctr)++; +} + +METHODDEF(void) +merged_1v_upsample (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +/* 1:1 vertical sampling case: much easier, never need a spare row. */ +{ + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + + /* Just do the upsampling. */ + (*upsample->upmethod) (cinfo, input_buf, *in_row_group_ctr, + output_buf + *out_row_ctr); + /* Adjust counts */ + (*out_row_ctr)++; + (*in_row_group_ctr)++; +} + +/* + * These are the routines invoked by the control routines to do + * the actual upsampling/conversion. One row group is processed per call. + * + * Note: since we may be writing directly into application-supplied buffers, + * we have to be honest about the output width; we can't assume the buffer + * has been rounded up to an even width. + */ + +/* + * Upsample and color convert for the case of 2:1 horizontal and 1:1 vertical. + */ + +METHODDEF(void) +h2v1_merged_upsample (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION in_row_group_ctr, + JSAMPARRAY output_buf) +{ + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + register int y, cred, cgreen, cblue; + int cb, cr; + register JSAMPROW outptr; + JSAMPROW inptr0, inptr1, inptr2; + JDIMENSION col; + /* copy these pointers into registers if possible */ + register JSAMPLE * range_limit = cinfo->sample_range_limit; + int * Crrtab = upsample->Cr_r_tab; + int * Cbbtab = upsample->Cb_b_tab; + INT32 * Crgtab = upsample->Cr_g_tab; + INT32 * Cbgtab = upsample->Cb_g_tab; + SHIFT_TEMPS + + inptr0 = input_buf[0][in_row_group_ctr]; + inptr1 = input_buf[1][in_row_group_ctr]; + inptr2 = input_buf[2][in_row_group_ctr]; + outptr = output_buf[0]; + /* Loop for each pair of output pixels */ + for (col = cinfo->output_width >> 1; col > 0; col--) { + /* Do the chroma part of the calculation */ + cb = GETJSAMPLE(*inptr1++); + cr = GETJSAMPLE(*inptr2++); + cred = Crrtab[cr]; + cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS); + cblue = Cbbtab[cb]; + /* Fetch 2 Y values and emit 2 pixels */ + y = GETJSAMPLE(*inptr0++); + outptr[RGB_RED] = range_limit[y + cred]; + outptr[RGB_GREEN] = range_limit[y + cgreen]; + outptr[RGB_BLUE] = range_limit[y + cblue]; + outptr += RGB_PIXELSIZE; + y = GETJSAMPLE(*inptr0++); + outptr[RGB_RED] = range_limit[y + cred]; + outptr[RGB_GREEN] = range_limit[y + cgreen]; + outptr[RGB_BLUE] = range_limit[y + cblue]; + outptr += RGB_PIXELSIZE; + } + /* If image width is odd, do the last output column separately */ + if (cinfo->output_width & 1) { + cb = GETJSAMPLE(*inptr1); + cr = GETJSAMPLE(*inptr2); + cred = Crrtab[cr]; + cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS); + cblue = Cbbtab[cb]; + y = GETJSAMPLE(*inptr0); + outptr[RGB_RED] = range_limit[y + cred]; + outptr[RGB_GREEN] = range_limit[y + cgreen]; + outptr[RGB_BLUE] = range_limit[y + cblue]; + } +} + +/* + * Upsample and color convert for the case of 2:1 horizontal and 2:1 vertical. + */ + +METHODDEF(void) +h2v2_merged_upsample (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION in_row_group_ctr, + JSAMPARRAY output_buf) +{ + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + register int y, cred, cgreen, cblue; + int cb, cr; + register JSAMPROW outptr0, outptr1; + JSAMPROW inptr00, inptr01, inptr1, inptr2; + JDIMENSION col; + /* copy these pointers into registers if possible */ + register JSAMPLE * range_limit = cinfo->sample_range_limit; + int * Crrtab = upsample->Cr_r_tab; + int * Cbbtab = upsample->Cb_b_tab; + INT32 * Crgtab = upsample->Cr_g_tab; + INT32 * Cbgtab = upsample->Cb_g_tab; + SHIFT_TEMPS + + inptr00 = input_buf[0][in_row_group_ctr*2]; + inptr01 = input_buf[0][in_row_group_ctr*2 + 1]; + inptr1 = input_buf[1][in_row_group_ctr]; + inptr2 = input_buf[2][in_row_group_ctr]; + outptr0 = output_buf[0]; + outptr1 = output_buf[1]; + /* Loop for each group of output pixels */ + for (col = cinfo->output_width >> 1; col > 0; col--) { + /* Do the chroma part of the calculation */ + cb = GETJSAMPLE(*inptr1++); + cr = GETJSAMPLE(*inptr2++); + cred = Crrtab[cr]; + cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS); + cblue = Cbbtab[cb]; + /* Fetch 4 Y values and emit 4 pixels */ + y = GETJSAMPLE(*inptr00++); + outptr0[RGB_RED] = range_limit[y + cred]; + outptr0[RGB_GREEN] = range_limit[y + cgreen]; + outptr0[RGB_BLUE] = range_limit[y + cblue]; + outptr0 += RGB_PIXELSIZE; + y = GETJSAMPLE(*inptr00++); + outptr0[RGB_RED] = range_limit[y + cred]; + outptr0[RGB_GREEN] = range_limit[y + cgreen]; + outptr0[RGB_BLUE] = range_limit[y + cblue]; + outptr0 += RGB_PIXELSIZE; + y = GETJSAMPLE(*inptr01++); + outptr1[RGB_RED] = range_limit[y + cred]; + outptr1[RGB_GREEN] = range_limit[y + cgreen]; + outptr1[RGB_BLUE] = range_limit[y + cblue]; + outptr1 += RGB_PIXELSIZE; + y = GETJSAMPLE(*inptr01++); + outptr1[RGB_RED] = range_limit[y + cred]; + outptr1[RGB_GREEN] = range_limit[y + cgreen]; + outptr1[RGB_BLUE] = range_limit[y + cblue]; + outptr1 += RGB_PIXELSIZE; + } + /* If image width is odd, do the last output column separately */ + if (cinfo->output_width & 1) { + cb = GETJSAMPLE(*inptr1); + cr = GETJSAMPLE(*inptr2); + cred = Crrtab[cr]; + cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS); + cblue = Cbbtab[cb]; + y = GETJSAMPLE(*inptr00); + outptr0[RGB_RED] = range_limit[y + cred]; + outptr0[RGB_GREEN] = range_limit[y + cgreen]; + outptr0[RGB_BLUE] = range_limit[y + cblue]; + y = GETJSAMPLE(*inptr01); + outptr1[RGB_RED] = range_limit[y + cred]; + outptr1[RGB_GREEN] = range_limit[y + cgreen]; + outptr1[RGB_BLUE] = range_limit[y + cblue]; + } +} + +/* + * Module initialization routine for merged upsampling/color conversion. + * + * NB: this is called under the conditions determined by use_merged_upsample() + * in jdmaster.c. That routine MUST correspond to the actual capabilities + * of this module; no safety checks are made here. + */ + +GLOBAL(void) +jinit_merged_upsampler (j_decompress_ptr cinfo) +{ + my_upsample_ptr upsample; + + upsample = (my_upsample_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_upsampler)); + cinfo->upsample = (struct jpeg_upsampler *) upsample; + upsample->pub.start_pass = start_pass_merged_upsample; + upsample->pub.need_context_rows = FALSE; + + upsample->out_row_width = cinfo->output_width * cinfo->out_color_components; + + if (cinfo->max_v_samp_factor == 2) { + upsample->pub.upsample = merged_2v_upsample; + upsample->upmethod = h2v2_merged_upsample; + /* Allocate a spare row buffer */ + upsample->spare_row = (JSAMPROW) + (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (size_t) (upsample->out_row_width * SIZEOF(JSAMPLE))); + } else { + upsample->pub.upsample = merged_1v_upsample; + upsample->upmethod = h2v1_merged_upsample; + /* No spare row needed */ + upsample->spare_row = NULL; + } + + build_ycc_rgb_table2(cinfo); +} + +#endif /* UPSAMPLE_MERGING_SUPPORTED */ +/********* End of inlined file: jdmerge.c *********/ + + #undef ASSIGN_STATE + +/********* Start of inlined file: jdphuff.c *********/ +#define JPEG_INTERNALS + +#ifdef D_PROGRESSIVE_SUPPORTED + +/* + * Expanded entropy decoder object for progressive Huffman decoding. + * + * The savable_state subrecord contains fields that change within an MCU, + * but must not be updated permanently until we complete the MCU. + */ + +typedef struct { + unsigned int EOBRUN; /* remaining EOBs in EOBRUN */ + int last_dc_val[MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ +} savable_state3; + +/* This macro is to work around compilers with missing or broken + * structure assignment. You'll need to fix this code if you have + * such a compiler and you change MAX_COMPS_IN_SCAN. + */ + +#ifndef NO_STRUCT_ASSIGN +#define ASSIGN_STATE(dest,src) ((dest) = (src)) +#else +#if MAX_COMPS_IN_SCAN == 4 +#define ASSIGN_STATE(dest,src) \ + ((dest).EOBRUN = (src).EOBRUN, \ + (dest).last_dc_val[0] = (src).last_dc_val[0], \ + (dest).last_dc_val[1] = (src).last_dc_val[1], \ + (dest).last_dc_val[2] = (src).last_dc_val[2], \ + (dest).last_dc_val[3] = (src).last_dc_val[3]) +#endif +#endif + +typedef struct { + struct jpeg_entropy_decoder pub; /* public fields */ + + /* These fields are loaded into local variables at start of each MCU. + * In case of suspension, we exit WITHOUT updating them. + */ + bitread_perm_state bitstate; /* Bit buffer at start of MCU */ + savable_state3 saved; /* Other state at start of MCU */ + + /* These fields are NOT loaded into local working state. */ + unsigned int restarts_to_go; /* MCUs left in this restart interval */ + + /* Pointers to derived tables (these workspaces have image lifespan) */ + d_derived_tbl * derived_tbls[NUM_HUFF_TBLS]; + + d_derived_tbl * ac_derived_tbl; /* active table during an AC scan */ +} phuff_entropy_decoder; + +typedef phuff_entropy_decoder * phuff_entropy_ptr2; + +/* Forward declarations */ +METHODDEF(boolean) decode_mcu_DC_first JPP((j_decompress_ptr cinfo, + JBLOCKROW *MCU_data)); +METHODDEF(boolean) decode_mcu_AC_first JPP((j_decompress_ptr cinfo, + JBLOCKROW *MCU_data)); +METHODDEF(boolean) decode_mcu_DC_refine JPP((j_decompress_ptr cinfo, + JBLOCKROW *MCU_data)); +METHODDEF(boolean) decode_mcu_AC_refine JPP((j_decompress_ptr cinfo, + JBLOCKROW *MCU_data)); + +/* + * Initialize for a Huffman-compressed scan. + */ + +METHODDEF(void) +start_pass_phuff_decoder (j_decompress_ptr cinfo) +{ + phuff_entropy_ptr2 entropy = (phuff_entropy_ptr2) cinfo->entropy; + boolean is_DC_band, bad; + int ci, coefi, tbl; + int *coef_bit_ptr; + jpeg_component_info * compptr; + + is_DC_band = (cinfo->Ss == 0); + + /* Validate scan parameters */ + bad = FALSE; + if (is_DC_band) { + if (cinfo->Se != 0) + bad = TRUE; + } else { + /* need not check Ss/Se < 0 since they came from unsigned bytes */ + if (cinfo->Ss > cinfo->Se || cinfo->Se >= DCTSIZE2) + bad = TRUE; + /* AC scans may have only one component */ + if (cinfo->comps_in_scan != 1) + bad = TRUE; + } + if (cinfo->Ah != 0) { + /* Successive approximation refinement scan: must have Al = Ah-1. */ + if (cinfo->Al != cinfo->Ah-1) + bad = TRUE; + } + if (cinfo->Al > 13) /* need not check for < 0 */ + bad = TRUE; + /* Arguably the maximum Al value should be less than 13 for 8-bit precision, + * but the spec doesn't say so, and we try to be liberal about what we + * accept. Note: large Al values could result in out-of-range DC + * coefficients during early scans, leading to bizarre displays due to + * overflows in the IDCT math. But we won't crash. + */ + if (bad) + ERREXIT4(cinfo, JERR_BAD_PROGRESSION, + cinfo->Ss, cinfo->Se, cinfo->Ah, cinfo->Al); + /* Update progression status, and verify that scan order is legal. + * Note that inter-scan inconsistencies are treated as warnings + * not fatal errors ... not clear if this is right way to behave. + */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + int cindex = cinfo->cur_comp_info[ci]->component_index; + coef_bit_ptr = & cinfo->coef_bits[cindex][0]; + if (!is_DC_band && coef_bit_ptr[0] < 0) /* AC without prior DC scan */ + WARNMS2(cinfo, JWRN_BOGUS_PROGRESSION, cindex, 0); + for (coefi = cinfo->Ss; coefi <= cinfo->Se; coefi++) { + int expected = (coef_bit_ptr[coefi] < 0) ? 0 : coef_bit_ptr[coefi]; + if (cinfo->Ah != expected) + WARNMS2(cinfo, JWRN_BOGUS_PROGRESSION, cindex, coefi); + coef_bit_ptr[coefi] = cinfo->Al; + } + } + + /* Select MCU decoding routine */ + if (cinfo->Ah == 0) { + if (is_DC_band) + entropy->pub.decode_mcu = decode_mcu_DC_first; + else + entropy->pub.decode_mcu = decode_mcu_AC_first; + } else { + if (is_DC_band) + entropy->pub.decode_mcu = decode_mcu_DC_refine; + else + entropy->pub.decode_mcu = decode_mcu_AC_refine; + } + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + /* Make sure requested tables are present, and compute derived tables. + * We may build same derived table more than once, but it's not expensive. + */ + if (is_DC_band) { + if (cinfo->Ah == 0) { /* DC refinement needs no table */ + tbl = compptr->dc_tbl_no; + jpeg_make_d_derived_tbl(cinfo, TRUE, tbl, + & entropy->derived_tbls[tbl]); + } + } else { + tbl = compptr->ac_tbl_no; + jpeg_make_d_derived_tbl(cinfo, FALSE, tbl, + & entropy->derived_tbls[tbl]); + /* remember the single active table */ + entropy->ac_derived_tbl = entropy->derived_tbls[tbl]; + } + /* Initialize DC predictions to 0 */ + entropy->saved.last_dc_val[ci] = 0; + } + + /* Initialize bitread state variables */ + entropy->bitstate.bits_left = 0; + entropy->bitstate.get_buffer = 0; /* unnecessary, but keeps Purify quiet */ + entropy->pub.insufficient_data = FALSE; + + /* Initialize private state variables */ + entropy->saved.EOBRUN = 0; + + /* Initialize restart counter */ + entropy->restarts_to_go = cinfo->restart_interval; +} + +/* + * Check for a restart marker & resynchronize decoder. + * Returns FALSE if must suspend. + */ + +LOCAL(boolean) +process_restartp (j_decompress_ptr cinfo) +{ + phuff_entropy_ptr2 entropy = (phuff_entropy_ptr2) cinfo->entropy; + int ci; + + /* Throw away any unused bits remaining in bit buffer; */ + /* include any full bytes in next_marker's count of discarded bytes */ + cinfo->marker->discarded_bytes += entropy->bitstate.bits_left / 8; + entropy->bitstate.bits_left = 0; + + /* Advance past the RSTn marker */ + if (! (*cinfo->marker->read_restart_marker) (cinfo)) + return FALSE; + + /* Re-initialize DC predictions to 0 */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) + entropy->saved.last_dc_val[ci] = 0; + /* Re-init EOB run count, too */ + entropy->saved.EOBRUN = 0; + + /* Reset restart counter */ + entropy->restarts_to_go = cinfo->restart_interval; + + /* Reset out-of-data flag, unless read_restart_marker left us smack up + * against a marker. In that case we will end up treating the next data + * segment as empty, and we can avoid producing bogus output pixels by + * leaving the flag set. + */ + if (cinfo->unread_marker == 0) + entropy->pub.insufficient_data = FALSE; + + return TRUE; +} + +/* + * Huffman MCU decoding. + * Each of these routines decodes and returns one MCU's worth of + * Huffman-compressed coefficients. + * The coefficients are reordered from zigzag order into natural array order, + * but are not dequantized. + * + * The i'th block of the MCU is stored into the block pointed to by + * MCU_data[i]. WE ASSUME THIS AREA IS INITIALLY ZEROED BY THE CALLER. + * + * We return FALSE if data source requested suspension. In that case no + * changes have been made to permanent state. (Exception: some output + * coefficients may already have been assigned. This is harmless for + * spectral selection, since we'll just re-assign them on the next call. + * Successive approximation AC refinement has to be more careful, however.) + */ + +/* + * MCU decoding for DC initial scan (either spectral selection, + * or first pass of successive approximation). + */ + +METHODDEF(boolean) +decode_mcu_DC_first (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) +{ + phuff_entropy_ptr2 entropy = (phuff_entropy_ptr2) cinfo->entropy; + int Al = cinfo->Al; + register int s, r; + int blkn, ci; + JBLOCKROW block; + BITREAD_STATE_VARS; + savable_state3 state; + d_derived_tbl * tbl; + jpeg_component_info * compptr; + + /* Process restart marker if needed; may have to suspend */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) + if (! process_restartp(cinfo)) + return FALSE; + } + + /* If we've run out of data, just leave the MCU set to zeroes. + * This way, we return uniform gray for the remainder of the segment. + */ + if (! entropy->pub.insufficient_data) { + + /* Load up working state */ + BITREAD_LOAD_STATE(cinfo,entropy->bitstate); + ASSIGN_STATE(state, entropy->saved); + + /* Outer loop handles each block in the MCU */ + + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + block = MCU_data[blkn]; + ci = cinfo->MCU_membership[blkn]; + compptr = cinfo->cur_comp_info[ci]; + tbl = entropy->derived_tbls[compptr->dc_tbl_no]; + + /* Decode a single block's worth of coefficients */ + + /* Section F.2.2.1: decode the DC coefficient difference */ + HUFF_DECODE(s, br_state, tbl, return FALSE, label1); + if (s) { + CHECK_BIT_BUFFER(br_state, s, return FALSE); + r = GET_BITS(s); + s = HUFF_EXTEND(r, s); + } + + /* Convert DC difference to actual value, update last_dc_val */ + s += state.last_dc_val[ci]; + state.last_dc_val[ci] = s; + /* Scale and output the coefficient (assumes jpeg_natural_order[0]=0) */ + (*block)[0] = (JCOEF) (s << Al); + } + + /* Completed MCU, so update state */ + BITREAD_SAVE_STATE(cinfo,entropy->bitstate); + ASSIGN_STATE(entropy->saved, state); + } + + /* Account for restart interval (no-op if not using restarts) */ + entropy->restarts_to_go--; + + return TRUE; +} + +/* + * MCU decoding for AC initial scan (either spectral selection, + * or first pass of successive approximation). + */ + +METHODDEF(boolean) +decode_mcu_AC_first (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) +{ + phuff_entropy_ptr2 entropy = (phuff_entropy_ptr2) cinfo->entropy; + int Se = cinfo->Se; + int Al = cinfo->Al; + register int s, k, r; + unsigned int EOBRUN; + JBLOCKROW block; + BITREAD_STATE_VARS; + d_derived_tbl * tbl; + + /* Process restart marker if needed; may have to suspend */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) + if (! process_restartp(cinfo)) + return FALSE; + } + + /* If we've run out of data, just leave the MCU set to zeroes. + * This way, we return uniform gray for the remainder of the segment. + */ + if (! entropy->pub.insufficient_data) { + + /* Load up working state. + * We can avoid loading/saving bitread state if in an EOB run. + */ + EOBRUN = entropy->saved.EOBRUN; /* only part of saved state we need */ + + /* There is always only one block per MCU */ + + if (EOBRUN > 0) /* if it's a band of zeroes... */ + EOBRUN--; /* ...process it now (we do nothing) */ + else { + BITREAD_LOAD_STATE(cinfo,entropy->bitstate); + block = MCU_data[0]; + tbl = entropy->ac_derived_tbl; + + for (k = cinfo->Ss; k <= Se; k++) { + HUFF_DECODE(s, br_state, tbl, return FALSE, label2); + r = s >> 4; + s &= 15; + if (s) { + k += r; + CHECK_BIT_BUFFER(br_state, s, return FALSE); + r = GET_BITS(s); + s = HUFF_EXTEND(r, s); + /* Scale and output coefficient in natural (dezigzagged) order */ + (*block)[jpeg_natural_order[k]] = (JCOEF) (s << Al); + } else { + if (r == 15) { /* ZRL */ + k += 15; /* skip 15 zeroes in band */ + } else { /* EOBr, run length is 2^r + appended bits */ + EOBRUN = 1 << r; + if (r) { /* EOBr, r > 0 */ + CHECK_BIT_BUFFER(br_state, r, return FALSE); + r = GET_BITS(r); + EOBRUN += r; + } + EOBRUN--; /* this band is processed at this moment */ + break; /* force end-of-band */ + } + } + } + + BITREAD_SAVE_STATE(cinfo,entropy->bitstate); + } + + /* Completed MCU, so update state */ + entropy->saved.EOBRUN = EOBRUN; /* only part of saved state we need */ + } + + /* Account for restart interval (no-op if not using restarts) */ + entropy->restarts_to_go--; + + return TRUE; +} + +/* + * MCU decoding for DC successive approximation refinement scan. + * Note: we assume such scans can be multi-component, although the spec + * is not very clear on the point. + */ + +METHODDEF(boolean) +decode_mcu_DC_refine (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) +{ + phuff_entropy_ptr2 entropy = (phuff_entropy_ptr2) cinfo->entropy; + int p1 = 1 << cinfo->Al; /* 1 in the bit position being coded */ + int blkn; + JBLOCKROW block; + BITREAD_STATE_VARS; + + /* Process restart marker if needed; may have to suspend */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) + if (! process_restartp(cinfo)) + return FALSE; + } + + /* Not worth the cycles to check insufficient_data here, + * since we will not change the data anyway if we read zeroes. + */ + + /* Load up working state */ + BITREAD_LOAD_STATE(cinfo,entropy->bitstate); + + /* Outer loop handles each block in the MCU */ + + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + block = MCU_data[blkn]; + + /* Encoded data is simply the next bit of the two's-complement DC value */ + CHECK_BIT_BUFFER(br_state, 1, return FALSE); + if (GET_BITS(1)) + (*block)[0] |= p1; + /* Note: since we use |=, repeating the assignment later is safe */ + } + + /* Completed MCU, so update state */ + BITREAD_SAVE_STATE(cinfo,entropy->bitstate); + + /* Account for restart interval (no-op if not using restarts) */ + entropy->restarts_to_go--; + + return TRUE; +} + +/* + * MCU decoding for AC successive approximation refinement scan. + */ + +METHODDEF(boolean) +decode_mcu_AC_refine (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) +{ + phuff_entropy_ptr2 entropy = (phuff_entropy_ptr2) cinfo->entropy; + int Se = cinfo->Se; + int p1 = 1 << cinfo->Al; /* 1 in the bit position being coded */ + int m1 = (-1) << cinfo->Al; /* -1 in the bit position being coded */ + register int s, k, r; + unsigned int EOBRUN; + JBLOCKROW block; + JCOEFPTR thiscoef; + BITREAD_STATE_VARS; + d_derived_tbl * tbl; + int num_newnz; + int newnz_pos[DCTSIZE2]; + + /* Process restart marker if needed; may have to suspend */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) + if (! process_restartp(cinfo)) + return FALSE; + } + + /* If we've run out of data, don't modify the MCU. + */ + if (! entropy->pub.insufficient_data) { + + /* Load up working state */ + BITREAD_LOAD_STATE(cinfo,entropy->bitstate); + EOBRUN = entropy->saved.EOBRUN; /* only part of saved state we need */ + + /* There is always only one block per MCU */ + block = MCU_data[0]; + tbl = entropy->ac_derived_tbl; + + /* If we are forced to suspend, we must undo the assignments to any newly + * nonzero coefficients in the block, because otherwise we'd get confused + * next time about which coefficients were already nonzero. + * But we need not undo addition of bits to already-nonzero coefficients; + * instead, we can test the current bit to see if we already did it. + */ + num_newnz = 0; + + /* initialize coefficient loop counter to start of band */ + k = cinfo->Ss; + + if (EOBRUN == 0) { + for (; k <= Se; k++) { + HUFF_DECODE(s, br_state, tbl, goto undoit, label3); + r = s >> 4; + s &= 15; + if (s) { + if (s != 1) /* size of new coef should always be 1 */ + WARNMS(cinfo, JWRN_HUFF_BAD_CODE); + CHECK_BIT_BUFFER(br_state, 1, goto undoit); + if (GET_BITS(1)) + s = p1; /* newly nonzero coef is positive */ + else + s = m1; /* newly nonzero coef is negative */ + } else { + if (r != 15) { + EOBRUN = 1 << r; /* EOBr, run length is 2^r + appended bits */ + if (r) { + CHECK_BIT_BUFFER(br_state, r, goto undoit); + r = GET_BITS(r); + EOBRUN += r; + } + break; /* rest of block is handled by EOB logic */ + } + /* note s = 0 for processing ZRL */ + } + /* Advance over already-nonzero coefs and r still-zero coefs, + * appending correction bits to the nonzeroes. A correction bit is 1 + * if the absolute value of the coefficient must be increased. + */ + do { + thiscoef = *block + jpeg_natural_order[k]; + if (*thiscoef != 0) { + CHECK_BIT_BUFFER(br_state, 1, goto undoit); + if (GET_BITS(1)) { + if ((*thiscoef & p1) == 0) { /* do nothing if already set it */ + if (*thiscoef >= 0) + *thiscoef += p1; + else + *thiscoef += m1; + } + } + } else { + if (--r < 0) + break; /* reached target zero coefficient */ + } + k++; + } while (k <= Se); + if (s) { + int pos = jpeg_natural_order[k]; + /* Output newly nonzero coefficient */ + (*block)[pos] = (JCOEF) s; + /* Remember its position in case we have to suspend */ + newnz_pos[num_newnz++] = pos; + } + } + } + + if (EOBRUN > 0) { + /* Scan any remaining coefficient positions after the end-of-band + * (the last newly nonzero coefficient, if any). Append a correction + * bit to each already-nonzero coefficient. A correction bit is 1 + * if the absolute value of the coefficient must be increased. + */ + for (; k <= Se; k++) { + thiscoef = *block + jpeg_natural_order[k]; + if (*thiscoef != 0) { + CHECK_BIT_BUFFER(br_state, 1, goto undoit); + if (GET_BITS(1)) { + if ((*thiscoef & p1) == 0) { /* do nothing if already changed it */ + if (*thiscoef >= 0) + *thiscoef += p1; + else + *thiscoef += m1; + } + } + } + } + /* Count one block completed in EOB run */ + EOBRUN--; + } + + /* Completed MCU, so update state */ + BITREAD_SAVE_STATE(cinfo,entropy->bitstate); + entropy->saved.EOBRUN = EOBRUN; /* only part of saved state we need */ + } + + /* Account for restart interval (no-op if not using restarts) */ + entropy->restarts_to_go--; + + return TRUE; + +undoit: + /* Re-zero any output coefficients that we made newly nonzero */ + while (num_newnz > 0) + (*block)[newnz_pos[--num_newnz]] = 0; + + return FALSE; +} + +/* + * Module initialization routine for progressive Huffman entropy decoding. + */ + +GLOBAL(void) +jinit_phuff_decoder (j_decompress_ptr cinfo) +{ + phuff_entropy_ptr2 entropy; + int *coef_bit_ptr; + int ci, i; + + entropy = (phuff_entropy_ptr2) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(phuff_entropy_decoder)); + cinfo->entropy = (struct jpeg_entropy_decoder *) entropy; + entropy->pub.start_pass = start_pass_phuff_decoder; + + /* Mark derived tables unallocated */ + for (i = 0; i < NUM_HUFF_TBLS; i++) { + entropy->derived_tbls[i] = NULL; + } + + /* Create progression status table */ + cinfo->coef_bits = (int (*)[DCTSIZE2]) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + cinfo->num_components*DCTSIZE2*SIZEOF(int)); + coef_bit_ptr = & cinfo->coef_bits[0][0]; + for (ci = 0; ci < cinfo->num_components; ci++) + for (i = 0; i < DCTSIZE2; i++) + *coef_bit_ptr++ = -1; +} + +#endif /* D_PROGRESSIVE_SUPPORTED */ +/********* End of inlined file: jdphuff.c *********/ + +/********* Start of inlined file: jdpostct.c *********/ +#define JPEG_INTERNALS + +/* Private buffer controller object */ + +typedef struct { + struct jpeg_d_post_controller pub; /* public fields */ + + /* Color quantization source buffer: this holds output data from + * the upsample/color conversion step to be passed to the quantizer. + * For two-pass color quantization, we need a full-image buffer; + * for one-pass operation, a strip buffer is sufficient. + */ + jvirt_sarray_ptr whole_image; /* virtual array, or NULL if one-pass */ + JSAMPARRAY buffer; /* strip buffer, or current strip of virtual */ + JDIMENSION strip_height; /* buffer size in rows */ + /* for two-pass mode only: */ + JDIMENSION starting_row; /* row # of first row in current strip */ + JDIMENSION next_row; /* index of next row to fill/empty in strip */ +} my_post_controller; + +typedef my_post_controller * my_post_ptr; + +/* Forward declarations */ +METHODDEF(void) post_process_1pass + JPP((j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); +#ifdef QUANT_2PASS_SUPPORTED +METHODDEF(void) post_process_prepass + JPP((j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); +METHODDEF(void) post_process_2pass + JPP((j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); +#endif + +/* + * Initialize for a processing pass. + */ + +METHODDEF(void) +start_pass_dpost (j_decompress_ptr cinfo, J_BUF_MODE pass_mode) +{ + my_post_ptr post = (my_post_ptr) cinfo->post; + + switch (pass_mode) { + case JBUF_PASS_THRU: + if (cinfo->quantize_colors) { + /* Single-pass processing with color quantization. */ + post->pub.post_process_data = post_process_1pass; + /* We could be doing buffered-image output before starting a 2-pass + * color quantization; in that case, jinit_d_post_controller did not + * allocate a strip buffer. Use the virtual-array buffer as workspace. + */ + if (post->buffer == NULL) { + post->buffer = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, post->whole_image, + (JDIMENSION) 0, post->strip_height, TRUE); + } + } else { + /* For single-pass processing without color quantization, + * I have no work to do; just call the upsampler directly. + */ + post->pub.post_process_data = cinfo->upsample->upsample; + } + break; +#ifdef QUANT_2PASS_SUPPORTED + case JBUF_SAVE_AND_PASS: + /* First pass of 2-pass quantization */ + if (post->whole_image == NULL) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + post->pub.post_process_data = post_process_prepass; + break; + case JBUF_CRANK_DEST: + /* Second pass of 2-pass quantization */ + if (post->whole_image == NULL) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + post->pub.post_process_data = post_process_2pass; + break; +#endif /* QUANT_2PASS_SUPPORTED */ + default: + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + break; + } + post->starting_row = post->next_row = 0; +} + +/* + * Process some data in the one-pass (strip buffer) case. + * This is used for color precision reduction as well as one-pass quantization. + */ + +METHODDEF(void) +post_process_1pass (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +{ + my_post_ptr post = (my_post_ptr) cinfo->post; + JDIMENSION num_rows, max_rows; + + /* Fill the buffer, but not more than what we can dump out in one go. */ + /* Note we rely on the upsampler to detect bottom of image. */ + max_rows = out_rows_avail - *out_row_ctr; + if (max_rows > post->strip_height) + max_rows = post->strip_height; + num_rows = 0; + (*cinfo->upsample->upsample) (cinfo, + input_buf, in_row_group_ctr, in_row_groups_avail, + post->buffer, &num_rows, max_rows); + /* Quantize and emit data. */ + (*cinfo->cquantize->color_quantize) (cinfo, + post->buffer, output_buf + *out_row_ctr, (int) num_rows); + *out_row_ctr += num_rows; +} + +#ifdef QUANT_2PASS_SUPPORTED + +/* + * Process some data in the first pass of 2-pass quantization. + */ + +METHODDEF(void) +post_process_prepass (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +{ + my_post_ptr post = (my_post_ptr) cinfo->post; + JDIMENSION old_next_row, num_rows; + + /* Reposition virtual buffer if at start of strip. */ + if (post->next_row == 0) { + post->buffer = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, post->whole_image, + post->starting_row, post->strip_height, TRUE); + } + + /* Upsample some data (up to a strip height's worth). */ + old_next_row = post->next_row; + (*cinfo->upsample->upsample) (cinfo, + input_buf, in_row_group_ctr, in_row_groups_avail, + post->buffer, &post->next_row, post->strip_height); + + /* Allow quantizer to scan new data. No data is emitted, */ + /* but we advance out_row_ctr so outer loop can tell when we're done. */ + if (post->next_row > old_next_row) { + num_rows = post->next_row - old_next_row; + (*cinfo->cquantize->color_quantize) (cinfo, post->buffer + old_next_row, + (JSAMPARRAY) NULL, (int) num_rows); + *out_row_ctr += num_rows; + } + + /* Advance if we filled the strip. */ + if (post->next_row >= post->strip_height) { + post->starting_row += post->strip_height; + post->next_row = 0; + } +} + +/* + * Process some data in the second pass of 2-pass quantization. + */ + +METHODDEF(void) +post_process_2pass (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +{ + my_post_ptr post = (my_post_ptr) cinfo->post; + JDIMENSION num_rows, max_rows; + + /* Reposition virtual buffer if at start of strip. */ + if (post->next_row == 0) { + post->buffer = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, post->whole_image, + post->starting_row, post->strip_height, FALSE); + } + + /* Determine number of rows to emit. */ + num_rows = post->strip_height - post->next_row; /* available in strip */ + max_rows = out_rows_avail - *out_row_ctr; /* available in output area */ + if (num_rows > max_rows) + num_rows = max_rows; + /* We have to check bottom of image here, can't depend on upsampler. */ + max_rows = cinfo->output_height - post->starting_row; + if (num_rows > max_rows) + num_rows = max_rows; + + /* Quantize and emit data. */ + (*cinfo->cquantize->color_quantize) (cinfo, + post->buffer + post->next_row, output_buf + *out_row_ctr, + (int) num_rows); + *out_row_ctr += num_rows; + + /* Advance if we filled the strip. */ + post->next_row += num_rows; + if (post->next_row >= post->strip_height) { + post->starting_row += post->strip_height; + post->next_row = 0; + } +} + +#endif /* QUANT_2PASS_SUPPORTED */ + +/* + * Initialize postprocessing controller. + */ + +GLOBAL(void) +jinit_d_post_controller (j_decompress_ptr cinfo, boolean need_full_buffer) +{ + my_post_ptr post; + + post = (my_post_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_post_controller)); + cinfo->post = (struct jpeg_d_post_controller *) post; + post->pub.start_pass = start_pass_dpost; + post->whole_image = NULL; /* flag for no virtual arrays */ + post->buffer = NULL; /* flag for no strip buffer */ + + /* Create the quantization buffer, if needed */ + if (cinfo->quantize_colors) { + /* The buffer strip height is max_v_samp_factor, which is typically + * an efficient number of rows for upsampling to return. + * (In the presence of output rescaling, we might want to be smarter?) + */ + post->strip_height = (JDIMENSION) cinfo->max_v_samp_factor; + if (need_full_buffer) { + /* Two-pass color quantization: need full-image storage. */ + /* We round up the number of rows to a multiple of the strip height. */ +#ifdef QUANT_2PASS_SUPPORTED + post->whole_image = (*cinfo->mem->request_virt_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, + cinfo->output_width * cinfo->out_color_components, + (JDIMENSION) jround_up((long) cinfo->output_height, + (long) post->strip_height), + post->strip_height); +#else + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); +#endif /* QUANT_2PASS_SUPPORTED */ + } else { + /* One-pass color quantization: just make a strip buffer. */ + post->buffer = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + cinfo->output_width * cinfo->out_color_components, + post->strip_height); + } + } +} +/********* End of inlined file: jdpostct.c *********/ + + #undef FIX + +/********* Start of inlined file: jdsample.c *********/ +#define JPEG_INTERNALS + +/* Pointer to routine to upsample a single component */ +typedef JMETHOD(void, upsample1_ptr, + (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr)); + +/* Private subobject */ + +typedef struct { + struct jpeg_upsampler pub; /* public fields */ + + /* Color conversion buffer. When using separate upsampling and color + * conversion steps, this buffer holds one upsampled row group until it + * has been color converted and output. + * Note: we do not allocate any storage for component(s) which are full-size, + * ie do not need rescaling. The corresponding entry of color_buf[] is + * simply set to point to the input data array, thereby avoiding copying. + */ + JSAMPARRAY color_buf[MAX_COMPONENTS]; + + /* Per-component upsampling method pointers */ + upsample1_ptr methods[MAX_COMPONENTS]; + + int next_row_out; /* counts rows emitted from color_buf */ + JDIMENSION rows_to_go; /* counts rows remaining in image */ + + /* Height of an input row group for each component. */ + int rowgroup_height[MAX_COMPONENTS]; + + /* These arrays save pixel expansion factors so that int_expand need not + * recompute them each time. They are unused for other upsampling methods. + */ + UINT8 h_expand[MAX_COMPONENTS]; + UINT8 v_expand[MAX_COMPONENTS]; +} my_upsampler2; + +typedef my_upsampler2 * my_upsample_ptr2; + +/* + * Initialize for an upsampling pass. + */ + +METHODDEF(void) +start_pass_upsample (j_decompress_ptr cinfo) +{ + my_upsample_ptr2 upsample = (my_upsample_ptr2) cinfo->upsample; + + /* Mark the conversion buffer empty */ + upsample->next_row_out = cinfo->max_v_samp_factor; + /* Initialize total-height counter for detecting bottom of image */ + upsample->rows_to_go = cinfo->output_height; +} + +/* + * Control routine to do upsampling (and color conversion). + * + * In this version we upsample each component independently. + * We upsample one row group into the conversion buffer, then apply + * color conversion a row at a time. + */ + +METHODDEF(void) +sep_upsample (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +{ + my_upsample_ptr2 upsample = (my_upsample_ptr2) cinfo->upsample; + int ci; + jpeg_component_info * compptr; + JDIMENSION num_rows; + + /* Fill the conversion buffer, if it's empty */ + if (upsample->next_row_out >= cinfo->max_v_samp_factor) { + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Invoke per-component upsample method. Notice we pass a POINTER + * to color_buf[ci], so that fullsize_upsample can change it. + */ + (*upsample->methods[ci]) (cinfo, compptr, + input_buf[ci] + (*in_row_group_ctr * upsample->rowgroup_height[ci]), + upsample->color_buf + ci); + } + upsample->next_row_out = 0; + } + + /* Color-convert and emit rows */ + + /* How many we have in the buffer: */ + num_rows = (JDIMENSION) (cinfo->max_v_samp_factor - upsample->next_row_out); + /* Not more than the distance to the end of the image. Need this test + * in case the image height is not a multiple of max_v_samp_factor: + */ + if (num_rows > upsample->rows_to_go) + num_rows = upsample->rows_to_go; + /* And not more than what the client can accept: */ + out_rows_avail -= *out_row_ctr; + if (num_rows > out_rows_avail) + num_rows = out_rows_avail; + + (*cinfo->cconvert->color_convert) (cinfo, upsample->color_buf, + (JDIMENSION) upsample->next_row_out, + output_buf + *out_row_ctr, + (int) num_rows); + + /* Adjust counts */ + *out_row_ctr += num_rows; + upsample->rows_to_go -= num_rows; + upsample->next_row_out += num_rows; + /* When the buffer is emptied, declare this input row group consumed */ + if (upsample->next_row_out >= cinfo->max_v_samp_factor) + (*in_row_group_ctr)++; +} + +/* + * These are the routines invoked by sep_upsample to upsample pixel values + * of a single component. One row group is processed per call. + */ + +/* + * For full-size components, we just make color_buf[ci] point at the + * input buffer, and thus avoid copying any data. Note that this is + * safe only because sep_upsample doesn't declare the input row group + * "consumed" until we are done color converting and emitting it. + */ + +METHODDEF(void) +fullsize_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) +{ + *output_data_ptr = input_data; +} + +/* + * This is a no-op version used for "uninteresting" components. + * These components will not be referenced by color conversion. + */ + +METHODDEF(void) +noop_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) +{ + *output_data_ptr = NULL; /* safety check */ +} + +/* + * This version handles any integral sampling ratios. + * This is not used for typical JPEG files, so it need not be fast. + * Nor, for that matter, is it particularly accurate: the algorithm is + * simple replication of the input pixel onto the corresponding output + * pixels. The hi-falutin sampling literature refers to this as a + * "box filter". A box filter tends to introduce visible artifacts, + * so if you are actually going to use 3:1 or 4:1 sampling ratios + * you would be well advised to improve this code. + */ + +METHODDEF(void) +int_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) +{ + my_upsample_ptr2 upsample = (my_upsample_ptr2) cinfo->upsample; + JSAMPARRAY output_data = *output_data_ptr; + register JSAMPROW inptr, outptr; + register JSAMPLE invalue; + register int h; + JSAMPROW outend; + int h_expand, v_expand; + int inrow, outrow; + + h_expand = upsample->h_expand[compptr->component_index]; + v_expand = upsample->v_expand[compptr->component_index]; + + inrow = outrow = 0; + while (outrow < cinfo->max_v_samp_factor) { + /* Generate one output row with proper horizontal expansion */ + inptr = input_data[inrow]; + outptr = output_data[outrow]; + outend = outptr + cinfo->output_width; + while (outptr < outend) { + invalue = *inptr++; /* don't need GETJSAMPLE() here */ + for (h = h_expand; h > 0; h--) { + *outptr++ = invalue; + } + } + /* Generate any additional output rows by duplicating the first one */ + if (v_expand > 1) { + jcopy_sample_rows(output_data, outrow, output_data, outrow+1, + v_expand-1, cinfo->output_width); + } + inrow++; + outrow += v_expand; + } +} + +/* + * Fast processing for the common case of 2:1 horizontal and 1:1 vertical. + * It's still a box filter. + */ + +METHODDEF(void) +h2v1_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) +{ + JSAMPARRAY output_data = *output_data_ptr; + register JSAMPROW inptr, outptr; + register JSAMPLE invalue; + JSAMPROW outend; + int inrow; + + for (inrow = 0; inrow < cinfo->max_v_samp_factor; inrow++) { + inptr = input_data[inrow]; + outptr = output_data[inrow]; + outend = outptr + cinfo->output_width; + while (outptr < outend) { + invalue = *inptr++; /* don't need GETJSAMPLE() here */ + *outptr++ = invalue; + *outptr++ = invalue; + } + } +} + +/* + * Fast processing for the common case of 2:1 horizontal and 2:1 vertical. + * It's still a box filter. + */ + +METHODDEF(void) +h2v2_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) +{ + JSAMPARRAY output_data = *output_data_ptr; + register JSAMPROW inptr, outptr; + register JSAMPLE invalue; + JSAMPROW outend; + int inrow, outrow; + + inrow = outrow = 0; + while (outrow < cinfo->max_v_samp_factor) { + inptr = input_data[inrow]; + outptr = output_data[outrow]; + outend = outptr + cinfo->output_width; + while (outptr < outend) { + invalue = *inptr++; /* don't need GETJSAMPLE() here */ + *outptr++ = invalue; + *outptr++ = invalue; + } + jcopy_sample_rows(output_data, outrow, output_data, outrow+1, + 1, cinfo->output_width); + inrow++; + outrow += 2; + } +} + +/* + * Fancy processing for the common case of 2:1 horizontal and 1:1 vertical. + * + * The upsampling algorithm is linear interpolation between pixel centers, + * also known as a "triangle filter". This is a good compromise between + * speed and visual quality. The centers of the output pixels are 1/4 and 3/4 + * of the way between input pixel centers. + * + * A note about the "bias" calculations: when rounding fractional values to + * integer, we do not want to always round 0.5 up to the next integer. + * If we did that, we'd introduce a noticeable bias towards larger values. + * Instead, this code is arranged so that 0.5 will be rounded up or down at + * alternate pixel locations (a simple ordered dither pattern). + */ + +METHODDEF(void) +h2v1_fancy_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) +{ + JSAMPARRAY output_data = *output_data_ptr; + register JSAMPROW inptr, outptr; + register int invalue; + register JDIMENSION colctr; + int inrow; + + for (inrow = 0; inrow < cinfo->max_v_samp_factor; inrow++) { + inptr = input_data[inrow]; + outptr = output_data[inrow]; + /* Special case for first column */ + invalue = GETJSAMPLE(*inptr++); + *outptr++ = (JSAMPLE) invalue; + *outptr++ = (JSAMPLE) ((invalue * 3 + GETJSAMPLE(*inptr) + 2) >> 2); + + for (colctr = compptr->downsampled_width - 2; colctr > 0; colctr--) { + /* General case: 3/4 * nearer pixel + 1/4 * further pixel */ + invalue = GETJSAMPLE(*inptr++) * 3; + *outptr++ = (JSAMPLE) ((invalue + GETJSAMPLE(inptr[-2]) + 1) >> 2); + *outptr++ = (JSAMPLE) ((invalue + GETJSAMPLE(*inptr) + 2) >> 2); + } + + /* Special case for last column */ + invalue = GETJSAMPLE(*inptr); + *outptr++ = (JSAMPLE) ((invalue * 3 + GETJSAMPLE(inptr[-1]) + 1) >> 2); + *outptr++ = (JSAMPLE) invalue; + } +} + +/* + * Fancy processing for the common case of 2:1 horizontal and 2:1 vertical. + * Again a triangle filter; see comments for h2v1 case, above. + * + * It is OK for us to reference the adjacent input rows because we demanded + * context from the main buffer controller (see initialization code). + */ + +METHODDEF(void) +h2v2_fancy_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) +{ + JSAMPARRAY output_data = *output_data_ptr; + register JSAMPROW inptr0, inptr1, outptr; +#if BITS_IN_JSAMPLE == 8 + register int thiscolsum, lastcolsum, nextcolsum; +#else + register INT32 thiscolsum, lastcolsum, nextcolsum; +#endif + register JDIMENSION colctr; + int inrow, outrow, v; + + inrow = outrow = 0; + while (outrow < cinfo->max_v_samp_factor) { + for (v = 0; v < 2; v++) { + /* inptr0 points to nearest input row, inptr1 points to next nearest */ + inptr0 = input_data[inrow]; + if (v == 0) /* next nearest is row above */ + inptr1 = input_data[inrow-1]; + else /* next nearest is row below */ + inptr1 = input_data[inrow+1]; + outptr = output_data[outrow++]; + + /* Special case for first column */ + thiscolsum = GETJSAMPLE(*inptr0++) * 3 + GETJSAMPLE(*inptr1++); + nextcolsum = GETJSAMPLE(*inptr0++) * 3 + GETJSAMPLE(*inptr1++); + *outptr++ = (JSAMPLE) ((thiscolsum * 4 + 8) >> 4); + *outptr++ = (JSAMPLE) ((thiscolsum * 3 + nextcolsum + 7) >> 4); + lastcolsum = thiscolsum; thiscolsum = nextcolsum; + + for (colctr = compptr->downsampled_width - 2; colctr > 0; colctr--) { + /* General case: 3/4 * nearer pixel + 1/4 * further pixel in each */ + /* dimension, thus 9/16, 3/16, 3/16, 1/16 overall */ + nextcolsum = GETJSAMPLE(*inptr0++) * 3 + GETJSAMPLE(*inptr1++); + *outptr++ = (JSAMPLE) ((thiscolsum * 3 + lastcolsum + 8) >> 4); + *outptr++ = (JSAMPLE) ((thiscolsum * 3 + nextcolsum + 7) >> 4); + lastcolsum = thiscolsum; thiscolsum = nextcolsum; + } + + /* Special case for last column */ + *outptr++ = (JSAMPLE) ((thiscolsum * 3 + lastcolsum + 8) >> 4); + *outptr++ = (JSAMPLE) ((thiscolsum * 4 + 7) >> 4); + } + inrow++; + } +} + +/* + * Module initialization routine for upsampling. + */ + +GLOBAL(void) +jinit_upsampler (j_decompress_ptr cinfo) +{ + my_upsample_ptr2 upsample; + int ci; + jpeg_component_info * compptr; + boolean need_buffer, do_fancy; + int h_in_group, v_in_group, h_out_group, v_out_group; + + upsample = (my_upsample_ptr2) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_upsampler2)); + cinfo->upsample = (struct jpeg_upsampler *) upsample; + upsample->pub.start_pass = start_pass_upsample; + upsample->pub.upsample = sep_upsample; + upsample->pub.need_context_rows = FALSE; /* until we find out differently */ + + if (cinfo->CCIR601_sampling) /* this isn't supported */ + ERREXIT(cinfo, JERR_CCIR601_NOTIMPL); + + /* jdmainct.c doesn't support context rows when min_DCT_scaled_size = 1, + * so don't ask for it. + */ + do_fancy = cinfo->do_fancy_upsampling && cinfo->min_DCT_scaled_size > 1; + + /* Verify we can handle the sampling factors, select per-component methods, + * and create storage as needed. + */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Compute size of an "input group" after IDCT scaling. This many samples + * are to be converted to max_h_samp_factor * max_v_samp_factor pixels. + */ + h_in_group = (compptr->h_samp_factor * compptr->DCT_scaled_size) / + cinfo->min_DCT_scaled_size; + v_in_group = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + cinfo->min_DCT_scaled_size; + h_out_group = cinfo->max_h_samp_factor; + v_out_group = cinfo->max_v_samp_factor; + upsample->rowgroup_height[ci] = v_in_group; /* save for use later */ + need_buffer = TRUE; + if (! compptr->component_needed) { + /* Don't bother to upsample an uninteresting component. */ + upsample->methods[ci] = noop_upsample; + need_buffer = FALSE; + } else if (h_in_group == h_out_group && v_in_group == v_out_group) { + /* Fullsize components can be processed without any work. */ + upsample->methods[ci] = fullsize_upsample; + need_buffer = FALSE; + } else if (h_in_group * 2 == h_out_group && + v_in_group == v_out_group) { + /* Special cases for 2h1v upsampling */ + if (do_fancy && compptr->downsampled_width > 2) + upsample->methods[ci] = h2v1_fancy_upsample; + else + upsample->methods[ci] = h2v1_upsample; + } else if (h_in_group * 2 == h_out_group && + v_in_group * 2 == v_out_group) { + /* Special cases for 2h2v upsampling */ + if (do_fancy && compptr->downsampled_width > 2) { + upsample->methods[ci] = h2v2_fancy_upsample; + upsample->pub.need_context_rows = TRUE; + } else + upsample->methods[ci] = h2v2_upsample; + } else if ((h_out_group % h_in_group) == 0 && + (v_out_group % v_in_group) == 0) { + /* Generic integral-factors upsampling method */ + upsample->methods[ci] = int_upsample; + upsample->h_expand[ci] = (UINT8) (h_out_group / h_in_group); + upsample->v_expand[ci] = (UINT8) (v_out_group / v_in_group); + } else + ERREXIT(cinfo, JERR_FRACT_SAMPLE_NOTIMPL); + if (need_buffer) { + upsample->color_buf[ci] = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + (JDIMENSION) jround_up((long) cinfo->output_width, + (long) cinfo->max_h_samp_factor), + (JDIMENSION) cinfo->max_v_samp_factor); + } + } +} +/********* End of inlined file: jdsample.c *********/ + +/********* Start of inlined file: jdtrans.c *********/ +#define JPEG_INTERNALS + +/* Forward declarations */ +LOCAL(void) transdecode_master_selection JPP((j_decompress_ptr cinfo)); + +/* + * Read the coefficient arrays from a JPEG file. + * jpeg_read_header must be completed before calling this. + * + * The entire image is read into a set of virtual coefficient-block arrays, + * one per component. The return value is a pointer to the array of + * virtual-array descriptors. These can be manipulated directly via the + * JPEG memory manager, or handed off to jpeg_write_coefficients(). + * To release the memory occupied by the virtual arrays, call + * jpeg_finish_decompress() when done with the data. + * + * An alternative usage is to simply obtain access to the coefficient arrays + * during a buffered-image-mode decompression operation. This is allowed + * after any jpeg_finish_output() call. The arrays can be accessed until + * jpeg_finish_decompress() is called. (Note that any call to the library + * may reposition the arrays, so don't rely on access_virt_barray() results + * to stay valid across library calls.) + * + * Returns NULL if suspended. This case need be checked only if + * a suspending data source is used. + */ + +GLOBAL(jvirt_barray_ptr *) +jpeg_read_coefficients (j_decompress_ptr cinfo) +{ + if (cinfo->global_state == DSTATE_READY) { + /* First call: initialize active modules */ + transdecode_master_selection(cinfo); + cinfo->global_state = DSTATE_RDCOEFS; + } + if (cinfo->global_state == DSTATE_RDCOEFS) { + /* Absorb whole file into the coef buffer */ + for (;;) { + int retcode; + /* Call progress monitor hook if present */ + if (cinfo->progress != NULL) + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + /* Absorb some more input */ + retcode = (*cinfo->inputctl->consume_input) (cinfo); + if (retcode == JPEG_SUSPENDED) + return NULL; + if (retcode == JPEG_REACHED_EOI) + break; + /* Advance progress counter if appropriate */ + if (cinfo->progress != NULL && + (retcode == JPEG_ROW_COMPLETED || retcode == JPEG_REACHED_SOS)) { + if (++cinfo->progress->pass_counter >= cinfo->progress->pass_limit) { + /* startup underestimated number of scans; ratchet up one scan */ + cinfo->progress->pass_limit += (long) cinfo->total_iMCU_rows; + } + } + } + /* Set state so that jpeg_finish_decompress does the right thing */ + cinfo->global_state = DSTATE_STOPPING; + } + /* At this point we should be in state DSTATE_STOPPING if being used + * standalone, or in state DSTATE_BUFIMAGE if being invoked to get access + * to the coefficients during a full buffered-image-mode decompression. + */ + if ((cinfo->global_state == DSTATE_STOPPING || + cinfo->global_state == DSTATE_BUFIMAGE) && cinfo->buffered_image) { + return cinfo->coef->coef_arrays; + } + /* Oops, improper usage */ + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + return NULL; /* keep compiler happy */ +} + +/* + * Master selection of decompression modules for transcoding. + * This substitutes for jdmaster.c's initialization of the full decompressor. + */ + +LOCAL(void) +transdecode_master_selection (j_decompress_ptr cinfo) +{ + /* This is effectively a buffered-image operation. */ + cinfo->buffered_image = TRUE; + + /* Entropy decoding: either Huffman or arithmetic coding. */ + if (cinfo->arith_code) { + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); + } else { + if (cinfo->progressive_mode) { +#ifdef D_PROGRESSIVE_SUPPORTED + jinit_phuff_decoder(cinfo); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else + jinit_huff_decoder(cinfo); + } + + /* Always get a full-image coefficient buffer. */ + jinit_d_coef_controller(cinfo, TRUE); + + /* We can now tell the memory manager to allocate virtual arrays. */ + (*cinfo->mem->realize_virt_arrays) ((j_common_ptr) cinfo); + + /* Initialize input side of decompressor to consume first scan. */ + (*cinfo->inputctl->start_input_pass) (cinfo); + + /* Initialize progress monitoring. */ + if (cinfo->progress != NULL) { + int nscans; + /* Estimate number of scans to set pass_limit. */ + if (cinfo->progressive_mode) { + /* Arbitrarily estimate 2 interleaved DC scans + 3 AC scans/component. */ + nscans = 2 + 3 * cinfo->num_components; + } else if (cinfo->inputctl->has_multiple_scans) { + /* For a nonprogressive multiscan file, estimate 1 scan per component. */ + nscans = cinfo->num_components; + } else { + nscans = 1; + } + cinfo->progress->pass_counter = 0L; + cinfo->progress->pass_limit = (long) cinfo->total_iMCU_rows * nscans; + cinfo->progress->completed_passes = 0; + cinfo->progress->total_passes = 1; + } +} +/********* End of inlined file: jdtrans.c *********/ + +/********* Start of inlined file: jfdctflt.c *********/ +#define JPEG_INTERNALS + +#ifdef DCT_FLOAT_SUPPORTED + +/* + * This module is specialized to the case DCTSIZE = 8. + */ + +#if DCTSIZE != 8 + Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */ +#endif + +/* + * Perform the forward DCT on one block of samples. + */ + +GLOBAL(void) +jpeg_fdct_float (FAST_FLOAT * data) +{ + FAST_FLOAT tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + FAST_FLOAT tmp10, tmp11, tmp12, tmp13; + FAST_FLOAT z1, z2, z3, z4, z5, z11, z13; + FAST_FLOAT *dataptr; + int ctr; + + /* Pass 1: process rows. */ + + dataptr = data; + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + tmp0 = dataptr[0] + dataptr[7]; + tmp7 = dataptr[0] - dataptr[7]; + tmp1 = dataptr[1] + dataptr[6]; + tmp6 = dataptr[1] - dataptr[6]; + tmp2 = dataptr[2] + dataptr[5]; + tmp5 = dataptr[2] - dataptr[5]; + tmp3 = dataptr[3] + dataptr[4]; + tmp4 = dataptr[3] - dataptr[4]; + + /* Even part */ + + tmp10 = tmp0 + tmp3; /* phase 2 */ + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + dataptr[0] = tmp10 + tmp11; /* phase 3 */ + dataptr[4] = tmp10 - tmp11; + + z1 = (tmp12 + tmp13) * ((FAST_FLOAT) 0.707106781); /* c4 */ + dataptr[2] = tmp13 + z1; /* phase 5 */ + dataptr[6] = tmp13 - z1; + + /* Odd part */ + + tmp10 = tmp4 + tmp5; /* phase 2 */ + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + /* The rotator is modified from fig 4-8 to avoid extra negations. */ + z5 = (tmp10 - tmp12) * ((FAST_FLOAT) 0.382683433); /* c6 */ + z2 = ((FAST_FLOAT) 0.541196100) * tmp10 + z5; /* c2-c6 */ + z4 = ((FAST_FLOAT) 1.306562965) * tmp12 + z5; /* c2+c6 */ + z3 = tmp11 * ((FAST_FLOAT) 0.707106781); /* c4 */ + + z11 = tmp7 + z3; /* phase 5 */ + z13 = tmp7 - z3; + + dataptr[5] = z13 + z2; /* phase 6 */ + dataptr[3] = z13 - z2; + dataptr[1] = z11 + z4; + dataptr[7] = z11 - z4; + + dataptr += DCTSIZE; /* advance pointer to next row */ + } + + /* Pass 2: process columns. */ + + dataptr = data; + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + tmp0 = dataptr[DCTSIZE*0] + dataptr[DCTSIZE*7]; + tmp7 = dataptr[DCTSIZE*0] - dataptr[DCTSIZE*7]; + tmp1 = dataptr[DCTSIZE*1] + dataptr[DCTSIZE*6]; + tmp6 = dataptr[DCTSIZE*1] - dataptr[DCTSIZE*6]; + tmp2 = dataptr[DCTSIZE*2] + dataptr[DCTSIZE*5]; + tmp5 = dataptr[DCTSIZE*2] - dataptr[DCTSIZE*5]; + tmp3 = dataptr[DCTSIZE*3] + dataptr[DCTSIZE*4]; + tmp4 = dataptr[DCTSIZE*3] - dataptr[DCTSIZE*4]; + + /* Even part */ + + tmp10 = tmp0 + tmp3; /* phase 2 */ + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + dataptr[DCTSIZE*0] = tmp10 + tmp11; /* phase 3 */ + dataptr[DCTSIZE*4] = tmp10 - tmp11; + + z1 = (tmp12 + tmp13) * ((FAST_FLOAT) 0.707106781); /* c4 */ + dataptr[DCTSIZE*2] = tmp13 + z1; /* phase 5 */ + dataptr[DCTSIZE*6] = tmp13 - z1; + + /* Odd part */ + + tmp10 = tmp4 + tmp5; /* phase 2 */ + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + /* The rotator is modified from fig 4-8 to avoid extra negations. */ + z5 = (tmp10 - tmp12) * ((FAST_FLOAT) 0.382683433); /* c6 */ + z2 = ((FAST_FLOAT) 0.541196100) * tmp10 + z5; /* c2-c6 */ + z4 = ((FAST_FLOAT) 1.306562965) * tmp12 + z5; /* c2+c6 */ + z3 = tmp11 * ((FAST_FLOAT) 0.707106781); /* c4 */ + + z11 = tmp7 + z3; /* phase 5 */ + z13 = tmp7 - z3; + + dataptr[DCTSIZE*5] = z13 + z2; /* phase 6 */ + dataptr[DCTSIZE*3] = z13 - z2; + dataptr[DCTSIZE*1] = z11 + z4; + dataptr[DCTSIZE*7] = z11 - z4; + + dataptr++; /* advance pointer to next column */ + } +} + +#endif /* DCT_FLOAT_SUPPORTED */ +/********* End of inlined file: jfdctflt.c *********/ + +/********* Start of inlined file: jfdctint.c *********/ +#define JPEG_INTERNALS + +#ifdef DCT_ISLOW_SUPPORTED + +/* + * This module is specialized to the case DCTSIZE = 8. + */ + +#if DCTSIZE != 8 + Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */ +#endif + +/* + * The poop on this scaling stuff is as follows: + * + * Each 1-D DCT step produces outputs which are a factor of sqrt(N) + * larger than the true DCT outputs. The final outputs are therefore + * a factor of N larger than desired; since N=8 this can be cured by + * a simple right shift at the end of the algorithm. The advantage of + * this arrangement is that we save two multiplications per 1-D DCT, + * because the y0 and y4 outputs need not be divided by sqrt(N). + * In the IJG code, this factor of 8 is removed by the quantization step + * (in jcdctmgr.c), NOT in this module. + * + * We have to do addition and subtraction of the integer inputs, which + * is no problem, and multiplication by fractional constants, which is + * a problem to do in integer arithmetic. We multiply all the constants + * by CONST_SCALE and convert them to integer constants (thus retaining + * CONST_BITS bits of precision in the constants). After doing a + * multiplication we have to divide the product by CONST_SCALE, with proper + * rounding, to produce the correct output. This division can be done + * cheaply as a right shift of CONST_BITS bits. We postpone shifting + * as long as possible so that partial sums can be added together with + * full fractional precision. + * + * The outputs of the first pass are scaled up by PASS1_BITS bits so that + * they are represented to better-than-integral precision. These outputs + * require BITS_IN_JSAMPLE + PASS1_BITS + 3 bits; this fits in a 16-bit word + * with the recommended scaling. (For 12-bit sample data, the intermediate + * array is INT32 anyway.) + * + * To avoid overflow of the 32-bit intermediate results in pass 2, we must + * have BITS_IN_JSAMPLE + CONST_BITS + PASS1_BITS <= 26. Error analysis + * shows that the values given below are the most effective. + */ + +#if BITS_IN_JSAMPLE == 8 +#define CONST_BITS 13 +#define PASS1_BITS 2 +#else +#define CONST_BITS 13 +#define PASS1_BITS 1 /* lose a little precision to avoid overflow */ +#endif + +/* Some C compilers fail to reduce "FIX(constant)" at compile time, thus + * causing a lot of useless floating-point operations at run time. + * To get around this we use the following pre-calculated constants. + * If you change CONST_BITS you may want to add appropriate values. + * (With a reasonable C compiler, you can just rely on the FIX() macro...) + */ + +#if CONST_BITS == 13 +#define FIX_0_298631336 ((INT32) 2446) /* FIX(0.298631336) */ +#define FIX_0_390180644 ((INT32) 3196) /* FIX(0.390180644) */ +#define FIX_0_541196100 ((INT32) 4433) /* FIX(0.541196100) */ +#define FIX_0_765366865 ((INT32) 6270) /* FIX(0.765366865) */ +#define FIX_0_899976223 ((INT32) 7373) /* FIX(0.899976223) */ +#define FIX_1_175875602 ((INT32) 9633) /* FIX(1.175875602) */ +#define FIX_1_501321110 ((INT32) 12299) /* FIX(1.501321110) */ +#define FIX_1_847759065 ((INT32) 15137) /* FIX(1.847759065) */ +#define FIX_1_961570560 ((INT32) 16069) /* FIX(1.961570560) */ +#define FIX_2_053119869 ((INT32) 16819) /* FIX(2.053119869) */ +#define FIX_2_562915447 ((INT32) 20995) /* FIX(2.562915447) */ +#define FIX_3_072711026 ((INT32) 25172) /* FIX(3.072711026) */ +#else +#define FIX_0_298631336 FIX(0.298631336) +#define FIX_0_390180644 FIX(0.390180644) +#define FIX_0_541196100 FIX(0.541196100) +#define FIX_0_765366865 FIX(0.765366865) +#define FIX_0_899976223 FIX(0.899976223) +#define FIX_1_175875602 FIX(1.175875602) +#define FIX_1_501321110 FIX(1.501321110) +#define FIX_1_847759065 FIX(1.847759065) +#define FIX_1_961570560 FIX(1.961570560) +#define FIX_2_053119869 FIX(2.053119869) +#define FIX_2_562915447 FIX(2.562915447) +#define FIX_3_072711026 FIX(3.072711026) +#endif + +/* Multiply an INT32 variable by an INT32 constant to yield an INT32 result. + * For 8-bit samples with the recommended scaling, all the variable + * and constant values involved are no more than 16 bits wide, so a + * 16x16->32 bit multiply can be used instead of a full 32x32 multiply. + * For 12-bit samples, a full 32-bit multiplication will be needed. + */ + +#if BITS_IN_JSAMPLE == 8 +#define MULTIPLY(var,const) MULTIPLY16C16(var,const) +#else +#define MULTIPLY(var,const) ((var) * (const)) +#endif + +/* + * Perform the forward DCT on one block of samples. + */ + +GLOBAL(void) +jpeg_fdct_islow (DCTELEM * data) +{ + INT32 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + INT32 tmp10, tmp11, tmp12, tmp13; + INT32 z1, z2, z3, z4, z5; + DCTELEM *dataptr; + int ctr; + SHIFT_TEMPS + + /* Pass 1: process rows. */ + /* Note results are scaled up by sqrt(8) compared to a true DCT; */ + /* furthermore, we scale the results by 2**PASS1_BITS. */ + + dataptr = data; + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + tmp0 = dataptr[0] + dataptr[7]; + tmp7 = dataptr[0] - dataptr[7]; + tmp1 = dataptr[1] + dataptr[6]; + tmp6 = dataptr[1] - dataptr[6]; + tmp2 = dataptr[2] + dataptr[5]; + tmp5 = dataptr[2] - dataptr[5]; + tmp3 = dataptr[3] + dataptr[4]; + tmp4 = dataptr[3] - dataptr[4]; + + /* Even part per LL&M figure 1 --- note that published figure is faulty; + * rotator "sqrt(2)*c1" should be "sqrt(2)*c6". + */ + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + dataptr[0] = (DCTELEM) ((tmp10 + tmp11) << PASS1_BITS); + dataptr[4] = (DCTELEM) ((tmp10 - tmp11) << PASS1_BITS); + + z1 = MULTIPLY(tmp12 + tmp13, FIX_0_541196100); + dataptr[2] = (DCTELEM) DESCALE(z1 + MULTIPLY(tmp13, FIX_0_765366865), + CONST_BITS-PASS1_BITS); + dataptr[6] = (DCTELEM) DESCALE(z1 + MULTIPLY(tmp12, - FIX_1_847759065), + CONST_BITS-PASS1_BITS); + + /* Odd part per figure 8 --- note paper omits factor of sqrt(2). + * cK represents cos(K*pi/16). + * i0..i3 in the paper are tmp4..tmp7 here. + */ + + z1 = tmp4 + tmp7; + z2 = tmp5 + tmp6; + z3 = tmp4 + tmp6; + z4 = tmp5 + tmp7; + z5 = MULTIPLY(z3 + z4, FIX_1_175875602); /* sqrt(2) * c3 */ + + tmp4 = MULTIPLY(tmp4, FIX_0_298631336); /* sqrt(2) * (-c1+c3+c5-c7) */ + tmp5 = MULTIPLY(tmp5, FIX_2_053119869); /* sqrt(2) * ( c1+c3-c5+c7) */ + tmp6 = MULTIPLY(tmp6, FIX_3_072711026); /* sqrt(2) * ( c1+c3+c5-c7) */ + tmp7 = MULTIPLY(tmp7, FIX_1_501321110); /* sqrt(2) * ( c1+c3-c5-c7) */ + z1 = MULTIPLY(z1, - FIX_0_899976223); /* sqrt(2) * (c7-c3) */ + z2 = MULTIPLY(z2, - FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ + z3 = MULTIPLY(z3, - FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ + z4 = MULTIPLY(z4, - FIX_0_390180644); /* sqrt(2) * (c5-c3) */ + + z3 += z5; + z4 += z5; + + dataptr[7] = (DCTELEM) DESCALE(tmp4 + z1 + z3, CONST_BITS-PASS1_BITS); + dataptr[5] = (DCTELEM) DESCALE(tmp5 + z2 + z4, CONST_BITS-PASS1_BITS); + dataptr[3] = (DCTELEM) DESCALE(tmp6 + z2 + z3, CONST_BITS-PASS1_BITS); + dataptr[1] = (DCTELEM) DESCALE(tmp7 + z1 + z4, CONST_BITS-PASS1_BITS); + + dataptr += DCTSIZE; /* advance pointer to next row */ + } + + /* Pass 2: process columns. + * We remove the PASS1_BITS scaling, but leave the results scaled up + * by an overall factor of 8. + */ + + dataptr = data; + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + tmp0 = dataptr[DCTSIZE*0] + dataptr[DCTSIZE*7]; + tmp7 = dataptr[DCTSIZE*0] - dataptr[DCTSIZE*7]; + tmp1 = dataptr[DCTSIZE*1] + dataptr[DCTSIZE*6]; + tmp6 = dataptr[DCTSIZE*1] - dataptr[DCTSIZE*6]; + tmp2 = dataptr[DCTSIZE*2] + dataptr[DCTSIZE*5]; + tmp5 = dataptr[DCTSIZE*2] - dataptr[DCTSIZE*5]; + tmp3 = dataptr[DCTSIZE*3] + dataptr[DCTSIZE*4]; + tmp4 = dataptr[DCTSIZE*3] - dataptr[DCTSIZE*4]; + + /* Even part per LL&M figure 1 --- note that published figure is faulty; + * rotator "sqrt(2)*c1" should be "sqrt(2)*c6". + */ + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + dataptr[DCTSIZE*0] = (DCTELEM) DESCALE(tmp10 + tmp11, PASS1_BITS); + dataptr[DCTSIZE*4] = (DCTELEM) DESCALE(tmp10 - tmp11, PASS1_BITS); + + z1 = MULTIPLY(tmp12 + tmp13, FIX_0_541196100); + dataptr[DCTSIZE*2] = (DCTELEM) DESCALE(z1 + MULTIPLY(tmp13, FIX_0_765366865), + CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*6] = (DCTELEM) DESCALE(z1 + MULTIPLY(tmp12, - FIX_1_847759065), + CONST_BITS+PASS1_BITS); + + /* Odd part per figure 8 --- note paper omits factor of sqrt(2). + * cK represents cos(K*pi/16). + * i0..i3 in the paper are tmp4..tmp7 here. + */ + + z1 = tmp4 + tmp7; + z2 = tmp5 + tmp6; + z3 = tmp4 + tmp6; + z4 = tmp5 + tmp7; + z5 = MULTIPLY(z3 + z4, FIX_1_175875602); /* sqrt(2) * c3 */ + + tmp4 = MULTIPLY(tmp4, FIX_0_298631336); /* sqrt(2) * (-c1+c3+c5-c7) */ + tmp5 = MULTIPLY(tmp5, FIX_2_053119869); /* sqrt(2) * ( c1+c3-c5+c7) */ + tmp6 = MULTIPLY(tmp6, FIX_3_072711026); /* sqrt(2) * ( c1+c3+c5-c7) */ + tmp7 = MULTIPLY(tmp7, FIX_1_501321110); /* sqrt(2) * ( c1+c3-c5-c7) */ + z1 = MULTIPLY(z1, - FIX_0_899976223); /* sqrt(2) * (c7-c3) */ + z2 = MULTIPLY(z2, - FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ + z3 = MULTIPLY(z3, - FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ + z4 = MULTIPLY(z4, - FIX_0_390180644); /* sqrt(2) * (c5-c3) */ + + z3 += z5; + z4 += z5; + + dataptr[DCTSIZE*7] = (DCTELEM) DESCALE(tmp4 + z1 + z3, + CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*5] = (DCTELEM) DESCALE(tmp5 + z2 + z4, + CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*3] = (DCTELEM) DESCALE(tmp6 + z2 + z3, + CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*1] = (DCTELEM) DESCALE(tmp7 + z1 + z4, + CONST_BITS+PASS1_BITS); + + dataptr++; /* advance pointer to next column */ + } +} + +#endif /* DCT_ISLOW_SUPPORTED */ +/********* End of inlined file: jfdctint.c *********/ + + #undef CONST_BITS + #undef MULTIPLY + #undef FIX_0_541196100 + +/********* Start of inlined file: jfdctfst.c *********/ +#define JPEG_INTERNALS + +#ifdef DCT_IFAST_SUPPORTED + +/* + * This module is specialized to the case DCTSIZE = 8. + */ + +#if DCTSIZE != 8 + Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */ +#endif + +/* Scaling decisions are generally the same as in the LL&M algorithm; + * see jfdctint.c for more details. However, we choose to descale + * (right shift) multiplication products as soon as they are formed, + * rather than carrying additional fractional bits into subsequent additions. + * This compromises accuracy slightly, but it lets us save a few shifts. + * More importantly, 16-bit arithmetic is then adequate (for 8-bit samples) + * everywhere except in the multiplications proper; this saves a good deal + * of work on 16-bit-int machines. + * + * Again to save a few shifts, the intermediate results between pass 1 and + * pass 2 are not upscaled, but are represented only to integral precision. + * + * A final compromise is to represent the multiplicative constants to only + * 8 fractional bits, rather than 13. This saves some shifting work on some + * machines, and may also reduce the cost of multiplication (since there + * are fewer one-bits in the constants). + */ + +#define CONST_BITS 8 + +/* Some C compilers fail to reduce "FIX(constant)" at compile time, thus + * causing a lot of useless floating-point operations at run time. + * To get around this we use the following pre-calculated constants. + * If you change CONST_BITS you may want to add appropriate values. + * (With a reasonable C compiler, you can just rely on the FIX() macro...) + */ + +#if CONST_BITS == 8 +#define FIX_0_382683433 ((INT32) 98) /* FIX(0.382683433) */ +#define FIX_0_541196100 ((INT32) 139) /* FIX(0.541196100) */ +#define FIX_0_707106781 ((INT32) 181) /* FIX(0.707106781) */ +#define FIX_1_306562965 ((INT32) 334) /* FIX(1.306562965) */ +#else +#define FIX_0_382683433 FIX(0.382683433) +#define FIX_0_541196100 FIX(0.541196100) +#define FIX_0_707106781 FIX(0.707106781) +#define FIX_1_306562965 FIX(1.306562965) +#endif + +/* We can gain a little more speed, with a further compromise in accuracy, + * by omitting the addition in a descaling shift. This yields an incorrectly + * rounded result half the time... + */ + +#ifndef USE_ACCURATE_ROUNDING +#undef DESCALE +#define DESCALE(x,n) RIGHT_SHIFT(x, n) +#endif + +/* Multiply a DCTELEM variable by an INT32 constant, and immediately + * descale to yield a DCTELEM result. + */ + +#define MULTIPLY(var,const) ((DCTELEM) DESCALE((var) * (const), CONST_BITS)) + +/* + * Perform the forward DCT on one block of samples. + */ + +GLOBAL(void) +jpeg_fdct_ifast (DCTELEM * data) +{ + DCTELEM tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + DCTELEM tmp10, tmp11, tmp12, tmp13; + DCTELEM z1, z2, z3, z4, z5, z11, z13; + DCTELEM *dataptr; + int ctr; + SHIFT_TEMPS + + /* Pass 1: process rows. */ + + dataptr = data; + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + tmp0 = dataptr[0] + dataptr[7]; + tmp7 = dataptr[0] - dataptr[7]; + tmp1 = dataptr[1] + dataptr[6]; + tmp6 = dataptr[1] - dataptr[6]; + tmp2 = dataptr[2] + dataptr[5]; + tmp5 = dataptr[2] - dataptr[5]; + tmp3 = dataptr[3] + dataptr[4]; + tmp4 = dataptr[3] - dataptr[4]; + + /* Even part */ + + tmp10 = tmp0 + tmp3; /* phase 2 */ + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + dataptr[0] = tmp10 + tmp11; /* phase 3 */ + dataptr[4] = tmp10 - tmp11; + + z1 = MULTIPLY(tmp12 + tmp13, FIX_0_707106781); /* c4 */ + dataptr[2] = tmp13 + z1; /* phase 5 */ + dataptr[6] = tmp13 - z1; + + /* Odd part */ + + tmp10 = tmp4 + tmp5; /* phase 2 */ + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + /* The rotator is modified from fig 4-8 to avoid extra negations. */ + z5 = MULTIPLY(tmp10 - tmp12, FIX_0_382683433); /* c6 */ + z2 = MULTIPLY(tmp10, FIX_0_541196100) + z5; /* c2-c6 */ + z4 = MULTIPLY(tmp12, FIX_1_306562965) + z5; /* c2+c6 */ + z3 = MULTIPLY(tmp11, FIX_0_707106781); /* c4 */ + + z11 = tmp7 + z3; /* phase 5 */ + z13 = tmp7 - z3; + + dataptr[5] = z13 + z2; /* phase 6 */ + dataptr[3] = z13 - z2; + dataptr[1] = z11 + z4; + dataptr[7] = z11 - z4; + + dataptr += DCTSIZE; /* advance pointer to next row */ + } + + /* Pass 2: process columns. */ + + dataptr = data; + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + tmp0 = dataptr[DCTSIZE*0] + dataptr[DCTSIZE*7]; + tmp7 = dataptr[DCTSIZE*0] - dataptr[DCTSIZE*7]; + tmp1 = dataptr[DCTSIZE*1] + dataptr[DCTSIZE*6]; + tmp6 = dataptr[DCTSIZE*1] - dataptr[DCTSIZE*6]; + tmp2 = dataptr[DCTSIZE*2] + dataptr[DCTSIZE*5]; + tmp5 = dataptr[DCTSIZE*2] - dataptr[DCTSIZE*5]; + tmp3 = dataptr[DCTSIZE*3] + dataptr[DCTSIZE*4]; + tmp4 = dataptr[DCTSIZE*3] - dataptr[DCTSIZE*4]; + + /* Even part */ + + tmp10 = tmp0 + tmp3; /* phase 2 */ + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + dataptr[DCTSIZE*0] = tmp10 + tmp11; /* phase 3 */ + dataptr[DCTSIZE*4] = tmp10 - tmp11; + + z1 = MULTIPLY(tmp12 + tmp13, FIX_0_707106781); /* c4 */ + dataptr[DCTSIZE*2] = tmp13 + z1; /* phase 5 */ + dataptr[DCTSIZE*6] = tmp13 - z1; + + /* Odd part */ + + tmp10 = tmp4 + tmp5; /* phase 2 */ + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + /* The rotator is modified from fig 4-8 to avoid extra negations. */ + z5 = MULTIPLY(tmp10 - tmp12, FIX_0_382683433); /* c6 */ + z2 = MULTIPLY(tmp10, FIX_0_541196100) + z5; /* c2-c6 */ + z4 = MULTIPLY(tmp12, FIX_1_306562965) + z5; /* c2+c6 */ + z3 = MULTIPLY(tmp11, FIX_0_707106781); /* c4 */ + + z11 = tmp7 + z3; /* phase 5 */ + z13 = tmp7 - z3; + + dataptr[DCTSIZE*5] = z13 + z2; /* phase 6 */ + dataptr[DCTSIZE*3] = z13 - z2; + dataptr[DCTSIZE*1] = z11 + z4; + dataptr[DCTSIZE*7] = z11 - z4; + + dataptr++; /* advance pointer to next column */ + } +} + +#endif /* DCT_IFAST_SUPPORTED */ +/********* End of inlined file: jfdctfst.c *********/ + + #undef FIX_0_541196100 + +/********* Start of inlined file: jidctflt.c *********/ +#define JPEG_INTERNALS + +#ifdef DCT_FLOAT_SUPPORTED + +/* + * This module is specialized to the case DCTSIZE = 8. + */ + +#if DCTSIZE != 8 + Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */ +#endif + +/* Dequantize a coefficient by multiplying it by the multiplier-table + * entry; produce a float result. + */ + +#define DEQUANTIZE(coef,quantval) (((FAST_FLOAT) (coef)) * (quantval)) + +/* + * Perform dequantization and inverse DCT on one block of coefficients. + */ + +GLOBAL(void) +jpeg_idct_float (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + FAST_FLOAT tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + FAST_FLOAT tmp10, tmp11, tmp12, tmp13; + FAST_FLOAT z5, z10, z11, z12, z13; + JCOEFPTR inptr; + FLOAT_MULT_TYPE * quantptr; + FAST_FLOAT * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + FAST_FLOAT workspace[DCTSIZE2]; /* buffers data between passes */ + SHIFT_TEMPS + + /* Pass 1: process columns from input, store into work array. */ + + inptr = coef_block; + quantptr = (FLOAT_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = DCTSIZE; ctr > 0; ctr--) { + /* Due to quantization, we will usually find that many of the input + * coefficients are zero, especially the AC terms. We can exploit this + * by short-circuiting the IDCT calculation for any column in which all + * the AC terms are zero. In that case each output is equal to the + * DC coefficient (with scale factor as needed). + * With typical images and quantization tables, half or more of the + * column DCT calculations can be simplified this way. + */ + + if (inptr[DCTSIZE*1] == 0 && inptr[DCTSIZE*2] == 0 && + inptr[DCTSIZE*3] == 0 && inptr[DCTSIZE*4] == 0 && + inptr[DCTSIZE*5] == 0 && inptr[DCTSIZE*6] == 0 && + inptr[DCTSIZE*7] == 0) { + /* AC terms all zero */ + FAST_FLOAT dcval = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + + wsptr[DCTSIZE*0] = dcval; + wsptr[DCTSIZE*1] = dcval; + wsptr[DCTSIZE*2] = dcval; + wsptr[DCTSIZE*3] = dcval; + wsptr[DCTSIZE*4] = dcval; + wsptr[DCTSIZE*5] = dcval; + wsptr[DCTSIZE*6] = dcval; + wsptr[DCTSIZE*7] = dcval; + + inptr++; /* advance pointers to next column */ + quantptr++; + wsptr++; + continue; + } + + /* Even part */ + + tmp0 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + tmp1 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + tmp2 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); + tmp3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); + + tmp10 = tmp0 + tmp2; /* phase 3 */ + tmp11 = tmp0 - tmp2; + + tmp13 = tmp1 + tmp3; /* phases 5-3 */ + tmp12 = (tmp1 - tmp3) * ((FAST_FLOAT) 1.414213562) - tmp13; /* 2*c4 */ + + tmp0 = tmp10 + tmp13; /* phase 2 */ + tmp3 = tmp10 - tmp13; + tmp1 = tmp11 + tmp12; + tmp2 = tmp11 - tmp12; + + /* Odd part */ + + tmp4 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + tmp5 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + tmp6 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + tmp7 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); + + z13 = tmp6 + tmp5; /* phase 6 */ + z10 = tmp6 - tmp5; + z11 = tmp4 + tmp7; + z12 = tmp4 - tmp7; + + tmp7 = z11 + z13; /* phase 5 */ + tmp11 = (z11 - z13) * ((FAST_FLOAT) 1.414213562); /* 2*c4 */ + + z5 = (z10 + z12) * ((FAST_FLOAT) 1.847759065); /* 2*c2 */ + tmp10 = ((FAST_FLOAT) 1.082392200) * z12 - z5; /* 2*(c2-c6) */ + tmp12 = ((FAST_FLOAT) -2.613125930) * z10 + z5; /* -2*(c2+c6) */ + + tmp6 = tmp12 - tmp7; /* phase 2 */ + tmp5 = tmp11 - tmp6; + tmp4 = tmp10 + tmp5; + + wsptr[DCTSIZE*0] = tmp0 + tmp7; + wsptr[DCTSIZE*7] = tmp0 - tmp7; + wsptr[DCTSIZE*1] = tmp1 + tmp6; + wsptr[DCTSIZE*6] = tmp1 - tmp6; + wsptr[DCTSIZE*2] = tmp2 + tmp5; + wsptr[DCTSIZE*5] = tmp2 - tmp5; + wsptr[DCTSIZE*4] = tmp3 + tmp4; + wsptr[DCTSIZE*3] = tmp3 - tmp4; + + inptr++; /* advance pointers to next column */ + quantptr++; + wsptr++; + } + + /* Pass 2: process rows from work array, store into output array. */ + /* Note that we must descale the results by a factor of 8 == 2**3. */ + + wsptr = workspace; + for (ctr = 0; ctr < DCTSIZE; ctr++) { + outptr = output_buf[ctr] + output_col; + /* Rows of zeroes can be exploited in the same way as we did with columns. + * However, the column calculation has created many nonzero AC terms, so + * the simplification applies less often (typically 5% to 10% of the time). + * And testing floats for zero is relatively expensive, so we don't bother. + */ + + /* Even part */ + + tmp10 = wsptr[0] + wsptr[4]; + tmp11 = wsptr[0] - wsptr[4]; + + tmp13 = wsptr[2] + wsptr[6]; + tmp12 = (wsptr[2] - wsptr[6]) * ((FAST_FLOAT) 1.414213562) - tmp13; + + tmp0 = tmp10 + tmp13; + tmp3 = tmp10 - tmp13; + tmp1 = tmp11 + tmp12; + tmp2 = tmp11 - tmp12; + + /* Odd part */ + + z13 = wsptr[5] + wsptr[3]; + z10 = wsptr[5] - wsptr[3]; + z11 = wsptr[1] + wsptr[7]; + z12 = wsptr[1] - wsptr[7]; + + tmp7 = z11 + z13; + tmp11 = (z11 - z13) * ((FAST_FLOAT) 1.414213562); + + z5 = (z10 + z12) * ((FAST_FLOAT) 1.847759065); /* 2*c2 */ + tmp10 = ((FAST_FLOAT) 1.082392200) * z12 - z5; /* 2*(c2-c6) */ + tmp12 = ((FAST_FLOAT) -2.613125930) * z10 + z5; /* -2*(c2+c6) */ + + tmp6 = tmp12 - tmp7; + tmp5 = tmp11 - tmp6; + tmp4 = tmp10 + tmp5; + + /* Final output stage: scale down by a factor of 8 and range-limit */ + + outptr[0] = range_limit[(int) DESCALE((INT32) (tmp0 + tmp7), 3) + & RANGE_MASK]; + outptr[7] = range_limit[(int) DESCALE((INT32) (tmp0 - tmp7), 3) + & RANGE_MASK]; + outptr[1] = range_limit[(int) DESCALE((INT32) (tmp1 + tmp6), 3) + & RANGE_MASK]; + outptr[6] = range_limit[(int) DESCALE((INT32) (tmp1 - tmp6), 3) + & RANGE_MASK]; + outptr[2] = range_limit[(int) DESCALE((INT32) (tmp2 + tmp5), 3) + & RANGE_MASK]; + outptr[5] = range_limit[(int) DESCALE((INT32) (tmp2 - tmp5), 3) + & RANGE_MASK]; + outptr[4] = range_limit[(int) DESCALE((INT32) (tmp3 + tmp4), 3) + & RANGE_MASK]; + outptr[3] = range_limit[(int) DESCALE((INT32) (tmp3 - tmp4), 3) + & RANGE_MASK]; + + wsptr += DCTSIZE; /* advance pointer to next row */ + } +} + +#endif /* DCT_FLOAT_SUPPORTED */ +/********* End of inlined file: jidctflt.c *********/ + + #undef CONST_BITS + #undef FIX_1_847759065 + #undef MULTIPLY + #undef DEQUANTIZE + #undef DESCALE + +/********* Start of inlined file: jidctfst.c *********/ +#define JPEG_INTERNALS + +#ifdef DCT_IFAST_SUPPORTED + +/* + * This module is specialized to the case DCTSIZE = 8. + */ + +#if DCTSIZE != 8 + Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */ +#endif + +/* Scaling decisions are generally the same as in the LL&M algorithm; + * see jidctint.c for more details. However, we choose to descale + * (right shift) multiplication products as soon as they are formed, + * rather than carrying additional fractional bits into subsequent additions. + * This compromises accuracy slightly, but it lets us save a few shifts. + * More importantly, 16-bit arithmetic is then adequate (for 8-bit samples) + * everywhere except in the multiplications proper; this saves a good deal + * of work on 16-bit-int machines. + * + * The dequantized coefficients are not integers because the AA&N scaling + * factors have been incorporated. We represent them scaled up by PASS1_BITS, + * so that the first and second IDCT rounds have the same input scaling. + * For 8-bit JSAMPLEs, we choose IFAST_SCALE_BITS = PASS1_BITS so as to + * avoid a descaling shift; this compromises accuracy rather drastically + * for small quantization table entries, but it saves a lot of shifts. + * For 12-bit JSAMPLEs, there's no hope of using 16x16 multiplies anyway, + * so we use a much larger scaling factor to preserve accuracy. + * + * A final compromise is to represent the multiplicative constants to only + * 8 fractional bits, rather than 13. This saves some shifting work on some + * machines, and may also reduce the cost of multiplication (since there + * are fewer one-bits in the constants). + */ + +#if BITS_IN_JSAMPLE == 8 +#define CONST_BITS 8 +#define PASS1_BITS 2 +#else +#define CONST_BITS 8 +#define PASS1_BITS 1 /* lose a little precision to avoid overflow */ +#endif + +/* Some C compilers fail to reduce "FIX(constant)" at compile time, thus + * causing a lot of useless floating-point operations at run time. + * To get around this we use the following pre-calculated constants. + * If you change CONST_BITS you may want to add appropriate values. + * (With a reasonable C compiler, you can just rely on the FIX() macro...) + */ + +#if CONST_BITS == 8 +#define FIX_1_082392200 ((INT32) 277) /* FIX(1.082392200) */ +#define FIX_1_414213562 ((INT32) 362) /* FIX(1.414213562) */ +#define FIX_1_847759065 ((INT32) 473) /* FIX(1.847759065) */ +#define FIX_2_613125930 ((INT32) 669) /* FIX(2.613125930) */ +#else +#define FIX_1_082392200 FIX(1.082392200) +#define FIX_1_414213562 FIX(1.414213562) +#define FIX_1_847759065 FIX(1.847759065) +#define FIX_2_613125930 FIX(2.613125930) +#endif + +/* We can gain a little more speed, with a further compromise in accuracy, + * by omitting the addition in a descaling shift. This yields an incorrectly + * rounded result half the time... + */ + +#ifndef USE_ACCURATE_ROUNDING +#undef DESCALE +#define DESCALE(x,n) RIGHT_SHIFT(x, n) +#endif + +/* Multiply a DCTELEM variable by an INT32 constant, and immediately + * descale to yield a DCTELEM result. + */ + +#define MULTIPLY(var,const) ((DCTELEM) DESCALE((var) * (const), CONST_BITS)) + +/* Dequantize a coefficient by multiplying it by the multiplier-table + * entry; produce a DCTELEM result. For 8-bit data a 16x16->16 + * multiplication will do. For 12-bit data, the multiplier table is + * declared INT32, so a 32-bit multiply will be used. + */ + +#if BITS_IN_JSAMPLE == 8 +#define DEQUANTIZE(coef,quantval) (((IFAST_MULT_TYPE) (coef)) * (quantval)) +#else +#define DEQUANTIZE(coef,quantval) \ + DESCALE((coef)*(quantval), IFAST_SCALE_BITS-PASS1_BITS) +#endif + +/* Like DESCALE, but applies to a DCTELEM and produces an int. + * We assume that int right shift is unsigned if INT32 right shift is. + */ + +#ifdef RIGHT_SHIFT_IS_UNSIGNED +#define ISHIFT_TEMPS DCTELEM ishift_temp; +#if BITS_IN_JSAMPLE == 8 +#define DCTELEMBITS 16 /* DCTELEM may be 16 or 32 bits */ +#else +#define DCTELEMBITS 32 /* DCTELEM must be 32 bits */ +#endif +#define IRIGHT_SHIFT(x,shft) \ + ((ishift_temp = (x)) < 0 ? \ + (ishift_temp >> (shft)) | ((~((DCTELEM) 0)) << (DCTELEMBITS-(shft))) : \ + (ishift_temp >> (shft))) +#else +#define ISHIFT_TEMPS +#define IRIGHT_SHIFT(x,shft) ((x) >> (shft)) +#endif + +#ifdef USE_ACCURATE_ROUNDING +#define IDESCALE(x,n) ((int) IRIGHT_SHIFT((x) + (1 << ((n)-1)), n)) +#else +#define IDESCALE(x,n) ((int) IRIGHT_SHIFT(x, n)) +#endif + +/* + * Perform dequantization and inverse DCT on one block of coefficients. + */ + +GLOBAL(void) +jpeg_idct_ifast (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + DCTELEM tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + DCTELEM tmp10, tmp11, tmp12, tmp13; + DCTELEM z5, z10, z11, z12, z13; + JCOEFPTR inptr; + IFAST_MULT_TYPE * quantptr; + int * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + int workspace[DCTSIZE2]; /* buffers data between passes */ + SHIFT_TEMPS /* for DESCALE */ + ISHIFT_TEMPS /* for IDESCALE */ + + /* Pass 1: process columns from input, store into work array. */ + + inptr = coef_block; + quantptr = (IFAST_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = DCTSIZE; ctr > 0; ctr--) { + /* Due to quantization, we will usually find that many of the input + * coefficients are zero, especially the AC terms. We can exploit this + * by short-circuiting the IDCT calculation for any column in which all + * the AC terms are zero. In that case each output is equal to the + * DC coefficient (with scale factor as needed). + * With typical images and quantization tables, half or more of the + * column DCT calculations can be simplified this way. + */ + + if (inptr[DCTSIZE*1] == 0 && inptr[DCTSIZE*2] == 0 && + inptr[DCTSIZE*3] == 0 && inptr[DCTSIZE*4] == 0 && + inptr[DCTSIZE*5] == 0 && inptr[DCTSIZE*6] == 0 && + inptr[DCTSIZE*7] == 0) { + /* AC terms all zero */ + int dcval = (int) DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + + wsptr[DCTSIZE*0] = dcval; + wsptr[DCTSIZE*1] = dcval; + wsptr[DCTSIZE*2] = dcval; + wsptr[DCTSIZE*3] = dcval; + wsptr[DCTSIZE*4] = dcval; + wsptr[DCTSIZE*5] = dcval; + wsptr[DCTSIZE*6] = dcval; + wsptr[DCTSIZE*7] = dcval; + + inptr++; /* advance pointers to next column */ + quantptr++; + wsptr++; + continue; + } + + /* Even part */ + + tmp0 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + tmp1 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + tmp2 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); + tmp3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); + + tmp10 = tmp0 + tmp2; /* phase 3 */ + tmp11 = tmp0 - tmp2; + + tmp13 = tmp1 + tmp3; /* phases 5-3 */ + tmp12 = MULTIPLY(tmp1 - tmp3, FIX_1_414213562) - tmp13; /* 2*c4 */ + + tmp0 = tmp10 + tmp13; /* phase 2 */ + tmp3 = tmp10 - tmp13; + tmp1 = tmp11 + tmp12; + tmp2 = tmp11 - tmp12; + + /* Odd part */ + + tmp4 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + tmp5 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + tmp6 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + tmp7 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); + + z13 = tmp6 + tmp5; /* phase 6 */ + z10 = tmp6 - tmp5; + z11 = tmp4 + tmp7; + z12 = tmp4 - tmp7; + + tmp7 = z11 + z13; /* phase 5 */ + tmp11 = MULTIPLY(z11 - z13, FIX_1_414213562); /* 2*c4 */ + + z5 = MULTIPLY(z10 + z12, FIX_1_847759065); /* 2*c2 */ + tmp10 = MULTIPLY(z12, FIX_1_082392200) - z5; /* 2*(c2-c6) */ + tmp12 = MULTIPLY(z10, - FIX_2_613125930) + z5; /* -2*(c2+c6) */ + + tmp6 = tmp12 - tmp7; /* phase 2 */ + tmp5 = tmp11 - tmp6; + tmp4 = tmp10 + tmp5; + + wsptr[DCTSIZE*0] = (int) (tmp0 + tmp7); + wsptr[DCTSIZE*7] = (int) (tmp0 - tmp7); + wsptr[DCTSIZE*1] = (int) (tmp1 + tmp6); + wsptr[DCTSIZE*6] = (int) (tmp1 - tmp6); + wsptr[DCTSIZE*2] = (int) (tmp2 + tmp5); + wsptr[DCTSIZE*5] = (int) (tmp2 - tmp5); + wsptr[DCTSIZE*4] = (int) (tmp3 + tmp4); + wsptr[DCTSIZE*3] = (int) (tmp3 - tmp4); + + inptr++; /* advance pointers to next column */ + quantptr++; + wsptr++; + } + + /* Pass 2: process rows from work array, store into output array. */ + /* Note that we must descale the results by a factor of 8 == 2**3, */ + /* and also undo the PASS1_BITS scaling. */ + + wsptr = workspace; + for (ctr = 0; ctr < DCTSIZE; ctr++) { + outptr = output_buf[ctr] + output_col; + /* Rows of zeroes can be exploited in the same way as we did with columns. + * However, the column calculation has created many nonzero AC terms, so + * the simplification applies less often (typically 5% to 10% of the time). + * On machines with very fast multiplication, it's possible that the + * test takes more time than it's worth. In that case this section + * may be commented out. + */ + +#ifndef NO_ZERO_ROW_TEST + if (wsptr[1] == 0 && wsptr[2] == 0 && wsptr[3] == 0 && wsptr[4] == 0 && + wsptr[5] == 0 && wsptr[6] == 0 && wsptr[7] == 0) { + /* AC terms all zero */ + JSAMPLE dcval = range_limit[IDESCALE(wsptr[0], PASS1_BITS+3) + & RANGE_MASK]; + + outptr[0] = dcval; + outptr[1] = dcval; + outptr[2] = dcval; + outptr[3] = dcval; + outptr[4] = dcval; + outptr[5] = dcval; + outptr[6] = dcval; + outptr[7] = dcval; + + wsptr += DCTSIZE; /* advance pointer to next row */ + continue; + } +#endif + + /* Even part */ + + tmp10 = ((DCTELEM) wsptr[0] + (DCTELEM) wsptr[4]); + tmp11 = ((DCTELEM) wsptr[0] - (DCTELEM) wsptr[4]); + + tmp13 = ((DCTELEM) wsptr[2] + (DCTELEM) wsptr[6]); + tmp12 = MULTIPLY((DCTELEM) wsptr[2] - (DCTELEM) wsptr[6], FIX_1_414213562) + - tmp13; + + tmp0 = tmp10 + tmp13; + tmp3 = tmp10 - tmp13; + tmp1 = tmp11 + tmp12; + tmp2 = tmp11 - tmp12; + + /* Odd part */ + + z13 = (DCTELEM) wsptr[5] + (DCTELEM) wsptr[3]; + z10 = (DCTELEM) wsptr[5] - (DCTELEM) wsptr[3]; + z11 = (DCTELEM) wsptr[1] + (DCTELEM) wsptr[7]; + z12 = (DCTELEM) wsptr[1] - (DCTELEM) wsptr[7]; + + tmp7 = z11 + z13; /* phase 5 */ + tmp11 = MULTIPLY(z11 - z13, FIX_1_414213562); /* 2*c4 */ + + z5 = MULTIPLY(z10 + z12, FIX_1_847759065); /* 2*c2 */ + tmp10 = MULTIPLY(z12, FIX_1_082392200) - z5; /* 2*(c2-c6) */ + tmp12 = MULTIPLY(z10, - FIX_2_613125930) + z5; /* -2*(c2+c6) */ + + tmp6 = tmp12 - tmp7; /* phase 2 */ + tmp5 = tmp11 - tmp6; + tmp4 = tmp10 + tmp5; + + /* Final output stage: scale down by a factor of 8 and range-limit */ + + outptr[0] = range_limit[IDESCALE(tmp0 + tmp7, PASS1_BITS+3) + & RANGE_MASK]; + outptr[7] = range_limit[IDESCALE(tmp0 - tmp7, PASS1_BITS+3) + & RANGE_MASK]; + outptr[1] = range_limit[IDESCALE(tmp1 + tmp6, PASS1_BITS+3) + & RANGE_MASK]; + outptr[6] = range_limit[IDESCALE(tmp1 - tmp6, PASS1_BITS+3) + & RANGE_MASK]; + outptr[2] = range_limit[IDESCALE(tmp2 + tmp5, PASS1_BITS+3) + & RANGE_MASK]; + outptr[5] = range_limit[IDESCALE(tmp2 - tmp5, PASS1_BITS+3) + & RANGE_MASK]; + outptr[4] = range_limit[IDESCALE(tmp3 + tmp4, PASS1_BITS+3) + & RANGE_MASK]; + outptr[3] = range_limit[IDESCALE(tmp3 - tmp4, PASS1_BITS+3) + & RANGE_MASK]; + + wsptr += DCTSIZE; /* advance pointer to next row */ + } +} + +#endif /* DCT_IFAST_SUPPORTED */ +/********* End of inlined file: jidctfst.c *********/ + + #undef CONST_BITS + #undef FIX_1_847759065 + #undef MULTIPLY + #undef DEQUANTIZE + +/********* Start of inlined file: jidctint.c *********/ +#define JPEG_INTERNALS + +#ifdef DCT_ISLOW_SUPPORTED + +/* + * This module is specialized to the case DCTSIZE = 8. + */ + +#if DCTSIZE != 8 + Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */ +#endif + +/* + * The poop on this scaling stuff is as follows: + * + * Each 1-D IDCT step produces outputs which are a factor of sqrt(N) + * larger than the true IDCT outputs. The final outputs are therefore + * a factor of N larger than desired; since N=8 this can be cured by + * a simple right shift at the end of the algorithm. The advantage of + * this arrangement is that we save two multiplications per 1-D IDCT, + * because the y0 and y4 inputs need not be divided by sqrt(N). + * + * We have to do addition and subtraction of the integer inputs, which + * is no problem, and multiplication by fractional constants, which is + * a problem to do in integer arithmetic. We multiply all the constants + * by CONST_SCALE and convert them to integer constants (thus retaining + * CONST_BITS bits of precision in the constants). After doing a + * multiplication we have to divide the product by CONST_SCALE, with proper + * rounding, to produce the correct output. This division can be done + * cheaply as a right shift of CONST_BITS bits. We postpone shifting + * as long as possible so that partial sums can be added together with + * full fractional precision. + * + * The outputs of the first pass are scaled up by PASS1_BITS bits so that + * they are represented to better-than-integral precision. These outputs + * require BITS_IN_JSAMPLE + PASS1_BITS + 3 bits; this fits in a 16-bit word + * with the recommended scaling. (To scale up 12-bit sample data further, an + * intermediate INT32 array would be needed.) + * + * To avoid overflow of the 32-bit intermediate results in pass 2, we must + * have BITS_IN_JSAMPLE + CONST_BITS + PASS1_BITS <= 26. Error analysis + * shows that the values given below are the most effective. + */ + +#if BITS_IN_JSAMPLE == 8 +#define CONST_BITS 13 +#define PASS1_BITS 2 +#else +#define CONST_BITS 13 +#define PASS1_BITS 1 /* lose a little precision to avoid overflow */ +#endif + +/* Some C compilers fail to reduce "FIX(constant)" at compile time, thus + * causing a lot of useless floating-point operations at run time. + * To get around this we use the following pre-calculated constants. + * If you change CONST_BITS you may want to add appropriate values. + * (With a reasonable C compiler, you can just rely on the FIX() macro...) + */ + +#if CONST_BITS == 13 +#define FIX_0_298631336 ((INT32) 2446) /* FIX(0.298631336) */ +#define FIX_0_390180644 ((INT32) 3196) /* FIX(0.390180644) */ +#define FIX_0_541196100 ((INT32) 4433) /* FIX(0.541196100) */ +#define FIX_0_765366865 ((INT32) 6270) /* FIX(0.765366865) */ +#define FIX_0_899976223 ((INT32) 7373) /* FIX(0.899976223) */ +#define FIX_1_175875602 ((INT32) 9633) /* FIX(1.175875602) */ +#define FIX_1_501321110 ((INT32) 12299) /* FIX(1.501321110) */ +#define FIX_1_847759065 ((INT32) 15137) /* FIX(1.847759065) */ +#define FIX_1_961570560 ((INT32) 16069) /* FIX(1.961570560) */ +#define FIX_2_053119869 ((INT32) 16819) /* FIX(2.053119869) */ +#define FIX_2_562915447 ((INT32) 20995) /* FIX(2.562915447) */ +#define FIX_3_072711026 ((INT32) 25172) /* FIX(3.072711026) */ +#else +#define FIX_0_298631336 FIX(0.298631336) +#define FIX_0_390180644 FIX(0.390180644) +#define FIX_0_541196100 FIX(0.541196100) +#define FIX_0_765366865 FIX(0.765366865) +#define FIX_0_899976223 FIX(0.899976223) +#define FIX_1_175875602 FIX(1.175875602) +#define FIX_1_501321110 FIX(1.501321110) +#define FIX_1_847759065 FIX(1.847759065) +#define FIX_1_961570560 FIX(1.961570560) +#define FIX_2_053119869 FIX(2.053119869) +#define FIX_2_562915447 FIX(2.562915447) +#define FIX_3_072711026 FIX(3.072711026) +#endif + +/* Multiply an INT32 variable by an INT32 constant to yield an INT32 result. + * For 8-bit samples with the recommended scaling, all the variable + * and constant values involved are no more than 16 bits wide, so a + * 16x16->32 bit multiply can be used instead of a full 32x32 multiply. + * For 12-bit samples, a full 32-bit multiplication will be needed. + */ + +#if BITS_IN_JSAMPLE == 8 +#define MULTIPLY(var,const) MULTIPLY16C16(var,const) +#else +#define MULTIPLY(var,const) ((var) * (const)) +#endif + +/* Dequantize a coefficient by multiplying it by the multiplier-table + * entry; produce an int result. In this module, both inputs and result + * are 16 bits or less, so either int or short multiply will work. + */ + +#define DEQUANTIZE(coef,quantval) (((ISLOW_MULT_TYPE) (coef)) * (quantval)) + +/* + * Perform dequantization and inverse DCT on one block of coefficients. + */ + +GLOBAL(void) +jpeg_idct_islow (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + INT32 tmp0, tmp1, tmp2, tmp3; + INT32 tmp10, tmp11, tmp12, tmp13; + INT32 z1, z2, z3, z4, z5; + JCOEFPTR inptr; + ISLOW_MULT_TYPE * quantptr; + int * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + int workspace[DCTSIZE2]; /* buffers data between passes */ + SHIFT_TEMPS + + /* Pass 1: process columns from input, store into work array. */ + /* Note results are scaled up by sqrt(8) compared to a true IDCT; */ + /* furthermore, we scale the results by 2**PASS1_BITS. */ + + inptr = coef_block; + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = DCTSIZE; ctr > 0; ctr--) { + /* Due to quantization, we will usually find that many of the input + * coefficients are zero, especially the AC terms. We can exploit this + * by short-circuiting the IDCT calculation for any column in which all + * the AC terms are zero. In that case each output is equal to the + * DC coefficient (with scale factor as needed). + * With typical images and quantization tables, half or more of the + * column DCT calculations can be simplified this way. + */ + + if (inptr[DCTSIZE*1] == 0 && inptr[DCTSIZE*2] == 0 && + inptr[DCTSIZE*3] == 0 && inptr[DCTSIZE*4] == 0 && + inptr[DCTSIZE*5] == 0 && inptr[DCTSIZE*6] == 0 && + inptr[DCTSIZE*7] == 0) { + /* AC terms all zero */ + int dcval = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]) << PASS1_BITS; + + wsptr[DCTSIZE*0] = dcval; + wsptr[DCTSIZE*1] = dcval; + wsptr[DCTSIZE*2] = dcval; + wsptr[DCTSIZE*3] = dcval; + wsptr[DCTSIZE*4] = dcval; + wsptr[DCTSIZE*5] = dcval; + wsptr[DCTSIZE*6] = dcval; + wsptr[DCTSIZE*7] = dcval; + + inptr++; /* advance pointers to next column */ + quantptr++; + wsptr++; + continue; + } + + /* Even part: reverse the even part of the forward DCT. */ + /* The rotator is sqrt(2)*c(-6). */ + + z2 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + z3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); + + z1 = MULTIPLY(z2 + z3, FIX_0_541196100); + tmp2 = z1 + MULTIPLY(z3, - FIX_1_847759065); + tmp3 = z1 + MULTIPLY(z2, FIX_0_765366865); + + z2 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + z3 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); + + tmp0 = (z2 + z3) << CONST_BITS; + tmp1 = (z2 - z3) << CONST_BITS; + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + /* Odd part per figure 8; the matrix is unitary and hence its + * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively. + */ + + tmp0 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); + tmp1 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + tmp2 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + tmp3 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + + z1 = tmp0 + tmp3; + z2 = tmp1 + tmp2; + z3 = tmp0 + tmp2; + z4 = tmp1 + tmp3; + z5 = MULTIPLY(z3 + z4, FIX_1_175875602); /* sqrt(2) * c3 */ + + tmp0 = MULTIPLY(tmp0, FIX_0_298631336); /* sqrt(2) * (-c1+c3+c5-c7) */ + tmp1 = MULTIPLY(tmp1, FIX_2_053119869); /* sqrt(2) * ( c1+c3-c5+c7) */ + tmp2 = MULTIPLY(tmp2, FIX_3_072711026); /* sqrt(2) * ( c1+c3+c5-c7) */ + tmp3 = MULTIPLY(tmp3, FIX_1_501321110); /* sqrt(2) * ( c1+c3-c5-c7) */ + z1 = MULTIPLY(z1, - FIX_0_899976223); /* sqrt(2) * (c7-c3) */ + z2 = MULTIPLY(z2, - FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ + z3 = MULTIPLY(z3, - FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ + z4 = MULTIPLY(z4, - FIX_0_390180644); /* sqrt(2) * (c5-c3) */ + + z3 += z5; + z4 += z5; + + tmp0 += z1 + z3; + tmp1 += z2 + z4; + tmp2 += z2 + z3; + tmp3 += z1 + z4; + + /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */ + + wsptr[DCTSIZE*0] = (int) DESCALE(tmp10 + tmp3, CONST_BITS-PASS1_BITS); + wsptr[DCTSIZE*7] = (int) DESCALE(tmp10 - tmp3, CONST_BITS-PASS1_BITS); + wsptr[DCTSIZE*1] = (int) DESCALE(tmp11 + tmp2, CONST_BITS-PASS1_BITS); + wsptr[DCTSIZE*6] = (int) DESCALE(tmp11 - tmp2, CONST_BITS-PASS1_BITS); + wsptr[DCTSIZE*2] = (int) DESCALE(tmp12 + tmp1, CONST_BITS-PASS1_BITS); + wsptr[DCTSIZE*5] = (int) DESCALE(tmp12 - tmp1, CONST_BITS-PASS1_BITS); + wsptr[DCTSIZE*3] = (int) DESCALE(tmp13 + tmp0, CONST_BITS-PASS1_BITS); + wsptr[DCTSIZE*4] = (int) DESCALE(tmp13 - tmp0, CONST_BITS-PASS1_BITS); + + inptr++; /* advance pointers to next column */ + quantptr++; + wsptr++; + } + + /* Pass 2: process rows from work array, store into output array. */ + /* Note that we must descale the results by a factor of 8 == 2**3, */ + /* and also undo the PASS1_BITS scaling. */ + + wsptr = workspace; + for (ctr = 0; ctr < DCTSIZE; ctr++) { + outptr = output_buf[ctr] + output_col; + /* Rows of zeroes can be exploited in the same way as we did with columns. + * However, the column calculation has created many nonzero AC terms, so + * the simplification applies less often (typically 5% to 10% of the time). + * On machines with very fast multiplication, it's possible that the + * test takes more time than it's worth. In that case this section + * may be commented out. + */ + +#ifndef NO_ZERO_ROW_TEST + if (wsptr[1] == 0 && wsptr[2] == 0 && wsptr[3] == 0 && wsptr[4] == 0 && + wsptr[5] == 0 && wsptr[6] == 0 && wsptr[7] == 0) { + /* AC terms all zero */ + JSAMPLE dcval = range_limit[(int) DESCALE((INT32) wsptr[0], PASS1_BITS+3) + & RANGE_MASK]; + + outptr[0] = dcval; + outptr[1] = dcval; + outptr[2] = dcval; + outptr[3] = dcval; + outptr[4] = dcval; + outptr[5] = dcval; + outptr[6] = dcval; + outptr[7] = dcval; + + wsptr += DCTSIZE; /* advance pointer to next row */ + continue; + } +#endif + + /* Even part: reverse the even part of the forward DCT. */ + /* The rotator is sqrt(2)*c(-6). */ + + z2 = (INT32) wsptr[2]; + z3 = (INT32) wsptr[6]; + + z1 = MULTIPLY(z2 + z3, FIX_0_541196100); + tmp2 = z1 + MULTIPLY(z3, - FIX_1_847759065); + tmp3 = z1 + MULTIPLY(z2, FIX_0_765366865); + + tmp0 = ((INT32) wsptr[0] + (INT32) wsptr[4]) << CONST_BITS; + tmp1 = ((INT32) wsptr[0] - (INT32) wsptr[4]) << CONST_BITS; + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + /* Odd part per figure 8; the matrix is unitary and hence its + * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively. + */ + + tmp0 = (INT32) wsptr[7]; + tmp1 = (INT32) wsptr[5]; + tmp2 = (INT32) wsptr[3]; + tmp3 = (INT32) wsptr[1]; + + z1 = tmp0 + tmp3; + z2 = tmp1 + tmp2; + z3 = tmp0 + tmp2; + z4 = tmp1 + tmp3; + z5 = MULTIPLY(z3 + z4, FIX_1_175875602); /* sqrt(2) * c3 */ + + tmp0 = MULTIPLY(tmp0, FIX_0_298631336); /* sqrt(2) * (-c1+c3+c5-c7) */ + tmp1 = MULTIPLY(tmp1, FIX_2_053119869); /* sqrt(2) * ( c1+c3-c5+c7) */ + tmp2 = MULTIPLY(tmp2, FIX_3_072711026); /* sqrt(2) * ( c1+c3+c5-c7) */ + tmp3 = MULTIPLY(tmp3, FIX_1_501321110); /* sqrt(2) * ( c1+c3-c5-c7) */ + z1 = MULTIPLY(z1, - FIX_0_899976223); /* sqrt(2) * (c7-c3) */ + z2 = MULTIPLY(z2, - FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ + z3 = MULTIPLY(z3, - FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ + z4 = MULTIPLY(z4, - FIX_0_390180644); /* sqrt(2) * (c5-c3) */ + + z3 += z5; + z4 += z5; + + tmp0 += z1 + z3; + tmp1 += z2 + z4; + tmp2 += z2 + z3; + tmp3 += z1 + z4; + + /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */ + + outptr[0] = range_limit[(int) DESCALE(tmp10 + tmp3, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[7] = range_limit[(int) DESCALE(tmp10 - tmp3, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[1] = range_limit[(int) DESCALE(tmp11 + tmp2, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[6] = range_limit[(int) DESCALE(tmp11 - tmp2, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[2] = range_limit[(int) DESCALE(tmp12 + tmp1, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[5] = range_limit[(int) DESCALE(tmp12 - tmp1, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[3] = range_limit[(int) DESCALE(tmp13 + tmp0, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[4] = range_limit[(int) DESCALE(tmp13 - tmp0, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + + wsptr += DCTSIZE; /* advance pointer to next row */ + } +} + +#endif /* DCT_ISLOW_SUPPORTED */ +/********* End of inlined file: jidctint.c *********/ + +/********* Start of inlined file: jidctred.c *********/ +#define JPEG_INTERNALS + +#ifdef IDCT_SCALING_SUPPORTED + +/* + * This module is specialized to the case DCTSIZE = 8. + */ + +#if DCTSIZE != 8 + Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */ +#endif + +/* Scaling is the same as in jidctint.c. */ + +#if BITS_IN_JSAMPLE == 8 +#define CONST_BITS 13 +#define PASS1_BITS 2 +#else +#define CONST_BITS 13 +#define PASS1_BITS 1 /* lose a little precision to avoid overflow */ +#endif + +/* Some C compilers fail to reduce "FIX(constant)" at compile time, thus + * causing a lot of useless floating-point operations at run time. + * To get around this we use the following pre-calculated constants. + * If you change CONST_BITS you may want to add appropriate values. + * (With a reasonable C compiler, you can just rely on the FIX() macro...) + */ + +#if CONST_BITS == 13 +#define FIX_0_211164243 ((INT32) 1730) /* FIX(0.211164243) */ +#define FIX_0_509795579 ((INT32) 4176) /* FIX(0.509795579) */ +#define FIX_0_601344887 ((INT32) 4926) /* FIX(0.601344887) */ +#define FIX_0_720959822 ((INT32) 5906) /* FIX(0.720959822) */ +#define FIX_0_765366865 ((INT32) 6270) /* FIX(0.765366865) */ +#define FIX_0_850430095 ((INT32) 6967) /* FIX(0.850430095) */ +#define FIX_0_899976223 ((INT32) 7373) /* FIX(0.899976223) */ +#define FIX_1_061594337 ((INT32) 8697) /* FIX(1.061594337) */ +#define FIX_1_272758580 ((INT32) 10426) /* FIX(1.272758580) */ +#define FIX_1_451774981 ((INT32) 11893) /* FIX(1.451774981) */ +#define FIX_1_847759065 ((INT32) 15137) /* FIX(1.847759065) */ +#define FIX_2_172734803 ((INT32) 17799) /* FIX(2.172734803) */ +#define FIX_2_562915447 ((INT32) 20995) /* FIX(2.562915447) */ +#define FIX_3_624509785 ((INT32) 29692) /* FIX(3.624509785) */ +#else +#define FIX_0_211164243 FIX(0.211164243) +#define FIX_0_509795579 FIX(0.509795579) +#define FIX_0_601344887 FIX(0.601344887) +#define FIX_0_720959822 FIX(0.720959822) +#define FIX_0_765366865 FIX(0.765366865) +#define FIX_0_850430095 FIX(0.850430095) +#define FIX_0_899976223 FIX(0.899976223) +#define FIX_1_061594337 FIX(1.061594337) +#define FIX_1_272758580 FIX(1.272758580) +#define FIX_1_451774981 FIX(1.451774981) +#define FIX_1_847759065 FIX(1.847759065) +#define FIX_2_172734803 FIX(2.172734803) +#define FIX_2_562915447 FIX(2.562915447) +#define FIX_3_624509785 FIX(3.624509785) +#endif + +/* Multiply an INT32 variable by an INT32 constant to yield an INT32 result. + * For 8-bit samples with the recommended scaling, all the variable + * and constant values involved are no more than 16 bits wide, so a + * 16x16->32 bit multiply can be used instead of a full 32x32 multiply. + * For 12-bit samples, a full 32-bit multiplication will be needed. + */ + +#if BITS_IN_JSAMPLE == 8 +#define MULTIPLY(var,const) MULTIPLY16C16(var,const) +#else +#define MULTIPLY(var,const) ((var) * (const)) +#endif + +/* Dequantize a coefficient by multiplying it by the multiplier-table + * entry; produce an int result. In this module, both inputs and result + * are 16 bits or less, so either int or short multiply will work. + */ + +#define DEQUANTIZE(coef,quantval) (((ISLOW_MULT_TYPE) (coef)) * (quantval)) + +/* + * Perform dequantization and inverse DCT on one block of coefficients, + * producing a reduced-size 4x4 output block. + */ + +GLOBAL(void) +jpeg_idct_4x4 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + INT32 tmp0, tmp2, tmp10, tmp12; + INT32 z1, z2, z3, z4; + JCOEFPTR inptr; + ISLOW_MULT_TYPE * quantptr; + int * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + int workspace[DCTSIZE*4]; /* buffers data between passes */ + SHIFT_TEMPS + + /* Pass 1: process columns from input, store into work array. */ + + inptr = coef_block; + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = DCTSIZE; ctr > 0; inptr++, quantptr++, wsptr++, ctr--) { + /* Don't bother to process column 4, because second pass won't use it */ + if (ctr == DCTSIZE-4) + continue; + if (inptr[DCTSIZE*1] == 0 && inptr[DCTSIZE*2] == 0 && + inptr[DCTSIZE*3] == 0 && inptr[DCTSIZE*5] == 0 && + inptr[DCTSIZE*6] == 0 && inptr[DCTSIZE*7] == 0) { + /* AC terms all zero; we need not examine term 4 for 4x4 output */ + int dcval = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]) << PASS1_BITS; + + wsptr[DCTSIZE*0] = dcval; + wsptr[DCTSIZE*1] = dcval; + wsptr[DCTSIZE*2] = dcval; + wsptr[DCTSIZE*3] = dcval; + + continue; + } + + /* Even part */ + + tmp0 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + tmp0 <<= (CONST_BITS+1); + + z2 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + z3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); + + tmp2 = MULTIPLY(z2, FIX_1_847759065) + MULTIPLY(z3, - FIX_0_765366865); + + tmp10 = tmp0 + tmp2; + tmp12 = tmp0 - tmp2; + + /* Odd part */ + + z1 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); + z2 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + z3 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + z4 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + + tmp0 = MULTIPLY(z1, - FIX_0_211164243) /* sqrt(2) * (c3-c1) */ + + MULTIPLY(z2, FIX_1_451774981) /* sqrt(2) * (c3+c7) */ + + MULTIPLY(z3, - FIX_2_172734803) /* sqrt(2) * (-c1-c5) */ + + MULTIPLY(z4, FIX_1_061594337); /* sqrt(2) * (c5+c7) */ + + tmp2 = MULTIPLY(z1, - FIX_0_509795579) /* sqrt(2) * (c7-c5) */ + + MULTIPLY(z2, - FIX_0_601344887) /* sqrt(2) * (c5-c1) */ + + MULTIPLY(z3, FIX_0_899976223) /* sqrt(2) * (c3-c7) */ + + MULTIPLY(z4, FIX_2_562915447); /* sqrt(2) * (c1+c3) */ + + /* Final output stage */ + + wsptr[DCTSIZE*0] = (int) DESCALE(tmp10 + tmp2, CONST_BITS-PASS1_BITS+1); + wsptr[DCTSIZE*3] = (int) DESCALE(tmp10 - tmp2, CONST_BITS-PASS1_BITS+1); + wsptr[DCTSIZE*1] = (int) DESCALE(tmp12 + tmp0, CONST_BITS-PASS1_BITS+1); + wsptr[DCTSIZE*2] = (int) DESCALE(tmp12 - tmp0, CONST_BITS-PASS1_BITS+1); + } + + /* Pass 2: process 4 rows from work array, store into output array. */ + + wsptr = workspace; + for (ctr = 0; ctr < 4; ctr++) { + outptr = output_buf[ctr] + output_col; + /* It's not clear whether a zero row test is worthwhile here ... */ + +#ifndef NO_ZERO_ROW_TEST + if (wsptr[1] == 0 && wsptr[2] == 0 && wsptr[3] == 0 && + wsptr[5] == 0 && wsptr[6] == 0 && wsptr[7] == 0) { + /* AC terms all zero */ + JSAMPLE dcval = range_limit[(int) DESCALE((INT32) wsptr[0], PASS1_BITS+3) + & RANGE_MASK]; + + outptr[0] = dcval; + outptr[1] = dcval; + outptr[2] = dcval; + outptr[3] = dcval; + + wsptr += DCTSIZE; /* advance pointer to next row */ + continue; + } +#endif + + /* Even part */ + + tmp0 = ((INT32) wsptr[0]) << (CONST_BITS+1); + + tmp2 = MULTIPLY((INT32) wsptr[2], FIX_1_847759065) + + MULTIPLY((INT32) wsptr[6], - FIX_0_765366865); + + tmp10 = tmp0 + tmp2; + tmp12 = tmp0 - tmp2; + + /* Odd part */ + + z1 = (INT32) wsptr[7]; + z2 = (INT32) wsptr[5]; + z3 = (INT32) wsptr[3]; + z4 = (INT32) wsptr[1]; + + tmp0 = MULTIPLY(z1, - FIX_0_211164243) /* sqrt(2) * (c3-c1) */ + + MULTIPLY(z2, FIX_1_451774981) /* sqrt(2) * (c3+c7) */ + + MULTIPLY(z3, - FIX_2_172734803) /* sqrt(2) * (-c1-c5) */ + + MULTIPLY(z4, FIX_1_061594337); /* sqrt(2) * (c5+c7) */ + + tmp2 = MULTIPLY(z1, - FIX_0_509795579) /* sqrt(2) * (c7-c5) */ + + MULTIPLY(z2, - FIX_0_601344887) /* sqrt(2) * (c5-c1) */ + + MULTIPLY(z3, FIX_0_899976223) /* sqrt(2) * (c3-c7) */ + + MULTIPLY(z4, FIX_2_562915447); /* sqrt(2) * (c1+c3) */ + + /* Final output stage */ + + outptr[0] = range_limit[(int) DESCALE(tmp10 + tmp2, + CONST_BITS+PASS1_BITS+3+1) + & RANGE_MASK]; + outptr[3] = range_limit[(int) DESCALE(tmp10 - tmp2, + CONST_BITS+PASS1_BITS+3+1) + & RANGE_MASK]; + outptr[1] = range_limit[(int) DESCALE(tmp12 + tmp0, + CONST_BITS+PASS1_BITS+3+1) + & RANGE_MASK]; + outptr[2] = range_limit[(int) DESCALE(tmp12 - tmp0, + CONST_BITS+PASS1_BITS+3+1) + & RANGE_MASK]; + + wsptr += DCTSIZE; /* advance pointer to next row */ + } +} + +/* + * Perform dequantization and inverse DCT on one block of coefficients, + * producing a reduced-size 2x2 output block. + */ + +GLOBAL(void) +jpeg_idct_2x2 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + INT32 tmp0, tmp10, z1; + JCOEFPTR inptr; + ISLOW_MULT_TYPE * quantptr; + int * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + int workspace[DCTSIZE*2]; /* buffers data between passes */ + SHIFT_TEMPS + + /* Pass 1: process columns from input, store into work array. */ + + inptr = coef_block; + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = DCTSIZE; ctr > 0; inptr++, quantptr++, wsptr++, ctr--) { + /* Don't bother to process columns 2,4,6 */ + if (ctr == DCTSIZE-2 || ctr == DCTSIZE-4 || ctr == DCTSIZE-6) + continue; + if (inptr[DCTSIZE*1] == 0 && inptr[DCTSIZE*3] == 0 && + inptr[DCTSIZE*5] == 0 && inptr[DCTSIZE*7] == 0) { + /* AC terms all zero; we need not examine terms 2,4,6 for 2x2 output */ + int dcval = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]) << PASS1_BITS; + + wsptr[DCTSIZE*0] = dcval; + wsptr[DCTSIZE*1] = dcval; + + continue; + } + + /* Even part */ + + z1 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + tmp10 = z1 << (CONST_BITS+2); + + /* Odd part */ + + z1 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); + tmp0 = MULTIPLY(z1, - FIX_0_720959822); /* sqrt(2) * (c7-c5+c3-c1) */ + z1 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + tmp0 += MULTIPLY(z1, FIX_0_850430095); /* sqrt(2) * (-c1+c3+c5+c7) */ + z1 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + tmp0 += MULTIPLY(z1, - FIX_1_272758580); /* sqrt(2) * (-c1+c3-c5-c7) */ + z1 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + tmp0 += MULTIPLY(z1, FIX_3_624509785); /* sqrt(2) * (c1+c3+c5+c7) */ + + /* Final output stage */ + + wsptr[DCTSIZE*0] = (int) DESCALE(tmp10 + tmp0, CONST_BITS-PASS1_BITS+2); + wsptr[DCTSIZE*1] = (int) DESCALE(tmp10 - tmp0, CONST_BITS-PASS1_BITS+2); + } + + /* Pass 2: process 2 rows from work array, store into output array. */ + + wsptr = workspace; + for (ctr = 0; ctr < 2; ctr++) { + outptr = output_buf[ctr] + output_col; + /* It's not clear whether a zero row test is worthwhile here ... */ + +#ifndef NO_ZERO_ROW_TEST + if (wsptr[1] == 0 && wsptr[3] == 0 && wsptr[5] == 0 && wsptr[7] == 0) { + /* AC terms all zero */ + JSAMPLE dcval = range_limit[(int) DESCALE((INT32) wsptr[0], PASS1_BITS+3) + & RANGE_MASK]; + + outptr[0] = dcval; + outptr[1] = dcval; + + wsptr += DCTSIZE; /* advance pointer to next row */ + continue; + } +#endif + + /* Even part */ + + tmp10 = ((INT32) wsptr[0]) << (CONST_BITS+2); + + /* Odd part */ + + tmp0 = MULTIPLY((INT32) wsptr[7], - FIX_0_720959822) /* sqrt(2) * (c7-c5+c3-c1) */ + + MULTIPLY((INT32) wsptr[5], FIX_0_850430095) /* sqrt(2) * (-c1+c3+c5+c7) */ + + MULTIPLY((INT32) wsptr[3], - FIX_1_272758580) /* sqrt(2) * (-c1+c3-c5-c7) */ + + MULTIPLY((INT32) wsptr[1], FIX_3_624509785); /* sqrt(2) * (c1+c3+c5+c7) */ + + /* Final output stage */ + + outptr[0] = range_limit[(int) DESCALE(tmp10 + tmp0, + CONST_BITS+PASS1_BITS+3+2) + & RANGE_MASK]; + outptr[1] = range_limit[(int) DESCALE(tmp10 - tmp0, + CONST_BITS+PASS1_BITS+3+2) + & RANGE_MASK]; + + wsptr += DCTSIZE; /* advance pointer to next row */ + } +} + +/* + * Perform dequantization and inverse DCT on one block of coefficients, + * producing a reduced-size 1x1 output block. + */ + +GLOBAL(void) +jpeg_idct_1x1 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + int dcval; + ISLOW_MULT_TYPE * quantptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + SHIFT_TEMPS + + /* We hardly need an inverse DCT routine for this: just take the + * average pixel value, which is one-eighth of the DC coefficient. + */ + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + dcval = DEQUANTIZE(coef_block[0], quantptr[0]); + dcval = (int) DESCALE((INT32) dcval, 3); + + output_buf[0][output_col] = range_limit[dcval & RANGE_MASK]; +} + +#endif /* IDCT_SCALING_SUPPORTED */ +/********* End of inlined file: jidctred.c *********/ + +/********* Start of inlined file: jmemmgr.c *********/ +#define JPEG_INTERNALS +#define AM_MEMORY_MANAGER /* we define jvirt_Xarray_control structs */ + +/********* Start of inlined file: jmemsys.h *********/ +#ifndef __jmemsys_h__ +#define __jmemsys_h__ + +/* Short forms of external names for systems with brain-damaged linkers. */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_get_small jGetSmall +#define jpeg_free_small jFreeSmall +#define jpeg_get_large jGetLarge +#define jpeg_free_large jFreeLarge +#define jpeg_mem_available jMemAvail +#define jpeg_open_backing_store jOpenBackStore +#define jpeg_mem_init jMemInit +#define jpeg_mem_term jMemTerm +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + +/* + * These two functions are used to allocate and release small chunks of + * memory. (Typically the total amount requested through jpeg_get_small is + * no more than 20K or so; this will be requested in chunks of a few K each.) + * Behavior should be the same as for the standard library functions malloc + * and free; in particular, jpeg_get_small must return NULL on failure. + * On most systems, these ARE malloc and free. jpeg_free_small is passed the + * size of the object being freed, just in case it's needed. + * On an 80x86 machine using small-data memory model, these manage near heap. + */ + +EXTERN(void *) jpeg_get_small JPP((j_common_ptr cinfo, size_t sizeofobject)); +EXTERN(void) jpeg_free_small JPP((j_common_ptr cinfo, void * object, + size_t sizeofobject)); + +/* + * These two functions are used to allocate and release large chunks of + * memory (up to the total free space designated by jpeg_mem_available). + * The interface is the same as above, except that on an 80x86 machine, + * far pointers are used. On most other machines these are identical to + * the jpeg_get/free_small routines; but we keep them separate anyway, + * in case a different allocation strategy is desirable for large chunks. + */ + +EXTERN(void FAR *) jpeg_get_large JPP((j_common_ptr cinfo, + size_t sizeofobject)); +EXTERN(void) jpeg_free_large JPP((j_common_ptr cinfo, void FAR * object, + size_t sizeofobject)); + +/* + * The macro MAX_ALLOC_CHUNK designates the maximum number of bytes that may + * be requested in a single call to jpeg_get_large (and jpeg_get_small for that + * matter, but that case should never come into play). This macro is needed + * to model the 64Kb-segment-size limit of far addressing on 80x86 machines. + * On those machines, we expect that jconfig.h will provide a proper value. + * On machines with 32-bit flat address spaces, any large constant may be used. + * + * NB: jmemmgr.c expects that MAX_ALLOC_CHUNK will be representable as type + * size_t and will be a multiple of sizeof(align_type). + */ + +#ifndef MAX_ALLOC_CHUNK /* may be overridden in jconfig.h */ +#define MAX_ALLOC_CHUNK 1000000000L +#endif + +/* + * This routine computes the total space still available for allocation by + * jpeg_get_large. If more space than this is needed, backing store will be + * used. NOTE: any memory already allocated must not be counted. + * + * There is a minimum space requirement, corresponding to the minimum + * feasible buffer sizes; jmemmgr.c will request that much space even if + * jpeg_mem_available returns zero. The maximum space needed, enough to hold + * all working storage in memory, is also passed in case it is useful. + * Finally, the total space already allocated is passed. If no better + * method is available, cinfo->mem->max_memory_to_use - already_allocated + * is often a suitable calculation. + * + * It is OK for jpeg_mem_available to underestimate the space available + * (that'll just lead to more backing-store access than is really necessary). + * However, an overestimate will lead to failure. Hence it's wise to subtract + * a slop factor from the true available space. 5% should be enough. + * + * On machines with lots of virtual memory, any large constant may be returned. + * Conversely, zero may be returned to always use the minimum amount of memory. + */ + +EXTERN(long) jpeg_mem_available JPP((j_common_ptr cinfo, + long min_bytes_needed, + long max_bytes_needed, + long already_allocated)); + +/* + * This structure holds whatever state is needed to access a single + * backing-store object. The read/write/close method pointers are called + * by jmemmgr.c to manipulate the backing-store object; all other fields + * are private to the system-dependent backing store routines. + */ + +#define TEMP_NAME_LENGTH 64 /* max length of a temporary file's name */ + +#ifdef USE_MSDOS_MEMMGR /* DOS-specific junk */ + +typedef unsigned short XMSH; /* type of extended-memory handles */ +typedef unsigned short EMSH; /* type of expanded-memory handles */ + +typedef union { + short file_handle; /* DOS file handle if it's a temp file */ + XMSH xms_handle; /* handle if it's a chunk of XMS */ + EMSH ems_handle; /* handle if it's a chunk of EMS */ +} handle_union; + +#endif /* USE_MSDOS_MEMMGR */ + +#ifdef USE_MAC_MEMMGR /* Mac-specific junk */ +#include +#endif /* USE_MAC_MEMMGR */ + +//typedef struct backing_store_struct * backing_store_ptr; + +typedef struct backing_store_struct { + /* Methods for reading/writing/closing this backing-store object */ + JMETHOD(void, read_backing_store, (j_common_ptr cinfo, + struct backing_store_struct *info, + void FAR * buffer_address, + long file_offset, long byte_count)); + JMETHOD(void, write_backing_store, (j_common_ptr cinfo, + struct backing_store_struct *info, + void FAR * buffer_address, + long file_offset, long byte_count)); + JMETHOD(void, close_backing_store, (j_common_ptr cinfo, + struct backing_store_struct *info)); + + /* Private fields for system-dependent backing-store management */ +#ifdef USE_MSDOS_MEMMGR + /* For the MS-DOS manager (jmemdos.c), we need: */ + handle_union handle; /* reference to backing-store storage object */ + char temp_name[TEMP_NAME_LENGTH]; /* name if it's a file */ +#else +#ifdef USE_MAC_MEMMGR + /* For the Mac manager (jmemmac.c), we need: */ + short temp_file; /* file reference number to temp file */ + FSSpec tempSpec; /* the FSSpec for the temp file */ + char temp_name[TEMP_NAME_LENGTH]; /* name if it's a file */ +#else + /* For a typical implementation with temp files, we need: */ + FILE * temp_file; /* stdio reference to temp file */ + char temp_name[TEMP_NAME_LENGTH]; /* name of temp file */ +#endif +#endif +} backing_store_info; + +/* + * Initial opening of a backing-store object. This must fill in the + * read/write/close pointers in the object. The read/write routines + * may take an error exit if the specified maximum file size is exceeded. + * (If jpeg_mem_available always returns a large value, this routine can + * just take an error exit.) + */ + +EXTERN(void) jpeg_open_backing_store JPP((j_common_ptr cinfo, + struct backing_store_struct *info, + long total_bytes_needed)); + +/* + * These routines take care of any system-dependent initialization and + * cleanup required. jpeg_mem_init will be called before anything is + * allocated (and, therefore, nothing in cinfo is of use except the error + * manager pointer). It should return a suitable default value for + * max_memory_to_use; this may subsequently be overridden by the surrounding + * application. (Note that max_memory_to_use is only important if + * jpeg_mem_available chooses to consult it ... no one else will.) + * jpeg_mem_term may assume that all requested memory has been freed and that + * all opened backing-store objects have been closed. + */ + +EXTERN(long) jpeg_mem_init JPP((j_common_ptr cinfo)); +EXTERN(void) jpeg_mem_term JPP((j_common_ptr cinfo)); + +#endif +/********* End of inlined file: jmemsys.h *********/ + + /* import the system-dependent declarations */ + +#ifndef NO_GETENV +#ifndef HAVE_STDLIB_H /* should declare getenv() */ +extern char * getenv JPP((const char * name)); +#endif +#endif + +/* + * Some important notes: + * The allocation routines provided here must never return NULL. + * They should exit to error_exit if unsuccessful. + * + * It's not a good idea to try to merge the sarray and barray routines, + * even though they are textually almost the same, because samples are + * usually stored as bytes while coefficients are shorts or ints. Thus, + * in machines where byte pointers have a different representation from + * word pointers, the resulting machine code could not be the same. + */ + +/* + * Many machines require storage alignment: longs must start on 4-byte + * boundaries, doubles on 8-byte boundaries, etc. On such machines, malloc() + * always returns pointers that are multiples of the worst-case alignment + * requirement, and we had better do so too. + * There isn't any really portable way to determine the worst-case alignment + * requirement. This module assumes that the alignment requirement is + * multiples of sizeof(ALIGN_TYPE). + * By default, we define ALIGN_TYPE as double. This is necessary on some + * workstations (where doubles really do need 8-byte alignment) and will work + * fine on nearly everything. If your machine has lesser alignment needs, + * you can save a few bytes by making ALIGN_TYPE smaller. + * The only place I know of where this will NOT work is certain Macintosh + * 680x0 compilers that define double as a 10-byte IEEE extended float. + * Doing 10-byte alignment is counterproductive because longwords won't be + * aligned well. Put "#define ALIGN_TYPE long" in jconfig.h if you have + * such a compiler. + */ + +#ifndef ALIGN_TYPE /* so can override from jconfig.h */ +#define ALIGN_TYPE double +#endif + +/* + * We allocate objects from "pools", where each pool is gotten with a single + * request to jpeg_get_small() or jpeg_get_large(). There is no per-object + * overhead within a pool, except for alignment padding. Each pool has a + * header with a link to the next pool of the same class. + * Small and large pool headers are identical except that the latter's + * link pointer must be FAR on 80x86 machines. + * Notice that the "real" header fields are union'ed with a dummy ALIGN_TYPE + * field. This forces the compiler to make SIZEOF(small_pool_hdr) a multiple + * of the alignment requirement of ALIGN_TYPE. + */ + +typedef union small_pool_struct * small_pool_ptr; + +typedef union small_pool_struct { + struct { + small_pool_ptr next; /* next in list of pools */ + size_t bytes_used; /* how many bytes already used within pool */ + size_t bytes_left; /* bytes still available in this pool */ + } hdr; + ALIGN_TYPE dummy; /* included in union to ensure alignment */ +} small_pool_hdr; + +typedef union large_pool_struct FAR * large_pool_ptr; + +typedef union large_pool_struct { + struct { + large_pool_ptr next; /* next in list of pools */ + size_t bytes_used; /* how many bytes already used within pool */ + size_t bytes_left; /* bytes still available in this pool */ + } hdr; + ALIGN_TYPE dummy; /* included in union to ensure alignment */ +} large_pool_hdr; + +/* + * Here is the full definition of a memory manager object. + */ + +typedef struct { + struct jpeg_memory_mgr pub; /* public fields */ + + /* Each pool identifier (lifetime class) names a linked list of pools. */ + small_pool_ptr small_list[JPOOL_NUMPOOLS]; + large_pool_ptr large_list[JPOOL_NUMPOOLS]; + + /* Since we only have one lifetime class of virtual arrays, only one + * linked list is necessary (for each datatype). Note that the virtual + * array control blocks being linked together are actually stored somewhere + * in the small-pool list. + */ + jvirt_sarray_ptr virt_sarray_list; + jvirt_barray_ptr virt_barray_list; + + /* This counts total space obtained from jpeg_get_small/large */ + long total_space_allocated; + + /* alloc_sarray and alloc_barray set this value for use by virtual + * array routines. + */ + JDIMENSION last_rowsperchunk; /* from most recent alloc_sarray/barray */ +} my_memory_mgr; + +typedef my_memory_mgr * my_mem_ptr; + +/* + * The control blocks for virtual arrays. + * Note that these blocks are allocated in the "small" pool area. + * System-dependent info for the associated backing store (if any) is hidden + * inside the backing_store_info struct. + */ + +struct jvirt_sarray_control { + JSAMPARRAY mem_buffer; /* => the in-memory buffer */ + JDIMENSION rows_in_array; /* total virtual array height */ + JDIMENSION samplesperrow; /* width of array (and of memory buffer) */ + JDIMENSION maxaccess; /* max rows accessed by access_virt_sarray */ + JDIMENSION rows_in_mem; /* height of memory buffer */ + JDIMENSION rowsperchunk; /* allocation chunk size in mem_buffer */ + JDIMENSION cur_start_row; /* first logical row # in the buffer */ + JDIMENSION first_undef_row; /* row # of first uninitialized row */ + boolean pre_zero; /* pre-zero mode requested? */ + boolean dirty; /* do current buffer contents need written? */ + boolean b_s_open; /* is backing-store data valid? */ + jvirt_sarray_ptr next; /* link to next virtual sarray control block */ + backing_store_info b_s_info; /* System-dependent control info */ +}; + +struct jvirt_barray_control { + JBLOCKARRAY mem_buffer; /* => the in-memory buffer */ + JDIMENSION rows_in_array; /* total virtual array height */ + JDIMENSION blocksperrow; /* width of array (and of memory buffer) */ + JDIMENSION maxaccess; /* max rows accessed by access_virt_barray */ + JDIMENSION rows_in_mem; /* height of memory buffer */ + JDIMENSION rowsperchunk; /* allocation chunk size in mem_buffer */ + JDIMENSION cur_start_row; /* first logical row # in the buffer */ + JDIMENSION first_undef_row; /* row # of first uninitialized row */ + boolean pre_zero; /* pre-zero mode requested? */ + boolean dirty; /* do current buffer contents need written? */ + boolean b_s_open; /* is backing-store data valid? */ + jvirt_barray_ptr next; /* link to next virtual barray control block */ + backing_store_info b_s_info; /* System-dependent control info */ +}; + +#ifdef MEM_STATS /* optional extra stuff for statistics */ + +LOCAL(void) +print_mem_stats (j_common_ptr cinfo, int pool_id) +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + small_pool_ptr shdr_ptr; + large_pool_ptr lhdr_ptr; + + /* Since this is only a debugging stub, we can cheat a little by using + * fprintf directly rather than going through the trace message code. + * This is helpful because message parm array can't handle longs. + */ + fprintf(stderr, "Freeing pool %d, total space = %ld\n", + pool_id, mem->total_space_allocated); + + for (lhdr_ptr = mem->large_list[pool_id]; lhdr_ptr != NULL; + lhdr_ptr = lhdr_ptr->hdr.next) { + fprintf(stderr, " Large chunk used %ld\n", + (long) lhdr_ptr->hdr.bytes_used); + } + + for (shdr_ptr = mem->small_list[pool_id]; shdr_ptr != NULL; + shdr_ptr = shdr_ptr->hdr.next) { + fprintf(stderr, " Small chunk used %ld free %ld\n", + (long) shdr_ptr->hdr.bytes_used, + (long) shdr_ptr->hdr.bytes_left); + } +} + +#endif /* MEM_STATS */ + +LOCAL(void) +out_of_memory (j_common_ptr cinfo, int which) +/* Report an out-of-memory error and stop execution */ +/* If we compiled MEM_STATS support, report alloc requests before dying */ +{ +#ifdef MEM_STATS + cinfo->err->trace_level = 2; /* force self_destruct to report stats */ +#endif + ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, which); +} + +/* + * Allocation of "small" objects. + * + * For these, we use pooled storage. When a new pool must be created, + * we try to get enough space for the current request plus a "slop" factor, + * where the slop will be the amount of leftover space in the new pool. + * The speed vs. space tradeoff is largely determined by the slop values. + * A different slop value is provided for each pool class (lifetime), + * and we also distinguish the first pool of a class from later ones. + * NOTE: the values given work fairly well on both 16- and 32-bit-int + * machines, but may be too small if longs are 64 bits or more. + */ + +static const size_t first_pool_slop[JPOOL_NUMPOOLS] = +{ + 1600, /* first PERMANENT pool */ + 16000 /* first IMAGE pool */ +}; + +static const size_t extra_pool_slop[JPOOL_NUMPOOLS] = +{ + 0, /* additional PERMANENT pools */ + 5000 /* additional IMAGE pools */ +}; + +#define MIN_SLOP 50 /* greater than 0 to avoid futile looping */ + +METHODDEF(void *) +alloc_small (j_common_ptr cinfo, int pool_id, size_t sizeofobject) +/* Allocate a "small" object */ +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + small_pool_ptr hdr_ptr, prev_hdr_ptr; + char * data_ptr; + size_t odd_bytes, min_request, slop; + + /* Check for unsatisfiable request (do now to ensure no overflow below) */ + if (sizeofobject > (size_t) (MAX_ALLOC_CHUNK-SIZEOF(small_pool_hdr))) + out_of_memory(cinfo, 1); /* request exceeds malloc's ability */ + + /* Round up the requested size to a multiple of SIZEOF(ALIGN_TYPE) */ + odd_bytes = sizeofobject % SIZEOF(ALIGN_TYPE); + if (odd_bytes > 0) + sizeofobject += SIZEOF(ALIGN_TYPE) - odd_bytes; + + /* See if space is available in any existing pool */ + if (pool_id < 0 || pool_id >= JPOOL_NUMPOOLS) + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); /* safety check */ + prev_hdr_ptr = NULL; + hdr_ptr = mem->small_list[pool_id]; + while (hdr_ptr != NULL) { + if (hdr_ptr->hdr.bytes_left >= sizeofobject) + break; /* found pool with enough space */ + prev_hdr_ptr = hdr_ptr; + hdr_ptr = hdr_ptr->hdr.next; + } + + /* Time to make a new pool? */ + if (hdr_ptr == NULL) { + /* min_request is what we need now, slop is what will be leftover */ + min_request = sizeofobject + SIZEOF(small_pool_hdr); + if (prev_hdr_ptr == NULL) /* first pool in class? */ + slop = first_pool_slop[pool_id]; + else + slop = extra_pool_slop[pool_id]; + /* Don't ask for more than MAX_ALLOC_CHUNK */ + if (slop > (size_t) (MAX_ALLOC_CHUNK-min_request)) + slop = (size_t) (MAX_ALLOC_CHUNK-min_request); + /* Try to get space, if fail reduce slop and try again */ + for (;;) { + hdr_ptr = (small_pool_ptr) jpeg_get_small(cinfo, min_request + slop); + if (hdr_ptr != NULL) + break; + slop /= 2; + if (slop < MIN_SLOP) /* give up when it gets real small */ + out_of_memory(cinfo, 2); /* jpeg_get_small failed */ + } + mem->total_space_allocated += min_request + slop; + /* Success, initialize the new pool header and add to end of list */ + hdr_ptr->hdr.next = NULL; + hdr_ptr->hdr.bytes_used = 0; + hdr_ptr->hdr.bytes_left = sizeofobject + slop; + if (prev_hdr_ptr == NULL) /* first pool in class? */ + mem->small_list[pool_id] = hdr_ptr; + else + prev_hdr_ptr->hdr.next = hdr_ptr; + } + + /* OK, allocate the object from the current pool */ + data_ptr = (char *) (hdr_ptr + 1); /* point to first data byte in pool */ + data_ptr += hdr_ptr->hdr.bytes_used; /* point to place for object */ + hdr_ptr->hdr.bytes_used += sizeofobject; + hdr_ptr->hdr.bytes_left -= sizeofobject; + + return (void *) data_ptr; +} + +/* + * Allocation of "large" objects. + * + * The external semantics of these are the same as "small" objects, + * except that FAR pointers are used on 80x86. However the pool + * management heuristics are quite different. We assume that each + * request is large enough that it may as well be passed directly to + * jpeg_get_large; the pool management just links everything together + * so that we can free it all on demand. + * Note: the major use of "large" objects is in JSAMPARRAY and JBLOCKARRAY + * structures. The routines that create these structures (see below) + * deliberately bunch rows together to ensure a large request size. + */ + +METHODDEF(void FAR *) +alloc_large (j_common_ptr cinfo, int pool_id, size_t sizeofobject) +/* Allocate a "large" object */ +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + large_pool_ptr hdr_ptr; + size_t odd_bytes; + + /* Check for unsatisfiable request (do now to ensure no overflow below) */ + if (sizeofobject > (size_t) (MAX_ALLOC_CHUNK-SIZEOF(large_pool_hdr))) + out_of_memory(cinfo, 3); /* request exceeds malloc's ability */ + + /* Round up the requested size to a multiple of SIZEOF(ALIGN_TYPE) */ + odd_bytes = sizeofobject % SIZEOF(ALIGN_TYPE); + if (odd_bytes > 0) + sizeofobject += SIZEOF(ALIGN_TYPE) - odd_bytes; + + /* Always make a new pool */ + if (pool_id < 0 || pool_id >= JPOOL_NUMPOOLS) + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); /* safety check */ + + hdr_ptr = (large_pool_ptr) jpeg_get_large(cinfo, sizeofobject + + SIZEOF(large_pool_hdr)); + if (hdr_ptr == NULL) + out_of_memory(cinfo, 4); /* jpeg_get_large failed */ + mem->total_space_allocated += sizeofobject + SIZEOF(large_pool_hdr); + + /* Success, initialize the new pool header and add to list */ + hdr_ptr->hdr.next = mem->large_list[pool_id]; + /* We maintain space counts in each pool header for statistical purposes, + * even though they are not needed for allocation. + */ + hdr_ptr->hdr.bytes_used = sizeofobject; + hdr_ptr->hdr.bytes_left = 0; + mem->large_list[pool_id] = hdr_ptr; + + return (void FAR *) (hdr_ptr + 1); /* point to first data byte in pool */ +} + +/* + * Creation of 2-D sample arrays. + * The pointers are in near heap, the samples themselves in FAR heap. + * + * To minimize allocation overhead and to allow I/O of large contiguous + * blocks, we allocate the sample rows in groups of as many rows as possible + * without exceeding MAX_ALLOC_CHUNK total bytes per allocation request. + * NB: the virtual array control routines, later in this file, know about + * this chunking of rows. The rowsperchunk value is left in the mem manager + * object so that it can be saved away if this sarray is the workspace for + * a virtual array. + */ + +METHODDEF(JSAMPARRAY) +alloc_sarray (j_common_ptr cinfo, int pool_id, + JDIMENSION samplesperrow, JDIMENSION numrows) +/* Allocate a 2-D sample array */ +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + JSAMPARRAY result; + JSAMPROW workspace; + JDIMENSION rowsperchunk, currow, i; + long ltemp; + + /* Calculate max # of rows allowed in one allocation chunk */ + ltemp = (MAX_ALLOC_CHUNK-SIZEOF(large_pool_hdr)) / + ((long) samplesperrow * SIZEOF(JSAMPLE)); + if (ltemp <= 0) + ERREXIT(cinfo, JERR_WIDTH_OVERFLOW); + if (ltemp < (long) numrows) + rowsperchunk = (JDIMENSION) ltemp; + else + rowsperchunk = numrows; + mem->last_rowsperchunk = rowsperchunk; + + /* Get space for row pointers (small object) */ + result = (JSAMPARRAY) alloc_small(cinfo, pool_id, + (size_t) (numrows * SIZEOF(JSAMPROW))); + + /* Get the rows themselves (large objects) */ + currow = 0; + while (currow < numrows) { + rowsperchunk = MIN(rowsperchunk, numrows - currow); + workspace = (JSAMPROW) alloc_large(cinfo, pool_id, + (size_t) ((size_t) rowsperchunk * (size_t) samplesperrow + * SIZEOF(JSAMPLE))); + for (i = rowsperchunk; i > 0; i--) { + result[currow++] = workspace; + workspace += samplesperrow; + } + } + + return result; +} + +/* + * Creation of 2-D coefficient-block arrays. + * This is essentially the same as the code for sample arrays, above. + */ + +METHODDEF(JBLOCKARRAY) +alloc_barray (j_common_ptr cinfo, int pool_id, + JDIMENSION blocksperrow, JDIMENSION numrows) +/* Allocate a 2-D coefficient-block array */ +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + JBLOCKARRAY result; + JBLOCKROW workspace; + JDIMENSION rowsperchunk, currow, i; + long ltemp; + + /* Calculate max # of rows allowed in one allocation chunk */ + ltemp = (MAX_ALLOC_CHUNK-SIZEOF(large_pool_hdr)) / + ((long) blocksperrow * SIZEOF(JBLOCK)); + if (ltemp <= 0) + ERREXIT(cinfo, JERR_WIDTH_OVERFLOW); + if (ltemp < (long) numrows) + rowsperchunk = (JDIMENSION) ltemp; + else + rowsperchunk = numrows; + mem->last_rowsperchunk = rowsperchunk; + + /* Get space for row pointers (small object) */ + result = (JBLOCKARRAY) alloc_small(cinfo, pool_id, + (size_t) (numrows * SIZEOF(JBLOCKROW))); + + /* Get the rows themselves (large objects) */ + currow = 0; + while (currow < numrows) { + rowsperchunk = MIN(rowsperchunk, numrows - currow); + workspace = (JBLOCKROW) alloc_large(cinfo, pool_id, + (size_t) ((size_t) rowsperchunk * (size_t) blocksperrow + * SIZEOF(JBLOCK))); + for (i = rowsperchunk; i > 0; i--) { + result[currow++] = workspace; + workspace += blocksperrow; + } + } + + return result; +} + +/* + * About virtual array management: + * + * The above "normal" array routines are only used to allocate strip buffers + * (as wide as the image, but just a few rows high). Full-image-sized buffers + * are handled as "virtual" arrays. The array is still accessed a strip at a + * time, but the memory manager must save the whole array for repeated + * accesses. The intended implementation is that there is a strip buffer in + * memory (as high as is possible given the desired memory limit), plus a + * backing file that holds the rest of the array. + * + * The request_virt_array routines are told the total size of the image and + * the maximum number of rows that will be accessed at once. The in-memory + * buffer must be at least as large as the maxaccess value. + * + * The request routines create control blocks but not the in-memory buffers. + * That is postponed until realize_virt_arrays is called. At that time the + * total amount of space needed is known (approximately, anyway), so free + * memory can be divided up fairly. + * + * The access_virt_array routines are responsible for making a specific strip + * area accessible (after reading or writing the backing file, if necessary). + * Note that the access routines are told whether the caller intends to modify + * the accessed strip; during a read-only pass this saves having to rewrite + * data to disk. The access routines are also responsible for pre-zeroing + * any newly accessed rows, if pre-zeroing was requested. + * + * In current usage, the access requests are usually for nonoverlapping + * strips; that is, successive access start_row numbers differ by exactly + * num_rows = maxaccess. This means we can get good performance with simple + * buffer dump/reload logic, by making the in-memory buffer be a multiple + * of the access height; then there will never be accesses across bufferload + * boundaries. The code will still work with overlapping access requests, + * but it doesn't handle bufferload overlaps very efficiently. + */ + +METHODDEF(jvirt_sarray_ptr) +request_virt_sarray (j_common_ptr cinfo, int pool_id, boolean pre_zero, + JDIMENSION samplesperrow, JDIMENSION numrows, + JDIMENSION maxaccess) +/* Request a virtual 2-D sample array */ +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + jvirt_sarray_ptr result; + + /* Only IMAGE-lifetime virtual arrays are currently supported */ + if (pool_id != JPOOL_IMAGE) + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); /* safety check */ + + /* get control block */ + result = (jvirt_sarray_ptr) alloc_small(cinfo, pool_id, + SIZEOF(struct jvirt_sarray_control)); + + result->mem_buffer = NULL; /* marks array not yet realized */ + result->rows_in_array = numrows; + result->samplesperrow = samplesperrow; + result->maxaccess = maxaccess; + result->pre_zero = pre_zero; + result->b_s_open = FALSE; /* no associated backing-store object */ + result->next = mem->virt_sarray_list; /* add to list of virtual arrays */ + mem->virt_sarray_list = result; + + return result; +} + +METHODDEF(jvirt_barray_ptr) +request_virt_barray (j_common_ptr cinfo, int pool_id, boolean pre_zero, + JDIMENSION blocksperrow, JDIMENSION numrows, + JDIMENSION maxaccess) +/* Request a virtual 2-D coefficient-block array */ +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + jvirt_barray_ptr result; + + /* Only IMAGE-lifetime virtual arrays are currently supported */ + if (pool_id != JPOOL_IMAGE) + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); /* safety check */ + + /* get control block */ + result = (jvirt_barray_ptr) alloc_small(cinfo, pool_id, + SIZEOF(struct jvirt_barray_control)); + + result->mem_buffer = NULL; /* marks array not yet realized */ + result->rows_in_array = numrows; + result->blocksperrow = blocksperrow; + result->maxaccess = maxaccess; + result->pre_zero = pre_zero; + result->b_s_open = FALSE; /* no associated backing-store object */ + result->next = mem->virt_barray_list; /* add to list of virtual arrays */ + mem->virt_barray_list = result; + + return result; +} + +METHODDEF(void) +realize_virt_arrays (j_common_ptr cinfo) +/* Allocate the in-memory buffers for any unrealized virtual arrays */ +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + long space_per_minheight, maximum_space, avail_mem; + long minheights, max_minheights; + jvirt_sarray_ptr sptr; + jvirt_barray_ptr bptr; + + /* Compute the minimum space needed (maxaccess rows in each buffer) + * and the maximum space needed (full image height in each buffer). + * These may be of use to the system-dependent jpeg_mem_available routine. + */ + space_per_minheight = 0; + maximum_space = 0; + for (sptr = mem->virt_sarray_list; sptr != NULL; sptr = sptr->next) { + if (sptr->mem_buffer == NULL) { /* if not realized yet */ + space_per_minheight += (long) sptr->maxaccess * + (long) sptr->samplesperrow * SIZEOF(JSAMPLE); + maximum_space += (long) sptr->rows_in_array * + (long) sptr->samplesperrow * SIZEOF(JSAMPLE); + } + } + for (bptr = mem->virt_barray_list; bptr != NULL; bptr = bptr->next) { + if (bptr->mem_buffer == NULL) { /* if not realized yet */ + space_per_minheight += (long) bptr->maxaccess * + (long) bptr->blocksperrow * SIZEOF(JBLOCK); + maximum_space += (long) bptr->rows_in_array * + (long) bptr->blocksperrow * SIZEOF(JBLOCK); + } + } + + if (space_per_minheight <= 0) + return; /* no unrealized arrays, no work */ + + /* Determine amount of memory to actually use; this is system-dependent. */ + avail_mem = jpeg_mem_available(cinfo, space_per_minheight, maximum_space, + mem->total_space_allocated); + + /* If the maximum space needed is available, make all the buffers full + * height; otherwise parcel it out with the same number of minheights + * in each buffer. + */ + if (avail_mem >= maximum_space) + max_minheights = 1000000000L; + else { + max_minheights = avail_mem / space_per_minheight; + /* If there doesn't seem to be enough space, try to get the minimum + * anyway. This allows a "stub" implementation of jpeg_mem_available(). + */ + if (max_minheights <= 0) + max_minheights = 1; + } + + /* Allocate the in-memory buffers and initialize backing store as needed. */ + + for (sptr = mem->virt_sarray_list; sptr != NULL; sptr = sptr->next) { + if (sptr->mem_buffer == NULL) { /* if not realized yet */ + minheights = ((long) sptr->rows_in_array - 1L) / sptr->maxaccess + 1L; + if (minheights <= max_minheights) { + /* This buffer fits in memory */ + sptr->rows_in_mem = sptr->rows_in_array; + } else { + /* It doesn't fit in memory, create backing store. */ + sptr->rows_in_mem = (JDIMENSION) (max_minheights * sptr->maxaccess); + jpeg_open_backing_store(cinfo, & sptr->b_s_info, + (long) sptr->rows_in_array * + (long) sptr->samplesperrow * + (long) SIZEOF(JSAMPLE)); + sptr->b_s_open = TRUE; + } + sptr->mem_buffer = alloc_sarray(cinfo, JPOOL_IMAGE, + sptr->samplesperrow, sptr->rows_in_mem); + sptr->rowsperchunk = mem->last_rowsperchunk; + sptr->cur_start_row = 0; + sptr->first_undef_row = 0; + sptr->dirty = FALSE; + } + } + + for (bptr = mem->virt_barray_list; bptr != NULL; bptr = bptr->next) { + if (bptr->mem_buffer == NULL) { /* if not realized yet */ + minheights = ((long) bptr->rows_in_array - 1L) / bptr->maxaccess + 1L; + if (minheights <= max_minheights) { + /* This buffer fits in memory */ + bptr->rows_in_mem = bptr->rows_in_array; + } else { + /* It doesn't fit in memory, create backing store. */ + bptr->rows_in_mem = (JDIMENSION) (max_minheights * bptr->maxaccess); + jpeg_open_backing_store(cinfo, & bptr->b_s_info, + (long) bptr->rows_in_array * + (long) bptr->blocksperrow * + (long) SIZEOF(JBLOCK)); + bptr->b_s_open = TRUE; + } + bptr->mem_buffer = alloc_barray(cinfo, JPOOL_IMAGE, + bptr->blocksperrow, bptr->rows_in_mem); + bptr->rowsperchunk = mem->last_rowsperchunk; + bptr->cur_start_row = 0; + bptr->first_undef_row = 0; + bptr->dirty = FALSE; + } + } +} + +LOCAL(void) +do_sarray_io (j_common_ptr cinfo, jvirt_sarray_ptr ptr, boolean writing) +/* Do backing store read or write of a virtual sample array */ +{ + long bytesperrow, file_offset, byte_count, rows, thisrow, i; + + bytesperrow = (long) ptr->samplesperrow * SIZEOF(JSAMPLE); + file_offset = ptr->cur_start_row * bytesperrow; + /* Loop to read or write each allocation chunk in mem_buffer */ + for (i = 0; i < (long) ptr->rows_in_mem; i += ptr->rowsperchunk) { + /* One chunk, but check for short chunk at end of buffer */ + rows = MIN((long) ptr->rowsperchunk, (long) ptr->rows_in_mem - i); + /* Transfer no more than is currently defined */ + thisrow = (long) ptr->cur_start_row + i; + rows = MIN(rows, (long) ptr->first_undef_row - thisrow); + /* Transfer no more than fits in file */ + rows = MIN(rows, (long) ptr->rows_in_array - thisrow); + if (rows <= 0) /* this chunk might be past end of file! */ + break; + byte_count = rows * bytesperrow; + if (writing) + (*ptr->b_s_info.write_backing_store) (cinfo, & ptr->b_s_info, + (void FAR *) ptr->mem_buffer[i], + file_offset, byte_count); + else + (*ptr->b_s_info.read_backing_store) (cinfo, & ptr->b_s_info, + (void FAR *) ptr->mem_buffer[i], + file_offset, byte_count); + file_offset += byte_count; + } +} + +LOCAL(void) +do_barray_io (j_common_ptr cinfo, jvirt_barray_ptr ptr, boolean writing) +/* Do backing store read or write of a virtual coefficient-block array */ +{ + long bytesperrow, file_offset, byte_count, rows, thisrow, i; + + bytesperrow = (long) ptr->blocksperrow * SIZEOF(JBLOCK); + file_offset = ptr->cur_start_row * bytesperrow; + /* Loop to read or write each allocation chunk in mem_buffer */ + for (i = 0; i < (long) ptr->rows_in_mem; i += ptr->rowsperchunk) { + /* One chunk, but check for short chunk at end of buffer */ + rows = MIN((long) ptr->rowsperchunk, (long) ptr->rows_in_mem - i); + /* Transfer no more than is currently defined */ + thisrow = (long) ptr->cur_start_row + i; + rows = MIN(rows, (long) ptr->first_undef_row - thisrow); + /* Transfer no more than fits in file */ + rows = MIN(rows, (long) ptr->rows_in_array - thisrow); + if (rows <= 0) /* this chunk might be past end of file! */ + break; + byte_count = rows * bytesperrow; + if (writing) + (*ptr->b_s_info.write_backing_store) (cinfo, & ptr->b_s_info, + (void FAR *) ptr->mem_buffer[i], + file_offset, byte_count); + else + (*ptr->b_s_info.read_backing_store) (cinfo, & ptr->b_s_info, + (void FAR *) ptr->mem_buffer[i], + file_offset, byte_count); + file_offset += byte_count; + } +} + +METHODDEF(JSAMPARRAY) +access_virt_sarray (j_common_ptr cinfo, jvirt_sarray_ptr ptr, + JDIMENSION start_row, JDIMENSION num_rows, + boolean writable) +/* Access the part of a virtual sample array starting at start_row */ +/* and extending for num_rows rows. writable is true if */ +/* caller intends to modify the accessed area. */ +{ + JDIMENSION end_row = start_row + num_rows; + JDIMENSION undef_row; + + /* debugging check */ + if (end_row > ptr->rows_in_array || num_rows > ptr->maxaccess || + ptr->mem_buffer == NULL) + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + + /* Make the desired part of the virtual array accessible */ + if (start_row < ptr->cur_start_row || + end_row > ptr->cur_start_row+ptr->rows_in_mem) { + if (! ptr->b_s_open) + ERREXIT(cinfo, JERR_VIRTUAL_BUG); + /* Flush old buffer contents if necessary */ + if (ptr->dirty) { + do_sarray_io(cinfo, ptr, TRUE); + ptr->dirty = FALSE; + } + /* Decide what part of virtual array to access. + * Algorithm: if target address > current window, assume forward scan, + * load starting at target address. If target address < current window, + * assume backward scan, load so that target area is top of window. + * Note that when switching from forward write to forward read, will have + * start_row = 0, so the limiting case applies and we load from 0 anyway. + */ + if (start_row > ptr->cur_start_row) { + ptr->cur_start_row = start_row; + } else { + /* use long arithmetic here to avoid overflow & unsigned problems */ + long ltemp; + + ltemp = (long) end_row - (long) ptr->rows_in_mem; + if (ltemp < 0) + ltemp = 0; /* don't fall off front end of file */ + ptr->cur_start_row = (JDIMENSION) ltemp; + } + /* Read in the selected part of the array. + * During the initial write pass, we will do no actual read + * because the selected part is all undefined. + */ + do_sarray_io(cinfo, ptr, FALSE); + } + /* Ensure the accessed part of the array is defined; prezero if needed. + * To improve locality of access, we only prezero the part of the array + * that the caller is about to access, not the entire in-memory array. + */ + if (ptr->first_undef_row < end_row) { + if (ptr->first_undef_row < start_row) { + if (writable) /* writer skipped over a section of array */ + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + undef_row = start_row; /* but reader is allowed to read ahead */ + } else { + undef_row = ptr->first_undef_row; + } + if (writable) + ptr->first_undef_row = end_row; + if (ptr->pre_zero) { + size_t bytesperrow = (size_t) ptr->samplesperrow * SIZEOF(JSAMPLE); + undef_row -= ptr->cur_start_row; /* make indexes relative to buffer */ + end_row -= ptr->cur_start_row; + while (undef_row < end_row) { + jzero_far((void FAR *) ptr->mem_buffer[undef_row], bytesperrow); + undef_row++; + } + } else { + if (! writable) /* reader looking at undefined data */ + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + } + } + /* Flag the buffer dirty if caller will write in it */ + if (writable) + ptr->dirty = TRUE; + /* Return address of proper part of the buffer */ + return ptr->mem_buffer + (start_row - ptr->cur_start_row); +} + +METHODDEF(JBLOCKARRAY) +access_virt_barray (j_common_ptr cinfo, jvirt_barray_ptr ptr, + JDIMENSION start_row, JDIMENSION num_rows, + boolean writable) +/* Access the part of a virtual block array starting at start_row */ +/* and extending for num_rows rows. writable is true if */ +/* caller intends to modify the accessed area. */ +{ + JDIMENSION end_row = start_row + num_rows; + JDIMENSION undef_row; + + /* debugging check */ + if (end_row > ptr->rows_in_array || num_rows > ptr->maxaccess || + ptr->mem_buffer == NULL) + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + + /* Make the desired part of the virtual array accessible */ + if (start_row < ptr->cur_start_row || + end_row > ptr->cur_start_row+ptr->rows_in_mem) { + if (! ptr->b_s_open) + ERREXIT(cinfo, JERR_VIRTUAL_BUG); + /* Flush old buffer contents if necessary */ + if (ptr->dirty) { + do_barray_io(cinfo, ptr, TRUE); + ptr->dirty = FALSE; + } + /* Decide what part of virtual array to access. + * Algorithm: if target address > current window, assume forward scan, + * load starting at target address. If target address < current window, + * assume backward scan, load so that target area is top of window. + * Note that when switching from forward write to forward read, will have + * start_row = 0, so the limiting case applies and we load from 0 anyway. + */ + if (start_row > ptr->cur_start_row) { + ptr->cur_start_row = start_row; + } else { + /* use long arithmetic here to avoid overflow & unsigned problems */ + long ltemp; + + ltemp = (long) end_row - (long) ptr->rows_in_mem; + if (ltemp < 0) + ltemp = 0; /* don't fall off front end of file */ + ptr->cur_start_row = (JDIMENSION) ltemp; + } + /* Read in the selected part of the array. + * During the initial write pass, we will do no actual read + * because the selected part is all undefined. + */ + do_barray_io(cinfo, ptr, FALSE); + } + /* Ensure the accessed part of the array is defined; prezero if needed. + * To improve locality of access, we only prezero the part of the array + * that the caller is about to access, not the entire in-memory array. + */ + if (ptr->first_undef_row < end_row) { + if (ptr->first_undef_row < start_row) { + if (writable) /* writer skipped over a section of array */ + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + undef_row = start_row; /* but reader is allowed to read ahead */ + } else { + undef_row = ptr->first_undef_row; + } + if (writable) + ptr->first_undef_row = end_row; + if (ptr->pre_zero) { + size_t bytesperrow = (size_t) ptr->blocksperrow * SIZEOF(JBLOCK); + undef_row -= ptr->cur_start_row; /* make indexes relative to buffer */ + end_row -= ptr->cur_start_row; + while (undef_row < end_row) { + jzero_far((void FAR *) ptr->mem_buffer[undef_row], bytesperrow); + undef_row++; + } + } else { + if (! writable) /* reader looking at undefined data */ + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + } + } + /* Flag the buffer dirty if caller will write in it */ + if (writable) + ptr->dirty = TRUE; + /* Return address of proper part of the buffer */ + return ptr->mem_buffer + (start_row - ptr->cur_start_row); +} + +/* + * Release all objects belonging to a specified pool. + */ + +METHODDEF(void) +free_pool (j_common_ptr cinfo, int pool_id) +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + small_pool_ptr shdr_ptr; + large_pool_ptr lhdr_ptr; + size_t space_freed; + + if (pool_id < 0 || pool_id >= JPOOL_NUMPOOLS) + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); /* safety check */ + +#ifdef MEM_STATS + if (cinfo->err->trace_level > 1) + print_mem_stats(cinfo, pool_id); /* print pool's memory usage statistics */ +#endif + + /* If freeing IMAGE pool, close any virtual arrays first */ + if (pool_id == JPOOL_IMAGE) { + jvirt_sarray_ptr sptr; + jvirt_barray_ptr bptr; + + for (sptr = mem->virt_sarray_list; sptr != NULL; sptr = sptr->next) { + if (sptr->b_s_open) { /* there may be no backing store */ + sptr->b_s_open = FALSE; /* prevent recursive close if error */ + (*sptr->b_s_info.close_backing_store) (cinfo, & sptr->b_s_info); + } + } + mem->virt_sarray_list = NULL; + for (bptr = mem->virt_barray_list; bptr != NULL; bptr = bptr->next) { + if (bptr->b_s_open) { /* there may be no backing store */ + bptr->b_s_open = FALSE; /* prevent recursive close if error */ + (*bptr->b_s_info.close_backing_store) (cinfo, & bptr->b_s_info); + } + } + mem->virt_barray_list = NULL; + } + + /* Release large objects */ + lhdr_ptr = mem->large_list[pool_id]; + mem->large_list[pool_id] = NULL; + + while (lhdr_ptr != NULL) { + large_pool_ptr next_lhdr_ptr = lhdr_ptr->hdr.next; + space_freed = lhdr_ptr->hdr.bytes_used + + lhdr_ptr->hdr.bytes_left + + SIZEOF(large_pool_hdr); + jpeg_free_large(cinfo, (void FAR *) lhdr_ptr, space_freed); + mem->total_space_allocated -= space_freed; + lhdr_ptr = next_lhdr_ptr; + } + + /* Release small objects */ + shdr_ptr = mem->small_list[pool_id]; + mem->small_list[pool_id] = NULL; + + while (shdr_ptr != NULL) { + small_pool_ptr next_shdr_ptr = shdr_ptr->hdr.next; + space_freed = shdr_ptr->hdr.bytes_used + + shdr_ptr->hdr.bytes_left + + SIZEOF(small_pool_hdr); + jpeg_free_small(cinfo, (void *) shdr_ptr, space_freed); + mem->total_space_allocated -= space_freed; + shdr_ptr = next_shdr_ptr; + } +} + +/* + * Close up shop entirely. + * Note that this cannot be called unless cinfo->mem is non-NULL. + */ + +METHODDEF(void) +self_destruct (j_common_ptr cinfo) +{ + int pool; + + /* Close all backing store, release all memory. + * Releasing pools in reverse order might help avoid fragmentation + * with some (brain-damaged) malloc libraries. + */ + for (pool = JPOOL_NUMPOOLS-1; pool >= JPOOL_PERMANENT; pool--) { + free_pool(cinfo, pool); + } + + /* Release the memory manager control block too. */ + jpeg_free_small(cinfo, (void *) cinfo->mem, SIZEOF(my_memory_mgr)); + cinfo->mem = NULL; /* ensures I will be called only once */ + + jpeg_mem_term(cinfo); /* system-dependent cleanup */ +} + +/* + * Memory manager initialization. + * When this is called, only the error manager pointer is valid in cinfo! + */ + +GLOBAL(void) +jinit_memory_mgr (j_common_ptr cinfo) +{ + my_mem_ptr mem; + long max_to_use; + int pool; + size_t test_mac; + + cinfo->mem = NULL; /* for safety if init fails */ + + /* Check for configuration errors. + * SIZEOF(ALIGN_TYPE) should be a power of 2; otherwise, it probably + * doesn't reflect any real hardware alignment requirement. + * The test is a little tricky: for X>0, X and X-1 have no one-bits + * in common if and only if X is a power of 2, ie has only one one-bit. + * Some compilers may give an "unreachable code" warning here; ignore it. + */ + if ((SIZEOF(ALIGN_TYPE) & (SIZEOF(ALIGN_TYPE)-1)) != 0) + ERREXIT(cinfo, JERR_BAD_ALIGN_TYPE); + /* MAX_ALLOC_CHUNK must be representable as type size_t, and must be + * a multiple of SIZEOF(ALIGN_TYPE). + * Again, an "unreachable code" warning may be ignored here. + * But a "constant too large" warning means you need to fix MAX_ALLOC_CHUNK. + */ + test_mac = (size_t) MAX_ALLOC_CHUNK; + if ((long) test_mac != MAX_ALLOC_CHUNK || + (MAX_ALLOC_CHUNK % SIZEOF(ALIGN_TYPE)) != 0) + ERREXIT(cinfo, JERR_BAD_ALLOC_CHUNK); + + max_to_use = jpeg_mem_init(cinfo); /* system-dependent initialization */ + + /* Attempt to allocate memory manager's control block */ + mem = (my_mem_ptr) jpeg_get_small(cinfo, SIZEOF(my_memory_mgr)); + + if (mem == NULL) { + jpeg_mem_term(cinfo); /* system-dependent cleanup */ + ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, 0); + } + + /* OK, fill in the method pointers */ + mem->pub.alloc_small = alloc_small; + mem->pub.alloc_large = alloc_large; + mem->pub.alloc_sarray = alloc_sarray; + mem->pub.alloc_barray = alloc_barray; + mem->pub.request_virt_sarray = request_virt_sarray; + mem->pub.request_virt_barray = request_virt_barray; + mem->pub.realize_virt_arrays = realize_virt_arrays; + mem->pub.access_virt_sarray = access_virt_sarray; + mem->pub.access_virt_barray = access_virt_barray; + mem->pub.free_pool = free_pool; + mem->pub.self_destruct = self_destruct; + + /* Make MAX_ALLOC_CHUNK accessible to other modules */ + mem->pub.max_alloc_chunk = MAX_ALLOC_CHUNK; + + /* Initialize working state */ + mem->pub.max_memory_to_use = max_to_use; + + for (pool = JPOOL_NUMPOOLS-1; pool >= JPOOL_PERMANENT; pool--) { + mem->small_list[pool] = NULL; + mem->large_list[pool] = NULL; + } + mem->virt_sarray_list = NULL; + mem->virt_barray_list = NULL; + + mem->total_space_allocated = SIZEOF(my_memory_mgr); + + /* Declare ourselves open for business */ + cinfo->mem = & mem->pub; + + /* Check for an environment variable JPEGMEM; if found, override the + * default max_memory setting from jpeg_mem_init. Note that the + * surrounding application may again override this value. + * If your system doesn't support getenv(), define NO_GETENV to disable + * this feature. + */ +#ifndef NO_GETENV + { char * memenv; + + if ((memenv = getenv("JPEGMEM")) != NULL) { + char ch = 'x'; + + if (sscanf(memenv, "%ld%c", &max_to_use, &ch) > 0) { + if (ch == 'm' || ch == 'M') + max_to_use *= 1000L; + mem->pub.max_memory_to_use = max_to_use * 1000L; + } + } + } +#endif + +} +/********* End of inlined file: jmemmgr.c *********/ + +/********* Start of inlined file: jmemnobs.c *********/ +#define JPEG_INTERNALS + +#ifndef HAVE_STDLIB_H /* should declare malloc(),free() */ +extern void * malloc JPP((size_t size)); +extern void free JPP((void *ptr)); +#endif + +/* + * Memory allocation and freeing are controlled by the regular library + * routines malloc() and free(). + */ + +GLOBAL(void *) +jpeg_get_small (j_common_ptr cinfo, size_t sizeofobject) +{ + return (void *) malloc(sizeofobject); +} + +GLOBAL(void) +jpeg_free_small (j_common_ptr cinfo, void * object, size_t sizeofobject) +{ + free(object); +} + +/* + * "Large" objects are treated the same as "small" ones. + * NB: although we include FAR keywords in the routine declarations, + * this file won't actually work in 80x86 small/medium model; at least, + * you probably won't be able to process useful-size images in only 64KB. + */ + +GLOBAL(void FAR *) +jpeg_get_large (j_common_ptr cinfo, size_t sizeofobject) +{ + return (void FAR *) malloc(sizeofobject); +} + +GLOBAL(void) +jpeg_free_large (j_common_ptr cinfo, void FAR * object, size_t sizeofobject) +{ + free(object); +} + +/* + * This routine computes the total memory space available for allocation. + * Here we always say, "we got all you want bud!" + */ + +GLOBAL(long) +jpeg_mem_available (j_common_ptr cinfo, long min_bytes_needed, + long max_bytes_needed, long already_allocated) +{ + return max_bytes_needed; +} + +/* + * Backing store (temporary file) management. + * Since jpeg_mem_available always promised the moon, + * this should never be called and we can just error out. + */ + +GLOBAL(void) +jpeg_open_backing_store (j_common_ptr cinfo, struct backing_store_struct *info, + long total_bytes_needed) +{ + ERREXIT(cinfo, JERR_NO_BACKING_STORE); +} + +/* + * These routines take care of any system-dependent initialization and + * cleanup required. Here, there isn't any. + */ + +GLOBAL(long) +jpeg_mem_init (j_common_ptr cinfo) +{ + return 0; /* just set max_memory_to_use to 0 */ +} + +GLOBAL(void) +jpeg_mem_term (j_common_ptr cinfo) +{ + /* no work */ +} +/********* End of inlined file: jmemnobs.c *********/ + +/********* Start of inlined file: jquant1.c *********/ +#define JPEG_INTERNALS + +#ifdef QUANT_1PASS_SUPPORTED + +/* + * The main purpose of 1-pass quantization is to provide a fast, if not very + * high quality, colormapped output capability. A 2-pass quantizer usually + * gives better visual quality; however, for quantized grayscale output this + * quantizer is perfectly adequate. Dithering is highly recommended with this + * quantizer, though you can turn it off if you really want to. + * + * In 1-pass quantization the colormap must be chosen in advance of seeing the + * image. We use a map consisting of all combinations of Ncolors[i] color + * values for the i'th component. The Ncolors[] values are chosen so that + * their product, the total number of colors, is no more than that requested. + * (In most cases, the product will be somewhat less.) + * + * Since the colormap is orthogonal, the representative value for each color + * component can be determined without considering the other components; + * then these indexes can be combined into a colormap index by a standard + * N-dimensional-array-subscript calculation. Most of the arithmetic involved + * can be precalculated and stored in the lookup table colorindex[]. + * colorindex[i][j] maps pixel value j in component i to the nearest + * representative value (grid plane) for that component; this index is + * multiplied by the array stride for component i, so that the + * index of the colormap entry closest to a given pixel value is just + * sum( colorindex[component-number][pixel-component-value] ) + * Aside from being fast, this scheme allows for variable spacing between + * representative values with no additional lookup cost. + * + * If gamma correction has been applied in color conversion, it might be wise + * to adjust the color grid spacing so that the representative colors are + * equidistant in linear space. At this writing, gamma correction is not + * implemented by jdcolor, so nothing is done here. + */ + +/* Declarations for ordered dithering. + * + * We use a standard 16x16 ordered dither array. The basic concept of ordered + * dithering is described in many references, for instance Dale Schumacher's + * chapter II.2 of Graphics Gems II (James Arvo, ed. Academic Press, 1991). + * In place of Schumacher's comparisons against a "threshold" value, we add a + * "dither" value to the input pixel and then round the result to the nearest + * output value. The dither value is equivalent to (0.5 - threshold) times + * the distance between output values. For ordered dithering, we assume that + * the output colors are equally spaced; if not, results will probably be + * worse, since the dither may be too much or too little at a given point. + * + * The normal calculation would be to form pixel value + dither, range-limit + * this to 0..MAXJSAMPLE, and then index into the colorindex table as usual. + * We can skip the separate range-limiting step by extending the colorindex + * table in both directions. + */ + +#define ODITHER_SIZE 16 /* dimension of dither matrix */ +/* NB: if ODITHER_SIZE is not a power of 2, ODITHER_MASK uses will break */ +#define ODITHER_CELLS (ODITHER_SIZE*ODITHER_SIZE) /* # cells in matrix */ +#define ODITHER_MASK (ODITHER_SIZE-1) /* mask for wrapping around counters */ + +typedef int ODITHER_MATRIX[ODITHER_SIZE][ODITHER_SIZE]; +typedef int (*ODITHER_MATRIX_PTR)[ODITHER_SIZE]; + +static const UINT8 base_dither_matrix[ODITHER_SIZE][ODITHER_SIZE] = { + /* Bayer's order-4 dither array. Generated by the code given in + * Stephen Hawley's article "Ordered Dithering" in Graphics Gems I. + * The values in this array must range from 0 to ODITHER_CELLS-1. + */ + { 0,192, 48,240, 12,204, 60,252, 3,195, 51,243, 15,207, 63,255 }, + { 128, 64,176,112,140, 76,188,124,131, 67,179,115,143, 79,191,127 }, + { 32,224, 16,208, 44,236, 28,220, 35,227, 19,211, 47,239, 31,223 }, + { 160, 96,144, 80,172,108,156, 92,163, 99,147, 83,175,111,159, 95 }, + { 8,200, 56,248, 4,196, 52,244, 11,203, 59,251, 7,199, 55,247 }, + { 136, 72,184,120,132, 68,180,116,139, 75,187,123,135, 71,183,119 }, + { 40,232, 24,216, 36,228, 20,212, 43,235, 27,219, 39,231, 23,215 }, + { 168,104,152, 88,164,100,148, 84,171,107,155, 91,167,103,151, 87 }, + { 2,194, 50,242, 14,206, 62,254, 1,193, 49,241, 13,205, 61,253 }, + { 130, 66,178,114,142, 78,190,126,129, 65,177,113,141, 77,189,125 }, + { 34,226, 18,210, 46,238, 30,222, 33,225, 17,209, 45,237, 29,221 }, + { 162, 98,146, 82,174,110,158, 94,161, 97,145, 81,173,109,157, 93 }, + { 10,202, 58,250, 6,198, 54,246, 9,201, 57,249, 5,197, 53,245 }, + { 138, 74,186,122,134, 70,182,118,137, 73,185,121,133, 69,181,117 }, + { 42,234, 26,218, 38,230, 22,214, 41,233, 25,217, 37,229, 21,213 }, + { 170,106,154, 90,166,102,150, 86,169,105,153, 89,165,101,149, 85 } +}; + +/* Declarations for Floyd-Steinberg dithering. + * + * Errors are accumulated into the array fserrors[], at a resolution of + * 1/16th of a pixel count. The error at a given pixel is propagated + * to its not-yet-processed neighbors using the standard F-S fractions, + * ... (here) 7/16 + * 3/16 5/16 1/16 + * We work left-to-right on even rows, right-to-left on odd rows. + * + * We can get away with a single array (holding one row's worth of errors) + * by using it to store the current row's errors at pixel columns not yet + * processed, but the next row's errors at columns already processed. We + * need only a few extra variables to hold the errors immediately around the + * current column. (If we are lucky, those variables are in registers, but + * even if not, they're probably cheaper to access than array elements are.) + * + * The fserrors[] array is indexed [component#][position]. + * We provide (#columns + 2) entries per component; the extra entry at each + * end saves us from special-casing the first and last pixels. + * + * Note: on a wide image, we might not have enough room in a PC's near data + * segment to hold the error array; so it is allocated with alloc_large. + */ + +#if BITS_IN_JSAMPLE == 8 +typedef INT16 FSERROR; /* 16 bits should be enough */ +typedef int LOCFSERROR; /* use 'int' for calculation temps */ +#else +typedef INT32 FSERROR; /* may need more than 16 bits */ +typedef INT32 LOCFSERROR; /* be sure calculation temps are big enough */ +#endif + +typedef FSERROR FAR *FSERRPTR; /* pointer to error array (in FAR storage!) */ + +/* Private subobject */ + +#define MAX_Q_COMPS 4 /* max components I can handle */ + +typedef struct { + struct jpeg_color_quantizer pub; /* public fields */ + + /* Initially allocated colormap is saved here */ + JSAMPARRAY sv_colormap; /* The color map as a 2-D pixel array */ + int sv_actual; /* number of entries in use */ + + JSAMPARRAY colorindex; /* Precomputed mapping for speed */ + /* colorindex[i][j] = index of color closest to pixel value j in component i, + * premultiplied as described above. Since colormap indexes must fit into + * JSAMPLEs, the entries of this array will too. + */ + boolean is_padded; /* is the colorindex padded for odither? */ + + int Ncolors[MAX_Q_COMPS]; /* # of values alloced to each component */ + + /* Variables for ordered dithering */ + int row_index; /* cur row's vertical index in dither matrix */ + ODITHER_MATRIX_PTR odither[MAX_Q_COMPS]; /* one dither array per component */ + + /* Variables for Floyd-Steinberg dithering */ + FSERRPTR fserrors[MAX_Q_COMPS]; /* accumulated errors */ + boolean on_odd_row; /* flag to remember which row we are on */ +} my_cquantizer; + +typedef my_cquantizer * my_cquantize_ptr; + +/* + * Policy-making subroutines for create_colormap and create_colorindex. + * These routines determine the colormap to be used. The rest of the module + * only assumes that the colormap is orthogonal. + * + * * select_ncolors decides how to divvy up the available colors + * among the components. + * * output_value defines the set of representative values for a component. + * * largest_input_value defines the mapping from input values to + * representative values for a component. + * Note that the latter two routines may impose different policies for + * different components, though this is not currently done. + */ + +LOCAL(int) +select_ncolors (j_decompress_ptr cinfo, int Ncolors[]) +/* Determine allocation of desired colors to components, */ +/* and fill in Ncolors[] array to indicate choice. */ +/* Return value is total number of colors (product of Ncolors[] values). */ +{ + int nc = cinfo->out_color_components; /* number of color components */ + int max_colors = cinfo->desired_number_of_colors; + int total_colors, iroot, i, j; + boolean changed; + long temp; + static const int RGB_order[3] = { RGB_GREEN, RGB_RED, RGB_BLUE }; + + /* We can allocate at least the nc'th root of max_colors per component. */ + /* Compute floor(nc'th root of max_colors). */ + iroot = 1; + do { + iroot++; + temp = iroot; /* set temp = iroot ** nc */ + for (i = 1; i < nc; i++) + temp *= iroot; + } while (temp <= (long) max_colors); /* repeat till iroot exceeds root */ + iroot--; /* now iroot = floor(root) */ + + /* Must have at least 2 color values per component */ + if (iroot < 2) + ERREXIT1(cinfo, JERR_QUANT_FEW_COLORS, (int) temp); + + /* Initialize to iroot color values for each component */ + total_colors = 1; + for (i = 0; i < nc; i++) { + Ncolors[i] = iroot; + total_colors *= iroot; + } + /* We may be able to increment the count for one or more components without + * exceeding max_colors, though we know not all can be incremented. + * Sometimes, the first component can be incremented more than once! + * (Example: for 16 colors, we start at 2*2*2, go to 3*2*2, then 4*2*2.) + * In RGB colorspace, try to increment G first, then R, then B. + */ + do { + changed = FALSE; + for (i = 0; i < nc; i++) { + j = (cinfo->out_color_space == JCS_RGB ? RGB_order[i] : i); + /* calculate new total_colors if Ncolors[j] is incremented */ + temp = total_colors / Ncolors[j]; + temp *= Ncolors[j]+1; /* done in long arith to avoid oflo */ + if (temp > (long) max_colors) + break; /* won't fit, done with this pass */ + Ncolors[j]++; /* OK, apply the increment */ + total_colors = (int) temp; + changed = TRUE; + } + } while (changed); + + return total_colors; +} + +LOCAL(int) +output_value (j_decompress_ptr cinfo, int ci, int j, int maxj) +/* Return j'th output value, where j will range from 0 to maxj */ +/* The output values must fall in 0..MAXJSAMPLE in increasing order */ +{ + /* We always provide values 0 and MAXJSAMPLE for each component; + * any additional values are equally spaced between these limits. + * (Forcing the upper and lower values to the limits ensures that + * dithering can't produce a color outside the selected gamut.) + */ + return (int) (((INT32) j * MAXJSAMPLE + maxj/2) / maxj); +} + +LOCAL(int) +largest_input_value (j_decompress_ptr cinfo, int ci, int j, int maxj) +/* Return largest input value that should map to j'th output value */ +/* Must have largest(j=0) >= 0, and largest(j=maxj) >= MAXJSAMPLE */ +{ + /* Breakpoints are halfway between values returned by output_value */ + return (int) (((INT32) (2*j + 1) * MAXJSAMPLE + maxj) / (2*maxj)); +} + +/* + * Create the colormap. + */ + +LOCAL(void) +create_colormap (j_decompress_ptr cinfo) +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + JSAMPARRAY colormap; /* Created colormap */ + int total_colors; /* Number of distinct output colors */ + int i,j,k, nci, blksize, blkdist, ptr, val; + + /* Select number of colors for each component */ + total_colors = select_ncolors(cinfo, cquantize->Ncolors); + + /* Report selected color counts */ + if (cinfo->out_color_components == 3) + TRACEMS4(cinfo, 1, JTRC_QUANT_3_NCOLORS, + total_colors, cquantize->Ncolors[0], + cquantize->Ncolors[1], cquantize->Ncolors[2]); + else + TRACEMS1(cinfo, 1, JTRC_QUANT_NCOLORS, total_colors); + + /* Allocate and fill in the colormap. */ + /* The colors are ordered in the map in standard row-major order, */ + /* i.e. rightmost (highest-indexed) color changes most rapidly. */ + + colormap = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + (JDIMENSION) total_colors, (JDIMENSION) cinfo->out_color_components); + + /* blksize is number of adjacent repeated entries for a component */ + /* blkdist is distance between groups of identical entries for a component */ + blkdist = total_colors; + + for (i = 0; i < cinfo->out_color_components; i++) { + /* fill in colormap entries for i'th color component */ + nci = cquantize->Ncolors[i]; /* # of distinct values for this color */ + blksize = blkdist / nci; + for (j = 0; j < nci; j++) { + /* Compute j'th output value (out of nci) for component */ + val = output_value(cinfo, i, j, nci-1); + /* Fill in all colormap entries that have this value of this component */ + for (ptr = j * blksize; ptr < total_colors; ptr += blkdist) { + /* fill in blksize entries beginning at ptr */ + for (k = 0; k < blksize; k++) + colormap[i][ptr+k] = (JSAMPLE) val; + } + } + blkdist = blksize; /* blksize of this color is blkdist of next */ + } + + /* Save the colormap in private storage, + * where it will survive color quantization mode changes. + */ + cquantize->sv_colormap = colormap; + cquantize->sv_actual = total_colors; +} + +/* + * Create the color index table. + */ + +LOCAL(void) +create_colorindex (j_decompress_ptr cinfo) +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + JSAMPROW indexptr; + int i,j,k, nci, blksize, val, pad; + + /* For ordered dither, we pad the color index tables by MAXJSAMPLE in + * each direction (input index values can be -MAXJSAMPLE .. 2*MAXJSAMPLE). + * This is not necessary in the other dithering modes. However, we + * flag whether it was done in case user changes dithering mode. + */ + if (cinfo->dither_mode == JDITHER_ORDERED) { + pad = MAXJSAMPLE*2; + cquantize->is_padded = TRUE; + } else { + pad = 0; + cquantize->is_padded = FALSE; + } + + cquantize->colorindex = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + (JDIMENSION) (MAXJSAMPLE+1 + pad), + (JDIMENSION) cinfo->out_color_components); + + /* blksize is number of adjacent repeated entries for a component */ + blksize = cquantize->sv_actual; + + for (i = 0; i < cinfo->out_color_components; i++) { + /* fill in colorindex entries for i'th color component */ + nci = cquantize->Ncolors[i]; /* # of distinct values for this color */ + blksize = blksize / nci; + + /* adjust colorindex pointers to provide padding at negative indexes. */ + if (pad) + cquantize->colorindex[i] += MAXJSAMPLE; + + /* in loop, val = index of current output value, */ + /* and k = largest j that maps to current val */ + indexptr = cquantize->colorindex[i]; + val = 0; + k = largest_input_value(cinfo, i, 0, nci-1); + for (j = 0; j <= MAXJSAMPLE; j++) { + while (j > k) /* advance val if past boundary */ + k = largest_input_value(cinfo, i, ++val, nci-1); + /* premultiply so that no multiplication needed in main processing */ + indexptr[j] = (JSAMPLE) (val * blksize); + } + /* Pad at both ends if necessary */ + if (pad) + for (j = 1; j <= MAXJSAMPLE; j++) { + indexptr[-j] = indexptr[0]; + indexptr[MAXJSAMPLE+j] = indexptr[MAXJSAMPLE]; + } + } +} + +/* + * Create an ordered-dither array for a component having ncolors + * distinct output values. + */ + +LOCAL(ODITHER_MATRIX_PTR) +make_odither_array (j_decompress_ptr cinfo, int ncolors) +{ + ODITHER_MATRIX_PTR odither; + int j,k; + INT32 num,den; + + odither = (ODITHER_MATRIX_PTR) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(ODITHER_MATRIX)); + /* The inter-value distance for this color is MAXJSAMPLE/(ncolors-1). + * Hence the dither value for the matrix cell with fill order f + * (f=0..N-1) should be (N-1-2*f)/(2*N) * MAXJSAMPLE/(ncolors-1). + * On 16-bit-int machine, be careful to avoid overflow. + */ + den = 2 * ODITHER_CELLS * ((INT32) (ncolors - 1)); + for (j = 0; j < ODITHER_SIZE; j++) { + for (k = 0; k < ODITHER_SIZE; k++) { + num = ((INT32) (ODITHER_CELLS-1 - 2*((int)base_dither_matrix[j][k]))) + * MAXJSAMPLE; + /* Ensure round towards zero despite C's lack of consistency + * about rounding negative values in integer division... + */ + odither[j][k] = (int) (num<0 ? -((-num)/den) : num/den); + } + } + return odither; +} + +/* + * Create the ordered-dither tables. + * Components having the same number of representative colors may + * share a dither table. + */ + +LOCAL(void) +create_odither_tables (j_decompress_ptr cinfo) +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + ODITHER_MATRIX_PTR odither; + int i, j, nci; + + for (i = 0; i < cinfo->out_color_components; i++) { + nci = cquantize->Ncolors[i]; /* # of distinct values for this color */ + odither = NULL; /* search for matching prior component */ + for (j = 0; j < i; j++) { + if (nci == cquantize->Ncolors[j]) { + odither = cquantize->odither[j]; + break; + } + } + if (odither == NULL) /* need a new table? */ + odither = make_odither_array(cinfo, nci); + cquantize->odither[i] = odither; + } +} + +/* + * Map some rows of pixels to the output colormapped representation. + */ + +METHODDEF(void) +color_quantize (j_decompress_ptr cinfo, JSAMPARRAY input_buf, + JSAMPARRAY output_buf, int num_rows) +/* General case, no dithering */ +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + JSAMPARRAY colorindex = cquantize->colorindex; + register int pixcode, ci; + register JSAMPROW ptrin, ptrout; + int row; + JDIMENSION col; + JDIMENSION width = cinfo->output_width; + register int nc = cinfo->out_color_components; + + for (row = 0; row < num_rows; row++) { + ptrin = input_buf[row]; + ptrout = output_buf[row]; + for (col = width; col > 0; col--) { + pixcode = 0; + for (ci = 0; ci < nc; ci++) { + pixcode += GETJSAMPLE(colorindex[ci][GETJSAMPLE(*ptrin++)]); + } + *ptrout++ = (JSAMPLE) pixcode; + } + } +} + +METHODDEF(void) +color_quantize3 (j_decompress_ptr cinfo, JSAMPARRAY input_buf, + JSAMPARRAY output_buf, int num_rows) +/* Fast path for out_color_components==3, no dithering */ +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + register int pixcode; + register JSAMPROW ptrin, ptrout; + JSAMPROW colorindex0 = cquantize->colorindex[0]; + JSAMPROW colorindex1 = cquantize->colorindex[1]; + JSAMPROW colorindex2 = cquantize->colorindex[2]; + int row; + JDIMENSION col; + JDIMENSION width = cinfo->output_width; + + for (row = 0; row < num_rows; row++) { + ptrin = input_buf[row]; + ptrout = output_buf[row]; + for (col = width; col > 0; col--) { + pixcode = GETJSAMPLE(colorindex0[GETJSAMPLE(*ptrin++)]); + pixcode += GETJSAMPLE(colorindex1[GETJSAMPLE(*ptrin++)]); + pixcode += GETJSAMPLE(colorindex2[GETJSAMPLE(*ptrin++)]); + *ptrout++ = (JSAMPLE) pixcode; + } + } +} + +METHODDEF(void) +quantize_ord_dither (j_decompress_ptr cinfo, JSAMPARRAY input_buf, + JSAMPARRAY output_buf, int num_rows) +/* General case, with ordered dithering */ +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + register JSAMPROW input_ptr; + register JSAMPROW output_ptr; + JSAMPROW colorindex_ci; + int * dither; /* points to active row of dither matrix */ + int row_index, col_index; /* current indexes into dither matrix */ + int nc = cinfo->out_color_components; + int ci; + int row; + JDIMENSION col; + JDIMENSION width = cinfo->output_width; + + for (row = 0; row < num_rows; row++) { + /* Initialize output values to 0 so can process components separately */ + jzero_far((void FAR *) output_buf[row], + (size_t) (width * SIZEOF(JSAMPLE))); + row_index = cquantize->row_index; + for (ci = 0; ci < nc; ci++) { + input_ptr = input_buf[row] + ci; + output_ptr = output_buf[row]; + colorindex_ci = cquantize->colorindex[ci]; + dither = cquantize->odither[ci][row_index]; + col_index = 0; + + for (col = width; col > 0; col--) { + /* Form pixel value + dither, range-limit to 0..MAXJSAMPLE, + * select output value, accumulate into output code for this pixel. + * Range-limiting need not be done explicitly, as we have extended + * the colorindex table to produce the right answers for out-of-range + * inputs. The maximum dither is +- MAXJSAMPLE; this sets the + * required amount of padding. + */ + *output_ptr += colorindex_ci[GETJSAMPLE(*input_ptr)+dither[col_index]]; + input_ptr += nc; + output_ptr++; + col_index = (col_index + 1) & ODITHER_MASK; + } + } + /* Advance row index for next row */ + row_index = (row_index + 1) & ODITHER_MASK; + cquantize->row_index = row_index; + } +} + +METHODDEF(void) +quantize3_ord_dither (j_decompress_ptr cinfo, JSAMPARRAY input_buf, + JSAMPARRAY output_buf, int num_rows) +/* Fast path for out_color_components==3, with ordered dithering */ +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + register int pixcode; + register JSAMPROW input_ptr; + register JSAMPROW output_ptr; + JSAMPROW colorindex0 = cquantize->colorindex[0]; + JSAMPROW colorindex1 = cquantize->colorindex[1]; + JSAMPROW colorindex2 = cquantize->colorindex[2]; + int * dither0; /* points to active row of dither matrix */ + int * dither1; + int * dither2; + int row_index, col_index; /* current indexes into dither matrix */ + int row; + JDIMENSION col; + JDIMENSION width = cinfo->output_width; + + for (row = 0; row < num_rows; row++) { + row_index = cquantize->row_index; + input_ptr = input_buf[row]; + output_ptr = output_buf[row]; + dither0 = cquantize->odither[0][row_index]; + dither1 = cquantize->odither[1][row_index]; + dither2 = cquantize->odither[2][row_index]; + col_index = 0; + + for (col = width; col > 0; col--) { + pixcode = GETJSAMPLE(colorindex0[GETJSAMPLE(*input_ptr++) + + dither0[col_index]]); + pixcode += GETJSAMPLE(colorindex1[GETJSAMPLE(*input_ptr++) + + dither1[col_index]]); + pixcode += GETJSAMPLE(colorindex2[GETJSAMPLE(*input_ptr++) + + dither2[col_index]]); + *output_ptr++ = (JSAMPLE) pixcode; + col_index = (col_index + 1) & ODITHER_MASK; + } + row_index = (row_index + 1) & ODITHER_MASK; + cquantize->row_index = row_index; + } +} + +METHODDEF(void) +quantize_fs_dither (j_decompress_ptr cinfo, JSAMPARRAY input_buf, + JSAMPARRAY output_buf, int num_rows) +/* General case, with Floyd-Steinberg dithering */ +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + register LOCFSERROR cur; /* current error or pixel value */ + LOCFSERROR belowerr; /* error for pixel below cur */ + LOCFSERROR bpreverr; /* error for below/prev col */ + LOCFSERROR bnexterr; /* error for below/next col */ + LOCFSERROR delta; + register FSERRPTR errorptr; /* => fserrors[] at column before current */ + register JSAMPROW input_ptr; + register JSAMPROW output_ptr; + JSAMPROW colorindex_ci; + JSAMPROW colormap_ci; + int pixcode; + int nc = cinfo->out_color_components; + int dir; /* 1 for left-to-right, -1 for right-to-left */ + int dirnc; /* dir * nc */ + int ci; + int row; + JDIMENSION col; + JDIMENSION width = cinfo->output_width; + JSAMPLE *range_limit = cinfo->sample_range_limit; + SHIFT_TEMPS + + for (row = 0; row < num_rows; row++) { + /* Initialize output values to 0 so can process components separately */ + jzero_far((void FAR *) output_buf[row], + (size_t) (width * SIZEOF(JSAMPLE))); + for (ci = 0; ci < nc; ci++) { + input_ptr = input_buf[row] + ci; + output_ptr = output_buf[row]; + if (cquantize->on_odd_row) { + /* work right to left in this row */ + input_ptr += (width-1) * nc; /* so point to rightmost pixel */ + output_ptr += width-1; + dir = -1; + dirnc = -nc; + errorptr = cquantize->fserrors[ci] + (width+1); /* => entry after last column */ + } else { + /* work left to right in this row */ + dir = 1; + dirnc = nc; + errorptr = cquantize->fserrors[ci]; /* => entry before first column */ + } + colorindex_ci = cquantize->colorindex[ci]; + colormap_ci = cquantize->sv_colormap[ci]; + /* Preset error values: no error propagated to first pixel from left */ + cur = 0; + /* and no error propagated to row below yet */ + belowerr = bpreverr = 0; + + for (col = width; col > 0; col--) { + /* cur holds the error propagated from the previous pixel on the + * current line. Add the error propagated from the previous line + * to form the complete error correction term for this pixel, and + * round the error term (which is expressed * 16) to an integer. + * RIGHT_SHIFT rounds towards minus infinity, so adding 8 is correct + * for either sign of the error value. + * Note: errorptr points to *previous* column's array entry. + */ + cur = RIGHT_SHIFT(cur + errorptr[dir] + 8, 4); + /* Form pixel value + error, and range-limit to 0..MAXJSAMPLE. + * The maximum error is +- MAXJSAMPLE; this sets the required size + * of the range_limit array. + */ + cur += GETJSAMPLE(*input_ptr); + cur = GETJSAMPLE(range_limit[cur]); + /* Select output value, accumulate into output code for this pixel */ + pixcode = GETJSAMPLE(colorindex_ci[cur]); + *output_ptr += (JSAMPLE) pixcode; + /* Compute actual representation error at this pixel */ + /* Note: we can do this even though we don't have the final */ + /* pixel code, because the colormap is orthogonal. */ + cur -= GETJSAMPLE(colormap_ci[pixcode]); + /* Compute error fractions to be propagated to adjacent pixels. + * Add these into the running sums, and simultaneously shift the + * next-line error sums left by 1 column. + */ + bnexterr = cur; + delta = cur * 2; + cur += delta; /* form error * 3 */ + errorptr[0] = (FSERROR) (bpreverr + cur); + cur += delta; /* form error * 5 */ + bpreverr = belowerr + cur; + belowerr = bnexterr; + cur += delta; /* form error * 7 */ + /* At this point cur contains the 7/16 error value to be propagated + * to the next pixel on the current line, and all the errors for the + * next line have been shifted over. We are therefore ready to move on. + */ + input_ptr += dirnc; /* advance input ptr to next column */ + output_ptr += dir; /* advance output ptr to next column */ + errorptr += dir; /* advance errorptr to current column */ + } + /* Post-loop cleanup: we must unload the final error value into the + * final fserrors[] entry. Note we need not unload belowerr because + * it is for the dummy column before or after the actual array. + */ + errorptr[0] = (FSERROR) bpreverr; /* unload prev err into array */ + } + cquantize->on_odd_row = (cquantize->on_odd_row ? FALSE : TRUE); + } +} + +/* + * Allocate workspace for Floyd-Steinberg errors. + */ + +LOCAL(void) +alloc_fs_workspace (j_decompress_ptr cinfo) +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + size_t arraysize; + int i; + + arraysize = (size_t) ((cinfo->output_width + 2) * SIZEOF(FSERROR)); + for (i = 0; i < cinfo->out_color_components; i++) { + cquantize->fserrors[i] = (FSERRPTR) + (*cinfo->mem->alloc_large)((j_common_ptr) cinfo, JPOOL_IMAGE, arraysize); + } +} + +/* + * Initialize for one-pass color quantization. + */ + +METHODDEF(void) +start_pass_1_quant (j_decompress_ptr cinfo, boolean is_pre_scan) +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + size_t arraysize; + int i; + + /* Install my colormap. */ + cinfo->colormap = cquantize->sv_colormap; + cinfo->actual_number_of_colors = cquantize->sv_actual; + + /* Initialize for desired dithering mode. */ + switch (cinfo->dither_mode) { + case JDITHER_NONE: + if (cinfo->out_color_components == 3) + cquantize->pub.color_quantize = color_quantize3; + else + cquantize->pub.color_quantize = color_quantize; + break; + case JDITHER_ORDERED: + if (cinfo->out_color_components == 3) + cquantize->pub.color_quantize = quantize3_ord_dither; + else + cquantize->pub.color_quantize = quantize_ord_dither; + cquantize->row_index = 0; /* initialize state for ordered dither */ + /* If user changed to ordered dither from another mode, + * we must recreate the color index table with padding. + * This will cost extra space, but probably isn't very likely. + */ + if (! cquantize->is_padded) + create_colorindex(cinfo); + /* Create ordered-dither tables if we didn't already. */ + if (cquantize->odither[0] == NULL) + create_odither_tables(cinfo); + break; + case JDITHER_FS: + cquantize->pub.color_quantize = quantize_fs_dither; + cquantize->on_odd_row = FALSE; /* initialize state for F-S dither */ + /* Allocate Floyd-Steinberg workspace if didn't already. */ + if (cquantize->fserrors[0] == NULL) + alloc_fs_workspace(cinfo); + /* Initialize the propagated errors to zero. */ + arraysize = (size_t) ((cinfo->output_width + 2) * SIZEOF(FSERROR)); + for (i = 0; i < cinfo->out_color_components; i++) + jzero_far((void FAR *) cquantize->fserrors[i], arraysize); + break; + default: + ERREXIT(cinfo, JERR_NOT_COMPILED); + break; + } +} + +/* + * Finish up at the end of the pass. + */ + +METHODDEF(void) +finish_pass_1_quant (j_decompress_ptr cinfo) +{ + /* no work in 1-pass case */ +} + +/* + * Switch to a new external colormap between output passes. + * Shouldn't get to this module! + */ + +METHODDEF(void) +new_color_map_1_quant (j_decompress_ptr cinfo) +{ + ERREXIT(cinfo, JERR_MODE_CHANGE); +} + +/* + * Module initialization routine for 1-pass color quantization. + */ + +GLOBAL(void) +jinit_1pass_quantizer (j_decompress_ptr cinfo) +{ + my_cquantize_ptr cquantize; + + cquantize = (my_cquantize_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_cquantizer)); + cinfo->cquantize = (struct jpeg_color_quantizer *) cquantize; + cquantize->pub.start_pass = start_pass_1_quant; + cquantize->pub.finish_pass = finish_pass_1_quant; + cquantize->pub.new_color_map = new_color_map_1_quant; + cquantize->fserrors[0] = NULL; /* Flag FS workspace not allocated */ + cquantize->odither[0] = NULL; /* Also flag odither arrays not allocated */ + + /* Make sure my internal arrays won't overflow */ + if (cinfo->out_color_components > MAX_Q_COMPS) + ERREXIT1(cinfo, JERR_QUANT_COMPONENTS, MAX_Q_COMPS); + /* Make sure colormap indexes can be represented by JSAMPLEs */ + if (cinfo->desired_number_of_colors > (MAXJSAMPLE+1)) + ERREXIT1(cinfo, JERR_QUANT_MANY_COLORS, MAXJSAMPLE+1); + + /* Create the colormap and color index table. */ + create_colormap(cinfo); + create_colorindex(cinfo); + + /* Allocate Floyd-Steinberg workspace now if requested. + * We do this now since it is FAR storage and may affect the memory + * manager's space calculations. If the user changes to FS dither + * mode in a later pass, we will allocate the space then, and will + * possibly overrun the max_memory_to_use setting. + */ + if (cinfo->dither_mode == JDITHER_FS) + alloc_fs_workspace(cinfo); +} + +#endif /* QUANT_1PASS_SUPPORTED */ +/********* End of inlined file: jquant1.c *********/ + +/********* Start of inlined file: jquant2.c *********/ +#define JPEG_INTERNALS + +#ifdef QUANT_2PASS_SUPPORTED + +/* + * This module implements the well-known Heckbert paradigm for color + * quantization. Most of the ideas used here can be traced back to + * Heckbert's seminal paper + * Heckbert, Paul. "Color Image Quantization for Frame Buffer Display", + * Proc. SIGGRAPH '82, Computer Graphics v.16 #3 (July 1982), pp 297-304. + * + * In the first pass over the image, we accumulate a histogram showing the + * usage count of each possible color. To keep the histogram to a reasonable + * size, we reduce the precision of the input; typical practice is to retain + * 5 or 6 bits per color, so that 8 or 4 different input values are counted + * in the same histogram cell. + * + * Next, the color-selection step begins with a box representing the whole + * color space, and repeatedly splits the "largest" remaining box until we + * have as many boxes as desired colors. Then the mean color in each + * remaining box becomes one of the possible output colors. + * + * The second pass over the image maps each input pixel to the closest output + * color (optionally after applying a Floyd-Steinberg dithering correction). + * This mapping is logically trivial, but making it go fast enough requires + * considerable care. + * + * Heckbert-style quantizers vary a good deal in their policies for choosing + * the "largest" box and deciding where to cut it. The particular policies + * used here have proved out well in experimental comparisons, but better ones + * may yet be found. + * + * In earlier versions of the IJG code, this module quantized in YCbCr color + * space, processing the raw upsampled data without a color conversion step. + * This allowed the color conversion math to be done only once per colormap + * entry, not once per pixel. However, that optimization precluded other + * useful optimizations (such as merging color conversion with upsampling) + * and it also interfered with desired capabilities such as quantizing to an + * externally-supplied colormap. We have therefore abandoned that approach. + * The present code works in the post-conversion color space, typically RGB. + * + * To improve the visual quality of the results, we actually work in scaled + * RGB space, giving G distances more weight than R, and R in turn more than + * B. To do everything in integer math, we must use integer scale factors. + * The 2/3/1 scale factors used here correspond loosely to the relative + * weights of the colors in the NTSC grayscale equation. + * If you want to use this code to quantize a non-RGB color space, you'll + * probably need to change these scale factors. + */ + +#define R_SCALE 2 /* scale R distances by this much */ +#define G_SCALE 3 /* scale G distances by this much */ +#define B_SCALE 1 /* and B by this much */ + +/* Relabel R/G/B as components 0/1/2, respecting the RGB ordering defined + * in jmorecfg.h. As the code stands, it will do the right thing for R,G,B + * and B,G,R orders. If you define some other weird order in jmorecfg.h, + * you'll get compile errors until you extend this logic. In that case + * you'll probably want to tweak the histogram sizes too. + */ + +#if RGB_RED == 0 +#define C0_SCALE R_SCALE +#endif +#if RGB_BLUE == 0 +#define C0_SCALE B_SCALE +#endif +#if RGB_GREEN == 1 +#define C1_SCALE G_SCALE +#endif +#if RGB_RED == 2 +#define C2_SCALE R_SCALE +#endif +#if RGB_BLUE == 2 +#define C2_SCALE B_SCALE +#endif + +/* + * First we have the histogram data structure and routines for creating it. + * + * The number of bits of precision can be adjusted by changing these symbols. + * We recommend keeping 6 bits for G and 5 each for R and B. + * If you have plenty of memory and cycles, 6 bits all around gives marginally + * better results; if you are short of memory, 5 bits all around will save + * some space but degrade the results. + * To maintain a fully accurate histogram, we'd need to allocate a "long" + * (preferably unsigned long) for each cell. In practice this is overkill; + * we can get by with 16 bits per cell. Few of the cell counts will overflow, + * and clamping those that do overflow to the maximum value will give close- + * enough results. This reduces the recommended histogram size from 256Kb + * to 128Kb, which is a useful savings on PC-class machines. + * (In the second pass the histogram space is re-used for pixel mapping data; + * in that capacity, each cell must be able to store zero to the number of + * desired colors. 16 bits/cell is plenty for that too.) + * Since the JPEG code is intended to run in small memory model on 80x86 + * machines, we can't just allocate the histogram in one chunk. Instead + * of a true 3-D array, we use a row of pointers to 2-D arrays. Each + * pointer corresponds to a C0 value (typically 2^5 = 32 pointers) and + * each 2-D array has 2^6*2^5 = 2048 or 2^6*2^6 = 4096 entries. Note that + * on 80x86 machines, the pointer row is in near memory but the actual + * arrays are in far memory (same arrangement as we use for image arrays). + */ + +#define MAXNUMCOLORS (MAXJSAMPLE+1) /* maximum size of colormap */ + +/* These will do the right thing for either R,G,B or B,G,R color order, + * but you may not like the results for other color orders. + */ +#define HIST_C0_BITS 5 /* bits of precision in R/B histogram */ +#define HIST_C1_BITS 6 /* bits of precision in G histogram */ +#define HIST_C2_BITS 5 /* bits of precision in B/R histogram */ + +/* Number of elements along histogram axes. */ +#define HIST_C0_ELEMS (1<cquantize; + register JSAMPROW ptr; + register histptr histp; + register hist3d histogram = cquantize->histogram; + int row; + JDIMENSION col; + JDIMENSION width = cinfo->output_width; + + for (row = 0; row < num_rows; row++) { + ptr = input_buf[row]; + for (col = width; col > 0; col--) { + /* get pixel value and index into the histogram */ + histp = & histogram[GETJSAMPLE(ptr[0]) >> C0_SHIFT] + [GETJSAMPLE(ptr[1]) >> C1_SHIFT] + [GETJSAMPLE(ptr[2]) >> C2_SHIFT]; + /* increment, check for overflow and undo increment if so. */ + if (++(*histp) <= 0) + (*histp)--; + ptr += 3; + } + } +} + +/* + * Next we have the really interesting routines: selection of a colormap + * given the completed histogram. + * These routines work with a list of "boxes", each representing a rectangular + * subset of the input color space (to histogram precision). + */ + +typedef struct { + /* The bounds of the box (inclusive); expressed as histogram indexes */ + int c0min, c0max; + int c1min, c1max; + int c2min, c2max; + /* The volume (actually 2-norm) of the box */ + INT32 volume; + /* The number of nonzero histogram cells within this box */ + long colorcount; +} box; + +typedef box * boxptr; + +LOCAL(boxptr) +find_biggest_color_pop (boxptr boxlist, int numboxes) +/* Find the splittable box with the largest color population */ +/* Returns NULL if no splittable boxes remain */ +{ + register boxptr boxp; + register int i; + register long maxc = 0; + boxptr which = NULL; + + for (i = 0, boxp = boxlist; i < numboxes; i++, boxp++) { + if (boxp->colorcount > maxc && boxp->volume > 0) { + which = boxp; + maxc = boxp->colorcount; + } + } + return which; +} + +LOCAL(boxptr) +find_biggest_volume (boxptr boxlist, int numboxes) +/* Find the splittable box with the largest (scaled) volume */ +/* Returns NULL if no splittable boxes remain */ +{ + register boxptr boxp; + register int i; + register INT32 maxv = 0; + boxptr which = NULL; + + for (i = 0, boxp = boxlist; i < numboxes; i++, boxp++) { + if (boxp->volume > maxv) { + which = boxp; + maxv = boxp->volume; + } + } + return which; +} + +LOCAL(void) +update_box (j_decompress_ptr cinfo, boxptr boxp) +/* Shrink the min/max bounds of a box to enclose only nonzero elements, */ +/* and recompute its volume and population */ +{ + my_cquantize_ptr2 cquantize = (my_cquantize_ptr2) cinfo->cquantize; + hist3d histogram = cquantize->histogram; + histptr histp; + int c0,c1,c2; + int c0min,c0max,c1min,c1max,c2min,c2max; + INT32 dist0,dist1,dist2; + long ccount; + + c0min = boxp->c0min; c0max = boxp->c0max; + c1min = boxp->c1min; c1max = boxp->c1max; + c2min = boxp->c2min; c2max = boxp->c2max; + + if (c0max > c0min) + for (c0 = c0min; c0 <= c0max; c0++) + for (c1 = c1min; c1 <= c1max; c1++) { + histp = & histogram[c0][c1][c2min]; + for (c2 = c2min; c2 <= c2max; c2++) + if (*histp++ != 0) { + boxp->c0min = c0min = c0; + goto have_c0min; + } + } + have_c0min: + if (c0max > c0min) + for (c0 = c0max; c0 >= c0min; c0--) + for (c1 = c1min; c1 <= c1max; c1++) { + histp = & histogram[c0][c1][c2min]; + for (c2 = c2min; c2 <= c2max; c2++) + if (*histp++ != 0) { + boxp->c0max = c0max = c0; + goto have_c0max; + } + } + have_c0max: + if (c1max > c1min) + for (c1 = c1min; c1 <= c1max; c1++) + for (c0 = c0min; c0 <= c0max; c0++) { + histp = & histogram[c0][c1][c2min]; + for (c2 = c2min; c2 <= c2max; c2++) + if (*histp++ != 0) { + boxp->c1min = c1min = c1; + goto have_c1min; + } + } + have_c1min: + if (c1max > c1min) + for (c1 = c1max; c1 >= c1min; c1--) + for (c0 = c0min; c0 <= c0max; c0++) { + histp = & histogram[c0][c1][c2min]; + for (c2 = c2min; c2 <= c2max; c2++) + if (*histp++ != 0) { + boxp->c1max = c1max = c1; + goto have_c1max; + } + } + have_c1max: + if (c2max > c2min) + for (c2 = c2min; c2 <= c2max; c2++) + for (c0 = c0min; c0 <= c0max; c0++) { + histp = & histogram[c0][c1min][c2]; + for (c1 = c1min; c1 <= c1max; c1++, histp += HIST_C2_ELEMS) + if (*histp != 0) { + boxp->c2min = c2min = c2; + goto have_c2min; + } + } + have_c2min: + if (c2max > c2min) + for (c2 = c2max; c2 >= c2min; c2--) + for (c0 = c0min; c0 <= c0max; c0++) { + histp = & histogram[c0][c1min][c2]; + for (c1 = c1min; c1 <= c1max; c1++, histp += HIST_C2_ELEMS) + if (*histp != 0) { + boxp->c2max = c2max = c2; + goto have_c2max; + } + } + have_c2max: + + /* Update box volume. + * We use 2-norm rather than real volume here; this biases the method + * against making long narrow boxes, and it has the side benefit that + * a box is splittable iff norm > 0. + * Since the differences are expressed in histogram-cell units, + * we have to shift back to JSAMPLE units to get consistent distances; + * after which, we scale according to the selected distance scale factors. + */ + dist0 = ((c0max - c0min) << C0_SHIFT) * C0_SCALE; + dist1 = ((c1max - c1min) << C1_SHIFT) * C1_SCALE; + dist2 = ((c2max - c2min) << C2_SHIFT) * C2_SCALE; + boxp->volume = dist0*dist0 + dist1*dist1 + dist2*dist2; + + /* Now scan remaining volume of box and compute population */ + ccount = 0; + for (c0 = c0min; c0 <= c0max; c0++) + for (c1 = c1min; c1 <= c1max; c1++) { + histp = & histogram[c0][c1][c2min]; + for (c2 = c2min; c2 <= c2max; c2++, histp++) + if (*histp != 0) { + ccount++; + } + } + boxp->colorcount = ccount; +} + +LOCAL(int) +median_cut (j_decompress_ptr cinfo, boxptr boxlist, int numboxes, + int desired_colors) +/* Repeatedly select and split the largest box until we have enough boxes */ +{ + int n,lb; + int c0,c1,c2,cmax; + register boxptr b1,b2; + + while (numboxes < desired_colors) { + /* Select box to split. + * Current algorithm: by population for first half, then by volume. + */ + if (numboxes*2 <= desired_colors) { + b1 = find_biggest_color_pop(boxlist, numboxes); + } else { + b1 = find_biggest_volume(boxlist, numboxes); + } + if (b1 == NULL) /* no splittable boxes left! */ + break; + b2 = &boxlist[numboxes]; /* where new box will go */ + /* Copy the color bounds to the new box. */ + b2->c0max = b1->c0max; b2->c1max = b1->c1max; b2->c2max = b1->c2max; + b2->c0min = b1->c0min; b2->c1min = b1->c1min; b2->c2min = b1->c2min; + /* Choose which axis to split the box on. + * Current algorithm: longest scaled axis. + * See notes in update_box about scaling distances. + */ + c0 = ((b1->c0max - b1->c0min) << C0_SHIFT) * C0_SCALE; + c1 = ((b1->c1max - b1->c1min) << C1_SHIFT) * C1_SCALE; + c2 = ((b1->c2max - b1->c2min) << C2_SHIFT) * C2_SCALE; + /* We want to break any ties in favor of green, then red, blue last. + * This code does the right thing for R,G,B or B,G,R color orders only. + */ +#if RGB_RED == 0 + cmax = c1; n = 1; + if (c0 > cmax) { cmax = c0; n = 0; } + if (c2 > cmax) { n = 2; } +#else + cmax = c1; n = 1; + if (c2 > cmax) { cmax = c2; n = 2; } + if (c0 > cmax) { n = 0; } +#endif + /* Choose split point along selected axis, and update box bounds. + * Current algorithm: split at halfway point. + * (Since the box has been shrunk to minimum volume, + * any split will produce two nonempty subboxes.) + * Note that lb value is max for lower box, so must be < old max. + */ + switch (n) { + case 0: + lb = (b1->c0max + b1->c0min) / 2; + b1->c0max = lb; + b2->c0min = lb+1; + break; + case 1: + lb = (b1->c1max + b1->c1min) / 2; + b1->c1max = lb; + b2->c1min = lb+1; + break; + case 2: + lb = (b1->c2max + b1->c2min) / 2; + b1->c2max = lb; + b2->c2min = lb+1; + break; + } + /* Update stats for boxes */ + update_box(cinfo, b1); + update_box(cinfo, b2); + numboxes++; + } + return numboxes; +} + +LOCAL(void) +compute_color (j_decompress_ptr cinfo, boxptr boxp, int icolor) +/* Compute representative color for a box, put it in colormap[icolor] */ +{ + /* Current algorithm: mean weighted by pixels (not colors) */ + /* Note it is important to get the rounding correct! */ + my_cquantize_ptr2 cquantize = (my_cquantize_ptr2) cinfo->cquantize; + hist3d histogram = cquantize->histogram; + histptr histp; + int c0,c1,c2; + int c0min,c0max,c1min,c1max,c2min,c2max; + long count; + long total = 0; + long c0total = 0; + long c1total = 0; + long c2total = 0; + + c0min = boxp->c0min; c0max = boxp->c0max; + c1min = boxp->c1min; c1max = boxp->c1max; + c2min = boxp->c2min; c2max = boxp->c2max; + + for (c0 = c0min; c0 <= c0max; c0++) + for (c1 = c1min; c1 <= c1max; c1++) { + histp = & histogram[c0][c1][c2min]; + for (c2 = c2min; c2 <= c2max; c2++) { + if ((count = *histp++) != 0) { + total += count; + c0total += ((c0 << C0_SHIFT) + ((1<>1)) * count; + c1total += ((c1 << C1_SHIFT) + ((1<>1)) * count; + c2total += ((c2 << C2_SHIFT) + ((1<>1)) * count; + } + } + } + + cinfo->colormap[0][icolor] = (JSAMPLE) ((c0total + (total>>1)) / total); + cinfo->colormap[1][icolor] = (JSAMPLE) ((c1total + (total>>1)) / total); + cinfo->colormap[2][icolor] = (JSAMPLE) ((c2total + (total>>1)) / total); +} + +LOCAL(void) +select_colors (j_decompress_ptr cinfo, int desired_colors) +/* Master routine for color selection */ +{ + boxptr boxlist; + int numboxes; + int i; + + /* Allocate workspace for box list */ + boxlist = (boxptr) (*cinfo->mem->alloc_small) + ((j_common_ptr) cinfo, JPOOL_IMAGE, desired_colors * SIZEOF(box)); + /* Initialize one box containing whole space */ + numboxes = 1; + boxlist[0].c0min = 0; + boxlist[0].c0max = MAXJSAMPLE >> C0_SHIFT; + boxlist[0].c1min = 0; + boxlist[0].c1max = MAXJSAMPLE >> C1_SHIFT; + boxlist[0].c2min = 0; + boxlist[0].c2max = MAXJSAMPLE >> C2_SHIFT; + /* Shrink it to actually-used volume and set its statistics */ + update_box(cinfo, & boxlist[0]); + /* Perform median-cut to produce final box list */ + numboxes = median_cut(cinfo, boxlist, numboxes, desired_colors); + /* Compute the representative color for each box, fill colormap */ + for (i = 0; i < numboxes; i++) + compute_color(cinfo, & boxlist[i], i); + cinfo->actual_number_of_colors = numboxes; + TRACEMS1(cinfo, 1, JTRC_QUANT_SELECTED, numboxes); +} + +/* + * These routines are concerned with the time-critical task of mapping input + * colors to the nearest color in the selected colormap. + * + * We re-use the histogram space as an "inverse color map", essentially a + * cache for the results of nearest-color searches. All colors within a + * histogram cell will be mapped to the same colormap entry, namely the one + * closest to the cell's center. This may not be quite the closest entry to + * the actual input color, but it's almost as good. A zero in the cache + * indicates we haven't found the nearest color for that cell yet; the array + * is cleared to zeroes before starting the mapping pass. When we find the + * nearest color for a cell, its colormap index plus one is recorded in the + * cache for future use. The pass2 scanning routines call fill_inverse_cmap + * when they need to use an unfilled entry in the cache. + * + * Our method of efficiently finding nearest colors is based on the "locally + * sorted search" idea described by Heckbert and on the incremental distance + * calculation described by Spencer W. Thomas in chapter III.1 of Graphics + * Gems II (James Arvo, ed. Academic Press, 1991). Thomas points out that + * the distances from a given colormap entry to each cell of the histogram can + * be computed quickly using an incremental method: the differences between + * distances to adjacent cells themselves differ by a constant. This allows a + * fairly fast implementation of the "brute force" approach of computing the + * distance from every colormap entry to every histogram cell. Unfortunately, + * it needs a work array to hold the best-distance-so-far for each histogram + * cell (because the inner loop has to be over cells, not colormap entries). + * The work array elements have to be INT32s, so the work array would need + * 256Kb at our recommended precision. This is not feasible in DOS machines. + * + * To get around these problems, we apply Thomas' method to compute the + * nearest colors for only the cells within a small subbox of the histogram. + * The work array need be only as big as the subbox, so the memory usage + * problem is solved. Furthermore, we need not fill subboxes that are never + * referenced in pass2; many images use only part of the color gamut, so a + * fair amount of work is saved. An additional advantage of this + * approach is that we can apply Heckbert's locality criterion to quickly + * eliminate colormap entries that are far away from the subbox; typically + * three-fourths of the colormap entries are rejected by Heckbert's criterion, + * and we need not compute their distances to individual cells in the subbox. + * The speed of this approach is heavily influenced by the subbox size: too + * small means too much overhead, too big loses because Heckbert's criterion + * can't eliminate as many colormap entries. Empirically the best subbox + * size seems to be about 1/512th of the histogram (1/8th in each direction). + * + * Thomas' article also describes a refined method which is asymptotically + * faster than the brute-force method, but it is also far more complex and + * cannot efficiently be applied to small subboxes. It is therefore not + * useful for programs intended to be portable to DOS machines. On machines + * with plenty of memory, filling the whole histogram in one shot with Thomas' + * refined method might be faster than the present code --- but then again, + * it might not be any faster, and it's certainly more complicated. + */ + +/* log2(histogram cells in update box) for each axis; this can be adjusted */ +#define BOX_C0_LOG (HIST_C0_BITS-3) +#define BOX_C1_LOG (HIST_C1_BITS-3) +#define BOX_C2_LOG (HIST_C2_BITS-3) + +#define BOX_C0_ELEMS (1<actual_number_of_colors; + int maxc0, maxc1, maxc2; + int centerc0, centerc1, centerc2; + int i, x, ncolors; + INT32 minmaxdist, min_dist, max_dist, tdist; + INT32 mindist[MAXNUMCOLORS]; /* min distance to colormap entry i */ + + /* Compute true coordinates of update box's upper corner and center. + * Actually we compute the coordinates of the center of the upper-corner + * histogram cell, which are the upper bounds of the volume we care about. + * Note that since ">>" rounds down, the "center" values may be closer to + * min than to max; hence comparisons to them must be "<=", not "<". + */ + maxc0 = minc0 + ((1 << BOX_C0_SHIFT) - (1 << C0_SHIFT)); + centerc0 = (minc0 + maxc0) >> 1; + maxc1 = minc1 + ((1 << BOX_C1_SHIFT) - (1 << C1_SHIFT)); + centerc1 = (minc1 + maxc1) >> 1; + maxc2 = minc2 + ((1 << BOX_C2_SHIFT) - (1 << C2_SHIFT)); + centerc2 = (minc2 + maxc2) >> 1; + + /* For each color in colormap, find: + * 1. its minimum squared-distance to any point in the update box + * (zero if color is within update box); + * 2. its maximum squared-distance to any point in the update box. + * Both of these can be found by considering only the corners of the box. + * We save the minimum distance for each color in mindist[]; + * only the smallest maximum distance is of interest. + */ + minmaxdist = 0x7FFFFFFFL; + + for (i = 0; i < numcolors; i++) { + /* We compute the squared-c0-distance term, then add in the other two. */ + x = GETJSAMPLE(cinfo->colormap[0][i]); + if (x < minc0) { + tdist = (x - minc0) * C0_SCALE; + min_dist = tdist*tdist; + tdist = (x - maxc0) * C0_SCALE; + max_dist = tdist*tdist; + } else if (x > maxc0) { + tdist = (x - maxc0) * C0_SCALE; + min_dist = tdist*tdist; + tdist = (x - minc0) * C0_SCALE; + max_dist = tdist*tdist; + } else { + /* within cell range so no contribution to min_dist */ + min_dist = 0; + if (x <= centerc0) { + tdist = (x - maxc0) * C0_SCALE; + max_dist = tdist*tdist; + } else { + tdist = (x - minc0) * C0_SCALE; + max_dist = tdist*tdist; + } + } + + x = GETJSAMPLE(cinfo->colormap[1][i]); + if (x < minc1) { + tdist = (x - minc1) * C1_SCALE; + min_dist += tdist*tdist; + tdist = (x - maxc1) * C1_SCALE; + max_dist += tdist*tdist; + } else if (x > maxc1) { + tdist = (x - maxc1) * C1_SCALE; + min_dist += tdist*tdist; + tdist = (x - minc1) * C1_SCALE; + max_dist += tdist*tdist; + } else { + /* within cell range so no contribution to min_dist */ + if (x <= centerc1) { + tdist = (x - maxc1) * C1_SCALE; + max_dist += tdist*tdist; + } else { + tdist = (x - minc1) * C1_SCALE; + max_dist += tdist*tdist; + } + } + + x = GETJSAMPLE(cinfo->colormap[2][i]); + if (x < minc2) { + tdist = (x - minc2) * C2_SCALE; + min_dist += tdist*tdist; + tdist = (x - maxc2) * C2_SCALE; + max_dist += tdist*tdist; + } else if (x > maxc2) { + tdist = (x - maxc2) * C2_SCALE; + min_dist += tdist*tdist; + tdist = (x - minc2) * C2_SCALE; + max_dist += tdist*tdist; + } else { + /* within cell range so no contribution to min_dist */ + if (x <= centerc2) { + tdist = (x - maxc2) * C2_SCALE; + max_dist += tdist*tdist; + } else { + tdist = (x - minc2) * C2_SCALE; + max_dist += tdist*tdist; + } + } + + mindist[i] = min_dist; /* save away the results */ + if (max_dist < minmaxdist) + minmaxdist = max_dist; + } + + /* Now we know that no cell in the update box is more than minmaxdist + * away from some colormap entry. Therefore, only colors that are + * within minmaxdist of some part of the box need be considered. + */ + ncolors = 0; + for (i = 0; i < numcolors; i++) { + if (mindist[i] <= minmaxdist) + colorlist[ncolors++] = (JSAMPLE) i; + } + return ncolors; +} + +LOCAL(void) +find_best_colors (j_decompress_ptr cinfo, int minc0, int minc1, int minc2, + int numcolors, JSAMPLE colorlist[], JSAMPLE bestcolor[]) +/* Find the closest colormap entry for each cell in the update box, + * given the list of candidate colors prepared by find_nearby_colors. + * Return the indexes of the closest entries in the bestcolor[] array. + * This routine uses Thomas' incremental distance calculation method to + * find the distance from a colormap entry to successive cells in the box. + */ +{ + int ic0, ic1, ic2; + int i, icolor; + register INT32 * bptr; /* pointer into bestdist[] array */ + JSAMPLE * cptr; /* pointer into bestcolor[] array */ + INT32 dist0, dist1; /* initial distance values */ + register INT32 dist2; /* current distance in inner loop */ + INT32 xx0, xx1; /* distance increments */ + register INT32 xx2; + INT32 inc0, inc1, inc2; /* initial values for increments */ + /* This array holds the distance to the nearest-so-far color for each cell */ + INT32 bestdist[BOX_C0_ELEMS * BOX_C1_ELEMS * BOX_C2_ELEMS]; + + /* Initialize best-distance for each cell of the update box */ + bptr = bestdist; + for (i = BOX_C0_ELEMS*BOX_C1_ELEMS*BOX_C2_ELEMS-1; i >= 0; i--) + *bptr++ = 0x7FFFFFFFL; + + /* For each color selected by find_nearby_colors, + * compute its distance to the center of each cell in the box. + * If that's less than best-so-far, update best distance and color number. + */ + + /* Nominal steps between cell centers ("x" in Thomas article) */ +#define STEP_C0 ((1 << C0_SHIFT) * C0_SCALE) +#define STEP_C1 ((1 << C1_SHIFT) * C1_SCALE) +#define STEP_C2 ((1 << C2_SHIFT) * C2_SCALE) + + for (i = 0; i < numcolors; i++) { + icolor = GETJSAMPLE(colorlist[i]); + /* Compute (square of) distance from minc0/c1/c2 to this color */ + inc0 = (minc0 - GETJSAMPLE(cinfo->colormap[0][icolor])) * C0_SCALE; + dist0 = inc0*inc0; + inc1 = (minc1 - GETJSAMPLE(cinfo->colormap[1][icolor])) * C1_SCALE; + dist0 += inc1*inc1; + inc2 = (minc2 - GETJSAMPLE(cinfo->colormap[2][icolor])) * C2_SCALE; + dist0 += inc2*inc2; + /* Form the initial difference increments */ + inc0 = inc0 * (2 * STEP_C0) + STEP_C0 * STEP_C0; + inc1 = inc1 * (2 * STEP_C1) + STEP_C1 * STEP_C1; + inc2 = inc2 * (2 * STEP_C2) + STEP_C2 * STEP_C2; + /* Now loop over all cells in box, updating distance per Thomas method */ + bptr = bestdist; + cptr = bestcolor; + xx0 = inc0; + for (ic0 = BOX_C0_ELEMS-1; ic0 >= 0; ic0--) { + dist1 = dist0; + xx1 = inc1; + for (ic1 = BOX_C1_ELEMS-1; ic1 >= 0; ic1--) { + dist2 = dist1; + xx2 = inc2; + for (ic2 = BOX_C2_ELEMS-1; ic2 >= 0; ic2--) { + if (dist2 < *bptr) { + *bptr = dist2; + *cptr = (JSAMPLE) icolor; + } + dist2 += xx2; + xx2 += 2 * STEP_C2 * STEP_C2; + bptr++; + cptr++; + } + dist1 += xx1; + xx1 += 2 * STEP_C1 * STEP_C1; + } + dist0 += xx0; + xx0 += 2 * STEP_C0 * STEP_C0; + } + } +} + +LOCAL(void) +fill_inverse_cmap (j_decompress_ptr cinfo, int c0, int c1, int c2) +/* Fill the inverse-colormap entries in the update box that contains */ +/* histogram cell c0/c1/c2. (Only that one cell MUST be filled, but */ +/* we can fill as many others as we wish.) */ +{ + my_cquantize_ptr2 cquantize = (my_cquantize_ptr2) cinfo->cquantize; + hist3d histogram = cquantize->histogram; + int minc0, minc1, minc2; /* lower left corner of update box */ + int ic0, ic1, ic2; + register JSAMPLE * cptr; /* pointer into bestcolor[] array */ + register histptr cachep; /* pointer into main cache array */ + /* This array lists the candidate colormap indexes. */ + JSAMPLE colorlist[MAXNUMCOLORS]; + int numcolors; /* number of candidate colors */ + /* This array holds the actually closest colormap index for each cell. */ + JSAMPLE bestcolor[BOX_C0_ELEMS * BOX_C1_ELEMS * BOX_C2_ELEMS]; + + /* Convert cell coordinates to update box ID */ + c0 >>= BOX_C0_LOG; + c1 >>= BOX_C1_LOG; + c2 >>= BOX_C2_LOG; + + /* Compute true coordinates of update box's origin corner. + * Actually we compute the coordinates of the center of the corner + * histogram cell, which are the lower bounds of the volume we care about. + */ + minc0 = (c0 << BOX_C0_SHIFT) + ((1 << C0_SHIFT) >> 1); + minc1 = (c1 << BOX_C1_SHIFT) + ((1 << C1_SHIFT) >> 1); + minc2 = (c2 << BOX_C2_SHIFT) + ((1 << C2_SHIFT) >> 1); + + /* Determine which colormap entries are close enough to be candidates + * for the nearest entry to some cell in the update box. + */ + numcolors = find_nearby_colors(cinfo, minc0, minc1, minc2, colorlist); + + /* Determine the actually nearest colors. */ + find_best_colors(cinfo, minc0, minc1, minc2, numcolors, colorlist, + bestcolor); + + /* Save the best color numbers (plus 1) in the main cache array */ + c0 <<= BOX_C0_LOG; /* convert ID back to base cell indexes */ + c1 <<= BOX_C1_LOG; + c2 <<= BOX_C2_LOG; + cptr = bestcolor; + for (ic0 = 0; ic0 < BOX_C0_ELEMS; ic0++) { + for (ic1 = 0; ic1 < BOX_C1_ELEMS; ic1++) { + cachep = & histogram[c0+ic0][c1+ic1][c2]; + for (ic2 = 0; ic2 < BOX_C2_ELEMS; ic2++) { + *cachep++ = (histcell) (GETJSAMPLE(*cptr++) + 1); + } + } + } +} + +/* + * Map some rows of pixels to the output colormapped representation. + */ + +METHODDEF(void) +pass2_no_dither (j_decompress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPARRAY output_buf, int num_rows) +/* This version performs no dithering */ +{ + my_cquantize_ptr2 cquantize = (my_cquantize_ptr2) cinfo->cquantize; + hist3d histogram = cquantize->histogram; + register JSAMPROW inptr, outptr; + register histptr cachep; + register int c0, c1, c2; + int row; + JDIMENSION col; + JDIMENSION width = cinfo->output_width; + + for (row = 0; row < num_rows; row++) { + inptr = input_buf[row]; + outptr = output_buf[row]; + for (col = width; col > 0; col--) { + /* get pixel value and index into the cache */ + c0 = GETJSAMPLE(*inptr++) >> C0_SHIFT; + c1 = GETJSAMPLE(*inptr++) >> C1_SHIFT; + c2 = GETJSAMPLE(*inptr++) >> C2_SHIFT; + cachep = & histogram[c0][c1][c2]; + /* If we have not seen this color before, find nearest colormap entry */ + /* and update the cache */ + if (*cachep == 0) + fill_inverse_cmap(cinfo, c0,c1,c2); + /* Now emit the colormap index for this cell */ + *outptr++ = (JSAMPLE) (*cachep - 1); + } + } +} + +METHODDEF(void) +pass2_fs_dither (j_decompress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPARRAY output_buf, int num_rows) +/* This version performs Floyd-Steinberg dithering */ +{ + my_cquantize_ptr2 cquantize = (my_cquantize_ptr2) cinfo->cquantize; + hist3d histogram = cquantize->histogram; + register LOCFSERROR cur0, cur1, cur2; /* current error or pixel value */ + LOCFSERROR belowerr0, belowerr1, belowerr2; /* error for pixel below cur */ + LOCFSERROR bpreverr0, bpreverr1, bpreverr2; /* error for below/prev col */ + register FSERRPTR errorptr; /* => fserrors[] at column before current */ + JSAMPROW inptr; /* => current input pixel */ + JSAMPROW outptr; /* => current output pixel */ + histptr cachep; + int dir; /* +1 or -1 depending on direction */ + int dir3; /* 3*dir, for advancing inptr & errorptr */ + int row; + JDIMENSION col; + JDIMENSION width = cinfo->output_width; + JSAMPLE *range_limit = cinfo->sample_range_limit; + int *error_limit = cquantize->error_limiter; + JSAMPROW colormap0 = cinfo->colormap[0]; + JSAMPROW colormap1 = cinfo->colormap[1]; + JSAMPROW colormap2 = cinfo->colormap[2]; + SHIFT_TEMPS + + for (row = 0; row < num_rows; row++) { + inptr = input_buf[row]; + outptr = output_buf[row]; + if (cquantize->on_odd_row) { + /* work right to left in this row */ + inptr += (width-1) * 3; /* so point to rightmost pixel */ + outptr += width-1; + dir = -1; + dir3 = -3; + errorptr = cquantize->fserrors + (width+1)*3; /* => entry after last column */ + cquantize->on_odd_row = FALSE; /* flip for next time */ + } else { + /* work left to right in this row */ + dir = 1; + dir3 = 3; + errorptr = cquantize->fserrors; /* => entry before first real column */ + cquantize->on_odd_row = TRUE; /* flip for next time */ + } + /* Preset error values: no error propagated to first pixel from left */ + cur0 = cur1 = cur2 = 0; + /* and no error propagated to row below yet */ + belowerr0 = belowerr1 = belowerr2 = 0; + bpreverr0 = bpreverr1 = bpreverr2 = 0; + + for (col = width; col > 0; col--) { + /* curN holds the error propagated from the previous pixel on the + * current line. Add the error propagated from the previous line + * to form the complete error correction term for this pixel, and + * round the error term (which is expressed * 16) to an integer. + * RIGHT_SHIFT rounds towards minus infinity, so adding 8 is correct + * for either sign of the error value. + * Note: errorptr points to *previous* column's array entry. + */ + cur0 = RIGHT_SHIFT(cur0 + errorptr[dir3+0] + 8, 4); + cur1 = RIGHT_SHIFT(cur1 + errorptr[dir3+1] + 8, 4); + cur2 = RIGHT_SHIFT(cur2 + errorptr[dir3+2] + 8, 4); + /* Limit the error using transfer function set by init_error_limit. + * See comments with init_error_limit for rationale. + */ + cur0 = error_limit[cur0]; + cur1 = error_limit[cur1]; + cur2 = error_limit[cur2]; + /* Form pixel value + error, and range-limit to 0..MAXJSAMPLE. + * The maximum error is +- MAXJSAMPLE (or less with error limiting); + * this sets the required size of the range_limit array. + */ + cur0 += GETJSAMPLE(inptr[0]); + cur1 += GETJSAMPLE(inptr[1]); + cur2 += GETJSAMPLE(inptr[2]); + cur0 = GETJSAMPLE(range_limit[cur0]); + cur1 = GETJSAMPLE(range_limit[cur1]); + cur2 = GETJSAMPLE(range_limit[cur2]); + /* Index into the cache with adjusted pixel value */ + cachep = & histogram[cur0>>C0_SHIFT][cur1>>C1_SHIFT][cur2>>C2_SHIFT]; + /* If we have not seen this color before, find nearest colormap */ + /* entry and update the cache */ + if (*cachep == 0) + fill_inverse_cmap(cinfo, cur0>>C0_SHIFT,cur1>>C1_SHIFT,cur2>>C2_SHIFT); + /* Now emit the colormap index for this cell */ + { register int pixcode = *cachep - 1; + *outptr = (JSAMPLE) pixcode; + /* Compute representation error for this pixel */ + cur0 -= GETJSAMPLE(colormap0[pixcode]); + cur1 -= GETJSAMPLE(colormap1[pixcode]); + cur2 -= GETJSAMPLE(colormap2[pixcode]); + } + /* Compute error fractions to be propagated to adjacent pixels. + * Add these into the running sums, and simultaneously shift the + * next-line error sums left by 1 column. + */ + { register LOCFSERROR bnexterr, delta; + + bnexterr = cur0; /* Process component 0 */ + delta = cur0 * 2; + cur0 += delta; /* form error * 3 */ + errorptr[0] = (FSERROR) (bpreverr0 + cur0); + cur0 += delta; /* form error * 5 */ + bpreverr0 = belowerr0 + cur0; + belowerr0 = bnexterr; + cur0 += delta; /* form error * 7 */ + bnexterr = cur1; /* Process component 1 */ + delta = cur1 * 2; + cur1 += delta; /* form error * 3 */ + errorptr[1] = (FSERROR) (bpreverr1 + cur1); + cur1 += delta; /* form error * 5 */ + bpreverr1 = belowerr1 + cur1; + belowerr1 = bnexterr; + cur1 += delta; /* form error * 7 */ + bnexterr = cur2; /* Process component 2 */ + delta = cur2 * 2; + cur2 += delta; /* form error * 3 */ + errorptr[2] = (FSERROR) (bpreverr2 + cur2); + cur2 += delta; /* form error * 5 */ + bpreverr2 = belowerr2 + cur2; + belowerr2 = bnexterr; + cur2 += delta; /* form error * 7 */ + } + /* At this point curN contains the 7/16 error value to be propagated + * to the next pixel on the current line, and all the errors for the + * next line have been shifted over. We are therefore ready to move on. + */ + inptr += dir3; /* Advance pixel pointers to next column */ + outptr += dir; + errorptr += dir3; /* advance errorptr to current column */ + } + /* Post-loop cleanup: we must unload the final error values into the + * final fserrors[] entry. Note we need not unload belowerrN because + * it is for the dummy column before or after the actual array. + */ + errorptr[0] = (FSERROR) bpreverr0; /* unload prev errs into array */ + errorptr[1] = (FSERROR) bpreverr1; + errorptr[2] = (FSERROR) bpreverr2; + } +} + +/* + * Initialize the error-limiting transfer function (lookup table). + * The raw F-S error computation can potentially compute error values of up to + * +- MAXJSAMPLE. But we want the maximum correction applied to a pixel to be + * much less, otherwise obviously wrong pixels will be created. (Typical + * effects include weird fringes at color-area boundaries, isolated bright + * pixels in a dark area, etc.) The standard advice for avoiding this problem + * is to ensure that the "corners" of the color cube are allocated as output + * colors; then repeated errors in the same direction cannot cause cascading + * error buildup. However, that only prevents the error from getting + * completely out of hand; Aaron Giles reports that error limiting improves + * the results even with corner colors allocated. + * A simple clamping of the error values to about +- MAXJSAMPLE/8 works pretty + * well, but the smoother transfer function used below is even better. Thanks + * to Aaron Giles for this idea. + */ + +LOCAL(void) +init_error_limit (j_decompress_ptr cinfo) +/* Allocate and fill in the error_limiter table */ +{ + my_cquantize_ptr2 cquantize = (my_cquantize_ptr2) cinfo->cquantize; + int * table; + int in, out; + + table = (int *) (*cinfo->mem->alloc_small) + ((j_common_ptr) cinfo, JPOOL_IMAGE, (MAXJSAMPLE*2+1) * SIZEOF(int)); + table += MAXJSAMPLE; /* so can index -MAXJSAMPLE .. +MAXJSAMPLE */ + cquantize->error_limiter = table; + +#define STEPSIZE ((MAXJSAMPLE+1)/16) + /* Map errors 1:1 up to +- MAXJSAMPLE/16 */ + out = 0; + for (in = 0; in < STEPSIZE; in++, out++) { + table[in] = out; table[-in] = -out; + } + /* Map errors 1:2 up to +- 3*MAXJSAMPLE/16 */ + for (; in < STEPSIZE*3; in++, out += (in&1) ? 0 : 1) { + table[in] = out; table[-in] = -out; + } + /* Clamp the rest to final out value (which is (MAXJSAMPLE+1)/8) */ + for (; in <= MAXJSAMPLE; in++) { + table[in] = out; table[-in] = -out; + } +#undef STEPSIZE +} + +/* + * Finish up at the end of each pass. + */ + +METHODDEF(void) +finish_pass1 (j_decompress_ptr cinfo) +{ + my_cquantize_ptr2 cquantize = (my_cquantize_ptr2) cinfo->cquantize; + + /* Select the representative colors and fill in cinfo->colormap */ + cinfo->colormap = cquantize->sv_colormap; + select_colors(cinfo, cquantize->desired); + /* Force next pass to zero the color index table */ + cquantize->needs_zeroed = TRUE; +} + +METHODDEF(void) +finish_pass2 (j_decompress_ptr cinfo) +{ + /* no work */ +} + +/* + * Initialize for each processing pass. + */ + +METHODDEF(void) +start_pass_2_quant (j_decompress_ptr cinfo, boolean is_pre_scan) +{ + my_cquantize_ptr2 cquantize = (my_cquantize_ptr2) cinfo->cquantize; + hist3d histogram = cquantize->histogram; + int i; + + /* Only F-S dithering or no dithering is supported. */ + /* If user asks for ordered dither, give him F-S. */ + if (cinfo->dither_mode != JDITHER_NONE) + cinfo->dither_mode = JDITHER_FS; + + if (is_pre_scan) { + /* Set up method pointers */ + cquantize->pub.color_quantize = prescan_quantize; + cquantize->pub.finish_pass = finish_pass1; + cquantize->needs_zeroed = TRUE; /* Always zero histogram */ + } else { + /* Set up method pointers */ + if (cinfo->dither_mode == JDITHER_FS) + cquantize->pub.color_quantize = pass2_fs_dither; + else + cquantize->pub.color_quantize = pass2_no_dither; + cquantize->pub.finish_pass = finish_pass2; + + /* Make sure color count is acceptable */ + i = cinfo->actual_number_of_colors; + if (i < 1) + ERREXIT1(cinfo, JERR_QUANT_FEW_COLORS, 1); + if (i > MAXNUMCOLORS) + ERREXIT1(cinfo, JERR_QUANT_MANY_COLORS, MAXNUMCOLORS); + + if (cinfo->dither_mode == JDITHER_FS) { + size_t arraysize = (size_t) ((cinfo->output_width + 2) * + (3 * SIZEOF(FSERROR))); + /* Allocate Floyd-Steinberg workspace if we didn't already. */ + if (cquantize->fserrors == NULL) + cquantize->fserrors = (FSERRPTR) (*cinfo->mem->alloc_large) + ((j_common_ptr) cinfo, JPOOL_IMAGE, arraysize); + /* Initialize the propagated errors to zero. */ + jzero_far((void FAR *) cquantize->fserrors, arraysize); + /* Make the error-limit table if we didn't already. */ + if (cquantize->error_limiter == NULL) + init_error_limit(cinfo); + cquantize->on_odd_row = FALSE; + } + + } + /* Zero the histogram or inverse color map, if necessary */ + if (cquantize->needs_zeroed) { + for (i = 0; i < HIST_C0_ELEMS; i++) { + jzero_far((void FAR *) histogram[i], + HIST_C1_ELEMS*HIST_C2_ELEMS * SIZEOF(histcell)); + } + cquantize->needs_zeroed = FALSE; + } +} + +/* + * Switch to a new external colormap between output passes. + */ + +METHODDEF(void) +new_color_map_2_quant (j_decompress_ptr cinfo) +{ + my_cquantize_ptr2 cquantize = (my_cquantize_ptr2) cinfo->cquantize; + + /* Reset the inverse color map */ + cquantize->needs_zeroed = TRUE; +} + +/* + * Module initialization routine for 2-pass color quantization. + */ + +GLOBAL(void) +jinit_2pass_quantizer (j_decompress_ptr cinfo) +{ + my_cquantize_ptr2 cquantize; + int i; + + cquantize = (my_cquantize_ptr2) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_cquantizer2)); + cinfo->cquantize = (struct jpeg_color_quantizer *) cquantize; + cquantize->pub.start_pass = start_pass_2_quant; + cquantize->pub.new_color_map = new_color_map_2_quant; + cquantize->fserrors = NULL; /* flag optional arrays not allocated */ + cquantize->error_limiter = NULL; + + /* Make sure jdmaster didn't give me a case I can't handle */ + if (cinfo->out_color_components != 3) + ERREXIT(cinfo, JERR_NOTIMPL); + + /* Allocate the histogram/inverse colormap storage */ + cquantize->histogram = (hist3d) (*cinfo->mem->alloc_small) + ((j_common_ptr) cinfo, JPOOL_IMAGE, HIST_C0_ELEMS * SIZEOF(hist2d)); + for (i = 0; i < HIST_C0_ELEMS; i++) { + cquantize->histogram[i] = (hist2d) (*cinfo->mem->alloc_large) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + HIST_C1_ELEMS*HIST_C2_ELEMS * SIZEOF(histcell)); + } + cquantize->needs_zeroed = TRUE; /* histogram is garbage now */ + + /* Allocate storage for the completed colormap, if required. + * We do this now since it is FAR storage and may affect + * the memory manager's space calculations. + */ + if (cinfo->enable_2pass_quant) { + /* Make sure color count is acceptable */ + int desired = cinfo->desired_number_of_colors; + /* Lower bound on # of colors ... somewhat arbitrary as long as > 0 */ + if (desired < 8) + ERREXIT1(cinfo, JERR_QUANT_FEW_COLORS, 8); + /* Make sure colormap indexes can be represented by JSAMPLEs */ + if (desired > MAXNUMCOLORS) + ERREXIT1(cinfo, JERR_QUANT_MANY_COLORS, MAXNUMCOLORS); + cquantize->sv_colormap = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo,JPOOL_IMAGE, (JDIMENSION) desired, (JDIMENSION) 3); + cquantize->desired = desired; + } else + cquantize->sv_colormap = NULL; + + /* Only F-S dithering or no dithering is supported. */ + /* If user asks for ordered dither, give him F-S. */ + if (cinfo->dither_mode != JDITHER_NONE) + cinfo->dither_mode = JDITHER_FS; + + /* Allocate Floyd-Steinberg workspace if necessary. + * This isn't really needed until pass 2, but again it is FAR storage. + * Although we will cope with a later change in dither_mode, + * we do not promise to honor max_memory_to_use if dither_mode changes. + */ + if (cinfo->dither_mode == JDITHER_FS) { + cquantize->fserrors = (FSERRPTR) (*cinfo->mem->alloc_large) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + (size_t) ((cinfo->output_width + 2) * (3 * SIZEOF(FSERROR)))); + /* Might as well create the error-limiting table too. */ + init_error_limit(cinfo); + } +} + +#endif /* QUANT_2PASS_SUPPORTED */ +/********* End of inlined file: jquant2.c *********/ + +/********* Start of inlined file: jutils.c *********/ +#define JPEG_INTERNALS + +/* + * jpeg_zigzag_order[i] is the zigzag-order position of the i'th element + * of a DCT block read in natural order (left to right, top to bottom). + */ + +#if 0 /* This table is not actually needed in v6a */ + +const int jpeg_zigzag_order[DCTSIZE2] = { + 0, 1, 5, 6, 14, 15, 27, 28, + 2, 4, 7, 13, 16, 26, 29, 42, + 3, 8, 12, 17, 25, 30, 41, 43, + 9, 11, 18, 24, 31, 40, 44, 53, + 10, 19, 23, 32, 39, 45, 52, 54, + 20, 22, 33, 38, 46, 51, 55, 60, + 21, 34, 37, 47, 50, 56, 59, 61, + 35, 36, 48, 49, 57, 58, 62, 63 +}; + +#endif + +/* + * jpeg_natural_order[i] is the natural-order position of the i'th element + * of zigzag order. + * + * When reading corrupted data, the Huffman decoders could attempt + * to reference an entry beyond the end of this array (if the decoded + * zero run length reaches past the end of the block). To prevent + * wild stores without adding an inner-loop test, we put some extra + * "63"s after the real entries. This will cause the extra coefficient + * to be stored in location 63 of the block, not somewhere random. + * The worst case would be a run-length of 15, which means we need 16 + * fake entries. + */ + +const int jpeg_natural_order[DCTSIZE2+16] = { + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + 63, 63, 63, 63, 63, 63, 63, 63, /* extra entries for safety in decoder */ + 63, 63, 63, 63, 63, 63, 63, 63 +}; + +/* + * Arithmetic utilities + */ + +GLOBAL(long) +jdiv_round_up (long a, long b) +/* Compute a/b rounded up to next integer, ie, ceil(a/b) */ +/* Assumes a >= 0, b > 0 */ +{ + return (a + b - 1L) / b; +} + +GLOBAL(long) +jround_up (long a, long b) +/* Compute a rounded up to next multiple of b, ie, ceil(a/b)*b */ +/* Assumes a >= 0, b > 0 */ +{ + a += b - 1L; + return a - (a % b); +} + +/* On normal machines we can apply MEMCOPY() and MEMZERO() to sample arrays + * and coefficient-block arrays. This won't work on 80x86 because the arrays + * are FAR and we're assuming a small-pointer memory model. However, some + * DOS compilers provide far-pointer versions of memcpy() and memset() even + * in the small-model libraries. These will be used if USE_FMEM is defined. + * Otherwise, the routines below do it the hard way. (The performance cost + * is not all that great, because these routines aren't very heavily used.) + */ + +#ifndef NEED_FAR_POINTERS /* normal case, same as regular macros */ +#define FMEMCOPY(dest,src,size) MEMCOPY(dest,src,size) +#define FMEMZERO(target,size) MEMZERO(target,size) +#else /* 80x86 case, define if we can */ +#ifdef USE_FMEM +#define FMEMCOPY(dest,src,size) _fmemcpy((void FAR *)(dest), (const void FAR *)(src), (size_t)(size)) +#define FMEMZERO(target,size) _fmemset((void FAR *)(target), 0, (size_t)(size)) +#endif +#endif + +GLOBAL(void) +jcopy_sample_rows (JSAMPARRAY input_array, int source_row, + JSAMPARRAY output_array, int dest_row, + int num_rows, JDIMENSION num_cols) +/* Copy some rows of samples from one place to another. + * num_rows rows are copied from input_array[source_row++] + * to output_array[dest_row++]; these areas may overlap for duplication. + * The source and destination arrays must be at least as wide as num_cols. + */ +{ + register JSAMPROW inptr, outptr; +#ifdef FMEMCOPY + register size_t count = (size_t) (num_cols * SIZEOF(JSAMPLE)); +#else + register JDIMENSION count; +#endif + register int row; + + input_array += source_row; + output_array += dest_row; + + for (row = num_rows; row > 0; row--) { + inptr = *input_array++; + outptr = *output_array++; +#ifdef FMEMCOPY + FMEMCOPY(outptr, inptr, count); +#else + for (count = num_cols; count > 0; count--) + *outptr++ = *inptr++; /* needn't bother with GETJSAMPLE() here */ +#endif + } +} + +GLOBAL(void) +jcopy_block_row (JBLOCKROW input_row, JBLOCKROW output_row, + JDIMENSION num_blocks) +/* Copy a row of coefficient blocks from one place to another. */ +{ +#ifdef FMEMCOPY + FMEMCOPY(output_row, input_row, num_blocks * (DCTSIZE2 * SIZEOF(JCOEF))); +#else + register JCOEFPTR inptr, outptr; + register long count; + + inptr = (JCOEFPTR) input_row; + outptr = (JCOEFPTR) output_row; + for (count = (long) num_blocks * DCTSIZE2; count > 0; count--) { + *outptr++ = *inptr++; + } +#endif +} + +GLOBAL(void) +jzero_far (void FAR * target, size_t bytestozero) +/* Zero out a chunk of FAR memory. */ +/* This might be sample-array data, block-array data, or alloc_large data. */ +{ +#ifdef FMEMZERO + FMEMZERO(target, bytestozero); +#else + register char FAR * ptr = (char FAR *) target; + register size_t count; + + for (count = bytestozero; count > 0; count--) { + *ptr++ = 0; + } +#endif +} +/********* End of inlined file: jutils.c *********/ + +/********* Start of inlined file: transupp.c *********/ +/* Although this file really shouldn't have access to the library internals, + * it's helpful to let it call jround_up() and jcopy_block_row(). + */ +#define JPEG_INTERNALS + +/********* Start of inlined file: transupp.h *********/ +/* If you happen not to want the image transform support, disable it here */ +#ifndef TRANSFORMS_SUPPORTED +#define TRANSFORMS_SUPPORTED 1 /* 0 disables transform code */ +#endif + +/* Short forms of external names for systems with brain-damaged linkers. */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jtransform_request_workspace jTrRequest +#define jtransform_adjust_parameters jTrAdjust +#define jtransform_execute_transformation jTrExec +#define jcopy_markers_setup jCMrkSetup +#define jcopy_markers_execute jCMrkExec +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + +/* + * Codes for supported types of image transformations. + */ + +typedef enum { + JXFORM_NONE, /* no transformation */ + JXFORM_FLIP_H, /* horizontal flip */ + JXFORM_FLIP_V, /* vertical flip */ + JXFORM_TRANSPOSE, /* transpose across UL-to-LR axis */ + JXFORM_TRANSVERSE, /* transpose across UR-to-LL axis */ + JXFORM_ROT_90, /* 90-degree clockwise rotation */ + JXFORM_ROT_180, /* 180-degree rotation */ + JXFORM_ROT_270 /* 270-degree clockwise (or 90 ccw) */ +} JXFORM_CODE; + +/* + * Although rotating and flipping data expressed as DCT coefficients is not + * hard, there is an asymmetry in the JPEG format specification for images + * whose dimensions aren't multiples of the iMCU size. The right and bottom + * image edges are padded out to the next iMCU boundary with junk data; but + * no padding is possible at the top and left edges. If we were to flip + * the whole image including the pad data, then pad garbage would become + * visible at the top and/or left, and real pixels would disappear into the + * pad margins --- perhaps permanently, since encoders & decoders may not + * bother to preserve DCT blocks that appear to be completely outside the + * nominal image area. So, we have to exclude any partial iMCUs from the + * basic transformation. + * + * Transpose is the only transformation that can handle partial iMCUs at the + * right and bottom edges completely cleanly. flip_h can flip partial iMCUs + * at the bottom, but leaves any partial iMCUs at the right edge untouched. + * Similarly flip_v leaves any partial iMCUs at the bottom edge untouched. + * The other transforms are defined as combinations of these basic transforms + * and process edge blocks in a way that preserves the equivalence. + * + * The "trim" option causes untransformable partial iMCUs to be dropped; + * this is not strictly lossless, but it usually gives the best-looking + * result for odd-size images. Note that when this option is active, + * the expected mathematical equivalences between the transforms may not hold. + * (For example, -rot 270 -trim trims only the bottom edge, but -rot 90 -trim + * followed by -rot 180 -trim trims both edges.) + * + * We also offer a "force to grayscale" option, which simply discards the + * chrominance channels of a YCbCr image. This is lossless in the sense that + * the luminance channel is preserved exactly. It's not the same kind of + * thing as the rotate/flip transformations, but it's convenient to handle it + * as part of this package, mainly because the transformation routines have to + * be aware of the option to know how many components to work on. + */ + +typedef struct { + /* Options: set by caller */ + JXFORM_CODE transform; /* image transform operator */ + boolean trim; /* if TRUE, trim partial MCUs as needed */ + boolean force_grayscale; /* if TRUE, convert color image to grayscale */ + + /* Internal workspace: caller should not touch these */ + int num_components; /* # of components in workspace */ + jvirt_barray_ptr * workspace_coef_arrays; /* workspace for transformations */ +} jpeg_transform_info; + +#if TRANSFORMS_SUPPORTED + +/* Request any required workspace */ +EXTERN(void) jtransform_request_workspace + JPP((j_decompress_ptr srcinfo, jpeg_transform_info *info)); +/* Adjust output image parameters */ +EXTERN(jvirt_barray_ptr *) jtransform_adjust_parameters + JPP((j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jpeg_transform_info *info)); +/* Execute the actual transformation, if any */ +EXTERN(void) jtransform_execute_transformation + JPP((j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jpeg_transform_info *info)); + +#endif /* TRANSFORMS_SUPPORTED */ + +/* + * Support for copying optional markers from source to destination file. + */ + +typedef enum { + JCOPYOPT_NONE, /* copy no optional markers */ + JCOPYOPT_COMMENTS, /* copy only comment (COM) markers */ + JCOPYOPT_ALL /* copy all optional markers */ +} JCOPY_OPTION; + +#define JCOPYOPT_DEFAULT JCOPYOPT_COMMENTS /* recommended default */ + +/* Setup decompression object to save desired markers in memory */ +EXTERN(void) jcopy_markers_setup + JPP((j_decompress_ptr srcinfo, JCOPY_OPTION option)); +/* Copy markers saved in the given source object to the destination object */ +EXTERN(void) jcopy_markers_execute + JPP((j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + JCOPY_OPTION option)); +/********* End of inlined file: transupp.h *********/ + + /* My own external interface */ + +#if TRANSFORMS_SUPPORTED + +/* + * Lossless image transformation routines. These routines work on DCT + * coefficient arrays and thus do not require any lossy decompression + * or recompression of the image. + * Thanks to Guido Vollbeding for the initial design and code of this feature. + * + * Horizontal flipping is done in-place, using a single top-to-bottom + * pass through the virtual source array. It will thus be much the + * fastest option for images larger than main memory. + * + * The other routines require a set of destination virtual arrays, so they + * need twice as much memory as jpegtran normally does. The destination + * arrays are always written in normal scan order (top to bottom) because + * the virtual array manager expects this. The source arrays will be scanned + * in the corresponding order, which means multiple passes through the source + * arrays for most of the transforms. That could result in much thrashing + * if the image is larger than main memory. + * + * Some notes about the operating environment of the individual transform + * routines: + * 1. Both the source and destination virtual arrays are allocated from the + * source JPEG object, and therefore should be manipulated by calling the + * source's memory manager. + * 2. The destination's component count should be used. It may be smaller + * than the source's when forcing to grayscale. + * 3. Likewise the destination's sampling factors should be used. When + * forcing to grayscale the destination's sampling factors will be all 1, + * and we may as well take that as the effective iMCU size. + * 4. When "trim" is in effect, the destination's dimensions will be the + * trimmed values but the source's will be untrimmed. + * 5. All the routines assume that the source and destination buffers are + * padded out to a full iMCU boundary. This is true, although for the + * source buffer it is an undocumented property of jdcoefct.c. + * Notes 2,3,4 boil down to this: generally we should use the destination's + * dimensions and ignore the source's. + */ + +LOCAL(void) +do_flip_h (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays) +/* Horizontal flip; done in-place, so no separate dest array is required */ +{ + JDIMENSION MCU_cols, comp_width, blk_x, blk_y; + int ci, k, offset_y; + JBLOCKARRAY buffer; + JCOEFPTR ptr1, ptr2; + JCOEF temp1, temp2; + jpeg_component_info *compptr; + + /* Horizontal mirroring of DCT blocks is accomplished by swapping + * pairs of blocks in-place. Within a DCT block, we perform horizontal + * mirroring by changing the signs of odd-numbered columns. + * Partial iMCUs at the right edge are left untouched. + */ + MCU_cols = dstinfo->image_width / (dstinfo->max_h_samp_factor * DCTSIZE); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_width = MCU_cols * compptr->h_samp_factor; + for (blk_y = 0; blk_y < compptr->height_in_blocks; + blk_y += compptr->v_samp_factor) { + buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + for (blk_x = 0; blk_x * 2 < comp_width; blk_x++) { + ptr1 = buffer[offset_y][blk_x]; + ptr2 = buffer[offset_y][comp_width - blk_x - 1]; + /* this unrolled loop doesn't need to know which row it's on... */ + for (k = 0; k < DCTSIZE2; k += 2) { + temp1 = *ptr1; /* swap even column */ + temp2 = *ptr2; + *ptr1++ = temp2; + *ptr2++ = temp1; + temp1 = *ptr1; /* swap odd column with sign change */ + temp2 = *ptr2; + *ptr1++ = -temp2; + *ptr2++ = -temp1; + } + } + } + } + } +} + +LOCAL(void) +do_flip_v (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* Vertical flip */ +{ + JDIMENSION MCU_rows, comp_height, dst_blk_x, dst_blk_y; + int ci, i, j, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JBLOCKROW src_row_ptr, dst_row_ptr; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + /* We output into a separate array because we can't touch different + * rows of the source virtual array simultaneously. Otherwise, this + * is a pretty straightforward analog of horizontal flip. + * Within a DCT block, vertical mirroring is done by changing the signs + * of odd-numbered rows. + * Partial iMCUs at the bottom edge are copied verbatim. + */ + MCU_rows = dstinfo->image_height / (dstinfo->max_v_samp_factor * DCTSIZE); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_height = MCU_rows * compptr->v_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + if (dst_blk_y < comp_height) { + /* Row is within the mirrorable area. */ + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], + comp_height - dst_blk_y - (JDIMENSION) compptr->v_samp_factor, + (JDIMENSION) compptr->v_samp_factor, FALSE); + } else { + /* Bottom-edge blocks will be copied verbatim. */ + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, FALSE); + } + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + if (dst_blk_y < comp_height) { + /* Row is within the mirrorable area. */ + dst_row_ptr = dst_buffer[offset_y]; + src_row_ptr = src_buffer[compptr->v_samp_factor - offset_y - 1]; + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; + dst_blk_x++) { + dst_ptr = dst_row_ptr[dst_blk_x]; + src_ptr = src_row_ptr[dst_blk_x]; + for (i = 0; i < DCTSIZE; i += 2) { + /* copy even row */ + for (j = 0; j < DCTSIZE; j++) + *dst_ptr++ = *src_ptr++; + /* copy odd row with sign change */ + for (j = 0; j < DCTSIZE; j++) + *dst_ptr++ = - *src_ptr++; + } + } + } else { + /* Just copy row verbatim. */ + jcopy_block_row(src_buffer[offset_y], dst_buffer[offset_y], + compptr->width_in_blocks); + } + } + } + } +} + +LOCAL(void) +do_transpose (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* Transpose source into destination */ +{ + JDIMENSION dst_blk_x, dst_blk_y; + int ci, i, j, offset_x, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + /* Transposing pixels within a block just requires transposing the + * DCT coefficients. + * Partial iMCUs at the edges require no special treatment; we simply + * process all the available DCT blocks for every component. + */ + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; + dst_blk_x += compptr->h_samp_factor) { + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_x, + (JDIMENSION) compptr->h_samp_factor, FALSE); + for (offset_x = 0; offset_x < compptr->h_samp_factor; offset_x++) { + src_ptr = src_buffer[offset_x][dst_blk_y + offset_y]; + dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; + for (i = 0; i < DCTSIZE; i++) + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + } + } + } + } + } +} + +LOCAL(void) +do_rot_90 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* 90 degree rotation is equivalent to + * 1. Transposing the image; + * 2. Horizontal mirroring. + * These two steps are merged into a single processing routine. + */ +{ + JDIMENSION MCU_cols, comp_width, dst_blk_x, dst_blk_y; + int ci, i, j, offset_x, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + /* Because of the horizontal mirror step, we can't process partial iMCUs + * at the (output) right edge properly. They just get transposed and + * not mirrored. + */ + MCU_cols = dstinfo->image_width / (dstinfo->max_h_samp_factor * DCTSIZE); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_width = MCU_cols * compptr->h_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; + dst_blk_x += compptr->h_samp_factor) { + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_x, + (JDIMENSION) compptr->h_samp_factor, FALSE); + for (offset_x = 0; offset_x < compptr->h_samp_factor; offset_x++) { + src_ptr = src_buffer[offset_x][dst_blk_y + offset_y]; + if (dst_blk_x < comp_width) { + /* Block is within the mirrorable area. */ + dst_ptr = dst_buffer[offset_y] + [comp_width - dst_blk_x - offset_x - 1]; + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + i++; + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + } + } else { + /* Edge blocks are transposed but not mirrored. */ + dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; + for (i = 0; i < DCTSIZE; i++) + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + } + } + } + } + } + } +} + +LOCAL(void) +do_rot_270 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* 270 degree rotation is equivalent to + * 1. Horizontal mirroring; + * 2. Transposing the image. + * These two steps are merged into a single processing routine. + */ +{ + JDIMENSION MCU_rows, comp_height, dst_blk_x, dst_blk_y; + int ci, i, j, offset_x, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + /* Because of the horizontal mirror step, we can't process partial iMCUs + * at the (output) bottom edge properly. They just get transposed and + * not mirrored. + */ + MCU_rows = dstinfo->image_height / (dstinfo->max_v_samp_factor * DCTSIZE); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_height = MCU_rows * compptr->v_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; + dst_blk_x += compptr->h_samp_factor) { + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_x, + (JDIMENSION) compptr->h_samp_factor, FALSE); + for (offset_x = 0; offset_x < compptr->h_samp_factor; offset_x++) { + dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; + if (dst_blk_y < comp_height) { + /* Block is within the mirrorable area. */ + src_ptr = src_buffer[offset_x] + [comp_height - dst_blk_y - offset_y - 1]; + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < DCTSIZE; j++) { + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + j++; + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + } + } + } else { + /* Edge blocks are transposed but not mirrored. */ + src_ptr = src_buffer[offset_x][dst_blk_y + offset_y]; + for (i = 0; i < DCTSIZE; i++) + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + } + } + } + } + } + } +} + +LOCAL(void) +do_rot_180 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* 180 degree rotation is equivalent to + * 1. Vertical mirroring; + * 2. Horizontal mirroring. + * These two steps are merged into a single processing routine. + */ +{ + JDIMENSION MCU_cols, MCU_rows, comp_width, comp_height, dst_blk_x, dst_blk_y; + int ci, i, j, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JBLOCKROW src_row_ptr, dst_row_ptr; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + MCU_cols = dstinfo->image_width / (dstinfo->max_h_samp_factor * DCTSIZE); + MCU_rows = dstinfo->image_height / (dstinfo->max_v_samp_factor * DCTSIZE); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_width = MCU_cols * compptr->h_samp_factor; + comp_height = MCU_rows * compptr->v_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + if (dst_blk_y < comp_height) { + /* Row is within the vertically mirrorable area. */ + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], + comp_height - dst_blk_y - (JDIMENSION) compptr->v_samp_factor, + (JDIMENSION) compptr->v_samp_factor, FALSE); + } else { + /* Bottom-edge rows are only mirrored horizontally. */ + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, FALSE); + } + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + if (dst_blk_y < comp_height) { + /* Row is within the mirrorable area. */ + dst_row_ptr = dst_buffer[offset_y]; + src_row_ptr = src_buffer[compptr->v_samp_factor - offset_y - 1]; + /* Process the blocks that can be mirrored both ways. */ + for (dst_blk_x = 0; dst_blk_x < comp_width; dst_blk_x++) { + dst_ptr = dst_row_ptr[dst_blk_x]; + src_ptr = src_row_ptr[comp_width - dst_blk_x - 1]; + for (i = 0; i < DCTSIZE; i += 2) { + /* For even row, negate every odd column. */ + for (j = 0; j < DCTSIZE; j += 2) { + *dst_ptr++ = *src_ptr++; + *dst_ptr++ = - *src_ptr++; + } + /* For odd row, negate every even column. */ + for (j = 0; j < DCTSIZE; j += 2) { + *dst_ptr++ = - *src_ptr++; + *dst_ptr++ = *src_ptr++; + } + } + } + /* Any remaining right-edge blocks are only mirrored vertically. */ + for (; dst_blk_x < compptr->width_in_blocks; dst_blk_x++) { + dst_ptr = dst_row_ptr[dst_blk_x]; + src_ptr = src_row_ptr[dst_blk_x]; + for (i = 0; i < DCTSIZE; i += 2) { + for (j = 0; j < DCTSIZE; j++) + *dst_ptr++ = *src_ptr++; + for (j = 0; j < DCTSIZE; j++) + *dst_ptr++ = - *src_ptr++; + } + } + } else { + /* Remaining rows are just mirrored horizontally. */ + dst_row_ptr = dst_buffer[offset_y]; + src_row_ptr = src_buffer[offset_y]; + /* Process the blocks that can be mirrored. */ + for (dst_blk_x = 0; dst_blk_x < comp_width; dst_blk_x++) { + dst_ptr = dst_row_ptr[dst_blk_x]; + src_ptr = src_row_ptr[comp_width - dst_blk_x - 1]; + for (i = 0; i < DCTSIZE2; i += 2) { + *dst_ptr++ = *src_ptr++; + *dst_ptr++ = - *src_ptr++; + } + } + /* Any remaining right-edge blocks are only copied. */ + for (; dst_blk_x < compptr->width_in_blocks; dst_blk_x++) { + dst_ptr = dst_row_ptr[dst_blk_x]; + src_ptr = src_row_ptr[dst_blk_x]; + for (i = 0; i < DCTSIZE2; i++) + *dst_ptr++ = *src_ptr++; + } + } + } + } + } +} + +LOCAL(void) +do_transverse (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* Transverse transpose is equivalent to + * 1. 180 degree rotation; + * 2. Transposition; + * or + * 1. Horizontal mirroring; + * 2. Transposition; + * 3. Horizontal mirroring. + * These steps are merged into a single processing routine. + */ +{ + JDIMENSION MCU_cols, MCU_rows, comp_width, comp_height, dst_blk_x, dst_blk_y; + int ci, i, j, offset_x, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + MCU_cols = dstinfo->image_width / (dstinfo->max_h_samp_factor * DCTSIZE); + MCU_rows = dstinfo->image_height / (dstinfo->max_v_samp_factor * DCTSIZE); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_width = MCU_cols * compptr->h_samp_factor; + comp_height = MCU_rows * compptr->v_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; + dst_blk_x += compptr->h_samp_factor) { + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_x, + (JDIMENSION) compptr->h_samp_factor, FALSE); + for (offset_x = 0; offset_x < compptr->h_samp_factor; offset_x++) { + if (dst_blk_y < comp_height) { + src_ptr = src_buffer[offset_x] + [comp_height - dst_blk_y - offset_y - 1]; + if (dst_blk_x < comp_width) { + /* Block is within the mirrorable area. */ + dst_ptr = dst_buffer[offset_y] + [comp_width - dst_blk_x - offset_x - 1]; + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < DCTSIZE; j++) { + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + j++; + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + } + i++; + for (j = 0; j < DCTSIZE; j++) { + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + j++; + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + } + } + } else { + /* Right-edge blocks are mirrored in y only */ + dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < DCTSIZE; j++) { + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + j++; + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + } + } + } + } else { + src_ptr = src_buffer[offset_x][dst_blk_y + offset_y]; + if (dst_blk_x < comp_width) { + /* Bottom-edge blocks are mirrored in x only */ + dst_ptr = dst_buffer[offset_y] + [comp_width - dst_blk_x - offset_x - 1]; + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + i++; + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + } + } else { + /* At lower right corner, just transpose, no mirroring */ + dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; + for (i = 0; i < DCTSIZE; i++) + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + } + } + } + } + } + } + } +} + +/* Request any required workspace. + * + * We allocate the workspace virtual arrays from the source decompression + * object, so that all the arrays (both the original data and the workspace) + * will be taken into account while making memory management decisions. + * Hence, this routine must be called after jpeg_read_header (which reads + * the image dimensions) and before jpeg_read_coefficients (which realizes + * the source's virtual arrays). + */ + +GLOBAL(void) +jtransform_request_workspace (j_decompress_ptr srcinfo, + jpeg_transform_info *info) +{ + jvirt_barray_ptr *coef_arrays = NULL; + jpeg_component_info *compptr; + int ci; + + if (info->force_grayscale && + srcinfo->jpeg_color_space == JCS_YCbCr && + srcinfo->num_components == 3) { + /* We'll only process the first component */ + info->num_components = 1; + } else { + /* Process all the components */ + info->num_components = srcinfo->num_components; + } + + switch (info->transform) { + case JXFORM_NONE: + case JXFORM_FLIP_H: + /* Don't need a workspace array */ + break; + case JXFORM_FLIP_V: + case JXFORM_ROT_180: + /* Need workspace arrays having same dimensions as source image. + * Note that we allocate arrays padded out to the next iMCU boundary, + * so that transform routines need not worry about missing edge blocks. + */ + coef_arrays = (jvirt_barray_ptr *) + (*srcinfo->mem->alloc_small) ((j_common_ptr) srcinfo, JPOOL_IMAGE, + SIZEOF(jvirt_barray_ptr) * info->num_components); + for (ci = 0; ci < info->num_components; ci++) { + compptr = srcinfo->comp_info + ci; + coef_arrays[ci] = (*srcinfo->mem->request_virt_barray) + ((j_common_ptr) srcinfo, JPOOL_IMAGE, FALSE, + (JDIMENSION) jround_up((long) compptr->width_in_blocks, + (long) compptr->h_samp_factor), + (JDIMENSION) jround_up((long) compptr->height_in_blocks, + (long) compptr->v_samp_factor), + (JDIMENSION) compptr->v_samp_factor); + } + break; + case JXFORM_TRANSPOSE: + case JXFORM_TRANSVERSE: + case JXFORM_ROT_90: + case JXFORM_ROT_270: + /* Need workspace arrays having transposed dimensions. + * Note that we allocate arrays padded out to the next iMCU boundary, + * so that transform routines need not worry about missing edge blocks. + */ + coef_arrays = (jvirt_barray_ptr *) + (*srcinfo->mem->alloc_small) ((j_common_ptr) srcinfo, JPOOL_IMAGE, + SIZEOF(jvirt_barray_ptr) * info->num_components); + for (ci = 0; ci < info->num_components; ci++) { + compptr = srcinfo->comp_info + ci; + coef_arrays[ci] = (*srcinfo->mem->request_virt_barray) + ((j_common_ptr) srcinfo, JPOOL_IMAGE, FALSE, + (JDIMENSION) jround_up((long) compptr->height_in_blocks, + (long) compptr->v_samp_factor), + (JDIMENSION) jround_up((long) compptr->width_in_blocks, + (long) compptr->h_samp_factor), + (JDIMENSION) compptr->h_samp_factor); + } + break; + } + info->workspace_coef_arrays = coef_arrays; +} + +/* Transpose destination image parameters */ + +LOCAL(void) +transpose_critical_parameters (j_compress_ptr dstinfo) +{ + int tblno, i, j, ci, itemp; + jpeg_component_info *compptr; + JQUANT_TBL *qtblptr; + JDIMENSION dtemp; + UINT16 qtemp; + + /* Transpose basic image dimensions */ + dtemp = dstinfo->image_width; + dstinfo->image_width = dstinfo->image_height; + dstinfo->image_height = dtemp; + + /* Transpose sampling factors */ + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + itemp = compptr->h_samp_factor; + compptr->h_samp_factor = compptr->v_samp_factor; + compptr->v_samp_factor = itemp; + } + + /* Transpose quantization tables */ + for (tblno = 0; tblno < NUM_QUANT_TBLS; tblno++) { + qtblptr = dstinfo->quant_tbl_ptrs[tblno]; + if (qtblptr != NULL) { + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < i; j++) { + qtemp = qtblptr->quantval[i*DCTSIZE+j]; + qtblptr->quantval[i*DCTSIZE+j] = qtblptr->quantval[j*DCTSIZE+i]; + qtblptr->quantval[j*DCTSIZE+i] = qtemp; + } + } + } + } +} + +/* Trim off any partial iMCUs on the indicated destination edge */ + +LOCAL(void) +trim_right_edge (j_compress_ptr dstinfo) +{ + int ci, max_h_samp_factor; + JDIMENSION MCU_cols; + + /* We have to compute max_h_samp_factor ourselves, + * because it hasn't been set yet in the destination + * (and we don't want to use the source's value). + */ + max_h_samp_factor = 1; + for (ci = 0; ci < dstinfo->num_components; ci++) { + int h_samp_factor = dstinfo->comp_info[ci].h_samp_factor; + max_h_samp_factor = MAX(max_h_samp_factor, h_samp_factor); + } + MCU_cols = dstinfo->image_width / (max_h_samp_factor * DCTSIZE); + if (MCU_cols > 0) /* can't trim to 0 pixels */ + dstinfo->image_width = MCU_cols * (max_h_samp_factor * DCTSIZE); +} + +LOCAL(void) +trim_bottom_edge (j_compress_ptr dstinfo) +{ + int ci, max_v_samp_factor; + JDIMENSION MCU_rows; + + /* We have to compute max_v_samp_factor ourselves, + * because it hasn't been set yet in the destination + * (and we don't want to use the source's value). + */ + max_v_samp_factor = 1; + for (ci = 0; ci < dstinfo->num_components; ci++) { + int v_samp_factor = dstinfo->comp_info[ci].v_samp_factor; + max_v_samp_factor = MAX(max_v_samp_factor, v_samp_factor); + } + MCU_rows = dstinfo->image_height / (max_v_samp_factor * DCTSIZE); + if (MCU_rows > 0) /* can't trim to 0 pixels */ + dstinfo->image_height = MCU_rows * (max_v_samp_factor * DCTSIZE); +} + +/* Adjust output image parameters as needed. + * + * This must be called after jpeg_copy_critical_parameters() + * and before jpeg_write_coefficients(). + * + * The return value is the set of virtual coefficient arrays to be written + * (either the ones allocated by jtransform_request_workspace, or the + * original source data arrays). The caller will need to pass this value + * to jpeg_write_coefficients(). + */ + +GLOBAL(jvirt_barray_ptr *) +jtransform_adjust_parameters (j_decompress_ptr srcinfo, + j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jpeg_transform_info *info) +{ + /* If force-to-grayscale is requested, adjust destination parameters */ + if (info->force_grayscale) { + /* We use jpeg_set_colorspace to make sure subsidiary settings get fixed + * properly. Among other things, the target h_samp_factor & v_samp_factor + * will get set to 1, which typically won't match the source. + * In fact we do this even if the source is already grayscale; that + * provides an easy way of coercing a grayscale JPEG with funny sampling + * factors to the customary 1,1. (Some decoders fail on other factors.) + */ + if ((dstinfo->jpeg_color_space == JCS_YCbCr && + dstinfo->num_components == 3) || + (dstinfo->jpeg_color_space == JCS_GRAYSCALE && + dstinfo->num_components == 1)) { + /* We have to preserve the source's quantization table number. */ + int sv_quant_tbl_no = dstinfo->comp_info[0].quant_tbl_no; + jpeg_set_colorspace(dstinfo, JCS_GRAYSCALE); + dstinfo->comp_info[0].quant_tbl_no = sv_quant_tbl_no; + } else { + /* Sorry, can't do it */ + ERREXIT(dstinfo, JERR_CONVERSION_NOTIMPL); + } + } + + /* Correct the destination's image dimensions etc if necessary */ + switch (info->transform) { + case JXFORM_NONE: + /* Nothing to do */ + break; + case JXFORM_FLIP_H: + if (info->trim) + trim_right_edge(dstinfo); + break; + case JXFORM_FLIP_V: + if (info->trim) + trim_bottom_edge(dstinfo); + break; + case JXFORM_TRANSPOSE: + transpose_critical_parameters(dstinfo); + /* transpose does NOT have to trim anything */ + break; + case JXFORM_TRANSVERSE: + transpose_critical_parameters(dstinfo); + if (info->trim) { + trim_right_edge(dstinfo); + trim_bottom_edge(dstinfo); + } + break; + case JXFORM_ROT_90: + transpose_critical_parameters(dstinfo); + if (info->trim) + trim_right_edge(dstinfo); + break; + case JXFORM_ROT_180: + if (info->trim) { + trim_right_edge(dstinfo); + trim_bottom_edge(dstinfo); + } + break; + case JXFORM_ROT_270: + transpose_critical_parameters(dstinfo); + if (info->trim) + trim_bottom_edge(dstinfo); + break; + } + + /* Return the appropriate output data set */ + if (info->workspace_coef_arrays != NULL) + return info->workspace_coef_arrays; + return src_coef_arrays; +} + +/* Execute the actual transformation, if any. + * + * This must be called *after* jpeg_write_coefficients, because it depends + * on jpeg_write_coefficients to have computed subsidiary values such as + * the per-component width and height fields in the destination object. + * + * Note that some transformations will modify the source data arrays! + */ + +GLOBAL(void) +jtransform_execute_transformation (j_decompress_ptr srcinfo, + j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jpeg_transform_info *info) +{ + jvirt_barray_ptr *dst_coef_arrays = info->workspace_coef_arrays; + + switch (info->transform) { + case JXFORM_NONE: + break; + case JXFORM_FLIP_H: + do_flip_h(srcinfo, dstinfo, src_coef_arrays); + break; + case JXFORM_FLIP_V: + do_flip_v(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_TRANSPOSE: + do_transpose(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_TRANSVERSE: + do_transverse(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_ROT_90: + do_rot_90(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_ROT_180: + do_rot_180(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_ROT_270: + do_rot_270(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); + break; + } +} + +#endif /* TRANSFORMS_SUPPORTED */ + +/* Setup decompression object to save desired markers in memory. + * This must be called before jpeg_read_header() to have the desired effect. + */ + +GLOBAL(void) +jcopy_markers_setup (j_decompress_ptr srcinfo, JCOPY_OPTION option) +{ +#ifdef SAVE_MARKERS_SUPPORTED + int m; + + /* Save comments except under NONE option */ + if (option != JCOPYOPT_NONE) { + jpeg_save_markers(srcinfo, JPEG_COM, 0xFFFF); + } + /* Save all types of APPn markers iff ALL option */ + if (option == JCOPYOPT_ALL) { + for (m = 0; m < 16; m++) + jpeg_save_markers(srcinfo, JPEG_APP0 + m, 0xFFFF); + } +#endif /* SAVE_MARKERS_SUPPORTED */ +} + +/* Copy markers saved in the given source object to the destination object. + * This should be called just after jpeg_start_compress() or + * jpeg_write_coefficients(). + * Note that those routines will have written the SOI, and also the + * JFIF APP0 or Adobe APP14 markers if selected. + */ + +GLOBAL(void) +jcopy_markers_execute (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + JCOPY_OPTION option) +{ + jpeg_saved_marker_ptr marker; + + /* In the current implementation, we don't actually need to examine the + * option flag here; we just copy everything that got saved. + * But to avoid confusion, we do not output JFIF and Adobe APP14 markers + * if the encoder library already wrote one. + */ + for (marker = srcinfo->marker_list; marker != NULL; marker = marker->next) { + if (dstinfo->write_JFIF_header && + marker->marker == JPEG_APP0 && + marker->data_length >= 5 && + GETJOCTET(marker->data[0]) == 0x4A && + GETJOCTET(marker->data[1]) == 0x46 && + GETJOCTET(marker->data[2]) == 0x49 && + GETJOCTET(marker->data[3]) == 0x46 && + GETJOCTET(marker->data[4]) == 0) + continue; /* reject duplicate JFIF */ + if (dstinfo->write_Adobe_marker && + marker->marker == JPEG_APP0+14 && + marker->data_length >= 5 && + GETJOCTET(marker->data[0]) == 0x41 && + GETJOCTET(marker->data[1]) == 0x64 && + GETJOCTET(marker->data[2]) == 0x6F && + GETJOCTET(marker->data[3]) == 0x62 && + GETJOCTET(marker->data[4]) == 0x65) + continue; /* reject duplicate Adobe */ +#ifdef NEED_FAR_POINTERS + /* We could use jpeg_write_marker if the data weren't FAR... */ + { + unsigned int i; + jpeg_write_m_header(dstinfo, marker->marker, marker->data_length); + for (i = 0; i < marker->data_length; i++) + jpeg_write_m_byte(dstinfo, marker->data[i]); + } +#else + jpeg_write_marker(dstinfo, marker->marker, + marker->data, marker->data_length); +#endif + } +} +/********* End of inlined file: transupp.c *********/ + + } +} + +#if JUCE_MSVC + #pragma warning (pop) +#endif + +BEGIN_JUCE_NAMESPACE + +using namespace jpeglibNamespace; + +struct JPEGDecodingFailure {}; + +static void fatalErrorHandler (j_common_ptr) +{ + throw JPEGDecodingFailure(); +} + +static void silentErrorCallback1 (j_common_ptr) {} +static void silentErrorCallback2 (j_common_ptr, int) {} +static void silentErrorCallback3 (j_common_ptr, char*) {} + +static void setupSilentErrorHandler (struct jpeg_error_mgr& err) +{ + zerostruct (err); + + err.error_exit = fatalErrorHandler; + err.emit_message = silentErrorCallback2; + err.output_message = silentErrorCallback1; + err.format_message = silentErrorCallback3; + err.reset_error_mgr = silentErrorCallback1; +} + +static void dummyCallback1 (j_decompress_ptr) throw() +{ +} + +static void jpegSkip (j_decompress_ptr decompStruct, long num) throw() +{ + decompStruct->src->next_input_byte += num; + + num = jmin (num, (int) decompStruct->src->bytes_in_buffer); + decompStruct->src->bytes_in_buffer -= num; +} + +static boolean jpegFill (j_decompress_ptr) throw() +{ + return 0; +} + +Image* juce_loadJPEGImageFromStream (InputStream& in) throw() +{ + MemoryBlock mb; + in.readIntoMemoryBlock (mb); + + Image* image = 0; + + if (mb.getSize() > 16) + { + struct jpeg_decompress_struct jpegDecompStruct; + + struct jpeg_error_mgr jerr; + setupSilentErrorHandler (jerr); + jpegDecompStruct.err = &jerr; + + jpeg_create_decompress (&jpegDecompStruct); + + jpegDecompStruct.src = (jpeg_source_mgr*)(jpegDecompStruct.mem->alloc_small) + ((j_common_ptr)(&jpegDecompStruct), JPOOL_PERMANENT, sizeof (jpeg_source_mgr)); + + jpegDecompStruct.src->init_source = dummyCallback1; + jpegDecompStruct.src->fill_input_buffer = jpegFill; + jpegDecompStruct.src->skip_input_data = jpegSkip; + jpegDecompStruct.src->resync_to_restart = jpeg_resync_to_restart; + jpegDecompStruct.src->term_source = dummyCallback1; + + jpegDecompStruct.src->next_input_byte = (const unsigned char*) mb.getData(); + jpegDecompStruct.src->bytes_in_buffer = mb.getSize(); + + try + { + jpeg_read_header (&jpegDecompStruct, TRUE); + + jpeg_calc_output_dimensions (&jpegDecompStruct); + + const int width = jpegDecompStruct.output_width; + const int height = jpegDecompStruct.output_height; + + jpegDecompStruct.out_color_space = JCS_RGB; + + JSAMPARRAY buffer + = (*jpegDecompStruct.mem->alloc_sarray) ((j_common_ptr) &jpegDecompStruct, + JPOOL_IMAGE, + width * 3, 1); + + if (jpeg_start_decompress (&jpegDecompStruct)) + { + image = new Image (Image::RGB, width, height, false); + + for (int y = 0; y < height; ++y) + { + jpeg_read_scanlines (&jpegDecompStruct, buffer, 1); + + int stride, pixelStride; + uint8* pixels = image->lockPixelDataReadWrite (0, y, width, 1, stride, pixelStride); + const uint8* src = *buffer; + uint8* dest = pixels; + + for (int i = width; --i >= 0;) + { + ((PixelRGB*) dest)->setARGB (0, src[0], src[1], src[2]); + dest += pixelStride; + src += 3; + } + + image->releasePixelDataReadWrite (pixels); + } + + jpeg_finish_decompress (&jpegDecompStruct); + } + + jpeg_destroy_decompress (&jpegDecompStruct); + } + catch (...) + {} + } + + return image; +} + +static const int bufferSize = 512; + +struct JuceJpegDest : public jpeg_destination_mgr +{ + OutputStream* output; + char* buffer; +}; + +static void jpegWriteInit (j_compress_ptr) throw() +{ +} + +static void jpegWriteTerminate (j_compress_ptr cinfo) throw() +{ + JuceJpegDest* const dest = (JuceJpegDest*) cinfo->dest; + + const int numToWrite = bufferSize - dest->free_in_buffer; + dest->output->write (dest->buffer, numToWrite); +} + +static boolean jpegWriteFlush (j_compress_ptr cinfo) throw() +{ + JuceJpegDest* const dest = (JuceJpegDest*) cinfo->dest; + + const int numToWrite = bufferSize; + + dest->next_output_byte = (JOCTET*) dest->buffer; + dest->free_in_buffer = bufferSize; + + return dest->output->write (dest->buffer, numToWrite); +} + +bool juce_writeJPEGImageToStream (const Image& image, + OutputStream& out, + float quality) throw() +{ + if (image.hasAlphaChannel()) + { + // this method could fill the background in white and still save the image.. + jassertfalse + return true; + } + + struct jpeg_compress_struct jpegCompStruct; + + struct jpeg_error_mgr jerr; + setupSilentErrorHandler (jerr); + jpegCompStruct.err = &jerr; + + jpeg_create_compress (&jpegCompStruct); + + JuceJpegDest dest; + jpegCompStruct.dest = &dest; + + dest.output = &out; + dest.buffer = (char*) juce_malloc (bufferSize); + dest.next_output_byte = (JOCTET*) dest.buffer; + dest.free_in_buffer = bufferSize; + dest.init_destination = jpegWriteInit; + dest.empty_output_buffer = jpegWriteFlush; + dest.term_destination = jpegWriteTerminate; + + jpegCompStruct.image_width = image.getWidth(); + jpegCompStruct.image_height = image.getHeight(); + jpegCompStruct.input_components = 3; + jpegCompStruct.in_color_space = JCS_RGB; + jpegCompStruct.write_JFIF_header = 1; + + jpegCompStruct.X_density = 72; + jpegCompStruct.Y_density = 72; + + jpeg_set_defaults (&jpegCompStruct); + + jpegCompStruct.dct_method = JDCT_FLOAT; + jpegCompStruct.optimize_coding = 1; +// jpegCompStruct.smoothing_factor = 10; + + if (quality < 0.0f) + quality = 6.0f; + + jpeg_set_quality (&jpegCompStruct, jlimit (0, 100, roundFloatToInt (quality * 100.0f)), TRUE); + + jpeg_start_compress (&jpegCompStruct, TRUE); + + const int strideBytes = jpegCompStruct.image_width * jpegCompStruct.input_components; + + JSAMPARRAY buffer = (*jpegCompStruct.mem->alloc_sarray) ((j_common_ptr) &jpegCompStruct, + JPOOL_IMAGE, + strideBytes, 1); + + while (jpegCompStruct.next_scanline < jpegCompStruct.image_height) + { + int stride, pixelStride; + const uint8* pixels = image.lockPixelDataReadOnly (0, jpegCompStruct.next_scanline, jpegCompStruct.image_width, 1, stride, pixelStride); + const uint8* src = pixels; + uint8* dst = *buffer; + + for (int i = jpegCompStruct.image_width; --i >= 0;) + { + *dst++ = ((const PixelRGB*) src)->getRed(); + *dst++ = ((const PixelRGB*) src)->getGreen(); + *dst++ = ((const PixelRGB*) src)->getBlue(); + src += pixelStride; + } + + jpeg_write_scanlines (&jpegCompStruct, buffer, 1); + image.releasePixelDataReadOnly (pixels); + } + + jpeg_finish_compress (&jpegCompStruct); + jpeg_destroy_compress (&jpegCompStruct); + + juce_free (dest.buffer); + + out.flush(); + + return true; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_JPEGLoader.cpp *********/ + +/********* Start of inlined file: juce_PNGLoader.cpp *********/ + +#ifdef _MSC_VER + #pragma warning (push) + #pragma warning (disable: 4390 4611) +#endif + +namespace zlibNamespace +{ + #undef OS_CODE + #undef fdopen + + #undef OS_CODE +} + +namespace pnglibNamespace +{ + using namespace zlibNamespace; + using ::malloc; + using ::free; + + extern "C" + { + using ::abs; + #define PNG_INTERNAL + #define NO_DUMMY_DECL + #define PNG_SETJMP_NOT_SUPPORTED + +/********* Start of inlined file: png.h *********/ +/* png.h - header file for PNG reference library + * + * libpng version 1.2.21 - October 4, 2007 + * Copyright (c) 1998-2007 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * Authors and maintainers: + * libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat + * libpng versions 0.89c, June 1996, through 0.96, May 1997: Andreas Dilger + * libpng versions 0.97, January 1998, through 1.2.21 - October 4, 2007: Glenn + * See also "Contributing Authors", below. + * + * Note about libpng version numbers: + * + * Due to various miscommunications, unforeseen code incompatibilities + * and occasional factors outside the authors' control, version numbering + * on the library has not always been consistent and straightforward. + * The following table summarizes matters since version 0.89c, which was + * the first widely used release: + * + * source png.h png.h shared-lib + * version string int version + * ------- ------ ----- ---------- + * 0.89c "1.0 beta 3" 0.89 89 1.0.89 + * 0.90 "1.0 beta 4" 0.90 90 0.90 [should have been 2.0.90] + * 0.95 "1.0 beta 5" 0.95 95 0.95 [should have been 2.0.95] + * 0.96 "1.0 beta 6" 0.96 96 0.96 [should have been 2.0.96] + * 0.97b "1.00.97 beta 7" 1.00.97 97 1.0.1 [should have been 2.0.97] + * 0.97c 0.97 97 2.0.97 + * 0.98 0.98 98 2.0.98 + * 0.99 0.99 98 2.0.99 + * 0.99a-m 0.99 99 2.0.99 + * 1.00 1.00 100 2.1.0 [100 should be 10000] + * 1.0.0 (from here on, the 100 2.1.0 [100 should be 10000] + * 1.0.1 png.h string is 10001 2.1.0 + * 1.0.1a-e identical to the 10002 from here on, the shared library + * 1.0.2 source version) 10002 is 2.V where V is the source code + * 1.0.2a-b 10003 version, except as noted. + * 1.0.3 10003 + * 1.0.3a-d 10004 + * 1.0.4 10004 + * 1.0.4a-f 10005 + * 1.0.5 (+ 2 patches) 10005 + * 1.0.5a-d 10006 + * 1.0.5e-r 10100 (not source compatible) + * 1.0.5s-v 10006 (not binary compatible) + * 1.0.6 (+ 3 patches) 10006 (still binary incompatible) + * 1.0.6d-f 10007 (still binary incompatible) + * 1.0.6g 10007 + * 1.0.6h 10007 10.6h (testing xy.z so-numbering) + * 1.0.6i 10007 10.6i + * 1.0.6j 10007 2.1.0.6j (incompatible with 1.0.0) + * 1.0.7beta11-14 DLLNUM 10007 2.1.0.7beta11-14 (binary compatible) + * 1.0.7beta15-18 1 10007 2.1.0.7beta15-18 (binary compatible) + * 1.0.7rc1-2 1 10007 2.1.0.7rc1-2 (binary compatible) + * 1.0.7 1 10007 (still compatible) + * 1.0.8beta1-4 1 10008 2.1.0.8beta1-4 + * 1.0.8rc1 1 10008 2.1.0.8rc1 + * 1.0.8 1 10008 2.1.0.8 + * 1.0.9beta1-6 1 10009 2.1.0.9beta1-6 + * 1.0.9rc1 1 10009 2.1.0.9rc1 + * 1.0.9beta7-10 1 10009 2.1.0.9beta7-10 + * 1.0.9rc2 1 10009 2.1.0.9rc2 + * 1.0.9 1 10009 2.1.0.9 + * 1.0.10beta1 1 10010 2.1.0.10beta1 + * 1.0.10rc1 1 10010 2.1.0.10rc1 + * 1.0.10 1 10010 2.1.0.10 + * 1.0.11beta1-3 1 10011 2.1.0.11beta1-3 + * 1.0.11rc1 1 10011 2.1.0.11rc1 + * 1.0.11 1 10011 2.1.0.11 + * 1.0.12beta1-2 2 10012 2.1.0.12beta1-2 + * 1.0.12rc1 2 10012 2.1.0.12rc1 + * 1.0.12 2 10012 2.1.0.12 + * 1.1.0a-f - 10100 2.1.1.0a-f (branch abandoned) + * 1.2.0beta1-2 2 10200 2.1.2.0beta1-2 + * 1.2.0beta3-5 3 10200 3.1.2.0beta3-5 + * 1.2.0rc1 3 10200 3.1.2.0rc1 + * 1.2.0 3 10200 3.1.2.0 + * 1.2.1beta1-4 3 10201 3.1.2.1beta1-4 + * 1.2.1rc1-2 3 10201 3.1.2.1rc1-2 + * 1.2.1 3 10201 3.1.2.1 + * 1.2.2beta1-6 12 10202 12.so.0.1.2.2beta1-6 + * 1.0.13beta1 10 10013 10.so.0.1.0.13beta1 + * 1.0.13rc1 10 10013 10.so.0.1.0.13rc1 + * 1.2.2rc1 12 10202 12.so.0.1.2.2rc1 + * 1.0.13 10 10013 10.so.0.1.0.13 + * 1.2.2 12 10202 12.so.0.1.2.2 + * 1.2.3rc1-6 12 10203 12.so.0.1.2.3rc1-6 + * 1.2.3 12 10203 12.so.0.1.2.3 + * 1.2.4beta1-3 13 10204 12.so.0.1.2.4beta1-3 + * 1.0.14rc1 13 10014 10.so.0.1.0.14rc1 + * 1.2.4rc1 13 10204 12.so.0.1.2.4rc1 + * 1.0.14 10 10014 10.so.0.1.0.14 + * 1.2.4 13 10204 12.so.0.1.2.4 + * 1.2.5beta1-2 13 10205 12.so.0.1.2.5beta1-2 + * 1.0.15rc1-3 10 10015 10.so.0.1.0.15rc1-3 + * 1.2.5rc1-3 13 10205 12.so.0.1.2.5rc1-3 + * 1.0.15 10 10015 10.so.0.1.0.15 + * 1.2.5 13 10205 12.so.0.1.2.5 + * 1.2.6beta1-4 13 10206 12.so.0.1.2.6beta1-4 + * 1.0.16 10 10016 10.so.0.1.0.16 + * 1.2.6 13 10206 12.so.0.1.2.6 + * 1.2.7beta1-2 13 10207 12.so.0.1.2.7beta1-2 + * 1.0.17rc1 10 10017 10.so.0.1.0.17rc1 + * 1.2.7rc1 13 10207 12.so.0.1.2.7rc1 + * 1.0.17 10 10017 10.so.0.1.0.17 + * 1.2.7 13 10207 12.so.0.1.2.7 + * 1.2.8beta1-5 13 10208 12.so.0.1.2.8beta1-5 + * 1.0.18rc1-5 10 10018 10.so.0.1.0.18rc1-5 + * 1.2.8rc1-5 13 10208 12.so.0.1.2.8rc1-5 + * 1.0.18 10 10018 10.so.0.1.0.18 + * 1.2.8 13 10208 12.so.0.1.2.8 + * 1.2.9beta1-3 13 10209 12.so.0.1.2.9beta1-3 + * 1.2.9beta4-11 13 10209 12.so.0.9[.0] + * 1.2.9rc1 13 10209 12.so.0.9[.0] + * 1.2.9 13 10209 12.so.0.9[.0] + * 1.2.10beta1-8 13 10210 12.so.0.10[.0] + * 1.2.10rc1-3 13 10210 12.so.0.10[.0] + * 1.2.10 13 10210 12.so.0.10[.0] + * 1.2.11beta1-4 13 10211 12.so.0.11[.0] + * 1.0.19rc1-5 10 10019 10.so.0.19[.0] + * 1.2.11rc1-5 13 10211 12.so.0.11[.0] + * 1.0.19 10 10019 10.so.0.19[.0] + * 1.2.11 13 10211 12.so.0.11[.0] + * 1.0.20 10 10020 10.so.0.20[.0] + * 1.2.12 13 10212 12.so.0.12[.0] + * 1.2.13beta1 13 10213 12.so.0.13[.0] + * 1.0.21 10 10021 10.so.0.21[.0] + * 1.2.13 13 10213 12.so.0.13[.0] + * 1.2.14beta1-2 13 10214 12.so.0.14[.0] + * 1.0.22rc1 10 10022 10.so.0.22[.0] + * 1.2.14rc1 13 10214 12.so.0.14[.0] + * 1.0.22 10 10022 10.so.0.22[.0] + * 1.2.14 13 10214 12.so.0.14[.0] + * 1.2.15beta1-6 13 10215 12.so.0.15[.0] + * 1.0.23rc1-5 10 10023 10.so.0.23[.0] + * 1.2.15rc1-5 13 10215 12.so.0.15[.0] + * 1.0.23 10 10023 10.so.0.23[.0] + * 1.2.15 13 10215 12.so.0.15[.0] + * 1.2.16beta1-2 13 10216 12.so.0.16[.0] + * 1.2.16rc1 13 10216 12.so.0.16[.0] + * 1.0.24 10 10024 10.so.0.24[.0] + * 1.2.16 13 10216 12.so.0.16[.0] + * 1.2.17beta1-2 13 10217 12.so.0.17[.0] + * 1.0.25rc1 10 10025 10.so.0.25[.0] + * 1.2.17rc1-3 13 10217 12.so.0.17[.0] + * 1.0.25 10 10025 10.so.0.25[.0] + * 1.2.17 13 10217 12.so.0.17[.0] + * 1.0.26 10 10026 10.so.0.26[.0] + * 1.2.18 13 10218 12.so.0.18[.0] + * 1.2.19beta1-31 13 10219 12.so.0.19[.0] + * 1.0.27rc1-6 10 10027 10.so.0.27[.0] + * 1.2.19rc1-6 13 10219 12.so.0.19[.0] + * 1.0.27 10 10027 10.so.0.27[.0] + * 1.2.19 13 10219 12.so.0.19[.0] + * 1.2.20beta01-04 13 10220 12.so.0.20[.0] + * 1.0.28rc1-6 10 10028 10.so.0.28[.0] + * 1.2.20rc1-6 13 10220 12.so.0.20[.0] + * 1.0.28 10 10028 10.so.0.28[.0] + * 1.2.20 13 10220 12.so.0.20[.0] + * 1.2.21beta1-2 13 10221 12.so.0.21[.0] + * 1.2.21rc1-3 13 10221 12.so.0.21[.0] + * 1.0.29 10 10029 10.so.0.29[.0] + * 1.2.21 13 10221 12.so.0.21[.0] + * + * Henceforth the source version will match the shared-library major + * and minor numbers; the shared-library major version number will be + * used for changes in backward compatibility, as it is intended. The + * PNG_LIBPNG_VER macro, which is not used within libpng but is available + * for applications, is an unsigned integer of the form xyyzz corresponding + * to the source version x.y.z (leading zeros in y and z). Beta versions + * were given the previous public release number plus a letter, until + * version 1.0.6j; from then on they were given the upcoming public + * release number plus "betaNN" or "rcN". + * + * Binary incompatibility exists only when applications make direct access + * to the info_ptr or png_ptr members through png.h, and the compiled + * application is loaded with a different version of the library. + * + * DLLNUM will change each time there are forward or backward changes + * in binary compatibility (e.g., when a new feature is added). + * + * See libpng.txt or libpng.3 for more information. The PNG specification + * is available as a W3C Recommendation and as an ISO Specification, + * + * e.g. #define PNG_USER_PRIVATEBUILD "Build by MyCompany for xyz reasons." + * #define PNG_USER_DLLFNAME_POSTFIX + * e.g. // private DLL "libpng13gx.dll" + * #define PNG_USER_DLLFNAME_POSTFIX "gx" + * + * The following macros are also at your disposal if you want to complete the + * DLL VERSIONINFO structure. + * - PNG_USER_VERSIONINFO_COMMENTS + * - PNG_USER_VERSIONINFO_COMPANYNAME + * - PNG_USER_VERSIONINFO_LEGALTRADEMARKS + */ + +#ifdef __STDC__ +#ifdef SPECIALBUILD +# pragma message("PNG_LIBPNG_SPECIALBUILD (and deprecated SPECIALBUILD)\ + are now LIBPNG reserved macros. Use PNG_USER_PRIVATEBUILD instead.") +#endif + +#ifdef PRIVATEBUILD +# pragma message("PRIVATEBUILD is deprecated.\ + Use PNG_USER_PRIVATEBUILD instead.") +# define PNG_USER_PRIVATEBUILD PRIVATEBUILD +#endif +#endif /* __STDC__ */ + +#ifndef PNG_VERSION_INFO_ONLY + +/* End of material added to libpng-1.2.8 */ + +/* Added at libpng-1.2.19, removed at libpng-1.2.20 because it caused trouble + Restored at libpng-1.2.21 */ +# define PNG_WARN_UNINITIALIZED_ROW 1 +/* End of material added at libpng-1.2.19/1.2.21 */ + +/* This is the size of the compression buffer, and thus the size of + * an IDAT chunk. Make this whatever size you feel is best for your + * machine. One of these will be allocated per png_struct. When this + * is full, it writes the data to the disk, and does some other + * calculations. Making this an extremely small size will slow + * the library down, but you may want to experiment to determine + * where it becomes significant, if you are concerned with memory + * usage. Note that zlib allocates at least 32Kb also. For readers, + * this describes the size of the buffer available to read the data in. + * Unless this gets smaller than the size of a row (compressed), + * it should not make much difference how big this is. + */ + +#ifndef PNG_ZBUF_SIZE +# define PNG_ZBUF_SIZE 8192 +#endif + +/* Enable if you want a write-only libpng */ + +#ifndef PNG_NO_READ_SUPPORTED +# define PNG_READ_SUPPORTED +#endif + +/* Enable if you want a read-only libpng */ + +#ifndef PNG_NO_WRITE_SUPPORTED +# define PNG_WRITE_SUPPORTED +#endif + +/* Enabled by default in 1.2.0. You can disable this if you don't need to + support PNGs that are embedded in MNG datastreams */ +#if !defined(PNG_1_0_X) && !defined(PNG_NO_MNG_FEATURES) +# ifndef PNG_MNG_FEATURES_SUPPORTED +# define PNG_MNG_FEATURES_SUPPORTED +# endif +#endif + +#ifndef PNG_NO_FLOATING_POINT_SUPPORTED +# ifndef PNG_FLOATING_POINT_SUPPORTED +# define PNG_FLOATING_POINT_SUPPORTED +# endif +#endif + +/* If you are running on a machine where you cannot allocate more + * than 64K of memory at once, uncomment this. While libpng will not + * normally need that much memory in a chunk (unless you load up a very + * large file), zlib needs to know how big of a chunk it can use, and + * libpng thus makes sure to check any memory allocation to verify it + * will fit into memory. +#define PNG_MAX_MALLOC_64K + */ +#if defined(MAXSEG_64K) && !defined(PNG_MAX_MALLOC_64K) +# define PNG_MAX_MALLOC_64K +#endif + +/* Special munging to support doing things the 'cygwin' way: + * 'Normal' png-on-win32 defines/defaults: + * PNG_BUILD_DLL -- building dll + * PNG_USE_DLL -- building an application, linking to dll + * (no define) -- building static library, or building an + * application and linking to the static lib + * 'Cygwin' defines/defaults: + * PNG_BUILD_DLL -- (ignored) building the dll + * (no define) -- (ignored) building an application, linking to the dll + * PNG_STATIC -- (ignored) building the static lib, or building an + * application that links to the static lib. + * ALL_STATIC -- (ignored) building various static libs, or building an + * application that links to the static libs. + * Thus, + * a cygwin user should define either PNG_BUILD_DLL or PNG_STATIC, and + * this bit of #ifdefs will define the 'correct' config variables based on + * that. If a cygwin user *wants* to define 'PNG_USE_DLL' that's okay, but + * unnecessary. + * + * Also, the precedence order is: + * ALL_STATIC (since we can't #undef something outside our namespace) + * PNG_BUILD_DLL + * PNG_STATIC + * (nothing) == PNG_USE_DLL + * + * CYGWIN (2002-01-20): The preceding is now obsolete. With the advent + * of auto-import in binutils, we no longer need to worry about + * __declspec(dllexport) / __declspec(dllimport) and friends. Therefore, + * we don't need to worry about PNG_STATIC or ALL_STATIC when it comes + * to __declspec() stuff. However, we DO need to worry about + * PNG_BUILD_DLL and PNG_STATIC because those change some defaults + * such as CONSOLE_IO and whether GLOBAL_ARRAYS are allowed. + */ +#if defined(__CYGWIN__) +# if defined(ALL_STATIC) +# if defined(PNG_BUILD_DLL) +# undef PNG_BUILD_DLL +# endif +# if defined(PNG_USE_DLL) +# undef PNG_USE_DLL +# endif +# if defined(PNG_DLL) +# undef PNG_DLL +# endif +# if !defined(PNG_STATIC) +# define PNG_STATIC +# endif +# else +# if defined (PNG_BUILD_DLL) +# if defined(PNG_STATIC) +# undef PNG_STATIC +# endif +# if defined(PNG_USE_DLL) +# undef PNG_USE_DLL +# endif +# if !defined(PNG_DLL) +# define PNG_DLL +# endif +# else +# if defined(PNG_STATIC) +# if defined(PNG_USE_DLL) +# undef PNG_USE_DLL +# endif +# if defined(PNG_DLL) +# undef PNG_DLL +# endif +# else +# if !defined(PNG_USE_DLL) +# define PNG_USE_DLL +# endif +# if !defined(PNG_DLL) +# define PNG_DLL +# endif +# endif +# endif +# endif +#endif + +/* This protects us against compilers that run on a windowing system + * and thus don't have or would rather us not use the stdio types: + * stdin, stdout, and stderr. The only one currently used is stderr + * in png_error() and png_warning(). #defining PNG_NO_CONSOLE_IO will + * prevent these from being compiled and used. #defining PNG_NO_STDIO + * will also prevent these, plus will prevent the entire set of stdio + * macros and functions (FILE *, printf, etc.) from being compiled and used, + * unless (PNG_DEBUG > 0) has been #defined. + * + * #define PNG_NO_CONSOLE_IO + * #define PNG_NO_STDIO + */ + +#if defined(_WIN32_WCE) +# include + /* Console I/O functions are not supported on WindowsCE */ +# define PNG_NO_CONSOLE_IO +# ifdef PNG_DEBUG +# undef PNG_DEBUG +# endif +#endif + +#ifdef PNG_BUILD_DLL +# ifndef PNG_CONSOLE_IO_SUPPORTED +# ifndef PNG_NO_CONSOLE_IO +# define PNG_NO_CONSOLE_IO +# endif +# endif +#endif + +# ifdef PNG_NO_STDIO +# ifndef PNG_NO_CONSOLE_IO +# define PNG_NO_CONSOLE_IO +# endif +# ifdef PNG_DEBUG +# if (PNG_DEBUG > 0) +# include +# endif +# endif +# else +# if !defined(_WIN32_WCE) +/* "stdio.h" functions are not supported on WindowsCE */ +# include +# endif +# endif + +/* This macro protects us against machines that don't have function + * prototypes (ie K&R style headers). If your compiler does not handle + * function prototypes, define this macro and use the included ansi2knr. + * I've always been able to use _NO_PROTO as the indicator, but you may + * need to drag the empty declaration out in front of here, or change the + * ifdef to suit your own needs. + */ +#ifndef PNGARG + +#ifdef OF /* zlib prototype munger */ +# define PNGARG(arglist) OF(arglist) +#else + +#ifdef _NO_PROTO +# define PNGARG(arglist) () +# ifndef PNG_TYPECAST_NULL +# define PNG_TYPECAST_NULL +# endif +#else +# define PNGARG(arglist) arglist +#endif /* _NO_PROTO */ + +#endif /* OF */ + +#endif /* PNGARG */ + +/* Try to determine if we are compiling on a Mac. Note that testing for + * just __MWERKS__ is not good enough, because the Codewarrior is now used + * on non-Mac platforms. + */ +#ifndef MACOS +# if (defined(__MWERKS__) && defined(macintosh)) || defined(applec) || \ + defined(THINK_C) || defined(__SC__) || defined(TARGET_OS_MAC) +# define MACOS +# endif +#endif + +/* enough people need this for various reasons to include it here */ +#if !defined(MACOS) && !defined(RISCOS) && !defined(_WIN32_WCE) +# include +#endif + +#if !defined(PNG_SETJMP_NOT_SUPPORTED) && !defined(PNG_NO_SETJMP_SUPPORTED) +# define PNG_SETJMP_SUPPORTED +#endif + +#ifdef PNG_SETJMP_SUPPORTED +/* This is an attempt to force a single setjmp behaviour on Linux. If + * the X config stuff didn't define _BSD_SOURCE we wouldn't need this. + */ + +# ifdef __linux__ +# ifdef _BSD_SOURCE +# define PNG_SAVE_BSD_SOURCE +# undef _BSD_SOURCE +# endif +# ifdef _SETJMP_H + /* If you encounter a compiler error here, see the explanation + * near the end of INSTALL. + */ + __png.h__ already includes setjmp.h; + __dont__ include it again.; +# endif +# endif /* __linux__ */ + + /* include setjmp.h for error handling */ +# include + +# ifdef __linux__ +# ifdef PNG_SAVE_BSD_SOURCE +# define _BSD_SOURCE +# undef PNG_SAVE_BSD_SOURCE +# endif +# endif /* __linux__ */ +#endif /* PNG_SETJMP_SUPPORTED */ + +#ifdef BSD +# include +#else +# include +#endif + +/* Other defines for things like memory and the like can go here. */ +#ifdef PNG_INTERNAL + +#include + +/* The functions exported by PNG_EXTERN are PNG_INTERNAL functions, which + * aren't usually used outside the library (as far as I know), so it is + * debatable if they should be exported at all. In the future, when it is + * possible to have run-time registry of chunk-handling functions, some of + * these will be made available again. +#define PNG_EXTERN extern + */ +#define PNG_EXTERN + +/* Other defines specific to compilers can go here. Try to keep + * them inside an appropriate ifdef/endif pair for portability. + */ + +#if defined(PNG_FLOATING_POINT_SUPPORTED) +# if defined(MACOS) + /* We need to check that hasn't already been included earlier + * as it seems it doesn't agree with , yet we should really use + * if possible. + */ +# if !defined(__MATH_H__) && !defined(__MATH_H) && !defined(__cmath__) +# include +# endif +# else +# include +# endif +# if defined(_AMIGA) && defined(__SASC) && defined(_M68881) + /* Amiga SAS/C: We must include builtin FPU functions when compiling using + * MATH=68881 + */ +# include +# endif +#endif + +/* Codewarrior on NT has linking problems without this. */ +#if (defined(__MWERKS__) && defined(WIN32)) || defined(__STDC__) +# define PNG_ALWAYS_EXTERN +#endif + +/* This provides the non-ANSI (far) memory allocation routines. */ +#if defined(__TURBOC__) && defined(__MSDOS__) +# include +# include +#endif + +/* I have no idea why is this necessary... */ +#if defined(_MSC_VER) && (defined(WIN32) || defined(_Windows) || \ + defined(_WINDOWS) || defined(_WIN32) || defined(__WIN32__)) +# include +#endif + +/* This controls how fine the dithering gets. As this allocates + * a largish chunk of memory (32K), those who are not as concerned + * with dithering quality can decrease some or all of these. + */ +#ifndef PNG_DITHER_RED_BITS +# define PNG_DITHER_RED_BITS 5 +#endif +#ifndef PNG_DITHER_GREEN_BITS +# define PNG_DITHER_GREEN_BITS 5 +#endif +#ifndef PNG_DITHER_BLUE_BITS +# define PNG_DITHER_BLUE_BITS 5 +#endif + +/* This controls how fine the gamma correction becomes when you + * are only interested in 8 bits anyway. Increasing this value + * results in more memory being used, and more pow() functions + * being called to fill in the gamma tables. Don't set this value + * less then 8, and even that may not work (I haven't tested it). + */ + +#ifndef PNG_MAX_GAMMA_8 +# define PNG_MAX_GAMMA_8 11 +#endif + +/* This controls how much a difference in gamma we can tolerate before + * we actually start doing gamma conversion. + */ +#ifndef PNG_GAMMA_THRESHOLD +# define PNG_GAMMA_THRESHOLD 0.05 +#endif + +#endif /* PNG_INTERNAL */ + +/* The following uses const char * instead of char * for error + * and warning message functions, so some compilers won't complain. + * If you do not want to use const, define PNG_NO_CONST here. + */ + +#ifndef PNG_NO_CONST +# define PNG_CONST const +#else +# define PNG_CONST +#endif + +/* The following defines give you the ability to remove code from the + * library that you will not be using. I wish I could figure out how to + * automate this, but I can't do that without making it seriously hard + * on the users. So if you are not using an ability, change the #define + * to and #undef, and that part of the library will not be compiled. If + * your linker can't find a function, you may want to make sure the + * ability is defined here. Some of these depend upon some others being + * defined. I haven't figured out all the interactions here, so you may + * have to experiment awhile to get everything to compile. If you are + * creating or using a shared library, you probably shouldn't touch this, + * as it will affect the size of the structures, and this will cause bad + * things to happen if the library and/or application ever change. + */ + +/* Any features you will not be using can be undef'ed here */ + +/* GR-P, 0.96a: Set "*TRANSFORMS_SUPPORTED as default but allow user + * to turn it off with "*TRANSFORMS_NOT_SUPPORTED" or *PNG_NO_*_TRANSFORMS + * on the compile line, then pick and choose which ones to define without + * having to edit this file. It is safe to use the *TRANSFORMS_NOT_SUPPORTED + * if you only want to have a png-compliant reader/writer but don't need + * any of the extra transformations. This saves about 80 kbytes in a + * typical installation of the library. (PNG_NO_* form added in version + * 1.0.1c, for consistency) + */ + +/* The size of the png_text structure changed in libpng-1.0.6 when + * iTXt support was added. iTXt support was turned off by default through + * libpng-1.2.x, to support old apps that malloc the png_text structure + * instead of calling png_set_text() and letting libpng malloc it. It + * was turned on by default in libpng-1.3.0. + */ + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +# ifndef PNG_NO_iTXt_SUPPORTED +# define PNG_NO_iTXt_SUPPORTED +# endif +# ifndef PNG_NO_READ_iTXt +# define PNG_NO_READ_iTXt +# endif +# ifndef PNG_NO_WRITE_iTXt +# define PNG_NO_WRITE_iTXt +# endif +#endif + +#if !defined(PNG_NO_iTXt_SUPPORTED) +# if !defined(PNG_READ_iTXt_SUPPORTED) && !defined(PNG_NO_READ_iTXt) +# define PNG_READ_iTXt +# endif +# if !defined(PNG_WRITE_iTXt_SUPPORTED) && !defined(PNG_NO_WRITE_iTXt) +# define PNG_WRITE_iTXt +# endif +#endif + +/* The following support, added after version 1.0.0, can be turned off here en + * masse by defining PNG_LEGACY_SUPPORTED in case you need binary compatibility + * with old applications that require the length of png_struct and png_info + * to remain unchanged. + */ + +#ifdef PNG_LEGACY_SUPPORTED +# define PNG_NO_FREE_ME +# define PNG_NO_READ_UNKNOWN_CHUNKS +# define PNG_NO_WRITE_UNKNOWN_CHUNKS +# define PNG_NO_READ_USER_CHUNKS +# define PNG_NO_READ_iCCP +# define PNG_NO_WRITE_iCCP +# define PNG_NO_READ_iTXt +# define PNG_NO_WRITE_iTXt +# define PNG_NO_READ_sCAL +# define PNG_NO_WRITE_sCAL +# define PNG_NO_READ_sPLT +# define PNG_NO_WRITE_sPLT +# define PNG_NO_INFO_IMAGE +# define PNG_NO_READ_RGB_TO_GRAY +# define PNG_NO_READ_USER_TRANSFORM +# define PNG_NO_WRITE_USER_TRANSFORM +# define PNG_NO_USER_MEM +# define PNG_NO_READ_EMPTY_PLTE +# define PNG_NO_MNG_FEATURES +# define PNG_NO_FIXED_POINT_SUPPORTED +#endif + +/* Ignore attempt to turn off both floating and fixed point support */ +#if !defined(PNG_FLOATING_POINT_SUPPORTED) || \ + !defined(PNG_NO_FIXED_POINT_SUPPORTED) +# define PNG_FIXED_POINT_SUPPORTED +#endif + +#ifndef PNG_NO_FREE_ME +# define PNG_FREE_ME_SUPPORTED +#endif + +#if defined(PNG_READ_SUPPORTED) + +#if !defined(PNG_READ_TRANSFORMS_NOT_SUPPORTED) && \ + !defined(PNG_NO_READ_TRANSFORMS) +# define PNG_READ_TRANSFORMS_SUPPORTED +#endif + +#ifdef PNG_READ_TRANSFORMS_SUPPORTED +# ifndef PNG_NO_READ_EXPAND +# define PNG_READ_EXPAND_SUPPORTED +# endif +# ifndef PNG_NO_READ_SHIFT +# define PNG_READ_SHIFT_SUPPORTED +# endif +# ifndef PNG_NO_READ_PACK +# define PNG_READ_PACK_SUPPORTED +# endif +# ifndef PNG_NO_READ_BGR +# define PNG_READ_BGR_SUPPORTED +# endif +# ifndef PNG_NO_READ_SWAP +# define PNG_READ_SWAP_SUPPORTED +# endif +# ifndef PNG_NO_READ_PACKSWAP +# define PNG_READ_PACKSWAP_SUPPORTED +# endif +# ifndef PNG_NO_READ_INVERT +# define PNG_READ_INVERT_SUPPORTED +# endif +# ifndef PNG_NO_READ_DITHER +# define PNG_READ_DITHER_SUPPORTED +# endif +# ifndef PNG_NO_READ_BACKGROUND +# define PNG_READ_BACKGROUND_SUPPORTED +# endif +# ifndef PNG_NO_READ_16_TO_8 +# define PNG_READ_16_TO_8_SUPPORTED +# endif +# ifndef PNG_NO_READ_FILLER +# define PNG_READ_FILLER_SUPPORTED +# endif +# ifndef PNG_NO_READ_GAMMA +# define PNG_READ_GAMMA_SUPPORTED +# endif +# ifndef PNG_NO_READ_GRAY_TO_RGB +# define PNG_READ_GRAY_TO_RGB_SUPPORTED +# endif +# ifndef PNG_NO_READ_SWAP_ALPHA +# define PNG_READ_SWAP_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_READ_INVERT_ALPHA +# define PNG_READ_INVERT_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_READ_STRIP_ALPHA +# define PNG_READ_STRIP_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_READ_USER_TRANSFORM +# define PNG_READ_USER_TRANSFORM_SUPPORTED +# endif +# ifndef PNG_NO_READ_RGB_TO_GRAY +# define PNG_READ_RGB_TO_GRAY_SUPPORTED +# endif +#endif /* PNG_READ_TRANSFORMS_SUPPORTED */ + +#if !defined(PNG_NO_PROGRESSIVE_READ) && \ + !defined(PNG_PROGRESSIVE_READ_SUPPORTED) /* if you don't do progressive */ +# define PNG_PROGRESSIVE_READ_SUPPORTED /* reading. This is not talking */ +#endif /* about interlacing capability! You'll */ + /* still have interlacing unless you change the following line: */ + +#define PNG_READ_INTERLACING_SUPPORTED /* required in PNG-compliant decoders */ + +#ifndef PNG_NO_READ_COMPOSITE_NODIV +# ifndef PNG_NO_READ_COMPOSITED_NODIV /* libpng-1.0.x misspelling */ +# define PNG_READ_COMPOSITE_NODIV_SUPPORTED /* well tested on Intel, SGI */ +# endif +#endif + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* Deprecated, will be removed from version 2.0.0. + Use PNG_MNG_FEATURES_SUPPORTED instead. */ +#ifndef PNG_NO_READ_EMPTY_PLTE +# define PNG_READ_EMPTY_PLTE_SUPPORTED +#endif +#endif + +#endif /* PNG_READ_SUPPORTED */ + +#if defined(PNG_WRITE_SUPPORTED) + +# if !defined(PNG_WRITE_TRANSFORMS_NOT_SUPPORTED) && \ + !defined(PNG_NO_WRITE_TRANSFORMS) +# define PNG_WRITE_TRANSFORMS_SUPPORTED +#endif + +#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED +# ifndef PNG_NO_WRITE_SHIFT +# define PNG_WRITE_SHIFT_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_PACK +# define PNG_WRITE_PACK_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_BGR +# define PNG_WRITE_BGR_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_SWAP +# define PNG_WRITE_SWAP_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_PACKSWAP +# define PNG_WRITE_PACKSWAP_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_INVERT +# define PNG_WRITE_INVERT_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_FILLER +# define PNG_WRITE_FILLER_SUPPORTED /* same as WRITE_STRIP_ALPHA */ +# endif +# ifndef PNG_NO_WRITE_SWAP_ALPHA +# define PNG_WRITE_SWAP_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_INVERT_ALPHA +# define PNG_WRITE_INVERT_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_USER_TRANSFORM +# define PNG_WRITE_USER_TRANSFORM_SUPPORTED +# endif +#endif /* PNG_WRITE_TRANSFORMS_SUPPORTED */ + +#if !defined(PNG_NO_WRITE_INTERLACING_SUPPORTED) && \ + !defined(PNG_WRITE_INTERLACING_SUPPORTED) +#define PNG_WRITE_INTERLACING_SUPPORTED /* not required for PNG-compliant + encoders, but can cause trouble + if left undefined */ +#endif + +#if !defined(PNG_NO_WRITE_WEIGHTED_FILTER) && \ + !defined(PNG_WRITE_WEIGHTED_FILTER) && \ + defined(PNG_FLOATING_POINT_SUPPORTED) +# define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED +#endif + +#ifndef PNG_NO_WRITE_FLUSH +# define PNG_WRITE_FLUSH_SUPPORTED +#endif + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* Deprecated, see PNG_MNG_FEATURES_SUPPORTED, above */ +#ifndef PNG_NO_WRITE_EMPTY_PLTE +# define PNG_WRITE_EMPTY_PLTE_SUPPORTED +#endif +#endif + +#endif /* PNG_WRITE_SUPPORTED */ + +#ifndef PNG_1_0_X +# ifndef PNG_NO_ERROR_NUMBERS +# define PNG_ERROR_NUMBERS_SUPPORTED +# endif +#endif /* PNG_1_0_X */ + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +# ifndef PNG_NO_USER_TRANSFORM_PTR +# define PNG_USER_TRANSFORM_PTR_SUPPORTED +# endif +#endif + +#ifndef PNG_NO_STDIO +# define PNG_TIME_RFC1123_SUPPORTED +#endif + +/* This adds extra functions in pngget.c for accessing data from the + * info pointer (added in version 0.99) + * png_get_image_width() + * png_get_image_height() + * png_get_bit_depth() + * png_get_color_type() + * png_get_compression_type() + * png_get_filter_type() + * png_get_interlace_type() + * png_get_pixel_aspect_ratio() + * png_get_pixels_per_meter() + * png_get_x_offset_pixels() + * png_get_y_offset_pixels() + * png_get_x_offset_microns() + * png_get_y_offset_microns() + */ +#if !defined(PNG_NO_EASY_ACCESS) && !defined(PNG_EASY_ACCESS_SUPPORTED) +# define PNG_EASY_ACCESS_SUPPORTED +#endif + +/* PNG_ASSEMBLER_CODE was enabled by default in version 1.2.0 + * and removed from version 1.2.20. The following will be removed + * from libpng-1.4.0 +*/ + +#if defined(PNG_READ_SUPPORTED) && !defined(PNG_NO_OPTIMIZED_CODE) +# ifndef PNG_OPTIMIZED_CODE_SUPPORTED +# define PNG_OPTIMIZED_CODE_SUPPORTED +# endif +#endif + +#if defined(PNG_READ_SUPPORTED) && !defined(PNG_NO_ASSEMBLER_CODE) +# ifndef PNG_ASSEMBLER_CODE_SUPPORTED +# define PNG_ASSEMBLER_CODE_SUPPORTED +# endif + +# if defined(__GNUC__) && defined(__x86_64__) && (__GNUC__ < 4) + /* work around 64-bit gcc compiler bugs in gcc-3.x */ +# if !defined(PNG_MMX_CODE_SUPPORTED) && !defined(PNG_NO_MMX_CODE) +# define PNG_NO_MMX_CODE +# endif +# endif + +# if defined(__APPLE__) +# if !defined(PNG_MMX_CODE_SUPPORTED) && !defined(PNG_NO_MMX_CODE) +# define PNG_NO_MMX_CODE +# endif +# endif + +# if (defined(__MWERKS__) && ((__MWERKS__ < 0x0900) || macintosh)) +# if !defined(PNG_MMX_CODE_SUPPORTED) && !defined(PNG_NO_MMX_CODE) +# define PNG_NO_MMX_CODE +# endif +# endif + +# if !defined(PNG_MMX_CODE_SUPPORTED) && !defined(PNG_NO_MMX_CODE) +# define PNG_MMX_CODE_SUPPORTED +# endif + +#endif +/* end of obsolete code to be removed from libpng-1.4.0 */ + +#if !defined(PNG_1_0_X) +#if !defined(PNG_NO_USER_MEM) && !defined(PNG_USER_MEM_SUPPORTED) +# define PNG_USER_MEM_SUPPORTED +#endif +#endif /* PNG_1_0_X */ + +/* Added at libpng-1.2.6 */ +#if !defined(PNG_1_0_X) +#ifndef PNG_SET_USER_LIMITS_SUPPORTED +#if !defined(PNG_NO_SET_USER_LIMITS) && !defined(PNG_SET_USER_LIMITS_SUPPORTED) +# define PNG_SET_USER_LIMITS_SUPPORTED +#endif +#endif +#endif /* PNG_1_0_X */ + +/* Added at libpng-1.0.16 and 1.2.6. To accept all valid PNGS no matter + * how large, set these limits to 0x7fffffffL + */ +#ifndef PNG_USER_WIDTH_MAX +# define PNG_USER_WIDTH_MAX 1000000L +#endif +#ifndef PNG_USER_HEIGHT_MAX +# define PNG_USER_HEIGHT_MAX 1000000L +#endif + +/* These are currently experimental features, define them if you want */ + +/* very little testing */ +/* +#ifdef PNG_READ_SUPPORTED +# ifndef PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED +# define PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED +# endif +#endif +*/ + +/* This is only for PowerPC big-endian and 680x0 systems */ +/* some testing */ +/* +#ifndef PNG_READ_BIG_ENDIAN_SUPPORTED +# define PNG_READ_BIG_ENDIAN_SUPPORTED +#endif +*/ + +/* Buggy compilers (e.g., gcc 2.7.2.2) need this */ +/* +#define PNG_NO_POINTER_INDEXING +*/ + +/* These functions are turned off by default, as they will be phased out. */ +/* +#define PNG_USELESS_TESTS_SUPPORTED +#define PNG_CORRECT_PALETTE_SUPPORTED +*/ + +/* Any chunks you are not interested in, you can undef here. The + * ones that allocate memory may be expecially important (hIST, + * tEXt, zTXt, tRNS, pCAL). Others will just save time and make png_info + * a bit smaller. + */ + +#if defined(PNG_READ_SUPPORTED) && \ + !defined(PNG_READ_ANCILLARY_CHUNKS_NOT_SUPPORTED) && \ + !defined(PNG_NO_READ_ANCILLARY_CHUNKS) +# define PNG_READ_ANCILLARY_CHUNKS_SUPPORTED +#endif + +#if defined(PNG_WRITE_SUPPORTED) && \ + !defined(PNG_WRITE_ANCILLARY_CHUNKS_NOT_SUPPORTED) && \ + !defined(PNG_NO_WRITE_ANCILLARY_CHUNKS) +# define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED +#endif + +#ifdef PNG_READ_ANCILLARY_CHUNKS_SUPPORTED + +#ifdef PNG_NO_READ_TEXT +# define PNG_NO_READ_iTXt +# define PNG_NO_READ_tEXt +# define PNG_NO_READ_zTXt +#endif +#ifndef PNG_NO_READ_bKGD +# define PNG_READ_bKGD_SUPPORTED +# define PNG_bKGD_SUPPORTED +#endif +#ifndef PNG_NO_READ_cHRM +# define PNG_READ_cHRM_SUPPORTED +# define PNG_cHRM_SUPPORTED +#endif +#ifndef PNG_NO_READ_gAMA +# define PNG_READ_gAMA_SUPPORTED +# define PNG_gAMA_SUPPORTED +#endif +#ifndef PNG_NO_READ_hIST +# define PNG_READ_hIST_SUPPORTED +# define PNG_hIST_SUPPORTED +#endif +#ifndef PNG_NO_READ_iCCP +# define PNG_READ_iCCP_SUPPORTED +# define PNG_iCCP_SUPPORTED +#endif +#ifndef PNG_NO_READ_iTXt +# ifndef PNG_READ_iTXt_SUPPORTED +# define PNG_READ_iTXt_SUPPORTED +# endif +# ifndef PNG_iTXt_SUPPORTED +# define PNG_iTXt_SUPPORTED +# endif +#endif +#ifndef PNG_NO_READ_oFFs +# define PNG_READ_oFFs_SUPPORTED +# define PNG_oFFs_SUPPORTED +#endif +#ifndef PNG_NO_READ_pCAL +# define PNG_READ_pCAL_SUPPORTED +# define PNG_pCAL_SUPPORTED +#endif +#ifndef PNG_NO_READ_sCAL +# define PNG_READ_sCAL_SUPPORTED +# define PNG_sCAL_SUPPORTED +#endif +#ifndef PNG_NO_READ_pHYs +# define PNG_READ_pHYs_SUPPORTED +# define PNG_pHYs_SUPPORTED +#endif +#ifndef PNG_NO_READ_sBIT +# define PNG_READ_sBIT_SUPPORTED +# define PNG_sBIT_SUPPORTED +#endif +#ifndef PNG_NO_READ_sPLT +# define PNG_READ_sPLT_SUPPORTED +# define PNG_sPLT_SUPPORTED +#endif +#ifndef PNG_NO_READ_sRGB +# define PNG_READ_sRGB_SUPPORTED +# define PNG_sRGB_SUPPORTED +#endif +#ifndef PNG_NO_READ_tEXt +# define PNG_READ_tEXt_SUPPORTED +# define PNG_tEXt_SUPPORTED +#endif +#ifndef PNG_NO_READ_tIME +# define PNG_READ_tIME_SUPPORTED +# define PNG_tIME_SUPPORTED +#endif +#ifndef PNG_NO_READ_tRNS +# define PNG_READ_tRNS_SUPPORTED +# define PNG_tRNS_SUPPORTED +#endif +#ifndef PNG_NO_READ_zTXt +# define PNG_READ_zTXt_SUPPORTED +# define PNG_zTXt_SUPPORTED +#endif +#ifndef PNG_NO_READ_UNKNOWN_CHUNKS +# define PNG_READ_UNKNOWN_CHUNKS_SUPPORTED +# ifndef PNG_UNKNOWN_CHUNKS_SUPPORTED +# define PNG_UNKNOWN_CHUNKS_SUPPORTED +# endif +# ifndef PNG_NO_HANDLE_AS_UNKNOWN +# define PNG_HANDLE_AS_UNKNOWN_SUPPORTED +# endif +#endif +#if !defined(PNG_NO_READ_USER_CHUNKS) && \ + defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) +# define PNG_READ_USER_CHUNKS_SUPPORTED +# define PNG_USER_CHUNKS_SUPPORTED +# ifdef PNG_NO_READ_UNKNOWN_CHUNKS +# undef PNG_NO_READ_UNKNOWN_CHUNKS +# endif +# ifdef PNG_NO_HANDLE_AS_UNKNOWN +# undef PNG_NO_HANDLE_AS_UNKNOWN +# endif +#endif +#ifndef PNG_NO_READ_OPT_PLTE +# define PNG_READ_OPT_PLTE_SUPPORTED /* only affects support of the */ +#endif /* optional PLTE chunk in RGB and RGBA images */ +#if defined(PNG_READ_iTXt_SUPPORTED) || defined(PNG_READ_tEXt_SUPPORTED) || \ + defined(PNG_READ_zTXt_SUPPORTED) +# define PNG_READ_TEXT_SUPPORTED +# define PNG_TEXT_SUPPORTED +#endif + +#endif /* PNG_READ_ANCILLARY_CHUNKS_SUPPORTED */ + +#ifdef PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED + +#ifdef PNG_NO_WRITE_TEXT +# define PNG_NO_WRITE_iTXt +# define PNG_NO_WRITE_tEXt +# define PNG_NO_WRITE_zTXt +#endif +#ifndef PNG_NO_WRITE_bKGD +# define PNG_WRITE_bKGD_SUPPORTED +# ifndef PNG_bKGD_SUPPORTED +# define PNG_bKGD_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_cHRM +# define PNG_WRITE_cHRM_SUPPORTED +# ifndef PNG_cHRM_SUPPORTED +# define PNG_cHRM_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_gAMA +# define PNG_WRITE_gAMA_SUPPORTED +# ifndef PNG_gAMA_SUPPORTED +# define PNG_gAMA_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_hIST +# define PNG_WRITE_hIST_SUPPORTED +# ifndef PNG_hIST_SUPPORTED +# define PNG_hIST_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_iCCP +# define PNG_WRITE_iCCP_SUPPORTED +# ifndef PNG_iCCP_SUPPORTED +# define PNG_iCCP_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_iTXt +# ifndef PNG_WRITE_iTXt_SUPPORTED +# define PNG_WRITE_iTXt_SUPPORTED +# endif +# ifndef PNG_iTXt_SUPPORTED +# define PNG_iTXt_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_oFFs +# define PNG_WRITE_oFFs_SUPPORTED +# ifndef PNG_oFFs_SUPPORTED +# define PNG_oFFs_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_pCAL +# define PNG_WRITE_pCAL_SUPPORTED +# ifndef PNG_pCAL_SUPPORTED +# define PNG_pCAL_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_sCAL +# define PNG_WRITE_sCAL_SUPPORTED +# ifndef PNG_sCAL_SUPPORTED +# define PNG_sCAL_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_pHYs +# define PNG_WRITE_pHYs_SUPPORTED +# ifndef PNG_pHYs_SUPPORTED +# define PNG_pHYs_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_sBIT +# define PNG_WRITE_sBIT_SUPPORTED +# ifndef PNG_sBIT_SUPPORTED +# define PNG_sBIT_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_sPLT +# define PNG_WRITE_sPLT_SUPPORTED +# ifndef PNG_sPLT_SUPPORTED +# define PNG_sPLT_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_sRGB +# define PNG_WRITE_sRGB_SUPPORTED +# ifndef PNG_sRGB_SUPPORTED +# define PNG_sRGB_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_tEXt +# define PNG_WRITE_tEXt_SUPPORTED +# ifndef PNG_tEXt_SUPPORTED +# define PNG_tEXt_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_tIME +# define PNG_WRITE_tIME_SUPPORTED +# ifndef PNG_tIME_SUPPORTED +# define PNG_tIME_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_tRNS +# define PNG_WRITE_tRNS_SUPPORTED +# ifndef PNG_tRNS_SUPPORTED +# define PNG_tRNS_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_zTXt +# define PNG_WRITE_zTXt_SUPPORTED +# ifndef PNG_zTXt_SUPPORTED +# define PNG_zTXt_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_UNKNOWN_CHUNKS +# define PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED +# ifndef PNG_UNKNOWN_CHUNKS_SUPPORTED +# define PNG_UNKNOWN_CHUNKS_SUPPORTED +# endif +# ifndef PNG_NO_HANDLE_AS_UNKNOWN +# ifndef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +# define PNG_HANDLE_AS_UNKNOWN_SUPPORTED +# endif +# endif +#endif +#if defined(PNG_WRITE_iTXt_SUPPORTED) || defined(PNG_WRITE_tEXt_SUPPORTED) || \ + defined(PNG_WRITE_zTXt_SUPPORTED) +# define PNG_WRITE_TEXT_SUPPORTED +# ifndef PNG_TEXT_SUPPORTED +# define PNG_TEXT_SUPPORTED +# endif +#endif + +#endif /* PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED */ + +/* Turn this off to disable png_read_png() and + * png_write_png() and leave the row_pointers member + * out of the info structure. + */ +#ifndef PNG_NO_INFO_IMAGE +# define PNG_INFO_IMAGE_SUPPORTED +#endif + +/* need the time information for reading tIME chunks */ +#if defined(PNG_tIME_SUPPORTED) +# if !defined(_WIN32_WCE) + /* "time.h" functions are not supported on WindowsCE */ +# include +# endif +#endif + +/* Some typedefs to get us started. These should be safe on most of the + * common platforms. The typedefs should be at least as large as the + * numbers suggest (a png_uint_32 must be at least 32 bits long), but they + * don't have to be exactly that size. Some compilers dislike passing + * unsigned shorts as function parameters, so you may be better off using + * unsigned int for png_uint_16. Likewise, for 64-bit systems, you may + * want to have unsigned int for png_uint_32 instead of unsigned long. + */ + +typedef unsigned long png_uint_32; +typedef long png_int_32; +typedef unsigned short png_uint_16; +typedef short png_int_16; +typedef unsigned char png_byte; + +/* This is usually size_t. It is typedef'ed just in case you need it to + change (I'm not sure if you will or not, so I thought I'd be safe) */ +#ifdef PNG_SIZE_T + typedef PNG_SIZE_T png_size_t; +# define png_sizeof(x) png_convert_size(sizeof (x)) +#else + typedef size_t png_size_t; +# define png_sizeof(x) sizeof (x) +#endif + +/* The following is needed for medium model support. It cannot be in the + * PNG_INTERNAL section. Needs modification for other compilers besides + * MSC. Model independent support declares all arrays and pointers to be + * large using the far keyword. The zlib version used must also support + * model independent data. As of version zlib 1.0.4, the necessary changes + * have been made in zlib. The USE_FAR_KEYWORD define triggers other + * changes that are needed. (Tim Wegner) + */ + +/* Separate compiler dependencies (problem here is that zlib.h always + defines FAR. (SJT) */ +#ifdef __BORLANDC__ +# if defined(__LARGE__) || defined(__HUGE__) || defined(__COMPACT__) +# define LDATA 1 +# else +# define LDATA 0 +# endif + /* GRR: why is Cygwin in here? Cygwin is not Borland C... */ +# if !defined(__WIN32__) && !defined(__FLAT__) && !defined(__CYGWIN__) +# define PNG_MAX_MALLOC_64K +# if (LDATA != 1) +# ifndef FAR +# define FAR __far +# endif +# define USE_FAR_KEYWORD +# endif /* LDATA != 1 */ + /* Possibly useful for moving data out of default segment. + * Uncomment it if you want. Could also define FARDATA as + * const if your compiler supports it. (SJT) +# define FARDATA FAR + */ +# endif /* __WIN32__, __FLAT__, __CYGWIN__ */ +#endif /* __BORLANDC__ */ + +/* Suggest testing for specific compiler first before testing for + * FAR. The Watcom compiler defines both __MEDIUM__ and M_I86MM, + * making reliance oncertain keywords suspect. (SJT) + */ + +/* MSC Medium model */ +#if defined(FAR) +# if defined(M_I86MM) +# define USE_FAR_KEYWORD +# define FARDATA FAR +# include +# endif +#endif + +/* SJT: default case */ +#ifndef FAR +# define FAR +#endif + +/* At this point FAR is always defined */ +#ifndef FARDATA +# define FARDATA +#endif + +/* Typedef for floating-point numbers that are converted + to fixed-point with a multiple of 100,000, e.g., int_gamma */ +typedef png_int_32 png_fixed_point; + +/* Add typedefs for pointers */ +typedef void FAR * png_voidp; +typedef png_byte FAR * png_bytep; +typedef png_uint_32 FAR * png_uint_32p; +typedef png_int_32 FAR * png_int_32p; +typedef png_uint_16 FAR * png_uint_16p; +typedef png_int_16 FAR * png_int_16p; +typedef PNG_CONST char FAR * png_const_charp; +typedef char FAR * png_charp; +typedef png_fixed_point FAR * png_fixed_point_p; + +#ifndef PNG_NO_STDIO +#if defined(_WIN32_WCE) +typedef HANDLE png_FILE_p; +#else +typedef FILE * png_FILE_p; +#endif +#endif + +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double FAR * png_doublep; +#endif + +/* Pointers to pointers; i.e. arrays */ +typedef png_byte FAR * FAR * png_bytepp; +typedef png_uint_32 FAR * FAR * png_uint_32pp; +typedef png_int_32 FAR * FAR * png_int_32pp; +typedef png_uint_16 FAR * FAR * png_uint_16pp; +typedef png_int_16 FAR * FAR * png_int_16pp; +typedef PNG_CONST char FAR * FAR * png_const_charpp; +typedef char FAR * FAR * png_charpp; +typedef png_fixed_point FAR * FAR * png_fixed_point_pp; +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double FAR * FAR * png_doublepp; +#endif + +/* Pointers to pointers to pointers; i.e., pointer to array */ +typedef char FAR * FAR * FAR * png_charppp; + +#if 0 +/* SPC - Is this stuff deprecated? */ +/* It'll be removed as of libpng-1.3.0 - GR-P */ +/* libpng typedefs for types in zlib. If zlib changes + * or another compression library is used, then change these. + * Eliminates need to change all the source files. + */ +typedef charf * png_zcharp; +typedef charf * FAR * png_zcharpp; +typedef z_stream FAR * png_zstreamp; +#endif /* (PNG_1_0_X) || defined(PNG_1_2_X) */ + +/* + * Define PNG_BUILD_DLL if the module being built is a Windows + * LIBPNG DLL. + * + * Define PNG_USE_DLL if you want to *link* to the Windows LIBPNG DLL. + * It is equivalent to Microsoft predefined macro _DLL that is + * automatically defined when you compile using the share + * version of the CRT (C Run-Time library) + * + * The cygwin mods make this behavior a little different: + * Define PNG_BUILD_DLL if you are building a dll for use with cygwin + * Define PNG_STATIC if you are building a static library for use with cygwin, + * -or- if you are building an application that you want to link to the + * static library. + * PNG_USE_DLL is defined by default (no user action needed) unless one of + * the other flags is defined. + */ + +#if !defined(PNG_DLL) && (defined(PNG_BUILD_DLL) || defined(PNG_USE_DLL)) +# define PNG_DLL +#endif +/* If CYGWIN, then disallow GLOBAL ARRAYS unless building a static lib. + * When building a static lib, default to no GLOBAL ARRAYS, but allow + * command-line override + */ +#if defined(__CYGWIN__) +# if !defined(PNG_STATIC) +# if defined(PNG_USE_GLOBAL_ARRAYS) +# undef PNG_USE_GLOBAL_ARRAYS +# endif +# if !defined(PNG_USE_LOCAL_ARRAYS) +# define PNG_USE_LOCAL_ARRAYS +# endif +# else +# if defined(PNG_USE_LOCAL_ARRAYS) || defined(PNG_NO_GLOBAL_ARRAYS) +# if defined(PNG_USE_GLOBAL_ARRAYS) +# undef PNG_USE_GLOBAL_ARRAYS +# endif +# endif +# endif +# if !defined(PNG_USE_LOCAL_ARRAYS) && !defined(PNG_USE_GLOBAL_ARRAYS) +# define PNG_USE_LOCAL_ARRAYS +# endif +#endif + +/* Do not use global arrays (helps with building DLL's) + * They are no longer used in libpng itself, since version 1.0.5c, + * but might be required for some pre-1.0.5c applications. + */ +#if !defined(PNG_USE_LOCAL_ARRAYS) && !defined(PNG_USE_GLOBAL_ARRAYS) +# if defined(PNG_NO_GLOBAL_ARRAYS) || \ + (defined(__GNUC__) && defined(PNG_DLL)) || defined(_MSC_VER) +# define PNG_USE_LOCAL_ARRAYS +# else +# define PNG_USE_GLOBAL_ARRAYS +# endif +#endif + +#if defined(__CYGWIN__) +# undef PNGAPI +# define PNGAPI __cdecl +# undef PNG_IMPEXP +# define PNG_IMPEXP +#endif + +/* If you define PNGAPI, e.g., with compiler option "-DPNGAPI=__stdcall", + * you may get warnings regarding the linkage of png_zalloc and png_zfree. + * Don't ignore those warnings; you must also reset the default calling + * convention in your compiler to match your PNGAPI, and you must build + * zlib and your applications the same way you build libpng. + */ + +#if defined(__MINGW32__) && !defined(PNG_MODULEDEF) +# ifndef PNG_NO_MODULEDEF +# define PNG_NO_MODULEDEF +# endif +#endif + +#if !defined(PNG_IMPEXP) && defined(PNG_BUILD_DLL) && !defined(PNG_NO_MODULEDEF) +# define PNG_IMPEXP +#endif + +#if defined(PNG_DLL) || defined(_DLL) || defined(__DLL__ ) || \ + (( defined(_Windows) || defined(_WINDOWS) || \ + defined(WIN32) || defined(_WIN32) || defined(__WIN32__) )) + +# ifndef PNGAPI +# if defined(__GNUC__) || (defined (_MSC_VER) && (_MSC_VER >= 800)) +# define PNGAPI __cdecl +# else +# define PNGAPI _cdecl +# endif +# endif + +# if !defined(PNG_IMPEXP) && (!defined(PNG_DLL) || \ + 0 /* WINCOMPILER_WITH_NO_SUPPORT_FOR_DECLIMPEXP */) +# define PNG_IMPEXP +# endif + +# if !defined(PNG_IMPEXP) + +# define PNG_EXPORT_TYPE1(type,symbol) PNG_IMPEXP type PNGAPI symbol +# define PNG_EXPORT_TYPE2(type,symbol) type PNG_IMPEXP PNGAPI symbol + + /* Borland/Microsoft */ +# if defined(_MSC_VER) || defined(__BORLANDC__) +# if (_MSC_VER >= 800) || (__BORLANDC__ >= 0x500) +# define PNG_EXPORT PNG_EXPORT_TYPE1 +# else +# define PNG_EXPORT PNG_EXPORT_TYPE2 +# if defined(PNG_BUILD_DLL) +# define PNG_IMPEXP __export +# else +# define PNG_IMPEXP /*__import */ /* doesn't exist AFAIK in + VC++ */ +# endif /* Exists in Borland C++ for + C++ classes (== huge) */ +# endif +# endif + +# if !defined(PNG_IMPEXP) +# if defined(PNG_BUILD_DLL) +# define PNG_IMPEXP __declspec(dllexport) +# else +# define PNG_IMPEXP __declspec(dllimport) +# endif +# endif +# endif /* PNG_IMPEXP */ +#else /* !(DLL || non-cygwin WINDOWS) */ +# if (defined(__IBMC__) || defined(__IBMCPP__)) && defined(__OS2__) +# ifndef PNGAPI +# define PNGAPI _System +# endif +# else +# if 0 /* ... other platforms, with other meanings */ +# endif +# endif +#endif + +#ifndef PNGAPI +# define PNGAPI +#endif +#ifndef PNG_IMPEXP +# define PNG_IMPEXP +#endif + +#ifdef PNG_BUILDSYMS +# ifndef PNG_EXPORT +# define PNG_EXPORT(type,symbol) PNG_FUNCTION_EXPORT symbol END +# endif +# ifdef PNG_USE_GLOBAL_ARRAYS +# ifndef PNG_EXPORT_VAR +# define PNG_EXPORT_VAR(type) PNG_DATA_EXPORT +# endif +# endif +#endif + +#ifndef PNG_EXPORT +# define PNG_EXPORT(type,symbol) PNG_IMPEXP type PNGAPI symbol +#endif + +#ifdef PNG_USE_GLOBAL_ARRAYS +# ifndef PNG_EXPORT_VAR +# define PNG_EXPORT_VAR(type) extern PNG_IMPEXP type +# endif +#endif + +/* User may want to use these so they are not in PNG_INTERNAL. Any library + * functions that are passed far data must be model independent. + */ + +#ifndef PNG_ABORT +# define PNG_ABORT() abort() +#endif + +#ifdef PNG_SETJMP_SUPPORTED +# define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf) +#else +# define png_jmpbuf(png_ptr) \ + (LIBPNG_WAS_COMPILED_WITH__PNG_SETJMP_NOT_SUPPORTED) +#endif + +#if defined(USE_FAR_KEYWORD) /* memory model independent fns */ +/* use this to make far-to-near assignments */ +# define CHECK 1 +# define NOCHECK 0 +# define CVT_PTR(ptr) (png_far_to_near(png_ptr,ptr,CHECK)) +# define CVT_PTR_NOCHECK(ptr) (png_far_to_near(png_ptr,ptr,NOCHECK)) +# define png_snprintf _fsnprintf /* Added to v 1.2.19 */ +# define png_strcpy _fstrcpy +# define png_strncpy _fstrncpy /* Added to v 1.2.6 */ +# define png_strlen _fstrlen +# define png_memcmp _fmemcmp /* SJT: added */ +# define png_memcpy _fmemcpy +# define png_memset _fmemset +#else /* use the usual functions */ +# define CVT_PTR(ptr) (ptr) +# define CVT_PTR_NOCHECK(ptr) (ptr) +# ifndef PNG_NO_SNPRINTF +# ifdef _MSC_VER +# define png_snprintf _snprintf /* Added to v 1.2.19 */ +# define png_snprintf2 _snprintf +# define png_snprintf6 _snprintf +# else +# define png_snprintf snprintf /* Added to v 1.2.19 */ +# define png_snprintf2 snprintf +# define png_snprintf6 snprintf +# endif +# else + /* You don't have or don't want to use snprintf(). Caution: Using + * sprintf instead of snprintf exposes your application to accidental + * or malevolent buffer overflows. If you don't have snprintf() + * as a general rule you should provide one (you can get one from + * Portable OpenSSH). */ +# define png_snprintf(s1,n,fmt,x1) sprintf(s1,fmt,x1) +# define png_snprintf2(s1,n,fmt,x1,x2) sprintf(s1,fmt,x1,x2) +# define png_snprintf6(s1,n,fmt,x1,x2,x3,x4,x5,x6) \ + sprintf(s1,fmt,x1,x2,x3,x4,x5,x6) +# endif +# define png_strcpy strcpy +# define png_strncpy strncpy /* Added to v 1.2.6 */ +# define png_strlen strlen +# define png_memcmp memcmp /* SJT: added */ +# define png_memcpy memcpy +# define png_memset memset +#endif +/* End of memory model independent support */ + +/* Just a little check that someone hasn't tried to define something + * contradictory. + */ +#if (PNG_ZBUF_SIZE > 65536L) && defined(PNG_MAX_MALLOC_64K) +# undef PNG_ZBUF_SIZE +# define PNG_ZBUF_SIZE 65536L +#endif + +/* Added at libpng-1.2.8 */ +#endif /* PNG_VERSION_INFO_ONLY */ + +#endif /* PNGCONF_H */ +/********* End of inlined file: pngconf.h *********/ + +#ifdef _MSC_VER + #pragma warning (disable: 4996 4100) +#endif + +/* + * Added at libpng-1.2.8 */ +/* Ref MSDN: Private as priority over Special + * VS_FF_PRIVATEBUILD File *was not* built using standard release + * procedures. If this value is given, the StringFileInfo block must + * contain a PrivateBuild string. + * + * VS_FF_SPECIALBUILD File *was* built by the original company using + * standard release procedures but is a variation of the standard + * file of the same version number. If this value is given, the + * StringFileInfo block must contain a SpecialBuild string. + */ + +#if defined(PNG_USER_PRIVATEBUILD) +# define PNG_LIBPNG_BUILD_TYPE \ + (PNG_LIBPNG_BUILD_BASE_TYPE | PNG_LIBPNG_BUILD_PRIVATE) +#else +# if defined(PNG_LIBPNG_SPECIALBUILD) +# define PNG_LIBPNG_BUILD_TYPE \ + (PNG_LIBPNG_BUILD_BASE_TYPE | PNG_LIBPNG_BUILD_SPECIAL) +# else +# define PNG_LIBPNG_BUILD_TYPE (PNG_LIBPNG_BUILD_BASE_TYPE) +# endif +#endif + +#ifndef PNG_VERSION_INFO_ONLY + +/* Inhibit C++ name-mangling for libpng functions but not for system calls. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* This file is arranged in several sections. The first section contains + * structure and type definitions. The second section contains the external + * library functions, while the third has the internal library functions, + * which applications aren't expected to use directly. + */ + +#ifndef PNG_NO_TYPECAST_NULL +#define int_p_NULL (int *)NULL +#define png_bytep_NULL (png_bytep)NULL +#define png_bytepp_NULL (png_bytepp)NULL +#define png_doublep_NULL (png_doublep)NULL +#define png_error_ptr_NULL (png_error_ptr)NULL +#define png_flush_ptr_NULL (png_flush_ptr)NULL +#define png_free_ptr_NULL (png_free_ptr)NULL +#define png_infopp_NULL (png_infopp)NULL +#define png_malloc_ptr_NULL (png_malloc_ptr)NULL +#define png_read_status_ptr_NULL (png_read_status_ptr)NULL +#define png_rw_ptr_NULL (png_rw_ptr)NULL +#define png_structp_NULL (png_structp)NULL +#define png_uint_16p_NULL (png_uint_16p)NULL +#define png_voidp_NULL (png_voidp)NULL +#define png_write_status_ptr_NULL (png_write_status_ptr)NULL +#else +#define int_p_NULL NULL +#define png_bytep_NULL NULL +#define png_bytepp_NULL NULL +#define png_doublep_NULL NULL +#define png_error_ptr_NULL NULL +#define png_flush_ptr_NULL NULL +#define png_free_ptr_NULL NULL +#define png_infopp_NULL NULL +#define png_malloc_ptr_NULL NULL +#define png_read_status_ptr_NULL NULL +#define png_rw_ptr_NULL NULL +#define png_structp_NULL NULL +#define png_uint_16p_NULL NULL +#define png_voidp_NULL NULL +#define png_write_status_ptr_NULL NULL +#endif + +/* variables declared in png.c - only it needs to define PNG_NO_EXTERN */ +#if !defined(PNG_NO_EXTERN) || defined(PNG_ALWAYS_EXTERN) +/* Version information for C files, stored in png.c. This had better match + * the version above. + */ +#ifdef PNG_USE_GLOBAL_ARRAYS +PNG_EXPORT_VAR (PNG_CONST char) png_libpng_ver[18]; + /* need room for 99.99.99beta99z */ +#else +#define png_libpng_ver png_get_header_ver(NULL) +#endif + +#ifdef PNG_USE_GLOBAL_ARRAYS +/* This was removed in version 1.0.5c */ +/* Structures to facilitate easy interlacing. See png.c for more details */ +PNG_EXPORT_VAR (PNG_CONST int FARDATA) png_pass_start[7]; +PNG_EXPORT_VAR (PNG_CONST int FARDATA) png_pass_inc[7]; +PNG_EXPORT_VAR (PNG_CONST int FARDATA) png_pass_ystart[7]; +PNG_EXPORT_VAR (PNG_CONST int FARDATA) png_pass_yinc[7]; +PNG_EXPORT_VAR (PNG_CONST int FARDATA) png_pass_mask[7]; +PNG_EXPORT_VAR (PNG_CONST int FARDATA) png_pass_dsp_mask[7]; +/* This isn't currently used. If you need it, see png.c for more details. +PNG_EXPORT_VAR (PNG_CONST int FARDATA) png_pass_height[7]; +*/ +#endif + +#endif /* PNG_NO_EXTERN */ + +/* Three color definitions. The order of the red, green, and blue, (and the + * exact size) is not important, although the size of the fields need to + * be png_byte or png_uint_16 (as defined below). + */ +typedef struct png_color_struct +{ + png_byte red; + png_byte green; + png_byte blue; +} png_color; +typedef png_color FAR * png_colorp; +typedef png_color FAR * FAR * png_colorpp; + +typedef struct png_color_16_struct +{ + png_byte index; /* used for palette files */ + png_uint_16 red; /* for use in red green blue files */ + png_uint_16 green; + png_uint_16 blue; + png_uint_16 gray; /* for use in grayscale files */ +} png_color_16; +typedef png_color_16 FAR * png_color_16p; +typedef png_color_16 FAR * FAR * png_color_16pp; + +typedef struct png_color_8_struct +{ + png_byte red; /* for use in red green blue files */ + png_byte green; + png_byte blue; + png_byte gray; /* for use in grayscale files */ + png_byte alpha; /* for alpha channel files */ +} png_color_8; +typedef png_color_8 FAR * png_color_8p; +typedef png_color_8 FAR * FAR * png_color_8pp; + +/* + * The following two structures are used for the in-core representation + * of sPLT chunks. + */ +typedef struct png_sPLT_entry_struct +{ + png_uint_16 red; + png_uint_16 green; + png_uint_16 blue; + png_uint_16 alpha; + png_uint_16 frequency; +} png_sPLT_entry; +typedef png_sPLT_entry FAR * png_sPLT_entryp; +typedef png_sPLT_entry FAR * FAR * png_sPLT_entrypp; + +/* When the depth of the sPLT palette is 8 bits, the color and alpha samples + * occupy the LSB of their respective members, and the MSB of each member + * is zero-filled. The frequency member always occupies the full 16 bits. + */ + +typedef struct png_sPLT_struct +{ + png_charp name; /* palette name */ + png_byte depth; /* depth of palette samples */ + png_sPLT_entryp entries; /* palette entries */ + png_int_32 nentries; /* number of palette entries */ +} png_sPLT_t; +typedef png_sPLT_t FAR * png_sPLT_tp; +typedef png_sPLT_t FAR * FAR * png_sPLT_tpp; + +#ifdef PNG_TEXT_SUPPORTED +/* png_text holds the contents of a text/ztxt/itxt chunk in a PNG file, + * and whether that contents is compressed or not. The "key" field + * points to a regular zero-terminated C string. The "text", "lang", and + * "lang_key" fields can be regular C strings, empty strings, or NULL pointers. + * However, the * structure returned by png_get_text() will always contain + * regular zero-terminated C strings (possibly empty), never NULL pointers, + * so they can be safely used in printf() and other string-handling functions. + */ +typedef struct png_text_struct +{ + int compression; /* compression value: + -1: tEXt, none + 0: zTXt, deflate + 1: iTXt, none + 2: iTXt, deflate */ + png_charp key; /* keyword, 1-79 character description of "text" */ + png_charp text; /* comment, may be an empty string (ie "") + or a NULL pointer */ + png_size_t text_length; /* length of the text string */ +#ifdef PNG_iTXt_SUPPORTED + png_size_t itxt_length; /* length of the itxt string */ + png_charp lang; /* language code, 0-79 characters + or a NULL pointer */ + png_charp lang_key; /* keyword translated UTF-8 string, 0 or more + chars or a NULL pointer */ +#endif +} png_text; +typedef png_text FAR * png_textp; +typedef png_text FAR * FAR * png_textpp; +#endif + +/* Supported compression types for text in PNG files (tEXt, and zTXt). + * The values of the PNG_TEXT_COMPRESSION_ defines should NOT be changed. */ +#define PNG_TEXT_COMPRESSION_NONE_WR -3 +#define PNG_TEXT_COMPRESSION_zTXt_WR -2 +#define PNG_TEXT_COMPRESSION_NONE -1 +#define PNG_TEXT_COMPRESSION_zTXt 0 +#define PNG_ITXT_COMPRESSION_NONE 1 +#define PNG_ITXT_COMPRESSION_zTXt 2 +#define PNG_TEXT_COMPRESSION_LAST 3 /* Not a valid value */ + +/* png_time is a way to hold the time in an machine independent way. + * Two conversions are provided, both from time_t and struct tm. There + * is no portable way to convert to either of these structures, as far + * as I know. If you know of a portable way, send it to me. As a side + * note - PNG has always been Year 2000 compliant! + */ +typedef struct png_time_struct +{ + png_uint_16 year; /* full year, as in, 1995 */ + png_byte month; /* month of year, 1 - 12 */ + png_byte day; /* day of month, 1 - 31 */ + png_byte hour; /* hour of day, 0 - 23 */ + png_byte minute; /* minute of hour, 0 - 59 */ + png_byte second; /* second of minute, 0 - 60 (for leap seconds) */ +} png_time; +typedef png_time FAR * png_timep; +typedef png_time FAR * FAR * png_timepp; + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) +/* png_unknown_chunk is a structure to hold queued chunks for which there is + * no specific support. The idea is that we can use this to queue + * up private chunks for output even though the library doesn't actually + * know about their semantics. + */ +typedef struct png_unknown_chunk_t +{ + png_byte name[5]; + png_byte *data; + png_size_t size; + + /* libpng-using applications should NOT directly modify this byte. */ + png_byte location; /* mode of operation at read time */ +} +png_unknown_chunk; +typedef png_unknown_chunk FAR * png_unknown_chunkp; +typedef png_unknown_chunk FAR * FAR * png_unknown_chunkpp; +#endif + +/* png_info is a structure that holds the information in a PNG file so + * that the application can find out the characteristics of the image. + * If you are reading the file, this structure will tell you what is + * in the PNG file. If you are writing the file, fill in the information + * you want to put into the PNG file, then call png_write_info(). + * The names chosen should be very close to the PNG specification, so + * consult that document for information about the meaning of each field. + * + * With libpng < 0.95, it was only possible to directly set and read the + * the values in the png_info_struct, which meant that the contents and + * order of the values had to remain fixed. With libpng 0.95 and later, + * however, there are now functions that abstract the contents of + * png_info_struct from the application, so this makes it easier to use + * libpng with dynamic libraries, and even makes it possible to use + * libraries that don't have all of the libpng ancillary chunk-handing + * functionality. + * + * In any case, the order of the parameters in png_info_struct should NOT + * be changed for as long as possible to keep compatibility with applications + * that use the old direct-access method with png_info_struct. + * + * The following members may have allocated storage attached that should be + * cleaned up before the structure is discarded: palette, trans, text, + * pcal_purpose, pcal_units, pcal_params, hist, iccp_name, iccp_profile, + * splt_palettes, scal_unit, row_pointers, and unknowns. By default, these + * are automatically freed when the info structure is deallocated, if they were + * allocated internally by libpng. This behavior can be changed by means + * of the png_data_freer() function. + * + * More allocation details: all the chunk-reading functions that + * change these members go through the corresponding png_set_* + * functions. A function to clear these members is available: see + * png_free_data(). The png_set_* functions do not depend on being + * able to point info structure members to any of the storage they are + * passed (they make their own copies), EXCEPT that the png_set_text + * functions use the same storage passed to them in the text_ptr or + * itxt_ptr structure argument, and the png_set_rows and png_set_unknowns + * functions do not make their own copies. + */ +typedef struct png_info_struct +{ + /* the following are necessary for every PNG file */ + png_uint_32 width; /* width of image in pixels (from IHDR) */ + png_uint_32 height; /* height of image in pixels (from IHDR) */ + png_uint_32 valid; /* valid chunk data (see PNG_INFO_ below) */ + png_uint_32 rowbytes; /* bytes needed to hold an untransformed row */ + png_colorp palette; /* array of color values (valid & PNG_INFO_PLTE) */ + png_uint_16 num_palette; /* number of color entries in "palette" (PLTE) */ + png_uint_16 num_trans; /* number of transparent palette color (tRNS) */ + png_byte bit_depth; /* 1, 2, 4, 8, or 16 bits/channel (from IHDR) */ + png_byte color_type; /* see PNG_COLOR_TYPE_ below (from IHDR) */ + /* The following three should have been named *_method not *_type */ + png_byte compression_type; /* must be PNG_COMPRESSION_TYPE_BASE (IHDR) */ + png_byte filter_type; /* must be PNG_FILTER_TYPE_BASE (from IHDR) */ + png_byte interlace_type; /* One of PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */ + + /* The following is informational only on read, and not used on writes. */ + png_byte channels; /* number of data channels per pixel (1, 2, 3, 4) */ + png_byte pixel_depth; /* number of bits per pixel */ + png_byte spare_byte; /* to align the data, and for future use */ + png_byte signature[8]; /* magic bytes read by libpng from start of file */ + + /* The rest of the data is optional. If you are reading, check the + * valid field to see if the information in these are valid. If you + * are writing, set the valid field to those chunks you want written, + * and initialize the appropriate fields below. + */ + +#if defined(PNG_gAMA_SUPPORTED) && defined(PNG_FLOATING_POINT_SUPPORTED) + /* The gAMA chunk describes the gamma characteristics of the system + * on which the image was created, normally in the range [1.0, 2.5]. + * Data is valid if (valid & PNG_INFO_gAMA) is non-zero. + */ + float gamma; /* gamma value of image, if (valid & PNG_INFO_gAMA) */ +#endif + +#if defined(PNG_sRGB_SUPPORTED) + /* GR-P, 0.96a */ + /* Data valid if (valid & PNG_INFO_sRGB) non-zero. */ + png_byte srgb_intent; /* sRGB rendering intent [0, 1, 2, or 3] */ +#endif + +#if defined(PNG_TEXT_SUPPORTED) + /* The tEXt, and zTXt chunks contain human-readable textual data in + * uncompressed, compressed, and optionally compressed forms, respectively. + * The data in "text" is an array of pointers to uncompressed, + * null-terminated C strings. Each chunk has a keyword that describes the + * textual data contained in that chunk. Keywords are not required to be + * unique, and the text string may be empty. Any number of text chunks may + * be in an image. + */ + int num_text; /* number of comments read/to write */ + int max_text; /* current size of text array */ + png_textp text; /* array of comments read/to write */ +#endif /* PNG_TEXT_SUPPORTED */ + +#if defined(PNG_tIME_SUPPORTED) + /* The tIME chunk holds the last time the displayed image data was + * modified. See the png_time struct for the contents of this struct. + */ + png_time mod_time; +#endif + +#if defined(PNG_sBIT_SUPPORTED) + /* The sBIT chunk specifies the number of significant high-order bits + * in the pixel data. Values are in the range [1, bit_depth], and are + * only specified for the channels in the pixel data. The contents of + * the low-order bits is not specified. Data is valid if + * (valid & PNG_INFO_sBIT) is non-zero. + */ + png_color_8 sig_bit; /* significant bits in color channels */ +#endif + +#if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_EXPAND_SUPPORTED) || \ +defined(PNG_READ_BACKGROUND_SUPPORTED) + /* The tRNS chunk supplies transparency data for paletted images and + * other image types that don't need a full alpha channel. There are + * "num_trans" transparency values for a paletted image, stored in the + * same order as the palette colors, starting from index 0. Values + * for the data are in the range [0, 255], ranging from fully transparent + * to fully opaque, respectively. For non-paletted images, there is a + * single color specified that should be treated as fully transparent. + * Data is valid if (valid & PNG_INFO_tRNS) is non-zero. + */ + png_bytep trans; /* transparent values for paletted image */ + png_color_16 trans_values; /* transparent color for non-palette image */ +#endif + +#if defined(PNG_bKGD_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + /* The bKGD chunk gives the suggested image background color if the + * display program does not have its own background color and the image + * is needs to composited onto a background before display. The colors + * in "background" are normally in the same color space/depth as the + * pixel data. Data is valid if (valid & PNG_INFO_bKGD) is non-zero. + */ + png_color_16 background; +#endif + +#if defined(PNG_oFFs_SUPPORTED) + /* The oFFs chunk gives the offset in "offset_unit_type" units rightwards + * and downwards from the top-left corner of the display, page, or other + * application-specific co-ordinate space. See the PNG_OFFSET_ defines + * below for the unit types. Valid if (valid & PNG_INFO_oFFs) non-zero. + */ + png_int_32 x_offset; /* x offset on page */ + png_int_32 y_offset; /* y offset on page */ + png_byte offset_unit_type; /* offset units type */ +#endif + +#if defined(PNG_pHYs_SUPPORTED) + /* The pHYs chunk gives the physical pixel density of the image for + * display or printing in "phys_unit_type" units (see PNG_RESOLUTION_ + * defines below). Data is valid if (valid & PNG_INFO_pHYs) is non-zero. + */ + png_uint_32 x_pixels_per_unit; /* horizontal pixel density */ + png_uint_32 y_pixels_per_unit; /* vertical pixel density */ + png_byte phys_unit_type; /* resolution type (see PNG_RESOLUTION_ below) */ +#endif + +#if defined(PNG_hIST_SUPPORTED) + /* The hIST chunk contains the relative frequency or importance of the + * various palette entries, so that a viewer can intelligently select a + * reduced-color palette, if required. Data is an array of "num_palette" + * values in the range [0,65535]. Data valid if (valid & PNG_INFO_hIST) + * is non-zero. + */ + png_uint_16p hist; +#endif + +#ifdef PNG_cHRM_SUPPORTED + /* The cHRM chunk describes the CIE color characteristics of the monitor + * on which the PNG was created. This data allows the viewer to do gamut + * mapping of the input image to ensure that the viewer sees the same + * colors in the image as the creator. Values are in the range + * [0.0, 0.8]. Data valid if (valid & PNG_INFO_cHRM) non-zero. + */ +#ifdef PNG_FLOATING_POINT_SUPPORTED + float x_white; + float y_white; + float x_red; + float y_red; + float x_green; + float y_green; + float x_blue; + float y_blue; +#endif +#endif + +#if defined(PNG_pCAL_SUPPORTED) + /* The pCAL chunk describes a transformation between the stored pixel + * values and original physical data values used to create the image. + * The integer range [0, 2^bit_depth - 1] maps to the floating-point + * range given by [pcal_X0, pcal_X1], and are further transformed by a + * (possibly non-linear) transformation function given by "pcal_type" + * and "pcal_params" into "pcal_units". Please see the PNG_EQUATION_ + * defines below, and the PNG-Group's PNG extensions document for a + * complete description of the transformations and how they should be + * implemented, and for a description of the ASCII parameter strings. + * Data values are valid if (valid & PNG_INFO_pCAL) non-zero. + */ + png_charp pcal_purpose; /* pCAL chunk description string */ + png_int_32 pcal_X0; /* minimum value */ + png_int_32 pcal_X1; /* maximum value */ + png_charp pcal_units; /* Latin-1 string giving physical units */ + png_charpp pcal_params; /* ASCII strings containing parameter values */ + png_byte pcal_type; /* equation type (see PNG_EQUATION_ below) */ + png_byte pcal_nparams; /* number of parameters given in pcal_params */ +#endif + +/* New members added in libpng-1.0.6 */ +#ifdef PNG_FREE_ME_SUPPORTED + png_uint_32 free_me; /* flags items libpng is responsible for freeing */ +#endif + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) + /* storage for unknown chunks that the library doesn't recognize. */ + png_unknown_chunkp unknown_chunks; + png_size_t unknown_chunks_num; +#endif + +#if defined(PNG_iCCP_SUPPORTED) + /* iCCP chunk data. */ + png_charp iccp_name; /* profile name */ + png_charp iccp_profile; /* International Color Consortium profile data */ + /* Note to maintainer: should be png_bytep */ + png_uint_32 iccp_proflen; /* ICC profile data length */ + png_byte iccp_compression; /* Always zero */ +#endif + +#if defined(PNG_sPLT_SUPPORTED) + /* data on sPLT chunks (there may be more than one). */ + png_sPLT_tp splt_palettes; + png_uint_32 splt_palettes_num; +#endif + +#if defined(PNG_sCAL_SUPPORTED) + /* The sCAL chunk describes the actual physical dimensions of the + * subject matter of the graphic. The chunk contains a unit specification + * a byte value, and two ASCII strings representing floating-point + * values. The values are width and height corresponsing to one pixel + * in the image. This external representation is converted to double + * here. Data values are valid if (valid & PNG_INFO_sCAL) is non-zero. + */ + png_byte scal_unit; /* unit of physical scale */ +#ifdef PNG_FLOATING_POINT_SUPPORTED + double scal_pixel_width; /* width of one pixel */ + double scal_pixel_height; /* height of one pixel */ +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + png_charp scal_s_width; /* string containing height */ + png_charp scal_s_height; /* string containing width */ +#endif +#endif + +#if defined(PNG_INFO_IMAGE_SUPPORTED) + /* Memory has been allocated if (valid & PNG_ALLOCATED_INFO_ROWS) non-zero */ + /* Data valid if (valid & PNG_INFO_IDAT) non-zero */ + png_bytepp row_pointers; /* the image bits */ +#endif + +#if defined(PNG_FIXED_POINT_SUPPORTED) && defined(PNG_gAMA_SUPPORTED) + png_fixed_point int_gamma; /* gamma of image, if (valid & PNG_INFO_gAMA) */ +#endif + +#if defined(PNG_cHRM_SUPPORTED) && defined(PNG_FIXED_POINT_SUPPORTED) + png_fixed_point int_x_white; + png_fixed_point int_y_white; + png_fixed_point int_x_red; + png_fixed_point int_y_red; + png_fixed_point int_x_green; + png_fixed_point int_y_green; + png_fixed_point int_x_blue; + png_fixed_point int_y_blue; +#endif + +} png_info; + +typedef png_info FAR * png_infop; +typedef png_info FAR * FAR * png_infopp; + +/* Maximum positive integer used in PNG is (2^31)-1 */ +#define PNG_UINT_31_MAX ((png_uint_32)0x7fffffffL) +#define PNG_UINT_32_MAX ((png_uint_32)(-1)) +#define PNG_SIZE_MAX ((png_size_t)(-1)) +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* PNG_MAX_UINT is deprecated; use PNG_UINT_31_MAX instead. */ +#define PNG_MAX_UINT PNG_UINT_31_MAX +#endif + +/* These describe the color_type field in png_info. */ +/* color type masks */ +#define PNG_COLOR_MASK_PALETTE 1 +#define PNG_COLOR_MASK_COLOR 2 +#define PNG_COLOR_MASK_ALPHA 4 + +/* color types. Note that not all combinations are legal */ +#define PNG_COLOR_TYPE_GRAY 0 +#define PNG_COLOR_TYPE_PALETTE (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE) +#define PNG_COLOR_TYPE_RGB (PNG_COLOR_MASK_COLOR) +#define PNG_COLOR_TYPE_RGB_ALPHA (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_ALPHA) +#define PNG_COLOR_TYPE_GRAY_ALPHA (PNG_COLOR_MASK_ALPHA) +/* aliases */ +#define PNG_COLOR_TYPE_RGBA PNG_COLOR_TYPE_RGB_ALPHA +#define PNG_COLOR_TYPE_GA PNG_COLOR_TYPE_GRAY_ALPHA + +/* This is for compression type. PNG 1.0-1.2 only define the single type. */ +#define PNG_COMPRESSION_TYPE_BASE 0 /* Deflate method 8, 32K window */ +#define PNG_COMPRESSION_TYPE_DEFAULT PNG_COMPRESSION_TYPE_BASE + +/* This is for filter type. PNG 1.0-1.2 only define the single type. */ +#define PNG_FILTER_TYPE_BASE 0 /* Single row per-byte filtering */ +#define PNG_INTRAPIXEL_DIFFERENCING 64 /* Used only in MNG datastreams */ +#define PNG_FILTER_TYPE_DEFAULT PNG_FILTER_TYPE_BASE + +/* These are for the interlacing type. These values should NOT be changed. */ +#define PNG_INTERLACE_NONE 0 /* Non-interlaced image */ +#define PNG_INTERLACE_ADAM7 1 /* Adam7 interlacing */ +#define PNG_INTERLACE_LAST 2 /* Not a valid value */ + +/* These are for the oFFs chunk. These values should NOT be changed. */ +#define PNG_OFFSET_PIXEL 0 /* Offset in pixels */ +#define PNG_OFFSET_MICROMETER 1 /* Offset in micrometers (1/10^6 meter) */ +#define PNG_OFFSET_LAST 2 /* Not a valid value */ + +/* These are for the pCAL chunk. These values should NOT be changed. */ +#define PNG_EQUATION_LINEAR 0 /* Linear transformation */ +#define PNG_EQUATION_BASE_E 1 /* Exponential base e transform */ +#define PNG_EQUATION_ARBITRARY 2 /* Arbitrary base exponential transform */ +#define PNG_EQUATION_HYPERBOLIC 3 /* Hyperbolic sine transformation */ +#define PNG_EQUATION_LAST 4 /* Not a valid value */ + +/* These are for the sCAL chunk. These values should NOT be changed. */ +#define PNG_SCALE_UNKNOWN 0 /* unknown unit (image scale) */ +#define PNG_SCALE_METER 1 /* meters per pixel */ +#define PNG_SCALE_RADIAN 2 /* radians per pixel */ +#define PNG_SCALE_LAST 3 /* Not a valid value */ + +/* These are for the pHYs chunk. These values should NOT be changed. */ +#define PNG_RESOLUTION_UNKNOWN 0 /* pixels/unknown unit (aspect ratio) */ +#define PNG_RESOLUTION_METER 1 /* pixels/meter */ +#define PNG_RESOLUTION_LAST 2 /* Not a valid value */ + +/* These are for the sRGB chunk. These values should NOT be changed. */ +#define PNG_sRGB_INTENT_PERCEPTUAL 0 +#define PNG_sRGB_INTENT_RELATIVE 1 +#define PNG_sRGB_INTENT_SATURATION 2 +#define PNG_sRGB_INTENT_ABSOLUTE 3 +#define PNG_sRGB_INTENT_LAST 4 /* Not a valid value */ + +/* This is for text chunks */ +#define PNG_KEYWORD_MAX_LENGTH 79 + +/* Maximum number of entries in PLTE/sPLT/tRNS arrays */ +#define PNG_MAX_PALETTE_LENGTH 256 + +/* These determine if an ancillary chunk's data has been successfully read + * from the PNG header, or if the application has filled in the corresponding + * data in the info_struct to be written into the output file. The values + * of the PNG_INFO_ defines should NOT be changed. + */ +#define PNG_INFO_gAMA 0x0001 +#define PNG_INFO_sBIT 0x0002 +#define PNG_INFO_cHRM 0x0004 +#define PNG_INFO_PLTE 0x0008 +#define PNG_INFO_tRNS 0x0010 +#define PNG_INFO_bKGD 0x0020 +#define PNG_INFO_hIST 0x0040 +#define PNG_INFO_pHYs 0x0080 +#define PNG_INFO_oFFs 0x0100 +#define PNG_INFO_tIME 0x0200 +#define PNG_INFO_pCAL 0x0400 +#define PNG_INFO_sRGB 0x0800 /* GR-P, 0.96a */ +#define PNG_INFO_iCCP 0x1000 /* ESR, 1.0.6 */ +#define PNG_INFO_sPLT 0x2000 /* ESR, 1.0.6 */ +#define PNG_INFO_sCAL 0x4000 /* ESR, 1.0.6 */ +#define PNG_INFO_IDAT 0x8000L /* ESR, 1.0.6 */ + +/* This is used for the transformation routines, as some of them + * change these values for the row. It also should enable using + * the routines for other purposes. + */ +typedef struct png_row_info_struct +{ + png_uint_32 width; /* width of row */ + png_uint_32 rowbytes; /* number of bytes in row */ + png_byte color_type; /* color type of row */ + png_byte bit_depth; /* bit depth of row */ + png_byte channels; /* number of channels (1, 2, 3, or 4) */ + png_byte pixel_depth; /* bits per pixel (depth * channels) */ +} png_row_info; + +typedef png_row_info FAR * png_row_infop; +typedef png_row_info FAR * FAR * png_row_infopp; + +/* These are the function types for the I/O functions and for the functions + * that allow the user to override the default I/O functions with his or her + * own. The png_error_ptr type should match that of user-supplied warning + * and error functions, while the png_rw_ptr type should match that of the + * user read/write data functions. + */ +typedef struct png_struct_def png_struct; +typedef png_struct FAR * png_structp; + +typedef void (PNGAPI *png_error_ptr) PNGARG((png_structp, png_const_charp)); +typedef void (PNGAPI *png_rw_ptr) PNGARG((png_structp, png_bytep, png_size_t)); +typedef void (PNGAPI *png_flush_ptr) PNGARG((png_structp)); +typedef void (PNGAPI *png_read_status_ptr) PNGARG((png_structp, png_uint_32, + int)); +typedef void (PNGAPI *png_write_status_ptr) PNGARG((png_structp, png_uint_32, + int)); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +typedef void (PNGAPI *png_progressive_info_ptr) PNGARG((png_structp, png_infop)); +typedef void (PNGAPI *png_progressive_end_ptr) PNGARG((png_structp, png_infop)); +typedef void (PNGAPI *png_progressive_row_ptr) PNGARG((png_structp, png_bytep, + png_uint_32, int)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +typedef void (PNGAPI *png_user_transform_ptr) PNGARG((png_structp, + png_row_infop, png_bytep)); +#endif + +#if defined(PNG_USER_CHUNKS_SUPPORTED) +typedef int (PNGAPI *png_user_chunk_ptr) PNGARG((png_structp, png_unknown_chunkp)); +#endif +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) +typedef void (PNGAPI *png_unknown_chunk_ptr) PNGARG((png_structp)); +#endif + +/* Transform masks for the high-level interface */ +#define PNG_TRANSFORM_IDENTITY 0x0000 /* read and write */ +#define PNG_TRANSFORM_STRIP_16 0x0001 /* read only */ +#define PNG_TRANSFORM_STRIP_ALPHA 0x0002 /* read only */ +#define PNG_TRANSFORM_PACKING 0x0004 /* read and write */ +#define PNG_TRANSFORM_PACKSWAP 0x0008 /* read and write */ +#define PNG_TRANSFORM_EXPAND 0x0010 /* read only */ +#define PNG_TRANSFORM_INVERT_MONO 0x0020 /* read and write */ +#define PNG_TRANSFORM_SHIFT 0x0040 /* read and write */ +#define PNG_TRANSFORM_BGR 0x0080 /* read and write */ +#define PNG_TRANSFORM_SWAP_ALPHA 0x0100 /* read and write */ +#define PNG_TRANSFORM_SWAP_ENDIAN 0x0200 /* read and write */ +#define PNG_TRANSFORM_INVERT_ALPHA 0x0400 /* read and write */ +#define PNG_TRANSFORM_STRIP_FILLER 0x0800 /* WRITE only */ + +/* Flags for MNG supported features */ +#define PNG_FLAG_MNG_EMPTY_PLTE 0x01 +#define PNG_FLAG_MNG_FILTER_64 0x04 +#define PNG_ALL_MNG_FEATURES 0x05 + +typedef png_voidp (*png_malloc_ptr) PNGARG((png_structp, png_size_t)); +typedef void (*png_free_ptr) PNGARG((png_structp, png_voidp)); + +/* The structure that holds the information to read and write PNG files. + * The only people who need to care about what is inside of this are the + * people who will be modifying the library for their own special needs. + * It should NOT be accessed directly by an application, except to store + * the jmp_buf. + */ + +struct png_struct_def +{ +#ifdef PNG_SETJMP_SUPPORTED + jmp_buf jmpbuf; /* used in png_error */ +#endif + png_error_ptr error_fn; /* function for printing errors and aborting */ + png_error_ptr warning_fn; /* function for printing warnings */ + png_voidp error_ptr; /* user supplied struct for error functions */ + png_rw_ptr write_data_fn; /* function for writing output data */ + png_rw_ptr read_data_fn; /* function for reading input data */ + png_voidp io_ptr; /* ptr to application struct for I/O functions */ + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) + png_user_transform_ptr read_user_transform_fn; /* user read transform */ +#endif + +#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) + png_user_transform_ptr write_user_transform_fn; /* user write transform */ +#endif + +/* These were added in libpng-1.0.2 */ +#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) + png_voidp user_transform_ptr; /* user supplied struct for user transform */ + png_byte user_transform_depth; /* bit depth of user transformed pixels */ + png_byte user_transform_channels; /* channels in user transformed pixels */ +#endif +#endif + + png_uint_32 mode; /* tells us where we are in the PNG file */ + png_uint_32 flags; /* flags indicating various things to libpng */ + png_uint_32 transformations; /* which transformations to perform */ + + z_stream zstream; /* pointer to decompression structure (below) */ + png_bytep zbuf; /* buffer for zlib */ + png_size_t zbuf_size; /* size of zbuf */ + int zlib_level; /* holds zlib compression level */ + int zlib_method; /* holds zlib compression method */ + int zlib_window_bits; /* holds zlib compression window bits */ + int zlib_mem_level; /* holds zlib compression memory level */ + int zlib_strategy; /* holds zlib compression strategy */ + + png_uint_32 width; /* width of image in pixels */ + png_uint_32 height; /* height of image in pixels */ + png_uint_32 num_rows; /* number of rows in current pass */ + png_uint_32 usr_width; /* width of row at start of write */ + png_uint_32 rowbytes; /* size of row in bytes */ + png_uint_32 irowbytes; /* size of current interlaced row in bytes */ + png_uint_32 iwidth; /* width of current interlaced row in pixels */ + png_uint_32 row_number; /* current row in interlace pass */ + png_bytep prev_row; /* buffer to save previous (unfiltered) row */ + png_bytep row_buf; /* buffer to save current (unfiltered) row */ + png_bytep sub_row; /* buffer to save "sub" row when filtering */ + png_bytep up_row; /* buffer to save "up" row when filtering */ + png_bytep avg_row; /* buffer to save "avg" row when filtering */ + png_bytep paeth_row; /* buffer to save "Paeth" row when filtering */ + png_row_info row_info; /* used for transformation routines */ + + png_uint_32 idat_size; /* current IDAT size for read */ + png_uint_32 crc; /* current chunk CRC value */ + png_colorp palette; /* palette from the input file */ + png_uint_16 num_palette; /* number of color entries in palette */ + png_uint_16 num_trans; /* number of transparency values */ + png_byte chunk_name[5]; /* null-terminated name of current chunk */ + png_byte compression; /* file compression type (always 0) */ + png_byte filter; /* file filter type (always 0) */ + png_byte interlaced; /* PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */ + png_byte pass; /* current interlace pass (0 - 6) */ + png_byte do_filter; /* row filter flags (see PNG_FILTER_ below ) */ + png_byte color_type; /* color type of file */ + png_byte bit_depth; /* bit depth of file */ + png_byte usr_bit_depth; /* bit depth of users row */ + png_byte pixel_depth; /* number of bits per pixel */ + png_byte channels; /* number of channels in file */ + png_byte usr_channels; /* channels at start of write */ + png_byte sig_bytes; /* magic bytes read/written from start of file */ + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +#ifdef PNG_LEGACY_SUPPORTED + png_byte filler; /* filler byte for pixel expansion */ +#else + png_uint_16 filler; /* filler bytes for pixel expansion */ +#endif +#endif + +#if defined(PNG_bKGD_SUPPORTED) + png_byte background_gamma_type; +# ifdef PNG_FLOATING_POINT_SUPPORTED + float background_gamma; +# endif + png_color_16 background; /* background color in screen gamma space */ +#if defined(PNG_READ_GAMMA_SUPPORTED) + png_color_16 background_1; /* background normalized to gamma 1.0 */ +#endif +#endif /* PNG_bKGD_SUPPORTED */ + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) + png_flush_ptr output_flush_fn;/* Function for flushing output */ + png_uint_32 flush_dist; /* how many rows apart to flush, 0 - no flush */ + png_uint_32 flush_rows; /* number of rows written since last flush */ +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + int gamma_shift; /* number of "insignificant" bits 16-bit gamma */ +#ifdef PNG_FLOATING_POINT_SUPPORTED + float gamma; /* file gamma value */ + float screen_gamma; /* screen gamma value (display_exponent) */ +#endif +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + png_bytep gamma_table; /* gamma table for 8-bit depth files */ + png_bytep gamma_from_1; /* converts from 1.0 to screen */ + png_bytep gamma_to_1; /* converts from file to 1.0 */ + png_uint_16pp gamma_16_table; /* gamma table for 16-bit depth files */ + png_uint_16pp gamma_16_from_1; /* converts from 1.0 to screen */ + png_uint_16pp gamma_16_to_1; /* converts from file to 1.0 */ +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_sBIT_SUPPORTED) + png_color_8 sig_bit; /* significant bits in each available channel */ +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) + png_color_8 shift; /* shift for significant bit tranformation */ +#endif + +#if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) \ + || defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + png_bytep trans; /* transparency values for paletted files */ + png_color_16 trans_values; /* transparency values for non-paletted files */ +#endif + + png_read_status_ptr read_row_fn; /* called after each row is decoded */ + png_write_status_ptr write_row_fn; /* called after each row is encoded */ +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED + png_progressive_info_ptr info_fn; /* called after header data fully read */ + png_progressive_row_ptr row_fn; /* called after each prog. row is decoded */ + png_progressive_end_ptr end_fn; /* called after image is complete */ + png_bytep save_buffer_ptr; /* current location in save_buffer */ + png_bytep save_buffer; /* buffer for previously read data */ + png_bytep current_buffer_ptr; /* current location in current_buffer */ + png_bytep current_buffer; /* buffer for recently used data */ + png_uint_32 push_length; /* size of current input chunk */ + png_uint_32 skip_length; /* bytes to skip in input data */ + png_size_t save_buffer_size; /* amount of data now in save_buffer */ + png_size_t save_buffer_max; /* total size of save_buffer */ + png_size_t buffer_size; /* total amount of available input data */ + png_size_t current_buffer_size; /* amount of data now in current_buffer */ + int process_mode; /* what push library is currently doing */ + int cur_palette; /* current push library palette index */ + +# if defined(PNG_TEXT_SUPPORTED) + png_size_t current_text_size; /* current size of text input data */ + png_size_t current_text_left; /* how much text left to read in input */ + png_charp current_text; /* current text chunk buffer */ + png_charp current_text_ptr; /* current location in current_text */ +# endif /* PNG_TEXT_SUPPORTED */ +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + +#if defined(__TURBOC__) && !defined(_Windows) && !defined(__FLAT__) +/* for the Borland special 64K segment handler */ + png_bytepp offset_table_ptr; + png_bytep offset_table; + png_uint_16 offset_table_number; + png_uint_16 offset_table_count; + png_uint_16 offset_table_count_free; +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) + png_bytep palette_lookup; /* lookup table for dithering */ + png_bytep dither_index; /* index translation for palette files */ +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) || defined(PNG_hIST_SUPPORTED) + png_uint_16p hist; /* histogram */ +#endif + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + png_byte heuristic_method; /* heuristic for row filter selection */ + png_byte num_prev_filters; /* number of weights for previous rows */ + png_bytep prev_filters; /* filter type(s) of previous row(s) */ + png_uint_16p filter_weights; /* weight(s) for previous line(s) */ + png_uint_16p inv_filter_weights; /* 1/weight(s) for previous line(s) */ + png_uint_16p filter_costs; /* relative filter calculation cost */ + png_uint_16p inv_filter_costs; /* 1/relative filter calculation cost */ +#endif + +#if defined(PNG_TIME_RFC1123_SUPPORTED) + png_charp time_buffer; /* String to hold RFC 1123 time text */ +#endif + +/* New members added in libpng-1.0.6 */ + +#ifdef PNG_FREE_ME_SUPPORTED + png_uint_32 free_me; /* flags items libpng is responsible for freeing */ +#endif + +#if defined(PNG_USER_CHUNKS_SUPPORTED) + png_voidp user_chunk_ptr; + png_user_chunk_ptr read_user_chunk_fn; /* user read chunk handler */ +#endif + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) + int num_chunk_list; + png_bytep chunk_list; +#endif + +/* New members added in libpng-1.0.3 */ +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + png_byte rgb_to_gray_status; + /* These were changed from png_byte in libpng-1.0.6 */ + png_uint_16 rgb_to_gray_red_coeff; + png_uint_16 rgb_to_gray_green_coeff; + png_uint_16 rgb_to_gray_blue_coeff; +#endif + +/* New member added in libpng-1.0.4 (renamed in 1.0.9) */ +#if defined(PNG_MNG_FEATURES_SUPPORTED) || \ + defined(PNG_READ_EMPTY_PLTE_SUPPORTED) || \ + defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) +/* changed from png_byte to png_uint_32 at version 1.2.0 */ +#ifdef PNG_1_0_X + png_byte mng_features_permitted; +#else + png_uint_32 mng_features_permitted; +#endif /* PNG_1_0_X */ +#endif + +/* New member added in libpng-1.0.7 */ +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + png_fixed_point int_gamma; +#endif + +/* New member added in libpng-1.0.9, ifdef'ed out in 1.0.12, enabled in 1.2.0 */ +#if defined(PNG_MNG_FEATURES_SUPPORTED) + png_byte filter_type; +#endif + +#if defined(PNG_1_0_X) +/* New member added in libpng-1.0.10, ifdef'ed out in 1.2.0 */ + png_uint_32 row_buf_size; +#endif + +/* New members added in libpng-1.2.0 */ +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) +# if !defined(PNG_1_0_X) +# if defined(PNG_MMX_CODE_SUPPORTED) + png_byte mmx_bitdepth_threshold; + png_uint_32 mmx_rowbytes_threshold; +# endif + png_uint_32 asm_flags; +# endif +#endif + +/* New members added in libpng-1.0.2 but first enabled by default in 1.2.0 */ +#ifdef PNG_USER_MEM_SUPPORTED + png_voidp mem_ptr; /* user supplied struct for mem functions */ + png_malloc_ptr malloc_fn; /* function for allocating memory */ + png_free_ptr free_fn; /* function for freeing memory */ +#endif + +/* New member added in libpng-1.0.13 and 1.2.0 */ + png_bytep big_row_buf; /* buffer to save current (unfiltered) row */ + +#if defined(PNG_READ_DITHER_SUPPORTED) +/* The following three members were added at version 1.0.14 and 1.2.4 */ + png_bytep dither_sort; /* working sort array */ + png_bytep index_to_palette; /* where the original index currently is */ + /* in the palette */ + png_bytep palette_to_index; /* which original index points to this */ + /* palette color */ +#endif + +/* New members added in libpng-1.0.16 and 1.2.6 */ + png_byte compression_type; + +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + png_uint_32 user_width_max; + png_uint_32 user_height_max; +#endif + +/* New member added in libpng-1.0.25 and 1.2.17 */ +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) + /* storage for unknown chunk that the library doesn't recognize. */ + png_unknown_chunk unknown_chunk; +#endif +}; + +/* This triggers a compiler error in png.c, if png.c and png.h + * do not agree upon the version number. + */ +typedef png_structp version_1_2_21; + +typedef png_struct FAR * FAR * png_structpp; + +/* Here are the function definitions most commonly used. This is not + * the place to find out how to use libpng. See libpng.txt for the + * full explanation, see example.c for the summary. This just provides + * a simple one line description of the use of each function. + */ + +/* Returns the version number of the library */ +extern PNG_EXPORT(png_uint_32,png_access_version_number) PNGARG((void)); + +/* Tell lib we have already handled the first magic bytes. + * Handling more than 8 bytes from the beginning of the file is an error. + */ +extern PNG_EXPORT(void,png_set_sig_bytes) PNGARG((png_structp png_ptr, + int num_bytes)); + +/* Check sig[start] through sig[start + num_to_check - 1] to see if it's a + * PNG file. Returns zero if the supplied bytes match the 8-byte PNG + * signature, and non-zero otherwise. Having num_to_check == 0 or + * start > 7 will always fail (ie return non-zero). + */ +extern PNG_EXPORT(int,png_sig_cmp) PNGARG((png_bytep sig, png_size_t start, + png_size_t num_to_check)); + +/* Simple signature checking function. This is the same as calling + * png_check_sig(sig, n) := !png_sig_cmp(sig, 0, n). + */ +extern PNG_EXPORT(int,png_check_sig) PNGARG((png_bytep sig, int num)); + +/* Allocate and initialize png_ptr struct for reading, and any other memory. */ +extern PNG_EXPORT(png_structp,png_create_read_struct) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn)); + +/* Allocate and initialize png_ptr struct for writing, and any other memory */ +extern PNG_EXPORT(png_structp,png_create_write_struct) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn)); + +#ifdef PNG_WRITE_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_compression_buffer_size) + PNGARG((png_structp png_ptr)); +#endif + +#ifdef PNG_WRITE_SUPPORTED +extern PNG_EXPORT(void,png_set_compression_buffer_size) + PNGARG((png_structp png_ptr, png_uint_32 size)); +#endif + +/* Reset the compression stream */ +extern PNG_EXPORT(int,png_reset_zstream) PNGARG((png_structp png_ptr)); + +/* New functions added in libpng-1.0.2 (not enabled by default until 1.2.0) */ +#ifdef PNG_USER_MEM_SUPPORTED +extern PNG_EXPORT(png_structp,png_create_read_struct_2) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn)); +extern PNG_EXPORT(png_structp,png_create_write_struct_2) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn)); +#endif + +/* Write a PNG chunk - size, type, (optional) data, CRC. */ +extern PNG_EXPORT(void,png_write_chunk) PNGARG((png_structp png_ptr, + png_bytep chunk_name, png_bytep data, png_size_t length)); + +/* Write the start of a PNG chunk - length and chunk name. */ +extern PNG_EXPORT(void,png_write_chunk_start) PNGARG((png_structp png_ptr, + png_bytep chunk_name, png_uint_32 length)); + +/* Write the data of a PNG chunk started with png_write_chunk_start(). */ +extern PNG_EXPORT(void,png_write_chunk_data) PNGARG((png_structp png_ptr, + png_bytep data, png_size_t length)); + +/* Finish a chunk started with png_write_chunk_start() (includes CRC). */ +extern PNG_EXPORT(void,png_write_chunk_end) PNGARG((png_structp png_ptr)); + +/* Allocate and initialize the info structure */ +extern PNG_EXPORT(png_infop,png_create_info_struct) + PNGARG((png_structp png_ptr)); + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* Initialize the info structure (old interface - DEPRECATED) */ +extern PNG_EXPORT(void,png_info_init) PNGARG((png_infop info_ptr)); +#undef png_info_init +#define png_info_init(info_ptr) png_info_init_3(&info_ptr,\ + png_sizeof(png_info)); +#endif + +extern PNG_EXPORT(void,png_info_init_3) PNGARG((png_infopp info_ptr, + png_size_t png_info_struct_size)); + +/* Writes all the PNG information before the image. */ +extern PNG_EXPORT(void,png_write_info_before_PLTE) PNGARG((png_structp png_ptr, + png_infop info_ptr)); +extern PNG_EXPORT(void,png_write_info) PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* read the information before the actual image data. */ +extern PNG_EXPORT(void,png_read_info) PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif + +#if defined(PNG_TIME_RFC1123_SUPPORTED) +extern PNG_EXPORT(png_charp,png_convert_to_rfc1123) + PNGARG((png_structp png_ptr, png_timep ptime)); +#endif + +#if !defined(_WIN32_WCE) +/* "time.h" functions are not supported on WindowsCE */ +#if defined(PNG_WRITE_tIME_SUPPORTED) +/* convert from a struct tm to png_time */ +extern PNG_EXPORT(void,png_convert_from_struct_tm) PNGARG((png_timep ptime, + struct tm FAR * ttime)); + +/* convert from time_t to png_time. Uses gmtime() */ +extern PNG_EXPORT(void,png_convert_from_time_t) PNGARG((png_timep ptime, + time_t ttime)); +#endif /* PNG_WRITE_tIME_SUPPORTED */ +#endif /* _WIN32_WCE */ + +#if defined(PNG_READ_EXPAND_SUPPORTED) +/* Expand data to 24-bit RGB, or 8-bit grayscale, with alpha if available. */ +extern PNG_EXPORT(void,png_set_expand) PNGARG((png_structp png_ptr)); +#if !defined(PNG_1_0_X) +extern PNG_EXPORT(void,png_set_expand_gray_1_2_4_to_8) PNGARG((png_structp + png_ptr)); +#endif +extern PNG_EXPORT(void,png_set_palette_to_rgb) PNGARG((png_structp png_ptr)); +extern PNG_EXPORT(void,png_set_tRNS_to_alpha) PNGARG((png_structp png_ptr)); +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* Deprecated */ +extern PNG_EXPORT(void,png_set_gray_1_2_4_to_8) PNGARG((png_structp png_ptr)); +#endif +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* Use blue, green, red order for pixels. */ +extern PNG_EXPORT(void,png_set_bgr) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) +/* Expand the grayscale to 24-bit RGB if necessary. */ +extern PNG_EXPORT(void,png_set_gray_to_rgb) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +/* Reduce RGB to grayscale. */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_rgb_to_gray) PNGARG((png_structp png_ptr, + int error_action, double red, double green )); +#endif +extern PNG_EXPORT(void,png_set_rgb_to_gray_fixed) PNGARG((png_structp png_ptr, + int error_action, png_fixed_point red, png_fixed_point green )); +extern PNG_EXPORT(png_byte,png_get_rgb_to_gray_status) PNGARG((png_structp + png_ptr)); +#endif + +extern PNG_EXPORT(void,png_build_grayscale_palette) PNGARG((int bit_depth, + png_colorp palette)); + +#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED) +extern PNG_EXPORT(void,png_set_strip_alpha) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +extern PNG_EXPORT(void,png_set_swap_alpha) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +extern PNG_EXPORT(void,png_set_invert_alpha) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +/* Add a filler byte to 8-bit Gray or 24-bit RGB images. */ +extern PNG_EXPORT(void,png_set_filler) PNGARG((png_structp png_ptr, + png_uint_32 filler, int flags)); +/* The values of the PNG_FILLER_ defines should NOT be changed */ +#define PNG_FILLER_BEFORE 0 +#define PNG_FILLER_AFTER 1 +/* Add an alpha byte to 8-bit Gray or 24-bit RGB images. */ +#if !defined(PNG_1_0_X) +extern PNG_EXPORT(void,png_set_add_alpha) PNGARG((png_structp png_ptr, + png_uint_32 filler, int flags)); +#endif +#endif /* PNG_READ_FILLER_SUPPORTED || PNG_WRITE_FILLER_SUPPORTED */ + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* Swap bytes in 16-bit depth files. */ +extern PNG_EXPORT(void,png_set_swap) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) +/* Use 1 byte per pixel in 1, 2, or 4-bit depth files. */ +extern PNG_EXPORT(void,png_set_packing) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) || defined(PNG_WRITE_PACKSWAP_SUPPORTED) +/* Swap packing order of pixels in bytes. */ +extern PNG_EXPORT(void,png_set_packswap) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) +/* Converts files to legal bit depths. */ +extern PNG_EXPORT(void,png_set_shift) PNGARG((png_structp png_ptr, + png_color_8p true_bits)); +#endif + +#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ + defined(PNG_WRITE_INTERLACING_SUPPORTED) +/* Have the code handle the interlacing. Returns the number of passes. */ +extern PNG_EXPORT(int,png_set_interlace_handling) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +/* Invert monochrome files */ +extern PNG_EXPORT(void,png_set_invert_mono) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) +/* Handle alpha and tRNS by replacing with a background color. */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_background) PNGARG((png_structp png_ptr, + png_color_16p background_color, int background_gamma_code, + int need_expand, double background_gamma)); +#endif +#define PNG_BACKGROUND_GAMMA_UNKNOWN 0 +#define PNG_BACKGROUND_GAMMA_SCREEN 1 +#define PNG_BACKGROUND_GAMMA_FILE 2 +#define PNG_BACKGROUND_GAMMA_UNIQUE 3 +#endif + +#if defined(PNG_READ_16_TO_8_SUPPORTED) +/* strip the second byte of information from a 16-bit depth file. */ +extern PNG_EXPORT(void,png_set_strip_16) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) +/* Turn on dithering, and reduce the palette to the number of colors available. */ +extern PNG_EXPORT(void,png_set_dither) PNGARG((png_structp png_ptr, + png_colorp palette, int num_palette, int maximum_colors, + png_uint_16p histogram, int full_dither)); +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) +/* Handle gamma correction. Screen_gamma=(display_exponent) */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_gamma) PNGARG((png_structp png_ptr, + double screen_gamma, double default_file_gamma)); +#endif +#endif + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED) || \ + defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) +/* Permit or disallow empty PLTE (0: not permitted, 1: permitted) */ +/* Deprecated and will be removed. Use png_permit_mng_features() instead. */ +extern PNG_EXPORT(void,png_permit_empty_plte) PNGARG((png_structp png_ptr, + int empty_plte_permitted)); +#endif +#endif + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) +/* Set how many lines between output flushes - 0 for no flushing */ +extern PNG_EXPORT(void,png_set_flush) PNGARG((png_structp png_ptr, int nrows)); +/* Flush the current PNG output buffer */ +extern PNG_EXPORT(void,png_write_flush) PNGARG((png_structp png_ptr)); +#endif + +/* optional update palette with requested transformations */ +extern PNG_EXPORT(void,png_start_read_image) PNGARG((png_structp png_ptr)); + +/* optional call to update the users info structure */ +extern PNG_EXPORT(void,png_read_update_info) PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* read one or more rows of image data. */ +extern PNG_EXPORT(void,png_read_rows) PNGARG((png_structp png_ptr, + png_bytepp row, png_bytepp display_row, png_uint_32 num_rows)); +#endif + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* read a row of data. */ +extern PNG_EXPORT(void,png_read_row) PNGARG((png_structp png_ptr, + png_bytep row, + png_bytep display_row)); +#endif + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* read the whole image into memory at once. */ +extern PNG_EXPORT(void,png_read_image) PNGARG((png_structp png_ptr, + png_bytepp image)); +#endif + +/* write a row of image data */ +extern PNG_EXPORT(void,png_write_row) PNGARG((png_structp png_ptr, + png_bytep row)); + +/* write a few rows of image data */ +extern PNG_EXPORT(void,png_write_rows) PNGARG((png_structp png_ptr, + png_bytepp row, png_uint_32 num_rows)); + +/* write the image data */ +extern PNG_EXPORT(void,png_write_image) PNGARG((png_structp png_ptr, + png_bytepp image)); + +/* writes the end of the PNG file. */ +extern PNG_EXPORT(void,png_write_end) PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* read the end of the PNG file. */ +extern PNG_EXPORT(void,png_read_end) PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif + +/* free any memory associated with the png_info_struct */ +extern PNG_EXPORT(void,png_destroy_info_struct) PNGARG((png_structp png_ptr, + png_infopp info_ptr_ptr)); + +/* free any memory associated with the png_struct and the png_info_structs */ +extern PNG_EXPORT(void,png_destroy_read_struct) PNGARG((png_structpp + png_ptr_ptr, png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr)); + +/* free all memory used by the read (old method - NOT DLL EXPORTED) */ +extern void png_read_destroy PNGARG((png_structp png_ptr, png_infop info_ptr, + png_infop end_info_ptr)); + +/* free any memory associated with the png_struct and the png_info_structs */ +extern PNG_EXPORT(void,png_destroy_write_struct) + PNGARG((png_structpp png_ptr_ptr, png_infopp info_ptr_ptr)); + +/* free any memory used in png_ptr struct (old method - NOT DLL EXPORTED) */ +extern void png_write_destroy PNGARG((png_structp png_ptr)); + +/* set the libpng method of handling chunk CRC errors */ +extern PNG_EXPORT(void,png_set_crc_action) PNGARG((png_structp png_ptr, + int crit_action, int ancil_action)); + +/* Values for png_set_crc_action() to say how to handle CRC errors in + * ancillary and critical chunks, and whether to use the data contained + * therein. Note that it is impossible to "discard" data in a critical + * chunk. For versions prior to 0.90, the action was always error/quit, + * whereas in version 0.90 and later, the action for CRC errors in ancillary + * chunks is warn/discard. These values should NOT be changed. + * + * value action:critical action:ancillary + */ +#define PNG_CRC_DEFAULT 0 /* error/quit warn/discard data */ +#define PNG_CRC_ERROR_QUIT 1 /* error/quit error/quit */ +#define PNG_CRC_WARN_DISCARD 2 /* (INVALID) warn/discard data */ +#define PNG_CRC_WARN_USE 3 /* warn/use data warn/use data */ +#define PNG_CRC_QUIET_USE 4 /* quiet/use data quiet/use data */ +#define PNG_CRC_NO_CHANGE 5 /* use current value use current value */ + +/* These functions give the user control over the scan-line filtering in + * libpng and the compression methods used by zlib. These functions are + * mainly useful for testing, as the defaults should work with most users. + * Those users who are tight on memory or want faster performance at the + * expense of compression can modify them. See the compression library + * header file (zlib.h) for an explination of the compression functions. + */ + +/* set the filtering method(s) used by libpng. Currently, the only valid + * value for "method" is 0. + */ +extern PNG_EXPORT(void,png_set_filter) PNGARG((png_structp png_ptr, int method, + int filters)); + +/* Flags for png_set_filter() to say which filters to use. The flags + * are chosen so that they don't conflict with real filter types + * below, in case they are supplied instead of the #defined constants. + * These values should NOT be changed. + */ +#define PNG_NO_FILTERS 0x00 +#define PNG_FILTER_NONE 0x08 +#define PNG_FILTER_SUB 0x10 +#define PNG_FILTER_UP 0x20 +#define PNG_FILTER_AVG 0x40 +#define PNG_FILTER_PAETH 0x80 +#define PNG_ALL_FILTERS (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP | \ + PNG_FILTER_AVG | PNG_FILTER_PAETH) + +/* Filter values (not flags) - used in pngwrite.c, pngwutil.c for now. + * These defines should NOT be changed. + */ +#define PNG_FILTER_VALUE_NONE 0 +#define PNG_FILTER_VALUE_SUB 1 +#define PNG_FILTER_VALUE_UP 2 +#define PNG_FILTER_VALUE_AVG 3 +#define PNG_FILTER_VALUE_PAETH 4 +#define PNG_FILTER_VALUE_LAST 5 + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) /* EXPERIMENTAL */ +/* The "heuristic_method" is given by one of the PNG_FILTER_HEURISTIC_ + * defines, either the default (minimum-sum-of-absolute-differences), or + * the experimental method (weighted-minimum-sum-of-absolute-differences). + * + * Weights are factors >= 1.0, indicating how important it is to keep the + * filter type consistent between rows. Larger numbers mean the current + * filter is that many times as likely to be the same as the "num_weights" + * previous filters. This is cumulative for each previous row with a weight. + * There needs to be "num_weights" values in "filter_weights", or it can be + * NULL if the weights aren't being specified. Weights have no influence on + * the selection of the first row filter. Well chosen weights can (in theory) + * improve the compression for a given image. + * + * Costs are factors >= 1.0 indicating the relative decoding costs of a + * filter type. Higher costs indicate more decoding expense, and are + * therefore less likely to be selected over a filter with lower computational + * costs. There needs to be a value in "filter_costs" for each valid filter + * type (given by PNG_FILTER_VALUE_LAST), or it can be NULL if you aren't + * setting the costs. Costs try to improve the speed of decompression without + * unduly increasing the compressed image size. + * + * A negative weight or cost indicates the default value is to be used, and + * values in the range [0.0, 1.0) indicate the value is to remain unchanged. + * The default values for both weights and costs are currently 1.0, but may + * change if good general weighting/cost heuristics can be found. If both + * the weights and costs are set to 1.0, this degenerates the WEIGHTED method + * to the UNWEIGHTED method, but with added encoding time/computation. + */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_filter_heuristics) PNGARG((png_structp png_ptr, + int heuristic_method, int num_weights, png_doublep filter_weights, + png_doublep filter_costs)); +#endif +#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */ + +/* Heuristic used for row filter selection. These defines should NOT be + * changed. + */ +#define PNG_FILTER_HEURISTIC_DEFAULT 0 /* Currently "UNWEIGHTED" */ +#define PNG_FILTER_HEURISTIC_UNWEIGHTED 1 /* Used by libpng < 0.95 */ +#define PNG_FILTER_HEURISTIC_WEIGHTED 2 /* Experimental feature */ +#define PNG_FILTER_HEURISTIC_LAST 3 /* Not a valid value */ + +/* Set the library compression level. Currently, valid values range from + * 0 - 9, corresponding directly to the zlib compression levels 0 - 9 + * (0 - no compression, 9 - "maximal" compression). Note that tests have + * shown that zlib compression levels 3-6 usually perform as well as level 9 + * for PNG images, and do considerably fewer caclulations. In the future, + * these values may not correspond directly to the zlib compression levels. + */ +extern PNG_EXPORT(void,png_set_compression_level) PNGARG((png_structp png_ptr, + int level)); + +extern PNG_EXPORT(void,png_set_compression_mem_level) + PNGARG((png_structp png_ptr, int mem_level)); + +extern PNG_EXPORT(void,png_set_compression_strategy) + PNGARG((png_structp png_ptr, int strategy)); + +extern PNG_EXPORT(void,png_set_compression_window_bits) + PNGARG((png_structp png_ptr, int window_bits)); + +extern PNG_EXPORT(void,png_set_compression_method) PNGARG((png_structp png_ptr, + int method)); + +/* These next functions are called for input/output, memory, and error + * handling. They are in the file pngrio.c, pngwio.c, and pngerror.c, + * and call standard C I/O routines such as fread(), fwrite(), and + * fprintf(). These functions can be made to use other I/O routines + * at run time for those applications that need to handle I/O in a + * different manner by calling png_set_???_fn(). See libpng.txt for + * more information. + */ + +#if !defined(PNG_NO_STDIO) +/* Initialize the input/output for the PNG file to the default functions. */ +extern PNG_EXPORT(void,png_init_io) PNGARG((png_structp png_ptr, png_FILE_p fp)); +#endif + +/* Replace the (error and abort), and warning functions with user + * supplied functions. If no messages are to be printed you must still + * write and use replacement functions. The replacement error_fn should + * still do a longjmp to the last setjmp location if you are using this + * method of error handling. If error_fn or warning_fn is NULL, the + * default function will be used. + */ + +extern PNG_EXPORT(void,png_set_error_fn) PNGARG((png_structp png_ptr, + png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warning_fn)); + +/* Return the user pointer associated with the error functions */ +extern PNG_EXPORT(png_voidp,png_get_error_ptr) PNGARG((png_structp png_ptr)); + +/* Replace the default data output functions with a user supplied one(s). + * If buffered output is not used, then output_flush_fn can be set to NULL. + * If PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile time + * output_flush_fn will be ignored (and thus can be NULL). + */ +extern PNG_EXPORT(void,png_set_write_fn) PNGARG((png_structp png_ptr, + png_voidp io_ptr, png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn)); + +/* Replace the default data input function with a user supplied one. */ +extern PNG_EXPORT(void,png_set_read_fn) PNGARG((png_structp png_ptr, + png_voidp io_ptr, png_rw_ptr read_data_fn)); + +/* Return the user pointer associated with the I/O functions */ +extern PNG_EXPORT(png_voidp,png_get_io_ptr) PNGARG((png_structp png_ptr)); + +extern PNG_EXPORT(void,png_set_read_status_fn) PNGARG((png_structp png_ptr, + png_read_status_ptr read_row_fn)); + +extern PNG_EXPORT(void,png_set_write_status_fn) PNGARG((png_structp png_ptr, + png_write_status_ptr write_row_fn)); + +#ifdef PNG_USER_MEM_SUPPORTED +/* Replace the default memory allocation functions with user supplied one(s). */ +extern PNG_EXPORT(void,png_set_mem_fn) PNGARG((png_structp png_ptr, + png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn)); +/* Return the user pointer associated with the memory functions */ +extern PNG_EXPORT(png_voidp,png_get_mem_ptr) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +extern PNG_EXPORT(void,png_set_read_user_transform_fn) PNGARG((png_structp + png_ptr, png_user_transform_ptr read_user_transform_fn)); +#endif + +#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +extern PNG_EXPORT(void,png_set_write_user_transform_fn) PNGARG((png_structp + png_ptr, png_user_transform_ptr write_user_transform_fn)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +extern PNG_EXPORT(void,png_set_user_transform_info) PNGARG((png_structp + png_ptr, png_voidp user_transform_ptr, int user_transform_depth, + int user_transform_channels)); +/* Return the user pointer associated with the user transform functions */ +extern PNG_EXPORT(png_voidp,png_get_user_transform_ptr) + PNGARG((png_structp png_ptr)); +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +extern PNG_EXPORT(void,png_set_read_user_chunk_fn) PNGARG((png_structp png_ptr, + png_voidp user_chunk_ptr, png_user_chunk_ptr read_user_chunk_fn)); +extern PNG_EXPORT(png_voidp,png_get_user_chunk_ptr) PNGARG((png_structp + png_ptr)); +#endif + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +/* Sets the function callbacks for the push reader, and a pointer to a + * user-defined structure available to the callback functions. + */ +extern PNG_EXPORT(void,png_set_progressive_read_fn) PNGARG((png_structp png_ptr, + png_voidp progressive_ptr, + png_progressive_info_ptr info_fn, png_progressive_row_ptr row_fn, + png_progressive_end_ptr end_fn)); + +/* returns the user pointer associated with the push read functions */ +extern PNG_EXPORT(png_voidp,png_get_progressive_ptr) + PNGARG((png_structp png_ptr)); + +/* function to be called when data becomes available */ +extern PNG_EXPORT(void,png_process_data) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytep buffer, png_size_t buffer_size)); + +/* function that combines rows. Not very much different than the + * png_combine_row() call. Is this even used????? + */ +extern PNG_EXPORT(void,png_progressive_combine_row) PNGARG((png_structp png_ptr, + png_bytep old_row, png_bytep new_row)); +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + +extern PNG_EXPORT(png_voidp,png_malloc) PNGARG((png_structp png_ptr, + png_uint_32 size)); + +#if defined(PNG_1_0_X) +# define png_malloc_warn png_malloc +#else +/* Added at libpng version 1.2.4 */ +extern PNG_EXPORT(png_voidp,png_malloc_warn) PNGARG((png_structp png_ptr, + png_uint_32 size)); +#endif + +/* frees a pointer allocated by png_malloc() */ +extern PNG_EXPORT(void,png_free) PNGARG((png_structp png_ptr, png_voidp ptr)); + +#if defined(PNG_1_0_X) +/* Function to allocate memory for zlib. */ +extern PNG_EXPORT(voidpf,png_zalloc) PNGARG((voidpf png_ptr, uInt items, + uInt size)); + +/* Function to free memory for zlib */ +extern PNG_EXPORT(void,png_zfree) PNGARG((voidpf png_ptr, voidpf ptr)); +#endif + +/* Free data that was allocated internally */ +extern PNG_EXPORT(void,png_free_data) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 free_me, int num)); +#ifdef PNG_FREE_ME_SUPPORTED +/* Reassign responsibility for freeing existing data, whether allocated + * by libpng or by the application */ +extern PNG_EXPORT(void,png_data_freer) PNGARG((png_structp png_ptr, + png_infop info_ptr, int freer, png_uint_32 mask)); +#endif +/* assignments for png_data_freer */ +#define PNG_DESTROY_WILL_FREE_DATA 1 +#define PNG_SET_WILL_FREE_DATA 1 +#define PNG_USER_WILL_FREE_DATA 2 +/* Flags for png_ptr->free_me and info_ptr->free_me */ +#define PNG_FREE_HIST 0x0008 +#define PNG_FREE_ICCP 0x0010 +#define PNG_FREE_SPLT 0x0020 +#define PNG_FREE_ROWS 0x0040 +#define PNG_FREE_PCAL 0x0080 +#define PNG_FREE_SCAL 0x0100 +#define PNG_FREE_UNKN 0x0200 +#define PNG_FREE_LIST 0x0400 +#define PNG_FREE_PLTE 0x1000 +#define PNG_FREE_TRNS 0x2000 +#define PNG_FREE_TEXT 0x4000 +#define PNG_FREE_ALL 0x7fff +#define PNG_FREE_MUL 0x4220 /* PNG_FREE_SPLT|PNG_FREE_TEXT|PNG_FREE_UNKN */ + +#ifdef PNG_USER_MEM_SUPPORTED +extern PNG_EXPORT(png_voidp,png_malloc_default) PNGARG((png_structp png_ptr, + png_uint_32 size)); +extern PNG_EXPORT(void,png_free_default) PNGARG((png_structp png_ptr, + png_voidp ptr)); +#endif + +extern PNG_EXPORT(png_voidp,png_memcpy_check) PNGARG((png_structp png_ptr, + png_voidp s1, png_voidp s2, png_uint_32 size)); + +extern PNG_EXPORT(png_voidp,png_memset_check) PNGARG((png_structp png_ptr, + png_voidp s1, int value, png_uint_32 size)); + +#if defined(USE_FAR_KEYWORD) /* memory model conversion function */ +extern void *png_far_to_near PNGARG((png_structp png_ptr,png_voidp ptr, + int check)); +#endif /* USE_FAR_KEYWORD */ + +#ifndef PNG_NO_ERROR_TEXT +/* Fatal error in PNG image of libpng - can't continue */ +extern PNG_EXPORT(void,png_error) PNGARG((png_structp png_ptr, + png_const_charp error_message)); + +/* The same, but the chunk name is prepended to the error string. */ +extern PNG_EXPORT(void,png_chunk_error) PNGARG((png_structp png_ptr, + png_const_charp error_message)); +#else +/* Fatal error in PNG image of libpng - can't continue */ +extern PNG_EXPORT(void,png_err) PNGARG((png_structp png_ptr)); +#endif + +#ifndef PNG_NO_WARNINGS +/* Non-fatal error in libpng. Can continue, but may have a problem. */ +extern PNG_EXPORT(void,png_warning) PNGARG((png_structp png_ptr, + png_const_charp warning_message)); + +#ifdef PNG_READ_SUPPORTED +/* Non-fatal error in libpng, chunk name is prepended to message. */ +extern PNG_EXPORT(void,png_chunk_warning) PNGARG((png_structp png_ptr, + png_const_charp warning_message)); +#endif /* PNG_READ_SUPPORTED */ +#endif /* PNG_NO_WARNINGS */ + +/* The png_set_ functions are for storing values in the png_info_struct. + * Similarly, the png_get_ calls are used to read values from the + * png_info_struct, either storing the parameters in the passed variables, or + * setting pointers into the png_info_struct where the data is stored. The + * png_get_ functions return a non-zero value if the data was available + * in info_ptr, or return zero and do not change any of the parameters if the + * data was not available. + * + * These functions should be used instead of directly accessing png_info + * to avoid problems with future changes in the size and internal layout of + * png_info_struct. + */ +/* Returns "flag" if chunk data is valid in info_ptr. */ +extern PNG_EXPORT(png_uint_32,png_get_valid) PNGARG((png_structp png_ptr, +png_infop info_ptr, png_uint_32 flag)); + +/* Returns number of bytes needed to hold a transformed row. */ +extern PNG_EXPORT(png_uint_32,png_get_rowbytes) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +#if defined(PNG_INFO_IMAGE_SUPPORTED) +/* Returns row_pointers, which is an array of pointers to scanlines that was +returned from png_read_png(). */ +extern PNG_EXPORT(png_bytepp,png_get_rows) PNGARG((png_structp png_ptr, +png_infop info_ptr)); +/* Set row_pointers, which is an array of pointers to scanlines for use +by png_write_png(). */ +extern PNG_EXPORT(void,png_set_rows) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytepp row_pointers)); +#endif + +/* Returns number of color channels in image. */ +extern PNG_EXPORT(png_byte,png_get_channels) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +#ifdef PNG_EASY_ACCESS_SUPPORTED +/* Returns image width in pixels. */ +extern PNG_EXPORT(png_uint_32, png_get_image_width) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image height in pixels. */ +extern PNG_EXPORT(png_uint_32, png_get_image_height) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image bit_depth. */ +extern PNG_EXPORT(png_byte, png_get_bit_depth) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image color_type. */ +extern PNG_EXPORT(png_byte, png_get_color_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image filter_type. */ +extern PNG_EXPORT(png_byte, png_get_filter_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image interlace_type. */ +extern PNG_EXPORT(png_byte, png_get_interlace_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image compression_type. */ +extern PNG_EXPORT(png_byte, png_get_compression_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image resolution in pixels per meter, from pHYs chunk data. */ +extern PNG_EXPORT(png_uint_32, png_get_pixels_per_meter) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_uint_32, png_get_x_pixels_per_meter) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_uint_32, png_get_y_pixels_per_meter) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns pixel aspect ratio, computed from pHYs chunk data. */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(float, png_get_pixel_aspect_ratio) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +#endif + +/* Returns image x, y offset in pixels or microns, from oFFs chunk data. */ +extern PNG_EXPORT(png_int_32, png_get_x_offset_pixels) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_int_32, png_get_y_offset_pixels) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_int_32, png_get_x_offset_microns) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_int_32, png_get_y_offset_microns) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +#endif /* PNG_EASY_ACCESS_SUPPORTED */ + +/* Returns pointer to signature string read from PNG header */ +extern PNG_EXPORT(png_bytep,png_get_signature) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +#if defined(PNG_bKGD_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_bKGD) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_16p *background)); +#endif + +#if defined(PNG_bKGD_SUPPORTED) +extern PNG_EXPORT(void,png_set_bKGD) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_16p background)); +#endif + +#if defined(PNG_cHRM_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_cHRM) PNGARG((png_structp png_ptr, + png_infop info_ptr, double *white_x, double *white_y, double *red_x, + double *red_y, double *green_x, double *green_y, double *blue_x, + double *blue_y)); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_cHRM_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point *int_white_x, png_fixed_point + *int_white_y, png_fixed_point *int_red_x, png_fixed_point *int_red_y, + png_fixed_point *int_green_x, png_fixed_point *int_green_y, png_fixed_point + *int_blue_x, png_fixed_point *int_blue_y)); +#endif +#endif + +#if defined(PNG_cHRM_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_cHRM) PNGARG((png_structp png_ptr, + png_infop info_ptr, double white_x, double white_y, double red_x, + double red_y, double green_x, double green_y, double blue_x, double blue_y)); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_cHRM_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point int_white_x, png_fixed_point int_white_y, + png_fixed_point int_red_x, png_fixed_point int_red_y, png_fixed_point + int_green_x, png_fixed_point int_green_y, png_fixed_point int_blue_x, + png_fixed_point int_blue_y)); +#endif +#endif + +#if defined(PNG_gAMA_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_gAMA) PNGARG((png_structp png_ptr, + png_infop info_ptr, double *file_gamma)); +#endif +extern PNG_EXPORT(png_uint_32,png_get_gAMA_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point *int_file_gamma)); +#endif + +#if defined(PNG_gAMA_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_gAMA) PNGARG((png_structp png_ptr, + png_infop info_ptr, double file_gamma)); +#endif +extern PNG_EXPORT(void,png_set_gAMA_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point int_file_gamma)); +#endif + +#if defined(PNG_hIST_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_hIST) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_16p *hist)); +#endif + +#if defined(PNG_hIST_SUPPORTED) +extern PNG_EXPORT(void,png_set_hIST) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_16p hist)); +#endif + +extern PNG_EXPORT(png_uint_32,png_get_IHDR) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 *width, png_uint_32 *height, + int *bit_depth, int *color_type, int *interlace_method, + int *compression_method, int *filter_method)); + +extern PNG_EXPORT(void,png_set_IHDR) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_method, int compression_method, + int filter_method)); + +#if defined(PNG_oFFs_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_oFFs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_int_32 *offset_x, png_int_32 *offset_y, + int *unit_type)); +#endif + +#if defined(PNG_oFFs_SUPPORTED) +extern PNG_EXPORT(void,png_set_oFFs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_int_32 offset_x, png_int_32 offset_y, + int unit_type)); +#endif + +#if defined(PNG_pCAL_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_pCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charp *purpose, png_int_32 *X0, png_int_32 *X1, + int *type, int *nparams, png_charp *units, png_charpp *params)); +#endif + +#if defined(PNG_pCAL_SUPPORTED) +extern PNG_EXPORT(void,png_set_pCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charp purpose, png_int_32 X0, png_int_32 X1, + int type, int nparams, png_charp units, png_charpp params)); +#endif + +#if defined(PNG_pHYs_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_pHYs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type)); +#endif + +#if defined(PNG_pHYs_SUPPORTED) +extern PNG_EXPORT(void,png_set_pHYs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 res_x, png_uint_32 res_y, int unit_type)); +#endif + +extern PNG_EXPORT(png_uint_32,png_get_PLTE) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_colorp *palette, int *num_palette)); + +extern PNG_EXPORT(void,png_set_PLTE) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_colorp palette, int num_palette)); + +#if defined(PNG_sBIT_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_sBIT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_8p *sig_bit)); +#endif + +#if defined(PNG_sBIT_SUPPORTED) +extern PNG_EXPORT(void,png_set_sBIT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_8p sig_bit)); +#endif + +#if defined(PNG_sRGB_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_sRGB) PNGARG((png_structp png_ptr, + png_infop info_ptr, int *intent)); +#endif + +#if defined(PNG_sRGB_SUPPORTED) +extern PNG_EXPORT(void,png_set_sRGB) PNGARG((png_structp png_ptr, + png_infop info_ptr, int intent)); +extern PNG_EXPORT(void,png_set_sRGB_gAMA_and_cHRM) PNGARG((png_structp png_ptr, + png_infop info_ptr, int intent)); +#endif + +#if defined(PNG_iCCP_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_iCCP) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charpp name, int *compression_type, + png_charpp profile, png_uint_32 *proflen)); + /* Note to maintainer: profile should be png_bytepp */ +#endif + +#if defined(PNG_iCCP_SUPPORTED) +extern PNG_EXPORT(void,png_set_iCCP) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charp name, int compression_type, + png_charp profile, png_uint_32 proflen)); + /* Note to maintainer: profile should be png_bytep */ +#endif + +#if defined(PNG_sPLT_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_sPLT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_sPLT_tpp entries)); +#endif + +#if defined(PNG_sPLT_SUPPORTED) +extern PNG_EXPORT(void,png_set_sPLT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_sPLT_tp entries, int nentries)); +#endif + +#if defined(PNG_TEXT_SUPPORTED) +/* png_get_text also returns the number of text chunks in *num_text */ +extern PNG_EXPORT(png_uint_32,png_get_text) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_textp *text_ptr, int *num_text)); +#endif + +/* + * Note while png_set_text() will accept a structure whose text, + * language, and translated keywords are NULL pointers, the structure + * returned by png_get_text will always contain regular + * zero-terminated C strings. They might be empty strings but + * they will never be NULL pointers. + */ + +#if defined(PNG_TEXT_SUPPORTED) +extern PNG_EXPORT(void,png_set_text) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_textp text_ptr, int num_text)); +#endif + +#if defined(PNG_tIME_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_tIME) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_timep *mod_time)); +#endif + +#if defined(PNG_tIME_SUPPORTED) +extern PNG_EXPORT(void,png_set_tIME) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_timep mod_time)); +#endif + +#if defined(PNG_tRNS_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_tRNS) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytep *trans, int *num_trans, + png_color_16p *trans_values)); +#endif + +#if defined(PNG_tRNS_SUPPORTED) +extern PNG_EXPORT(void,png_set_tRNS) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytep trans, int num_trans, + png_color_16p trans_values)); +#endif + +#if defined(PNG_tRNS_SUPPORTED) +#endif + +#if defined(PNG_sCAL_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_sCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, int *unit, double *width, double *height)); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_sCAL_s) PNGARG((png_structp png_ptr, + png_infop info_ptr, int *unit, png_charpp swidth, png_charpp sheight)); +#endif +#endif +#endif /* PNG_sCAL_SUPPORTED */ + +#if defined(PNG_sCAL_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_sCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, int unit, double width, double height)); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_sCAL_s) PNGARG((png_structp png_ptr, + png_infop info_ptr, int unit, png_charp swidth, png_charp sheight)); +#endif +#endif +#endif /* PNG_sCAL_SUPPORTED || PNG_WRITE_sCAL_SUPPORTED */ + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) +/* provide a list of chunks and how they are to be handled, if the built-in + handling or default unknown chunk handling is not desired. Any chunks not + listed will be handled in the default manner. The IHDR and IEND chunks + must not be listed. + keep = 0: follow default behaviour + = 1: do not keep + = 2: keep only if safe-to-copy + = 3: keep even if unsafe-to-copy +*/ +extern PNG_EXPORT(void, png_set_keep_unknown_chunks) PNGARG((png_structp + png_ptr, int keep, png_bytep chunk_list, int num_chunks)); +extern PNG_EXPORT(void, png_set_unknown_chunks) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_unknown_chunkp unknowns, int num_unknowns)); +extern PNG_EXPORT(void, png_set_unknown_chunk_location) + PNGARG((png_structp png_ptr, png_infop info_ptr, int chunk, int location)); +extern PNG_EXPORT(png_uint_32,png_get_unknown_chunks) PNGARG((png_structp + png_ptr, png_infop info_ptr, png_unknown_chunkpp entries)); +#endif +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +PNG_EXPORT(int,png_handle_as_unknown) PNGARG((png_structp png_ptr, png_bytep + chunk_name)); +#endif + +/* Png_free_data() will turn off the "valid" flag for anything it frees. + If you need to turn it off for a chunk that your application has freed, + you can use png_set_invalid(png_ptr, info_ptr, PNG_INFO_CHNK); */ +extern PNG_EXPORT(void, png_set_invalid) PNGARG((png_structp png_ptr, + png_infop info_ptr, int mask)); + +#if defined(PNG_INFO_IMAGE_SUPPORTED) +/* The "params" pointer is currently not used and is for future expansion. */ +extern PNG_EXPORT(void, png_read_png) PNGARG((png_structp png_ptr, + png_infop info_ptr, + int transforms, + png_voidp params)); +extern PNG_EXPORT(void, png_write_png) PNGARG((png_structp png_ptr, + png_infop info_ptr, + int transforms, + png_voidp params)); +#endif + +/* Define PNG_DEBUG at compile time for debugging information. Higher + * numbers for PNG_DEBUG mean more debugging information. This has + * only been added since version 0.95 so it is not implemented throughout + * libpng yet, but more support will be added as needed. + */ +#ifdef PNG_DEBUG +#if (PNG_DEBUG > 0) +#if !defined(PNG_DEBUG_FILE) && defined(_MSC_VER) +#include +#if (PNG_DEBUG > 1) +#define png_debug(l,m) _RPT0(_CRT_WARN,m) +#define png_debug1(l,m,p1) _RPT1(_CRT_WARN,m,p1) +#define png_debug2(l,m,p1,p2) _RPT2(_CRT_WARN,m,p1,p2) +#endif +#else /* PNG_DEBUG_FILE || !_MSC_VER */ +#ifndef PNG_DEBUG_FILE +#define PNG_DEBUG_FILE stderr +#endif /* PNG_DEBUG_FILE */ +#if (PNG_DEBUG > 1) +#define png_debug(l,m) \ +{ \ + int num_tabs=l; \ + fprintf(PNG_DEBUG_FILE,"%s"m,(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":"")))); \ +} +#define png_debug1(l,m,p1) \ +{ \ + int num_tabs=l; \ + fprintf(PNG_DEBUG_FILE,"%s"m,(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))),p1); \ +} +#define png_debug2(l,m,p1,p2) \ +{ \ + int num_tabs=l; \ + fprintf(PNG_DEBUG_FILE,"%s"m,(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))),p1,p2); \ +} +#endif /* (PNG_DEBUG > 1) */ +#endif /* _MSC_VER */ +#endif /* (PNG_DEBUG > 0) */ +#endif /* PNG_DEBUG */ +#ifndef png_debug +#define png_debug(l, m) +#endif +#ifndef png_debug1 +#define png_debug1(l, m, p1) +#endif +#ifndef png_debug2 +#define png_debug2(l, m, p1, p2) +#endif + +extern PNG_EXPORT(png_charp,png_get_copyright) PNGARG((png_structp png_ptr)); +extern PNG_EXPORT(png_charp,png_get_header_ver) PNGARG((png_structp png_ptr)); +extern PNG_EXPORT(png_charp,png_get_header_version) PNGARG((png_structp png_ptr)); +extern PNG_EXPORT(png_charp,png_get_libpng_ver) PNGARG((png_structp png_ptr)); + +#ifdef PNG_MNG_FEATURES_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_permit_mng_features) PNGARG((png_structp + png_ptr, png_uint_32 mng_features_permitted)); +#endif + +/* For use in png_set_keep_unknown, added to version 1.2.6 */ +#define PNG_HANDLE_CHUNK_AS_DEFAULT 0 +#define PNG_HANDLE_CHUNK_NEVER 1 +#define PNG_HANDLE_CHUNK_IF_SAFE 2 +#define PNG_HANDLE_CHUNK_ALWAYS 3 + +/* Added to version 1.2.0 */ +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) +#if defined(PNG_MMX_CODE_SUPPORTED) +#define PNG_ASM_FLAG_MMX_SUPPORT_COMPILED 0x01 /* not user-settable */ +#define PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU 0x02 /* not user-settable */ +#define PNG_ASM_FLAG_MMX_READ_COMBINE_ROW 0x04 +#define PNG_ASM_FLAG_MMX_READ_INTERLACE 0x08 +#define PNG_ASM_FLAG_MMX_READ_FILTER_SUB 0x10 +#define PNG_ASM_FLAG_MMX_READ_FILTER_UP 0x20 +#define PNG_ASM_FLAG_MMX_READ_FILTER_AVG 0x40 +#define PNG_ASM_FLAG_MMX_READ_FILTER_PAETH 0x80 +#define PNG_ASM_FLAGS_INITIALIZED 0x80000000 /* not user-settable */ + +#define PNG_MMX_READ_FLAGS ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \ + | PNG_ASM_FLAG_MMX_READ_INTERLACE \ + | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \ + | PNG_ASM_FLAG_MMX_READ_FILTER_UP \ + | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \ + | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH ) +#define PNG_MMX_WRITE_FLAGS ( 0 ) + +#define PNG_MMX_FLAGS ( PNG_ASM_FLAG_MMX_SUPPORT_COMPILED \ + | PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU \ + | PNG_MMX_READ_FLAGS \ + | PNG_MMX_WRITE_FLAGS ) + +#define PNG_SELECT_READ 1 +#define PNG_SELECT_WRITE 2 +#endif /* PNG_MMX_CODE_SUPPORTED */ + +#if !defined(PNG_1_0_X) +/* pngget.c */ +extern PNG_EXPORT(png_uint_32,png_get_mmx_flagmask) + PNGARG((int flag_select, int *compilerID)); + +/* pngget.c */ +extern PNG_EXPORT(png_uint_32,png_get_asm_flagmask) + PNGARG((int flag_select)); + +/* pngget.c */ +extern PNG_EXPORT(png_uint_32,png_get_asm_flags) + PNGARG((png_structp png_ptr)); + +/* pngget.c */ +extern PNG_EXPORT(png_byte,png_get_mmx_bitdepth_threshold) + PNGARG((png_structp png_ptr)); + +/* pngget.c */ +extern PNG_EXPORT(png_uint_32,png_get_mmx_rowbytes_threshold) + PNGARG((png_structp png_ptr)); + +/* pngset.c */ +extern PNG_EXPORT(void,png_set_asm_flags) + PNGARG((png_structp png_ptr, png_uint_32 asm_flags)); + +/* pngset.c */ +extern PNG_EXPORT(void,png_set_mmx_thresholds) + PNGARG((png_structp png_ptr, png_byte mmx_bitdepth_threshold, + png_uint_32 mmx_rowbytes_threshold)); + +#endif /* PNG_1_0_X */ + +#if !defined(PNG_1_0_X) +/* png.c, pnggccrd.c, or pngvcrd.c */ +extern PNG_EXPORT(int,png_mmx_support) PNGARG((void)); +#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */ + +/* Strip the prepended error numbers ("#nnn ") from error and warning + * messages before passing them to the error or warning handler. */ +#ifdef PNG_ERROR_NUMBERS_SUPPORTED +extern PNG_EXPORT(void,png_set_strip_error_numbers) PNGARG((png_structp + png_ptr, png_uint_32 strip_mode)); +#endif + +#endif /* PNG_1_0_X */ + +/* Added at libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +extern PNG_EXPORT(void,png_set_user_limits) PNGARG((png_structp + png_ptr, png_uint_32 user_width_max, png_uint_32 user_height_max)); +extern PNG_EXPORT(png_uint_32,png_get_user_width_max) PNGARG((png_structp + png_ptr)); +extern PNG_EXPORT(png_uint_32,png_get_user_height_max) PNGARG((png_structp + png_ptr)); +#endif + +/* Maintainer: Put new public prototypes here ^, in libpng.3, and project defs */ + +#ifdef PNG_READ_COMPOSITE_NODIV_SUPPORTED +/* With these routines we avoid an integer divide, which will be slower on + * most machines. However, it does take more operations than the corresponding + * divide method, so it may be slower on a few RISC systems. There are two + * shifts (by 8 or 16 bits) and an addition, versus a single integer divide. + * + * Note that the rounding factors are NOT supposed to be the same! 128 and + * 32768 are correct for the NODIV code; 127 and 32767 are correct for the + * standard method. + * + * [Optimized code by Greg Roelofs and Mark Adler...blame us for bugs. :-) ] + */ + + /* fg and bg should be in `gamma 1.0' space; alpha is the opacity */ + +# define png_composite(composite, fg, alpha, bg) \ + { png_uint_16 temp = (png_uint_16)((png_uint_16)(fg) * (png_uint_16)(alpha) \ + + (png_uint_16)(bg)*(png_uint_16)(255 - \ + (png_uint_16)(alpha)) + (png_uint_16)128); \ + (composite) = (png_byte)((temp + (temp >> 8)) >> 8); } + +# define png_composite_16(composite, fg, alpha, bg) \ + { png_uint_32 temp = (png_uint_32)((png_uint_32)(fg) * (png_uint_32)(alpha) \ + + (png_uint_32)(bg)*(png_uint_32)(65535L - \ + (png_uint_32)(alpha)) + (png_uint_32)32768L); \ + (composite) = (png_uint_16)((temp + (temp >> 16)) >> 16); } + +#else /* standard method using integer division */ + +# define png_composite(composite, fg, alpha, bg) \ + (composite) = (png_byte)(((png_uint_16)(fg) * (png_uint_16)(alpha) + \ + (png_uint_16)(bg) * (png_uint_16)(255 - (png_uint_16)(alpha)) + \ + (png_uint_16)127) / 255) + +# define png_composite_16(composite, fg, alpha, bg) \ + (composite) = (png_uint_16)(((png_uint_32)(fg) * (png_uint_32)(alpha) + \ + (png_uint_32)(bg)*(png_uint_32)(65535L - (png_uint_32)(alpha)) + \ + (png_uint_32)32767) / (png_uint_32)65535L) + +#endif /* PNG_READ_COMPOSITE_NODIV_SUPPORTED */ + +/* Inline macros to do direct reads of bytes from the input buffer. These + * require that you are using an architecture that uses PNG byte ordering + * (MSB first) and supports unaligned data storage. I think that PowerPC + * in big-endian mode and 680x0 are the only ones that will support this. + * The x86 line of processors definitely do not. The png_get_int_32() + * routine also assumes we are using two's complement format for negative + * values, which is almost certainly true. + */ +#if defined(PNG_READ_BIG_ENDIAN_SUPPORTED) +# define png_get_uint_32(buf) ( *((png_uint_32p) (buf))) +# define png_get_uint_16(buf) ( *((png_uint_16p) (buf))) +# define png_get_int_32(buf) ( *((png_int_32p) (buf))) +#else +extern PNG_EXPORT(png_uint_32,png_get_uint_32) PNGARG((png_bytep buf)); +extern PNG_EXPORT(png_uint_16,png_get_uint_16) PNGARG((png_bytep buf)); +extern PNG_EXPORT(png_int_32,png_get_int_32) PNGARG((png_bytep buf)); +#endif /* !PNG_READ_BIG_ENDIAN_SUPPORTED */ +extern PNG_EXPORT(png_uint_32,png_get_uint_31) + PNGARG((png_structp png_ptr, png_bytep buf)); +/* No png_get_int_16 -- may be added if there's a real need for it. */ + +/* Place a 32-bit number into a buffer in PNG byte order (big-endian). + */ +extern PNG_EXPORT(void,png_save_uint_32) + PNGARG((png_bytep buf, png_uint_32 i)); +extern PNG_EXPORT(void,png_save_int_32) + PNGARG((png_bytep buf, png_int_32 i)); + +/* Place a 16-bit number into a buffer in PNG byte order. + * The parameter is declared unsigned int, not png_uint_16, + * just to avoid potential problems on pre-ANSI C compilers. + */ +extern PNG_EXPORT(void,png_save_uint_16) + PNGARG((png_bytep buf, unsigned int i)); +/* No png_save_int_16 -- may be added if there's a real need for it. */ + +/* ************************************************************************* */ + +/* These next functions are used internally in the code. They generally + * shouldn't be used unless you are writing code to add or replace some + * functionality in libpng. More information about most functions can + * be found in the files where the functions are located. + */ + +/* Various modes of operation, that are visible to applications because + * they are used for unknown chunk location. + */ +#define PNG_HAVE_IHDR 0x01 +#define PNG_HAVE_PLTE 0x02 +#define PNG_HAVE_IDAT 0x04 +#define PNG_AFTER_IDAT 0x08 /* Have complete zlib datastream */ +#define PNG_HAVE_IEND 0x10 + +#if defined(PNG_INTERNAL) + +/* More modes of operation. Note that after an init, mode is set to + * zero automatically when the structure is created. + */ +#define PNG_HAVE_gAMA 0x20 +#define PNG_HAVE_cHRM 0x40 +#define PNG_HAVE_sRGB 0x80 +#define PNG_HAVE_CHUNK_HEADER 0x100 +#define PNG_WROTE_tIME 0x200 +#define PNG_WROTE_INFO_BEFORE_PLTE 0x400 +#define PNG_BACKGROUND_IS_GRAY 0x800 +#define PNG_HAVE_PNG_SIGNATURE 0x1000 +#define PNG_HAVE_CHUNK_AFTER_IDAT 0x2000 /* Have another chunk after IDAT */ + +/* flags for the transformations the PNG library does on the image data */ +#define PNG_BGR 0x0001 +#define PNG_INTERLACE 0x0002 +#define PNG_PACK 0x0004 +#define PNG_SHIFT 0x0008 +#define PNG_SWAP_BYTES 0x0010 +#define PNG_INVERT_MONO 0x0020 +#define PNG_DITHER 0x0040 +#define PNG_BACKGROUND 0x0080 +#define PNG_BACKGROUND_EXPAND 0x0100 + /* 0x0200 unused */ +#define PNG_16_TO_8 0x0400 +#define PNG_RGBA 0x0800 +#define PNG_EXPAND 0x1000 +#define PNG_GAMMA 0x2000 +#define PNG_GRAY_TO_RGB 0x4000 +#define PNG_FILLER 0x8000L +#define PNG_PACKSWAP 0x10000L +#define PNG_SWAP_ALPHA 0x20000L +#define PNG_STRIP_ALPHA 0x40000L +#define PNG_INVERT_ALPHA 0x80000L +#define PNG_USER_TRANSFORM 0x100000L +#define PNG_RGB_TO_GRAY_ERR 0x200000L +#define PNG_RGB_TO_GRAY_WARN 0x400000L +#define PNG_RGB_TO_GRAY 0x600000L /* two bits, RGB_TO_GRAY_ERR|WARN */ + /* 0x800000L Unused */ +#define PNG_ADD_ALPHA 0x1000000L /* Added to libpng-1.2.7 */ +#define PNG_EXPAND_tRNS 0x2000000L /* Added to libpng-1.2.9 */ + /* 0x4000000L unused */ + /* 0x8000000L unused */ + /* 0x10000000L unused */ + /* 0x20000000L unused */ + /* 0x40000000L unused */ + +/* flags for png_create_struct */ +#define PNG_STRUCT_PNG 0x0001 +#define PNG_STRUCT_INFO 0x0002 + +/* Scaling factor for filter heuristic weighting calculations */ +#define PNG_WEIGHT_SHIFT 8 +#define PNG_WEIGHT_FACTOR (1<<(PNG_WEIGHT_SHIFT)) +#define PNG_COST_SHIFT 3 +#define PNG_COST_FACTOR (1<<(PNG_COST_SHIFT)) + +/* flags for the png_ptr->flags rather than declaring a byte for each one */ +#define PNG_FLAG_ZLIB_CUSTOM_STRATEGY 0x0001 +#define PNG_FLAG_ZLIB_CUSTOM_LEVEL 0x0002 +#define PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL 0x0004 +#define PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS 0x0008 +#define PNG_FLAG_ZLIB_CUSTOM_METHOD 0x0010 +#define PNG_FLAG_ZLIB_FINISHED 0x0020 +#define PNG_FLAG_ROW_INIT 0x0040 +#define PNG_FLAG_FILLER_AFTER 0x0080 +#define PNG_FLAG_CRC_ANCILLARY_USE 0x0100 +#define PNG_FLAG_CRC_ANCILLARY_NOWARN 0x0200 +#define PNG_FLAG_CRC_CRITICAL_USE 0x0400 +#define PNG_FLAG_CRC_CRITICAL_IGNORE 0x0800 +#define PNG_FLAG_FREE_PLTE 0x1000 +#define PNG_FLAG_FREE_TRNS 0x2000 +#define PNG_FLAG_FREE_HIST 0x4000 +#define PNG_FLAG_KEEP_UNKNOWN_CHUNKS 0x8000L +#define PNG_FLAG_KEEP_UNSAFE_CHUNKS 0x10000L +#define PNG_FLAG_LIBRARY_MISMATCH 0x20000L +#define PNG_FLAG_STRIP_ERROR_NUMBERS 0x40000L +#define PNG_FLAG_STRIP_ERROR_TEXT 0x80000L +#define PNG_FLAG_MALLOC_NULL_MEM_OK 0x100000L +#define PNG_FLAG_ADD_ALPHA 0x200000L /* Added to libpng-1.2.8 */ +#define PNG_FLAG_STRIP_ALPHA 0x400000L /* Added to libpng-1.2.8 */ + /* 0x800000L unused */ + /* 0x1000000L unused */ + /* 0x2000000L unused */ + /* 0x4000000L unused */ + /* 0x8000000L unused */ + /* 0x10000000L unused */ + /* 0x20000000L unused */ + /* 0x40000000L unused */ + +#define PNG_FLAG_CRC_ANCILLARY_MASK (PNG_FLAG_CRC_ANCILLARY_USE | \ + PNG_FLAG_CRC_ANCILLARY_NOWARN) + +#define PNG_FLAG_CRC_CRITICAL_MASK (PNG_FLAG_CRC_CRITICAL_USE | \ + PNG_FLAG_CRC_CRITICAL_IGNORE) + +#define PNG_FLAG_CRC_MASK (PNG_FLAG_CRC_ANCILLARY_MASK | \ + PNG_FLAG_CRC_CRITICAL_MASK) + +/* save typing and make code easier to understand */ + +#define PNG_COLOR_DIST(c1, c2) (abs((int)((c1).red) - (int)((c2).red)) + \ + abs((int)((c1).green) - (int)((c2).green)) + \ + abs((int)((c1).blue) - (int)((c2).blue))) + +/* Added to libpng-1.2.6 JB */ +#define PNG_ROWBYTES(pixel_bits, width) \ + ((pixel_bits) >= 8 ? \ + ((width) * (((png_uint_32)(pixel_bits)) >> 3)) : \ + (( ((width) * ((png_uint_32)(pixel_bits))) + 7) >> 3) ) + +/* PNG_OUT_OF_RANGE returns true if value is outside the range + ideal-delta..ideal+delta. Each argument is evaluated twice. + "ideal" and "delta" should be constants, normally simple + integers, "value" a variable. Added to libpng-1.2.6 JB */ +#define PNG_OUT_OF_RANGE(value, ideal, delta) \ + ( (value) < (ideal)-(delta) || (value) > (ideal)+(delta) ) + +/* variables declared in png.c - only it needs to define PNG_NO_EXTERN */ +#if !defined(PNG_NO_EXTERN) || defined(PNG_ALWAYS_EXTERN) +/* place to hold the signature string for a PNG file. */ +#ifdef PNG_USE_GLOBAL_ARRAYS + PNG_EXPORT_VAR (PNG_CONST png_byte FARDATA) png_sig[8]; +#else +#endif +#endif /* PNG_NO_EXTERN */ + +/* Constant strings for known chunk types. If you need to add a chunk, + * define the name here, and add an invocation of the macro in png.c and + * wherever it's needed. + */ +#define PNG_IHDR png_byte png_IHDR[5] = { 73, 72, 68, 82, '\0'} +#define PNG_IDAT png_byte png_IDAT[5] = { 73, 68, 65, 84, '\0'} +#define PNG_IEND png_byte png_IEND[5] = { 73, 69, 78, 68, '\0'} +#define PNG_PLTE png_byte png_PLTE[5] = { 80, 76, 84, 69, '\0'} +#define PNG_bKGD png_byte png_bKGD[5] = { 98, 75, 71, 68, '\0'} +#define PNG_cHRM png_byte png_cHRM[5] = { 99, 72, 82, 77, '\0'} +#define PNG_gAMA png_byte png_gAMA[5] = {103, 65, 77, 65, '\0'} +#define PNG_hIST png_byte png_hIST[5] = {104, 73, 83, 84, '\0'} +#define PNG_iCCP png_byte png_iCCP[5] = {105, 67, 67, 80, '\0'} +#define PNG_iTXt png_byte png_iTXt[5] = {105, 84, 88, 116, '\0'} +#define PNG_oFFs png_byte png_oFFs[5] = {111, 70, 70, 115, '\0'} +#define PNG_pCAL png_byte png_pCAL[5] = {112, 67, 65, 76, '\0'} +#define PNG_sCAL png_byte png_sCAL[5] = {115, 67, 65, 76, '\0'} +#define PNG_pHYs png_byte png_pHYs[5] = {112, 72, 89, 115, '\0'} +#define PNG_sBIT png_byte png_sBIT[5] = {115, 66, 73, 84, '\0'} +#define PNG_sPLT png_byte png_sPLT[5] = {115, 80, 76, 84, '\0'} +#define PNG_sRGB png_byte png_sRGB[5] = {115, 82, 71, 66, '\0'} +#define PNG_tEXt png_byte png_tEXt[5] = {116, 69, 88, 116, '\0'} +#define PNG_tIME png_byte png_tIME[5] = {116, 73, 77, 69, '\0'} +#define PNG_tRNS png_byte png_tRNS[5] = {116, 82, 78, 83, '\0'} +#define PNG_zTXt png_byte png_zTXt[5] = {122, 84, 88, 116, '\0'} + +#ifdef PNG_USE_GLOBAL_ARRAYS +PNG_EXPORT_VAR (png_byte FARDATA) png_IHDR[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_IDAT[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_IEND[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_PLTE[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_bKGD[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_cHRM[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_gAMA[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_hIST[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_iCCP[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_iTXt[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_oFFs[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_pCAL[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_sCAL[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_pHYs[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_sBIT[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_sPLT[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_sRGB[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_tEXt[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_tIME[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_tRNS[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_zTXt[5]; +#endif /* PNG_USE_GLOBAL_ARRAYS */ + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* Initialize png_ptr struct for reading, and allocate any other memory. + * (old interface - DEPRECATED - use png_create_read_struct instead). + */ +extern PNG_EXPORT(void,png_read_init) PNGARG((png_structp png_ptr)); +#undef png_read_init +#define png_read_init(png_ptr) png_read_init_3(&png_ptr, \ + PNG_LIBPNG_VER_STRING, png_sizeof(png_struct)); +#endif + +extern PNG_EXPORT(void,png_read_init_3) PNGARG((png_structpp ptr_ptr, + png_const_charp user_png_ver, png_size_t png_struct_size)); +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +extern PNG_EXPORT(void,png_read_init_2) PNGARG((png_structp png_ptr, + png_const_charp user_png_ver, png_size_t png_struct_size, png_size_t + png_info_size)); +#endif + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* Initialize png_ptr struct for writing, and allocate any other memory. + * (old interface - DEPRECATED - use png_create_write_struct instead). + */ +extern PNG_EXPORT(void,png_write_init) PNGARG((png_structp png_ptr)); +#undef png_write_init +#define png_write_init(png_ptr) png_write_init_3(&png_ptr, \ + PNG_LIBPNG_VER_STRING, png_sizeof(png_struct)); +#endif + +extern PNG_EXPORT(void,png_write_init_3) PNGARG((png_structpp ptr_ptr, + png_const_charp user_png_ver, png_size_t png_struct_size)); +extern PNG_EXPORT(void,png_write_init_2) PNGARG((png_structp png_ptr, + png_const_charp user_png_ver, png_size_t png_struct_size, png_size_t + png_info_size)); + +/* Allocate memory for an internal libpng struct */ +PNG_EXTERN png_voidp png_create_struct PNGARG((int type)); + +/* Free memory from internal libpng struct */ +PNG_EXTERN void png_destroy_struct PNGARG((png_voidp struct_ptr)); + +PNG_EXTERN png_voidp png_create_struct_2 PNGARG((int type, png_malloc_ptr + malloc_fn, png_voidp mem_ptr)); +PNG_EXTERN void png_destroy_struct_2 PNGARG((png_voidp struct_ptr, + png_free_ptr free_fn, png_voidp mem_ptr)); + +/* Free any memory that info_ptr points to and reset struct. */ +PNG_EXTERN void png_info_destroy PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +#ifndef PNG_1_0_X +/* Function to allocate memory for zlib. */ +PNG_EXTERN voidpf png_zalloc PNGARG((voidpf png_ptr, uInt items, uInt size)); + +/* Function to free memory for zlib */ +PNG_EXTERN void png_zfree PNGARG((voidpf png_ptr, voidpf ptr)); + +#ifdef PNG_SIZE_T +/* Function to convert a sizeof an item to png_sizeof item */ + PNG_EXTERN png_size_t PNGAPI png_convert_size PNGARG((size_t size)); +#endif + +/* Next four functions are used internally as callbacks. PNGAPI is required + * but not PNG_EXPORT. PNGAPI added at libpng version 1.2.3. */ + +PNG_EXTERN void PNGAPI png_default_read_data PNGARG((png_structp png_ptr, + png_bytep data, png_size_t length)); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +PNG_EXTERN void PNGAPI png_push_fill_buffer PNGARG((png_structp png_ptr, + png_bytep buffer, png_size_t length)); +#endif + +PNG_EXTERN void PNGAPI png_default_write_data PNGARG((png_structp png_ptr, + png_bytep data, png_size_t length)); + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) +#if !defined(PNG_NO_STDIO) +PNG_EXTERN void PNGAPI png_default_flush PNGARG((png_structp png_ptr)); +#endif +#endif +#else /* PNG_1_0_X */ +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +PNG_EXTERN void png_push_fill_buffer PNGARG((png_structp png_ptr, + png_bytep buffer, png_size_t length)); +#endif +#endif /* PNG_1_0_X */ + +/* Reset the CRC variable */ +PNG_EXTERN void png_reset_crc PNGARG((png_structp png_ptr)); + +/* Write the "data" buffer to whatever output you are using. */ +PNG_EXTERN void png_write_data PNGARG((png_structp png_ptr, png_bytep data, + png_size_t length)); + +/* Read data from whatever input you are using into the "data" buffer */ +PNG_EXTERN void png_read_data PNGARG((png_structp png_ptr, png_bytep data, + png_size_t length)); + +/* Read bytes into buf, and update png_ptr->crc */ +PNG_EXTERN void png_crc_read PNGARG((png_structp png_ptr, png_bytep buf, + png_size_t length)); + +/* Decompress data in a chunk that uses compression */ +#if defined(PNG_zTXt_SUPPORTED) || defined(PNG_iTXt_SUPPORTED) || \ + defined(PNG_iCCP_SUPPORTED) || defined(PNG_sPLT_SUPPORTED) +PNG_EXTERN png_charp png_decompress_chunk PNGARG((png_structp png_ptr, + int comp_type, png_charp chunkdata, png_size_t chunklength, + png_size_t prefix_length, png_size_t *data_length)); +#endif + +/* Read "skip" bytes, read the file crc, and (optionally) verify png_ptr->crc */ +PNG_EXTERN int png_crc_finish PNGARG((png_structp png_ptr, png_uint_32 skip)); + +/* Read the CRC from the file and compare it to the libpng calculated CRC */ +PNG_EXTERN int png_crc_error PNGARG((png_structp png_ptr)); + +/* Calculate the CRC over a section of data. Note that we are only + * passing a maximum of 64K on systems that have this as a memory limit, + * since this is the maximum buffer size we can specify. + */ +PNG_EXTERN void png_calculate_crc PNGARG((png_structp png_ptr, png_bytep ptr, + png_size_t length)); + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) +PNG_EXTERN void png_flush PNGARG((png_structp png_ptr)); +#endif + +/* simple function to write the signature */ +PNG_EXTERN void png_write_sig PNGARG((png_structp png_ptr)); + +/* write various chunks */ + +/* Write the IHDR chunk, and update the png_struct with the necessary + * information. + */ +PNG_EXTERN void png_write_IHDR PNGARG((png_structp png_ptr, png_uint_32 width, + png_uint_32 height, + int bit_depth, int color_type, int compression_method, int filter_method, + int interlace_method)); + +PNG_EXTERN void png_write_PLTE PNGARG((png_structp png_ptr, png_colorp palette, + png_uint_32 num_pal)); + +PNG_EXTERN void png_write_IDAT PNGARG((png_structp png_ptr, png_bytep data, + png_size_t length)); + +PNG_EXTERN void png_write_IEND PNGARG((png_structp png_ptr)); + +#if defined(PNG_WRITE_gAMA_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_EXTERN void png_write_gAMA PNGARG((png_structp png_ptr, double file_gamma)); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +PNG_EXTERN void png_write_gAMA_fixed PNGARG((png_structp png_ptr, png_fixed_point + file_gamma)); +#endif +#endif + +#if defined(PNG_WRITE_sBIT_SUPPORTED) +PNG_EXTERN void png_write_sBIT PNGARG((png_structp png_ptr, png_color_8p sbit, + int color_type)); +#endif + +#if defined(PNG_WRITE_cHRM_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_EXTERN void png_write_cHRM PNGARG((png_structp png_ptr, + double white_x, double white_y, + double red_x, double red_y, double green_x, double green_y, + double blue_x, double blue_y)); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +PNG_EXTERN void png_write_cHRM_fixed PNGARG((png_structp png_ptr, + png_fixed_point int_white_x, png_fixed_point int_white_y, + png_fixed_point int_red_x, png_fixed_point int_red_y, png_fixed_point + int_green_x, png_fixed_point int_green_y, png_fixed_point int_blue_x, + png_fixed_point int_blue_y)); +#endif +#endif + +#if defined(PNG_WRITE_sRGB_SUPPORTED) +PNG_EXTERN void png_write_sRGB PNGARG((png_structp png_ptr, + int intent)); +#endif + +#if defined(PNG_WRITE_iCCP_SUPPORTED) +PNG_EXTERN void png_write_iCCP PNGARG((png_structp png_ptr, + png_charp name, int compression_type, + png_charp profile, int proflen)); + /* Note to maintainer: profile should be png_bytep */ +#endif + +#if defined(PNG_WRITE_sPLT_SUPPORTED) +PNG_EXTERN void png_write_sPLT PNGARG((png_structp png_ptr, + png_sPLT_tp palette)); +#endif + +#if defined(PNG_WRITE_tRNS_SUPPORTED) +PNG_EXTERN void png_write_tRNS PNGARG((png_structp png_ptr, png_bytep trans, + png_color_16p values, int number, int color_type)); +#endif + +#if defined(PNG_WRITE_bKGD_SUPPORTED) +PNG_EXTERN void png_write_bKGD PNGARG((png_structp png_ptr, + png_color_16p values, int color_type)); +#endif + +#if defined(PNG_WRITE_hIST_SUPPORTED) +PNG_EXTERN void png_write_hIST PNGARG((png_structp png_ptr, png_uint_16p hist, + int num_hist)); +#endif + +#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED) || \ + defined(PNG_WRITE_iCCP_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED) +PNG_EXTERN png_size_t png_check_keyword PNGARG((png_structp png_ptr, + png_charp key, png_charpp new_key)); +#endif + +#if defined(PNG_WRITE_tEXt_SUPPORTED) +PNG_EXTERN void png_write_tEXt PNGARG((png_structp png_ptr, png_charp key, + png_charp text, png_size_t text_len)); +#endif + +#if defined(PNG_WRITE_zTXt_SUPPORTED) +PNG_EXTERN void png_write_zTXt PNGARG((png_structp png_ptr, png_charp key, + png_charp text, png_size_t text_len, int compression)); +#endif + +#if defined(PNG_WRITE_iTXt_SUPPORTED) +PNG_EXTERN void png_write_iTXt PNGARG((png_structp png_ptr, + int compression, png_charp key, png_charp lang, png_charp lang_key, + png_charp text)); +#endif + +#if defined(PNG_TEXT_SUPPORTED) /* Added at version 1.0.14 and 1.2.4 */ +PNG_EXTERN int png_set_text_2 PNGARG((png_structp png_ptr, + png_infop info_ptr, png_textp text_ptr, int num_text)); +#endif + +#if defined(PNG_WRITE_oFFs_SUPPORTED) +PNG_EXTERN void png_write_oFFs PNGARG((png_structp png_ptr, + png_int_32 x_offset, png_int_32 y_offset, int unit_type)); +#endif + +#if defined(PNG_WRITE_pCAL_SUPPORTED) +PNG_EXTERN void png_write_pCAL PNGARG((png_structp png_ptr, png_charp purpose, + png_int_32 X0, png_int_32 X1, int type, int nparams, + png_charp units, png_charpp params)); +#endif + +#if defined(PNG_WRITE_pHYs_SUPPORTED) +PNG_EXTERN void png_write_pHYs PNGARG((png_structp png_ptr, + png_uint_32 x_pixels_per_unit, png_uint_32 y_pixels_per_unit, + int unit_type)); +#endif + +#if defined(PNG_WRITE_tIME_SUPPORTED) +PNG_EXTERN void png_write_tIME PNGARG((png_structp png_ptr, + png_timep mod_time)); +#endif + +#if defined(PNG_WRITE_sCAL_SUPPORTED) +#if defined(PNG_FLOATING_POINT_SUPPORTED) && !defined(PNG_NO_STDIO) +PNG_EXTERN void png_write_sCAL PNGARG((png_structp png_ptr, + int unit, double width, double height)); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +PNG_EXTERN void png_write_sCAL_s PNGARG((png_structp png_ptr, + int unit, png_charp width, png_charp height)); +#endif +#endif +#endif + +/* Called when finished processing a row of data */ +PNG_EXTERN void png_write_finish_row PNGARG((png_structp png_ptr)); + +/* Internal use only. Called before first row of data */ +PNG_EXTERN void png_write_start_row PNGARG((png_structp png_ptr)); + +#if defined(PNG_READ_GAMMA_SUPPORTED) +PNG_EXTERN void png_build_gamma_table PNGARG((png_structp png_ptr)); +#endif + +/* combine a row of data, dealing with alpha, etc. if requested */ +PNG_EXTERN void png_combine_row PNGARG((png_structp png_ptr, png_bytep row, + int mask)); + +#if defined(PNG_READ_INTERLACING_SUPPORTED) +/* expand an interlaced row */ +/* OLD pre-1.0.9 interface: +PNG_EXTERN void png_do_read_interlace PNGARG((png_row_infop row_info, + png_bytep row, int pass, png_uint_32 transformations)); + */ +PNG_EXTERN void png_do_read_interlace PNGARG((png_structp png_ptr)); +#endif + +/* GRR TO DO (2.0 or whenever): simplify other internal calling interfaces */ + +#if defined(PNG_WRITE_INTERLACING_SUPPORTED) +/* grab pixels out of a row for an interlaced pass */ +PNG_EXTERN void png_do_write_interlace PNGARG((png_row_infop row_info, + png_bytep row, int pass)); +#endif + +/* unfilter a row */ +PNG_EXTERN void png_read_filter_row PNGARG((png_structp png_ptr, + png_row_infop row_info, png_bytep row, png_bytep prev_row, int filter)); + +/* Choose the best filter to use and filter the row data */ +PNG_EXTERN void png_write_find_filter PNGARG((png_structp png_ptr, + png_row_infop row_info)); + +/* Write out the filtered row. */ +PNG_EXTERN void png_write_filtered_row PNGARG((png_structp png_ptr, + png_bytep filtered_row)); +/* finish a row while reading, dealing with interlacing passes, etc. */ +PNG_EXTERN void png_read_finish_row PNGARG((png_structp png_ptr)); + +/* initialize the row buffers, etc. */ +PNG_EXTERN void png_read_start_row PNGARG((png_structp png_ptr)); +/* optional call to update the users info structure */ +PNG_EXTERN void png_read_transform_info PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +/* these are the functions that do the transformations */ +#if defined(PNG_READ_FILLER_SUPPORTED) +PNG_EXTERN void png_do_read_filler PNGARG((png_row_infop row_info, + png_bytep row, png_uint_32 filler, png_uint_32 flags)); +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) +PNG_EXTERN void png_do_read_swap_alpha PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +PNG_EXTERN void png_do_write_swap_alpha PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) +PNG_EXTERN void png_do_read_invert_alpha PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +PNG_EXTERN void png_do_write_invert_alpha PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_WRITE_FILLER_SUPPORTED) || \ + defined(PNG_READ_STRIP_ALPHA_SUPPORTED) +PNG_EXTERN void png_do_strip_filler PNGARG((png_row_infop row_info, + png_bytep row, png_uint_32 flags)); +#endif + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +PNG_EXTERN void png_do_swap PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) || defined(PNG_WRITE_PACKSWAP_SUPPORTED) +PNG_EXTERN void png_do_packswap PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +PNG_EXTERN int png_do_rgb_to_gray PNGARG((png_structp png_ptr, png_row_infop + row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) +PNG_EXTERN void png_do_gray_to_rgb PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) +PNG_EXTERN void png_do_unpack PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) +PNG_EXTERN void png_do_unshift PNGARG((png_row_infop row_info, png_bytep row, + png_color_8p sig_bits)); +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +PNG_EXTERN void png_do_invert PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_16_TO_8_SUPPORTED) +PNG_EXTERN void png_do_chop PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) +PNG_EXTERN void png_do_dither PNGARG((png_row_infop row_info, + png_bytep row, png_bytep palette_lookup, png_bytep dither_lookup)); + +# if defined(PNG_CORRECT_PALETTE_SUPPORTED) +PNG_EXTERN void png_correct_palette PNGARG((png_structp png_ptr, + png_colorp palette, int num_palette)); +# endif +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +PNG_EXTERN void png_do_bgr PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_WRITE_PACK_SUPPORTED) +PNG_EXTERN void png_do_pack PNGARG((png_row_infop row_info, + png_bytep row, png_uint_32 bit_depth)); +#endif + +#if defined(PNG_WRITE_SHIFT_SUPPORTED) +PNG_EXTERN void png_do_shift PNGARG((png_row_infop row_info, png_bytep row, + png_color_8p bit_depth)); +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) +#if defined(PNG_READ_GAMMA_SUPPORTED) +PNG_EXTERN void png_do_background PNGARG((png_row_infop row_info, png_bytep row, + png_color_16p trans_values, png_color_16p background, + png_color_16p background_1, + png_bytep gamma_table, png_bytep gamma_from_1, png_bytep gamma_to_1, + png_uint_16pp gamma_16, png_uint_16pp gamma_16_from_1, + png_uint_16pp gamma_16_to_1, int gamma_shift)); +#else +PNG_EXTERN void png_do_background PNGARG((png_row_infop row_info, png_bytep row, + png_color_16p trans_values, png_color_16p background)); +#endif +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) +PNG_EXTERN void png_do_gamma PNGARG((png_row_infop row_info, png_bytep row, + png_bytep gamma_table, png_uint_16pp gamma_16_table, + int gamma_shift)); +#endif + +#if defined(PNG_READ_EXPAND_SUPPORTED) +PNG_EXTERN void png_do_expand_palette PNGARG((png_row_infop row_info, + png_bytep row, png_colorp palette, png_bytep trans, int num_trans)); +PNG_EXTERN void png_do_expand PNGARG((png_row_infop row_info, + png_bytep row, png_color_16p trans_value)); +#endif + +/* The following decodes the appropriate chunks, and does error correction, + * then calls the appropriate callback for the chunk if it is valid. + */ + +/* decode the IHDR chunk */ +PNG_EXTERN void png_handle_IHDR PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +PNG_EXTERN void png_handle_PLTE PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +PNG_EXTERN void png_handle_IEND PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); + +#if defined(PNG_READ_bKGD_SUPPORTED) +PNG_EXTERN void png_handle_bKGD PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_cHRM_SUPPORTED) +PNG_EXTERN void png_handle_cHRM PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_gAMA_SUPPORTED) +PNG_EXTERN void png_handle_gAMA PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_hIST_SUPPORTED) +PNG_EXTERN void png_handle_hIST PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_iCCP_SUPPORTED) +extern void png_handle_iCCP PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif /* PNG_READ_iCCP_SUPPORTED */ + +#if defined(PNG_READ_iTXt_SUPPORTED) +PNG_EXTERN void png_handle_iTXt PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_oFFs_SUPPORTED) +PNG_EXTERN void png_handle_oFFs PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_pCAL_SUPPORTED) +PNG_EXTERN void png_handle_pCAL PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_pHYs_SUPPORTED) +PNG_EXTERN void png_handle_pHYs PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_sBIT_SUPPORTED) +PNG_EXTERN void png_handle_sBIT PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_sCAL_SUPPORTED) +PNG_EXTERN void png_handle_sCAL PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_sPLT_SUPPORTED) +extern void png_handle_sPLT PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif /* PNG_READ_sPLT_SUPPORTED */ + +#if defined(PNG_READ_sRGB_SUPPORTED) +PNG_EXTERN void png_handle_sRGB PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_tEXt_SUPPORTED) +PNG_EXTERN void png_handle_tEXt PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_tIME_SUPPORTED) +PNG_EXTERN void png_handle_tIME PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_tRNS_SUPPORTED) +PNG_EXTERN void png_handle_tRNS PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_zTXt_SUPPORTED) +PNG_EXTERN void png_handle_zTXt PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +PNG_EXTERN void png_handle_unknown PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)); + +PNG_EXTERN void png_check_chunk_name PNGARG((png_structp png_ptr, + png_bytep chunk_name)); + +/* handle the transformations for reading and writing */ +PNG_EXTERN void png_do_read_transformations PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_do_write_transformations PNGARG((png_structp png_ptr)); + +PNG_EXTERN void png_init_read_transformations PNGARG((png_structp png_ptr)); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +PNG_EXTERN void png_push_read_chunk PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_push_read_sig PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_push_check_crc PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_push_crc_skip PNGARG((png_structp png_ptr, + png_uint_32 length)); +PNG_EXTERN void png_push_crc_finish PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_push_save_buffer PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_push_restore_buffer PNGARG((png_structp png_ptr, + png_bytep buffer, png_size_t buffer_length)); +PNG_EXTERN void png_push_read_IDAT PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_process_IDAT_data PNGARG((png_structp png_ptr, + png_bytep buffer, png_size_t buffer_length)); +PNG_EXTERN void png_push_process_row PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_push_handle_unknown PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)); +PNG_EXTERN void png_push_have_info PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_push_have_end PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_push_have_row PNGARG((png_structp png_ptr, png_bytep row)); +PNG_EXTERN void png_push_read_end PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_process_some_data PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_read_push_finish_row PNGARG((png_structp png_ptr)); +#if defined(PNG_READ_tEXt_SUPPORTED) +PNG_EXTERN void png_push_handle_tEXt PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)); +PNG_EXTERN void png_push_read_tEXt PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif +#if defined(PNG_READ_zTXt_SUPPORTED) +PNG_EXTERN void png_push_handle_zTXt PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)); +PNG_EXTERN void png_push_read_zTXt PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif +#if defined(PNG_READ_iTXt_SUPPORTED) +PNG_EXTERN void png_push_handle_iTXt PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)); +PNG_EXTERN void png_push_read_iTXt PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif + +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + +#ifdef PNG_MNG_FEATURES_SUPPORTED +PNG_EXTERN void png_do_read_intrapixel PNGARG((png_row_infop row_info, + png_bytep row)); +PNG_EXTERN void png_do_write_intrapixel PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) +#if defined(PNG_MMX_CODE_SUPPORTED) +/* png.c */ /* PRIVATE */ +PNG_EXTERN void png_init_mmx_flags PNGARG((png_structp png_ptr)); +#endif +#endif + +#if defined(PNG_INCH_CONVERSIONS) && defined(PNG_FLOATING_POINT_SUPPORTED) +PNG_EXTERN png_uint_32 png_get_pixels_per_inch PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +PNG_EXTERN png_uint_32 png_get_x_pixels_per_inch PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +PNG_EXTERN png_uint_32 png_get_y_pixels_per_inch PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +PNG_EXTERN float png_get_x_offset_inches PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +PNG_EXTERN float png_get_y_offset_inches PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +#if defined(PNG_pHYs_SUPPORTED) +PNG_EXTERN png_uint_32 png_get_pHYs_dpi PNGARG((png_structp png_ptr, +png_infop info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type)); +#endif /* PNG_pHYs_SUPPORTED */ +#endif /* PNG_INCH_CONVERSIONS && PNG_FLOATING_POINT_SUPPORTED */ + +/* Maintainer: Put new private prototypes here ^ and in libpngpf.3 */ + +#endif /* PNG_INTERNAL */ + +#ifdef __cplusplus +} +#endif + +#endif /* PNG_VERSION_INFO_ONLY */ +/* do not put anything past this line */ +#endif /* PNG_H */ +/********* End of inlined file: png.h *********/ + + #define PNG_NO_EXTERN + +/********* Start of inlined file: png.c *********/ +/* png.c - location for general purpose libpng functions + * + * Last changed in libpng 1.2.21 [October 4, 2007] + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2007 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + */ + +#define PNG_INTERNAL +#define PNG_NO_EXTERN + +/* Generate a compiler error if there is an old png.h in the search path. */ +typedef version_1_2_21 Your_png_h_is_not_version_1_2_21; + +/* Version information for C files. This had better match the version + * string defined in png.h. */ + +#ifdef PNG_USE_GLOBAL_ARRAYS +/* png_libpng_ver was changed to a function in version 1.0.5c */ +PNG_CONST char png_libpng_ver[18] = PNG_LIBPNG_VER_STRING; + +#ifdef PNG_READ_SUPPORTED + +/* png_sig was changed to a function in version 1.0.5c */ +/* Place to hold the signature string for a PNG file. */ +PNG_CONST png_byte FARDATA png_sig[8] = {137, 80, 78, 71, 13, 10, 26, 10}; +#endif /* PNG_READ_SUPPORTED */ + +/* Invoke global declarations for constant strings for known chunk types */ +PNG_IHDR; +PNG_IDAT; +PNG_IEND; +PNG_PLTE; +PNG_bKGD; +PNG_cHRM; +PNG_gAMA; +PNG_hIST; +PNG_iCCP; +PNG_iTXt; +PNG_oFFs; +PNG_pCAL; +PNG_sCAL; +PNG_pHYs; +PNG_sBIT; +PNG_sPLT; +PNG_sRGB; +PNG_tEXt; +PNG_tIME; +PNG_tRNS; +PNG_zTXt; + +#ifdef PNG_READ_SUPPORTED +/* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + +/* start of interlace block */ +PNG_CONST int FARDATA png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; + +/* offset to next interlace block */ +PNG_CONST int FARDATA png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; + +/* start of interlace block in the y direction */ +PNG_CONST int FARDATA png_pass_ystart[] = {0, 0, 4, 0, 2, 0, 1}; + +/* offset to next interlace block in the y direction */ +PNG_CONST int FARDATA png_pass_yinc[] = {8, 8, 8, 4, 4, 2, 2}; + +/* Height of interlace block. This is not currently used - if you need + * it, uncomment it here and in png.h +PNG_CONST int FARDATA png_pass_height[] = {8, 8, 4, 4, 2, 2, 1}; +*/ + +/* Mask to determine which pixels are valid in a pass */ +PNG_CONST int FARDATA png_pass_mask[] = {0x80, 0x08, 0x88, 0x22, 0xaa, 0x55, 0xff}; + +/* Mask to determine which pixels to overwrite while displaying */ +PNG_CONST int FARDATA png_pass_dsp_mask[] + = {0xff, 0x0f, 0xff, 0x33, 0xff, 0x55, 0xff}; + +#endif /* PNG_READ_SUPPORTED */ +#endif /* PNG_USE_GLOBAL_ARRAYS */ + +/* Tells libpng that we have already handled the first "num_bytes" bytes + * of the PNG file signature. If the PNG data is embedded into another + * stream we can set num_bytes = 8 so that libpng will not attempt to read + * or write any of the magic bytes before it starts on the IHDR. + */ + +#ifdef PNG_READ_SUPPORTED +void PNGAPI +png_set_sig_bytes(png_structp png_ptr, int num_bytes) +{ + if(png_ptr == NULL) return; + png_debug(1, "in png_set_sig_bytes\n"); + if (num_bytes > 8) + png_error(png_ptr, "Too many bytes for PNG signature."); + + png_ptr->sig_bytes = (png_byte)(num_bytes < 0 ? 0 : num_bytes); +} + +/* Checks whether the supplied bytes match the PNG signature. We allow + * checking less than the full 8-byte signature so that those apps that + * already read the first few bytes of a file to determine the file type + * can simply check the remaining bytes for extra assurance. Returns + * an integer less than, equal to, or greater than zero if sig is found, + * respectively, to be less than, to match, or be greater than the correct + * PNG signature (this is the same behaviour as strcmp, memcmp, etc). + */ +int PNGAPI +png_sig_cmp(png_bytep sig, png_size_t start, png_size_t num_to_check) +{ + png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10}; + if (num_to_check > 8) + num_to_check = 8; + else if (num_to_check < 1) + return (-1); + + if (start > 7) + return (-1); + + if (start + num_to_check > 8) + num_to_check = 8 - start; + + return ((int)(png_memcmp(&sig[start], &png_signature[start], num_to_check))); +} + +#if defined(PNG_1_0_X) || defined(PNG_1_2_X) +/* (Obsolete) function to check signature bytes. It does not allow one + * to check a partial signature. This function might be removed in the + * future - use png_sig_cmp(). Returns true (nonzero) if the file is PNG. + */ +int PNGAPI +png_check_sig(png_bytep sig, int num) +{ + return ((int)!png_sig_cmp(sig, (png_size_t)0, (png_size_t)num)); +} +#endif +#endif /* PNG_READ_SUPPORTED */ + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +/* Function to allocate memory for zlib and clear it to 0. */ +#ifdef PNG_1_0_X +voidpf PNGAPI +#else +voidpf /* private */ +#endif +png_zalloc(voidpf png_ptr, uInt items, uInt size) +{ + png_voidp ptr; + png_structp p=(png_structp)png_ptr; + png_uint_32 save_flags=p->flags; + png_uint_32 num_bytes; + + if(png_ptr == NULL) return (NULL); + if (items > PNG_UINT_32_MAX/size) + { + png_warning (p, "Potential overflow in png_zalloc()"); + return (NULL); + } + num_bytes = (png_uint_32)items * size; + + p->flags|=PNG_FLAG_MALLOC_NULL_MEM_OK; + ptr = (png_voidp)png_malloc((png_structp)png_ptr, num_bytes); + p->flags=save_flags; + +#if defined(PNG_1_0_X) && !defined(PNG_NO_ZALLOC_ZERO) + if (ptr == NULL) + return ((voidpf)ptr); + + if (num_bytes > (png_uint_32)0x8000L) + { + png_memset(ptr, 0, (png_size_t)0x8000L); + png_memset((png_bytep)ptr + (png_size_t)0x8000L, 0, + (png_size_t)(num_bytes - (png_uint_32)0x8000L)); + } + else + { + png_memset(ptr, 0, (png_size_t)num_bytes); + } +#endif + return ((voidpf)ptr); +} + +/* function to free memory for zlib */ +#ifdef PNG_1_0_X +void PNGAPI +#else +void /* private */ +#endif +png_zfree(voidpf png_ptr, voidpf ptr) +{ + png_free((png_structp)png_ptr, (png_voidp)ptr); +} + +/* Reset the CRC variable to 32 bits of 1's. Care must be taken + * in case CRC is > 32 bits to leave the top bits 0. + */ +void /* PRIVATE */ +png_reset_crc(png_structp png_ptr) +{ + png_ptr->crc = crc32(0, Z_NULL, 0); +} + +/* Calculate the CRC over a section of data. We can only pass as + * much data to this routine as the largest single buffer size. We + * also check that this data will actually be used before going to the + * trouble of calculating it. + */ +void /* PRIVATE */ +png_calculate_crc(png_structp png_ptr, png_bytep ptr, png_size_t length) +{ + int need_crc = 1; + + if (png_ptr->chunk_name[0] & 0x20) /* ancillary */ + { + if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) == + (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN)) + need_crc = 0; + } + else /* critical */ + { + if (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE) + need_crc = 0; + } + + if (need_crc) + png_ptr->crc = crc32(png_ptr->crc, ptr, (uInt)length); +} + +/* Allocate the memory for an info_struct for the application. We don't + * really need the png_ptr, but it could potentially be useful in the + * future. This should be used in favour of malloc(png_sizeof(png_info)) + * and png_info_init() so that applications that want to use a shared + * libpng don't have to be recompiled if png_info changes size. + */ +png_infop PNGAPI +png_create_info_struct(png_structp png_ptr) +{ + png_infop info_ptr; + + png_debug(1, "in png_create_info_struct\n"); + if(png_ptr == NULL) return (NULL); +#ifdef PNG_USER_MEM_SUPPORTED + info_ptr = (png_infop)png_create_struct_2(PNG_STRUCT_INFO, + png_ptr->malloc_fn, png_ptr->mem_ptr); +#else + info_ptr = (png_infop)png_create_struct(PNG_STRUCT_INFO); +#endif + if (info_ptr != NULL) + png_info_init_3(&info_ptr, png_sizeof(png_info)); + + return (info_ptr); +} + +/* This function frees the memory associated with a single info struct. + * Normally, one would use either png_destroy_read_struct() or + * png_destroy_write_struct() to free an info struct, but this may be + * useful for some applications. + */ +void PNGAPI +png_destroy_info_struct(png_structp png_ptr, png_infopp info_ptr_ptr) +{ + png_infop info_ptr = NULL; + if(png_ptr == NULL) return; + + png_debug(1, "in png_destroy_info_struct\n"); + if (info_ptr_ptr != NULL) + info_ptr = *info_ptr_ptr; + + if (info_ptr != NULL) + { + png_info_destroy(png_ptr, info_ptr); + +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)info_ptr, png_ptr->free_fn, + png_ptr->mem_ptr); +#else + png_destroy_struct((png_voidp)info_ptr); +#endif + *info_ptr_ptr = NULL; + } +} + +/* Initialize the info structure. This is now an internal function (0.89) + * and applications using it are urged to use png_create_info_struct() + * instead. + */ +#if defined(PNG_1_0_X) || defined(PNG_1_2_X) +#undef png_info_init +void PNGAPI +png_info_init(png_infop info_ptr) +{ + /* We only come here via pre-1.0.12-compiled applications */ + png_info_init_3(&info_ptr, 0); +} +#endif + +void PNGAPI +png_info_init_3(png_infopp ptr_ptr, png_size_t png_info_struct_size) +{ + png_infop info_ptr = *ptr_ptr; + + if(info_ptr == NULL) return; + + png_debug(1, "in png_info_init_3\n"); + + if(png_sizeof(png_info) > png_info_struct_size) + { + png_destroy_struct(info_ptr); + info_ptr = (png_infop)png_create_struct(PNG_STRUCT_INFO); + *ptr_ptr = info_ptr; + } + + /* set everything to 0 */ + png_memset(info_ptr, 0, png_sizeof (png_info)); +} + +#ifdef PNG_FREE_ME_SUPPORTED +void PNGAPI +png_data_freer(png_structp png_ptr, png_infop info_ptr, + int freer, png_uint_32 mask) +{ + png_debug(1, "in png_data_freer\n"); + if (png_ptr == NULL || info_ptr == NULL) + return; + if(freer == PNG_DESTROY_WILL_FREE_DATA) + info_ptr->free_me |= mask; + else if(freer == PNG_USER_WILL_FREE_DATA) + info_ptr->free_me &= ~mask; + else + png_warning(png_ptr, + "Unknown freer parameter in png_data_freer."); +} +#endif + +void PNGAPI +png_free_data(png_structp png_ptr, png_infop info_ptr, png_uint_32 mask, + int num) +{ + png_debug(1, "in png_free_data\n"); + if (png_ptr == NULL || info_ptr == NULL) + return; + +#if defined(PNG_TEXT_SUPPORTED) +/* free text item num or (if num == -1) all text items */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_TEXT) & info_ptr->free_me) +#else +if (mask & PNG_FREE_TEXT) +#endif +{ + if (num != -1) + { + if (info_ptr->text && info_ptr->text[num].key) + { + png_free(png_ptr, info_ptr->text[num].key); + info_ptr->text[num].key = NULL; + } + } + else + { + int i; + for (i = 0; i < info_ptr->num_text; i++) + png_free_data(png_ptr, info_ptr, PNG_FREE_TEXT, i); + png_free(png_ptr, info_ptr->text); + info_ptr->text = NULL; + info_ptr->num_text=0; + } +} +#endif + +#if defined(PNG_tRNS_SUPPORTED) +/* free any tRNS entry */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_TRNS) & info_ptr->free_me) +#else +if ((mask & PNG_FREE_TRNS) && (png_ptr->flags & PNG_FLAG_FREE_TRNS)) +#endif +{ + png_free(png_ptr, info_ptr->trans); + info_ptr->valid &= ~PNG_INFO_tRNS; +#ifndef PNG_FREE_ME_SUPPORTED + png_ptr->flags &= ~PNG_FLAG_FREE_TRNS; +#endif + info_ptr->trans = NULL; +} +#endif + +#if defined(PNG_sCAL_SUPPORTED) +/* free any sCAL entry */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_SCAL) & info_ptr->free_me) +#else +if (mask & PNG_FREE_SCAL) +#endif +{ +#if defined(PNG_FIXED_POINT_SUPPORTED) && !defined(PNG_FLOATING_POINT_SUPPORTED) + png_free(png_ptr, info_ptr->scal_s_width); + png_free(png_ptr, info_ptr->scal_s_height); + info_ptr->scal_s_width = NULL; + info_ptr->scal_s_height = NULL; +#endif + info_ptr->valid &= ~PNG_INFO_sCAL; +} +#endif + +#if defined(PNG_pCAL_SUPPORTED) +/* free any pCAL entry */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_PCAL) & info_ptr->free_me) +#else +if (mask & PNG_FREE_PCAL) +#endif +{ + png_free(png_ptr, info_ptr->pcal_purpose); + png_free(png_ptr, info_ptr->pcal_units); + info_ptr->pcal_purpose = NULL; + info_ptr->pcal_units = NULL; + if (info_ptr->pcal_params != NULL) + { + int i; + for (i = 0; i < (int)info_ptr->pcal_nparams; i++) + { + png_free(png_ptr, info_ptr->pcal_params[i]); + info_ptr->pcal_params[i]=NULL; + } + png_free(png_ptr, info_ptr->pcal_params); + info_ptr->pcal_params = NULL; + } + info_ptr->valid &= ~PNG_INFO_pCAL; +} +#endif + +#if defined(PNG_iCCP_SUPPORTED) +/* free any iCCP entry */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_ICCP) & info_ptr->free_me) +#else +if (mask & PNG_FREE_ICCP) +#endif +{ + png_free(png_ptr, info_ptr->iccp_name); + png_free(png_ptr, info_ptr->iccp_profile); + info_ptr->iccp_name = NULL; + info_ptr->iccp_profile = NULL; + info_ptr->valid &= ~PNG_INFO_iCCP; +} +#endif + +#if defined(PNG_sPLT_SUPPORTED) +/* free a given sPLT entry, or (if num == -1) all sPLT entries */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_SPLT) & info_ptr->free_me) +#else +if (mask & PNG_FREE_SPLT) +#endif +{ + if (num != -1) + { + if(info_ptr->splt_palettes) + { + png_free(png_ptr, info_ptr->splt_palettes[num].name); + png_free(png_ptr, info_ptr->splt_palettes[num].entries); + info_ptr->splt_palettes[num].name = NULL; + info_ptr->splt_palettes[num].entries = NULL; + } + } + else + { + if(info_ptr->splt_palettes_num) + { + int i; + for (i = 0; i < (int)info_ptr->splt_palettes_num; i++) + png_free_data(png_ptr, info_ptr, PNG_FREE_SPLT, i); + + png_free(png_ptr, info_ptr->splt_palettes); + info_ptr->splt_palettes = NULL; + info_ptr->splt_palettes_num = 0; + } + info_ptr->valid &= ~PNG_INFO_sPLT; + } +} +#endif + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) + if(png_ptr->unknown_chunk.data) + { + png_free(png_ptr, png_ptr->unknown_chunk.data); + png_ptr->unknown_chunk.data = NULL; + } +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_UNKN) & info_ptr->free_me) +#else +if (mask & PNG_FREE_UNKN) +#endif +{ + if (num != -1) + { + if(info_ptr->unknown_chunks) + { + png_free(png_ptr, info_ptr->unknown_chunks[num].data); + info_ptr->unknown_chunks[num].data = NULL; + } + } + else + { + int i; + + if(info_ptr->unknown_chunks_num) + { + for (i = 0; i < (int)info_ptr->unknown_chunks_num; i++) + png_free_data(png_ptr, info_ptr, PNG_FREE_UNKN, i); + + png_free(png_ptr, info_ptr->unknown_chunks); + info_ptr->unknown_chunks = NULL; + info_ptr->unknown_chunks_num = 0; + } + } +} +#endif + +#if defined(PNG_hIST_SUPPORTED) +/* free any hIST entry */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_HIST) & info_ptr->free_me) +#else +if ((mask & PNG_FREE_HIST) && (png_ptr->flags & PNG_FLAG_FREE_HIST)) +#endif +{ + png_free(png_ptr, info_ptr->hist); + info_ptr->hist = NULL; + info_ptr->valid &= ~PNG_INFO_hIST; +#ifndef PNG_FREE_ME_SUPPORTED + png_ptr->flags &= ~PNG_FLAG_FREE_HIST; +#endif +} +#endif + +/* free any PLTE entry that was internally allocated */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_PLTE) & info_ptr->free_me) +#else +if ((mask & PNG_FREE_PLTE) && (png_ptr->flags & PNG_FLAG_FREE_PLTE)) +#endif +{ + png_zfree(png_ptr, info_ptr->palette); + info_ptr->palette = NULL; + info_ptr->valid &= ~PNG_INFO_PLTE; +#ifndef PNG_FREE_ME_SUPPORTED + png_ptr->flags &= ~PNG_FLAG_FREE_PLTE; +#endif + info_ptr->num_palette = 0; +} + +#if defined(PNG_INFO_IMAGE_SUPPORTED) +/* free any image bits attached to the info structure */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_ROWS) & info_ptr->free_me) +#else +if (mask & PNG_FREE_ROWS) +#endif +{ + if(info_ptr->row_pointers) + { + int row; + for (row = 0; row < (int)info_ptr->height; row++) + { + png_free(png_ptr, info_ptr->row_pointers[row]); + info_ptr->row_pointers[row]=NULL; + } + png_free(png_ptr, info_ptr->row_pointers); + info_ptr->row_pointers=NULL; + } + info_ptr->valid &= ~PNG_INFO_IDAT; +} +#endif + +#ifdef PNG_FREE_ME_SUPPORTED + if(num == -1) + info_ptr->free_me &= ~mask; + else + info_ptr->free_me &= ~(mask & ~PNG_FREE_MUL); +#endif +} + +/* This is an internal routine to free any memory that the info struct is + * pointing to before re-using it or freeing the struct itself. Recall + * that png_free() checks for NULL pointers for us. + */ +void /* PRIVATE */ +png_info_destroy(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_info_destroy\n"); + + png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) + if (png_ptr->num_chunk_list) + { + png_free(png_ptr, png_ptr->chunk_list); + png_ptr->chunk_list=NULL; + png_ptr->num_chunk_list=0; + } +#endif + + png_info_init_3(&info_ptr, png_sizeof(png_info)); +} +#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */ + +/* This function returns a pointer to the io_ptr associated with the user + * functions. The application should free any memory associated with this + * pointer before png_write_destroy() or png_read_destroy() are called. + */ +png_voidp PNGAPI +png_get_io_ptr(png_structp png_ptr) +{ + if(png_ptr == NULL) return (NULL); + return (png_ptr->io_ptr); +} + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +#if !defined(PNG_NO_STDIO) +/* Initialize the default input/output functions for the PNG file. If you + * use your own read or write routines, you can call either png_set_read_fn() + * or png_set_write_fn() instead of png_init_io(). If you have defined + * PNG_NO_STDIO, you must use a function of your own because "FILE *" isn't + * necessarily available. + */ +void PNGAPI +png_init_io(png_structp png_ptr, png_FILE_p fp) +{ + png_debug(1, "in png_init_io\n"); + if(png_ptr == NULL) return; + png_ptr->io_ptr = (png_voidp)fp; +} +#endif + +#if defined(PNG_TIME_RFC1123_SUPPORTED) +/* Convert the supplied time into an RFC 1123 string suitable for use in + * a "Creation Time" or other text-based time string. + */ +png_charp PNGAPI +png_convert_to_rfc1123(png_structp png_ptr, png_timep ptime) +{ + static PNG_CONST char short_months[12][4] = + {"Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + + if(png_ptr == NULL) return (NULL); + if (png_ptr->time_buffer == NULL) + { + png_ptr->time_buffer = (png_charp)png_malloc(png_ptr, (png_uint_32)(29* + png_sizeof(char))); + } + +#if defined(_WIN32_WCE) + { + wchar_t time_buf[29]; + wsprintf(time_buf, TEXT("%d %S %d %02d:%02d:%02d +0000"), + ptime->day % 32, short_months[(ptime->month - 1) % 12], + ptime->year, ptime->hour % 24, ptime->minute % 60, + ptime->second % 61); + WideCharToMultiByte(CP_ACP, 0, time_buf, -1, png_ptr->time_buffer, 29, + NULL, NULL); + } +#else +#ifdef USE_FAR_KEYWORD + { + char near_time_buf[29]; + png_snprintf6(near_time_buf,29,"%d %s %d %02d:%02d:%02d +0000", + ptime->day % 32, short_months[(ptime->month - 1) % 12], + ptime->year, ptime->hour % 24, ptime->minute % 60, + ptime->second % 61); + png_memcpy(png_ptr->time_buffer, near_time_buf, + 29*png_sizeof(char)); + } +#else + png_snprintf6(png_ptr->time_buffer,29,"%d %s %d %02d:%02d:%02d +0000", + ptime->day % 32, short_months[(ptime->month - 1) % 12], + ptime->year, ptime->hour % 24, ptime->minute % 60, + ptime->second % 61); +#endif +#endif /* _WIN32_WCE */ + return ((png_charp)png_ptr->time_buffer); +} +#endif /* PNG_TIME_RFC1123_SUPPORTED */ + +#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */ + +png_charp PNGAPI +png_get_copyright(png_structp png_ptr) +{ + png_ptr = png_ptr; /* silence compiler warning about unused png_ptr */ + return ((png_charp) "\n libpng version 1.2.21 - October 4, 2007\n\ + Copyright (c) 1998-2007 Glenn Randers-Pehrson\n\ + Copyright (c) 1996-1997 Andreas Dilger\n\ + Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.\n"); +} + +/* The following return the library version as a short string in the + * format 1.0.0 through 99.99.99zz. To get the version of *.h files + * used with your application, print out PNG_LIBPNG_VER_STRING, which + * is defined in png.h. + * Note: now there is no difference between png_get_libpng_ver() and + * png_get_header_ver(). Due to the version_nn_nn_nn typedef guard, + * it is guaranteed that png.c uses the correct version of png.h. + */ +png_charp PNGAPI +png_get_libpng_ver(png_structp png_ptr) +{ + /* Version of *.c files used when building libpng */ + png_ptr = png_ptr; /* silence compiler warning about unused png_ptr */ + return ((png_charp) PNG_LIBPNG_VER_STRING); +} + +png_charp PNGAPI +png_get_header_ver(png_structp png_ptr) +{ + /* Version of *.h files used when building libpng */ + png_ptr = png_ptr; /* silence compiler warning about unused png_ptr */ + return ((png_charp) PNG_LIBPNG_VER_STRING); +} + +png_charp PNGAPI +png_get_header_version(png_structp png_ptr) +{ + /* Returns longer string containing both version and date */ + png_ptr = png_ptr; /* silence compiler warning about unused png_ptr */ + return ((png_charp) PNG_HEADER_VERSION_STRING +#ifndef PNG_READ_SUPPORTED + " (NO READ SUPPORT)" +#endif + "\n"); +} + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +int PNGAPI +png_handle_as_unknown(png_structp png_ptr, png_bytep chunk_name) +{ + /* check chunk_name and return "keep" value if it's on the list, else 0 */ + int i; + png_bytep p; + if(png_ptr == NULL || chunk_name == NULL || png_ptr->num_chunk_list<=0) + return 0; + p=png_ptr->chunk_list+png_ptr->num_chunk_list*5-5; + for (i = png_ptr->num_chunk_list; i; i--, p-=5) + if (!png_memcmp(chunk_name, p, 4)) + return ((int)*(p+4)); + return 0; +} +#endif + +/* This function, added to libpng-1.0.6g, is untested. */ +int PNGAPI +png_reset_zstream(png_structp png_ptr) +{ + if (png_ptr == NULL) return Z_STREAM_ERROR; + return (inflateReset(&png_ptr->zstream)); +} +#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */ + +/* This function was added to libpng-1.0.7 */ +png_uint_32 PNGAPI +png_access_version_number(void) +{ + /* Version of *.c files used when building libpng */ + return((png_uint_32) PNG_LIBPNG_VER); +} + +#if defined(PNG_READ_SUPPORTED) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) +#if !defined(PNG_1_0_X) +/* this function was added to libpng 1.2.0 */ +int PNGAPI +png_mmx_support(void) +{ + /* obsolete, to be removed from libpng-1.4.0 */ + return -1; +} +#endif /* PNG_1_0_X */ +#endif /* PNG_READ_SUPPORTED && PNG_ASSEMBLER_CODE_SUPPORTED */ + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +#ifdef PNG_SIZE_T +/* Added at libpng version 1.2.6 */ + PNG_EXTERN png_size_t PNGAPI png_convert_size PNGARG((size_t size)); +png_size_t PNGAPI +png_convert_size(size_t size) +{ + if (size > (png_size_t)-1) + PNG_ABORT(); /* We haven't got access to png_ptr, so no png_error() */ + return ((png_size_t)size); +} +#endif /* PNG_SIZE_T */ +#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */ +/********* End of inlined file: png.c *********/ + +/********* Start of inlined file: pngerror.c *********/ +/* pngerror.c - stub functions for i/o and memory allocation + * + * Last changed in libpng 1.2.20 October 4, 2007 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2007 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This file provides a location for all error handling. Users who + * need special error handling are expected to write replacement functions + * and use png_set_error_fn() to use those functions. See the instructions + * at each function. + */ + +#define PNG_INTERNAL + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +static void /* PRIVATE */ +png_default_error PNGARG((png_structp png_ptr, + png_const_charp error_message)); +#ifndef PNG_NO_WARNINGS +static void /* PRIVATE */ +png_default_warning PNGARG((png_structp png_ptr, + png_const_charp warning_message)); +#endif /* PNG_NO_WARNINGS */ + +/* This function is called whenever there is a fatal error. This function + * should not be changed. If there is a need to handle errors differently, + * you should supply a replacement error function and use png_set_error_fn() + * to replace the error function at run-time. + */ +#ifndef PNG_NO_ERROR_TEXT +void PNGAPI +png_error(png_structp png_ptr, png_const_charp error_message) +{ +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + char msg[16]; + if (png_ptr != NULL) + { + if (png_ptr->flags& + (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) + { + if (*error_message == '#') + { + int offset; + for (offset=1; offset<15; offset++) + if (*(error_message+offset) == ' ') + break; + if (png_ptr->flags&PNG_FLAG_STRIP_ERROR_TEXT) + { + int i; + for (i=0; iflags&PNG_FLAG_STRIP_ERROR_TEXT) + { + msg[0]='0'; + msg[1]='\0'; + error_message=msg; + } + } + } + } +#endif + if (png_ptr != NULL && png_ptr->error_fn != NULL) + (*(png_ptr->error_fn))(png_ptr, error_message); + + /* If the custom handler doesn't exist, or if it returns, + use the default handler, which will not return. */ + png_default_error(png_ptr, error_message); +} +#else +void PNGAPI +png_err(png_structp png_ptr) +{ + if (png_ptr != NULL && png_ptr->error_fn != NULL) + (*(png_ptr->error_fn))(png_ptr, '\0'); + + /* If the custom handler doesn't exist, or if it returns, + use the default handler, which will not return. */ + png_default_error(png_ptr, '\0'); +} +#endif /* PNG_NO_ERROR_TEXT */ + +#ifndef PNG_NO_WARNINGS +/* This function is called whenever there is a non-fatal error. This function + * should not be changed. If there is a need to handle warnings differently, + * you should supply a replacement warning function and use + * png_set_error_fn() to replace the warning function at run-time. + */ +void PNGAPI +png_warning(png_structp png_ptr, png_const_charp warning_message) +{ + int offset = 0; + if (png_ptr != NULL) + { +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + if (png_ptr->flags& + (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) +#endif + { + if (*warning_message == '#') + { + for (offset=1; offset<15; offset++) + if (*(warning_message+offset) == ' ') + break; + } + } + if (png_ptr != NULL && png_ptr->warning_fn != NULL) + (*(png_ptr->warning_fn))(png_ptr, warning_message+offset); + } + else + png_default_warning(png_ptr, warning_message+offset); +} +#endif /* PNG_NO_WARNINGS */ + +/* These utilities are used internally to build an error message that relates + * to the current chunk. The chunk name comes from png_ptr->chunk_name, + * this is used to prefix the message. The message is limited in length + * to 63 bytes, the name characters are output as hex digits wrapped in [] + * if the character is invalid. + */ +#define isnonalpha(c) ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97)) +/*static PNG_CONST char png_digit[16] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F' +};*/ + +#if !defined(PNG_NO_WARNINGS) || !defined(PNG_NO_ERROR_TEXT) +static void /* PRIVATE */ +png_format_buffer(png_structp png_ptr, png_charp buffer, png_const_charp + error_message) +{ + int iout = 0, iin = 0; + + while (iin < 4) + { + int c = png_ptr->chunk_name[iin++]; + if (isnonalpha(c)) + { + buffer[iout++] = '['; + buffer[iout++] = png_digit[(c & 0xf0) >> 4]; + buffer[iout++] = png_digit[c & 0x0f]; + buffer[iout++] = ']'; + } + else + { + buffer[iout++] = (png_byte)c; + } + } + + if (error_message == NULL) + buffer[iout] = 0; + else + { + buffer[iout++] = ':'; + buffer[iout++] = ' '; + png_strncpy(buffer+iout, error_message, 63); + buffer[iout+63] = 0; + } +} + +#ifdef PNG_READ_SUPPORTED +void PNGAPI +png_chunk_error(png_structp png_ptr, png_const_charp error_message) +{ + char msg[18+64]; + if (png_ptr == NULL) + png_error(png_ptr, error_message); + else + { + png_format_buffer(png_ptr, msg, error_message); + png_error(png_ptr, msg); + } +} +#endif /* PNG_READ_SUPPORTED */ +#endif /* !defined(PNG_NO_WARNINGS) || !defined(PNG_NO_ERROR_TEXT) */ + +#ifndef PNG_NO_WARNINGS +void PNGAPI +png_chunk_warning(png_structp png_ptr, png_const_charp warning_message) +{ + char msg[18+64]; + if (png_ptr == NULL) + png_warning(png_ptr, warning_message); + else + { + png_format_buffer(png_ptr, msg, warning_message); + png_warning(png_ptr, msg); + } +} +#endif /* PNG_NO_WARNINGS */ + +/* This is the default error handling function. Note that replacements for + * this function MUST NOT RETURN, or the program will likely crash. This + * function is used by default, or if the program supplies NULL for the + * error function pointer in png_set_error_fn(). + */ +static void /* PRIVATE */ +png_default_error(png_structp png_ptr, png_const_charp error_message) +{ +#ifndef PNG_NO_CONSOLE_IO +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + if (*error_message == '#') + { + int offset; + char error_number[16]; + for (offset=0; offset<15; offset++) + { + error_number[offset] = *(error_message+offset+1); + if (*(error_message+offset) == ' ') + break; + } + if((offset > 1) && (offset < 15)) + { + error_number[offset-1]='\0'; + fprintf(stderr, "libpng error no. %s: %s\n", error_number, + error_message+offset); + } + else + fprintf(stderr, "libpng error: %s, offset=%d\n", error_message,offset); + } + else +#endif + fprintf(stderr, "libpng error: %s\n", error_message); +#endif + +#ifdef PNG_SETJMP_SUPPORTED + if (png_ptr) + { +# ifdef USE_FAR_KEYWORD + { + jmp_buf jmpbuf; + png_memcpy(jmpbuf, png_ptr->jmpbuf, png_sizeof(jmp_buf)); + longjmp(jmpbuf, 1); + } +# else + longjmp(png_ptr->jmpbuf, 1); +# endif + } +#else + PNG_ABORT(); +#endif +#ifdef PNG_NO_CONSOLE_IO + error_message = error_message; /* make compiler happy */ +#endif +} + +#ifndef PNG_NO_WARNINGS +/* This function is called when there is a warning, but the library thinks + * it can continue anyway. Replacement functions don't have to do anything + * here if you don't want them to. In the default configuration, png_ptr is + * not used, but it is passed in case it may be useful. + */ +static void /* PRIVATE */ +png_default_warning(png_structp png_ptr, png_const_charp warning_message) +{ +#ifndef PNG_NO_CONSOLE_IO +# ifdef PNG_ERROR_NUMBERS_SUPPORTED + if (*warning_message == '#') + { + int offset; + char warning_number[16]; + for (offset=0; offset<15; offset++) + { + warning_number[offset]=*(warning_message+offset+1); + if (*(warning_message+offset) == ' ') + break; + } + if((offset > 1) && (offset < 15)) + { + warning_number[offset-1]='\0'; + fprintf(stderr, "libpng warning no. %s: %s\n", warning_number, + warning_message+offset); + } + else + fprintf(stderr, "libpng warning: %s\n", warning_message); + } + else +# endif + fprintf(stderr, "libpng warning: %s\n", warning_message); +#else + warning_message = warning_message; /* make compiler happy */ +#endif + png_ptr = png_ptr; /* make compiler happy */ +} +#endif /* PNG_NO_WARNINGS */ + +/* This function is called when the application wants to use another method + * of handling errors and warnings. Note that the error function MUST NOT + * return to the calling routine or serious problems will occur. The return + * method used in the default routine calls longjmp(png_ptr->jmpbuf, 1) + */ +void PNGAPI +png_set_error_fn(png_structp png_ptr, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warning_fn) +{ + if (png_ptr == NULL) + return; + png_ptr->error_ptr = error_ptr; + png_ptr->error_fn = error_fn; + png_ptr->warning_fn = warning_fn; +} + +/* This function returns a pointer to the error_ptr associated with the user + * functions. The application should free any memory associated with this + * pointer before png_write_destroy and png_read_destroy are called. + */ +png_voidp PNGAPI +png_get_error_ptr(png_structp png_ptr) +{ + if (png_ptr == NULL) + return NULL; + return ((png_voidp)png_ptr->error_ptr); +} + +#ifdef PNG_ERROR_NUMBERS_SUPPORTED +void PNGAPI +png_set_strip_error_numbers(png_structp png_ptr, png_uint_32 strip_mode) +{ + if(png_ptr != NULL) + { + png_ptr->flags &= + ((~(PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT))&strip_mode); + } +} +#endif +#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ +/********* End of inlined file: pngerror.c *********/ + +/********* Start of inlined file: pngget.c *********/ +/* pngget.c - retrieval of values from info struct + * + * Last changed in libpng 1.2.15 January 5, 2007 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2007 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + */ + +#define PNG_INTERNAL + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) + +png_uint_32 PNGAPI +png_get_valid(png_structp png_ptr, png_infop info_ptr, png_uint_32 flag) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->valid & flag); + else + return(0); +} + +png_uint_32 PNGAPI +png_get_rowbytes(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->rowbytes); + else + return(0); +} + +#if defined(PNG_INFO_IMAGE_SUPPORTED) +png_bytepp PNGAPI +png_get_rows(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->row_pointers); + else + return(0); +} +#endif + +#ifdef PNG_EASY_ACCESS_SUPPORTED +/* easy access to info, added in libpng-0.99 */ +png_uint_32 PNGAPI +png_get_image_width(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + { + return info_ptr->width; + } + return (0); +} + +png_uint_32 PNGAPI +png_get_image_height(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + { + return info_ptr->height; + } + return (0); +} + +png_byte PNGAPI +png_get_bit_depth(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + { + return info_ptr->bit_depth; + } + return (0); +} + +png_byte PNGAPI +png_get_color_type(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + { + return info_ptr->color_type; + } + return (0); +} + +png_byte PNGAPI +png_get_filter_type(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + { + return info_ptr->filter_type; + } + return (0); +} + +png_byte PNGAPI +png_get_interlace_type(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + { + return info_ptr->interlace_type; + } + return (0); +} + +png_byte PNGAPI +png_get_compression_type(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + { + return info_ptr->compression_type; + } + return (0); +} + +png_uint_32 PNGAPI +png_get_x_pixels_per_meter(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#if defined(PNG_pHYs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_pHYs) + { + png_debug1(1, "in %s retrieval function\n", "png_get_x_pixels_per_meter"); + if(info_ptr->phys_unit_type != PNG_RESOLUTION_METER) + return (0); + else return (info_ptr->x_pixels_per_unit); + } +#else + return (0); +#endif + return (0); +} + +png_uint_32 PNGAPI +png_get_y_pixels_per_meter(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#if defined(PNG_pHYs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_pHYs) + { + png_debug1(1, "in %s retrieval function\n", "png_get_y_pixels_per_meter"); + if(info_ptr->phys_unit_type != PNG_RESOLUTION_METER) + return (0); + else return (info_ptr->y_pixels_per_unit); + } +#else + return (0); +#endif + return (0); +} + +png_uint_32 PNGAPI +png_get_pixels_per_meter(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#if defined(PNG_pHYs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_pHYs) + { + png_debug1(1, "in %s retrieval function\n", "png_get_pixels_per_meter"); + if(info_ptr->phys_unit_type != PNG_RESOLUTION_METER || + info_ptr->x_pixels_per_unit != info_ptr->y_pixels_per_unit) + return (0); + else return (info_ptr->x_pixels_per_unit); + } +#else + return (0); +#endif + return (0); +} + +#ifdef PNG_FLOATING_POINT_SUPPORTED +float PNGAPI +png_get_pixel_aspect_ratio(png_structp png_ptr, png_infop info_ptr) + { + if (png_ptr != NULL && info_ptr != NULL) +#if defined(PNG_pHYs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_pHYs) + { + png_debug1(1, "in %s retrieval function\n", "png_get_aspect_ratio"); + if (info_ptr->x_pixels_per_unit == 0) + return ((float)0.0); + else + return ((float)((float)info_ptr->y_pixels_per_unit + /(float)info_ptr->x_pixels_per_unit)); + } +#else + return (0.0); +#endif + return ((float)0.0); +} +#endif + +png_int_32 PNGAPI +png_get_x_offset_microns(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#if defined(PNG_oFFs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_oFFs) + { + png_debug1(1, "in %s retrieval function\n", "png_get_x_offset_microns"); + if(info_ptr->offset_unit_type != PNG_OFFSET_MICROMETER) + return (0); + else return (info_ptr->x_offset); + } +#else + return (0); +#endif + return (0); +} + +png_int_32 PNGAPI +png_get_y_offset_microns(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#if defined(PNG_oFFs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_oFFs) + { + png_debug1(1, "in %s retrieval function\n", "png_get_y_offset_microns"); + if(info_ptr->offset_unit_type != PNG_OFFSET_MICROMETER) + return (0); + else return (info_ptr->y_offset); + } +#else + return (0); +#endif + return (0); +} + +png_int_32 PNGAPI +png_get_x_offset_pixels(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#if defined(PNG_oFFs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_oFFs) + { + png_debug1(1, "in %s retrieval function\n", "png_get_x_offset_microns"); + if(info_ptr->offset_unit_type != PNG_OFFSET_PIXEL) + return (0); + else return (info_ptr->x_offset); + } +#else + return (0); +#endif + return (0); +} + +png_int_32 PNGAPI +png_get_y_offset_pixels(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#if defined(PNG_oFFs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_oFFs) + { + png_debug1(1, "in %s retrieval function\n", "png_get_y_offset_microns"); + if(info_ptr->offset_unit_type != PNG_OFFSET_PIXEL) + return (0); + else return (info_ptr->y_offset); + } +#else + return (0); +#endif + return (0); +} + +#if defined(PNG_INCH_CONVERSIONS) && defined(PNG_FLOATING_POINT_SUPPORTED) +png_uint_32 PNGAPI +png_get_pixels_per_inch(png_structp png_ptr, png_infop info_ptr) +{ + return ((png_uint_32)((float)png_get_pixels_per_meter(png_ptr, info_ptr) + *.0254 +.5)); +} + +png_uint_32 PNGAPI +png_get_x_pixels_per_inch(png_structp png_ptr, png_infop info_ptr) +{ + return ((png_uint_32)((float)png_get_x_pixels_per_meter(png_ptr, info_ptr) + *.0254 +.5)); +} + +png_uint_32 PNGAPI +png_get_y_pixels_per_inch(png_structp png_ptr, png_infop info_ptr) +{ + return ((png_uint_32)((float)png_get_y_pixels_per_meter(png_ptr, info_ptr) + *.0254 +.5)); +} + +float PNGAPI +png_get_x_offset_inches(png_structp png_ptr, png_infop info_ptr) +{ + return ((float)png_get_x_offset_microns(png_ptr, info_ptr) + *.00003937); +} + +float PNGAPI +png_get_y_offset_inches(png_structp png_ptr, png_infop info_ptr) +{ + return ((float)png_get_y_offset_microns(png_ptr, info_ptr) + *.00003937); +} + +#if defined(PNG_pHYs_SUPPORTED) +png_uint_32 PNGAPI +png_get_pHYs_dpi(png_structp png_ptr, png_infop info_ptr, + png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type) +{ + png_uint_32 retval = 0; + + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs)) + { + png_debug1(1, "in %s retrieval function\n", "pHYs"); + if (res_x != NULL) + { + *res_x = info_ptr->x_pixels_per_unit; + retval |= PNG_INFO_pHYs; + } + if (res_y != NULL) + { + *res_y = info_ptr->y_pixels_per_unit; + retval |= PNG_INFO_pHYs; + } + if (unit_type != NULL) + { + *unit_type = (int)info_ptr->phys_unit_type; + retval |= PNG_INFO_pHYs; + if(*unit_type == 1) + { + if (res_x != NULL) *res_x = (png_uint_32)(*res_x * .0254 + .50); + if (res_y != NULL) *res_y = (png_uint_32)(*res_y * .0254 + .50); + } + } + } + return (retval); +} +#endif /* PNG_pHYs_SUPPORTED */ +#endif /* PNG_INCH_CONVERSIONS && PNG_FLOATING_POINT_SUPPORTED */ + +/* png_get_channels really belongs in here, too, but it's been around longer */ + +#endif /* PNG_EASY_ACCESS_SUPPORTED */ + +png_byte PNGAPI +png_get_channels(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->channels); + else + return (0); +} + +png_bytep PNGAPI +png_get_signature(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->signature); + else + return (NULL); +} + +#if defined(PNG_bKGD_SUPPORTED) +png_uint_32 PNGAPI +png_get_bKGD(png_structp png_ptr, png_infop info_ptr, + png_color_16p *background) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD) + && background != NULL) + { + png_debug1(1, "in %s retrieval function\n", "bKGD"); + *background = &(info_ptr->background); + return (PNG_INFO_bKGD); + } + return (0); +} +#endif + +#if defined(PNG_cHRM_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_cHRM(png_structp png_ptr, png_infop info_ptr, + double *white_x, double *white_y, double *red_x, double *red_y, + double *green_x, double *green_y, double *blue_x, double *blue_y) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM)) + { + png_debug1(1, "in %s retrieval function\n", "cHRM"); + if (white_x != NULL) + *white_x = (double)info_ptr->x_white; + if (white_y != NULL) + *white_y = (double)info_ptr->y_white; + if (red_x != NULL) + *red_x = (double)info_ptr->x_red; + if (red_y != NULL) + *red_y = (double)info_ptr->y_red; + if (green_x != NULL) + *green_x = (double)info_ptr->x_green; + if (green_y != NULL) + *green_y = (double)info_ptr->y_green; + if (blue_x != NULL) + *blue_x = (double)info_ptr->x_blue; + if (blue_y != NULL) + *blue_y = (double)info_ptr->y_blue; + return (PNG_INFO_cHRM); + } + return (0); +} +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_cHRM_fixed(png_structp png_ptr, png_infop info_ptr, + png_fixed_point *white_x, png_fixed_point *white_y, png_fixed_point *red_x, + png_fixed_point *red_y, png_fixed_point *green_x, png_fixed_point *green_y, + png_fixed_point *blue_x, png_fixed_point *blue_y) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM)) + { + png_debug1(1, "in %s retrieval function\n", "cHRM"); + if (white_x != NULL) + *white_x = info_ptr->int_x_white; + if (white_y != NULL) + *white_y = info_ptr->int_y_white; + if (red_x != NULL) + *red_x = info_ptr->int_x_red; + if (red_y != NULL) + *red_y = info_ptr->int_y_red; + if (green_x != NULL) + *green_x = info_ptr->int_x_green; + if (green_y != NULL) + *green_y = info_ptr->int_y_green; + if (blue_x != NULL) + *blue_x = info_ptr->int_x_blue; + if (blue_y != NULL) + *blue_y = info_ptr->int_y_blue; + return (PNG_INFO_cHRM); + } + return (0); +} +#endif +#endif + +#if defined(PNG_gAMA_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_gAMA(png_structp png_ptr, png_infop info_ptr, double *file_gamma) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA) + && file_gamma != NULL) + { + png_debug1(1, "in %s retrieval function\n", "gAMA"); + *file_gamma = (double)info_ptr->gamma; + return (PNG_INFO_gAMA); + } + return (0); +} +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_gAMA_fixed(png_structp png_ptr, png_infop info_ptr, + png_fixed_point *int_file_gamma) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA) + && int_file_gamma != NULL) + { + png_debug1(1, "in %s retrieval function\n", "gAMA"); + *int_file_gamma = info_ptr->int_gamma; + return (PNG_INFO_gAMA); + } + return (0); +} +#endif +#endif + +#if defined(PNG_sRGB_SUPPORTED) +png_uint_32 PNGAPI +png_get_sRGB(png_structp png_ptr, png_infop info_ptr, int *file_srgb_intent) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB) + && file_srgb_intent != NULL) + { + png_debug1(1, "in %s retrieval function\n", "sRGB"); + *file_srgb_intent = (int)info_ptr->srgb_intent; + return (PNG_INFO_sRGB); + } + return (0); +} +#endif + +#if defined(PNG_iCCP_SUPPORTED) +png_uint_32 PNGAPI +png_get_iCCP(png_structp png_ptr, png_infop info_ptr, + png_charpp name, int *compression_type, + png_charpp profile, png_uint_32 *proflen) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_iCCP) + && name != NULL && profile != NULL && proflen != NULL) + { + png_debug1(1, "in %s retrieval function\n", "iCCP"); + *name = info_ptr->iccp_name; + *profile = info_ptr->iccp_profile; + /* compression_type is a dummy so the API won't have to change + if we introduce multiple compression types later. */ + *proflen = (int)info_ptr->iccp_proflen; + *compression_type = (int)info_ptr->iccp_compression; + return (PNG_INFO_iCCP); + } + return (0); +} +#endif + +#if defined(PNG_sPLT_SUPPORTED) +png_uint_32 PNGAPI +png_get_sPLT(png_structp png_ptr, png_infop info_ptr, + png_sPLT_tpp spalettes) +{ + if (png_ptr != NULL && info_ptr != NULL && spalettes != NULL) + { + *spalettes = info_ptr->splt_palettes; + return ((png_uint_32)info_ptr->splt_palettes_num); + } + return (0); +} +#endif + +#if defined(PNG_hIST_SUPPORTED) +png_uint_32 PNGAPI +png_get_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_16p *hist) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST) + && hist != NULL) + { + png_debug1(1, "in %s retrieval function\n", "hIST"); + *hist = info_ptr->hist; + return (PNG_INFO_hIST); + } + return (0); +} +#endif + +png_uint_32 PNGAPI +png_get_IHDR(png_structp png_ptr, png_infop info_ptr, + png_uint_32 *width, png_uint_32 *height, int *bit_depth, + int *color_type, int *interlace_type, int *compression_type, + int *filter_type) + +{ + if (png_ptr != NULL && info_ptr != NULL && width != NULL && height != NULL && + bit_depth != NULL && color_type != NULL) + { + png_debug1(1, "in %s retrieval function\n", "IHDR"); + *width = info_ptr->width; + *height = info_ptr->height; + *bit_depth = info_ptr->bit_depth; + if (info_ptr->bit_depth < 1 || info_ptr->bit_depth > 16) + png_error(png_ptr, "Invalid bit depth"); + *color_type = info_ptr->color_type; + if (info_ptr->color_type > 6) + png_error(png_ptr, "Invalid color type"); + if (compression_type != NULL) + *compression_type = info_ptr->compression_type; + if (filter_type != NULL) + *filter_type = info_ptr->filter_type; + if (interlace_type != NULL) + *interlace_type = info_ptr->interlace_type; + + /* check for potential overflow of rowbytes */ + if (*width == 0 || *width > PNG_UINT_31_MAX) + png_error(png_ptr, "Invalid image width"); + if (*height == 0 || *height > PNG_UINT_31_MAX) + png_error(png_ptr, "Invalid image height"); + if (info_ptr->width > (PNG_UINT_32_MAX + >> 3) /* 8-byte RGBA pixels */ + - 64 /* bigrowbuf hack */ + - 1 /* filter byte */ + - 7*8 /* rounding of width to multiple of 8 pixels */ + - 8) /* extra max_pixel_depth pad */ + { + png_warning(png_ptr, + "Width too large for libpng to process image data."); + } + return (1); + } + return (0); +} + +#if defined(PNG_oFFs_SUPPORTED) +png_uint_32 PNGAPI +png_get_oFFs(png_structp png_ptr, png_infop info_ptr, + png_int_32 *offset_x, png_int_32 *offset_y, int *unit_type) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs) + && offset_x != NULL && offset_y != NULL && unit_type != NULL) + { + png_debug1(1, "in %s retrieval function\n", "oFFs"); + *offset_x = info_ptr->x_offset; + *offset_y = info_ptr->y_offset; + *unit_type = (int)info_ptr->offset_unit_type; + return (PNG_INFO_oFFs); + } + return (0); +} +#endif + +#if defined(PNG_pCAL_SUPPORTED) +png_uint_32 PNGAPI +png_get_pCAL(png_structp png_ptr, png_infop info_ptr, + png_charp *purpose, png_int_32 *X0, png_int_32 *X1, int *type, int *nparams, + png_charp *units, png_charpp *params) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pCAL) + && purpose != NULL && X0 != NULL && X1 != NULL && type != NULL && + nparams != NULL && units != NULL && params != NULL) + { + png_debug1(1, "in %s retrieval function\n", "pCAL"); + *purpose = info_ptr->pcal_purpose; + *X0 = info_ptr->pcal_X0; + *X1 = info_ptr->pcal_X1; + *type = (int)info_ptr->pcal_type; + *nparams = (int)info_ptr->pcal_nparams; + *units = info_ptr->pcal_units; + *params = info_ptr->pcal_params; + return (PNG_INFO_pCAL); + } + return (0); +} +#endif + +#if defined(PNG_sCAL_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_sCAL(png_structp png_ptr, png_infop info_ptr, + int *unit, double *width, double *height) +{ + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_sCAL)) + { + *unit = info_ptr->scal_unit; + *width = info_ptr->scal_pixel_width; + *height = info_ptr->scal_pixel_height; + return (PNG_INFO_sCAL); + } + return(0); +} +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_sCAL_s(png_structp png_ptr, png_infop info_ptr, + int *unit, png_charpp width, png_charpp height) +{ + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_sCAL)) + { + *unit = info_ptr->scal_unit; + *width = info_ptr->scal_s_width; + *height = info_ptr->scal_s_height; + return (PNG_INFO_sCAL); + } + return(0); +} +#endif +#endif +#endif + +#if defined(PNG_pHYs_SUPPORTED) +png_uint_32 PNGAPI +png_get_pHYs(png_structp png_ptr, png_infop info_ptr, + png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type) +{ + png_uint_32 retval = 0; + + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_pHYs)) + { + png_debug1(1, "in %s retrieval function\n", "pHYs"); + if (res_x != NULL) + { + *res_x = info_ptr->x_pixels_per_unit; + retval |= PNG_INFO_pHYs; + } + if (res_y != NULL) + { + *res_y = info_ptr->y_pixels_per_unit; + retval |= PNG_INFO_pHYs; + } + if (unit_type != NULL) + { + *unit_type = (int)info_ptr->phys_unit_type; + retval |= PNG_INFO_pHYs; + } + } + return (retval); +} +#endif + +png_uint_32 PNGAPI +png_get_PLTE(png_structp png_ptr, png_infop info_ptr, png_colorp *palette, + int *num_palette) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_PLTE) + && palette != NULL) + { + png_debug1(1, "in %s retrieval function\n", "PLTE"); + *palette = info_ptr->palette; + *num_palette = info_ptr->num_palette; + png_debug1(3, "num_palette = %d\n", *num_palette); + return (PNG_INFO_PLTE); + } + return (0); +} + +#if defined(PNG_sBIT_SUPPORTED) +png_uint_32 PNGAPI +png_get_sBIT(png_structp png_ptr, png_infop info_ptr, png_color_8p *sig_bit) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_sBIT) + && sig_bit != NULL) + { + png_debug1(1, "in %s retrieval function\n", "sBIT"); + *sig_bit = &(info_ptr->sig_bit); + return (PNG_INFO_sBIT); + } + return (0); +} +#endif + +#if defined(PNG_TEXT_SUPPORTED) +png_uint_32 PNGAPI +png_get_text(png_structp png_ptr, png_infop info_ptr, png_textp *text_ptr, + int *num_text) +{ + if (png_ptr != NULL && info_ptr != NULL && info_ptr->num_text > 0) + { + png_debug1(1, "in %s retrieval function\n", + (png_ptr->chunk_name[0] == '\0' ? "text" + : (png_const_charp)png_ptr->chunk_name)); + if (text_ptr != NULL) + *text_ptr = info_ptr->text; + if (num_text != NULL) + *num_text = info_ptr->num_text; + return ((png_uint_32)info_ptr->num_text); + } + if (num_text != NULL) + *num_text = 0; + return(0); +} +#endif + +#if defined(PNG_tIME_SUPPORTED) +png_uint_32 PNGAPI +png_get_tIME(png_structp png_ptr, png_infop info_ptr, png_timep *mod_time) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME) + && mod_time != NULL) + { + png_debug1(1, "in %s retrieval function\n", "tIME"); + *mod_time = &(info_ptr->mod_time); + return (PNG_INFO_tIME); + } + return (0); +} +#endif + +#if defined(PNG_tRNS_SUPPORTED) +png_uint_32 PNGAPI +png_get_tRNS(png_structp png_ptr, png_infop info_ptr, + png_bytep *trans, int *num_trans, png_color_16p *trans_values) +{ + png_uint_32 retval = 0; + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS)) + { + png_debug1(1, "in %s retrieval function\n", "tRNS"); + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (trans != NULL) + { + *trans = info_ptr->trans; + retval |= PNG_INFO_tRNS; + } + if (trans_values != NULL) + *trans_values = &(info_ptr->trans_values); + } + else /* if (info_ptr->color_type != PNG_COLOR_TYPE_PALETTE) */ + { + if (trans_values != NULL) + { + *trans_values = &(info_ptr->trans_values); + retval |= PNG_INFO_tRNS; + } + if(trans != NULL) + *trans = NULL; + } + if(num_trans != NULL) + { + *num_trans = info_ptr->num_trans; + retval |= PNG_INFO_tRNS; + } + } + return (retval); +} +#endif + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) +png_uint_32 PNGAPI +png_get_unknown_chunks(png_structp png_ptr, png_infop info_ptr, + png_unknown_chunkpp unknowns) +{ + if (png_ptr != NULL && info_ptr != NULL && unknowns != NULL) + { + *unknowns = info_ptr->unknown_chunks; + return ((png_uint_32)info_ptr->unknown_chunks_num); + } + return (0); +} +#endif + +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +png_byte PNGAPI +png_get_rgb_to_gray_status (png_structp png_ptr) +{ + return (png_byte)(png_ptr? png_ptr->rgb_to_gray_status : 0); +} +#endif + +#if defined(PNG_USER_CHUNKS_SUPPORTED) +png_voidp PNGAPI +png_get_user_chunk_ptr(png_structp png_ptr) +{ + return (png_ptr? png_ptr->user_chunk_ptr : NULL); +} +#endif + +#ifdef PNG_WRITE_SUPPORTED +png_uint_32 PNGAPI +png_get_compression_buffer_size(png_structp png_ptr) +{ + return (png_uint_32)(png_ptr? png_ptr->zbuf_size : 0L); +} +#endif + +#ifdef PNG_ASSEMBLER_CODE_SUPPORTED +#ifndef PNG_1_0_X +/* this function was added to libpng 1.2.0 and should exist by default */ +png_uint_32 PNGAPI +png_get_asm_flags (png_structp png_ptr) +{ + /* obsolete, to be removed from libpng-1.4.0 */ + return (png_ptr? 0L: 0L); +} + +/* this function was added to libpng 1.2.0 and should exist by default */ +png_uint_32 PNGAPI +png_get_asm_flagmask (int flag_select) +{ + /* obsolete, to be removed from libpng-1.4.0 */ + flag_select=flag_select; + return 0L; +} + + /* GRR: could add this: && defined(PNG_MMX_CODE_SUPPORTED) */ +/* this function was added to libpng 1.2.0 */ +png_uint_32 PNGAPI +png_get_mmx_flagmask (int flag_select, int *compilerID) +{ + /* obsolete, to be removed from libpng-1.4.0 */ + flag_select=flag_select; + *compilerID = -1; /* unknown (i.e., no asm/MMX code compiled) */ + return 0L; +} + +/* this function was added to libpng 1.2.0 */ +png_byte PNGAPI +png_get_mmx_bitdepth_threshold (png_structp png_ptr) +{ + /* obsolete, to be removed from libpng-1.4.0 */ + return (png_ptr? 0: 0); +} + +/* this function was added to libpng 1.2.0 */ +png_uint_32 PNGAPI +png_get_mmx_rowbytes_threshold (png_structp png_ptr) +{ + /* obsolete, to be removed from libpng-1.4.0 */ + return (png_ptr? 0L: 0L); +} +#endif /* ?PNG_1_0_X */ +#endif /* ?PNG_ASSEMBLER_CODE_SUPPORTED */ + +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +/* these functions were added to libpng 1.2.6 */ +png_uint_32 PNGAPI +png_get_user_width_max (png_structp png_ptr) +{ + return (png_ptr? png_ptr->user_width_max : 0); +} +png_uint_32 PNGAPI +png_get_user_height_max (png_structp png_ptr) +{ + return (png_ptr? png_ptr->user_height_max : 0); +} +#endif /* ?PNG_SET_USER_LIMITS_SUPPORTED */ + +#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ +/********* End of inlined file: pngget.c *********/ + +/********* Start of inlined file: pngmem.c *********/ +/* pngmem.c - stub functions for memory allocation + * + * Last changed in libpng 1.2.13 November 13, 2006 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2006 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This file provides a location for all memory allocation. Users who + * need special memory handling are expected to supply replacement + * functions for png_malloc() and png_free(), and to use + * png_create_read_struct_2() and png_create_write_struct_2() to + * identify the replacement functions. + */ + +#define PNG_INTERNAL + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) + +/* Borland DOS special memory handler */ +#if defined(__TURBOC__) && !defined(_Windows) && !defined(__FLAT__) +/* if you change this, be sure to change the one in png.h also */ + +/* Allocate memory for a png_struct. The malloc and memset can be replaced + by a single call to calloc() if this is thought to improve performance. */ +png_voidp /* PRIVATE */ +png_create_struct(int type) +{ +#ifdef PNG_USER_MEM_SUPPORTED + return (png_create_struct_2(type, png_malloc_ptr_NULL, png_voidp_NULL)); +} + +/* Alternate version of png_create_struct, for use with user-defined malloc. */ +png_voidp /* PRIVATE */ +png_create_struct_2(int type, png_malloc_ptr malloc_fn, png_voidp mem_ptr) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ + png_size_t size; + png_voidp struct_ptr; + + if (type == PNG_STRUCT_INFO) + size = png_sizeof(png_info); + else if (type == PNG_STRUCT_PNG) + size = png_sizeof(png_struct); + else + return (png_get_copyright(NULL)); + +#ifdef PNG_USER_MEM_SUPPORTED + if(malloc_fn != NULL) + { + png_struct dummy_struct; + png_structp png_ptr = &dummy_struct; + png_ptr->mem_ptr=mem_ptr; + struct_ptr = (*(malloc_fn))(png_ptr, (png_uint_32)size); + } + else +#endif /* PNG_USER_MEM_SUPPORTED */ + struct_ptr = (png_voidp)farmalloc(size); + if (struct_ptr != NULL) + png_memset(struct_ptr, 0, size); + return (struct_ptr); +} + +/* Free memory allocated by a png_create_struct() call */ +void /* PRIVATE */ +png_destroy_struct(png_voidp struct_ptr) +{ +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2(struct_ptr, png_free_ptr_NULL, png_voidp_NULL); +} + +/* Free memory allocated by a png_create_struct() call */ +void /* PRIVATE */ +png_destroy_struct_2(png_voidp struct_ptr, png_free_ptr free_fn, + png_voidp mem_ptr) +{ +#endif + if (struct_ptr != NULL) + { +#ifdef PNG_USER_MEM_SUPPORTED + if(free_fn != NULL) + { + png_struct dummy_struct; + png_structp png_ptr = &dummy_struct; + png_ptr->mem_ptr=mem_ptr; + (*(free_fn))(png_ptr, struct_ptr); + return; + } +#endif /* PNG_USER_MEM_SUPPORTED */ + farfree (struct_ptr); + } +} + +/* Allocate memory. For reasonable files, size should never exceed + * 64K. However, zlib may allocate more then 64K if you don't tell + * it not to. See zconf.h and png.h for more information. zlib does + * need to allocate exactly 64K, so whatever you call here must + * have the ability to do that. + * + * Borland seems to have a problem in DOS mode for exactly 64K. + * It gives you a segment with an offset of 8 (perhaps to store its + * memory stuff). zlib doesn't like this at all, so we have to + * detect and deal with it. This code should not be needed in + * Windows or OS/2 modes, and only in 16 bit mode. This code has + * been updated by Alexander Lehmann for version 0.89 to waste less + * memory. + * + * Note that we can't use png_size_t for the "size" declaration, + * since on some systems a png_size_t is a 16-bit quantity, and as a + * result, we would be truncating potentially larger memory requests + * (which should cause a fatal error) and introducing major problems. + */ + +png_voidp PNGAPI +png_malloc(png_structp png_ptr, png_uint_32 size) +{ + png_voidp ret; + + if (png_ptr == NULL || size == 0) + return (NULL); + +#ifdef PNG_USER_MEM_SUPPORTED + if(png_ptr->malloc_fn != NULL) + ret = ((png_voidp)(*(png_ptr->malloc_fn))(png_ptr, (png_size_t)size)); + else + ret = (png_malloc_default(png_ptr, size)); + if (ret == NULL && (png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out of memory!"); + return (ret); +} + +png_voidp PNGAPI +png_malloc_default(png_structp png_ptr, png_uint_32 size) +{ + png_voidp ret; +#endif /* PNG_USER_MEM_SUPPORTED */ + + if (png_ptr == NULL || size == 0) + return (NULL); + +#ifdef PNG_MAX_MALLOC_64K + if (size > (png_uint_32)65536L) + { + png_warning(png_ptr, "Cannot Allocate > 64K"); + ret = NULL; + } + else +#endif + + if (size != (size_t)size) + ret = NULL; + else if (size == (png_uint_32)65536L) + { + if (png_ptr->offset_table == NULL) + { + /* try to see if we need to do any of this fancy stuff */ + ret = farmalloc(size); + if (ret == NULL || ((png_size_t)ret & 0xffff)) + { + int num_blocks; + png_uint_32 total_size; + png_bytep table; + int i; + png_byte huge * hptr; + + if (ret != NULL) + { + farfree(ret); + ret = NULL; + } + + if(png_ptr->zlib_window_bits > 14) + num_blocks = (int)(1 << (png_ptr->zlib_window_bits - 14)); + else + num_blocks = 1; + if (png_ptr->zlib_mem_level >= 7) + num_blocks += (int)(1 << (png_ptr->zlib_mem_level - 7)); + else + num_blocks++; + + total_size = ((png_uint_32)65536L) * (png_uint_32)num_blocks+16; + + table = farmalloc(total_size); + + if (table == NULL) + { +#ifndef PNG_USER_MEM_SUPPORTED + if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out Of Memory."); /* Note "O" and "M" */ + else + png_warning(png_ptr, "Out Of Memory."); +#endif + return (NULL); + } + + if ((png_size_t)table & 0xfff0) + { +#ifndef PNG_USER_MEM_SUPPORTED + if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, + "Farmalloc didn't return normalized pointer"); + else + png_warning(png_ptr, + "Farmalloc didn't return normalized pointer"); +#endif + return (NULL); + } + + png_ptr->offset_table = table; + png_ptr->offset_table_ptr = farmalloc(num_blocks * + png_sizeof (png_bytep)); + + if (png_ptr->offset_table_ptr == NULL) + { +#ifndef PNG_USER_MEM_SUPPORTED + if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out Of memory."); /* Note "O" and "M" */ + else + png_warning(png_ptr, "Out Of memory."); +#endif + return (NULL); + } + + hptr = (png_byte huge *)table; + if ((png_size_t)hptr & 0xf) + { + hptr = (png_byte huge *)((long)(hptr) & 0xfffffff0L); + hptr = hptr + 16L; /* "hptr += 16L" fails on Turbo C++ 3.0 */ + } + for (i = 0; i < num_blocks; i++) + { + png_ptr->offset_table_ptr[i] = (png_bytep)hptr; + hptr = hptr + (png_uint_32)65536L; /* "+=" fails on TC++3.0 */ + } + + png_ptr->offset_table_number = num_blocks; + png_ptr->offset_table_count = 0; + png_ptr->offset_table_count_free = 0; + } + } + + if (png_ptr->offset_table_count >= png_ptr->offset_table_number) + { +#ifndef PNG_USER_MEM_SUPPORTED + if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out of Memory."); /* Note "o" and "M" */ + else + png_warning(png_ptr, "Out of Memory."); +#endif + return (NULL); + } + + ret = png_ptr->offset_table_ptr[png_ptr->offset_table_count++]; + } + else + ret = farmalloc(size); + +#ifndef PNG_USER_MEM_SUPPORTED + if (ret == NULL) + { + if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out of memory."); /* Note "o" and "m" */ + else + png_warning(png_ptr, "Out of memory."); /* Note "o" and "m" */ + } +#endif + + return (ret); +} + +/* free a pointer allocated by png_malloc(). In the default + configuration, png_ptr is not used, but is passed in case it + is needed. If ptr is NULL, return without taking any action. */ +void PNGAPI +png_free(png_structp png_ptr, png_voidp ptr) +{ + if (png_ptr == NULL || ptr == NULL) + return; + +#ifdef PNG_USER_MEM_SUPPORTED + if (png_ptr->free_fn != NULL) + { + (*(png_ptr->free_fn))(png_ptr, ptr); + return; + } + else png_free_default(png_ptr, ptr); +} + +void PNGAPI +png_free_default(png_structp png_ptr, png_voidp ptr) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ + + if(png_ptr == NULL) return; + + if (png_ptr->offset_table != NULL) + { + int i; + + for (i = 0; i < png_ptr->offset_table_count; i++) + { + if (ptr == png_ptr->offset_table_ptr[i]) + { + ptr = NULL; + png_ptr->offset_table_count_free++; + break; + } + } + if (png_ptr->offset_table_count_free == png_ptr->offset_table_count) + { + farfree(png_ptr->offset_table); + farfree(png_ptr->offset_table_ptr); + png_ptr->offset_table = NULL; + png_ptr->offset_table_ptr = NULL; + } + } + + if (ptr != NULL) + { + farfree(ptr); + } +} + +#else /* Not the Borland DOS special memory handler */ + +/* Allocate memory for a png_struct or a png_info. The malloc and + memset can be replaced by a single call to calloc() if this is thought + to improve performance noticably. */ +png_voidp /* PRIVATE */ +png_create_struct(int type) +{ +#ifdef PNG_USER_MEM_SUPPORTED + return (png_create_struct_2(type, png_malloc_ptr_NULL, png_voidp_NULL)); +} + +/* Allocate memory for a png_struct or a png_info. The malloc and + memset can be replaced by a single call to calloc() if this is thought + to improve performance noticably. */ +png_voidp /* PRIVATE */ +png_create_struct_2(int type, png_malloc_ptr malloc_fn, png_voidp mem_ptr) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ + png_size_t size; + png_voidp struct_ptr; + + if (type == PNG_STRUCT_INFO) + size = png_sizeof(png_info); + else if (type == PNG_STRUCT_PNG) + size = png_sizeof(png_struct); + else + return (NULL); + +#ifdef PNG_USER_MEM_SUPPORTED + if(malloc_fn != NULL) + { + png_struct dummy_struct; + png_structp png_ptr = &dummy_struct; + png_ptr->mem_ptr=mem_ptr; + struct_ptr = (*(malloc_fn))(png_ptr, size); + if (struct_ptr != NULL) + png_memset(struct_ptr, 0, size); + return (struct_ptr); + } +#endif /* PNG_USER_MEM_SUPPORTED */ + +#if defined(__TURBOC__) && !defined(__FLAT__) + struct_ptr = (png_voidp)farmalloc(size); +#else +# if defined(_MSC_VER) && defined(MAXSEG_64K) + struct_ptr = (png_voidp)halloc(size,1); +# else + struct_ptr = (png_voidp)malloc(size); +# endif +#endif + if (struct_ptr != NULL) + png_memset(struct_ptr, 0, size); + + return (struct_ptr); +} + +/* Free memory allocated by a png_create_struct() call */ +void /* PRIVATE */ +png_destroy_struct(png_voidp struct_ptr) +{ +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2(struct_ptr, png_free_ptr_NULL, png_voidp_NULL); +} + +/* Free memory allocated by a png_create_struct() call */ +void /* PRIVATE */ +png_destroy_struct_2(png_voidp struct_ptr, png_free_ptr free_fn, + png_voidp mem_ptr) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ + if (struct_ptr != NULL) + { +#ifdef PNG_USER_MEM_SUPPORTED + if(free_fn != NULL) + { + png_struct dummy_struct; + png_structp png_ptr = &dummy_struct; + png_ptr->mem_ptr=mem_ptr; + (*(free_fn))(png_ptr, struct_ptr); + return; + } +#endif /* PNG_USER_MEM_SUPPORTED */ +#if defined(__TURBOC__) && !defined(__FLAT__) + farfree(struct_ptr); +#else +# if defined(_MSC_VER) && defined(MAXSEG_64K) + hfree(struct_ptr); +# else + free(struct_ptr); +# endif +#endif + } +} + +/* Allocate memory. For reasonable files, size should never exceed + 64K. However, zlib may allocate more then 64K if you don't tell + it not to. See zconf.h and png.h for more information. zlib does + need to allocate exactly 64K, so whatever you call here must + have the ability to do that. */ + +png_voidp PNGAPI +png_malloc(png_structp png_ptr, png_uint_32 size) +{ + png_voidp ret; + +#ifdef PNG_USER_MEM_SUPPORTED + if (png_ptr == NULL || size == 0) + return (NULL); + + if(png_ptr->malloc_fn != NULL) + ret = ((png_voidp)(*(png_ptr->malloc_fn))(png_ptr, (png_size_t)size)); + else + ret = (png_malloc_default(png_ptr, size)); + if (ret == NULL && (png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out of Memory!"); + return (ret); +} + +png_voidp PNGAPI +png_malloc_default(png_structp png_ptr, png_uint_32 size) +{ + png_voidp ret; +#endif /* PNG_USER_MEM_SUPPORTED */ + + if (png_ptr == NULL || size == 0) + return (NULL); + +#ifdef PNG_MAX_MALLOC_64K + if (size > (png_uint_32)65536L) + { +#ifndef PNG_USER_MEM_SUPPORTED + if(png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Cannot Allocate > 64K"); + else +#endif + return NULL; + } +#endif + + /* Check for overflow */ +#if defined(__TURBOC__) && !defined(__FLAT__) + if (size != (unsigned long)size) + ret = NULL; + else + ret = farmalloc(size); +#else +# if defined(_MSC_VER) && defined(MAXSEG_64K) + if (size != (unsigned long)size) + ret = NULL; + else + ret = halloc(size, 1); +# else + if (size != (size_t)size) + ret = NULL; + else + ret = malloc((size_t)size); +# endif +#endif + +#ifndef PNG_USER_MEM_SUPPORTED + if (ret == NULL && (png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out of Memory"); +#endif + + return (ret); +} + +/* Free a pointer allocated by png_malloc(). If ptr is NULL, return + without taking any action. */ +void PNGAPI +png_free(png_structp png_ptr, png_voidp ptr) +{ + if (png_ptr == NULL || ptr == NULL) + return; + +#ifdef PNG_USER_MEM_SUPPORTED + if (png_ptr->free_fn != NULL) + { + (*(png_ptr->free_fn))(png_ptr, ptr); + return; + } + else png_free_default(png_ptr, ptr); +} +void PNGAPI +png_free_default(png_structp png_ptr, png_voidp ptr) +{ + if (png_ptr == NULL || ptr == NULL) + return; + +#endif /* PNG_USER_MEM_SUPPORTED */ + +#if defined(__TURBOC__) && !defined(__FLAT__) + farfree(ptr); +#else +# if defined(_MSC_VER) && defined(MAXSEG_64K) + hfree(ptr); +# else + free(ptr); +# endif +#endif +} + +#endif /* Not Borland DOS special memory handler */ + +#if defined(PNG_1_0_X) +# define png_malloc_warn png_malloc +#else +/* This function was added at libpng version 1.2.3. The png_malloc_warn() + * function will set up png_malloc() to issue a png_warning and return NULL + * instead of issuing a png_error, if it fails to allocate the requested + * memory. + */ +png_voidp PNGAPI +png_malloc_warn(png_structp png_ptr, png_uint_32 size) +{ + png_voidp ptr; + png_uint_32 save_flags; + if(png_ptr == NULL) return (NULL); + + save_flags=png_ptr->flags; + png_ptr->flags|=PNG_FLAG_MALLOC_NULL_MEM_OK; + ptr = (png_voidp)png_malloc((png_structp)png_ptr, size); + png_ptr->flags=save_flags; + return(ptr); +} +#endif + +png_voidp PNGAPI +png_memcpy_check (png_structp png_ptr, png_voidp s1, png_voidp s2, + png_uint_32 length) +{ + png_size_t size; + + size = (png_size_t)length; + if ((png_uint_32)size != length) + png_error(png_ptr,"Overflow in png_memcpy_check."); + + return(png_memcpy (s1, s2, size)); +} + +png_voidp PNGAPI +png_memset_check (png_structp png_ptr, png_voidp s1, int value, + png_uint_32 length) +{ + png_size_t size; + + size = (png_size_t)length; + if ((png_uint_32)size != length) + png_error(png_ptr,"Overflow in png_memset_check."); + + return (png_memset (s1, value, size)); + +} + +#ifdef PNG_USER_MEM_SUPPORTED +/* This function is called when the application wants to use another method + * of allocating and freeing memory. + */ +void PNGAPI +png_set_mem_fn(png_structp png_ptr, png_voidp mem_ptr, png_malloc_ptr + malloc_fn, png_free_ptr free_fn) +{ + if(png_ptr != NULL) { + png_ptr->mem_ptr = mem_ptr; + png_ptr->malloc_fn = malloc_fn; + png_ptr->free_fn = free_fn; + } +} + +/* This function returns a pointer to the mem_ptr associated with the user + * functions. The application should free any memory associated with this + * pointer before png_write_destroy and png_read_destroy are called. + */ +png_voidp PNGAPI +png_get_mem_ptr(png_structp png_ptr) +{ + if(png_ptr == NULL) return (NULL); + return ((png_voidp)png_ptr->mem_ptr); +} +#endif /* PNG_USER_MEM_SUPPORTED */ +#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ +/********* End of inlined file: pngmem.c *********/ + +/********* Start of inlined file: pngread.c *********/ +/* pngread.c - read a PNG file + * + * Last changed in libpng 1.2.20 September 7, 2007 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2007 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This file contains routines that an application calls directly to + * read a PNG file or stream. + */ + +#define PNG_INTERNAL + +#if defined(PNG_READ_SUPPORTED) + +/* Create a PNG structure for reading, and allocate any memory needed. */ +png_structp PNGAPI +png_create_read_struct(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn) +{ + +#ifdef PNG_USER_MEM_SUPPORTED + return (png_create_read_struct_2(user_png_ver, error_ptr, error_fn, + warn_fn, png_voidp_NULL, png_malloc_ptr_NULL, png_free_ptr_NULL)); +} + +/* Alternate create PNG structure for reading, and allocate any memory needed. */ +png_structp PNGAPI +png_create_read_struct_2(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ + + png_structp png_ptr; + +#ifdef PNG_SETJMP_SUPPORTED +#ifdef USE_FAR_KEYWORD + jmp_buf jmpbuf; +#endif +#endif + + int i; + + png_debug(1, "in png_create_read_struct\n"); +#ifdef PNG_USER_MEM_SUPPORTED + png_ptr = (png_structp)png_create_struct_2(PNG_STRUCT_PNG, + (png_malloc_ptr)malloc_fn, (png_voidp)mem_ptr); +#else + png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG); +#endif + if (png_ptr == NULL) + return (NULL); + + /* added at libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + png_ptr->user_width_max=PNG_USER_WIDTH_MAX; + png_ptr->user_height_max=PNG_USER_HEIGHT_MAX; +#endif + +#ifdef PNG_SETJMP_SUPPORTED +#ifdef USE_FAR_KEYWORD + if (setjmp(jmpbuf)) +#else + if (setjmp(png_ptr->jmpbuf)) +#endif + { + png_free(png_ptr, png_ptr->zbuf); + png_ptr->zbuf=NULL; +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)png_ptr, + (png_free_ptr)free_fn, (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)png_ptr); +#endif + return (NULL); + } +#ifdef USE_FAR_KEYWORD + png_memcpy(png_ptr->jmpbuf,jmpbuf,png_sizeof(jmp_buf)); +#endif +#endif + +#ifdef PNG_USER_MEM_SUPPORTED + png_set_mem_fn(png_ptr, mem_ptr, malloc_fn, free_fn); +#endif + + png_set_error_fn(png_ptr, error_ptr, error_fn, warn_fn); + + i=0; + do + { + if(user_png_ver[i] != png_libpng_ver[i]) + png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; + } while (png_libpng_ver[i++]); + + if (png_ptr->flags & PNG_FLAG_LIBRARY_MISMATCH) + { + /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so + * we must recompile any applications that use any older library version. + * For versions after libpng 1.0, we will be compatible, so we need + * only check the first digit. + */ + if (user_png_ver == NULL || user_png_ver[0] != png_libpng_ver[0] || + (user_png_ver[0] == '1' && user_png_ver[2] != png_libpng_ver[2]) || + (user_png_ver[0] == '0' && user_png_ver[2] < '9')) + { +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + char msg[80]; + if (user_png_ver) + { + png_snprintf(msg, 80, + "Application was compiled with png.h from libpng-%.20s", + user_png_ver); + png_warning(png_ptr, msg); + } + png_snprintf(msg, 80, + "Application is running with png.c from libpng-%.20s", + png_libpng_ver); + png_warning(png_ptr, msg); +#endif +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags=0; +#endif + png_error(png_ptr, + "Incompatible libpng version in application and library"); + } + } + + /* initialize zbuf - compression buffer */ + png_ptr->zbuf_size = PNG_ZBUF_SIZE; + png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, + (png_uint_32)png_ptr->zbuf_size); + png_ptr->zstream.zalloc = png_zalloc; + png_ptr->zstream.zfree = png_zfree; + png_ptr->zstream.opaque = (voidpf)png_ptr; + + switch (inflateInit(&png_ptr->zstream)) + { + case Z_OK: /* Do nothing */ break; + case Z_MEM_ERROR: + case Z_STREAM_ERROR: png_error(png_ptr, "zlib memory error"); break; + case Z_VERSION_ERROR: png_error(png_ptr, "zlib version error"); break; + default: png_error(png_ptr, "Unknown zlib error"); + } + + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + + png_set_read_fn(png_ptr, png_voidp_NULL, png_rw_ptr_NULL); + +#ifdef PNG_SETJMP_SUPPORTED +/* Applications that neglect to set up their own setjmp() and then encounter + a png_error() will longjmp here. Since the jmpbuf is then meaningless we + abort instead of returning. */ +#ifdef USE_FAR_KEYWORD + if (setjmp(jmpbuf)) + PNG_ABORT(); + png_memcpy(png_ptr->jmpbuf,jmpbuf,png_sizeof(jmp_buf)); +#else + if (setjmp(png_ptr->jmpbuf)) + PNG_ABORT(); +#endif +#endif + return (png_ptr); +} + +#if defined(PNG_1_0_X) || defined(PNG_1_2_X) +/* Initialize PNG structure for reading, and allocate any memory needed. + This interface is deprecated in favour of the png_create_read_struct(), + and it will disappear as of libpng-1.3.0. */ +#undef png_read_init +void PNGAPI +png_read_init(png_structp png_ptr) +{ + /* We only come here via pre-1.0.7-compiled applications */ + png_read_init_2(png_ptr, "1.0.6 or earlier", 0, 0); +} + +void PNGAPI +png_read_init_2(png_structp png_ptr, png_const_charp user_png_ver, + png_size_t png_struct_size, png_size_t png_info_size) +{ + /* We only come here via pre-1.0.12-compiled applications */ + if(png_ptr == NULL) return; +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + if(png_sizeof(png_struct) > png_struct_size || + png_sizeof(png_info) > png_info_size) + { + char msg[80]; + png_ptr->warning_fn=NULL; + if (user_png_ver) + { + png_snprintf(msg, 80, + "Application was compiled with png.h from libpng-%.20s", + user_png_ver); + png_warning(png_ptr, msg); + } + png_snprintf(msg, 80, + "Application is running with png.c from libpng-%.20s", + png_libpng_ver); + png_warning(png_ptr, msg); + } +#endif + if(png_sizeof(png_struct) > png_struct_size) + { + png_ptr->error_fn=NULL; +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags=0; +#endif + png_error(png_ptr, + "The png struct allocated by the application for reading is too small."); + } + if(png_sizeof(png_info) > png_info_size) + { + png_ptr->error_fn=NULL; +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags=0; +#endif + png_error(png_ptr, + "The info struct allocated by application for reading is too small."); + } + png_read_init_3(&png_ptr, user_png_ver, png_struct_size); +} +#endif /* PNG_1_0_X || PNG_1_2_X */ + +void PNGAPI +png_read_init_3(png_structpp ptr_ptr, png_const_charp user_png_ver, + png_size_t png_struct_size) +{ +#ifdef PNG_SETJMP_SUPPORTED + jmp_buf tmp_jmp; /* to save current jump buffer */ +#endif + + int i=0; + + png_structp png_ptr=*ptr_ptr; + + if(png_ptr == NULL) return; + + do + { + if(user_png_ver[i] != png_libpng_ver[i]) + { +#ifdef PNG_LEGACY_SUPPORTED + png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; +#else + png_ptr->warning_fn=NULL; + png_warning(png_ptr, + "Application uses deprecated png_read_init() and should be recompiled."); + break; +#endif + } + } while (png_libpng_ver[i++]); + + png_debug(1, "in png_read_init_3\n"); + +#ifdef PNG_SETJMP_SUPPORTED + /* save jump buffer and error functions */ + png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof (jmp_buf)); +#endif + + if(png_sizeof(png_struct) > png_struct_size) + { + png_destroy_struct(png_ptr); + *ptr_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG); + png_ptr = *ptr_ptr; + } + + /* reset all variables to 0 */ + png_memset(png_ptr, 0, png_sizeof (png_struct)); + +#ifdef PNG_SETJMP_SUPPORTED + /* restore jump buffer */ + png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof (jmp_buf)); +#endif + + /* added at libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + png_ptr->user_width_max=PNG_USER_WIDTH_MAX; + png_ptr->user_height_max=PNG_USER_HEIGHT_MAX; +#endif + + /* initialize zbuf - compression buffer */ + png_ptr->zbuf_size = PNG_ZBUF_SIZE; + png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, + (png_uint_32)png_ptr->zbuf_size); + png_ptr->zstream.zalloc = png_zalloc; + png_ptr->zstream.zfree = png_zfree; + png_ptr->zstream.opaque = (voidpf)png_ptr; + + switch (inflateInit(&png_ptr->zstream)) + { + case Z_OK: /* Do nothing */ break; + case Z_MEM_ERROR: + case Z_STREAM_ERROR: png_error(png_ptr, "zlib memory"); break; + case Z_VERSION_ERROR: png_error(png_ptr, "zlib version"); break; + default: png_error(png_ptr, "Unknown zlib error"); + } + + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + + png_set_read_fn(png_ptr, png_voidp_NULL, png_rw_ptr_NULL); +} + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* Read the information before the actual image data. This has been + * changed in v0.90 to allow reading a file that already has the magic + * bytes read from the stream. You can tell libpng how many bytes have + * been read from the beginning of the stream (up to the maximum of 8) + * via png_set_sig_bytes(), and we will only check the remaining bytes + * here. The application can then have access to the signature bytes we + * read if it is determined that this isn't a valid PNG file. + */ +void PNGAPI +png_read_info(png_structp png_ptr, png_infop info_ptr) +{ + if(png_ptr == NULL) return; + png_debug(1, "in png_read_info\n"); + /* If we haven't checked all of the PNG signature bytes, do so now. */ + if (png_ptr->sig_bytes < 8) + { + png_size_t num_checked = png_ptr->sig_bytes, + num_to_check = 8 - num_checked; + + png_read_data(png_ptr, &(info_ptr->signature[num_checked]), num_to_check); + png_ptr->sig_bytes = 8; + + if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check)) + { + if (num_checked < 4 && + png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4)) + png_error(png_ptr, "Not a PNG file"); + else + png_error(png_ptr, "PNG file corrupted by ASCII conversion"); + } + if (num_checked < 3) + png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE; + } + + for(;;) + { +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_CONST PNG_IHDR; + PNG_CONST PNG_IDAT; + PNG_CONST PNG_IEND; + PNG_CONST PNG_PLTE; +#if defined(PNG_READ_bKGD_SUPPORTED) + PNG_CONST PNG_bKGD; +#endif +#if defined(PNG_READ_cHRM_SUPPORTED) + PNG_CONST PNG_cHRM; +#endif +#if defined(PNG_READ_gAMA_SUPPORTED) + PNG_CONST PNG_gAMA; +#endif +#if defined(PNG_READ_hIST_SUPPORTED) + PNG_CONST PNG_hIST; +#endif +#if defined(PNG_READ_iCCP_SUPPORTED) + PNG_CONST PNG_iCCP; +#endif +#if defined(PNG_READ_iTXt_SUPPORTED) + PNG_CONST PNG_iTXt; +#endif +#if defined(PNG_READ_oFFs_SUPPORTED) + PNG_CONST PNG_oFFs; +#endif +#if defined(PNG_READ_pCAL_SUPPORTED) + PNG_CONST PNG_pCAL; +#endif +#if defined(PNG_READ_pHYs_SUPPORTED) + PNG_CONST PNG_pHYs; +#endif +#if defined(PNG_READ_sBIT_SUPPORTED) + PNG_CONST PNG_sBIT; +#endif +#if defined(PNG_READ_sCAL_SUPPORTED) + PNG_CONST PNG_sCAL; +#endif +#if defined(PNG_READ_sPLT_SUPPORTED) + PNG_CONST PNG_sPLT; +#endif +#if defined(PNG_READ_sRGB_SUPPORTED) + PNG_CONST PNG_sRGB; +#endif +#if defined(PNG_READ_tEXt_SUPPORTED) + PNG_CONST PNG_tEXt; +#endif +#if defined(PNG_READ_tIME_SUPPORTED) + PNG_CONST PNG_tIME; +#endif +#if defined(PNG_READ_tRNS_SUPPORTED) + PNG_CONST PNG_tRNS; +#endif +#if defined(PNG_READ_zTXt_SUPPORTED) + PNG_CONST PNG_zTXt; +#endif +#endif /* PNG_USE_LOCAL_ARRAYS */ + png_byte chunk_length[4]; + png_uint_32 length; + + png_read_data(png_ptr, chunk_length, 4); + length = png_get_uint_31(png_ptr,chunk_length); + + png_reset_crc(png_ptr); + png_crc_read(png_ptr, png_ptr->chunk_name, 4); + + png_debug2(0, "Reading %s chunk, length=%lu.\n", png_ptr->chunk_name, + length); + + /* This should be a binary subdivision search or a hash for + * matching the chunk name rather than a linear search. + */ + if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + if(png_ptr->mode & PNG_AFTER_IDAT) + png_ptr->mode |= PNG_HAVE_CHUNK_AFTER_IDAT; + + if (!png_memcmp(png_ptr->chunk_name, png_IHDR, 4)) + png_handle_IHDR(png_ptr, info_ptr, length); + else if (!png_memcmp(png_ptr->chunk_name, png_IEND, 4)) + png_handle_IEND(png_ptr, info_ptr, length); +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + else if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name)) + { + if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + png_ptr->mode |= PNG_HAVE_IDAT; + png_handle_unknown(png_ptr, info_ptr, length); + if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4)) + png_ptr->mode |= PNG_HAVE_PLTE; + else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + { + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before IDAT"); + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + !(png_ptr->mode & PNG_HAVE_PLTE)) + png_error(png_ptr, "Missing PLTE before IDAT"); + break; + } + } +#endif + else if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4)) + png_handle_PLTE(png_ptr, info_ptr, length); + else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + { + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before IDAT"); + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + !(png_ptr->mode & PNG_HAVE_PLTE)) + png_error(png_ptr, "Missing PLTE before IDAT"); + + png_ptr->idat_size = length; + png_ptr->mode |= PNG_HAVE_IDAT; + break; + } +#if defined(PNG_READ_bKGD_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_bKGD, 4)) + png_handle_bKGD(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_cHRM_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_cHRM, 4)) + png_handle_cHRM(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_gAMA_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_gAMA, 4)) + png_handle_gAMA(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_hIST_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_hIST, 4)) + png_handle_hIST(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_oFFs_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_oFFs, 4)) + png_handle_oFFs(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_pCAL_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_pCAL, 4)) + png_handle_pCAL(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_sCAL_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sCAL, 4)) + png_handle_sCAL(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_pHYs_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_pHYs, 4)) + png_handle_pHYs(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_sBIT_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sBIT, 4)) + png_handle_sBIT(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_sRGB_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sRGB, 4)) + png_handle_sRGB(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_iCCP_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_iCCP, 4)) + png_handle_iCCP(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_sPLT_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sPLT, 4)) + png_handle_sPLT(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_tEXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tEXt, 4)) + png_handle_tEXt(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_tIME_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tIME, 4)) + png_handle_tIME(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_tRNS_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tRNS, 4)) + png_handle_tRNS(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_zTXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_zTXt, 4)) + png_handle_zTXt(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_iTXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_iTXt, 4)) + png_handle_iTXt(png_ptr, info_ptr, length); +#endif + else + png_handle_unknown(png_ptr, info_ptr, length); + } +} +#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */ + +/* optional call to update the users info_ptr structure */ +void PNGAPI +png_read_update_info(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_read_update_info\n"); + if(png_ptr == NULL) return; + if (!(png_ptr->flags & PNG_FLAG_ROW_INIT)) + png_read_start_row(png_ptr); + else + png_warning(png_ptr, + "Ignoring extra png_read_update_info() call; row buffer not reallocated"); + png_read_transform_info(png_ptr, info_ptr); +} + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* Initialize palette, background, etc, after transformations + * are set, but before any reading takes place. This allows + * the user to obtain a gamma-corrected palette, for example. + * If the user doesn't call this, we will do it ourselves. + */ +void PNGAPI +png_start_read_image(png_structp png_ptr) +{ + png_debug(1, "in png_start_read_image\n"); + if(png_ptr == NULL) return; + if (!(png_ptr->flags & PNG_FLAG_ROW_INIT)) + png_read_start_row(png_ptr); +} +#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */ + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +void PNGAPI +png_read_row(png_structp png_ptr, png_bytep row, png_bytep dsp_row) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_CONST PNG_IDAT; + PNG_CONST int png_pass_dsp_mask[7] = {0xff, 0x0f, 0xff, 0x33, 0xff, 0x55, + 0xff}; + PNG_CONST int png_pass_mask[7] = {0x80, 0x08, 0x88, 0x22, 0xaa, 0x55, 0xff}; +#endif + int ret; + if(png_ptr == NULL) return; + png_debug2(1, "in png_read_row (row %lu, pass %d)\n", + png_ptr->row_number, png_ptr->pass); + if (!(png_ptr->flags & PNG_FLAG_ROW_INIT)) + png_read_start_row(png_ptr); + if (png_ptr->row_number == 0 && png_ptr->pass == 0) + { + /* check for transforms that have been set but were defined out */ +#if defined(PNG_WRITE_INVERT_SUPPORTED) && !defined(PNG_READ_INVERT_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_MONO) + png_warning(png_ptr, "PNG_READ_INVERT_SUPPORTED is not defined."); +#endif +#if defined(PNG_WRITE_FILLER_SUPPORTED) && !defined(PNG_READ_FILLER_SUPPORTED) + if (png_ptr->transformations & PNG_FILLER) + png_warning(png_ptr, "PNG_READ_FILLER_SUPPORTED is not defined."); +#endif +#if defined(PNG_WRITE_PACKSWAP_SUPPORTED) && !defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + png_warning(png_ptr, "PNG_READ_PACKSWAP_SUPPORTED is not defined."); +#endif +#if defined(PNG_WRITE_PACK_SUPPORTED) && !defined(PNG_READ_PACK_SUPPORTED) + if (png_ptr->transformations & PNG_PACK) + png_warning(png_ptr, "PNG_READ_PACK_SUPPORTED is not defined."); +#endif +#if defined(PNG_WRITE_SHIFT_SUPPORTED) && !defined(PNG_READ_SHIFT_SUPPORTED) + if (png_ptr->transformations & PNG_SHIFT) + png_warning(png_ptr, "PNG_READ_SHIFT_SUPPORTED is not defined."); +#endif +#if defined(PNG_WRITE_BGR_SUPPORTED) && !defined(PNG_READ_BGR_SUPPORTED) + if (png_ptr->transformations & PNG_BGR) + png_warning(png_ptr, "PNG_READ_BGR_SUPPORTED is not defined."); +#endif +#if defined(PNG_WRITE_SWAP_SUPPORTED) && !defined(PNG_READ_SWAP_SUPPORTED) + if (png_ptr->transformations & PNG_SWAP_BYTES) + png_warning(png_ptr, "PNG_READ_SWAP_SUPPORTED is not defined."); +#endif + } + +#if defined(PNG_READ_INTERLACING_SUPPORTED) + /* if interlaced and we do not need a new row, combine row and return */ + if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE)) + { + switch (png_ptr->pass) + { + case 0: + if (png_ptr->row_number & 0x07) + { + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + png_read_finish_row(png_ptr); + return; + } + break; + case 1: + if ((png_ptr->row_number & 0x07) || png_ptr->width < 5) + { + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + png_read_finish_row(png_ptr); + return; + } + break; + case 2: + if ((png_ptr->row_number & 0x07) != 4) + { + if (dsp_row != NULL && (png_ptr->row_number & 4)) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + png_read_finish_row(png_ptr); + return; + } + break; + case 3: + if ((png_ptr->row_number & 3) || png_ptr->width < 3) + { + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + png_read_finish_row(png_ptr); + return; + } + break; + case 4: + if ((png_ptr->row_number & 3) != 2) + { + if (dsp_row != NULL && (png_ptr->row_number & 2)) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + png_read_finish_row(png_ptr); + return; + } + break; + case 5: + if ((png_ptr->row_number & 1) || png_ptr->width < 2) + { + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + png_read_finish_row(png_ptr); + return; + } + break; + case 6: + if (!(png_ptr->row_number & 1)) + { + png_read_finish_row(png_ptr); + return; + } + break; + } + } +#endif + + if (!(png_ptr->mode & PNG_HAVE_IDAT)) + png_error(png_ptr, "Invalid attempt to read row data"); + + png_ptr->zstream.next_out = png_ptr->row_buf; + png_ptr->zstream.avail_out = (uInt)png_ptr->irowbytes; + do + { + if (!(png_ptr->zstream.avail_in)) + { + while (!png_ptr->idat_size) + { + png_byte chunk_length[4]; + + png_crc_finish(png_ptr, 0); + + png_read_data(png_ptr, chunk_length, 4); + png_ptr->idat_size = png_get_uint_31(png_ptr,chunk_length); + + png_reset_crc(png_ptr); + png_crc_read(png_ptr, png_ptr->chunk_name, 4); + if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + png_error(png_ptr, "Not enough image data"); + } + png_ptr->zstream.avail_in = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_in = png_ptr->zbuf; + if (png_ptr->zbuf_size > png_ptr->idat_size) + png_ptr->zstream.avail_in = (uInt)png_ptr->idat_size; + png_crc_read(png_ptr, png_ptr->zbuf, + (png_size_t)png_ptr->zstream.avail_in); + png_ptr->idat_size -= png_ptr->zstream.avail_in; + } + ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH); + if (ret == Z_STREAM_END) + { + if (png_ptr->zstream.avail_out || png_ptr->zstream.avail_in || + png_ptr->idat_size) + png_error(png_ptr, "Extra compressed data"); + png_ptr->mode |= PNG_AFTER_IDAT; + png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; + break; + } + if (ret != Z_OK) + png_error(png_ptr, png_ptr->zstream.msg ? png_ptr->zstream.msg : + "Decompression error"); + + } while (png_ptr->zstream.avail_out); + + png_ptr->row_info.color_type = png_ptr->color_type; + png_ptr->row_info.width = png_ptr->iwidth; + png_ptr->row_info.channels = png_ptr->channels; + png_ptr->row_info.bit_depth = png_ptr->bit_depth; + png_ptr->row_info.pixel_depth = png_ptr->pixel_depth; + png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth, + png_ptr->row_info.width); + + if(png_ptr->row_buf[0]) + png_read_filter_row(png_ptr, &(png_ptr->row_info), + png_ptr->row_buf + 1, png_ptr->prev_row + 1, + (int)(png_ptr->row_buf[0])); + + png_memcpy_check(png_ptr, png_ptr->prev_row, png_ptr->row_buf, + png_ptr->rowbytes + 1); + +#if defined(PNG_MNG_FEATURES_SUPPORTED) + if((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING)) + { + /* Intrapixel differencing */ + png_do_read_intrapixel(&(png_ptr->row_info), png_ptr->row_buf + 1); + } +#endif + + if (png_ptr->transformations || (png_ptr->flags&PNG_FLAG_STRIP_ALPHA)) + png_do_read_transformations(png_ptr); + +#if defined(PNG_READ_INTERLACING_SUPPORTED) + /* blow up interlaced rows to full size */ + if (png_ptr->interlaced && + (png_ptr->transformations & PNG_INTERLACE)) + { + if (png_ptr->pass < 6) +/* old interface (pre-1.0.9): + png_do_read_interlace(&(png_ptr->row_info), + png_ptr->row_buf + 1, png_ptr->pass, png_ptr->transformations); + */ + png_do_read_interlace(png_ptr); + + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + if (row != NULL) + png_combine_row(png_ptr, row, + png_pass_mask[png_ptr->pass]); + } + else +#endif + { + if (row != NULL) + png_combine_row(png_ptr, row, 0xff); + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, 0xff); + } + png_read_finish_row(png_ptr); + + if (png_ptr->read_row_fn != NULL) + (*(png_ptr->read_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass); +} +#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */ + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* Read one or more rows of image data. If the image is interlaced, + * and png_set_interlace_handling() has been called, the rows need to + * contain the contents of the rows from the previous pass. If the + * image has alpha or transparency, and png_handle_alpha()[*] has been + * called, the rows contents must be initialized to the contents of the + * screen. + * + * "row" holds the actual image, and pixels are placed in it + * as they arrive. If the image is displayed after each pass, it will + * appear to "sparkle" in. "display_row" can be used to display a + * "chunky" progressive image, with finer detail added as it becomes + * available. If you do not want this "chunky" display, you may pass + * NULL for display_row. If you do not want the sparkle display, and + * you have not called png_handle_alpha(), you may pass NULL for rows. + * If you have called png_handle_alpha(), and the image has either an + * alpha channel or a transparency chunk, you must provide a buffer for + * rows. In this case, you do not have to provide a display_row buffer + * also, but you may. If the image is not interlaced, or if you have + * not called png_set_interlace_handling(), the display_row buffer will + * be ignored, so pass NULL to it. + * + * [*] png_handle_alpha() does not exist yet, as of this version of libpng + */ + +void PNGAPI +png_read_rows(png_structp png_ptr, png_bytepp row, + png_bytepp display_row, png_uint_32 num_rows) +{ + png_uint_32 i; + png_bytepp rp; + png_bytepp dp; + + png_debug(1, "in png_read_rows\n"); + if(png_ptr == NULL) return; + rp = row; + dp = display_row; + if (rp != NULL && dp != NULL) + for (i = 0; i < num_rows; i++) + { + png_bytep rptr = *rp++; + png_bytep dptr = *dp++; + + png_read_row(png_ptr, rptr, dptr); + } + else if(rp != NULL) + for (i = 0; i < num_rows; i++) + { + png_bytep rptr = *rp; + png_read_row(png_ptr, rptr, png_bytep_NULL); + rp++; + } + else if(dp != NULL) + for (i = 0; i < num_rows; i++) + { + png_bytep dptr = *dp; + png_read_row(png_ptr, png_bytep_NULL, dptr); + dp++; + } +} +#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */ + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* Read the entire image. If the image has an alpha channel or a tRNS + * chunk, and you have called png_handle_alpha()[*], you will need to + * initialize the image to the current image that PNG will be overlaying. + * We set the num_rows again here, in case it was incorrectly set in + * png_read_start_row() by a call to png_read_update_info() or + * png_start_read_image() if png_set_interlace_handling() wasn't called + * prior to either of these functions like it should have been. You can + * only call this function once. If you desire to have an image for + * each pass of a interlaced image, use png_read_rows() instead. + * + * [*] png_handle_alpha() does not exist yet, as of this version of libpng + */ +void PNGAPI +png_read_image(png_structp png_ptr, png_bytepp image) +{ + png_uint_32 i,image_height; + int pass, j; + png_bytepp rp; + + png_debug(1, "in png_read_image\n"); + if(png_ptr == NULL) return; + +#ifdef PNG_READ_INTERLACING_SUPPORTED + pass = png_set_interlace_handling(png_ptr); +#else + if (png_ptr->interlaced) + png_error(png_ptr, + "Cannot read interlaced image -- interlace handler disabled."); + pass = 1; +#endif + + image_height=png_ptr->height; + png_ptr->num_rows = image_height; /* Make sure this is set correctly */ + + for (j = 0; j < pass; j++) + { + rp = image; + for (i = 0; i < image_height; i++) + { + png_read_row(png_ptr, *rp, png_bytep_NULL); + rp++; + } + } +} +#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */ + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* Read the end of the PNG file. Will not read past the end of the + * file, will verify the end is accurate, and will read any comments + * or time information at the end of the file, if info is not NULL. + */ +void PNGAPI +png_read_end(png_structp png_ptr, png_infop info_ptr) +{ + png_byte chunk_length[4]; + png_uint_32 length; + + png_debug(1, "in png_read_end\n"); + if(png_ptr == NULL) return; + png_crc_finish(png_ptr, 0); /* Finish off CRC from last IDAT chunk */ + + do + { +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_CONST PNG_IHDR; + PNG_CONST PNG_IDAT; + PNG_CONST PNG_IEND; + PNG_CONST PNG_PLTE; +#if defined(PNG_READ_bKGD_SUPPORTED) + PNG_CONST PNG_bKGD; +#endif +#if defined(PNG_READ_cHRM_SUPPORTED) + PNG_CONST PNG_cHRM; +#endif +#if defined(PNG_READ_gAMA_SUPPORTED) + PNG_CONST PNG_gAMA; +#endif +#if defined(PNG_READ_hIST_SUPPORTED) + PNG_CONST PNG_hIST; +#endif +#if defined(PNG_READ_iCCP_SUPPORTED) + PNG_CONST PNG_iCCP; +#endif +#if defined(PNG_READ_iTXt_SUPPORTED) + PNG_CONST PNG_iTXt; +#endif +#if defined(PNG_READ_oFFs_SUPPORTED) + PNG_CONST PNG_oFFs; +#endif +#if defined(PNG_READ_pCAL_SUPPORTED) + PNG_CONST PNG_pCAL; +#endif +#if defined(PNG_READ_pHYs_SUPPORTED) + PNG_CONST PNG_pHYs; +#endif +#if defined(PNG_READ_sBIT_SUPPORTED) + PNG_CONST PNG_sBIT; +#endif +#if defined(PNG_READ_sCAL_SUPPORTED) + PNG_CONST PNG_sCAL; +#endif +#if defined(PNG_READ_sPLT_SUPPORTED) + PNG_CONST PNG_sPLT; +#endif +#if defined(PNG_READ_sRGB_SUPPORTED) + PNG_CONST PNG_sRGB; +#endif +#if defined(PNG_READ_tEXt_SUPPORTED) + PNG_CONST PNG_tEXt; +#endif +#if defined(PNG_READ_tIME_SUPPORTED) + PNG_CONST PNG_tIME; +#endif +#if defined(PNG_READ_tRNS_SUPPORTED) + PNG_CONST PNG_tRNS; +#endif +#if defined(PNG_READ_zTXt_SUPPORTED) + PNG_CONST PNG_zTXt; +#endif +#endif /* PNG_USE_LOCAL_ARRAYS */ + + png_read_data(png_ptr, chunk_length, 4); + length = png_get_uint_31(png_ptr,chunk_length); + + png_reset_crc(png_ptr); + png_crc_read(png_ptr, png_ptr->chunk_name, 4); + + png_debug1(0, "Reading %s chunk.\n", png_ptr->chunk_name); + + if (!png_memcmp(png_ptr->chunk_name, png_IHDR, 4)) + png_handle_IHDR(png_ptr, info_ptr, length); + else if (!png_memcmp(png_ptr->chunk_name, png_IEND, 4)) + png_handle_IEND(png_ptr, info_ptr, length); +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + else if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name)) + { + if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + { + if ((length > 0) || (png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT)) + png_error(png_ptr, "Too many IDAT's found"); + } + png_handle_unknown(png_ptr, info_ptr, length); + if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4)) + png_ptr->mode |= PNG_HAVE_PLTE; + } +#endif + else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + { + /* Zero length IDATs are legal after the last IDAT has been + * read, but not after other chunks have been read. + */ + if ((length > 0) || (png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT)) + png_error(png_ptr, "Too many IDAT's found"); + png_crc_finish(png_ptr, length); + } + else if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4)) + png_handle_PLTE(png_ptr, info_ptr, length); +#if defined(PNG_READ_bKGD_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_bKGD, 4)) + png_handle_bKGD(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_cHRM_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_cHRM, 4)) + png_handle_cHRM(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_gAMA_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_gAMA, 4)) + png_handle_gAMA(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_hIST_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_hIST, 4)) + png_handle_hIST(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_oFFs_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_oFFs, 4)) + png_handle_oFFs(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_pCAL_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_pCAL, 4)) + png_handle_pCAL(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_sCAL_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sCAL, 4)) + png_handle_sCAL(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_pHYs_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_pHYs, 4)) + png_handle_pHYs(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_sBIT_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sBIT, 4)) + png_handle_sBIT(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_sRGB_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sRGB, 4)) + png_handle_sRGB(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_iCCP_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_iCCP, 4)) + png_handle_iCCP(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_sPLT_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sPLT, 4)) + png_handle_sPLT(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_tEXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tEXt, 4)) + png_handle_tEXt(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_tIME_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tIME, 4)) + png_handle_tIME(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_tRNS_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tRNS, 4)) + png_handle_tRNS(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_zTXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_zTXt, 4)) + png_handle_zTXt(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_iTXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_iTXt, 4)) + png_handle_iTXt(png_ptr, info_ptr, length); +#endif + else + png_handle_unknown(png_ptr, info_ptr, length); + } while (!(png_ptr->mode & PNG_HAVE_IEND)); +} +#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */ + +/* free all memory used by the read */ +void PNGAPI +png_destroy_read_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr, + png_infopp end_info_ptr_ptr) +{ + png_structp png_ptr = NULL; + png_infop info_ptr = NULL, end_info_ptr = NULL; +#ifdef PNG_USER_MEM_SUPPORTED + png_free_ptr free_fn; + png_voidp mem_ptr; +#endif + + png_debug(1, "in png_destroy_read_struct\n"); + if (png_ptr_ptr != NULL) + png_ptr = *png_ptr_ptr; + + if (info_ptr_ptr != NULL) + info_ptr = *info_ptr_ptr; + + if (end_info_ptr_ptr != NULL) + end_info_ptr = *end_info_ptr_ptr; + +#ifdef PNG_USER_MEM_SUPPORTED + free_fn = png_ptr->free_fn; + mem_ptr = png_ptr->mem_ptr; +#endif + + png_read_destroy(png_ptr, info_ptr, end_info_ptr); + + if (info_ptr != NULL) + { +#if defined(PNG_TEXT_SUPPORTED) + png_free_data(png_ptr, info_ptr, PNG_FREE_TEXT, -1); +#endif + +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)info_ptr, (png_free_ptr)free_fn, + (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)info_ptr); +#endif + *info_ptr_ptr = NULL; + } + + if (end_info_ptr != NULL) + { +#if defined(PNG_READ_TEXT_SUPPORTED) + png_free_data(png_ptr, end_info_ptr, PNG_FREE_TEXT, -1); +#endif +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)end_info_ptr, (png_free_ptr)free_fn, + (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)end_info_ptr); +#endif + *end_info_ptr_ptr = NULL; + } + + if (png_ptr != NULL) + { +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)png_ptr, (png_free_ptr)free_fn, + (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)png_ptr); +#endif + *png_ptr_ptr = NULL; + } +} + +/* free all memory used by the read (old method) */ +void /* PRIVATE */ +png_read_destroy(png_structp png_ptr, png_infop info_ptr, png_infop end_info_ptr) +{ +#ifdef PNG_SETJMP_SUPPORTED + jmp_buf tmp_jmp; +#endif + png_error_ptr error_fn; + png_error_ptr warning_fn; + png_voidp error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + png_free_ptr free_fn; +#endif + + png_debug(1, "in png_read_destroy\n"); + if (info_ptr != NULL) + png_info_destroy(png_ptr, info_ptr); + + if (end_info_ptr != NULL) + png_info_destroy(png_ptr, end_info_ptr); + + png_free(png_ptr, png_ptr->zbuf); + png_free(png_ptr, png_ptr->big_row_buf); + png_free(png_ptr, png_ptr->prev_row); +#if defined(PNG_READ_DITHER_SUPPORTED) + png_free(png_ptr, png_ptr->palette_lookup); + png_free(png_ptr, png_ptr->dither_index); +#endif +#if defined(PNG_READ_GAMMA_SUPPORTED) + png_free(png_ptr, png_ptr->gamma_table); +#endif +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + png_free(png_ptr, png_ptr->gamma_from_1); + png_free(png_ptr, png_ptr->gamma_to_1); +#endif +#ifdef PNG_FREE_ME_SUPPORTED + if (png_ptr->free_me & PNG_FREE_PLTE) + png_zfree(png_ptr, png_ptr->palette); + png_ptr->free_me &= ~PNG_FREE_PLTE; +#else + if (png_ptr->flags & PNG_FLAG_FREE_PLTE) + png_zfree(png_ptr, png_ptr->palette); + png_ptr->flags &= ~PNG_FLAG_FREE_PLTE; +#endif +#if defined(PNG_tRNS_SUPPORTED) || \ + defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) +#ifdef PNG_FREE_ME_SUPPORTED + if (png_ptr->free_me & PNG_FREE_TRNS) + png_free(png_ptr, png_ptr->trans); + png_ptr->free_me &= ~PNG_FREE_TRNS; +#else + if (png_ptr->flags & PNG_FLAG_FREE_TRNS) + png_free(png_ptr, png_ptr->trans); + png_ptr->flags &= ~PNG_FLAG_FREE_TRNS; +#endif +#endif +#if defined(PNG_READ_hIST_SUPPORTED) +#ifdef PNG_FREE_ME_SUPPORTED + if (png_ptr->free_me & PNG_FREE_HIST) + png_free(png_ptr, png_ptr->hist); + png_ptr->free_me &= ~PNG_FREE_HIST; +#else + if (png_ptr->flags & PNG_FLAG_FREE_HIST) + png_free(png_ptr, png_ptr->hist); + png_ptr->flags &= ~PNG_FLAG_FREE_HIST; +#endif +#endif +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (png_ptr->gamma_16_table != NULL) + { + int i; + int istop = (1 << (8 - png_ptr->gamma_shift)); + for (i = 0; i < istop; i++) + { + png_free(png_ptr, png_ptr->gamma_16_table[i]); + } + png_free(png_ptr, png_ptr->gamma_16_table); + } +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->gamma_16_from_1 != NULL) + { + int i; + int istop = (1 << (8 - png_ptr->gamma_shift)); + for (i = 0; i < istop; i++) + { + png_free(png_ptr, png_ptr->gamma_16_from_1[i]); + } + png_free(png_ptr, png_ptr->gamma_16_from_1); + } + if (png_ptr->gamma_16_to_1 != NULL) + { + int i; + int istop = (1 << (8 - png_ptr->gamma_shift)); + for (i = 0; i < istop; i++) + { + png_free(png_ptr, png_ptr->gamma_16_to_1[i]); + } + png_free(png_ptr, png_ptr->gamma_16_to_1); + } +#endif +#endif +#if defined(PNG_TIME_RFC1123_SUPPORTED) + png_free(png_ptr, png_ptr->time_buffer); +#endif + + inflateEnd(&png_ptr->zstream); +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED + png_free(png_ptr, png_ptr->save_buffer); +#endif + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +#ifdef PNG_TEXT_SUPPORTED + png_free(png_ptr, png_ptr->current_text); +#endif /* PNG_TEXT_SUPPORTED */ +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + + /* Save the important info out of the png_struct, in case it is + * being used again. + */ +#ifdef PNG_SETJMP_SUPPORTED + png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof (jmp_buf)); +#endif + + error_fn = png_ptr->error_fn; + warning_fn = png_ptr->warning_fn; + error_ptr = png_ptr->error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + free_fn = png_ptr->free_fn; +#endif + + png_memset(png_ptr, 0, png_sizeof (png_struct)); + + png_ptr->error_fn = error_fn; + png_ptr->warning_fn = warning_fn; + png_ptr->error_ptr = error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + png_ptr->free_fn = free_fn; +#endif + +#ifdef PNG_SETJMP_SUPPORTED + png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof (jmp_buf)); +#endif + +} + +void PNGAPI +png_set_read_status_fn(png_structp png_ptr, png_read_status_ptr read_row_fn) +{ + if(png_ptr == NULL) return; + png_ptr->read_row_fn = read_row_fn; +} + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +#if defined(PNG_INFO_IMAGE_SUPPORTED) +void PNGAPI +png_read_png(png_structp png_ptr, png_infop info_ptr, + int transforms, + voidp params) +{ + int row; + + if(png_ptr == NULL) return; +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) + /* invert the alpha channel from opacity to transparency + */ + if (transforms & PNG_TRANSFORM_INVERT_ALPHA) + png_set_invert_alpha(png_ptr); +#endif + + /* png_read_info() gives us all of the information from the + * PNG file before the first IDAT (image data chunk). + */ + png_read_info(png_ptr, info_ptr); + if (info_ptr->height > PNG_UINT_32_MAX/png_sizeof(png_bytep)) + png_error(png_ptr,"Image is too high to process with png_read_png()"); + + /* -------------- image transformations start here ------------------- */ + +#if defined(PNG_READ_16_TO_8_SUPPORTED) + /* tell libpng to strip 16 bit/color files down to 8 bits per color + */ + if (transforms & PNG_TRANSFORM_STRIP_16) + png_set_strip_16(png_ptr); +#endif + +#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED) + /* Strip alpha bytes from the input data without combining with + * the background (not recommended). + */ + if (transforms & PNG_TRANSFORM_STRIP_ALPHA) + png_set_strip_alpha(png_ptr); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) && !defined(PNG_READ_EXPAND_SUPPORTED) + /* Extract multiple pixels with bit depths of 1, 2, or 4 from a single + * byte into separate bytes (useful for paletted and grayscale images). + */ + if (transforms & PNG_TRANSFORM_PACKING) + png_set_packing(png_ptr); +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + /* Change the order of packed pixels to least significant bit first + * (not useful if you are using png_set_packing). + */ + if (transforms & PNG_TRANSFORM_PACKSWAP) + png_set_packswap(png_ptr); +#endif + +#if defined(PNG_READ_EXPAND_SUPPORTED) + /* Expand paletted colors into true RGB triplets + * Expand grayscale images to full 8 bits from 1, 2, or 4 bits/pixel + * Expand paletted or RGB images with transparency to full alpha + * channels so the data will be available as RGBA quartets. + */ + if (transforms & PNG_TRANSFORM_EXPAND) + if ((png_ptr->bit_depth < 8) || + (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) || + (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))) + png_set_expand(png_ptr); +#endif + + /* We don't handle background color or gamma transformation or dithering. + */ + +#if defined(PNG_READ_INVERT_SUPPORTED) + /* invert monochrome files to have 0 as white and 1 as black + */ + if (transforms & PNG_TRANSFORM_INVERT_MONO) + png_set_invert_mono(png_ptr); +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) + /* If you want to shift the pixel values from the range [0,255] or + * [0,65535] to the original [0,7] or [0,31], or whatever range the + * colors were originally in: + */ + if ((transforms & PNG_TRANSFORM_SHIFT) + && png_get_valid(png_ptr, info_ptr, PNG_INFO_sBIT)) + { + png_color_8p sig_bit; + + png_get_sBIT(png_ptr, info_ptr, &sig_bit); + png_set_shift(png_ptr, sig_bit); + } +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) + /* flip the RGB pixels to BGR (or RGBA to BGRA) + */ + if (transforms & PNG_TRANSFORM_BGR) + png_set_bgr(png_ptr); +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) + /* swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR) + */ + if (transforms & PNG_TRANSFORM_SWAP_ALPHA) + png_set_swap_alpha(png_ptr); +#endif + +#if defined(PNG_READ_SWAP_SUPPORTED) + /* swap bytes of 16 bit files to least significant byte first + */ + if (transforms & PNG_TRANSFORM_SWAP_ENDIAN) + png_set_swap(png_ptr); +#endif + + /* We don't handle adding filler bytes */ + + /* Optional call to gamma correct and add the background to the palette + * and update info structure. REQUIRED if you are expecting libpng to + * update the palette for you (i.e., you selected such a transform above). + */ + png_read_update_info(png_ptr, info_ptr); + + /* -------------- image transformations end here ------------------- */ + +#ifdef PNG_FREE_ME_SUPPORTED + png_free_data(png_ptr, info_ptr, PNG_FREE_ROWS, 0); +#endif + if(info_ptr->row_pointers == NULL) + { + info_ptr->row_pointers = (png_bytepp)png_malloc(png_ptr, + info_ptr->height * png_sizeof(png_bytep)); +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_ROWS; +#endif + for (row = 0; row < (int)info_ptr->height; row++) + { + info_ptr->row_pointers[row] = (png_bytep)png_malloc(png_ptr, + png_get_rowbytes(png_ptr, info_ptr)); + } + } + + png_read_image(png_ptr, info_ptr->row_pointers); + info_ptr->valid |= PNG_INFO_IDAT; + + /* read rest of file, and get additional chunks in info_ptr - REQUIRED */ + png_read_end(png_ptr, info_ptr); + + transforms = transforms; /* quiet compiler warnings */ + params = params; + +} +#endif /* PNG_INFO_IMAGE_SUPPORTED */ +#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */ +#endif /* PNG_READ_SUPPORTED */ +/********* End of inlined file: pngread.c *********/ + +/********* Start of inlined file: pngpread.c *********/ +/* pngpread.c - read a png file in push mode + * + * Last changed in libpng 1.2.21 October 4, 2007 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2007 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + */ + +#define PNG_INTERNAL + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED + +/* push model modes */ +#define PNG_READ_SIG_MODE 0 +#define PNG_READ_CHUNK_MODE 1 +#define PNG_READ_IDAT_MODE 2 +#define PNG_SKIP_MODE 3 +#define PNG_READ_tEXt_MODE 4 +#define PNG_READ_zTXt_MODE 5 +#define PNG_READ_DONE_MODE 6 +#define PNG_READ_iTXt_MODE 7 +#define PNG_ERROR_MODE 8 + +void PNGAPI +png_process_data(png_structp png_ptr, png_infop info_ptr, + png_bytep buffer, png_size_t buffer_size) +{ + if(png_ptr == NULL) return; + png_push_restore_buffer(png_ptr, buffer, buffer_size); + + while (png_ptr->buffer_size) + { + png_process_some_data(png_ptr, info_ptr); + } +} + +/* What we do with the incoming data depends on what we were previously + * doing before we ran out of data... + */ +void /* PRIVATE */ +png_process_some_data(png_structp png_ptr, png_infop info_ptr) +{ + if(png_ptr == NULL) return; + switch (png_ptr->process_mode) + { + case PNG_READ_SIG_MODE: + { + png_push_read_sig(png_ptr, info_ptr); + break; + } + case PNG_READ_CHUNK_MODE: + { + png_push_read_chunk(png_ptr, info_ptr); + break; + } + case PNG_READ_IDAT_MODE: + { + png_push_read_IDAT(png_ptr); + break; + } +#if defined(PNG_READ_tEXt_SUPPORTED) + case PNG_READ_tEXt_MODE: + { + png_push_read_tEXt(png_ptr, info_ptr); + break; + } +#endif +#if defined(PNG_READ_zTXt_SUPPORTED) + case PNG_READ_zTXt_MODE: + { + png_push_read_zTXt(png_ptr, info_ptr); + break; + } +#endif +#if defined(PNG_READ_iTXt_SUPPORTED) + case PNG_READ_iTXt_MODE: + { + png_push_read_iTXt(png_ptr, info_ptr); + break; + } +#endif + case PNG_SKIP_MODE: + { + png_push_crc_finish(png_ptr); + break; + } + default: + { + png_ptr->buffer_size = 0; + break; + } + } +} + +/* Read any remaining signature bytes from the stream and compare them with + * the correct PNG signature. It is possible that this routine is called + * with bytes already read from the signature, either because they have been + * checked by the calling application, or because of multiple calls to this + * routine. + */ +void /* PRIVATE */ +png_push_read_sig(png_structp png_ptr, png_infop info_ptr) +{ + png_size_t num_checked = png_ptr->sig_bytes, + num_to_check = 8 - num_checked; + + if (png_ptr->buffer_size < num_to_check) + { + num_to_check = png_ptr->buffer_size; + } + + png_push_fill_buffer(png_ptr, &(info_ptr->signature[num_checked]), + num_to_check); + png_ptr->sig_bytes = (png_byte)(png_ptr->sig_bytes+num_to_check); + + if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check)) + { + if (num_checked < 4 && + png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4)) + png_error(png_ptr, "Not a PNG file"); + else + png_error(png_ptr, "PNG file corrupted by ASCII conversion"); + } + else + { + if (png_ptr->sig_bytes >= 8) + { + png_ptr->process_mode = PNG_READ_CHUNK_MODE; + } + } +} + +void /* PRIVATE */ +png_push_read_chunk(png_structp png_ptr, png_infop info_ptr) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_CONST PNG_IHDR; + PNG_CONST PNG_IDAT; + PNG_CONST PNG_IEND; + PNG_CONST PNG_PLTE; +#if defined(PNG_READ_bKGD_SUPPORTED) + PNG_CONST PNG_bKGD; +#endif +#if defined(PNG_READ_cHRM_SUPPORTED) + PNG_CONST PNG_cHRM; +#endif +#if defined(PNG_READ_gAMA_SUPPORTED) + PNG_CONST PNG_gAMA; +#endif +#if defined(PNG_READ_hIST_SUPPORTED) + PNG_CONST PNG_hIST; +#endif +#if defined(PNG_READ_iCCP_SUPPORTED) + PNG_CONST PNG_iCCP; +#endif +#if defined(PNG_READ_iTXt_SUPPORTED) + PNG_CONST PNG_iTXt; +#endif +#if defined(PNG_READ_oFFs_SUPPORTED) + PNG_CONST PNG_oFFs; +#endif +#if defined(PNG_READ_pCAL_SUPPORTED) + PNG_CONST PNG_pCAL; +#endif +#if defined(PNG_READ_pHYs_SUPPORTED) + PNG_CONST PNG_pHYs; +#endif +#if defined(PNG_READ_sBIT_SUPPORTED) + PNG_CONST PNG_sBIT; +#endif +#if defined(PNG_READ_sCAL_SUPPORTED) + PNG_CONST PNG_sCAL; +#endif +#if defined(PNG_READ_sRGB_SUPPORTED) + PNG_CONST PNG_sRGB; +#endif +#if defined(PNG_READ_sPLT_SUPPORTED) + PNG_CONST PNG_sPLT; +#endif +#if defined(PNG_READ_tEXt_SUPPORTED) + PNG_CONST PNG_tEXt; +#endif +#if defined(PNG_READ_tIME_SUPPORTED) + PNG_CONST PNG_tIME; +#endif +#if defined(PNG_READ_tRNS_SUPPORTED) + PNG_CONST PNG_tRNS; +#endif +#if defined(PNG_READ_zTXt_SUPPORTED) + PNG_CONST PNG_zTXt; +#endif +#endif /* PNG_USE_LOCAL_ARRAYS */ + /* First we make sure we have enough data for the 4 byte chunk name + * and the 4 byte chunk length before proceeding with decoding the + * chunk data. To fully decode each of these chunks, we also make + * sure we have enough data in the buffer for the 4 byte CRC at the + * end of every chunk (except IDAT, which is handled separately). + */ + if (!(png_ptr->mode & PNG_HAVE_CHUNK_HEADER)) + { + png_byte chunk_length[4]; + + if (png_ptr->buffer_size < 8) + { + png_push_save_buffer(png_ptr); + return; + } + + png_push_fill_buffer(png_ptr, chunk_length, 4); + png_ptr->push_length = png_get_uint_31(png_ptr,chunk_length); + png_reset_crc(png_ptr); + png_crc_read(png_ptr, png_ptr->chunk_name, 4); + png_ptr->mode |= PNG_HAVE_CHUNK_HEADER; + } + + if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + if(png_ptr->mode & PNG_AFTER_IDAT) + png_ptr->mode |= PNG_HAVE_CHUNK_AFTER_IDAT; + + if (!png_memcmp(png_ptr->chunk_name, png_IHDR, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_IHDR(png_ptr, info_ptr, png_ptr->push_length); + } + else if (!png_memcmp(png_ptr->chunk_name, png_IEND, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_IEND(png_ptr, info_ptr, png_ptr->push_length); + + png_ptr->process_mode = PNG_READ_DONE_MODE; + png_push_have_end(png_ptr, info_ptr); + } +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + else if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + png_ptr->mode |= PNG_HAVE_IDAT; + png_handle_unknown(png_ptr, info_ptr, png_ptr->push_length); + if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4)) + png_ptr->mode |= PNG_HAVE_PLTE; + else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + { + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before IDAT"); + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + !(png_ptr->mode & PNG_HAVE_PLTE)) + png_error(png_ptr, "Missing PLTE before IDAT"); + } + } +#endif + else if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_PLTE(png_ptr, info_ptr, png_ptr->push_length); + } + else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + { + /* If we reach an IDAT chunk, this means we have read all of the + * header chunks, and we can start reading the image (or if this + * is called after the image has been read - we have an error). + */ + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before IDAT"); + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + !(png_ptr->mode & PNG_HAVE_PLTE)) + png_error(png_ptr, "Missing PLTE before IDAT"); + + if (png_ptr->mode & PNG_HAVE_IDAT) + { + if (!(png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT)) + if (png_ptr->push_length == 0) + return; + + if (png_ptr->mode & PNG_AFTER_IDAT) + png_error(png_ptr, "Too many IDAT's found"); + } + + png_ptr->idat_size = png_ptr->push_length; + png_ptr->mode |= PNG_HAVE_IDAT; + png_ptr->process_mode = PNG_READ_IDAT_MODE; + png_push_have_info(png_ptr, info_ptr); + png_ptr->zstream.avail_out = (uInt)png_ptr->irowbytes; + png_ptr->zstream.next_out = png_ptr->row_buf; + return; + } +#if defined(PNG_READ_gAMA_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_gAMA, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_gAMA(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_sBIT_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sBIT, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_sBIT(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_cHRM_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_cHRM, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_cHRM(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_sRGB_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sRGB, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_sRGB(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_iCCP_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_iCCP, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_iCCP(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_sPLT_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sPLT, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_sPLT(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_tRNS_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tRNS, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_tRNS(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_bKGD_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_bKGD, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_bKGD(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_hIST_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_hIST, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_hIST(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_pHYs_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_pHYs, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_pHYs(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_oFFs_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_oFFs, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_oFFs(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_pCAL_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_pCAL, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_pCAL(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_sCAL_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sCAL, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_sCAL(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_tIME_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tIME, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_tIME(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_tEXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tEXt, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_push_handle_tEXt(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_zTXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_zTXt, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_push_handle_zTXt(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_iTXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_iTXt, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_push_handle_iTXt(png_ptr, info_ptr, png_ptr->push_length); + } +#endif + else + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_push_handle_unknown(png_ptr, info_ptr, png_ptr->push_length); + } + + png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; +} + +void /* PRIVATE */ +png_push_crc_skip(png_structp png_ptr, png_uint_32 skip) +{ + png_ptr->process_mode = PNG_SKIP_MODE; + png_ptr->skip_length = skip; +} + +void /* PRIVATE */ +png_push_crc_finish(png_structp png_ptr) +{ + if (png_ptr->skip_length && png_ptr->save_buffer_size) + { + png_size_t save_size; + + if (png_ptr->skip_length < (png_uint_32)png_ptr->save_buffer_size) + save_size = (png_size_t)png_ptr->skip_length; + else + save_size = png_ptr->save_buffer_size; + + png_calculate_crc(png_ptr, png_ptr->save_buffer_ptr, save_size); + + png_ptr->skip_length -= save_size; + png_ptr->buffer_size -= save_size; + png_ptr->save_buffer_size -= save_size; + png_ptr->save_buffer_ptr += save_size; + } + if (png_ptr->skip_length && png_ptr->current_buffer_size) + { + png_size_t save_size; + + if (png_ptr->skip_length < (png_uint_32)png_ptr->current_buffer_size) + save_size = (png_size_t)png_ptr->skip_length; + else + save_size = png_ptr->current_buffer_size; + + png_calculate_crc(png_ptr, png_ptr->current_buffer_ptr, save_size); + + png_ptr->skip_length -= save_size; + png_ptr->buffer_size -= save_size; + png_ptr->current_buffer_size -= save_size; + png_ptr->current_buffer_ptr += save_size; + } + if (!png_ptr->skip_length) + { + if (png_ptr->buffer_size < 4) + { + png_push_save_buffer(png_ptr); + return; + } + + png_crc_finish(png_ptr, 0); + png_ptr->process_mode = PNG_READ_CHUNK_MODE; + } +} + +void PNGAPI +png_push_fill_buffer(png_structp png_ptr, png_bytep buffer, png_size_t length) +{ + png_bytep ptr; + + if(png_ptr == NULL) return; + ptr = buffer; + if (png_ptr->save_buffer_size) + { + png_size_t save_size; + + if (length < png_ptr->save_buffer_size) + save_size = length; + else + save_size = png_ptr->save_buffer_size; + + png_memcpy(ptr, png_ptr->save_buffer_ptr, save_size); + length -= save_size; + ptr += save_size; + png_ptr->buffer_size -= save_size; + png_ptr->save_buffer_size -= save_size; + png_ptr->save_buffer_ptr += save_size; + } + if (length && png_ptr->current_buffer_size) + { + png_size_t save_size; + + if (length < png_ptr->current_buffer_size) + save_size = length; + else + save_size = png_ptr->current_buffer_size; + + png_memcpy(ptr, png_ptr->current_buffer_ptr, save_size); + png_ptr->buffer_size -= save_size; + png_ptr->current_buffer_size -= save_size; + png_ptr->current_buffer_ptr += save_size; + } +} + +void /* PRIVATE */ +png_push_save_buffer(png_structp png_ptr) +{ + if (png_ptr->save_buffer_size) + { + if (png_ptr->save_buffer_ptr != png_ptr->save_buffer) + { + png_size_t i,istop; + png_bytep sp; + png_bytep dp; + + istop = png_ptr->save_buffer_size; + for (i = 0, sp = png_ptr->save_buffer_ptr, dp = png_ptr->save_buffer; + i < istop; i++, sp++, dp++) + { + *dp = *sp; + } + } + } + if (png_ptr->save_buffer_size + png_ptr->current_buffer_size > + png_ptr->save_buffer_max) + { + png_size_t new_max; + png_bytep old_buffer; + + if (png_ptr->save_buffer_size > PNG_SIZE_MAX - + (png_ptr->current_buffer_size + 256)) + { + png_error(png_ptr, "Potential overflow of save_buffer"); + } + new_max = png_ptr->save_buffer_size + png_ptr->current_buffer_size + 256; + old_buffer = png_ptr->save_buffer; + png_ptr->save_buffer = (png_bytep)png_malloc(png_ptr, + (png_uint_32)new_max); + png_memcpy(png_ptr->save_buffer, old_buffer, png_ptr->save_buffer_size); + png_free(png_ptr, old_buffer); + png_ptr->save_buffer_max = new_max; + } + if (png_ptr->current_buffer_size) + { + png_memcpy(png_ptr->save_buffer + png_ptr->save_buffer_size, + png_ptr->current_buffer_ptr, png_ptr->current_buffer_size); + png_ptr->save_buffer_size += png_ptr->current_buffer_size; + png_ptr->current_buffer_size = 0; + } + png_ptr->save_buffer_ptr = png_ptr->save_buffer; + png_ptr->buffer_size = 0; +} + +void /* PRIVATE */ +png_push_restore_buffer(png_structp png_ptr, png_bytep buffer, + png_size_t buffer_length) +{ + png_ptr->current_buffer = buffer; + png_ptr->current_buffer_size = buffer_length; + png_ptr->buffer_size = buffer_length + png_ptr->save_buffer_size; + png_ptr->current_buffer_ptr = png_ptr->current_buffer; +} + +void /* PRIVATE */ +png_push_read_IDAT(png_structp png_ptr) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_CONST PNG_IDAT; +#endif + if (!(png_ptr->mode & PNG_HAVE_CHUNK_HEADER)) + { + png_byte chunk_length[4]; + + if (png_ptr->buffer_size < 8) + { + png_push_save_buffer(png_ptr); + return; + } + + png_push_fill_buffer(png_ptr, chunk_length, 4); + png_ptr->push_length = png_get_uint_31(png_ptr,chunk_length); + png_reset_crc(png_ptr); + png_crc_read(png_ptr, png_ptr->chunk_name, 4); + png_ptr->mode |= PNG_HAVE_CHUNK_HEADER; + + if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + { + png_ptr->process_mode = PNG_READ_CHUNK_MODE; + if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED)) + png_error(png_ptr, "Not enough compressed data"); + return; + } + + png_ptr->idat_size = png_ptr->push_length; + } + if (png_ptr->idat_size && png_ptr->save_buffer_size) + { + png_size_t save_size; + + if (png_ptr->idat_size < (png_uint_32)png_ptr->save_buffer_size) + { + save_size = (png_size_t)png_ptr->idat_size; + /* check for overflow */ + if((png_uint_32)save_size != png_ptr->idat_size) + png_error(png_ptr, "save_size overflowed in pngpread"); + } + else + save_size = png_ptr->save_buffer_size; + + png_calculate_crc(png_ptr, png_ptr->save_buffer_ptr, save_size); + if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED)) + png_process_IDAT_data(png_ptr, png_ptr->save_buffer_ptr, save_size); + png_ptr->idat_size -= save_size; + png_ptr->buffer_size -= save_size; + png_ptr->save_buffer_size -= save_size; + png_ptr->save_buffer_ptr += save_size; + } + if (png_ptr->idat_size && png_ptr->current_buffer_size) + { + png_size_t save_size; + + if (png_ptr->idat_size < (png_uint_32)png_ptr->current_buffer_size) + { + save_size = (png_size_t)png_ptr->idat_size; + /* check for overflow */ + if((png_uint_32)save_size != png_ptr->idat_size) + png_error(png_ptr, "save_size overflowed in pngpread"); + } + else + save_size = png_ptr->current_buffer_size; + + png_calculate_crc(png_ptr, png_ptr->current_buffer_ptr, save_size); + if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED)) + png_process_IDAT_data(png_ptr, png_ptr->current_buffer_ptr, save_size); + + png_ptr->idat_size -= save_size; + png_ptr->buffer_size -= save_size; + png_ptr->current_buffer_size -= save_size; + png_ptr->current_buffer_ptr += save_size; + } + if (!png_ptr->idat_size) + { + if (png_ptr->buffer_size < 4) + { + png_push_save_buffer(png_ptr); + return; + } + + png_crc_finish(png_ptr, 0); + png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; + png_ptr->mode |= PNG_AFTER_IDAT; + } +} + +void /* PRIVATE */ +png_process_IDAT_data(png_structp png_ptr, png_bytep buffer, + png_size_t buffer_length) +{ + int ret; + + if ((png_ptr->flags & PNG_FLAG_ZLIB_FINISHED) && buffer_length) + png_error(png_ptr, "Extra compression data"); + + png_ptr->zstream.next_in = buffer; + png_ptr->zstream.avail_in = (uInt)buffer_length; + for(;;) + { + ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH); + if (ret != Z_OK) + { + if (ret == Z_STREAM_END) + { + if (png_ptr->zstream.avail_in) + png_error(png_ptr, "Extra compressed data"); + if (!(png_ptr->zstream.avail_out)) + { + png_push_process_row(png_ptr); + } + + png_ptr->mode |= PNG_AFTER_IDAT; + png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; + break; + } + else if (ret == Z_BUF_ERROR) + break; + else + png_error(png_ptr, "Decompression Error"); + } + if (!(png_ptr->zstream.avail_out)) + { + if (( +#if defined(PNG_READ_INTERLACING_SUPPORTED) + png_ptr->interlaced && png_ptr->pass > 6) || + (!png_ptr->interlaced && +#endif + png_ptr->row_number == png_ptr->num_rows)) + { + if (png_ptr->zstream.avail_in) + { + png_warning(png_ptr, "Too much data in IDAT chunks"); + } + + png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; + break; + } + png_push_process_row(png_ptr); + png_ptr->zstream.avail_out = (uInt)png_ptr->irowbytes; + png_ptr->zstream.next_out = png_ptr->row_buf; + } + else + break; + } +} + +void /* PRIVATE */ +png_push_process_row(png_structp png_ptr) +{ + png_ptr->row_info.color_type = png_ptr->color_type; + png_ptr->row_info.width = png_ptr->iwidth; + png_ptr->row_info.channels = png_ptr->channels; + png_ptr->row_info.bit_depth = png_ptr->bit_depth; + png_ptr->row_info.pixel_depth = png_ptr->pixel_depth; + + png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth, + png_ptr->row_info.width); + + png_read_filter_row(png_ptr, &(png_ptr->row_info), + png_ptr->row_buf + 1, png_ptr->prev_row + 1, + (int)(png_ptr->row_buf[0])); + + png_memcpy_check(png_ptr, png_ptr->prev_row, png_ptr->row_buf, + png_ptr->rowbytes + 1); + + if (png_ptr->transformations || (png_ptr->flags&PNG_FLAG_STRIP_ALPHA)) + png_do_read_transformations(png_ptr); + +#if defined(PNG_READ_INTERLACING_SUPPORTED) + /* blow up interlaced rows to full size */ + if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE)) + { + if (png_ptr->pass < 6) +/* old interface (pre-1.0.9): + png_do_read_interlace(&(png_ptr->row_info), + png_ptr->row_buf + 1, png_ptr->pass, png_ptr->transformations); + */ + png_do_read_interlace(png_ptr); + + switch (png_ptr->pass) + { + case 0: + { + int i; + for (i = 0; i < 8 && png_ptr->pass == 0; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); /* updates png_ptr->pass */ + } + if (png_ptr->pass == 2) /* pass 1 might be empty */ + { + for (i = 0; i < 4 && png_ptr->pass == 2; i++) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + } + if (png_ptr->pass == 4 && png_ptr->height <= 4) + { + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + } + if (png_ptr->pass == 6 && png_ptr->height <= 4) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + break; + } + case 1: + { + int i; + for (i = 0; i < 8 && png_ptr->pass == 1; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + if (png_ptr->pass == 2) /* skip top 4 generated rows */ + { + for (i = 0; i < 4 && png_ptr->pass == 2; i++) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + } + break; + } + case 2: + { + int i; + for (i = 0; i < 4 && png_ptr->pass == 2; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + for (i = 0; i < 4 && png_ptr->pass == 2; i++) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + if (png_ptr->pass == 4) /* pass 3 might be empty */ + { + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + } + break; + } + case 3: + { + int i; + for (i = 0; i < 4 && png_ptr->pass == 3; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + if (png_ptr->pass == 4) /* skip top two generated rows */ + { + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + } + break; + } + case 4: + { + int i; + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + if (png_ptr->pass == 6) /* pass 5 might be empty */ + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + break; + } + case 5: + { + int i; + for (i = 0; i < 2 && png_ptr->pass == 5; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + if (png_ptr->pass == 6) /* skip top generated row */ + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + break; + } + case 6: + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + if (png_ptr->pass != 6) + break; + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + } + } + else +#endif + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } +} + +void /* PRIVATE */ +png_read_push_finish_row(png_structp png_ptr) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* start of interlace block */ + PNG_CONST int FARDATA png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; + + /* offset to next interlace block */ + PNG_CONST int FARDATA png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; + + /* start of interlace block in the y direction */ + PNG_CONST int FARDATA png_pass_ystart[] = {0, 0, 4, 0, 2, 0, 1}; + + /* offset to next interlace block in the y direction */ + PNG_CONST int FARDATA png_pass_yinc[] = {8, 8, 8, 4, 4, 2, 2}; + + /* Height of interlace block. This is not currently used - if you need + * it, uncomment it here and in png.h + PNG_CONST int FARDATA png_pass_height[] = {8, 8, 4, 4, 2, 2, 1}; + */ +#endif + + png_ptr->row_number++; + if (png_ptr->row_number < png_ptr->num_rows) + return; + + if (png_ptr->interlaced) + { + png_ptr->row_number = 0; + png_memset_check(png_ptr, png_ptr->prev_row, 0, + png_ptr->rowbytes + 1); + do + { + png_ptr->pass++; + if ((png_ptr->pass == 1 && png_ptr->width < 5) || + (png_ptr->pass == 3 && png_ptr->width < 3) || + (png_ptr->pass == 5 && png_ptr->width < 2)) + png_ptr->pass++; + + if (png_ptr->pass > 7) + png_ptr->pass--; + if (png_ptr->pass >= 7) + break; + + png_ptr->iwidth = (png_ptr->width + + png_pass_inc[png_ptr->pass] - 1 - + png_pass_start[png_ptr->pass]) / + png_pass_inc[png_ptr->pass]; + + png_ptr->irowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, + png_ptr->iwidth) + 1; + + if (png_ptr->transformations & PNG_INTERLACE) + break; + + png_ptr->num_rows = (png_ptr->height + + png_pass_yinc[png_ptr->pass] - 1 - + png_pass_ystart[png_ptr->pass]) / + png_pass_yinc[png_ptr->pass]; + + } while (png_ptr->iwidth == 0 || png_ptr->num_rows == 0); + } +} + +#if defined(PNG_READ_tEXt_SUPPORTED) +void /* PRIVATE */ +png_push_handle_tEXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 + length) +{ + if (!(png_ptr->mode & PNG_HAVE_IHDR) || (png_ptr->mode & PNG_HAVE_IEND)) + { + png_error(png_ptr, "Out of place tEXt"); + info_ptr = info_ptr; /* to quiet some compiler warnings */ + } + +#ifdef PNG_MAX_MALLOC_64K + png_ptr->skip_length = 0; /* This may not be necessary */ + + if (length > (png_uint_32)65535L) /* Can't hold entire string in memory */ + { + png_warning(png_ptr, "tEXt chunk too large to fit in memory"); + png_ptr->skip_length = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + + png_ptr->current_text = (png_charp)png_malloc(png_ptr, + (png_uint_32)(length+1)); + png_ptr->current_text[length] = '\0'; + png_ptr->current_text_ptr = png_ptr->current_text; + png_ptr->current_text_size = (png_size_t)length; + png_ptr->current_text_left = (png_size_t)length; + png_ptr->process_mode = PNG_READ_tEXt_MODE; +} + +void /* PRIVATE */ +png_push_read_tEXt(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr->buffer_size && png_ptr->current_text_left) + { + png_size_t text_size; + + if (png_ptr->buffer_size < png_ptr->current_text_left) + text_size = png_ptr->buffer_size; + else + text_size = png_ptr->current_text_left; + png_crc_read(png_ptr, (png_bytep)png_ptr->current_text_ptr, text_size); + png_ptr->current_text_left -= text_size; + png_ptr->current_text_ptr += text_size; + } + if (!(png_ptr->current_text_left)) + { + png_textp text_ptr; + png_charp text; + png_charp key; + int ret; + + if (png_ptr->buffer_size < 4) + { + png_push_save_buffer(png_ptr); + return; + } + + png_push_crc_finish(png_ptr); + +#if defined(PNG_MAX_MALLOC_64K) + if (png_ptr->skip_length) + return; +#endif + + key = png_ptr->current_text; + + for (text = key; *text; text++) + /* empty loop */ ; + + if (text < key + png_ptr->current_text_size) + text++; + + text_ptr = (png_textp)png_malloc(png_ptr, + (png_uint_32)png_sizeof(png_text)); + text_ptr->compression = PNG_TEXT_COMPRESSION_NONE; + text_ptr->key = key; +#ifdef PNG_iTXt_SUPPORTED + text_ptr->lang = NULL; + text_ptr->lang_key = NULL; +#endif + text_ptr->text = text; + + ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1); + + png_free(png_ptr, key); + png_free(png_ptr, text_ptr); + png_ptr->current_text = NULL; + + if (ret) + png_warning(png_ptr, "Insufficient memory to store text chunk."); + } +} +#endif + +#if defined(PNG_READ_zTXt_SUPPORTED) +void /* PRIVATE */ +png_push_handle_zTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 + length) +{ + if (!(png_ptr->mode & PNG_HAVE_IHDR) || (png_ptr->mode & PNG_HAVE_IEND)) + { + png_error(png_ptr, "Out of place zTXt"); + info_ptr = info_ptr; /* to quiet some compiler warnings */ + } + +#ifdef PNG_MAX_MALLOC_64K + /* We can't handle zTXt chunks > 64K, since we don't have enough space + * to be able to store the uncompressed data. Actually, the threshold + * is probably around 32K, but it isn't as definite as 64K is. + */ + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "zTXt chunk too large to fit in memory"); + png_push_crc_skip(png_ptr, length); + return; + } +#endif + + png_ptr->current_text = (png_charp)png_malloc(png_ptr, + (png_uint_32)(length+1)); + png_ptr->current_text[length] = '\0'; + png_ptr->current_text_ptr = png_ptr->current_text; + png_ptr->current_text_size = (png_size_t)length; + png_ptr->current_text_left = (png_size_t)length; + png_ptr->process_mode = PNG_READ_zTXt_MODE; +} + +void /* PRIVATE */ +png_push_read_zTXt(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr->buffer_size && png_ptr->current_text_left) + { + png_size_t text_size; + + if (png_ptr->buffer_size < (png_uint_32)png_ptr->current_text_left) + text_size = png_ptr->buffer_size; + else + text_size = png_ptr->current_text_left; + png_crc_read(png_ptr, (png_bytep)png_ptr->current_text_ptr, text_size); + png_ptr->current_text_left -= text_size; + png_ptr->current_text_ptr += text_size; + } + if (!(png_ptr->current_text_left)) + { + png_textp text_ptr; + png_charp text; + png_charp key; + int ret; + png_size_t text_size, key_size; + + if (png_ptr->buffer_size < 4) + { + png_push_save_buffer(png_ptr); + return; + } + + png_push_crc_finish(png_ptr); + + key = png_ptr->current_text; + + for (text = key; *text; text++) + /* empty loop */ ; + + /* zTXt can't have zero text */ + if (text >= key + png_ptr->current_text_size) + { + png_ptr->current_text = NULL; + png_free(png_ptr, key); + return; + } + + text++; + + if (*text != PNG_TEXT_COMPRESSION_zTXt) /* check compression byte */ + { + png_ptr->current_text = NULL; + png_free(png_ptr, key); + return; + } + + text++; + + png_ptr->zstream.next_in = (png_bytep )text; + png_ptr->zstream.avail_in = (uInt)(png_ptr->current_text_size - + (text - key)); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + + key_size = text - key; + text_size = 0; + text = NULL; + ret = Z_STREAM_END; + + while (png_ptr->zstream.avail_in) + { + ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH); + if (ret != Z_OK && ret != Z_STREAM_END) + { + inflateReset(&png_ptr->zstream); + png_ptr->zstream.avail_in = 0; + png_ptr->current_text = NULL; + png_free(png_ptr, key); + png_free(png_ptr, text); + return; + } + if (!(png_ptr->zstream.avail_out) || ret == Z_STREAM_END) + { + if (text == NULL) + { + text = (png_charp)png_malloc(png_ptr, + (png_uint_32)(png_ptr->zbuf_size - png_ptr->zstream.avail_out + + key_size + 1)); + png_memcpy(text + key_size, png_ptr->zbuf, + png_ptr->zbuf_size - png_ptr->zstream.avail_out); + png_memcpy(text, key, key_size); + text_size = key_size + png_ptr->zbuf_size - + png_ptr->zstream.avail_out; + *(text + text_size) = '\0'; + } + else + { + png_charp tmp; + + tmp = text; + text = (png_charp)png_malloc(png_ptr, text_size + + (png_uint_32)(png_ptr->zbuf_size - png_ptr->zstream.avail_out + + 1)); + png_memcpy(text, tmp, text_size); + png_free(png_ptr, tmp); + png_memcpy(text + text_size, png_ptr->zbuf, + png_ptr->zbuf_size - png_ptr->zstream.avail_out); + text_size += png_ptr->zbuf_size - png_ptr->zstream.avail_out; + *(text + text_size) = '\0'; + } + if (ret != Z_STREAM_END) + { + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + } + } + else + { + break; + } + + if (ret == Z_STREAM_END) + break; + } + + inflateReset(&png_ptr->zstream); + png_ptr->zstream.avail_in = 0; + + if (ret != Z_STREAM_END) + { + png_ptr->current_text = NULL; + png_free(png_ptr, key); + png_free(png_ptr, text); + return; + } + + png_ptr->current_text = NULL; + png_free(png_ptr, key); + key = text; + text += key_size; + + text_ptr = (png_textp)png_malloc(png_ptr, + (png_uint_32)png_sizeof(png_text)); + text_ptr->compression = PNG_TEXT_COMPRESSION_zTXt; + text_ptr->key = key; +#ifdef PNG_iTXt_SUPPORTED + text_ptr->lang = NULL; + text_ptr->lang_key = NULL; +#endif + text_ptr->text = text; + + ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1); + + png_free(png_ptr, key); + png_free(png_ptr, text_ptr); + + if (ret) + png_warning(png_ptr, "Insufficient memory to store text chunk."); + } +} +#endif + +#if defined(PNG_READ_iTXt_SUPPORTED) +void /* PRIVATE */ +png_push_handle_iTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 + length) +{ + if (!(png_ptr->mode & PNG_HAVE_IHDR) || (png_ptr->mode & PNG_HAVE_IEND)) + { + png_error(png_ptr, "Out of place iTXt"); + info_ptr = info_ptr; /* to quiet some compiler warnings */ + } + +#ifdef PNG_MAX_MALLOC_64K + png_ptr->skip_length = 0; /* This may not be necessary */ + + if (length > (png_uint_32)65535L) /* Can't hold entire string in memory */ + { + png_warning(png_ptr, "iTXt chunk too large to fit in memory"); + png_ptr->skip_length = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + + png_ptr->current_text = (png_charp)png_malloc(png_ptr, + (png_uint_32)(length+1)); + png_ptr->current_text[length] = '\0'; + png_ptr->current_text_ptr = png_ptr->current_text; + png_ptr->current_text_size = (png_size_t)length; + png_ptr->current_text_left = (png_size_t)length; + png_ptr->process_mode = PNG_READ_iTXt_MODE; +} + +void /* PRIVATE */ +png_push_read_iTXt(png_structp png_ptr, png_infop info_ptr) +{ + + if (png_ptr->buffer_size && png_ptr->current_text_left) + { + png_size_t text_size; + + if (png_ptr->buffer_size < png_ptr->current_text_left) + text_size = png_ptr->buffer_size; + else + text_size = png_ptr->current_text_left; + png_crc_read(png_ptr, (png_bytep)png_ptr->current_text_ptr, text_size); + png_ptr->current_text_left -= text_size; + png_ptr->current_text_ptr += text_size; + } + if (!(png_ptr->current_text_left)) + { + png_textp text_ptr; + png_charp key; + int comp_flag; + png_charp lang; + png_charp lang_key; + png_charp text; + int ret; + + if (png_ptr->buffer_size < 4) + { + png_push_save_buffer(png_ptr); + return; + } + + png_push_crc_finish(png_ptr); + +#if defined(PNG_MAX_MALLOC_64K) + if (png_ptr->skip_length) + return; +#endif + + key = png_ptr->current_text; + + for (lang = key; *lang; lang++) + /* empty loop */ ; + + if (lang < key + png_ptr->current_text_size - 3) + lang++; + + comp_flag = *lang++; + lang++; /* skip comp_type, always zero */ + + for (lang_key = lang; *lang_key; lang_key++) + /* empty loop */ ; + lang_key++; /* skip NUL separator */ + + text=lang_key; + if (lang_key < key + png_ptr->current_text_size - 1) + { + for (; *text; text++) + /* empty loop */ ; + } + + if (text < key + png_ptr->current_text_size) + text++; + + text_ptr = (png_textp)png_malloc(png_ptr, + (png_uint_32)png_sizeof(png_text)); + text_ptr->compression = comp_flag + 2; + text_ptr->key = key; + text_ptr->lang = lang; + text_ptr->lang_key = lang_key; + text_ptr->text = text; + text_ptr->text_length = 0; + text_ptr->itxt_length = png_strlen(text); + + ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1); + + png_ptr->current_text = NULL; + + png_free(png_ptr, text_ptr); + if (ret) + png_warning(png_ptr, "Insufficient memory to store iTXt chunk."); + } +} +#endif + +/* This function is called when we haven't found a handler for this + * chunk. If there isn't a problem with the chunk itself (ie a bad chunk + * name or a critical chunk), the chunk is (currently) silently ignored. + */ +void /* PRIVATE */ +png_push_handle_unknown(png_structp png_ptr, png_infop info_ptr, png_uint_32 + length) +{ + png_uint_32 skip=0; + png_check_chunk_name(png_ptr, png_ptr->chunk_name); + + if (!(png_ptr->chunk_name[0] & 0x20)) + { +#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) + if(png_handle_as_unknown(png_ptr, png_ptr->chunk_name) != + PNG_HANDLE_CHUNK_ALWAYS +#if defined(PNG_READ_USER_CHUNKS_SUPPORTED) + && png_ptr->read_user_chunk_fn == NULL +#endif + ) +#endif + png_chunk_error(png_ptr, "unknown critical chunk"); + + info_ptr = info_ptr; /* to quiet some compiler warnings */ + } + +#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) + if (png_ptr->flags & PNG_FLAG_KEEP_UNKNOWN_CHUNKS) + { +#ifdef PNG_MAX_MALLOC_64K + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "unknown chunk too large to fit in memory"); + skip = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + png_strncpy((png_charp)png_ptr->unknown_chunk.name, + (png_charp)png_ptr->chunk_name, 5); + png_ptr->unknown_chunk.data = (png_bytep)png_malloc(png_ptr, length); + png_ptr->unknown_chunk.size = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)png_ptr->unknown_chunk.data, length); +#if defined(PNG_READ_USER_CHUNKS_SUPPORTED) + if(png_ptr->read_user_chunk_fn != NULL) + { + /* callback to user unknown chunk handler */ + int ret; + ret = (*(png_ptr->read_user_chunk_fn)) + (png_ptr, &png_ptr->unknown_chunk); + if (ret < 0) + png_chunk_error(png_ptr, "error in user chunk"); + if (ret == 0) + { + if (!(png_ptr->chunk_name[0] & 0x20)) + if(png_handle_as_unknown(png_ptr, png_ptr->chunk_name) != + PNG_HANDLE_CHUNK_ALWAYS) + png_chunk_error(png_ptr, "unknown critical chunk"); + png_set_unknown_chunks(png_ptr, info_ptr, + &png_ptr->unknown_chunk, 1); + } + } +#else + png_set_unknown_chunks(png_ptr, info_ptr, &png_ptr->unknown_chunk, 1); +#endif + png_free(png_ptr, png_ptr->unknown_chunk.data); + png_ptr->unknown_chunk.data = NULL; + } + else +#endif + skip=length; + png_push_crc_skip(png_ptr, skip); +} + +void /* PRIVATE */ +png_push_have_info(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr->info_fn != NULL) + (*(png_ptr->info_fn))(png_ptr, info_ptr); +} + +void /* PRIVATE */ +png_push_have_end(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr->end_fn != NULL) + (*(png_ptr->end_fn))(png_ptr, info_ptr); +} + +void /* PRIVATE */ +png_push_have_row(png_structp png_ptr, png_bytep row) +{ + if (png_ptr->row_fn != NULL) + (*(png_ptr->row_fn))(png_ptr, row, png_ptr->row_number, + (int)png_ptr->pass); +} + +void PNGAPI +png_progressive_combine_row (png_structp png_ptr, + png_bytep old_row, png_bytep new_row) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_CONST int FARDATA png_pass_dsp_mask[7] = + {0xff, 0x0f, 0xff, 0x33, 0xff, 0x55, 0xff}; +#endif + if(png_ptr == NULL) return; + if (new_row != NULL) /* new_row must == png_ptr->row_buf here. */ + png_combine_row(png_ptr, old_row, png_pass_dsp_mask[png_ptr->pass]); +} + +void PNGAPI +png_set_progressive_read_fn(png_structp png_ptr, png_voidp progressive_ptr, + png_progressive_info_ptr info_fn, png_progressive_row_ptr row_fn, + png_progressive_end_ptr end_fn) +{ + if(png_ptr == NULL) return; + png_ptr->info_fn = info_fn; + png_ptr->row_fn = row_fn; + png_ptr->end_fn = end_fn; + + png_set_read_fn(png_ptr, progressive_ptr, png_push_fill_buffer); +} + +png_voidp PNGAPI +png_get_progressive_ptr(png_structp png_ptr) +{ + if(png_ptr == NULL) return (NULL); + return png_ptr->io_ptr; +} +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ +/********* End of inlined file: pngpread.c *********/ + +/********* Start of inlined file: pngrio.c *********/ +/* pngrio.c - functions for data input + * + * Last changed in libpng 1.2.13 November 13, 2006 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2006 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This file provides a location for all input. Users who need + * special handling are expected to write a function that has the same + * arguments as this and performs a similar function, but that possibly + * has a different input method. Note that you shouldn't change this + * function, but rather write a replacement function and then make + * libpng use it at run time with png_set_read_fn(...). + */ + +#define PNG_INTERNAL + +#if defined(PNG_READ_SUPPORTED) + +/* Read the data from whatever input you are using. The default routine + reads from a file pointer. Note that this routine sometimes gets called + with very small lengths, so you should implement some kind of simple + buffering if you are using unbuffered reads. This should never be asked + to read more then 64K on a 16 bit machine. */ +void /* PRIVATE */ +png_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_debug1(4,"reading %d bytes\n", (int)length); + if (png_ptr->read_data_fn != NULL) + (*(png_ptr->read_data_fn))(png_ptr, data, length); + else + png_error(png_ptr, "Call to NULL read function"); +} + +#if !defined(PNG_NO_STDIO) +/* This is the function that does the actual reading of data. If you are + not reading from a standard C stream, you should create a replacement + read_data function and use it at run time with png_set_read_fn(), rather + than changing the library. */ +#ifndef USE_FAR_KEYWORD +void PNGAPI +png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_size_t check; + + if(png_ptr == NULL) return; + /* fread() returns 0 on error, so it is OK to store this in a png_size_t + * instead of an int, which is what fread() actually returns. + */ +#if defined(_WIN32_WCE) + if ( !ReadFile((HANDLE)(png_ptr->io_ptr), data, length, &check, NULL) ) + check = 0; +#else + check = (png_size_t)fread(data, (png_size_t)1, length, + (png_FILE_p)png_ptr->io_ptr); +#endif + + if (check != length) + png_error(png_ptr, "Read Error"); +} +#else +/* this is the model-independent version. Since the standard I/O library + can't handle far buffers in the medium and small models, we have to copy + the data. +*/ + +#define NEAR_BUF_SIZE 1024 +#define MIN(a,b) (a <= b ? a : b) + +static void PNGAPI +png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + int check; + png_byte *n_data; + png_FILE_p io_ptr; + + if(png_ptr == NULL) return; + /* Check if data really is near. If so, use usual code. */ + n_data = (png_byte *)CVT_PTR_NOCHECK(data); + io_ptr = (png_FILE_p)CVT_PTR(png_ptr->io_ptr); + if ((png_bytep)n_data == data) + { +#if defined(_WIN32_WCE) + if ( !ReadFile((HANDLE)(png_ptr->io_ptr), data, length, &check, NULL) ) + check = 0; +#else + check = fread(n_data, 1, length, io_ptr); +#endif + } + else + { + png_byte buf[NEAR_BUF_SIZE]; + png_size_t read, remaining, err; + check = 0; + remaining = length; + do + { + read = MIN(NEAR_BUF_SIZE, remaining); +#if defined(_WIN32_WCE) + if ( !ReadFile((HANDLE)(io_ptr), buf, read, &err, NULL) ) + err = 0; +#else + err = fread(buf, (png_size_t)1, read, io_ptr); +#endif + png_memcpy(data, buf, read); /* copy far buffer to near buffer */ + if(err != read) + break; + else + check += err; + data += read; + remaining -= read; + } + while (remaining != 0); + } + if ((png_uint_32)check != (png_uint_32)length) + png_error(png_ptr, "read Error"); +} +#endif +#endif + +/* This function allows the application to supply a new input function + for libpng if standard C streams aren't being used. + + This function takes as its arguments: + png_ptr - pointer to a png input data structure + io_ptr - pointer to user supplied structure containing info about + the input functions. May be NULL. + read_data_fn - pointer to a new input function that takes as its + arguments a pointer to a png_struct, a pointer to + a location where input data can be stored, and a 32-bit + unsigned int that is the number of bytes to be read. + To exit and output any fatal error messages the new write + function should call png_error(png_ptr, "Error msg"). */ +void PNGAPI +png_set_read_fn(png_structp png_ptr, png_voidp io_ptr, + png_rw_ptr read_data_fn) +{ + if(png_ptr == NULL) return; + png_ptr->io_ptr = io_ptr; + +#if !defined(PNG_NO_STDIO) + if (read_data_fn != NULL) + png_ptr->read_data_fn = read_data_fn; + else + png_ptr->read_data_fn = png_default_read_data; +#else + png_ptr->read_data_fn = read_data_fn; +#endif + + /* It is an error to write to a read device */ + if (png_ptr->write_data_fn != NULL) + { + png_ptr->write_data_fn = NULL; + png_warning(png_ptr, + "It's an error to set both read_data_fn and write_data_fn in the "); + png_warning(png_ptr, + "same structure. Resetting write_data_fn to NULL."); + } + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) + png_ptr->output_flush_fn = NULL; +#endif +} +#endif /* PNG_READ_SUPPORTED */ +/********* End of inlined file: pngrio.c *********/ + +/********* Start of inlined file: pngrtran.c *********/ +/* pngrtran.c - transforms the data in a row for PNG readers + * + * Last changed in libpng 1.2.21 [October 4, 2007] + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2007 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This file contains functions optionally called by an application + * in order to tell libpng how to handle data when reading a PNG. + * Transformations that are used in both reading and writing are + * in pngtrans.c. + */ + +#define PNG_INTERNAL + +#if defined(PNG_READ_SUPPORTED) + +/* Set the action on getting a CRC error for an ancillary or critical chunk. */ +void PNGAPI +png_set_crc_action(png_structp png_ptr, int crit_action, int ancil_action) +{ + png_debug(1, "in png_set_crc_action\n"); + /* Tell libpng how we react to CRC errors in critical chunks */ + if(png_ptr == NULL) return; + switch (crit_action) + { + case PNG_CRC_NO_CHANGE: /* leave setting as is */ + break; + case PNG_CRC_WARN_USE: /* warn/use data */ + png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK; + png_ptr->flags |= PNG_FLAG_CRC_CRITICAL_USE; + break; + case PNG_CRC_QUIET_USE: /* quiet/use data */ + png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK; + png_ptr->flags |= PNG_FLAG_CRC_CRITICAL_USE | + PNG_FLAG_CRC_CRITICAL_IGNORE; + break; + case PNG_CRC_WARN_DISCARD: /* not a valid action for critical data */ + png_warning(png_ptr, "Can't discard critical data on CRC error."); + case PNG_CRC_ERROR_QUIT: /* error/quit */ + case PNG_CRC_DEFAULT: + default: + png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK; + break; + } + + switch (ancil_action) + { + case PNG_CRC_NO_CHANGE: /* leave setting as is */ + break; + case PNG_CRC_WARN_USE: /* warn/use data */ + png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; + png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_USE; + break; + case PNG_CRC_QUIET_USE: /* quiet/use data */ + png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; + png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_USE | + PNG_FLAG_CRC_ANCILLARY_NOWARN; + break; + case PNG_CRC_ERROR_QUIT: /* error/quit */ + png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; + png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_NOWARN; + break; + case PNG_CRC_WARN_DISCARD: /* warn/discard data */ + case PNG_CRC_DEFAULT: + default: + png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; + break; + } +} + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) && \ + defined(PNG_FLOATING_POINT_SUPPORTED) +/* handle alpha and tRNS via a background color */ +void PNGAPI +png_set_background(png_structp png_ptr, + png_color_16p background_color, int background_gamma_code, + int need_expand, double background_gamma) +{ + png_debug(1, "in png_set_background\n"); + if(png_ptr == NULL) return; + if (background_gamma_code == PNG_BACKGROUND_GAMMA_UNKNOWN) + { + png_warning(png_ptr, "Application must supply a known background gamma"); + return; + } + + png_ptr->transformations |= PNG_BACKGROUND; + png_memcpy(&(png_ptr->background), background_color, + png_sizeof(png_color_16)); + png_ptr->background_gamma = (float)background_gamma; + png_ptr->background_gamma_type = (png_byte)(background_gamma_code); + png_ptr->transformations |= (need_expand ? PNG_BACKGROUND_EXPAND : 0); +} +#endif + +#if defined(PNG_READ_16_TO_8_SUPPORTED) +/* strip 16 bit depth files to 8 bit depth */ +void PNGAPI +png_set_strip_16(png_structp png_ptr) +{ + png_debug(1, "in png_set_strip_16\n"); + if(png_ptr == NULL) return; + png_ptr->transformations |= PNG_16_TO_8; +} +#endif + +#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED) +void PNGAPI +png_set_strip_alpha(png_structp png_ptr) +{ + png_debug(1, "in png_set_strip_alpha\n"); + if(png_ptr == NULL) return; + png_ptr->flags |= PNG_FLAG_STRIP_ALPHA; +} +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) +/* Dither file to 8 bit. Supply a palette, the current number + * of elements in the palette, the maximum number of elements + * allowed, and a histogram if possible. If the current number + * of colors is greater then the maximum number, the palette will be + * modified to fit in the maximum number. "full_dither" indicates + * whether we need a dithering cube set up for RGB images, or if we + * simply are reducing the number of colors in a paletted image. + */ + +typedef struct png_dsort_struct +{ + struct png_dsort_struct FAR * next; + png_byte left; + png_byte right; +} png_dsort; +typedef png_dsort FAR * png_dsortp; +typedef png_dsort FAR * FAR * png_dsortpp; + +void PNGAPI +png_set_dither(png_structp png_ptr, png_colorp palette, + int num_palette, int maximum_colors, png_uint_16p histogram, + int full_dither) +{ + png_debug(1, "in png_set_dither\n"); + if(png_ptr == NULL) return; + png_ptr->transformations |= PNG_DITHER; + + if (!full_dither) + { + int i; + + png_ptr->dither_index = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(num_palette * png_sizeof (png_byte))); + for (i = 0; i < num_palette; i++) + png_ptr->dither_index[i] = (png_byte)i; + } + + if (num_palette > maximum_colors) + { + if (histogram != NULL) + { + /* This is easy enough, just throw out the least used colors. + Perhaps not the best solution, but good enough. */ + + int i; + + /* initialize an array to sort colors */ + png_ptr->dither_sort = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(num_palette * png_sizeof (png_byte))); + + /* initialize the dither_sort array */ + for (i = 0; i < num_palette; i++) + png_ptr->dither_sort[i] = (png_byte)i; + + /* Find the least used palette entries by starting a + bubble sort, and running it until we have sorted + out enough colors. Note that we don't care about + sorting all the colors, just finding which are + least used. */ + + for (i = num_palette - 1; i >= maximum_colors; i--) + { + int done; /* to stop early if the list is pre-sorted */ + int j; + + done = 1; + for (j = 0; j < i; j++) + { + if (histogram[png_ptr->dither_sort[j]] + < histogram[png_ptr->dither_sort[j + 1]]) + { + png_byte t; + + t = png_ptr->dither_sort[j]; + png_ptr->dither_sort[j] = png_ptr->dither_sort[j + 1]; + png_ptr->dither_sort[j + 1] = t; + done = 0; + } + } + if (done) + break; + } + + /* swap the palette around, and set up a table, if necessary */ + if (full_dither) + { + int j = num_palette; + + /* put all the useful colors within the max, but don't + move the others */ + for (i = 0; i < maximum_colors; i++) + { + if ((int)png_ptr->dither_sort[i] >= maximum_colors) + { + do + j--; + while ((int)png_ptr->dither_sort[j] >= maximum_colors); + palette[i] = palette[j]; + } + } + } + else + { + int j = num_palette; + + /* move all the used colors inside the max limit, and + develop a translation table */ + for (i = 0; i < maximum_colors; i++) + { + /* only move the colors we need to */ + if ((int)png_ptr->dither_sort[i] >= maximum_colors) + { + png_color tmp_color; + + do + j--; + while ((int)png_ptr->dither_sort[j] >= maximum_colors); + + tmp_color = palette[j]; + palette[j] = palette[i]; + palette[i] = tmp_color; + /* indicate where the color went */ + png_ptr->dither_index[j] = (png_byte)i; + png_ptr->dither_index[i] = (png_byte)j; + } + } + + /* find closest color for those colors we are not using */ + for (i = 0; i < num_palette; i++) + { + if ((int)png_ptr->dither_index[i] >= maximum_colors) + { + int min_d, k, min_k, d_index; + + /* find the closest color to one we threw out */ + d_index = png_ptr->dither_index[i]; + min_d = PNG_COLOR_DIST(palette[d_index], palette[0]); + for (k = 1, min_k = 0; k < maximum_colors; k++) + { + int d; + + d = PNG_COLOR_DIST(palette[d_index], palette[k]); + + if (d < min_d) + { + min_d = d; + min_k = k; + } + } + /* point to closest color */ + png_ptr->dither_index[i] = (png_byte)min_k; + } + } + } + png_free(png_ptr, png_ptr->dither_sort); + png_ptr->dither_sort=NULL; + } + else + { + /* This is much harder to do simply (and quickly). Perhaps + we need to go through a median cut routine, but those + don't always behave themselves with only a few colors + as input. So we will just find the closest two colors, + and throw out one of them (chosen somewhat randomly). + [We don't understand this at all, so if someone wants to + work on improving it, be our guest - AED, GRP] + */ + int i; + int max_d; + int num_new_palette; + png_dsortp t; + png_dsortpp hash; + + t=NULL; + + /* initialize palette index arrays */ + png_ptr->index_to_palette = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(num_palette * png_sizeof (png_byte))); + png_ptr->palette_to_index = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(num_palette * png_sizeof (png_byte))); + + /* initialize the sort array */ + for (i = 0; i < num_palette; i++) + { + png_ptr->index_to_palette[i] = (png_byte)i; + png_ptr->palette_to_index[i] = (png_byte)i; + } + + hash = (png_dsortpp)png_malloc(png_ptr, (png_uint_32)(769 * + png_sizeof (png_dsortp))); + for (i = 0; i < 769; i++) + hash[i] = NULL; +/* png_memset(hash, 0, 769 * png_sizeof (png_dsortp)); */ + + num_new_palette = num_palette; + + /* initial wild guess at how far apart the farthest pixel + pair we will be eliminating will be. Larger + numbers mean more areas will be allocated, Smaller + numbers run the risk of not saving enough data, and + having to do this all over again. + + I have not done extensive checking on this number. + */ + max_d = 96; + + while (num_new_palette > maximum_colors) + { + for (i = 0; i < num_new_palette - 1; i++) + { + int j; + + for (j = i + 1; j < num_new_palette; j++) + { + int d; + + d = PNG_COLOR_DIST(palette[i], palette[j]); + + if (d <= max_d) + { + + t = (png_dsortp)png_malloc_warn(png_ptr, + (png_uint_32)(png_sizeof(png_dsort))); + if (t == NULL) + break; + t->next = hash[d]; + t->left = (png_byte)i; + t->right = (png_byte)j; + hash[d] = t; + } + } + if (t == NULL) + break; + } + + if (t != NULL) + for (i = 0; i <= max_d; i++) + { + if (hash[i] != NULL) + { + png_dsortp p; + + for (p = hash[i]; p; p = p->next) + { + if ((int)png_ptr->index_to_palette[p->left] + < num_new_palette && + (int)png_ptr->index_to_palette[p->right] + < num_new_palette) + { + int j, next_j; + + if (num_new_palette & 0x01) + { + j = p->left; + next_j = p->right; + } + else + { + j = p->right; + next_j = p->left; + } + + num_new_palette--; + palette[png_ptr->index_to_palette[j]] + = palette[num_new_palette]; + if (!full_dither) + { + int k; + + for (k = 0; k < num_palette; k++) + { + if (png_ptr->dither_index[k] == + png_ptr->index_to_palette[j]) + png_ptr->dither_index[k] = + png_ptr->index_to_palette[next_j]; + if ((int)png_ptr->dither_index[k] == + num_new_palette) + png_ptr->dither_index[k] = + png_ptr->index_to_palette[j]; + } + } + + png_ptr->index_to_palette[png_ptr->palette_to_index + [num_new_palette]] = png_ptr->index_to_palette[j]; + png_ptr->palette_to_index[png_ptr->index_to_palette[j]] + = png_ptr->palette_to_index[num_new_palette]; + + png_ptr->index_to_palette[j] = (png_byte)num_new_palette; + png_ptr->palette_to_index[num_new_palette] = (png_byte)j; + } + if (num_new_palette <= maximum_colors) + break; + } + if (num_new_palette <= maximum_colors) + break; + } + } + + for (i = 0; i < 769; i++) + { + if (hash[i] != NULL) + { + png_dsortp p = hash[i]; + while (p) + { + t = p->next; + png_free(png_ptr, p); + p = t; + } + } + hash[i] = 0; + } + max_d += 96; + } + png_free(png_ptr, hash); + png_free(png_ptr, png_ptr->palette_to_index); + png_free(png_ptr, png_ptr->index_to_palette); + png_ptr->palette_to_index=NULL; + png_ptr->index_to_palette=NULL; + } + num_palette = maximum_colors; + } + if (png_ptr->palette == NULL) + { + png_ptr->palette = palette; + } + png_ptr->num_palette = (png_uint_16)num_palette; + + if (full_dither) + { + int i; + png_bytep distance; + int total_bits = PNG_DITHER_RED_BITS + PNG_DITHER_GREEN_BITS + + PNG_DITHER_BLUE_BITS; + int num_red = (1 << PNG_DITHER_RED_BITS); + int num_green = (1 << PNG_DITHER_GREEN_BITS); + int num_blue = (1 << PNG_DITHER_BLUE_BITS); + png_size_t num_entries = ((png_size_t)1 << total_bits); + + png_ptr->palette_lookup = (png_bytep )png_malloc(png_ptr, + (png_uint_32)(num_entries * png_sizeof (png_byte))); + + png_memset(png_ptr->palette_lookup, 0, num_entries * + png_sizeof (png_byte)); + + distance = (png_bytep)png_malloc(png_ptr, (png_uint_32)(num_entries * + png_sizeof(png_byte))); + + png_memset(distance, 0xff, num_entries * png_sizeof(png_byte)); + + for (i = 0; i < num_palette; i++) + { + int ir, ig, ib; + int r = (palette[i].red >> (8 - PNG_DITHER_RED_BITS)); + int g = (palette[i].green >> (8 - PNG_DITHER_GREEN_BITS)); + int b = (palette[i].blue >> (8 - PNG_DITHER_BLUE_BITS)); + + for (ir = 0; ir < num_red; ir++) + { + /* int dr = abs(ir - r); */ + int dr = ((ir > r) ? ir - r : r - ir); + int index_r = (ir << (PNG_DITHER_BLUE_BITS + PNG_DITHER_GREEN_BITS)); + + for (ig = 0; ig < num_green; ig++) + { + /* int dg = abs(ig - g); */ + int dg = ((ig > g) ? ig - g : g - ig); + int dt = dr + dg; + int dm = ((dr > dg) ? dr : dg); + int index_g = index_r | (ig << PNG_DITHER_BLUE_BITS); + + for (ib = 0; ib < num_blue; ib++) + { + int d_index = index_g | ib; + /* int db = abs(ib - b); */ + int db = ((ib > b) ? ib - b : b - ib); + int dmax = ((dm > db) ? dm : db); + int d = dmax + dt + db; + + if (d < (int)distance[d_index]) + { + distance[d_index] = (png_byte)d; + png_ptr->palette_lookup[d_index] = (png_byte)i; + } + } + } + } + } + + png_free(png_ptr, distance); + } +} +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) && defined(PNG_FLOATING_POINT_SUPPORTED) +/* Transform the image from the file_gamma to the screen_gamma. We + * only do transformations on images where the file_gamma and screen_gamma + * are not close reciprocals, otherwise it slows things down slightly, and + * also needlessly introduces small errors. + * + * We will turn off gamma transformation later if no semitransparent entries + * are present in the tRNS array for palette images. We can't do it here + * because we don't necessarily have the tRNS chunk yet. + */ +void PNGAPI +png_set_gamma(png_structp png_ptr, double scrn_gamma, double file_gamma) +{ + png_debug(1, "in png_set_gamma\n"); + if(png_ptr == NULL) return; + if ((fabs(scrn_gamma * file_gamma - 1.0) > PNG_GAMMA_THRESHOLD) || + (png_ptr->color_type & PNG_COLOR_MASK_ALPHA) || + (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)) + png_ptr->transformations |= PNG_GAMMA; + png_ptr->gamma = (float)file_gamma; + png_ptr->screen_gamma = (float)scrn_gamma; +} +#endif + +#if defined(PNG_READ_EXPAND_SUPPORTED) +/* Expand paletted images to RGB, expand grayscale images of + * less than 8-bit depth to 8-bit depth, and expand tRNS chunks + * to alpha channels. + */ +void PNGAPI +png_set_expand(png_structp png_ptr) +{ + png_debug(1, "in png_set_expand\n"); + if(png_ptr == NULL) return; + png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS); +#ifdef PNG_WARN_UNINITIALIZED_ROW + png_ptr->flags &= ~PNG_FLAG_ROW_INIT; +#endif +} + +/* GRR 19990627: the following three functions currently are identical + * to png_set_expand(). However, it is entirely reasonable that someone + * might wish to expand an indexed image to RGB but *not* expand a single, + * fully transparent palette entry to a full alpha channel--perhaps instead + * convert tRNS to the grayscale/RGB format (16-bit RGB value), or replace + * the transparent color with a particular RGB value, or drop tRNS entirely. + * IOW, a future version of the library may make the transformations flag + * a bit more fine-grained, with separate bits for each of these three + * functions. + * + * More to the point, these functions make it obvious what libpng will be + * doing, whereas "expand" can (and does) mean any number of things. + * + * GRP 20060307: In libpng-1.4.0, png_set_gray_1_2_4_to_8() was modified + * to expand only the sample depth but not to expand the tRNS to alpha. + */ + +/* Expand paletted images to RGB. */ +void PNGAPI +png_set_palette_to_rgb(png_structp png_ptr) +{ + png_debug(1, "in png_set_palette_to_rgb\n"); + if(png_ptr == NULL) return; + png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS); +#ifdef PNG_WARN_UNINITIALIZED_ROW + png_ptr->flags &= !(PNG_FLAG_ROW_INIT); + png_ptr->flags &= ~PNG_FLAG_ROW_INIT; +#endif +} + +#if !defined(PNG_1_0_X) +/* Expand grayscale images of less than 8-bit depth to 8 bits. */ +void PNGAPI +png_set_expand_gray_1_2_4_to_8(png_structp png_ptr) +{ + png_debug(1, "in png_set_expand_gray_1_2_4_to_8\n"); + if(png_ptr == NULL) return; + png_ptr->transformations |= PNG_EXPAND; +#ifdef PNG_WARN_UNINITIALIZED_ROW + png_ptr->flags &= ~PNG_FLAG_ROW_INIT; +#endif +} +#endif + +#if defined(PNG_1_0_X) || defined(PNG_1_2_X) +/* Expand grayscale images of less than 8-bit depth to 8 bits. */ +/* Deprecated as of libpng-1.2.9 */ +void PNGAPI +png_set_gray_1_2_4_to_8(png_structp png_ptr) +{ + png_debug(1, "in png_set_gray_1_2_4_to_8\n"); + if(png_ptr == NULL) return; + png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS); +} +#endif + +/* Expand tRNS chunks to alpha channels. */ +void PNGAPI +png_set_tRNS_to_alpha(png_structp png_ptr) +{ + png_debug(1, "in png_set_tRNS_to_alpha\n"); + png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS); +#ifdef PNG_WARN_UNINITIALIZED_ROW + png_ptr->flags &= ~PNG_FLAG_ROW_INIT; +#endif +} +#endif /* defined(PNG_READ_EXPAND_SUPPORTED) */ + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) +void PNGAPI +png_set_gray_to_rgb(png_structp png_ptr) +{ + png_debug(1, "in png_set_gray_to_rgb\n"); + png_ptr->transformations |= PNG_GRAY_TO_RGB; +#ifdef PNG_WARN_UNINITIALIZED_ROW + png_ptr->flags &= ~PNG_FLAG_ROW_INIT; +#endif +} +#endif + +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +#if defined(PNG_FLOATING_POINT_SUPPORTED) +/* Convert a RGB image to a grayscale of the same width. This allows us, + * for example, to convert a 24 bpp RGB image into an 8 bpp grayscale image. + */ + +void PNGAPI +png_set_rgb_to_gray(png_structp png_ptr, int error_action, double red, + double green) +{ + int red_fixed = (int)((float)red*100000.0 + 0.5); + int green_fixed = (int)((float)green*100000.0 + 0.5); + if(png_ptr == NULL) return; + png_set_rgb_to_gray_fixed(png_ptr, error_action, red_fixed, green_fixed); +} +#endif + +void PNGAPI +png_set_rgb_to_gray_fixed(png_structp png_ptr, int error_action, + png_fixed_point red, png_fixed_point green) +{ + png_debug(1, "in png_set_rgb_to_gray\n"); + if(png_ptr == NULL) return; + switch(error_action) + { + case 1: png_ptr->transformations |= PNG_RGB_TO_GRAY; + break; + case 2: png_ptr->transformations |= PNG_RGB_TO_GRAY_WARN; + break; + case 3: png_ptr->transformations |= PNG_RGB_TO_GRAY_ERR; + } + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) +#if defined(PNG_READ_EXPAND_SUPPORTED) + png_ptr->transformations |= PNG_EXPAND; +#else + { + png_warning(png_ptr, "Cannot do RGB_TO_GRAY without EXPAND_SUPPORTED."); + png_ptr->transformations &= ~PNG_RGB_TO_GRAY; + } +#endif + { + png_uint_16 red_int, green_int; + if(red < 0 || green < 0) + { + red_int = 6968; /* .212671 * 32768 + .5 */ + green_int = 23434; /* .715160 * 32768 + .5 */ + } + else if(red + green < 100000L) + { + red_int = (png_uint_16)(((png_uint_32)red*32768L)/100000L); + green_int = (png_uint_16)(((png_uint_32)green*32768L)/100000L); + } + else + { + png_warning(png_ptr, "ignoring out of range rgb_to_gray coefficients"); + red_int = 6968; + green_int = 23434; + } + png_ptr->rgb_to_gray_red_coeff = red_int; + png_ptr->rgb_to_gray_green_coeff = green_int; + png_ptr->rgb_to_gray_blue_coeff = (png_uint_16)(32768-red_int-green_int); + } +} +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +void PNGAPI +png_set_read_user_transform_fn(png_structp png_ptr, png_user_transform_ptr + read_user_transform_fn) +{ + png_debug(1, "in png_set_read_user_transform_fn\n"); + if(png_ptr == NULL) return; +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) + png_ptr->transformations |= PNG_USER_TRANSFORM; + png_ptr->read_user_transform_fn = read_user_transform_fn; +#endif +#ifdef PNG_LEGACY_SUPPORTED + if(read_user_transform_fn) + png_warning(png_ptr, + "This version of libpng does not support user transforms"); +#endif +} +#endif + +/* Initialize everything needed for the read. This includes modifying + * the palette. + */ +void /* PRIVATE */ +png_init_read_transformations(png_structp png_ptr) +{ + png_debug(1, "in png_init_read_transformations\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if(png_ptr != NULL) +#endif + { +#if defined(PNG_READ_BACKGROUND_SUPPORTED) || defined(PNG_READ_SHIFT_SUPPORTED) \ + || defined(PNG_READ_GAMMA_SUPPORTED) + int color_type = png_ptr->color_type; +#endif + +#if defined(PNG_READ_EXPAND_SUPPORTED) && defined(PNG_READ_BACKGROUND_SUPPORTED) + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) + /* Detect gray background and attempt to enable optimization + * for gray --> RGB case */ + /* Note: if PNG_BACKGROUND_EXPAND is set and color_type is either RGB or + * RGB_ALPHA (in which case need_expand is superfluous anyway), the + * background color might actually be gray yet not be flagged as such. + * This is not a problem for the current code, which uses + * PNG_BACKGROUND_IS_GRAY only to decide when to do the + * png_do_gray_to_rgb() transformation. + */ + if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) && + !(color_type & PNG_COLOR_MASK_COLOR)) + { + png_ptr->mode |= PNG_BACKGROUND_IS_GRAY; + } else if ((png_ptr->transformations & PNG_BACKGROUND) && + !(png_ptr->transformations & PNG_BACKGROUND_EXPAND) && + (png_ptr->transformations & PNG_GRAY_TO_RGB) && + png_ptr->background.red == png_ptr->background.green && + png_ptr->background.red == png_ptr->background.blue) + { + png_ptr->mode |= PNG_BACKGROUND_IS_GRAY; + png_ptr->background.gray = png_ptr->background.red; + } +#endif + + if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) && + (png_ptr->transformations & PNG_EXPAND)) + { + if (!(color_type & PNG_COLOR_MASK_COLOR)) /* i.e., GRAY or GRAY_ALPHA */ + { + /* expand background and tRNS chunks */ + switch (png_ptr->bit_depth) + { + case 1: + png_ptr->background.gray *= (png_uint_16)0xff; + png_ptr->background.red = png_ptr->background.green + = png_ptr->background.blue = png_ptr->background.gray; + if (!(png_ptr->transformations & PNG_EXPAND_tRNS)) + { + png_ptr->trans_values.gray *= (png_uint_16)0xff; + png_ptr->trans_values.red = png_ptr->trans_values.green + = png_ptr->trans_values.blue = png_ptr->trans_values.gray; + } + break; + case 2: + png_ptr->background.gray *= (png_uint_16)0x55; + png_ptr->background.red = png_ptr->background.green + = png_ptr->background.blue = png_ptr->background.gray; + if (!(png_ptr->transformations & PNG_EXPAND_tRNS)) + { + png_ptr->trans_values.gray *= (png_uint_16)0x55; + png_ptr->trans_values.red = png_ptr->trans_values.green + = png_ptr->trans_values.blue = png_ptr->trans_values.gray; + } + break; + case 4: + png_ptr->background.gray *= (png_uint_16)0x11; + png_ptr->background.red = png_ptr->background.green + = png_ptr->background.blue = png_ptr->background.gray; + if (!(png_ptr->transformations & PNG_EXPAND_tRNS)) + { + png_ptr->trans_values.gray *= (png_uint_16)0x11; + png_ptr->trans_values.red = png_ptr->trans_values.green + = png_ptr->trans_values.blue = png_ptr->trans_values.gray; + } + break; + case 8: + case 16: + png_ptr->background.red = png_ptr->background.green + = png_ptr->background.blue = png_ptr->background.gray; + break; + } + } + else if (color_type == PNG_COLOR_TYPE_PALETTE) + { + png_ptr->background.red = + png_ptr->palette[png_ptr->background.index].red; + png_ptr->background.green = + png_ptr->palette[png_ptr->background.index].green; + png_ptr->background.blue = + png_ptr->palette[png_ptr->background.index].blue; + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_ALPHA) + { +#if defined(PNG_READ_EXPAND_SUPPORTED) + if (!(png_ptr->transformations & PNG_EXPAND_tRNS)) +#endif + { + /* invert the alpha channel (in tRNS) unless the pixels are + going to be expanded, in which case leave it for later */ + int i,istop; + istop=(int)png_ptr->num_trans; + for (i=0; itrans[i] = (png_byte)(255 - png_ptr->trans[i]); + } + } +#endif + + } + } +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) && defined(PNG_READ_GAMMA_SUPPORTED) + png_ptr->background_1 = png_ptr->background; +#endif +#if defined(PNG_READ_GAMMA_SUPPORTED) && defined(PNG_FLOATING_POINT_SUPPORTED) + + if ((color_type == PNG_COLOR_TYPE_PALETTE && png_ptr->num_trans != 0) + && (fabs(png_ptr->screen_gamma * png_ptr->gamma - 1.0) + < PNG_GAMMA_THRESHOLD)) + { + int i,k; + k=0; + for (i=0; inum_trans; i++) + { + if (png_ptr->trans[i] != 0 && png_ptr->trans[i] != 0xff) + k=1; /* partial transparency is present */ + } + if (k == 0) + png_ptr->transformations &= (~PNG_GAMMA); + } + + if ((png_ptr->transformations & (PNG_GAMMA | PNG_RGB_TO_GRAY)) && + png_ptr->gamma != 0.0) + { + png_build_gamma_table(png_ptr); +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->transformations & PNG_BACKGROUND) + { + if (color_type == PNG_COLOR_TYPE_PALETTE) + { + /* could skip if no transparency and + */ + png_color back, back_1; + png_colorp palette = png_ptr->palette; + int num_palette = png_ptr->num_palette; + int i; + if (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_FILE) + { + back.red = png_ptr->gamma_table[png_ptr->background.red]; + back.green = png_ptr->gamma_table[png_ptr->background.green]; + back.blue = png_ptr->gamma_table[png_ptr->background.blue]; + + back_1.red = png_ptr->gamma_to_1[png_ptr->background.red]; + back_1.green = png_ptr->gamma_to_1[png_ptr->background.green]; + back_1.blue = png_ptr->gamma_to_1[png_ptr->background.blue]; + } + else + { + double g, gs; + + switch (png_ptr->background_gamma_type) + { + case PNG_BACKGROUND_GAMMA_SCREEN: + g = (png_ptr->screen_gamma); + gs = 1.0; + break; + case PNG_BACKGROUND_GAMMA_FILE: + g = 1.0 / (png_ptr->gamma); + gs = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma); + break; + case PNG_BACKGROUND_GAMMA_UNIQUE: + g = 1.0 / (png_ptr->background_gamma); + gs = 1.0 / (png_ptr->background_gamma * + png_ptr->screen_gamma); + break; + default: + g = 1.0; /* back_1 */ + gs = 1.0; /* back */ + } + + if ( fabs(gs - 1.0) < PNG_GAMMA_THRESHOLD) + { + back.red = (png_byte)png_ptr->background.red; + back.green = (png_byte)png_ptr->background.green; + back.blue = (png_byte)png_ptr->background.blue; + } + else + { + back.red = (png_byte)(pow( + (double)png_ptr->background.red/255, gs) * 255.0 + .5); + back.green = (png_byte)(pow( + (double)png_ptr->background.green/255, gs) * 255.0 + .5); + back.blue = (png_byte)(pow( + (double)png_ptr->background.blue/255, gs) * 255.0 + .5); + } + + back_1.red = (png_byte)(pow( + (double)png_ptr->background.red/255, g) * 255.0 + .5); + back_1.green = (png_byte)(pow( + (double)png_ptr->background.green/255, g) * 255.0 + .5); + back_1.blue = (png_byte)(pow( + (double)png_ptr->background.blue/255, g) * 255.0 + .5); + } + for (i = 0; i < num_palette; i++) + { + if (i < (int)png_ptr->num_trans && png_ptr->trans[i] != 0xff) + { + if (png_ptr->trans[i] == 0) + { + palette[i] = back; + } + else /* if (png_ptr->trans[i] != 0xff) */ + { + png_byte v, w; + + v = png_ptr->gamma_to_1[palette[i].red]; + png_composite(w, v, png_ptr->trans[i], back_1.red); + palette[i].red = png_ptr->gamma_from_1[w]; + + v = png_ptr->gamma_to_1[palette[i].green]; + png_composite(w, v, png_ptr->trans[i], back_1.green); + palette[i].green = png_ptr->gamma_from_1[w]; + + v = png_ptr->gamma_to_1[palette[i].blue]; + png_composite(w, v, png_ptr->trans[i], back_1.blue); + palette[i].blue = png_ptr->gamma_from_1[w]; + } + } + else + { + palette[i].red = png_ptr->gamma_table[palette[i].red]; + palette[i].green = png_ptr->gamma_table[palette[i].green]; + palette[i].blue = png_ptr->gamma_table[palette[i].blue]; + } + } + } + /* if (png_ptr->background_gamma_type!=PNG_BACKGROUND_GAMMA_UNKNOWN) */ + else + /* color_type != PNG_COLOR_TYPE_PALETTE */ + { + double m = (double)(((png_uint_32)1 << png_ptr->bit_depth) - 1); + double g = 1.0; + double gs = 1.0; + + switch (png_ptr->background_gamma_type) + { + case PNG_BACKGROUND_GAMMA_SCREEN: + g = (png_ptr->screen_gamma); + gs = 1.0; + break; + case PNG_BACKGROUND_GAMMA_FILE: + g = 1.0 / (png_ptr->gamma); + gs = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma); + break; + case PNG_BACKGROUND_GAMMA_UNIQUE: + g = 1.0 / (png_ptr->background_gamma); + gs = 1.0 / (png_ptr->background_gamma * + png_ptr->screen_gamma); + break; + } + + png_ptr->background_1.gray = (png_uint_16)(pow( + (double)png_ptr->background.gray / m, g) * m + .5); + png_ptr->background.gray = (png_uint_16)(pow( + (double)png_ptr->background.gray / m, gs) * m + .5); + + if ((png_ptr->background.red != png_ptr->background.green) || + (png_ptr->background.red != png_ptr->background.blue) || + (png_ptr->background.red != png_ptr->background.gray)) + { + /* RGB or RGBA with color background */ + png_ptr->background_1.red = (png_uint_16)(pow( + (double)png_ptr->background.red / m, g) * m + .5); + png_ptr->background_1.green = (png_uint_16)(pow( + (double)png_ptr->background.green / m, g) * m + .5); + png_ptr->background_1.blue = (png_uint_16)(pow( + (double)png_ptr->background.blue / m, g) * m + .5); + png_ptr->background.red = (png_uint_16)(pow( + (double)png_ptr->background.red / m, gs) * m + .5); + png_ptr->background.green = (png_uint_16)(pow( + (double)png_ptr->background.green / m, gs) * m + .5); + png_ptr->background.blue = (png_uint_16)(pow( + (double)png_ptr->background.blue / m, gs) * m + .5); + } + else + { + /* GRAY, GRAY ALPHA, RGB, or RGBA with gray background */ + png_ptr->background_1.red = png_ptr->background_1.green + = png_ptr->background_1.blue = png_ptr->background_1.gray; + png_ptr->background.red = png_ptr->background.green + = png_ptr->background.blue = png_ptr->background.gray; + } + } + } + else + /* transformation does not include PNG_BACKGROUND */ +#endif /* PNG_READ_BACKGROUND_SUPPORTED */ + if (color_type == PNG_COLOR_TYPE_PALETTE) + { + png_colorp palette = png_ptr->palette; + int num_palette = png_ptr->num_palette; + int i; + + for (i = 0; i < num_palette; i++) + { + palette[i].red = png_ptr->gamma_table[palette[i].red]; + palette[i].green = png_ptr->gamma_table[palette[i].green]; + palette[i].blue = png_ptr->gamma_table[palette[i].blue]; + } + } + } +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + else +#endif +#endif /* PNG_READ_GAMMA_SUPPORTED && PNG_FLOATING_POINT_SUPPORTED */ +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + /* No GAMMA transformation */ + if ((png_ptr->transformations & PNG_BACKGROUND) && + (color_type == PNG_COLOR_TYPE_PALETTE)) + { + int i; + int istop = (int)png_ptr->num_trans; + png_color back; + png_colorp palette = png_ptr->palette; + + back.red = (png_byte)png_ptr->background.red; + back.green = (png_byte)png_ptr->background.green; + back.blue = (png_byte)png_ptr->background.blue; + + for (i = 0; i < istop; i++) + { + if (png_ptr->trans[i] == 0) + { + palette[i] = back; + } + else if (png_ptr->trans[i] != 0xff) + { + /* The png_composite() macro is defined in png.h */ + png_composite(palette[i].red, palette[i].red, + png_ptr->trans[i], back.red); + png_composite(palette[i].green, palette[i].green, + png_ptr->trans[i], back.green); + png_composite(palette[i].blue, palette[i].blue, + png_ptr->trans[i], back.blue); + } + } + } +#endif /* PNG_READ_BACKGROUND_SUPPORTED */ + +#if defined(PNG_READ_SHIFT_SUPPORTED) + if ((png_ptr->transformations & PNG_SHIFT) && + (color_type == PNG_COLOR_TYPE_PALETTE)) + { + png_uint_16 i; + png_uint_16 istop = png_ptr->num_palette; + int sr = 8 - png_ptr->sig_bit.red; + int sg = 8 - png_ptr->sig_bit.green; + int sb = 8 - png_ptr->sig_bit.blue; + + if (sr < 0 || sr > 8) + sr = 0; + if (sg < 0 || sg > 8) + sg = 0; + if (sb < 0 || sb > 8) + sb = 0; + for (i = 0; i < istop; i++) + { + png_ptr->palette[i].red >>= sr; + png_ptr->palette[i].green >>= sg; + png_ptr->palette[i].blue >>= sb; + } + } +#endif /* PNG_READ_SHIFT_SUPPORTED */ + } +#if !defined(PNG_READ_GAMMA_SUPPORTED) && !defined(PNG_READ_SHIFT_SUPPORTED) \ + && !defined(PNG_READ_BACKGROUND_SUPPORTED) + if(png_ptr) + return; +#endif +} + +/* Modify the info structure to reflect the transformations. The + * info should be updated so a PNG file could be written with it, + * assuming the transformations result in valid PNG data. + */ +void /* PRIVATE */ +png_read_transform_info(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_read_transform_info\n"); +#if defined(PNG_READ_EXPAND_SUPPORTED) + if (png_ptr->transformations & PNG_EXPAND) + { + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (png_ptr->num_trans && (png_ptr->transformations & PNG_EXPAND_tRNS)) + info_ptr->color_type = PNG_COLOR_TYPE_RGB_ALPHA; + else + info_ptr->color_type = PNG_COLOR_TYPE_RGB; + info_ptr->bit_depth = 8; + info_ptr->num_trans = 0; + } + else + { + if (png_ptr->num_trans) + { + if (png_ptr->transformations & PNG_EXPAND_tRNS) + info_ptr->color_type |= PNG_COLOR_MASK_ALPHA; + else + info_ptr->color_type |= PNG_COLOR_MASK_COLOR; + } + if (info_ptr->bit_depth < 8) + info_ptr->bit_depth = 8; + info_ptr->num_trans = 0; + } + } +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->transformations & PNG_BACKGROUND) + { + info_ptr->color_type &= ~PNG_COLOR_MASK_ALPHA; + info_ptr->num_trans = 0; + info_ptr->background = png_ptr->background; + } +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (png_ptr->transformations & PNG_GAMMA) + { +#ifdef PNG_FLOATING_POINT_SUPPORTED + info_ptr->gamma = png_ptr->gamma; +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + info_ptr->int_gamma = png_ptr->int_gamma; +#endif + } +#endif + +#if defined(PNG_READ_16_TO_8_SUPPORTED) + if ((png_ptr->transformations & PNG_16_TO_8) && (info_ptr->bit_depth == 16)) + info_ptr->bit_depth = 8; +#endif + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) + if (png_ptr->transformations & PNG_GRAY_TO_RGB) + info_ptr->color_type |= PNG_COLOR_MASK_COLOR; +#endif + +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + if (png_ptr->transformations & PNG_RGB_TO_GRAY) + info_ptr->color_type &= ~PNG_COLOR_MASK_COLOR; +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) + if (png_ptr->transformations & PNG_DITHER) + { + if (((info_ptr->color_type == PNG_COLOR_TYPE_RGB) || + (info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)) && + png_ptr->palette_lookup && info_ptr->bit_depth == 8) + { + info_ptr->color_type = PNG_COLOR_TYPE_PALETTE; + } + } +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) + if ((png_ptr->transformations & PNG_PACK) && (info_ptr->bit_depth < 8)) + info_ptr->bit_depth = 8; +#endif + + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + info_ptr->channels = 1; + else if (info_ptr->color_type & PNG_COLOR_MASK_COLOR) + info_ptr->channels = 3; + else + info_ptr->channels = 1; + +#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED) + if (png_ptr->flags & PNG_FLAG_STRIP_ALPHA) + info_ptr->color_type &= ~PNG_COLOR_MASK_ALPHA; +#endif + + if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA) + info_ptr->channels++; + +#if defined(PNG_READ_FILLER_SUPPORTED) + /* STRIP_ALPHA and FILLER allowed: MASK_ALPHA bit stripped above */ + if ((png_ptr->transformations & PNG_FILLER) && + ((info_ptr->color_type == PNG_COLOR_TYPE_RGB) || + (info_ptr->color_type == PNG_COLOR_TYPE_GRAY))) + { + info_ptr->channels++; + /* if adding a true alpha channel not just filler */ +#if !defined(PNG_1_0_X) + if (png_ptr->transformations & PNG_ADD_ALPHA) + info_ptr->color_type |= PNG_COLOR_MASK_ALPHA; +#endif + } +#endif + +#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) && \ +defined(PNG_READ_USER_TRANSFORM_SUPPORTED) + if(png_ptr->transformations & PNG_USER_TRANSFORM) + { + if(info_ptr->bit_depth < png_ptr->user_transform_depth) + info_ptr->bit_depth = png_ptr->user_transform_depth; + if(info_ptr->channels < png_ptr->user_transform_channels) + info_ptr->channels = png_ptr->user_transform_channels; + } +#endif + + info_ptr->pixel_depth = (png_byte)(info_ptr->channels * + info_ptr->bit_depth); + + info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth,info_ptr->width); + +#if !defined(PNG_READ_EXPAND_SUPPORTED) + if(png_ptr) + return; +#endif +} + +/* Transform the row. The order of transformations is significant, + * and is very touchy. If you add a transformation, take care to + * decide how it fits in with the other transformations here. + */ +void /* PRIVATE */ +png_do_read_transformations(png_structp png_ptr) +{ + png_debug(1, "in png_do_read_transformations\n"); + if (png_ptr->row_buf == NULL) + { +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + char msg[50]; + + png_snprintf2(msg, 50, + "NULL row buffer for row %ld, pass %d", png_ptr->row_number, + png_ptr->pass); + png_error(png_ptr, msg); +#else + png_error(png_ptr, "NULL row buffer"); +#endif + } +#ifdef PNG_WARN_UNINITIALIZED_ROW + if (!(png_ptr->flags & PNG_FLAG_ROW_INIT)) + /* Application has failed to call either png_read_start_image() + * or png_read_update_info() after setting transforms that expand + * pixels. This check added to libpng-1.2.19 */ +#if (PNG_WARN_UNINITIALIZED_ROW==1) + png_error(png_ptr, "Uninitialized row"); +#else + png_warning(png_ptr, "Uninitialized row"); +#endif +#endif + +#if defined(PNG_READ_EXPAND_SUPPORTED) + if (png_ptr->transformations & PNG_EXPAND) + { + if (png_ptr->row_info.color_type == PNG_COLOR_TYPE_PALETTE) + { + png_do_expand_palette(&(png_ptr->row_info), png_ptr->row_buf + 1, + png_ptr->palette, png_ptr->trans, png_ptr->num_trans); + } + else + { + if (png_ptr->num_trans && + (png_ptr->transformations & PNG_EXPAND_tRNS)) + png_do_expand(&(png_ptr->row_info), png_ptr->row_buf + 1, + &(png_ptr->trans_values)); + else + png_do_expand(&(png_ptr->row_info), png_ptr->row_buf + 1, + NULL); + } + } +#endif + +#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED) + if (png_ptr->flags & PNG_FLAG_STRIP_ALPHA) + png_do_strip_filler(&(png_ptr->row_info), png_ptr->row_buf + 1, + PNG_FLAG_FILLER_AFTER | (png_ptr->flags & PNG_FLAG_STRIP_ALPHA)); +#endif + +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + if (png_ptr->transformations & PNG_RGB_TO_GRAY) + { + int rgb_error = + png_do_rgb_to_gray(png_ptr, &(png_ptr->row_info), png_ptr->row_buf + 1); + if(rgb_error) + { + png_ptr->rgb_to_gray_status=1; + if((png_ptr->transformations & PNG_RGB_TO_GRAY) == + PNG_RGB_TO_GRAY_WARN) + png_warning(png_ptr, "png_do_rgb_to_gray found nongray pixel"); + if((png_ptr->transformations & PNG_RGB_TO_GRAY) == + PNG_RGB_TO_GRAY_ERR) + png_error(png_ptr, "png_do_rgb_to_gray found nongray pixel"); + } + } +#endif + +/* +From Andreas Dilger e-mail to png-implement, 26 March 1998: + + In most cases, the "simple transparency" should be done prior to doing + gray-to-RGB, or you will have to test 3x as many bytes to check if a + pixel is transparent. You would also need to make sure that the + transparency information is upgraded to RGB. + + To summarize, the current flow is: + - Gray + simple transparency -> compare 1 or 2 gray bytes and composite + with background "in place" if transparent, + convert to RGB if necessary + - Gray + alpha -> composite with gray background and remove alpha bytes, + convert to RGB if necessary + + To support RGB backgrounds for gray images we need: + - Gray + simple transparency -> convert to RGB + simple transparency, compare + 3 or 6 bytes and composite with background + "in place" if transparent (3x compare/pixel + compared to doing composite with gray bkgrnd) + - Gray + alpha -> convert to RGB + alpha, composite with background and + remove alpha bytes (3x float operations/pixel + compared with composite on gray background) + + Greg's change will do this. The reason it wasn't done before is for + performance, as this increases the per-pixel operations. If we would check + in advance if the background was gray or RGB, and position the gray-to-RGB + transform appropriately, then it would save a lot of work/time. + */ + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) + /* if gray -> RGB, do so now only if background is non-gray; else do later + * for performance reasons */ + if ((png_ptr->transformations & PNG_GRAY_TO_RGB) && + !(png_ptr->mode & PNG_BACKGROUND_IS_GRAY)) + png_do_gray_to_rgb(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + if ((png_ptr->transformations & PNG_BACKGROUND) && + ((png_ptr->num_trans != 0 ) || + (png_ptr->color_type & PNG_COLOR_MASK_ALPHA))) + png_do_background(&(png_ptr->row_info), png_ptr->row_buf + 1, + &(png_ptr->trans_values), &(png_ptr->background) +#if defined(PNG_READ_GAMMA_SUPPORTED) + , &(png_ptr->background_1), + png_ptr->gamma_table, png_ptr->gamma_from_1, + png_ptr->gamma_to_1, png_ptr->gamma_16_table, + png_ptr->gamma_16_from_1, png_ptr->gamma_16_to_1, + png_ptr->gamma_shift +#endif +); +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) + if ((png_ptr->transformations & PNG_GAMMA) && +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + !((png_ptr->transformations & PNG_BACKGROUND) && + ((png_ptr->num_trans != 0) || + (png_ptr->color_type & PNG_COLOR_MASK_ALPHA))) && +#endif + (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE)) + png_do_gamma(&(png_ptr->row_info), png_ptr->row_buf + 1, + png_ptr->gamma_table, png_ptr->gamma_16_table, + png_ptr->gamma_shift); +#endif + +#if defined(PNG_READ_16_TO_8_SUPPORTED) + if (png_ptr->transformations & PNG_16_TO_8) + png_do_chop(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) + if (png_ptr->transformations & PNG_DITHER) + { + png_do_dither((png_row_infop)&(png_ptr->row_info), png_ptr->row_buf + 1, + png_ptr->palette_lookup, png_ptr->dither_index); + if(png_ptr->row_info.rowbytes == (png_uint_32)0) + png_error(png_ptr, "png_do_dither returned rowbytes=0"); + } +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_MONO) + png_do_invert(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) + if (png_ptr->transformations & PNG_SHIFT) + png_do_unshift(&(png_ptr->row_info), png_ptr->row_buf + 1, + &(png_ptr->shift)); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) + if (png_ptr->transformations & PNG_PACK) + png_do_unpack(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) + if (png_ptr->transformations & PNG_BGR) + png_do_bgr(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + png_do_packswap(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) + /* if gray -> RGB, do so now only if we did not do so above */ + if ((png_ptr->transformations & PNG_GRAY_TO_RGB) && + (png_ptr->mode & PNG_BACKGROUND_IS_GRAY)) + png_do_gray_to_rgb(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) + if (png_ptr->transformations & PNG_FILLER) + png_do_read_filler(&(png_ptr->row_info), png_ptr->row_buf + 1, + (png_uint_32)png_ptr->filler, png_ptr->flags); +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_ALPHA) + png_do_read_invert_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) + if (png_ptr->transformations & PNG_SWAP_ALPHA) + png_do_read_swap_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_SWAP_SUPPORTED) + if (png_ptr->transformations & PNG_SWAP_BYTES) + png_do_swap(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) + if (png_ptr->transformations & PNG_USER_TRANSFORM) + { + if(png_ptr->read_user_transform_fn != NULL) + (*(png_ptr->read_user_transform_fn)) /* user read transform function */ + (png_ptr, /* png_ptr */ + &(png_ptr->row_info), /* row_info: */ + /* png_uint_32 width; width of row */ + /* png_uint_32 rowbytes; number of bytes in row */ + /* png_byte color_type; color type of pixels */ + /* png_byte bit_depth; bit depth of samples */ + /* png_byte channels; number of channels (1-4) */ + /* png_byte pixel_depth; bits per pixel (depth*channels) */ + png_ptr->row_buf + 1); /* start of pixel data for row */ +#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) + if(png_ptr->user_transform_depth) + png_ptr->row_info.bit_depth = png_ptr->user_transform_depth; + if(png_ptr->user_transform_channels) + png_ptr->row_info.channels = png_ptr->user_transform_channels; +#endif + png_ptr->row_info.pixel_depth = (png_byte)(png_ptr->row_info.bit_depth * + png_ptr->row_info.channels); + png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth, + png_ptr->row_info.width); + } +#endif + +} + +#if defined(PNG_READ_PACK_SUPPORTED) +/* Unpack pixels of 1, 2, or 4 bits per pixel into 1 byte per pixel, + * without changing the actual values. Thus, if you had a row with + * a bit depth of 1, you would end up with bytes that only contained + * the numbers 0 or 1. If you would rather they contain 0 and 255, use + * png_do_shift() after this. + */ +void /* PRIVATE */ +png_do_unpack(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_unpack\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL && row_info->bit_depth < 8) +#else + if (row_info->bit_depth < 8) +#endif + { + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + switch (row_info->bit_depth) + { + case 1: + { + png_bytep sp = row + (png_size_t)((row_width - 1) >> 3); + png_bytep dp = row + (png_size_t)row_width - 1; + png_uint_32 shift = 7 - (int)((row_width + 7) & 0x07); + for (i = 0; i < row_width; i++) + { + *dp = (png_byte)((*sp >> shift) & 0x01); + if (shift == 7) + { + shift = 0; + sp--; + } + else + shift++; + + dp--; + } + break; + } + case 2: + { + + png_bytep sp = row + (png_size_t)((row_width - 1) >> 2); + png_bytep dp = row + (png_size_t)row_width - 1; + png_uint_32 shift = (int)((3 - ((row_width + 3) & 0x03)) << 1); + for (i = 0; i < row_width; i++) + { + *dp = (png_byte)((*sp >> shift) & 0x03); + if (shift == 6) + { + shift = 0; + sp--; + } + else + shift += 2; + + dp--; + } + break; + } + case 4: + { + png_bytep sp = row + (png_size_t)((row_width - 1) >> 1); + png_bytep dp = row + (png_size_t)row_width - 1; + png_uint_32 shift = (int)((1 - ((row_width + 1) & 0x01)) << 2); + for (i = 0; i < row_width; i++) + { + *dp = (png_byte)((*sp >> shift) & 0x0f); + if (shift == 4) + { + shift = 0; + sp--; + } + else + shift = 4; + + dp--; + } + break; + } + } + row_info->bit_depth = 8; + row_info->pixel_depth = (png_byte)(8 * row_info->channels); + row_info->rowbytes = row_width * row_info->channels; + } +} +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) +/* Reverse the effects of png_do_shift. This routine merely shifts the + * pixels back to their significant bits values. Thus, if you have + * a row of bit depth 8, but only 5 are significant, this will shift + * the values back to 0 through 31. + */ +void /* PRIVATE */ +png_do_unshift(png_row_infop row_info, png_bytep row, png_color_8p sig_bits) +{ + png_debug(1, "in png_do_unshift\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && sig_bits != NULL && +#endif + row_info->color_type != PNG_COLOR_TYPE_PALETTE) + { + int shift[4]; + int channels = 0; + int c; + png_uint_16 value = 0; + png_uint_32 row_width = row_info->width; + + if (row_info->color_type & PNG_COLOR_MASK_COLOR) + { + shift[channels++] = row_info->bit_depth - sig_bits->red; + shift[channels++] = row_info->bit_depth - sig_bits->green; + shift[channels++] = row_info->bit_depth - sig_bits->blue; + } + else + { + shift[channels++] = row_info->bit_depth - sig_bits->gray; + } + if (row_info->color_type & PNG_COLOR_MASK_ALPHA) + { + shift[channels++] = row_info->bit_depth - sig_bits->alpha; + } + + for (c = 0; c < channels; c++) + { + if (shift[c] <= 0) + shift[c] = 0; + else + value = 1; + } + + if (!value) + return; + + switch (row_info->bit_depth) + { + case 2: + { + png_bytep bp; + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + + for (bp = row, i = 0; i < istop; i++) + { + *bp >>= 1; + *bp++ &= 0x55; + } + break; + } + case 4: + { + png_bytep bp = row; + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + png_byte mask = (png_byte)((((int)0xf0 >> shift[0]) & (int)0xf0) | + (png_byte)((int)0xf >> shift[0])); + + for (i = 0; i < istop; i++) + { + *bp >>= shift[0]; + *bp++ &= mask; + } + break; + } + case 8: + { + png_bytep bp = row; + png_uint_32 i; + png_uint_32 istop = row_width * channels; + + for (i = 0; i < istop; i++) + { + *bp++ >>= shift[i%channels]; + } + break; + } + case 16: + { + png_bytep bp = row; + png_uint_32 i; + png_uint_32 istop = channels * row_width; + + for (i = 0; i < istop; i++) + { + value = (png_uint_16)((*bp << 8) + *(bp + 1)); + value >>= shift[i%channels]; + *bp++ = (png_byte)(value >> 8); + *bp++ = (png_byte)(value & 0xff); + } + break; + } + } + } +} +#endif + +#if defined(PNG_READ_16_TO_8_SUPPORTED) +/* chop rows of bit depth 16 down to 8 */ +void /* PRIVATE */ +png_do_chop(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_chop\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL && row_info->bit_depth == 16) +#else + if (row_info->bit_depth == 16) +#endif + { + png_bytep sp = row; + png_bytep dp = row; + png_uint_32 i; + png_uint_32 istop = row_info->width * row_info->channels; + + for (i = 0; i> 8)) >> 8; + * + * Approximate calculation with shift/add instead of multiply/divide: + * *dp = ((((png_uint_32)(*sp) << 8) | + * (png_uint_32)((int)(*(sp + 1)) - *sp)) + 128) >> 8; + * + * What we actually do to avoid extra shifting and conversion: + */ + + *dp = *sp + ((((int)(*(sp + 1)) - *sp) > 128) ? 1 : 0); +#else + /* Simply discard the low order byte */ + *dp = *sp; +#endif + } + row_info->bit_depth = 8; + row_info->pixel_depth = (png_byte)(8 * row_info->channels); + row_info->rowbytes = row_info->width * row_info->channels; + } +} +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) +void /* PRIVATE */ +png_do_read_swap_alpha(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_read_swap_alpha\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL) +#endif + { + png_uint_32 row_width = row_info->width; + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + /* This converts from RGBA to ARGB */ + if (row_info->bit_depth == 8) + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_byte save; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + save = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = save; + } + } + /* This converts from RRGGBBAA to AARRGGBB */ + else + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_byte save[2]; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + save[0] = *(--sp); + save[1] = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = save[0]; + *(--dp) = save[1]; + } + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + /* This converts from GA to AG */ + if (row_info->bit_depth == 8) + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_byte save; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + save = *(--sp); + *(--dp) = *(--sp); + *(--dp) = save; + } + } + /* This converts from GGAA to AAGG */ + else + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_byte save[2]; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + save[0] = *(--sp); + save[1] = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = save[0]; + *(--dp) = save[1]; + } + } + } + } +} +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) +void /* PRIVATE */ +png_do_read_invert_alpha(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_read_invert_alpha\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL) +#endif + { + png_uint_32 row_width = row_info->width; + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + /* This inverts the alpha channel in RGBA */ + if (row_info->bit_depth == 8) + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + *(--dp) = (png_byte)(255 - *(--sp)); + +/* This does nothing: + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + We can replace it with: +*/ + sp-=3; + dp=sp; + } + } + /* This inverts the alpha channel in RRGGBBAA */ + else + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + *(--dp) = (png_byte)(255 - *(--sp)); + *(--dp) = (png_byte)(255 - *(--sp)); + +/* This does nothing: + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + We can replace it with: +*/ + sp-=6; + dp=sp; + } + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + /* This inverts the alpha channel in GA */ + if (row_info->bit_depth == 8) + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + *(--dp) = (png_byte)(255 - *(--sp)); + *(--dp) = *(--sp); + } + } + /* This inverts the alpha channel in GGAA */ + else + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + *(--dp) = (png_byte)(255 - *(--sp)); + *(--dp) = (png_byte)(255 - *(--sp)); +/* + *(--dp) = *(--sp); + *(--dp) = *(--sp); +*/ + sp-=2; + dp=sp; + } + } + } + } +} +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) +/* Add filler channel if we have RGB color */ +void /* PRIVATE */ +png_do_read_filler(png_row_infop row_info, png_bytep row, + png_uint_32 filler, png_uint_32 flags) +{ + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + png_byte hi_filler = (png_byte)((filler>>8) & 0xff); + png_byte lo_filler = (png_byte)(filler & 0xff); + + png_debug(1, "in png_do_read_filler\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + row_info->color_type == PNG_COLOR_TYPE_GRAY) + { + if(row_info->bit_depth == 8) + { + /* This changes the data from G to GX */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + png_bytep sp = row + (png_size_t)row_width; + png_bytep dp = sp + (png_size_t)row_width; + for (i = 1; i < row_width; i++) + { + *(--dp) = lo_filler; + *(--dp) = *(--sp); + } + *(--dp) = lo_filler; + row_info->channels = 2; + row_info->pixel_depth = 16; + row_info->rowbytes = row_width * 2; + } + /* This changes the data from G to XG */ + else + { + png_bytep sp = row + (png_size_t)row_width; + png_bytep dp = sp + (png_size_t)row_width; + for (i = 0; i < row_width; i++) + { + *(--dp) = *(--sp); + *(--dp) = lo_filler; + } + row_info->channels = 2; + row_info->pixel_depth = 16; + row_info->rowbytes = row_width * 2; + } + } + else if(row_info->bit_depth == 16) + { + /* This changes the data from GG to GGXX */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + png_bytep sp = row + (png_size_t)row_width * 2; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 1; i < row_width; i++) + { + *(--dp) = hi_filler; + *(--dp) = lo_filler; + *(--dp) = *(--sp); + *(--dp) = *(--sp); + } + *(--dp) = hi_filler; + *(--dp) = lo_filler; + row_info->channels = 2; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + } + /* This changes the data from GG to XXGG */ + else + { + png_bytep sp = row + (png_size_t)row_width * 2; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 0; i < row_width; i++) + { + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = hi_filler; + *(--dp) = lo_filler; + } + row_info->channels = 2; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + } + } + } /* COLOR_TYPE == GRAY */ + else if (row_info->color_type == PNG_COLOR_TYPE_RGB) + { + if(row_info->bit_depth == 8) + { + /* This changes the data from RGB to RGBX */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + png_bytep sp = row + (png_size_t)row_width * 3; + png_bytep dp = sp + (png_size_t)row_width; + for (i = 1; i < row_width; i++) + { + *(--dp) = lo_filler; + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + } + *(--dp) = lo_filler; + row_info->channels = 4; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + } + /* This changes the data from RGB to XRGB */ + else + { + png_bytep sp = row + (png_size_t)row_width * 3; + png_bytep dp = sp + (png_size_t)row_width; + for (i = 0; i < row_width; i++) + { + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = lo_filler; + } + row_info->channels = 4; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + } + } + else if(row_info->bit_depth == 16) + { + /* This changes the data from RRGGBB to RRGGBBXX */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + png_bytep sp = row + (png_size_t)row_width * 6; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 1; i < row_width; i++) + { + *(--dp) = hi_filler; + *(--dp) = lo_filler; + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + } + *(--dp) = hi_filler; + *(--dp) = lo_filler; + row_info->channels = 4; + row_info->pixel_depth = 64; + row_info->rowbytes = row_width * 8; + } + /* This changes the data from RRGGBB to XXRRGGBB */ + else + { + png_bytep sp = row + (png_size_t)row_width * 6; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 0; i < row_width; i++) + { + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = hi_filler; + *(--dp) = lo_filler; + } + row_info->channels = 4; + row_info->pixel_depth = 64; + row_info->rowbytes = row_width * 8; + } + } + } /* COLOR_TYPE == RGB */ +} +#endif + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) +/* expand grayscale files to RGB, with or without alpha */ +void /* PRIVATE */ +png_do_gray_to_rgb(png_row_infop row_info, png_bytep row) +{ + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + png_debug(1, "in png_do_gray_to_rgb\n"); + if (row_info->bit_depth >= 8 && +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + !(row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + if (row_info->color_type == PNG_COLOR_TYPE_GRAY) + { + if (row_info->bit_depth == 8) + { + png_bytep sp = row + (png_size_t)row_width - 1; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 0; i < row_width; i++) + { + *(dp--) = *sp; + *(dp--) = *sp; + *(dp--) = *(sp--); + } + } + else + { + png_bytep sp = row + (png_size_t)row_width * 2 - 1; + png_bytep dp = sp + (png_size_t)row_width * 4; + for (i = 0; i < row_width; i++) + { + *(dp--) = *sp; + *(dp--) = *(sp - 1); + *(dp--) = *sp; + *(dp--) = *(sp - 1); + *(dp--) = *(sp--); + *(dp--) = *(sp--); + } + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + if (row_info->bit_depth == 8) + { + png_bytep sp = row + (png_size_t)row_width * 2 - 1; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 0; i < row_width; i++) + { + *(dp--) = *(sp--); + *(dp--) = *sp; + *(dp--) = *sp; + *(dp--) = *(sp--); + } + } + else + { + png_bytep sp = row + (png_size_t)row_width * 4 - 1; + png_bytep dp = sp + (png_size_t)row_width * 4; + for (i = 0; i < row_width; i++) + { + *(dp--) = *(sp--); + *(dp--) = *(sp--); + *(dp--) = *sp; + *(dp--) = *(sp - 1); + *(dp--) = *sp; + *(dp--) = *(sp - 1); + *(dp--) = *(sp--); + *(dp--) = *(sp--); + } + } + } + row_info->channels += (png_byte)2; + row_info->color_type |= PNG_COLOR_MASK_COLOR; + row_info->pixel_depth = (png_byte)(row_info->channels * + row_info->bit_depth); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,row_width); + } +} +#endif + +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +/* reduce RGB files to grayscale, with or without alpha + * using the equation given in Poynton's ColorFAQ at + * + * Copyright (c) 1998-01-04 Charles Poynton poynton at inforamp.net + * + * Y = 0.212671 * R + 0.715160 * G + 0.072169 * B + * + * We approximate this with + * + * Y = 0.21268 * R + 0.7151 * G + 0.07217 * B + * + * which can be expressed with integers as + * + * Y = (6969 * R + 23434 * G + 2365 * B)/32768 + * + * The calculation is to be done in a linear colorspace. + * + * Other integer coefficents can be used via png_set_rgb_to_gray(). + */ +int /* PRIVATE */ +png_do_rgb_to_gray(png_structp png_ptr, png_row_infop row_info, png_bytep row) + +{ + png_uint_32 i; + + png_uint_32 row_width = row_info->width; + int rgb_error = 0; + + png_debug(1, "in png_do_rgb_to_gray\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + (row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + png_uint_32 rc = png_ptr->rgb_to_gray_red_coeff; + png_uint_32 gc = png_ptr->rgb_to_gray_green_coeff; + png_uint_32 bc = png_ptr->rgb_to_gray_blue_coeff; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + { + if (row_info->bit_depth == 8) + { +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->gamma_from_1 != NULL && png_ptr->gamma_to_1 != NULL) + { + png_bytep sp = row; + png_bytep dp = row; + + for (i = 0; i < row_width; i++) + { + png_byte red = png_ptr->gamma_to_1[*(sp++)]; + png_byte green = png_ptr->gamma_to_1[*(sp++)]; + png_byte blue = png_ptr->gamma_to_1[*(sp++)]; + if(red != green || red != blue) + { + rgb_error |= 1; + *(dp++) = png_ptr->gamma_from_1[ + (rc*red+gc*green+bc*blue)>>15]; + } + else + *(dp++) = *(sp-1); + } + } + else +#endif + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_byte red = *(sp++); + png_byte green = *(sp++); + png_byte blue = *(sp++); + if(red != green || red != blue) + { + rgb_error |= 1; + *(dp++) = (png_byte)((rc*red+gc*green+bc*blue)>>15); + } + else + *(dp++) = *(sp-1); + } + } + } + + else /* RGB bit_depth == 16 */ + { +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->gamma_16_to_1 != NULL && + png_ptr->gamma_16_from_1 != NULL) + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 red, green, blue, w; + + red = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + green = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + blue = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + + if(red == green && red == blue) + w = red; + else + { + png_uint_16 red_1 = png_ptr->gamma_16_to_1[(red&0xff) >> + png_ptr->gamma_shift][red>>8]; + png_uint_16 green_1 = png_ptr->gamma_16_to_1[(green&0xff) >> + png_ptr->gamma_shift][green>>8]; + png_uint_16 blue_1 = png_ptr->gamma_16_to_1[(blue&0xff) >> + png_ptr->gamma_shift][blue>>8]; + png_uint_16 gray16 = (png_uint_16)((rc*red_1 + gc*green_1 + + bc*blue_1)>>15); + w = png_ptr->gamma_16_from_1[(gray16&0xff) >> + png_ptr->gamma_shift][gray16 >> 8]; + rgb_error |= 1; + } + + *(dp++) = (png_byte)((w>>8) & 0xff); + *(dp++) = (png_byte)(w & 0xff); + } + } + else +#endif + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 red, green, blue, gray16; + + red = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + green = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + blue = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + + if(red != green || red != blue) + rgb_error |= 1; + gray16 = (png_uint_16)((rc*red + gc*green + bc*blue)>>15); + *(dp++) = (png_byte)((gray16>>8) & 0xff); + *(dp++) = (png_byte)(gray16 & 0xff); + } + } + } + } + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + if (row_info->bit_depth == 8) + { +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->gamma_from_1 != NULL && png_ptr->gamma_to_1 != NULL) + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_byte red = png_ptr->gamma_to_1[*(sp++)]; + png_byte green = png_ptr->gamma_to_1[*(sp++)]; + png_byte blue = png_ptr->gamma_to_1[*(sp++)]; + if(red != green || red != blue) + rgb_error |= 1; + *(dp++) = png_ptr->gamma_from_1 + [(rc*red + gc*green + bc*blue)>>15]; + *(dp++) = *(sp++); /* alpha */ + } + } + else +#endif + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_byte red = *(sp++); + png_byte green = *(sp++); + png_byte blue = *(sp++); + if(red != green || red != blue) + rgb_error |= 1; + *(dp++) = (png_byte)((rc*red + gc*green + bc*blue)>>15); + *(dp++) = *(sp++); /* alpha */ + } + } + } + else /* RGBA bit_depth == 16 */ + { +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->gamma_16_to_1 != NULL && + png_ptr->gamma_16_from_1 != NULL) + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 red, green, blue, w; + + red = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + green = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + blue = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + + if(red == green && red == blue) + w = red; + else + { + png_uint_16 red_1 = png_ptr->gamma_16_to_1[(red&0xff) >> + png_ptr->gamma_shift][red>>8]; + png_uint_16 green_1 = png_ptr->gamma_16_to_1[(green&0xff) >> + png_ptr->gamma_shift][green>>8]; + png_uint_16 blue_1 = png_ptr->gamma_16_to_1[(blue&0xff) >> + png_ptr->gamma_shift][blue>>8]; + png_uint_16 gray16 = (png_uint_16)((rc * red_1 + + gc * green_1 + bc * blue_1)>>15); + w = png_ptr->gamma_16_from_1[(gray16&0xff) >> + png_ptr->gamma_shift][gray16 >> 8]; + rgb_error |= 1; + } + + *(dp++) = (png_byte)((w>>8) & 0xff); + *(dp++) = (png_byte)(w & 0xff); + *(dp++) = *(sp++); /* alpha */ + *(dp++) = *(sp++); + } + } + else +#endif + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 red, green, blue, gray16; + red = (png_uint_16)((*(sp)<<8) | *(sp+1)); sp+=2; + green = (png_uint_16)((*(sp)<<8) | *(sp+1)); sp+=2; + blue = (png_uint_16)((*(sp)<<8) | *(sp+1)); sp+=2; + if(red != green || red != blue) + rgb_error |= 1; + gray16 = (png_uint_16)((rc*red + gc*green + bc*blue)>>15); + *(dp++) = (png_byte)((gray16>>8) & 0xff); + *(dp++) = (png_byte)(gray16 & 0xff); + *(dp++) = *(sp++); /* alpha */ + *(dp++) = *(sp++); + } + } + } + } + row_info->channels -= (png_byte)2; + row_info->color_type &= ~PNG_COLOR_MASK_COLOR; + row_info->pixel_depth = (png_byte)(row_info->channels * + row_info->bit_depth); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,row_width); + } + return rgb_error; +} +#endif + +/* Build a grayscale palette. Palette is assumed to be 1 << bit_depth + * large of png_color. This lets grayscale images be treated as + * paletted. Most useful for gamma correction and simplification + * of code. + */ +void PNGAPI +png_build_grayscale_palette(int bit_depth, png_colorp palette) +{ + int num_palette; + int color_inc; + int i; + int v; + + png_debug(1, "in png_do_build_grayscale_palette\n"); + if (palette == NULL) + return; + + switch (bit_depth) + { + case 1: + num_palette = 2; + color_inc = 0xff; + break; + case 2: + num_palette = 4; + color_inc = 0x55; + break; + case 4: + num_palette = 16; + color_inc = 0x11; + break; + case 8: + num_palette = 256; + color_inc = 1; + break; + default: + num_palette = 0; + color_inc = 0; + break; + } + + for (i = 0, v = 0; i < num_palette; i++, v += color_inc) + { + palette[i].red = (png_byte)v; + palette[i].green = (png_byte)v; + palette[i].blue = (png_byte)v; + } +} + +/* This function is currently unused. Do we really need it? */ +#if defined(PNG_READ_DITHER_SUPPORTED) && defined(PNG_CORRECT_PALETTE_SUPPORTED) +void /* PRIVATE */ +png_correct_palette(png_structp png_ptr, png_colorp palette, + int num_palette) +{ + png_debug(1, "in png_correct_palette\n"); +#if defined(PNG_READ_BACKGROUND_SUPPORTED) && \ + defined(PNG_READ_GAMMA_SUPPORTED) && defined(PNG_FLOATING_POINT_SUPPORTED) + if (png_ptr->transformations & (PNG_GAMMA | PNG_BACKGROUND)) + { + png_color back, back_1; + + if (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_FILE) + { + back.red = png_ptr->gamma_table[png_ptr->background.red]; + back.green = png_ptr->gamma_table[png_ptr->background.green]; + back.blue = png_ptr->gamma_table[png_ptr->background.blue]; + + back_1.red = png_ptr->gamma_to_1[png_ptr->background.red]; + back_1.green = png_ptr->gamma_to_1[png_ptr->background.green]; + back_1.blue = png_ptr->gamma_to_1[png_ptr->background.blue]; + } + else + { + double g; + + g = 1.0 / (png_ptr->background_gamma * png_ptr->screen_gamma); + + if (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_SCREEN || + fabs(g - 1.0) < PNG_GAMMA_THRESHOLD) + { + back.red = png_ptr->background.red; + back.green = png_ptr->background.green; + back.blue = png_ptr->background.blue; + } + else + { + back.red = + (png_byte)(pow((double)png_ptr->background.red/255, g) * + 255.0 + 0.5); + back.green = + (png_byte)(pow((double)png_ptr->background.green/255, g) * + 255.0 + 0.5); + back.blue = + (png_byte)(pow((double)png_ptr->background.blue/255, g) * + 255.0 + 0.5); + } + + g = 1.0 / png_ptr->background_gamma; + + back_1.red = + (png_byte)(pow((double)png_ptr->background.red/255, g) * + 255.0 + 0.5); + back_1.green = + (png_byte)(pow((double)png_ptr->background.green/255, g) * + 255.0 + 0.5); + back_1.blue = + (png_byte)(pow((double)png_ptr->background.blue/255, g) * + 255.0 + 0.5); + } + + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + png_uint_32 i; + + for (i = 0; i < (png_uint_32)num_palette; i++) + { + if (i < png_ptr->num_trans && png_ptr->trans[i] == 0) + { + palette[i] = back; + } + else if (i < png_ptr->num_trans && png_ptr->trans[i] != 0xff) + { + png_byte v, w; + + v = png_ptr->gamma_to_1[png_ptr->palette[i].red]; + png_composite(w, v, png_ptr->trans[i], back_1.red); + palette[i].red = png_ptr->gamma_from_1[w]; + + v = png_ptr->gamma_to_1[png_ptr->palette[i].green]; + png_composite(w, v, png_ptr->trans[i], back_1.green); + palette[i].green = png_ptr->gamma_from_1[w]; + + v = png_ptr->gamma_to_1[png_ptr->palette[i].blue]; + png_composite(w, v, png_ptr->trans[i], back_1.blue); + palette[i].blue = png_ptr->gamma_from_1[w]; + } + else + { + palette[i].red = png_ptr->gamma_table[palette[i].red]; + palette[i].green = png_ptr->gamma_table[palette[i].green]; + palette[i].blue = png_ptr->gamma_table[palette[i].blue]; + } + } + } + else + { + int i; + + for (i = 0; i < num_palette; i++) + { + if (palette[i].red == (png_byte)png_ptr->trans_values.gray) + { + palette[i] = back; + } + else + { + palette[i].red = png_ptr->gamma_table[palette[i].red]; + palette[i].green = png_ptr->gamma_table[palette[i].green]; + palette[i].blue = png_ptr->gamma_table[palette[i].blue]; + } + } + } + } + else +#endif +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (png_ptr->transformations & PNG_GAMMA) + { + int i; + + for (i = 0; i < num_palette; i++) + { + palette[i].red = png_ptr->gamma_table[palette[i].red]; + palette[i].green = png_ptr->gamma_table[palette[i].green]; + palette[i].blue = png_ptr->gamma_table[palette[i].blue]; + } + } +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + else +#endif +#endif +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->transformations & PNG_BACKGROUND) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + png_color back; + + back.red = (png_byte)png_ptr->background.red; + back.green = (png_byte)png_ptr->background.green; + back.blue = (png_byte)png_ptr->background.blue; + + for (i = 0; i < (int)png_ptr->num_trans; i++) + { + if (png_ptr->trans[i] == 0) + { + palette[i].red = back.red; + palette[i].green = back.green; + palette[i].blue = back.blue; + } + else if (png_ptr->trans[i] != 0xff) + { + png_composite(palette[i].red, png_ptr->palette[i].red, + png_ptr->trans[i], back.red); + png_composite(palette[i].green, png_ptr->palette[i].green, + png_ptr->trans[i], back.green); + png_composite(palette[i].blue, png_ptr->palette[i].blue, + png_ptr->trans[i], back.blue); + } + } + } + else /* assume grayscale palette (what else could it be?) */ + { + int i; + + for (i = 0; i < num_palette; i++) + { + if (i == (png_byte)png_ptr->trans_values.gray) + { + palette[i].red = (png_byte)png_ptr->background.red; + palette[i].green = (png_byte)png_ptr->background.green; + palette[i].blue = (png_byte)png_ptr->background.blue; + } + } + } + } +#endif +} +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) +/* Replace any alpha or transparency with the supplied background color. + * "background" is already in the screen gamma, while "background_1" is + * at a gamma of 1.0. Paletted files have already been taken care of. + */ +void /* PRIVATE */ +png_do_background(png_row_infop row_info, png_bytep row, + png_color_16p trans_values, png_color_16p background +#if defined(PNG_READ_GAMMA_SUPPORTED) + , png_color_16p background_1, + png_bytep gamma_table, png_bytep gamma_from_1, png_bytep gamma_to_1, + png_uint_16pp gamma_16, png_uint_16pp gamma_16_from_1, + png_uint_16pp gamma_16_to_1, int gamma_shift +#endif + ) +{ + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + int shift; + + png_debug(1, "in png_do_background\n"); + if (background != NULL && +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + (!(row_info->color_type & PNG_COLOR_MASK_ALPHA) || + (row_info->color_type != PNG_COLOR_TYPE_PALETTE && trans_values))) + { + switch (row_info->color_type) + { + case PNG_COLOR_TYPE_GRAY: + { + switch (row_info->bit_depth) + { + case 1: + { + sp = row; + shift = 7; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x01) + == trans_values->gray) + { + *sp &= (png_byte)((0x7f7f >> (7 - shift)) & 0xff); + *sp |= (png_byte)(background->gray << shift); + } + if (!shift) + { + shift = 7; + sp++; + } + else + shift--; + } + break; + } + case 2: + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_table != NULL) + { + sp = row; + shift = 6; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x03) + == trans_values->gray) + { + *sp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff); + *sp |= (png_byte)(background->gray << shift); + } + else + { + png_byte p = (png_byte)((*sp >> shift) & 0x03); + png_byte g = (png_byte)((gamma_table [p | (p << 2) | + (p << 4) | (p << 6)] >> 6) & 0x03); + *sp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff); + *sp |= (png_byte)(g << shift); + } + if (!shift) + { + shift = 6; + sp++; + } + else + shift -= 2; + } + } + else +#endif + { + sp = row; + shift = 6; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x03) + == trans_values->gray) + { + *sp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff); + *sp |= (png_byte)(background->gray << shift); + } + if (!shift) + { + shift = 6; + sp++; + } + else + shift -= 2; + } + } + break; + } + case 4: + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_table != NULL) + { + sp = row; + shift = 4; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x0f) + == trans_values->gray) + { + *sp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff); + *sp |= (png_byte)(background->gray << shift); + } + else + { + png_byte p = (png_byte)((*sp >> shift) & 0x0f); + png_byte g = (png_byte)((gamma_table[p | + (p << 4)] >> 4) & 0x0f); + *sp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff); + *sp |= (png_byte)(g << shift); + } + if (!shift) + { + shift = 4; + sp++; + } + else + shift -= 4; + } + } + else +#endif + { + sp = row; + shift = 4; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x0f) + == trans_values->gray) + { + *sp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff); + *sp |= (png_byte)(background->gray << shift); + } + if (!shift) + { + shift = 4; + sp++; + } + else + shift -= 4; + } + } + break; + } + case 8: + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_table != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp++) + { + if (*sp == trans_values->gray) + { + *sp = (png_byte)background->gray; + } + else + { + *sp = gamma_table[*sp]; + } + } + } + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp++) + { + if (*sp == trans_values->gray) + { + *sp = (png_byte)background->gray; + } + } + } + break; + } + case 16: + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_16 != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp += 2) + { + png_uint_16 v; + + v = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + if (v == trans_values->gray) + { + /* background is already in screen gamma */ + *sp = (png_byte)((background->gray >> 8) & 0xff); + *(sp + 1) = (png_byte)(background->gray & 0xff); + } + else + { + v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + } + } + } + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp += 2) + { + png_uint_16 v; + + v = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + if (v == trans_values->gray) + { + *sp = (png_byte)((background->gray >> 8) & 0xff); + *(sp + 1) = (png_byte)(background->gray & 0xff); + } + } + } + break; + } + } + break; + } + case PNG_COLOR_TYPE_RGB: + { + if (row_info->bit_depth == 8) + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_table != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp += 3) + { + if (*sp == trans_values->red && + *(sp + 1) == trans_values->green && + *(sp + 2) == trans_values->blue) + { + *sp = (png_byte)background->red; + *(sp + 1) = (png_byte)background->green; + *(sp + 2) = (png_byte)background->blue; + } + else + { + *sp = gamma_table[*sp]; + *(sp + 1) = gamma_table[*(sp + 1)]; + *(sp + 2) = gamma_table[*(sp + 2)]; + } + } + } + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp += 3) + { + if (*sp == trans_values->red && + *(sp + 1) == trans_values->green && + *(sp + 2) == trans_values->blue) + { + *sp = (png_byte)background->red; + *(sp + 1) = (png_byte)background->green; + *(sp + 2) = (png_byte)background->blue; + } + } + } + } + else /* if (row_info->bit_depth == 16) */ + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_16 != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp += 6) + { + png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + png_uint_16 g = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3)); + png_uint_16 b = (png_uint_16)(((*(sp+4)) << 8) + *(sp+5)); + if (r == trans_values->red && g == trans_values->green && + b == trans_values->blue) + { + /* background is already in screen gamma */ + *sp = (png_byte)((background->red >> 8) & 0xff); + *(sp + 1) = (png_byte)(background->red & 0xff); + *(sp + 2) = (png_byte)((background->green >> 8) & 0xff); + *(sp + 3) = (png_byte)(background->green & 0xff); + *(sp + 4) = (png_byte)((background->blue >> 8) & 0xff); + *(sp + 5) = (png_byte)(background->blue & 0xff); + } + else + { + png_uint_16 v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)]; + *(sp + 2) = (png_byte)((v >> 8) & 0xff); + *(sp + 3) = (png_byte)(v & 0xff); + v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)]; + *(sp + 4) = (png_byte)((v >> 8) & 0xff); + *(sp + 5) = (png_byte)(v & 0xff); + } + } + } + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp += 6) + { + png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp+1)); + png_uint_16 g = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3)); + png_uint_16 b = (png_uint_16)(((*(sp+4)) << 8) + *(sp+5)); + + if (r == trans_values->red && g == trans_values->green && + b == trans_values->blue) + { + *sp = (png_byte)((background->red >> 8) & 0xff); + *(sp + 1) = (png_byte)(background->red & 0xff); + *(sp + 2) = (png_byte)((background->green >> 8) & 0xff); + *(sp + 3) = (png_byte)(background->green & 0xff); + *(sp + 4) = (png_byte)((background->blue >> 8) & 0xff); + *(sp + 5) = (png_byte)(background->blue & 0xff); + } + } + } + } + break; + } + case PNG_COLOR_TYPE_GRAY_ALPHA: + { + if (row_info->bit_depth == 8) + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_to_1 != NULL && gamma_from_1 != NULL && + gamma_table != NULL) + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 2, dp++) + { + png_uint_16 a = *(sp + 1); + + if (a == 0xff) + { + *dp = gamma_table[*sp]; + } + else if (a == 0) + { + /* background is already in screen gamma */ + *dp = (png_byte)background->gray; + } + else + { + png_byte v, w; + + v = gamma_to_1[*sp]; + png_composite(w, v, a, background_1->gray); + *dp = gamma_from_1[w]; + } + } + } + else +#endif + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 2, dp++) + { + png_byte a = *(sp + 1); + + if (a == 0xff) + { + *dp = *sp; + } +#if defined(PNG_READ_GAMMA_SUPPORTED) + else if (a == 0) + { + *dp = (png_byte)background->gray; + } + else + { + png_composite(*dp, *sp, a, background_1->gray); + } +#else + *dp = (png_byte)background->gray; +#endif + } + } + } + else /* if (png_ptr->bit_depth == 16) */ + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_16 != NULL && gamma_16_from_1 != NULL && + gamma_16_to_1 != NULL) + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 4, dp += 2) + { + png_uint_16 a = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3)); + + if (a == (png_uint_16)0xffff) + { + png_uint_16 v; + + v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + *dp = (png_byte)((v >> 8) & 0xff); + *(dp + 1) = (png_byte)(v & 0xff); + } +#if defined(PNG_READ_GAMMA_SUPPORTED) + else if (a == 0) +#else + else +#endif + { + /* background is already in screen gamma */ + *dp = (png_byte)((background->gray >> 8) & 0xff); + *(dp + 1) = (png_byte)(background->gray & 0xff); + } +#if defined(PNG_READ_GAMMA_SUPPORTED) + else + { + png_uint_16 g, v, w; + + g = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp]; + png_composite_16(v, g, a, background_1->gray); + w = gamma_16_from_1[(v&0xff) >> gamma_shift][v >> 8]; + *dp = (png_byte)((w >> 8) & 0xff); + *(dp + 1) = (png_byte)(w & 0xff); + } +#endif + } + } + else +#endif + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 4, dp += 2) + { + png_uint_16 a = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3)); + if (a == (png_uint_16)0xffff) + { + png_memcpy(dp, sp, 2); + } +#if defined(PNG_READ_GAMMA_SUPPORTED) + else if (a == 0) +#else + else +#endif + { + *dp = (png_byte)((background->gray >> 8) & 0xff); + *(dp + 1) = (png_byte)(background->gray & 0xff); + } +#if defined(PNG_READ_GAMMA_SUPPORTED) + else + { + png_uint_16 g, v; + + g = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + png_composite_16(v, g, a, background_1->gray); + *dp = (png_byte)((v >> 8) & 0xff); + *(dp + 1) = (png_byte)(v & 0xff); + } +#endif + } + } + } + break; + } + case PNG_COLOR_TYPE_RGB_ALPHA: + { + if (row_info->bit_depth == 8) + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_to_1 != NULL && gamma_from_1 != NULL && + gamma_table != NULL) + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 4, dp += 3) + { + png_byte a = *(sp + 3); + + if (a == 0xff) + { + *dp = gamma_table[*sp]; + *(dp + 1) = gamma_table[*(sp + 1)]; + *(dp + 2) = gamma_table[*(sp + 2)]; + } + else if (a == 0) + { + /* background is already in screen gamma */ + *dp = (png_byte)background->red; + *(dp + 1) = (png_byte)background->green; + *(dp + 2) = (png_byte)background->blue; + } + else + { + png_byte v, w; + + v = gamma_to_1[*sp]; + png_composite(w, v, a, background_1->red); + *dp = gamma_from_1[w]; + v = gamma_to_1[*(sp + 1)]; + png_composite(w, v, a, background_1->green); + *(dp + 1) = gamma_from_1[w]; + v = gamma_to_1[*(sp + 2)]; + png_composite(w, v, a, background_1->blue); + *(dp + 2) = gamma_from_1[w]; + } + } + } + else +#endif + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 4, dp += 3) + { + png_byte a = *(sp + 3); + + if (a == 0xff) + { + *dp = *sp; + *(dp + 1) = *(sp + 1); + *(dp + 2) = *(sp + 2); + } + else if (a == 0) + { + *dp = (png_byte)background->red; + *(dp + 1) = (png_byte)background->green; + *(dp + 2) = (png_byte)background->blue; + } + else + { + png_composite(*dp, *sp, a, background->red); + png_composite(*(dp + 1), *(sp + 1), a, + background->green); + png_composite(*(dp + 2), *(sp + 2), a, + background->blue); + } + } + } + } + else /* if (row_info->bit_depth == 16) */ + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_16 != NULL && gamma_16_from_1 != NULL && + gamma_16_to_1 != NULL) + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 8, dp += 6) + { + png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6)) + << 8) + (png_uint_16)(*(sp + 7))); + if (a == (png_uint_16)0xffff) + { + png_uint_16 v; + + v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + *dp = (png_byte)((v >> 8) & 0xff); + *(dp + 1) = (png_byte)(v & 0xff); + v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)]; + *(dp + 2) = (png_byte)((v >> 8) & 0xff); + *(dp + 3) = (png_byte)(v & 0xff); + v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)]; + *(dp + 4) = (png_byte)((v >> 8) & 0xff); + *(dp + 5) = (png_byte)(v & 0xff); + } + else if (a == 0) + { + /* background is already in screen gamma */ + *dp = (png_byte)((background->red >> 8) & 0xff); + *(dp + 1) = (png_byte)(background->red & 0xff); + *(dp + 2) = (png_byte)((background->green >> 8) & 0xff); + *(dp + 3) = (png_byte)(background->green & 0xff); + *(dp + 4) = (png_byte)((background->blue >> 8) & 0xff); + *(dp + 5) = (png_byte)(background->blue & 0xff); + } + else + { + png_uint_16 v, w, x; + + v = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp]; + png_composite_16(w, v, a, background_1->red); + x = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >> 8]; + *dp = (png_byte)((x >> 8) & 0xff); + *(dp + 1) = (png_byte)(x & 0xff); + v = gamma_16_to_1[*(sp + 3) >> gamma_shift][*(sp + 2)]; + png_composite_16(w, v, a, background_1->green); + x = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >> 8]; + *(dp + 2) = (png_byte)((x >> 8) & 0xff); + *(dp + 3) = (png_byte)(x & 0xff); + v = gamma_16_to_1[*(sp + 5) >> gamma_shift][*(sp + 4)]; + png_composite_16(w, v, a, background_1->blue); + x = gamma_16_from_1[(w & 0xff) >> gamma_shift][w >> 8]; + *(dp + 4) = (png_byte)((x >> 8) & 0xff); + *(dp + 5) = (png_byte)(x & 0xff); + } + } + } + else +#endif + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 8, dp += 6) + { + png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6)) + << 8) + (png_uint_16)(*(sp + 7))); + if (a == (png_uint_16)0xffff) + { + png_memcpy(dp, sp, 6); + } + else if (a == 0) + { + *dp = (png_byte)((background->red >> 8) & 0xff); + *(dp + 1) = (png_byte)(background->red & 0xff); + *(dp + 2) = (png_byte)((background->green >> 8) & 0xff); + *(dp + 3) = (png_byte)(background->green & 0xff); + *(dp + 4) = (png_byte)((background->blue >> 8) & 0xff); + *(dp + 5) = (png_byte)(background->blue & 0xff); + } + else + { + png_uint_16 v; + + png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8) + + *(sp + 3)); + png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8) + + *(sp + 5)); + + png_composite_16(v, r, a, background->red); + *dp = (png_byte)((v >> 8) & 0xff); + *(dp + 1) = (png_byte)(v & 0xff); + png_composite_16(v, g, a, background->green); + *(dp + 2) = (png_byte)((v >> 8) & 0xff); + *(dp + 3) = (png_byte)(v & 0xff); + png_composite_16(v, b, a, background->blue); + *(dp + 4) = (png_byte)((v >> 8) & 0xff); + *(dp + 5) = (png_byte)(v & 0xff); + } + } + } + } + break; + } + } + + if (row_info->color_type & PNG_COLOR_MASK_ALPHA) + { + row_info->color_type &= ~PNG_COLOR_MASK_ALPHA; + row_info->channels--; + row_info->pixel_depth = (png_byte)(row_info->channels * + row_info->bit_depth); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,row_width); + } + } +} +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) +/* Gamma correct the image, avoiding the alpha channel. Make sure + * you do this after you deal with the transparency issue on grayscale + * or RGB images. If your bit depth is 8, use gamma_table, if it + * is 16, use gamma_16_table and gamma_shift. Build these with + * build_gamma_table(). + */ +void /* PRIVATE */ +png_do_gamma(png_row_infop row_info, png_bytep row, + png_bytep gamma_table, png_uint_16pp gamma_16_table, + int gamma_shift) +{ + png_bytep sp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + png_debug(1, "in png_do_gamma\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + ((row_info->bit_depth <= 8 && gamma_table != NULL) || + (row_info->bit_depth == 16 && gamma_16_table != NULL))) + { + switch (row_info->color_type) + { + case PNG_COLOR_TYPE_RGB: + { + if (row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++) + { + *sp = gamma_table[*sp]; + sp++; + *sp = gamma_table[*sp]; + sp++; + *sp = gamma_table[*sp]; + sp++; + } + } + else /* if (row_info->bit_depth == 16) */ + { + sp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 v; + + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + } + } + break; + } + case PNG_COLOR_TYPE_RGB_ALPHA: + { + if (row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++) + { + *sp = gamma_table[*sp]; + sp++; + *sp = gamma_table[*sp]; + sp++; + *sp = gamma_table[*sp]; + sp++; + sp++; + } + } + else /* if (row_info->bit_depth == 16) */ + { + sp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 4; + } + } + break; + } + case PNG_COLOR_TYPE_GRAY_ALPHA: + { + if (row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++) + { + *sp = gamma_table[*sp]; + sp += 2; + } + } + else /* if (row_info->bit_depth == 16) */ + { + sp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 4; + } + } + break; + } + case PNG_COLOR_TYPE_GRAY: + { + if (row_info->bit_depth == 2) + { + sp = row; + for (i = 0; i < row_width; i += 4) + { + int a = *sp & 0xc0; + int b = *sp & 0x30; + int c = *sp & 0x0c; + int d = *sp & 0x03; + + *sp = (png_byte)( + ((((int)gamma_table[a|(a>>2)|(a>>4)|(a>>6)]) ) & 0xc0)| + ((((int)gamma_table[(b<<2)|b|(b>>2)|(b>>4)])>>2) & 0x30)| + ((((int)gamma_table[(c<<4)|(c<<2)|c|(c>>2)])>>4) & 0x0c)| + ((((int)gamma_table[(d<<6)|(d<<4)|(d<<2)|d])>>6) )); + sp++; + } + } + if (row_info->bit_depth == 4) + { + sp = row; + for (i = 0; i < row_width; i += 2) + { + int msb = *sp & 0xf0; + int lsb = *sp & 0x0f; + + *sp = (png_byte)((((int)gamma_table[msb | (msb >> 4)]) & 0xf0) + | (((int)gamma_table[(lsb << 4) | lsb]) >> 4)); + sp++; + } + } + else if (row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++) + { + *sp = gamma_table[*sp]; + sp++; + } + } + else if (row_info->bit_depth == 16) + { + sp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + } + } + break; + } + } + } +} +#endif + +#if defined(PNG_READ_EXPAND_SUPPORTED) +/* Expands a palette row to an RGB or RGBA row depending + * upon whether you supply trans and num_trans. + */ +void /* PRIVATE */ +png_do_expand_palette(png_row_infop row_info, png_bytep row, + png_colorp palette, png_bytep trans, int num_trans) +{ + int shift, value; + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + png_debug(1, "in png_do_expand_palette\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + row_info->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (row_info->bit_depth < 8) + { + switch (row_info->bit_depth) + { + case 1: + { + sp = row + (png_size_t)((row_width - 1) >> 3); + dp = row + (png_size_t)row_width - 1; + shift = 7 - (int)((row_width + 7) & 0x07); + for (i = 0; i < row_width; i++) + { + if ((*sp >> shift) & 0x01) + *dp = 1; + else + *dp = 0; + if (shift == 7) + { + shift = 0; + sp--; + } + else + shift++; + + dp--; + } + break; + } + case 2: + { + sp = row + (png_size_t)((row_width - 1) >> 2); + dp = row + (png_size_t)row_width - 1; + shift = (int)((3 - ((row_width + 3) & 0x03)) << 1); + for (i = 0; i < row_width; i++) + { + value = (*sp >> shift) & 0x03; + *dp = (png_byte)value; + if (shift == 6) + { + shift = 0; + sp--; + } + else + shift += 2; + + dp--; + } + break; + } + case 4: + { + sp = row + (png_size_t)((row_width - 1) >> 1); + dp = row + (png_size_t)row_width - 1; + shift = (int)((row_width & 0x01) << 2); + for (i = 0; i < row_width; i++) + { + value = (*sp >> shift) & 0x0f; + *dp = (png_byte)value; + if (shift == 4) + { + shift = 0; + sp--; + } + else + shift += 4; + + dp--; + } + break; + } + } + row_info->bit_depth = 8; + row_info->pixel_depth = 8; + row_info->rowbytes = row_width; + } + switch (row_info->bit_depth) + { + case 8: + { + if (trans != NULL) + { + sp = row + (png_size_t)row_width - 1; + dp = row + (png_size_t)(row_width << 2) - 1; + + for (i = 0; i < row_width; i++) + { + if ((int)(*sp) >= num_trans) + *dp-- = 0xff; + else + *dp-- = trans[*sp]; + *dp-- = palette[*sp].blue; + *dp-- = palette[*sp].green; + *dp-- = palette[*sp].red; + sp--; + } + row_info->bit_depth = 8; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + row_info->color_type = 6; + row_info->channels = 4; + } + else + { + sp = row + (png_size_t)row_width - 1; + dp = row + (png_size_t)(row_width * 3) - 1; + + for (i = 0; i < row_width; i++) + { + *dp-- = palette[*sp].blue; + *dp-- = palette[*sp].green; + *dp-- = palette[*sp].red; + sp--; + } + row_info->bit_depth = 8; + row_info->pixel_depth = 24; + row_info->rowbytes = row_width * 3; + row_info->color_type = 2; + row_info->channels = 3; + } + break; + } + } + } +} + +/* If the bit depth < 8, it is expanded to 8. Also, if the already + * expanded transparency value is supplied, an alpha channel is built. + */ +void /* PRIVATE */ +png_do_expand(png_row_infop row_info, png_bytep row, + png_color_16p trans_value) +{ + int shift, value; + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + png_debug(1, "in png_do_expand\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL) +#endif + { + if (row_info->color_type == PNG_COLOR_TYPE_GRAY) + { + png_uint_16 gray = (png_uint_16)(trans_value ? trans_value->gray : 0); + + if (row_info->bit_depth < 8) + { + switch (row_info->bit_depth) + { + case 1: + { + gray = (png_uint_16)((gray&0x01)*0xff); + sp = row + (png_size_t)((row_width - 1) >> 3); + dp = row + (png_size_t)row_width - 1; + shift = 7 - (int)((row_width + 7) & 0x07); + for (i = 0; i < row_width; i++) + { + if ((*sp >> shift) & 0x01) + *dp = 0xff; + else + *dp = 0; + if (shift == 7) + { + shift = 0; + sp--; + } + else + shift++; + + dp--; + } + break; + } + case 2: + { + gray = (png_uint_16)((gray&0x03)*0x55); + sp = row + (png_size_t)((row_width - 1) >> 2); + dp = row + (png_size_t)row_width - 1; + shift = (int)((3 - ((row_width + 3) & 0x03)) << 1); + for (i = 0; i < row_width; i++) + { + value = (*sp >> shift) & 0x03; + *dp = (png_byte)(value | (value << 2) | (value << 4) | + (value << 6)); + if (shift == 6) + { + shift = 0; + sp--; + } + else + shift += 2; + + dp--; + } + break; + } + case 4: + { + gray = (png_uint_16)((gray&0x0f)*0x11); + sp = row + (png_size_t)((row_width - 1) >> 1); + dp = row + (png_size_t)row_width - 1; + shift = (int)((1 - ((row_width + 1) & 0x01)) << 2); + for (i = 0; i < row_width; i++) + { + value = (*sp >> shift) & 0x0f; + *dp = (png_byte)(value | (value << 4)); + if (shift == 4) + { + shift = 0; + sp--; + } + else + shift = 4; + + dp--; + } + break; + } + } + row_info->bit_depth = 8; + row_info->pixel_depth = 8; + row_info->rowbytes = row_width; + } + + if (trans_value != NULL) + { + if (row_info->bit_depth == 8) + { + gray = gray & 0xff; + sp = row + (png_size_t)row_width - 1; + dp = row + (png_size_t)(row_width << 1) - 1; + for (i = 0; i < row_width; i++) + { + if (*sp == gray) + *dp-- = 0; + else + *dp-- = 0xff; + *dp-- = *sp--; + } + } + else if (row_info->bit_depth == 16) + { + png_byte gray_high = (gray >> 8) & 0xff; + png_byte gray_low = gray & 0xff; + sp = row + row_info->rowbytes - 1; + dp = row + (row_info->rowbytes << 1) - 1; + for (i = 0; i < row_width; i++) + { + if (*(sp-1) == gray_high && *(sp) == gray_low) + { + *dp-- = 0; + *dp-- = 0; + } + else + { + *dp-- = 0xff; + *dp-- = 0xff; + } + *dp-- = *sp--; + *dp-- = *sp--; + } + } + row_info->color_type = PNG_COLOR_TYPE_GRAY_ALPHA; + row_info->channels = 2; + row_info->pixel_depth = (png_byte)(row_info->bit_depth << 1); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, + row_width); + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_RGB && trans_value) + { + if (row_info->bit_depth == 8) + { + png_byte red = trans_value->red & 0xff; + png_byte green = trans_value->green & 0xff; + png_byte blue = trans_value->blue & 0xff; + sp = row + (png_size_t)row_info->rowbytes - 1; + dp = row + (png_size_t)(row_width << 2) - 1; + for (i = 0; i < row_width; i++) + { + if (*(sp - 2) == red && *(sp - 1) == green && *(sp) == blue) + *dp-- = 0; + else + *dp-- = 0xff; + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + } + } + else if (row_info->bit_depth == 16) + { + png_byte red_high = (trans_value->red >> 8) & 0xff; + png_byte green_high = (trans_value->green >> 8) & 0xff; + png_byte blue_high = (trans_value->blue >> 8) & 0xff; + png_byte red_low = trans_value->red & 0xff; + png_byte green_low = trans_value->green & 0xff; + png_byte blue_low = trans_value->blue & 0xff; + sp = row + row_info->rowbytes - 1; + dp = row + (png_size_t)(row_width << 3) - 1; + for (i = 0; i < row_width; i++) + { + if (*(sp - 5) == red_high && + *(sp - 4) == red_low && + *(sp - 3) == green_high && + *(sp - 2) == green_low && + *(sp - 1) == blue_high && + *(sp ) == blue_low) + { + *dp-- = 0; + *dp-- = 0; + } + else + { + *dp-- = 0xff; + *dp-- = 0xff; + } + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + } + } + row_info->color_type = PNG_COLOR_TYPE_RGB_ALPHA; + row_info->channels = 4; + row_info->pixel_depth = (png_byte)(row_info->bit_depth << 2); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,row_width); + } + } +} +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) +void /* PRIVATE */ +png_do_dither(png_row_infop row_info, png_bytep row, + png_bytep palette_lookup, png_bytep dither_lookup) +{ + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + png_debug(1, "in png_do_dither\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL) +#endif + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB && + palette_lookup && row_info->bit_depth == 8) + { + int r, g, b, p; + sp = row; + dp = row; + for (i = 0; i < row_width; i++) + { + r = *sp++; + g = *sp++; + b = *sp++; + + /* this looks real messy, but the compiler will reduce + it down to a reasonable formula. For example, with + 5 bits per color, we get: + p = (((r >> 3) & 0x1f) << 10) | + (((g >> 3) & 0x1f) << 5) | + ((b >> 3) & 0x1f); + */ + p = (((r >> (8 - PNG_DITHER_RED_BITS)) & + ((1 << PNG_DITHER_RED_BITS) - 1)) << + (PNG_DITHER_GREEN_BITS + PNG_DITHER_BLUE_BITS)) | + (((g >> (8 - PNG_DITHER_GREEN_BITS)) & + ((1 << PNG_DITHER_GREEN_BITS) - 1)) << + (PNG_DITHER_BLUE_BITS)) | + ((b >> (8 - PNG_DITHER_BLUE_BITS)) & + ((1 << PNG_DITHER_BLUE_BITS) - 1)); + + *dp++ = palette_lookup[p]; + } + row_info->color_type = PNG_COLOR_TYPE_PALETTE; + row_info->channels = 1; + row_info->pixel_depth = row_info->bit_depth; + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,row_width); + } + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA && + palette_lookup != NULL && row_info->bit_depth == 8) + { + int r, g, b, p; + sp = row; + dp = row; + for (i = 0; i < row_width; i++) + { + r = *sp++; + g = *sp++; + b = *sp++; + sp++; + + p = (((r >> (8 - PNG_DITHER_RED_BITS)) & + ((1 << PNG_DITHER_RED_BITS) - 1)) << + (PNG_DITHER_GREEN_BITS + PNG_DITHER_BLUE_BITS)) | + (((g >> (8 - PNG_DITHER_GREEN_BITS)) & + ((1 << PNG_DITHER_GREEN_BITS) - 1)) << + (PNG_DITHER_BLUE_BITS)) | + ((b >> (8 - PNG_DITHER_BLUE_BITS)) & + ((1 << PNG_DITHER_BLUE_BITS) - 1)); + + *dp++ = palette_lookup[p]; + } + row_info->color_type = PNG_COLOR_TYPE_PALETTE; + row_info->channels = 1; + row_info->pixel_depth = row_info->bit_depth; + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,row_width); + } + else if (row_info->color_type == PNG_COLOR_TYPE_PALETTE && + dither_lookup && row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++, sp++) + { + *sp = dither_lookup[*sp]; + } + } + } +} +#endif + +#ifdef PNG_FLOATING_POINT_SUPPORTED +#if defined(PNG_READ_GAMMA_SUPPORTED) +static PNG_CONST int png_gamma_shift[] = + {0x10, 0x21, 0x42, 0x84, 0x110, 0x248, 0x550, 0xff0, 0x00}; + +/* We build the 8- or 16-bit gamma tables here. Note that for 16-bit + * tables, we don't make a full table if we are reducing to 8-bit in + * the future. Note also how the gamma_16 tables are segmented so that + * we don't need to allocate > 64K chunks for a full 16-bit table. + */ +void /* PRIVATE */ +png_build_gamma_table(png_structp png_ptr) +{ + png_debug(1, "in png_build_gamma_table\n"); + + if (png_ptr->bit_depth <= 8) + { + int i; + double g; + + if (png_ptr->screen_gamma > .000001) + g = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma); + else + g = 1.0; + + png_ptr->gamma_table = (png_bytep)png_malloc(png_ptr, + (png_uint_32)256); + + for (i = 0; i < 256; i++) + { + png_ptr->gamma_table[i] = (png_byte)(pow((double)i / 255.0, + g) * 255.0 + .5); + } + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ + defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + if (png_ptr->transformations & ((PNG_BACKGROUND) | PNG_RGB_TO_GRAY)) + { + + g = 1.0 / (png_ptr->gamma); + + png_ptr->gamma_to_1 = (png_bytep)png_malloc(png_ptr, + (png_uint_32)256); + + for (i = 0; i < 256; i++) + { + png_ptr->gamma_to_1[i] = (png_byte)(pow((double)i / 255.0, + g) * 255.0 + .5); + } + + png_ptr->gamma_from_1 = (png_bytep)png_malloc(png_ptr, + (png_uint_32)256); + + if(png_ptr->screen_gamma > 0.000001) + g = 1.0 / png_ptr->screen_gamma; + else + g = png_ptr->gamma; /* probably doing rgb_to_gray */ + + for (i = 0; i < 256; i++) + { + png_ptr->gamma_from_1[i] = (png_byte)(pow((double)i / 255.0, + g) * 255.0 + .5); + + } + } +#endif /* PNG_READ_BACKGROUND_SUPPORTED || PNG_RGB_TO_GRAY_SUPPORTED */ + } + else + { + double g; + int i, j, shift, num; + int sig_bit; + png_uint_32 ig; + + if (png_ptr->color_type & PNG_COLOR_MASK_COLOR) + { + sig_bit = (int)png_ptr->sig_bit.red; + if ((int)png_ptr->sig_bit.green > sig_bit) + sig_bit = png_ptr->sig_bit.green; + if ((int)png_ptr->sig_bit.blue > sig_bit) + sig_bit = png_ptr->sig_bit.blue; + } + else + { + sig_bit = (int)png_ptr->sig_bit.gray; + } + + if (sig_bit > 0) + shift = 16 - sig_bit; + else + shift = 0; + + if (png_ptr->transformations & PNG_16_TO_8) + { + if (shift < (16 - PNG_MAX_GAMMA_8)) + shift = (16 - PNG_MAX_GAMMA_8); + } + + if (shift > 8) + shift = 8; + if (shift < 0) + shift = 0; + + png_ptr->gamma_shift = (png_byte)shift; + + num = (1 << (8 - shift)); + + if (png_ptr->screen_gamma > .000001) + g = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma); + else + g = 1.0; + + png_ptr->gamma_16_table = (png_uint_16pp)png_malloc(png_ptr, + (png_uint_32)(num * png_sizeof (png_uint_16p))); + + if (png_ptr->transformations & (PNG_16_TO_8 | PNG_BACKGROUND)) + { + double fin, fout; + png_uint_32 last, max; + + for (i = 0; i < num; i++) + { + png_ptr->gamma_16_table[i] = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(256 * png_sizeof (png_uint_16))); + } + + g = 1.0 / g; + last = 0; + for (i = 0; i < 256; i++) + { + fout = ((double)i + 0.5) / 256.0; + fin = pow(fout, g); + max = (png_uint_32)(fin * (double)((png_uint_32)num << 8)); + while (last <= max) + { + png_ptr->gamma_16_table[(int)(last & (0xff >> shift))] + [(int)(last >> (8 - shift))] = (png_uint_16)( + (png_uint_16)i | ((png_uint_16)i << 8)); + last++; + } + } + while (last < ((png_uint_32)num << 8)) + { + png_ptr->gamma_16_table[(int)(last & (0xff >> shift))] + [(int)(last >> (8 - shift))] = (png_uint_16)65535L; + last++; + } + } + else + { + for (i = 0; i < num; i++) + { + png_ptr->gamma_16_table[i] = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(256 * png_sizeof (png_uint_16))); + + ig = (((png_uint_32)i * (png_uint_32)png_gamma_shift[shift]) >> 4); + for (j = 0; j < 256; j++) + { + png_ptr->gamma_16_table[i][j] = + (png_uint_16)(pow((double)(ig + ((png_uint_32)j << 8)) / + 65535.0, g) * 65535.0 + .5); + } + } + } + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ + defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + if (png_ptr->transformations & (PNG_BACKGROUND | PNG_RGB_TO_GRAY)) + { + + g = 1.0 / (png_ptr->gamma); + + png_ptr->gamma_16_to_1 = (png_uint_16pp)png_malloc(png_ptr, + (png_uint_32)(num * png_sizeof (png_uint_16p ))); + + for (i = 0; i < num; i++) + { + png_ptr->gamma_16_to_1[i] = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(256 * png_sizeof (png_uint_16))); + + ig = (((png_uint_32)i * + (png_uint_32)png_gamma_shift[shift]) >> 4); + for (j = 0; j < 256; j++) + { + png_ptr->gamma_16_to_1[i][j] = + (png_uint_16)(pow((double)(ig + ((png_uint_32)j << 8)) / + 65535.0, g) * 65535.0 + .5); + } + } + + if(png_ptr->screen_gamma > 0.000001) + g = 1.0 / png_ptr->screen_gamma; + else + g = png_ptr->gamma; /* probably doing rgb_to_gray */ + + png_ptr->gamma_16_from_1 = (png_uint_16pp)png_malloc(png_ptr, + (png_uint_32)(num * png_sizeof (png_uint_16p))); + + for (i = 0; i < num; i++) + { + png_ptr->gamma_16_from_1[i] = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(256 * png_sizeof (png_uint_16))); + + ig = (((png_uint_32)i * + (png_uint_32)png_gamma_shift[shift]) >> 4); + for (j = 0; j < 256; j++) + { + png_ptr->gamma_16_from_1[i][j] = + (png_uint_16)(pow((double)(ig + ((png_uint_32)j << 8)) / + 65535.0, g) * 65535.0 + .5); + } + } + } +#endif /* PNG_READ_BACKGROUND_SUPPORTED || PNG_RGB_TO_GRAY_SUPPORTED */ + } +} +#endif +/* To do: install integer version of png_build_gamma_table here */ +#endif + +#if defined(PNG_MNG_FEATURES_SUPPORTED) +/* undoes intrapixel differencing */ +void /* PRIVATE */ +png_do_read_intrapixel(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_read_intrapixel\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + (row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + int bytes_per_pixel; + png_uint_32 row_width = row_info->width; + if (row_info->bit_depth == 8) + { + png_bytep rp; + png_uint_32 i; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + bytes_per_pixel = 3; + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + bytes_per_pixel = 4; + else + return; + + for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + { + *(rp) = (png_byte)((256 + *rp + *(rp+1))&0xff); + *(rp+2) = (png_byte)((256 + *(rp+2) + *(rp+1))&0xff); + } + } + else if (row_info->bit_depth == 16) + { + png_bytep rp; + png_uint_32 i; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + bytes_per_pixel = 6; + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + bytes_per_pixel = 8; + else + return; + + for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + { + png_uint_32 s0 = (*(rp ) << 8) | *(rp+1); + png_uint_32 s1 = (*(rp+2) << 8) | *(rp+3); + png_uint_32 s2 = (*(rp+4) << 8) | *(rp+5); + png_uint_32 red = (png_uint_32)((s0+s1+65536L) & 0xffffL); + png_uint_32 blue = (png_uint_32)((s2+s1+65536L) & 0xffffL); + *(rp ) = (png_byte)((red >> 8) & 0xff); + *(rp+1) = (png_byte)(red & 0xff); + *(rp+4) = (png_byte)((blue >> 8) & 0xff); + *(rp+5) = (png_byte)(blue & 0xff); + } + } + } +} +#endif /* PNG_MNG_FEATURES_SUPPORTED */ +#endif /* PNG_READ_SUPPORTED */ +/********* End of inlined file: pngrtran.c *********/ + +/********* Start of inlined file: pngrutil.c *********/ +/* pngrutil.c - utilities to read a PNG file + * + * Last changed in libpng 1.2.21 [October 4, 2007] + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2007 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This file contains routines that are only called from within + * libpng itself during the course of reading an image. + */ + +#define PNG_INTERNAL + +#if defined(PNG_READ_SUPPORTED) + +#if defined(_WIN32_WCE) && (_WIN32_WCE<0x500) +# define WIN32_WCE_OLD +#endif + +#ifdef PNG_FLOATING_POINT_SUPPORTED +# if defined(WIN32_WCE_OLD) +/* strtod() function is not supported on WindowsCE */ +__inline double png_strtod(png_structp png_ptr, PNG_CONST char *nptr, char **endptr) +{ + double result = 0; + int len; + wchar_t *str, *end; + + len = MultiByteToWideChar(CP_ACP, 0, nptr, -1, NULL, 0); + str = (wchar_t *)png_malloc(png_ptr, len * sizeof(wchar_t)); + if ( NULL != str ) + { + MultiByteToWideChar(CP_ACP, 0, nptr, -1, str, len); + result = wcstod(str, &end); + len = WideCharToMultiByte(CP_ACP, 0, end, -1, NULL, 0, NULL, NULL); + *endptr = (char *)nptr + (png_strlen(nptr) - len + 1); + png_free(png_ptr, str); + } + return result; +} +# else +# define png_strtod(p,a,b) strtod(a,b) +# endif +#endif + +png_uint_32 PNGAPI +png_get_uint_31(png_structp png_ptr, png_bytep buf) +{ + png_uint_32 i = png_get_uint_32(buf); + if (i > PNG_UINT_31_MAX) + png_error(png_ptr, "PNG unsigned integer out of range."); + return (i); +} +#ifndef PNG_READ_BIG_ENDIAN_SUPPORTED +/* Grab an unsigned 32-bit integer from a buffer in big-endian format. */ +png_uint_32 PNGAPI +png_get_uint_32(png_bytep buf) +{ + png_uint_32 i = ((png_uint_32)(*buf) << 24) + + ((png_uint_32)(*(buf + 1)) << 16) + + ((png_uint_32)(*(buf + 2)) << 8) + + (png_uint_32)(*(buf + 3)); + + return (i); +} + +/* Grab a signed 32-bit integer from a buffer in big-endian format. The + * data is stored in the PNG file in two's complement format, and it is + * assumed that the machine format for signed integers is the same. */ +png_int_32 PNGAPI +png_get_int_32(png_bytep buf) +{ + png_int_32 i = ((png_int_32)(*buf) << 24) + + ((png_int_32)(*(buf + 1)) << 16) + + ((png_int_32)(*(buf + 2)) << 8) + + (png_int_32)(*(buf + 3)); + + return (i); +} + +/* Grab an unsigned 16-bit integer from a buffer in big-endian format. */ +png_uint_16 PNGAPI +png_get_uint_16(png_bytep buf) +{ + png_uint_16 i = (png_uint_16)(((png_uint_16)(*buf) << 8) + + (png_uint_16)(*(buf + 1))); + + return (i); +} +#endif /* PNG_READ_BIG_ENDIAN_SUPPORTED */ + +/* Read data, and (optionally) run it through the CRC. */ +void /* PRIVATE */ +png_crc_read(png_structp png_ptr, png_bytep buf, png_size_t length) +{ + if(png_ptr == NULL) return; + png_read_data(png_ptr, buf, length); + png_calculate_crc(png_ptr, buf, length); +} + +/* Optionally skip data and then check the CRC. Depending on whether we + are reading a ancillary or critical chunk, and how the program has set + things up, we may calculate the CRC on the data and print a message. + Returns '1' if there was a CRC error, '0' otherwise. */ +int /* PRIVATE */ +png_crc_finish(png_structp png_ptr, png_uint_32 skip) +{ + png_size_t i; + png_size_t istop = png_ptr->zbuf_size; + + for (i = (png_size_t)skip; i > istop; i -= istop) + { + png_crc_read(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size); + } + if (i) + { + png_crc_read(png_ptr, png_ptr->zbuf, i); + } + + if (png_crc_error(png_ptr)) + { + if (((png_ptr->chunk_name[0] & 0x20) && /* Ancillary */ + !(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN)) || + (!(png_ptr->chunk_name[0] & 0x20) && /* Critical */ + (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_USE))) + { + png_chunk_warning(png_ptr, "CRC error"); + } + else + { + png_chunk_error(png_ptr, "CRC error"); + } + return (1); + } + + return (0); +} + +/* Compare the CRC stored in the PNG file with that calculated by libpng from + the data it has read thus far. */ +int /* PRIVATE */ +png_crc_error(png_structp png_ptr) +{ + png_byte crc_bytes[4]; + png_uint_32 crc; + int need_crc = 1; + + if (png_ptr->chunk_name[0] & 0x20) /* ancillary */ + { + if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) == + (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN)) + need_crc = 0; + } + else /* critical */ + { + if (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE) + need_crc = 0; + } + + png_read_data(png_ptr, crc_bytes, 4); + + if (need_crc) + { + crc = png_get_uint_32(crc_bytes); + return ((int)(crc != png_ptr->crc)); + } + else + return (0); +} + +#if defined(PNG_READ_zTXt_SUPPORTED) || defined(PNG_READ_iTXt_SUPPORTED) || \ + defined(PNG_READ_iCCP_SUPPORTED) +/* + * Decompress trailing data in a chunk. The assumption is that chunkdata + * points at an allocated area holding the contents of a chunk with a + * trailing compressed part. What we get back is an allocated area + * holding the original prefix part and an uncompressed version of the + * trailing part (the malloc area passed in is freed). + */ +png_charp /* PRIVATE */ +png_decompress_chunk(png_structp png_ptr, int comp_type, + png_charp chunkdata, png_size_t chunklength, + png_size_t prefix_size, png_size_t *newlength) +{ + static PNG_CONST char msg[] = "Error decoding compressed text"; + png_charp text; + png_size_t text_size; + + if (comp_type == PNG_COMPRESSION_TYPE_BASE) + { + int ret = Z_OK; + png_ptr->zstream.next_in = (png_bytep)(chunkdata + prefix_size); + png_ptr->zstream.avail_in = (uInt)(chunklength - prefix_size); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + + text_size = 0; + text = NULL; + + while (png_ptr->zstream.avail_in) + { + ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH); + if (ret != Z_OK && ret != Z_STREAM_END) + { + if (png_ptr->zstream.msg != NULL) + png_warning(png_ptr, png_ptr->zstream.msg); + else + png_warning(png_ptr, msg); + inflateReset(&png_ptr->zstream); + png_ptr->zstream.avail_in = 0; + + if (text == NULL) + { + text_size = prefix_size + png_sizeof(msg) + 1; + text = (png_charp)png_malloc_warn(png_ptr, text_size); + if (text == NULL) + { + png_free(png_ptr,chunkdata); + png_error(png_ptr,"Not enough memory to decompress chunk"); + } + png_memcpy(text, chunkdata, prefix_size); + } + + text[text_size - 1] = 0x00; + + /* Copy what we can of the error message into the text chunk */ + text_size = (png_size_t)(chunklength - (text - chunkdata) - 1); + text_size = png_sizeof(msg) > text_size ? text_size : + png_sizeof(msg); + png_memcpy(text + prefix_size, msg, text_size + 1); + break; + } + if (!png_ptr->zstream.avail_out || ret == Z_STREAM_END) + { + if (text == NULL) + { + text_size = prefix_size + + png_ptr->zbuf_size - png_ptr->zstream.avail_out; + text = (png_charp)png_malloc_warn(png_ptr, text_size + 1); + if (text == NULL) + { + png_free(png_ptr,chunkdata); + png_error(png_ptr,"Not enough memory to decompress chunk."); + } + png_memcpy(text + prefix_size, png_ptr->zbuf, + text_size - prefix_size); + png_memcpy(text, chunkdata, prefix_size); + *(text + text_size) = 0x00; + } + else + { + png_charp tmp; + + tmp = text; + text = (png_charp)png_malloc_warn(png_ptr, + (png_uint_32)(text_size + + png_ptr->zbuf_size - png_ptr->zstream.avail_out + 1)); + if (text == NULL) + { + png_free(png_ptr, tmp); + png_free(png_ptr, chunkdata); + png_error(png_ptr,"Not enough memory to decompress chunk.."); + } + png_memcpy(text, tmp, text_size); + png_free(png_ptr, tmp); + png_memcpy(text + text_size, png_ptr->zbuf, + (png_ptr->zbuf_size - png_ptr->zstream.avail_out)); + text_size += png_ptr->zbuf_size - png_ptr->zstream.avail_out; + *(text + text_size) = 0x00; + } + if (ret == Z_STREAM_END) + break; + else + { + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + } + } + } + if (ret != Z_STREAM_END) + { +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + char umsg[52]; + + if (ret == Z_BUF_ERROR) + png_snprintf(umsg, 52, + "Buffer error in compressed datastream in %s chunk", + png_ptr->chunk_name); + else if (ret == Z_DATA_ERROR) + png_snprintf(umsg, 52, + "Data error in compressed datastream in %s chunk", + png_ptr->chunk_name); + else + png_snprintf(umsg, 52, + "Incomplete compressed datastream in %s chunk", + png_ptr->chunk_name); + png_warning(png_ptr, umsg); +#else + png_warning(png_ptr, + "Incomplete compressed datastream in chunk other than IDAT"); +#endif + text_size=prefix_size; + if (text == NULL) + { + text = (png_charp)png_malloc_warn(png_ptr, text_size+1); + if (text == NULL) + { + png_free(png_ptr, chunkdata); + png_error(png_ptr,"Not enough memory for text."); + } + png_memcpy(text, chunkdata, prefix_size); + } + *(text + text_size) = 0x00; + } + + inflateReset(&png_ptr->zstream); + png_ptr->zstream.avail_in = 0; + + png_free(png_ptr, chunkdata); + chunkdata = text; + *newlength=text_size; + } + else /* if (comp_type != PNG_COMPRESSION_TYPE_BASE) */ + { +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + char umsg[50]; + + png_snprintf(umsg, 50, + "Unknown zTXt compression type %d", comp_type); + png_warning(png_ptr, umsg); +#else + png_warning(png_ptr, "Unknown zTXt compression type"); +#endif + + *(chunkdata + prefix_size) = 0x00; + *newlength=prefix_size; + } + + return chunkdata; +} +#endif + +/* read and check the IDHR chunk */ +void /* PRIVATE */ +png_handle_IHDR(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_byte buf[13]; + png_uint_32 width, height; + int bit_depth, color_type, compression_type, filter_type; + int interlace_type; + + png_debug(1, "in png_handle_IHDR\n"); + + if (png_ptr->mode & PNG_HAVE_IHDR) + png_error(png_ptr, "Out of place IHDR"); + + /* check the length */ + if (length != 13) + png_error(png_ptr, "Invalid IHDR chunk"); + + png_ptr->mode |= PNG_HAVE_IHDR; + + png_crc_read(png_ptr, buf, 13); + png_crc_finish(png_ptr, 0); + + width = png_get_uint_31(png_ptr, buf); + height = png_get_uint_31(png_ptr, buf + 4); + bit_depth = buf[8]; + color_type = buf[9]; + compression_type = buf[10]; + filter_type = buf[11]; + interlace_type = buf[12]; + + /* set internal variables */ + png_ptr->width = width; + png_ptr->height = height; + png_ptr->bit_depth = (png_byte)bit_depth; + png_ptr->interlaced = (png_byte)interlace_type; + png_ptr->color_type = (png_byte)color_type; +#if defined(PNG_MNG_FEATURES_SUPPORTED) + png_ptr->filter_type = (png_byte)filter_type; +#endif + png_ptr->compression_type = (png_byte)compression_type; + + /* find number of channels */ + switch (png_ptr->color_type) + { + case PNG_COLOR_TYPE_GRAY: + case PNG_COLOR_TYPE_PALETTE: + png_ptr->channels = 1; + break; + case PNG_COLOR_TYPE_RGB: + png_ptr->channels = 3; + break; + case PNG_COLOR_TYPE_GRAY_ALPHA: + png_ptr->channels = 2; + break; + case PNG_COLOR_TYPE_RGB_ALPHA: + png_ptr->channels = 4; + break; + } + + /* set up other useful info */ + png_ptr->pixel_depth = (png_byte)(png_ptr->bit_depth * + png_ptr->channels); + png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth,png_ptr->width); + png_debug1(3,"bit_depth = %d\n", png_ptr->bit_depth); + png_debug1(3,"channels = %d\n", png_ptr->channels); + png_debug1(3,"rowbytes = %lu\n", png_ptr->rowbytes); + png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, + color_type, interlace_type, compression_type, filter_type); +} + +/* read and check the palette */ +void /* PRIVATE */ +png_handle_PLTE(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_color palette[PNG_MAX_PALETTE_LENGTH]; + int num, i; +#ifndef PNG_NO_POINTER_INDEXING + png_colorp pal_ptr; +#endif + + png_debug(1, "in png_handle_PLTE\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before PLTE"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid PLTE after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->mode & PNG_HAVE_PLTE) + png_error(png_ptr, "Duplicate PLTE chunk"); + + png_ptr->mode |= PNG_HAVE_PLTE; + + if (!(png_ptr->color_type&PNG_COLOR_MASK_COLOR)) + { + png_warning(png_ptr, + "Ignoring PLTE chunk in grayscale PNG"); + png_crc_finish(png_ptr, length); + return; + } +#if !defined(PNG_READ_OPT_PLTE_SUPPORTED) + if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE) + { + png_crc_finish(png_ptr, length); + return; + } +#endif + + if (length > 3*PNG_MAX_PALETTE_LENGTH || length % 3) + { + if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE) + { + png_warning(png_ptr, "Invalid palette chunk"); + png_crc_finish(png_ptr, length); + return; + } + else + { + png_error(png_ptr, "Invalid palette chunk"); + } + } + + num = (int)length / 3; + +#ifndef PNG_NO_POINTER_INDEXING + for (i = 0, pal_ptr = palette; i < num; i++, pal_ptr++) + { + png_byte buf[3]; + + png_crc_read(png_ptr, buf, 3); + pal_ptr->red = buf[0]; + pal_ptr->green = buf[1]; + pal_ptr->blue = buf[2]; + } +#else + for (i = 0; i < num; i++) + { + png_byte buf[3]; + + png_crc_read(png_ptr, buf, 3); + /* don't depend upon png_color being any order */ + palette[i].red = buf[0]; + palette[i].green = buf[1]; + palette[i].blue = buf[2]; + } +#endif + + /* If we actually NEED the PLTE chunk (ie for a paletted image), we do + whatever the normal CRC configuration tells us. However, if we + have an RGB image, the PLTE can be considered ancillary, so + we will act as though it is. */ +#if !defined(PNG_READ_OPT_PLTE_SUPPORTED) + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) +#endif + { + png_crc_finish(png_ptr, 0); + } +#if !defined(PNG_READ_OPT_PLTE_SUPPORTED) + else if (png_crc_error(png_ptr)) /* Only if we have a CRC error */ + { + /* If we don't want to use the data from an ancillary chunk, + we have two options: an error abort, or a warning and we + ignore the data in this chunk (which should be OK, since + it's considered ancillary for a RGB or RGBA image). */ + if (!(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_USE)) + { + if (png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN) + { + png_chunk_error(png_ptr, "CRC error"); + } + else + { + png_chunk_warning(png_ptr, "CRC error"); + return; + } + } + /* Otherwise, we (optionally) emit a warning and use the chunk. */ + else if (!(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN)) + { + png_chunk_warning(png_ptr, "CRC error"); + } + } +#endif + + png_set_PLTE(png_ptr, info_ptr, palette, num); + +#if defined(PNG_READ_tRNS_SUPPORTED) + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS)) + { + if (png_ptr->num_trans > (png_uint_16)num) + { + png_warning(png_ptr, "Truncating incorrect tRNS chunk length"); + png_ptr->num_trans = (png_uint_16)num; + } + if (info_ptr->num_trans > (png_uint_16)num) + { + png_warning(png_ptr, "Truncating incorrect info tRNS chunk length"); + info_ptr->num_trans = (png_uint_16)num; + } + } + } +#endif + +} + +void /* PRIVATE */ +png_handle_IEND(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_debug(1, "in png_handle_IEND\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR) || !(png_ptr->mode & PNG_HAVE_IDAT)) + { + png_error(png_ptr, "No image in file"); + } + + png_ptr->mode |= (PNG_AFTER_IDAT | PNG_HAVE_IEND); + + if (length != 0) + { + png_warning(png_ptr, "Incorrect IEND chunk length"); + } + png_crc_finish(png_ptr, length); + + info_ptr =info_ptr; /* quiet compiler warnings about unused info_ptr */ +} + +#if defined(PNG_READ_gAMA_SUPPORTED) +void /* PRIVATE */ +png_handle_gAMA(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_fixed_point igamma; +#ifdef PNG_FLOATING_POINT_SUPPORTED + float file_gamma; +#endif + png_byte buf[4]; + + png_debug(1, "in png_handle_gAMA\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before gAMA"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid gAMA after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->mode & PNG_HAVE_PLTE) + /* Should be an error, but we can cope with it */ + png_warning(png_ptr, "Out of place gAMA chunk"); + + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA) +#if defined(PNG_READ_sRGB_SUPPORTED) + && !(info_ptr->valid & PNG_INFO_sRGB) +#endif + ) + { + png_warning(png_ptr, "Duplicate gAMA chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (length != 4) + { + png_warning(png_ptr, "Incorrect gAMA chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 4); + if (png_crc_finish(png_ptr, 0)) + return; + + igamma = (png_fixed_point)png_get_uint_32(buf); + /* check for zero gamma */ + if (igamma == 0) + { + png_warning(png_ptr, + "Ignoring gAMA chunk with gamma=0"); + return; + } + +#if defined(PNG_READ_sRGB_SUPPORTED) + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB)) + if (PNG_OUT_OF_RANGE(igamma, 45500L, 500)) + { + png_warning(png_ptr, + "Ignoring incorrect gAMA value when sRGB is also present"); +#ifndef PNG_NO_CONSOLE_IO + fprintf(stderr, "gamma = (%d/100000)\n", (int)igamma); +#endif + return; + } +#endif /* PNG_READ_sRGB_SUPPORTED */ + +#ifdef PNG_FLOATING_POINT_SUPPORTED + file_gamma = (float)igamma / (float)100000.0; +# ifdef PNG_READ_GAMMA_SUPPORTED + png_ptr->gamma = file_gamma; +# endif + png_set_gAMA(png_ptr, info_ptr, file_gamma); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + png_set_gAMA_fixed(png_ptr, info_ptr, igamma); +#endif +} +#endif + +#if defined(PNG_READ_sBIT_SUPPORTED) +void /* PRIVATE */ +png_handle_sBIT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_size_t truelen; + png_byte buf[4]; + + png_debug(1, "in png_handle_sBIT\n"); + + buf[0] = buf[1] = buf[2] = buf[3] = 0; + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before sBIT"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid sBIT after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->mode & PNG_HAVE_PLTE) + { + /* Should be an error, but we can cope with it */ + png_warning(png_ptr, "Out of place sBIT chunk"); + } + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sBIT)) + { + png_warning(png_ptr, "Duplicate sBIT chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + truelen = 3; + else + truelen = (png_size_t)png_ptr->channels; + + if (length != truelen || length > 4) + { + png_warning(png_ptr, "Incorrect sBIT chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, truelen); + if (png_crc_finish(png_ptr, 0)) + return; + + if (png_ptr->color_type & PNG_COLOR_MASK_COLOR) + { + png_ptr->sig_bit.red = buf[0]; + png_ptr->sig_bit.green = buf[1]; + png_ptr->sig_bit.blue = buf[2]; + png_ptr->sig_bit.alpha = buf[3]; + } + else + { + png_ptr->sig_bit.gray = buf[0]; + png_ptr->sig_bit.red = buf[0]; + png_ptr->sig_bit.green = buf[0]; + png_ptr->sig_bit.blue = buf[0]; + png_ptr->sig_bit.alpha = buf[1]; + } + png_set_sBIT(png_ptr, info_ptr, &(png_ptr->sig_bit)); +} +#endif + +#if defined(PNG_READ_cHRM_SUPPORTED) +void /* PRIVATE */ +png_handle_cHRM(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_byte buf[4]; +#ifdef PNG_FLOATING_POINT_SUPPORTED + float white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y; +#endif + png_fixed_point int_x_white, int_y_white, int_x_red, int_y_red, int_x_green, + int_y_green, int_x_blue, int_y_blue; + + png_uint_32 uint_x, uint_y; + + png_debug(1, "in png_handle_cHRM\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before cHRM"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid cHRM after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->mode & PNG_HAVE_PLTE) + /* Should be an error, but we can cope with it */ + png_warning(png_ptr, "Missing PLTE before cHRM"); + + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM) +#if defined(PNG_READ_sRGB_SUPPORTED) + && !(info_ptr->valid & PNG_INFO_sRGB) +#endif + ) + { + png_warning(png_ptr, "Duplicate cHRM chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (length != 32) + { + png_warning(png_ptr, "Incorrect cHRM chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 4); + uint_x = png_get_uint_32(buf); + + png_crc_read(png_ptr, buf, 4); + uint_y = png_get_uint_32(buf); + + if (uint_x > 80000L || uint_y > 80000L || + uint_x + uint_y > 100000L) + { + png_warning(png_ptr, "Invalid cHRM white point"); + png_crc_finish(png_ptr, 24); + return; + } + int_x_white = (png_fixed_point)uint_x; + int_y_white = (png_fixed_point)uint_y; + + png_crc_read(png_ptr, buf, 4); + uint_x = png_get_uint_32(buf); + + png_crc_read(png_ptr, buf, 4); + uint_y = png_get_uint_32(buf); + + if (uint_x + uint_y > 100000L) + { + png_warning(png_ptr, "Invalid cHRM red point"); + png_crc_finish(png_ptr, 16); + return; + } + int_x_red = (png_fixed_point)uint_x; + int_y_red = (png_fixed_point)uint_y; + + png_crc_read(png_ptr, buf, 4); + uint_x = png_get_uint_32(buf); + + png_crc_read(png_ptr, buf, 4); + uint_y = png_get_uint_32(buf); + + if (uint_x + uint_y > 100000L) + { + png_warning(png_ptr, "Invalid cHRM green point"); + png_crc_finish(png_ptr, 8); + return; + } + int_x_green = (png_fixed_point)uint_x; + int_y_green = (png_fixed_point)uint_y; + + png_crc_read(png_ptr, buf, 4); + uint_x = png_get_uint_32(buf); + + png_crc_read(png_ptr, buf, 4); + uint_y = png_get_uint_32(buf); + + if (uint_x + uint_y > 100000L) + { + png_warning(png_ptr, "Invalid cHRM blue point"); + png_crc_finish(png_ptr, 0); + return; + } + int_x_blue = (png_fixed_point)uint_x; + int_y_blue = (png_fixed_point)uint_y; + +#ifdef PNG_FLOATING_POINT_SUPPORTED + white_x = (float)int_x_white / (float)100000.0; + white_y = (float)int_y_white / (float)100000.0; + red_x = (float)int_x_red / (float)100000.0; + red_y = (float)int_y_red / (float)100000.0; + green_x = (float)int_x_green / (float)100000.0; + green_y = (float)int_y_green / (float)100000.0; + blue_x = (float)int_x_blue / (float)100000.0; + blue_y = (float)int_y_blue / (float)100000.0; +#endif + +#if defined(PNG_READ_sRGB_SUPPORTED) + if ((info_ptr != NULL) && (info_ptr->valid & PNG_INFO_sRGB)) + { + if (PNG_OUT_OF_RANGE(int_x_white, 31270, 1000) || + PNG_OUT_OF_RANGE(int_y_white, 32900, 1000) || + PNG_OUT_OF_RANGE(int_x_red, 64000L, 1000) || + PNG_OUT_OF_RANGE(int_y_red, 33000, 1000) || + PNG_OUT_OF_RANGE(int_x_green, 30000, 1000) || + PNG_OUT_OF_RANGE(int_y_green, 60000L, 1000) || + PNG_OUT_OF_RANGE(int_x_blue, 15000, 1000) || + PNG_OUT_OF_RANGE(int_y_blue, 6000, 1000)) + { + png_warning(png_ptr, + "Ignoring incorrect cHRM value when sRGB is also present"); +#ifndef PNG_NO_CONSOLE_IO +#ifdef PNG_FLOATING_POINT_SUPPORTED + fprintf(stderr,"wx=%f, wy=%f, rx=%f, ry=%f\n", + white_x, white_y, red_x, red_y); + fprintf(stderr,"gx=%f, gy=%f, bx=%f, by=%f\n", + green_x, green_y, blue_x, blue_y); +#else + fprintf(stderr,"wx=%ld, wy=%ld, rx=%ld, ry=%ld\n", + int_x_white, int_y_white, int_x_red, int_y_red); + fprintf(stderr,"gx=%ld, gy=%ld, bx=%ld, by=%ld\n", + int_x_green, int_y_green, int_x_blue, int_y_blue); +#endif +#endif /* PNG_NO_CONSOLE_IO */ + } + png_crc_finish(png_ptr, 0); + return; + } +#endif /* PNG_READ_sRGB_SUPPORTED */ + +#ifdef PNG_FLOATING_POINT_SUPPORTED + png_set_cHRM(png_ptr, info_ptr, + white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + png_set_cHRM_fixed(png_ptr, info_ptr, + int_x_white, int_y_white, int_x_red, int_y_red, int_x_green, + int_y_green, int_x_blue, int_y_blue); +#endif + if (png_crc_finish(png_ptr, 0)) + return; +} +#endif + +#if defined(PNG_READ_sRGB_SUPPORTED) +void /* PRIVATE */ +png_handle_sRGB(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + int intent; + png_byte buf[1]; + + png_debug(1, "in png_handle_sRGB\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before sRGB"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid sRGB after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->mode & PNG_HAVE_PLTE) + /* Should be an error, but we can cope with it */ + png_warning(png_ptr, "Out of place sRGB chunk"); + + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB)) + { + png_warning(png_ptr, "Duplicate sRGB chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (length != 1) + { + png_warning(png_ptr, "Incorrect sRGB chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 1); + if (png_crc_finish(png_ptr, 0)) + return; + + intent = buf[0]; + /* check for bad intent */ + if (intent >= PNG_sRGB_INTENT_LAST) + { + png_warning(png_ptr, "Unknown sRGB intent"); + return; + } + +#if defined(PNG_READ_gAMA_SUPPORTED) && defined(PNG_READ_GAMMA_SUPPORTED) + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA)) + { + png_fixed_point igamma; +#ifdef PNG_FIXED_POINT_SUPPORTED + igamma=info_ptr->int_gamma; +#else +# ifdef PNG_FLOATING_POINT_SUPPORTED + igamma=(png_fixed_point)(info_ptr->gamma * 100000.); +# endif +#endif + if (PNG_OUT_OF_RANGE(igamma, 45500L, 500)) + { + png_warning(png_ptr, + "Ignoring incorrect gAMA value when sRGB is also present"); +#ifndef PNG_NO_CONSOLE_IO +# ifdef PNG_FIXED_POINT_SUPPORTED + fprintf(stderr,"incorrect gamma=(%d/100000)\n",(int)png_ptr->int_gamma); +# else +# ifdef PNG_FLOATING_POINT_SUPPORTED + fprintf(stderr,"incorrect gamma=%f\n",png_ptr->gamma); +# endif +# endif +#endif + } + } +#endif /* PNG_READ_gAMA_SUPPORTED */ + +#ifdef PNG_READ_cHRM_SUPPORTED +#ifdef PNG_FIXED_POINT_SUPPORTED + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM)) + if (PNG_OUT_OF_RANGE(info_ptr->int_x_white, 31270, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_y_white, 32900, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_x_red, 64000L, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_y_red, 33000, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_x_green, 30000, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_y_green, 60000L, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_x_blue, 15000, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_y_blue, 6000, 1000)) + { + png_warning(png_ptr, + "Ignoring incorrect cHRM value when sRGB is also present"); + } +#endif /* PNG_FIXED_POINT_SUPPORTED */ +#endif /* PNG_READ_cHRM_SUPPORTED */ + + png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, intent); +} +#endif /* PNG_READ_sRGB_SUPPORTED */ + +#if defined(PNG_READ_iCCP_SUPPORTED) +void /* PRIVATE */ +png_handle_iCCP(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +/* Note: this does not properly handle chunks that are > 64K under DOS */ +{ + png_charp chunkdata; + png_byte compression_type; + png_bytep pC; + png_charp profile; + png_uint_32 skip = 0; + png_uint_32 profile_size, profile_length; + png_size_t slength, prefix_length, data_length; + + png_debug(1, "in png_handle_iCCP\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before iCCP"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid iCCP after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->mode & PNG_HAVE_PLTE) + /* Should be an error, but we can cope with it */ + png_warning(png_ptr, "Out of place iCCP chunk"); + + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_iCCP)) + { + png_warning(png_ptr, "Duplicate iCCP chunk"); + png_crc_finish(png_ptr, length); + return; + } + +#ifdef PNG_MAX_MALLOC_64K + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "iCCP chunk too large to fit in memory"); + skip = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + + chunkdata = (png_charp)png_malloc(png_ptr, length + 1); + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)chunkdata, slength); + + if (png_crc_finish(png_ptr, skip)) + { + png_free(png_ptr, chunkdata); + return; + } + + chunkdata[slength] = 0x00; + + for (profile = chunkdata; *profile; profile++) + /* empty loop to find end of name */ ; + + ++profile; + + /* there should be at least one zero (the compression type byte) + following the separator, and we should be on it */ + if ( profile >= chunkdata + slength - 1) + { + png_free(png_ptr, chunkdata); + png_warning(png_ptr, "Malformed iCCP chunk"); + return; + } + + /* compression_type should always be zero */ + compression_type = *profile++; + if (compression_type) + { + png_warning(png_ptr, "Ignoring nonzero compression type in iCCP chunk"); + compression_type=0x00; /* Reset it to zero (libpng-1.0.6 through 1.0.8 + wrote nonzero) */ + } + + prefix_length = profile - chunkdata; + chunkdata = png_decompress_chunk(png_ptr, compression_type, chunkdata, + slength, prefix_length, &data_length); + + profile_length = data_length - prefix_length; + + if ( prefix_length > data_length || profile_length < 4) + { + png_free(png_ptr, chunkdata); + png_warning(png_ptr, "Profile size field missing from iCCP chunk"); + return; + } + + /* Check the profile_size recorded in the first 32 bits of the ICC profile */ + pC = (png_bytep)(chunkdata+prefix_length); + profile_size = ((*(pC ))<<24) | + ((*(pC+1))<<16) | + ((*(pC+2))<< 8) | + ((*(pC+3)) ); + + if(profile_size < profile_length) + profile_length = profile_size; + + if(profile_size > profile_length) + { + png_free(png_ptr, chunkdata); + png_warning(png_ptr, "Ignoring truncated iCCP profile."); + return; + } + + png_set_iCCP(png_ptr, info_ptr, chunkdata, compression_type, + chunkdata + prefix_length, profile_length); + png_free(png_ptr, chunkdata); +} +#endif /* PNG_READ_iCCP_SUPPORTED */ + +#if defined(PNG_READ_sPLT_SUPPORTED) +void /* PRIVATE */ +png_handle_sPLT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +/* Note: this does not properly handle chunks that are > 64K under DOS */ +{ + png_bytep chunkdata; + png_bytep entry_start; + png_sPLT_t new_palette; +#ifdef PNG_NO_POINTER_INDEXING + png_sPLT_entryp pp; +#endif + int data_length, entry_size, i; + png_uint_32 skip = 0; + png_size_t slength; + + png_debug(1, "in png_handle_sPLT\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before sPLT"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid sPLT after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + +#ifdef PNG_MAX_MALLOC_64K + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "sPLT chunk too large to fit in memory"); + skip = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + + chunkdata = (png_bytep)png_malloc(png_ptr, length + 1); + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)chunkdata, slength); + + if (png_crc_finish(png_ptr, skip)) + { + png_free(png_ptr, chunkdata); + return; + } + + chunkdata[slength] = 0x00; + + for (entry_start = chunkdata; *entry_start; entry_start++) + /* empty loop to find end of name */ ; + ++entry_start; + + /* a sample depth should follow the separator, and we should be on it */ + if (entry_start > chunkdata + slength - 2) + { + png_free(png_ptr, chunkdata); + png_warning(png_ptr, "malformed sPLT chunk"); + return; + } + + new_palette.depth = *entry_start++; + entry_size = (new_palette.depth == 8 ? 6 : 10); + data_length = (slength - (entry_start - chunkdata)); + + /* integrity-check the data length */ + if (data_length % entry_size) + { + png_free(png_ptr, chunkdata); + png_warning(png_ptr, "sPLT chunk has bad length"); + return; + } + + new_palette.nentries = (png_int_32) ( data_length / entry_size); + if ((png_uint_32) new_palette.nentries > (png_uint_32) (PNG_SIZE_MAX / + png_sizeof(png_sPLT_entry))) + { + png_warning(png_ptr, "sPLT chunk too long"); + return; + } + new_palette.entries = (png_sPLT_entryp)png_malloc_warn( + png_ptr, new_palette.nentries * png_sizeof(png_sPLT_entry)); + if (new_palette.entries == NULL) + { + png_warning(png_ptr, "sPLT chunk requires too much memory"); + return; + } + +#ifndef PNG_NO_POINTER_INDEXING + for (i = 0; i < new_palette.nentries; i++) + { + png_sPLT_entryp pp = new_palette.entries + i; + + if (new_palette.depth == 8) + { + pp->red = *entry_start++; + pp->green = *entry_start++; + pp->blue = *entry_start++; + pp->alpha = *entry_start++; + } + else + { + pp->red = png_get_uint_16(entry_start); entry_start += 2; + pp->green = png_get_uint_16(entry_start); entry_start += 2; + pp->blue = png_get_uint_16(entry_start); entry_start += 2; + pp->alpha = png_get_uint_16(entry_start); entry_start += 2; + } + pp->frequency = png_get_uint_16(entry_start); entry_start += 2; + } +#else + pp = new_palette.entries; + for (i = 0; i < new_palette.nentries; i++) + { + + if (new_palette.depth == 8) + { + pp[i].red = *entry_start++; + pp[i].green = *entry_start++; + pp[i].blue = *entry_start++; + pp[i].alpha = *entry_start++; + } + else + { + pp[i].red = png_get_uint_16(entry_start); entry_start += 2; + pp[i].green = png_get_uint_16(entry_start); entry_start += 2; + pp[i].blue = png_get_uint_16(entry_start); entry_start += 2; + pp[i].alpha = png_get_uint_16(entry_start); entry_start += 2; + } + pp->frequency = png_get_uint_16(entry_start); entry_start += 2; + } +#endif + + /* discard all chunk data except the name and stash that */ + new_palette.name = (png_charp)chunkdata; + + png_set_sPLT(png_ptr, info_ptr, &new_palette, 1); + + png_free(png_ptr, chunkdata); + png_free(png_ptr, new_palette.entries); +} +#endif /* PNG_READ_sPLT_SUPPORTED */ + +#if defined(PNG_READ_tRNS_SUPPORTED) +void /* PRIVATE */ +png_handle_tRNS(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_byte readbuf[PNG_MAX_PALETTE_LENGTH]; + int bit_mask; + + png_debug(1, "in png_handle_tRNS\n"); + + /* For non-indexed color, mask off any bits in the tRNS value that + * exceed the bit depth. Some creators were writing extra bits there. + * This is not needed for indexed color. */ + bit_mask = (1 << png_ptr->bit_depth) - 1; + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before tRNS"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid tRNS after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS)) + { + png_warning(png_ptr, "Duplicate tRNS chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY) + { + png_byte buf[2]; + + if (length != 2) + { + png_warning(png_ptr, "Incorrect tRNS chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 2); + png_ptr->num_trans = 1; + png_ptr->trans_values.gray = png_get_uint_16(buf) & bit_mask; + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) + { + png_byte buf[6]; + + if (length != 6) + { + png_warning(png_ptr, "Incorrect tRNS chunk length"); + png_crc_finish(png_ptr, length); + return; + } + png_crc_read(png_ptr, buf, (png_size_t)length); + png_ptr->num_trans = 1; + png_ptr->trans_values.red = png_get_uint_16(buf) & bit_mask; + png_ptr->trans_values.green = png_get_uint_16(buf + 2) & bit_mask; + png_ptr->trans_values.blue = png_get_uint_16(buf + 4) & bit_mask; + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (!(png_ptr->mode & PNG_HAVE_PLTE)) + { + /* Should be an error, but we can cope with it. */ + png_warning(png_ptr, "Missing PLTE before tRNS"); + } + if (length > (png_uint_32)png_ptr->num_palette || + length > PNG_MAX_PALETTE_LENGTH) + { + png_warning(png_ptr, "Incorrect tRNS chunk length"); + png_crc_finish(png_ptr, length); + return; + } + if (length == 0) + { + png_warning(png_ptr, "Zero length tRNS chunk"); + png_crc_finish(png_ptr, length); + return; + } + png_crc_read(png_ptr, readbuf, (png_size_t)length); + png_ptr->num_trans = (png_uint_16)length; + } + else + { + png_warning(png_ptr, "tRNS chunk not allowed with alpha channel"); + png_crc_finish(png_ptr, length); + return; + } + + if (png_crc_finish(png_ptr, 0)) + { + png_ptr->num_trans = 0; + return; + } + + png_set_tRNS(png_ptr, info_ptr, readbuf, png_ptr->num_trans, + &(png_ptr->trans_values)); +} +#endif + +#if defined(PNG_READ_bKGD_SUPPORTED) +void /* PRIVATE */ +png_handle_bKGD(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_size_t truelen; + png_byte buf[6]; + + png_debug(1, "in png_handle_bKGD\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before bKGD"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid bKGD after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + !(png_ptr->mode & PNG_HAVE_PLTE)) + { + png_warning(png_ptr, "Missing PLTE before bKGD"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD)) + { + png_warning(png_ptr, "Duplicate bKGD chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + truelen = 1; + else if (png_ptr->color_type & PNG_COLOR_MASK_COLOR) + truelen = 6; + else + truelen = 2; + + if (length != truelen) + { + png_warning(png_ptr, "Incorrect bKGD chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, truelen); + if (png_crc_finish(png_ptr, 0)) + return; + + /* We convert the index value into RGB components so that we can allow + * arbitrary RGB values for background when we have transparency, and + * so it is easy to determine the RGB values of the background color + * from the info_ptr struct. */ + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + png_ptr->background.index = buf[0]; + if(info_ptr->num_palette) + { + if(buf[0] > info_ptr->num_palette) + { + png_warning(png_ptr, "Incorrect bKGD chunk index value"); + return; + } + png_ptr->background.red = + (png_uint_16)png_ptr->palette[buf[0]].red; + png_ptr->background.green = + (png_uint_16)png_ptr->palette[buf[0]].green; + png_ptr->background.blue = + (png_uint_16)png_ptr->palette[buf[0]].blue; + } + } + else if (!(png_ptr->color_type & PNG_COLOR_MASK_COLOR)) /* GRAY */ + { + png_ptr->background.red = + png_ptr->background.green = + png_ptr->background.blue = + png_ptr->background.gray = png_get_uint_16(buf); + } + else + { + png_ptr->background.red = png_get_uint_16(buf); + png_ptr->background.green = png_get_uint_16(buf + 2); + png_ptr->background.blue = png_get_uint_16(buf + 4); + } + + png_set_bKGD(png_ptr, info_ptr, &(png_ptr->background)); +} +#endif + +#if defined(PNG_READ_hIST_SUPPORTED) +void /* PRIVATE */ +png_handle_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + unsigned int num, i; + png_uint_16 readbuf[PNG_MAX_PALETTE_LENGTH]; + + png_debug(1, "in png_handle_hIST\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before hIST"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid hIST after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (!(png_ptr->mode & PNG_HAVE_PLTE)) + { + png_warning(png_ptr, "Missing PLTE before hIST"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST)) + { + png_warning(png_ptr, "Duplicate hIST chunk"); + png_crc_finish(png_ptr, length); + return; + } + + num = length / 2 ; + if (num != (unsigned int) png_ptr->num_palette || num > + (unsigned int) PNG_MAX_PALETTE_LENGTH) + { + png_warning(png_ptr, "Incorrect hIST chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + for (i = 0; i < num; i++) + { + png_byte buf[2]; + + png_crc_read(png_ptr, buf, 2); + readbuf[i] = png_get_uint_16(buf); + } + + if (png_crc_finish(png_ptr, 0)) + return; + + png_set_hIST(png_ptr, info_ptr, readbuf); +} +#endif + +#if defined(PNG_READ_pHYs_SUPPORTED) +void /* PRIVATE */ +png_handle_pHYs(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_byte buf[9]; + png_uint_32 res_x, res_y; + int unit_type; + + png_debug(1, "in png_handle_pHYs\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before pHYs"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid pHYs after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs)) + { + png_warning(png_ptr, "Duplicate pHYs chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (length != 9) + { + png_warning(png_ptr, "Incorrect pHYs chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 9); + if (png_crc_finish(png_ptr, 0)) + return; + + res_x = png_get_uint_32(buf); + res_y = png_get_uint_32(buf + 4); + unit_type = buf[8]; + png_set_pHYs(png_ptr, info_ptr, res_x, res_y, unit_type); +} +#endif + +#if defined(PNG_READ_oFFs_SUPPORTED) +void /* PRIVATE */ +png_handle_oFFs(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_byte buf[9]; + png_int_32 offset_x, offset_y; + int unit_type; + + png_debug(1, "in png_handle_oFFs\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before oFFs"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid oFFs after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs)) + { + png_warning(png_ptr, "Duplicate oFFs chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (length != 9) + { + png_warning(png_ptr, "Incorrect oFFs chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 9); + if (png_crc_finish(png_ptr, 0)) + return; + + offset_x = png_get_int_32(buf); + offset_y = png_get_int_32(buf + 4); + unit_type = buf[8]; + png_set_oFFs(png_ptr, info_ptr, offset_x, offset_y, unit_type); +} +#endif + +#if defined(PNG_READ_pCAL_SUPPORTED) +/* read the pCAL chunk (described in the PNG Extensions document) */ +void /* PRIVATE */ +png_handle_pCAL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_charp purpose; + png_int_32 X0, X1; + png_byte type, nparams; + png_charp buf, units, endptr; + png_charpp params; + png_size_t slength; + int i; + + png_debug(1, "in png_handle_pCAL\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before pCAL"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid pCAL after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pCAL)) + { + png_warning(png_ptr, "Duplicate pCAL chunk"); + png_crc_finish(png_ptr, length); + return; + } + + png_debug1(2, "Allocating and reading pCAL chunk data (%lu bytes)\n", + length + 1); + purpose = (png_charp)png_malloc_warn(png_ptr, length + 1); + if (purpose == NULL) + { + png_warning(png_ptr, "No memory for pCAL purpose."); + return; + } + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)purpose, slength); + + if (png_crc_finish(png_ptr, 0)) + { + png_free(png_ptr, purpose); + return; + } + + purpose[slength] = 0x00; /* null terminate the last string */ + + png_debug(3, "Finding end of pCAL purpose string\n"); + for (buf = purpose; *buf; buf++) + /* empty loop */ ; + + endptr = purpose + slength; + + /* We need to have at least 12 bytes after the purpose string + in order to get the parameter information. */ + if (endptr <= buf + 12) + { + png_warning(png_ptr, "Invalid pCAL data"); + png_free(png_ptr, purpose); + return; + } + + png_debug(3, "Reading pCAL X0, X1, type, nparams, and units\n"); + X0 = png_get_int_32((png_bytep)buf+1); + X1 = png_get_int_32((png_bytep)buf+5); + type = buf[9]; + nparams = buf[10]; + units = buf + 11; + + png_debug(3, "Checking pCAL equation type and number of parameters\n"); + /* Check that we have the right number of parameters for known + equation types. */ + if ((type == PNG_EQUATION_LINEAR && nparams != 2) || + (type == PNG_EQUATION_BASE_E && nparams != 3) || + (type == PNG_EQUATION_ARBITRARY && nparams != 3) || + (type == PNG_EQUATION_HYPERBOLIC && nparams != 4)) + { + png_warning(png_ptr, "Invalid pCAL parameters for equation type"); + png_free(png_ptr, purpose); + return; + } + else if (type >= PNG_EQUATION_LAST) + { + png_warning(png_ptr, "Unrecognized equation type for pCAL chunk"); + } + + for (buf = units; *buf; buf++) + /* Empty loop to move past the units string. */ ; + + png_debug(3, "Allocating pCAL parameters array\n"); + params = (png_charpp)png_malloc_warn(png_ptr, (png_uint_32)(nparams + *png_sizeof(png_charp))) ; + if (params == NULL) + { + png_free(png_ptr, purpose); + png_warning(png_ptr, "No memory for pCAL params."); + return; + } + + /* Get pointers to the start of each parameter string. */ + for (i = 0; i < (int)nparams; i++) + { + buf++; /* Skip the null string terminator from previous parameter. */ + + png_debug1(3, "Reading pCAL parameter %d\n", i); + for (params[i] = buf; buf <= endptr && *buf != 0x00; buf++) + /* Empty loop to move past each parameter string */ ; + + /* Make sure we haven't run out of data yet */ + if (buf > endptr) + { + png_warning(png_ptr, "Invalid pCAL data"); + png_free(png_ptr, purpose); + png_free(png_ptr, params); + return; + } + } + + png_set_pCAL(png_ptr, info_ptr, purpose, X0, X1, type, nparams, + units, params); + + png_free(png_ptr, purpose); + png_free(png_ptr, params); +} +#endif + +#if defined(PNG_READ_sCAL_SUPPORTED) +/* read the sCAL chunk */ +void /* PRIVATE */ +png_handle_sCAL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_charp buffer, ep; +#ifdef PNG_FLOATING_POINT_SUPPORTED + double width, height; + png_charp vp; +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + png_charp swidth, sheight; +#endif +#endif + png_size_t slength; + + png_debug(1, "in png_handle_sCAL\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before sCAL"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid sCAL after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sCAL)) + { + png_warning(png_ptr, "Duplicate sCAL chunk"); + png_crc_finish(png_ptr, length); + return; + } + + png_debug1(2, "Allocating and reading sCAL chunk data (%lu bytes)\n", + length + 1); + buffer = (png_charp)png_malloc_warn(png_ptr, length + 1); + if (buffer == NULL) + { + png_warning(png_ptr, "Out of memory while processing sCAL chunk"); + return; + } + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)buffer, slength); + + if (png_crc_finish(png_ptr, 0)) + { + png_free(png_ptr, buffer); + return; + } + + buffer[slength] = 0x00; /* null terminate the last string */ + + ep = buffer + 1; /* skip unit byte */ + +#ifdef PNG_FLOATING_POINT_SUPPORTED + width = png_strtod(png_ptr, ep, &vp); + if (*vp) + { + png_warning(png_ptr, "malformed width string in sCAL chunk"); + return; + } +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + swidth = (png_charp)png_malloc_warn(png_ptr, png_strlen(ep) + 1); + if (swidth == NULL) + { + png_warning(png_ptr, "Out of memory while processing sCAL chunk width"); + return; + } + png_memcpy(swidth, ep, (png_size_t)png_strlen(ep)); +#endif +#endif + + for (ep = buffer; *ep; ep++) + /* empty loop */ ; + ep++; + + if (buffer + slength < ep) + { + png_warning(png_ptr, "Truncated sCAL chunk"); +#if defined(PNG_FIXED_POINT_SUPPORTED) && \ + !defined(PNG_FLOATING_POINT_SUPPORTED) + png_free(png_ptr, swidth); +#endif + png_free(png_ptr, buffer); + return; + } + +#ifdef PNG_FLOATING_POINT_SUPPORTED + height = png_strtod(png_ptr, ep, &vp); + if (*vp) + { + png_warning(png_ptr, "malformed height string in sCAL chunk"); + return; + } +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + sheight = (png_charp)png_malloc_warn(png_ptr, png_strlen(ep) + 1); + if (swidth == NULL) + { + png_warning(png_ptr, "Out of memory while processing sCAL chunk height"); + return; + } + png_memcpy(sheight, ep, (png_size_t)png_strlen(ep)); +#endif +#endif + + if (buffer + slength < ep +#ifdef PNG_FLOATING_POINT_SUPPORTED + || width <= 0. || height <= 0. +#endif + ) + { + png_warning(png_ptr, "Invalid sCAL data"); + png_free(png_ptr, buffer); +#if defined(PNG_FIXED_POINT_SUPPORTED) && !defined(PNG_FLOATING_POINT_SUPPORTED) + png_free(png_ptr, swidth); + png_free(png_ptr, sheight); +#endif + return; + } + +#ifdef PNG_FLOATING_POINT_SUPPORTED + png_set_sCAL(png_ptr, info_ptr, buffer[0], width, height); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + png_set_sCAL_s(png_ptr, info_ptr, buffer[0], swidth, sheight); +#endif +#endif + + png_free(png_ptr, buffer); +#if defined(PNG_FIXED_POINT_SUPPORTED) && !defined(PNG_FLOATING_POINT_SUPPORTED) + png_free(png_ptr, swidth); + png_free(png_ptr, sheight); +#endif +} +#endif + +#if defined(PNG_READ_tIME_SUPPORTED) +void /* PRIVATE */ +png_handle_tIME(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_byte buf[7]; + png_time mod_time; + + png_debug(1, "in png_handle_tIME\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Out of place tIME chunk"); + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME)) + { + png_warning(png_ptr, "Duplicate tIME chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (png_ptr->mode & PNG_HAVE_IDAT) + png_ptr->mode |= PNG_AFTER_IDAT; + + if (length != 7) + { + png_warning(png_ptr, "Incorrect tIME chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 7); + if (png_crc_finish(png_ptr, 0)) + return; + + mod_time.second = buf[6]; + mod_time.minute = buf[5]; + mod_time.hour = buf[4]; + mod_time.day = buf[3]; + mod_time.month = buf[2]; + mod_time.year = png_get_uint_16(buf); + + png_set_tIME(png_ptr, info_ptr, &mod_time); +} +#endif + +#if defined(PNG_READ_tEXt_SUPPORTED) +/* Note: this does not properly handle chunks that are > 64K under DOS */ +void /* PRIVATE */ +png_handle_tEXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_textp text_ptr; + png_charp key; + png_charp text; + png_uint_32 skip = 0; + png_size_t slength; + int ret; + + png_debug(1, "in png_handle_tEXt\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before tEXt"); + + if (png_ptr->mode & PNG_HAVE_IDAT) + png_ptr->mode |= PNG_AFTER_IDAT; + +#ifdef PNG_MAX_MALLOC_64K + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "tEXt chunk too large to fit in memory"); + skip = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + + key = (png_charp)png_malloc_warn(png_ptr, length + 1); + if (key == NULL) + { + png_warning(png_ptr, "No memory to process text chunk."); + return; + } + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)key, slength); + + if (png_crc_finish(png_ptr, skip)) + { + png_free(png_ptr, key); + return; + } + + key[slength] = 0x00; + + for (text = key; *text; text++) + /* empty loop to find end of key */ ; + + if (text != key + slength) + text++; + + text_ptr = (png_textp)png_malloc_warn(png_ptr, + (png_uint_32)png_sizeof(png_text)); + if (text_ptr == NULL) + { + png_warning(png_ptr, "Not enough memory to process text chunk."); + png_free(png_ptr, key); + return; + } + text_ptr->compression = PNG_TEXT_COMPRESSION_NONE; + text_ptr->key = key; +#ifdef PNG_iTXt_SUPPORTED + text_ptr->lang = NULL; + text_ptr->lang_key = NULL; + text_ptr->itxt_length = 0; +#endif + text_ptr->text = text; + text_ptr->text_length = png_strlen(text); + + ret=png_set_text_2(png_ptr, info_ptr, text_ptr, 1); + + png_free(png_ptr, key); + png_free(png_ptr, text_ptr); + if (ret) + png_warning(png_ptr, "Insufficient memory to process text chunk."); +} +#endif + +#if defined(PNG_READ_zTXt_SUPPORTED) +/* note: this does not correctly handle chunks that are > 64K under DOS */ +void /* PRIVATE */ +png_handle_zTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_textp text_ptr; + png_charp chunkdata; + png_charp text; + int comp_type; + int ret; + png_size_t slength, prefix_len, data_len; + + png_debug(1, "in png_handle_zTXt\n"); + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before zTXt"); + + if (png_ptr->mode & PNG_HAVE_IDAT) + png_ptr->mode |= PNG_AFTER_IDAT; + +#ifdef PNG_MAX_MALLOC_64K + /* We will no doubt have problems with chunks even half this size, but + there is no hard and fast rule to tell us where to stop. */ + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr,"zTXt chunk too large to fit in memory"); + png_crc_finish(png_ptr, length); + return; + } +#endif + + chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1); + if (chunkdata == NULL) + { + png_warning(png_ptr,"Out of memory processing zTXt chunk."); + return; + } + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)chunkdata, slength); + if (png_crc_finish(png_ptr, 0)) + { + png_free(png_ptr, chunkdata); + return; + } + + chunkdata[slength] = 0x00; + + for (text = chunkdata; *text; text++) + /* empty loop */ ; + + /* zTXt must have some text after the chunkdataword */ + if (text >= chunkdata + slength - 2) + { + png_warning(png_ptr, "Truncated zTXt chunk"); + png_free(png_ptr, chunkdata); + return; + } + else + { + comp_type = *(++text); + if (comp_type != PNG_TEXT_COMPRESSION_zTXt) + { + png_warning(png_ptr, "Unknown compression type in zTXt chunk"); + comp_type = PNG_TEXT_COMPRESSION_zTXt; + } + text++; /* skip the compression_method byte */ + } + prefix_len = text - chunkdata; + + chunkdata = (png_charp)png_decompress_chunk(png_ptr, comp_type, chunkdata, + (png_size_t)length, prefix_len, &data_len); + + text_ptr = (png_textp)png_malloc_warn(png_ptr, + (png_uint_32)png_sizeof(png_text)); + if (text_ptr == NULL) + { + png_warning(png_ptr,"Not enough memory to process zTXt chunk."); + png_free(png_ptr, chunkdata); + return; + } + text_ptr->compression = comp_type; + text_ptr->key = chunkdata; +#ifdef PNG_iTXt_SUPPORTED + text_ptr->lang = NULL; + text_ptr->lang_key = NULL; + text_ptr->itxt_length = 0; +#endif + text_ptr->text = chunkdata + prefix_len; + text_ptr->text_length = data_len; + + ret=png_set_text_2(png_ptr, info_ptr, text_ptr, 1); + + png_free(png_ptr, text_ptr); + png_free(png_ptr, chunkdata); + if (ret) + png_error(png_ptr, "Insufficient memory to store zTXt chunk."); +} +#endif + +#if defined(PNG_READ_iTXt_SUPPORTED) +/* note: this does not correctly handle chunks that are > 64K under DOS */ +void /* PRIVATE */ +png_handle_iTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_textp text_ptr; + png_charp chunkdata; + png_charp key, lang, text, lang_key; + int comp_flag; + int comp_type = 0; + int ret; + png_size_t slength, prefix_len, data_len; + + png_debug(1, "in png_handle_iTXt\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before iTXt"); + + if (png_ptr->mode & PNG_HAVE_IDAT) + png_ptr->mode |= PNG_AFTER_IDAT; + +#ifdef PNG_MAX_MALLOC_64K + /* We will no doubt have problems with chunks even half this size, but + there is no hard and fast rule to tell us where to stop. */ + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr,"iTXt chunk too large to fit in memory"); + png_crc_finish(png_ptr, length); + return; + } +#endif + + chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1); + if (chunkdata == NULL) + { + png_warning(png_ptr, "No memory to process iTXt chunk."); + return; + } + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)chunkdata, slength); + if (png_crc_finish(png_ptr, 0)) + { + png_free(png_ptr, chunkdata); + return; + } + + chunkdata[slength] = 0x00; + + for (lang = chunkdata; *lang; lang++) + /* empty loop */ ; + lang++; /* skip NUL separator */ + + /* iTXt must have a language tag (possibly empty), two compression bytes, + translated keyword (possibly empty), and possibly some text after the + keyword */ + + if (lang >= chunkdata + slength - 3) + { + png_warning(png_ptr, "Truncated iTXt chunk"); + png_free(png_ptr, chunkdata); + return; + } + else + { + comp_flag = *lang++; + comp_type = *lang++; + } + + for (lang_key = lang; *lang_key; lang_key++) + /* empty loop */ ; + lang_key++; /* skip NUL separator */ + + if (lang_key >= chunkdata + slength) + { + png_warning(png_ptr, "Truncated iTXt chunk"); + png_free(png_ptr, chunkdata); + return; + } + + for (text = lang_key; *text; text++) + /* empty loop */ ; + text++; /* skip NUL separator */ + if (text >= chunkdata + slength) + { + png_warning(png_ptr, "Malformed iTXt chunk"); + png_free(png_ptr, chunkdata); + return; + } + + prefix_len = text - chunkdata; + + key=chunkdata; + if (comp_flag) + chunkdata = png_decompress_chunk(png_ptr, comp_type, chunkdata, + (size_t)length, prefix_len, &data_len); + else + data_len=png_strlen(chunkdata + prefix_len); + text_ptr = (png_textp)png_malloc_warn(png_ptr, + (png_uint_32)png_sizeof(png_text)); + if (text_ptr == NULL) + { + png_warning(png_ptr,"Not enough memory to process iTXt chunk."); + png_free(png_ptr, chunkdata); + return; + } + text_ptr->compression = (int)comp_flag + 1; + text_ptr->lang_key = chunkdata+(lang_key-key); + text_ptr->lang = chunkdata+(lang-key); + text_ptr->itxt_length = data_len; + text_ptr->text_length = 0; + text_ptr->key = chunkdata; + text_ptr->text = chunkdata + prefix_len; + + ret=png_set_text_2(png_ptr, info_ptr, text_ptr, 1); + + png_free(png_ptr, text_ptr); + png_free(png_ptr, chunkdata); + if (ret) + png_error(png_ptr, "Insufficient memory to store iTXt chunk."); +} +#endif + +/* This function is called when we haven't found a handler for a + chunk. If there isn't a problem with the chunk itself (ie bad + chunk name, CRC, or a critical chunk), the chunk is silently ignored + -- unless the PNG_FLAG_UNKNOWN_CHUNKS_SUPPORTED flag is on in which + case it will be saved away to be written out later. */ +void /* PRIVATE */ +png_handle_unknown(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_uint_32 skip = 0; + + png_debug(1, "in png_handle_unknown\n"); + + if (png_ptr->mode & PNG_HAVE_IDAT) + { +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_CONST PNG_IDAT; +#endif + if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) /* not an IDAT */ + png_ptr->mode |= PNG_AFTER_IDAT; + } + + png_check_chunk_name(png_ptr, png_ptr->chunk_name); + + if (!(png_ptr->chunk_name[0] & 0x20)) + { +#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) + if(png_handle_as_unknown(png_ptr, png_ptr->chunk_name) != + PNG_HANDLE_CHUNK_ALWAYS +#if defined(PNG_READ_USER_CHUNKS_SUPPORTED) + && png_ptr->read_user_chunk_fn == NULL +#endif + ) +#endif + png_chunk_error(png_ptr, "unknown critical chunk"); + } + +#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) + if ((png_ptr->flags & PNG_FLAG_KEEP_UNKNOWN_CHUNKS) || + (png_ptr->read_user_chunk_fn != NULL)) + { +#ifdef PNG_MAX_MALLOC_64K + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "unknown chunk too large to fit in memory"); + skip = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + png_strncpy((png_charp)png_ptr->unknown_chunk.name, + (png_charp)png_ptr->chunk_name, 5); + png_ptr->unknown_chunk.data = (png_bytep)png_malloc(png_ptr, length); + png_ptr->unknown_chunk.size = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)png_ptr->unknown_chunk.data, length); +#if defined(PNG_READ_USER_CHUNKS_SUPPORTED) + if(png_ptr->read_user_chunk_fn != NULL) + { + /* callback to user unknown chunk handler */ + int ret; + ret = (*(png_ptr->read_user_chunk_fn)) + (png_ptr, &png_ptr->unknown_chunk); + if (ret < 0) + png_chunk_error(png_ptr, "error in user chunk"); + if (ret == 0) + { + if (!(png_ptr->chunk_name[0] & 0x20)) + if(png_handle_as_unknown(png_ptr, png_ptr->chunk_name) != + PNG_HANDLE_CHUNK_ALWAYS) + png_chunk_error(png_ptr, "unknown critical chunk"); + png_set_unknown_chunks(png_ptr, info_ptr, + &png_ptr->unknown_chunk, 1); + } + } +#else + png_set_unknown_chunks(png_ptr, info_ptr, &png_ptr->unknown_chunk, 1); +#endif + png_free(png_ptr, png_ptr->unknown_chunk.data); + png_ptr->unknown_chunk.data = NULL; + } + else +#endif + skip = length; + + png_crc_finish(png_ptr, skip); + +#if !defined(PNG_READ_USER_CHUNKS_SUPPORTED) + info_ptr = info_ptr; /* quiet compiler warnings about unused info_ptr */ +#endif +} + +/* This function is called to verify that a chunk name is valid. + This function can't have the "critical chunk check" incorporated + into it, since in the future we will need to be able to call user + functions to handle unknown critical chunks after we check that + the chunk name itself is valid. */ + +#define isnonalpha(c) ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97)) + +void /* PRIVATE */ +png_check_chunk_name(png_structp png_ptr, png_bytep chunk_name) +{ + png_debug(1, "in png_check_chunk_name\n"); + if (isnonalpha(chunk_name[0]) || isnonalpha(chunk_name[1]) || + isnonalpha(chunk_name[2]) || isnonalpha(chunk_name[3])) + { + png_chunk_error(png_ptr, "invalid chunk type"); + } +} + +/* Combines the row recently read in with the existing pixels in the + row. This routine takes care of alpha and transparency if requested. + This routine also handles the two methods of progressive display + of interlaced images, depending on the mask value. + The mask value describes which pixels are to be combined with + the row. The pattern always repeats every 8 pixels, so just 8 + bits are needed. A one indicates the pixel is to be combined, + a zero indicates the pixel is to be skipped. This is in addition + to any alpha or transparency value associated with the pixel. If + you want all pixels to be combined, pass 0xff (255) in mask. */ + +void /* PRIVATE */ +png_combine_row(png_structp png_ptr, png_bytep row, int mask) +{ + png_debug(1,"in png_combine_row\n"); + if (mask == 0xff) + { + png_memcpy(row, png_ptr->row_buf + 1, + PNG_ROWBYTES(png_ptr->row_info.pixel_depth, png_ptr->width)); + } + else + { + switch (png_ptr->row_info.pixel_depth) + { + case 1: + { + png_bytep sp = png_ptr->row_buf + 1; + png_bytep dp = row; + int s_inc, s_start, s_end; + int m = 0x80; + int shift; + png_uint_32 i; + png_uint_32 row_width = png_ptr->width; + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + { + s_start = 0; + s_end = 7; + s_inc = 1; + } + else +#endif + { + s_start = 7; + s_end = 0; + s_inc = -1; + } + + shift = s_start; + + for (i = 0; i < row_width; i++) + { + if (m & mask) + { + int value; + + value = (*sp >> shift) & 0x01; + *dp &= (png_byte)((0x7f7f >> (7 - shift)) & 0xff); + *dp |= (png_byte)(value << shift); + } + + if (shift == s_end) + { + shift = s_start; + sp++; + dp++; + } + else + shift += s_inc; + + if (m == 1) + m = 0x80; + else + m >>= 1; + } + break; + } + case 2: + { + png_bytep sp = png_ptr->row_buf + 1; + png_bytep dp = row; + int s_start, s_end, s_inc; + int m = 0x80; + int shift; + png_uint_32 i; + png_uint_32 row_width = png_ptr->width; + int value; + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + { + s_start = 0; + s_end = 6; + s_inc = 2; + } + else +#endif + { + s_start = 6; + s_end = 0; + s_inc = -2; + } + + shift = s_start; + + for (i = 0; i < row_width; i++) + { + if (m & mask) + { + value = (*sp >> shift) & 0x03; + *dp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff); + *dp |= (png_byte)(value << shift); + } + + if (shift == s_end) + { + shift = s_start; + sp++; + dp++; + } + else + shift += s_inc; + if (m == 1) + m = 0x80; + else + m >>= 1; + } + break; + } + case 4: + { + png_bytep sp = png_ptr->row_buf + 1; + png_bytep dp = row; + int s_start, s_end, s_inc; + int m = 0x80; + int shift; + png_uint_32 i; + png_uint_32 row_width = png_ptr->width; + int value; + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + { + s_start = 0; + s_end = 4; + s_inc = 4; + } + else +#endif + { + s_start = 4; + s_end = 0; + s_inc = -4; + } + shift = s_start; + + for (i = 0; i < row_width; i++) + { + if (m & mask) + { + value = (*sp >> shift) & 0xf; + *dp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff); + *dp |= (png_byte)(value << shift); + } + + if (shift == s_end) + { + shift = s_start; + sp++; + dp++; + } + else + shift += s_inc; + if (m == 1) + m = 0x80; + else + m >>= 1; + } + break; + } + default: + { + png_bytep sp = png_ptr->row_buf + 1; + png_bytep dp = row; + png_size_t pixel_bytes = (png_ptr->row_info.pixel_depth >> 3); + png_uint_32 i; + png_uint_32 row_width = png_ptr->width; + png_byte m = 0x80; + + for (i = 0; i < row_width; i++) + { + if (m & mask) + { + png_memcpy(dp, sp, pixel_bytes); + } + + sp += pixel_bytes; + dp += pixel_bytes; + + if (m == 1) + m = 0x80; + else + m >>= 1; + } + break; + } + } + } +} + +#ifdef PNG_READ_INTERLACING_SUPPORTED +/* OLD pre-1.0.9 interface: +void png_do_read_interlace(png_row_infop row_info, png_bytep row, int pass, + png_uint_32 transformations) + */ +void /* PRIVATE */ +png_do_read_interlace(png_structp png_ptr) +{ + png_row_infop row_info = &(png_ptr->row_info); + png_bytep row = png_ptr->row_buf + 1; + int pass = png_ptr->pass; + png_uint_32 transformations = png_ptr->transformations; +#ifdef PNG_USE_LOCAL_ARRAYS + /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + /* offset to next interlace block */ + PNG_CONST int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; +#endif + + png_debug(1,"in png_do_read_interlace\n"); + if (row != NULL && row_info != NULL) + { + png_uint_32 final_width; + + final_width = row_info->width * png_pass_inc[pass]; + + switch (row_info->pixel_depth) + { + case 1: + { + png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 3); + png_bytep dp = row + (png_size_t)((final_width - 1) >> 3); + int sshift, dshift; + int s_start, s_end, s_inc; + int jstop = png_pass_inc[pass]; + png_byte v; + png_uint_32 i; + int j; + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (transformations & PNG_PACKSWAP) + { + sshift = (int)((row_info->width + 7) & 0x07); + dshift = (int)((final_width + 7) & 0x07); + s_start = 7; + s_end = 0; + s_inc = -1; + } + else +#endif + { + sshift = 7 - (int)((row_info->width + 7) & 0x07); + dshift = 7 - (int)((final_width + 7) & 0x07); + s_start = 0; + s_end = 7; + s_inc = 1; + } + + for (i = 0; i < row_info->width; i++) + { + v = (png_byte)((*sp >> sshift) & 0x01); + for (j = 0; j < jstop; j++) + { + *dp &= (png_byte)((0x7f7f >> (7 - dshift)) & 0xff); + *dp |= (png_byte)(v << dshift); + if (dshift == s_end) + { + dshift = s_start; + dp--; + } + else + dshift += s_inc; + } + if (sshift == s_end) + { + sshift = s_start; + sp--; + } + else + sshift += s_inc; + } + break; + } + case 2: + { + png_bytep sp = row + (png_uint_32)((row_info->width - 1) >> 2); + png_bytep dp = row + (png_uint_32)((final_width - 1) >> 2); + int sshift, dshift; + int s_start, s_end, s_inc; + int jstop = png_pass_inc[pass]; + png_uint_32 i; + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (transformations & PNG_PACKSWAP) + { + sshift = (int)(((row_info->width + 3) & 0x03) << 1); + dshift = (int)(((final_width + 3) & 0x03) << 1); + s_start = 6; + s_end = 0; + s_inc = -2; + } + else +#endif + { + sshift = (int)((3 - ((row_info->width + 3) & 0x03)) << 1); + dshift = (int)((3 - ((final_width + 3) & 0x03)) << 1); + s_start = 0; + s_end = 6; + s_inc = 2; + } + + for (i = 0; i < row_info->width; i++) + { + png_byte v; + int j; + + v = (png_byte)((*sp >> sshift) & 0x03); + for (j = 0; j < jstop; j++) + { + *dp &= (png_byte)((0x3f3f >> (6 - dshift)) & 0xff); + *dp |= (png_byte)(v << dshift); + if (dshift == s_end) + { + dshift = s_start; + dp--; + } + else + dshift += s_inc; + } + if (sshift == s_end) + { + sshift = s_start; + sp--; + } + else + sshift += s_inc; + } + break; + } + case 4: + { + png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 1); + png_bytep dp = row + (png_size_t)((final_width - 1) >> 1); + int sshift, dshift; + int s_start, s_end, s_inc; + png_uint_32 i; + int jstop = png_pass_inc[pass]; + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (transformations & PNG_PACKSWAP) + { + sshift = (int)(((row_info->width + 1) & 0x01) << 2); + dshift = (int)(((final_width + 1) & 0x01) << 2); + s_start = 4; + s_end = 0; + s_inc = -4; + } + else +#endif + { + sshift = (int)((1 - ((row_info->width + 1) & 0x01)) << 2); + dshift = (int)((1 - ((final_width + 1) & 0x01)) << 2); + s_start = 0; + s_end = 4; + s_inc = 4; + } + + for (i = 0; i < row_info->width; i++) + { + png_byte v = (png_byte)((*sp >> sshift) & 0xf); + int j; + + for (j = 0; j < jstop; j++) + { + *dp &= (png_byte)((0xf0f >> (4 - dshift)) & 0xff); + *dp |= (png_byte)(v << dshift); + if (dshift == s_end) + { + dshift = s_start; + dp--; + } + else + dshift += s_inc; + } + if (sshift == s_end) + { + sshift = s_start; + sp--; + } + else + sshift += s_inc; + } + break; + } + default: + { + png_size_t pixel_bytes = (row_info->pixel_depth >> 3); + png_bytep sp = row + (png_size_t)(row_info->width - 1) * pixel_bytes; + png_bytep dp = row + (png_size_t)(final_width - 1) * pixel_bytes; + + int jstop = png_pass_inc[pass]; + png_uint_32 i; + + for (i = 0; i < row_info->width; i++) + { + png_byte v[8]; + int j; + + png_memcpy(v, sp, pixel_bytes); + for (j = 0; j < jstop; j++) + { + png_memcpy(dp, v, pixel_bytes); + dp -= pixel_bytes; + } + sp -= pixel_bytes; + } + break; + } + } + row_info->width = final_width; + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,final_width); + } +#if !defined(PNG_READ_PACKSWAP_SUPPORTED) + transformations = transformations; /* silence compiler warning */ +#endif +} +#endif /* PNG_READ_INTERLACING_SUPPORTED */ + +void /* PRIVATE */ +png_read_filter_row(png_structp png_ptr, png_row_infop row_info, png_bytep row, + png_bytep prev_row, int filter) +{ + png_debug(1, "in png_read_filter_row\n"); + png_debug2(2,"row = %lu, filter = %d\n", png_ptr->row_number, filter); + switch (filter) + { + case PNG_FILTER_VALUE_NONE: + break; + case PNG_FILTER_VALUE_SUB: + { + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3; + png_bytep rp = row + bpp; + png_bytep lp = row; + + for (i = bpp; i < istop; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*lp++)) & 0xff); + rp++; + } + break; + } + case PNG_FILTER_VALUE_UP: + { + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + png_bytep rp = row; + png_bytep pp = prev_row; + + for (i = 0; i < istop; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff); + rp++; + } + break; + } + case PNG_FILTER_VALUE_AVG: + { + png_uint_32 i; + png_bytep rp = row; + png_bytep pp = prev_row; + png_bytep lp = row; + png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3; + png_uint_32 istop = row_info->rowbytes - bpp; + + for (i = 0; i < bpp; i++) + { + *rp = (png_byte)(((int)(*rp) + + ((int)(*pp++) / 2 )) & 0xff); + rp++; + } + + for (i = 0; i < istop; i++) + { + *rp = (png_byte)(((int)(*rp) + + (int)(*pp++ + *lp++) / 2 ) & 0xff); + rp++; + } + break; + } + case PNG_FILTER_VALUE_PAETH: + { + png_uint_32 i; + png_bytep rp = row; + png_bytep pp = prev_row; + png_bytep lp = row; + png_bytep cp = prev_row; + png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3; + png_uint_32 istop=row_info->rowbytes - bpp; + + for (i = 0; i < bpp; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff); + rp++; + } + + for (i = 0; i < istop; i++) /* use leftover rp,pp */ + { + int a, b, c, pa, pb, pc, p; + + a = *lp++; + b = *pp++; + c = *cp++; + + p = b - c; + pc = a - c; + +#ifdef PNG_USE_ABS + pa = abs(p); + pb = abs(pc); + pc = abs(p + pc); +#else + pa = p < 0 ? -p : p; + pb = pc < 0 ? -pc : pc; + pc = (p + pc) < 0 ? -(p + pc) : p + pc; +#endif + + /* + if (pa <= pb && pa <= pc) + p = a; + else if (pb <= pc) + p = b; + else + p = c; + */ + + p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c; + + *rp = (png_byte)(((int)(*rp) + p) & 0xff); + rp++; + } + break; + } + default: + png_warning(png_ptr, "Ignoring bad adaptive filter type"); + *row=0; + break; + } +} + +void /* PRIVATE */ +png_read_finish_row(png_structp png_ptr) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* start of interlace block */ + PNG_CONST int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* offset to next interlace block */ + PNG_CONST int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + /* start of interlace block in the y direction */ + PNG_CONST int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; + + /* offset to next interlace block in the y direction */ + PNG_CONST int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; +#endif + + png_debug(1, "in png_read_finish_row\n"); + png_ptr->row_number++; + if (png_ptr->row_number < png_ptr->num_rows) + return; + + if (png_ptr->interlaced) + { + png_ptr->row_number = 0; + png_memset_check(png_ptr, png_ptr->prev_row, 0, + png_ptr->rowbytes + 1); + do + { + png_ptr->pass++; + if (png_ptr->pass >= 7) + break; + png_ptr->iwidth = (png_ptr->width + + png_pass_inc[png_ptr->pass] - 1 - + png_pass_start[png_ptr->pass]) / + png_pass_inc[png_ptr->pass]; + + png_ptr->irowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, + png_ptr->iwidth) + 1; + + if (!(png_ptr->transformations & PNG_INTERLACE)) + { + png_ptr->num_rows = (png_ptr->height + + png_pass_yinc[png_ptr->pass] - 1 - + png_pass_ystart[png_ptr->pass]) / + png_pass_yinc[png_ptr->pass]; + if (!(png_ptr->num_rows)) + continue; + } + else /* if (png_ptr->transformations & PNG_INTERLACE) */ + break; + } while (png_ptr->iwidth == 0); + + if (png_ptr->pass < 7) + return; + } + + if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED)) + { +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_CONST PNG_IDAT; +#endif + char extra; + int ret; + + png_ptr->zstream.next_out = (Byte *)&extra; + png_ptr->zstream.avail_out = (uInt)1; + for(;;) + { + if (!(png_ptr->zstream.avail_in)) + { + while (!png_ptr->idat_size) + { + png_byte chunk_length[4]; + + png_crc_finish(png_ptr, 0); + + png_read_data(png_ptr, chunk_length, 4); + png_ptr->idat_size = png_get_uint_31(png_ptr, chunk_length); + png_reset_crc(png_ptr); + png_crc_read(png_ptr, png_ptr->chunk_name, 4); + if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + png_error(png_ptr, "Not enough image data"); + + } + png_ptr->zstream.avail_in = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_in = png_ptr->zbuf; + if (png_ptr->zbuf_size > png_ptr->idat_size) + png_ptr->zstream.avail_in = (uInt)png_ptr->idat_size; + png_crc_read(png_ptr, png_ptr->zbuf, png_ptr->zstream.avail_in); + png_ptr->idat_size -= png_ptr->zstream.avail_in; + } + ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH); + if (ret == Z_STREAM_END) + { + if (!(png_ptr->zstream.avail_out) || png_ptr->zstream.avail_in || + png_ptr->idat_size) + png_warning(png_ptr, "Extra compressed data"); + png_ptr->mode |= PNG_AFTER_IDAT; + png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; + break; + } + if (ret != Z_OK) + png_error(png_ptr, png_ptr->zstream.msg ? png_ptr->zstream.msg : + "Decompression Error"); + + if (!(png_ptr->zstream.avail_out)) + { + png_warning(png_ptr, "Extra compressed data."); + png_ptr->mode |= PNG_AFTER_IDAT; + png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; + break; + } + + } + png_ptr->zstream.avail_out = 0; + } + + if (png_ptr->idat_size || png_ptr->zstream.avail_in) + png_warning(png_ptr, "Extra compression data"); + + inflateReset(&png_ptr->zstream); + + png_ptr->mode |= PNG_AFTER_IDAT; +} + +void /* PRIVATE */ +png_read_start_row(png_structp png_ptr) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* start of interlace block */ + PNG_CONST int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* offset to next interlace block */ + PNG_CONST int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + /* start of interlace block in the y direction */ + PNG_CONST int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; + + /* offset to next interlace block in the y direction */ + PNG_CONST int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; +#endif + + int max_pixel_depth; + png_uint_32 row_bytes; + + png_debug(1, "in png_read_start_row\n"); + png_ptr->zstream.avail_in = 0; + png_init_read_transformations(png_ptr); + if (png_ptr->interlaced) + { + if (!(png_ptr->transformations & PNG_INTERLACE)) + png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 - + png_pass_ystart[0]) / png_pass_yinc[0]; + else + png_ptr->num_rows = png_ptr->height; + + png_ptr->iwidth = (png_ptr->width + + png_pass_inc[png_ptr->pass] - 1 - + png_pass_start[png_ptr->pass]) / + png_pass_inc[png_ptr->pass]; + + row_bytes = PNG_ROWBYTES(png_ptr->pixel_depth,png_ptr->iwidth) + 1; + + png_ptr->irowbytes = (png_size_t)row_bytes; + if((png_uint_32)png_ptr->irowbytes != row_bytes) + png_error(png_ptr, "Rowbytes overflow in png_read_start_row"); + } + else + { + png_ptr->num_rows = png_ptr->height; + png_ptr->iwidth = png_ptr->width; + png_ptr->irowbytes = png_ptr->rowbytes + 1; + } + max_pixel_depth = png_ptr->pixel_depth; + +#if defined(PNG_READ_PACK_SUPPORTED) + if ((png_ptr->transformations & PNG_PACK) && png_ptr->bit_depth < 8) + max_pixel_depth = 8; +#endif + +#if defined(PNG_READ_EXPAND_SUPPORTED) + if (png_ptr->transformations & PNG_EXPAND) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (png_ptr->num_trans) + max_pixel_depth = 32; + else + max_pixel_depth = 24; + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY) + { + if (max_pixel_depth < 8) + max_pixel_depth = 8; + if (png_ptr->num_trans) + max_pixel_depth *= 2; + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) + { + if (png_ptr->num_trans) + { + max_pixel_depth *= 4; + max_pixel_depth /= 3; + } + } + } +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) + if (png_ptr->transformations & (PNG_FILLER)) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + max_pixel_depth = 32; + else if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY) + { + if (max_pixel_depth <= 8) + max_pixel_depth = 16; + else + max_pixel_depth = 32; + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) + { + if (max_pixel_depth <= 32) + max_pixel_depth = 32; + else + max_pixel_depth = 64; + } + } +#endif + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) + if (png_ptr->transformations & PNG_GRAY_TO_RGB) + { + if ( +#if defined(PNG_READ_EXPAND_SUPPORTED) + (png_ptr->num_trans && (png_ptr->transformations & PNG_EXPAND)) || +#endif +#if defined(PNG_READ_FILLER_SUPPORTED) + (png_ptr->transformations & (PNG_FILLER)) || +#endif + png_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + if (max_pixel_depth <= 16) + max_pixel_depth = 32; + else + max_pixel_depth = 64; + } + else + { + if (max_pixel_depth <= 8) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + max_pixel_depth = 32; + else + max_pixel_depth = 24; + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + max_pixel_depth = 64; + else + max_pixel_depth = 48; + } + } +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) && \ +defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) + if(png_ptr->transformations & PNG_USER_TRANSFORM) + { + int user_pixel_depth=png_ptr->user_transform_depth* + png_ptr->user_transform_channels; + if(user_pixel_depth > max_pixel_depth) + max_pixel_depth=user_pixel_depth; + } +#endif + + /* align the width on the next larger 8 pixels. Mainly used + for interlacing */ + row_bytes = ((png_ptr->width + 7) & ~((png_uint_32)7)); + /* calculate the maximum bytes needed, adding a byte and a pixel + for safety's sake */ + row_bytes = PNG_ROWBYTES(max_pixel_depth,row_bytes) + + 1 + ((max_pixel_depth + 7) >> 3); +#ifdef PNG_MAX_MALLOC_64K + if (row_bytes > (png_uint_32)65536L) + png_error(png_ptr, "This image requires a row greater than 64KB"); +#endif + png_ptr->big_row_buf = (png_bytep)png_malloc(png_ptr, row_bytes+64); + png_ptr->row_buf = png_ptr->big_row_buf+32; + +#ifdef PNG_MAX_MALLOC_64K + if ((png_uint_32)png_ptr->rowbytes + 1 > (png_uint_32)65536L) + png_error(png_ptr, "This image requires a row greater than 64KB"); +#endif + if ((png_uint_32)png_ptr->rowbytes > (png_uint_32)(PNG_SIZE_MAX - 1)) + png_error(png_ptr, "Row has too many bytes to allocate in memory."); + png_ptr->prev_row = (png_bytep)png_malloc(png_ptr, (png_uint_32)( + png_ptr->rowbytes + 1)); + + png_memset_check(png_ptr, png_ptr->prev_row, 0, png_ptr->rowbytes + 1); + + png_debug1(3, "width = %lu,\n", png_ptr->width); + png_debug1(3, "height = %lu,\n", png_ptr->height); + png_debug1(3, "iwidth = %lu,\n", png_ptr->iwidth); + png_debug1(3, "num_rows = %lu\n", png_ptr->num_rows); + png_debug1(3, "rowbytes = %lu,\n", png_ptr->rowbytes); + png_debug1(3, "irowbytes = %lu,\n", png_ptr->irowbytes); + + png_ptr->flags |= PNG_FLAG_ROW_INIT; +} +#endif /* PNG_READ_SUPPORTED */ +/********* End of inlined file: pngrutil.c *********/ + +/********* Start of inlined file: pngset.c *********/ +/* pngset.c - storage of image information into info struct + * + * Last changed in libpng 1.2.21 [October 4, 2007] + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2007 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * The functions here are used during reads to store data from the file + * into the info struct, and during writes to store application data + * into the info struct for writing into the file. This abstracts the + * info struct and allows us to change the structure in the future. + */ + +#define PNG_INTERNAL + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) + +#if defined(PNG_bKGD_SUPPORTED) +void PNGAPI +png_set_bKGD(png_structp png_ptr, png_infop info_ptr, png_color_16p background) +{ + png_debug1(1, "in %s storage function\n", "bKGD"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + png_memcpy(&(info_ptr->background), background, png_sizeof(png_color_16)); + info_ptr->valid |= PNG_INFO_bKGD; +} +#endif + +#if defined(PNG_cHRM_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_cHRM(png_structp png_ptr, png_infop info_ptr, + double white_x, double white_y, double red_x, double red_y, + double green_x, double green_y, double blue_x, double blue_y) +{ + png_debug1(1, "in %s storage function\n", "cHRM"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (white_x < 0.0 || white_y < 0.0 || + red_x < 0.0 || red_y < 0.0 || + green_x < 0.0 || green_y < 0.0 || + blue_x < 0.0 || blue_y < 0.0) + { + png_warning(png_ptr, + "Ignoring attempt to set negative chromaticity value"); + return; + } + if (white_x > 21474.83 || white_y > 21474.83 || + red_x > 21474.83 || red_y > 21474.83 || + green_x > 21474.83 || green_y > 21474.83 || + blue_x > 21474.83 || blue_y > 21474.83) + { + png_warning(png_ptr, + "Ignoring attempt to set chromaticity value exceeding 21474.83"); + return; + } + + info_ptr->x_white = (float)white_x; + info_ptr->y_white = (float)white_y; + info_ptr->x_red = (float)red_x; + info_ptr->y_red = (float)red_y; + info_ptr->x_green = (float)green_x; + info_ptr->y_green = (float)green_y; + info_ptr->x_blue = (float)blue_x; + info_ptr->y_blue = (float)blue_y; +#ifdef PNG_FIXED_POINT_SUPPORTED + info_ptr->int_x_white = (png_fixed_point)(white_x*100000.+0.5); + info_ptr->int_y_white = (png_fixed_point)(white_y*100000.+0.5); + info_ptr->int_x_red = (png_fixed_point)( red_x*100000.+0.5); + info_ptr->int_y_red = (png_fixed_point)( red_y*100000.+0.5); + info_ptr->int_x_green = (png_fixed_point)(green_x*100000.+0.5); + info_ptr->int_y_green = (png_fixed_point)(green_y*100000.+0.5); + info_ptr->int_x_blue = (png_fixed_point)( blue_x*100000.+0.5); + info_ptr->int_y_blue = (png_fixed_point)( blue_y*100000.+0.5); +#endif + info_ptr->valid |= PNG_INFO_cHRM; +} +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +void PNGAPI +png_set_cHRM_fixed(png_structp png_ptr, png_infop info_ptr, + png_fixed_point white_x, png_fixed_point white_y, png_fixed_point red_x, + png_fixed_point red_y, png_fixed_point green_x, png_fixed_point green_y, + png_fixed_point blue_x, png_fixed_point blue_y) +{ + png_debug1(1, "in %s storage function\n", "cHRM"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (white_x < 0 || white_y < 0 || + red_x < 0 || red_y < 0 || + green_x < 0 || green_y < 0 || + blue_x < 0 || blue_y < 0) + { + png_warning(png_ptr, + "Ignoring attempt to set negative chromaticity value"); + return; + } +#ifdef PNG_FLOATING_POINT_SUPPORTED + if (white_x > (double) PNG_UINT_31_MAX || + white_y > (double) PNG_UINT_31_MAX || + red_x > (double) PNG_UINT_31_MAX || + red_y > (double) PNG_UINT_31_MAX || + green_x > (double) PNG_UINT_31_MAX || + green_y > (double) PNG_UINT_31_MAX || + blue_x > (double) PNG_UINT_31_MAX || + blue_y > (double) PNG_UINT_31_MAX) +#else + if (white_x > (png_fixed_point) PNG_UINT_31_MAX/100000L || + white_y > (png_fixed_point) PNG_UINT_31_MAX/100000L || + red_x > (png_fixed_point) PNG_UINT_31_MAX/100000L || + red_y > (png_fixed_point) PNG_UINT_31_MAX/100000L || + green_x > (png_fixed_point) PNG_UINT_31_MAX/100000L || + green_y > (png_fixed_point) PNG_UINT_31_MAX/100000L || + blue_x > (png_fixed_point) PNG_UINT_31_MAX/100000L || + blue_y > (png_fixed_point) PNG_UINT_31_MAX/100000L) +#endif + { + png_warning(png_ptr, + "Ignoring attempt to set chromaticity value exceeding 21474.83"); + return; + } + info_ptr->int_x_white = white_x; + info_ptr->int_y_white = white_y; + info_ptr->int_x_red = red_x; + info_ptr->int_y_red = red_y; + info_ptr->int_x_green = green_x; + info_ptr->int_y_green = green_y; + info_ptr->int_x_blue = blue_x; + info_ptr->int_y_blue = blue_y; +#ifdef PNG_FLOATING_POINT_SUPPORTED + info_ptr->x_white = (float)(white_x/100000.); + info_ptr->y_white = (float)(white_y/100000.); + info_ptr->x_red = (float)( red_x/100000.); + info_ptr->y_red = (float)( red_y/100000.); + info_ptr->x_green = (float)(green_x/100000.); + info_ptr->y_green = (float)(green_y/100000.); + info_ptr->x_blue = (float)( blue_x/100000.); + info_ptr->y_blue = (float)( blue_y/100000.); +#endif + info_ptr->valid |= PNG_INFO_cHRM; +} +#endif +#endif + +#if defined(PNG_gAMA_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_gAMA(png_structp png_ptr, png_infop info_ptr, double file_gamma) +{ + double gamma; + png_debug1(1, "in %s storage function\n", "gAMA"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + /* Check for overflow */ + if (file_gamma > 21474.83) + { + png_warning(png_ptr, "Limiting gamma to 21474.83"); + gamma=21474.83; + } + else + gamma=file_gamma; + info_ptr->gamma = (float)gamma; +#ifdef PNG_FIXED_POINT_SUPPORTED + info_ptr->int_gamma = (int)(gamma*100000.+.5); +#endif + info_ptr->valid |= PNG_INFO_gAMA; + if(gamma == 0.0) + png_warning(png_ptr, "Setting gamma=0"); +} +#endif +void PNGAPI +png_set_gAMA_fixed(png_structp png_ptr, png_infop info_ptr, png_fixed_point + int_gamma) +{ + png_fixed_point gamma; + + png_debug1(1, "in %s storage function\n", "gAMA"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (int_gamma > (png_fixed_point) PNG_UINT_31_MAX) + { + png_warning(png_ptr, "Limiting gamma to 21474.83"); + gamma=PNG_UINT_31_MAX; + } + else + { + if (int_gamma < 0) + { + png_warning(png_ptr, "Setting negative gamma to zero"); + gamma=0; + } + else + gamma=int_gamma; + } +#ifdef PNG_FLOATING_POINT_SUPPORTED + info_ptr->gamma = (float)(gamma/100000.); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + info_ptr->int_gamma = gamma; +#endif + info_ptr->valid |= PNG_INFO_gAMA; + if(gamma == 0) + png_warning(png_ptr, "Setting gamma=0"); +} +#endif + +#if defined(PNG_hIST_SUPPORTED) +void PNGAPI +png_set_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_16p hist) +{ + int i; + + png_debug1(1, "in %s storage function\n", "hIST"); + if (png_ptr == NULL || info_ptr == NULL) + return; + if (info_ptr->num_palette == 0 || info_ptr->num_palette + > PNG_MAX_PALETTE_LENGTH) + { + png_warning(png_ptr, + "Invalid palette size, hIST allocation skipped."); + return; + } + +#ifdef PNG_FREE_ME_SUPPORTED + png_free_data(png_ptr, info_ptr, PNG_FREE_HIST, 0); +#endif + /* Changed from info->num_palette to PNG_MAX_PALETTE_LENGTH in version + 1.2.1 */ + png_ptr->hist = (png_uint_16p)png_malloc_warn(png_ptr, + (png_uint_32)(PNG_MAX_PALETTE_LENGTH * png_sizeof (png_uint_16))); + if (png_ptr->hist == NULL) + { + png_warning(png_ptr, "Insufficient memory for hIST chunk data."); + return; + } + + for (i = 0; i < info_ptr->num_palette; i++) + png_ptr->hist[i] = hist[i]; + info_ptr->hist = png_ptr->hist; + info_ptr->valid |= PNG_INFO_hIST; + +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_HIST; +#else + png_ptr->flags |= PNG_FLAG_FREE_HIST; +#endif +} +#endif + +void PNGAPI +png_set_IHDR(png_structp png_ptr, png_infop info_ptr, + png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_type, int compression_type, + int filter_type) +{ + png_debug1(1, "in %s storage function\n", "IHDR"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + /* check for width and height valid values */ + if (width == 0 || height == 0) + png_error(png_ptr, "Image width or height is zero in IHDR"); +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + if (width > png_ptr->user_width_max || height > png_ptr->user_height_max) + png_error(png_ptr, "image size exceeds user limits in IHDR"); +#else + if (width > PNG_USER_WIDTH_MAX || height > PNG_USER_HEIGHT_MAX) + png_error(png_ptr, "image size exceeds user limits in IHDR"); +#endif + if (width > PNG_UINT_31_MAX || height > PNG_UINT_31_MAX) + png_error(png_ptr, "Invalid image size in IHDR"); + if ( width > (PNG_UINT_32_MAX + >> 3) /* 8-byte RGBA pixels */ + - 64 /* bigrowbuf hack */ + - 1 /* filter byte */ + - 7*8 /* rounding of width to multiple of 8 pixels */ + - 8) /* extra max_pixel_depth pad */ + png_warning(png_ptr, "Width is too large for libpng to process pixels"); + + /* check other values */ + if (bit_depth != 1 && bit_depth != 2 && bit_depth != 4 && + bit_depth != 8 && bit_depth != 16) + png_error(png_ptr, "Invalid bit depth in IHDR"); + + if (color_type < 0 || color_type == 1 || + color_type == 5 || color_type > 6) + png_error(png_ptr, "Invalid color type in IHDR"); + + if (((color_type == PNG_COLOR_TYPE_PALETTE) && bit_depth > 8) || + ((color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA || + color_type == PNG_COLOR_TYPE_RGB_ALPHA) && bit_depth < 8)) + png_error(png_ptr, "Invalid color type/bit depth combination in IHDR"); + + if (interlace_type >= PNG_INTERLACE_LAST) + png_error(png_ptr, "Unknown interlace method in IHDR"); + + if (compression_type != PNG_COMPRESSION_TYPE_BASE) + png_error(png_ptr, "Unknown compression method in IHDR"); + +#if defined(PNG_MNG_FEATURES_SUPPORTED) + /* Accept filter_method 64 (intrapixel differencing) only if + * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and + * 2. Libpng did not read a PNG signature (this filter_method is only + * used in PNG datastreams that are embedded in MNG datastreams) and + * 3. The application called png_permit_mng_features with a mask that + * included PNG_FLAG_MNG_FILTER_64 and + * 4. The filter_method is 64 and + * 5. The color_type is RGB or RGBA + */ + if((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE)&&png_ptr->mng_features_permitted) + png_warning(png_ptr,"MNG features are not allowed in a PNG datastream"); + if(filter_type != PNG_FILTER_TYPE_BASE) + { + if(!((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + (filter_type == PNG_INTRAPIXEL_DIFFERENCING) && + ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) == 0) && + (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_RGB_ALPHA))) + png_error(png_ptr, "Unknown filter method in IHDR"); + if(png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) + png_warning(png_ptr, "Invalid filter method in IHDR"); + } +#else + if(filter_type != PNG_FILTER_TYPE_BASE) + png_error(png_ptr, "Unknown filter method in IHDR"); +#endif + + info_ptr->width = width; + info_ptr->height = height; + info_ptr->bit_depth = (png_byte)bit_depth; + info_ptr->color_type =(png_byte) color_type; + info_ptr->compression_type = (png_byte)compression_type; + info_ptr->filter_type = (png_byte)filter_type; + info_ptr->interlace_type = (png_byte)interlace_type; + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + info_ptr->channels = 1; + else if (info_ptr->color_type & PNG_COLOR_MASK_COLOR) + info_ptr->channels = 3; + else + info_ptr->channels = 1; + if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA) + info_ptr->channels++; + info_ptr->pixel_depth = (png_byte)(info_ptr->channels * info_ptr->bit_depth); + + /* check for potential overflow */ + if (width > (PNG_UINT_32_MAX + >> 3) /* 8-byte RGBA pixels */ + - 64 /* bigrowbuf hack */ + - 1 /* filter byte */ + - 7*8 /* rounding of width to multiple of 8 pixels */ + - 8) /* extra max_pixel_depth pad */ + info_ptr->rowbytes = (png_size_t)0; + else + info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth,width); +} + +#if defined(PNG_oFFs_SUPPORTED) +void PNGAPI +png_set_oFFs(png_structp png_ptr, png_infop info_ptr, + png_int_32 offset_x, png_int_32 offset_y, int unit_type) +{ + png_debug1(1, "in %s storage function\n", "oFFs"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->x_offset = offset_x; + info_ptr->y_offset = offset_y; + info_ptr->offset_unit_type = (png_byte)unit_type; + info_ptr->valid |= PNG_INFO_oFFs; +} +#endif + +#if defined(PNG_pCAL_SUPPORTED) +void PNGAPI +png_set_pCAL(png_structp png_ptr, png_infop info_ptr, + png_charp purpose, png_int_32 X0, png_int_32 X1, int type, int nparams, + png_charp units, png_charpp params) +{ + png_uint_32 length; + int i; + + png_debug1(1, "in %s storage function\n", "pCAL"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + length = png_strlen(purpose) + 1; + png_debug1(3, "allocating purpose for info (%lu bytes)\n", length); + info_ptr->pcal_purpose = (png_charp)png_malloc_warn(png_ptr, length); + if (info_ptr->pcal_purpose == NULL) + { + png_warning(png_ptr, "Insufficient memory for pCAL purpose."); + return; + } + png_memcpy(info_ptr->pcal_purpose, purpose, (png_size_t)length); + + png_debug(3, "storing X0, X1, type, and nparams in info\n"); + info_ptr->pcal_X0 = X0; + info_ptr->pcal_X1 = X1; + info_ptr->pcal_type = (png_byte)type; + info_ptr->pcal_nparams = (png_byte)nparams; + + length = png_strlen(units) + 1; + png_debug1(3, "allocating units for info (%lu bytes)\n", length); + info_ptr->pcal_units = (png_charp)png_malloc_warn(png_ptr, length); + if (info_ptr->pcal_units == NULL) + { + png_warning(png_ptr, "Insufficient memory for pCAL units."); + return; + } + png_memcpy(info_ptr->pcal_units, units, (png_size_t)length); + + info_ptr->pcal_params = (png_charpp)png_malloc_warn(png_ptr, + (png_uint_32)((nparams + 1) * png_sizeof(png_charp))); + if (info_ptr->pcal_params == NULL) + { + png_warning(png_ptr, "Insufficient memory for pCAL params."); + return; + } + + info_ptr->pcal_params[nparams] = NULL; + + for (i = 0; i < nparams; i++) + { + length = png_strlen(params[i]) + 1; + png_debug2(3, "allocating parameter %d for info (%lu bytes)\n", i, length); + info_ptr->pcal_params[i] = (png_charp)png_malloc_warn(png_ptr, length); + if (info_ptr->pcal_params[i] == NULL) + { + png_warning(png_ptr, "Insufficient memory for pCAL parameter."); + return; + } + png_memcpy(info_ptr->pcal_params[i], params[i], (png_size_t)length); + } + + info_ptr->valid |= PNG_INFO_pCAL; +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_PCAL; +#endif +} +#endif + +#if defined(PNG_READ_sCAL_SUPPORTED) || defined(PNG_WRITE_sCAL_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_sCAL(png_structp png_ptr, png_infop info_ptr, + int unit, double width, double height) +{ + png_debug1(1, "in %s storage function\n", "sCAL"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->scal_unit = (png_byte)unit; + info_ptr->scal_pixel_width = width; + info_ptr->scal_pixel_height = height; + + info_ptr->valid |= PNG_INFO_sCAL; +} +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +void PNGAPI +png_set_sCAL_s(png_structp png_ptr, png_infop info_ptr, + int unit, png_charp swidth, png_charp sheight) +{ + png_uint_32 length; + + png_debug1(1, "in %s storage function\n", "sCAL"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->scal_unit = (png_byte)unit; + + length = png_strlen(swidth) + 1; + png_debug1(3, "allocating unit for info (%d bytes)\n", length); + info_ptr->scal_s_width = (png_charp)png_malloc_warn(png_ptr, length); + if (info_ptr->scal_s_width == NULL) + { + png_warning(png_ptr, + "Memory allocation failed while processing sCAL."); + } + png_memcpy(info_ptr->scal_s_width, swidth, (png_size_t)length); + + length = png_strlen(sheight) + 1; + png_debug1(3, "allocating unit for info (%d bytes)\n", length); + info_ptr->scal_s_height = (png_charp)png_malloc_warn(png_ptr, length); + if (info_ptr->scal_s_height == NULL) + { + png_free (png_ptr, info_ptr->scal_s_width); + png_warning(png_ptr, + "Memory allocation failed while processing sCAL."); + } + png_memcpy(info_ptr->scal_s_height, sheight, (png_size_t)length); + + info_ptr->valid |= PNG_INFO_sCAL; +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_SCAL; +#endif +} +#endif +#endif +#endif + +#if defined(PNG_pHYs_SUPPORTED) +void PNGAPI +png_set_pHYs(png_structp png_ptr, png_infop info_ptr, + png_uint_32 res_x, png_uint_32 res_y, int unit_type) +{ + png_debug1(1, "in %s storage function\n", "pHYs"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->x_pixels_per_unit = res_x; + info_ptr->y_pixels_per_unit = res_y; + info_ptr->phys_unit_type = (png_byte)unit_type; + info_ptr->valid |= PNG_INFO_pHYs; +} +#endif + +void PNGAPI +png_set_PLTE(png_structp png_ptr, png_infop info_ptr, + png_colorp palette, int num_palette) +{ + + png_debug1(1, "in %s storage function\n", "PLTE"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (num_palette < 0 || num_palette > PNG_MAX_PALETTE_LENGTH) + { + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + png_error(png_ptr, "Invalid palette length"); + else + { + png_warning(png_ptr, "Invalid palette length"); + return; + } + } + + /* + * It may not actually be necessary to set png_ptr->palette here; + * we do it for backward compatibility with the way the png_handle_tRNS + * function used to do the allocation. + */ +#ifdef PNG_FREE_ME_SUPPORTED + png_free_data(png_ptr, info_ptr, PNG_FREE_PLTE, 0); +#endif + + /* Changed in libpng-1.2.1 to allocate PNG_MAX_PALETTE_LENGTH instead + of num_palette entries, + in case of an invalid PNG file that has too-large sample values. */ + png_ptr->palette = (png_colorp)png_malloc(png_ptr, + PNG_MAX_PALETTE_LENGTH * png_sizeof(png_color)); + png_memset(png_ptr->palette, 0, PNG_MAX_PALETTE_LENGTH * + png_sizeof(png_color)); + png_memcpy(png_ptr->palette, palette, num_palette * png_sizeof (png_color)); + info_ptr->palette = png_ptr->palette; + info_ptr->num_palette = png_ptr->num_palette = (png_uint_16)num_palette; + +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_PLTE; +#else + png_ptr->flags |= PNG_FLAG_FREE_PLTE; +#endif + + info_ptr->valid |= PNG_INFO_PLTE; +} + +#if defined(PNG_sBIT_SUPPORTED) +void PNGAPI +png_set_sBIT(png_structp png_ptr, png_infop info_ptr, + png_color_8p sig_bit) +{ + png_debug1(1, "in %s storage function\n", "sBIT"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + png_memcpy(&(info_ptr->sig_bit), sig_bit, png_sizeof (png_color_8)); + info_ptr->valid |= PNG_INFO_sBIT; +} +#endif + +#if defined(PNG_sRGB_SUPPORTED) +void PNGAPI +png_set_sRGB(png_structp png_ptr, png_infop info_ptr, int intent) +{ + png_debug1(1, "in %s storage function\n", "sRGB"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->srgb_intent = (png_byte)intent; + info_ptr->valid |= PNG_INFO_sRGB; +} + +void PNGAPI +png_set_sRGB_gAMA_and_cHRM(png_structp png_ptr, png_infop info_ptr, + int intent) +{ +#if defined(PNG_gAMA_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED + float file_gamma; +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + png_fixed_point int_file_gamma; +#endif +#endif +#if defined(PNG_cHRM_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED + float white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y; +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + png_fixed_point int_white_x, int_white_y, int_red_x, int_red_y, int_green_x, + int_green_y, int_blue_x, int_blue_y; +#endif +#endif + png_debug1(1, "in %s storage function\n", "sRGB_gAMA_and_cHRM"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + png_set_sRGB(png_ptr, info_ptr, intent); + +#if defined(PNG_gAMA_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED + file_gamma = (float).45455; + png_set_gAMA(png_ptr, info_ptr, file_gamma); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + int_file_gamma = 45455L; + png_set_gAMA_fixed(png_ptr, info_ptr, int_file_gamma); +#endif +#endif + +#if defined(PNG_cHRM_SUPPORTED) +#ifdef PNG_FIXED_POINT_SUPPORTED + int_white_x = 31270L; + int_white_y = 32900L; + int_red_x = 64000L; + int_red_y = 33000L; + int_green_x = 30000L; + int_green_y = 60000L; + int_blue_x = 15000L; + int_blue_y = 6000L; + + png_set_cHRM_fixed(png_ptr, info_ptr, + int_white_x, int_white_y, int_red_x, int_red_y, int_green_x, int_green_y, + int_blue_x, int_blue_y); +#endif +#ifdef PNG_FLOATING_POINT_SUPPORTED + white_x = (float).3127; + white_y = (float).3290; + red_x = (float).64; + red_y = (float).33; + green_x = (float).30; + green_y = (float).60; + blue_x = (float).15; + blue_y = (float).06; + + png_set_cHRM(png_ptr, info_ptr, + white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y); +#endif +#endif +} +#endif + +#if defined(PNG_iCCP_SUPPORTED) +void PNGAPI +png_set_iCCP(png_structp png_ptr, png_infop info_ptr, + png_charp name, int compression_type, + png_charp profile, png_uint_32 proflen) +{ + png_charp new_iccp_name; + png_charp new_iccp_profile; + + png_debug1(1, "in %s storage function\n", "iCCP"); + if (png_ptr == NULL || info_ptr == NULL || name == NULL || profile == NULL) + return; + + new_iccp_name = (png_charp)png_malloc_warn(png_ptr, png_strlen(name)+1); + if (new_iccp_name == NULL) + { + png_warning(png_ptr, "Insufficient memory to process iCCP chunk."); + return; + } + png_strncpy(new_iccp_name, name, png_strlen(name)+1); + new_iccp_profile = (png_charp)png_malloc_warn(png_ptr, proflen); + if (new_iccp_profile == NULL) + { + png_free (png_ptr, new_iccp_name); + png_warning(png_ptr, "Insufficient memory to process iCCP profile."); + return; + } + png_memcpy(new_iccp_profile, profile, (png_size_t)proflen); + + png_free_data(png_ptr, info_ptr, PNG_FREE_ICCP, 0); + + info_ptr->iccp_proflen = proflen; + info_ptr->iccp_name = new_iccp_name; + info_ptr->iccp_profile = new_iccp_profile; + /* Compression is always zero but is here so the API and info structure + * does not have to change if we introduce multiple compression types */ + info_ptr->iccp_compression = (png_byte)compression_type; +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_ICCP; +#endif + info_ptr->valid |= PNG_INFO_iCCP; +} +#endif + +#if defined(PNG_TEXT_SUPPORTED) +void PNGAPI +png_set_text(png_structp png_ptr, png_infop info_ptr, png_textp text_ptr, + int num_text) +{ + int ret; + ret=png_set_text_2(png_ptr, info_ptr, text_ptr, num_text); + if (ret) + png_error(png_ptr, "Insufficient memory to store text"); +} + +int /* PRIVATE */ +png_set_text_2(png_structp png_ptr, png_infop info_ptr, png_textp text_ptr, + int num_text) +{ + int i; + + png_debug1(1, "in %s storage function\n", (png_ptr->chunk_name[0] == '\0' ? + "text" : (png_const_charp)png_ptr->chunk_name)); + + if (png_ptr == NULL || info_ptr == NULL || num_text == 0) + return(0); + + /* Make sure we have enough space in the "text" array in info_struct + * to hold all of the incoming text_ptr objects. + */ + if (info_ptr->num_text + num_text > info_ptr->max_text) + { + if (info_ptr->text != NULL) + { + png_textp old_text; + int old_max; + + old_max = info_ptr->max_text; + info_ptr->max_text = info_ptr->num_text + num_text + 8; + old_text = info_ptr->text; + info_ptr->text = (png_textp)png_malloc_warn(png_ptr, + (png_uint_32)(info_ptr->max_text * png_sizeof (png_text))); + if (info_ptr->text == NULL) + { + png_free(png_ptr, old_text); + return(1); + } + png_memcpy(info_ptr->text, old_text, (png_size_t)(old_max * + png_sizeof(png_text))); + png_free(png_ptr, old_text); + } + else + { + info_ptr->max_text = num_text + 8; + info_ptr->num_text = 0; + info_ptr->text = (png_textp)png_malloc_warn(png_ptr, + (png_uint_32)(info_ptr->max_text * png_sizeof (png_text))); + if (info_ptr->text == NULL) + return(1); +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_TEXT; +#endif + } + png_debug1(3, "allocated %d entries for info_ptr->text\n", + info_ptr->max_text); + } + for (i = 0; i < num_text; i++) + { + png_size_t text_length,key_len; + png_size_t lang_len,lang_key_len; + png_textp textp = &(info_ptr->text[info_ptr->num_text]); + + if (text_ptr[i].key == NULL) + continue; + + key_len = png_strlen(text_ptr[i].key); + + if(text_ptr[i].compression <= 0) + { + lang_len = 0; + lang_key_len = 0; + } + else +#ifdef PNG_iTXt_SUPPORTED + { + /* set iTXt data */ + if (text_ptr[i].lang != NULL) + lang_len = png_strlen(text_ptr[i].lang); + else + lang_len = 0; + if (text_ptr[i].lang_key != NULL) + lang_key_len = png_strlen(text_ptr[i].lang_key); + else + lang_key_len = 0; + } +#else + { + png_warning(png_ptr, "iTXt chunk not supported."); + continue; + } +#endif + + if (text_ptr[i].text == NULL || text_ptr[i].text[0] == '\0') + { + text_length = 0; +#ifdef PNG_iTXt_SUPPORTED + if(text_ptr[i].compression > 0) + textp->compression = PNG_ITXT_COMPRESSION_NONE; + else +#endif + textp->compression = PNG_TEXT_COMPRESSION_NONE; + } + else + { + text_length = png_strlen(text_ptr[i].text); + textp->compression = text_ptr[i].compression; + } + + textp->key = (png_charp)png_malloc_warn(png_ptr, + (png_uint_32)(key_len + text_length + lang_len + lang_key_len + 4)); + if (textp->key == NULL) + return(1); + png_debug2(2, "Allocated %lu bytes at %x in png_set_text\n", + (png_uint_32)(key_len + lang_len + lang_key_len + text_length + 4), + (int)textp->key); + + png_memcpy(textp->key, text_ptr[i].key, + (png_size_t)(key_len)); + *(textp->key+key_len) = '\0'; +#ifdef PNG_iTXt_SUPPORTED + if (text_ptr[i].compression > 0) + { + textp->lang=textp->key + key_len + 1; + png_memcpy(textp->lang, text_ptr[i].lang, lang_len); + *(textp->lang+lang_len) = '\0'; + textp->lang_key=textp->lang + lang_len + 1; + png_memcpy(textp->lang_key, text_ptr[i].lang_key, lang_key_len); + *(textp->lang_key+lang_key_len) = '\0'; + textp->text=textp->lang_key + lang_key_len + 1; + } + else +#endif + { +#ifdef PNG_iTXt_SUPPORTED + textp->lang=NULL; + textp->lang_key=NULL; +#endif + textp->text=textp->key + key_len + 1; + } + if(text_length) + png_memcpy(textp->text, text_ptr[i].text, + (png_size_t)(text_length)); + *(textp->text+text_length) = '\0'; + +#ifdef PNG_iTXt_SUPPORTED + if(textp->compression > 0) + { + textp->text_length = 0; + textp->itxt_length = text_length; + } + else +#endif + { + textp->text_length = text_length; +#ifdef PNG_iTXt_SUPPORTED + textp->itxt_length = 0; +#endif + } + info_ptr->num_text++; + png_debug1(3, "transferred text chunk %d\n", info_ptr->num_text); + } + return(0); +} +#endif + +#if defined(PNG_tIME_SUPPORTED) +void PNGAPI +png_set_tIME(png_structp png_ptr, png_infop info_ptr, png_timep mod_time) +{ + png_debug1(1, "in %s storage function\n", "tIME"); + if (png_ptr == NULL || info_ptr == NULL || + (png_ptr->mode & PNG_WROTE_tIME)) + return; + + png_memcpy(&(info_ptr->mod_time), mod_time, png_sizeof (png_time)); + info_ptr->valid |= PNG_INFO_tIME; +} +#endif + +#if defined(PNG_tRNS_SUPPORTED) +void PNGAPI +png_set_tRNS(png_structp png_ptr, png_infop info_ptr, + png_bytep trans, int num_trans, png_color_16p trans_values) +{ + png_debug1(1, "in %s storage function\n", "tRNS"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (trans != NULL) + { + /* + * It may not actually be necessary to set png_ptr->trans here; + * we do it for backward compatibility with the way the png_handle_tRNS + * function used to do the allocation. + */ +#ifdef PNG_FREE_ME_SUPPORTED + png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, 0); +#endif + /* Changed from num_trans to PNG_MAX_PALETTE_LENGTH in version 1.2.1 */ + png_ptr->trans = info_ptr->trans = (png_bytep)png_malloc(png_ptr, + (png_uint_32)PNG_MAX_PALETTE_LENGTH); + if (num_trans <= PNG_MAX_PALETTE_LENGTH) + png_memcpy(info_ptr->trans, trans, (png_size_t)num_trans); +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_TRNS; +#else + png_ptr->flags |= PNG_FLAG_FREE_TRNS; +#endif + } + + if (trans_values != NULL) + { + png_memcpy(&(info_ptr->trans_values), trans_values, + png_sizeof(png_color_16)); + if (num_trans == 0) + num_trans = 1; + } + info_ptr->num_trans = (png_uint_16)num_trans; + info_ptr->valid |= PNG_INFO_tRNS; +} +#endif + +#if defined(PNG_sPLT_SUPPORTED) +void PNGAPI +png_set_sPLT(png_structp png_ptr, + png_infop info_ptr, png_sPLT_tp entries, int nentries) +{ + png_sPLT_tp np; + int i; + + if (png_ptr == NULL || info_ptr == NULL) + return; + + np = (png_sPLT_tp)png_malloc_warn(png_ptr, + (info_ptr->splt_palettes_num + nentries) * png_sizeof(png_sPLT_t)); + if (np == NULL) + { + png_warning(png_ptr, "No memory for sPLT palettes."); + return; + } + + png_memcpy(np, info_ptr->splt_palettes, + info_ptr->splt_palettes_num * png_sizeof(png_sPLT_t)); + png_free(png_ptr, info_ptr->splt_palettes); + info_ptr->splt_palettes=NULL; + + for (i = 0; i < nentries; i++) + { + png_sPLT_tp to = np + info_ptr->splt_palettes_num + i; + png_sPLT_tp from = entries + i; + + to->name = (png_charp)png_malloc_warn(png_ptr, + png_strlen(from->name) + 1); + if (to->name == NULL) + { + png_warning(png_ptr, + "Out of memory while processing sPLT chunk"); + } + /* TODO: use png_malloc_warn */ + png_strncpy(to->name, from->name, png_strlen(from->name)+1); + to->entries = (png_sPLT_entryp)png_malloc_warn(png_ptr, + from->nentries * png_sizeof(png_sPLT_entry)); + /* TODO: use png_malloc_warn */ + png_memcpy(to->entries, from->entries, + from->nentries * png_sizeof(png_sPLT_entry)); + if (to->entries == NULL) + { + png_warning(png_ptr, + "Out of memory while processing sPLT chunk"); + png_free(png_ptr,to->name); + to->name = NULL; + } + to->nentries = from->nentries; + to->depth = from->depth; + } + + info_ptr->splt_palettes = np; + info_ptr->splt_palettes_num += nentries; + info_ptr->valid |= PNG_INFO_sPLT; +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_SPLT; +#endif +} +#endif /* PNG_sPLT_SUPPORTED */ + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) +void PNGAPI +png_set_unknown_chunks(png_structp png_ptr, + png_infop info_ptr, png_unknown_chunkp unknowns, int num_unknowns) +{ + png_unknown_chunkp np; + int i; + + if (png_ptr == NULL || info_ptr == NULL || num_unknowns == 0) + return; + + np = (png_unknown_chunkp)png_malloc_warn(png_ptr, + (info_ptr->unknown_chunks_num + num_unknowns) * + png_sizeof(png_unknown_chunk)); + if (np == NULL) + { + png_warning(png_ptr, + "Out of memory while processing unknown chunk."); + return; + } + + png_memcpy(np, info_ptr->unknown_chunks, + info_ptr->unknown_chunks_num * png_sizeof(png_unknown_chunk)); + png_free(png_ptr, info_ptr->unknown_chunks); + info_ptr->unknown_chunks=NULL; + + for (i = 0; i < num_unknowns; i++) + { + png_unknown_chunkp to = np + info_ptr->unknown_chunks_num + i; + png_unknown_chunkp from = unknowns + i; + + png_strncpy((png_charp)to->name, (png_charp)from->name, 5); + to->data = (png_bytep)png_malloc_warn(png_ptr, from->size); + if (to->data == NULL) + { + png_warning(png_ptr, + "Out of memory while processing unknown chunk."); + } + else + { + png_memcpy(to->data, from->data, from->size); + to->size = from->size; + + /* note our location in the read or write sequence */ + to->location = (png_byte)(png_ptr->mode & 0xff); + } + } + + info_ptr->unknown_chunks = np; + info_ptr->unknown_chunks_num += num_unknowns; +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_UNKN; +#endif +} +void PNGAPI +png_set_unknown_chunk_location(png_structp png_ptr, png_infop info_ptr, + int chunk, int location) +{ + if(png_ptr != NULL && info_ptr != NULL && chunk >= 0 && chunk < + (int)info_ptr->unknown_chunks_num) + info_ptr->unknown_chunks[chunk].location = (png_byte)location; +} +#endif + +#if defined(PNG_1_0_X) || defined(PNG_1_2_X) +#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED) || \ + defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) +void PNGAPI +png_permit_empty_plte (png_structp png_ptr, int empty_plte_permitted) +{ + /* This function is deprecated in favor of png_permit_mng_features() + and will be removed from libpng-1.3.0 */ + png_debug(1, "in png_permit_empty_plte, DEPRECATED.\n"); + if (png_ptr == NULL) + return; + png_ptr->mng_features_permitted = (png_byte) + ((png_ptr->mng_features_permitted & (~(PNG_FLAG_MNG_EMPTY_PLTE))) | + ((empty_plte_permitted & PNG_FLAG_MNG_EMPTY_PLTE))); +} +#endif +#endif + +#if defined(PNG_MNG_FEATURES_SUPPORTED) +png_uint_32 PNGAPI +png_permit_mng_features (png_structp png_ptr, png_uint_32 mng_features) +{ + png_debug(1, "in png_permit_mng_features\n"); + if (png_ptr == NULL) + return (png_uint_32)0; + png_ptr->mng_features_permitted = + (png_byte)(mng_features & PNG_ALL_MNG_FEATURES); + return (png_uint_32)png_ptr->mng_features_permitted; +} +#endif + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) +void PNGAPI +png_set_keep_unknown_chunks(png_structp png_ptr, int keep, png_bytep + chunk_list, int num_chunks) +{ + png_bytep new_list, p; + int i, old_num_chunks; + if (png_ptr == NULL) + return; + if (num_chunks == 0) + { + if(keep == PNG_HANDLE_CHUNK_ALWAYS || keep == PNG_HANDLE_CHUNK_IF_SAFE) + png_ptr->flags |= PNG_FLAG_KEEP_UNKNOWN_CHUNKS; + else + png_ptr->flags &= ~PNG_FLAG_KEEP_UNKNOWN_CHUNKS; + + if(keep == PNG_HANDLE_CHUNK_ALWAYS) + png_ptr->flags |= PNG_FLAG_KEEP_UNSAFE_CHUNKS; + else + png_ptr->flags &= ~PNG_FLAG_KEEP_UNSAFE_CHUNKS; + return; + } + if (chunk_list == NULL) + return; + old_num_chunks=png_ptr->num_chunk_list; + new_list=(png_bytep)png_malloc(png_ptr, + (png_uint_32)(5*(num_chunks+old_num_chunks))); + if(png_ptr->chunk_list != NULL) + { + png_memcpy(new_list, png_ptr->chunk_list, + (png_size_t)(5*old_num_chunks)); + png_free(png_ptr, png_ptr->chunk_list); + png_ptr->chunk_list=NULL; + } + png_memcpy(new_list+5*old_num_chunks, chunk_list, + (png_size_t)(5*num_chunks)); + for (p=new_list+5*old_num_chunks+4, i=0; inum_chunk_list=old_num_chunks+num_chunks; + png_ptr->chunk_list=new_list; +#ifdef PNG_FREE_ME_SUPPORTED + png_ptr->free_me |= PNG_FREE_LIST; +#endif +} +#endif + +#if defined(PNG_READ_USER_CHUNKS_SUPPORTED) +void PNGAPI +png_set_read_user_chunk_fn(png_structp png_ptr, png_voidp user_chunk_ptr, + png_user_chunk_ptr read_user_chunk_fn) +{ + png_debug(1, "in png_set_read_user_chunk_fn\n"); + if (png_ptr == NULL) + return; + png_ptr->read_user_chunk_fn = read_user_chunk_fn; + png_ptr->user_chunk_ptr = user_chunk_ptr; +} +#endif + +#if defined(PNG_INFO_IMAGE_SUPPORTED) +void PNGAPI +png_set_rows(png_structp png_ptr, png_infop info_ptr, png_bytepp row_pointers) +{ + png_debug1(1, "in %s storage function\n", "rows"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + if(info_ptr->row_pointers && (info_ptr->row_pointers != row_pointers)) + png_free_data(png_ptr, info_ptr, PNG_FREE_ROWS, 0); + info_ptr->row_pointers = row_pointers; + if(row_pointers) + info_ptr->valid |= PNG_INFO_IDAT; +} +#endif + +#ifdef PNG_WRITE_SUPPORTED +void PNGAPI +png_set_compression_buffer_size(png_structp png_ptr, png_uint_32 size) +{ + if (png_ptr == NULL) + return; + if(png_ptr->zbuf) + png_free(png_ptr, png_ptr->zbuf); + png_ptr->zbuf_size = (png_size_t)size; + png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, size); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; +} +#endif + +void PNGAPI +png_set_invalid(png_structp png_ptr, png_infop info_ptr, int mask) +{ + if (png_ptr && info_ptr) + info_ptr->valid &= ~(mask); +} + +#ifndef PNG_1_0_X +#ifdef PNG_ASSEMBLER_CODE_SUPPORTED +/* function was added to libpng 1.2.0 and should always exist by default */ +void PNGAPI +png_set_asm_flags (png_structp png_ptr, png_uint_32 asm_flags) +{ +/* Obsolete as of libpng-1.2.20 and will be removed from libpng-1.4.0 */ + if (png_ptr != NULL) + png_ptr->asm_flags = 0; +} + +/* this function was added to libpng 1.2.0 */ +void PNGAPI +png_set_mmx_thresholds (png_structp png_ptr, + png_byte mmx_bitdepth_threshold, + png_uint_32 mmx_rowbytes_threshold) +{ +/* Obsolete as of libpng-1.2.20 and will be removed from libpng-1.4.0 */ + if (png_ptr == NULL) + return; +} +#endif /* ?PNG_ASSEMBLER_CODE_SUPPORTED */ + +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +/* this function was added to libpng 1.2.6 */ +void PNGAPI +png_set_user_limits (png_structp png_ptr, png_uint_32 user_width_max, + png_uint_32 user_height_max) +{ + /* Images with dimensions larger than these limits will be + * rejected by png_set_IHDR(). To accept any PNG datastream + * regardless of dimensions, set both limits to 0x7ffffffL. + */ + if(png_ptr == NULL) return; + png_ptr->user_width_max = user_width_max; + png_ptr->user_height_max = user_height_max; +} +#endif /* ?PNG_SET_USER_LIMITS_SUPPORTED */ + +#endif /* ?PNG_1_0_X */ +#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ +/********* End of inlined file: pngset.c *********/ + +/********* Start of inlined file: pngtrans.c *********/ +/* pngtrans.c - transforms the data in a row (used by both readers and writers) + * + * Last changed in libpng 1.2.17 May 15, 2007 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2007 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + */ + +#define PNG_INTERNAL + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* turn on BGR-to-RGB mapping */ +void PNGAPI +png_set_bgr(png_structp png_ptr) +{ + png_debug(1, "in png_set_bgr\n"); + if(png_ptr == NULL) return; + png_ptr->transformations |= PNG_BGR; +} +#endif + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* turn on 16 bit byte swapping */ +void PNGAPI +png_set_swap(png_structp png_ptr) +{ + png_debug(1, "in png_set_swap\n"); + if(png_ptr == NULL) return; + if (png_ptr->bit_depth == 16) + png_ptr->transformations |= PNG_SWAP_BYTES; +} +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) +/* turn on pixel packing */ +void PNGAPI +png_set_packing(png_structp png_ptr) +{ + png_debug(1, "in png_set_packing\n"); + if(png_ptr == NULL) return; + if (png_ptr->bit_depth < 8) + { + png_ptr->transformations |= PNG_PACK; + png_ptr->usr_bit_depth = 8; + } +} +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED) +/* turn on packed pixel swapping */ +void PNGAPI +png_set_packswap(png_structp png_ptr) +{ + png_debug(1, "in png_set_packswap\n"); + if(png_ptr == NULL) return; + if (png_ptr->bit_depth < 8) + png_ptr->transformations |= PNG_PACKSWAP; +} +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) +void PNGAPI +png_set_shift(png_structp png_ptr, png_color_8p true_bits) +{ + png_debug(1, "in png_set_shift\n"); + if(png_ptr == NULL) return; + png_ptr->transformations |= PNG_SHIFT; + png_ptr->shift = *true_bits; +} +#endif + +#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ + defined(PNG_WRITE_INTERLACING_SUPPORTED) +int PNGAPI +png_set_interlace_handling(png_structp png_ptr) +{ + png_debug(1, "in png_set_interlace handling\n"); + if (png_ptr && png_ptr->interlaced) + { + png_ptr->transformations |= PNG_INTERLACE; + return (7); + } + + return (1); +} +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +/* Add a filler byte on read, or remove a filler or alpha byte on write. + * The filler type has changed in v0.95 to allow future 2-byte fillers + * for 48-bit input data, as well as to avoid problems with some compilers + * that don't like bytes as parameters. + */ +void PNGAPI +png_set_filler(png_structp png_ptr, png_uint_32 filler, int filler_loc) +{ + png_debug(1, "in png_set_filler\n"); + if(png_ptr == NULL) return; + png_ptr->transformations |= PNG_FILLER; + png_ptr->filler = (png_byte)filler; + if (filler_loc == PNG_FILLER_AFTER) + png_ptr->flags |= PNG_FLAG_FILLER_AFTER; + else + png_ptr->flags &= ~PNG_FLAG_FILLER_AFTER; + + /* This should probably go in the "do_read_filler" routine. + * I attempted to do that in libpng-1.0.1a but that caused problems + * so I restored it in libpng-1.0.2a + */ + + if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) + { + png_ptr->usr_channels = 4; + } + + /* Also I added this in libpng-1.0.2a (what happens when we expand + * a less-than-8-bit grayscale to GA? */ + + if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY && png_ptr->bit_depth >= 8) + { + png_ptr->usr_channels = 2; + } +} + +#if !defined(PNG_1_0_X) +/* Added to libpng-1.2.7 */ +void PNGAPI +png_set_add_alpha(png_structp png_ptr, png_uint_32 filler, int filler_loc) +{ + png_debug(1, "in png_set_add_alpha\n"); + if(png_ptr == NULL) return; + png_set_filler(png_ptr, filler, filler_loc); + png_ptr->transformations |= PNG_ADD_ALPHA; +} +#endif + +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +void PNGAPI +png_set_swap_alpha(png_structp png_ptr) +{ + png_debug(1, "in png_set_swap_alpha\n"); + if(png_ptr == NULL) return; + png_ptr->transformations |= PNG_SWAP_ALPHA; +} +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +void PNGAPI +png_set_invert_alpha(png_structp png_ptr) +{ + png_debug(1, "in png_set_invert_alpha\n"); + if(png_ptr == NULL) return; + png_ptr->transformations |= PNG_INVERT_ALPHA; +} +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +void PNGAPI +png_set_invert_mono(png_structp png_ptr) +{ + png_debug(1, "in png_set_invert_mono\n"); + if(png_ptr == NULL) return; + png_ptr->transformations |= PNG_INVERT_MONO; +} + +/* invert monochrome grayscale data */ +void /* PRIVATE */ +png_do_invert(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_invert\n"); + /* This test removed from libpng version 1.0.13 and 1.2.0: + * if (row_info->bit_depth == 1 && + */ +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row == NULL || row_info == NULL) + return; +#endif + if (row_info->color_type == PNG_COLOR_TYPE_GRAY) + { + png_bytep rp = row; + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + + for (i = 0; i < istop; i++) + { + *rp = (png_byte)(~(*rp)); + rp++; + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && + row_info->bit_depth == 8) + { + png_bytep rp = row; + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + + for (i = 0; i < istop; i+=2) + { + *rp = (png_byte)(~(*rp)); + rp+=2; + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && + row_info->bit_depth == 16) + { + png_bytep rp = row; + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + + for (i = 0; i < istop; i+=4) + { + *rp = (png_byte)(~(*rp)); + *(rp+1) = (png_byte)(~(*(rp+1))); + rp+=4; + } + } +} +#endif + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* swaps byte order on 16 bit depth images */ +void /* PRIVATE */ +png_do_swap(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_swap\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + row_info->bit_depth == 16) + { + png_bytep rp = row; + png_uint_32 i; + png_uint_32 istop= row_info->width * row_info->channels; + + for (i = 0; i < istop; i++, rp += 2) + { + png_byte t = *rp; + *rp = *(rp + 1); + *(rp + 1) = t; + } + } +} +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED) +static PNG_CONST png_byte onebppswaptable[256] = { + 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, + 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, + 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, + 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, + 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, + 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, + 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, + 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, + 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, + 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, + 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, + 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, + 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, + 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, + 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, + 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, + 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, + 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, + 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, + 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, + 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, + 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, + 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, + 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, + 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, + 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, + 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, + 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, + 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, + 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, + 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, + 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF +}; + +static PNG_CONST png_byte twobppswaptable[256] = { + 0x00, 0x40, 0x80, 0xC0, 0x10, 0x50, 0x90, 0xD0, + 0x20, 0x60, 0xA0, 0xE0, 0x30, 0x70, 0xB0, 0xF0, + 0x04, 0x44, 0x84, 0xC4, 0x14, 0x54, 0x94, 0xD4, + 0x24, 0x64, 0xA4, 0xE4, 0x34, 0x74, 0xB4, 0xF4, + 0x08, 0x48, 0x88, 0xC8, 0x18, 0x58, 0x98, 0xD8, + 0x28, 0x68, 0xA8, 0xE8, 0x38, 0x78, 0xB8, 0xF8, + 0x0C, 0x4C, 0x8C, 0xCC, 0x1C, 0x5C, 0x9C, 0xDC, + 0x2C, 0x6C, 0xAC, 0xEC, 0x3C, 0x7C, 0xBC, 0xFC, + 0x01, 0x41, 0x81, 0xC1, 0x11, 0x51, 0x91, 0xD1, + 0x21, 0x61, 0xA1, 0xE1, 0x31, 0x71, 0xB1, 0xF1, + 0x05, 0x45, 0x85, 0xC5, 0x15, 0x55, 0x95, 0xD5, + 0x25, 0x65, 0xA5, 0xE5, 0x35, 0x75, 0xB5, 0xF5, + 0x09, 0x49, 0x89, 0xC9, 0x19, 0x59, 0x99, 0xD9, + 0x29, 0x69, 0xA9, 0xE9, 0x39, 0x79, 0xB9, 0xF9, + 0x0D, 0x4D, 0x8D, 0xCD, 0x1D, 0x5D, 0x9D, 0xDD, + 0x2D, 0x6D, 0xAD, 0xED, 0x3D, 0x7D, 0xBD, 0xFD, + 0x02, 0x42, 0x82, 0xC2, 0x12, 0x52, 0x92, 0xD2, + 0x22, 0x62, 0xA2, 0xE2, 0x32, 0x72, 0xB2, 0xF2, + 0x06, 0x46, 0x86, 0xC6, 0x16, 0x56, 0x96, 0xD6, + 0x26, 0x66, 0xA6, 0xE6, 0x36, 0x76, 0xB6, 0xF6, + 0x0A, 0x4A, 0x8A, 0xCA, 0x1A, 0x5A, 0x9A, 0xDA, + 0x2A, 0x6A, 0xAA, 0xEA, 0x3A, 0x7A, 0xBA, 0xFA, + 0x0E, 0x4E, 0x8E, 0xCE, 0x1E, 0x5E, 0x9E, 0xDE, + 0x2E, 0x6E, 0xAE, 0xEE, 0x3E, 0x7E, 0xBE, 0xFE, + 0x03, 0x43, 0x83, 0xC3, 0x13, 0x53, 0x93, 0xD3, + 0x23, 0x63, 0xA3, 0xE3, 0x33, 0x73, 0xB3, 0xF3, + 0x07, 0x47, 0x87, 0xC7, 0x17, 0x57, 0x97, 0xD7, + 0x27, 0x67, 0xA7, 0xE7, 0x37, 0x77, 0xB7, 0xF7, + 0x0B, 0x4B, 0x8B, 0xCB, 0x1B, 0x5B, 0x9B, 0xDB, + 0x2B, 0x6B, 0xAB, 0xEB, 0x3B, 0x7B, 0xBB, 0xFB, + 0x0F, 0x4F, 0x8F, 0xCF, 0x1F, 0x5F, 0x9F, 0xDF, + 0x2F, 0x6F, 0xAF, 0xEF, 0x3F, 0x7F, 0xBF, 0xFF +}; + +static PNG_CONST png_byte fourbppswaptable[256] = { + 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, + 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0, + 0x01, 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71, + 0x81, 0x91, 0xA1, 0xB1, 0xC1, 0xD1, 0xE1, 0xF1, + 0x02, 0x12, 0x22, 0x32, 0x42, 0x52, 0x62, 0x72, + 0x82, 0x92, 0xA2, 0xB2, 0xC2, 0xD2, 0xE2, 0xF2, + 0x03, 0x13, 0x23, 0x33, 0x43, 0x53, 0x63, 0x73, + 0x83, 0x93, 0xA3, 0xB3, 0xC3, 0xD3, 0xE3, 0xF3, + 0x04, 0x14, 0x24, 0x34, 0x44, 0x54, 0x64, 0x74, + 0x84, 0x94, 0xA4, 0xB4, 0xC4, 0xD4, 0xE4, 0xF4, + 0x05, 0x15, 0x25, 0x35, 0x45, 0x55, 0x65, 0x75, + 0x85, 0x95, 0xA5, 0xB5, 0xC5, 0xD5, 0xE5, 0xF5, + 0x06, 0x16, 0x26, 0x36, 0x46, 0x56, 0x66, 0x76, + 0x86, 0x96, 0xA6, 0xB6, 0xC6, 0xD6, 0xE6, 0xF6, + 0x07, 0x17, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77, + 0x87, 0x97, 0xA7, 0xB7, 0xC7, 0xD7, 0xE7, 0xF7, + 0x08, 0x18, 0x28, 0x38, 0x48, 0x58, 0x68, 0x78, + 0x88, 0x98, 0xA8, 0xB8, 0xC8, 0xD8, 0xE8, 0xF8, + 0x09, 0x19, 0x29, 0x39, 0x49, 0x59, 0x69, 0x79, + 0x89, 0x99, 0xA9, 0xB9, 0xC9, 0xD9, 0xE9, 0xF9, + 0x0A, 0x1A, 0x2A, 0x3A, 0x4A, 0x5A, 0x6A, 0x7A, + 0x8A, 0x9A, 0xAA, 0xBA, 0xCA, 0xDA, 0xEA, 0xFA, + 0x0B, 0x1B, 0x2B, 0x3B, 0x4B, 0x5B, 0x6B, 0x7B, + 0x8B, 0x9B, 0xAB, 0xBB, 0xCB, 0xDB, 0xEB, 0xFB, + 0x0C, 0x1C, 0x2C, 0x3C, 0x4C, 0x5C, 0x6C, 0x7C, + 0x8C, 0x9C, 0xAC, 0xBC, 0xCC, 0xDC, 0xEC, 0xFC, + 0x0D, 0x1D, 0x2D, 0x3D, 0x4D, 0x5D, 0x6D, 0x7D, + 0x8D, 0x9D, 0xAD, 0xBD, 0xCD, 0xDD, 0xED, 0xFD, + 0x0E, 0x1E, 0x2E, 0x3E, 0x4E, 0x5E, 0x6E, 0x7E, + 0x8E, 0x9E, 0xAE, 0xBE, 0xCE, 0xDE, 0xEE, 0xFE, + 0x0F, 0x1F, 0x2F, 0x3F, 0x4F, 0x5F, 0x6F, 0x7F, + 0x8F, 0x9F, 0xAF, 0xBF, 0xCF, 0xDF, 0xEF, 0xFF +}; + +/* swaps pixel packing order within bytes */ +void /* PRIVATE */ +png_do_packswap(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_packswap\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + row_info->bit_depth < 8) + { + png_bytep rp, end, table; + + end = row + row_info->rowbytes; + + if (row_info->bit_depth == 1) + table = (png_bytep)onebppswaptable; + else if (row_info->bit_depth == 2) + table = (png_bytep)twobppswaptable; + else if (row_info->bit_depth == 4) + table = (png_bytep)fourbppswaptable; + else + return; + + for (rp = row; rp < end; rp++) + *rp = table[*rp]; + } +} +#endif /* PNG_READ_PACKSWAP_SUPPORTED or PNG_WRITE_PACKSWAP_SUPPORTED */ + +#if defined(PNG_WRITE_FILLER_SUPPORTED) || \ + defined(PNG_READ_STRIP_ALPHA_SUPPORTED) +/* remove filler or alpha byte(s) */ +void /* PRIVATE */ +png_do_strip_filler(png_row_infop row_info, png_bytep row, png_uint_32 flags) +{ + png_debug(1, "in png_do_strip_filler\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL) +#endif + { + png_bytep sp=row; + png_bytep dp=row; + png_uint_32 row_width=row_info->width; + png_uint_32 i; + + if ((row_info->color_type == PNG_COLOR_TYPE_RGB || + (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA && + (flags & PNG_FLAG_STRIP_ALPHA))) && + row_info->channels == 4) + { + if (row_info->bit_depth == 8) + { + /* This converts from RGBX or RGBA to RGB */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + dp+=3; sp+=4; + for (i = 1; i < row_width; i++) + { + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + sp++; + } + } + /* This converts from XRGB or ARGB to RGB */ + else + { + for (i = 0; i < row_width; i++) + { + sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + } + } + row_info->pixel_depth = 24; + row_info->rowbytes = row_width * 3; + } + else /* if (row_info->bit_depth == 16) */ + { + if (flags & PNG_FLAG_FILLER_AFTER) + { + /* This converts from RRGGBBXX or RRGGBBAA to RRGGBB */ + sp += 8; dp += 6; + for (i = 1; i < row_width; i++) + { + /* This could be (although png_memcpy is probably slower): + png_memcpy(dp, sp, 6); + sp += 8; + dp += 6; + */ + + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + sp += 2; + } + } + else + { + /* This converts from XXRRGGBB or AARRGGBB to RRGGBB */ + for (i = 0; i < row_width; i++) + { + /* This could be (although png_memcpy is probably slower): + png_memcpy(dp, sp, 6); + sp += 8; + dp += 6; + */ + + sp+=2; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + } + } + row_info->pixel_depth = 48; + row_info->rowbytes = row_width * 6; + } + row_info->channels = 3; + } + else if ((row_info->color_type == PNG_COLOR_TYPE_GRAY || + (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && + (flags & PNG_FLAG_STRIP_ALPHA))) && + row_info->channels == 2) + { + if (row_info->bit_depth == 8) + { + /* This converts from GX or GA to G */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + for (i = 0; i < row_width; i++) + { + *dp++ = *sp++; + sp++; + } + } + /* This converts from XG or AG to G */ + else + { + for (i = 0; i < row_width; i++) + { + sp++; + *dp++ = *sp++; + } + } + row_info->pixel_depth = 8; + row_info->rowbytes = row_width; + } + else /* if (row_info->bit_depth == 16) */ + { + if (flags & PNG_FLAG_FILLER_AFTER) + { + /* This converts from GGXX or GGAA to GG */ + sp += 4; dp += 2; + for (i = 1; i < row_width; i++) + { + *dp++ = *sp++; + *dp++ = *sp++; + sp += 2; + } + } + else + { + /* This converts from XXGG or AAGG to GG */ + for (i = 0; i < row_width; i++) + { + sp += 2; + *dp++ = *sp++; + *dp++ = *sp++; + } + } + row_info->pixel_depth = 16; + row_info->rowbytes = row_width * 2; + } + row_info->channels = 1; + } + if (flags & PNG_FLAG_STRIP_ALPHA) + row_info->color_type &= ~PNG_COLOR_MASK_ALPHA; + } +} +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* swaps red and blue bytes within a pixel */ +void /* PRIVATE */ +png_do_bgr(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_bgr\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + (row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + png_uint_32 row_width = row_info->width; + if (row_info->bit_depth == 8) + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 3) + { + png_byte save = *rp; + *rp = *(rp + 2); + *(rp + 2) = save; + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 4) + { + png_byte save = *rp; + *rp = *(rp + 2); + *(rp + 2) = save; + } + } + } + else if (row_info->bit_depth == 16) + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 6) + { + png_byte save = *rp; + *rp = *(rp + 4); + *(rp + 4) = save; + save = *(rp + 1); + *(rp + 1) = *(rp + 5); + *(rp + 5) = save; + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 8) + { + png_byte save = *rp; + *rp = *(rp + 4); + *(rp + 4) = save; + save = *(rp + 1); + *(rp + 1) = *(rp + 5); + *(rp + 5) = save; + } + } + } + } +} +#endif /* PNG_READ_BGR_SUPPORTED or PNG_WRITE_BGR_SUPPORTED */ + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +void PNGAPI +png_set_user_transform_info(png_structp png_ptr, png_voidp + user_transform_ptr, int user_transform_depth, int user_transform_channels) +{ + png_debug(1, "in png_set_user_transform_info\n"); + if(png_ptr == NULL) return; +#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) + png_ptr->user_transform_ptr = user_transform_ptr; + png_ptr->user_transform_depth = (png_byte)user_transform_depth; + png_ptr->user_transform_channels = (png_byte)user_transform_channels; +#else + if(user_transform_ptr || user_transform_depth || user_transform_channels) + png_warning(png_ptr, + "This version of libpng does not support user transform info"); +#endif +} +#endif + +/* This function returns a pointer to the user_transform_ptr associated with + * the user transform functions. The application should free any memory + * associated with this pointer before png_write_destroy and png_read_destroy + * are called. + */ +png_voidp PNGAPI +png_get_user_transform_ptr(png_structp png_ptr) +{ +#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) + if (png_ptr == NULL) return (NULL); + return ((png_voidp)png_ptr->user_transform_ptr); +#else + return (NULL); +#endif +} +#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ +/********* End of inlined file: pngtrans.c *********/ + +/********* Start of inlined file: pngwio.c *********/ +/* pngwio.c - functions for data output + * + * Last changed in libpng 1.2.13 November 13, 2006 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2006 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This file provides a location for all output. Users who need + * special handling are expected to write functions that have the same + * arguments as these and perform similar functions, but that possibly + * use different output methods. Note that you shouldn't change these + * functions, but rather write replacement functions and then change + * them at run time with png_set_write_fn(...). + */ + +#define PNG_INTERNAL + +#ifdef PNG_WRITE_SUPPORTED + +/* Write the data to whatever output you are using. The default routine + writes to a file pointer. Note that this routine sometimes gets called + with very small lengths, so you should implement some kind of simple + buffering if you are using unbuffered writes. This should never be asked + to write more than 64K on a 16 bit machine. */ + +void /* PRIVATE */ +png_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + if (png_ptr->write_data_fn != NULL ) + (*(png_ptr->write_data_fn))(png_ptr, data, length); + else + png_error(png_ptr, "Call to NULL write function"); +} + +#if !defined(PNG_NO_STDIO) +/* This is the function that does the actual writing of data. If you are + not writing to a standard C stream, you should create a replacement + write_data function and use it at run time with png_set_write_fn(), rather + than changing the library. */ +#ifndef USE_FAR_KEYWORD +void PNGAPI +png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_uint_32 check; + + if(png_ptr == NULL) return; +#if defined(_WIN32_WCE) + if ( !WriteFile((HANDLE)(png_ptr->io_ptr), data, length, &check, NULL) ) + check = 0; +#else + check = fwrite(data, 1, length, (png_FILE_p)(png_ptr->io_ptr)); +#endif + if (check != length) + png_error(png_ptr, "Write Error"); +} +#else +/* this is the model-independent version. Since the standard I/O library + can't handle far buffers in the medium and small models, we have to copy + the data. +*/ + +#define NEAR_BUF_SIZE 1024 +#define MIN(a,b) (a <= b ? a : b) + +void PNGAPI +png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_uint_32 check; + png_byte *near_data; /* Needs to be "png_byte *" instead of "png_bytep" */ + png_FILE_p io_ptr; + + if(png_ptr == NULL) return; + /* Check if data really is near. If so, use usual code. */ + near_data = (png_byte *)CVT_PTR_NOCHECK(data); + io_ptr = (png_FILE_p)CVT_PTR(png_ptr->io_ptr); + if ((png_bytep)near_data == data) + { +#if defined(_WIN32_WCE) + if ( !WriteFile(io_ptr, near_data, length, &check, NULL) ) + check = 0; +#else + check = fwrite(near_data, 1, length, io_ptr); +#endif + } + else + { + png_byte buf[NEAR_BUF_SIZE]; + png_size_t written, remaining, err; + check = 0; + remaining = length; + do + { + written = MIN(NEAR_BUF_SIZE, remaining); + png_memcpy(buf, data, written); /* copy far buffer to near buffer */ +#if defined(_WIN32_WCE) + if ( !WriteFile(io_ptr, buf, written, &err, NULL) ) + err = 0; +#else + err = fwrite(buf, 1, written, io_ptr); +#endif + if (err != written) + break; + else + check += err; + data += written; + remaining -= written; + } + while (remaining != 0); + } + if (check != length) + png_error(png_ptr, "Write Error"); +} + +#endif +#endif + +/* This function is called to output any data pending writing (normally + to disk). After png_flush is called, there should be no data pending + writing in any buffers. */ +#if defined(PNG_WRITE_FLUSH_SUPPORTED) +void /* PRIVATE */ +png_flush(png_structp png_ptr) +{ + if (png_ptr->output_flush_fn != NULL) + (*(png_ptr->output_flush_fn))(png_ptr); +} + +#if !defined(PNG_NO_STDIO) +void PNGAPI +png_default_flush(png_structp png_ptr) +{ +#if !defined(_WIN32_WCE) + png_FILE_p io_ptr; +#endif + if(png_ptr == NULL) return; +#if !defined(_WIN32_WCE) + io_ptr = (png_FILE_p)CVT_PTR((png_ptr->io_ptr)); + if (io_ptr != NULL) + fflush(io_ptr); +#endif +} +#endif +#endif + +/* This function allows the application to supply new output functions for + libpng if standard C streams aren't being used. + + This function takes as its arguments: + png_ptr - pointer to a png output data structure + io_ptr - pointer to user supplied structure containing info about + the output functions. May be NULL. + write_data_fn - pointer to a new output function that takes as its + arguments a pointer to a png_struct, a pointer to + data to be written, and a 32-bit unsigned int that is + the number of bytes to be written. The new write + function should call png_error(png_ptr, "Error msg") + to exit and output any fatal error messages. + flush_data_fn - pointer to a new flush function that takes as its + arguments a pointer to a png_struct. After a call to + the flush function, there should be no data in any buffers + or pending transmission. If the output method doesn't do + any buffering of ouput, a function prototype must still be + supplied although it doesn't have to do anything. If + PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile + time, output_flush_fn will be ignored, although it must be + supplied for compatibility. */ +void PNGAPI +png_set_write_fn(png_structp png_ptr, png_voidp io_ptr, + png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn) +{ + if(png_ptr == NULL) return; + png_ptr->io_ptr = io_ptr; + +#if !defined(PNG_NO_STDIO) + if (write_data_fn != NULL) + png_ptr->write_data_fn = write_data_fn; + else + png_ptr->write_data_fn = png_default_write_data; +#else + png_ptr->write_data_fn = write_data_fn; +#endif + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) +#if !defined(PNG_NO_STDIO) + if (output_flush_fn != NULL) + png_ptr->output_flush_fn = output_flush_fn; + else + png_ptr->output_flush_fn = png_default_flush; +#else + png_ptr->output_flush_fn = output_flush_fn; +#endif +#endif /* PNG_WRITE_FLUSH_SUPPORTED */ + + /* It is an error to read while writing a png file */ + if (png_ptr->read_data_fn != NULL) + { + png_ptr->read_data_fn = NULL; + png_warning(png_ptr, + "Attempted to set both read_data_fn and write_data_fn in"); + png_warning(png_ptr, + "the same structure. Resetting read_data_fn to NULL."); + } +} + +#if defined(USE_FAR_KEYWORD) +#if defined(_MSC_VER) +void *png_far_to_near(png_structp png_ptr,png_voidp ptr, int check) +{ + void *near_ptr; + void FAR *far_ptr; + FP_OFF(near_ptr) = FP_OFF(ptr); + far_ptr = (void FAR *)near_ptr; + if(check != 0) + if(FP_SEG(ptr) != FP_SEG(far_ptr)) + png_error(png_ptr,"segment lost in conversion"); + return(near_ptr); +} +# else +void *png_far_to_near(png_structp png_ptr,png_voidp ptr, int check) +{ + void *near_ptr; + void FAR *far_ptr; + near_ptr = (void FAR *)ptr; + far_ptr = (void FAR *)near_ptr; + if(check != 0) + if(far_ptr != ptr) + png_error(png_ptr,"segment lost in conversion"); + return(near_ptr); +} +# endif +# endif +#endif /* PNG_WRITE_SUPPORTED */ +/********* End of inlined file: pngwio.c *********/ + +/********* Start of inlined file: pngwrite.c *********/ +/* pngwrite.c - general routines to write a PNG file + * + * Last changed in libpng 1.2.15 January 5, 2007 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2007 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + */ + +/* get internal access to png.h */ +#define PNG_INTERNAL + +#ifdef PNG_WRITE_SUPPORTED + +/* Writes all the PNG information. This is the suggested way to use the + * library. If you have a new chunk to add, make a function to write it, + * and put it in the correct location here. If you want the chunk written + * after the image data, put it in png_write_end(). I strongly encourage + * you to supply a PNG_INFO_ flag, and check info_ptr->valid before writing + * the chunk, as that will keep the code from breaking if you want to just + * write a plain PNG file. If you have long comments, I suggest writing + * them in png_write_end(), and compressing them. + */ +void PNGAPI +png_write_info_before_PLTE(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_write_info_before_PLTE\n"); + if (png_ptr == NULL || info_ptr == NULL) + return; + if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE)) + { + png_write_sig(png_ptr); /* write PNG signature */ +#if defined(PNG_MNG_FEATURES_SUPPORTED) + if((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE)&&(png_ptr->mng_features_permitted)) + { + png_warning(png_ptr,"MNG features are not allowed in a PNG datastream"); + png_ptr->mng_features_permitted=0; + } +#endif + /* write IHDR information. */ + png_write_IHDR(png_ptr, info_ptr->width, info_ptr->height, + info_ptr->bit_depth, info_ptr->color_type, info_ptr->compression_type, + info_ptr->filter_type, +#if defined(PNG_WRITE_INTERLACING_SUPPORTED) + info_ptr->interlace_type); +#else + 0); +#endif + /* the rest of these check to see if the valid field has the appropriate + flag set, and if it does, writes the chunk. */ +#if defined(PNG_WRITE_gAMA_SUPPORTED) + if (info_ptr->valid & PNG_INFO_gAMA) + { +# ifdef PNG_FLOATING_POINT_SUPPORTED + png_write_gAMA(png_ptr, info_ptr->gamma); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + png_write_gAMA_fixed(png_ptr, info_ptr->int_gamma); +# endif +#endif + } +#endif +#if defined(PNG_WRITE_sRGB_SUPPORTED) + if (info_ptr->valid & PNG_INFO_sRGB) + png_write_sRGB(png_ptr, (int)info_ptr->srgb_intent); +#endif +#if defined(PNG_WRITE_iCCP_SUPPORTED) + if (info_ptr->valid & PNG_INFO_iCCP) + png_write_iCCP(png_ptr, info_ptr->iccp_name, PNG_COMPRESSION_TYPE_BASE, + info_ptr->iccp_profile, (int)info_ptr->iccp_proflen); +#endif +#if defined(PNG_WRITE_sBIT_SUPPORTED) + if (info_ptr->valid & PNG_INFO_sBIT) + png_write_sBIT(png_ptr, &(info_ptr->sig_bit), info_ptr->color_type); +#endif +#if defined(PNG_WRITE_cHRM_SUPPORTED) + if (info_ptr->valid & PNG_INFO_cHRM) + { +#ifdef PNG_FLOATING_POINT_SUPPORTED + png_write_cHRM(png_ptr, + info_ptr->x_white, info_ptr->y_white, + info_ptr->x_red, info_ptr->y_red, + info_ptr->x_green, info_ptr->y_green, + info_ptr->x_blue, info_ptr->y_blue); +#else +# ifdef PNG_FIXED_POINT_SUPPORTED + png_write_cHRM_fixed(png_ptr, + info_ptr->int_x_white, info_ptr->int_y_white, + info_ptr->int_x_red, info_ptr->int_y_red, + info_ptr->int_x_green, info_ptr->int_y_green, + info_ptr->int_x_blue, info_ptr->int_y_blue); +# endif +#endif + } +#endif +#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) + if (info_ptr->unknown_chunks_num) + { + png_unknown_chunk *up; + + png_debug(5, "writing extra chunks\n"); + + for (up = info_ptr->unknown_chunks; + up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num; + up++) + { + int keep=png_handle_as_unknown(png_ptr, up->name); + if (keep != PNG_HANDLE_CHUNK_NEVER && + up->location && !(up->location & PNG_HAVE_PLTE) && + !(up->location & PNG_HAVE_IDAT) && + ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS || + (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS))) + { + png_write_chunk(png_ptr, up->name, up->data, up->size); + } + } + } +#endif + png_ptr->mode |= PNG_WROTE_INFO_BEFORE_PLTE; + } +} + +void PNGAPI +png_write_info(png_structp png_ptr, png_infop info_ptr) +{ +#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED) + int i; +#endif + + png_debug(1, "in png_write_info\n"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + png_write_info_before_PLTE(png_ptr, info_ptr); + + if (info_ptr->valid & PNG_INFO_PLTE) + png_write_PLTE(png_ptr, info_ptr->palette, + (png_uint_32)info_ptr->num_palette); + else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + png_error(png_ptr, "Valid palette required for paletted images"); + +#if defined(PNG_WRITE_tRNS_SUPPORTED) + if (info_ptr->valid & PNG_INFO_tRNS) + { +#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) + /* invert the alpha channel (in tRNS) */ + if ((png_ptr->transformations & PNG_INVERT_ALPHA) && + info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + int j; + for (j=0; j<(int)info_ptr->num_trans; j++) + info_ptr->trans[j] = (png_byte)(255 - info_ptr->trans[j]); + } +#endif + png_write_tRNS(png_ptr, info_ptr->trans, &(info_ptr->trans_values), + info_ptr->num_trans, info_ptr->color_type); + } +#endif +#if defined(PNG_WRITE_bKGD_SUPPORTED) + if (info_ptr->valid & PNG_INFO_bKGD) + png_write_bKGD(png_ptr, &(info_ptr->background), info_ptr->color_type); +#endif +#if defined(PNG_WRITE_hIST_SUPPORTED) + if (info_ptr->valid & PNG_INFO_hIST) + png_write_hIST(png_ptr, info_ptr->hist, info_ptr->num_palette); +#endif +#if defined(PNG_WRITE_oFFs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_oFFs) + png_write_oFFs(png_ptr, info_ptr->x_offset, info_ptr->y_offset, + info_ptr->offset_unit_type); +#endif +#if defined(PNG_WRITE_pCAL_SUPPORTED) + if (info_ptr->valid & PNG_INFO_pCAL) + png_write_pCAL(png_ptr, info_ptr->pcal_purpose, info_ptr->pcal_X0, + info_ptr->pcal_X1, info_ptr->pcal_type, info_ptr->pcal_nparams, + info_ptr->pcal_units, info_ptr->pcal_params); +#endif +#if defined(PNG_WRITE_sCAL_SUPPORTED) + if (info_ptr->valid & PNG_INFO_sCAL) +#if defined(PNG_FLOATING_POINT_SUPPORTED) && !defined(PNG_NO_STDIO) + png_write_sCAL(png_ptr, (int)info_ptr->scal_unit, + info_ptr->scal_pixel_width, info_ptr->scal_pixel_height); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + png_write_sCAL_s(png_ptr, (int)info_ptr->scal_unit, + info_ptr->scal_s_width, info_ptr->scal_s_height); +#else + png_warning(png_ptr, + "png_write_sCAL not supported; sCAL chunk not written."); +#endif +#endif +#endif +#if defined(PNG_WRITE_pHYs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_pHYs) + png_write_pHYs(png_ptr, info_ptr->x_pixels_per_unit, + info_ptr->y_pixels_per_unit, info_ptr->phys_unit_type); +#endif +#if defined(PNG_WRITE_tIME_SUPPORTED) + if (info_ptr->valid & PNG_INFO_tIME) + { + png_write_tIME(png_ptr, &(info_ptr->mod_time)); + png_ptr->mode |= PNG_WROTE_tIME; + } +#endif +#if defined(PNG_WRITE_sPLT_SUPPORTED) + if (info_ptr->valid & PNG_INFO_sPLT) + for (i = 0; i < (int)info_ptr->splt_palettes_num; i++) + png_write_sPLT(png_ptr, info_ptr->splt_palettes + i); +#endif +#if defined(PNG_WRITE_TEXT_SUPPORTED) + /* Check to see if we need to write text chunks */ + for (i = 0; i < info_ptr->num_text; i++) + { + png_debug2(2, "Writing header text chunk %d, type %d\n", i, + info_ptr->text[i].compression); + /* an internationalized chunk? */ + if (info_ptr->text[i].compression > 0) + { +#if defined(PNG_WRITE_iTXt_SUPPORTED) + /* write international chunk */ + png_write_iTXt(png_ptr, + info_ptr->text[i].compression, + info_ptr->text[i].key, + info_ptr->text[i].lang, + info_ptr->text[i].lang_key, + info_ptr->text[i].text); +#else + png_warning(png_ptr, "Unable to write international text"); +#endif + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; + } + /* If we want a compressed text chunk */ + else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_zTXt) + { +#if defined(PNG_WRITE_zTXt_SUPPORTED) + /* write compressed chunk */ + png_write_zTXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, 0, + info_ptr->text[i].compression); +#else + png_warning(png_ptr, "Unable to write compressed text"); +#endif + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR; + } + else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE) + { +#if defined(PNG_WRITE_tEXt_SUPPORTED) + /* write uncompressed chunk */ + png_write_tEXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, + 0); +#else + png_warning(png_ptr, "Unable to write uncompressed text"); +#endif + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; + } + } +#endif +#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) + if (info_ptr->unknown_chunks_num) + { + png_unknown_chunk *up; + + png_debug(5, "writing extra chunks\n"); + + for (up = info_ptr->unknown_chunks; + up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num; + up++) + { + int keep=png_handle_as_unknown(png_ptr, up->name); + if (keep != PNG_HANDLE_CHUNK_NEVER && + up->location && (up->location & PNG_HAVE_PLTE) && + !(up->location & PNG_HAVE_IDAT) && + ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS || + (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS))) + { + png_write_chunk(png_ptr, up->name, up->data, up->size); + } + } + } +#endif +} + +/* Writes the end of the PNG file. If you don't want to write comments or + * time information, you can pass NULL for info. If you already wrote these + * in png_write_info(), do not write them again here. If you have long + * comments, I suggest writing them here, and compressing them. + */ +void PNGAPI +png_write_end(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_write_end\n"); + if (png_ptr == NULL) + return; + if (!(png_ptr->mode & PNG_HAVE_IDAT)) + png_error(png_ptr, "No IDATs written into file"); + + /* see if user wants us to write information chunks */ + if (info_ptr != NULL) + { +#if defined(PNG_WRITE_TEXT_SUPPORTED) + int i; /* local index variable */ +#endif +#if defined(PNG_WRITE_tIME_SUPPORTED) + /* check to see if user has supplied a time chunk */ + if ((info_ptr->valid & PNG_INFO_tIME) && + !(png_ptr->mode & PNG_WROTE_tIME)) + png_write_tIME(png_ptr, &(info_ptr->mod_time)); +#endif +#if defined(PNG_WRITE_TEXT_SUPPORTED) + /* loop through comment chunks */ + for (i = 0; i < info_ptr->num_text; i++) + { + png_debug2(2, "Writing trailer text chunk %d, type %d\n", i, + info_ptr->text[i].compression); + /* an internationalized chunk? */ + if (info_ptr->text[i].compression > 0) + { +#if defined(PNG_WRITE_iTXt_SUPPORTED) + /* write international chunk */ + png_write_iTXt(png_ptr, + info_ptr->text[i].compression, + info_ptr->text[i].key, + info_ptr->text[i].lang, + info_ptr->text[i].lang_key, + info_ptr->text[i].text); +#else + png_warning(png_ptr, "Unable to write international text"); +#endif + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; + } + else if (info_ptr->text[i].compression >= PNG_TEXT_COMPRESSION_zTXt) + { +#if defined(PNG_WRITE_zTXt_SUPPORTED) + /* write compressed chunk */ + png_write_zTXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, 0, + info_ptr->text[i].compression); +#else + png_warning(png_ptr, "Unable to write compressed text"); +#endif + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR; + } + else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE) + { +#if defined(PNG_WRITE_tEXt_SUPPORTED) + /* write uncompressed chunk */ + png_write_tEXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, 0); +#else + png_warning(png_ptr, "Unable to write uncompressed text"); +#endif + + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; + } + } +#endif +#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) + if (info_ptr->unknown_chunks_num) + { + png_unknown_chunk *up; + + png_debug(5, "writing extra chunks\n"); + + for (up = info_ptr->unknown_chunks; + up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num; + up++) + { + int keep=png_handle_as_unknown(png_ptr, up->name); + if (keep != PNG_HANDLE_CHUNK_NEVER && + up->location && (up->location & PNG_AFTER_IDAT) && + ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS || + (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS))) + { + png_write_chunk(png_ptr, up->name, up->data, up->size); + } + } + } +#endif + } + + png_ptr->mode |= PNG_AFTER_IDAT; + + /* write end of PNG file */ + png_write_IEND(png_ptr); +} + +#if defined(PNG_WRITE_tIME_SUPPORTED) +#if !defined(_WIN32_WCE) +/* "time.h" functions are not supported on WindowsCE */ +void PNGAPI +png_convert_from_struct_tm(png_timep ptime, struct tm FAR * ttime) +{ + png_debug(1, "in png_convert_from_struct_tm\n"); + ptime->year = (png_uint_16)(1900 + ttime->tm_year); + ptime->month = (png_byte)(ttime->tm_mon + 1); + ptime->day = (png_byte)ttime->tm_mday; + ptime->hour = (png_byte)ttime->tm_hour; + ptime->minute = (png_byte)ttime->tm_min; + ptime->second = (png_byte)ttime->tm_sec; +} + +void PNGAPI +png_convert_from_time_t(png_timep ptime, time_t ttime) +{ + struct tm *tbuf; + + png_debug(1, "in png_convert_from_time_t\n"); + tbuf = gmtime(&ttime); + png_convert_from_struct_tm(ptime, tbuf); +} +#endif +#endif + +/* Initialize png_ptr structure, and allocate any memory needed */ +png_structp PNGAPI +png_create_write_struct(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn) +{ +#ifdef PNG_USER_MEM_SUPPORTED + return (png_create_write_struct_2(user_png_ver, error_ptr, error_fn, + warn_fn, png_voidp_NULL, png_malloc_ptr_NULL, png_free_ptr_NULL)); +} + +/* Alternate initialize png_ptr structure, and allocate any memory needed */ +png_structp PNGAPI +png_create_write_struct_2(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ + png_structp png_ptr; +#ifdef PNG_SETJMP_SUPPORTED +#ifdef USE_FAR_KEYWORD + jmp_buf jmpbuf; +#endif +#endif + int i; + png_debug(1, "in png_create_write_struct\n"); +#ifdef PNG_USER_MEM_SUPPORTED + png_ptr = (png_structp)png_create_struct_2(PNG_STRUCT_PNG, + (png_malloc_ptr)malloc_fn, (png_voidp)mem_ptr); +#else + png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG); +#endif /* PNG_USER_MEM_SUPPORTED */ + if (png_ptr == NULL) + return (NULL); + + /* added at libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + png_ptr->user_width_max=PNG_USER_WIDTH_MAX; + png_ptr->user_height_max=PNG_USER_HEIGHT_MAX; +#endif + +#ifdef PNG_SETJMP_SUPPORTED +#ifdef USE_FAR_KEYWORD + if (setjmp(jmpbuf)) +#else + if (setjmp(png_ptr->jmpbuf)) +#endif + { + png_free(png_ptr, png_ptr->zbuf); + png_ptr->zbuf=NULL; + png_destroy_struct(png_ptr); + return (NULL); + } +#ifdef USE_FAR_KEYWORD + png_memcpy(png_ptr->jmpbuf,jmpbuf,png_sizeof(jmp_buf)); +#endif +#endif + +#ifdef PNG_USER_MEM_SUPPORTED + png_set_mem_fn(png_ptr, mem_ptr, malloc_fn, free_fn); +#endif /* PNG_USER_MEM_SUPPORTED */ + png_set_error_fn(png_ptr, error_ptr, error_fn, warn_fn); + + i=0; + do + { + if(user_png_ver[i] != png_libpng_ver[i]) + png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; + } while (png_libpng_ver[i++]); + + if (png_ptr->flags & PNG_FLAG_LIBRARY_MISMATCH) + { + /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so + * we must recompile any applications that use any older library version. + * For versions after libpng 1.0, we will be compatible, so we need + * only check the first digit. + */ + if (user_png_ver == NULL || user_png_ver[0] != png_libpng_ver[0] || + (user_png_ver[0] == '1' && user_png_ver[2] != png_libpng_ver[2]) || + (user_png_ver[0] == '0' && user_png_ver[2] < '9')) + { +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + char msg[80]; + if (user_png_ver) + { + png_snprintf(msg, 80, + "Application was compiled with png.h from libpng-%.20s", + user_png_ver); + png_warning(png_ptr, msg); + } + png_snprintf(msg, 80, + "Application is running with png.c from libpng-%.20s", + png_libpng_ver); + png_warning(png_ptr, msg); +#endif +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags=0; +#endif + png_error(png_ptr, + "Incompatible libpng version in application and library"); + } + } + + /* initialize zbuf - compression buffer */ + png_ptr->zbuf_size = PNG_ZBUF_SIZE; + png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, + (png_uint_32)png_ptr->zbuf_size); + + png_set_write_fn(png_ptr, png_voidp_NULL, png_rw_ptr_NULL, + png_flush_ptr_NULL); + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + png_set_filter_heuristics(png_ptr, PNG_FILTER_HEURISTIC_DEFAULT, + 1, png_doublep_NULL, png_doublep_NULL); +#endif + +#ifdef PNG_SETJMP_SUPPORTED +/* Applications that neglect to set up their own setjmp() and then encounter + a png_error() will longjmp here. Since the jmpbuf is then meaningless we + abort instead of returning. */ +#ifdef USE_FAR_KEYWORD + if (setjmp(jmpbuf)) + PNG_ABORT(); + png_memcpy(png_ptr->jmpbuf,jmpbuf,png_sizeof(jmp_buf)); +#else + if (setjmp(png_ptr->jmpbuf)) + PNG_ABORT(); +#endif +#endif + return (png_ptr); +} + +/* Initialize png_ptr structure, and allocate any memory needed */ +#if defined(PNG_1_0_X) || defined(PNG_1_2_X) +/* Deprecated. */ +#undef png_write_init +void PNGAPI +png_write_init(png_structp png_ptr) +{ + /* We only come here via pre-1.0.7-compiled applications */ + png_write_init_2(png_ptr, "1.0.6 or earlier", 0, 0); +} + +void PNGAPI +png_write_init_2(png_structp png_ptr, png_const_charp user_png_ver, + png_size_t png_struct_size, png_size_t png_info_size) +{ + /* We only come here via pre-1.0.12-compiled applications */ + if(png_ptr == NULL) return; +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + if(png_sizeof(png_struct) > png_struct_size || + png_sizeof(png_info) > png_info_size) + { + char msg[80]; + png_ptr->warning_fn=NULL; + if (user_png_ver) + { + png_snprintf(msg, 80, + "Application was compiled with png.h from libpng-%.20s", + user_png_ver); + png_warning(png_ptr, msg); + } + png_snprintf(msg, 80, + "Application is running with png.c from libpng-%.20s", + png_libpng_ver); + png_warning(png_ptr, msg); + } +#endif + if(png_sizeof(png_struct) > png_struct_size) + { + png_ptr->error_fn=NULL; +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags=0; +#endif + png_error(png_ptr, + "The png struct allocated by the application for writing is too small."); + } + if(png_sizeof(png_info) > png_info_size) + { + png_ptr->error_fn=NULL; +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags=0; +#endif + png_error(png_ptr, + "The info struct allocated by the application for writing is too small."); + } + png_write_init_3(&png_ptr, user_png_ver, png_struct_size); +} +#endif /* PNG_1_0_X || PNG_1_2_X */ + +void PNGAPI +png_write_init_3(png_structpp ptr_ptr, png_const_charp user_png_ver, + png_size_t png_struct_size) +{ + png_structp png_ptr=*ptr_ptr; +#ifdef PNG_SETJMP_SUPPORTED + jmp_buf tmp_jmp; /* to save current jump buffer */ +#endif + + int i = 0; + + if (png_ptr == NULL) + return; + + do + { + if (user_png_ver[i] != png_libpng_ver[i]) + { +#ifdef PNG_LEGACY_SUPPORTED + png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; +#else + png_ptr->warning_fn=NULL; + png_warning(png_ptr, + "Application uses deprecated png_write_init() and should be recompiled."); + break; +#endif + } + } while (png_libpng_ver[i++]); + + png_debug(1, "in png_write_init_3\n"); + +#ifdef PNG_SETJMP_SUPPORTED + /* save jump buffer and error functions */ + png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof (jmp_buf)); +#endif + + if (png_sizeof(png_struct) > png_struct_size) + { + png_destroy_struct(png_ptr); + png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG); + *ptr_ptr = png_ptr; + } + + /* reset all variables to 0 */ + png_memset(png_ptr, 0, png_sizeof (png_struct)); + + /* added at libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + png_ptr->user_width_max=PNG_USER_WIDTH_MAX; + png_ptr->user_height_max=PNG_USER_HEIGHT_MAX; +#endif + +#ifdef PNG_SETJMP_SUPPORTED + /* restore jump buffer */ + png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof (jmp_buf)); +#endif + + png_set_write_fn(png_ptr, png_voidp_NULL, png_rw_ptr_NULL, + png_flush_ptr_NULL); + + /* initialize zbuf - compression buffer */ + png_ptr->zbuf_size = PNG_ZBUF_SIZE; + png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, + (png_uint_32)png_ptr->zbuf_size); + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + png_set_filter_heuristics(png_ptr, PNG_FILTER_HEURISTIC_DEFAULT, + 1, png_doublep_NULL, png_doublep_NULL); +#endif +} + +/* Write a few rows of image data. If the image is interlaced, + * either you will have to write the 7 sub images, or, if you + * have called png_set_interlace_handling(), you will have to + * "write" the image seven times. + */ +void PNGAPI +png_write_rows(png_structp png_ptr, png_bytepp row, + png_uint_32 num_rows) +{ + png_uint_32 i; /* row counter */ + png_bytepp rp; /* row pointer */ + + png_debug(1, "in png_write_rows\n"); + + if (png_ptr == NULL) + return; + + /* loop through the rows */ + for (i = 0, rp = row; i < num_rows; i++, rp++) + { + png_write_row(png_ptr, *rp); + } +} + +/* Write the image. You only need to call this function once, even + * if you are writing an interlaced image. + */ +void PNGAPI +png_write_image(png_structp png_ptr, png_bytepp image) +{ + png_uint_32 i; /* row index */ + int pass, num_pass; /* pass variables */ + png_bytepp rp; /* points to current row */ + + if (png_ptr == NULL) + return; + + png_debug(1, "in png_write_image\n"); +#if defined(PNG_WRITE_INTERLACING_SUPPORTED) + /* intialize interlace handling. If image is not interlaced, + this will set pass to 1 */ + num_pass = png_set_interlace_handling(png_ptr); +#else + num_pass = 1; +#endif + /* loop through passes */ + for (pass = 0; pass < num_pass; pass++) + { + /* loop through image */ + for (i = 0, rp = image; i < png_ptr->height; i++, rp++) + { + png_write_row(png_ptr, *rp); + } + } +} + +/* called by user to write a row of image data */ +void PNGAPI +png_write_row(png_structp png_ptr, png_bytep row) +{ + if (png_ptr == NULL) + return; + png_debug2(1, "in png_write_row (row %ld, pass %d)\n", + png_ptr->row_number, png_ptr->pass); + + /* initialize transformations and other stuff if first time */ + if (png_ptr->row_number == 0 && png_ptr->pass == 0) + { + /* make sure we wrote the header info */ + if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE)) + png_error(png_ptr, + "png_write_info was never called before png_write_row."); + + /* check for transforms that have been set but were defined out */ +#if !defined(PNG_WRITE_INVERT_SUPPORTED) && defined(PNG_READ_INVERT_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_MONO) + png_warning(png_ptr, "PNG_WRITE_INVERT_SUPPORTED is not defined."); +#endif +#if !defined(PNG_WRITE_FILLER_SUPPORTED) && defined(PNG_READ_FILLER_SUPPORTED) + if (png_ptr->transformations & PNG_FILLER) + png_warning(png_ptr, "PNG_WRITE_FILLER_SUPPORTED is not defined."); +#endif +#if !defined(PNG_WRITE_PACKSWAP_SUPPORTED) && defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + png_warning(png_ptr, "PNG_WRITE_PACKSWAP_SUPPORTED is not defined."); +#endif +#if !defined(PNG_WRITE_PACK_SUPPORTED) && defined(PNG_READ_PACK_SUPPORTED) + if (png_ptr->transformations & PNG_PACK) + png_warning(png_ptr, "PNG_WRITE_PACK_SUPPORTED is not defined."); +#endif +#if !defined(PNG_WRITE_SHIFT_SUPPORTED) && defined(PNG_READ_SHIFT_SUPPORTED) + if (png_ptr->transformations & PNG_SHIFT) + png_warning(png_ptr, "PNG_WRITE_SHIFT_SUPPORTED is not defined."); +#endif +#if !defined(PNG_WRITE_BGR_SUPPORTED) && defined(PNG_READ_BGR_SUPPORTED) + if (png_ptr->transformations & PNG_BGR) + png_warning(png_ptr, "PNG_WRITE_BGR_SUPPORTED is not defined."); +#endif +#if !defined(PNG_WRITE_SWAP_SUPPORTED) && defined(PNG_READ_SWAP_SUPPORTED) + if (png_ptr->transformations & PNG_SWAP_BYTES) + png_warning(png_ptr, "PNG_WRITE_SWAP_SUPPORTED is not defined."); +#endif + + png_write_start_row(png_ptr); + } + +#if defined(PNG_WRITE_INTERLACING_SUPPORTED) + /* if interlaced and not interested in row, return */ + if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE)) + { + switch (png_ptr->pass) + { + case 0: + if (png_ptr->row_number & 0x07) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 1: + if ((png_ptr->row_number & 0x07) || png_ptr->width < 5) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 2: + if ((png_ptr->row_number & 0x07) != 4) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 3: + if ((png_ptr->row_number & 0x03) || png_ptr->width < 3) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 4: + if ((png_ptr->row_number & 0x03) != 2) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 5: + if ((png_ptr->row_number & 0x01) || png_ptr->width < 2) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 6: + if (!(png_ptr->row_number & 0x01)) + { + png_write_finish_row(png_ptr); + return; + } + break; + } + } +#endif + + /* set up row info for transformations */ + png_ptr->row_info.color_type = png_ptr->color_type; + png_ptr->row_info.width = png_ptr->usr_width; + png_ptr->row_info.channels = png_ptr->usr_channels; + png_ptr->row_info.bit_depth = png_ptr->usr_bit_depth; + png_ptr->row_info.pixel_depth = (png_byte)(png_ptr->row_info.bit_depth * + png_ptr->row_info.channels); + + png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth, + png_ptr->row_info.width); + + png_debug1(3, "row_info->color_type = %d\n", png_ptr->row_info.color_type); + png_debug1(3, "row_info->width = %lu\n", png_ptr->row_info.width); + png_debug1(3, "row_info->channels = %d\n", png_ptr->row_info.channels); + png_debug1(3, "row_info->bit_depth = %d\n", png_ptr->row_info.bit_depth); + png_debug1(3, "row_info->pixel_depth = %d\n", png_ptr->row_info.pixel_depth); + png_debug1(3, "row_info->rowbytes = %lu\n", png_ptr->row_info.rowbytes); + + /* Copy user's row into buffer, leaving room for filter byte. */ + png_memcpy_check(png_ptr, png_ptr->row_buf + 1, row, + png_ptr->row_info.rowbytes); + +#if defined(PNG_WRITE_INTERLACING_SUPPORTED) + /* handle interlacing */ + if (png_ptr->interlaced && png_ptr->pass < 6 && + (png_ptr->transformations & PNG_INTERLACE)) + { + png_do_write_interlace(&(png_ptr->row_info), + png_ptr->row_buf + 1, png_ptr->pass); + /* this should always get caught above, but still ... */ + if (!(png_ptr->row_info.width)) + { + png_write_finish_row(png_ptr); + return; + } + } +#endif + + /* handle other transformations */ + if (png_ptr->transformations) + png_do_write_transformations(png_ptr); + +#if defined(PNG_MNG_FEATURES_SUPPORTED) + /* Write filter_method 64 (intrapixel differencing) only if + * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and + * 2. Libpng did not write a PNG signature (this filter_method is only + * used in PNG datastreams that are embedded in MNG datastreams) and + * 3. The application called png_permit_mng_features with a mask that + * included PNG_FLAG_MNG_FILTER_64 and + * 4. The filter_method is 64 and + * 5. The color_type is RGB or RGBA + */ + if((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING)) + { + /* Intrapixel differencing */ + png_do_write_intrapixel(&(png_ptr->row_info), png_ptr->row_buf + 1); + } +#endif + + /* Find a filter if necessary, filter the row and write it out. */ + png_write_find_filter(png_ptr, &(png_ptr->row_info)); + + if (png_ptr->write_row_fn != NULL) + (*(png_ptr->write_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass); +} + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) +/* Set the automatic flush interval or 0 to turn flushing off */ +void PNGAPI +png_set_flush(png_structp png_ptr, int nrows) +{ + png_debug(1, "in png_set_flush\n"); + if (png_ptr == NULL) + return; + png_ptr->flush_dist = (nrows < 0 ? 0 : nrows); +} + +/* flush the current output buffers now */ +void PNGAPI +png_write_flush(png_structp png_ptr) +{ + int wrote_IDAT; + + png_debug(1, "in png_write_flush\n"); + if (png_ptr == NULL) + return; + /* We have already written out all of the data */ + if (png_ptr->row_number >= png_ptr->num_rows) + return; + + do + { + int ret; + + /* compress the data */ + ret = deflate(&png_ptr->zstream, Z_SYNC_FLUSH); + wrote_IDAT = 0; + + /* check for compression errors */ + if (ret != Z_OK) + { + if (png_ptr->zstream.msg != NULL) + png_error(png_ptr, png_ptr->zstream.msg); + else + png_error(png_ptr, "zlib error"); + } + + if (!(png_ptr->zstream.avail_out)) + { + /* write the IDAT and reset the zlib output buffer */ + png_write_IDAT(png_ptr, png_ptr->zbuf, + png_ptr->zbuf_size); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + wrote_IDAT = 1; + } + } while(wrote_IDAT == 1); + + /* If there is any data left to be output, write it into a new IDAT */ + if (png_ptr->zbuf_size != png_ptr->zstream.avail_out) + { + /* write the IDAT and reset the zlib output buffer */ + png_write_IDAT(png_ptr, png_ptr->zbuf, + png_ptr->zbuf_size - png_ptr->zstream.avail_out); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + } + png_ptr->flush_rows = 0; + png_flush(png_ptr); +} +#endif /* PNG_WRITE_FLUSH_SUPPORTED */ + +/* free all memory used by the write */ +void PNGAPI +png_destroy_write_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr) +{ + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; +#ifdef PNG_USER_MEM_SUPPORTED + png_free_ptr free_fn = NULL; + png_voidp mem_ptr = NULL; +#endif + + png_debug(1, "in png_destroy_write_struct\n"); + if (png_ptr_ptr != NULL) + { + png_ptr = *png_ptr_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + free_fn = png_ptr->free_fn; + mem_ptr = png_ptr->mem_ptr; +#endif + } + + if (info_ptr_ptr != NULL) + info_ptr = *info_ptr_ptr; + + if (info_ptr != NULL) + { + png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) + if (png_ptr->num_chunk_list) + { + png_free(png_ptr, png_ptr->chunk_list); + png_ptr->chunk_list=NULL; + png_ptr->num_chunk_list=0; + } +#endif + +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)info_ptr, (png_free_ptr)free_fn, + (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)info_ptr); +#endif + *info_ptr_ptr = NULL; + } + + if (png_ptr != NULL) + { + png_write_destroy(png_ptr); +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)png_ptr, (png_free_ptr)free_fn, + (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)png_ptr); +#endif + *png_ptr_ptr = NULL; + } +} + +/* Free any memory used in png_ptr struct (old method) */ +void /* PRIVATE */ +png_write_destroy(png_structp png_ptr) +{ +#ifdef PNG_SETJMP_SUPPORTED + jmp_buf tmp_jmp; /* save jump buffer */ +#endif + png_error_ptr error_fn; + png_error_ptr warning_fn; + png_voidp error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + png_free_ptr free_fn; +#endif + + png_debug(1, "in png_write_destroy\n"); + /* free any memory zlib uses */ + deflateEnd(&png_ptr->zstream); + + /* free our memory. png_free checks NULL for us. */ + png_free(png_ptr, png_ptr->zbuf); + png_free(png_ptr, png_ptr->row_buf); + png_free(png_ptr, png_ptr->prev_row); + png_free(png_ptr, png_ptr->sub_row); + png_free(png_ptr, png_ptr->up_row); + png_free(png_ptr, png_ptr->avg_row); + png_free(png_ptr, png_ptr->paeth_row); + +#if defined(PNG_TIME_RFC1123_SUPPORTED) + png_free(png_ptr, png_ptr->time_buffer); +#endif + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + png_free(png_ptr, png_ptr->prev_filters); + png_free(png_ptr, png_ptr->filter_weights); + png_free(png_ptr, png_ptr->inv_filter_weights); + png_free(png_ptr, png_ptr->filter_costs); + png_free(png_ptr, png_ptr->inv_filter_costs); +#endif + +#ifdef PNG_SETJMP_SUPPORTED + /* reset structure */ + png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof (jmp_buf)); +#endif + + error_fn = png_ptr->error_fn; + warning_fn = png_ptr->warning_fn; + error_ptr = png_ptr->error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + free_fn = png_ptr->free_fn; +#endif + + png_memset(png_ptr, 0, png_sizeof (png_struct)); + + png_ptr->error_fn = error_fn; + png_ptr->warning_fn = warning_fn; + png_ptr->error_ptr = error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + png_ptr->free_fn = free_fn; +#endif + +#ifdef PNG_SETJMP_SUPPORTED + png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof (jmp_buf)); +#endif +} + +/* Allow the application to select one or more row filters to use. */ +void PNGAPI +png_set_filter(png_structp png_ptr, int method, int filters) +{ + png_debug(1, "in png_set_filter\n"); + if (png_ptr == NULL) + return; +#if defined(PNG_MNG_FEATURES_SUPPORTED) + if((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + (method == PNG_INTRAPIXEL_DIFFERENCING)) + method = PNG_FILTER_TYPE_BASE; +#endif + if (method == PNG_FILTER_TYPE_BASE) + { + switch (filters & (PNG_ALL_FILTERS | 0x07)) + { +#ifndef PNG_NO_WRITE_FILTER + case 5: + case 6: + case 7: png_warning(png_ptr, "Unknown row filter for method 0"); +#endif /* PNG_NO_WRITE_FILTER */ + case PNG_FILTER_VALUE_NONE: + png_ptr->do_filter=PNG_FILTER_NONE; break; +#ifndef PNG_NO_WRITE_FILTER + case PNG_FILTER_VALUE_SUB: + png_ptr->do_filter=PNG_FILTER_SUB; break; + case PNG_FILTER_VALUE_UP: + png_ptr->do_filter=PNG_FILTER_UP; break; + case PNG_FILTER_VALUE_AVG: + png_ptr->do_filter=PNG_FILTER_AVG; break; + case PNG_FILTER_VALUE_PAETH: + png_ptr->do_filter=PNG_FILTER_PAETH; break; + default: png_ptr->do_filter = (png_byte)filters; break; +#else + default: png_warning(png_ptr, "Unknown row filter for method 0"); +#endif /* PNG_NO_WRITE_FILTER */ + } + + /* If we have allocated the row_buf, this means we have already started + * with the image and we should have allocated all of the filter buffers + * that have been selected. If prev_row isn't already allocated, then + * it is too late to start using the filters that need it, since we + * will be missing the data in the previous row. If an application + * wants to start and stop using particular filters during compression, + * it should start out with all of the filters, and then add and + * remove them after the start of compression. + */ + if (png_ptr->row_buf != NULL) + { +#ifndef PNG_NO_WRITE_FILTER + if ((png_ptr->do_filter & PNG_FILTER_SUB) && png_ptr->sub_row == NULL) + { + png_ptr->sub_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB; + } + + if ((png_ptr->do_filter & PNG_FILTER_UP) && png_ptr->up_row == NULL) + { + if (png_ptr->prev_row == NULL) + { + png_warning(png_ptr, "Can't add Up filter after starting"); + png_ptr->do_filter &= ~PNG_FILTER_UP; + } + else + { + png_ptr->up_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->up_row[0] = PNG_FILTER_VALUE_UP; + } + } + + if ((png_ptr->do_filter & PNG_FILTER_AVG) && png_ptr->avg_row == NULL) + { + if (png_ptr->prev_row == NULL) + { + png_warning(png_ptr, "Can't add Average filter after starting"); + png_ptr->do_filter &= ~PNG_FILTER_AVG; + } + else + { + png_ptr->avg_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG; + } + } + + if ((png_ptr->do_filter & PNG_FILTER_PAETH) && + png_ptr->paeth_row == NULL) + { + if (png_ptr->prev_row == NULL) + { + png_warning(png_ptr, "Can't add Paeth filter after starting"); + png_ptr->do_filter &= (png_byte)(~PNG_FILTER_PAETH); + } + else + { + png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH; + } + } + + if (png_ptr->do_filter == PNG_NO_FILTERS) +#endif /* PNG_NO_WRITE_FILTER */ + png_ptr->do_filter = PNG_FILTER_NONE; + } + } + else + png_error(png_ptr, "Unknown custom filter method"); +} + +/* This allows us to influence the way in which libpng chooses the "best" + * filter for the current scanline. While the "minimum-sum-of-absolute- + * differences metric is relatively fast and effective, there is some + * question as to whether it can be improved upon by trying to keep the + * filtered data going to zlib more consistent, hopefully resulting in + * better compression. + */ +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) /* GRR 970116 */ +void PNGAPI +png_set_filter_heuristics(png_structp png_ptr, int heuristic_method, + int num_weights, png_doublep filter_weights, + png_doublep filter_costs) +{ + int i; + + png_debug(1, "in png_set_filter_heuristics\n"); + if (png_ptr == NULL) + return; + if (heuristic_method >= PNG_FILTER_HEURISTIC_LAST) + { + png_warning(png_ptr, "Unknown filter heuristic method"); + return; + } + + if (heuristic_method == PNG_FILTER_HEURISTIC_DEFAULT) + { + heuristic_method = PNG_FILTER_HEURISTIC_UNWEIGHTED; + } + + if (num_weights < 0 || filter_weights == NULL || + heuristic_method == PNG_FILTER_HEURISTIC_UNWEIGHTED) + { + num_weights = 0; + } + + png_ptr->num_prev_filters = (png_byte)num_weights; + png_ptr->heuristic_method = (png_byte)heuristic_method; + + if (num_weights > 0) + { + if (png_ptr->prev_filters == NULL) + { + png_ptr->prev_filters = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(png_sizeof(png_byte) * num_weights)); + + /* To make sure that the weighting starts out fairly */ + for (i = 0; i < num_weights; i++) + { + png_ptr->prev_filters[i] = 255; + } + } + + if (png_ptr->filter_weights == NULL) + { + png_ptr->filter_weights = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(png_sizeof(png_uint_16) * num_weights)); + + png_ptr->inv_filter_weights = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(png_sizeof(png_uint_16) * num_weights)); + for (i = 0; i < num_weights; i++) + { + png_ptr->inv_filter_weights[i] = + png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR; + } + } + + for (i = 0; i < num_weights; i++) + { + if (filter_weights[i] < 0.0) + { + png_ptr->inv_filter_weights[i] = + png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR; + } + else + { + png_ptr->inv_filter_weights[i] = + (png_uint_16)((double)PNG_WEIGHT_FACTOR*filter_weights[i]+0.5); + png_ptr->filter_weights[i] = + (png_uint_16)((double)PNG_WEIGHT_FACTOR/filter_weights[i]+0.5); + } + } + } + + /* If, in the future, there are other filter methods, this would + * need to be based on png_ptr->filter. + */ + if (png_ptr->filter_costs == NULL) + { + png_ptr->filter_costs = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(png_sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST)); + + png_ptr->inv_filter_costs = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(png_sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST)); + + for (i = 0; i < PNG_FILTER_VALUE_LAST; i++) + { + png_ptr->inv_filter_costs[i] = + png_ptr->filter_costs[i] = PNG_COST_FACTOR; + } + } + + /* Here is where we set the relative costs of the different filters. We + * should take the desired compression level into account when setting + * the costs, so that Paeth, for instance, has a high relative cost at low + * compression levels, while it has a lower relative cost at higher + * compression settings. The filter types are in order of increasing + * relative cost, so it would be possible to do this with an algorithm. + */ + for (i = 0; i < PNG_FILTER_VALUE_LAST; i++) + { + if (filter_costs == NULL || filter_costs[i] < 0.0) + { + png_ptr->inv_filter_costs[i] = + png_ptr->filter_costs[i] = PNG_COST_FACTOR; + } + else if (filter_costs[i] >= 1.0) + { + png_ptr->inv_filter_costs[i] = + (png_uint_16)((double)PNG_COST_FACTOR / filter_costs[i] + 0.5); + png_ptr->filter_costs[i] = + (png_uint_16)((double)PNG_COST_FACTOR * filter_costs[i] + 0.5); + } + } +} +#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */ + +void PNGAPI +png_set_compression_level(png_structp png_ptr, int level) +{ + png_debug(1, "in png_set_compression_level\n"); + if (png_ptr == NULL) + return; + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_LEVEL; + png_ptr->zlib_level = level; +} + +void PNGAPI +png_set_compression_mem_level(png_structp png_ptr, int mem_level) +{ + png_debug(1, "in png_set_compression_mem_level\n"); + if (png_ptr == NULL) + return; + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL; + png_ptr->zlib_mem_level = mem_level; +} + +void PNGAPI +png_set_compression_strategy(png_structp png_ptr, int strategy) +{ + png_debug(1, "in png_set_compression_strategy\n"); + if (png_ptr == NULL) + return; + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY; + png_ptr->zlib_strategy = strategy; +} + +void PNGAPI +png_set_compression_window_bits(png_structp png_ptr, int window_bits) +{ + if (png_ptr == NULL) + return; + if (window_bits > 15) + png_warning(png_ptr, "Only compression windows <= 32k supported by PNG"); + else if (window_bits < 8) + png_warning(png_ptr, "Only compression windows >= 256 supported by PNG"); +#ifndef WBITS_8_OK + /* avoid libpng bug with 256-byte windows */ + if (window_bits == 8) + { + png_warning(png_ptr, "Compression window is being reset to 512"); + window_bits=9; + } +#endif + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS; + png_ptr->zlib_window_bits = window_bits; +} + +void PNGAPI +png_set_compression_method(png_structp png_ptr, int method) +{ + png_debug(1, "in png_set_compression_method\n"); + if (png_ptr == NULL) + return; + if (method != 8) + png_warning(png_ptr, "Only compression method 8 is supported by PNG"); + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_METHOD; + png_ptr->zlib_method = method; +} + +void PNGAPI +png_set_write_status_fn(png_structp png_ptr, png_write_status_ptr write_row_fn) +{ + if (png_ptr == NULL) + return; + png_ptr->write_row_fn = write_row_fn; +} + +#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +void PNGAPI +png_set_write_user_transform_fn(png_structp png_ptr, png_user_transform_ptr + write_user_transform_fn) +{ + png_debug(1, "in png_set_write_user_transform_fn\n"); + if (png_ptr == NULL) + return; + png_ptr->transformations |= PNG_USER_TRANSFORM; + png_ptr->write_user_transform_fn = write_user_transform_fn; +} +#endif + +#if defined(PNG_INFO_IMAGE_SUPPORTED) +void PNGAPI +png_write_png(png_structp png_ptr, png_infop info_ptr, + int transforms, voidp params) +{ + if (png_ptr == NULL || info_ptr == NULL) + return; +#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) + /* invert the alpha channel from opacity to transparency */ + if (transforms & PNG_TRANSFORM_INVERT_ALPHA) + png_set_invert_alpha(png_ptr); +#endif + + /* Write the file header information. */ + png_write_info(png_ptr, info_ptr); + + /* ------ these transformations don't touch the info structure ------- */ + +#if defined(PNG_WRITE_INVERT_SUPPORTED) + /* invert monochrome pixels */ + if (transforms & PNG_TRANSFORM_INVERT_MONO) + png_set_invert_mono(png_ptr); +#endif + +#if defined(PNG_WRITE_SHIFT_SUPPORTED) + /* Shift the pixels up to a legal bit depth and fill in + * as appropriate to correctly scale the image. + */ + if ((transforms & PNG_TRANSFORM_SHIFT) + && (info_ptr->valid & PNG_INFO_sBIT)) + png_set_shift(png_ptr, &info_ptr->sig_bit); +#endif + +#if defined(PNG_WRITE_PACK_SUPPORTED) + /* pack pixels into bytes */ + if (transforms & PNG_TRANSFORM_PACKING) + png_set_packing(png_ptr); +#endif + +#if defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) + /* swap location of alpha bytes from ARGB to RGBA */ + if (transforms & PNG_TRANSFORM_SWAP_ALPHA) + png_set_swap_alpha(png_ptr); +#endif + +#if defined(PNG_WRITE_FILLER_SUPPORTED) + /* Get rid of filler (OR ALPHA) bytes, pack XRGB/RGBX/ARGB/RGBA into + * RGB (4 channels -> 3 channels). The second parameter is not used. + */ + if (transforms & PNG_TRANSFORM_STRIP_FILLER) + png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE); +#endif + +#if defined(PNG_WRITE_BGR_SUPPORTED) + /* flip BGR pixels to RGB */ + if (transforms & PNG_TRANSFORM_BGR) + png_set_bgr(png_ptr); +#endif + +#if defined(PNG_WRITE_SWAP_SUPPORTED) + /* swap bytes of 16-bit files to most significant byte first */ + if (transforms & PNG_TRANSFORM_SWAP_ENDIAN) + png_set_swap(png_ptr); +#endif + +#if defined(PNG_WRITE_PACKSWAP_SUPPORTED) + /* swap bits of 1, 2, 4 bit packed pixel formats */ + if (transforms & PNG_TRANSFORM_PACKSWAP) + png_set_packswap(png_ptr); +#endif + + /* ----------------------- end of transformations ------------------- */ + + /* write the bits */ + if (info_ptr->valid & PNG_INFO_IDAT) + png_write_image(png_ptr, info_ptr->row_pointers); + + /* It is REQUIRED to call this to finish writing the rest of the file */ + png_write_end(png_ptr, info_ptr); + + transforms = transforms; /* quiet compiler warnings */ + params = params; +} +#endif +#endif /* PNG_WRITE_SUPPORTED */ +/********* End of inlined file: pngwrite.c *********/ + +/********* Start of inlined file: pngwtran.c *********/ +/* pngwtran.c - transforms the data in a row for PNG writers + * + * Last changed in libpng 1.2.9 April 14, 2006 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2006 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + */ + +#define PNG_INTERNAL + +#ifdef PNG_WRITE_SUPPORTED + +/* Transform the data according to the user's wishes. The order of + * transformations is significant. + */ +void /* PRIVATE */ +png_do_write_transformations(png_structp png_ptr) +{ + png_debug(1, "in png_do_write_transformations\n"); + + if (png_ptr == NULL) + return; + +#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) + if (png_ptr->transformations & PNG_USER_TRANSFORM) + if(png_ptr->write_user_transform_fn != NULL) + (*(png_ptr->write_user_transform_fn)) /* user write transform function */ + (png_ptr, /* png_ptr */ + &(png_ptr->row_info), /* row_info: */ + /* png_uint_32 width; width of row */ + /* png_uint_32 rowbytes; number of bytes in row */ + /* png_byte color_type; color type of pixels */ + /* png_byte bit_depth; bit depth of samples */ + /* png_byte channels; number of channels (1-4) */ + /* png_byte pixel_depth; bits per pixel (depth*channels) */ + png_ptr->row_buf + 1); /* start of pixel data for row */ +#endif +#if defined(PNG_WRITE_FILLER_SUPPORTED) + if (png_ptr->transformations & PNG_FILLER) + png_do_strip_filler(&(png_ptr->row_info), png_ptr->row_buf + 1, + png_ptr->flags); +#endif +#if defined(PNG_WRITE_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + png_do_packswap(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#if defined(PNG_WRITE_PACK_SUPPORTED) + if (png_ptr->transformations & PNG_PACK) + png_do_pack(&(png_ptr->row_info), png_ptr->row_buf + 1, + (png_uint_32)png_ptr->bit_depth); +#endif +#if defined(PNG_WRITE_SWAP_SUPPORTED) + if (png_ptr->transformations & PNG_SWAP_BYTES) + png_do_swap(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#if defined(PNG_WRITE_SHIFT_SUPPORTED) + if (png_ptr->transformations & PNG_SHIFT) + png_do_shift(&(png_ptr->row_info), png_ptr->row_buf + 1, + &(png_ptr->shift)); +#endif +#if defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) + if (png_ptr->transformations & PNG_SWAP_ALPHA) + png_do_write_swap_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_ALPHA) + png_do_write_invert_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#if defined(PNG_WRITE_BGR_SUPPORTED) + if (png_ptr->transformations & PNG_BGR) + png_do_bgr(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#if defined(PNG_WRITE_INVERT_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_MONO) + png_do_invert(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +} + +#if defined(PNG_WRITE_PACK_SUPPORTED) +/* Pack pixels into bytes. Pass the true bit depth in bit_depth. The + * row_info bit depth should be 8 (one pixel per byte). The channels + * should be 1 (this only happens on grayscale and paletted images). + */ +void /* PRIVATE */ +png_do_pack(png_row_infop row_info, png_bytep row, png_uint_32 bit_depth) +{ + png_debug(1, "in png_do_pack\n"); + if (row_info->bit_depth == 8 && +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + row_info->channels == 1) + { + switch ((int)bit_depth) + { + case 1: + { + png_bytep sp, dp; + int mask, v; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + sp = row; + dp = row; + mask = 0x80; + v = 0; + + for (i = 0; i < row_width; i++) + { + if (*sp != 0) + v |= mask; + sp++; + if (mask > 1) + mask >>= 1; + else + { + mask = 0x80; + *dp = (png_byte)v; + dp++; + v = 0; + } + } + if (mask != 0x80) + *dp = (png_byte)v; + break; + } + case 2: + { + png_bytep sp, dp; + int shift, v; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + sp = row; + dp = row; + shift = 6; + v = 0; + for (i = 0; i < row_width; i++) + { + png_byte value; + + value = (png_byte)(*sp & 0x03); + v |= (value << shift); + if (shift == 0) + { + shift = 6; + *dp = (png_byte)v; + dp++; + v = 0; + } + else + shift -= 2; + sp++; + } + if (shift != 6) + *dp = (png_byte)v; + break; + } + case 4: + { + png_bytep sp, dp; + int shift, v; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + sp = row; + dp = row; + shift = 4; + v = 0; + for (i = 0; i < row_width; i++) + { + png_byte value; + + value = (png_byte)(*sp & 0x0f); + v |= (value << shift); + + if (shift == 0) + { + shift = 4; + *dp = (png_byte)v; + dp++; + v = 0; + } + else + shift -= 4; + + sp++; + } + if (shift != 4) + *dp = (png_byte)v; + break; + } + } + row_info->bit_depth = (png_byte)bit_depth; + row_info->pixel_depth = (png_byte)(bit_depth * row_info->channels); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, + row_info->width); + } +} +#endif + +#if defined(PNG_WRITE_SHIFT_SUPPORTED) +/* Shift pixel values to take advantage of whole range. Pass the + * true number of bits in bit_depth. The row should be packed + * according to row_info->bit_depth. Thus, if you had a row of + * bit depth 4, but the pixels only had values from 0 to 7, you + * would pass 3 as bit_depth, and this routine would translate the + * data to 0 to 15. + */ +void /* PRIVATE */ +png_do_shift(png_row_infop row_info, png_bytep row, png_color_8p bit_depth) +{ + png_debug(1, "in png_do_shift\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL && +#else + if ( +#endif + row_info->color_type != PNG_COLOR_TYPE_PALETTE) + { + int shift_start[4], shift_dec[4]; + int channels = 0; + + if (row_info->color_type & PNG_COLOR_MASK_COLOR) + { + shift_start[channels] = row_info->bit_depth - bit_depth->red; + shift_dec[channels] = bit_depth->red; + channels++; + shift_start[channels] = row_info->bit_depth - bit_depth->green; + shift_dec[channels] = bit_depth->green; + channels++; + shift_start[channels] = row_info->bit_depth - bit_depth->blue; + shift_dec[channels] = bit_depth->blue; + channels++; + } + else + { + shift_start[channels] = row_info->bit_depth - bit_depth->gray; + shift_dec[channels] = bit_depth->gray; + channels++; + } + if (row_info->color_type & PNG_COLOR_MASK_ALPHA) + { + shift_start[channels] = row_info->bit_depth - bit_depth->alpha; + shift_dec[channels] = bit_depth->alpha; + channels++; + } + + /* with low row depths, could only be grayscale, so one channel */ + if (row_info->bit_depth < 8) + { + png_bytep bp = row; + png_uint_32 i; + png_byte mask; + png_uint_32 row_bytes = row_info->rowbytes; + + if (bit_depth->gray == 1 && row_info->bit_depth == 2) + mask = 0x55; + else if (row_info->bit_depth == 4 && bit_depth->gray == 3) + mask = 0x11; + else + mask = 0xff; + + for (i = 0; i < row_bytes; i++, bp++) + { + png_uint_16 v; + int j; + + v = *bp; + *bp = 0; + for (j = shift_start[0]; j > -shift_dec[0]; j -= shift_dec[0]) + { + if (j > 0) + *bp |= (png_byte)((v << j) & 0xff); + else + *bp |= (png_byte)((v >> (-j)) & mask); + } + } + } + else if (row_info->bit_depth == 8) + { + png_bytep bp = row; + png_uint_32 i; + png_uint_32 istop = channels * row_info->width; + + for (i = 0; i < istop; i++, bp++) + { + + png_uint_16 v; + int j; + int c = (int)(i%channels); + + v = *bp; + *bp = 0; + for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c]) + { + if (j > 0) + *bp |= (png_byte)((v << j) & 0xff); + else + *bp |= (png_byte)((v >> (-j)) & 0xff); + } + } + } + else + { + png_bytep bp; + png_uint_32 i; + png_uint_32 istop = channels * row_info->width; + + for (bp = row, i = 0; i < istop; i++) + { + int c = (int)(i%channels); + png_uint_16 value, v; + int j; + + v = (png_uint_16)(((png_uint_16)(*bp) << 8) + *(bp + 1)); + value = 0; + for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c]) + { + if (j > 0) + value |= (png_uint_16)((v << j) & (png_uint_16)0xffff); + else + value |= (png_uint_16)((v >> (-j)) & (png_uint_16)0xffff); + } + *bp++ = (png_byte)(value >> 8); + *bp++ = (png_byte)(value & 0xff); + } + } + } +} +#endif + +#if defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +void /* PRIVATE */ +png_do_write_swap_alpha(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_write_swap_alpha\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL) +#endif + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + /* This converts from ARGB to RGBA */ + if (row_info->bit_depth == 8) + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save; + } + } + /* This converts from AARRGGBB to RRGGBBAA */ + else + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save[2]; + save[0] = *(sp++); + save[1] = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save[0]; + *(dp++) = save[1]; + } + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + /* This converts from AG to GA */ + if (row_info->bit_depth == 8) + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save; + } + } + /* This converts from AAGG to GGAA */ + else + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save[2]; + save[0] = *(sp++); + save[1] = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save[0]; + *(dp++) = save[1]; + } + } + } + } +} +#endif + +#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +void /* PRIVATE */ +png_do_write_invert_alpha(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_write_invert_alpha\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL) +#endif + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + /* This inverts the alpha channel in RGBA */ + if (row_info->bit_depth == 8) + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + for (i = 0, sp = dp = row; i < row_width; i++) + { + /* does nothing + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + */ + sp+=3; dp = sp; + *(dp++) = (png_byte)(255 - *(sp++)); + } + } + /* This inverts the alpha channel in RRGGBBAA */ + else + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + /* does nothing + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + */ + sp+=6; dp = sp; + *(dp++) = (png_byte)(255 - *(sp++)); + *(dp++) = (png_byte)(255 - *(sp++)); + } + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + /* This inverts the alpha channel in GA */ + if (row_info->bit_depth == 8) + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + *(dp++) = *(sp++); + *(dp++) = (png_byte)(255 - *(sp++)); + } + } + /* This inverts the alpha channel in GGAA */ + else + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + /* does nothing + *(dp++) = *(sp++); + *(dp++) = *(sp++); + */ + sp+=2; dp = sp; + *(dp++) = (png_byte)(255 - *(sp++)); + *(dp++) = (png_byte)(255 - *(sp++)); + } + } + } + } +} +#endif + +#if defined(PNG_MNG_FEATURES_SUPPORTED) +/* undoes intrapixel differencing */ +void /* PRIVATE */ +png_do_write_intrapixel(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_write_intrapixel\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + (row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + int bytes_per_pixel; + png_uint_32 row_width = row_info->width; + if (row_info->bit_depth == 8) + { + png_bytep rp; + png_uint_32 i; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + bytes_per_pixel = 3; + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + bytes_per_pixel = 4; + else + return; + + for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + { + *(rp) = (png_byte)((*rp - *(rp+1))&0xff); + *(rp+2) = (png_byte)((*(rp+2) - *(rp+1))&0xff); + } + } + else if (row_info->bit_depth == 16) + { + png_bytep rp; + png_uint_32 i; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + bytes_per_pixel = 6; + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + bytes_per_pixel = 8; + else + return; + + for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + { + png_uint_32 s0 = (*(rp ) << 8) | *(rp+1); + png_uint_32 s1 = (*(rp+2) << 8) | *(rp+3); + png_uint_32 s2 = (*(rp+4) << 8) | *(rp+5); + png_uint_32 red = (png_uint_32)((s0-s1) & 0xffffL); + png_uint_32 blue = (png_uint_32)((s2-s1) & 0xffffL); + *(rp ) = (png_byte)((red >> 8) & 0xff); + *(rp+1) = (png_byte)(red & 0xff); + *(rp+4) = (png_byte)((blue >> 8) & 0xff); + *(rp+5) = (png_byte)(blue & 0xff); + } + } + } +} +#endif /* PNG_MNG_FEATURES_SUPPORTED */ +#endif /* PNG_WRITE_SUPPORTED */ +/********* End of inlined file: pngwtran.c *********/ + +/********* Start of inlined file: pngwutil.c *********/ +/* pngwutil.c - utilities to write a PNG file + * + * Last changed in libpng 1.2.20 Septhember 3, 2007 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2007 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + */ + +#define PNG_INTERNAL + +#ifdef PNG_WRITE_SUPPORTED + +/* Place a 32-bit number into a buffer in PNG byte order. We work + * with unsigned numbers for convenience, although one supported + * ancillary chunk uses signed (two's complement) numbers. + */ +void PNGAPI +png_save_uint_32(png_bytep buf, png_uint_32 i) +{ + buf[0] = (png_byte)((i >> 24) & 0xff); + buf[1] = (png_byte)((i >> 16) & 0xff); + buf[2] = (png_byte)((i >> 8) & 0xff); + buf[3] = (png_byte)(i & 0xff); +} + +/* The png_save_int_32 function assumes integers are stored in two's + * complement format. If this isn't the case, then this routine needs to + * be modified to write data in two's complement format. + */ +void PNGAPI +png_save_int_32(png_bytep buf, png_int_32 i) +{ + buf[0] = (png_byte)((i >> 24) & 0xff); + buf[1] = (png_byte)((i >> 16) & 0xff); + buf[2] = (png_byte)((i >> 8) & 0xff); + buf[3] = (png_byte)(i & 0xff); +} + +/* Place a 16-bit number into a buffer in PNG byte order. + * The parameter is declared unsigned int, not png_uint_16, + * just to avoid potential problems on pre-ANSI C compilers. + */ +void PNGAPI +png_save_uint_16(png_bytep buf, unsigned int i) +{ + buf[0] = (png_byte)((i >> 8) & 0xff); + buf[1] = (png_byte)(i & 0xff); +} + +/* Write a PNG chunk all at once. The type is an array of ASCII characters + * representing the chunk name. The array must be at least 4 bytes in + * length, and does not need to be null terminated. To be safe, pass the + * pre-defined chunk names here, and if you need a new one, define it + * where the others are defined. The length is the length of the data. + * All the data must be present. If that is not possible, use the + * png_write_chunk_start(), png_write_chunk_data(), and png_write_chunk_end() + * functions instead. + */ +void PNGAPI +png_write_chunk(png_structp png_ptr, png_bytep chunk_name, + png_bytep data, png_size_t length) +{ + if(png_ptr == NULL) return; + png_write_chunk_start(png_ptr, chunk_name, (png_uint_32)length); + png_write_chunk_data(png_ptr, data, length); + png_write_chunk_end(png_ptr); +} + +/* Write the start of a PNG chunk. The type is the chunk type. + * The total_length is the sum of the lengths of all the data you will be + * passing in png_write_chunk_data(). + */ +void PNGAPI +png_write_chunk_start(png_structp png_ptr, png_bytep chunk_name, + png_uint_32 length) +{ + png_byte buf[4]; + png_debug2(0, "Writing %s chunk (%lu bytes)\n", chunk_name, length); + if(png_ptr == NULL) return; + + /* write the length */ + png_save_uint_32(buf, length); + png_write_data(png_ptr, buf, (png_size_t)4); + + /* write the chunk name */ + png_write_data(png_ptr, chunk_name, (png_size_t)4); + /* reset the crc and run it over the chunk name */ + png_reset_crc(png_ptr); + png_calculate_crc(png_ptr, chunk_name, (png_size_t)4); +} + +/* Write the data of a PNG chunk started with png_write_chunk_start(). + * Note that multiple calls to this function are allowed, and that the + * sum of the lengths from these calls *must* add up to the total_length + * given to png_write_chunk_start(). + */ +void PNGAPI +png_write_chunk_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + /* write the data, and run the CRC over it */ + if(png_ptr == NULL) return; + if (data != NULL && length > 0) + { + png_calculate_crc(png_ptr, data, length); + png_write_data(png_ptr, data, length); + } +} + +/* Finish a chunk started with png_write_chunk_start(). */ +void PNGAPI +png_write_chunk_end(png_structp png_ptr) +{ + png_byte buf[4]; + + if(png_ptr == NULL) return; + + /* write the crc */ + png_save_uint_32(buf, png_ptr->crc); + + png_write_data(png_ptr, buf, (png_size_t)4); +} + +/* Simple function to write the signature. If we have already written + * the magic bytes of the signature, or more likely, the PNG stream is + * being embedded into another stream and doesn't need its own signature, + * we should call png_set_sig_bytes() to tell libpng how many of the + * bytes have already been written. + */ +void /* PRIVATE */ +png_write_sig(png_structp png_ptr) +{ + png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10}; + /* write the rest of the 8 byte signature */ + png_write_data(png_ptr, &png_signature[png_ptr->sig_bytes], + (png_size_t)8 - png_ptr->sig_bytes); + if(png_ptr->sig_bytes < 3) + png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE; +} + +#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_iCCP_SUPPORTED) +/* + * This pair of functions encapsulates the operation of (a) compressing a + * text string, and (b) issuing it later as a series of chunk data writes. + * The compression_state structure is shared context for these functions + * set up by the caller in order to make the whole mess thread-safe. + */ + +typedef struct +{ + char *input; /* the uncompressed input data */ + int input_len; /* its length */ + int num_output_ptr; /* number of output pointers used */ + int max_output_ptr; /* size of output_ptr */ + png_charpp output_ptr; /* array of pointers to output */ +} compression_state; + +/* compress given text into storage in the png_ptr structure */ +static int /* PRIVATE */ +png_text_compress(png_structp png_ptr, + png_charp text, png_size_t text_len, int compression, + compression_state *comp) +{ + int ret; + + comp->num_output_ptr = 0; + comp->max_output_ptr = 0; + comp->output_ptr = NULL; + comp->input = NULL; + comp->input_len = 0; + + /* we may just want to pass the text right through */ + if (compression == PNG_TEXT_COMPRESSION_NONE) + { + comp->input = text; + comp->input_len = text_len; + return((int)text_len); + } + + if (compression >= PNG_TEXT_COMPRESSION_LAST) + { +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + char msg[50]; + png_snprintf(msg, 50, "Unknown compression type %d", compression); + png_warning(png_ptr, msg); +#else + png_warning(png_ptr, "Unknown compression type"); +#endif + } + + /* We can't write the chunk until we find out how much data we have, + * which means we need to run the compressor first and save the + * output. This shouldn't be a problem, as the vast majority of + * comments should be reasonable, but we will set up an array of + * malloc'd pointers to be sure. + * + * If we knew the application was well behaved, we could simplify this + * greatly by assuming we can always malloc an output buffer large + * enough to hold the compressed text ((1001 * text_len / 1000) + 12) + * and malloc this directly. The only time this would be a bad idea is + * if we can't malloc more than 64K and we have 64K of random input + * data, or if the input string is incredibly large (although this + * wouldn't cause a failure, just a slowdown due to swapping). + */ + + /* set up the compression buffers */ + png_ptr->zstream.avail_in = (uInt)text_len; + png_ptr->zstream.next_in = (Bytef *)text; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_out = (Bytef *)png_ptr->zbuf; + + /* this is the same compression loop as in png_write_row() */ + do + { + /* compress the data */ + ret = deflate(&png_ptr->zstream, Z_NO_FLUSH); + if (ret != Z_OK) + { + /* error */ + if (png_ptr->zstream.msg != NULL) + png_error(png_ptr, png_ptr->zstream.msg); + else + png_error(png_ptr, "zlib error"); + } + /* check to see if we need more room */ + if (!(png_ptr->zstream.avail_out)) + { + /* make sure the output array has room */ + if (comp->num_output_ptr >= comp->max_output_ptr) + { + int old_max; + + old_max = comp->max_output_ptr; + comp->max_output_ptr = comp->num_output_ptr + 4; + if (comp->output_ptr != NULL) + { + png_charpp old_ptr; + + old_ptr = comp->output_ptr; + comp->output_ptr = (png_charpp)png_malloc(png_ptr, + (png_uint_32)(comp->max_output_ptr * + png_sizeof (png_charpp))); + png_memcpy(comp->output_ptr, old_ptr, old_max + * png_sizeof (png_charp)); + png_free(png_ptr, old_ptr); + } + else + comp->output_ptr = (png_charpp)png_malloc(png_ptr, + (png_uint_32)(comp->max_output_ptr * + png_sizeof (png_charp))); + } + + /* save the data */ + comp->output_ptr[comp->num_output_ptr] = (png_charp)png_malloc(png_ptr, + (png_uint_32)png_ptr->zbuf_size); + png_memcpy(comp->output_ptr[comp->num_output_ptr], png_ptr->zbuf, + png_ptr->zbuf_size); + comp->num_output_ptr++; + + /* and reset the buffer */ + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_out = png_ptr->zbuf; + } + /* continue until we don't have any more to compress */ + } while (png_ptr->zstream.avail_in); + + /* finish the compression */ + do + { + /* tell zlib we are finished */ + ret = deflate(&png_ptr->zstream, Z_FINISH); + + if (ret == Z_OK) + { + /* check to see if we need more room */ + if (!(png_ptr->zstream.avail_out)) + { + /* check to make sure our output array has room */ + if (comp->num_output_ptr >= comp->max_output_ptr) + { + int old_max; + + old_max = comp->max_output_ptr; + comp->max_output_ptr = comp->num_output_ptr + 4; + if (comp->output_ptr != NULL) + { + png_charpp old_ptr; + + old_ptr = comp->output_ptr; + /* This could be optimized to realloc() */ + comp->output_ptr = (png_charpp)png_malloc(png_ptr, + (png_uint_32)(comp->max_output_ptr * + png_sizeof (png_charpp))); + png_memcpy(comp->output_ptr, old_ptr, + old_max * png_sizeof (png_charp)); + png_free(png_ptr, old_ptr); + } + else + comp->output_ptr = (png_charpp)png_malloc(png_ptr, + (png_uint_32)(comp->max_output_ptr * + png_sizeof (png_charp))); + } + + /* save off the data */ + comp->output_ptr[comp->num_output_ptr] = + (png_charp)png_malloc(png_ptr, (png_uint_32)png_ptr->zbuf_size); + png_memcpy(comp->output_ptr[comp->num_output_ptr], png_ptr->zbuf, + png_ptr->zbuf_size); + comp->num_output_ptr++; + + /* and reset the buffer pointers */ + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_out = png_ptr->zbuf; + } + } + else if (ret != Z_STREAM_END) + { + /* we got an error */ + if (png_ptr->zstream.msg != NULL) + png_error(png_ptr, png_ptr->zstream.msg); + else + png_error(png_ptr, "zlib error"); + } + } while (ret != Z_STREAM_END); + + /* text length is number of buffers plus last buffer */ + text_len = png_ptr->zbuf_size * comp->num_output_ptr; + if (png_ptr->zstream.avail_out < png_ptr->zbuf_size) + text_len += png_ptr->zbuf_size - (png_size_t)png_ptr->zstream.avail_out; + + return((int)text_len); +} + +/* ship the compressed text out via chunk writes */ +static void /* PRIVATE */ +png_write_compressed_data_out(png_structp png_ptr, compression_state *comp) +{ + int i; + + /* handle the no-compression case */ + if (comp->input) + { + png_write_chunk_data(png_ptr, (png_bytep)comp->input, + (png_size_t)comp->input_len); + return; + } + + /* write saved output buffers, if any */ + for (i = 0; i < comp->num_output_ptr; i++) + { + png_write_chunk_data(png_ptr,(png_bytep)comp->output_ptr[i], + png_ptr->zbuf_size); + png_free(png_ptr, comp->output_ptr[i]); + comp->output_ptr[i]=NULL; + } + if (comp->max_output_ptr != 0) + png_free(png_ptr, comp->output_ptr); + comp->output_ptr=NULL; + /* write anything left in zbuf */ + if (png_ptr->zstream.avail_out < (png_uint_32)png_ptr->zbuf_size) + png_write_chunk_data(png_ptr, png_ptr->zbuf, + png_ptr->zbuf_size - png_ptr->zstream.avail_out); + + /* reset zlib for another zTXt/iTXt or image data */ + deflateReset(&png_ptr->zstream); + png_ptr->zstream.data_type = Z_BINARY; +} +#endif + +/* Write the IHDR chunk, and update the png_struct with the necessary + * information. Note that the rest of this code depends upon this + * information being correct. + */ +void /* PRIVATE */ +png_write_IHDR(png_structp png_ptr, png_uint_32 width, png_uint_32 height, + int bit_depth, int color_type, int compression_type, int filter_type, + int interlace_type) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_IHDR; +#endif + png_byte buf[13]; /* buffer to store the IHDR info */ + + png_debug(1, "in png_write_IHDR\n"); + /* Check that we have valid input data from the application info */ + switch (color_type) + { + case PNG_COLOR_TYPE_GRAY: + switch (bit_depth) + { + case 1: + case 2: + case 4: + case 8: + case 16: png_ptr->channels = 1; break; + default: png_error(png_ptr,"Invalid bit depth for grayscale image"); + } + break; + case PNG_COLOR_TYPE_RGB: + if (bit_depth != 8 && bit_depth != 16) + png_error(png_ptr, "Invalid bit depth for RGB image"); + png_ptr->channels = 3; + break; + case PNG_COLOR_TYPE_PALETTE: + switch (bit_depth) + { + case 1: + case 2: + case 4: + case 8: png_ptr->channels = 1; break; + default: png_error(png_ptr, "Invalid bit depth for paletted image"); + } + break; + case PNG_COLOR_TYPE_GRAY_ALPHA: + if (bit_depth != 8 && bit_depth != 16) + png_error(png_ptr, "Invalid bit depth for grayscale+alpha image"); + png_ptr->channels = 2; + break; + case PNG_COLOR_TYPE_RGB_ALPHA: + if (bit_depth != 8 && bit_depth != 16) + png_error(png_ptr, "Invalid bit depth for RGBA image"); + png_ptr->channels = 4; + break; + default: + png_error(png_ptr, "Invalid image color type specified"); + } + + if (compression_type != PNG_COMPRESSION_TYPE_BASE) + { + png_warning(png_ptr, "Invalid compression type specified"); + compression_type = PNG_COMPRESSION_TYPE_BASE; + } + + /* Write filter_method 64 (intrapixel differencing) only if + * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and + * 2. Libpng did not write a PNG signature (this filter_method is only + * used in PNG datastreams that are embedded in MNG datastreams) and + * 3. The application called png_permit_mng_features with a mask that + * included PNG_FLAG_MNG_FILTER_64 and + * 4. The filter_method is 64 and + * 5. The color_type is RGB or RGBA + */ + if ( +#if defined(PNG_MNG_FEATURES_SUPPORTED) + !((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) == 0) && + (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_RGB_ALPHA) && + (filter_type == PNG_INTRAPIXEL_DIFFERENCING)) && +#endif + filter_type != PNG_FILTER_TYPE_BASE) + { + png_warning(png_ptr, "Invalid filter type specified"); + filter_type = PNG_FILTER_TYPE_BASE; + } + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + if (interlace_type != PNG_INTERLACE_NONE && + interlace_type != PNG_INTERLACE_ADAM7) + { + png_warning(png_ptr, "Invalid interlace type specified"); + interlace_type = PNG_INTERLACE_ADAM7; + } +#else + interlace_type=PNG_INTERLACE_NONE; +#endif + + /* save off the relevent information */ + png_ptr->bit_depth = (png_byte)bit_depth; + png_ptr->color_type = (png_byte)color_type; + png_ptr->interlaced = (png_byte)interlace_type; +#if defined(PNG_MNG_FEATURES_SUPPORTED) + png_ptr->filter_type = (png_byte)filter_type; +#endif + png_ptr->compression_type = (png_byte)compression_type; + png_ptr->width = width; + png_ptr->height = height; + + png_ptr->pixel_depth = (png_byte)(bit_depth * png_ptr->channels); + png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, width); + /* set the usr info, so any transformations can modify it */ + png_ptr->usr_width = png_ptr->width; + png_ptr->usr_bit_depth = png_ptr->bit_depth; + png_ptr->usr_channels = png_ptr->channels; + + /* pack the header information into the buffer */ + png_save_uint_32(buf, width); + png_save_uint_32(buf + 4, height); + buf[8] = (png_byte)bit_depth; + buf[9] = (png_byte)color_type; + buf[10] = (png_byte)compression_type; + buf[11] = (png_byte)filter_type; + buf[12] = (png_byte)interlace_type; + + /* write the chunk */ + png_write_chunk(png_ptr, png_IHDR, buf, (png_size_t)13); + + /* initialize zlib with PNG info */ + png_ptr->zstream.zalloc = png_zalloc; + png_ptr->zstream.zfree = png_zfree; + png_ptr->zstream.opaque = (voidpf)png_ptr; + if (!(png_ptr->do_filter)) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE || + png_ptr->bit_depth < 8) + png_ptr->do_filter = PNG_FILTER_NONE; + else + png_ptr->do_filter = PNG_ALL_FILTERS; + } + if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_STRATEGY)) + { + if (png_ptr->do_filter != PNG_FILTER_NONE) + png_ptr->zlib_strategy = Z_FILTERED; + else + png_ptr->zlib_strategy = Z_DEFAULT_STRATEGY; + } + if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_LEVEL)) + png_ptr->zlib_level = Z_DEFAULT_COMPRESSION; + if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL)) + png_ptr->zlib_mem_level = 8; + if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS)) + png_ptr->zlib_window_bits = 15; + if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_METHOD)) + png_ptr->zlib_method = 8; + if (deflateInit2(&png_ptr->zstream, png_ptr->zlib_level, + png_ptr->zlib_method, png_ptr->zlib_window_bits, + png_ptr->zlib_mem_level, png_ptr->zlib_strategy) != Z_OK) + png_error(png_ptr, "zlib failed to initialize compressor"); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + /* libpng is not interested in zstream.data_type */ + /* set it to a predefined value, to avoid its evaluation inside zlib */ + png_ptr->zstream.data_type = Z_BINARY; + + png_ptr->mode = PNG_HAVE_IHDR; +} + +/* write the palette. We are careful not to trust png_color to be in the + * correct order for PNG, so people can redefine it to any convenient + * structure. + */ +void /* PRIVATE */ +png_write_PLTE(png_structp png_ptr, png_colorp palette, png_uint_32 num_pal) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_PLTE; +#endif + png_uint_32 i; + png_colorp pal_ptr; + png_byte buf[3]; + + png_debug(1, "in png_write_PLTE\n"); + if (( +#if defined(PNG_MNG_FEATURES_SUPPORTED) + !(png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) && +#endif + num_pal == 0) || num_pal > 256) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + png_error(png_ptr, "Invalid number of colors in palette"); + } + else + { + png_warning(png_ptr, "Invalid number of colors in palette"); + return; + } + } + + if (!(png_ptr->color_type&PNG_COLOR_MASK_COLOR)) + { + png_warning(png_ptr, + "Ignoring request to write a PLTE chunk in grayscale PNG"); + return; + } + + png_ptr->num_palette = (png_uint_16)num_pal; + png_debug1(3, "num_palette = %d\n", png_ptr->num_palette); + + png_write_chunk_start(png_ptr, png_PLTE, num_pal * 3); +#ifndef PNG_NO_POINTER_INDEXING + for (i = 0, pal_ptr = palette; i < num_pal; i++, pal_ptr++) + { + buf[0] = pal_ptr->red; + buf[1] = pal_ptr->green; + buf[2] = pal_ptr->blue; + png_write_chunk_data(png_ptr, buf, (png_size_t)3); + } +#else + /* This is a little slower but some buggy compilers need to do this instead */ + pal_ptr=palette; + for (i = 0; i < num_pal; i++) + { + buf[0] = pal_ptr[i].red; + buf[1] = pal_ptr[i].green; + buf[2] = pal_ptr[i].blue; + png_write_chunk_data(png_ptr, buf, (png_size_t)3); + } +#endif + png_write_chunk_end(png_ptr); + png_ptr->mode |= PNG_HAVE_PLTE; +} + +/* write an IDAT chunk */ +void /* PRIVATE */ +png_write_IDAT(png_structp png_ptr, png_bytep data, png_size_t length) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_IDAT; +#endif + png_debug(1, "in png_write_IDAT\n"); + + /* Optimize the CMF field in the zlib stream. */ + /* This hack of the zlib stream is compliant to the stream specification. */ + if (!(png_ptr->mode & PNG_HAVE_IDAT) && + png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE) + { + unsigned int z_cmf = data[0]; /* zlib compression method and flags */ + if ((z_cmf & 0x0f) == 8 && (z_cmf & 0xf0) <= 0x70) + { + /* Avoid memory underflows and multiplication overflows. */ + /* The conditions below are practically always satisfied; + however, they still must be checked. */ + if (length >= 2 && + png_ptr->height < 16384 && png_ptr->width < 16384) + { + png_uint_32 uncompressed_idat_size = png_ptr->height * + ((png_ptr->width * + png_ptr->channels * png_ptr->bit_depth + 15) >> 3); + unsigned int z_cinfo = z_cmf >> 4; + unsigned int half_z_window_size = 1 << (z_cinfo + 7); + while (uncompressed_idat_size <= half_z_window_size && + half_z_window_size >= 256) + { + z_cinfo--; + half_z_window_size >>= 1; + } + z_cmf = (z_cmf & 0x0f) | (z_cinfo << 4); + if (data[0] != (png_byte)z_cmf) + { + data[0] = (png_byte)z_cmf; + data[1] &= 0xe0; + data[1] += (png_byte)(0x1f - ((z_cmf << 8) + data[1]) % 0x1f); + } + } + } + else + png_error(png_ptr, + "Invalid zlib compression method or flags in IDAT"); + } + + png_write_chunk(png_ptr, png_IDAT, data, length); + png_ptr->mode |= PNG_HAVE_IDAT; +} + +/* write an IEND chunk */ +void /* PRIVATE */ +png_write_IEND(png_structp png_ptr) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_IEND; +#endif + png_debug(1, "in png_write_IEND\n"); + png_write_chunk(png_ptr, png_IEND, png_bytep_NULL, + (png_size_t)0); + png_ptr->mode |= PNG_HAVE_IEND; +} + +#if defined(PNG_WRITE_gAMA_SUPPORTED) +/* write a gAMA chunk */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +void /* PRIVATE */ +png_write_gAMA(png_structp png_ptr, double file_gamma) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_gAMA; +#endif + png_uint_32 igamma; + png_byte buf[4]; + + png_debug(1, "in png_write_gAMA\n"); + /* file_gamma is saved in 1/100,000ths */ + igamma = (png_uint_32)(file_gamma * 100000.0 + 0.5); + png_save_uint_32(buf, igamma); + png_write_chunk(png_ptr, png_gAMA, buf, (png_size_t)4); +} +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +void /* PRIVATE */ +png_write_gAMA_fixed(png_structp png_ptr, png_fixed_point file_gamma) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_gAMA; +#endif + png_byte buf[4]; + + png_debug(1, "in png_write_gAMA\n"); + /* file_gamma is saved in 1/100,000ths */ + png_save_uint_32(buf, (png_uint_32)file_gamma); + png_write_chunk(png_ptr, png_gAMA, buf, (png_size_t)4); +} +#endif +#endif + +#if defined(PNG_WRITE_sRGB_SUPPORTED) +/* write a sRGB chunk */ +void /* PRIVATE */ +png_write_sRGB(png_structp png_ptr, int srgb_intent) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_sRGB; +#endif + png_byte buf[1]; + + png_debug(1, "in png_write_sRGB\n"); + if(srgb_intent >= PNG_sRGB_INTENT_LAST) + png_warning(png_ptr, + "Invalid sRGB rendering intent specified"); + buf[0]=(png_byte)srgb_intent; + png_write_chunk(png_ptr, png_sRGB, buf, (png_size_t)1); +} +#endif + +#if defined(PNG_WRITE_iCCP_SUPPORTED) +/* write an iCCP chunk */ +void /* PRIVATE */ +png_write_iCCP(png_structp png_ptr, png_charp name, int compression_type, + png_charp profile, int profile_len) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_iCCP; +#endif + png_size_t name_len; + png_charp new_name; + compression_state comp; + int embedded_profile_len = 0; + + png_debug(1, "in png_write_iCCP\n"); + + comp.num_output_ptr = 0; + comp.max_output_ptr = 0; + comp.output_ptr = NULL; + comp.input = NULL; + comp.input_len = 0; + + if (name == NULL || (name_len = png_check_keyword(png_ptr, name, + &new_name)) == 0) + { + png_warning(png_ptr, "Empty keyword in iCCP chunk"); + return; + } + + if (compression_type != PNG_COMPRESSION_TYPE_BASE) + png_warning(png_ptr, "Unknown compression type in iCCP chunk"); + + if (profile == NULL) + profile_len = 0; + + if (profile_len > 3) + embedded_profile_len = + ((*( (png_bytep)profile ))<<24) | + ((*( (png_bytep)profile+1))<<16) | + ((*( (png_bytep)profile+2))<< 8) | + ((*( (png_bytep)profile+3)) ); + + if (profile_len < embedded_profile_len) + { + png_warning(png_ptr, + "Embedded profile length too large in iCCP chunk"); + return; + } + + if (profile_len > embedded_profile_len) + { + png_warning(png_ptr, + "Truncating profile to actual length in iCCP chunk"); + profile_len = embedded_profile_len; + } + + if (profile_len) + profile_len = png_text_compress(png_ptr, profile, (png_size_t)profile_len, + PNG_COMPRESSION_TYPE_BASE, &comp); + + /* make sure we include the NULL after the name and the compression type */ + png_write_chunk_start(png_ptr, png_iCCP, + (png_uint_32)name_len+profile_len+2); + new_name[name_len+1]=0x00; + png_write_chunk_data(png_ptr, (png_bytep)new_name, name_len + 2); + + if (profile_len) + png_write_compressed_data_out(png_ptr, &comp); + + png_write_chunk_end(png_ptr); + png_free(png_ptr, new_name); +} +#endif + +#if defined(PNG_WRITE_sPLT_SUPPORTED) +/* write a sPLT chunk */ +void /* PRIVATE */ +png_write_sPLT(png_structp png_ptr, png_sPLT_tp spalette) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_sPLT; +#endif + png_size_t name_len; + png_charp new_name; + png_byte entrybuf[10]; + int entry_size = (spalette->depth == 8 ? 6 : 10); + int palette_size = entry_size * spalette->nentries; + png_sPLT_entryp ep; +#ifdef PNG_NO_POINTER_INDEXING + int i; +#endif + + png_debug(1, "in png_write_sPLT\n"); + if (spalette->name == NULL || (name_len = png_check_keyword(png_ptr, + spalette->name, &new_name))==0) + { + png_warning(png_ptr, "Empty keyword in sPLT chunk"); + return; + } + + /* make sure we include the NULL after the name */ + png_write_chunk_start(png_ptr, png_sPLT, + (png_uint_32)(name_len + 2 + palette_size)); + png_write_chunk_data(png_ptr, (png_bytep)new_name, name_len + 1); + png_write_chunk_data(png_ptr, (png_bytep)&spalette->depth, 1); + + /* loop through each palette entry, writing appropriately */ +#ifndef PNG_NO_POINTER_INDEXING + for (ep = spalette->entries; epentries+spalette->nentries; ep++) + { + if (spalette->depth == 8) + { + entrybuf[0] = (png_byte)ep->red; + entrybuf[1] = (png_byte)ep->green; + entrybuf[2] = (png_byte)ep->blue; + entrybuf[3] = (png_byte)ep->alpha; + png_save_uint_16(entrybuf + 4, ep->frequency); + } + else + { + png_save_uint_16(entrybuf + 0, ep->red); + png_save_uint_16(entrybuf + 2, ep->green); + png_save_uint_16(entrybuf + 4, ep->blue); + png_save_uint_16(entrybuf + 6, ep->alpha); + png_save_uint_16(entrybuf + 8, ep->frequency); + } + png_write_chunk_data(png_ptr, entrybuf, (png_size_t)entry_size); + } +#else + ep=spalette->entries; + for (i=0; i>spalette->nentries; i++) + { + if (spalette->depth == 8) + { + entrybuf[0] = (png_byte)ep[i].red; + entrybuf[1] = (png_byte)ep[i].green; + entrybuf[2] = (png_byte)ep[i].blue; + entrybuf[3] = (png_byte)ep[i].alpha; + png_save_uint_16(entrybuf + 4, ep[i].frequency); + } + else + { + png_save_uint_16(entrybuf + 0, ep[i].red); + png_save_uint_16(entrybuf + 2, ep[i].green); + png_save_uint_16(entrybuf + 4, ep[i].blue); + png_save_uint_16(entrybuf + 6, ep[i].alpha); + png_save_uint_16(entrybuf + 8, ep[i].frequency); + } + png_write_chunk_data(png_ptr, entrybuf, entry_size); + } +#endif + + png_write_chunk_end(png_ptr); + png_free(png_ptr, new_name); +} +#endif + +#if defined(PNG_WRITE_sBIT_SUPPORTED) +/* write the sBIT chunk */ +void /* PRIVATE */ +png_write_sBIT(png_structp png_ptr, png_color_8p sbit, int color_type) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_sBIT; +#endif + png_byte buf[4]; + png_size_t size; + + png_debug(1, "in png_write_sBIT\n"); + /* make sure we don't depend upon the order of PNG_COLOR_8 */ + if (color_type & PNG_COLOR_MASK_COLOR) + { + png_byte maxbits; + + maxbits = (png_byte)(color_type==PNG_COLOR_TYPE_PALETTE ? 8 : + png_ptr->usr_bit_depth); + if (sbit->red == 0 || sbit->red > maxbits || + sbit->green == 0 || sbit->green > maxbits || + sbit->blue == 0 || sbit->blue > maxbits) + { + png_warning(png_ptr, "Invalid sBIT depth specified"); + return; + } + buf[0] = sbit->red; + buf[1] = sbit->green; + buf[2] = sbit->blue; + size = 3; + } + else + { + if (sbit->gray == 0 || sbit->gray > png_ptr->usr_bit_depth) + { + png_warning(png_ptr, "Invalid sBIT depth specified"); + return; + } + buf[0] = sbit->gray; + size = 1; + } + + if (color_type & PNG_COLOR_MASK_ALPHA) + { + if (sbit->alpha == 0 || sbit->alpha > png_ptr->usr_bit_depth) + { + png_warning(png_ptr, "Invalid sBIT depth specified"); + return; + } + buf[size++] = sbit->alpha; + } + + png_write_chunk(png_ptr, png_sBIT, buf, size); +} +#endif + +#if defined(PNG_WRITE_cHRM_SUPPORTED) +/* write the cHRM chunk */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +void /* PRIVATE */ +png_write_cHRM(png_structp png_ptr, double white_x, double white_y, + double red_x, double red_y, double green_x, double green_y, + double blue_x, double blue_y) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_cHRM; +#endif + png_byte buf[32]; + png_uint_32 itemp; + + png_debug(1, "in png_write_cHRM\n"); + /* each value is saved in 1/100,000ths */ + if (white_x < 0 || white_x > 0.8 || white_y < 0 || white_y > 0.8 || + white_x + white_y > 1.0) + { + png_warning(png_ptr, "Invalid cHRM white point specified"); +#if !defined(PNG_NO_CONSOLE_IO) + fprintf(stderr,"white_x=%f, white_y=%f\n",white_x, white_y); +#endif + return; + } + itemp = (png_uint_32)(white_x * 100000.0 + 0.5); + png_save_uint_32(buf, itemp); + itemp = (png_uint_32)(white_y * 100000.0 + 0.5); + png_save_uint_32(buf + 4, itemp); + + if (red_x < 0 || red_y < 0 || red_x + red_y > 1.0) + { + png_warning(png_ptr, "Invalid cHRM red point specified"); + return; + } + itemp = (png_uint_32)(red_x * 100000.0 + 0.5); + png_save_uint_32(buf + 8, itemp); + itemp = (png_uint_32)(red_y * 100000.0 + 0.5); + png_save_uint_32(buf + 12, itemp); + + if (green_x < 0 || green_y < 0 || green_x + green_y > 1.0) + { + png_warning(png_ptr, "Invalid cHRM green point specified"); + return; + } + itemp = (png_uint_32)(green_x * 100000.0 + 0.5); + png_save_uint_32(buf + 16, itemp); + itemp = (png_uint_32)(green_y * 100000.0 + 0.5); + png_save_uint_32(buf + 20, itemp); + + if (blue_x < 0 || blue_y < 0 || blue_x + blue_y > 1.0) + { + png_warning(png_ptr, "Invalid cHRM blue point specified"); + return; + } + itemp = (png_uint_32)(blue_x * 100000.0 + 0.5); + png_save_uint_32(buf + 24, itemp); + itemp = (png_uint_32)(blue_y * 100000.0 + 0.5); + png_save_uint_32(buf + 28, itemp); + + png_write_chunk(png_ptr, png_cHRM, buf, (png_size_t)32); +} +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +void /* PRIVATE */ +png_write_cHRM_fixed(png_structp png_ptr, png_fixed_point white_x, + png_fixed_point white_y, png_fixed_point red_x, png_fixed_point red_y, + png_fixed_point green_x, png_fixed_point green_y, png_fixed_point blue_x, + png_fixed_point blue_y) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_cHRM; +#endif + png_byte buf[32]; + + png_debug(1, "in png_write_cHRM\n"); + /* each value is saved in 1/100,000ths */ + if (white_x > 80000L || white_y > 80000L || white_x + white_y > 100000L) + { + png_warning(png_ptr, "Invalid fixed cHRM white point specified"); +#if !defined(PNG_NO_CONSOLE_IO) + fprintf(stderr,"white_x=%ld, white_y=%ld\n",white_x, white_y); +#endif + return; + } + png_save_uint_32(buf, (png_uint_32)white_x); + png_save_uint_32(buf + 4, (png_uint_32)white_y); + + if (red_x + red_y > 100000L) + { + png_warning(png_ptr, "Invalid cHRM fixed red point specified"); + return; + } + png_save_uint_32(buf + 8, (png_uint_32)red_x); + png_save_uint_32(buf + 12, (png_uint_32)red_y); + + if (green_x + green_y > 100000L) + { + png_warning(png_ptr, "Invalid fixed cHRM green point specified"); + return; + } + png_save_uint_32(buf + 16, (png_uint_32)green_x); + png_save_uint_32(buf + 20, (png_uint_32)green_y); + + if (blue_x + blue_y > 100000L) + { + png_warning(png_ptr, "Invalid fixed cHRM blue point specified"); + return; + } + png_save_uint_32(buf + 24, (png_uint_32)blue_x); + png_save_uint_32(buf + 28, (png_uint_32)blue_y); + + png_write_chunk(png_ptr, png_cHRM, buf, (png_size_t)32); +} +#endif +#endif + +#if defined(PNG_WRITE_tRNS_SUPPORTED) +/* write the tRNS chunk */ +void /* PRIVATE */ +png_write_tRNS(png_structp png_ptr, png_bytep trans, png_color_16p tran, + int num_trans, int color_type) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_tRNS; +#endif + png_byte buf[6]; + + png_debug(1, "in png_write_tRNS\n"); + if (color_type == PNG_COLOR_TYPE_PALETTE) + { + if (num_trans <= 0 || num_trans > (int)png_ptr->num_palette) + { + png_warning(png_ptr,"Invalid number of transparent colors specified"); + return; + } + /* write the chunk out as it is */ + png_write_chunk(png_ptr, png_tRNS, trans, (png_size_t)num_trans); + } + else if (color_type == PNG_COLOR_TYPE_GRAY) + { + /* one 16 bit value */ + if(tran->gray >= (1 << png_ptr->bit_depth)) + { + png_warning(png_ptr, + "Ignoring attempt to write tRNS chunk out-of-range for bit_depth"); + return; + } + png_save_uint_16(buf, tran->gray); + png_write_chunk(png_ptr, png_tRNS, buf, (png_size_t)2); + } + else if (color_type == PNG_COLOR_TYPE_RGB) + { + /* three 16 bit values */ + png_save_uint_16(buf, tran->red); + png_save_uint_16(buf + 2, tran->green); + png_save_uint_16(buf + 4, tran->blue); + if(png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4])) + { + png_warning(png_ptr, + "Ignoring attempt to write 16-bit tRNS chunk when bit_depth is 8"); + return; + } + png_write_chunk(png_ptr, png_tRNS, buf, (png_size_t)6); + } + else + { + png_warning(png_ptr, "Can't write tRNS with an alpha channel"); + } +} +#endif + +#if defined(PNG_WRITE_bKGD_SUPPORTED) +/* write the background chunk */ +void /* PRIVATE */ +png_write_bKGD(png_structp png_ptr, png_color_16p back, int color_type) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_bKGD; +#endif + png_byte buf[6]; + + png_debug(1, "in png_write_bKGD\n"); + if (color_type == PNG_COLOR_TYPE_PALETTE) + { + if ( +#if defined(PNG_MNG_FEATURES_SUPPORTED) + (png_ptr->num_palette || + (!(png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE))) && +#endif + back->index > png_ptr->num_palette) + { + png_warning(png_ptr, "Invalid background palette index"); + return; + } + buf[0] = back->index; + png_write_chunk(png_ptr, png_bKGD, buf, (png_size_t)1); + } + else if (color_type & PNG_COLOR_MASK_COLOR) + { + png_save_uint_16(buf, back->red); + png_save_uint_16(buf + 2, back->green); + png_save_uint_16(buf + 4, back->blue); + if(png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4])) + { + png_warning(png_ptr, + "Ignoring attempt to write 16-bit bKGD chunk when bit_depth is 8"); + return; + } + png_write_chunk(png_ptr, png_bKGD, buf, (png_size_t)6); + } + else + { + if(back->gray >= (1 << png_ptr->bit_depth)) + { + png_warning(png_ptr, + "Ignoring attempt to write bKGD chunk out-of-range for bit_depth"); + return; + } + png_save_uint_16(buf, back->gray); + png_write_chunk(png_ptr, png_bKGD, buf, (png_size_t)2); + } +} +#endif + +#if defined(PNG_WRITE_hIST_SUPPORTED) +/* write the histogram */ +void /* PRIVATE */ +png_write_hIST(png_structp png_ptr, png_uint_16p hist, int num_hist) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_hIST; +#endif + int i; + png_byte buf[3]; + + png_debug(1, "in png_write_hIST\n"); + if (num_hist > (int)png_ptr->num_palette) + { + png_debug2(3, "num_hist = %d, num_palette = %d\n", num_hist, + png_ptr->num_palette); + png_warning(png_ptr, "Invalid number of histogram entries specified"); + return; + } + + png_write_chunk_start(png_ptr, png_hIST, (png_uint_32)(num_hist * 2)); + for (i = 0; i < num_hist; i++) + { + png_save_uint_16(buf, hist[i]); + png_write_chunk_data(png_ptr, buf, (png_size_t)2); + } + png_write_chunk_end(png_ptr); +} +#endif + +#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED) || \ + defined(PNG_WRITE_iCCP_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED) +/* Check that the tEXt or zTXt keyword is valid per PNG 1.0 specification, + * and if invalid, correct the keyword rather than discarding the entire + * chunk. The PNG 1.0 specification requires keywords 1-79 characters in + * length, forbids leading or trailing whitespace, multiple internal spaces, + * and the non-break space (0x80) from ISO 8859-1. Returns keyword length. + * + * The new_key is allocated to hold the corrected keyword and must be freed + * by the calling routine. This avoids problems with trying to write to + * static keywords without having to have duplicate copies of the strings. + */ +png_size_t /* PRIVATE */ +png_check_keyword(png_structp png_ptr, png_charp key, png_charpp new_key) +{ + png_size_t key_len; + png_charp kp, dp; + int kflag; + int kwarn=0; + + png_debug(1, "in png_check_keyword\n"); + *new_key = NULL; + + if (key == NULL || (key_len = png_strlen(key)) == 0) + { + png_warning(png_ptr, "zero length keyword"); + return ((png_size_t)0); + } + + png_debug1(2, "Keyword to be checked is '%s'\n", key); + + *new_key = (png_charp)png_malloc_warn(png_ptr, (png_uint_32)(key_len + 2)); + if (*new_key == NULL) + { + png_warning(png_ptr, "Out of memory while procesing keyword"); + return ((png_size_t)0); + } + + /* Replace non-printing characters with a blank and print a warning */ + for (kp = key, dp = *new_key; *kp != '\0'; kp++, dp++) + { + if ((png_byte)*kp < 0x20 || + ((png_byte)*kp > 0x7E && (png_byte)*kp < 0xA1)) + { +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + char msg[40]; + + png_snprintf(msg, 40, + "invalid keyword character 0x%02X", (png_byte)*kp); + png_warning(png_ptr, msg); +#else + png_warning(png_ptr, "invalid character in keyword"); +#endif + *dp = ' '; + } + else + { + *dp = *kp; + } + } + *dp = '\0'; + + /* Remove any trailing white space. */ + kp = *new_key + key_len - 1; + if (*kp == ' ') + { + png_warning(png_ptr, "trailing spaces removed from keyword"); + + while (*kp == ' ') + { + *(kp--) = '\0'; + key_len--; + } + } + + /* Remove any leading white space. */ + kp = *new_key; + if (*kp == ' ') + { + png_warning(png_ptr, "leading spaces removed from keyword"); + + while (*kp == ' ') + { + kp++; + key_len--; + } + } + + png_debug1(2, "Checking for multiple internal spaces in '%s'\n", kp); + + /* Remove multiple internal spaces. */ + for (kflag = 0, dp = *new_key; *kp != '\0'; kp++) + { + if (*kp == ' ' && kflag == 0) + { + *(dp++) = *kp; + kflag = 1; + } + else if (*kp == ' ') + { + key_len--; + kwarn=1; + } + else + { + *(dp++) = *kp; + kflag = 0; + } + } + *dp = '\0'; + if(kwarn) + png_warning(png_ptr, "extra interior spaces removed from keyword"); + + if (key_len == 0) + { + png_free(png_ptr, *new_key); + *new_key=NULL; + png_warning(png_ptr, "Zero length keyword"); + } + + if (key_len > 79) + { + png_warning(png_ptr, "keyword length must be 1 - 79 characters"); + new_key[79] = '\0'; + key_len = 79; + } + + return (key_len); +} +#endif + +#if defined(PNG_WRITE_tEXt_SUPPORTED) +/* write a tEXt chunk */ +void /* PRIVATE */ +png_write_tEXt(png_structp png_ptr, png_charp key, png_charp text, + png_size_t text_len) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_tEXt; +#endif + png_size_t key_len; + png_charp new_key; + + png_debug(1, "in png_write_tEXt\n"); + if (key == NULL || (key_len = png_check_keyword(png_ptr, key, &new_key))==0) + { + png_warning(png_ptr, "Empty keyword in tEXt chunk"); + return; + } + + if (text == NULL || *text == '\0') + text_len = 0; + else + text_len = png_strlen(text); + + /* make sure we include the 0 after the key */ + png_write_chunk_start(png_ptr, png_tEXt, (png_uint_32)key_len+text_len+1); + /* + * We leave it to the application to meet PNG-1.0 requirements on the + * contents of the text. PNG-1.0 through PNG-1.2 discourage the use of + * any non-Latin-1 characters except for NEWLINE. ISO PNG will forbid them. + * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG. + */ + png_write_chunk_data(png_ptr, (png_bytep)new_key, key_len + 1); + if (text_len) + png_write_chunk_data(png_ptr, (png_bytep)text, text_len); + + png_write_chunk_end(png_ptr); + png_free(png_ptr, new_key); +} +#endif + +#if defined(PNG_WRITE_zTXt_SUPPORTED) +/* write a compressed text chunk */ +void /* PRIVATE */ +png_write_zTXt(png_structp png_ptr, png_charp key, png_charp text, + png_size_t text_len, int compression) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_zTXt; +#endif + png_size_t key_len; + char buf[1]; + png_charp new_key; + compression_state comp; + + png_debug(1, "in png_write_zTXt\n"); + + comp.num_output_ptr = 0; + comp.max_output_ptr = 0; + comp.output_ptr = NULL; + comp.input = NULL; + comp.input_len = 0; + + if (key == NULL || (key_len = png_check_keyword(png_ptr, key, &new_key))==0) + { + png_warning(png_ptr, "Empty keyword in zTXt chunk"); + return; + } + + if (text == NULL || *text == '\0' || compression==PNG_TEXT_COMPRESSION_NONE) + { + png_write_tEXt(png_ptr, new_key, text, (png_size_t)0); + png_free(png_ptr, new_key); + return; + } + + text_len = png_strlen(text); + + /* compute the compressed data; do it now for the length */ + text_len = png_text_compress(png_ptr, text, text_len, compression, + &comp); + + /* write start of chunk */ + png_write_chunk_start(png_ptr, png_zTXt, (png_uint_32) + (key_len+text_len+2)); + /* write key */ + png_write_chunk_data(png_ptr, (png_bytep)new_key, key_len + 1); + png_free(png_ptr, new_key); + + buf[0] = (png_byte)compression; + /* write compression */ + png_write_chunk_data(png_ptr, (png_bytep)buf, (png_size_t)1); + /* write the compressed data */ + png_write_compressed_data_out(png_ptr, &comp); + + /* close the chunk */ + png_write_chunk_end(png_ptr); +} +#endif + +#if defined(PNG_WRITE_iTXt_SUPPORTED) +/* write an iTXt chunk */ +void /* PRIVATE */ +png_write_iTXt(png_structp png_ptr, int compression, png_charp key, + png_charp lang, png_charp lang_key, png_charp text) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_iTXt; +#endif + png_size_t lang_len, key_len, lang_key_len, text_len; + png_charp new_lang, new_key; + png_byte cbuf[2]; + compression_state comp; + + png_debug(1, "in png_write_iTXt\n"); + + comp.num_output_ptr = 0; + comp.max_output_ptr = 0; + comp.output_ptr = NULL; + comp.input = NULL; + + if (key == NULL || (key_len = png_check_keyword(png_ptr, key, &new_key))==0) + { + png_warning(png_ptr, "Empty keyword in iTXt chunk"); + return; + } + if (lang == NULL || (lang_len = png_check_keyword(png_ptr, lang, &new_lang))==0) + { + png_warning(png_ptr, "Empty language field in iTXt chunk"); + new_lang = NULL; + lang_len = 0; + } + + if (lang_key == NULL) + lang_key_len = 0; + else + lang_key_len = png_strlen(lang_key); + + if (text == NULL) + text_len = 0; + else + text_len = png_strlen(text); + + /* compute the compressed data; do it now for the length */ + text_len = png_text_compress(png_ptr, text, text_len, compression-2, + &comp); + + /* make sure we include the compression flag, the compression byte, + * and the NULs after the key, lang, and lang_key parts */ + + png_write_chunk_start(png_ptr, png_iTXt, + (png_uint_32)( + 5 /* comp byte, comp flag, terminators for key, lang and lang_key */ + + key_len + + lang_len + + lang_key_len + + text_len)); + + /* + * We leave it to the application to meet PNG-1.0 requirements on the + * contents of the text. PNG-1.0 through PNG-1.2 discourage the use of + * any non-Latin-1 characters except for NEWLINE. ISO PNG will forbid them. + * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG. + */ + png_write_chunk_data(png_ptr, (png_bytep)new_key, key_len + 1); + + /* set the compression flag */ + if (compression == PNG_ITXT_COMPRESSION_NONE || \ + compression == PNG_TEXT_COMPRESSION_NONE) + cbuf[0] = 0; + else /* compression == PNG_ITXT_COMPRESSION_zTXt */ + cbuf[0] = 1; + /* set the compression method */ + cbuf[1] = 0; + png_write_chunk_data(png_ptr, cbuf, 2); + + cbuf[0] = 0; + png_write_chunk_data(png_ptr, (new_lang ? (png_bytep)new_lang : cbuf), lang_len + 1); + png_write_chunk_data(png_ptr, (lang_key ? (png_bytep)lang_key : cbuf), lang_key_len + 1); + png_write_compressed_data_out(png_ptr, &comp); + + png_write_chunk_end(png_ptr); + png_free(png_ptr, new_key); + if (new_lang) + png_free(png_ptr, new_lang); +} +#endif + +#if defined(PNG_WRITE_oFFs_SUPPORTED) +/* write the oFFs chunk */ +void /* PRIVATE */ +png_write_oFFs(png_structp png_ptr, png_int_32 x_offset, png_int_32 y_offset, + int unit_type) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_oFFs; +#endif + png_byte buf[9]; + + png_debug(1, "in png_write_oFFs\n"); + if (unit_type >= PNG_OFFSET_LAST) + png_warning(png_ptr, "Unrecognized unit type for oFFs chunk"); + + png_save_int_32(buf, x_offset); + png_save_int_32(buf + 4, y_offset); + buf[8] = (png_byte)unit_type; + + png_write_chunk(png_ptr, png_oFFs, buf, (png_size_t)9); +} +#endif +#if defined(PNG_WRITE_pCAL_SUPPORTED) +/* write the pCAL chunk (described in the PNG extensions document) */ +void /* PRIVATE */ +png_write_pCAL(png_structp png_ptr, png_charp purpose, png_int_32 X0, + png_int_32 X1, int type, int nparams, png_charp units, png_charpp params) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_pCAL; +#endif + png_size_t purpose_len, units_len, total_len; + png_uint_32p params_len; + png_byte buf[10]; + png_charp new_purpose; + int i; + + png_debug1(1, "in png_write_pCAL (%d parameters)\n", nparams); + if (type >= PNG_EQUATION_LAST) + png_warning(png_ptr, "Unrecognized equation type for pCAL chunk"); + + purpose_len = png_check_keyword(png_ptr, purpose, &new_purpose) + 1; + png_debug1(3, "pCAL purpose length = %d\n", (int)purpose_len); + units_len = png_strlen(units) + (nparams == 0 ? 0 : 1); + png_debug1(3, "pCAL units length = %d\n", (int)units_len); + total_len = purpose_len + units_len + 10; + + params_len = (png_uint_32p)png_malloc(png_ptr, (png_uint_32)(nparams + *png_sizeof(png_uint_32))); + + /* Find the length of each parameter, making sure we don't count the + null terminator for the last parameter. */ + for (i = 0; i < nparams; i++) + { + params_len[i] = png_strlen(params[i]) + (i == nparams - 1 ? 0 : 1); + png_debug2(3, "pCAL parameter %d length = %lu\n", i, params_len[i]); + total_len += (png_size_t)params_len[i]; + } + + png_debug1(3, "pCAL total length = %d\n", (int)total_len); + png_write_chunk_start(png_ptr, png_pCAL, (png_uint_32)total_len); + png_write_chunk_data(png_ptr, (png_bytep)new_purpose, purpose_len); + png_save_int_32(buf, X0); + png_save_int_32(buf + 4, X1); + buf[8] = (png_byte)type; + buf[9] = (png_byte)nparams; + png_write_chunk_data(png_ptr, buf, (png_size_t)10); + png_write_chunk_data(png_ptr, (png_bytep)units, (png_size_t)units_len); + + png_free(png_ptr, new_purpose); + + for (i = 0; i < nparams; i++) + { + png_write_chunk_data(png_ptr, (png_bytep)params[i], + (png_size_t)params_len[i]); + } + + png_free(png_ptr, params_len); + png_write_chunk_end(png_ptr); +} +#endif + +#if defined(PNG_WRITE_sCAL_SUPPORTED) +/* write the sCAL chunk */ +#if defined(PNG_FLOATING_POINT_SUPPORTED) && !defined(PNG_NO_STDIO) +void /* PRIVATE */ +png_write_sCAL(png_structp png_ptr, int unit, double width, double height) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_sCAL; +#endif + char buf[64]; + png_size_t total_len; + + png_debug(1, "in png_write_sCAL\n"); + + buf[0] = (char)unit; +#if defined(_WIN32_WCE) +/* sprintf() function is not supported on WindowsCE */ + { + wchar_t wc_buf[32]; + size_t wc_len; + swprintf(wc_buf, TEXT("%12.12e"), width); + wc_len = wcslen(wc_buf); + WideCharToMultiByte(CP_ACP, 0, wc_buf, -1, buf + 1, wc_len, NULL, NULL); + total_len = wc_len + 2; + swprintf(wc_buf, TEXT("%12.12e"), height); + wc_len = wcslen(wc_buf); + WideCharToMultiByte(CP_ACP, 0, wc_buf, -1, buf + total_len, wc_len, + NULL, NULL); + total_len += wc_len; + } +#else + png_snprintf(buf + 1, 63, "%12.12e", width); + total_len = 1 + png_strlen(buf + 1) + 1; + png_snprintf(buf + total_len, 64-total_len, "%12.12e", height); + total_len += png_strlen(buf + total_len); +#endif + + png_debug1(3, "sCAL total length = %u\n", (unsigned int)total_len); + png_write_chunk(png_ptr, png_sCAL, (png_bytep)buf, total_len); +} +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +void /* PRIVATE */ +png_write_sCAL_s(png_structp png_ptr, int unit, png_charp width, + png_charp height) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_sCAL; +#endif + png_byte buf[64]; + png_size_t wlen, hlen, total_len; + + png_debug(1, "in png_write_sCAL_s\n"); + + wlen = png_strlen(width); + hlen = png_strlen(height); + total_len = wlen + hlen + 2; + if (total_len > 64) + { + png_warning(png_ptr, "Can't write sCAL (buffer too small)"); + return; + } + + buf[0] = (png_byte)unit; + png_memcpy(buf + 1, width, wlen + 1); /* append the '\0' here */ + png_memcpy(buf + wlen + 2, height, hlen); /* do NOT append the '\0' here */ + + png_debug1(3, "sCAL total length = %u\n", (unsigned int)total_len); + png_write_chunk(png_ptr, png_sCAL, buf, total_len); +} +#endif +#endif +#endif + +#if defined(PNG_WRITE_pHYs_SUPPORTED) +/* write the pHYs chunk */ +void /* PRIVATE */ +png_write_pHYs(png_structp png_ptr, png_uint_32 x_pixels_per_unit, + png_uint_32 y_pixels_per_unit, + int unit_type) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_pHYs; +#endif + png_byte buf[9]; + + png_debug(1, "in png_write_pHYs\n"); + if (unit_type >= PNG_RESOLUTION_LAST) + png_warning(png_ptr, "Unrecognized unit type for pHYs chunk"); + + png_save_uint_32(buf, x_pixels_per_unit); + png_save_uint_32(buf + 4, y_pixels_per_unit); + buf[8] = (png_byte)unit_type; + + png_write_chunk(png_ptr, png_pHYs, buf, (png_size_t)9); +} +#endif + +#if defined(PNG_WRITE_tIME_SUPPORTED) +/* Write the tIME chunk. Use either png_convert_from_struct_tm() + * or png_convert_from_time_t(), or fill in the structure yourself. + */ +void /* PRIVATE */ +png_write_tIME(png_structp png_ptr, png_timep mod_time) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_tIME; +#endif + png_byte buf[7]; + + png_debug(1, "in png_write_tIME\n"); + if (mod_time->month > 12 || mod_time->month < 1 || + mod_time->day > 31 || mod_time->day < 1 || + mod_time->hour > 23 || mod_time->second > 60) + { + png_warning(png_ptr, "Invalid time specified for tIME chunk"); + return; + } + + png_save_uint_16(buf, mod_time->year); + buf[2] = mod_time->month; + buf[3] = mod_time->day; + buf[4] = mod_time->hour; + buf[5] = mod_time->minute; + buf[6] = mod_time->second; + + png_write_chunk(png_ptr, png_tIME, buf, (png_size_t)7); +} +#endif + +/* initializes the row writing capability of libpng */ +void /* PRIVATE */ +png_write_start_row(png_structp png_ptr) +{ +#ifdef PNG_WRITE_INTERLACING_SUPPORTED +#ifdef PNG_USE_LOCAL_ARRAYS + /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* start of interlace block */ + int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* offset to next interlace block */ + int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + /* start of interlace block in the y direction */ + int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; + + /* offset to next interlace block in the y direction */ + int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; +#endif +#endif + + png_size_t buf_size; + + png_debug(1, "in png_write_start_row\n"); + buf_size = (png_size_t)(PNG_ROWBYTES( + png_ptr->usr_channels*png_ptr->usr_bit_depth,png_ptr->width)+1); + + /* set up row buffer */ + png_ptr->row_buf = (png_bytep)png_malloc(png_ptr, (png_uint_32)buf_size); + png_ptr->row_buf[0] = PNG_FILTER_VALUE_NONE; + +#ifndef PNG_NO_WRITE_FILTERING + /* set up filtering buffer, if using this filter */ + if (png_ptr->do_filter & PNG_FILTER_SUB) + { + png_ptr->sub_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB; + } + + /* We only need to keep the previous row if we are using one of these. */ + if (png_ptr->do_filter & (PNG_FILTER_AVG | PNG_FILTER_UP | PNG_FILTER_PAETH)) + { + /* set up previous row buffer */ + png_ptr->prev_row = (png_bytep)png_malloc(png_ptr, (png_uint_32)buf_size); + png_memset(png_ptr->prev_row, 0, buf_size); + + if (png_ptr->do_filter & PNG_FILTER_UP) + { + png_ptr->up_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->up_row[0] = PNG_FILTER_VALUE_UP; + } + + if (png_ptr->do_filter & PNG_FILTER_AVG) + { + png_ptr->avg_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG; + } + + if (png_ptr->do_filter & PNG_FILTER_PAETH) + { + png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH; + } +#endif /* PNG_NO_WRITE_FILTERING */ + } + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* if interlaced, we need to set up width and height of pass */ + if (png_ptr->interlaced) + { + if (!(png_ptr->transformations & PNG_INTERLACE)) + { + png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 - + png_pass_ystart[0]) / png_pass_yinc[0]; + png_ptr->usr_width = (png_ptr->width + png_pass_inc[0] - 1 - + png_pass_start[0]) / png_pass_inc[0]; + } + else + { + png_ptr->num_rows = png_ptr->height; + png_ptr->usr_width = png_ptr->width; + } + } + else +#endif + { + png_ptr->num_rows = png_ptr->height; + png_ptr->usr_width = png_ptr->width; + } + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_out = png_ptr->zbuf; +} + +/* Internal use only. Called when finished processing a row of data. */ +void /* PRIVATE */ +png_write_finish_row(png_structp png_ptr) +{ +#ifdef PNG_WRITE_INTERLACING_SUPPORTED +#ifdef PNG_USE_LOCAL_ARRAYS + /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* start of interlace block */ + int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* offset to next interlace block */ + int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + /* start of interlace block in the y direction */ + int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; + + /* offset to next interlace block in the y direction */ + int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; +#endif +#endif + + int ret; + + png_debug(1, "in png_write_finish_row\n"); + /* next row */ + png_ptr->row_number++; + + /* see if we are done */ + if (png_ptr->row_number < png_ptr->num_rows) + return; + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* if interlaced, go to next pass */ + if (png_ptr->interlaced) + { + png_ptr->row_number = 0; + if (png_ptr->transformations & PNG_INTERLACE) + { + png_ptr->pass++; + } + else + { + /* loop until we find a non-zero width or height pass */ + do + { + png_ptr->pass++; + if (png_ptr->pass >= 7) + break; + png_ptr->usr_width = (png_ptr->width + + png_pass_inc[png_ptr->pass] - 1 - + png_pass_start[png_ptr->pass]) / + png_pass_inc[png_ptr->pass]; + png_ptr->num_rows = (png_ptr->height + + png_pass_yinc[png_ptr->pass] - 1 - + png_pass_ystart[png_ptr->pass]) / + png_pass_yinc[png_ptr->pass]; + if (png_ptr->transformations & PNG_INTERLACE) + break; + } while (png_ptr->usr_width == 0 || png_ptr->num_rows == 0); + + } + + /* reset the row above the image for the next pass */ + if (png_ptr->pass < 7) + { + if (png_ptr->prev_row != NULL) + png_memset(png_ptr->prev_row, 0, + (png_size_t)(PNG_ROWBYTES(png_ptr->usr_channels* + png_ptr->usr_bit_depth,png_ptr->width))+1); + return; + } + } +#endif + + /* if we get here, we've just written the last row, so we need + to flush the compressor */ + do + { + /* tell the compressor we are done */ + ret = deflate(&png_ptr->zstream, Z_FINISH); + /* check for an error */ + if (ret == Z_OK) + { + /* check to see if we need more room */ + if (!(png_ptr->zstream.avail_out)) + { + png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + } + } + else if (ret != Z_STREAM_END) + { + if (png_ptr->zstream.msg != NULL) + png_error(png_ptr, png_ptr->zstream.msg); + else + png_error(png_ptr, "zlib error"); + } + } while (ret != Z_STREAM_END); + + /* write any extra space */ + if (png_ptr->zstream.avail_out < png_ptr->zbuf_size) + { + png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size - + png_ptr->zstream.avail_out); + } + + deflateReset(&png_ptr->zstream); + png_ptr->zstream.data_type = Z_BINARY; +} + +#if defined(PNG_WRITE_INTERLACING_SUPPORTED) +/* Pick out the correct pixels for the interlace pass. + * The basic idea here is to go through the row with a source + * pointer and a destination pointer (sp and dp), and copy the + * correct pixels for the pass. As the row gets compacted, + * sp will always be >= dp, so we should never overwrite anything. + * See the default: case for the easiest code to understand. + */ +void /* PRIVATE */ +png_do_write_interlace(png_row_infop row_info, png_bytep row, int pass) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* start of interlace block */ + int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* offset to next interlace block */ + int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; +#endif + + png_debug(1, "in png_do_write_interlace\n"); + /* we don't have to do anything on the last pass (6) */ +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL && pass < 6) +#else + if (pass < 6) +#endif + { + /* each pixel depth is handled separately */ + switch (row_info->pixel_depth) + { + case 1: + { + png_bytep sp; + png_bytep dp; + int shift; + int d; + int value; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + dp = row; + d = 0; + shift = 7; + for (i = png_pass_start[pass]; i < row_width; + i += png_pass_inc[pass]) + { + sp = row + (png_size_t)(i >> 3); + value = (int)(*sp >> (7 - (int)(i & 0x07))) & 0x01; + d |= (value << shift); + + if (shift == 0) + { + shift = 7; + *dp++ = (png_byte)d; + d = 0; + } + else + shift--; + + } + if (shift != 7) + *dp = (png_byte)d; + break; + } + case 2: + { + png_bytep sp; + png_bytep dp; + int shift; + int d; + int value; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + dp = row; + shift = 6; + d = 0; + for (i = png_pass_start[pass]; i < row_width; + i += png_pass_inc[pass]) + { + sp = row + (png_size_t)(i >> 2); + value = (*sp >> ((3 - (int)(i & 0x03)) << 1)) & 0x03; + d |= (value << shift); + + if (shift == 0) + { + shift = 6; + *dp++ = (png_byte)d; + d = 0; + } + else + shift -= 2; + } + if (shift != 6) + *dp = (png_byte)d; + break; + } + case 4: + { + png_bytep sp; + png_bytep dp; + int shift; + int d; + int value; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + dp = row; + shift = 4; + d = 0; + for (i = png_pass_start[pass]; i < row_width; + i += png_pass_inc[pass]) + { + sp = row + (png_size_t)(i >> 1); + value = (*sp >> ((1 - (int)(i & 0x01)) << 2)) & 0x0f; + d |= (value << shift); + + if (shift == 0) + { + shift = 4; + *dp++ = (png_byte)d; + d = 0; + } + else + shift -= 4; + } + if (shift != 4) + *dp = (png_byte)d; + break; + } + default: + { + png_bytep sp; + png_bytep dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + png_size_t pixel_bytes; + + /* start at the beginning */ + dp = row; + /* find out how many bytes each pixel takes up */ + pixel_bytes = (row_info->pixel_depth >> 3); + /* loop through the row, only looking at the pixels that + matter */ + for (i = png_pass_start[pass]; i < row_width; + i += png_pass_inc[pass]) + { + /* find out where the original pixel is */ + sp = row + (png_size_t)i * pixel_bytes; + /* move the pixel */ + if (dp != sp) + png_memcpy(dp, sp, pixel_bytes); + /* next pixel */ + dp += pixel_bytes; + } + break; + } + } + /* set new row width */ + row_info->width = (row_info->width + + png_pass_inc[pass] - 1 - + png_pass_start[pass]) / + png_pass_inc[pass]; + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, + row_info->width); + } +} +#endif + +/* This filters the row, chooses which filter to use, if it has not already + * been specified by the application, and then writes the row out with the + * chosen filter. + */ +#define PNG_MAXSUM (((png_uint_32)(-1)) >> 1) +#define PNG_HISHIFT 10 +#define PNG_LOMASK ((png_uint_32)0xffffL) +#define PNG_HIMASK ((png_uint_32)(~PNG_LOMASK >> PNG_HISHIFT)) +void /* PRIVATE */ +png_write_find_filter(png_structp png_ptr, png_row_infop row_info) +{ + png_bytep best_row; +#ifndef PNG_NO_WRITE_FILTER + png_bytep prev_row, row_buf; + png_uint_32 mins, bpp; + png_byte filter_to_do = png_ptr->do_filter; + png_uint_32 row_bytes = row_info->rowbytes; +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + int num_p_filters = (int)png_ptr->num_prev_filters; +#endif + + png_debug(1, "in png_write_find_filter\n"); + /* find out how many bytes offset each pixel is */ + bpp = (row_info->pixel_depth + 7) >> 3; + + prev_row = png_ptr->prev_row; +#endif + best_row = png_ptr->row_buf; +#ifndef PNG_NO_WRITE_FILTER + row_buf = best_row; + mins = PNG_MAXSUM; + + /* The prediction method we use is to find which method provides the + * smallest value when summing the absolute values of the distances + * from zero, using anything >= 128 as negative numbers. This is known + * as the "minimum sum of absolute differences" heuristic. Other + * heuristics are the "weighted minimum sum of absolute differences" + * (experimental and can in theory improve compression), and the "zlib + * predictive" method (not implemented yet), which does test compressions + * of lines using different filter methods, and then chooses the + * (series of) filter(s) that give minimum compressed data size (VERY + * computationally expensive). + * + * GRR 980525: consider also + * (1) minimum sum of absolute differences from running average (i.e., + * keep running sum of non-absolute differences & count of bytes) + * [track dispersion, too? restart average if dispersion too large?] + * (1b) minimum sum of absolute differences from sliding average, probably + * with window size <= deflate window (usually 32K) + * (2) minimum sum of squared differences from zero or running average + * (i.e., ~ root-mean-square approach) + */ + + /* We don't need to test the 'no filter' case if this is the only filter + * that has been chosen, as it doesn't actually do anything to the data. + */ + if ((filter_to_do & PNG_FILTER_NONE) && + filter_to_do != PNG_FILTER_NONE) + { + png_bytep rp; + png_uint_32 sum = 0; + png_uint_32 i; + int v; + + for (i = 0, rp = row_buf + 1; i < row_bytes; i++, rp++) + { + v = *rp; + sum += (v < 128) ? v : 256 - v; + } + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + png_uint_32 sumhi, sumlo; + int j; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; /* Gives us some footroom */ + + /* Reduce the sum if we match any of the previous rows */ + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_NONE) + { + sumlo = (sumlo * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + /* Factor in the cost of this filter (this is here for completeness, + * but it makes no sense to have a "cost" for the NONE filter, as + * it has the minimum possible computational cost - none). + */ + sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_NONE]) >> + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_NONE]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + mins = sum; + } + + /* sub filter */ + if (filter_to_do == PNG_FILTER_SUB) + /* it's the only filter so no testing is needed */ + { + png_bytep rp, lp, dp; + png_uint_32 i; + for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp; + i++, rp++, dp++) + { + *dp = *rp; + } + for (lp = row_buf + 1; i < row_bytes; + i++, rp++, lp++, dp++) + { + *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff); + } + best_row = png_ptr->sub_row; + } + + else if (filter_to_do & PNG_FILTER_SUB) + { + png_bytep rp, dp, lp; + png_uint_32 sum = 0, lmins = mins; + png_uint_32 i; + int v; + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + /* We temporarily increase the "minimum sum" by the factor we + * would reduce the sum of this filter, so that we can do the + * early exit comparison without scaling the sum each time. + */ + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 lmhi, lmlo; + lmlo = lmins & PNG_LOMASK; + lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_SUB) + { + lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> + PNG_COST_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> + PNG_COST_SHIFT; + + if (lmhi > PNG_HIMASK) + lmins = PNG_MAXSUM; + else + lmins = (lmhi << PNG_HISHIFT) + lmlo; + } +#endif + + for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp; + i++, rp++, dp++) + { + v = *dp = *rp; + + sum += (v < 128) ? v : 256 - v; + } + for (lp = row_buf + 1; i < row_bytes; + i++, rp++, lp++, dp++) + { + v = *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff); + + sum += (v < 128) ? v : 256 - v; + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 sumhi, sumlo; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_SUB) + { + sumlo = (sumlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + sumlo = (sumlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + + if (sum < mins) + { + mins = sum; + best_row = png_ptr->sub_row; + } + } + + /* up filter */ + if (filter_to_do == PNG_FILTER_UP) + { + png_bytep rp, dp, pp; + png_uint_32 i; + + for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1, + pp = prev_row + 1; i < row_bytes; + i++, rp++, pp++, dp++) + { + *dp = (png_byte)(((int)*rp - (int)*pp) & 0xff); + } + best_row = png_ptr->up_row; + } + + else if (filter_to_do & PNG_FILTER_UP) + { + png_bytep rp, dp, pp; + png_uint_32 sum = 0, lmins = mins; + png_uint_32 i; + int v; + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 lmhi, lmlo; + lmlo = lmins & PNG_LOMASK; + lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_UP) + { + lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_UP]) >> + PNG_COST_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_UP]) >> + PNG_COST_SHIFT; + + if (lmhi > PNG_HIMASK) + lmins = PNG_MAXSUM; + else + lmins = (lmhi << PNG_HISHIFT) + lmlo; + } +#endif + + for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1, + pp = prev_row + 1; i < row_bytes; i++) + { + v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); + + sum += (v < 128) ? v : 256 - v; + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 sumhi, sumlo; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_UP) + { + sumlo = (sumlo * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_UP]) >> + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_UP]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + + if (sum < mins) + { + mins = sum; + best_row = png_ptr->up_row; + } + } + + /* avg filter */ + if (filter_to_do == PNG_FILTER_AVG) + { + png_bytep rp, dp, pp, lp; + png_uint_32 i; + for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1, + pp = prev_row + 1; i < bpp; i++) + { + *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff); + } + for (lp = row_buf + 1; i < row_bytes; i++) + { + *dp++ = (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2)) + & 0xff); + } + best_row = png_ptr->avg_row; + } + + else if (filter_to_do & PNG_FILTER_AVG) + { + png_bytep rp, dp, pp, lp; + png_uint_32 sum = 0, lmins = mins; + png_uint_32 i; + int v; + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 lmhi, lmlo; + lmlo = lmins & PNG_LOMASK; + lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_AVG) + { + lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_AVG]) >> + PNG_COST_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_AVG]) >> + PNG_COST_SHIFT; + + if (lmhi > PNG_HIMASK) + lmins = PNG_MAXSUM; + else + lmins = (lmhi << PNG_HISHIFT) + lmlo; + } +#endif + + for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1, + pp = prev_row + 1; i < bpp; i++) + { + v = *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff); + + sum += (v < 128) ? v : 256 - v; + } + for (lp = row_buf + 1; i < row_bytes; i++) + { + v = *dp++ = + (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2)) & 0xff); + + sum += (v < 128) ? v : 256 - v; + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 sumhi, sumlo; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_NONE) + { + sumlo = (sumlo * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_AVG]) >> + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_AVG]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + + if (sum < mins) + { + mins = sum; + best_row = png_ptr->avg_row; + } + } + + /* Paeth filter */ + if (filter_to_do == PNG_FILTER_PAETH) + { + png_bytep rp, dp, pp, cp, lp; + png_uint_32 i; + for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1, + pp = prev_row + 1; i < bpp; i++) + { + *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); + } + + for (lp = row_buf + 1, cp = prev_row + 1; i < row_bytes; i++) + { + int a, b, c, pa, pb, pc, p; + + b = *pp++; + c = *cp++; + a = *lp++; + + p = b - c; + pc = a - c; + +#ifdef PNG_USE_ABS + pa = abs(p); + pb = abs(pc); + pc = abs(p + pc); +#else + pa = p < 0 ? -p : p; + pb = pc < 0 ? -pc : pc; + pc = (p + pc) < 0 ? -(p + pc) : p + pc; +#endif + + p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c; + + *dp++ = (png_byte)(((int)*rp++ - p) & 0xff); + } + best_row = png_ptr->paeth_row; + } + + else if (filter_to_do & PNG_FILTER_PAETH) + { + png_bytep rp, dp, pp, cp, lp; + png_uint_32 sum = 0, lmins = mins; + png_uint_32 i; + int v; + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 lmhi, lmlo; + lmlo = lmins & PNG_LOMASK; + lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_PAETH) + { + lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_PAETH]) >> + PNG_COST_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_PAETH]) >> + PNG_COST_SHIFT; + + if (lmhi > PNG_HIMASK) + lmins = PNG_MAXSUM; + else + lmins = (lmhi << PNG_HISHIFT) + lmlo; + } +#endif + + for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1, + pp = prev_row + 1; i < bpp; i++) + { + v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); + + sum += (v < 128) ? v : 256 - v; + } + + for (lp = row_buf + 1, cp = prev_row + 1; i < row_bytes; i++) + { + int a, b, c, pa, pb, pc, p; + + b = *pp++; + c = *cp++; + a = *lp++; + +#ifndef PNG_SLOW_PAETH + p = b - c; + pc = a - c; +#ifdef PNG_USE_ABS + pa = abs(p); + pb = abs(pc); + pc = abs(p + pc); +#else + pa = p < 0 ? -p : p; + pb = pc < 0 ? -pc : pc; + pc = (p + pc) < 0 ? -(p + pc) : p + pc; +#endif + p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c; +#else /* PNG_SLOW_PAETH */ + p = a + b - c; + pa = abs(p - a); + pb = abs(p - b); + pc = abs(p - c); + if (pa <= pb && pa <= pc) + p = a; + else if (pb <= pc) + p = b; + else + p = c; +#endif /* PNG_SLOW_PAETH */ + + v = *dp++ = (png_byte)(((int)*rp++ - p) & 0xff); + + sum += (v < 128) ? v : 256 - v; + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 sumhi, sumlo; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_PAETH) + { + sumlo = (sumlo * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_PAETH]) >> + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_PAETH]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + + if (sum < mins) + { + best_row = png_ptr->paeth_row; + } + } +#endif /* PNG_NO_WRITE_FILTER */ + /* Do the actual writing of the filtered row data from the chosen filter. */ + + png_write_filtered_row(png_ptr, best_row); + +#ifndef PNG_NO_WRITE_FILTER +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + /* Save the type of filter we picked this time for future calculations */ + if (png_ptr->num_prev_filters > 0) + { + int j; + for (j = 1; j < num_p_filters; j++) + { + png_ptr->prev_filters[j] = png_ptr->prev_filters[j - 1]; + } + png_ptr->prev_filters[j] = best_row[0]; + } +#endif +#endif /* PNG_NO_WRITE_FILTER */ +} + +/* Do the actual writing of a previously filtered row. */ +void /* PRIVATE */ +png_write_filtered_row(png_structp png_ptr, png_bytep filtered_row) +{ + png_debug(1, "in png_write_filtered_row\n"); + png_debug1(2, "filter = %d\n", filtered_row[0]); + /* set up the zlib input buffer */ + + png_ptr->zstream.next_in = filtered_row; + png_ptr->zstream.avail_in = (uInt)png_ptr->row_info.rowbytes + 1; + /* repeat until we have compressed all the data */ + do + { + int ret; /* return of zlib */ + + /* compress the data */ + ret = deflate(&png_ptr->zstream, Z_NO_FLUSH); + /* check for compression errors */ + if (ret != Z_OK) + { + if (png_ptr->zstream.msg != NULL) + png_error(png_ptr, png_ptr->zstream.msg); + else + png_error(png_ptr, "zlib error"); + } + + /* see if it is time to write another IDAT */ + if (!(png_ptr->zstream.avail_out)) + { + /* write the IDAT and reset the zlib output buffer */ + png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + } + /* repeat until all data has been compressed */ + } while (png_ptr->zstream.avail_in); + + /* swap the current and previous rows */ + if (png_ptr->prev_row != NULL) + { + png_bytep tptr; + + tptr = png_ptr->prev_row; + png_ptr->prev_row = png_ptr->row_buf; + png_ptr->row_buf = tptr; + } + + /* finish row - updates counters and flushes zlib if last row */ + png_write_finish_row(png_ptr); + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) + png_ptr->flush_rows++; + + if (png_ptr->flush_dist > 0 && + png_ptr->flush_rows >= png_ptr->flush_dist) + { + png_write_flush(png_ptr); + } +#endif +} +#endif /* PNG_WRITE_SUPPORTED */ +/********* End of inlined file: pngwutil.c *********/ + + } +} + +#ifdef _MSC_VER + #pragma warning (pop) +#endif + +BEGIN_JUCE_NAMESPACE + +using namespace pnglibNamespace; +using ::malloc; +using ::free; + +static void pngReadCallback (png_structp pngReadStruct, png_bytep data, png_size_t length) throw() +{ + InputStream* const in = (InputStream*) png_get_io_ptr (pngReadStruct); + in->read (data, (int) length); +} + +struct PNGErrorStruct {}; + +static void pngErrorCallback (png_structp, png_const_charp) +{ + throw PNGErrorStruct(); +} + +Image* juce_loadPNGImageFromStream (InputStream& in) throw() +{ + Image* image = 0; + + png_structp pngReadStruct; + png_infop pngInfoStruct; + + pngReadStruct = png_create_read_struct (PNG_LIBPNG_VER_STRING, 0, 0, 0); + + if (pngReadStruct != 0) + { + pngInfoStruct = png_create_info_struct (pngReadStruct); + + if (pngInfoStruct == 0) + { + png_destroy_read_struct (&pngReadStruct, 0, 0); + return 0; + } + + png_set_error_fn (pngReadStruct, 0, pngErrorCallback, pngErrorCallback); + + // read the header.. + png_set_read_fn (pngReadStruct, &in, pngReadCallback); + + png_uint_32 width, height; + int bitDepth, colorType, interlaceType; + + try + { + png_read_info (pngReadStruct, pngInfoStruct); + + png_get_IHDR (pngReadStruct, pngInfoStruct, + &width, &height, + &bitDepth, &colorType, + &interlaceType, 0, 0); + } + catch (...) + { + png_destroy_read_struct (&pngReadStruct, 0, 0); + return 0; + } + + if (bitDepth == 16) + png_set_strip_16 (pngReadStruct); + + if (colorType == PNG_COLOR_TYPE_PALETTE) + png_set_expand (pngReadStruct); + + if (bitDepth < 8) + png_set_expand (pngReadStruct); + + if (png_get_valid (pngReadStruct, pngInfoStruct, PNG_INFO_tRNS)) + png_set_expand (pngReadStruct); + + if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb (pngReadStruct); + + png_set_add_alpha (pngReadStruct, 0xff, PNG_FILLER_AFTER); + + const bool hasAlphaChan = (colorType & PNG_COLOR_MASK_ALPHA) != 0 + || pngInfoStruct->num_trans > 0; + + // Load the image into a temp buffer in the pnglib format.. + uint8* const tempBuffer = (uint8*) juce_malloc (height * (width << 2)); + + png_bytepp rows = (png_bytepp) juce_malloc (sizeof (png_bytep) * height); + int y; + for (y = (int) height; --y >= 0;) + rows[y] = (png_bytep) (tempBuffer + (width << 2) * y); + + bool crashed = false; + + try + { + png_read_image (pngReadStruct, rows); + png_read_end (pngReadStruct, pngInfoStruct); + } + catch (...) + { + crashed = true; + } + + juce_free (rows); + png_destroy_read_struct (&pngReadStruct, &pngInfoStruct, 0); + + if (crashed) + return 0; + + // now convert the data to a juce image format.. + image = new Image (hasAlphaChan ? Image::ARGB : Image::RGB, + width, height, hasAlphaChan); + + int stride, pixelStride; + uint8* const pixels = image->lockPixelDataReadWrite (0, 0, width, height, stride, pixelStride); + uint8* srcRow = tempBuffer; + uint8* destRow = pixels; + + for (y = 0; y < (int) height; ++y) + { + const uint8* src = srcRow; + srcRow += (width << 2); + uint8* dest = destRow; + destRow += stride; + + if (hasAlphaChan) + { + for (int i = width; --i >= 0;) + { + ((PixelARGB*) dest)->setARGB (src[3], src[0], src[1], src[2]); + ((PixelARGB*) dest)->premultiply(); + dest += pixelStride; + src += 4; + } + } + else + { + for (int i = width; --i >= 0;) + { + ((PixelRGB*) dest)->setARGB (0, src[0], src[1], src[2]); + dest += pixelStride; + src += 4; + } + } + } + + image->releasePixelDataReadWrite (pixels); + juce_free (tempBuffer); + } + + return image; +} + +static void pngWriteDataCallback (png_structp png_ptr, png_bytep data, png_size_t length) throw() +{ + OutputStream* const out = (OutputStream*) png_ptr->io_ptr; + + const bool ok = out->write (data, length); + + (void) ok; + jassert (ok); +} + +bool juce_writePNGImageToStream (const Image& image, OutputStream& out) throw() +{ + const int width = image.getWidth(); + const int height = image.getHeight(); + + png_structp pngWriteStruct = png_create_write_struct (PNG_LIBPNG_VER_STRING, 0, 0, 0); + + if (pngWriteStruct == 0) + return false; + + png_infop pngInfoStruct = png_create_info_struct (pngWriteStruct); + + if (pngInfoStruct == 0) + { + png_destroy_write_struct (&pngWriteStruct, (png_infopp) 0); + return false; + } + + png_set_write_fn (pngWriteStruct, &out, pngWriteDataCallback, 0); + + png_set_IHDR (pngWriteStruct, pngInfoStruct, width, height, 8, + image.hasAlphaChannel() ? PNG_COLOR_TYPE_RGB_ALPHA + : PNG_COLOR_TYPE_RGB, + PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, + PNG_FILTER_TYPE_BASE); + + png_bytep rowData = (png_bytep) juce_malloc (width * 4 * sizeof (png_byte)); + + png_color_8 sig_bit; + sig_bit.red = 8; + sig_bit.green = 8; + sig_bit.blue = 8; + sig_bit.alpha = 8; + png_set_sBIT (pngWriteStruct, pngInfoStruct, &sig_bit); + + png_write_info (pngWriteStruct, pngInfoStruct); + + png_set_shift (pngWriteStruct, &sig_bit); + png_set_packing (pngWriteStruct); + + for (int y = 0; y < height; ++y) + { + uint8* dst = (uint8*) rowData; + int stride, pixelStride; + const uint8* pixels = image.lockPixelDataReadOnly (0, y, width, 1, stride, pixelStride); + const uint8* src = pixels; + + if (image.hasAlphaChannel()) + { + for (int i = width; --i >= 0;) + { + PixelARGB p (*(const PixelARGB*) src); + p.unpremultiply(); + + *dst++ = p.getRed(); + *dst++ = p.getGreen(); + *dst++ = p.getBlue(); + *dst++ = p.getAlpha(); + src += pixelStride; + } + } + else + { + for (int i = width; --i >= 0;) + { + *dst++ = ((const PixelRGB*) src)->getRed(); + *dst++ = ((const PixelRGB*) src)->getGreen(); + *dst++ = ((const PixelRGB*) src)->getBlue(); + src += pixelStride; + } + } + + png_write_rows (pngWriteStruct, &rowData, 1); + image.releasePixelDataReadOnly (pixels); + } + + juce_free (rowData); + + png_write_end (pngWriteStruct, pngInfoStruct); + png_destroy_write_struct (&pngWriteStruct, &pngInfoStruct); + + out.flush(); + + return true; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_PNGLoader.cpp *********/ + +#endif + +//============================================================================== +#if JUCE_WIN32 + +/********* Start of inlined file: juce_win32_Files.cpp *********/ +#ifdef _MSC_VER + #pragma warning (disable: 4514) + #pragma warning (push) +#endif + +#include + +#ifndef _WIN32_IE + #define _WIN32_IE 0x0400 +#endif +#include + +#ifndef CSIDL_MYMUSIC + #define CSIDL_MYMUSIC 0x000d +#endif + +#ifndef CSIDL_MYVIDEO + #define CSIDL_MYVIDEO 0x000e +#endif + +BEGIN_JUCE_NAMESPACE + +#ifdef _MSC_VER + #pragma warning (pop) +#endif + +const tchar File::separator = T('\\'); +const tchar* File::separatorString = T("\\"); + +bool juce_fileExists (const String& fileName, + const bool dontCountDirectories) throw() +{ + if (fileName.isEmpty()) + return false; + + const DWORD attr = GetFileAttributes (fileName); + + return dontCountDirectories ? ((attr & FILE_ATTRIBUTE_DIRECTORY) == 0) + : (attr != 0xffffffff); +} + +bool juce_isDirectory (const String& fileName) throw() +{ + const DWORD attr = GetFileAttributes (fileName); + + return (attr != 0xffffffff) + && ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0); +} + +bool juce_canWriteToFile (const String& fileName) throw() +{ + const DWORD attr = GetFileAttributes (fileName); + + return ((attr & FILE_ATTRIBUTE_READONLY) == 0); +} + +bool juce_setFileReadOnly (const String& fileName, + bool isReadOnly) +{ + DWORD attr = GetFileAttributes (fileName); + + if (attr == 0xffffffff) + return false; + + if (isReadOnly != juce_canWriteToFile (fileName)) + return true; + + if (isReadOnly) + attr |= FILE_ATTRIBUTE_READONLY; + else + attr &= ~FILE_ATTRIBUTE_READONLY; + + return SetFileAttributes (fileName, attr) != FALSE; +} + +bool File::isHidden() const throw() +{ + return (GetFileAttributes (getFullPathName()) & FILE_ATTRIBUTE_HIDDEN) != 0; +} + +bool juce_deleteFile (const String& fileName) throw() +{ + if (juce_isDirectory (fileName)) + return RemoveDirectory (fileName) != 0; + + return DeleteFile (fileName) != 0; +} + +bool juce_moveFile (const String& source, const String& dest) throw() +{ + return MoveFile (source, dest) != 0; +} + +bool juce_copyFile (const String& source, const String& dest) throw() +{ + return CopyFile (source, dest, false) != 0; +} + +void juce_createDirectory (const String& fileName) throw() +{ + if (! juce_fileExists (fileName, true)) + { + CreateDirectory (fileName, 0); + } +} + +// return 0 if not possible +void* juce_fileOpen (const String& fileName, bool forWriting) throw() +{ + HANDLE h; + + if (forWriting) + { + h = CreateFile (fileName, GENERIC_WRITE, FILE_SHARE_READ, 0, + OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); + + if (h != INVALID_HANDLE_VALUE) + SetFilePointer (h, 0, 0, FILE_END); + else + h = 0; + } + else + { + h = CreateFile (fileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, 0); + + if (h == INVALID_HANDLE_VALUE) + h = 0; + } + + return (void*) h; +} + +void juce_fileClose (void* handle) throw() +{ + CloseHandle (handle); +} + +int juce_fileRead (void* handle, void* buffer, int size) throw() +{ + DWORD num = 0; + ReadFile ((HANDLE) handle, buffer, size, &num, 0); + return num; +} + +int juce_fileWrite (void* handle, const void* buffer, int size) throw() +{ + DWORD num; + + WriteFile ((HANDLE) handle, + buffer, size, + &num, 0); + + return num; +} + +int64 juce_fileSetPosition (void* handle, int64 pos) throw() +{ + LARGE_INTEGER li; + li.QuadPart = pos; + li.LowPart = SetFilePointer ((HANDLE) handle, + li.LowPart, + &li.HighPart, + FILE_BEGIN); // (returns -1 if it fails) + + return li.QuadPart; +} + +int64 juce_fileGetPosition (void* handle) throw() +{ + LARGE_INTEGER li; + li.QuadPart = 0; + li.LowPart = SetFilePointer ((HANDLE) handle, + 0, &li.HighPart, + FILE_CURRENT); // (returns -1 if it fails) + + return jmax ((int64) 0, li.QuadPart); +} + +void juce_fileFlush (void* handle) throw() +{ + FlushFileBuffers ((HANDLE) handle); +} + +int64 juce_getFileSize (const String& fileName) throw() +{ + WIN32_FILE_ATTRIBUTE_DATA attributes; + + if (GetFileAttributesEx (fileName, GetFileExInfoStandard, &attributes)) + { + return (((int64) attributes.nFileSizeHigh) << 32) + | attributes.nFileSizeLow; + } + + return 0; +} + +static int64 fileTimeToTime (const FILETIME* const ft) throw() +{ + // tell me if this fails! + static_jassert (sizeof (ULARGE_INTEGER) == sizeof (FILETIME)); + +#if JUCE_GCC + return (((const ULARGE_INTEGER*) ft)->QuadPart - 116444736000000000LL) / 10000; +#else + return (((const ULARGE_INTEGER*) ft)->QuadPart - 116444736000000000) / 10000; +#endif +} + +static void timeToFileTime (const int64 time, FILETIME* const ft) throw() +{ +#if JUCE_GCC + ((ULARGE_INTEGER*) ft)->QuadPart = time * 10000 + 116444736000000000LL; +#else + ((ULARGE_INTEGER*) ft)->QuadPart = time * 10000 + 116444736000000000; +#endif +} + +void juce_getFileTimes (const String& fileName, + int64& modificationTime, + int64& accessTime, + int64& creationTime) throw() +{ + WIN32_FILE_ATTRIBUTE_DATA attributes; + + if (GetFileAttributesEx (fileName, GetFileExInfoStandard, &attributes)) + { + modificationTime = fileTimeToTime (&attributes.ftLastWriteTime); + creationTime = fileTimeToTime (&attributes.ftCreationTime); + accessTime = fileTimeToTime (&attributes.ftLastAccessTime); + } + else + { + creationTime = accessTime = modificationTime = 0; + } +} + +bool juce_setFileTimes (const String& fileName, + int64 modificationTime, + int64 accessTime, + int64 creationTime) throw() +{ + FILETIME m, a, c; + + if (modificationTime > 0) + timeToFileTime (modificationTime, &m); + + if (accessTime > 0) + timeToFileTime (accessTime, &a); + + if (creationTime > 0) + timeToFileTime (creationTime, &c); + + void* const h = juce_fileOpen (fileName, true); + bool ok = false; + + if (h != 0) + { + ok = SetFileTime ((HANDLE) h, + (creationTime > 0) ? &c : 0, + (accessTime > 0) ? &a : 0, + (modificationTime > 0) ? &m : 0) != 0; + juce_fileClose (h); + } + + return ok; +} + +// return '\0' separated list of strings +const StringArray juce_getFileSystemRoots() throw() +{ + TCHAR buffer [2048]; + buffer[0] = 0; + buffer[1] = 0; + GetLogicalDriveStrings (2048, buffer); + + TCHAR* n = buffer; + StringArray roots; + + while (*n != 0) + { + roots.add (String (n)); + + while (*n++ != 0) + { + } + } + + roots.sort (true); + return roots; +} + +const String juce_getVolumeLabel (const String& filenameOnVolume, + int& volumeSerialNumber) throw() +{ + TCHAR n [4]; + n[0] = *(const TCHAR*) filenameOnVolume; + n[1] = L':'; + n[2] = L'\\'; + n[3] = 0; + + TCHAR dest [64]; + DWORD serialNum; + + if (! GetVolumeInformation (n, dest, 64, (DWORD*) &serialNum, 0, 0, 0, 0)) + { + dest[0] = 0; + serialNum = 0; + } + + volumeSerialNumber = serialNum; + return String (dest); +} + +int64 File::getBytesFreeOnVolume() const throw() +{ + String fn (getFullPathName()); + if (fn[1] == T(':')) + fn = fn.substring (0, 2) + T("\\"); + + ULARGE_INTEGER spc; + ULARGE_INTEGER tot; + ULARGE_INTEGER totFree; + + if (GetDiskFreeSpaceEx (fn, &spc, &tot, &totFree)) + return (int64)(spc.QuadPart); + + return 0; +} + +static unsigned int getWindowsDriveType (const String& fileName) throw() +{ + TCHAR n[4]; + n[0] = *(const TCHAR*) fileName; + n[1] = L':'; + n[2] = L'\\'; + n[3] = 0; + + return GetDriveType (n); +} + +bool File::isOnCDRomDrive() const throw() +{ + return getWindowsDriveType (getFullPathName()) == DRIVE_CDROM; +} + +bool File::isOnHardDisk() const throw() +{ + if (fullPath.isEmpty()) + return false; + + const unsigned int n = getWindowsDriveType (getFullPathName()); + + if (fullPath.toLowerCase()[0] <= 'b' + && fullPath[1] == T(':')) + { + return n != DRIVE_REMOVABLE; + } + else + { + return n != DRIVE_CDROM && n != DRIVE_REMOTE; + } +} + +bool File::isOnRemovableDrive() const throw() +{ + if (fullPath.isEmpty()) + return false; + + const unsigned int n = getWindowsDriveType (getFullPathName()); + + return n == DRIVE_CDROM + || n == DRIVE_REMOTE + || n == DRIVE_REMOVABLE + || n == DRIVE_RAMDISK; +} + +#define MAX_PATH_CHARS (MAX_PATH + 256) + +static const File juce_getSpecialFolderPath (int type) throw() +{ + WCHAR path [MAX_PATH_CHARS]; + + if (SHGetSpecialFolderPath (0, path, type, 0)) + return File (String (path)); + + return File::nonexistent; +} + +const File JUCE_CALLTYPE File::getSpecialLocation (const SpecialLocationType type) +{ + int csidlType = 0; + + switch (type) + { + case userHomeDirectory: + case userDocumentsDirectory: + csidlType = CSIDL_PERSONAL; + break; + + case userDesktopDirectory: + csidlType = CSIDL_DESKTOP; + break; + + case userApplicationDataDirectory: + csidlType = CSIDL_APPDATA; + break; + + case commonApplicationDataDirectory: + csidlType = CSIDL_COMMON_APPDATA; + break; + + case globalApplicationsDirectory: + csidlType = CSIDL_PROGRAM_FILES; + break; + + case userMusicDirectory: + csidlType = CSIDL_MYMUSIC; + break; + + case userMoviesDirectory: + csidlType = CSIDL_MYVIDEO; + break; + + case tempDirectory: + { + WCHAR dest [2048]; + dest[0] = 0; + GetTempPath (2048, dest); + return File (String (dest)); + } + + case currentExecutableFile: + case currentApplicationFile: + { + HINSTANCE moduleHandle = (HINSTANCE) PlatformUtilities::getCurrentModuleInstanceHandle(); + + WCHAR dest [MAX_PATH_CHARS]; + dest[0] = 0; + GetModuleFileName (moduleHandle, dest, MAX_PATH_CHARS); + return File (String (dest)); + } + break; + + default: + jassertfalse // unknown type? + return File::nonexistent; + } + + return juce_getSpecialFolderPath (csidlType); +} + +void juce_setCurrentExecutableFileName (const String&) throw() +{ + // n/a on windows +} + +const File File::getCurrentWorkingDirectory() throw() +{ + WCHAR dest [MAX_PATH_CHARS]; + dest[0] = 0; + GetCurrentDirectory (MAX_PATH_CHARS, dest); + return File (String (dest)); +} + +bool File::setAsCurrentWorkingDirectory() const throw() +{ + return SetCurrentDirectory (getFullPathName()) != FALSE; +} + +template +static void getFindFileInfo (FindDataType& findData, + String& filename, bool* const isDir, bool* const isHidden, + int64* const fileSize, Time* const modTime, Time* const creationTime, + bool* const isReadOnly) throw() +{ + filename = findData.cFileName; + + if (isDir != 0) + *isDir = ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0); + + if (isHidden != 0) + *isHidden = ((findData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0); + + if (fileSize != 0) + *fileSize = findData.nFileSizeLow + (((int64) findData.nFileSizeHigh) << 32); + + if (modTime != 0) + *modTime = fileTimeToTime (&findData.ftLastWriteTime); + + if (creationTime != 0) + *creationTime = fileTimeToTime (&findData.ftCreationTime); + + if (isReadOnly != 0) + *isReadOnly = ((findData.dwFileAttributes & FILE_ATTRIBUTE_READONLY) != 0); + +} + +void* juce_findFileStart (const String& directory, const String& wildCard, String& firstResult, + bool* isDir, bool* isHidden, int64* fileSize, + Time* modTime, Time* creationTime, bool* isReadOnly) throw() +{ + String wc (directory); + + if (! wc.endsWithChar (File::separator)) + wc += File::separator; + + wc += wildCard; + + WIN32_FIND_DATA findData; + HANDLE h = FindFirstFile (wc, &findData); + + if (h != INVALID_HANDLE_VALUE) + { + getFindFileInfo (findData, firstResult, isDir, isHidden, fileSize, + modTime, creationTime, isReadOnly); + return h; + } + + firstResult = String::empty; + return 0; +} + +bool juce_findFileNext (void* handle, String& resultFile, + bool* isDir, bool* isHidden, int64* fileSize, + Time* modTime, Time* creationTime, bool* isReadOnly) throw() +{ + WIN32_FIND_DATA findData; + + if (handle != 0 && FindNextFile ((HANDLE) handle, &findData) != 0) + { + getFindFileInfo (findData, resultFile, isDir, isHidden, fileSize, + modTime, creationTime, isReadOnly); + return true; + } + + resultFile = String::empty; + return false; +} + +void juce_findFileClose (void* handle) throw() +{ + FindClose (handle); +} + +bool juce_launchFile (const String& fileName, + const String& parameters) throw() +{ + HINSTANCE hInstance = 0; + + JUCE_TRY + { + hInstance = ShellExecute (0, 0, fileName, parameters, 0, SW_SHOWDEFAULT); + } + JUCE_CATCH_ALL + + return hInstance > (HINSTANCE) 32; +} + +struct NamedPipeInternal +{ + HANDLE pipeH; + HANDLE cancelEvent; + bool connected, createdPipe; + + NamedPipeInternal() + : pipeH (0), + cancelEvent (0), + connected (false), + createdPipe (false) + { + cancelEvent = CreateEvent (0, FALSE, FALSE, 0); + } + + ~NamedPipeInternal() + { + disconnect(); + + if (pipeH != 0) + CloseHandle (pipeH); + + CloseHandle (cancelEvent); + } + + bool connect (const int timeOutMs) + { + if (! createdPipe) + return true; + + if (! connected) + { + OVERLAPPED over; + zerostruct (over); + + over.hEvent = CreateEvent (0, TRUE, FALSE, 0); + + if (ConnectNamedPipe (pipeH, &over)) + { + connected = false; // yes, you read that right. In overlapped mode it should always return 0. + } + else + { + const int err = GetLastError(); + + if (err == ERROR_IO_PENDING || err == ERROR_PIPE_LISTENING) + { + HANDLE handles[] = { over.hEvent, cancelEvent }; + + if (WaitForMultipleObjects (2, handles, FALSE, + timeOutMs >= 0 ? timeOutMs : INFINITE) == WAIT_OBJECT_0) + connected = true; + } + else if (err == ERROR_PIPE_CONNECTED) + { + connected = true; + } + } + + CloseHandle (over.hEvent); + } + + return connected; + } + + void disconnect() + { + if (connected) + { + DisconnectNamedPipe (pipeH); + connected = false; + } + } +}; + +void NamedPipe::close() +{ + NamedPipeInternal* const intern = (NamedPipeInternal*) internal; + delete intern; + internal = 0; +} + +bool NamedPipe::openInternal (const String& pipeName, const bool createPipe) +{ + close(); + + NamedPipeInternal* const intern = new NamedPipeInternal(); + + String file ("\\\\.\\pipe\\"); + file += pipeName; + + intern->createdPipe = createPipe; + + if (createPipe) + { + intern->pipeH = CreateNamedPipe (file, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, 0, + 1, 64, 64, 0, NULL); + } + else + { + intern->pipeH = CreateFile (file, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, 0); + } + + if (intern->pipeH != INVALID_HANDLE_VALUE) + { + internal = intern; + return true; + } + + delete intern; + return false; +} + +int NamedPipe::read (void* destBuffer, int maxBytesToRead, int timeOutMilliseconds) +{ + int bytesRead = -1; + bool waitAgain = true; + + while (waitAgain && internal != 0) + { + NamedPipeInternal* const intern = (NamedPipeInternal*) internal; + waitAgain = false; + + if (! intern->connect (timeOutMilliseconds)) + break; + + if (maxBytesToRead <= 0) + return 0; + + OVERLAPPED over; + zerostruct (over); + over.hEvent = CreateEvent (0, TRUE, FALSE, 0); + + unsigned long numRead; + + if (ReadFile (intern->pipeH, destBuffer, maxBytesToRead, &numRead, &over)) + { + bytesRead = (int) numRead; + } + else if (GetLastError() == ERROR_IO_PENDING) + { + HANDLE handles[] = { over.hEvent, intern->cancelEvent }; + + if (WaitForMultipleObjects (2, handles, FALSE, + timeOutMilliseconds >= 0 ? timeOutMilliseconds + : INFINITE) == WAIT_OBJECT_0) + { + if (GetOverlappedResult (intern->pipeH, &over, &numRead, FALSE)) + { + bytesRead = (int) numRead; + } + else if (GetLastError() == ERROR_BROKEN_PIPE && intern->createdPipe) + { + intern->disconnect(); + waitAgain = true; + } + } + } + else + { + waitAgain = internal != 0; + Sleep (5); + } + + CloseHandle (over.hEvent); + } + + return bytesRead; +} + +int NamedPipe::write (const void* sourceBuffer, int numBytesToWrite, int timeOutMilliseconds) +{ + int bytesWritten = -1; + NamedPipeInternal* const intern = (NamedPipeInternal*) internal; + + if (intern != 0 && intern->connect (timeOutMilliseconds)) + { + if (numBytesToWrite <= 0) + return 0; + + OVERLAPPED over; + zerostruct (over); + + over.hEvent = CreateEvent (0, TRUE, FALSE, 0); + + unsigned long numWritten; + + if (WriteFile (intern->pipeH, sourceBuffer, numBytesToWrite, &numWritten, &over)) + { + bytesWritten = (int) numWritten; + } + else if (GetLastError() == ERROR_IO_PENDING) + { + HANDLE handles[] = { over.hEvent, intern->cancelEvent }; + + if (WaitForMultipleObjects (2, handles, FALSE, timeOutMilliseconds >= 0 ? timeOutMilliseconds + : INFINITE) == WAIT_OBJECT_0) + { + if (GetOverlappedResult (intern->pipeH, &over, &numWritten, FALSE)) + { + bytesWritten = (int) numWritten; + } + else if (GetLastError() == ERROR_BROKEN_PIPE && intern->createdPipe) + { + intern->disconnect(); + } + } + } + + CloseHandle (over.hEvent); + } + + return bytesWritten; +} + +void NamedPipe::cancelPendingReads() +{ + NamedPipeInternal* const intern = (NamedPipeInternal*) internal; + + if (intern != 0) + SetEvent (intern->cancelEvent); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_win32_Files.cpp *********/ + +/********* Start of inlined file: juce_win32_Network.cpp *********/ +#ifdef _MSC_VER + #pragma warning (disable: 4514) + #pragma warning (push) +#endif + +#include +#include +#include +#include + +BEGIN_JUCE_NAMESPACE + +/********* Start of inlined file: juce_win32_DynamicLibraryLoader.h *********/ +#ifndef __JUCE_WIN32_DYNAMICLIBRARYLOADER_JUCEHEADER__ +#define __JUCE_WIN32_DYNAMICLIBRARYLOADER_JUCEHEADER__ + +#ifndef DOXYGEN + +// use with DynamicLibraryLoader to simplify importing functions +// +// functionName: function to import +// localFunctionName: name you want to use to actually call it (must be different) +// returnType: the return type +// object: the DynamicLibraryLoader to use +// params: list of params (bracketed) +// +#define DynamicLibraryImport(functionName, localFunctionName, returnType, object, params) \ + typedef returnType (WINAPI *type##localFunctionName) params; \ + type##localFunctionName localFunctionName \ + = (type##localFunctionName)object.findProcAddress (#functionName); + +// loads and unloads a DLL automatically +class JUCE_API DynamicLibraryLoader +{ +public: + DynamicLibraryLoader (const String& name); + ~DynamicLibraryLoader(); + + void* findProcAddress (const String& functionName); + +private: + void* libHandle; +}; + +#endif +#endif // __JUCE_WIN32_DYNAMICLIBRARYLOADER_JUCEHEADER__ +/********* End of inlined file: juce_win32_DynamicLibraryLoader.h *********/ + +#ifndef INTERNET_FLAG_NEED_FILE + #define INTERNET_FLAG_NEED_FILE 0x00000010 +#endif + +#ifdef _MSC_VER + #pragma warning (pop) +#endif + +bool juce_isOnLine() +{ + DWORD connectionType; + + return InternetGetConnectedState (&connectionType, 0) != 0 + || (connectionType & (INTERNET_CONNECTION_LAN | INTERNET_CONNECTION_PROXY)) != 0; +} + +struct ConnectionAndRequestStruct +{ + HINTERNET connection, request; +}; + +static HINTERNET sessionHandle = 0; + +void* juce_openInternetFile (const String& url, + const String& headers, + const MemoryBlock& postData, + const bool isPost, + URL::OpenStreamProgressCallback* callback, + void* callbackContext) +{ + if (sessionHandle == 0) + sessionHandle = InternetOpen (_T("juce"), + INTERNET_OPEN_TYPE_PRECONFIG, + 0, 0, 0); + + if (sessionHandle != 0) + { + // break up the url.. + TCHAR file[1024], server[1024]; + + URL_COMPONENTS uc; + zerostruct (uc); + + uc.dwStructSize = sizeof (uc); + uc.dwUrlPathLength = sizeof (file); + uc.dwHostNameLength = sizeof (server); + uc.lpszUrlPath = file; + uc.lpszHostName = server; + + if (InternetCrackUrl (url, 0, ICU_ESCAPE | ICU_DECODE, &uc)) + { + const bool isFtp = url.startsWithIgnoreCase (T("ftp:")); + + HINTERNET connection = InternetConnect (sessionHandle, + uc.lpszHostName, + uc.nPort, + _T(""), _T(""), + isFtp ? INTERNET_SERVICE_FTP + : INTERNET_SERVICE_HTTP, + 0, 0); + + if (connection != 0) + { + if (isFtp) + { + HINTERNET request = FtpOpenFile (connection, + uc.lpszUrlPath, + GENERIC_READ, + FTP_TRANSFER_TYPE_BINARY | INTERNET_FLAG_NEED_FILE, + 0); + + ConnectionAndRequestStruct* const result = new ConnectionAndRequestStruct(); + result->connection = connection; + result->request = request; + return result; + } + else + { + const TCHAR* mimeTypes[] = { _T("*"), 0 }; + + HINTERNET request = HttpOpenRequest (connection, + isPost ? _T("POST") + : _T("GET"), + uc.lpszUrlPath, + 0, 0, mimeTypes, + INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE, + 0); + + if (request != 0) + { + INTERNET_BUFFERS buffers; + zerostruct (buffers); + buffers.dwStructSize = sizeof (INTERNET_BUFFERS); + buffers.lpcszHeader = (LPCTSTR) headers; + buffers.dwHeadersLength = headers.length(); + buffers.dwBufferTotal = (DWORD) postData.getSize(); + ConnectionAndRequestStruct* result = 0; + + if (HttpSendRequestEx (request, &buffers, 0, HSR_INITIATE, 0)) + { + int bytesSent = 0; + + for (;;) + { + const int bytesToDo = jmin (1024, postData.getSize() - bytesSent); + DWORD bytesDone = 0; + + if (bytesToDo > 0 + && ! InternetWriteFile (request, + ((const char*) postData.getData()) + bytesSent, + bytesToDo, &bytesDone)) + { + break; + } + + if (bytesToDo == 0 || (int) bytesDone < bytesToDo) + { + result = new ConnectionAndRequestStruct(); + result->connection = connection; + result->request = request; + + HttpEndRequest (request, 0, 0, 0); + return result; + } + + bytesSent += bytesDone; + + if (callback != 0 && ! callback (callbackContext, bytesSent, postData.getSize())) + break; + } + } + + InternetCloseHandle (request); + } + + InternetCloseHandle (connection); + } + } + } + } + + return 0; +} + +int juce_readFromInternetFile (void* handle, void* buffer, int bytesToRead) +{ + DWORD bytesRead = 0; + + const ConnectionAndRequestStruct* const crs = (const ConnectionAndRequestStruct*) handle; + + if (crs != 0) + InternetReadFile (crs->request, + buffer, bytesToRead, + &bytesRead); + + return bytesRead; +} + +int juce_seekInInternetFile (void* handle, int newPosition) +{ + if (handle != 0) + { + const ConnectionAndRequestStruct* const crs = (const ConnectionAndRequestStruct*) handle; + + return InternetSetFilePointer (crs->request, + newPosition, 0, + FILE_BEGIN, 0); + } + else + { + return -1; + } +} + +void juce_closeInternetFile (void* handle) +{ + if (handle != 0) + { + ConnectionAndRequestStruct* const crs = (ConnectionAndRequestStruct*) handle; + InternetCloseHandle (crs->request); + InternetCloseHandle (crs->connection); + delete crs; + } +} + +static int getMACAddressViaGetAdaptersInfo (int64* addresses, int maxNum, const bool littleEndian) throw() +{ + int numFound = 0; + + DynamicLibraryLoader dll ("iphlpapi.dll"); + DynamicLibraryImport (GetAdaptersInfo, getAdaptersInfo, DWORD, dll, (PIP_ADAPTER_INFO, PULONG)) + + if (getAdaptersInfo != 0) + { + ULONG len = sizeof (IP_ADAPTER_INFO); + MemoryBlock mb; + PIP_ADAPTER_INFO adapterInfo = (PIP_ADAPTER_INFO) mb.getData(); + + if (getAdaptersInfo (adapterInfo, &len) == ERROR_BUFFER_OVERFLOW) + { + mb.setSize (len); + adapterInfo = (PIP_ADAPTER_INFO) mb.getData(); + } + + if (getAdaptersInfo (adapterInfo, &len) == NO_ERROR) + { + PIP_ADAPTER_INFO adapter = adapterInfo; + + while (adapter != 0) + { + int64 mac = 0; + for (unsigned int i = 0; i < adapter->AddressLength; ++i) + mac = (mac << 8) | adapter->Address[i]; + + if (littleEndian) + mac = (int64) swapByteOrder ((uint64) mac); + + if (numFound < maxNum && mac != 0) + addresses [numFound++] = mac; + + adapter = adapter->Next; + } + } + } + + return numFound; +} + +static int getMACAddressesViaNetBios (int64* addresses, int maxNum, const bool littleEndian) throw() +{ + int numFound = 0; + + DynamicLibraryLoader dll ("netapi32.dll"); + DynamicLibraryImport (Netbios, NetbiosCall, UCHAR, dll, (PNCB)) + + if (NetbiosCall != 0) + { + NCB ncb; + zerostruct (ncb); + + typedef struct _ASTAT_ + { + ADAPTER_STATUS adapt; + NAME_BUFFER NameBuff [30]; + } ASTAT; + + ASTAT astat; + zerostruct (astat); + + LANA_ENUM enums; + zerostruct (enums); + + ncb.ncb_command = NCBENUM; + ncb.ncb_buffer = (unsigned char*) &enums; + ncb.ncb_length = sizeof (LANA_ENUM); + NetbiosCall (&ncb); + + for (int i = 0; i < enums.length; ++i) + { + zerostruct (ncb); + ncb.ncb_command = NCBRESET; + ncb.ncb_lana_num = enums.lana[i]; + + if (NetbiosCall (&ncb) == 0) + { + zerostruct (ncb); + memcpy (ncb.ncb_callname, "* ", NCBNAMSZ); + ncb.ncb_command = NCBASTAT; + ncb.ncb_lana_num = enums.lana[i]; + + ncb.ncb_buffer = (unsigned char*) &astat; + ncb.ncb_length = sizeof (ASTAT); + + if (NetbiosCall (&ncb) == 0) + { + if (astat.adapt.adapter_type == 0xfe) + { + int64 mac = 0; + for (unsigned int i = 0; i < 6; ++i) + mac = (mac << 8) | astat.adapt.adapter_address[i]; + + if (littleEndian) + mac = (int64) swapByteOrder ((uint64) mac); + + if (numFound < maxNum && mac != 0) + addresses [numFound++] = mac; + } + } + } + } + } + + return numFound; +} + +int SystemStats::getMACAddresses (int64* addresses, int maxNum, const bool littleEndian) throw() +{ + int numFound = getMACAddressViaGetAdaptersInfo (addresses, maxNum, littleEndian); + + if (numFound == 0) + numFound = getMACAddressesViaNetBios (addresses, maxNum, littleEndian); + + return numFound; +} + +typedef ULONG (WINAPI *MAPISendMailType) (LHANDLE, ULONG, lpMapiMessage, ::FLAGS, ULONG); + +bool PlatformUtilities::launchEmailWithAttachments (const String& targetEmailAddress, + const String& emailSubject, + const String& bodyText, + const StringArray& filesToAttach) +{ + HMODULE h = LoadLibraryA ("MAPI32.dll"); + + MAPISendMailType mapiSendMail = (MAPISendMailType) GetProcAddress (h, "MAPISendMail"); + bool ok = false; + + if (mapiSendMail != 0) + { + MapiMessage message; + zerostruct (message); + message.lpszSubject = (LPSTR) (LPCSTR) emailSubject; + message.lpszNoteText = (LPSTR) (LPCSTR) bodyText; + + MapiRecipDesc recip; + zerostruct (recip); + recip.ulRecipClass = MAPI_TO; + recip.lpszName = (LPSTR) (LPCSTR) targetEmailAddress; + message.nRecipCount = 1; + message.lpRecips = &recip; + + MemoryBlock mb (sizeof (MapiFileDesc) * filesToAttach.size()); + mb.fillWith (0); + MapiFileDesc* files = (MapiFileDesc*) mb.getData(); + + message.nFileCount = filesToAttach.size(); + message.lpFiles = files; + + for (int i = 0; i < filesToAttach.size(); ++i) + { + files[i].nPosition = (ULONG) -1; + files[i].lpszPathName = (LPSTR) (LPCSTR) filesToAttach [i]; + } + + ok = (mapiSendMail (0, 0, &message, MAPI_DIALOG | MAPI_LOGON_UI, 0) == SUCCESS_SUCCESS); + } + + FreeLibrary (h); + return ok; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_win32_Network.cpp *********/ + +/********* Start of inlined file: juce_win32_Misc.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +#if ! JUCE_ONLY_BUILD_CORE_LIBRARY +bool AlertWindow::showNativeDialogBox (const String& title, + const String& bodyText, + bool isOkCancel) +{ + return MessageBox (0, bodyText, title, + (isOkCancel) ? MB_OKCANCEL + : MB_OK) == IDOK; +} + +#endif + +void PlatformUtilities::beep() +{ + MessageBeep (MB_OK); +} + +#if JUCE_MSVC + #pragma warning (disable : 4127) // "Conditional expression is constant" warning +#endif + +#if ! JUCE_ONLY_BUILD_CORE_LIBRARY + +void SystemClipboard::copyTextToClipboard (const String& text) throw() +{ + if (OpenClipboard (0) != 0) + { + if (EmptyClipboard() != 0) + { + const int len = text.length(); + + if (len > 0) + { + HGLOBAL bufH = GlobalAlloc (GMEM_MOVEABLE | GMEM_DDESHARE, + (len + 1) * sizeof (wchar_t)); + + if (bufH != 0) + { + wchar_t* const data = (wchar_t*) GlobalLock (bufH); + text.copyToBuffer (data, len); + GlobalUnlock (bufH); + + SetClipboardData (CF_UNICODETEXT, bufH); + } + } + } + + CloseClipboard(); + } +} + +const String SystemClipboard::getTextFromClipboard() throw() +{ + String result; + + if (OpenClipboard (0) != 0) + { + HANDLE bufH = GetClipboardData (CF_UNICODETEXT); + + if (bufH != 0) + { + const wchar_t* const data = (const wchar_t*) GlobalLock (bufH); + + if (data != 0) + { + result = String (data, (int) (GlobalSize (bufH) / sizeof (tchar))); + + GlobalUnlock (bufH); + } + } + + CloseClipboard(); + } + + return result; +} + +#endif + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_win32_Misc.cpp *********/ + +/********* Start of inlined file: juce_win32_PlatformUtils.cpp *********/ +#ifdef _MSC_VER + #pragma warning (disable: 4514) + #pragma warning (push) +#endif + +#include + +BEGIN_JUCE_NAMESPACE + +#ifdef _MSC_VER + #pragma warning (pop) +#endif + +static HKEY findKeyForPath (String name, + const bool createForWriting, + String& valueName) throw() +{ + HKEY rootKey = 0; + + if (name.startsWithIgnoreCase (T("HKEY_CURRENT_USER\\"))) + rootKey = HKEY_CURRENT_USER; + else if (name.startsWithIgnoreCase (T("HKEY_LOCAL_MACHINE\\"))) + rootKey = HKEY_LOCAL_MACHINE; + else if (name.startsWithIgnoreCase (T("HKEY_CLASSES_ROOT\\"))) + rootKey = HKEY_CLASSES_ROOT; + + if (rootKey != 0) + { + name = name.substring (name.indexOfChar (T('\\')) + 1); + + const int lastSlash = name.lastIndexOfChar (T('\\')); + valueName = name.substring (lastSlash + 1); + name = name.substring (0, lastSlash); + + HKEY key; + DWORD result; + + if (createForWriting) + { + if (RegCreateKeyEx (rootKey, name, 0, L"", REG_OPTION_NON_VOLATILE, + (KEY_WRITE | KEY_QUERY_VALUE), 0, &key, &result) == ERROR_SUCCESS) + return key; + } + else + { + if (RegOpenKeyEx (rootKey, name, 0, KEY_READ, &key) == ERROR_SUCCESS) + return key; + } + } + + return 0; +} + +const String PlatformUtilities::getRegistryValue (const String& regValuePath, + const String& defaultValue) +{ + String valueName, s; + HKEY k = findKeyForPath (regValuePath, false, valueName); + + if (k != 0) + { + WCHAR buffer [2048]; + unsigned long bufferSize = sizeof (buffer); + DWORD type = REG_SZ; + + if (RegQueryValueEx (k, valueName, 0, &type, (LPBYTE) buffer, &bufferSize) == ERROR_SUCCESS) + s = buffer; + else + s = defaultValue; + + RegCloseKey (k); + } + + return s; +} + +void PlatformUtilities::setRegistryValue (const String& regValuePath, + const String& value) +{ + String valueName; + HKEY k = findKeyForPath (regValuePath, true, valueName); + + if (k != 0) + { + RegSetValueEx (k, valueName, 0, REG_SZ, + (const BYTE*) (const WCHAR*) value, + sizeof (WCHAR) * (value.length() + 1)); + + RegCloseKey (k); + } +} + +bool PlatformUtilities::registryValueExists (const String& regValuePath) +{ + bool exists = false; + String valueName; + HKEY k = findKeyForPath (regValuePath, false, valueName); + + if (k != 0) + { + unsigned char buffer [2048]; + unsigned long bufferSize = sizeof (buffer); + DWORD type = 0; + + if (RegQueryValueEx (k, valueName, 0, &type, buffer, &bufferSize) == ERROR_SUCCESS) + exists = true; + + RegCloseKey (k); + } + + return exists; +} + +void PlatformUtilities::deleteRegistryValue (const String& regValuePath) +{ + String valueName; + HKEY k = findKeyForPath (regValuePath, true, valueName); + + if (k != 0) + { + RegDeleteValue (k, valueName); + RegCloseKey (k); + } +} + +void PlatformUtilities::deleteRegistryKey (const String& regKeyPath) +{ + String valueName; + HKEY k = findKeyForPath (regKeyPath, true, valueName); + + if (k != 0) + { + RegDeleteKey (k, valueName); + RegCloseKey (k); + } +} + +bool juce_IsRunningInWine() throw() +{ + HKEY key; + if (RegOpenKeyEx (HKEY_CURRENT_USER, _T("Software\\Wine"), 0, KEY_READ, &key) == ERROR_SUCCESS) + { + RegCloseKey (key); + return true; + } + + return false; +} + +static void* currentModuleHandle = 0; + +void* PlatformUtilities::getCurrentModuleInstanceHandle() throw() +{ + if (currentModuleHandle == 0) + currentModuleHandle = GetModuleHandle (0); + + return currentModuleHandle; +} + +void PlatformUtilities::setCurrentModuleInstanceHandle (void* const newHandle) throw() +{ + currentModuleHandle = newHandle; +} + +void PlatformUtilities::fpuReset() +{ +#if JUCE_MSVC + _clearfp(); +#endif +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_win32_PlatformUtils.cpp *********/ + +/********* Start of inlined file: juce_win32_SystemStats.cpp *********/ + +// Auto-link the other win32 libs that are needed by library calls.. +#if defined (JUCE_DLL_BUILD) && JUCE_MSVC + +/********* Start of inlined file: juce_win32_AutoLinkLibraries.h *********/ +// Auto-links to various win32 libs that are needed by library calls.. +#pragma comment(lib, "kernel32.lib") +#pragma comment(lib, "user32.lib") +#pragma comment(lib, "shell32.lib") +#pragma comment(lib, "gdi32.lib") +#pragma comment(lib, "vfw32.lib") +#pragma comment(lib, "comdlg32.lib") +#pragma comment(lib, "winmm.lib") +#pragma comment(lib, "wininet.lib") +#pragma comment(lib, "ole32.lib") +#pragma comment(lib, "advapi32.lib") +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "comsupp.lib") + +#if JUCE_OPENGL + #pragma comment(lib, "OpenGL32.Lib") + #pragma comment(lib, "GlU32.Lib") +#endif + +#if JUCE_QUICKTIME + #pragma comment (lib, "QTMLClient.lib") +#endif +/********* End of inlined file: juce_win32_AutoLinkLibraries.h *********/ + +#endif + +BEGIN_JUCE_NAMESPACE + +extern void juce_updateMultiMonitorInfo() throw(); +extern void juce_initialiseThreadEvents() throw(); + +void Logger::outputDebugString (const String& text) throw() +{ + OutputDebugString (text + T("\n")); +} + +void Logger::outputDebugPrintf (const tchar* format, ...) throw() +{ + String text; + va_list args; + va_start (args, format); + text.vprintf(format, args); + outputDebugString (text); +} + +static int64 hiResTicksPerSecond; +static double hiResTicksScaleFactor; + +#if JUCE_USE_INTRINSICS + +// CPU info functions using intrinsics... + +#pragma intrinsic (__cpuid) +#pragma intrinsic (__rdtsc) + +/*static unsigned int getCPUIDWord (int* familyModel = 0, int* extFeatures = 0) throw() +{ + int info [4]; + __cpuid (info, 1); + + if (familyModel != 0) + *familyModel = info [0]; + + if (extFeatures != 0) + *extFeatures = info[1]; + + return info[3]; +}*/ + +const String SystemStats::getCpuVendor() throw() +{ + int info [4]; + __cpuid (info, 0); + + char v [12]; + memcpy (v, info + 1, 4); + memcpy (v + 4, info + 3, 4); + memcpy (v + 8, info + 2, 4); + + return String (v, 12); +} + +#else + +// CPU info functions using old fashioned inline asm... + +/*static juce_noinline unsigned int getCPUIDWord (int* familyModel = 0, int* extFeatures = 0) +{ + unsigned int cpu = 0; + unsigned int ext = 0; + unsigned int family = 0; + + #if JUCE_GCC + unsigned int dummy = 0; + #endif + + #ifndef __MINGW32__ + __try + #endif + { + #if JUCE_GCC + __asm__ ("cpuid" : "=a" (family), "=b" (ext), "=c" (dummy),"=d" (cpu) : "a" (1)); + #else + __asm + { + mov eax, 1 + cpuid + mov cpu, edx + mov family, eax + mov ext, ebx + } + + #endif + } + #ifndef __MINGW32__ + __except (EXCEPTION_EXECUTE_HANDLER) + { + return 0; + } + #endif + + if (familyModel != 0) + *familyModel = family; + + if (extFeatures != 0) + *extFeatures = ext; + + return cpu; +}*/ + +static void juce_getCpuVendor (char* const v) +{ + int vendor[4]; + zeromem (vendor, 16); + +#ifdef JUCE_64BIT +#else + #ifndef __MINGW32__ + __try + #endif + { + #if JUCE_GCC + unsigned int dummy = 0; + __asm__ ("cpuid" : "=a" (dummy), "=b" (vendor[0]), "=c" (vendor[2]),"=d" (vendor[1]) : "a" (0)); + #else + __asm + { + mov eax, 0 + cpuid + mov [vendor], ebx + mov [vendor + 4], edx + mov [vendor + 8], ecx + } + #endif + } + #ifndef __MINGW32__ + __except (EXCEPTION_EXECUTE_HANDLER) + { + *v = 0; + } + #endif +#endif + + memcpy (v, vendor, 16); +} + +const String SystemStats::getCpuVendor() throw() +{ + char v [16]; + juce_getCpuVendor (v); + return String (v, 16); +} +#endif + +struct CPUFlags +{ + bool hasMMX : 1; + bool hasSSE : 1; + bool hasSSE2 : 1; + bool has3DNow : 1; +}; + +static CPUFlags cpuFlags; + +bool SystemStats::hasMMX() throw() +{ + return cpuFlags.hasMMX; +} + +bool SystemStats::hasSSE() throw() +{ + return cpuFlags.hasSSE; +} + +bool SystemStats::hasSSE2() throw() +{ + return cpuFlags.hasSSE2; +} + +bool SystemStats::has3DNow() throw() +{ + return cpuFlags.has3DNow; +} + +void SystemStats::initialiseStats() throw() +{ + juce_initialiseThreadEvents(); + + cpuFlags.hasMMX = IsProcessorFeaturePresent (PF_MMX_INSTRUCTIONS_AVAILABLE) != 0; + cpuFlags.hasSSE = IsProcessorFeaturePresent (PF_XMMI_INSTRUCTIONS_AVAILABLE) != 0; + cpuFlags.hasSSE2 = IsProcessorFeaturePresent (PF_XMMI64_INSTRUCTIONS_AVAILABLE) != 0; +#ifdef PF_AMD3D_INSTRUCTIONS_AVAILABLE + cpuFlags.has3DNow = IsProcessorFeaturePresent (PF_AMD3D_INSTRUCTIONS_AVAILABLE) != 0; +#else + cpuFlags.has3DNow = IsProcessorFeaturePresent (PF_3DNOW_INSTRUCTIONS_AVAILABLE) != 0; +#endif + + LARGE_INTEGER f; + QueryPerformanceFrequency (&f); + hiResTicksPerSecond = f.QuadPart; + hiResTicksScaleFactor = 1000.0 / hiResTicksPerSecond; + + String s (SystemStats::getJUCEVersion()); + +#ifdef JUCE_DEBUG + const MMRESULT res = timeBeginPeriod (1); + jassert (res == TIMERR_NOERROR); +#else + timeBeginPeriod (1); +#endif + +#if defined (JUCE_DEBUG) && JUCE_MSVC && JUCE_CHECK_MEMORY_LEAKS + _CrtSetDbgFlag (_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); +#endif +} + +SystemStats::OperatingSystemType SystemStats::getOperatingSystemType() throw() +{ + OSVERSIONINFO info; + info.dwOSVersionInfoSize = sizeof (info); + GetVersionEx (&info); + + if (info.dwPlatformId == VER_PLATFORM_WIN32_NT) + { + switch (info.dwMajorVersion) + { + case 5: + return (info.dwMinorVersion == 0) ? Win2000 : WinXP; + + case 6: + return WinVista; + + default: + jassertfalse // !! not a supported OS! + break; + } + } + else if (info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) + { + jassert (info.dwMinorVersion != 0); // !! still running on Windows 95?? + + return Win98; + } + + return UnknownOS; +} + +const String SystemStats::getOperatingSystemName() throw() +{ + const char* name = "Unknown OS"; + + switch (getOperatingSystemType()) + { + case WinVista: + name = "Windows Vista"; + break; + + case WinXP: + name = "Windows XP"; + break; + + case Win2000: + name = "Windows 2000"; + break; + + case Win98: + name = "Windows 98"; + break; + + default: + jassertfalse // !! new type of OS? + break; + } + + return name; +} + +bool SystemStats::isOperatingSystem64Bit() throw() +{ +#ifdef _WIN64 + return true; +#else + typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL); + + LPFN_ISWOW64PROCESS fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress (GetModuleHandle (L"kernel32"), "IsWow64Process"); + + BOOL isWow64 = FALSE; + + return (fnIsWow64Process != 0) + && fnIsWow64Process (GetCurrentProcess(), &isWow64) + && (isWow64 != FALSE); +#endif +} + +int SystemStats::getMemorySizeInMegabytes() throw() +{ + MEMORYSTATUS mem; + GlobalMemoryStatus (&mem); + return (int) (mem.dwTotalPhys / (1024 * 1024)) + 1; +} + +int SystemStats::getNumCpus() throw() +{ + SYSTEM_INFO systemInfo; + GetSystemInfo (&systemInfo); + + return systemInfo.dwNumberOfProcessors; +} + +uint32 juce_millisecondsSinceStartup() throw() +{ + return (uint32) GetTickCount(); +} + +int64 Time::getHighResolutionTicks() throw() +{ + LARGE_INTEGER ticks; + QueryPerformanceCounter (&ticks); + + const int64 mainCounterAsHiResTicks = (GetTickCount() * hiResTicksPerSecond) / 1000; + const int64 newOffset = mainCounterAsHiResTicks - ticks.QuadPart; + + // fix for a very obscure PCI hardware bug that can make the counter + // sometimes jump forwards by a few seconds.. + static int64 hiResTicksOffset = 0; + const int64 offsetDrift = abs64 (newOffset - hiResTicksOffset); + + if (offsetDrift > (hiResTicksPerSecond >> 1)) + hiResTicksOffset = newOffset; + + return ticks.QuadPart + hiResTicksOffset; +} + +double Time::getMillisecondCounterHiRes() throw() +{ + return getHighResolutionTicks() * hiResTicksScaleFactor; +} + +int64 Time::getHighResolutionTicksPerSecond() throw() +{ + return hiResTicksPerSecond; +} + +int64 SystemStats::getClockCycleCounter() throw() +{ +#if JUCE_USE_INTRINSICS + // MS intrinsics version... + return __rdtsc(); + +#elif JUCE_GCC + // GNU inline asm version... + unsigned int hi = 0, lo = 0; + + __asm__ __volatile__ ( + "xor %%eax, %%eax \n\ + xor %%edx, %%edx \n\ + rdtsc \n\ + movl %%eax, %[lo] \n\ + movl %%edx, %[hi]" + : + : [hi] "m" (hi), + [lo] "m" (lo) + : "cc", "eax", "ebx", "ecx", "edx", "memory"); + + return (int64) ((((uint64) hi) << 32) | lo); +#else + // MSVC inline asm version... + unsigned int hi = 0, lo = 0; + + __asm + { + xor eax, eax + xor edx, edx + rdtsc + mov lo, eax + mov hi, edx + } + + return (int64) ((((uint64) hi) << 32) | lo); +#endif +} + +int SystemStats::getCpuSpeedInMegaherz() throw() +{ + const int64 cycles = SystemStats::getClockCycleCounter(); + const uint32 millis = Time::getMillisecondCounter(); + int lastResult = 0; + + for (;;) + { + int n = 1000000; + while (--n > 0) {} + + const uint32 millisElapsed = Time::getMillisecondCounter() - millis; + const int64 cyclesNow = SystemStats::getClockCycleCounter(); + + if (millisElapsed > 80) + { + const int newResult = (int) (((cyclesNow - cycles) / millisElapsed) / 1000); + + if (millisElapsed > 500 || (lastResult == newResult && newResult > 100)) + return newResult; + + lastResult = newResult; + } + } +} + +bool Time::setSystemTimeToThisTime() const throw() +{ + SYSTEMTIME st; + + st.wDayOfWeek = 0; + st.wYear = (WORD) getYear(); + st.wMonth = (WORD) (getMonth() + 1); + st.wDay = (WORD) getDayOfMonth(); + st.wHour = (WORD) getHours(); + st.wMinute = (WORD) getMinutes(); + st.wSecond = (WORD) getSeconds(); + st.wMilliseconds = (WORD) (millisSinceEpoch % 1000); + + // do this twice because of daylight saving conversion problems - the + // first one sets it up, the second one kicks it in. + return SetLocalTime (&st) != 0 + && SetLocalTime (&st) != 0; +} + +int SystemStats::getPageSize() throw() +{ + SYSTEM_INFO systemInfo; + GetSystemInfo (&systemInfo); + + return systemInfo.dwPageSize; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_win32_SystemStats.cpp *********/ + +/********* Start of inlined file: juce_win32_Threads.cpp *********/ + +#ifdef _MSC_VER + #pragma warning (disable: 4514) + #pragma warning (push) + #include +#endif + +#include + +BEGIN_JUCE_NAMESPACE + +#if ! JUCE_ONLY_BUILD_CORE_LIBRARY + extern HWND juce_messageWindowHandle; +#endif + +#ifdef _MSC_VER + #pragma warning (pop) +#endif + +CriticalSection::CriticalSection() throw() +{ + // (just to check the MS haven't changed this structure and broken things...) +#if _MSC_VER >= 1400 + static_jassert (sizeof (CRITICAL_SECTION) <= sizeof (internal)); +#else + static_jassert (sizeof (CRITICAL_SECTION) <= 24); +#endif + + InitializeCriticalSection ((CRITICAL_SECTION*) internal); +} + +CriticalSection::~CriticalSection() throw() +{ + DeleteCriticalSection ((CRITICAL_SECTION*) internal); +} + +void CriticalSection::enter() const throw() +{ + EnterCriticalSection ((CRITICAL_SECTION*) internal); +} + +bool CriticalSection::tryEnter() const throw() +{ + return TryEnterCriticalSection ((CRITICAL_SECTION*) internal) != FALSE; +} + +void CriticalSection::exit() const throw() +{ + LeaveCriticalSection ((CRITICAL_SECTION*) internal); +} + +WaitableEvent::WaitableEvent() throw() + : internal (CreateEvent (0, FALSE, FALSE, 0)) +{ +} + +WaitableEvent::~WaitableEvent() throw() +{ + CloseHandle (internal); +} + +bool WaitableEvent::wait (const int timeOutMillisecs) const throw() +{ + return WaitForSingleObject (internal, timeOutMillisecs) == WAIT_OBJECT_0; +} + +void WaitableEvent::signal() const throw() +{ + SetEvent (internal); +} + +void WaitableEvent::reset() const throw() +{ + ResetEvent (internal); +} + +void JUCE_API juce_threadEntryPoint (void*); + +static unsigned int __stdcall threadEntryProc (void* userData) throw() +{ +#if ! JUCE_ONLY_BUILD_CORE_LIBRARY + AttachThreadInput (GetWindowThreadProcessId (juce_messageWindowHandle, 0), + GetCurrentThreadId(), TRUE); +#endif + + juce_threadEntryPoint (userData); + + _endthreadex(0); + return 0; +} + +void juce_CloseThreadHandle (void* handle) throw() +{ + CloseHandle ((HANDLE) handle); +} + +void* juce_createThread (void* userData) throw() +{ + unsigned int threadId; + + return (void*) _beginthreadex (0, 0, + &threadEntryProc, + userData, + 0, &threadId); +} + +void juce_killThread (void* handle) throw() +{ + if (handle != 0) + { +#ifdef JUCE_DEBUG + OutputDebugString (_T("** Warning - Forced thread termination **\n")); +#endif + TerminateThread (handle, 0); + } +} + +void juce_setCurrentThreadName (const String& name) throw() +{ +#if defined (JUCE_DEBUG) && JUCE_MSVC + struct + { + DWORD dwType; + LPCSTR szName; + DWORD dwThreadID; + DWORD dwFlags; + } info; + + info.dwType = 0x1000; + info.szName = name; + info.dwThreadID = GetCurrentThreadId(); + info.dwFlags = 0; + + #define MS_VC_EXCEPTION 0x406d1388 + + __try + { + RaiseException (MS_VC_EXCEPTION, 0, sizeof (info) / sizeof (ULONG_PTR), (ULONG_PTR*) &info); + } + __except (EXCEPTION_CONTINUE_EXECUTION) + {} +#else + (void) name; +#endif +} + +int Thread::getCurrentThreadId() throw() +{ + return (int) GetCurrentThreadId(); +} + +// priority 1 to 10 where 5=normal, 1=low +void juce_setThreadPriority (void* threadHandle, int priority) throw() +{ + int pri = THREAD_PRIORITY_TIME_CRITICAL; + + if (priority < 1) + pri = THREAD_PRIORITY_IDLE; + else if (priority < 2) + pri = THREAD_PRIORITY_LOWEST; + else if (priority < 5) + pri = THREAD_PRIORITY_BELOW_NORMAL; + else if (priority < 7) + pri = THREAD_PRIORITY_NORMAL; + else if (priority < 9) + pri = THREAD_PRIORITY_ABOVE_NORMAL; + else if (priority < 10) + pri = THREAD_PRIORITY_HIGHEST; + + if (threadHandle == 0) + threadHandle = GetCurrentThread(); + + SetThreadPriority (threadHandle, pri); +} + +void Thread::setCurrentThreadAffinityMask (const uint32 affinityMask) throw() +{ + SetThreadAffinityMask (GetCurrentThread(), affinityMask); +} + +static HANDLE sleepEvent = 0; + +void juce_initialiseThreadEvents() throw() +{ + if (sleepEvent == 0) +#ifdef JUCE_DEBUG + sleepEvent = CreateEvent (0, 0, 0, _T("Juce Sleep Event")); +#else + sleepEvent = CreateEvent (0, 0, 0, 0); +#endif +} + +void Thread::yield() throw() +{ + Sleep (0); +} + +void JUCE_CALLTYPE Thread::sleep (const int millisecs) throw() +{ + if (millisecs >= 10) + { + Sleep (millisecs); + } + else + { + jassert (sleepEvent != 0); + + // unlike Sleep() this is guaranteed to return to the current thread after + // the time expires, so we'll use this for short waits, which are more likely + // to need to be accurate + WaitForSingleObject (sleepEvent, millisecs); + } +} + +static int lastProcessPriority = -1; + +// called by WindowDriver because Windows does wierd things to process priority +// when you swap apps, and this forces an update when the app is brought to the front. +void juce_repeatLastProcessPriority() throw() +{ + if (lastProcessPriority >= 0) // (avoid changing this if it's not been explicitly set by the app..) + { + DWORD p; + + switch (lastProcessPriority) + { + case Process::LowPriority: + p = IDLE_PRIORITY_CLASS; + break; + + case Process::NormalPriority: + p = NORMAL_PRIORITY_CLASS; + break; + + case Process::HighPriority: + p = HIGH_PRIORITY_CLASS; + break; + + case Process::RealtimePriority: + p = REALTIME_PRIORITY_CLASS; + break; + + default: + jassertfalse // bad priority value + return; + } + + SetPriorityClass (GetCurrentProcess(), p); + } +} + +void Process::setPriority (ProcessPriority prior) +{ + if (lastProcessPriority != (int) prior) + { + lastProcessPriority = (int) prior; + juce_repeatLastProcessPriority(); + } +} + +bool JUCE_API JUCE_CALLTYPE juce_isRunningUnderDebugger() throw() +{ + return IsDebuggerPresent() != FALSE; +} + +bool JUCE_CALLTYPE Process::isRunningUnderDebugger() throw() +{ + return juce_isRunningUnderDebugger(); +} + +void Process::raisePrivilege() +{ + jassertfalse // xxx not implemented +} + +void Process::lowerPrivilege() +{ + jassertfalse // xxx not implemented +} + +void Process::terminate() +{ +#if defined (JUCE_DEBUG) && JUCE_MSVC && JUCE_CHECK_MEMORY_LEAKS + _CrtDumpMemoryLeaks(); +#endif + + // bullet in the head in case there's a problem shutting down.. + ExitProcess (0); +} + +void* Process::loadDynamicLibrary (const String& name) +{ + void* result = 0; + + JUCE_TRY + { + result = (void*) LoadLibrary (name); + } + JUCE_CATCH_ALL + + return result; +} + +void Process::freeDynamicLibrary (void* h) +{ + JUCE_TRY + { + if (h != 0) + FreeLibrary ((HMODULE) h); + } + JUCE_CATCH_ALL +} + +void* Process::getProcedureEntryPoint (void* h, const String& name) +{ + return (h != 0) ? (void*) GetProcAddress ((HMODULE) h, name) + : 0; +} + +InterProcessLock::InterProcessLock (const String& name_) throw() + : internal (0), + name (name_), + reentrancyLevel (0) +{ +} + +InterProcessLock::~InterProcessLock() throw() +{ + exit(); +} + +bool InterProcessLock::enter (const int timeOutMillisecs) throw() +{ + if (reentrancyLevel++ == 0) + { + internal = CreateMutex (0, TRUE, name); + + if (internal != 0 && GetLastError() == ERROR_ALREADY_EXISTS) + { + if (timeOutMillisecs == 0 + || WaitForSingleObject (internal, (timeOutMillisecs < 0) ? INFINITE : timeOutMillisecs) + == WAIT_TIMEOUT) + { + ReleaseMutex (internal); + CloseHandle (internal); + internal = 0; + } + } + } + + return (internal != 0); +} + +void InterProcessLock::exit() throw() +{ + if (--reentrancyLevel == 0 && internal != 0) + { + ReleaseMutex (internal); + CloseHandle (internal); + internal = 0; + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_win32_Threads.cpp *********/ + +/********* Start of inlined file: juce_win32_DynamicLibraryLoader.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +DynamicLibraryLoader::DynamicLibraryLoader (const String& name) +{ + libHandle = LoadLibrary (name); +} + +DynamicLibraryLoader::~DynamicLibraryLoader() +{ + FreeLibrary ((HMODULE) libHandle); +} + +void* DynamicLibraryLoader::findProcAddress (const String& functionName) +{ + return (void*) GetProcAddress ((HMODULE) libHandle, functionName); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_win32_DynamicLibraryLoader.cpp *********/ + +#if ! JUCE_ONLY_BUILD_CORE_LIBRARY + +/********* Start of inlined file: juce_win32_ASIO.cpp *********/ + +#undef WINDOWS + +#if JUCE_ASIO + +/* + This is very frustrating - we only need to use a handful of definitions from + a couple of the header files in Steinberg's ASIO SDK, and it'd be easy to copy + about 30 lines of code into this cpp file to create a fully stand-alone ASIO + implementation... + + ..unfortunately that would break Steinberg's license agreement for use of + their SDK, so I'm not allowed to do this. + + This means that anyone who wants to use JUCE's ASIO abilities will have to: + + 1) Agree to Steinberg's licensing terms and download the ASIO SDK + (see www.steinberg.net/Steinberg/Developers.asp). + + 2) Rebuild the whole of JUCE, setting the global definition JUCE_ASIO (you + can un-comment the "#define JUCE_ASIO" line in juce_Config.h + if you prefer). Make sure that your header search path will find the + iasiodrv.h file that comes with the SDK. (Only about 2-3 of the SDK header + files are actually needed - so to simplify things, you could just copy + these into your JUCE directory). +*/ +#include "iasiodrv.h" // if you're compiling and this line causes an error because + // you don't have the ASIO SDK installed, you can disable ASIO + // support by commenting-out the "#define JUCE_ASIO" line in + // juce_Config.h + +BEGIN_JUCE_NAMESPACE + +// #define ASIO_DEBUGGING + +#ifdef ASIO_DEBUGGING + #define log(a) { Logger::writeToLog (a); DBG (a) } +#else + #define log(a) {} +#endif + +#ifdef ASIO_DEBUGGING +static void logError (const String& context, long error) +{ + String err ("unknown error"); + + if (error == ASE_NotPresent) + err = "Not Present"; + else if (error == ASE_HWMalfunction) + err = "Hardware Malfunction"; + else if (error == ASE_InvalidParameter) + err = "Invalid Parameter"; + else if (error == ASE_InvalidMode) + err = "Invalid Mode"; + else if (error == ASE_SPNotAdvancing) + err = "Sample position not advancing"; + else if (error == ASE_NoClock) + err = "No Clock"; + else if (error == ASE_NoMemory) + err = "Out of memory"; + + log (T("!!error: ") + context + T(" - ") + err); +} +#else + #define logError(a, b) {} +#endif + +class ASIOAudioIODevice; +static ASIOAudioIODevice* volatile currentASIODev = 0; + +static IASIO* volatile asioObject = 0; + +static const int maxASIOChannels = 160; + +static ASIOCallbacks callbacks; +static ASIOBufferInfo bufferInfos[64]; + +static bool volatile insideControlPanelModalLoop = false; +static bool volatile shouldUsePreferredSize = false; + +class JUCE_API ASIOAudioIODevice : public AudioIODevice, + private Thread, + private Timer +{ +public: + Component ourWindow; + + ASIOAudioIODevice (const String& name_, CLSID classId_) + : AudioIODevice (name_, T("ASIO")), + Thread ("Juce ASIO"), + classId (classId_), + currentBitDepth (16), + currentSampleRate (0), + tempBuffer (0), + isOpen_ (false), + isStarted (false), + postOutput (true) + { + name = name_; + + ourWindow.addToDesktop (0); + windowHandle = ourWindow.getWindowHandle(); + + jassert (currentASIODev == 0); + currentASIODev = this; + shouldUseThread = false; + + openDevice(); + } + + ~ASIOAudioIODevice() + { + jassert (currentASIODev == this); + if (currentASIODev == this) + currentASIODev = 0; + + close(); + log ("ASIO - exiting"); + removeCurrentDriver(); + + juce_free (tempBuffer); + + if (isUsingThread) + { + signalThreadShouldExit(); + event1.signal(); + stopThread (3000); + } + } + + void updateSampleRates() + { + // find a list of sample rates.. + const double possibleSampleRates[] = { 44100.0, 48000.0, 88200.0, 96000.0, 176400.0, 192000.0 }; + sampleRates.clear(); + + if (asioObject != 0) + { + for (int index = 0; index < numElementsInArray (possibleSampleRates); ++index) + { + const long err = asioObject->canSampleRate (possibleSampleRates[index]); + + if (err == 0) + { + sampleRates.add ((int) possibleSampleRates[index]); + log (T("rate: ") + String ((int) possibleSampleRates[index])); + } + else if (err != ASE_NoClock) + { + logError (T("CanSampleRate"), err); + } + } + + if (sampleRates.size() == 0) + { + double cr = 0; + const long err = asioObject->getSampleRate (&cr); + log (T("No sample rates supported - current rate: ") + String ((int) cr)); + + if (err == 0) + sampleRates.add ((int) cr); + } + } + } + + const StringArray getOutputChannelNames() + { + return outputChannelNames; + } + + const StringArray getInputChannelNames() + { + return inputChannelNames; + } + + int getNumSampleRates() + { + return sampleRates.size(); + } + + double getSampleRate (int index) + { + return sampleRates [index]; + } + + int getNumBufferSizesAvailable() + { + return bufferSizes.size(); + } + + int getBufferSizeSamples (int index) + { + return bufferSizes [index]; + } + + int getDefaultBufferSize() + { + return preferredSize; + } + + const String open (const BitArray& inputChannels, + const BitArray& outputChannels, + double sr, + int bufferSizeSamples) + { + close(); + currentCallback = 0; + + if (bufferSizeSamples <= 0) + shouldUsePreferredSize = true; + + if (asioObject == 0 || ! isASIOOpen) + { + log ("Warning: device not open"); + const String err (openDevice()); + + if (asioObject == 0 || ! isASIOOpen) + return err; + } + + isStarted = false; + bufferIndex = -1; + long err = 0; + + long newPreferredSize = 0; + + // if the preferred size has just changed, assume it's a control panel thing and use it as the new value. + minSize = 0; + maxSize = 0; + newPreferredSize = 0; + granularity = 0; + + if (asioObject->getBufferSize (&minSize, &maxSize, &newPreferredSize, &granularity) == 0) + { + if (preferredSize != 0 && newPreferredSize != 0 && newPreferredSize != preferredSize) + shouldUsePreferredSize = true; + + preferredSize = newPreferredSize; + } + + // unfortunate workaround for certain manufacturers whose drivers crash horribly if you make + // dynamic changes to the buffer size... + shouldUsePreferredSize = shouldUsePreferredSize + || getName().containsIgnoreCase (T("Digidesign")); + + if (shouldUsePreferredSize) + { + log ("Using preferred size for buffer.."); + + if ((err = asioObject->getBufferSize (&minSize, &maxSize, &preferredSize, &granularity)) == 0) + { + bufferSizeSamples = preferredSize; + } + else + { + bufferSizeSamples = 1024; + logError ("GetBufferSize1", err); + } + + shouldUsePreferredSize = false; + } + + int sampleRate = roundDoubleToInt (sr); + currentSampleRate = sampleRate; + currentBlockSizeSamples = bufferSizeSamples; + currentChansOut.clear(); + currentChansIn.clear(); + + updateSampleRates(); + + if (sampleRate == 0 || (sampleRates.size() > 0 && ! sampleRates.contains (sampleRate))) + sampleRate = sampleRates[0]; + + jassert (sampleRate != 0); + if (sampleRate == 0) + sampleRate = 44100; + + long numSources = 32; + ASIOClockSource clocks[32]; + zeromem (clocks, sizeof (clocks)); + asioObject->getClockSources (clocks, &numSources); + bool isSourceSet = false; + + // careful not to remove this loop because it does more than just logging! + int i; + for (i = 0; i < numSources; ++i) + { + String s ("clock: "); + s += clocks[i].name; + + if (clocks[i].isCurrentSource) + { + isSourceSet = true; + s << " (cur)"; + } + + log (s); + } + + if (numSources > 1 && ! isSourceSet) + { + log ("setting clock source"); + asioObject->setClockSource (clocks[0].index); + Thread::sleep (20); + } + else + { + if (numSources == 0) + { + log ("ASIO - no clock sources!"); + } + } + + double cr = 0; + err = asioObject->getSampleRate (&cr); + if (err == 0) + { + currentSampleRate = cr; + } + else + { + logError ("GetSampleRate", err); + currentSampleRate = 0; + } + + error = String::empty; + needToReset = false; + isReSync = false; + err = 0; + bool buffersCreated = false; + + if (currentSampleRate != sampleRate) + { + log (T("ASIO samplerate: ") + String (currentSampleRate) + T(" to ") + String (sampleRate)); + err = asioObject->setSampleRate (sampleRate); + + if (err == ASE_NoClock && numSources > 0) + { + log ("trying to set a clock source.."); + Thread::sleep (10); + err = asioObject->setClockSource (clocks[0].index); + if (err != 0) + { + logError ("SetClock", err); + } + + Thread::sleep (10); + err = asioObject->setSampleRate (sampleRate); + } + } + + if (err == 0) + { + currentSampleRate = sampleRate; + + if (needToReset) + { + if (isReSync) + { + log ("Resync request"); + } + + log ("! Resetting ASIO after sample rate change"); + removeCurrentDriver(); + + loadDriver(); + const String error (initDriver()); + + if (error.isNotEmpty()) + { + log (T("ASIOInit: ") + error); + } + + needToReset = false; + isReSync = false; + } + + numActiveInputChans = 0; + numActiveOutputChans = 0; + + ASIOBufferInfo* info = bufferInfos; + int i; + for (i = 0; i < numInputs; ++i) + { + if (inputChannels[i]) + { + currentChansIn.setBit (i); + info->isInput = 1; + info->channelNum = i; + info->buffers[0] = info->buffers[1] = 0; + ++info; + ++numActiveInputChans; + } + } + + for (i = 0; i < numOutputs; ++i) + { + if (outputChannels[i]) + { + currentChansOut.setBit (i); + info->isInput = 0; + info->channelNum = i; + info->buffers[0] = info->buffers[1] = 0; + ++info; + ++numActiveOutputChans; + } + } + + const int totalBuffers = numActiveInputChans + numActiveOutputChans; + + callbacks.bufferSwitch = &bufferSwitchCallback; + callbacks.sampleRateDidChange = &sampleRateChangedCallback; + callbacks.asioMessage = &asioMessagesCallback; + callbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfoCallback; + + log ("disposing buffers"); + err = asioObject->disposeBuffers(); + + log (T("creating buffers: ") + String (totalBuffers) + T(", ") + String (currentBlockSizeSamples)); + err = asioObject->createBuffers (bufferInfos, + totalBuffers, + currentBlockSizeSamples, + &callbacks); + + if (err != 0) + { + currentBlockSizeSamples = preferredSize; + logError ("create buffers 2", err); + + asioObject->disposeBuffers(); + err = asioObject->createBuffers (bufferInfos, + totalBuffers, + currentBlockSizeSamples, + &callbacks); + } + + if (err == 0) + { + buffersCreated = true; + + jassert (! isThreadRunning()); + + juce_free (tempBuffer); + + tempBuffer = (float*) juce_calloc (totalBuffers * currentBlockSizeSamples * sizeof (float) + 128); + + int n = 0; + Array types; + currentBitDepth = 16; + + for (i = 0; i < jmin (numInputs, maxASIOChannels); ++i) + { + if (inputChannels[i]) + { + inBuffers[i] = tempBuffer + (currentBlockSizeSamples * n++); + + ASIOChannelInfo channelInfo; + zerostruct (channelInfo); + + channelInfo.channel = i; + channelInfo.isInput = 1; + asioObject->getChannelInfo (&channelInfo); + + types.addIfNotAlreadyThere (channelInfo.type); + typeToFormatParameters (channelInfo.type, + inputChannelBitDepths[i], + inputChannelBytesPerSample[i], + inputChannelIsFloat[i], + inputChannelLittleEndian[i]); + + currentBitDepth = jmax (currentBitDepth, inputChannelBitDepths[i]); + } + else + { + inBuffers[i] = 0; + } + } + + for (i = 0; i < jmin (numOutputs, maxASIOChannels); ++i) + { + if (outputChannels[i]) + { + outBuffers[i] = tempBuffer + (currentBlockSizeSamples * n++); + + ASIOChannelInfo channelInfo; + zerostruct (channelInfo); + + channelInfo.channel = i; + channelInfo.isInput = 0; + asioObject->getChannelInfo (&channelInfo); + + types.addIfNotAlreadyThere (channelInfo.type); + typeToFormatParameters (channelInfo.type, + outputChannelBitDepths[i], + outputChannelBytesPerSample[i], + outputChannelIsFloat[i], + outputChannelLittleEndian[i]); + + currentBitDepth = jmax (currentBitDepth, outputChannelBitDepths[i]); + } + else + { + outBuffers[i] = 0; + } + } + + for (i = types.size(); --i >= 0;) + { + log (T("channel format: ") + String (types[i])); + } + + jassert (n <= totalBuffers); + + n = numActiveInputChans; + for (i = 0; i < numOutputs; ++i) + { + if (outputChannels[i]) + { + const int size = currentBlockSizeSamples * (outputChannelBitDepths[i] >> 3); + + if (bufferInfos[n].buffers[0] == 0 + || bufferInfos[n].buffers[1] == 0) + { + log ("!! Null buffers"); + } + else + { + zeromem (bufferInfos[n].buffers[0], size); + zeromem (bufferInfos[n].buffers[1], size); + } + + ++n; + } + } + + jassert (n <= totalBuffers); + + inputLatency = outputLatency = 0; + + if (asioObject->getLatencies (&inputLatency, &outputLatency) != 0) + { + log ("ASIO - no latencies"); + } + else + { + log (T("ASIO latencies: ") + + String ((int) outputLatency) + + T(", ") + + String ((int) inputLatency)); + } + + isOpen_ = true; + isThreadReady = false; + + if (isUsingThread) + { + event1.wait (1); // reset the event in case it was flipped by a callback from the ASIO->start call in openDevice() + startThread (8); + + int count = 5000; + while (--count > 0 && ! isThreadReady) + sleep (1); + } + + if (isUsingThread && ! isThreadRunning()) + { + error = "Can't start thread!"; + } + else + { + log ("starting ASIO"); + calledback = false; + err = asioObject->start(); + + if (err != 0) + { + if (isUsingThread) + { + signalThreadShouldExit(); + event1.signal(); + stopThread (3000); + } + + isOpen_ = false; + log ("ASIO - stop on failure"); + Thread::sleep (10); + asioObject->stop(); + error = "Can't start device"; + Thread::sleep (10); + } + else + { + int count = 300; + while (--count > 0 && ! calledback) + Thread::sleep (10); + + isStarted = true; + + if (! calledback) + { + error = "Device didn't start correctly"; + log ("ASIO didn't callback - stopping.."); + asioObject->stop(); + } + } + } + } + else + { + error = "Can't create i/o buffers"; + } + } + else + { + error = "Can't set sample rate: "; + error << sampleRate; + } + + if (error.isNotEmpty()) + { + logError (error, err); + + if (asioObject != 0 && buffersCreated) + asioObject->disposeBuffers(); + + Thread::sleep (20); + isStarted = false; + isOpen_ = false; + close(); + } + + needToReset = false; + isReSync = false; + + return error; + } + + void close() + { + error = String::empty; + stopTimer(); + stop(); + + if (isASIOOpen && isOpen_) + { + const ScopedLock sl (callbackLock); + + if (isUsingThread) + { + signalThreadShouldExit(); + event1.signal(); + stopThread (3000); + } + + isOpen_ = false; + isStarted = false; + needToReset = false; + isReSync = false; + + log ("ASIO - stopping"); + + if (asioObject != 0) + { + Thread::sleep (20); + asioObject->stop(); + Thread::sleep (10); + asioObject->disposeBuffers(); + } + + Thread::sleep (10); + } + } + + bool isOpen() + { + return isOpen_ || insideControlPanelModalLoop; + } + + int getCurrentBufferSizeSamples() + { + return currentBlockSizeSamples; + } + + double getCurrentSampleRate() + { + return currentSampleRate; + } + + const BitArray getActiveOutputChannels() const + { + return currentChansOut; + } + + const BitArray getActiveInputChannels() const + { + return currentChansIn; + } + + int getCurrentBitDepth() + { + return currentBitDepth; + } + + int getOutputLatencyInSamples() + { + return outputLatency + currentBlockSizeSamples / 4; + } + + int getInputLatencyInSamples() + { + return inputLatency + currentBlockSizeSamples / 4; + } + + void start (AudioIODeviceCallback* callback) + { + if (callback != 0) + { + callback->audioDeviceAboutToStart (this); + + const ScopedLock sl (callbackLock); + currentCallback = callback; + } + } + + void stop() + { + AudioIODeviceCallback* const lastCallback = currentCallback; + + { + const ScopedLock sl (callbackLock); + currentCallback = 0; + } + + if (lastCallback != 0) + lastCallback->audioDeviceStopped(); + } + + bool isPlaying() + { + return isASIOOpen + && (isThreadRunning() || ! isUsingThread) + && (currentCallback != 0); + } + + const String getLastError() + { + return error; + } + + void setUsingThread (bool b) + { + shouldUseThread = b; + } + + bool hasControlPanel() const + { + return true; + } + + bool showControlPanel() + { + log ("ASIO - showing control panel"); + + Component modalWindow (String::empty); + modalWindow.setOpaque (true); + modalWindow.addToDesktop (0); + modalWindow.enterModalState(); + bool done = false; + + JUCE_TRY + { + close(); + insideControlPanelModalLoop = true; + + const uint32 started = Time::getMillisecondCounter(); + + if (asioObject != 0) + { + asioObject->controlPanel(); + + const int spent = (int) Time::getMillisecondCounter() - (int) started; + + log (T("spent: ") + String (spent)); + + if (spent > 300) + { + shouldUsePreferredSize = true; + done = true; + } + } + } + JUCE_CATCH_ALL + + insideControlPanelModalLoop = false; + return done; + } + + void run() + { + isThreadReady = true; + + for (;;) + { + event1.wait(); + + if (threadShouldExit()) + break; + + processBuffer(); + } + + if (bufferIndex < 0) + { + log ("! ASIO callback never called"); + } + } + + void resetRequest() throw() + { + needToReset = true; + } + + void resyncRequest() throw() + { + needToReset = true; + isReSync = true; + } + + void timerCallback() + { + if (! insideControlPanelModalLoop) + { + stopTimer(); + + // used to cause a reset + log ("! ASIO restart request!"); + + if (isOpen_) + { + AudioIODeviceCallback* const oldCallback = currentCallback; + + close(); + open (currentChansIn, currentChansOut, + currentSampleRate, currentBlockSizeSamples); + + if (oldCallback != 0) + start (oldCallback); + } + } + else + { + startTimer (100); + } + } + + juce_UseDebuggingNewOperator + +private: + + void* windowHandle; + CLSID classId; + String error; + + long numInputs, numOutputs; + StringArray outputChannelNames, inputChannelNames; + + Array sampleRates, bufferSizes; + long inputLatency, outputLatency; + long minSize, maxSize, preferredSize, granularity; + + int volatile currentBlockSizeSamples; + int volatile currentBitDepth; + double volatile currentSampleRate; + BitArray currentChansOut, currentChansIn; + AudioIODeviceCallback* volatile currentCallback; + CriticalSection callbackLock; + + float* inBuffers[maxASIOChannels]; + float* outBuffers[maxASIOChannels]; + int inputChannelBitDepths[maxASIOChannels]; + int outputChannelBitDepths[maxASIOChannels]; + int inputChannelBytesPerSample[maxASIOChannels]; + int outputChannelBytesPerSample[maxASIOChannels]; + bool inputChannelIsFloat[maxASIOChannels]; + bool outputChannelIsFloat[maxASIOChannels]; + bool inputChannelLittleEndian[maxASIOChannels]; + bool outputChannelLittleEndian[maxASIOChannels]; + + WaitableEvent event1; + float* tempBuffer; + int volatile bufferIndex, numActiveInputChans, numActiveOutputChans; + + bool isOpen_, isStarted; + bool isUsingThread, shouldUseThread; + bool volatile isASIOOpen; + bool volatile calledback; + bool volatile littleEndian, postOutput, needToReset, isReSync, isThreadReady; + + static void removeCurrentDriver() + { + if (asioObject != 0) + { + asioObject->Release(); + asioObject = 0; + } + } + + bool loadDriver() + { + removeCurrentDriver(); + + JUCE_TRY + { + if (CoCreateInstance (classId, 0, CLSCTX_INPROC_SERVER, + classId, (void**) &asioObject) == S_OK) + { + return true; + } + } + JUCE_CATCH_ALL + + asioObject = 0; + + return false; + } + + const String initDriver() + { + if (asioObject != 0) + { + char buffer [256]; + zeromem (buffer, sizeof (buffer)); + + if (! asioObject->init (windowHandle)) + { + asioObject->getErrorMessage (buffer); + return String (buffer, sizeof (buffer) - 1); + } + + // just in case any daft drivers expect this to be called.. + asioObject->getDriverName (buffer); + + return String::empty; + } + + return "No Driver"; + } + + const String openDevice() + { + // use this in case the driver starts opening dialog boxes.. + Component modalWindow (String::empty); + modalWindow.setOpaque (true); + modalWindow.addToDesktop (0); + modalWindow.enterModalState(); + + isUsingThread = shouldUseThread; + + // open the device and get its info.. + log (T("opening ASIO device: ") + getName()); + + needToReset = false; + isReSync = false; + outputChannelNames.clear(); + inputChannelNames.clear(); + bufferSizes.clear(); + sampleRates.clear(); + isASIOOpen = false; + isOpen_ = false; + numInputs = 0; + numOutputs = 0; + currentCallback = 0; + + error = String::empty; + + if (getName().isEmpty()) + return error; + + long err = 0; + + if (loadDriver()) + { + String driverName; + + if ((error = initDriver()).isEmpty()) + { + numInputs = 0; + numOutputs = 0; + + if (asioObject != 0 + && (err = asioObject->getChannels (&numInputs, &numOutputs)) == 0) + { + log (String ((int) numInputs) + T(" in, ") + String ((int) numOutputs) + T(" out")); + + if ((err = asioObject->getBufferSize (&minSize, &maxSize, &preferredSize, &granularity)) == 0) + { + // find a list of buffer sizes.. + log (String ((int) minSize) + T(" ") + String ((int) maxSize) + T(" ") + String ((int)preferredSize) + T(" ") + String ((int)granularity)); + + if (granularity >= 0) + { + granularity = jmax (1, (int) granularity); + + for (int i = jmax (minSize, (int) granularity); i < jmin (6400, maxSize); i += granularity) + bufferSizes.addIfNotAlreadyThere (granularity * (i / granularity)); + } + else if (granularity < 0) + { + for (int i = 0; i < 18; ++i) + { + const int s = (1 << i); + + if (s >= minSize && s <= maxSize) + bufferSizes.add (s); + } + } + + if (! bufferSizes.contains (preferredSize)) + bufferSizes.insert (0, preferredSize); + + double currentRate = 0; + asioObject->getSampleRate (¤tRate); + + if (currentRate <= 0.0 || currentRate > 192001.0) + { + log ("setting sample rate"); + err = asioObject->setSampleRate (44100.0); + if (err != 0) + { + logError ("setting sample rate", err); + } + + asioObject->getSampleRate (¤tRate); + } + + currentSampleRate = currentRate; + + postOutput = (asioObject->outputReady() == 0); + if (postOutput) + { + log ("ASIO outputReady = ok"); + } + + updateSampleRates(); + + // ..because cubase does it at this point + inputLatency = outputLatency = 0; + if (asioObject->getLatencies (&inputLatency, &outputLatency) != 0) + { + log ("ASIO - no latencies"); + } + + log (String ("latencies: ") + + String ((int) inputLatency) + + T(", ") + String ((int) outputLatency)); + + // create some dummy buffers now.. because cubase does.. + numActiveInputChans = 0; + numActiveOutputChans = 0; + + ASIOBufferInfo* info = bufferInfos; + int i, numChans = 0; + for (i = 0; i < jmin (2, numInputs); ++i) + { + info->isInput = 1; + info->channelNum = i; + info->buffers[0] = info->buffers[1] = 0; + ++info; + ++numChans; + } + + const int outputBufferIndex = numChans; + + for (i = 0; i < jmin (2, numOutputs); ++i) + { + info->isInput = 0; + info->channelNum = i; + info->buffers[0] = info->buffers[1] = 0; + ++info; + ++numChans; + } + + callbacks.bufferSwitch = &bufferSwitchCallback; + callbacks.sampleRateDidChange = &sampleRateChangedCallback; + callbacks.asioMessage = &asioMessagesCallback; + callbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfoCallback; + + log (T("creating buffers (dummy): ") + String (numChans) + + T(", ") + String ((int) preferredSize)); + + if (preferredSize > 0) + { + err = asioObject->createBuffers (bufferInfos, numChans, preferredSize, &callbacks); + if (err != 0) + { + logError ("dummy buffers", err); + } + } + + long newInps = 0, newOuts = 0; + asioObject->getChannels (&newInps, &newOuts); + + if (numInputs != newInps || numOutputs != newOuts) + { + numInputs = newInps; + numOutputs = newOuts; + + log (String ((int) numInputs) + T(" in; ") + + String ((int) numOutputs) + T(" out")); + } + + updateSampleRates(); + + ASIOChannelInfo channelInfo; + channelInfo.type = 0; + + for (i = 0; i < numInputs; ++i) + { + zerostruct (channelInfo); + channelInfo.channel = i; + channelInfo.isInput = 1; + asioObject->getChannelInfo (&channelInfo); + + inputChannelNames.add (String (channelInfo.name)); + } + + for (i = 0; i < numOutputs; ++i) + { + zerostruct (channelInfo); + channelInfo.channel = i; + channelInfo.isInput = 0; + asioObject->getChannelInfo (&channelInfo); + + outputChannelNames.add (String (channelInfo.name)); + + typeToFormatParameters (channelInfo.type, + outputChannelBitDepths[i], + outputChannelBytesPerSample[i], + outputChannelIsFloat[i], + outputChannelLittleEndian[i]); + + if (i < 2) + { + // clear the channels that are used with the dummy stuff + const int bytesPerBuffer = preferredSize * (outputChannelBitDepths[i] >> 3); + zeromem (bufferInfos [outputBufferIndex + i].buffers[0], bytesPerBuffer); + zeromem (bufferInfos [outputBufferIndex + i].buffers[1], bytesPerBuffer); + } + } + + outputChannelNames.trim(); + inputChannelNames.trim(); + outputChannelNames.appendNumbersToDuplicates (false, true); + inputChannelNames.appendNumbersToDuplicates (false, true); + + // start and stop because cubase does it.. + asioObject->getLatencies (&inputLatency, &outputLatency); + + if ((err = asioObject->start()) != 0) + { + // ignore an error here, as it might start later after setting other stuff up + logError ("ASIO start", err); + } + + Thread::sleep (100); + asioObject->stop(); + } + else + { + error = "Can't detect buffer sizes"; + } + } + else + { + error = "Can't detect asio channels"; + } + } + } + else + { + error = "No such device"; + } + + if (error.isNotEmpty()) + { + logError (error, err); + + if (asioObject != 0) + asioObject->disposeBuffers(); + + removeCurrentDriver(); + isASIOOpen = false; + } + else + { + isASIOOpen = true; + log ("ASIO device open"); + } + + isOpen_ = false; + needToReset = false; + isReSync = false; + + return error; + } + + void callback (const long index) throw() + { + if (isStarted) + { + bufferIndex = index; + + if (isUsingThread) // if not started, just use processBuffer() to clear the buffers directly + { + event1.signal(); + + if (postOutput && (! isThreadRunning()) && asioObject != 0) + asioObject->outputReady(); + } + else + { + processBuffer(); + } + } + else + { + if (postOutput && (asioObject != 0)) + asioObject->outputReady(); + } + + calledback = true; + } + + void processBuffer() throw() + { + const ASIOBufferInfo* const infos = bufferInfos; + const int bi = bufferIndex; + + const ScopedLock sl (callbackLock); + + if (needToReset) + { + needToReset = false; + + if (isReSync) + { + log ("! ASIO resync"); + isReSync = false; + } + else + { + startTimer (20); + } + } + + if (bi >= 0) + { + const int samps = currentBlockSizeSamples; + + if (currentCallback != 0) + { + int n = 0; + int i; + for (i = 0; i < numInputs; ++i) + { + float* const dst = inBuffers[i]; + + if (dst != 0) + { + const char* const src = (const char*) (infos[n].buffers[bi]); + + if (inputChannelIsFloat[i]) + { + memcpy (dst, src, samps * sizeof (float)); + } + else + { + jassert (dst == tempBuffer + (samps * n)); + + switch (inputChannelBitDepths[i]) + { + case 16: + convertInt16ToFloat (src, dst, inputChannelBytesPerSample[i], + samps, inputChannelLittleEndian[i]); + break; + + case 24: + convertInt24ToFloat (src, dst, inputChannelBytesPerSample[i], + samps, inputChannelLittleEndian[i]); + break; + + case 32: + convertInt32ToFloat (src, dst, inputChannelBytesPerSample[i], + samps, inputChannelLittleEndian[i]); + break; + + case 64: + jassertfalse + break; + } + } + + ++n; + } + } + + currentCallback->audioDeviceIOCallback ((const float**) inBuffers, + numInputs, + outBuffers, + numOutputs, + samps); + + for (i = 0; i < numOutputs; ++i) + { + float* const src = outBuffers[i]; + + if (src != 0) + { + char* const dst = (char*) (infos[n].buffers[bi]); + + if (outputChannelIsFloat[i]) + { + memcpy (dst, src, samps * sizeof (float)); + } + else + { + jassert (src == tempBuffer + (samps * n)); + + switch (outputChannelBitDepths[i]) + { + case 16: + convertFloatToInt16 (src, dst, outputChannelBytesPerSample[i], + samps, outputChannelLittleEndian[i]); + break; + + case 24: + convertFloatToInt24 (src, dst, outputChannelBytesPerSample[i], + samps, outputChannelLittleEndian[i]); + break; + + case 32: + convertFloatToInt32 (src, dst, outputChannelBytesPerSample[i], + samps, outputChannelLittleEndian[i]); + break; + + case 64: + jassertfalse + break; + } + } + + ++n; + } + } + } + else + { + int n = 0; + int i; + + for (i = 0; i < numInputs; ++i) + if (inBuffers[i] != 0) + ++n; + + for (i = 0; i < numOutputs; ++i) + { + if (outBuffers[i] != 0) + { + const int bytesPerBuffer = samps * (outputChannelBitDepths[i] >> 3); + zeromem (infos[n].buffers[bi], bytesPerBuffer); + ++n; + } + } + } + } + + if (postOutput) + asioObject->outputReady(); + } + + static ASIOTime* bufferSwitchTimeInfoCallback (ASIOTime*, long index, long) throw() + { + if (currentASIODev != 0) + currentASIODev->callback (index); + + return 0; + } + + static void bufferSwitchCallback (long index, long) throw() + { + if (currentASIODev != 0) + currentASIODev->callback (index); + } + + static long asioMessagesCallback (long selector, long value, void*, double*) throw() + { + switch (selector) + { + case kAsioSelectorSupported: + if (value == kAsioResetRequest + || value == kAsioEngineVersion + || value == kAsioResyncRequest + || value == kAsioLatenciesChanged + || value == kAsioSupportsInputMonitor) + return 1; + break; + + case kAsioBufferSizeChange: + break; + + case kAsioResetRequest: + if (currentASIODev != 0) + currentASIODev->resetRequest(); + + return 1; + + case kAsioResyncRequest: + if (currentASIODev != 0) + currentASIODev->resyncRequest(); + + return 1; + + case kAsioLatenciesChanged: + return 1; + + case kAsioEngineVersion: + return 2; + + case kAsioSupportsTimeInfo: + case kAsioSupportsTimeCode: + return 0; + } + + return 0; + } + + static void sampleRateChangedCallback (ASIOSampleRate) throw() + { + } + + static void convertInt16ToFloat (const char* src, + float* dest, + const int srcStrideBytes, + int numSamples, + const bool littleEndian) throw() + { + const double g = 1.0 / 32768.0; + + if (littleEndian) + { + while (--numSamples >= 0) + { + *dest++ = (float) (g * (short) littleEndianShort (src)); + src += srcStrideBytes; + } + } + else + { + while (--numSamples >= 0) + { + *dest++ = (float) (g * (short) bigEndianShort (src)); + src += srcStrideBytes; + } + } + } + + static void convertFloatToInt16 (const float* src, + char* dest, + const int dstStrideBytes, + int numSamples, + const bool littleEndian) throw() + { + const double maxVal = (double) 0x7fff; + + if (littleEndian) + { + while (--numSamples >= 0) + { + *(uint16*) dest = swapIfBigEndian ((uint16) (short) roundDoubleToInt (jlimit (-maxVal, maxVal, maxVal * *src++))); + dest += dstStrideBytes; + } + } + else + { + while (--numSamples >= 0) + { + *(uint16*) dest = swapIfLittleEndian ((uint16) (short) roundDoubleToInt (jlimit (-maxVal, maxVal, maxVal * *src++))); + dest += dstStrideBytes; + } + } + } + + static void convertInt24ToFloat (const char* src, + float* dest, + const int srcStrideBytes, + int numSamples, + const bool littleEndian) throw() + { + const double g = 1.0 / 0x7fffff; + + if (littleEndian) + { + while (--numSamples >= 0) + { + *dest++ = (float) (g * littleEndian24Bit (src)); + src += srcStrideBytes; + } + } + else + { + while (--numSamples >= 0) + { + *dest++ = (float) (g * bigEndian24Bit (src)); + src += srcStrideBytes; + } + } + } + + static void convertFloatToInt24 (const float* src, + char* dest, + const int dstStrideBytes, + int numSamples, + const bool littleEndian) throw() + { + const double maxVal = (double) 0x7fffff; + + if (littleEndian) + { + while (--numSamples >= 0) + { + littleEndian24BitToChars ((uint32) roundDoubleToInt (jlimit (-maxVal, maxVal, maxVal * *src++)), dest); + dest += dstStrideBytes; + } + } + else + { + while (--numSamples >= 0) + { + bigEndian24BitToChars ((uint32) roundDoubleToInt (jlimit (-maxVal, maxVal, maxVal * *src++)), dest); + dest += dstStrideBytes; + } + } + } + + static void convertInt32ToFloat (const char* src, + float* dest, + const int srcStrideBytes, + int numSamples, + const bool littleEndian) throw() + { + const double g = 1.0 / 0x7fffffff; + + if (littleEndian) + { + while (--numSamples >= 0) + { + *dest++ = (float) (g * (int) littleEndianInt (src)); + src += srcStrideBytes; + } + } + else + { + while (--numSamples >= 0) + { + *dest++ = (float) (g * (int) bigEndianInt (src)); + src += srcStrideBytes; + } + } + } + + static void convertFloatToInt32 (const float* src, + char* dest, + const int dstStrideBytes, + int numSamples, + const bool littleEndian) throw() + { + const double maxVal = (double) 0x7fffffff; + + if (littleEndian) + { + while (--numSamples >= 0) + { + *(uint32*) dest = swapIfBigEndian ((uint32) roundDoubleToInt (jlimit (-maxVal, maxVal, maxVal * *src++))); + dest += dstStrideBytes; + } + } + else + { + while (--numSamples >= 0) + { + *(uint32*) dest = swapIfLittleEndian ((uint32) roundDoubleToInt (jlimit (-maxVal, maxVal, maxVal * *src++))); + dest += dstStrideBytes; + } + } + } + + static void typeToFormatParameters (const long type, + int& bitDepth, + int& byteStride, + bool& formatIsFloat, + bool& littleEndian) throw() + { + bitDepth = 0; + littleEndian = false; + formatIsFloat = false; + + switch (type) + { + case ASIOSTInt16MSB: + case ASIOSTInt16LSB: + case ASIOSTInt32MSB16: + case ASIOSTInt32LSB16: + bitDepth = 16; break; + + case ASIOSTFloat32MSB: + case ASIOSTFloat32LSB: + formatIsFloat = true; + bitDepth = 32; break; + + case ASIOSTInt32MSB: + case ASIOSTInt32LSB: + bitDepth = 32; break; + + case ASIOSTInt24MSB: + case ASIOSTInt24LSB: + case ASIOSTInt32MSB24: + case ASIOSTInt32LSB24: + case ASIOSTInt32MSB18: + case ASIOSTInt32MSB20: + case ASIOSTInt32LSB18: + case ASIOSTInt32LSB20: + bitDepth = 24; break; + + case ASIOSTFloat64MSB: + case ASIOSTFloat64LSB: + default: + bitDepth = 64; + break; + } + + switch (type) + { + case ASIOSTInt16MSB: + case ASIOSTInt32MSB16: + case ASIOSTFloat32MSB: + case ASIOSTFloat64MSB: + case ASIOSTInt32MSB: + case ASIOSTInt32MSB18: + case ASIOSTInt32MSB20: + case ASIOSTInt32MSB24: + case ASIOSTInt24MSB: + littleEndian = false; break; + + case ASIOSTInt16LSB: + case ASIOSTInt32LSB16: + case ASIOSTFloat32LSB: + case ASIOSTFloat64LSB: + case ASIOSTInt32LSB: + case ASIOSTInt32LSB18: + case ASIOSTInt32LSB20: + case ASIOSTInt32LSB24: + case ASIOSTInt24LSB: + littleEndian = true; break; + + default: + break; + } + + switch (type) + { + case ASIOSTInt16LSB: + case ASIOSTInt16MSB: + byteStride = 2; break; + + case ASIOSTInt24LSB: + case ASIOSTInt24MSB: + byteStride = 3; break; + + case ASIOSTInt32MSB16: + case ASIOSTInt32LSB16: + case ASIOSTInt32MSB: + case ASIOSTInt32MSB18: + case ASIOSTInt32MSB20: + case ASIOSTInt32MSB24: + case ASIOSTInt32LSB: + case ASIOSTInt32LSB18: + case ASIOSTInt32LSB20: + case ASIOSTInt32LSB24: + case ASIOSTFloat32LSB: + case ASIOSTFloat32MSB: + byteStride = 4; break; + + case ASIOSTFloat64MSB: + case ASIOSTFloat64LSB: + byteStride = 8; break; + + default: + break; + } + } +}; + +class ASIOAudioIODeviceType : public AudioIODeviceType +{ +public: + ASIOAudioIODeviceType() + : AudioIODeviceType (T("ASIO")), + classIds (2), + hasScanned (false) + { + CoInitialize (0); + } + + ~ASIOAudioIODeviceType() + { + } + + void scanForDevices() + { + hasScanned = true; + + deviceNames.clear(); + classIds.clear(); + + HKEY hk = 0; + int index = 0; + + if (RegOpenKeyA (HKEY_LOCAL_MACHINE, "software\\asio", &hk) == ERROR_SUCCESS) + { + for (;;) + { + char name [256]; + + if (RegEnumKeyA (hk, index++, name, 256) == ERROR_SUCCESS) + { + addDriverInfo (name, hk); + } + else + { + break; + } + } + + RegCloseKey (hk); + } + } + + const StringArray getDeviceNames (const bool /*preferInputNames*/) const + { + jassert (hasScanned); // need to call scanForDevices() before doing this + + return deviceNames; + } + + const String getDefaultDeviceName (const bool /*preferInputNames*/, + const int /*numInputChannelsNeeded*/, + const int /*numOutputChannelsNeeded*/) const + { + jassert (hasScanned); // need to call scanForDevices() before doing this + + return deviceNames [0]; + } + + AudioIODevice* createDevice (const String& deviceName) + { + jassert (hasScanned); // need to call scanForDevices() before doing this + + const int index = deviceNames.indexOf (deviceName); + + if (index >= 0) + { + jassert (currentASIODev == 0); // unfortunately you can't have more than one ASIO device + // open at the same time.. + + if (currentASIODev == 0) + return new ASIOAudioIODevice (deviceName, *(classIds [index])); + } + + return 0; + } + + juce_UseDebuggingNewOperator + +private: + StringArray deviceNames; + OwnedArray classIds; + + bool hasScanned; + + static bool checkClassIsOk (const String& classId) + { + HKEY hk = 0; + bool ok = false; + + if (RegOpenKeyA (HKEY_CLASSES_ROOT, "clsid", &hk) == ERROR_SUCCESS) + { + int index = 0; + + for (;;) + { + char buf [512]; + + if (RegEnumKeyA (hk, index++, buf, 512) == ERROR_SUCCESS) + { + if (classId.equalsIgnoreCase (buf)) + { + HKEY subKey, pathKey; + + if (RegOpenKeyExA (hk, buf, 0, KEY_READ, &subKey) == ERROR_SUCCESS) + { + if (RegOpenKeyExA (subKey, "InprocServer32", 0, KEY_READ, &pathKey) == ERROR_SUCCESS) + { + char pathName [600]; + DWORD dtype = REG_SZ; + DWORD dsize = sizeof (pathName); + + if (RegQueryValueExA (pathKey, 0, 0, &dtype, + (LPBYTE) pathName, &dsize) == ERROR_SUCCESS) + { + OFSTRUCT of; + zerostruct (of); + + of.cBytes = sizeof (of); + + ok = (OpenFile (String (pathName), &of, OF_EXIST) != 0); + } + + RegCloseKey (pathKey); + } + + RegCloseKey (subKey); + } + + break; + } + } + else + { + break; + } + } + + RegCloseKey (hk); + } + + return ok; + } + + void addDriverInfo (const String& keyName, HKEY hk) + { + HKEY subKey; + + if (RegOpenKeyExA (hk, keyName, 0, KEY_READ, &subKey) == ERROR_SUCCESS) + { + char buf [256]; + DWORD dtype = REG_SZ; + DWORD dsize = sizeof (buf); + zeromem (buf, dsize); + + if (RegQueryValueExA (subKey, "clsid", 0, &dtype, (LPBYTE) buf, &dsize) == ERROR_SUCCESS) + { + if (dsize > 0 && checkClassIsOk (buf)) + { + wchar_t classIdStr [130]; + MultiByteToWideChar (CP_ACP, 0, buf, -1, classIdStr, 128); + + String deviceName; + CLSID classId; + + if (CLSIDFromString ((LPOLESTR) classIdStr, &classId) == S_OK) + { + dtype = REG_SZ; + dsize = sizeof (buf); + + if (RegQueryValueExA (subKey, "description", 0, &dtype, (LPBYTE) buf, &dsize) == ERROR_SUCCESS) + deviceName = buf; + else + deviceName = keyName; + + log (T("found ") + deviceName); + deviceNames.add (deviceName); + classIds.add (new CLSID (classId)); + } + } + + RegCloseKey (subKey); + } + } + } + + ASIOAudioIODeviceType (const ASIOAudioIODeviceType&); + const ASIOAudioIODeviceType& operator= (const ASIOAudioIODeviceType&); +}; + +AudioIODeviceType* juce_createASIOAudioIODeviceType() +{ + return new ASIOAudioIODeviceType(); +} + +END_JUCE_NAMESPACE + +#undef log + +#endif +/********* End of inlined file: juce_win32_ASIO.cpp *********/ + +/********* Start of inlined file: juce_win32_AudioCDReader.cpp *********/ +#ifdef _MSC_VER + #pragma warning (disable: 4514) + #pragma warning (push) +#endif + +#include + +#if JUCE_USE_CDBURNER + // you'll need the Platform SDK for these headers - if you don't have it and don't + // need to use CD-burning, then you might just want to disable the JUCE_USE_CDBURNER + // flag in juce_Config.h to avoid these includes. + #include + #include +#endif + +BEGIN_JUCE_NAMESPACE + +#ifdef _MSC_VER + #pragma warning (pop) +#endif + +//*************************************************************************** +// %%% TARGET STATUS VALUES %%% +//*************************************************************************** +#define STATUS_GOOD 0x00 // Status Good +#define STATUS_CHKCOND 0x02 // Check Condition +#define STATUS_CONDMET 0x04 // Condition Met +#define STATUS_BUSY 0x08 // Busy +#define STATUS_INTERM 0x10 // Intermediate +#define STATUS_INTCDMET 0x14 // Intermediate-condition met +#define STATUS_RESCONF 0x18 // Reservation conflict +#define STATUS_COMTERM 0x22 // Command Terminated +#define STATUS_QFULL 0x28 // Queue full + +//*************************************************************************** +// %%% SCSI MISCELLANEOUS EQUATES %%% +//*************************************************************************** +#define MAXLUN 7 // Maximum Logical Unit Id +#define MAXTARG 7 // Maximum Target Id +#define MAX_SCSI_LUNS 64 // Maximum Number of SCSI LUNs +#define MAX_NUM_HA 8 // Maximum Number of SCSI HA's + +//*************************************************************************** +// %%% Commands for all Device Types %%% +//*************************************************************************** +#define SCSI_CHANGE_DEF 0x40 // Change Definition (Optional) +#define SCSI_COMPARE 0x39 // Compare (O) +#define SCSI_COPY 0x18 // Copy (O) +#define SCSI_COP_VERIFY 0x3A // Copy and Verify (O) +#define SCSI_INQUIRY 0x12 // Inquiry (MANDATORY) +#define SCSI_LOG_SELECT 0x4C // Log Select (O) +#define SCSI_LOG_SENSE 0x4D // Log Sense (O) +#define SCSI_MODE_SEL6 0x15 // Mode Select 6-byte (Device Specific) +#define SCSI_MODE_SEL10 0x55 // Mode Select 10-byte (Device Specific) +#define SCSI_MODE_SEN6 0x1A // Mode Sense 6-byte (Device Specific) +#define SCSI_MODE_SEN10 0x5A // Mode Sense 10-byte (Device Specific) +#define SCSI_READ_BUFF 0x3C // Read Buffer (O) +#define SCSI_REQ_SENSE 0x03 // Request Sense (MANDATORY) +#define SCSI_SEND_DIAG 0x1D // Send Diagnostic (O) +#define SCSI_TST_U_RDY 0x00 // Test Unit Ready (MANDATORY) +#define SCSI_WRITE_BUFF 0x3B // Write Buffer (O) + +//*************************************************************************** +// %%% Commands Unique to Direct Access Devices %%% +//*************************************************************************** +#define SCSI_COMPARE 0x39 // Compare (O) +#define SCSI_FORMAT 0x04 // Format Unit (MANDATORY) +#define SCSI_LCK_UN_CAC 0x36 // Lock Unlock Cache (O) +#define SCSI_PREFETCH 0x34 // Prefetch (O) +#define SCSI_MED_REMOVL 0x1E // Prevent/Allow medium Removal (O) +#define SCSI_READ6 0x08 // Read 6-byte (MANDATORY) +#define SCSI_READ10 0x28 // Read 10-byte (MANDATORY) +#define SCSI_RD_CAPAC 0x25 // Read Capacity (MANDATORY) +#define SCSI_RD_DEFECT 0x37 // Read Defect Data (O) +#define SCSI_READ_LONG 0x3E // Read Long (O) +#define SCSI_REASS_BLK 0x07 // Reassign Blocks (O) +#define SCSI_RCV_DIAG 0x1C // Receive Diagnostic Results (O) +#define SCSI_RELEASE 0x17 // Release Unit (MANDATORY) +#define SCSI_REZERO 0x01 // Rezero Unit (O) +#define SCSI_SRCH_DAT_E 0x31 // Search Data Equal (O) +#define SCSI_SRCH_DAT_H 0x30 // Search Data High (O) +#define SCSI_SRCH_DAT_L 0x32 // Search Data Low (O) +#define SCSI_SEEK6 0x0B // Seek 6-Byte (O) +#define SCSI_SEEK10 0x2B // Seek 10-Byte (O) +#define SCSI_SEND_DIAG 0x1D // Send Diagnostics (MANDATORY) +#define SCSI_SET_LIMIT 0x33 // Set Limits (O) +#define SCSI_START_STP 0x1B // Start/Stop Unit (O) +#define SCSI_SYNC_CACHE 0x35 // Synchronize Cache (O) +#define SCSI_VERIFY 0x2F // Verify (O) +#define SCSI_WRITE6 0x0A // Write 6-Byte (MANDATORY) +#define SCSI_WRITE10 0x2A // Write 10-Byte (MANDATORY) +#define SCSI_WRT_VERIFY 0x2E // Write and Verify (O) +#define SCSI_WRITE_LONG 0x3F // Write Long (O) +#define SCSI_WRITE_SAME 0x41 // Write Same (O) + +//*************************************************************************** +// %%% Commands Unique to Sequential Access Devices %%% +//*************************************************************************** +#define SCSI_ERASE 0x19 // Erase (MANDATORY) +#define SCSI_LOAD_UN 0x1b // Load/Unload (O) +#define SCSI_LOCATE 0x2B // Locate (O) +#define SCSI_RD_BLK_LIM 0x05 // Read Block Limits (MANDATORY) +#define SCSI_READ_POS 0x34 // Read Position (O) +#define SCSI_READ_REV 0x0F // Read Reverse (O) +#define SCSI_REC_BF_DAT 0x14 // Recover Buffer Data (O) +#define SCSI_RESERVE 0x16 // Reserve Unit (MANDATORY) +#define SCSI_REWIND 0x01 // Rewind (MANDATORY) +#define SCSI_SPACE 0x11 // Space (MANDATORY) +#define SCSI_VERIFY_T 0x13 // Verify (Tape) (O) +#define SCSI_WRT_FILE 0x10 // Write Filemarks (MANDATORY) + +//*************************************************************************** +// %%% Commands Unique to Printer Devices %%% +//*************************************************************************** +#define SCSI_PRINT 0x0A // Print (MANDATORY) +#define SCSI_SLEW_PNT 0x0B // Slew and Print (O) +#define SCSI_STOP_PNT 0x1B // Stop Print (O) +#define SCSI_SYNC_BUFF 0x10 // Synchronize Buffer (O) + +//*************************************************************************** +// %%% Commands Unique to Processor Devices %%% +//*************************************************************************** +#define SCSI_RECEIVE 0x08 // Receive (O) +#define SCSI_SEND 0x0A // Send (O) + +//*************************************************************************** +// %%% Commands Unique to Write-Once Devices %%% +//*************************************************************************** +#define SCSI_MEDIUM_SCN 0x38 // Medium Scan (O) +#define SCSI_SRCHDATE10 0x31 // Search Data Equal 10-Byte (O) +#define SCSI_SRCHDATE12 0xB1 // Search Data Equal 12-Byte (O) +#define SCSI_SRCHDATH10 0x30 // Search Data High 10-Byte (O) +#define SCSI_SRCHDATH12 0xB0 // Search Data High 12-Byte (O) +#define SCSI_SRCHDATL10 0x32 // Search Data Low 10-Byte (O) +#define SCSI_SRCHDATL12 0xB2 // Search Data Low 12-Byte (O) +#define SCSI_SET_LIM_10 0x33 // Set Limits 10-Byte (O) +#define SCSI_SET_LIM_12 0xB3 // Set Limits 10-Byte (O) +#define SCSI_VERIFY10 0x2F // Verify 10-Byte (O) +#define SCSI_VERIFY12 0xAF // Verify 12-Byte (O) +#define SCSI_WRITE12 0xAA // Write 12-Byte (O) +#define SCSI_WRT_VER10 0x2E // Write and Verify 10-Byte (O) +#define SCSI_WRT_VER12 0xAE // Write and Verify 12-Byte (O) + +//*************************************************************************** +// %%% Commands Unique to CD-ROM Devices %%% +//*************************************************************************** +#define SCSI_PLAYAUD_10 0x45 // Play Audio 10-Byte (O) +#define SCSI_PLAYAUD_12 0xA5 // Play Audio 12-Byte 12-Byte (O) +#define SCSI_PLAYAUDMSF 0x47 // Play Audio MSF (O) +#define SCSI_PLAYA_TKIN 0x48 // Play Audio Track/Index (O) +#define SCSI_PLYTKREL10 0x49 // Play Track Relative 10-Byte (O) +#define SCSI_PLYTKREL12 0xA9 // Play Track Relative 12-Byte (O) +#define SCSI_READCDCAP 0x25 // Read CD-ROM Capacity (MANDATORY) +#define SCSI_READHEADER 0x44 // Read Header (O) +#define SCSI_SUBCHANNEL 0x42 // Read Subchannel (O) +#define SCSI_READ_TOC 0x43 // Read TOC (O) + +//*************************************************************************** +// %%% Commands Unique to Scanner Devices %%% +//*************************************************************************** +#define SCSI_GETDBSTAT 0x34 // Get Data Buffer Status (O) +#define SCSI_GETWINDOW 0x25 // Get Window (O) +#define SCSI_OBJECTPOS 0x31 // Object Postion (O) +#define SCSI_SCAN 0x1B // Scan (O) +#define SCSI_SETWINDOW 0x24 // Set Window (MANDATORY) + +//*************************************************************************** +// %%% Commands Unique to Optical Memory Devices %%% +//*************************************************************************** +#define SCSI_UpdateBlk 0x3D // Update Block (O) + +//*************************************************************************** +// %%% Commands Unique to Medium Changer Devices %%% +//*************************************************************************** +#define SCSI_EXCHMEDIUM 0xA6 // Exchange Medium (O) +#define SCSI_INITELSTAT 0x07 // Initialize Element Status (O) +#define SCSI_POSTOELEM 0x2B // Position to Element (O) +#define SCSI_REQ_VE_ADD 0xB5 // Request Volume Element Address (O) +#define SCSI_SENDVOLTAG 0xB6 // Send Volume Tag (O) + +//*************************************************************************** +// %%% Commands Unique to Communication Devices %%% +//*************************************************************************** +#define SCSI_GET_MSG_6 0x08 // Get Message 6-Byte (MANDATORY) +#define SCSI_GET_MSG_10 0x28 // Get Message 10-Byte (O) +#define SCSI_GET_MSG_12 0xA8 // Get Message 12-Byte (O) +#define SCSI_SND_MSG_6 0x0A // Send Message 6-Byte (MANDATORY) +#define SCSI_SND_MSG_10 0x2A // Send Message 10-Byte (O) +#define SCSI_SND_MSG_12 0xAA // Send Message 12-Byte (O) + +//*************************************************************************** +// %%% Request Sense Data Format %%% +//*************************************************************************** +typedef struct { + BYTE ErrorCode; // Error Code (70H or 71H) + BYTE SegmentNum; // Number of current segment descriptor + BYTE SenseKey; // Sense Key(See bit definitions too) + BYTE InfoByte0; // Information MSB + BYTE InfoByte1; // Information MID + BYTE InfoByte2; // Information MID + BYTE InfoByte3; // Information LSB + BYTE AddSenLen; // Additional Sense Length + BYTE ComSpecInf0; // Command Specific Information MSB + BYTE ComSpecInf1; // Command Specific Information MID + BYTE ComSpecInf2; // Command Specific Information MID + BYTE ComSpecInf3; // Command Specific Information LSB + BYTE AddSenseCode; // Additional Sense Code + BYTE AddSenQual; // Additional Sense Code Qualifier + BYTE FieldRepUCode; // Field Replaceable Unit Code + BYTE SenKeySpec15; // Sense Key Specific 15th byte + BYTE SenKeySpec16; // Sense Key Specific 16th byte + BYTE SenKeySpec17; // Sense Key Specific 17th byte + BYTE AddSenseBytes; // Additional Sense Bytes +} SENSE_DATA_FMT; + +//*************************************************************************** +// %%% REQUEST SENSE ERROR CODE %%% +//*************************************************************************** +#define SERROR_CURRENT 0x70 // Current Errors +#define SERROR_DEFERED 0x71 // Deferred Errors + +//*************************************************************************** +// %%% REQUEST SENSE BIT DEFINITIONS %%% +//*************************************************************************** +#define SENSE_VALID 0x80 // Byte 0 Bit 7 +#define SENSE_FILEMRK 0x80 // Byte 2 Bit 7 +#define SENSE_EOM 0x40 // Byte 2 Bit 6 +#define SENSE_ILI 0x20 // Byte 2 Bit 5 + +//*************************************************************************** +// %%% REQUEST SENSE SENSE KEY DEFINITIONS %%% +//*************************************************************************** +#define KEY_NOSENSE 0x00 // No Sense +#define KEY_RECERROR 0x01 // Recovered Error +#define KEY_NOTREADY 0x02 // Not Ready +#define KEY_MEDIUMERR 0x03 // Medium Error +#define KEY_HARDERROR 0x04 // Hardware Error +#define KEY_ILLGLREQ 0x05 // Illegal Request +#define KEY_UNITATT 0x06 // Unit Attention +#define KEY_DATAPROT 0x07 // Data Protect +#define KEY_BLANKCHK 0x08 // Blank Check +#define KEY_VENDSPEC 0x09 // Vendor Specific +#define KEY_COPYABORT 0x0A // Copy Abort +#define KEY_EQUAL 0x0C // Equal (Search) +#define KEY_VOLOVRFLW 0x0D // Volume Overflow +#define KEY_MISCOMP 0x0E // Miscompare (Search) +#define KEY_RESERVED 0x0F // Reserved + +//*************************************************************************** +// %%% PERIPHERAL DEVICE TYPE DEFINITIONS %%% +//*************************************************************************** +#define DTYPE_DASD 0x00 // Disk Device +#define DTYPE_SEQD 0x01 // Tape Device +#define DTYPE_PRNT 0x02 // Printer +#define DTYPE_PROC 0x03 // Processor +#define DTYPE_WORM 0x04 // Write-once read-multiple +#define DTYPE_CROM 0x05 // CD-ROM device +#define DTYPE_SCAN 0x06 // Scanner device +#define DTYPE_OPTI 0x07 // Optical memory device +#define DTYPE_JUKE 0x08 // Medium Changer device +#define DTYPE_COMM 0x09 // Communications device +#define DTYPE_RESL 0x0A // Reserved (low) +#define DTYPE_RESH 0x1E // Reserved (high) +#define DTYPE_UNKNOWN 0x1F // Unknown or no device type + +//*************************************************************************** +// %%% ANSI APPROVED VERSION DEFINITIONS %%% +//*************************************************************************** +#define ANSI_MAYBE 0x0 // Device may or may not be ANSI approved stand +#define ANSI_SCSI1 0x1 // Device complies to ANSI X3.131-1986 (SCSI-1) +#define ANSI_SCSI2 0x2 // Device complies to SCSI-2 +#define ANSI_RESLO 0x3 // Reserved (low) +#define ANSI_RESHI 0x7 // Reserved (high) + +typedef struct +{ + USHORT Length; + UCHAR ScsiStatus; + UCHAR PathId; + UCHAR TargetId; + UCHAR Lun; + UCHAR CdbLength; + UCHAR SenseInfoLength; + UCHAR DataIn; + ULONG DataTransferLength; + ULONG TimeOutValue; + ULONG DataBufferOffset; + ULONG SenseInfoOffset; + UCHAR Cdb[16]; +} SCSI_PASS_THROUGH, *PSCSI_PASS_THROUGH; + +typedef struct +{ + USHORT Length; + UCHAR ScsiStatus; + UCHAR PathId; + UCHAR TargetId; + UCHAR Lun; + UCHAR CdbLength; + UCHAR SenseInfoLength; + UCHAR DataIn; + ULONG DataTransferLength; + ULONG TimeOutValue; + PVOID DataBuffer; + ULONG SenseInfoOffset; + UCHAR Cdb[16]; +} SCSI_PASS_THROUGH_DIRECT, *PSCSI_PASS_THROUGH_DIRECT; + +typedef struct +{ + SCSI_PASS_THROUGH_DIRECT spt; + ULONG Filler; + UCHAR ucSenseBuf[32]; +} SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, *PSCSI_PASS_THROUGH_DIRECT_WITH_BUFFER; + +typedef struct +{ + ULONG Length; + UCHAR PortNumber; + UCHAR PathId; + UCHAR TargetId; + UCHAR Lun; +} SCSI_ADDRESS, *PSCSI_ADDRESS; + +#define METHOD_BUFFERED 0 +#define METHOD_IN_DIRECT 1 +#define METHOD_OUT_DIRECT 2 +#define METHOD_NEITHER 3 + +#define FILE_ANY_ACCESS 0 +#ifndef FILE_READ_ACCESS +#define FILE_READ_ACCESS (0x0001) +#endif +#ifndef FILE_WRITE_ACCESS +#define FILE_WRITE_ACCESS (0x0002) +#endif + +#define IOCTL_SCSI_BASE 0x00000004 + +#define SCSI_IOCTL_DATA_OUT 0 +#define SCSI_IOCTL_DATA_IN 1 +#define SCSI_IOCTL_DATA_UNSPECIFIED 2 + +#define CTL_CODE2( DevType, Function, Method, Access ) ( \ + ((DevType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) \ +) + +#define IOCTL_SCSI_PASS_THROUGH CTL_CODE2( IOCTL_SCSI_BASE, 0x0401, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS ) +#define IOCTL_SCSI_GET_CAPABILITIES CTL_CODE2( IOCTL_SCSI_BASE, 0x0404, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_SCSI_PASS_THROUGH_DIRECT CTL_CODE2( IOCTL_SCSI_BASE, 0x0405, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS ) +#define IOCTL_SCSI_GET_ADDRESS CTL_CODE2( IOCTL_SCSI_BASE, 0x0406, METHOD_BUFFERED, FILE_ANY_ACCESS ) + +#define SENSE_LEN 14 +#define SRB_DIR_SCSI 0x00 +#define SRB_POSTING 0x01 +#define SRB_ENABLE_RESIDUAL_COUNT 0x04 +#define SRB_DIR_IN 0x08 +#define SRB_DIR_OUT 0x10 +#define SRB_EVENT_NOTIFY 0x40 +#define RESIDUAL_COUNT_SUPPORTED 0x02 +#define MAX_SRB_TIMEOUT 1080001u +#define DEFAULT_SRB_TIMEOUT 1080001u + +#define SC_HA_INQUIRY 0x00 +#define SC_GET_DEV_TYPE 0x01 +#define SC_EXEC_SCSI_CMD 0x02 +#define SC_ABORT_SRB 0x03 +#define SC_RESET_DEV 0x04 +#define SC_SET_HA_PARMS 0x05 +#define SC_GET_DISK_INFO 0x06 +#define SC_RESCAN_SCSI_BUS 0x07 +#define SC_GETSET_TIMEOUTS 0x08 + +#define SS_PENDING 0x00 +#define SS_COMP 0x01 +#define SS_ABORTED 0x02 +#define SS_ABORT_FAIL 0x03 +#define SS_ERR 0x04 +#define SS_INVALID_CMD 0x80 +#define SS_INVALID_HA 0x81 +#define SS_NO_DEVICE 0x82 +#define SS_INVALID_SRB 0xE0 +#define SS_OLD_MANAGER 0xE1 +#define SS_BUFFER_ALIGN 0xE1 +#define SS_ILLEGAL_MODE 0xE2 +#define SS_NO_ASPI 0xE3 +#define SS_FAILED_INIT 0xE4 +#define SS_ASPI_IS_BUSY 0xE5 +#define SS_BUFFER_TO_BIG 0xE6 +#define SS_BUFFER_TOO_BIG 0xE6 +#define SS_MISMATCHED_COMPONENTS 0xE7 +#define SS_NO_ADAPTERS 0xE8 +#define SS_INSUFFICIENT_RESOURCES 0xE9 +#define SS_ASPI_IS_SHUTDOWN 0xEA +#define SS_BAD_INSTALL 0xEB + +#define HASTAT_OK 0x00 +#define HASTAT_SEL_TO 0x11 +#define HASTAT_DO_DU 0x12 +#define HASTAT_BUS_FREE 0x13 +#define HASTAT_PHASE_ERR 0x14 +#define HASTAT_TIMEOUT 0x09 +#define HASTAT_COMMAND_TIMEOUT 0x0B +#define HASTAT_MESSAGE_REJECT 0x0D +#define HASTAT_BUS_RESET 0x0E +#define HASTAT_PARITY_ERROR 0x0F +#define HASTAT_REQUEST_SENSE_FAILED 0x10 + +#define PACKED +#pragma pack(1) + +typedef struct +{ + BYTE SRB_Cmd; + BYTE SRB_Status; + BYTE SRB_HaID; + BYTE SRB_Flags; + DWORD SRB_Hdr_Rsvd; + BYTE HA_Count; + BYTE HA_SCSI_ID; + BYTE HA_ManagerId[16]; + BYTE HA_Identifier[16]; + BYTE HA_Unique[16]; + WORD HA_Rsvd1; + BYTE pad[20]; +} PACKED SRB_HAInquiry, *PSRB_HAInquiry, FAR *LPSRB_HAInquiry; + +typedef struct +{ + BYTE SRB_Cmd; + BYTE SRB_Status; + BYTE SRB_HaID; + BYTE SRB_Flags; + DWORD SRB_Hdr_Rsvd; + BYTE SRB_Target; + BYTE SRB_Lun; + BYTE SRB_DeviceType; + BYTE SRB_Rsvd1; + BYTE pad[68]; +} PACKED SRB_GDEVBlock, *PSRB_GDEVBlock, FAR *LPSRB_GDEVBlock; + +typedef struct +{ + BYTE SRB_Cmd; + BYTE SRB_Status; + BYTE SRB_HaID; + BYTE SRB_Flags; + DWORD SRB_Hdr_Rsvd; + BYTE SRB_Target; + BYTE SRB_Lun; + WORD SRB_Rsvd1; + DWORD SRB_BufLen; + BYTE FAR *SRB_BufPointer; + BYTE SRB_SenseLen; + BYTE SRB_CDBLen; + BYTE SRB_HaStat; + BYTE SRB_TargStat; + VOID FAR *SRB_PostProc; + BYTE SRB_Rsvd2[20]; + BYTE CDBByte[16]; + BYTE SenseArea[SENSE_LEN+2]; +} PACKED SRB_ExecSCSICmd, *PSRB_ExecSCSICmd, FAR *LPSRB_ExecSCSICmd; + +typedef struct +{ + BYTE SRB_Cmd; + BYTE SRB_Status; + BYTE SRB_HaId; + BYTE SRB_Flags; + DWORD SRB_Hdr_Rsvd; +} PACKED SRB, *PSRB, FAR *LPSRB; + +#pragma pack() + +struct CDDeviceInfo +{ + char vendor[9]; + char productId[17]; + char rev[5]; + char vendorSpec[21]; + + BYTE ha; + BYTE tgt; + BYTE lun; + char scsiDriveLetter; // will be 0 if not using scsi +}; + +class CDReadBuffer +{ +public: + int startFrame; + int numFrames; + int dataStartOffset; + int dataLength; + BYTE* buffer; + int bufferSize; + int index; + bool wantsIndex; + + CDReadBuffer (const int numberOfFrames) + : startFrame (0), + numFrames (0), + dataStartOffset (0), + dataLength (0), + index (0), + wantsIndex (false) + { + bufferSize = 2352 * numberOfFrames; + buffer = (BYTE*) malloc (bufferSize); + } + + ~CDReadBuffer() + { + free (buffer); + } + + bool isZero() const + { + BYTE* p = buffer + dataStartOffset; + + for (int i = dataLength; --i >= 0;) + if (*p++ != 0) + return false; + + return true; + } +}; + +class CDDeviceHandle; + +class CDController +{ +public: + CDController(); + virtual ~CDController(); + + virtual bool read (CDReadBuffer* t) = 0; + virtual void shutDown(); + + bool readAudio (CDReadBuffer* t, CDReadBuffer* overlapBuffer = 0); + int getLastIndex(); + +public: + bool initialised; + + CDDeviceHandle* deviceInfo; + int framesToCheck, framesOverlap; + + void prepare (SRB_ExecSCSICmd& s); + void perform (SRB_ExecSCSICmd& s); + void setPaused (bool paused); +}; + +#pragma pack(1) + +struct TOCTRACK +{ + BYTE rsvd; + BYTE ADR; + BYTE trackNumber; + BYTE rsvd2; + BYTE addr[4]; +}; + +struct TOC +{ + WORD tocLen; + BYTE firstTrack; + BYTE lastTrack; + TOCTRACK tracks[100]; +}; + +#pragma pack() + +enum +{ + READTYPE_ANY = 0, + READTYPE_ATAPI1 = 1, + READTYPE_ATAPI2 = 2, + READTYPE_READ6 = 3, + READTYPE_READ10 = 4, + READTYPE_READ_D8 = 5, + READTYPE_READ_D4 = 6, + READTYPE_READ_D4_1 = 7, + READTYPE_READ10_2 = 8 +}; + +class CDDeviceHandle +{ +public: + CDDeviceHandle (const CDDeviceInfo* const device) + : scsiHandle (0), + readType (READTYPE_ANY), + controller (0) + { + memcpy (&info, device, sizeof (info)); + } + + ~CDDeviceHandle() + { + if (controller != 0) + { + controller->shutDown(); + delete controller; + } + + if (scsiHandle != 0) + CloseHandle (scsiHandle); + } + + bool readTOC (TOC* lpToc, bool useMSF); + bool readAudio (CDReadBuffer* buffer, CDReadBuffer* overlapBuffer = 0); + void openDrawer (bool shouldBeOpen); + + CDDeviceInfo info; + HANDLE scsiHandle; + BYTE readType; + +private: + CDController* controller; + + bool testController (const int readType, + CDController* const newController, + CDReadBuffer* const bufferToUse); +}; + +DWORD (*fGetASPI32SupportInfo)(void); +DWORD (*fSendASPI32Command)(LPSRB); + +static HINSTANCE winAspiLib = 0; +static bool usingScsi = false; +static bool initialised = false; + +static bool InitialiseCDRipper() +{ + if (! initialised) + { + initialised = true; + + OSVERSIONINFO info; + info.dwOSVersionInfoSize = sizeof (info); + GetVersionEx (&info); + + usingScsi = (info.dwPlatformId == VER_PLATFORM_WIN32_NT) && (info.dwMajorVersion > 4); + + if (! usingScsi) + { + fGetASPI32SupportInfo = 0; + fSendASPI32Command = 0; + winAspiLib = LoadLibrary (_T("WNASPI32.DLL")); + + if (winAspiLib != 0) + { + fGetASPI32SupportInfo = (DWORD(*)(void)) GetProcAddress (winAspiLib, "GetASPI32SupportInfo"); + fSendASPI32Command = (DWORD(*)(LPSRB)) GetProcAddress (winAspiLib, "SendASPI32Command"); + + if (fGetASPI32SupportInfo == 0 || fSendASPI32Command == 0) + return false; + } + else + { + usingScsi = true; + } + } + } + + return true; +} + +static void DeinitialiseCDRipper() +{ + if (winAspiLib != 0) + { + fGetASPI32SupportInfo = 0; + fSendASPI32Command = 0; + FreeLibrary (winAspiLib); + winAspiLib = 0; + } + + initialised = false; +} + +static HANDLE CreateSCSIDeviceHandle (char driveLetter) +{ + TCHAR devicePath[8]; + devicePath[0] = '\\'; + devicePath[1] = '\\'; + devicePath[2] = '.'; + devicePath[3] = '\\'; + devicePath[4] = driveLetter; + devicePath[5] = ':'; + devicePath[6] = 0; + + OSVERSIONINFO info; + info.dwOSVersionInfoSize = sizeof (info); + GetVersionEx (&info); + + DWORD flags = GENERIC_READ; + + if ((info.dwPlatformId == VER_PLATFORM_WIN32_NT) && (info.dwMajorVersion > 4)) + flags = GENERIC_READ | GENERIC_WRITE; + + HANDLE h = CreateFile (devicePath, flags, FILE_SHARE_WRITE | FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + + if (h == INVALID_HANDLE_VALUE) + { + flags ^= GENERIC_WRITE; + h = CreateFile (devicePath, flags, FILE_SHARE_WRITE | FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + } + + return h; +} + +static DWORD performScsiPassThroughCommand (const LPSRB_ExecSCSICmd srb, + const char driveLetter, + HANDLE& deviceHandle, + const bool retryOnFailure = true) +{ + SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER s; + zerostruct (s); + + s.spt.Length = sizeof (SCSI_PASS_THROUGH); + s.spt.CdbLength = srb->SRB_CDBLen; + + s.spt.DataIn = (BYTE) ((srb->SRB_Flags & SRB_DIR_IN) + ? SCSI_IOCTL_DATA_IN + : ((srb->SRB_Flags & SRB_DIR_OUT) + ? SCSI_IOCTL_DATA_OUT + : SCSI_IOCTL_DATA_UNSPECIFIED)); + + s.spt.DataTransferLength = srb->SRB_BufLen; + s.spt.TimeOutValue = 5; + s.spt.DataBuffer = srb->SRB_BufPointer; + s.spt.SenseInfoOffset = offsetof (SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf); + + memcpy (s.spt.Cdb, srb->CDBByte, srb->SRB_CDBLen); + + srb->SRB_Status = SS_ERR; + srb->SRB_TargStat = 0x0004; + + DWORD bytesReturned = 0; + + if (DeviceIoControl (deviceHandle, IOCTL_SCSI_PASS_THROUGH_DIRECT, + &s, sizeof (s), + &s, sizeof (s), + &bytesReturned, 0) != 0) + { + srb->SRB_Status = SS_COMP; + } + else if (retryOnFailure) + { + const DWORD error = GetLastError(); + + if ((error == ERROR_MEDIA_CHANGED) || (error == ERROR_INVALID_HANDLE)) + { + if (error != ERROR_INVALID_HANDLE) + CloseHandle (deviceHandle); + + deviceHandle = CreateSCSIDeviceHandle (driveLetter); + + return performScsiPassThroughCommand (srb, driveLetter, deviceHandle, false); + } + } + + return srb->SRB_Status; +} + +// Controller types.. + +class ControllerType1 : public CDController +{ +public: + ControllerType1() {} + ~ControllerType1() {} + + bool read (CDReadBuffer* rb) + { + if (rb->numFrames * 2352 > rb->bufferSize) + return false; + + SRB_ExecSCSICmd s; + prepare (s); + s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY; + s.SRB_BufLen = rb->bufferSize; + s.SRB_BufPointer = rb->buffer; + s.SRB_CDBLen = 12; + s.CDBByte[0] = 0xBE; + s.CDBByte[3] = (BYTE)((rb->startFrame >> 16) & 0xFF); + s.CDBByte[4] = (BYTE)((rb->startFrame >> 8) & 0xFF); + s.CDBByte[5] = (BYTE)(rb->startFrame & 0xFF); + s.CDBByte[8] = (BYTE)(rb->numFrames & 0xFF); + s.CDBByte[9] = (BYTE)((deviceInfo->readType == READTYPE_ATAPI1) ? 0x10 : 0xF0); + perform (s); + + if (s.SRB_Status != SS_COMP) + return false; + + rb->dataLength = rb->numFrames * 2352; + rb->dataStartOffset = 0; + return true; + } +}; + +class ControllerType2 : public CDController +{ +public: + ControllerType2() {} + ~ControllerType2() {} + + void shutDown() + { + if (initialised) + { + BYTE bufPointer[] = { 0, 0, 0, 8, 83, 0, 0, 0, 0, 0, 8, 0 }; + + SRB_ExecSCSICmd s; + prepare (s); + s.SRB_Flags = SRB_EVENT_NOTIFY | SRB_ENABLE_RESIDUAL_COUNT; + s.SRB_BufLen = 0x0C; + s.SRB_BufPointer = bufPointer; + s.SRB_CDBLen = 6; + s.CDBByte[0] = 0x15; + s.CDBByte[4] = 0x0C; + perform (s); + } + } + + bool init() + { + SRB_ExecSCSICmd s; + s.SRB_Status = SS_ERR; + + if (deviceInfo->readType == READTYPE_READ10_2) + { + BYTE bufPointer1[] = { 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 9, 48, 35, 6, 0, 0, 0, 0, 0, 128 }; + BYTE bufPointer2[] = { 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 9, 48, 1, 6, 32, 7, 0, 0, 0, 0 }; + + for (int i = 0; i < 2; ++i) + { + prepare (s); + s.SRB_Flags = SRB_EVENT_NOTIFY; + s.SRB_BufLen = 0x14; + s.SRB_BufPointer = (i == 0) ? bufPointer1 : bufPointer2; + s.SRB_CDBLen = 6; + s.CDBByte[0] = 0x15; + s.CDBByte[1] = 0x10; + s.CDBByte[4] = 0x14; + perform (s); + + if (s.SRB_Status != SS_COMP) + return false; + } + } + else + { + BYTE bufPointer[] = { 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 9, 48 }; + + prepare (s); + s.SRB_Flags = SRB_EVENT_NOTIFY; + s.SRB_BufLen = 0x0C; + s.SRB_BufPointer = bufPointer; + s.SRB_CDBLen = 6; + s.CDBByte[0] = 0x15; + s.CDBByte[4] = 0x0C; + perform (s); + } + + return s.SRB_Status == SS_COMP; + } + + bool read (CDReadBuffer* rb) + { + if (rb->numFrames * 2352 > rb->bufferSize) + return false; + + if (!initialised) + { + initialised = init(); + + if (!initialised) + return false; + } + + SRB_ExecSCSICmd s; + prepare (s); + s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY; + s.SRB_BufLen = rb->bufferSize; + s.SRB_BufPointer = rb->buffer; + s.SRB_CDBLen = 10; + s.CDBByte[0] = 0x28; + s.CDBByte[1] = (BYTE)(deviceInfo->info.lun << 5); + s.CDBByte[3] = (BYTE)((rb->startFrame >> 16) & 0xFF); + s.CDBByte[4] = (BYTE)((rb->startFrame >> 8) & 0xFF); + s.CDBByte[5] = (BYTE)(rb->startFrame & 0xFF); + s.CDBByte[8] = (BYTE)(rb->numFrames & 0xFF); + perform (s); + + if (s.SRB_Status != SS_COMP) + return false; + + rb->dataLength = rb->numFrames * 2352; + rb->dataStartOffset = 0; + + return true; + } +}; + +class ControllerType3 : public CDController +{ +public: + ControllerType3() {} + ~ControllerType3() {} + + bool read (CDReadBuffer* rb) + { + if (rb->numFrames * 2352 > rb->bufferSize) + return false; + + if (!initialised) + { + setPaused (false); + initialised = true; + } + + SRB_ExecSCSICmd s; + prepare (s); + s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY; + s.SRB_BufLen = rb->numFrames * 2352; + s.SRB_BufPointer = rb->buffer; + s.SRB_CDBLen = 12; + s.CDBByte[0] = 0xD8; + s.CDBByte[3] = (BYTE)((rb->startFrame >> 16) & 0xFF); + s.CDBByte[4] = (BYTE)((rb->startFrame >> 8) & 0xFF); + s.CDBByte[5] = (BYTE)(rb->startFrame & 0xFF); + s.CDBByte[9] = (BYTE)(rb->numFrames & 0xFF); + perform (s); + + if (s.SRB_Status != SS_COMP) + return false; + + rb->dataLength = rb->numFrames * 2352; + rb->dataStartOffset = 0; + + return true; + } +}; + +class ControllerType4 : public CDController +{ +public: + ControllerType4() {} + ~ControllerType4() {} + + bool selectD4Mode() + { + BYTE bufPointer[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 48 }; + + SRB_ExecSCSICmd s; + prepare (s); + s.SRB_Flags = SRB_EVENT_NOTIFY; + s.SRB_CDBLen = 6; + s.SRB_BufLen = 12; + s.SRB_BufPointer = bufPointer; + s.CDBByte[0] = 0x15; + s.CDBByte[1] = 0x10; + s.CDBByte[4] = 0x08; + perform (s); + + return s.SRB_Status == SS_COMP; + } + + bool read (CDReadBuffer* rb) + { + if (rb->numFrames * 2352 > rb->bufferSize) + return false; + + if (!initialised) + { + setPaused (true); + + if (deviceInfo->readType == READTYPE_READ_D4_1) + selectD4Mode(); + + initialised = true; + } + + SRB_ExecSCSICmd s; + prepare (s); + s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY; + s.SRB_BufLen = rb->bufferSize; + s.SRB_BufPointer = rb->buffer; + s.SRB_CDBLen = 10; + s.CDBByte[0] = 0xD4; + s.CDBByte[3] = (BYTE)((rb->startFrame >> 16) & 0xFF); + s.CDBByte[4] = (BYTE)((rb->startFrame >> 8) & 0xFF); + s.CDBByte[5] = (BYTE)(rb->startFrame & 0xFF); + s.CDBByte[8] = (BYTE)(rb->numFrames & 0xFF); + perform (s); + + if (s.SRB_Status != SS_COMP) + return false; + + rb->dataLength = rb->numFrames * 2352; + rb->dataStartOffset = 0; + + return true; + } +}; + +CDController::CDController() : initialised (false) +{ +} + +CDController::~CDController() +{ +} + +void CDController::prepare (SRB_ExecSCSICmd& s) +{ + zerostruct (s); + + s.SRB_Cmd = SC_EXEC_SCSI_CMD; + s.SRB_HaID = deviceInfo->info.ha; + s.SRB_Target = deviceInfo->info.tgt; + s.SRB_Lun = deviceInfo->info.lun; + s.SRB_SenseLen = SENSE_LEN; +} + +void CDController::perform (SRB_ExecSCSICmd& s) +{ + HANDLE event = CreateEvent (0, TRUE, FALSE, 0); + s.SRB_PostProc = (void*)event; + + ResetEvent (event); + + DWORD status = (usingScsi) ? performScsiPassThroughCommand ((LPSRB_ExecSCSICmd)&s, + deviceInfo->info.scsiDriveLetter, + deviceInfo->scsiHandle) + : fSendASPI32Command ((LPSRB)&s); + + if (status == SS_PENDING) + WaitForSingleObject (event, 4000); + + CloseHandle (event); +} + +void CDController::setPaused (bool paused) +{ + SRB_ExecSCSICmd s; + prepare (s); + s.SRB_Flags = SRB_EVENT_NOTIFY; + s.SRB_CDBLen = 10; + s.CDBByte[0] = 0x4B; + s.CDBByte[8] = (BYTE) (paused ? 0 : 1); + perform (s); +} + +void CDController::shutDown() +{ +} + +bool CDController::readAudio (CDReadBuffer* rb, CDReadBuffer* overlapBuffer) +{ + if (overlapBuffer != 0) + { + const bool canDoJitter = (overlapBuffer->bufferSize >= 2352 * framesToCheck); + const bool doJitter = canDoJitter && ! overlapBuffer->isZero(); + + if (doJitter + && overlapBuffer->startFrame > 0 + && overlapBuffer->numFrames > 0 + && overlapBuffer->dataLength > 0) + { + const int numFrames = rb->numFrames; + + if (overlapBuffer->startFrame == (rb->startFrame - framesToCheck)) + { + rb->startFrame -= framesOverlap; + + if (framesToCheck < framesOverlap + && numFrames + framesOverlap <= rb->bufferSize / 2352) + rb->numFrames += framesOverlap; + } + else + { + overlapBuffer->dataLength = 0; + overlapBuffer->startFrame = 0; + overlapBuffer->numFrames = 0; + } + } + + if (! read (rb)) + return false; + + if (doJitter) + { + const int checkLen = framesToCheck * 2352; + const int maxToCheck = rb->dataLength - checkLen; + + if (overlapBuffer->dataLength == 0 || overlapBuffer->isZero()) + return true; + + BYTE* const p = overlapBuffer->buffer + overlapBuffer->dataStartOffset; + bool found = false; + + for (int i = 0; i < maxToCheck; ++i) + { + if (!memcmp (p, rb->buffer + i, checkLen)) + { + i += checkLen; + rb->dataStartOffset = i; + rb->dataLength -= i; + rb->startFrame = overlapBuffer->startFrame + framesToCheck; + found = true; + break; + } + } + + rb->numFrames = rb->dataLength / 2352; + rb->dataLength = 2352 * rb->numFrames; + + if (!found) + return false; + } + + if (canDoJitter) + { + memcpy (overlapBuffer->buffer, + rb->buffer + rb->dataStartOffset + 2352 * (rb->numFrames - framesToCheck), + 2352 * framesToCheck); + + overlapBuffer->startFrame = rb->startFrame + rb->numFrames - framesToCheck; + overlapBuffer->numFrames = framesToCheck; + overlapBuffer->dataLength = 2352 * framesToCheck; + overlapBuffer->dataStartOffset = 0; + } + else + { + overlapBuffer->startFrame = 0; + overlapBuffer->numFrames = 0; + overlapBuffer->dataLength = 0; + } + + return true; + } + else + { + return read (rb); + } +} + +int CDController::getLastIndex() +{ + char qdata[100]; + + SRB_ExecSCSICmd s; + prepare (s); + s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY; + s.SRB_BufLen = sizeof (qdata); + s.SRB_BufPointer = (BYTE*)qdata; + s.SRB_CDBLen = 12; + s.CDBByte[0] = 0x42; + s.CDBByte[1] = (BYTE)(deviceInfo->info.lun << 5); + s.CDBByte[2] = 64; + s.CDBByte[3] = 1; // get current position + s.CDBByte[7] = 0; + s.CDBByte[8] = (BYTE)sizeof (qdata); + perform (s); + + if (s.SRB_Status == SS_COMP) + return qdata[7]; + + return 0; +} + +bool CDDeviceHandle::readTOC (TOC* lpToc, bool useMSF) +{ + HANDLE event = CreateEvent (0, TRUE, FALSE, 0); + + SRB_ExecSCSICmd s; + zerostruct (s); + + s.SRB_Cmd = SC_EXEC_SCSI_CMD; + s.SRB_HaID = info.ha; + s.SRB_Target = info.tgt; + s.SRB_Lun = info.lun; + s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY; + s.SRB_BufLen = 0x324; + s.SRB_BufPointer = (BYTE*)lpToc; + s.SRB_SenseLen = 0x0E; + s.SRB_CDBLen = 0x0A; + s.SRB_PostProc = (void*)event; + s.CDBByte[0] = 0x43; + s.CDBByte[1] = (BYTE)(useMSF ? 0x02 : 0x00); + s.CDBByte[7] = 0x03; + s.CDBByte[8] = 0x24; + + ResetEvent (event); + DWORD status = (usingScsi) ? performScsiPassThroughCommand ((LPSRB_ExecSCSICmd)&s, info.scsiDriveLetter, scsiHandle) + : fSendASPI32Command ((LPSRB)&s); + + if (status == SS_PENDING) + WaitForSingleObject (event, 4000); + + CloseHandle (event); + return (s.SRB_Status == SS_COMP); +} + +bool CDDeviceHandle::readAudio (CDReadBuffer* const buffer, + CDReadBuffer* const overlapBuffer) +{ + if (controller == 0) + { + testController (READTYPE_ATAPI2, new ControllerType1(), buffer) + || testController (READTYPE_ATAPI1, new ControllerType1(), buffer) + || testController (READTYPE_READ10_2, new ControllerType2(), buffer) + || testController (READTYPE_READ10, new ControllerType2(), buffer) + || testController (READTYPE_READ_D8, new ControllerType3(), buffer) + || testController (READTYPE_READ_D4, new ControllerType4(), buffer) + || testController (READTYPE_READ_D4_1, new ControllerType4(), buffer); + } + + buffer->index = 0; + + if ((controller != 0) + && controller->readAudio (buffer, overlapBuffer)) + { + if (buffer->wantsIndex) + buffer->index = controller->getLastIndex(); + + return true; + } + + return false; +} + +void CDDeviceHandle::openDrawer (bool shouldBeOpen) +{ + if (shouldBeOpen) + { + if (controller != 0) + { + controller->shutDown(); + delete controller; + controller = 0; + } + + if (scsiHandle != 0) + { + CloseHandle (scsiHandle); + scsiHandle = 0; + } + } + + SRB_ExecSCSICmd s; + zerostruct (s); + + s.SRB_Cmd = SC_EXEC_SCSI_CMD; + s.SRB_HaID = info.ha; + s.SRB_Target = info.tgt; + s.SRB_Lun = info.lun; + s.SRB_SenseLen = SENSE_LEN; + s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY; + s.SRB_BufLen = 0; + s.SRB_BufPointer = 0; + s.SRB_CDBLen = 12; + s.CDBByte[0] = 0x1b; + s.CDBByte[1] = (BYTE)(info.lun << 5); + s.CDBByte[4] = (BYTE)((shouldBeOpen) ? 2 : 3); + + HANDLE event = CreateEvent (0, TRUE, FALSE, 0); + s.SRB_PostProc = (void*)event; + + ResetEvent (event); + + DWORD status = (usingScsi) ? performScsiPassThroughCommand ((LPSRB_ExecSCSICmd)&s, info.scsiDriveLetter, scsiHandle) + : fSendASPI32Command ((LPSRB)&s); + + if (status == SS_PENDING) + WaitForSingleObject (event, 4000); + + CloseHandle (event); +} + +bool CDDeviceHandle::testController (const int type, + CDController* const newController, + CDReadBuffer* const rb) +{ + controller = newController; + readType = (BYTE)type; + + controller->deviceInfo = this; + controller->framesToCheck = 1; + controller->framesOverlap = 3; + + bool passed = false; + + memset (rb->buffer, 0xcd, rb->bufferSize); + + if (controller->read (rb)) + { + passed = true; + int* p = (int*) (rb->buffer + rb->dataStartOffset); + int wrong = 0; + + for (int i = rb->dataLength / 4; --i >= 0;) + { + if (*p++ == (int) 0xcdcdcdcd) + { + if (++wrong == 4) + { + passed = false; + break; + } + } + else + { + wrong = 0; + } + } + } + + if (! passed) + { + controller->shutDown(); + delete controller; + controller = 0; + } + + return passed; +} + +static void GetAspiDeviceInfo (CDDeviceInfo* dev, BYTE ha, BYTE tgt, BYTE lun) +{ + HANDLE event = CreateEvent (0, TRUE, FALSE, 0); + + const int bufSize = 128; + BYTE buffer[bufSize]; + zeromem (buffer, bufSize); + + SRB_ExecSCSICmd s; + zerostruct (s); + + s.SRB_Cmd = SC_EXEC_SCSI_CMD; + s.SRB_HaID = ha; + s.SRB_Target = tgt; + s.SRB_Lun = lun; + s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY; + s.SRB_BufLen = bufSize; + s.SRB_BufPointer = buffer; + s.SRB_SenseLen = SENSE_LEN; + s.SRB_CDBLen = 6; + s.SRB_PostProc = (void*)event; + s.CDBByte[0] = SCSI_INQUIRY; + s.CDBByte[4] = 100; + + ResetEvent (event); + + if (fSendASPI32Command ((LPSRB)&s) == SS_PENDING) + WaitForSingleObject (event, 4000); + + CloseHandle (event); + + if (s.SRB_Status == SS_COMP) + { + memcpy (dev->vendor, &buffer[8], 8); + memcpy (dev->productId, &buffer[16], 16); + memcpy (dev->rev, &buffer[32], 4); + memcpy (dev->vendorSpec, &buffer[36], 20); + } +} + +static int FindCDDevices (CDDeviceInfo* const list, + int maxItems) +{ + int count = 0; + + if (usingScsi) + { + for (char driveLetter = 'b'; driveLetter <= 'z'; ++driveLetter) + { + TCHAR drivePath[8]; + drivePath[0] = driveLetter; + drivePath[1] = ':'; + drivePath[2] = '\\'; + drivePath[3] = 0; + + if (GetDriveType (drivePath) == DRIVE_CDROM) + { + HANDLE h = CreateSCSIDeviceHandle (driveLetter); + + if (h != INVALID_HANDLE_VALUE) + { + BYTE buffer[100], passThroughStruct[1024]; + zeromem (buffer, sizeof (buffer)); + zeromem (passThroughStruct, sizeof (passThroughStruct)); + + PSCSI_PASS_THROUGH_DIRECT_WITH_BUFFER p = (PSCSI_PASS_THROUGH_DIRECT_WITH_BUFFER)passThroughStruct; + + p->spt.Length = sizeof (SCSI_PASS_THROUGH); + p->spt.CdbLength = 6; + p->spt.SenseInfoLength = 24; + p->spt.DataIn = SCSI_IOCTL_DATA_IN; + p->spt.DataTransferLength = 100; + p->spt.TimeOutValue = 2; + p->spt.DataBuffer = buffer; + p->spt.SenseInfoOffset = offsetof (SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf); + p->spt.Cdb[0] = 0x12; + p->spt.Cdb[4] = 100; + + DWORD bytesReturned = 0; + + if (DeviceIoControl (h, IOCTL_SCSI_PASS_THROUGH_DIRECT, + p, sizeof (SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER), + p, sizeof (SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER), + &bytesReturned, 0) != 0) + { + zeromem (&list[count], sizeof (CDDeviceInfo)); + + list[count].scsiDriveLetter = driveLetter; + + memcpy (list[count].vendor, &buffer[8], 8); + memcpy (list[count].productId, &buffer[16], 16); + memcpy (list[count].rev, &buffer[32], 4); + memcpy (list[count].vendorSpec, &buffer[36], 20); + + zeromem (passThroughStruct, sizeof (passThroughStruct)); + PSCSI_ADDRESS scsiAddr = (PSCSI_ADDRESS)passThroughStruct; + + scsiAddr->Length = sizeof (SCSI_ADDRESS); + + if (DeviceIoControl (h, IOCTL_SCSI_GET_ADDRESS, + 0, 0, scsiAddr, sizeof (SCSI_ADDRESS), + &bytesReturned, 0) != 0) + { + list[count].ha = scsiAddr->PortNumber; + list[count].tgt = scsiAddr->TargetId; + list[count].lun = scsiAddr->Lun; + ++count; + } + } + + CloseHandle (h); + } + } + } + } + else + { + const DWORD d = fGetASPI32SupportInfo(); + BYTE status = HIBYTE (LOWORD (d)); + + if (status != SS_COMP || status == SS_NO_ADAPTERS) + return 0; + + const int numAdapters = LOBYTE (LOWORD (d)); + + for (BYTE ha = 0; ha < numAdapters; ++ha) + { + SRB_HAInquiry s; + zerostruct (s); + + s.SRB_Cmd = SC_HA_INQUIRY; + s.SRB_HaID = ha; + fSendASPI32Command ((LPSRB)&s); + + if (s.SRB_Status == SS_COMP) + { + maxItems = (int)s.HA_Unique[3]; + + if (maxItems == 0) + maxItems = 8; + + for (BYTE tgt = 0; tgt < maxItems; ++tgt) + { + for (BYTE lun = 0; lun < 8; ++lun) + { + SRB_GDEVBlock sb; + zerostruct (sb); + + sb.SRB_Cmd = SC_GET_DEV_TYPE; + sb.SRB_HaID = ha; + sb.SRB_Target = tgt; + sb.SRB_Lun = lun; + fSendASPI32Command ((LPSRB) &sb); + + if (sb.SRB_Status == SS_COMP + && sb.SRB_DeviceType == DTYPE_CROM) + { + zeromem (&list[count], sizeof (CDDeviceInfo)); + + list[count].ha = ha; + list[count].tgt = tgt; + list[count].lun = lun; + + GetAspiDeviceInfo (&(list[count]), ha, tgt, lun); + + ++count; + } + } + } + } + } + } + + return count; +} + +static int ripperUsers = 0; +static bool initialisedOk = false; + +class DeinitialiseTimer : private Timer, + private DeletedAtShutdown +{ + DeinitialiseTimer (const DeinitialiseTimer&); + const DeinitialiseTimer& operator= (const DeinitialiseTimer&); + +public: + DeinitialiseTimer() + { + startTimer (4000); + } + + ~DeinitialiseTimer() + { + if (--ripperUsers == 0) + DeinitialiseCDRipper(); + } + + void timerCallback() + { + delete this; + } + + juce_UseDebuggingNewOperator +}; + +static void incUserCount() +{ + if (ripperUsers++ == 0) + initialisedOk = InitialiseCDRipper(); +} + +static void decUserCount() +{ + new DeinitialiseTimer(); +} + +struct CDDeviceWrapper +{ + CDDeviceHandle* cdH; + CDReadBuffer* overlapBuffer; + bool jitter; +}; + +static int getAddressOf (const TOCTRACK* const t) +{ + return (((DWORD)t->addr[0]) << 24) + (((DWORD)t->addr[1]) << 16) + + (((DWORD)t->addr[2]) << 8) + ((DWORD)t->addr[3]); +} + +static int getMSFAddressOf (const TOCTRACK* const t) +{ + return 60 * t->addr[1] + t->addr[2]; +} + +static const int samplesPerFrame = 44100 / 75; +static const int bytesPerFrame = samplesPerFrame * 4; + +const StringArray AudioCDReader::getAvailableCDNames() +{ + StringArray results; + incUserCount(); + + if (initialisedOk) + { + CDDeviceInfo list[8]; + const int num = FindCDDevices (list, 8); + + decUserCount(); + + for (int i = 0; i < num; ++i) + { + String s; + + if (list[i].scsiDriveLetter > 0) + s << String::charToString (list[i].scsiDriveLetter).toUpperCase() << T(": "); + + s << String (list[i].vendor).trim() + << T(" ") << String (list[i].productId).trim() + << T(" ") << String (list[i].rev).trim(); + + results.add (s); + } + } + + return results; +} + +static CDDeviceHandle* openHandle (const CDDeviceInfo* const device) +{ + SRB_GDEVBlock s; + zerostruct (s); + + s.SRB_Cmd = SC_GET_DEV_TYPE; + s.SRB_HaID = device->ha; + s.SRB_Target = device->tgt; + s.SRB_Lun = device->lun; + + if (usingScsi) + { + HANDLE h = CreateSCSIDeviceHandle (device->scsiDriveLetter); + + if (h != INVALID_HANDLE_VALUE) + { + CDDeviceHandle* cdh = new CDDeviceHandle (device); + cdh->scsiHandle = h; + return cdh; + } + } + else + { + if (fSendASPI32Command ((LPSRB)&s) == SS_COMP + && s.SRB_DeviceType == DTYPE_CROM) + { + return new CDDeviceHandle (device); + } + } + + return 0; +} + +AudioCDReader* AudioCDReader::createReaderForCD (const int deviceIndex) +{ + incUserCount(); + + if (initialisedOk) + { + CDDeviceInfo list[8]; + const int num = FindCDDevices (list, 8); + + if (((unsigned int) deviceIndex) < (unsigned int) num) + { + CDDeviceHandle* const handle = openHandle (&(list[deviceIndex])); + + if (handle != 0) + { + CDDeviceWrapper* const d = new CDDeviceWrapper(); + d->cdH = handle; + d->overlapBuffer = new CDReadBuffer(3); + + return new AudioCDReader (d); + } + } + } + + decUserCount(); + return 0; +} + +AudioCDReader::AudioCDReader (void* handle_) + : AudioFormatReader (0, T("CD Audio")), + handle (handle_), + indexingEnabled (false), + lastIndex (0), + firstFrameInBuffer (0), + samplesInBuffer (0) +{ + jassert (handle_ != 0); + + refreshTrackLengths(); + + sampleRate = 44100.0; + bitsPerSample = 16; + lengthInSamples = getPositionOfTrackStart (numTracks); + numChannels = 2; + usesFloatingPointData = false; + + buffer.setSize (4 * bytesPerFrame, true); +} + +AudioCDReader::~AudioCDReader() +{ + CDDeviceWrapper* const device = (CDDeviceWrapper*)handle; + + delete device->cdH; + delete device->overlapBuffer; + delete device; + + decUserCount(); +} + +bool AudioCDReader::read (int** destSamples, + int64 startSampleInFile, + int numSamples) +{ + CDDeviceWrapper* const device = (CDDeviceWrapper*)handle; + + bool ok = true; + int offset = 0; + + if (startSampleInFile < 0) + { + int* l = destSamples[0]; + int* r = destSamples[1]; + + numSamples += (int) startSampleInFile; + offset -= (int) startSampleInFile; + + while (++startSampleInFile <= 0) + { + *l++ = 0; + + if (r != 0) + *r++ = 0; + } + } + + while (numSamples > 0) + { + const int bufferStartSample = firstFrameInBuffer * samplesPerFrame; + const int bufferEndSample = bufferStartSample + samplesInBuffer; + + if (startSampleInFile >= bufferStartSample + && startSampleInFile < bufferEndSample) + { + const int toDo = (int) jmin ((int64) numSamples, bufferEndSample - startSampleInFile); + + int* const l = destSamples[0] + offset; + int* const r = destSamples[1] + offset; + const short* src = (const short*) buffer.getData(); + src += 2 * (startSampleInFile - bufferStartSample); + + for (int i = 0; i < toDo; ++i) + { + l[i] = src [i << 1] << 16; + + if (r != 0) + r[i] = src [(i << 1) + 1] << 16; + } + + offset += toDo; + startSampleInFile += toDo; + numSamples -= toDo; + } + else + { + const int framesInBuffer = buffer.getSize() / bytesPerFrame; + const int frameNeeded = (int) (startSampleInFile / samplesPerFrame); + + if (firstFrameInBuffer + framesInBuffer != frameNeeded) + { + device->overlapBuffer->dataLength = 0; + device->overlapBuffer->startFrame = 0; + device->overlapBuffer->numFrames = 0; + device->jitter = false; + } + + firstFrameInBuffer = frameNeeded; + lastIndex = 0; + + CDReadBuffer readBuffer (framesInBuffer + 4); + readBuffer.wantsIndex = indexingEnabled; + + int i; + for (i = 5; --i >= 0;) + { + readBuffer.startFrame = frameNeeded; + readBuffer.numFrames = framesInBuffer; + + if (device->cdH->readAudio (&readBuffer, (device->jitter) ? device->overlapBuffer : 0)) + break; + else + device->overlapBuffer->dataLength = 0; + } + + if (i >= 0) + { + memcpy ((char*) buffer.getData(), + readBuffer.buffer + readBuffer.dataStartOffset, + readBuffer.dataLength); + + samplesInBuffer = readBuffer.dataLength >> 2; + lastIndex = readBuffer.index; + } + else + { + int* l = destSamples[0] + offset; + int* r = destSamples[1] + offset; + + while (--numSamples >= 0) + { + *l++ = 0; + + if (r != 0) + *r++ = 0; + } + + // sometimes the read fails for just the very last couple of blocks, so + // we'll ignore and errors in the last half-second of the disk.. + ok = startSampleInFile > (trackStarts [numTracks] - 20000); + break; + } + } + } + + return ok; +} + +bool AudioCDReader::isCDStillPresent() const +{ + TOC toc; + zerostruct (toc); + + return ((CDDeviceWrapper*)handle)->cdH->readTOC (&toc, false); +} + +int AudioCDReader::getNumTracks() const +{ + return numTracks; +} + +int AudioCDReader::getPositionOfTrackStart (int trackNum) const +{ + return (trackNum >= 0 && trackNum <= numTracks) ? trackStarts [trackNum] * samplesPerFrame + : 0; +} + +void AudioCDReader::refreshTrackLengths() +{ + zeromem (trackStarts, sizeof (trackStarts)); + zeromem (audioTracks, sizeof (audioTracks)); + + TOC toc; + zerostruct (toc); + + if (((CDDeviceWrapper*)handle)->cdH->readTOC (&toc, false)) + { + numTracks = 1 + toc.lastTrack - toc.firstTrack; + + for (int i = 0; i <= numTracks; ++i) + { + trackStarts[i] = getAddressOf (&toc.tracks[i]); + audioTracks[i] = ((toc.tracks[i].ADR & 4) == 0); + } + } + else + { + numTracks = 0; + } +} + +bool AudioCDReader::isTrackAudio (int trackNum) const +{ + return (trackNum >= 0 && trackNum <= numTracks) ? audioTracks [trackNum] + : false; +} + +void AudioCDReader::enableIndexScanning (bool b) +{ + indexingEnabled = b; +} + +int AudioCDReader::getLastIndex() const +{ + return lastIndex; +} + +const int framesPerIndexRead = 4; + +int AudioCDReader::getIndexAt (int samplePos) +{ + CDDeviceWrapper* const device = (CDDeviceWrapper*) handle; + + const int frameNeeded = samplePos / samplesPerFrame; + + device->overlapBuffer->dataLength = 0; + device->overlapBuffer->startFrame = 0; + device->overlapBuffer->numFrames = 0; + device->jitter = false; + + firstFrameInBuffer = 0; + lastIndex = 0; + + CDReadBuffer readBuffer (4 + framesPerIndexRead); + readBuffer.wantsIndex = true; + + int i; + for (i = 5; --i >= 0;) + { + readBuffer.startFrame = frameNeeded; + readBuffer.numFrames = framesPerIndexRead; + + if (device->cdH->readAudio (&readBuffer, (false) ? device->overlapBuffer : 0)) + break; + } + + if (i >= 0) + return readBuffer.index; + + return -1; +} + +const Array AudioCDReader::findIndexesInTrack (const int trackNumber) +{ + Array indexes; + + const int trackStart = getPositionOfTrackStart (trackNumber); + const int trackEnd = getPositionOfTrackStart (trackNumber + 1); + + bool needToScan = true; + + if (trackEnd - trackStart > 20 * 44100) + { + // check the end of the track for indexes before scanning the whole thing + needToScan = false; + int pos = jmax (trackStart, trackEnd - 44100 * 5); + bool seenAnIndex = false; + + while (pos <= trackEnd - samplesPerFrame) + { + const int index = getIndexAt (pos); + + if (index == 0) + { + // lead-out, so skip back a bit if we've not found any indexes yet.. + if (seenAnIndex) + break; + + pos -= 44100 * 5; + + if (pos < trackStart) + break; + } + else + { + if (index > 0) + seenAnIndex = true; + + if (index > 1) + { + needToScan = true; + break; + } + + pos += samplesPerFrame * framesPerIndexRead; + } + } + } + + if (needToScan) + { + CDDeviceWrapper* const device = (CDDeviceWrapper*) handle; + + int pos = trackStart; + int last = -1; + + while (pos < trackEnd - samplesPerFrame * 10) + { + const int frameNeeded = pos / samplesPerFrame; + + device->overlapBuffer->dataLength = 0; + device->overlapBuffer->startFrame = 0; + device->overlapBuffer->numFrames = 0; + device->jitter = false; + + firstFrameInBuffer = 0; + + CDReadBuffer readBuffer (4); + readBuffer.wantsIndex = true; + + int i; + for (i = 5; --i >= 0;) + { + readBuffer.startFrame = frameNeeded; + readBuffer.numFrames = framesPerIndexRead; + + if (device->cdH->readAudio (&readBuffer, (false) ? device->overlapBuffer : 0)) + break; + } + + if (i < 0) + break; + + if (readBuffer.index > last && readBuffer.index > 1) + { + last = readBuffer.index; + indexes.add (pos); + } + + pos += samplesPerFrame * framesPerIndexRead; + } + + indexes.removeValue (trackStart); + } + + return indexes; +} + +int AudioCDReader::getCDDBId() +{ + refreshTrackLengths(); + + if (numTracks > 0) + { + TOC toc; + zerostruct (toc); + + if (((CDDeviceWrapper*) handle)->cdH->readTOC (&toc, true)) + { + int n = 0; + + for (int i = numTracks; --i >= 0;) + { + int j = getMSFAddressOf (&toc.tracks[i]); + + while (j > 0) + { + n += (j % 10); + j /= 10; + } + } + + if (n != 0) + { + const int t = getMSFAddressOf (&toc.tracks[numTracks]) + - getMSFAddressOf (&toc.tracks[0]); + + return ((n % 0xff) << 24) | (t << 8) | numTracks; + } + } + } + + return 0; +} + +void AudioCDReader::ejectDisk() +{ + ((CDDeviceWrapper*) handle)->cdH->openDrawer (true); +} + +#if JUCE_USE_CDBURNER + +static IDiscRecorder* enumCDBurners (StringArray* list, int indexToOpen, IDiscMaster** master) +{ + CoInitialize (0); + + IDiscMaster* dm; + IDiscRecorder* result = 0; + + if (SUCCEEDED (CoCreateInstance (CLSID_MSDiscMasterObj, 0, + CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, + IID_IDiscMaster, + (void**) &dm))) + { + if (SUCCEEDED (dm->Open())) + { + IEnumDiscRecorders* drEnum = 0; + + if (SUCCEEDED (dm->EnumDiscRecorders (&drEnum))) + { + IDiscRecorder* dr = 0; + DWORD dummy; + int index = 0; + + while (drEnum->Next (1, &dr, &dummy) == S_OK) + { + if (indexToOpen == index) + { + result = dr; + break; + } + else if (list != 0) + { + BSTR path; + + if (SUCCEEDED (dr->GetPath (&path))) + list->add ((const WCHAR*) path); + } + + ++index; + dr->Release(); + } + + drEnum->Release(); + } + + /*if (redbookFormat != 0) + { + IEnumDiscMasterFormats* mfEnum; + + if (SUCCEEDED (dm->EnumDiscMasterFormats (&mfEnum))) + { + IID formatIID; + DWORD dummy; + + while (mfEnum->Next (1, &formatIID, &dummy) == S_OK) + { + } + + mfEnum->Release(); + } + + redbookFormat + }*/ + + if (master == 0) + dm->Close(); + } + + if (master != 0) + *master = dm; + else + dm->Release(); + } + + return result; +} + +const StringArray AudioCDBurner::findAvailableDevices() +{ + StringArray devs; + enumCDBurners (&devs, -1, 0); + return devs; +} + +AudioCDBurner* AudioCDBurner::openDevice (const int deviceIndex) +{ + AudioCDBurner* b = new AudioCDBurner (deviceIndex); + + if (b->internal == 0) + deleteAndZero (b); + + return b; +} + +class CDBurnerInfo : public IDiscMasterProgressEvents +{ +public: + CDBurnerInfo() + : refCount (1), + progress (0), + shouldCancel (false), + listener (0) + { + } + + ~CDBurnerInfo() + { + } + + HRESULT __stdcall QueryInterface (REFIID id, void __RPC_FAR* __RPC_FAR* result) + { + if (result == 0) + return E_POINTER; + + if (id == IID_IUnknown || id == IID_IDiscMasterProgressEvents) + { + AddRef(); + *result = this; + return S_OK; + } + + *result = 0; + return E_NOINTERFACE; + } + + ULONG __stdcall AddRef() { return ++refCount; } + ULONG __stdcall Release() { jassert (refCount > 0); const int r = --refCount; if (r == 0) delete this; return r; } + + HRESULT __stdcall QueryCancel (boolean* pbCancel) + { + if (listener != 0 && ! shouldCancel) + shouldCancel = listener->audioCDBurnProgress (progress); + + *pbCancel = shouldCancel; + + return S_OK; + } + + HRESULT __stdcall NotifyBlockProgress (long nCompleted, long nTotal) + { + progress = nCompleted / (float) nTotal; + shouldCancel = listener != 0 && listener->audioCDBurnProgress (progress); + + return E_NOTIMPL; + } + + HRESULT __stdcall NotifyPnPActivity (void) { return E_NOTIMPL; } + HRESULT __stdcall NotifyAddProgress (long /*nCompletedSteps*/, long /*nTotalSteps*/) { return E_NOTIMPL; } + HRESULT __stdcall NotifyTrackProgress (long /*nCurrentTrack*/, long /*nTotalTracks*/) { return E_NOTIMPL; } + HRESULT __stdcall NotifyPreparingBurn (long /*nEstimatedSeconds*/) { return E_NOTIMPL; } + HRESULT __stdcall NotifyClosingDisc (long /*nEstimatedSeconds*/) { return E_NOTIMPL; } + HRESULT __stdcall NotifyBurnComplete (HRESULT /*status*/) { return E_NOTIMPL; } + HRESULT __stdcall NotifyEraseComplete (HRESULT /*status*/) { return E_NOTIMPL; } + + IDiscMaster* discMaster; + IDiscRecorder* discRecorder; + IRedbookDiscMaster* redbook; + AudioCDBurner::BurnProgressListener* listener; + float progress; + bool shouldCancel; + +private: + int refCount; +}; + +AudioCDBurner::AudioCDBurner (const int deviceIndex) + : internal (0) +{ + IDiscMaster* discMaster; + IDiscRecorder* dr = enumCDBurners (0, deviceIndex, &discMaster); + + if (dr != 0) + { + IRedbookDiscMaster* redbook; + HRESULT hr = discMaster->SetActiveDiscMasterFormat (IID_IRedbookDiscMaster, (void**) &redbook); + + hr = discMaster->SetActiveDiscRecorder (dr); + + CDBurnerInfo* const info = new CDBurnerInfo(); + internal = info; + + info->discMaster = discMaster; + info->discRecorder = dr; + info->redbook = redbook; + } +} + +AudioCDBurner::~AudioCDBurner() +{ + CDBurnerInfo* const info = (CDBurnerInfo*) internal; + + if (info != 0) + { + info->discRecorder->Close(); + info->redbook->Release(); + info->discRecorder->Release(); + info->discMaster->Release(); + + info->Release(); + } +} + +bool AudioCDBurner::isDiskPresent() const +{ + CDBurnerInfo* const info = (CDBurnerInfo*) internal; + + HRESULT hr = info->discRecorder->OpenExclusive(); + + long type, flags; + hr = info->discRecorder->QueryMediaType (&type, &flags); + + info->discRecorder->Close(); + return hr == S_OK && type != 0 && (flags & MEDIA_WRITABLE) != 0; +} + +int AudioCDBurner::getNumAvailableAudioBlocks() const +{ + CDBurnerInfo* const info = (CDBurnerInfo*) internal; + long blocksFree = 0; + info->redbook->GetAvailableAudioTrackBlocks (&blocksFree); + return blocksFree; +} + +const String AudioCDBurner::burn (AudioCDBurner::BurnProgressListener* listener, + const bool ejectDiscAfterwards, + const bool performFakeBurnForTesting) +{ + CDBurnerInfo* const info = (CDBurnerInfo*) internal; + + info->listener = listener; + info->progress = 0; + info->shouldCancel = false; + + UINT_PTR cookie; + HRESULT hr = info->discMaster->ProgressAdvise (info, &cookie); + + hr = info->discMaster->RecordDisc (performFakeBurnForTesting, + ejectDiscAfterwards); + + String error; + if (hr != S_OK) + { + const char* e = "Couldn't open or write to the CD device"; + + if (hr == IMAPI_E_USERABORT) + e = "User cancelled the write operation"; + else if (hr == IMAPI_E_MEDIUM_NOTPRESENT || hr == IMAPI_E_TRACKOPEN) + e = "No Disk present"; + + error = e; + } + + info->discMaster->ProgressUnadvise (cookie); + info->listener = 0; + + return error; +} + +bool AudioCDBurner::addAudioTrack (AudioSource* source, int numSamples) +{ + if (source == 0) + return false; + + CDBurnerInfo* const info = (CDBurnerInfo*) internal; + + long bytesPerBlock; + HRESULT hr = info->redbook->GetAudioBlockSize (&bytesPerBlock); + + const int samplesPerBlock = bytesPerBlock / 4; + bool ok = true; + + hr = info->redbook->CreateAudioTrack ((long) numSamples / (bytesPerBlock * 4)); + + byte* const buffer = (byte*) juce_malloc (bytesPerBlock); + + AudioSampleBuffer sourceBuffer (2, samplesPerBlock); + int samplesDone = 0; + + source->prepareToPlay (samplesPerBlock, 44100.0); + + while (ok) + { + { + AudioSourceChannelInfo info; + info.buffer = &sourceBuffer; + info.numSamples = samplesPerBlock; + info.startSample = 0; + sourceBuffer.clear(); + + source->getNextAudioBlock (info); + } + + zeromem (buffer, bytesPerBlock); + + AudioDataConverters::convertFloatToInt16LE (sourceBuffer.getSampleData (0, 0), + buffer, samplesPerBlock, 4); + + AudioDataConverters::convertFloatToInt16LE (sourceBuffer.getSampleData (1, 0), + buffer + 2, samplesPerBlock, 4); + + hr = info->redbook->AddAudioTrackBlocks (buffer, bytesPerBlock); + + if (hr != S_OK) + ok = false; + + samplesDone += samplesPerBlock; + + if (samplesDone >= numSamples) + break; + } + + juce_free (buffer); + + hr = info->redbook->CloseAudioTrack(); + + delete source; + + return ok && hr == S_OK; +} + +#endif + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_win32_AudioCDReader.cpp *********/ + +/********* Start of inlined file: juce_win32_DirectSound.cpp *********/ + +extern "C" +{ + +// Declare just the minimum number of interfaces for the DSound objects that we need.. +typedef struct typeDSBUFFERDESC +{ + DWORD dwSize; + DWORD dwFlags; + DWORD dwBufferBytes; + DWORD dwReserved; + LPWAVEFORMATEX lpwfxFormat; + GUID guid3DAlgorithm; +} DSBUFFERDESC; + +struct IDirectSoundBuffer; + +#undef INTERFACE +#define INTERFACE IDirectSound +DECLARE_INTERFACE_(IDirectSound, IUnknown) +{ + STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID*) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + STDMETHOD(CreateSoundBuffer) (THIS_ DSBUFFERDESC*, IDirectSoundBuffer**, LPUNKNOWN) PURE; + STDMETHOD(GetCaps) (THIS_ void*) PURE; + STDMETHOD(DuplicateSoundBuffer) (THIS_ IDirectSoundBuffer*, IDirectSoundBuffer**) PURE; + STDMETHOD(SetCooperativeLevel) (THIS_ HWND, DWORD) PURE; + STDMETHOD(Compact) (THIS) PURE; + STDMETHOD(GetSpeakerConfig) (THIS_ LPDWORD) PURE; + STDMETHOD(SetSpeakerConfig) (THIS_ DWORD) PURE; + STDMETHOD(Initialize) (THIS_ const GUID*) PURE; +}; + +#undef INTERFACE +#define INTERFACE IDirectSoundBuffer +DECLARE_INTERFACE_(IDirectSoundBuffer, IUnknown) +{ + STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID*) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + STDMETHOD(GetCaps) (THIS_ void*) PURE; + STDMETHOD(GetCurrentPosition) (THIS_ LPDWORD, LPDWORD) PURE; + STDMETHOD(GetFormat) (THIS_ LPWAVEFORMATEX, DWORD, LPDWORD) PURE; + STDMETHOD(GetVolume) (THIS_ LPLONG) PURE; + STDMETHOD(GetPan) (THIS_ LPLONG) PURE; + STDMETHOD(GetFrequency) (THIS_ LPDWORD) PURE; + STDMETHOD(GetStatus) (THIS_ LPDWORD) PURE; + STDMETHOD(Initialize) (THIS_ IDirectSound*, DSBUFFERDESC*) PURE; + STDMETHOD(Lock) (THIS_ DWORD, DWORD, LPVOID*, LPDWORD, LPVOID*, LPDWORD, DWORD) PURE; + STDMETHOD(Play) (THIS_ DWORD, DWORD, DWORD) PURE; + STDMETHOD(SetCurrentPosition) (THIS_ DWORD) PURE; + STDMETHOD(SetFormat) (THIS_ const WAVEFORMATEX*) PURE; + STDMETHOD(SetVolume) (THIS_ LONG) PURE; + STDMETHOD(SetPan) (THIS_ LONG) PURE; + STDMETHOD(SetFrequency) (THIS_ DWORD) PURE; + STDMETHOD(Stop) (THIS) PURE; + STDMETHOD(Unlock) (THIS_ LPVOID, DWORD, LPVOID, DWORD) PURE; + STDMETHOD(Restore) (THIS) PURE; +}; + +typedef struct typeDSCBUFFERDESC +{ + DWORD dwSize; + DWORD dwFlags; + DWORD dwBufferBytes; + DWORD dwReserved; + LPWAVEFORMATEX lpwfxFormat; +} DSCBUFFERDESC; + +struct IDirectSoundCaptureBuffer; + +#undef INTERFACE +#define INTERFACE IDirectSoundCapture +DECLARE_INTERFACE_(IDirectSoundCapture, IUnknown) +{ + STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID*) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + STDMETHOD(CreateCaptureBuffer) (THIS_ DSCBUFFERDESC*, IDirectSoundCaptureBuffer**, LPUNKNOWN) PURE; + STDMETHOD(GetCaps) (THIS_ void*) PURE; + STDMETHOD(Initialize) (THIS_ const GUID*) PURE; +}; + +#undef INTERFACE +#define INTERFACE IDirectSoundCaptureBuffer +DECLARE_INTERFACE_(IDirectSoundCaptureBuffer, IUnknown) +{ + STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID*) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + STDMETHOD(GetCaps) (THIS_ void*) PURE; + STDMETHOD(GetCurrentPosition) (THIS_ LPDWORD, LPDWORD) PURE; + STDMETHOD(GetFormat) (THIS_ LPWAVEFORMATEX, DWORD, LPDWORD) PURE; + STDMETHOD(GetStatus) (THIS_ LPDWORD) PURE; + STDMETHOD(Initialize) (THIS_ IDirectSoundCapture*, DSCBUFFERDESC*) PURE; + STDMETHOD(Lock) (THIS_ DWORD, DWORD, LPVOID*, LPDWORD, LPVOID*, LPDWORD, DWORD) PURE; + STDMETHOD(Start) (THIS_ DWORD) PURE; + STDMETHOD(Stop) (THIS) PURE; + STDMETHOD(Unlock) (THIS_ LPVOID, DWORD, LPVOID, DWORD) PURE; +}; + +}; + +BEGIN_JUCE_NAMESPACE + +static const String getDSErrorMessage (HRESULT hr) +{ + const char* result = 0; + + switch (hr) + { + case MAKE_HRESULT(1, 0x878, 10): + result = "Device already allocated"; + break; + case MAKE_HRESULT(1, 0x878, 30): + result = "Control unavailable"; + break; + case E_INVALIDARG: + result = "Invalid parameter"; + break; + case MAKE_HRESULT(1, 0x878, 50): + result = "Invalid call"; + break; + case E_FAIL: + result = "Generic error"; + break; + case MAKE_HRESULT(1, 0x878, 70): + result = "Priority level error"; + break; + case E_OUTOFMEMORY: + result = "Out of memory"; + break; + case MAKE_HRESULT(1, 0x878, 100): + result = "Bad format"; + break; + case E_NOTIMPL: + result = "Unsupported function"; + break; + case MAKE_HRESULT(1, 0x878, 120): + result = "No driver"; + break; + case MAKE_HRESULT(1, 0x878, 130): + result = "Already initialised"; + break; + case CLASS_E_NOAGGREGATION: + result = "No aggregation"; + break; + case MAKE_HRESULT(1, 0x878, 150): + result = "Buffer lost"; + break; + case MAKE_HRESULT(1, 0x878, 160): + result = "Another app has priority"; + break; + case MAKE_HRESULT(1, 0x878, 170): + result = "Uninitialised"; + break; + case E_NOINTERFACE: + result = "No interface"; + break; + case S_OK: + result = "No error"; + break; + + default: + return "Unknown error: " + String ((int) hr); + } + + return result; +} + +#define DS_DEBUGGING 1 + +#ifdef DS_DEBUGGING + #define CATCH JUCE_CATCH_EXCEPTION + #undef log + #define log(a) Logger::writeToLog(a); + #undef logError + #define logError(a) logDSError(a, __LINE__); + + static void logDSError (HRESULT hr, int lineNum) + { + if (hr != S_OK) + { + String error ("DS error at line "); + error << lineNum << T(" - ") << getDSErrorMessage (hr); + log (error); + } + } +#else + #define CATCH JUCE_CATCH_ALL + #define log(a) + #define logError(a) +#endif + +#define DSOUND_FUNCTION(functionName, params) \ + typedef HRESULT (WINAPI *type##functionName) params; \ + static type##functionName ds##functionName = 0; + +#define DSOUND_FUNCTION_LOAD(functionName) \ + ds##functionName = (type##functionName) GetProcAddress (h, #functionName); \ + jassert (ds##functionName != 0); + +typedef BOOL (CALLBACK *LPDSENUMCALLBACKW) (LPGUID, LPCWSTR, LPCWSTR, LPVOID); +typedef BOOL (CALLBACK *LPDSENUMCALLBACKA) (LPGUID, LPCSTR, LPCSTR, LPVOID); + +DSOUND_FUNCTION (DirectSoundCreate, (const GUID*, IDirectSound**, LPUNKNOWN)) +DSOUND_FUNCTION (DirectSoundCaptureCreate, (const GUID*, IDirectSoundCapture**, LPUNKNOWN)) +DSOUND_FUNCTION (DirectSoundEnumerateW, (LPDSENUMCALLBACKW, LPVOID)) +DSOUND_FUNCTION (DirectSoundCaptureEnumerateW, (LPDSENUMCALLBACKW, LPVOID)) + +static void initialiseDSoundFunctions() +{ + if (dsDirectSoundCreate == 0) + { + HMODULE h = LoadLibraryA ("dsound.dll"); + + DSOUND_FUNCTION_LOAD (DirectSoundCreate) + DSOUND_FUNCTION_LOAD (DirectSoundCaptureCreate) + DSOUND_FUNCTION_LOAD (DirectSoundEnumerateW) + DSOUND_FUNCTION_LOAD (DirectSoundCaptureEnumerateW) + } +} + +class DSoundInternalOutChannel +{ + String name; + LPGUID guid; + int sampleRate, bufferSizeSamples; + float* leftBuffer; + float* rightBuffer; + + IDirectSound* pDirectSound; + IDirectSoundBuffer* pOutputBuffer; + DWORD writeOffset; + int totalBytesPerBuffer; + int bytesPerBuffer; + unsigned int lastPlayCursor; + +public: + int bitDepth; + bool doneFlag; + + DSoundInternalOutChannel (const String& name_, + LPGUID guid_, + int rate, + int bufferSize, + float* left, + float* right) + : name (name_), + guid (guid_), + sampleRate (rate), + bufferSizeSamples (bufferSize), + leftBuffer (left), + rightBuffer (right), + pDirectSound (0), + pOutputBuffer (0), + bitDepth (16) + { + } + + ~DSoundInternalOutChannel() + { + close(); + } + + void close() + { + HRESULT hr; + + if (pOutputBuffer != 0) + { + JUCE_TRY + { + log (T("closing dsound out: ") + name); + hr = pOutputBuffer->Stop(); + logError (hr); + } + CATCH + + JUCE_TRY + { + hr = pOutputBuffer->Release(); + logError (hr); + } + CATCH + + pOutputBuffer = 0; + } + + if (pDirectSound != 0) + { + JUCE_TRY + { + hr = pDirectSound->Release(); + logError (hr); + } + CATCH + + pDirectSound = 0; + } + } + + const String open() + { + log (T("opening dsound out device: ") + name + + T(" rate=") + String (sampleRate) + + T(" bits=") + String (bitDepth) + + T(" buf=") + String (bufferSizeSamples)); + + pDirectSound = 0; + pOutputBuffer = 0; + writeOffset = 0; + + String error; + HRESULT hr = E_NOINTERFACE; + + if (dsDirectSoundCreate != 0) + hr = dsDirectSoundCreate (guid, &pDirectSound, 0); + + if (hr == S_OK) + { + bytesPerBuffer = (bufferSizeSamples * (bitDepth >> 2)) & ~15; + totalBytesPerBuffer = (3 * bytesPerBuffer) & ~15; + const int numChannels = 2; + + hr = pDirectSound->SetCooperativeLevel (GetDesktopWindow(), 3 /* DSSCL_EXCLUSIVE */); + logError (hr); + + if (hr == S_OK) + { + IDirectSoundBuffer* pPrimaryBuffer; + + DSBUFFERDESC primaryDesc; + zerostruct (primaryDesc); + + primaryDesc.dwSize = sizeof (DSBUFFERDESC); + primaryDesc.dwFlags = 1 /* DSBCAPS_PRIMARYBUFFER */; + primaryDesc.dwBufferBytes = 0; + primaryDesc.lpwfxFormat = 0; + + log ("opening dsound out step 2"); + hr = pDirectSound->CreateSoundBuffer (&primaryDesc, &pPrimaryBuffer, 0); + logError (hr); + + if (hr == S_OK) + { + WAVEFORMATEX wfFormat; + wfFormat.wFormatTag = WAVE_FORMAT_PCM; + wfFormat.nChannels = (unsigned short) numChannels; + wfFormat.nSamplesPerSec = sampleRate; + wfFormat.wBitsPerSample = (unsigned short) bitDepth; + wfFormat.nBlockAlign = (unsigned short) (wfFormat.nChannels * wfFormat.wBitsPerSample / 8); + wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign; + wfFormat.cbSize = 0; + + hr = pPrimaryBuffer->SetFormat (&wfFormat); + logError (hr); + + if (hr == S_OK) + { + DSBUFFERDESC secondaryDesc; + zerostruct (secondaryDesc); + + secondaryDesc.dwSize = sizeof (DSBUFFERDESC); + secondaryDesc.dwFlags = 0x8000 /* DSBCAPS_GLOBALFOCUS */ + | 0x10000 /* DSBCAPS_GETCURRENTPOSITION2 */; + secondaryDesc.dwBufferBytes = totalBytesPerBuffer; + secondaryDesc.lpwfxFormat = &wfFormat; + + hr = pDirectSound->CreateSoundBuffer (&secondaryDesc, &pOutputBuffer, 0); + logError (hr); + + if (hr == S_OK) + { + log ("opening dsound out step 3"); + + DWORD dwDataLen; + unsigned char* pDSBuffData; + + hr = pOutputBuffer->Lock (0, totalBytesPerBuffer, + (LPVOID*) &pDSBuffData, &dwDataLen, 0, 0, 0); + logError (hr); + + if (hr == S_OK) + { + zeromem (pDSBuffData, dwDataLen); + + hr = pOutputBuffer->Unlock (pDSBuffData, dwDataLen, 0, 0); + + if (hr == S_OK) + { + hr = pOutputBuffer->SetCurrentPosition (0); + + if (hr == S_OK) + { + hr = pOutputBuffer->Play (0, 0, 1 /* DSBPLAY_LOOPING */); + + if (hr == S_OK) + return String::empty; + } + } + } + } + } + } + } + } + + error = getDSErrorMessage (hr); + close(); + return error; + } + + void synchronisePosition() + { + if (pOutputBuffer != 0) + { + DWORD playCursor; + pOutputBuffer->GetCurrentPosition (&playCursor, &writeOffset); + } + } + + bool service() + { + if (pOutputBuffer == 0) + return true; + + DWORD playCursor, writeCursor; + HRESULT hr = pOutputBuffer->GetCurrentPosition (&playCursor, &writeCursor); + + if (hr != S_OK) + { + logError (hr); + jassertfalse + return true; + } + + int playWriteGap = writeCursor - playCursor; + if (playWriteGap < 0) + playWriteGap += totalBytesPerBuffer; + + int bytesEmpty = playCursor - writeOffset; + + if (bytesEmpty < 0) + bytesEmpty += totalBytesPerBuffer; + + if (bytesEmpty > (totalBytesPerBuffer - playWriteGap)) + { + writeOffset = writeCursor; + bytesEmpty = totalBytesPerBuffer - playWriteGap; + } + + if (bytesEmpty >= bytesPerBuffer) + { + LPBYTE lpbuf1 = 0; + LPBYTE lpbuf2 = 0; + DWORD dwSize1 = 0; + DWORD dwSize2 = 0; + + HRESULT hr = pOutputBuffer->Lock (writeOffset, + bytesPerBuffer, + (void**) &lpbuf1, &dwSize1, + (void**) &lpbuf2, &dwSize2, 0); + + if (hr == S_OK) + { + if (bitDepth == 16) + { + const float gainL = 32767.0f; + const float gainR = 32767.0f; + + int* dest = (int*)lpbuf1; + const float* left = leftBuffer; + const float* right = rightBuffer; + int samples1 = dwSize1 >> 2; + int samples2 = dwSize2 >> 2; + + if (left == 0) + { + while (--samples1 >= 0) + { + int r = roundFloatToInt (gainR * *right++); + + if (r < -32768) + r = -32768; + else if (r > 32767) + r = 32767; + + *dest++ = (r << 16); + } + + dest = (int*)lpbuf2; + + while (--samples2 >= 0) + { + int r = roundFloatToInt (gainR * *right++); + + if (r < -32768) + r = -32768; + else if (r > 32767) + r = 32767; + + *dest++ = (r << 16); + } + } + else if (right == 0) + { + while (--samples1 >= 0) + { + int l = roundFloatToInt (gainL * *left++); + + if (l < -32768) + l = -32768; + else if (l > 32767) + l = 32767; + + l &= 0xffff; + + *dest++ = l; + } + + dest = (int*)lpbuf2; + + while (--samples2 >= 0) + { + int l = roundFloatToInt (gainL * *left++); + + if (l < -32768) + l = -32768; + else if (l > 32767) + l = 32767; + + l &= 0xffff; + + *dest++ = l; + } + } + else + { + while (--samples1 >= 0) + { + int l = roundFloatToInt (gainL * *left++); + + if (l < -32768) + l = -32768; + else if (l > 32767) + l = 32767; + + l &= 0xffff; + + int r = roundFloatToInt (gainR * *right++); + + if (r < -32768) + r = -32768; + else if (r > 32767) + r = 32767; + + *dest++ = (r << 16) | l; + } + + dest = (int*)lpbuf2; + + while (--samples2 >= 0) + { + int l = roundFloatToInt (gainL * *left++); + + if (l < -32768) + l = -32768; + else if (l > 32767) + l = 32767; + + l &= 0xffff; + + int r = roundFloatToInt (gainR * *right++); + + if (r < -32768) + r = -32768; + else if (r > 32767) + r = 32767; + + *dest++ = (r << 16) | l; + } + } + } + else + { + jassertfalse + } + + writeOffset = (writeOffset + dwSize1 + dwSize2) % totalBytesPerBuffer; + + pOutputBuffer->Unlock (lpbuf1, dwSize1, lpbuf2, dwSize2); + } + else + { + jassertfalse + logError (hr); + } + + bytesEmpty -= bytesPerBuffer; + + return true; + } + else + { + return false; + } + } +}; + +struct DSoundInternalInChannel +{ + String name; + LPGUID guid; + int sampleRate, bufferSizeSamples; + float* leftBuffer; + float* rightBuffer; + + IDirectSound* pDirectSound; + IDirectSoundCapture* pDirectSoundCapture; + IDirectSoundCaptureBuffer* pInputBuffer; + +public: + unsigned int readOffset; + int bytesPerBuffer, totalBytesPerBuffer; + int bitDepth; + bool doneFlag; + + DSoundInternalInChannel (const String& name_, + LPGUID guid_, + int rate, + int bufferSize, + float* left, + float* right) + : name (name_), + guid (guid_), + sampleRate (rate), + bufferSizeSamples (bufferSize), + leftBuffer (left), + rightBuffer (right), + pDirectSound (0), + pDirectSoundCapture (0), + pInputBuffer (0), + bitDepth (16) + { + } + + ~DSoundInternalInChannel() + { + close(); + } + + void close() + { + HRESULT hr; + + if (pInputBuffer != 0) + { + JUCE_TRY + { + log (T("closing dsound in: ") + name); + hr = pInputBuffer->Stop(); + logError (hr); + } + CATCH + + JUCE_TRY + { + hr = pInputBuffer->Release(); + logError (hr); + } + CATCH + + pInputBuffer = 0; + } + + if (pDirectSoundCapture != 0) + { + JUCE_TRY + { + hr = pDirectSoundCapture->Release(); + logError (hr); + } + CATCH + + pDirectSoundCapture = 0; + } + + if (pDirectSound != 0) + { + JUCE_TRY + { + hr = pDirectSound->Release(); + logError (hr); + } + CATCH + + pDirectSound = 0; + } + } + + const String open() + { + log (T("opening dsound in device: ") + name + + T(" rate=") + String (sampleRate) + T(" bits=") + String (bitDepth) + T(" buf=") + String (bufferSizeSamples)); + + pDirectSound = 0; + pDirectSoundCapture = 0; + pInputBuffer = 0; + readOffset = 0; + totalBytesPerBuffer = 0; + + String error; + HRESULT hr = E_NOINTERFACE; + + if (dsDirectSoundCaptureCreate != 0) + hr = dsDirectSoundCaptureCreate (guid, &pDirectSoundCapture, 0); + + logError (hr); + + if (hr == S_OK) + { + const int numChannels = 2; + bytesPerBuffer = (bufferSizeSamples * (bitDepth >> 2)) & ~15; + totalBytesPerBuffer = (3 * bytesPerBuffer) & ~15; + + WAVEFORMATEX wfFormat; + wfFormat.wFormatTag = WAVE_FORMAT_PCM; + wfFormat.nChannels = (unsigned short)numChannels; + wfFormat.nSamplesPerSec = sampleRate; + wfFormat.wBitsPerSample = (unsigned short)bitDepth; + wfFormat.nBlockAlign = (unsigned short)(wfFormat.nChannels * (wfFormat.wBitsPerSample / 8)); + wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign; + wfFormat.cbSize = 0; + + DSCBUFFERDESC captureDesc; + zerostruct (captureDesc); + + captureDesc.dwSize = sizeof (DSCBUFFERDESC); + captureDesc.dwFlags = 0; + captureDesc.dwBufferBytes = totalBytesPerBuffer; + captureDesc.lpwfxFormat = &wfFormat; + + log (T("opening dsound in step 2")); + hr = pDirectSoundCapture->CreateCaptureBuffer (&captureDesc, &pInputBuffer, 0); + + logError (hr); + + if (hr == S_OK) + { + hr = pInputBuffer->Start (1 /* DSCBSTART_LOOPING */); + logError (hr); + + if (hr == S_OK) + return String::empty; + } + } + + error = getDSErrorMessage (hr); + close(); + + return error; + } + + void synchronisePosition() + { + if (pInputBuffer != 0) + { + DWORD capturePos; + pInputBuffer->GetCurrentPosition (&capturePos, (DWORD*)&readOffset); + } + } + + bool service() + { + if (pInputBuffer == 0) + return true; + + DWORD capturePos, readPos; + HRESULT hr = pInputBuffer->GetCurrentPosition (&capturePos, &readPos); + logError (hr); + + if (hr != S_OK) + return true; + + int bytesFilled = readPos - readOffset; + if (bytesFilled < 0) + bytesFilled += totalBytesPerBuffer; + + if (bytesFilled >= bytesPerBuffer) + { + LPBYTE lpbuf1 = 0; + LPBYTE lpbuf2 = 0; + DWORD dwsize1 = 0; + DWORD dwsize2 = 0; + + HRESULT hr = pInputBuffer->Lock (readOffset, + bytesPerBuffer, + (void**) &lpbuf1, &dwsize1, + (void**) &lpbuf2, &dwsize2, 0); + + if (hr == S_OK) + { + if (bitDepth == 16) + { + const float g = 1.0f / 32768.0f; + + float* destL = leftBuffer; + float* destR = rightBuffer; + int samples1 = dwsize1 >> 2; + int samples2 = dwsize2 >> 2; + + const short* src = (const short*)lpbuf1; + + if (destL == 0) + { + while (--samples1 >= 0) + { + ++src; + *destR++ = *src++ * g; + } + + src = (const short*)lpbuf2; + + while (--samples2 >= 0) + { + ++src; + *destR++ = *src++ * g; + } + } + else if (destR == 0) + { + while (--samples1 >= 0) + { + *destL++ = *src++ * g; + ++src; + } + + src = (const short*)lpbuf2; + + while (--samples2 >= 0) + { + *destL++ = *src++ * g; + ++src; + } + } + else + { + while (--samples1 >= 0) + { + *destL++ = *src++ * g; + *destR++ = *src++ * g; + } + + src = (const short*)lpbuf2; + + while (--samples2 >= 0) + { + *destL++ = *src++ * g; + *destR++ = *src++ * g; + } + } + } + else + { + jassertfalse + } + + readOffset = (readOffset + dwsize1 + dwsize2) % totalBytesPerBuffer; + + pInputBuffer->Unlock (lpbuf1, dwsize1, lpbuf2, dwsize2); + } + else + { + logError (hr); + jassertfalse + } + + bytesFilled -= bytesPerBuffer; + + return true; + } + else + { + return false; + } + } +}; + +static int findBestMatchForName (const String& name, const StringArray& names) +{ + int i = names.indexOf (name); + + if (i >= 0) + return i; + + StringArray tokens1; + tokens1.addTokens (name, T(" :-"), 0); + + int bestResult = -1; + int bestNumMatches = 1; + + for (i = 0; i < names.size(); ++i) + { + StringArray tokens2; + tokens2.addTokens (names[i], T(" :-"), 0); + + int matches = 0; + + for (int j = tokens1.size(); --j >= 0;) + if (tokens2.contains (tokens1 [j])) + ++matches; + + if (matches > bestNumMatches) + bestResult = i; + } + + return bestResult; +} + +class DSoundAudioIODevice : public AudioIODevice, + public Thread +{ +public: + DSoundAudioIODevice (const String& deviceName, + const int index, + const int inputIndex_) + : AudioIODevice (deviceName, "DirectSound"), + Thread ("Juce DSound"), + isOpen_ (false), + isStarted (false), + deviceIndex (index), + inputIndex (inputIndex_), + inChans (4), + outChans (4), + numInputBuffers (0), + numOutputBuffers (0), + totalSamplesOut (0), + sampleRate (0.0), + inputBuffers (0), + outputBuffers (0), + callback (0) + { + } + + ~DSoundAudioIODevice() + { + close(); + } + + const StringArray getOutputChannelNames() + { + return outChannels; + } + + const StringArray getInputChannelNames() + { + return inChannels; + } + + int getNumSampleRates() + { + return 4; + } + + double getSampleRate (int index) + { + const double samps[] = { 44100.0, 48000.0, 88200.0, 96000.0 }; + + return samps [jlimit (0, 3, index)]; + } + + int getNumBufferSizesAvailable() + { + return 50; + } + + int getBufferSizeSamples (int index) + { + int n = 64; + for (int i = 0; i < index; ++i) + n += (n < 512) ? 32 + : ((n < 1024) ? 64 + : ((n < 2048) ? 128 : 256)); + + return n; + } + + int getDefaultBufferSize() + { + return 2560; + } + + const String open (const BitArray& inputChannels, + const BitArray& outputChannels, + double sampleRate, + int bufferSizeSamples) + { + BitArray ins, outs; + + if (deviceIndex >= 0) + { + if (outputChannels[0]) + outs.setBit (2 * deviceIndex); + + if (outputChannels[1]) + outs.setBit (2 * deviceIndex + 1); + + if (inputIndex >= 0) + { + if (inputChannels[0]) + ins.setBit (2 * inputIndex); + + if (inputChannels[1]) + ins.setBit (2 * inputIndex + 1); + } + } + else + { + ins = inputChannels; + outs = outputChannels; + } + + lastError = openDevice (ins, outs, sampleRate, bufferSizeSamples); + isOpen_ = lastError.isEmpty(); + + return lastError; + } + + void close() + { + stop(); + + if (isOpen_) + { + closeDevice(); + isOpen_ = false; + } + } + + bool isOpen() + { + return isOpen_ && isThreadRunning(); + } + + int getCurrentBufferSizeSamples() + { + return bufferSizeSamples; + } + + double getCurrentSampleRate() + { + return sampleRate; + } + + int getCurrentBitDepth() + { + int i, bits = 256; + + for (i = inChans.size(); --i >= 0;) + if (inChans[i] != 0) + bits = jmin (bits, inChans[i]->bitDepth); + + for (i = outChans.size(); --i >= 0;) + if (outChans[i] != 0) + bits = jmin (bits, outChans[i]->bitDepth); + + if (bits > 32) + bits = 16; + + return bits; + } + + const BitArray getActiveOutputChannels() const + { + return enabledOutputs; + } + + const BitArray getActiveInputChannels() const + { + return enabledInputs; + } + + int getOutputLatencyInSamples() + { + return (int) (getCurrentBufferSizeSamples() * 1.5); + } + + int getInputLatencyInSamples() + { + return getOutputLatencyInSamples(); + } + + void start (AudioIODeviceCallback* call) + { + if (isOpen_ && call != 0 && ! isStarted) + { + if (! isThreadRunning()) + { + // something gone wrong and the thread's stopped.. + isOpen_ = false; + return; + } + + call->audioDeviceAboutToStart (this); + + const ScopedLock sl (startStopLock); + callback = call; + isStarted = true; + } + } + + void stop() + { + if (isStarted) + { + AudioIODeviceCallback* const callbackLocal = callback; + + { + const ScopedLock sl (startStopLock); + isStarted = false; + } + + if (callbackLocal != 0) + callbackLocal->audioDeviceStopped(); + } + } + + bool isPlaying() + { + return isStarted && isOpen_ && isThreadRunning(); + } + + const String getLastError() + { + return lastError; + } + + juce_UseDebuggingNewOperator + + StringArray inChannels, outChannels; + +private: + bool isOpen_; + bool isStarted; + String lastError; + + int deviceIndex, inputIndex; + OwnedArray inChans; + OwnedArray outChans; + WaitableEvent startEvent; + + int numInputBuffers, numOutputBuffers, bufferSizeSamples; + int volatile totalSamplesOut; + int64 volatile lastBlockTime; + double sampleRate; + BitArray enabledInputs, enabledOutputs; + float** inputBuffers; + float** outputBuffers; + + AudioIODeviceCallback* callback; + CriticalSection startStopLock; + + DSoundAudioIODevice (const DSoundAudioIODevice&); + const DSoundAudioIODevice& operator= (const DSoundAudioIODevice&); + + const String openDevice (const BitArray& inputChannels, + const BitArray& outputChannels, + double sampleRate_, + int bufferSizeSamples_); + + void closeDevice() + { + isStarted = false; + stopThread (5000); + + inChans.clear(); + outChans.clear(); + + int i; + for (i = 0; i < numInputBuffers; ++i) + juce_free (inputBuffers[i]); + + delete[] inputBuffers; + inputBuffers = 0; + numInputBuffers = 0; + + for (i = 0; i < numOutputBuffers; ++i) + juce_free (outputBuffers[i]); + + delete[] outputBuffers; + outputBuffers = 0; + numOutputBuffers = 0; + } + + void resync() + { + int i; + for (i = outChans.size(); --i >= 0;) + { + DSoundInternalOutChannel* const out = outChans.getUnchecked(i); + if (out != 0) + out->close(); + } + + for (i = inChans.size(); --i >= 0;) + { + DSoundInternalInChannel* const in = inChans.getUnchecked(i); + if (in != 0) + in->close(); + } + + if (threadShouldExit()) + return; + + // boost our priority while opening the devices to try to get better sync between them + const int oldThreadPri = GetThreadPriority (GetCurrentThread()); + const int oldProcPri = GetPriorityClass (GetCurrentProcess()); + SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); + SetPriorityClass (GetCurrentProcess(), REALTIME_PRIORITY_CLASS); + + for (i = outChans.size(); --i >= 0;) + { + DSoundInternalOutChannel* const out = outChans.getUnchecked(i); + if (out != 0) + out->open(); + } + + for (i = inChans.size(); --i >= 0;) + { + DSoundInternalInChannel* const in = inChans.getUnchecked(i); + if (in != 0) + in->open(); + } + + if (! threadShouldExit()) + { + sleep (5); + + for (i = 0; i < numOutputBuffers; ++i) + if (outChans[i] != 0) + outChans[i]->synchronisePosition(); + + for (i = 0; i < numInputBuffers; ++i) + if (inChans[i] != 0) + inChans[i]->synchronisePosition(); + } + + SetThreadPriority (GetCurrentThread(), oldThreadPri); + SetPriorityClass (GetCurrentProcess(), oldProcPri); + } + +public: + void run() + { + while (! threadShouldExit()) + { + if (wait (100)) + break; + } + + const int latencyMs = (int) (bufferSizeSamples * 1000.0 / sampleRate); + const int maxTimeMS = jmax (5, 3 * latencyMs); + + while (! threadShouldExit()) + { + int numToDo = 0; + uint32 startTime = Time::getMillisecondCounter(); + + int i; + for (i = inChans.size(); --i >= 0;) + { + DSoundInternalInChannel* const in = inChans.getUnchecked(i); + + if (in != 0) + { + in->doneFlag = false; + ++numToDo; + } + } + + for (i = outChans.size(); --i >= 0;) + { + DSoundInternalOutChannel* const out = outChans.getUnchecked(i); + + if (out != 0) + { + out->doneFlag = false; + ++numToDo; + } + } + + if (numToDo > 0) + { + const int maxCount = 3; + int count = maxCount; + + for (;;) + { + for (i = inChans.size(); --i >= 0;) + { + DSoundInternalInChannel* const in = inChans.getUnchecked(i); + + if (in != 0 && !in->doneFlag) + { + if (in->service()) + { + in->doneFlag = true; + --numToDo; + } + } + } + + for (i = outChans.size(); --i >= 0;) + { + DSoundInternalOutChannel* const out = outChans.getUnchecked(i); + + if (out != 0 && !out->doneFlag) + { + if (out->service()) + { + out->doneFlag = true; + --numToDo; + } + } + } + + if (numToDo <= 0) + break; + + if (Time::getMillisecondCounter() > startTime + maxTimeMS) + { + resync(); + break; + } + + if (--count <= 0) + { + Sleep (1); + count = maxCount; + } + + if (threadShouldExit()) + return; + } + } + else + { + sleep (1); + } + + const ScopedLock sl (startStopLock); + + if (isStarted) + { + JUCE_TRY + { + callback->audioDeviceIOCallback ((const float**) inputBuffers, + numInputBuffers, + outputBuffers, + numOutputBuffers, + bufferSizeSamples); + } + JUCE_CATCH_EXCEPTION + + totalSamplesOut += bufferSizeSamples; + } + else + { + for (i = 0; i < numOutputBuffers; ++i) + if (outputBuffers[i] != 0) + zeromem (outputBuffers[i], bufferSizeSamples * sizeof (float)); + + totalSamplesOut = 0; + sleep (1); + } + } + } +}; + +class DSoundAudioIODeviceType : public AudioIODeviceType +{ +public: + DSoundAudioIODeviceType() + : AudioIODeviceType (T("DirectSound")), + hasScanned (false) + { + initialiseDSoundFunctions(); + } + + ~DSoundAudioIODeviceType() + { + } + + void scanForDevices() + { + hasScanned = true; + + outputDeviceNames.clear(); + outputGuids.clear(); + inputDeviceNames.clear(); + inputGuids.clear(); + + if (dsDirectSoundEnumerateW != 0) + { + dsDirectSoundEnumerateW (outputEnumProcW, this); + dsDirectSoundCaptureEnumerateW (inputEnumProcW, this); + } + } + + const StringArray getDeviceNames (const bool preferInputNames) const + { + jassert (hasScanned); // need to call scanForDevices() before doing this + + return preferInputNames ? inputDeviceNames + : outputDeviceNames; + } + + const String getDefaultDeviceName (const bool preferInputNames, + const int /*numInputChannelsNeeded*/, + const int /*numOutputChannelsNeeded*/) const + { + jassert (hasScanned); // need to call scanForDevices() before doing this + + return getDeviceNames (preferInputNames) [0]; + } + + AudioIODevice* createDevice (const String& deviceName) + { + jassert (hasScanned); // need to call scanForDevices() before doing this + + if (deviceName.isEmpty() || deviceName.equalsIgnoreCase (T("DirectSound"))) + { + DSoundAudioIODevice* device = new DSoundAudioIODevice (deviceName, -1, -1); + + int i; + for (i = 0; i < outputDeviceNames.size(); ++i) + { + device->outChannels.add (outputDeviceNames[i] + TRANS(" (left)")); + device->outChannels.add (outputDeviceNames[i] + TRANS(" (right)")); + } + + for (i = 0; i < inputDeviceNames.size(); ++i) + { + device->inChannels.add (inputDeviceNames[i] + TRANS(" (left)")); + device->inChannels.add (inputDeviceNames[i] + TRANS(" (right)")); + } + + return device; + } + else if (outputDeviceNames.contains (deviceName) + || inputDeviceNames.contains (deviceName)) + { + int outputIndex = outputDeviceNames.indexOf (deviceName); + int inputIndex = findBestMatchForName (deviceName, inputDeviceNames); + + if (outputIndex < 0) + { + // using an input device name instead.. + inputIndex = inputDeviceNames.indexOf (deviceName); + outputIndex = jmax (0, findBestMatchForName (deviceName, outputDeviceNames)); + } + + DSoundAudioIODevice* device = new DSoundAudioIODevice (deviceName, outputIndex, inputIndex); + + device->outChannels.add (TRANS("Left")); + device->outChannels.add (TRANS("Right")); + + if (inputIndex >= 0) + { + device->inChannels.add (TRANS("Left")); + device->inChannels.add (TRANS("Right")); + } + + return device; + } + + return 0; + } + + juce_UseDebuggingNewOperator + + StringArray outputDeviceNames; + OwnedArray outputGuids; + + StringArray inputDeviceNames; + OwnedArray inputGuids; + +private: + bool hasScanned; + + BOOL outputEnumProc (LPGUID lpGUID, String desc) + { + desc = desc.trim(); + + if (desc.isNotEmpty()) + { + const String origDesc (desc); + + int n = 2; + while (outputDeviceNames.contains (desc)) + desc = origDesc + T(" (") + String (n++) + T(")"); + + outputDeviceNames.add (desc); + + if (lpGUID != 0) + outputGuids.add (new GUID (*lpGUID)); + else + outputGuids.add (0); + } + + return TRUE; + } + + static BOOL CALLBACK outputEnumProcW (LPGUID lpGUID, LPCWSTR description, LPCWSTR, LPVOID object) + { + return ((DSoundAudioIODeviceType*) object) + ->outputEnumProc (lpGUID, String (description)); + } + + static BOOL CALLBACK outputEnumProcA (LPGUID lpGUID, LPCTSTR description, LPCTSTR, LPVOID object) + { + return ((DSoundAudioIODeviceType*) object) + ->outputEnumProc (lpGUID, String (description)); + } + + BOOL CALLBACK inputEnumProc (LPGUID lpGUID, String desc) + { + desc = desc.trim(); + + if (desc.isNotEmpty()) + { + const String origDesc (desc); + + int n = 2; + while (inputDeviceNames.contains (desc)) + desc = origDesc + T(" (") + String (n++) + T(")"); + + inputDeviceNames.add (desc); + + if (lpGUID != 0) + inputGuids.add (new GUID (*lpGUID)); + else + inputGuids.add (0); + } + + return TRUE; + } + + static BOOL CALLBACK inputEnumProcW (LPGUID lpGUID, LPCWSTR description, LPCWSTR, LPVOID object) + { + return ((DSoundAudioIODeviceType*) object) + ->inputEnumProc (lpGUID, String (description)); + } + + static BOOL CALLBACK inputEnumProcA (LPGUID lpGUID, LPCTSTR description, LPCTSTR, LPVOID object) + { + return ((DSoundAudioIODeviceType*) object) + ->inputEnumProc (lpGUID, String (description)); + } + + DSoundAudioIODeviceType (const DSoundAudioIODeviceType&); + const DSoundAudioIODeviceType& operator= (const DSoundAudioIODeviceType&); +}; + +AudioIODeviceType* juce_createDefaultAudioIODeviceType() +{ + return new DSoundAudioIODeviceType(); +} + +const String DSoundAudioIODevice::openDevice (const BitArray& inputChannels, + const BitArray& outputChannels, + double sampleRate_, + int bufferSizeSamples_) +{ + closeDevice(); + totalSamplesOut = 0; + enabledInputs.clear(); + enabledOutputs.clear(); + + sampleRate = sampleRate_; + + if (bufferSizeSamples_ <= 0) + bufferSizeSamples_ = 960; // use as a default size if none is set. + + bufferSizeSamples = bufferSizeSamples_ & ~7; + + DSoundAudioIODeviceType dlh; + dlh.scanForDevices(); + + numInputBuffers = 2 * dlh.inputDeviceNames.size(); + inputBuffers = new float* [numInputBuffers + 2]; + + numOutputBuffers = 2 * dlh.outputDeviceNames.size(); + outputBuffers = new float* [numOutputBuffers + 2]; + + int i; + for (i = 0; i < numInputBuffers + 2; ++i) + { + if (inputChannels[i] && i < numInputBuffers) + { + inputBuffers[i] = (float*) juce_calloc ((bufferSizeSamples + 16) * sizeof (float)); + enabledInputs.setBit (i); + } + else + { + inputBuffers[i] = 0; + } + } + + for (i = 0; i < numOutputBuffers + 2; ++i) + { + if (outputChannels[i] && i < numOutputBuffers) + { + outputBuffers[i] = (float*) juce_calloc ((bufferSizeSamples + 16) * sizeof (float)); + enabledOutputs.setBit (i); + } + else + { + outputBuffers[i] = 0; + } + } + + for (i = 0; i < numInputBuffers; ++i) + { + if (inputChannels[i] || inputChannels[i + 1]) + { + inChans.add (new DSoundInternalInChannel (dlh.inputDeviceNames [i / 2], + dlh.inputGuids [i / 2], + (int) sampleRate, bufferSizeSamples, + inputBuffers[i], inputBuffers[i + 1])); + } + else + { + inChans.add (0); + } + + ++i; + } + + for (i = 0; i < numOutputBuffers; ++i) + { + if (outputChannels[i] || outputChannels[i + 1]) + { + outChans.add (new DSoundInternalOutChannel (dlh.outputDeviceNames[i / 2], + dlh.outputGuids [i / 2], + (int) sampleRate, bufferSizeSamples, + outputBuffers[i], outputBuffers[i + 1])); + } + else + { + outChans.add (0); + } + + ++i; + } + + String error; + + // boost our priority while opening the devices to try to get better sync between them + const int oldThreadPri = GetThreadPriority (GetCurrentThread()); + const int oldProcPri = GetPriorityClass (GetCurrentProcess()); + SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); + SetPriorityClass (GetCurrentProcess(), REALTIME_PRIORITY_CLASS); + + for (i = 0; i < numOutputBuffers; ++i) + { + if (outChans[i] != 0) + { + error = outChans[i]->open(); + + if (error.isNotEmpty()) + { + error = T("Error opening ") + dlh.outputDeviceNames[i] + + T(": \"") + error + T("\""); + break; + } + } + } + + if (error.isEmpty()) + { + for (i = 0; i < numInputBuffers; ++i) + { + if (inChans[i] != 0) + { + error = inChans[i]->open(); + + if (error.isNotEmpty()) + { + error = T("Error opening ") + dlh.inputDeviceNames[i] + + T(": \"") + error + T("\""); + break; + } + } + } + } + + if (error.isEmpty()) + { + totalSamplesOut = 0; + + for (i = 0; i < numOutputBuffers; ++i) + if (outChans[i] != 0) + outChans[i]->synchronisePosition(); + + for (i = 0; i < numInputBuffers; ++i) + if (inChans[i] != 0) + inChans[i]->synchronisePosition(); + + startThread (9); + sleep (10); + + notify(); + } + else + { + log (error); + } + + SetThreadPriority (GetCurrentThread(), oldThreadPri); + SetPriorityClass (GetCurrentProcess(), oldProcPri); + + return error; +} + +#undef log + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_win32_DirectSound.cpp *********/ + +/********* Start of inlined file: juce_win32_FileChooser.cpp *********/ +#ifdef _MSC_VER + #pragma warning (disable: 4514) + #pragma warning (push) +#endif + +#include + +BEGIN_JUCE_NAMESPACE + +#ifdef _MSC_VER + #pragma warning (pop) +#endif + +static const void* defaultDirPath = 0; +static String returnedString; // need this to get non-existent pathnames from the directory chooser +static Component* currentExtraFileWin = 0; + +static bool areThereAnyAlwaysOnTopWindows() +{ + for (int i = Desktop::getInstance().getNumComponents(); --i >= 0;) + { + Component* c = Desktop::getInstance().getComponent (i); + + if (c != 0 && c->isAlwaysOnTop() && c->isShowing()) + return true; + } + + return false; +} + +static int CALLBACK browseCallbackProc (HWND hWnd, UINT msg, LPARAM lParam, LPARAM /*lpData*/) +{ + if (msg == BFFM_INITIALIZED) + { + SendMessage (hWnd, BFFM_SETSELECTIONW, TRUE, (LPARAM) defaultDirPath); + } + else if (msg == BFFM_VALIDATEFAILEDW) + { + returnedString = (LPCWSTR) lParam; + } + else if (msg == BFFM_VALIDATEFAILEDA) + { + returnedString = (const char*) lParam; + } + + return 0; +} + +void juce_setWindowStyleBit (HWND h, int styleType, int feature, bool bitIsSet); + +static UINT_PTR CALLBACK openCallback (HWND hdlg, UINT uiMsg, WPARAM /*wParam*/, LPARAM lParam) +{ + if (currentExtraFileWin != 0) + { + if (uiMsg == WM_INITDIALOG) + { + HWND dialogH = GetParent (hdlg); + jassert (dialogH != 0); + if (dialogH == 0) + dialogH = hdlg; + + RECT r, cr; + GetWindowRect (dialogH, &r); + GetClientRect (dialogH, &cr); + + SetWindowPos (dialogH, 0, + r.left, r.top, + currentExtraFileWin->getWidth() + jmax (150, r.right - r.left), + jmax (150, r.bottom - r.top), + SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER); + + currentExtraFileWin->setBounds (cr.right, cr.top, currentExtraFileWin->getWidth(), cr.bottom - cr.top); + currentExtraFileWin->getChildComponent(0)->setBounds (0, 0, currentExtraFileWin->getWidth(), currentExtraFileWin->getHeight()); + + SetParent ((HWND) currentExtraFileWin->getWindowHandle(), (HWND) dialogH); + juce_setWindowStyleBit ((HWND)currentExtraFileWin->getWindowHandle(), GWL_STYLE, WS_CHILD, (dialogH != 0)); + juce_setWindowStyleBit ((HWND)currentExtraFileWin->getWindowHandle(), GWL_STYLE, WS_POPUP, (dialogH == 0)); + } + else if (uiMsg == WM_NOTIFY) + { + LPOFNOTIFY ofn = (LPOFNOTIFY) lParam; + + if (ofn->hdr.code == CDN_SELCHANGE) + { + FilePreviewComponent* comp = (FilePreviewComponent*) currentExtraFileWin->getChildComponent(0); + + if (comp != 0) + { + TCHAR path [MAX_PATH * 2]; + path[0] = 0; + CommDlg_OpenSave_GetFilePath (GetParent (hdlg), (LPARAM) &path, MAX_PATH); + + const String fn ((const WCHAR*) path); + + comp->selectedFileChanged (File (fn)); + } + } + } + } + + return 0; +} + +class FPComponentHolder : public Component +{ +public: + FPComponentHolder() + { + setVisible (true); + setOpaque (true); + } + + ~FPComponentHolder() + { + } + + void paint (Graphics& g) + { + g.fillAll (Colours::lightgrey); + } + +private: + FPComponentHolder (const FPComponentHolder&); + const FPComponentHolder& operator= (const FPComponentHolder&); +}; + +void FileChooser::showPlatformDialog (OwnedArray& results, + const String& title, + const File& currentFileOrDirectory, + const String& filter, + bool selectsDirectory, + bool isSaveDialogue, + bool warnAboutOverwritingExistingFiles, + bool selectMultipleFiles, + FilePreviewComponent* extraInfoComponent) +{ + const int numCharsAvailable = 32768; + MemoryBlock filenameSpace ((numCharsAvailable + 1) * sizeof (WCHAR), true); + WCHAR* const fname = (WCHAR*) filenameSpace.getData(); + int fnameIdx = 0; + + JUCE_TRY + { + // use a modal window as the parent for this dialog box + // to block input from other app windows + const Rectangle mainMon (Desktop::getInstance().getMainMonitorArea()); + + Component w (String::empty); + w.setBounds (mainMon.getX() + mainMon.getWidth() / 4, + mainMon.getY() + mainMon.getHeight() / 4, + 0, 0); + w.setOpaque (true); + w.setAlwaysOnTop (areThereAnyAlwaysOnTopWindows()); + w.addToDesktop (0); + + if (extraInfoComponent == 0) + w.enterModalState(); + + String initialDir; + + if (currentFileOrDirectory.isDirectory()) + { + initialDir = currentFileOrDirectory.getFullPathName(); + } + else + { + currentFileOrDirectory.getFileName().copyToBuffer (fname, numCharsAvailable); + + initialDir = currentFileOrDirectory.getParentDirectory().getFullPathName(); + } + + if (currentExtraFileWin->isValidComponent()) + { + jassertfalse + return; + } + + if (selectsDirectory) + { + LPITEMIDLIST list = 0; + filenameSpace.fillWith (0); + + { + BROWSEINFO bi; + zerostruct (bi); + + bi.hwndOwner = (HWND) w.getWindowHandle(); + bi.pszDisplayName = fname; + bi.lpszTitle = title; + bi.lpfn = browseCallbackProc; +#ifdef BIF_USENEWUI + bi.ulFlags = BIF_USENEWUI | BIF_VALIDATE; +#else + bi.ulFlags = 0x50; +#endif + defaultDirPath = (const WCHAR*) initialDir; + + list = SHBrowseForFolder (&bi); + + if (! SHGetPathFromIDListW (list, fname)) + { + fname[0] = 0; + returnedString = String::empty; + } + } + + LPMALLOC al; + if (list != 0 && SUCCEEDED (SHGetMalloc (&al))) + al->Free (list); + + defaultDirPath = 0; + + if (returnedString.isNotEmpty()) + { + const String stringFName (fname); + + results.add (new File (File (stringFName).getSiblingFile (returnedString))); + returnedString = String::empty; + + return; + } + } + else + { + DWORD flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_NOCHANGEDIR | OFN_HIDEREADONLY; + + if (warnAboutOverwritingExistingFiles) + flags |= OFN_OVERWRITEPROMPT; + + if (selectMultipleFiles) + flags |= OFN_ALLOWMULTISELECT; + + if (extraInfoComponent != 0) + { + flags |= OFN_ENABLEHOOK; + + currentExtraFileWin = new FPComponentHolder(); + currentExtraFileWin->addAndMakeVisible (extraInfoComponent); + currentExtraFileWin->setSize (jlimit (20, 800, extraInfoComponent->getWidth()), + extraInfoComponent->getHeight()); + currentExtraFileWin->addToDesktop (0); + + currentExtraFileWin->enterModalState(); + } + + { + WCHAR filters [1024]; + zeromem (filters, sizeof (filters)); + filter.copyToBuffer (filters, 1024); + filter.copyToBuffer (filters + filter.length() + 1, + 1022 - filter.length()); + + OPENFILENAMEW of; + zerostruct (of); + +#ifdef OPENFILENAME_SIZE_VERSION_400W + of.lStructSize = OPENFILENAME_SIZE_VERSION_400W; +#else + of.lStructSize = sizeof (of); +#endif + of.hwndOwner = (HWND) w.getWindowHandle(); + of.lpstrFilter = filters; + of.nFilterIndex = 1; + of.lpstrFile = fname; + of.nMaxFile = numCharsAvailable; + of.lpstrInitialDir = initialDir; + of.lpstrTitle = title; + of.Flags = flags; + + if (extraInfoComponent != 0) + of.lpfnHook = &openCallback; + + if (isSaveDialogue) + { + if (! GetSaveFileName (&of)) + fname[0] = 0; + else + fnameIdx = of.nFileOffset; + } + else + { + if (! GetOpenFileName (&of)) + fname[0] = 0; + else + fnameIdx = of.nFileOffset; + } + } + } + } +#if JUCE_CATCH_UNHANDLED_EXCEPTIONS + catch (...) + { + fname[0] = 0; + } +#endif + + deleteAndZero (currentExtraFileWin); + + const WCHAR* const files = fname; + + if (selectMultipleFiles && fnameIdx > 0 && files [fnameIdx - 1] == 0) + { + const WCHAR* filename = files + fnameIdx; + + while (*filename != 0) + { + const String filepath (String (files) + T("\\") + String (filename)); + results.add (new File (filepath)); + filename += CharacterFunctions::length (filename) + 1; + } + } + else if (files[0] != 0) + { + results.add (new File (files)); + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_win32_FileChooser.cpp *********/ + +/********* Start of inlined file: juce_win32_Fonts.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +static int CALLBACK wfontEnum2 (ENUMLOGFONTEXW* lpelfe, + NEWTEXTMETRICEXW*, + int type, + LPARAM lParam) +{ + if (lpelfe != 0 && type == TRUETYPE_FONTTYPE) + { + const String fontName (lpelfe->elfLogFont.lfFaceName); + + ((StringArray*) lParam)->addIfNotAlreadyThere (fontName.removeCharacters (T("@"))); + } + + return 1; +} + +static int CALLBACK wfontEnum1 (ENUMLOGFONTEXW* lpelfe, + NEWTEXTMETRICEXW*, + int type, + LPARAM lParam) +{ + if (lpelfe != 0 + && ((type & (DEVICE_FONTTYPE | RASTER_FONTTYPE)) == 0)) + { + LOGFONTW lf; + zerostruct (lf); + + lf.lfWeight = FW_DONTCARE; + lf.lfOutPrecision = OUT_TT_ONLY_PRECIS; + lf.lfQuality = DEFAULT_QUALITY; + lf.lfCharSet = DEFAULT_CHARSET; + lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; + lf.lfPitchAndFamily = FF_DONTCARE; + + const String fontName (lpelfe->elfLogFont.lfFaceName); + fontName.copyToBuffer (lf.lfFaceName, LF_FACESIZE - 1); + + HDC dc = CreateCompatibleDC (0); + EnumFontFamiliesEx (dc, &lf, + (FONTENUMPROCW) &wfontEnum2, + lParam, 0); + DeleteDC (dc); + } + + return 1; +} + +const StringArray Font::findAllTypefaceNames() throw() +{ + StringArray results; + HDC dc = CreateCompatibleDC (0); + + { + LOGFONTW lf; + zerostruct (lf); + + lf.lfWeight = FW_DONTCARE; + lf.lfOutPrecision = OUT_TT_ONLY_PRECIS; + lf.lfQuality = DEFAULT_QUALITY; + lf.lfCharSet = DEFAULT_CHARSET; + lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; + lf.lfPitchAndFamily = FF_DONTCARE; + lf.lfFaceName[0] = 0; + + EnumFontFamiliesEx (dc, &lf, + (FONTENUMPROCW) &wfontEnum1, + (LPARAM) &results, 0); + } + + DeleteDC (dc); + + results.sort (true); + return results; +} + +extern bool juce_IsRunningInWine() throw(); + +void Font::getDefaultFontNames (String& defaultSans, + String& defaultSerif, + String& defaultFixed) throw() +{ + if (juce_IsRunningInWine()) + { + // If we're running in Wine, then use fonts that might be available on Linux.. + defaultSans = "Bitstream Vera Sans"; + defaultSerif = "Bitstream Vera Serif"; + defaultFixed = "Bitstream Vera Sans Mono"; + } + else + { + defaultSans = "Verdana"; + defaultSerif = "Times"; + defaultFixed = "Lucida Console"; + } +} + +class FontDCHolder : private DeletedAtShutdown +{ + HDC dc; + String fontName; + KERNINGPAIR* kps; + int numKPs; + bool bold, italic; + int size; + + FontDCHolder (const FontDCHolder&); + const FontDCHolder& operator= (const FontDCHolder&); + +public: + HFONT fontH; + + FontDCHolder() throw() + : dc (0), + kps (0), + numKPs (0), + bold (false), + italic (false), + size (0) + { + } + + ~FontDCHolder() throw() + { + if (dc != 0) + { + DeleteDC (dc); + DeleteObject (fontH); + juce_free (kps); + } + + clearSingletonInstance(); + } + + juce_DeclareSingleton_SingleThreaded_Minimal (FontDCHolder); + + HDC loadFont (const String& fontName_, + const bool bold_, + const bool italic_, + const int size_) throw() + { + if (fontName != fontName_ || bold != bold_ || italic != italic_ || size != size_) + { + fontName = fontName_; + bold = bold_; + italic = italic_; + size = size_; + + if (dc != 0) + { + DeleteDC (dc); + DeleteObject (fontH); + + juce_free (kps); + kps = 0; + } + + fontH = 0; + + dc = CreateCompatibleDC (0); + SetMapperFlags (dc, 0); + SetMapMode (dc, MM_TEXT); + + LOGFONTW lfw; + zerostruct (lfw); + + lfw.lfCharSet = DEFAULT_CHARSET; + lfw.lfClipPrecision = CLIP_DEFAULT_PRECIS; + lfw.lfOutPrecision = OUT_OUTLINE_PRECIS; + lfw.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; + lfw.lfQuality = PROOF_QUALITY; + lfw.lfItalic = (BYTE) (italic ? TRUE : FALSE); + lfw.lfWeight = bold ? FW_BOLD : FW_NORMAL; + fontName.copyToBuffer (lfw.lfFaceName, LF_FACESIZE - 1); + + lfw.lfHeight = size > 0 ? size : -256; + HFONT standardSizedFont = CreateFontIndirect (&lfw); + + if (standardSizedFont != 0) + { + if (SelectObject (dc, standardSizedFont) != 0) + { + fontH = standardSizedFont; + + if (size == 0) + { + OUTLINETEXTMETRIC otm; + if (GetOutlineTextMetrics (dc, sizeof (otm), &otm) != 0) + { + lfw.lfHeight = -(int) otm.otmEMSquare; + fontH = CreateFontIndirect (&lfw); + + SelectObject (dc, fontH); + DeleteObject (standardSizedFont); + } + } + } + else + { + jassertfalse + } + } + else + { + jassertfalse + } + } + + return dc; + } + + KERNINGPAIR* getKerningPairs (int& numKPs_) throw() + { + if (kps == 0) + { + numKPs = GetKerningPairs (dc, 0, 0); + kps = (KERNINGPAIR*) juce_calloc (sizeof (KERNINGPAIR) * numKPs); + GetKerningPairs (dc, numKPs, kps); + } + + numKPs_ = numKPs; + return kps; + } +}; + +juce_ImplementSingleton_SingleThreaded (FontDCHolder); + +static bool addGlyphToTypeface (HDC dc, + juce_wchar character, + Typeface& dest, + bool addKerning) +{ + Path destShape; + GLYPHMETRICS gm; + + float height; + BOOL ok = false; + + { + const WCHAR charToTest[] = { (WCHAR) character, 0 }; + WORD index = 0; + + if (GetGlyphIndices (dc, charToTest, 1, &index, GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR + && index == 0xffff) + { + return false; + } + } + + TEXTMETRIC tm; + ok = GetTextMetrics (dc, &tm); + + height = (float) tm.tmHeight; + + if (! ok) + { + dest.addGlyph (character, destShape, 0); + return true; + } + + const float scaleX = 1.0f / height; + const float scaleY = -1.0f / height; + static const MAT2 identityMatrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } }; + + const int bufSize = GetGlyphOutline (dc, character, GGO_NATIVE, + &gm, 0, 0, &identityMatrix); + + if (bufSize > 0) + { + char* const data = (char*) juce_malloc (bufSize); + + GetGlyphOutline (dc, character, GGO_NATIVE, &gm, + bufSize, data, &identityMatrix); + + const TTPOLYGONHEADER* pheader = (TTPOLYGONHEADER*) data; + + while ((char*) pheader < data + bufSize) + { + #define remapX(v) (scaleX * (v).x.value) + #define remapY(v) (scaleY * (v).y.value) + + float x = remapX (pheader->pfxStart); + float y = remapY (pheader->pfxStart); + + destShape.startNewSubPath (x, y); + + const TTPOLYCURVE* curve = (const TTPOLYCURVE*) ((const char*) pheader + sizeof (TTPOLYGONHEADER)); + const char* const curveEnd = ((const char*) pheader) + pheader->cb; + + while ((const char*) curve < curveEnd) + { + if (curve->wType == TT_PRIM_LINE) + { + for (int i = 0; i < curve->cpfx; ++i) + { + x = remapX (curve->apfx [i]); + y = remapY (curve->apfx [i]); + + destShape.lineTo (x, y); + } + } + else if (curve->wType == TT_PRIM_QSPLINE) + { + for (int i = 0; i < curve->cpfx - 1; ++i) + { + const float x2 = remapX (curve->apfx [i]); + const float y2 = remapY (curve->apfx [i]); + float x3, y3; + + if (i < curve->cpfx - 2) + { + x3 = 0.5f * (x2 + remapX (curve->apfx [i + 1])); + y3 = 0.5f * (y2 + remapY (curve->apfx [i + 1])); + } + else + { + x3 = remapX (curve->apfx [i + 1]); + y3 = remapY (curve->apfx [i + 1]); + } + + destShape.quadraticTo (x2, y2, x3, y3); + + x = x3; + y = y3; + } + } + + curve = (const TTPOLYCURVE*) &(curve->apfx [curve->cpfx]); + } + + pheader = (const TTPOLYGONHEADER*) curve; + + destShape.closeSubPath(); + } + + juce_free (data); + } + + dest.addGlyph (character, destShape, gm.gmCellIncX / height); + + if (addKerning) + { + int numKPs; + const KERNINGPAIR* const kps = FontDCHolder::getInstance()->getKerningPairs (numKPs); + + for (int i = 0; i < numKPs; ++i) + { + if (kps[i].wFirst == character) + { + dest.addKerningPair (kps[i].wFirst, + kps[i].wSecond, + kps[i].iKernAmount / height); + } + } + } + + return true; +} + +bool Typeface::findAndAddSystemGlyph (juce_wchar character) throw() +{ + HDC dc = FontDCHolder::getInstance()->loadFont (getName(), isBold(), isItalic(), 0); + return addGlyphToTypeface (dc, character, *this, true); +} + +/*Image* Typeface::renderGlyphToImage (juce_wchar character, float& topLeftX, float& topLeftY) +{ + HDC dc = FontDCHolder::getInstance()->loadFont (getName(), isBold(), isItalic(), hintingSize); + + int bufSize; + GLYPHMETRICS gm; + + const UINT format = GGO_GRAY2_BITMAP; + const int shift = 6; + + if (wGetGlyphOutlineW != 0) + bufSize = wGetGlyphOutlineW (dc, character, format, &gm, 0, 0, &identityMatrix); + else + bufSize = GetGlyphOutline (dc, character, format, &gm, 0, 0, &identityMatrix); + + Image* im = new Image (Image::SingleChannel, jmax (1, gm.gmBlackBoxX), jmax (1, gm.gmBlackBoxY), true); + + if (bufSize > 0) + { + topLeftX = (float) gm.gmptGlyphOrigin.x; + topLeftY = (float) -gm.gmptGlyphOrigin.y; + + uint8* const data = (uint8*) juce_calloc (bufSize); + + if (wGetGlyphOutlineW != 0) + wGetGlyphOutlineW (dc, character, format, &gm, bufSize, data, &identityMatrix); + else + GetGlyphOutline (dc, character, format, &gm, bufSize, data, &identityMatrix); + + const int stride = ((gm.gmBlackBoxX + 3) & ~3); + + for (int y = gm.gmBlackBoxY; --y >= 0;) + { + for (int x = gm.gmBlackBoxX; --x >= 0;) + { + const int level = data [x + y * stride] << shift; + + if (level > 0) + im->setPixelAt (x, y, Colour ((uint8) 0xff, (uint8) 0xff, (uint8) 0xff, (uint8) jmin (0xff, level))); + } + } + + juce_free (data); + } + + return im; +}*/ + +void Typeface::initialiseTypefaceCharacteristics (const String& fontName, + bool bold, + bool italic, + bool addAllGlyphsToFont) throw() +{ + clear(); + + HDC dc = FontDCHolder::getInstance()->loadFont (fontName, bold, italic, 0); + + float height; + int firstChar, lastChar; + + { + TEXTMETRIC tm; + GetTextMetrics (dc, &tm); + + height = (float) tm.tmHeight; + firstChar = tm.tmFirstChar; + lastChar = tm.tmLastChar; + + setAscent (tm.tmAscent / height); + setDefaultCharacter (tm.tmDefaultChar); + } + + setName (fontName); + setBold (bold); + setItalic (italic); + + if (addAllGlyphsToFont) + { + for (int character = firstChar; character <= lastChar; ++character) + addGlyphToTypeface (dc, (juce_wchar) character, *this, false); + + int numKPs; + const KERNINGPAIR* const kps = FontDCHolder::getInstance()->getKerningPairs (numKPs); + + for (int i = 0; i < numKPs; ++i) + { + addKerningPair (kps[i].wFirst, + kps[i].wSecond, + kps[i].iKernAmount / height); + } + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_win32_Fonts.cpp *********/ + +/********* Start of inlined file: juce_win32_Messaging.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +static const unsigned int specialId = WM_APP + 0x4400; +static const unsigned int broadcastId = WM_APP + 0x4403; +static const unsigned int specialCallbackId = WM_APP + 0x4402; + +static const TCHAR* const messageWindowName = _T("JUCEWindow"); + +HWND juce_messageWindowHandle = 0; + +extern long improbableWindowNumber; // defined in windowing.cpp + +static LRESULT CALLBACK juce_MessageWndProc (HWND h, + const UINT message, + const WPARAM wParam, + const LPARAM lParam) throw() +{ + JUCE_TRY + { + if (h == juce_messageWindowHandle) + { + if (message == specialCallbackId) + { + MessageCallbackFunction* const func = (MessageCallbackFunction*) wParam; + return (LRESULT) (*func) ((void*) lParam); + } + else if (message == specialId) + { + // these are trapped early in the dispatch call, but must also be checked + // here in case there are windows modal dialog boxes doing their own + // dispatch loop and not calling our version + + MessageManager::getInstance()->deliverMessage ((void*) lParam); + return 0; + } + else if (message == broadcastId) + { + String* const messageString = (String*) lParam; + MessageManager::getInstance()->deliverBroadcastMessage (*messageString); + delete messageString; + return 0; + } + else if (message == WM_COPYDATA && ((const COPYDATASTRUCT*) lParam)->dwData == broadcastId) + { + const String messageString ((const juce_wchar*) ((const COPYDATASTRUCT*) lParam)->lpData, + ((const COPYDATASTRUCT*) lParam)->cbData / sizeof (juce_wchar)); + + PostMessage (juce_messageWindowHandle, broadcastId, 0, (LPARAM) new String (messageString)); + return 0; + } + } + } + JUCE_CATCH_EXCEPTION + + return DefWindowProc (h, message, wParam, lParam); +} + +bool juce_dispatchNextMessageOnSystemQueue (const bool returnIfNoPendingMessages) +{ + MSG m; + + if (returnIfNoPendingMessages && ! PeekMessage (&m, (HWND) 0, 0, 0, 0)) + return false; + + if (GetMessage (&m, (HWND) 0, 0, 0) > 0) + { + if (m.message == specialId + && m.hwnd == juce_messageWindowHandle) + { + MessageManager::getInstance()->deliverMessage ((void*) m.lParam); + } + else + { + if (GetWindowLong (m.hwnd, GWLP_USERDATA) != improbableWindowNumber + && (m.message == WM_LBUTTONDOWN || m.message == WM_RBUTTONDOWN)) + { + // if it's someone else's window being clicked on, and the focus is + // currently on a juce window, pass the kb focus over.. + HWND currentFocus = GetFocus(); + + if (currentFocus == 0 || GetWindowLong (currentFocus, GWLP_USERDATA) == improbableWindowNumber) + SetFocus (m.hwnd); + } + + TranslateMessage (&m); + DispatchMessage (&m); + } + } + + return true; +} + +bool juce_postMessageToSystemQueue (void* message) +{ + return PostMessage (juce_messageWindowHandle, specialId, 0, (LPARAM) message) != 0; +} + +void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* callback, + void* userData) +{ + if (MessageManager::getInstance()->isThisTheMessageThread()) + { + return (*callback) (userData); + } + else + { + // If a thread has a MessageManagerLock and then tries to call this method, it'll + // deadlock because the message manager is blocked from running, and can't + // call your function.. + jassert (! MessageManager::getInstance()->currentThreadHasLockedMessageManager()); + + return (void*) SendMessage (juce_messageWindowHandle, + specialCallbackId, + (WPARAM) callback, + (LPARAM) userData); + } +} + +static BOOL CALLBACK BroadcastEnumWindowProc (HWND hwnd, LPARAM lParam) +{ + if (hwnd != juce_messageWindowHandle) + (reinterpret_cast (lParam))->add ((void*) hwnd); + + return TRUE; +} + +void MessageManager::broadcastMessage (const String& value) throw() +{ + VoidArray windows; + EnumWindows (&BroadcastEnumWindowProc, (LPARAM) &windows); + + const String localCopy (value); + + COPYDATASTRUCT data; + data.dwData = broadcastId; + data.cbData = (localCopy.length() + 1) * sizeof (juce_wchar); + data.lpData = (void*) (const juce_wchar*) localCopy; + + for (int i = windows.size(); --i >= 0;) + { + HWND hwnd = (HWND) windows.getUnchecked(i); + + TCHAR windowName [64]; // no need to read longer strings than this + GetWindowText (hwnd, windowName, 64); + windowName [63] = 0; + + if (String (windowName) == String (messageWindowName)) + { + DWORD_PTR result; + SendMessageTimeout (hwnd, WM_COPYDATA, + (WPARAM) juce_messageWindowHandle, + (LPARAM) &data, + SMTO_BLOCK | SMTO_ABORTIFHUNG, + 8000, + &result); + } + } +} + +static const String getMessageWindowClassName() +{ + // this name has to be different for each app/dll instance because otherwise + // poor old Win32 can get a bit confused (even despite it not being a process-global + // window class). + + static int number = 0; + if (number == 0) + number = 0x7fffffff & (int) Time::getHighResolutionTicks(); + + return T("JUCEcs_") + String (number); +} + +void MessageManager::doPlatformSpecificInitialisation() +{ + OleInitialize (0); + + const String className (getMessageWindowClassName()); + + HMODULE hmod = (HMODULE) PlatformUtilities::getCurrentModuleInstanceHandle(); + + WNDCLASSEX wc; + zerostruct (wc); + + wc.cbSize = sizeof (wc); + wc.lpfnWndProc = (WNDPROC) juce_MessageWndProc; + wc.cbWndExtra = 4; + wc.hInstance = hmod; + wc.lpszClassName = className; + + RegisterClassEx (&wc); + + juce_messageWindowHandle = CreateWindow (wc.lpszClassName, + messageWindowName, + 0, 0, 0, 0, 0, 0, 0, + hmod, 0); +} + +void MessageManager::doPlatformSpecificShutdown() +{ + DestroyWindow (juce_messageWindowHandle); + UnregisterClass (getMessageWindowClassName(), 0); + OleUninitialize(); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_win32_Messaging.cpp *********/ + +/********* Start of inlined file: juce_win32_Midi.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +#if JUCE_MSVC + #pragma warning (disable: 4312) +#endif + +using ::free; + +static const int midiBufferSize = 1024 * 10; +static const int numInHeaders = 32; +static const int inBufferSize = 256; +static Array activeMidiThreads; + +class MidiInThread : public Thread +{ +public: + + MidiInThread (MidiInput* const input_, + MidiInputCallback* const callback_) + : Thread ("Juce Midi"), + hIn (0), + input (input_), + callback (callback_), + isStarted (false), + startTime (0), + pendingLength(0) + { + for (int i = numInHeaders; --i >= 0;) + { + zeromem (&hdr[i], sizeof (MIDIHDR)); + hdr[i].lpData = inData[i]; + hdr[i].dwBufferLength = inBufferSize; + } + }; + + ~MidiInThread() + { + stop(); + + if (hIn != 0) + { + int count = 5; + while (--count >= 0) + { + if (midiInClose (hIn) == MMSYSERR_NOERROR) + break; + + Sleep (20); + } + } + } + + void handle (const uint32 message, const uint32 timeStamp) throw() + { + const int byte = message & 0xff; + if (byte < 0x80) + return; + + const int numBytes = MidiMessage::getMessageLengthFromFirstByte ((uint8) byte); + + const double time = timeStampToTime (timeStamp); + + lock.enter(); + if (pendingLength < midiBufferSize - 12) + { + char* const p = pending + pendingLength; + *(double*) p = time; + *(uint32*) (p + 8) = numBytes; + *(uint32*) (p + 12) = message; + pendingLength += 12 + numBytes; + } + else + { + jassertfalse // midi buffer overflow! You might need to increase the size.. + } + + lock.exit(); + notify(); + } + + void handleSysEx (MIDIHDR* const hdr, const uint32 timeStamp) throw() + { + const int num = hdr->dwBytesRecorded; + + if (num > 0) + { + const double time = timeStampToTime (timeStamp); + + lock.enter(); + + if (pendingLength < midiBufferSize - (8 + num)) + { + char* const p = pending + pendingLength; + *(double*) p = time; + *(uint32*) (p + 8) = num; + memcpy (p + 12, hdr->lpData, num); + pendingLength += 12 + num; + } + else + { + jassertfalse // midi buffer overflow! You might need to increase the size.. + } + + lock.exit(); + notify(); + } + } + + void writeBlock (const int i) throw() + { + hdr[i].dwBytesRecorded = 0; + MMRESULT res = midiInPrepareHeader (hIn, &hdr[i], sizeof (MIDIHDR)); + jassert (res == MMSYSERR_NOERROR); + res = midiInAddBuffer (hIn, &hdr[i], sizeof (MIDIHDR)); + jassert (res == MMSYSERR_NOERROR); + } + + void run() + { + MemoryBlock pendingCopy (64); + + while (! threadShouldExit()) + { + for (int i = 0; i < numInHeaders; ++i) + { + if ((hdr[i].dwFlags & WHDR_DONE) != 0) + { + MMRESULT res = midiInUnprepareHeader (hIn, &hdr[i], sizeof (MIDIHDR)); + (void) res; + jassert (res == MMSYSERR_NOERROR); + writeBlock (i); + } + } + + lock.enter(); + + int len = pendingLength; + + if (len > 0) + { + pendingCopy.ensureSize (len); + pendingCopy.copyFrom (pending, 0, len); + pendingLength = 0; + } + + lock.exit(); + +//xxx needs to figure out if blocks are broken up or not + + if (len == 0) + { + wait (500); + } + else + { + const char* p = (const char*) pendingCopy.getData(); + + while (len > 0) + { + const double time = *(const double*) p; + const int messageLen = *(const int*) (p + 8); + + const MidiMessage message ((const uint8*) (p + 12), messageLen, time); + + callback->handleIncomingMidiMessage (input, message); + + p += 12 + messageLen; + len -= 12 + messageLen; + } + } + } + } + + void start() throw() + { + jassert (hIn != 0); + if (hIn != 0 && ! isStarted) + { + stop(); + + activeMidiThreads.addIfNotAlreadyThere (this); + + int i; + for (i = 0; i < numInHeaders; ++i) + writeBlock (i); + + startTime = Time::getMillisecondCounter(); + MMRESULT res = midiInStart (hIn); + + jassert (res == MMSYSERR_NOERROR); + + if (res == MMSYSERR_NOERROR) + { + isStarted = true; + pendingLength = 0; + startThread (6); + } + } + } + + void stop() throw() + { + if (isStarted) + { + stopThread (5000); + + midiInReset (hIn); + midiInStop (hIn); + + activeMidiThreads.removeValue (this); + + lock.enter(); + lock.exit(); + + for (int i = numInHeaders; --i >= 0;) + { + if ((hdr[i].dwFlags & WHDR_DONE) != 0) + { + int c = 10; + while (--c >= 0 && midiInUnprepareHeader (hIn, &hdr[i], sizeof (MIDIHDR)) == MIDIERR_STILLPLAYING) + Sleep (20); + + jassert (c >= 0); + } + } + + isStarted = false; + pendingLength = 0; + } + } + + juce_UseDebuggingNewOperator + + HMIDIIN hIn; + +private: + MidiInput* input; + MidiInputCallback* callback; + bool isStarted; + uint32 startTime; + CriticalSection lock; + + MIDIHDR hdr [numInHeaders]; + char inData [numInHeaders] [inBufferSize]; + + int pendingLength; + char pending [midiBufferSize]; + + double timeStampToTime (uint32 timeStamp) throw() + { + timeStamp += startTime; + + const uint32 now = Time::getMillisecondCounter(); + if (timeStamp > now) + { + if (timeStamp > now + 2) + --startTime; + + timeStamp = now; + } + + return 0.001 * timeStamp; + } + + MidiInThread (const MidiInThread&); + const MidiInThread& operator= (const MidiInThread&); +}; + +static void CALLBACK midiInCallback (HMIDIIN, + UINT uMsg, + DWORD_PTR dwInstance, + DWORD_PTR midiMessage, + DWORD_PTR timeStamp) +{ + MidiInThread* const thread = (MidiInThread*) dwInstance; + + if (thread != 0 && activeMidiThreads.contains (thread)) + { + if (uMsg == MIM_DATA) + thread->handle ((uint32) midiMessage, (uint32) timeStamp); + else if (uMsg == MIM_LONGDATA) + thread->handleSysEx ((MIDIHDR*) midiMessage, (uint32) timeStamp); + } +} + +const StringArray MidiInput::getDevices() +{ + StringArray s; + const int num = midiInGetNumDevs(); + + for (int i = 0; i < num; ++i) + { + MIDIINCAPS mc; + zerostruct (mc); + + if (midiInGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR) + s.add (String (mc.szPname, sizeof (mc.szPname))); + } + + return s; +} + +int MidiInput::getDefaultDeviceIndex() +{ + return 0; +} + +MidiInput* MidiInput::openDevice (const int index, MidiInputCallback* const callback) +{ + if (callback == 0) + return 0; + + UINT deviceId = MIDI_MAPPER; + int n = 0; + String name; + + const int num = midiInGetNumDevs(); + + for (int i = 0; i < num; ++i) + { + MIDIINCAPS mc; + zerostruct (mc); + + if (midiInGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR) + { + if (index == n) + { + deviceId = i; + name = String (mc.szPname, sizeof (mc.szPname)); + break; + } + + ++n; + } + } + + MidiInput* const in = new MidiInput (name); + MidiInThread* const thread = new MidiInThread (in, callback); + + HMIDIIN h; + HRESULT err = midiInOpen (&h, deviceId, + (DWORD_PTR) &midiInCallback, + (DWORD_PTR) thread, + CALLBACK_FUNCTION); + + if (err == MMSYSERR_NOERROR) + { + thread->hIn = h; + in->internal = (void*) thread; + return in; + } + else + { + delete in; + delete thread; + return 0; + } +} + +MidiInput::MidiInput (const String& name_) + : name (name_), + internal (0) +{ +} + +MidiInput::~MidiInput() +{ + if (internal != 0) + { + MidiInThread* const thread = (MidiInThread*) internal; + delete thread; + } +} + +void MidiInput::start() +{ + ((MidiInThread*) internal)->start(); +} + +void MidiInput::stop() +{ + ((MidiInThread*) internal)->stop(); +} + +struct MidiOutHandle +{ + int refCount; + UINT deviceId; + HMIDIOUT handle; + + juce_UseDebuggingNewOperator +}; + +static VoidArray handles (4); + +const StringArray MidiOutput::getDevices() +{ + StringArray s; + const int num = midiOutGetNumDevs(); + + for (int i = 0; i < num; ++i) + { + MIDIOUTCAPS mc; + zerostruct (mc); + + if (midiOutGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR) + s.add (String (mc.szPname, sizeof (mc.szPname))); + } + + return s; +} + +int MidiOutput::getDefaultDeviceIndex() +{ + const int num = midiOutGetNumDevs(); + int n = 0; + + for (int i = 0; i < num; ++i) + { + MIDIOUTCAPS mc; + zerostruct (mc); + + if (midiOutGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR) + { + if ((mc.wTechnology & MOD_MAPPER) != 0) + return n; + + ++n; + } + } + + return 0; +} + +MidiOutput* MidiOutput::openDevice (int index) +{ + UINT deviceId = MIDI_MAPPER; + const int num = midiOutGetNumDevs(); + int i, n = 0; + + for (i = 0; i < num; ++i) + { + MIDIOUTCAPS mc; + zerostruct (mc); + + if (midiOutGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR) + { + // use the microsoft sw synth as a default - best not to allow deviceId + // to be MIDI_MAPPER, or else device sharing breaks + if (String (mc.szPname, sizeof (mc.szPname)).containsIgnoreCase (T("microsoft"))) + deviceId = i; + + if (index == n) + { + deviceId = i; + break; + } + + ++n; + } + } + + for (i = handles.size(); --i >= 0;) + { + MidiOutHandle* const han = (MidiOutHandle*) handles.getUnchecked(i); + + if (han != 0 && han->deviceId == deviceId) + { + han->refCount++; + + MidiOutput* const out = new MidiOutput(); + out->internal = (void*) han; + return out; + } + } + + for (i = 4; --i >= 0;) + { + HMIDIOUT h = 0; + MMRESULT res = midiOutOpen (&h, deviceId, 0, 0, CALLBACK_NULL); + + if (res == MMSYSERR_NOERROR) + { + MidiOutHandle* const han = new MidiOutHandle(); + han->deviceId = deviceId; + han->refCount = 1; + han->handle = h; + handles.add (han); + + MidiOutput* const out = new MidiOutput(); + out->internal = (void*) han; + return out; + } + else if (res == MMSYSERR_ALLOCATED) + { + Sleep (100); + } + else + { + break; + } + } + + return 0; +} + +MidiOutput::~MidiOutput() +{ + MidiOutHandle* const h = (MidiOutHandle*) internal; + + if (handles.contains ((void*) h) && --(h->refCount) == 0) + { + midiOutClose (h->handle); + handles.removeValue ((void*) h); + delete h; + } +} + +void MidiOutput::reset() +{ + const MidiOutHandle* const h = (MidiOutHandle*) internal; + midiOutReset (h->handle); +} + +bool MidiOutput::getVolume (float& leftVol, + float& rightVol) +{ + const MidiOutHandle* const handle = (const MidiOutHandle*) internal; + + DWORD n; + if (midiOutGetVolume (handle->handle, &n) == MMSYSERR_NOERROR) + { + const unsigned short* const nn = (const unsigned short*) &n; + rightVol = nn[0] / (float) 0xffff; + leftVol = nn[1] / (float) 0xffff; + return true; + } + else + { + rightVol = leftVol = 1.0f; + return false; + } +} + +void MidiOutput::setVolume (float leftVol, + float rightVol) +{ + const MidiOutHandle* const handle = (MidiOutHandle*) internal; + + DWORD n; + unsigned short* const nn = (unsigned short*) &n; + nn[0] = (unsigned short) jlimit (0, 0xffff, (int)(rightVol * 0xffff)); + nn[1] = (unsigned short) jlimit (0, 0xffff, (int)(leftVol * 0xffff)); + midiOutSetVolume (handle->handle, n); +} + +void MidiOutput::sendMessageNow (const MidiMessage& message) +{ + const MidiOutHandle* const handle = (const MidiOutHandle*) internal; + + if (message.getRawDataSize() > 3 + || message.isSysEx()) + { + MIDIHDR h; + zerostruct (h); + + h.lpData = (char*) message.getRawData(); + h.dwBufferLength = message.getRawDataSize(); + h.dwBytesRecorded = message.getRawDataSize(); + + if (midiOutPrepareHeader (handle->handle, &h, sizeof (MIDIHDR)) == MMSYSERR_NOERROR) + { + MMRESULT res = midiOutLongMsg (handle->handle, &h, sizeof (MIDIHDR)); + + if (res == MMSYSERR_NOERROR) + { + while ((h.dwFlags & MHDR_DONE) == 0) + Sleep (1); + + int count = 500; // 1 sec timeout + + while (--count >= 0) + { + res = midiOutUnprepareHeader (handle->handle, &h, sizeof (MIDIHDR)); + + if (res == MIDIERR_STILLPLAYING) + Sleep (2); + else + break; + } + } + } + } + else + { + midiOutShortMsg (handle->handle, + *(unsigned int*) message.getRawData()); + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_win32_Midi.cpp *********/ + +/********* Start of inlined file: juce_win32_WebBrowserComponent.cpp *********/ + +#ifdef _MSC_VER + #pragma warning (disable: 4514) + #pragma warning (push) +#endif + +#include +#include +#include + +#ifdef _MSC_VER + #pragma warning (pop) + #pragma warning (disable: 4312 4244) +#endif + +BEGIN_JUCE_NAMESPACE + +class WebBrowserComponentInternal : public ActiveXControlComponent +{ +public: + + WebBrowserComponentInternal() + : browser (0), + connectionPoint (0), + adviseCookie (0) + { + } + + ~WebBrowserComponentInternal() + { + if (connectionPoint != 0) + connectionPoint->Unadvise (adviseCookie); + + if (browser != 0) + browser->Release(); + } + + void createBrowser() + { + createControl (&CLSID_WebBrowser); + browser = (IWebBrowser2*) queryInterface (&IID_IWebBrowser2); + + IConnectionPointContainer* connectionPointContainer = (IConnectionPointContainer*) queryInterface (&IID_IConnectionPointContainer); + + if (connectionPointContainer != 0) + { + connectionPointContainer->FindConnectionPoint (DIID_DWebBrowserEvents2, + &connectionPoint); + + if (connectionPoint != 0) + { + WebBrowserComponent* const owner = dynamic_cast (getParentComponent()); + jassert (owner != 0); + + EventHandler* handler = new EventHandler (owner); + connectionPoint->Advise (handler, &adviseCookie); + } + } + } + + void goToURL (const String& url, + const StringArray* headers, + const MemoryBlock* postData) + { + if (browser != 0) + { + LPSAFEARRAY sa = 0; + _variant_t flags, frame, postDataVar, headersVar; + + if (headers != 0) + headersVar = (const tchar*) headers->joinIntoString ("\r\n"); + + if (postData != 0 && postData->getSize() > 0) + { + LPSAFEARRAY sa = SafeArrayCreateVector (VT_UI1, 0, postData->getSize()); + + if (sa != 0) + { + void* data = 0; + SafeArrayAccessData (sa, &data); + jassert (data != 0); + + if (data != 0) + { + postData->copyTo (data, 0, postData->getSize()); + SafeArrayUnaccessData (sa); + + VARIANT postDataVar2; + VariantInit (&postDataVar2); + V_VT (&postDataVar2) = VT_ARRAY | VT_UI1; + V_ARRAY (&postDataVar2) = sa; + + postDataVar = postDataVar2; + } + } + } + + browser->Navigate ((BSTR) (const OLECHAR*) url, + &flags, &frame, + &postDataVar, &headersVar); + + if (sa != 0) + SafeArrayDestroy (sa); + } + } + + IWebBrowser2* browser; + + juce_UseDebuggingNewOperator + +private: + IConnectionPoint* connectionPoint; + DWORD adviseCookie; + + class EventHandler : public IDispatch + { + public: + EventHandler (WebBrowserComponent* owner_) + : owner (owner_), + refCount (0) + { + } + + ~EventHandler() + { + } + + HRESULT __stdcall QueryInterface (REFIID id, void __RPC_FAR* __RPC_FAR* result) + { + if (id == IID_IUnknown || id == IID_IDispatch || id == DIID_DWebBrowserEvents2) + { + AddRef(); + *result = this; + return S_OK; + } + + *result = 0; + return E_NOINTERFACE; + } + + ULONG __stdcall AddRef() { return ++refCount; } + ULONG __stdcall Release() { jassert (refCount > 0); const int r = --refCount; if (r == 0) delete this; return r; } + + HRESULT __stdcall GetTypeInfoCount (UINT __RPC_FAR*) { return E_NOTIMPL; } + HRESULT __stdcall GetTypeInfo (UINT, LCID, ITypeInfo __RPC_FAR *__RPC_FAR*) { return E_NOTIMPL; } + HRESULT __stdcall GetIDsOfNames (REFIID, LPOLESTR __RPC_FAR*, UINT, LCID, DISPID __RPC_FAR*) { return E_NOTIMPL; } + + HRESULT __stdcall Invoke (DISPID dispIdMember, REFIID /*riid*/, LCID /*lcid*/, + WORD /*wFlags*/, DISPPARAMS __RPC_FAR* pDispParams, + VARIANT __RPC_FAR* /*pVarResult*/, EXCEPINFO __RPC_FAR* /*pExcepInfo*/, + UINT __RPC_FAR* /*puArgErr*/) + { + switch (dispIdMember) + { + case DISPID_BEFORENAVIGATE2: + { + VARIANT* const vurl = pDispParams->rgvarg[5].pvarVal; + + String url; + + if ((vurl->vt & VT_BYREF) != 0) + url = *vurl->pbstrVal; + else + url = vurl->bstrVal; + + *pDispParams->rgvarg->pboolVal + = owner->pageAboutToLoad (url) ? VARIANT_FALSE + : VARIANT_TRUE; + + return S_OK; + } + + default: + break; + } + + return E_NOTIMPL; + } + + juce_UseDebuggingNewOperator + + private: + WebBrowserComponent* const owner; + int refCount; + + EventHandler (const EventHandler&); + const EventHandler& operator= (const EventHandler&); + }; +}; + +WebBrowserComponent::WebBrowserComponent() + : browser (0), + blankPageShown (false) +{ + setOpaque (true); + addAndMakeVisible (browser = new WebBrowserComponentInternal()); +} + +WebBrowserComponent::~WebBrowserComponent() +{ + delete browser; +} + +void WebBrowserComponent::goToURL (const String& url, + const StringArray* headers, + const MemoryBlock* postData) +{ + lastURL = url; + + lastHeaders.clear(); + if (headers != 0) + lastHeaders = *headers; + + lastPostData.setSize (0); + if (postData != 0) + lastPostData = *postData; + + blankPageShown = false; + + browser->goToURL (url, headers, postData); +} + +void WebBrowserComponent::stop() +{ + if (browser->browser != 0) + browser->browser->Stop(); +} + +void WebBrowserComponent::goBack() +{ + lastURL = String::empty; + blankPageShown = false; + + if (browser->browser != 0) + browser->browser->GoBack(); +} + +void WebBrowserComponent::goForward() +{ + lastURL = String::empty; + + if (browser->browser != 0) + browser->browser->GoForward(); +} + +void WebBrowserComponent::paint (Graphics& g) +{ + if (browser->browser == 0) + g.fillAll (Colours::white); +} + +void WebBrowserComponent::checkWindowAssociation() +{ + if (isShowing()) + { + if (blankPageShown) + goBack(); + + if (browser->browser == 0 && getPeer() != 0) + { + browser->createBrowser(); + reloadLastURL(); + } + } + else + { + if (browser != 0 && ! blankPageShown) + { + // when the component becomes invisible, some stuff like flash + // carries on playing audio, so we need to force it onto a blank + // page to avoid this.. + + blankPageShown = true; + browser->goToURL ("about:blank", 0, 0); + } + } +} + +void WebBrowserComponent::reloadLastURL() +{ + if (lastURL.isNotEmpty()) + { + goToURL (lastURL, &lastHeaders, &lastPostData); + lastURL = String::empty; + } +} + +void WebBrowserComponent::parentHierarchyChanged() +{ + checkWindowAssociation(); +} + +void WebBrowserComponent::moved() +{ +} + +void WebBrowserComponent::resized() +{ + browser->setSize (getWidth(), getHeight()); +} + +void WebBrowserComponent::visibilityChanged() +{ + checkWindowAssociation(); +} + +bool WebBrowserComponent::pageAboutToLoad (const String&) +{ + return true; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_win32_WebBrowserComponent.cpp *********/ + +/********* Start of inlined file: juce_win32_Windowing.cpp *********/ +#ifdef _MSC_VER + #pragma warning (disable: 4514) + #pragma warning (push) +#endif + +#include +#include +#include + +#if JUCE_OPENGL + #include +#endif + +#ifdef _MSC_VER + #pragma warning (pop) + #pragma warning (disable: 4312 4244) +#endif + +#undef GetSystemMetrics // multimon overrides this for some reason and causes a mess.. + +// these are in the windows SDK, but need to be repeated here for GCC.. +#ifndef GET_APPCOMMAND_LPARAM + #define FAPPCOMMAND_MASK 0xF000 + #define GET_APPCOMMAND_LPARAM(lParam) ((short) (HIWORD (lParam) & ~FAPPCOMMAND_MASK)) + #define APPCOMMAND_MEDIA_NEXTTRACK 11 + #define APPCOMMAND_MEDIA_PREVIOUSTRACK 12 + #define APPCOMMAND_MEDIA_STOP 13 + #define APPCOMMAND_MEDIA_PLAY_PAUSE 14 + #define WM_APPCOMMAND 0x0319 +#endif + +BEGIN_JUCE_NAMESPACE + +extern void juce_repeatLastProcessPriority() throw(); // in juce_win32_Threads.cpp +extern void juce_CheckCurrentlyFocusedTopLevelWindow() throw(); // in juce_TopLevelWindow.cpp +extern bool juce_IsRunningInWine() throw(); + +#ifndef ULW_ALPHA + #define ULW_ALPHA 0x00000002 +#endif + +#ifndef AC_SRC_ALPHA + #define AC_SRC_ALPHA 0x01 +#endif + +#define DEBUG_REPAINT_TIMES 0 + +static HPALETTE palette = 0; +static bool createPaletteIfNeeded = true; +static bool shouldDeactivateTitleBar = true; +static bool screenSaverAllowed = true; + +static HICON createHICONFromImage (const Image& image, const BOOL isIcon, int hotspotX, int hotspotY) throw(); +#define WM_TRAYNOTIFY WM_USER + 100 + +using ::abs; + +typedef BOOL (WINAPI* UpdateLayeredWinFunc) (HWND, HDC, POINT*, SIZE*, HDC, POINT*, COLORREF, BLENDFUNCTION*, DWORD); +static UpdateLayeredWinFunc updateLayeredWindow = 0; + +bool Desktop::canUseSemiTransparentWindows() throw() +{ + if (updateLayeredWindow == 0) + { + if (! juce_IsRunningInWine()) + { + HMODULE user32Mod = GetModuleHandle (_T("user32.dll")); + updateLayeredWindow = (UpdateLayeredWinFunc) GetProcAddress (user32Mod, "UpdateLayeredWindow"); + } + } + + return updateLayeredWindow != 0; +} + +#undef DefWindowProc +#define DefWindowProc DefWindowProcW + +const int extendedKeyModifier = 0x10000; + +const int KeyPress::spaceKey = VK_SPACE; +const int KeyPress::returnKey = VK_RETURN; +const int KeyPress::escapeKey = VK_ESCAPE; +const int KeyPress::backspaceKey = VK_BACK; +const int KeyPress::deleteKey = VK_DELETE | extendedKeyModifier; +const int KeyPress::insertKey = VK_INSERT | extendedKeyModifier; +const int KeyPress::tabKey = VK_TAB; +const int KeyPress::leftKey = VK_LEFT | extendedKeyModifier; +const int KeyPress::rightKey = VK_RIGHT | extendedKeyModifier; +const int KeyPress::upKey = VK_UP | extendedKeyModifier; +const int KeyPress::downKey = VK_DOWN | extendedKeyModifier; +const int KeyPress::homeKey = VK_HOME | extendedKeyModifier; +const int KeyPress::endKey = VK_END | extendedKeyModifier; +const int KeyPress::pageUpKey = VK_PRIOR | extendedKeyModifier; +const int KeyPress::pageDownKey = VK_NEXT | extendedKeyModifier; +const int KeyPress::F1Key = VK_F1 | extendedKeyModifier; +const int KeyPress::F2Key = VK_F2 | extendedKeyModifier; +const int KeyPress::F3Key = VK_F3 | extendedKeyModifier; +const int KeyPress::F4Key = VK_F4 | extendedKeyModifier; +const int KeyPress::F5Key = VK_F5 | extendedKeyModifier; +const int KeyPress::F6Key = VK_F6 | extendedKeyModifier; +const int KeyPress::F7Key = VK_F7 | extendedKeyModifier; +const int KeyPress::F8Key = VK_F8 | extendedKeyModifier; +const int KeyPress::F9Key = VK_F9 | extendedKeyModifier; +const int KeyPress::F10Key = VK_F10 | extendedKeyModifier; +const int KeyPress::F11Key = VK_F11 | extendedKeyModifier; +const int KeyPress::F12Key = VK_F12 | extendedKeyModifier; +const int KeyPress::F13Key = VK_F13 | extendedKeyModifier; +const int KeyPress::F14Key = VK_F14 | extendedKeyModifier; +const int KeyPress::F15Key = VK_F15 | extendedKeyModifier; +const int KeyPress::F16Key = VK_F16 | extendedKeyModifier; +const int KeyPress::numberPad0 = VK_NUMPAD0 | extendedKeyModifier; +const int KeyPress::numberPad1 = VK_NUMPAD1 | extendedKeyModifier; +const int KeyPress::numberPad2 = VK_NUMPAD2 | extendedKeyModifier; +const int KeyPress::numberPad3 = VK_NUMPAD3 | extendedKeyModifier; +const int KeyPress::numberPad4 = VK_NUMPAD4 | extendedKeyModifier; +const int KeyPress::numberPad5 = VK_NUMPAD5 | extendedKeyModifier; +const int KeyPress::numberPad6 = VK_NUMPAD6 | extendedKeyModifier; +const int KeyPress::numberPad7 = VK_NUMPAD7 | extendedKeyModifier; +const int KeyPress::numberPad8 = VK_NUMPAD8 | extendedKeyModifier; +const int KeyPress::numberPad9 = VK_NUMPAD9 | extendedKeyModifier; +const int KeyPress::numberPadAdd = VK_ADD | extendedKeyModifier; +const int KeyPress::numberPadSubtract = VK_SUBTRACT | extendedKeyModifier; +const int KeyPress::numberPadMultiply = VK_MULTIPLY | extendedKeyModifier; +const int KeyPress::numberPadDivide = VK_DIVIDE | extendedKeyModifier; +const int KeyPress::numberPadSeparator = VK_SEPARATOR | extendedKeyModifier; +const int KeyPress::numberPadDecimalPoint = VK_DECIMAL | extendedKeyModifier; +const int KeyPress::numberPadEquals = 0x92 /*VK_OEM_NEC_EQUAL*/ | extendedKeyModifier; +const int KeyPress::numberPadDelete = VK_DELETE | extendedKeyModifier; +const int KeyPress::playKey = 0x30000; +const int KeyPress::stopKey = 0x30001; +const int KeyPress::fastForwardKey = 0x30002; +const int KeyPress::rewindKey = 0x30003; + +class WindowsBitmapImage : public Image +{ +public: + + HBITMAP hBitmap; + BITMAPV4HEADER bitmapInfo; + HDC hdc; + unsigned char* bitmapData; + + WindowsBitmapImage (const PixelFormat format_, + const int w, const int h, const bool clearImage) + : Image (format_, w, h) + { + jassert (format_ == RGB || format_ == ARGB); + + pixelStride = (format_ == RGB) ? 3 : 4; + + zerostruct (bitmapInfo); + bitmapInfo.bV4Size = sizeof (BITMAPV4HEADER); + bitmapInfo.bV4Width = w; + bitmapInfo.bV4Height = h; + bitmapInfo.bV4Planes = 1; + bitmapInfo.bV4BitCount = (unsigned short) (pixelStride * 8); + + if (format_ == ARGB) + { + bitmapInfo.bV4AlphaMask = 0xff000000; + bitmapInfo.bV4RedMask = 0xff0000; + bitmapInfo.bV4GreenMask = 0xff00; + bitmapInfo.bV4BlueMask = 0xff; + bitmapInfo.bV4V4Compression = BI_BITFIELDS; + } + else + { + bitmapInfo.bV4V4Compression = BI_RGB; + } + + lineStride = -((w * pixelStride + 3) & ~3); + + HDC dc = GetDC (0); + hdc = CreateCompatibleDC (dc); + ReleaseDC (0, dc); + + SetMapMode (hdc, MM_TEXT); + + hBitmap = CreateDIBSection (hdc, + (BITMAPINFO*) &(bitmapInfo), + DIB_RGB_COLORS, + (void**) &bitmapData, + 0, 0); + + SelectObject (hdc, hBitmap); + + if (format_ == ARGB && clearImage) + zeromem (bitmapData, abs (h * lineStride)); + + imageData = bitmapData - (lineStride * (h - 1)); + } + + ~WindowsBitmapImage() + { + DeleteDC (hdc); + DeleteObject (hBitmap); + imageData = 0; // to stop the base class freeing this + } + + void blitToWindow (HWND hwnd, HDC dc, const bool transparent, + const int x, const int y, + const RectangleList& maskedRegion) throw() + { + static HDRAWDIB hdd = 0; + static bool needToCreateDrawDib = true; + + if (needToCreateDrawDib) + { + needToCreateDrawDib = false; + + HDC dc = GetDC (0); + const int n = GetDeviceCaps (dc, BITSPIXEL); + ReleaseDC (0, dc); + + // only open if we're not palettised + if (n > 8) + hdd = DrawDibOpen(); + } + + if (createPaletteIfNeeded) + { + HDC dc = GetDC (0); + const int n = GetDeviceCaps (dc, BITSPIXEL); + ReleaseDC (0, dc); + + if (n <= 8) + palette = CreateHalftonePalette (dc); + + createPaletteIfNeeded = false; + } + + if (palette != 0) + { + SelectPalette (dc, palette, FALSE); + RealizePalette (dc); + SetStretchBltMode (dc, HALFTONE); + } + + SetMapMode (dc, MM_TEXT); + + if (transparent) + { + POINT p, pos; + SIZE size; + + RECT windowBounds; + GetWindowRect (hwnd, &windowBounds); + + p.x = -x; + p.y = -y; + pos.x = windowBounds.left; + pos.y = windowBounds.top; + size.cx = windowBounds.right - windowBounds.left; + size.cy = windowBounds.bottom - windowBounds.top; + + BLENDFUNCTION bf; + bf.AlphaFormat = AC_SRC_ALPHA; + bf.BlendFlags = 0; + bf.BlendOp = AC_SRC_OVER; + bf.SourceConstantAlpha = 0xff; + + if (! maskedRegion.isEmpty()) + { + for (RectangleList::Iterator i (maskedRegion); i.next();) + { + const Rectangle& r = *i.getRectangle(); + ExcludeClipRect (hdc, r.getX(), r.getY(), r.getRight(), r.getBottom()); + } + } + + updateLayeredWindow (hwnd, 0, &pos, &size, hdc, &p, 0, &bf, ULW_ALPHA); + } + else + { + int savedDC = 0; + + if (! maskedRegion.isEmpty()) + { + savedDC = SaveDC (dc); + + for (RectangleList::Iterator i (maskedRegion); i.next();) + { + const Rectangle& r = *i.getRectangle(); + ExcludeClipRect (dc, r.getX(), r.getY(), r.getRight(), r.getBottom()); + } + } + + const int w = getWidth(); + const int h = getHeight(); + + if (hdd == 0) + { + StretchDIBits (dc, + x, y, w, h, + 0, 0, w, h, + bitmapData, (const BITMAPINFO*) &bitmapInfo, + DIB_RGB_COLORS, SRCCOPY); + } + else + { + DrawDibDraw (hdd, dc, x, y, -1, -1, + (BITMAPINFOHEADER*) &bitmapInfo, bitmapData, + 0, 0, w, h, 0); + } + + if (! maskedRegion.isEmpty()) + RestoreDC (dc, savedDC); + } + } + + juce_UseDebuggingNewOperator + +private: + WindowsBitmapImage (const WindowsBitmapImage&); + const WindowsBitmapImage& operator= (const WindowsBitmapImage&); +}; + +long improbableWindowNumber = 0xf965aa01; // also referenced by messaging.cpp + +static int currentModifiers = 0; +static int modifiersAtLastCallback = 0; + +static void updateKeyModifiers() throw() +{ + currentModifiers &= ~(ModifierKeys::shiftModifier + | ModifierKeys::ctrlModifier + | ModifierKeys::altModifier); + + if ((GetKeyState (VK_SHIFT) & 0x8000) != 0) + currentModifiers |= ModifierKeys::shiftModifier; + + if ((GetKeyState (VK_CONTROL) & 0x8000) != 0) + currentModifiers |= ModifierKeys::ctrlModifier; + + if ((GetKeyState (VK_MENU) & 0x8000) != 0) + currentModifiers |= ModifierKeys::altModifier; + + if ((GetKeyState (VK_RMENU) & 0x8000) != 0) + currentModifiers &= ~(ModifierKeys::ctrlModifier | ModifierKeys::altModifier); +} + +void ModifierKeys::updateCurrentModifiers() throw() +{ + currentModifierFlags = currentModifiers; +} + +bool KeyPress::isKeyCurrentlyDown (const int keyCode) throw() +{ + SHORT k = (SHORT) keyCode; + + if ((keyCode & extendedKeyModifier) == 0 + && (k >= (SHORT) T('a') && k <= (SHORT) T('z'))) + k += (SHORT) T('A') - (SHORT) T('a'); + + const SHORT translatedValues[] = { (SHORT) ',', VK_OEM_COMMA, + (SHORT) '+', VK_OEM_PLUS, + (SHORT) '-', VK_OEM_MINUS, + (SHORT) '.', VK_OEM_PERIOD, + (SHORT) ';', VK_OEM_1, + (SHORT) ':', VK_OEM_1, + (SHORT) '/', VK_OEM_2, + (SHORT) '?', VK_OEM_2, + (SHORT) '[', VK_OEM_4, + (SHORT) ']', VK_OEM_6 }; + + for (int i = 0; i < numElementsInArray (translatedValues); i += 2) + if (k == translatedValues [i]) + k = translatedValues [i + 1]; + + return (GetKeyState (k) & 0x8000) != 0; +} + +const ModifierKeys ModifierKeys::getCurrentModifiersRealtime() throw() +{ + updateKeyModifiers(); + + currentModifiers &= ~ModifierKeys::allMouseButtonModifiers; + + if ((GetKeyState (VK_LBUTTON) & 0x8000) != 0) + currentModifiers |= ModifierKeys::leftButtonModifier; + + if ((GetKeyState (VK_RBUTTON) & 0x8000) != 0) + currentModifiers |= ModifierKeys::rightButtonModifier; + + if ((GetKeyState (VK_MBUTTON) & 0x8000) != 0) + currentModifiers |= ModifierKeys::middleButtonModifier; + + return ModifierKeys (currentModifiers); +} + +static int64 getMouseEventTime() throw() +{ + static int64 eventTimeOffset = 0; + static DWORD lastMessageTime = 0; + const DWORD thisMessageTime = GetMessageTime(); + + if (thisMessageTime < lastMessageTime || lastMessageTime == 0) + { + lastMessageTime = thisMessageTime; + eventTimeOffset = Time::currentTimeMillis() - thisMessageTime; + } + + return eventTimeOffset + thisMessageTime; +} + +class Win32ComponentPeer : public ComponentPeer +{ +public: + + Win32ComponentPeer (Component* const component, + const int windowStyleFlags) + : ComponentPeer (component, windowStyleFlags), + dontRepaint (false), + fullScreen (false), + isDragging (false), + isMouseOver (false), + currentWindowIcon (0), + taskBarIcon (0), + dropTarget (0) + { + MessageManager::getInstance() + ->callFunctionOnMessageThread (&createWindowCallback, (void*) this); + + setTitle (component->getName()); + + if ((windowStyleFlags & windowHasDropShadow) != 0 + && Desktop::canUseSemiTransparentWindows()) + { + shadower = component->getLookAndFeel().createDropShadowerForComponent (component); + + if (shadower != 0) + shadower->setOwner (component); + } + else + { + shadower = 0; + } + } + + ~Win32ComponentPeer() + { + setTaskBarIcon (0); + deleteAndZero (shadower); + + // do this before the next bit to avoid messages arriving for this window + // before it's destroyed + SetWindowLongPtr (hwnd, GWLP_USERDATA, 0); + + MessageManager::getInstance() + ->callFunctionOnMessageThread (&destroyWindowCallback, (void*) hwnd); + + if (currentWindowIcon != 0) + DestroyIcon (currentWindowIcon); + + if (dropTarget != 0) + { + dropTarget->Release(); + dropTarget = 0; + } + } + + void* getNativeHandle() const + { + return (void*) hwnd; + } + + void setVisible (bool shouldBeVisible) + { + ShowWindow (hwnd, shouldBeVisible ? SW_SHOWNA : SW_HIDE); + + if (shouldBeVisible) + InvalidateRect (hwnd, 0, 0); + else + lastPaintTime = 0; + } + + void setTitle (const String& title) + { + SetWindowText (hwnd, title); + } + + void setPosition (int x, int y) + { + offsetWithinParent (x, y); + SetWindowPos (hwnd, 0, + x - windowBorder.getLeft(), + y - windowBorder.getTop(), + 0, 0, + SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER); + } + + void repaintNowIfTransparent() + { + if (isTransparent() && lastPaintTime > 0 && Time::getMillisecondCounter() > lastPaintTime + 30) + handlePaintMessage(); + } + + void updateBorderSize() + { + WINDOWINFO info; + info.cbSize = sizeof (info); + + if (GetWindowInfo (hwnd, &info)) + { + windowBorder = BorderSize (info.rcClient.top - info.rcWindow.top, + info.rcClient.left - info.rcWindow.left, + info.rcWindow.bottom - info.rcClient.bottom, + info.rcWindow.right - info.rcClient.right); + } + } + + void setSize (int w, int h) + { + SetWindowPos (hwnd, 0, 0, 0, + w + windowBorder.getLeftAndRight(), + h + windowBorder.getTopAndBottom(), + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER); + + updateBorderSize(); + + repaintNowIfTransparent(); + } + + void setBounds (int x, int y, int w, int h, const bool isNowFullScreen) + { + fullScreen = isNowFullScreen; + offsetWithinParent (x, y); + + SetWindowPos (hwnd, 0, + x - windowBorder.getLeft(), + y - windowBorder.getTop(), + w + windowBorder.getLeftAndRight(), + h + windowBorder.getTopAndBottom(), + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER); + + updateBorderSize(); + + repaintNowIfTransparent(); + } + + void getBounds (int& x, int& y, int& w, int& h) const + { + RECT r; + GetWindowRect (hwnd, &r); + + x = r.left; + y = r.top; + w = r.right - x; + h = r.bottom - y; + + HWND parentH = GetParent (hwnd); + if (parentH != 0) + { + GetWindowRect (parentH, &r); + x -= r.left; + y -= r.top; + } + + x += windowBorder.getLeft(); + y += windowBorder.getTop(); + w -= windowBorder.getLeftAndRight(); + h -= windowBorder.getTopAndBottom(); + } + + int getScreenX() const + { + RECT r; + GetWindowRect (hwnd, &r); + return r.left + windowBorder.getLeft(); + } + + int getScreenY() const + { + RECT r; + GetWindowRect (hwnd, &r); + return r.top + windowBorder.getTop(); + } + + void relativePositionToGlobal (int& x, int& y) + { + RECT r; + GetWindowRect (hwnd, &r); + + x += r.left + windowBorder.getLeft(); + y += r.top + windowBorder.getTop(); + } + + void globalPositionToRelative (int& x, int& y) + { + RECT r; + GetWindowRect (hwnd, &r); + + x -= r.left + windowBorder.getLeft(); + y -= r.top + windowBorder.getTop(); + } + + void setMinimised (bool shouldBeMinimised) + { + if (shouldBeMinimised != isMinimised()) + ShowWindow (hwnd, shouldBeMinimised ? SW_MINIMIZE : SW_SHOWNORMAL); + } + + bool isMinimised() const + { + WINDOWPLACEMENT wp; + wp.length = sizeof (WINDOWPLACEMENT); + GetWindowPlacement (hwnd, &wp); + + return wp.showCmd == SW_SHOWMINIMIZED; + } + + void setFullScreen (bool shouldBeFullScreen) + { + setMinimised (false); + + if (fullScreen != shouldBeFullScreen) + { + fullScreen = shouldBeFullScreen; + const ComponentDeletionWatcher deletionChecker (component); + + if (! fullScreen) + { + const Rectangle boundsCopy (lastNonFullscreenBounds); + + if (hasTitleBar()) + ShowWindow (hwnd, SW_SHOWNORMAL); + + if (! boundsCopy.isEmpty()) + { + setBounds (boundsCopy.getX(), + boundsCopy.getY(), + boundsCopy.getWidth(), + boundsCopy.getHeight(), + false); + } + } + else + { + if (hasTitleBar()) + ShowWindow (hwnd, SW_SHOWMAXIMIZED); + else + SendMessageW (hwnd, WM_SETTINGCHANGE, 0, 0); + } + + if (! deletionChecker.hasBeenDeleted()) + handleMovedOrResized(); + } + } + + bool isFullScreen() const + { + if (! hasTitleBar()) + return fullScreen; + + WINDOWPLACEMENT wp; + wp.length = sizeof (wp); + GetWindowPlacement (hwnd, &wp); + + return wp.showCmd == SW_SHOWMAXIMIZED; + } + + bool contains (int x, int y, bool trueIfInAChildWindow) const + { + RECT r; + GetWindowRect (hwnd, &r); + + POINT p; + p.x = x + r.left + windowBorder.getLeft(); + p.y = y + r.top + windowBorder.getTop(); + + HWND w = WindowFromPoint (p); + + return w == hwnd || (trueIfInAChildWindow && (IsChild (hwnd, w) != 0)); + } + + const BorderSize getFrameSize() const + { + return windowBorder; + } + + bool setAlwaysOnTop (bool alwaysOnTop) + { + const bool oldDeactivate = shouldDeactivateTitleBar; + shouldDeactivateTitleBar = ((styleFlags & windowIsTemporary) == 0); + + SetWindowPos (hwnd, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST, + 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOSENDCHANGING); + + shouldDeactivateTitleBar = oldDeactivate; + + if (shadower != 0) + shadower->componentBroughtToFront (*component); + + return true; + } + + void toFront (bool makeActive) + { + setMinimised (false); + + const bool oldDeactivate = shouldDeactivateTitleBar; + shouldDeactivateTitleBar = ((styleFlags & windowIsTemporary) == 0); + + MessageManager::getInstance() + ->callFunctionOnMessageThread (makeActive ? &toFrontCallback1 + : &toFrontCallback2, + (void*) hwnd); + + shouldDeactivateTitleBar = oldDeactivate; + + if (! makeActive) + { + // in this case a broughttofront call won't have occured, so do it now.. + handleBroughtToFront(); + } + } + + void toBehind (ComponentPeer* other) + { + Win32ComponentPeer* const otherPeer = dynamic_cast (other); + + jassert (otherPeer != 0); // wrong type of window? + + if (otherPeer != 0) + { + setMinimised (false); + + SetWindowPos (hwnd, otherPeer->hwnd, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOSENDCHANGING); + } + } + + bool isFocused() const + { + return MessageManager::getInstance() + ->callFunctionOnMessageThread (&getFocusCallback, 0) == (void*) hwnd; + } + + void grabFocus() + { + const bool oldDeactivate = shouldDeactivateTitleBar; + shouldDeactivateTitleBar = ((styleFlags & windowIsTemporary) == 0); + + MessageManager::getInstance() + ->callFunctionOnMessageThread (&setFocusCallback, (void*) hwnd); + + shouldDeactivateTitleBar = oldDeactivate; + } + + void repaint (int x, int y, int w, int h) + { + const RECT r = { x, y, x + w, y + h }; + InvalidateRect (hwnd, &r, FALSE); + } + + void performAnyPendingRepaintsNow() + { + MSG m; + if (component->isVisible() && PeekMessage (&m, hwnd, WM_PAINT, WM_PAINT, PM_REMOVE)) + DispatchMessage (&m); + } + + static Win32ComponentPeer* getOwnerOfWindow (HWND h) throw() + { + if (h != 0 && GetWindowLongPtr (h, GWLP_USERDATA) == improbableWindowNumber) + return (Win32ComponentPeer*) GetWindowLongPtr (h, 8); + + return 0; + } + + void setTaskBarIcon (const Image* const image) + { + if (image != 0) + { + HICON hicon = createHICONFromImage (*image, TRUE, 0, 0); + + if (taskBarIcon == 0) + { + taskBarIcon = new NOTIFYICONDATA(); + taskBarIcon->cbSize = sizeof (NOTIFYICONDATA); + taskBarIcon->hWnd = (HWND) hwnd; + taskBarIcon->uID = (int) (pointer_sized_int) hwnd; + taskBarIcon->uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; + taskBarIcon->uCallbackMessage = WM_TRAYNOTIFY; + taskBarIcon->hIcon = hicon; + taskBarIcon->szTip[0] = 0; + + Shell_NotifyIcon (NIM_ADD, taskBarIcon); + } + else + { + HICON oldIcon = taskBarIcon->hIcon; + + taskBarIcon->hIcon = hicon; + taskBarIcon->uFlags = NIF_ICON; + Shell_NotifyIcon (NIM_MODIFY, taskBarIcon); + + DestroyIcon (oldIcon); + } + + DestroyIcon (hicon); + } + else if (taskBarIcon != 0) + { + taskBarIcon->uFlags = 0; + Shell_NotifyIcon (NIM_DELETE, taskBarIcon); + DestroyIcon (taskBarIcon->hIcon); + deleteAndZero (taskBarIcon); + } + } + + void setTaskBarIconToolTip (const String& toolTip) const + { + if (taskBarIcon != 0) + { + taskBarIcon->uFlags = NIF_TIP; + toolTip.copyToBuffer (taskBarIcon->szTip, sizeof (taskBarIcon->szTip) - 1); + Shell_NotifyIcon (NIM_MODIFY, taskBarIcon); + } + } + + juce_UseDebuggingNewOperator + + bool dontRepaint; + +private: + HWND hwnd; + DropShadower* shadower; + bool fullScreen, isDragging, isMouseOver; + BorderSize windowBorder; + HICON currentWindowIcon; + NOTIFYICONDATA* taskBarIcon; + IDropTarget* dropTarget; + + class TemporaryImage : public Timer + { + public: + + TemporaryImage() + : image (0) + { + } + + ~TemporaryImage() + { + delete image; + } + + WindowsBitmapImage* getImage (const bool transparent, const int w, const int h) throw() + { + const Image::PixelFormat format = transparent ? Image::ARGB : Image::RGB; + + if (image == 0 || image->getWidth() < w || image->getHeight() < h || image->getFormat() != format) + { + delete image; + image = new WindowsBitmapImage (format, (w + 31) & ~31, (h + 31) & ~31, false); + } + + startTimer (3000); + return image; + } + + void timerCallback() + { + stopTimer(); + deleteAndZero (image); + } + + private: + WindowsBitmapImage* image; + + TemporaryImage (const TemporaryImage&); + const TemporaryImage& operator= (const TemporaryImage&); + }; + + TemporaryImage offscreenImageGenerator; + + class WindowClassHolder : public DeletedAtShutdown + { + public: + WindowClassHolder() + : windowClassName ("JUCE_") + { + // this name has to be different for each app/dll instance because otherwise + // poor old Win32 can get a bit confused (even despite it not being a process-global + // window class). + windowClassName << (int) (Time::currentTimeMillis() & 0x7fffffff); + + HINSTANCE moduleHandle = (HINSTANCE) PlatformUtilities::getCurrentModuleInstanceHandle(); + + TCHAR moduleFile [1024]; + moduleFile[0] = 0; + GetModuleFileName (moduleHandle, moduleFile, 1024); + WORD iconNum = 0; + + WNDCLASSEX wcex; + wcex.cbSize = sizeof (wcex); + wcex.style = CS_OWNDC; + wcex.lpfnWndProc = (WNDPROC) windowProc; + wcex.lpszClassName = windowClassName; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 32; + wcex.hInstance = moduleHandle; + wcex.hIcon = ExtractAssociatedIcon (moduleHandle, moduleFile, &iconNum); + iconNum = 1; + wcex.hIconSm = ExtractAssociatedIcon (moduleHandle, moduleFile, &iconNum); + wcex.hCursor = 0; + wcex.hbrBackground = 0; + wcex.lpszMenuName = 0; + + RegisterClassEx (&wcex); + } + + ~WindowClassHolder() + { + if (ComponentPeer::getNumPeers() == 0) + UnregisterClass (windowClassName, (HINSTANCE) PlatformUtilities::getCurrentModuleInstanceHandle()); + + clearSingletonInstance(); + } + + String windowClassName; + + juce_DeclareSingleton_SingleThreaded_Minimal (WindowClassHolder); + }; + + static void* createWindowCallback (void* userData) + { + ((Win32ComponentPeer*) userData)->createWindow(); + return 0; + } + + void createWindow() + { + DWORD exstyle = WS_EX_ACCEPTFILES; + DWORD type = WS_CLIPSIBLINGS | WS_CLIPCHILDREN; + + if (hasTitleBar()) + { + type |= WS_OVERLAPPED; + exstyle |= WS_EX_APPWINDOW; + + if ((styleFlags & windowHasCloseButton) != 0) + { + type |= WS_SYSMENU; + } + else + { + // annoyingly, windows won't let you have a min/max button without a close button + jassert ((styleFlags & (windowHasMinimiseButton | windowHasMaximiseButton)) == 0); + } + + if ((styleFlags & windowIsResizable) != 0) + type |= WS_THICKFRAME; + } + else + { + type |= WS_POPUP | WS_SYSMENU; + + if ((styleFlags & windowAppearsOnTaskbar) == 0) + exstyle |= WS_EX_TOOLWINDOW; + else + exstyle |= WS_EX_APPWINDOW; + } + + if ((styleFlags & windowHasMinimiseButton) != 0) + type |= WS_MINIMIZEBOX; + + if ((styleFlags & windowHasMaximiseButton) != 0) + type |= WS_MAXIMIZEBOX; + + if ((styleFlags & windowIgnoresMouseClicks) != 0) + exstyle |= WS_EX_TRANSPARENT; + + if ((styleFlags & windowIsSemiTransparent) != 0 + && Desktop::canUseSemiTransparentWindows()) + exstyle |= WS_EX_LAYERED; + + hwnd = CreateWindowEx (exstyle, WindowClassHolder::getInstance()->windowClassName, L"", type, 0, 0, 0, 0, 0, 0, 0, 0); + + if (hwnd != 0) + { + SetWindowLongPtr (hwnd, 0, 0); + SetWindowLongPtr (hwnd, 8, (LONG_PTR) this); + SetWindowLongPtr (hwnd, GWLP_USERDATA, improbableWindowNumber); + + if (dropTarget == 0) + dropTarget = new JuceDropTarget (this); + + RegisterDragDrop (hwnd, dropTarget); + + updateBorderSize(); + + // Calling this function here is (for some reason) necessary to make Windows + // correctly enable the menu items that we specify in the wm_initmenu message. + GetSystemMenu (hwnd, false); + } + else + { + jassertfalse + } + } + + static void* destroyWindowCallback (void* handle) + { + RevokeDragDrop ((HWND) handle); + DestroyWindow ((HWND) handle); + return 0; + } + + static void* toFrontCallback1 (void* h) + { + SetForegroundWindow ((HWND) h); + return 0; + } + + static void* toFrontCallback2 (void* h) + { + SetWindowPos ((HWND) h, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOSENDCHANGING); + return 0; + } + + static void* setFocusCallback (void* h) + { + SetFocus ((HWND) h); + return 0; + } + + static void* getFocusCallback (void*) + { + return (void*) GetFocus(); + } + + void offsetWithinParent (int& x, int& y) const + { + if (isTransparent()) + { + HWND parentHwnd = GetParent (hwnd); + + if (parentHwnd != 0) + { + RECT parentRect; + GetWindowRect (parentHwnd, &parentRect); + x += parentRect.left; + y += parentRect.top; + } + } + } + + bool isTransparent() const + { + return (GetWindowLong (hwnd, GWL_EXSTYLE) & WS_EX_LAYERED) != 0; + } + + inline bool hasTitleBar() const throw() { return (styleFlags & windowHasTitleBar) != 0; } + + void setIcon (const Image& newIcon) + { + HICON hicon = createHICONFromImage (newIcon, TRUE, 0, 0); + + if (hicon != 0) + { + SendMessage (hwnd, WM_SETICON, ICON_BIG, (LPARAM) hicon); + SendMessage (hwnd, WM_SETICON, ICON_SMALL, (LPARAM) hicon); + + if (currentWindowIcon != 0) + DestroyIcon (currentWindowIcon); + + currentWindowIcon = hicon; + } + } + + void handlePaintMessage() + { +#if DEBUG_REPAINT_TIMES + const double paintStart = Time::getMillisecondCounterHiRes(); +#endif + HRGN rgn = CreateRectRgn (0, 0, 0, 0); + const int regionType = GetUpdateRgn (hwnd, rgn, false); + + PAINTSTRUCT paintStruct; + HDC dc = BeginPaint (hwnd, &paintStruct); // Note this can immediately generate a WM_NCPAINT + // message and become re-entrant, but that's OK + + // if something in a paint handler calls, e.g. a message box, this can become reentrant and + // corrupt the image it's using to paint into, so do a check here. + static bool reentrant = false; + if (reentrant) + { + DeleteObject (rgn); + EndPaint (hwnd, &paintStruct); + return; + } + + reentrant = true; + + // this is the rectangle to update.. + int x = paintStruct.rcPaint.left; + int y = paintStruct.rcPaint.top; + int w = paintStruct.rcPaint.right - x; + int h = paintStruct.rcPaint.bottom - y; + + const bool transparent = isTransparent(); + + if (transparent) + { + // it's not possible to have a transparent window with a title bar at the moment! + jassert (! hasTitleBar()); + + RECT r; + GetWindowRect (hwnd, &r); + x = y = 0; + w = r.right - r.left; + h = r.bottom - r.top; + } + + if (w > 0 && h > 0) + { + clearMaskedRegion(); + + WindowsBitmapImage* const offscreenImage = offscreenImageGenerator.getImage (transparent, w, h); + + LowLevelGraphicsSoftwareRenderer context (*offscreenImage); + + RectangleList* const contextClip = context.getRawClipRegion(); + contextClip->clear(); + + context.setOrigin (-x, -y); + + bool needToPaintAll = true; + + if (regionType == COMPLEXREGION && ! transparent) + { + HRGN clipRgn = CreateRectRgnIndirect (&paintStruct.rcPaint); + CombineRgn (rgn, rgn, clipRgn, RGN_AND); + DeleteObject (clipRgn); + + char rgnData [8192]; + const DWORD res = GetRegionData (rgn, sizeof (rgnData), (RGNDATA*) rgnData); + + if (res > 0 && res <= sizeof (rgnData)) + { + const RGNDATAHEADER* const hdr = &(((const RGNDATA*) rgnData)->rdh); + + if (hdr->iType == RDH_RECTANGLES + && hdr->rcBound.right - hdr->rcBound.left >= w + && hdr->rcBound.bottom - hdr->rcBound.top >= h) + { + needToPaintAll = false; + + const RECT* rects = (const RECT*) (rgnData + sizeof (RGNDATAHEADER)); + int num = ((RGNDATA*) rgnData)->rdh.nCount; + + while (--num >= 0) + { + // (need to move this one pixel to the left because of a win32 bug) + const int cx = jmax (x, rects->left - 1); + const int cy = rects->top; + const int cw = rects->right - cx; + const int ch = rects->bottom - rects->top; + + if (cx + cw - x <= w && cy + ch - y <= h) + { + contextClip->addWithoutMerging (Rectangle (cx - x, cy - y, cw, ch)); + } + else + { + needToPaintAll = true; + break; + } + + ++rects; + } + } + } + } + + if (needToPaintAll) + { + contextClip->clear(); + contextClip->addWithoutMerging (Rectangle (0, 0, w, h)); + } + + if (transparent) + { + RectangleList::Iterator i (*contextClip); + + while (i.next()) + { + const Rectangle& r = *i.getRectangle(); + offscreenImage->clear (r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + } + + // if the component's not opaque, this won't draw properly unless the platform can support this + jassert (Desktop::canUseSemiTransparentWindows() || component->isOpaque()); + + updateCurrentModifiers(); + + handlePaint (context); + + if (! dontRepaint) + offscreenImage->blitToWindow (hwnd, dc, transparent, x, y, maskedRegion); + } + + DeleteObject (rgn); + EndPaint (hwnd, &paintStruct); + reentrant = false; + +#ifndef JUCE_GCC //xxx should add this fn for gcc.. + _fpreset(); // because some graphics cards can unmask FP exceptions +#endif + + lastPaintTime = Time::getMillisecondCounter(); + +#if DEBUG_REPAINT_TIMES + const double elapsed = Time::getMillisecondCounterHiRes() - paintStart; + Logger::outputDebugString (T("repaint time: ") + String (elapsed, 2)); +#endif + } + + void doMouseMove (const int x, const int y) + { + static uint32 lastMouseTime = 0; + // this can be set to throttle the mouse-messages to less than a + // certain number per second, as things can get unresponsive + // if each drag or move callback has to do a lot of work. + const int maxMouseMovesPerSecond = 60; + + const int64 mouseEventTime = getMouseEventTime(); + + if (! isMouseOver) + { + isMouseOver = true; + + TRACKMOUSEEVENT tme; + tme.cbSize = sizeof (tme); + tme.dwFlags = TME_LEAVE; + tme.hwndTrack = hwnd; + tme.dwHoverTime = 0; + + if (! TrackMouseEvent (&tme)) + { + jassertfalse; + } + + updateKeyModifiers(); + handleMouseEnter (x, y, mouseEventTime); + } + else if (! isDragging) + { + if (((unsigned int) x) < (unsigned int) component->getWidth() + && ((unsigned int) y) < (unsigned int) component->getHeight()) + { + RECT r; + GetWindowRect (hwnd, &r); + + POINT p; + p.x = x + r.left + windowBorder.getLeft(); + p.y = y + r.top + windowBorder.getTop(); + + if (WindowFromPoint (p) == hwnd) + { + const uint32 now = Time::getMillisecondCounter(); + + if (now > lastMouseTime + 1000 / maxMouseMovesPerSecond) + { + lastMouseTime = now; + handleMouseMove (x, y, mouseEventTime); + } + } + } + } + else + { + const uint32 now = Time::getMillisecondCounter(); + + if (now > lastMouseTime + 1000 / maxMouseMovesPerSecond) + { + lastMouseTime = now; + handleMouseDrag (x, y, mouseEventTime); + } + } + } + + void doMouseDown (const int x, const int y, const WPARAM wParam) + { + if (GetCapture() != hwnd) + SetCapture (hwnd); + + doMouseMove (x, y); + + currentModifiers &= ~ModifierKeys::allMouseButtonModifiers; + + if ((wParam & MK_LBUTTON) != 0) + currentModifiers |= ModifierKeys::leftButtonModifier; + + if ((wParam & MK_RBUTTON) != 0) + currentModifiers |= ModifierKeys::rightButtonModifier; + + if ((wParam & MK_MBUTTON) != 0) + currentModifiers |= ModifierKeys::middleButtonModifier; + + updateKeyModifiers(); + isDragging = true; + + handleMouseDown (x, y, getMouseEventTime()); + } + + void doMouseUp (const int x, const int y, const WPARAM wParam) + { + int numButtons = 0; + + if ((wParam & MK_LBUTTON) != 0) + ++numButtons; + + if ((wParam & MK_RBUTTON) != 0) + ++numButtons; + + if ((wParam & MK_MBUTTON) != 0) + ++numButtons; + + const int oldModifiers = currentModifiers; + + // update the currentmodifiers only after the callback, so the callback + // knows which button was released. + currentModifiers &= ~ModifierKeys::allMouseButtonModifiers; + + if ((wParam & MK_LBUTTON) != 0) + currentModifiers |= ModifierKeys::leftButtonModifier; + + if ((wParam & MK_RBUTTON) != 0) + currentModifiers |= ModifierKeys::rightButtonModifier; + + if ((wParam & MK_MBUTTON) != 0) + currentModifiers |= ModifierKeys::middleButtonModifier; + + updateKeyModifiers(); + isDragging = false; + + // release the mouse capture if the user's not still got a button down + if (numButtons == 0 && hwnd == GetCapture()) + ReleaseCapture(); + + handleMouseUp (oldModifiers, x, y, getMouseEventTime()); + } + + void doCaptureChanged() + { + if (isDragging) + { + RECT wr; + GetWindowRect (hwnd, &wr); + + const DWORD mp = GetMessagePos(); + + doMouseUp (GET_X_LPARAM (mp) - wr.left - windowBorder.getLeft(), + GET_Y_LPARAM (mp) - wr.top - windowBorder.getTop(), + getMouseEventTime()); + } + } + + void doMouseExit() + { + if (isMouseOver) + { + isMouseOver = false; + RECT wr; + GetWindowRect (hwnd, &wr); + + const DWORD mp = GetMessagePos(); + + handleMouseExit (GET_X_LPARAM (mp) - wr.left - windowBorder.getLeft(), + GET_Y_LPARAM (mp) - wr.top - windowBorder.getTop(), + getMouseEventTime()); + } + } + + void doMouseWheel (const WPARAM wParam, const bool isVertical) + { + updateKeyModifiers(); + + const int amount = jlimit (-1000, 1000, (int) (0.75f * (short) HIWORD (wParam))); + + handleMouseWheel (isVertical ? 0 : amount, + isVertical ? amount : 0, + getMouseEventTime()); + } + + void sendModifierKeyChangeIfNeeded() + { + if (modifiersAtLastCallback != currentModifiers) + { + modifiersAtLastCallback = currentModifiers; + handleModifierKeysChange(); + } + } + + bool doKeyUp (const WPARAM key) + { + updateKeyModifiers(); + + switch (key) + { + case VK_SHIFT: + case VK_CONTROL: + case VK_MENU: + case VK_CAPITAL: + case VK_LWIN: + case VK_RWIN: + case VK_APPS: + case VK_NUMLOCK: + case VK_SCROLL: + case VK_LSHIFT: + case VK_RSHIFT: + case VK_LCONTROL: + case VK_LMENU: + case VK_RCONTROL: + case VK_RMENU: + sendModifierKeyChangeIfNeeded(); + } + + return handleKeyUpOrDown(); + } + + bool doKeyDown (const WPARAM key) + { + updateKeyModifiers(); + bool used = false; + + switch (key) + { + case VK_SHIFT: + case VK_LSHIFT: + case VK_RSHIFT: + case VK_CONTROL: + case VK_LCONTROL: + case VK_RCONTROL: + case VK_MENU: + case VK_LMENU: + case VK_RMENU: + case VK_LWIN: + case VK_RWIN: + case VK_CAPITAL: + case VK_NUMLOCK: + case VK_SCROLL: + case VK_APPS: + sendModifierKeyChangeIfNeeded(); + break; + + case VK_LEFT: + case VK_RIGHT: + case VK_UP: + case VK_DOWN: + case VK_PRIOR: + case VK_NEXT: + case VK_HOME: + case VK_END: + case VK_DELETE: + case VK_INSERT: + case VK_F1: + case VK_F2: + case VK_F3: + case VK_F4: + case VK_F5: + case VK_F6: + case VK_F7: + case VK_F8: + case VK_F9: + case VK_F10: + case VK_F11: + case VK_F12: + case VK_F13: + case VK_F14: + case VK_F15: + case VK_F16: + used = handleKeyUpOrDown(); + used = handleKeyPress (extendedKeyModifier | (int) key, 0) || used; + break; + + case VK_ADD: + case VK_SUBTRACT: + case VK_MULTIPLY: + case VK_DIVIDE: + case VK_SEPARATOR: + case VK_DECIMAL: + used = handleKeyUpOrDown(); + break; + + default: + used = handleKeyUpOrDown(); + + { + MSG msg; + + if (! PeekMessage (&msg, hwnd, WM_CHAR, WM_DEADCHAR, PM_NOREMOVE)) + { + // if there isn't a WM_CHAR or WM_DEADCHAR message pending, we need to + // manually generate the key-press event that matches this key-down. + + const UINT keyChar = MapVirtualKey (key, 2); + used = handleKeyPress ((int) LOWORD (keyChar), 0) || used; + } + } + + break; + } + + return used; + } + + bool doKeyChar (int key, const LPARAM flags) + { + updateKeyModifiers(); + + juce_wchar textChar = (juce_wchar) key; + + const int virtualScanCode = (flags >> 16) & 0xff; + + if (key >= '0' && key <= '9') + { + switch (virtualScanCode) // check for a numeric keypad scan-code + { + case 0x52: + case 0x4f: + case 0x50: + case 0x51: + case 0x4b: + case 0x4c: + case 0x4d: + case 0x47: + case 0x48: + case 0x49: + key = (key - '0') + KeyPress::numberPad0; + break; + default: + break; + } + } + else + { + // convert the scan code to an unmodified character code.. + const UINT virtualKey = MapVirtualKey (virtualScanCode, 1); + UINT keyChar = MapVirtualKey (virtualKey, 2); + + keyChar = LOWORD (keyChar); + + if (keyChar != 0) + key = (int) keyChar; + + // avoid sending junk text characters for some control-key combinations + if (textChar < ' ' && (currentModifiers & (ModifierKeys::ctrlModifier | ModifierKeys::altModifier)) != 0) + textChar = 0; + } + + return handleKeyPress (key, textChar); + } + + bool doAppCommand (const LPARAM lParam) + { + int key = 0; + + switch (GET_APPCOMMAND_LPARAM (lParam)) + { + case APPCOMMAND_MEDIA_PLAY_PAUSE: + key = KeyPress::playKey; + break; + + case APPCOMMAND_MEDIA_STOP: + key = KeyPress::stopKey; + break; + + case APPCOMMAND_MEDIA_NEXTTRACK: + key = KeyPress::fastForwardKey; + break; + + case APPCOMMAND_MEDIA_PREVIOUSTRACK: + key = KeyPress::rewindKey; + break; + } + + if (key != 0) + { + updateKeyModifiers(); + + if (hwnd == GetActiveWindow()) + { + handleKeyPress (key, 0); + return true; + } + } + + return false; + } + + class JuceDropTarget : public IDropTarget + { + public: + JuceDropTarget (Win32ComponentPeer* const owner_) + : owner (owner_), + refCount (1) + { + } + + virtual ~JuceDropTarget() + { + jassert (refCount == 0); + } + + HRESULT __stdcall QueryInterface (REFIID id, void __RPC_FAR* __RPC_FAR* result) + { + if (id == IID_IUnknown || id == IID_IDropTarget) + { + AddRef(); + *result = this; + return S_OK; + } + + *result = 0; + return E_NOINTERFACE; + } + + ULONG __stdcall AddRef() { return ++refCount; } + ULONG __stdcall Release() { jassert (refCount > 0); const int r = --refCount; if (r == 0) delete this; return r; } + + HRESULT __stdcall DragEnter (IDataObject* pDataObject, DWORD /*grfKeyState*/, POINTL mousePos, DWORD* pdwEffect) + { + updateFileList (pDataObject); + int x = mousePos.x, y = mousePos.y; + owner->globalPositionToRelative (x, y); + owner->handleFileDragMove (files, x, y); + *pdwEffect = DROPEFFECT_COPY; + return S_OK; + } + + HRESULT __stdcall DragLeave() + { + owner->handleFileDragExit (files); + return S_OK; + } + + HRESULT __stdcall DragOver (DWORD /*grfKeyState*/, POINTL mousePos, DWORD* pdwEffect) + { + int x = mousePos.x, y = mousePos.y; + owner->globalPositionToRelative (x, y); + owner->handleFileDragMove (files, x, y); + *pdwEffect = DROPEFFECT_COPY; + return S_OK; + } + + HRESULT __stdcall Drop (IDataObject* pDataObject, DWORD /*grfKeyState*/, POINTL mousePos, DWORD* pdwEffect) + { + updateFileList (pDataObject); + int x = mousePos.x, y = mousePos.y; + owner->globalPositionToRelative (x, y); + owner->handleFileDragDrop (files, x, y); + *pdwEffect = DROPEFFECT_COPY; + return S_OK; + } + + private: + Win32ComponentPeer* const owner; + int refCount; + StringArray files; + + void updateFileList (IDataObject* const pDataObject) + { + files.clear(); + + FORMATETC format = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; + STGMEDIUM medium = { TYMED_HGLOBAL, { 0 }, 0 }; + + if (pDataObject->GetData (&format, &medium) == S_OK) + { + const SIZE_T totalLen = GlobalSize (medium.hGlobal); + const LPDROPFILES pDropFiles = (const LPDROPFILES) GlobalLock (medium.hGlobal); + unsigned int i = 0; + + if (pDropFiles->fWide) + { + const WCHAR* const fname = (WCHAR*) (((const char*) pDropFiles) + sizeof (DROPFILES)); + + for (;;) + { + unsigned int len = 0; + while (i + len < totalLen && fname [i + len] != 0) + ++len; + + if (len == 0) + break; + + files.add (String (fname + i, len)); + i += len + 1; + } + } + else + { + const char* const fname = ((const char*) pDropFiles) + sizeof (DROPFILES); + + for (;;) + { + unsigned int len = 0; + while (i + len < totalLen && fname [i + len] != 0) + ++len; + + if (len == 0) + break; + + files.add (String (fname + i, len)); + i += len + 1; + } + } + + GlobalUnlock (medium.hGlobal); + } + } + + JuceDropTarget (const JuceDropTarget&); + const JuceDropTarget& operator= (const JuceDropTarget&); + }; + + void doSettingChange() + { + Desktop::getInstance().refreshMonitorSizes(); + + if (fullScreen && ! isMinimised()) + { + const Rectangle r (component->getParentMonitorArea()); + + SetWindowPos (hwnd, 0, + r.getX(), r.getY(), r.getWidth(), r.getHeight(), + SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOSENDCHANGING); + } + } + +public: + static LRESULT CALLBACK windowProc (HWND h, UINT message, WPARAM wParam, LPARAM lParam) + { + Win32ComponentPeer* const peer = getOwnerOfWindow (h); + + if (peer != 0) + return peer->peerWindowProc (h, message, wParam, lParam); + + return DefWindowProc (h, message, wParam, lParam); + } + +private: + LRESULT peerWindowProc (HWND h, UINT message, WPARAM wParam, LPARAM lParam) + { + { + const MessageManagerLock messLock; + + if (isValidPeer (this)) + { + switch (message) + { + case WM_NCHITTEST: + if (hasTitleBar()) + break; + + return HTCLIENT; + + case WM_PAINT: + handlePaintMessage(); + return 0; + + case WM_NCPAINT: + if (wParam != 1) + handlePaintMessage(); + + if (hasTitleBar()) + break; + + return 0; + + case WM_ERASEBKGND: + case WM_NCCALCSIZE: + if (hasTitleBar()) + break; + + return 1; + + case WM_MOUSEMOVE: + doMouseMove (GET_X_LPARAM (lParam), GET_Y_LPARAM (lParam)); + return 0; + + case WM_MOUSELEAVE: + doMouseExit(); + return 0; + + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_RBUTTONDOWN: + doMouseDown (GET_X_LPARAM (lParam), GET_Y_LPARAM (lParam), wParam); + return 0; + + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: + doMouseUp (GET_X_LPARAM (lParam), GET_Y_LPARAM (lParam), wParam); + return 0; + + case WM_CAPTURECHANGED: + doCaptureChanged(); + return 0; + + case WM_NCMOUSEMOVE: + if (hasTitleBar()) + break; + + return 0; + + case 0x020A: /* WM_MOUSEWHEEL */ + doMouseWheel (wParam, true); + return 0; + + case 0x020E: /* WM_MOUSEHWHEEL */ + doMouseWheel (wParam, false); + return 0; + + case WM_WINDOWPOSCHANGING: + if ((styleFlags & (windowHasTitleBar | windowIsResizable)) == (windowHasTitleBar | windowIsResizable)) + { + WINDOWPOS* const wp = (WINDOWPOS*) lParam; + + if ((wp->flags & (SWP_NOMOVE | SWP_NOSIZE)) != (SWP_NOMOVE | SWP_NOSIZE)) + { + if (constrainer != 0) + { + const Rectangle current (component->getX() - windowBorder.getLeft(), + component->getY() - windowBorder.getTop(), + component->getWidth() + windowBorder.getLeftAndRight(), + component->getHeight() + windowBorder.getTopAndBottom()); + + constrainer->checkBounds (wp->x, wp->y, wp->cx, wp->cy, + current, + Desktop::getInstance().getAllMonitorDisplayAreas().getBounds(), + wp->y != current.getY() && wp->y + wp->cy == current.getBottom(), + wp->x != current.getX() && wp->x + wp->cx == current.getRight(), + wp->y == current.getY() && wp->y + wp->cy != current.getBottom(), + wp->x == current.getX() && wp->x + wp->cx != current.getRight()); + } + } + } + + return 0; + + case WM_WINDOWPOSCHANGED: + handleMovedOrResized(); + + if (dontRepaint) + break; // needed for non-accelerated openGL windows to draw themselves correctly.. + else + return 0; + + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + if (doKeyDown (wParam)) + return 0; + + break; + + case WM_KEYUP: + case WM_SYSKEYUP: + if (doKeyUp (wParam)) + return 0; + + break; + + case WM_CHAR: + if (doKeyChar ((int) wParam, lParam)) + return 0; + + break; + + case WM_APPCOMMAND: + if (doAppCommand (lParam)) + return TRUE; + + break; + + case WM_SETFOCUS: + updateKeyModifiers(); + handleFocusGain(); + break; + + case WM_KILLFOCUS: + handleFocusLoss(); + break; + + case WM_ACTIVATEAPP: + // Windows does weird things to process priority when you swap apps, + // so this forces an update when the app is brought to the front + if (wParam != FALSE) + juce_repeatLastProcessPriority(); + + juce_CheckCurrentlyFocusedTopLevelWindow(); + modifiersAtLastCallback = -1; + return 0; + + case WM_ACTIVATE: + if (LOWORD (wParam) == WA_ACTIVE || LOWORD (wParam) == WA_CLICKACTIVE) + { + modifiersAtLastCallback = -1; + updateKeyModifiers(); + + if (isMinimised()) + { + component->repaint(); + handleMovedOrResized(); + + if (! isValidMessageListener()) + return 0; + } + + if (LOWORD (wParam) == WA_CLICKACTIVE + && component->isCurrentlyBlockedByAnotherModalComponent()) + { + int mx, my; + component->getMouseXYRelative (mx, my); + Component* const underMouse = component->getComponentAt (mx, my); + + if (underMouse != 0 && underMouse->isCurrentlyBlockedByAnotherModalComponent()) + Component::getCurrentlyModalComponent()->inputAttemptWhenModal(); + + return 0; + } + + handleBroughtToFront(); + return 0; + } + + break; + + case WM_NCACTIVATE: + // while a temporary window is being shown, prevent Windows from deactivating the + // title bars of our main windows. + if (wParam == 0 && ! shouldDeactivateTitleBar) + wParam = TRUE; // change this and let it get passed to the DefWindowProc. + + break; + + case WM_MOUSEACTIVATE: + if (! component->getMouseClickGrabsKeyboardFocus()) + return MA_NOACTIVATE; + + break; + + case WM_SHOWWINDOW: + if (wParam != 0) + handleBroughtToFront(); + + break; + + case WM_CLOSE: + handleUserClosingWindow(); + return 0; + + case WM_QUIT: + JUCEApplication::quit(); + return 0; + + case WM_TRAYNOTIFY: + if (component->isCurrentlyBlockedByAnotherModalComponent()) + { + if (lParam == WM_LBUTTONDOWN || lParam == WM_RBUTTONDOWN + || lParam == WM_LBUTTONDBLCLK || lParam == WM_LBUTTONDBLCLK) + { + Component* const current = Component::getCurrentlyModalComponent(); + + if (current != 0) + current->inputAttemptWhenModal(); + } + } + else + { + const int oldModifiers = currentModifiers; + + MouseEvent e (0, 0, ModifierKeys::getCurrentModifiersRealtime(), component, + getMouseEventTime(), 0, 0, getMouseEventTime(), 1, false); + + if (lParam == WM_LBUTTONDOWN || lParam == WM_LBUTTONDBLCLK) + e.mods = ModifierKeys (e.mods.getRawFlags() | ModifierKeys::leftButtonModifier); + else if (lParam == WM_RBUTTONDOWN || lParam == WM_RBUTTONDBLCLK) + e.mods = ModifierKeys (e.mods.getRawFlags() | ModifierKeys::rightButtonModifier); + + if (lParam == WM_LBUTTONDOWN || lParam == WM_RBUTTONDOWN) + { + SetFocus (hwnd); + SetForegroundWindow (hwnd); + + component->mouseDown (e); + } + else if (lParam == WM_LBUTTONUP || lParam == WM_RBUTTONUP) + { + e.mods = ModifierKeys (oldModifiers); + component->mouseUp (e); + } + else if (lParam == WM_LBUTTONDBLCLK || lParam == WM_LBUTTONDBLCLK) + { + e.mods = ModifierKeys (oldModifiers); + component->mouseDoubleClick (e); + } + else if (lParam == WM_MOUSEMOVE) + { + component->mouseMove (e); + } + } + + break; + + case WM_SYNCPAINT: + return 0; + + case WM_PALETTECHANGED: + InvalidateRect (h, 0, 0); + break; + + case WM_DISPLAYCHANGE: + InvalidateRect (h, 0, 0); + createPaletteIfNeeded = true; + // intentional fall-through... + case WM_SETTINGCHANGE: // note the fall-through in the previous case! + doSettingChange(); + break; + + case WM_INITMENU: + if (! hasTitleBar()) + { + if (isFullScreen()) + { + EnableMenuItem ((HMENU) wParam, SC_RESTORE, MF_BYCOMMAND | MF_ENABLED); + EnableMenuItem ((HMENU) wParam, SC_MOVE, MF_BYCOMMAND | MF_GRAYED); + } + else if (! isMinimised()) + { + EnableMenuItem ((HMENU) wParam, SC_MAXIMIZE, MF_BYCOMMAND | MF_GRAYED); + } + } + break; + + case WM_SYSCOMMAND: + switch (wParam & 0xfff0) + { + case SC_CLOSE: + if (hasTitleBar()) + { + PostMessage (h, WM_CLOSE, 0, 0); + return 0; + } + break; + + case SC_KEYMENU: + if (hasTitleBar() && h == GetCapture()) + ReleaseCapture(); + + break; + + case SC_MAXIMIZE: + setFullScreen (true); + return 0; + + case SC_MINIMIZE: + if (! hasTitleBar()) + { + setMinimised (true); + return 0; + } + break; + + case SC_RESTORE: + if (hasTitleBar()) + { + if (isFullScreen()) + { + setFullScreen (false); + return 0; + } + } + else + { + if (isMinimised()) + setMinimised (false); + else if (isFullScreen()) + setFullScreen (false); + + return 0; + } + + break; + + case SC_MONITORPOWER: + case SC_SCREENSAVE: + if (! screenSaverAllowed) + return 0; + + break; + } + + break; + + case WM_NCLBUTTONDOWN: + case WM_NCRBUTTONDOWN: + case WM_NCMBUTTONDOWN: + if (component->isCurrentlyBlockedByAnotherModalComponent()) + { + Component* const current = Component::getCurrentlyModalComponent(); + + if (current != 0) + current->inputAttemptWhenModal(); + } + + break; + + //case WM_IME_STARTCOMPOSITION; + // return 0; + + case WM_GETDLGCODE: + return DLGC_WANTALLKEYS; + + default: + break; + } + } + } + + // (the message manager lock exits before calling this, to avoid deadlocks if + // this calls into non-juce windows) + return DefWindowProc (h, message, wParam, lParam); + } + + Win32ComponentPeer (const Win32ComponentPeer&); + const Win32ComponentPeer& operator= (const Win32ComponentPeer&); +}; + +ComponentPeer* Component::createNewPeer (int styleFlags, void* /*nativeWindowToAttachTo*/) +{ + return new Win32ComponentPeer (this, styleFlags); +} + +juce_ImplementSingleton_SingleThreaded (Win32ComponentPeer::WindowClassHolder); + +void SystemTrayIconComponent::setIconImage (const Image& newImage) +{ + Win32ComponentPeer* const wp = dynamic_cast (getPeer()); + + if (wp != 0) + wp->setTaskBarIcon (&newImage); +} + +void SystemTrayIconComponent::setIconTooltip (const String& tooltip) +{ + Win32ComponentPeer* const wp = dynamic_cast (getPeer()); + + if (wp != 0) + wp->setTaskBarIconToolTip (tooltip); +} + +void juce_setWindowStyleBit (HWND h, const int styleType, const int feature, const bool bitIsSet) throw() +{ + DWORD val = GetWindowLong (h, styleType); + + if (bitIsSet) + val |= feature; + else + val &= ~feature; + + SetWindowLongPtr (h, styleType, val); + SetWindowPos (h, 0, 0, 0, 0, 0, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER + | SWP_NOOWNERZORDER | SWP_FRAMECHANGED | SWP_NOSENDCHANGING); +} + +bool Process::isForegroundProcess() throw() +{ + HWND fg = GetForegroundWindow(); + + if (fg == 0) + return true; + + DWORD processId = 0; + GetWindowThreadProcessId (fg, &processId); + + return processId == GetCurrentProcessId(); +} + +void Desktop::getMousePosition (int& x, int& y) throw() +{ + POINT mousePos; + GetCursorPos (&mousePos); + x = mousePos.x; + y = mousePos.y; +} + +void Desktop::setMousePosition (int x, int y) throw() +{ + SetCursorPos (x, y); +} + +void Desktop::setScreenSaverEnabled (const bool isEnabled) throw() +{ + screenSaverAllowed = isEnabled; +} + +bool Desktop::isScreenSaverEnabled() throw() +{ + return screenSaverAllowed; +} + +static BOOL CALLBACK enumMonitorsProc (HMONITOR, HDC, LPRECT r, LPARAM userInfo) +{ + Array * const monitorCoords = (Array *) userInfo; + + monitorCoords->add (Rectangle (r->left, r->top, r->right - r->left, r->bottom - r->top)); + + return TRUE; +} + +void juce_updateMultiMonitorInfo (Array & monitorCoords, const bool clipToWorkArea) throw() +{ + EnumDisplayMonitors (0, 0, &enumMonitorsProc, (LPARAM) &monitorCoords); + + // make sure the first in the list is the main monitor + for (int i = 1; i < monitorCoords.size(); ++i) + if (monitorCoords[i].getX() == 0 && monitorCoords[i].getY() == 0) + monitorCoords.swap (i, 0); + + if (monitorCoords.size() == 0) + { + RECT r; + GetWindowRect (GetDesktopWindow(), &r); + + monitorCoords.add (Rectangle (r.left, r.top, r.right - r.left, r.bottom - r.top)); + } + + if (clipToWorkArea) + { + // clip the main monitor to the active non-taskbar area + RECT r; + SystemParametersInfo (SPI_GETWORKAREA, 0, &r, 0); + + Rectangle& screen = monitorCoords.getReference (0); + + screen.setPosition (jmax (screen.getX(), r.left), + jmax (screen.getY(), r.top)); + + screen.setSize (jmin (screen.getRight(), r.right) - screen.getX(), + jmin (screen.getBottom(), r.bottom) - screen.getY()); + } +} + +static Image* createImageFromHBITMAP (HBITMAP bitmap) throw() +{ + Image* im = 0; + + if (bitmap != 0) + { + BITMAP bm; + + if (GetObject (bitmap, sizeof (BITMAP), &bm) + && bm.bmWidth > 0 && bm.bmHeight > 0) + { + HDC tempDC = GetDC (0); + HDC dc = CreateCompatibleDC (tempDC); + ReleaseDC (0, tempDC); + + SelectObject (dc, bitmap); + + im = new Image (Image::ARGB, bm.bmWidth, bm.bmHeight, true); + + for (int y = bm.bmHeight; --y >= 0;) + { + for (int x = bm.bmWidth; --x >= 0;) + { + COLORREF col = GetPixel (dc, x, y); + + im->setPixelAt (x, y, Colour ((uint8) GetRValue (col), + (uint8) GetGValue (col), + (uint8) GetBValue (col))); + } + } + + DeleteDC (dc); + } + } + + return im; +} + +static Image* createImageFromHICON (HICON icon) throw() +{ + ICONINFO info; + + if (GetIconInfo (icon, &info)) + { + Image* const mask = createImageFromHBITMAP (info.hbmMask); + + if (mask == 0) + return 0; + + Image* const image = createImageFromHBITMAP (info.hbmColor); + + if (image == 0) + return mask; + + for (int y = image->getHeight(); --y >= 0;) + { + for (int x = image->getWidth(); --x >= 0;) + { + const float brightness = mask->getPixelAt (x, y).getBrightness(); + + if (brightness > 0.0f) + image->multiplyAlphaAt (x, y, 1.0f - brightness); + } + } + + delete mask; + return image; + } + + return 0; +} + +static HICON createHICONFromImage (const Image& image, const BOOL isIcon, int hotspotX, int hotspotY) throw() +{ + HBITMAP mask = CreateBitmap (image.getWidth(), image.getHeight(), 1, 1, 0); + + ICONINFO info; + info.fIcon = isIcon; + info.xHotspot = hotspotX; + info.yHotspot = hotspotY; + info.hbmMask = mask; + HICON hi = 0; + + if (SystemStats::getOperatingSystemType() >= SystemStats::WinXP) + { + WindowsBitmapImage bitmap (Image::ARGB, image.getWidth(), image.getHeight(), true); + Graphics g (bitmap); + g.drawImageAt (&image, 0, 0); + + info.hbmColor = bitmap.hBitmap; + hi = CreateIconIndirect (&info); + } + else + { + HBITMAP colour = CreateCompatibleBitmap (GetDC (0), image.getWidth(), image.getHeight()); + + HDC colDC = CreateCompatibleDC (GetDC (0)); + HDC alphaDC = CreateCompatibleDC (GetDC (0)); + SelectObject (colDC, colour); + SelectObject (alphaDC, mask); + + for (int y = image.getHeight(); --y >= 0;) + { + for (int x = image.getWidth(); --x >= 0;) + { + const Colour c (image.getPixelAt (x, y)); + + SetPixel (colDC, x, y, COLORREF (c.getRed() | (c.getGreen() << 8) | (c.getBlue() << 16))); + SetPixel (alphaDC, x, y, COLORREF (0xffffff - (c.getAlpha() | (c.getAlpha() << 8) | (c.getAlpha() << 16)))); + } + } + + DeleteDC (colDC); + DeleteDC (alphaDC); + + info.hbmColor = colour; + hi = CreateIconIndirect (&info); + DeleteObject (colour); + } + + DeleteObject (mask); + return hi; +} + +Image* juce_createIconForFile (const File& file) +{ + Image* image = 0; + + TCHAR filename [1024]; + file.getFullPathName().copyToBuffer (filename, 1023); + WORD iconNum = 0; + + HICON icon = ExtractAssociatedIcon ((HINSTANCE) PlatformUtilities::getCurrentModuleInstanceHandle(), + filename, &iconNum); + + if (icon != 0) + { + image = createImageFromHICON (icon); + DestroyIcon (icon); + } + + return image; +} + +void* juce_createMouseCursorFromImage (const Image& image, int hotspotX, int hotspotY) throw() +{ + const int maxW = GetSystemMetrics (SM_CXCURSOR); + const int maxH = GetSystemMetrics (SM_CYCURSOR); + + const Image* im = ℑ + Image* newIm = 0; + + if (image.getWidth() > maxW || image.getHeight() > maxH) + { + im = newIm = image.createCopy (maxW, maxH); + + hotspotX = (hotspotX * maxW) / image.getWidth(); + hotspotY = (hotspotY * maxH) / image.getHeight(); + } + + void* cursorH = 0; + + const SystemStats::OperatingSystemType os = SystemStats::getOperatingSystemType(); + + if (os == SystemStats::WinXP) + { + cursorH = (void*) createHICONFromImage (*im, FALSE, hotspotX, hotspotY); + } + else + { + const int stride = (maxW + 7) >> 3; + uint8* const andPlane = (uint8*) juce_calloc (stride * maxH); + uint8* const xorPlane = (uint8*) juce_calloc (stride * maxH); + int index = 0; + + for (int y = 0; y < maxH; ++y) + { + for (int x = 0; x < maxW; ++x) + { + const unsigned char bit = (unsigned char) (1 << (7 - (x & 7))); + + const Colour pixelColour (im->getPixelAt (x, y)); + + if (pixelColour.getAlpha() < 127) + andPlane [index + (x >> 3)] |= bit; + else if (pixelColour.getBrightness() >= 0.5f) + xorPlane [index + (x >> 3)] |= bit; + } + + index += stride; + } + + cursorH = CreateCursor (0, hotspotX, hotspotY, maxW, maxH, andPlane, xorPlane); + + juce_free (andPlane); + juce_free (xorPlane); + } + + delete newIm; + return cursorH; +} + +void juce_deleteMouseCursor (void* const cursorHandle, const bool isStandard) throw() +{ + if (cursorHandle != 0 && ! isStandard) + DestroyCursor ((HCURSOR) cursorHandle); +} + +void* juce_createStandardMouseCursor (MouseCursor::StandardCursorType type) throw() +{ + LPCTSTR cursorName = IDC_ARROW; + + switch (type) + { + case MouseCursor::NormalCursor: + cursorName = IDC_ARROW; + break; + + case MouseCursor::NoCursor: + return 0; + + case MouseCursor::DraggingHandCursor: + { + static void* dragHandCursor = 0; + + if (dragHandCursor == 0) + { + static const unsigned char dragHandData[] = + { 71,73,70,56,57,97,16,0,16,0,145,2,0,0,0,0,255,255,255,0,0,0,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, + 16,0,0,2,52,148,47,0,200,185,16,130,90,12,74,139,107,84,123,39, 132,117,151,116,132,146,248,60,209,138, + 98,22,203,114,34,236,37,52,77,217,247,154,191,119,110,240,193,128,193,95,163,56,60,234,98,135,2,0,59 }; + + Image* const image = ImageFileFormat::loadFrom ((const char*) dragHandData, sizeof (dragHandData)); + dragHandCursor = juce_createMouseCursorFromImage (*image, 8, 7); + delete image; + } + + return dragHandCursor; + } + + case MouseCursor::WaitCursor: + cursorName = IDC_WAIT; + break; + + case MouseCursor::IBeamCursor: + cursorName = IDC_IBEAM; + break; + + case MouseCursor::PointingHandCursor: + cursorName = MAKEINTRESOURCE(32649); + break; + + case MouseCursor::LeftRightResizeCursor: + case MouseCursor::LeftEdgeResizeCursor: + case MouseCursor::RightEdgeResizeCursor: + cursorName = IDC_SIZEWE; + break; + + case MouseCursor::UpDownResizeCursor: + case MouseCursor::TopEdgeResizeCursor: + case MouseCursor::BottomEdgeResizeCursor: + cursorName = IDC_SIZENS; + break; + + case MouseCursor::TopLeftCornerResizeCursor: + case MouseCursor::BottomRightCornerResizeCursor: + cursorName = IDC_SIZENWSE; + break; + + case MouseCursor::TopRightCornerResizeCursor: + case MouseCursor::BottomLeftCornerResizeCursor: + cursorName = IDC_SIZENESW; + break; + + case MouseCursor::UpDownLeftRightResizeCursor: + cursorName = IDC_SIZEALL; + break; + + case MouseCursor::CrosshairCursor: + cursorName = IDC_CROSS; + break; + + case MouseCursor::CopyingCursor: + // can't seem to find one of these in the win32 list.. + break; + } + + HCURSOR cursorH = LoadCursor (0, cursorName); + + if (cursorH == 0) + cursorH = LoadCursor (0, IDC_ARROW); + + return (void*) cursorH; +} + +void MouseCursor::showInWindow (ComponentPeer*) const throw() +{ + SetCursor ((HCURSOR) getHandle()); +} + +void MouseCursor::showInAllWindows() const throw() +{ + showInWindow (0); +} + +class JuceDropSource : public IDropSource +{ + int refCount; + +public: + JuceDropSource() + : refCount (1) + { + } + + virtual ~JuceDropSource() + { + jassert (refCount == 0); + } + + HRESULT __stdcall QueryInterface (REFIID id, void __RPC_FAR* __RPC_FAR* result) + { + if (id == IID_IUnknown || id == IID_IDropSource) + { + AddRef(); + *result = this; + return S_OK; + } + + *result = 0; + return E_NOINTERFACE; + } + + ULONG __stdcall AddRef() { return ++refCount; } + ULONG __stdcall Release() { jassert (refCount > 0); const int r = --refCount; if (r == 0) delete this; return r; } + + HRESULT __stdcall QueryContinueDrag (BOOL escapePressed, DWORD keys) + { + if (escapePressed) + return DRAGDROP_S_CANCEL; + + if ((keys & (MK_LBUTTON | MK_RBUTTON)) == 0) + return DRAGDROP_S_DROP; + + return S_OK; + } + + HRESULT __stdcall GiveFeedback (DWORD) + { + return DRAGDROP_S_USEDEFAULTCURSORS; + } +}; + +class JuceEnumFormatEtc : public IEnumFORMATETC +{ +public: + JuceEnumFormatEtc (const FORMATETC* const format_) + : refCount (1), + format (format_), + index (0) + { + } + + virtual ~JuceEnumFormatEtc() + { + jassert (refCount == 0); + } + + HRESULT __stdcall QueryInterface (REFIID id, void __RPC_FAR* __RPC_FAR* result) + { + if (id == IID_IUnknown || id == IID_IEnumFORMATETC) + { + AddRef(); + *result = this; + return S_OK; + } + + *result = 0; + return E_NOINTERFACE; + } + + ULONG __stdcall AddRef() { return ++refCount; } + ULONG __stdcall Release() { jassert (refCount > 0); const int r = --refCount; if (r == 0) delete this; return r; } + + HRESULT __stdcall Clone (IEnumFORMATETC** result) + { + if (result == 0) + return E_POINTER; + + JuceEnumFormatEtc* const newOne = new JuceEnumFormatEtc (format); + newOne->index = index; + + *result = newOne; + return S_OK; + } + + HRESULT __stdcall Next (ULONG celt, LPFORMATETC lpFormatEtc, ULONG* pceltFetched) + { + if (pceltFetched != 0) + *pceltFetched = 0; + else if (celt != 1) + return S_FALSE; + + if (index == 0 && celt > 0 && lpFormatEtc != 0) + { + copyFormatEtc (lpFormatEtc [0], *format); + ++index; + + if (pceltFetched != 0) + *pceltFetched = 1; + + return S_OK; + } + + return S_FALSE; + } + + HRESULT __stdcall Skip (ULONG celt) + { + if (index + (int) celt >= 1) + return S_FALSE; + + index += celt; + return S_OK; + } + + HRESULT __stdcall Reset() + { + index = 0; + return S_OK; + } + +private: + int refCount; + const FORMATETC* const format; + int index; + + static void copyFormatEtc (FORMATETC& dest, const FORMATETC& source) + { + dest = source; + + if (source.ptd != 0) + { + dest.ptd = (DVTARGETDEVICE*) CoTaskMemAlloc (sizeof (DVTARGETDEVICE)); + *(dest.ptd) = *(source.ptd); + } + } + + JuceEnumFormatEtc (const JuceEnumFormatEtc&); + const JuceEnumFormatEtc& operator= (const JuceEnumFormatEtc&); +}; + +class JuceDataObject : public IDataObject +{ + JuceDropSource* const dropSource; + const FORMATETC* const format; + const STGMEDIUM* const medium; + int refCount; + + JuceDataObject (const JuceDataObject&); + const JuceDataObject& operator= (const JuceDataObject&); + +public: + JuceDataObject (JuceDropSource* const dropSource_, + const FORMATETC* const format_, + const STGMEDIUM* const medium_) + : dropSource (dropSource_), + format (format_), + medium (medium_), + refCount (1) + { + } + + virtual ~JuceDataObject() + { + jassert (refCount == 0); + } + + HRESULT __stdcall QueryInterface (REFIID id, void __RPC_FAR* __RPC_FAR* result) + { + if (id == IID_IUnknown || id == IID_IDataObject) + { + AddRef(); + *result = this; + return S_OK; + } + + *result = 0; + return E_NOINTERFACE; + } + + ULONG __stdcall AddRef() { return ++refCount; } + ULONG __stdcall Release() { jassert (refCount > 0); const int r = --refCount; if (r == 0) delete this; return r; } + + HRESULT __stdcall GetData (FORMATETC __RPC_FAR* pFormatEtc, STGMEDIUM __RPC_FAR* pMedium) + { + if (pFormatEtc->tymed == format->tymed + && pFormatEtc->cfFormat == format->cfFormat + && pFormatEtc->dwAspect == format->dwAspect) + { + pMedium->tymed = format->tymed; + pMedium->pUnkForRelease = 0; + + if (format->tymed == TYMED_HGLOBAL) + { + const SIZE_T len = GlobalSize (medium->hGlobal); + void* const src = GlobalLock (medium->hGlobal); + void* const dst = GlobalAlloc (GMEM_FIXED, len); + + memcpy (dst, src, len); + + GlobalUnlock (medium->hGlobal); + + pMedium->hGlobal = dst; + return S_OK; + } + } + + return DV_E_FORMATETC; + } + + HRESULT __stdcall QueryGetData (FORMATETC __RPC_FAR* f) + { + if (f == 0) + return E_INVALIDARG; + + if (f->tymed == format->tymed + && f->cfFormat == format->cfFormat + && f->dwAspect == format->dwAspect) + return S_OK; + + return DV_E_FORMATETC; + } + + HRESULT __stdcall GetCanonicalFormatEtc (FORMATETC __RPC_FAR*, FORMATETC __RPC_FAR* pFormatEtcOut) + { + pFormatEtcOut->ptd = 0; + return E_NOTIMPL; + } + + HRESULT __stdcall EnumFormatEtc (DWORD direction, IEnumFORMATETC __RPC_FAR *__RPC_FAR *result) + { + if (result == 0) + return E_POINTER; + + if (direction == DATADIR_GET) + { + *result = new JuceEnumFormatEtc (format); + return S_OK; + } + + *result = 0; + return E_NOTIMPL; + } + + HRESULT __stdcall GetDataHere (FORMATETC __RPC_FAR*, STGMEDIUM __RPC_FAR*) { return DATA_E_FORMATETC; } + HRESULT __stdcall SetData (FORMATETC __RPC_FAR*, STGMEDIUM __RPC_FAR*, BOOL) { return E_NOTIMPL; } + HRESULT __stdcall DAdvise (FORMATETC __RPC_FAR*, DWORD, IAdviseSink __RPC_FAR*, DWORD __RPC_FAR*) { return OLE_E_ADVISENOTSUPPORTED; } + HRESULT __stdcall DUnadvise (DWORD) { return E_NOTIMPL; } + HRESULT __stdcall EnumDAdvise (IEnumSTATDATA __RPC_FAR *__RPC_FAR *) { return OLE_E_ADVISENOTSUPPORTED; } +}; + +static HDROP createHDrop (const StringArray& fileNames) throw() +{ + int totalChars = 0; + for (int i = fileNames.size(); --i >= 0;) + totalChars += fileNames[i].length() + 1; + + HDROP hDrop = (HDROP) GlobalAlloc (GMEM_MOVEABLE | GMEM_ZEROINIT, + sizeof (DROPFILES) + + sizeof (WCHAR) * (totalChars + 2)); + + if (hDrop != 0) + { + LPDROPFILES pDropFiles = (LPDROPFILES) GlobalLock (hDrop); + pDropFiles->pFiles = sizeof (DROPFILES); + + pDropFiles->fWide = true; + + WCHAR* fname = (WCHAR*) (((char*) pDropFiles) + sizeof (DROPFILES)); + + for (int i = 0; i < fileNames.size(); ++i) + { + fileNames[i].copyToBuffer (fname, 2048); + fname += fileNames[i].length() + 1; + } + + *fname = 0; + + GlobalUnlock (hDrop); + } + + return hDrop; +} + +static bool performDragDrop (FORMATETC* const format, STGMEDIUM* const medium, const DWORD whatToDo) throw() +{ + JuceDropSource* const source = new JuceDropSource(); + JuceDataObject* const data = new JuceDataObject (source, format, medium); + + DWORD effect; + const HRESULT res = DoDragDrop (data, source, whatToDo, &effect); + + data->Release(); + source->Release(); + + return res == DRAGDROP_S_DROP; +} + +bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, const bool canMove) +{ + FORMATETC format = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; + STGMEDIUM medium = { TYMED_HGLOBAL, { 0 }, 0 }; + + medium.hGlobal = createHDrop (files); + + return performDragDrop (&format, &medium, canMove ? (DROPEFFECT_COPY | DROPEFFECT_MOVE) + : DROPEFFECT_COPY); +} + +bool DragAndDropContainer::performExternalDragDropOfText (const String& text) +{ + FORMATETC format = { CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; + STGMEDIUM medium = { TYMED_HGLOBAL, { 0 }, 0 }; + + const int numChars = text.length(); + + medium.hGlobal = GlobalAlloc (GMEM_MOVEABLE | GMEM_ZEROINIT, (numChars + 2) * sizeof (WCHAR)); + char* d = (char*) GlobalLock (medium.hGlobal); + + text.copyToBuffer ((WCHAR*) d, numChars + 1); + format.cfFormat = CF_UNICODETEXT; + + GlobalUnlock (medium.hGlobal); + + return performDragDrop (&format, &medium, DROPEFFECT_COPY | DROPEFFECT_MOVE); +} + +#if JUCE_OPENGL + +#define WGL_EXT_FUNCTION_INIT(extType, extFunc) \ + ((extFunc = (extType) wglGetProcAddress (#extFunc)) != 0) + +typedef const char* (WINAPI* PFNWGLGETEXTENSIONSSTRINGARBPROC) (HDC hdc); +typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBIVARBPROC) (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int *piAttributes, int *piValues); +typedef BOOL (WINAPI * PFNWGLCHOOSEPIXELFORMATARBPROC) (HDC hdc, const int* piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats); +typedef BOOL (WINAPI * PFNWGLSWAPINTERVALEXTPROC) (int interval); +typedef int (WINAPI * PFNWGLGETSWAPINTERVALEXTPROC) (void); + +#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000 +#define WGL_DRAW_TO_WINDOW_ARB 0x2001 +#define WGL_ACCELERATION_ARB 0x2003 +#define WGL_SWAP_METHOD_ARB 0x2007 +#define WGL_SUPPORT_OPENGL_ARB 0x2010 +#define WGL_PIXEL_TYPE_ARB 0x2013 +#define WGL_DOUBLE_BUFFER_ARB 0x2011 +#define WGL_COLOR_BITS_ARB 0x2014 +#define WGL_RED_BITS_ARB 0x2015 +#define WGL_GREEN_BITS_ARB 0x2017 +#define WGL_BLUE_BITS_ARB 0x2019 +#define WGL_ALPHA_BITS_ARB 0x201B +#define WGL_DEPTH_BITS_ARB 0x2022 +#define WGL_STENCIL_BITS_ARB 0x2023 +#define WGL_FULL_ACCELERATION_ARB 0x2027 +#define WGL_ACCUM_RED_BITS_ARB 0x201E +#define WGL_ACCUM_GREEN_BITS_ARB 0x201F +#define WGL_ACCUM_BLUE_BITS_ARB 0x2020 +#define WGL_ACCUM_ALPHA_BITS_ARB 0x2021 +#define WGL_STEREO_ARB 0x2012 +#define WGL_SAMPLE_BUFFERS_ARB 0x2041 +#define WGL_SAMPLES_ARB 0x2042 +#define WGL_TYPE_RGBA_ARB 0x202B + +static void getWglExtensions (HDC dc, StringArray& result) throw() +{ + PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB = 0; + + if (WGL_EXT_FUNCTION_INIT (PFNWGLGETEXTENSIONSSTRINGARBPROC, wglGetExtensionsStringARB)) + result.addTokens (String (wglGetExtensionsStringARB (dc)), false); + else + jassertfalse // If this fails, it may be because you didn't activate the openGL context +} + +class WindowedGLContext : public OpenGLContext +{ +public: + WindowedGLContext (Component* const component_, + HGLRC contextToShareWith, + const OpenGLPixelFormat& pixelFormat) + : renderContext (0), + nativeWindow (0), + dc (0), + component (component_) + { + jassert (component != 0); + + createNativeWindow(); + + // Use a default pixel format that should be supported everywhere + PIXELFORMATDESCRIPTOR pfd; + zerostruct (pfd); + pfd.nSize = sizeof (pfd); + pfd.nVersion = 1; + pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.cColorBits = 24; + pfd.cDepthBits = 16; + + const int format = ChoosePixelFormat (dc, &pfd); + + if (format != 0) + SetPixelFormat (dc, format, &pfd); + + renderContext = wglCreateContext (dc); + makeActive(); + + setPixelFormat (pixelFormat); + + if (contextToShareWith != 0 && renderContext != 0) + wglShareLists (contextToShareWith, renderContext); + } + + ~WindowedGLContext() + { + makeInactive(); + + wglDeleteContext (renderContext); + + ReleaseDC ((HWND) nativeWindow->getNativeHandle(), dc); + delete nativeWindow; + } + + bool makeActive() const throw() + { + jassert (renderContext != 0); + return wglMakeCurrent (dc, renderContext) != 0; + } + + bool makeInactive() const throw() + { + return (! isActive()) || (wglMakeCurrent (0, 0) != 0); + } + + bool isActive() const throw() + { + return wglGetCurrentContext() == renderContext; + } + + const OpenGLPixelFormat getPixelFormat() const + { + OpenGLPixelFormat pf; + + makeActive(); + StringArray availableExtensions; + getWglExtensions (dc, availableExtensions); + + fillInPixelFormatDetails (GetPixelFormat (dc), pf, availableExtensions); + return pf; + } + + void* getRawContext() const throw() + { + return renderContext; + } + + bool setPixelFormat (const OpenGLPixelFormat& pixelFormat) + { + makeActive(); + + PIXELFORMATDESCRIPTOR pfd; + zerostruct (pfd); + pfd.nSize = sizeof (pfd); + pfd.nVersion = 1; + pfd.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER; + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.iLayerType = PFD_MAIN_PLANE; + pfd.cColorBits = pixelFormat.redBits + pixelFormat.greenBits + pixelFormat.blueBits; + pfd.cRedBits = pixelFormat.redBits; + pfd.cGreenBits = pixelFormat.greenBits; + pfd.cBlueBits = pixelFormat.blueBits; + pfd.cAlphaBits = pixelFormat.alphaBits; + pfd.cDepthBits = pixelFormat.depthBufferBits; + pfd.cStencilBits = pixelFormat.stencilBufferBits; + pfd.cAccumBits = pixelFormat.accumulationBufferRedBits + pixelFormat.accumulationBufferGreenBits + + pixelFormat.accumulationBufferBlueBits + pixelFormat.accumulationBufferAlphaBits; + pfd.cAccumRedBits = pixelFormat.accumulationBufferRedBits; + pfd.cAccumGreenBits = pixelFormat.accumulationBufferGreenBits; + pfd.cAccumBlueBits = pixelFormat.accumulationBufferBlueBits; + pfd.cAccumAlphaBits = pixelFormat.accumulationBufferAlphaBits; + + int format = 0; + + PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = 0; + + StringArray availableExtensions; + getWglExtensions (dc, availableExtensions); + + if (availableExtensions.contains ("WGL_ARB_pixel_format") + && WGL_EXT_FUNCTION_INIT (PFNWGLCHOOSEPIXELFORMATARBPROC, wglChoosePixelFormatARB)) + { + int attributes[64]; + int n = 0; + + attributes[n++] = WGL_DRAW_TO_WINDOW_ARB; + attributes[n++] = GL_TRUE; + attributes[n++] = WGL_SUPPORT_OPENGL_ARB; + attributes[n++] = GL_TRUE; + attributes[n++] = WGL_ACCELERATION_ARB; + attributes[n++] = WGL_FULL_ACCELERATION_ARB; + attributes[n++] = WGL_DOUBLE_BUFFER_ARB; + attributes[n++] = GL_TRUE; + attributes[n++] = WGL_PIXEL_TYPE_ARB; + attributes[n++] = WGL_TYPE_RGBA_ARB; + + attributes[n++] = WGL_COLOR_BITS_ARB; + attributes[n++] = pfd.cColorBits; + attributes[n++] = WGL_RED_BITS_ARB; + attributes[n++] = pixelFormat.redBits; + attributes[n++] = WGL_GREEN_BITS_ARB; + attributes[n++] = pixelFormat.greenBits; + attributes[n++] = WGL_BLUE_BITS_ARB; + attributes[n++] = pixelFormat.blueBits; + attributes[n++] = WGL_ALPHA_BITS_ARB; + attributes[n++] = pixelFormat.alphaBits; + attributes[n++] = WGL_DEPTH_BITS_ARB; + attributes[n++] = pixelFormat.depthBufferBits; + + if (pixelFormat.stencilBufferBits > 0) + { + attributes[n++] = WGL_STENCIL_BITS_ARB; + attributes[n++] = pixelFormat.stencilBufferBits; + } + + attributes[n++] = WGL_ACCUM_RED_BITS_ARB; + attributes[n++] = pixelFormat.accumulationBufferRedBits; + attributes[n++] = WGL_ACCUM_GREEN_BITS_ARB; + attributes[n++] = pixelFormat.accumulationBufferGreenBits; + attributes[n++] = WGL_ACCUM_BLUE_BITS_ARB; + attributes[n++] = pixelFormat.accumulationBufferBlueBits; + attributes[n++] = WGL_ACCUM_ALPHA_BITS_ARB; + attributes[n++] = pixelFormat.accumulationBufferAlphaBits; + + if (availableExtensions.contains ("WGL_ARB_multisample") + && pixelFormat.fullSceneAntiAliasingNumSamples > 0) + { + attributes[n++] = WGL_SAMPLE_BUFFERS_ARB; + attributes[n++] = 1; + attributes[n++] = WGL_SAMPLES_ARB; + attributes[n++] = pixelFormat.fullSceneAntiAliasingNumSamples; + } + + attributes[n++] = 0; + + UINT formatsCount; + const BOOL ok = wglChoosePixelFormatARB (dc, attributes, 0, 1, &format, &formatsCount); + (void) ok; + jassert (ok); + } + else + { + format = ChoosePixelFormat (dc, &pfd); + } + + if (format != 0) + { + makeInactive(); + + // win32 can't change the pixel format of a window, so need to delete the + // old one and create a new one.. + jassert (nativeWindow != 0); + ReleaseDC ((HWND) nativeWindow->getNativeHandle(), dc); + delete nativeWindow; + + createNativeWindow(); + + if (SetPixelFormat (dc, format, &pfd)) + { + wglDeleteContext (renderContext); + renderContext = wglCreateContext (dc); + + jassert (renderContext != 0); + return renderContext != 0; + } + } + + return false; + } + + void updateWindowPosition (int x, int y, int w, int h, int) + { + SetWindowPos ((HWND) nativeWindow->getNativeHandle(), 0, + x, y, w, h, + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER); + } + + void repaint() + { + int x, y, w, h; + nativeWindow->getBounds (x, y, w, h); + nativeWindow->repaint (0, 0, w, h); + } + + void swapBuffers() + { + SwapBuffers (dc); + } + + bool setSwapInterval (const int numFramesPerSwap) + { + makeActive(); + + StringArray availableExtensions; + getWglExtensions (dc, availableExtensions); + + PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = 0; + + return availableExtensions.contains ("WGL_EXT_swap_control") + && WGL_EXT_FUNCTION_INIT (PFNWGLSWAPINTERVALEXTPROC, wglSwapIntervalEXT) + && wglSwapIntervalEXT (numFramesPerSwap) != FALSE; + } + + int getSwapInterval() const + { + makeActive(); + + StringArray availableExtensions; + getWglExtensions (dc, availableExtensions); + + PFNWGLGETSWAPINTERVALEXTPROC wglGetSwapIntervalEXT = 0; + + if (availableExtensions.contains ("WGL_EXT_swap_control") + && WGL_EXT_FUNCTION_INIT (PFNWGLGETSWAPINTERVALEXTPROC, wglGetSwapIntervalEXT)) + return wglGetSwapIntervalEXT(); + + return 0; + } + + void findAlternativeOpenGLPixelFormats (OwnedArray & results) + { + jassert (isActive()); + + StringArray availableExtensions; + getWglExtensions (dc, availableExtensions); + + PFNWGLGETPIXELFORMATATTRIBIVARBPROC wglGetPixelFormatAttribivARB = 0; + int numTypes = 0; + + if (availableExtensions.contains("WGL_ARB_pixel_format") + && WGL_EXT_FUNCTION_INIT (PFNWGLGETPIXELFORMATATTRIBIVARBPROC, wglGetPixelFormatAttribivARB)) + { + int attributes = WGL_NUMBER_PIXEL_FORMATS_ARB; + + if (! wglGetPixelFormatAttribivARB (dc, 1, 0, 1, &attributes, &numTypes)) + jassertfalse + } + else + { + numTypes = DescribePixelFormat (dc, 0, 0, 0); + } + + OpenGLPixelFormat pf; + + for (int i = 0; i < numTypes; ++i) + { + if (fillInPixelFormatDetails (i + 1, pf, availableExtensions)) + { + bool alreadyListed = false; + for (int j = results.size(); --j >= 0;) + if (pf == *results.getUnchecked(j)) + alreadyListed = true; + + if (! alreadyListed) + results.add (new OpenGLPixelFormat (pf)); + } + } + } + + juce_UseDebuggingNewOperator + + HGLRC renderContext; + +private: + Win32ComponentPeer* nativeWindow; + Component* const component; + HDC dc; + + void createNativeWindow() + { + nativeWindow = new Win32ComponentPeer (component, 0); + nativeWindow->dontRepaint = true; + nativeWindow->setVisible (true); + + HWND hwnd = (HWND) nativeWindow->getNativeHandle(); + + Win32ComponentPeer* const peer = dynamic_cast (component->getTopLevelComponent()->getPeer()); + + if (peer != 0) + { + SetParent (hwnd, (HWND) peer->getNativeHandle()); + juce_setWindowStyleBit (hwnd, GWL_STYLE, WS_CHILD, true); + juce_setWindowStyleBit (hwnd, GWL_STYLE, WS_POPUP, false); + } + + dc = GetDC (hwnd); + } + + bool fillInPixelFormatDetails (const int pixelFormatIndex, + OpenGLPixelFormat& result, + const StringArray& availableExtensions) const throw() + { + PFNWGLGETPIXELFORMATATTRIBIVARBPROC wglGetPixelFormatAttribivARB = 0; + + if (availableExtensions.contains ("WGL_ARB_pixel_format") + && WGL_EXT_FUNCTION_INIT (PFNWGLGETPIXELFORMATATTRIBIVARBPROC, wglGetPixelFormatAttribivARB)) + { + int attributes[32]; + int numAttributes = 0; + + attributes[numAttributes++] = WGL_DRAW_TO_WINDOW_ARB; + attributes[numAttributes++] = WGL_SUPPORT_OPENGL_ARB; + attributes[numAttributes++] = WGL_ACCELERATION_ARB; + attributes[numAttributes++] = WGL_DOUBLE_BUFFER_ARB; + attributes[numAttributes++] = WGL_PIXEL_TYPE_ARB; + attributes[numAttributes++] = WGL_RED_BITS_ARB; + attributes[numAttributes++] = WGL_GREEN_BITS_ARB; + attributes[numAttributes++] = WGL_BLUE_BITS_ARB; + attributes[numAttributes++] = WGL_ALPHA_BITS_ARB; + attributes[numAttributes++] = WGL_DEPTH_BITS_ARB; + attributes[numAttributes++] = WGL_STENCIL_BITS_ARB; + attributes[numAttributes++] = WGL_ACCUM_RED_BITS_ARB; + attributes[numAttributes++] = WGL_ACCUM_GREEN_BITS_ARB; + attributes[numAttributes++] = WGL_ACCUM_BLUE_BITS_ARB; + attributes[numAttributes++] = WGL_ACCUM_ALPHA_BITS_ARB; + + if (availableExtensions.contains ("WGL_ARB_multisample")) + attributes[numAttributes++] = WGL_SAMPLES_ARB; + + int values[32]; + zeromem (values, sizeof (values)); + + if (wglGetPixelFormatAttribivARB (dc, pixelFormatIndex, 0, numAttributes, attributes, values)) + { + int n = 0; + bool isValidFormat = (values[n++] == GL_TRUE); // WGL_DRAW_TO_WINDOW_ARB + isValidFormat = (values[n++] == GL_TRUE) && isValidFormat; // WGL_SUPPORT_OPENGL_ARB + isValidFormat = (values[n++] == WGL_FULL_ACCELERATION_ARB) && isValidFormat; // WGL_ACCELERATION_ARB + isValidFormat = (values[n++] == GL_TRUE) && isValidFormat; // WGL_DOUBLE_BUFFER_ARB: + isValidFormat = (values[n++] == WGL_TYPE_RGBA_ARB) && isValidFormat; // WGL_PIXEL_TYPE_ARB + result.redBits = values[n++]; // WGL_RED_BITS_ARB + result.greenBits = values[n++]; // WGL_GREEN_BITS_ARB + result.blueBits = values[n++]; // WGL_BLUE_BITS_ARB + result.alphaBits = values[n++]; // WGL_ALPHA_BITS_ARB + result.depthBufferBits = values[n++]; // WGL_DEPTH_BITS_ARB + result.stencilBufferBits = values[n++]; // WGL_STENCIL_BITS_ARB + result.accumulationBufferRedBits = values[n++]; // WGL_ACCUM_RED_BITS_ARB + result.accumulationBufferGreenBits = values[n++]; // WGL_ACCUM_GREEN_BITS_ARB + result.accumulationBufferBlueBits = values[n++]; // WGL_ACCUM_BLUE_BITS_ARB + result.accumulationBufferAlphaBits = values[n++]; // WGL_ACCUM_ALPHA_BITS_ARB + result.fullSceneAntiAliasingNumSamples = values[n++]; // WGL_SAMPLES_ARB + + return isValidFormat; + } + else + { + jassertfalse + } + } + else + { + PIXELFORMATDESCRIPTOR pfd; + + if (DescribePixelFormat (dc, pixelFormatIndex, sizeof (pfd), &pfd)) + { + result.redBits = pfd.cRedBits; + result.greenBits = pfd.cGreenBits; + result.blueBits = pfd.cBlueBits; + result.alphaBits = pfd.cAlphaBits; + result.depthBufferBits = pfd.cDepthBits; + result.stencilBufferBits = pfd.cStencilBits; + result.accumulationBufferRedBits = pfd.cAccumRedBits; + result.accumulationBufferGreenBits = pfd.cAccumGreenBits; + result.accumulationBufferBlueBits = pfd.cAccumBlueBits; + result.accumulationBufferAlphaBits = pfd.cAccumAlphaBits; + result.fullSceneAntiAliasingNumSamples = 0; + + return true; + } + else + { + jassertfalse + } + } + + return false; + } + + WindowedGLContext (const WindowedGLContext&); + const WindowedGLContext& operator= (const WindowedGLContext&); +}; + +OpenGLContext* OpenGLContext::createContextForWindow (Component* const component, + const OpenGLPixelFormat& pixelFormat, + const OpenGLContext* const contextToShareWith) +{ + WindowedGLContext* c = new WindowedGLContext (component, + contextToShareWith != 0 ? (HGLRC) contextToShareWith->getRawContext() : 0, + pixelFormat); + + if (c->renderContext == 0) + deleteAndZero (c); + + return c; +} + +void juce_glViewport (const int w, const int h) +{ + glViewport (0, 0, w, h); +} + +void OpenGLPixelFormat::getAvailablePixelFormats (Component* component, + OwnedArray & results) +{ + Component tempComp; + + { + WindowedGLContext wc (component, 0, OpenGLPixelFormat (8, 8, 16, 0)); + wc.makeActive(); + wc.findAlternativeOpenGLPixelFormats (results); + } +} + +#endif + +class JuceIStorage : public IStorage +{ + int refCount; + +public: + JuceIStorage() : refCount (1) {} + + virtual ~JuceIStorage() + { + jassert (refCount == 0); + } + + HRESULT __stdcall QueryInterface (REFIID id, void __RPC_FAR* __RPC_FAR* result) + { + if (id == IID_IUnknown || id == IID_IStorage) + { + AddRef(); + *result = this; + return S_OK; + } + + *result = 0; + return E_NOINTERFACE; + } + + ULONG __stdcall AddRef() { return ++refCount; } + ULONG __stdcall Release() { const int r = --refCount; if (r == 0) delete this; return r; } + + HRESULT __stdcall CreateStream (const WCHAR*, DWORD, DWORD, DWORD, IStream**) { return E_NOTIMPL; } + HRESULT __stdcall OpenStream (const WCHAR*, void*, DWORD, DWORD, IStream**) { return E_NOTIMPL; } + HRESULT __stdcall CreateStorage (const WCHAR*, DWORD, DWORD, DWORD, IStorage**) { return E_NOTIMPL; } + HRESULT __stdcall OpenStorage (const WCHAR*, IStorage*, DWORD, SNB, DWORD, IStorage**) { return E_NOTIMPL; } + HRESULT __stdcall CopyTo (DWORD, IID const*, SNB, IStorage*) { return E_NOTIMPL; } + HRESULT __stdcall MoveElementTo (const OLECHAR*,IStorage*, const OLECHAR*, DWORD) { return E_NOTIMPL; } + HRESULT __stdcall Commit (DWORD) { return E_NOTIMPL; } + HRESULT __stdcall Revert() { return E_NOTIMPL; } + HRESULT __stdcall EnumElements (DWORD, void*, DWORD, IEnumSTATSTG**) { return E_NOTIMPL; } + HRESULT __stdcall DestroyElement (const OLECHAR*) { return E_NOTIMPL; } + HRESULT __stdcall RenameElement (const WCHAR*, const WCHAR*) { return E_NOTIMPL; } + HRESULT __stdcall SetElementTimes (const WCHAR*, FILETIME const*, FILETIME const*, FILETIME const*) { return E_NOTIMPL; } + HRESULT __stdcall SetClass (REFCLSID) { return S_OK; } + HRESULT __stdcall SetStateBits (DWORD, DWORD) { return E_NOTIMPL; } + HRESULT __stdcall Stat (STATSTG*, DWORD) { return E_NOTIMPL; } + + juce_UseDebuggingNewOperator +}; + +class JuceOleInPlaceFrame : public IOleInPlaceFrame +{ + int refCount; + HWND window; + +public: + JuceOleInPlaceFrame (HWND window_) + : refCount (1), + window (window_) + { + } + + virtual ~JuceOleInPlaceFrame() + { + jassert (refCount == 0); + } + + HRESULT __stdcall QueryInterface (REFIID id, void __RPC_FAR* __RPC_FAR* result) + { + if (id == IID_IUnknown || id == IID_IOleInPlaceFrame) + { + AddRef(); + *result = this; + return S_OK; + } + + *result = 0; + return E_NOINTERFACE; + } + + ULONG __stdcall AddRef() { return ++refCount; } + ULONG __stdcall Release() { const int r = --refCount; if (r == 0) delete this; return r; } + + HRESULT __stdcall GetWindow (HWND* lphwnd) { *lphwnd = window; return S_OK; } + HRESULT __stdcall ContextSensitiveHelp (BOOL) { return E_NOTIMPL; } + HRESULT __stdcall GetBorder (LPRECT) { return E_NOTIMPL; } + HRESULT __stdcall RequestBorderSpace (LPCBORDERWIDTHS) { return E_NOTIMPL; } + HRESULT __stdcall SetBorderSpace (LPCBORDERWIDTHS) { return E_NOTIMPL; } + HRESULT __stdcall SetActiveObject (IOleInPlaceActiveObject*, LPCOLESTR) { return S_OK; } + HRESULT __stdcall InsertMenus (HMENU, LPOLEMENUGROUPWIDTHS) { return E_NOTIMPL; } + HRESULT __stdcall SetMenu (HMENU, HOLEMENU, HWND) { return S_OK; } + HRESULT __stdcall RemoveMenus (HMENU) { return E_NOTIMPL; } + HRESULT __stdcall SetStatusText (LPCOLESTR) { return S_OK; } + HRESULT __stdcall EnableModeless (BOOL) { return S_OK; } + HRESULT __stdcall TranslateAccelerator(LPMSG, WORD) { return E_NOTIMPL; } + + juce_UseDebuggingNewOperator +}; + +class JuceIOleInPlaceSite : public IOleInPlaceSite +{ + int refCount; + HWND window; + JuceOleInPlaceFrame* frame; + +public: + JuceIOleInPlaceSite (HWND window_) + : refCount (1), + window (window_) + { + frame = new JuceOleInPlaceFrame (window); + } + + virtual ~JuceIOleInPlaceSite() + { + jassert (refCount == 0); + frame->Release(); + } + + HRESULT __stdcall QueryInterface (REFIID id, void __RPC_FAR* __RPC_FAR* result) + { + if (id == IID_IUnknown || id == IID_IOleInPlaceSite) + { + AddRef(); + *result = this; + return S_OK; + } + + *result = 0; + return E_NOINTERFACE; + } + + ULONG __stdcall AddRef() { return ++refCount; } + ULONG __stdcall Release() { const int r = --refCount; if (r == 0) delete this; return r; } + + HRESULT __stdcall GetWindow (HWND* lphwnd) { *lphwnd = window; return S_OK; } + HRESULT __stdcall ContextSensitiveHelp (BOOL) { return E_NOTIMPL; } + HRESULT __stdcall CanInPlaceActivate() { return S_OK; } + HRESULT __stdcall OnInPlaceActivate() { return S_OK; } + HRESULT __stdcall OnUIActivate() { return S_OK; } + + HRESULT __stdcall GetWindowContext (LPOLEINPLACEFRAME* lplpFrame, LPOLEINPLACEUIWINDOW* lplpDoc, LPRECT, LPRECT, LPOLEINPLACEFRAMEINFO lpFrameInfo) + { + frame->AddRef(); + *lplpFrame = frame; + *lplpDoc = 0; + lpFrameInfo->fMDIApp = FALSE; + lpFrameInfo->hwndFrame = window; + lpFrameInfo->haccel = 0; + lpFrameInfo->cAccelEntries = 0; + return S_OK; + } + + HRESULT __stdcall Scroll (SIZE) { return E_NOTIMPL; } + HRESULT __stdcall OnUIDeactivate (BOOL) { return S_OK; } + HRESULT __stdcall OnInPlaceDeactivate() { return S_OK; } + HRESULT __stdcall DiscardUndoState() { return E_NOTIMPL; } + HRESULT __stdcall DeactivateAndUndo() { return E_NOTIMPL; } + HRESULT __stdcall OnPosRectChange (LPCRECT) { return S_OK; } + + juce_UseDebuggingNewOperator +}; + +class JuceIOleClientSite : public IOleClientSite +{ + int refCount; + JuceIOleInPlaceSite* inplaceSite; + +public: + JuceIOleClientSite (HWND window) + : refCount (1) + { + inplaceSite = new JuceIOleInPlaceSite (window); + } + + virtual ~JuceIOleClientSite() + { + jassert (refCount == 0); + inplaceSite->Release(); + } + + HRESULT __stdcall QueryInterface (REFIID id, void __RPC_FAR* __RPC_FAR* result) + { + if (id == IID_IUnknown || id == IID_IOleClientSite) + { + AddRef(); + *result = this; + return S_OK; + } + else if (id == IID_IOleInPlaceSite) + { + inplaceSite->AddRef(); + *result = inplaceSite; + return S_OK; + } + + *result = 0; + return E_NOINTERFACE; + } + + ULONG __stdcall AddRef() { return ++refCount; } + ULONG __stdcall Release() { const int r = --refCount; if (r == 0) delete this; return r; } + + HRESULT __stdcall SaveObject() { return E_NOTIMPL; } + HRESULT __stdcall GetMoniker (DWORD, DWORD, IMoniker**) { return E_NOTIMPL; } + HRESULT __stdcall GetContainer (LPOLECONTAINER* ppContainer) { *ppContainer = 0; return E_NOINTERFACE; } + HRESULT __stdcall ShowObject() { return S_OK; } + HRESULT __stdcall OnShowWindow (BOOL) { return E_NOTIMPL; } + HRESULT __stdcall RequestNewObjectLayout() { return E_NOTIMPL; } + + juce_UseDebuggingNewOperator +}; + +class ActiveXControlData : public ComponentMovementWatcher +{ + ActiveXControlComponent* const owner; + bool wasShowing; + +public: + IStorage* storage; + IOleClientSite* clientSite; + IOleObject* control; + + ActiveXControlData (HWND hwnd, + ActiveXControlComponent* const owner_) + : ComponentMovementWatcher (owner_), + owner (owner_), + wasShowing (owner_ != 0 && owner_->isShowing()), + storage (new JuceIStorage()), + clientSite (new JuceIOleClientSite (hwnd)), + control (0) + { + } + + ~ActiveXControlData() + { + if (control != 0) + { + control->Close (OLECLOSE_NOSAVE); + control->Release(); + } + + clientSite->Release(); + storage->Release(); + } + + void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) + { + Component* const topComp = owner->getTopLevelComponent(); + + if (topComp->getPeer() != 0) + { + int x = 0, y = 0; + owner->relativePositionToOtherComponent (topComp, x, y); + + owner->setControlBounds (Rectangle (x, y, owner->getWidth(), owner->getHeight())); + } + } + + void componentPeerChanged() + { + const bool isShowingNow = owner->isShowing(); + + if (wasShowing != isShowingNow) + { + wasShowing = isShowingNow; + + owner->setControlVisible (isShowingNow); + } + } + + void componentVisibilityChanged (Component&) + { + componentPeerChanged(); + } +}; + +static VoidArray activeXComps; + +static HWND getHWND (const ActiveXControlComponent* const component) +{ + HWND hwnd = 0; + + const IID iid = IID_IOleWindow; + IOleWindow* const window = (IOleWindow*) component->queryInterface (&iid); + + if (window != 0) + { + window->GetWindow (&hwnd); + window->Release(); + } + + return hwnd; +} + +static void offerActiveXMouseEventToPeer (ComponentPeer* const peer, HWND hwnd, UINT message, LPARAM lParam) +{ + RECT activeXRect, peerRect; + GetWindowRect (hwnd, &activeXRect); + GetWindowRect ((HWND) peer->getNativeHandle(), &peerRect); + + const int mx = GET_X_LPARAM (lParam) + activeXRect.left - peerRect.left; + const int my = GET_Y_LPARAM (lParam) + activeXRect.top - peerRect.top; + const int64 mouseEventTime = getMouseEventTime(); + + const int oldModifiers = currentModifiers; + ModifierKeys::getCurrentModifiersRealtime(); // to update the mouse button flags + + switch (message) + { + case WM_MOUSEMOVE: + if (ModifierKeys (currentModifiers).isAnyMouseButtonDown()) + peer->handleMouseDrag (mx, my, mouseEventTime); + else + peer->handleMouseMove (mx, my, mouseEventTime); + break; + + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_RBUTTONDOWN: + peer->handleMouseDown (mx, my, mouseEventTime); + break; + + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: + peer->handleMouseUp (oldModifiers, mx, my, mouseEventTime); + break; + + default: + break; + } +} + +// intercepts events going to an activeX control, so we can sneakily use the mouse events +static LRESULT CALLBACK activeXHookWndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + for (int i = activeXComps.size(); --i >= 0;) + { + const ActiveXControlComponent* const ax = (const ActiveXControlComponent*) activeXComps.getUnchecked(i); + + HWND controlHWND = getHWND (ax); + + if (controlHWND == hwnd) + { + switch (message) + { + case WM_MOUSEMOVE: + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_RBUTTONDOWN: + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: + case WM_LBUTTONDBLCLK: + case WM_MBUTTONDBLCLK: + case WM_RBUTTONDBLCLK: + if (ax->isShowing()) + { + ComponentPeer* const peer = ax->getPeer(); + + if (peer != 0) + { + offerActiveXMouseEventToPeer (peer, hwnd, message, lParam); + + if (! ax->areMouseEventsAllowed()) + return 0; + } + } + break; + + default: + break; + } + + return CallWindowProc ((WNDPROC) (ax->originalWndProc), hwnd, message, wParam, lParam); + } + } + + return DefWindowProc (hwnd, message, wParam, lParam); +} + +ActiveXControlComponent::ActiveXControlComponent() + : originalWndProc (0), + control (0), + mouseEventsAllowed (true) +{ + activeXComps.add (this); +} + +ActiveXControlComponent::~ActiveXControlComponent() +{ + deleteControl(); + activeXComps.removeValue (this); +} + +void ActiveXControlComponent::paint (Graphics& g) +{ + if (control == 0) + g.fillAll (Colours::lightgrey); +} + +bool ActiveXControlComponent::createControl (const void* controlIID) +{ + deleteControl(); + ComponentPeer* const peer = getPeer(); + + // the component must have already been added to a real window when you call this! + jassert (dynamic_cast (peer) != 0); + + if (dynamic_cast (peer) != 0) + { + int x = 0, y = 0; + relativePositionToOtherComponent (getTopLevelComponent(), x, y); + + HWND hwnd = (HWND) peer->getNativeHandle(); + + ActiveXControlData* const info = new ActiveXControlData (hwnd, this); + + HRESULT hr; + if ((hr = OleCreate (*(const IID*) controlIID, IID_IOleObject, 1 /*OLERENDER_DRAW*/, 0, + info->clientSite, info->storage, + (void**) &(info->control))) == S_OK) + { + info->control->SetHostNames (L"Juce", 0); + + if (OleSetContainedObject (info->control, TRUE) == S_OK) + { + RECT rect; + rect.left = x; + rect.top = y; + rect.right = x + getWidth(); + rect.bottom = y + getHeight(); + + if (info->control->DoVerb (OLEIVERB_SHOW, 0, info->clientSite, 0, hwnd, &rect) == S_OK) + { + control = info; + setControlBounds (Rectangle (x, y, getWidth(), getHeight())); + + HWND controlHWND = getHWND (this); + + if (controlHWND != 0) + { + originalWndProc = (void*) GetWindowLongPtr (controlHWND, GWLP_WNDPROC); + SetWindowLongPtr (controlHWND, GWLP_WNDPROC, (LONG_PTR) activeXHookWndProc); + } + + return true; + } + } + } + + delete info; + } + + return false; +} + +void ActiveXControlComponent::deleteControl() +{ + ActiveXControlData* const info = (ActiveXControlData*) control; + + if (info != 0) + { + delete info; + control = 0; + originalWndProc = 0; + } +} + +void* ActiveXControlComponent::queryInterface (const void* iid) const +{ + ActiveXControlData* const info = (ActiveXControlData*) control; + + void* result = 0; + + if (info != 0 && info->control != 0 + && info->control->QueryInterface (*(const IID*) iid, &result) == S_OK) + return result; + + return 0; +} + +void ActiveXControlComponent::setControlBounds (const Rectangle& newBounds) const +{ + HWND hwnd = getHWND (this); + + if (hwnd != 0) + MoveWindow (hwnd, newBounds.getX(), newBounds.getY(), newBounds.getWidth(), newBounds.getHeight(), TRUE); +} + +void ActiveXControlComponent::setControlVisible (const bool shouldBeVisible) const +{ + HWND hwnd = getHWND (this); + + if (hwnd != 0) + ShowWindow (hwnd, shouldBeVisible ? SW_SHOWNA : SW_HIDE); +} + +void ActiveXControlComponent::setMouseEventsAllowed (const bool eventsCanReachControl) +{ + mouseEventsAllowed = eventsCanReachControl; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_win32_Windowing.cpp *********/ + +#endif + +/********* Start of inlined file: juce_win32_AutoLinkLibraries.h *********/ +// Auto-links to various win32 libs that are needed by library calls.. +#pragma comment(lib, "kernel32.lib") +#pragma comment(lib, "user32.lib") +#pragma comment(lib, "shell32.lib") +#pragma comment(lib, "gdi32.lib") +#pragma comment(lib, "vfw32.lib") +#pragma comment(lib, "comdlg32.lib") +#pragma comment(lib, "winmm.lib") +#pragma comment(lib, "wininet.lib") +#pragma comment(lib, "ole32.lib") +#pragma comment(lib, "advapi32.lib") +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "comsupp.lib") + +#if JUCE_OPENGL + #pragma comment(lib, "OpenGL32.Lib") + #pragma comment(lib, "GlU32.Lib") +#endif + +#if JUCE_QUICKTIME + #pragma comment (lib, "QTMLClient.lib") +#endif +/********* End of inlined file: juce_win32_AutoLinkLibraries.h *********/ + +#endif + +//============================================================================== +#if JUCE_LINUX + +/********* Start of inlined file: juce_linux_Files.cpp *********/ + +/********* Start of inlined file: linuxincludes.h *********/ +#ifndef __LINUXINCLUDES_JUCEHEADER__ +#define __LINUXINCLUDES_JUCEHEADER__ + +// Linux Header Files: +#include +#include +#include +#include +#include +#include + +/* Remove this macro if you're having problems compiling the cpu affinity + calls (the API for these has changed about quite a bit in various Linux + versions, and a lot of distros seem to ship with obsolete versions) +*/ +#ifndef SUPPORT_AFFINITIES + #define SUPPORT_AFFINITIES 1 +#endif + +#endif // __LINUXINCLUDES_JUCEHEADER__ +/********* End of inlined file: linuxincludes.h *********/ + +#include +#include +#include +#include // for statfs +#include +#include +#include +#include +#include +#include + +#define U_ISOFS_SUPER_MAGIC (short) 0x9660 // linux/iso_fs.h +#define U_MSDOS_SUPER_MAGIC (short) 0x4d44 // linux/msdos_fs.h +#define U_NFS_SUPER_MAGIC (short) 0x6969 // linux/nfs_fs.h +#define U_SMB_SUPER_MAGIC (short) 0x517B // linux/smb_fs.h + +BEGIN_JUCE_NAMESPACE + +/* + Note that a lot of methods that you'd expect to find in this file actually + live in juce_posix_SharedCode.cpp! +*/ + +/********* Start of inlined file: juce_posix_SharedCode.cpp *********/ +/* + This file contains posix routines that are common to both the Linux and Mac builds. + + It gets included directly in the cpp files for these platforms. +*/ + +CriticalSection::CriticalSection() throw() +{ + pthread_mutexattr_t atts; + pthread_mutexattr_init (&atts); + pthread_mutexattr_settype (&atts, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init (&internal, &atts); +} + +CriticalSection::~CriticalSection() throw() +{ + pthread_mutex_destroy (&internal); +} + +void CriticalSection::enter() const throw() +{ + pthread_mutex_lock (&internal); +} + +bool CriticalSection::tryEnter() const throw() +{ + return pthread_mutex_trylock (&internal) == 0; +} + +void CriticalSection::exit() const throw() +{ + pthread_mutex_unlock (&internal); +} + +struct EventStruct +{ + pthread_cond_t condition; + pthread_mutex_t mutex; + bool triggered; +}; + +WaitableEvent::WaitableEvent() throw() +{ + EventStruct* const es = new EventStruct(); + es->triggered = false; + + pthread_cond_init (&es->condition, 0); + pthread_mutex_init (&es->mutex, 0); + + internal = es; +} + +WaitableEvent::~WaitableEvent() throw() +{ + EventStruct* const es = (EventStruct*) internal; + + pthread_cond_destroy (&es->condition); + pthread_mutex_destroy (&es->mutex); + + delete es; +} + +bool WaitableEvent::wait (const int timeOutMillisecs) const throw() +{ + EventStruct* const es = (EventStruct*) internal; + + bool ok = true; + pthread_mutex_lock (&es->mutex); + + if (! es->triggered) + { + if (timeOutMillisecs < 0) + { + pthread_cond_wait (&es->condition, &es->mutex); + } + else + { + struct timespec time; + +#if JUCE_MAC + time.tv_sec = timeOutMillisecs / 1000; + time.tv_nsec = (timeOutMillisecs % 1000) * 1000000; + pthread_cond_timedwait_relative_np (&es->condition, &es->mutex, &time); +#else + struct timeval t; + int timeout = 0; + + gettimeofday (&t, 0); + + time.tv_sec = t.tv_sec + (timeOutMillisecs / 1000); + time.tv_nsec = (t.tv_usec + ((timeOutMillisecs % 1000) * 1000)) * 1000; + + while (time.tv_nsec >= 1000000000) + { + time.tv_nsec -= 1000000000; + time.tv_sec++; + } + + while (! timeout) + { + timeout = pthread_cond_timedwait (&es->condition, &es->mutex, &time); + + if (! timeout) + // Success + break; + + if (timeout == EINTR) + // Go round again + timeout = 0; + } +#endif + } + + ok = es->triggered; + } + + es->triggered = false; + + pthread_mutex_unlock (&es->mutex); + return ok; +} + +void WaitableEvent::signal() const throw() +{ + EventStruct* const es = (EventStruct*) internal; + + pthread_mutex_lock (&es->mutex); + es->triggered = true; + pthread_cond_broadcast (&es->condition); + pthread_mutex_unlock (&es->mutex); +} + +void WaitableEvent::reset() const throw() +{ + EventStruct* const es = (EventStruct*) internal; + + pthread_mutex_lock (&es->mutex); + es->triggered = false; + pthread_mutex_unlock (&es->mutex); +} + +void JUCE_CALLTYPE Thread::sleep (int millisecs) throw() +{ + struct timespec time; + time.tv_sec = millisecs / 1000; + time.tv_nsec = (millisecs % 1000) * 1000000; + nanosleep (&time, 0); +} + +const tchar File::separator = T('/'); +const tchar* File::separatorString = T("/"); + +bool juce_copyFile (const String& s, const String& d) throw(); + +static bool juce_stat (const String& fileName, struct stat& info) throw() +{ + return fileName.isNotEmpty() + && (stat (fileName.toUTF8(), &info) == 0); +} + +bool juce_isDirectory (const String& fileName) throw() +{ + struct stat info; + + return fileName.isEmpty() + || (juce_stat (fileName, info) + && ((info.st_mode & S_IFDIR) != 0)); +} + +bool juce_fileExists (const String& fileName, const bool dontCountDirectories) throw() +{ + if (fileName.isEmpty()) + return false; + + const char* const fileNameUTF8 = fileName.toUTF8(); + bool exists = access (fileNameUTF8, F_OK) == 0; + + if (exists && dontCountDirectories) + { + struct stat info; + const int res = stat (fileNameUTF8, &info); + + if (res == 0 && (info.st_mode & S_IFDIR) != 0) + exists = false; + } + + return exists; +} + +int64 juce_getFileSize (const String& fileName) throw() +{ + struct stat info; + return juce_stat (fileName, info) ? info.st_size : 0; +} + +bool juce_canWriteToFile (const String& fileName) throw() +{ + return access (fileName.toUTF8(), W_OK) == 0; +} + +bool juce_deleteFile (const String& fileName) throw() +{ + const char* const fileNameUTF8 = fileName.toUTF8(); + + if (juce_isDirectory (fileName)) + return rmdir (fileNameUTF8) == 0; + else + return remove (fileNameUTF8) == 0; +} + +bool juce_moveFile (const String& source, const String& dest) throw() +{ + if (rename (source.toUTF8(), dest.toUTF8()) == 0) + return true; + + if (juce_canWriteToFile (source) + && juce_copyFile (source, dest)) + { + if (juce_deleteFile (source)) + return true; + + juce_deleteFile (dest); + } + + return false; +} + +void juce_createDirectory (const String& fileName) throw() +{ + mkdir (fileName.toUTF8(), 0777); +} + +void* juce_fileOpen (const String& fileName, bool forWriting) throw() +{ + const char* const fileNameUTF8 = fileName.toUTF8(); + int flags = O_RDONLY; + + if (forWriting) + { + if (juce_fileExists (fileName, false)) + { + const int f = open (fileNameUTF8, O_RDWR, 00644); + + if (f != -1) + lseek (f, 0, SEEK_END); + + return (void*) f; + } + else + { + flags = O_RDWR + O_CREAT; + } + } + + return (void*) open (fileNameUTF8, flags, 00644); +} + +void juce_fileClose (void* handle) throw() +{ + if (handle != 0) + close ((int) (pointer_sized_int) handle); +} + +int juce_fileRead (void* handle, void* buffer, int size) throw() +{ + if (handle != 0) + return read ((int) (pointer_sized_int) handle, buffer, size); + + return 0; +} + +int juce_fileWrite (void* handle, const void* buffer, int size) throw() +{ + if (handle != 0) + return write ((int) (pointer_sized_int) handle, buffer, size); + + return 0; +} + +int64 juce_fileSetPosition (void* handle, int64 pos) throw() +{ + if (handle != 0 && lseek ((int) (pointer_sized_int) handle, pos, SEEK_SET) == pos) + return pos; + + return -1; +} + +int64 juce_fileGetPosition (void* handle) throw() +{ + if (handle != 0) + return lseek ((int) (pointer_sized_int) handle, 0, SEEK_CUR); + else + return -1; +} + +void juce_fileFlush (void* handle) throw() +{ + if (handle != 0) + fsync ((int) (pointer_sized_int) handle); +} + +// if this file doesn't exist, find a parent of it that does.. +static bool doStatFS (const File* file, struct statfs& result) throw() +{ + File f (*file); + + for (int i = 5; --i >= 0;) + { + if (f.exists()) + break; + + f = f.getParentDirectory(); + } + + return statfs (f.getFullPathName().toUTF8(), &result) == 0; +} + +int64 File::getBytesFreeOnVolume() const throw() +{ + int64 free_space = 0; + + struct statfs buf; + if (doStatFS (this, buf)) + // Note: this returns space available to non-super user + free_space = (int64) buf.f_bsize * (int64) buf.f_bavail; + + return free_space; +} + +const String juce_getVolumeLabel (const String& filenameOnVolume, + int& volumeSerialNumber) throw() +{ + // There is no equivalent on Linux + volumeSerialNumber = 0; + return String::empty; +} + +#if JUCE_64BIT + #define filedesc ((long long) internal) +#else + #define filedesc ((int) internal) +#endif + +InterProcessLock::InterProcessLock (const String& name_) throw() + : internal (0), + name (name_), + reentrancyLevel (0) +{ +#if JUCE_MAC + // (don't use getSpecialLocation() to avoid the temp folder being different for each app) + const File temp (File (T("~/Library/Caches/Juce")).getChildFile (name)); +#else + const File temp (File::getSpecialLocation (File::tempDirectory).getChildFile (name)); +#endif + + temp.create(); + + internal = (void*) open (temp.getFullPathName().toUTF8(), O_RDWR); +} + +InterProcessLock::~InterProcessLock() throw() +{ + while (reentrancyLevel > 0) + this->exit(); + + close (filedesc); +} + +bool InterProcessLock::enter (const int timeOutMillisecs) throw() +{ + if (internal == 0) + return false; + + if (reentrancyLevel != 0) + return true; + + const int64 endTime = Time::currentTimeMillis() + timeOutMillisecs; + + struct flock fl; + zerostruct (fl); + fl.l_whence = SEEK_SET; + fl.l_type = F_WRLCK; + + for (;;) + { + const int result = fcntl (filedesc, F_SETLK, &fl); + + if (result >= 0) + { + ++reentrancyLevel; + return true; + } + + if (errno != EINTR) + { + if (timeOutMillisecs == 0 + || (timeOutMillisecs > 0 && Time::currentTimeMillis() >= endTime)) + break; + + Thread::sleep (10); + } + } + + return false; +} + +void InterProcessLock::exit() throw() +{ + if (reentrancyLevel > 0 && internal != 0) + { + --reentrancyLevel; + + struct flock fl; + zerostruct (fl); + fl.l_whence = SEEK_SET; + fl.l_type = F_UNLCK; + + for (;;) + { + const int result = fcntl (filedesc, F_SETLKW, &fl); + + if (result >= 0 || errno != EINTR) + break; + } + } +} +/********* End of inlined file: juce_posix_SharedCode.cpp *********/ + +static File executableFile; + +void juce_getFileTimes (const String& fileName, + int64& modificationTime, + int64& accessTime, + int64& creationTime) throw() +{ + modificationTime = 0; + accessTime = 0; + creationTime = 0; + + struct stat info; + const int res = stat (fileName.toUTF8(), &info); + if (res == 0) + { + modificationTime = (int64) info.st_mtime * 1000; + accessTime = (int64) info.st_atime * 1000; + creationTime = (int64) info.st_ctime * 1000; + } +} + +bool juce_setFileTimes (const String& fileName, + int64 modificationTime, + int64 accessTime, + int64 creationTime) throw() +{ + struct utimbuf times; + times.actime = (time_t) (accessTime / 1000); + times.modtime = (time_t) (modificationTime / 1000); + + return utime (fileName.toUTF8(), ×) == 0; +} + +bool juce_setFileReadOnly (const String& fileName, bool isReadOnly) throw() +{ + struct stat info; + const int res = stat (fileName.toUTF8(), &info); + if (res != 0) + return false; + + info.st_mode &= 0777; // Just permissions + + if( isReadOnly ) + info.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); + else + // Give everybody write permission? + info.st_mode |= S_IWUSR | S_IWGRP | S_IWOTH; + + return chmod (fileName.toUTF8(), info.st_mode) == 0; +} + +bool juce_copyFile (const String& s, const String& d) throw() +{ + const File source (s), dest (d); + + FileInputStream* in = source.createInputStream(); + bool ok = false; + + if (in != 0) + { + if (dest.deleteFile()) + { + FileOutputStream* const out = dest.createOutputStream(); + + if (out != 0) + { + const int bytesCopied = out->writeFromInputStream (*in, -1); + delete out; + + ok = (bytesCopied == source.getSize()); + + if (! ok) + dest.deleteFile(); + } + } + + delete in; + } + + return ok; +} + +const StringArray juce_getFileSystemRoots() throw() +{ + StringArray s; + s.add (T("/")); + return s; +} + +bool File::isOnCDRomDrive() const throw() +{ + struct statfs buf; + + if (statfs (getFullPathName().toUTF8(), &buf) == 0) + return (buf.f_type == U_ISOFS_SUPER_MAGIC); + + // Assume not if this fails for some reason + return false; +} + +bool File::isOnHardDisk() const throw() +{ + struct statfs buf; + + if (statfs (getFullPathName().toUTF8(), &buf) == 0) + { + switch (buf.f_type) + { + case U_ISOFS_SUPER_MAGIC: // CD-ROM + case U_MSDOS_SUPER_MAGIC: // Probably floppy (but could be mounted FAT filesystem) + case U_NFS_SUPER_MAGIC: // Network NFS + case U_SMB_SUPER_MAGIC: // Network Samba + return false; + + default: + // Assume anything else is a hard-disk (but note it could + // be a RAM disk. There isn't a good way of determining + // this for sure) + return true; + } + } + + // Assume so if this fails for some reason + return true; +} + +bool File::isHidden() const throw() +{ + return getFileName().startsWithChar (T('.')); +} + +const File File::getSpecialLocation (const SpecialLocationType type) +{ + switch (type) + { + case userHomeDirectory: + { + const char* homeDir = getenv ("HOME"); + + if (homeDir == 0) + { + struct passwd* const pw = getpwuid (getuid()); + if (pw != 0) + homeDir = pw->pw_dir; + } + + return File (String::fromUTF8 ((const uint8*) homeDir)); + } + + case userDocumentsDirectory: + case userMusicDirectory: + case userMoviesDirectory: + case userApplicationDataDirectory: + return File ("~"); + + case userDesktopDirectory: + return File ("~/Desktop"); + + case commonApplicationDataDirectory: + return File ("/var"); + + case globalApplicationsDirectory: + return File ("/usr"); + + case tempDirectory: + { + File tmp ("/var/tmp"); + + if (! tmp.isDirectory()) + { + tmp = T("/tmp"); + + if (! tmp.isDirectory()) + tmp = File::getCurrentWorkingDirectory(); + } + + return tmp; + } + + case currentExecutableFile: + case currentApplicationFile: + // if this fails, it's probably because juce_setCurrentExecutableFileName() + // was never called to set the filename - this should be done by the juce + // main() function, so maybe you've hacked it to use your own custom main()? + jassert (executableFile.exists()); + + return executableFile; + + default: + jassertfalse // unknown type? + break; + } + + return File::nonexistent; +} + +void juce_setCurrentExecutableFileName (const String& filename) throw() +{ + executableFile = File::getCurrentWorkingDirectory().getChildFile (filename); +} + +const File File::getCurrentWorkingDirectory() throw() +{ + char buf [2048]; + getcwd (buf, sizeof(buf)); + return File (String::fromUTF8 ((const uint8*) buf)); +} + +bool File::setAsCurrentWorkingDirectory() const throw() +{ + return chdir (getFullPathName().toUTF8()) == 0; +} + +struct FindFileStruct +{ + String parentDir, wildCard; + DIR* dir; + + bool getNextMatch (String& result, bool* const isDir, bool* const isHidden, int64* const fileSize, + Time* const modTime, Time* const creationTime, bool* const isReadOnly) throw() + { + const char* const wildcardUTF8 = wildCard.toUTF8(); + + for (;;) + { + struct dirent* const de = readdir (dir); + + if (de == 0) + break; + + if (fnmatch (wildcardUTF8, de->d_name, FNM_CASEFOLD) == 0) + { + result = String::fromUTF8 ((const uint8*) de->d_name); + + const String path (parentDir + result); + + if (isDir != 0 || fileSize != 0) + { + struct stat info; + const bool statOk = (stat (path.toUTF8(), &info) == 0); + + if (isDir != 0) + *isDir = path.isEmpty() || (statOk && ((info.st_mode & S_IFDIR) != 0)); + + if (isHidden != 0) + *isHidden = (de->d_name[0] == '.'); + + if (fileSize != 0) + *fileSize = statOk ? info.st_size : 0; + } + + if (modTime != 0 || creationTime != 0) + { + int64 m, a, c; + juce_getFileTimes (path, m, a, c); + + if (modTime != 0) + *modTime = m; + + if (creationTime != 0) + *creationTime = c; + } + + if (isReadOnly != 0) + *isReadOnly = ! juce_canWriteToFile (path); + + return true; + } + } + + return false; + } +}; + +// returns 0 on failure +void* juce_findFileStart (const String& directory, const String& wildCard, String& firstResultFile, + bool* isDir, bool* isHidden, int64* fileSize, Time* modTime, + Time* creationTime, bool* isReadOnly) throw() +{ + DIR* d = opendir (directory.toUTF8()); + + if (d != 0) + { + FindFileStruct* ff = new FindFileStruct(); + ff->parentDir = directory; + + if (!ff->parentDir.endsWithChar (File::separator)) + ff->parentDir += File::separator; + + ff->wildCard = wildCard; + if (wildCard == T("*.*")) + ff->wildCard = T("*"); + + ff->dir = d; + + if (ff->getNextMatch (firstResultFile, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly)) + { + return ff; + } + else + { + firstResultFile = String::empty; + isDir = false; + isHidden = false; + closedir (d); + delete ff; + } + } + + return 0; +} + +bool juce_findFileNext (void* handle, String& resultFile, + bool* isDir, bool* isHidden, int64* fileSize, Time* modTime, Time* creationTime, bool* isReadOnly) throw() +{ + FindFileStruct* const ff = (FindFileStruct*) handle; + + if (ff != 0) + return ff->getNextMatch (resultFile, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly); + + return false; +} + +void juce_findFileClose (void* handle) throw() +{ + FindFileStruct* const ff = (FindFileStruct*) handle; + + if (ff != 0) + { + closedir (ff->dir); + delete ff; + } +} + +bool juce_launchFile (const String& fileName, + const String& parameters) throw() +{ + String cmdString (fileName); + cmdString << " " << parameters; + + if (URL::isProbablyAWebsiteURL (cmdString) || URL::isProbablyAnEmailAddress (cmdString)) + { + // create a command that tries to launch a bunch of likely browsers + const char* const browserNames[] = { "/etc/alternatives/x-www-browser", "firefox", "mozilla", "konqueror", "opera" }; + + StringArray cmdLines; + + for (int i = 0; i < numElementsInArray (browserNames); ++i) + cmdLines.add (String (browserNames[i]) + T(" ") + cmdString.trim().quoted()); + + cmdString = cmdLines.joinIntoString (T(" || ")); + } + + char* const argv[4] = { "/bin/sh", "-c", (char*) cmdString.toUTF8(), 0 }; + + const int cpid = fork(); + + if (cpid == 0) + { + setsid(); + + // Child process + execve (argv[0], argv, environ); + exit (0); + } + + return cpid >= 0; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_linux_Files.cpp *********/ + +/********* Start of inlined file: juce_linux_NamedPipe.cpp *********/ + +/********* Start of inlined file: juce_mac_NamedPipe.cpp *********/ + +#include +#include +#include + +// As well as being for the mac, this file is included by the linux build. + +#if JUCE_MAC + #include +#else + #include + #include + #include +#endif + +BEGIN_JUCE_NAMESPACE + +struct NamedPipeInternal +{ + String pipeInName, pipeOutName; + int pipeIn, pipeOut; + + bool volatile createdPipe, blocked, stopReadOperation; + + static void signalHandler (int) {} +}; + +void NamedPipe::cancelPendingReads() +{ + while (internal != 0 && ((NamedPipeInternal*) internal)->blocked) + { + NamedPipeInternal* const intern = (NamedPipeInternal*) internal; + + intern->stopReadOperation = true; + + char buffer [1] = { 0 }; + ::write (intern->pipeIn, buffer, 1); + + int timeout = 2000; + while (intern->blocked && --timeout >= 0) + sleep (2); + + intern->stopReadOperation = false; + } +} + +void NamedPipe::close() +{ + NamedPipeInternal* const intern = (NamedPipeInternal*) internal; + + if (intern != 0) + { + internal = 0; + + if (intern->pipeIn != -1) + ::close (intern->pipeIn); + + if (intern->pipeOut != -1) + ::close (intern->pipeOut); + + if (intern->createdPipe) + { + unlink (intern->pipeInName); + unlink (intern->pipeOutName); + } + + delete intern; + } +} + +bool NamedPipe::openInternal (const String& pipeName, const bool createPipe) +{ + close(); + + NamedPipeInternal* const intern = new NamedPipeInternal(); + internal = intern; + intern->createdPipe = createPipe; + intern->blocked = false; + intern->stopReadOperation = false; + + signal (SIGPIPE, NamedPipeInternal::signalHandler); + siginterrupt (SIGPIPE, 1); + + const String pipePath (T("/tmp/") + File::createLegalFileName (pipeName)); + + intern->pipeInName = pipePath + T("_in"); + intern->pipeOutName = pipePath + T("_out"); + intern->pipeIn = -1; + intern->pipeOut = -1; + + if (createPipe) + { + if ((mkfifo (intern->pipeInName, 0666) && errno != EEXIST) + || (mkfifo (intern->pipeOutName, 0666) && errno != EEXIST)) + { + delete intern; + internal = 0; + + return false; + } + } + + return true; +} + +int NamedPipe::read (void* destBuffer, int maxBytesToRead, int /*timeOutMilliseconds*/) +{ + int bytesRead = -1; + NamedPipeInternal* const intern = (NamedPipeInternal*) internal; + + if (intern != 0) + { + intern->blocked = true; + + if (intern->pipeIn == -1) + { + if (intern->createdPipe) + intern->pipeIn = ::open (intern->pipeInName, O_RDWR); + else + intern->pipeIn = ::open (intern->pipeOutName, O_RDWR); + + if (intern->pipeIn == -1) + { + intern->blocked = false; + return -1; + } + } + + bytesRead = 0; + + char* p = (char*) destBuffer; + + while (bytesRead < maxBytesToRead) + { + const int bytesThisTime = maxBytesToRead - bytesRead; + const int numRead = ::read (intern->pipeIn, p, bytesThisTime); + + if (numRead <= 0 || intern->stopReadOperation) + { + bytesRead = -1; + break; + } + + bytesRead += numRead; + p += bytesRead; + } + + intern->blocked = false; + } + + return bytesRead; +} + +int NamedPipe::write (const void* sourceBuffer, int numBytesToWrite, int timeOutMilliseconds) +{ + int bytesWritten = -1; + NamedPipeInternal* const intern = (NamedPipeInternal*) internal; + + if (intern != 0) + { + if (intern->pipeOut == -1) + { + if (intern->createdPipe) + intern->pipeOut = ::open (intern->pipeOutName, O_WRONLY); + else + intern->pipeOut = ::open (intern->pipeInName, O_WRONLY); + + if (intern->pipeOut == -1) + { + return -1; + } + } + + const char* p = (const char*) sourceBuffer; + bytesWritten = 0; + + const uint32 timeOutTime = Time::getMillisecondCounter() + timeOutMilliseconds; + + while (bytesWritten < numBytesToWrite + && (timeOutMilliseconds < 0 || Time::getMillisecondCounter() < timeOutTime)) + { + const int bytesThisTime = numBytesToWrite - bytesWritten; + const int numWritten = ::write (intern->pipeOut, p, bytesThisTime); + + if (numWritten <= 0) + { + bytesWritten = -1; + break; + } + + bytesWritten += numWritten; + p += bytesWritten; + } + } + + return bytesWritten; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_mac_NamedPipe.cpp *********/ + +/********* End of inlined file: juce_linux_NamedPipe.cpp *********/ + +/********* Start of inlined file: juce_linux_Network.cpp *********/ + +#include +#include +#include +#include +#include +#include +#include +#include + +BEGIN_JUCE_NAMESPACE + +// we'll borrow the mac's socket-based http streaming code.. + +/********* Start of inlined file: juce_mac_HTTPStream.h *********/ +// (This file gets included by the mac + linux networking code) + +/** A HTTP input stream that uses sockets. +*/ +class JUCE_HTTPSocketStream +{ +public: + + JUCE_HTTPSocketStream() + : readPosition (0), + socketHandle (-1), + levelsOfRedirection (0), + timeoutSeconds (15) + { + } + + ~JUCE_HTTPSocketStream() + { + closeSocket(); + } + + bool open (const String& url, + const String& headers, + const MemoryBlock& postData, + const bool isPost, + URL::OpenStreamProgressCallback* callback, + void* callbackContext) + { + closeSocket(); + + String hostName, hostPath; + int hostPort; + + if (! decomposeURL (url, hostName, hostPath, hostPort)) + return false; + + struct hostent* const host + = gethostbyname ((const char*) hostName.toUTF8()); + + if (host == 0) + return false; + + struct sockaddr_in address; + zerostruct (address); + memcpy ((void*) &address.sin_addr, (const void*) host->h_addr, host->h_length); + address.sin_family = host->h_addrtype; + address.sin_port = htons (hostPort); + + socketHandle = socket (host->h_addrtype, SOCK_STREAM, 0); + + if (socketHandle == -1) + return false; + + int receiveBufferSize = 16384; + setsockopt (socketHandle, SOL_SOCKET, SO_RCVBUF, (char*) &receiveBufferSize, sizeof (receiveBufferSize)); + setsockopt (socketHandle, SOL_SOCKET, SO_KEEPALIVE, 0, 0); + +#if JUCE_MAC + setsockopt (socketHandle, SOL_SOCKET, SO_NOSIGPIPE, 0, 0); +#endif + + if (connect (socketHandle, (struct sockaddr*) &address, sizeof (address)) == -1) + { + closeSocket(); + return false; + } + + String proxyURL (getenv ("http_proxy")); + + if (! proxyURL.startsWithIgnoreCase (T("http://"))) + proxyURL = String::empty; + + const MemoryBlock requestHeader (createRequestHeader (hostName, hostPath, + proxyURL, url, + hostPort, + headers, postData, + isPost)); + + int totalHeaderSent = 0; + + while (totalHeaderSent < requestHeader.getSize()) + { + const int numToSend = jmin (1024, requestHeader.getSize() - totalHeaderSent); + + if (send (socketHandle, + ((const char*) requestHeader.getData()) + totalHeaderSent, + numToSend, 0) + != numToSend) + { + closeSocket(); + return false; + } + + totalHeaderSent += numToSend; + + if (callback != 0 && ! callback (callbackContext, totalHeaderSent, requestHeader.getSize())) + { + closeSocket(); + return false; + } + } + + const String responseHeader (readResponse()); + + if (responseHeader.isNotEmpty()) + { + //DBG (responseHeader); + + StringArray lines; + lines.addLines (responseHeader); + + // NB - using charToString() here instead of just T(" "), because that was + // causing a mysterious gcc internal compiler error... + const int statusCode = responseHeader.fromFirstOccurrenceOf (String::charToString (T(' ')), false, false) + .substring (0, 3) + .getIntValue(); + + //int contentLength = findHeaderItem (lines, T("Content-Length:")).getIntValue(); + //bool isChunked = findHeaderItem (lines, T("Transfer-Encoding:")).equalsIgnoreCase ("chunked"); + + String location (findHeaderItem (lines, T("Location:"))); + + if (statusCode >= 300 && statusCode < 400 + && location.isNotEmpty()) + { + if (! location.startsWithIgnoreCase (T("http://"))) + location = T("http://") + location; + + if (levelsOfRedirection++ < 3) + return open (location, headers, postData, isPost, callback, callbackContext); + } + else + { + levelsOfRedirection = 0; + return true; + } + } + + closeSocket(); + return false; + } + + int read (void* buffer, int bytesToRead) + { + fd_set readbits; + FD_ZERO (&readbits); + FD_SET (socketHandle, &readbits); + + struct timeval tv; + tv.tv_sec = timeoutSeconds; + tv.tv_usec = 0; + + if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0) + return 0; // (timeout) + + const int bytesRead = jmax (0, recv (socketHandle, buffer, bytesToRead, MSG_WAITALL)); + readPosition += bytesRead; + return bytesRead; + } + + int readPosition; + + juce_UseDebuggingNewOperator + +private: + int socketHandle, levelsOfRedirection; + const int timeoutSeconds; + + void closeSocket() + { + if (socketHandle >= 0) + close (socketHandle); + + socketHandle = -1; + } + + const MemoryBlock createRequestHeader (const String& hostName, + const String& hostPath, + const String& proxyURL, + const String& originalURL, + const int hostPort, + const String& headers, + const MemoryBlock& postData, + const bool isPost) + { + String header (isPost ? "POST " : "GET "); + + if (proxyURL.isEmpty()) + { + header << hostPath << " HTTP/1.0\r\nHost: " + << hostName << ':' << hostPort; + } + else + { + String proxyName, proxyPath; + int proxyPort; + + if (! decomposeURL (proxyURL, proxyName, proxyPath, proxyPort)) + return MemoryBlock(); + + header << originalURL << " HTTP/1.0\r\nHost: " + << proxyName << ':' << proxyPort; + + /* xxx needs finishing + const char* proxyAuth = getenv ("http_proxy_auth"); + if (proxyAuth != 0) + header << T("\r\nProxy-Authorization: ") << Base64Encode (proxyAuth); + */ + } + + header << "\r\nUser-Agent: JUCE/" + << JUCE_MAJOR_VERSION << '.' << JUCE_MINOR_VERSION + << "\r\nConnection: Close\r\nContent-Length: " + << postData.getSize() << "\r\n" + << headers << "\r\n"; + + MemoryBlock mb; + mb.append (header.toUTF8(), (int) strlen (header.toUTF8())); + mb.append (postData.getData(), postData.getSize()); + + return mb; + } + + const String readResponse() + { + int bytesRead = 0, numConsecutiveLFs = 0; + MemoryBlock buffer (1024, true); + + while (numConsecutiveLFs < 2 && bytesRead < 32768) + { + fd_set readbits; + FD_ZERO (&readbits); + FD_SET (socketHandle, &readbits); + + struct timeval tv; + tv.tv_sec = timeoutSeconds; + tv.tv_usec = 0; + + if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0) + return String::empty; // (timeout) + + buffer.ensureSize (bytesRead + 8, true); + char* const dest = (char*) buffer.getData() + bytesRead; + + if (recv (socketHandle, dest, 1, 0) == -1) + return String::empty; + + const char lastByte = *dest; + ++bytesRead; + + if (lastByte == '\n') + ++numConsecutiveLFs; + else if (lastByte != '\r') + numConsecutiveLFs = 0; + } + + const String header (String::fromUTF8 ((const uint8*) buffer.getData())); + + if (header.startsWithIgnoreCase (T("HTTP/"))) + return header.trimEnd(); + + return String::empty; + } + + static bool decomposeURL (const String& url, + String& host, String& path, int& port) + { + if (! url.startsWithIgnoreCase (T("http://"))) + return false; + + const int nextSlash = url.indexOfChar (7, '/'); + int nextColon = url.indexOfChar (7, ':'); + if (nextColon > nextSlash && nextSlash > 0) + nextColon = -1; + + if (nextColon >= 0) + { + host = url.substring (7, nextColon); + + if (nextSlash >= 0) + port = url.substring (nextColon + 1, nextSlash).getIntValue(); + else + port = url.substring (nextColon + 1).getIntValue(); + } + else + { + port = 80; + + if (nextSlash >= 0) + host = url.substring (7, nextSlash); + else + host = url.substring (7); + } + + if (nextSlash >= 0) + path = url.substring (nextSlash); + else + path = T("/"); + + return true; + } + + static const String findHeaderItem (const StringArray& lines, const String& itemName) + { + for (int i = 0; i < lines.size(); ++i) + if (lines[i].startsWithIgnoreCase (itemName)) + return lines[i].substring (itemName.length()).trim(); + + return String::empty; + } +}; + +bool juce_isOnLine() +{ + return true; +} + +void* juce_openInternetFile (const String& url, + const String& headers, + const MemoryBlock& postData, + const bool isPost, + URL::OpenStreamProgressCallback* callback, + void* callbackContext) +{ + JUCE_HTTPSocketStream* const s = new JUCE_HTTPSocketStream(); + + if (s->open (url, headers, postData, isPost, + callback, callbackContext)) + return s; + + delete s; + return 0; +} + +void juce_closeInternetFile (void* handle) +{ + JUCE_HTTPSocketStream* const s = (JUCE_HTTPSocketStream*) handle; + + if (s != 0) + delete s; +} + +int juce_readFromInternetFile (void* handle, void* buffer, int bytesToRead) +{ + JUCE_HTTPSocketStream* const s = (JUCE_HTTPSocketStream*) handle; + + if (s != 0) + return s->read (buffer, bytesToRead); + + return 0; +} + +int juce_seekInInternetFile (void* handle, int newPosition) +{ + JUCE_HTTPSocketStream* const s = (JUCE_HTTPSocketStream*) handle; + + if (s != 0) + return s->readPosition; + + return 0; +} + +/********* End of inlined file: juce_mac_HTTPStream.h *********/ + +int SystemStats::getMACAddresses (int64* addresses, int maxNum, const bool littleEndian) throw() +{ + int numResults = 0; + + const int s = socket (AF_INET, SOCK_DGRAM, 0); + if (s != -1) + { + char buf [1024]; + struct ifconf ifc; + ifc.ifc_len = sizeof (buf); + ifc.ifc_buf = buf; + ioctl (s, SIOCGIFCONF, &ifc); + + for (unsigned int i = 0; i < ifc.ifc_len / sizeof (struct ifreq); ++i) + { + struct ifreq ifr; + strcpy (ifr.ifr_name, ifc.ifc_req[i].ifr_name); + + if (ioctl (s, SIOCGIFFLAGS, &ifr) == 0 + && (ifr.ifr_flags & IFF_LOOPBACK) == 0 + && ioctl (s, SIOCGIFHWADDR, &ifr) == 0 + && numResults < maxNum) + { + int64 a = 0; + for (int j = 6; --j >= 0;) + a = (a << 8) | ifr.ifr_hwaddr.sa_data[j]; + + *addresses++ = a; + ++numResults; + } + } + + close (s); + } + + return numResults; +} + +bool PlatformUtilities::launchEmailWithAttachments (const String& targetEmailAddress, + const String& emailSubject, + const String& bodyText, + const StringArray& filesToAttach) +{ + jassertfalse // xxx todo + + return false; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_linux_Network.cpp *********/ + +/********* Start of inlined file: juce_linux_SystemStats.cpp *********/ + +#include +#include + +#ifndef CPU_ISSET + #undef SUPPORT_AFFINITIES +#endif + +BEGIN_JUCE_NAMESPACE + +/*static juce_noinline unsigned int getCPUIDWord (int* familyModel, int* extFeatures) throw() +{ + unsigned int cpu = 0; + unsigned int ext = 0; + unsigned int family = 0; + unsigned int dummy = 0; + +#if JUCE_64BIT + __asm__ ("cpuid" + : "=a" (family), "=b" (ext), "=c" (dummy), "=d" (cpu) : "a" (1)); + +#else + __asm__ ("push %%ebx; cpuid; mov %%ebx, %%edi; pop %%ebx" + : "=a" (family), "=D" (ext), "=c" (dummy), "=d" (cpu) : "a" (1)); +#endif + + if (familyModel != 0) + *familyModel = family; + + if (extFeatures != 0) + *extFeatures = ext; + + return cpu; +}*/ + +void Logger::outputDebugString (const String& text) throw() +{ + fprintf (stdout, text.toUTF8()); + fprintf (stdout, "\n"); +} + +void Logger::outputDebugPrintf (const tchar* format, ...) throw() +{ + String text; + va_list args; + va_start (args, format); + text.vprintf(format, args); + outputDebugString(text); +} + +SystemStats::OperatingSystemType SystemStats::getOperatingSystemType() throw() +{ + return Linux; +} + +const String SystemStats::getOperatingSystemName() throw() +{ + return T("Linux"); +} + +bool SystemStats::isOperatingSystem64Bit() throw() +{ +#if JUCE_64BIT + return true; +#else + //xxx not sure how to find this out?.. + return false; +#endif +} + +static const String getCpuInfo (const char* key, bool lastOne = false) throw() +{ + String info; + char buf [256]; + + FILE* f = fopen ("/proc/cpuinfo", "r"); + + while (f != 0 && fgets (buf, sizeof(buf), f)) + { + if (strncmp (buf, key, strlen (key)) == 0) + { + char* p = buf; + + while (*p && *p != '\n') + ++p; + + if (*p != 0) + *p = 0; + + p = buf; + + while (*p != 0 && *p != ':') + ++p; + + if (*p != 0 && *(p + 1) != 0) + info = p + 2; + + if (! lastOne) + break; + } + } + + fclose (f); + return info; +} + +bool SystemStats::hasMMX() throw() +{ + return getCpuInfo ("flags").contains (T("mmx")); +} + +bool SystemStats::hasSSE() throw() +{ + return getCpuInfo ("flags").contains (T("sse")); +} + +bool SystemStats::hasSSE2() throw() +{ + return getCpuInfo ("flags").contains (T("sse2")); +} + +bool SystemStats::has3DNow() throw() +{ + return getCpuInfo ("flags").contains (T("3dnow")); +} + +const String SystemStats::getCpuVendor() throw() +{ + return getCpuInfo ("vendor_id"); +} + +int SystemStats::getCpuSpeedInMegaherz() throw() +{ + const String speed (getCpuInfo ("cpu MHz")); + + return (int) (speed.getFloatValue() + 0.5f); +} + +int SystemStats::getMemorySizeInMegabytes() throw() +{ + struct sysinfo sysi; + + if (sysinfo (&sysi) == 0) + return (sysi.totalram * sysi.mem_unit / (1024 * 1024)); + + return 0; +} + +uint32 juce_millisecondsSinceStartup() throw() +{ + static unsigned int calibrate = 0; + static bool calibrated = false; + timeval t; + unsigned int ret = 0; + + if (! gettimeofday (&t, 0)) + { + if (! calibrated) + { + struct sysinfo sysi; + + if (sysinfo (&sysi) == 0) + // Safe to assume system was not brought up earlier than 1970! + calibrate = t.tv_sec - sysi.uptime; + + calibrated = true; + } + + ret = 1000 * (t.tv_sec - calibrate) + (t.tv_usec / 1000); + } + + return ret; +} + +double Time::getMillisecondCounterHiRes() throw() +{ + return getHighResolutionTicks() * 0.001; +} + +int64 Time::getHighResolutionTicks() throw() +{ + timeval t; + if (gettimeofday (&t, 0)) + return 0; + + return ((int64) t.tv_sec * (int64) 1000000) + (int64) t.tv_usec; +} + +int64 Time::getHighResolutionTicksPerSecond() throw() +{ + // Microseconds + return 1000000; +} + +bool Time::setSystemTimeToThisTime() const throw() +{ + timeval t; + t.tv_sec = millisSinceEpoch % 1000000; + t.tv_usec = millisSinceEpoch - t.tv_sec; + + return settimeofday (&t, NULL) ? false : true; +} + +int SystemStats::getPageSize() throw() +{ + static int systemPageSize = 0; + + if (systemPageSize == 0) + systemPageSize = sysconf (_SC_PAGESIZE); + + return systemPageSize; +} + +int SystemStats::getNumCpus() throw() +{ + const int lastCpu = getCpuInfo ("processor", true).getIntValue(); + + return lastCpu + 1; +} + +void SystemStats::initialiseStats() throw() +{ + // Process starts off as root when running suid + Process::lowerPrivilege(); + + String s (SystemStats::getJUCEVersion()); +} + +void PlatformUtilities::fpuReset() +{ +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_linux_SystemStats.cpp *********/ + +/********* Start of inlined file: juce_linux_Threads.cpp *********/ + +#include +#include +#include +#include + +BEGIN_JUCE_NAMESPACE + +/* + Note that a lot of methods that you'd expect to find in this file actually + live in juce_posix_SharedCode.cpp! +*/ + +#ifndef CPU_ISSET + #undef SUPPORT_AFFINITIES +#endif + +void JUCE_API juce_threadEntryPoint (void*); + +void* threadEntryProc (void* value) throw() +{ + // New threads start off as root when running suid + Process::lowerPrivilege(); + + juce_threadEntryPoint (value); + return 0; +} + +void* juce_createThread (void* userData) throw() +{ + pthread_t handle = 0; + + if (pthread_create (&handle, 0, threadEntryProc, userData) == 0) + { + pthread_detach (handle); + return (void*)handle; + } + + return 0; +} + +void juce_killThread (void* handle) throw() +{ + if (handle != 0) + pthread_cancel ((pthread_t)handle); +} + +void juce_setCurrentThreadName (const String& /*name*/) throw() +{ +} + +int Thread::getCurrentThreadId() throw() +{ + return (int) pthread_self(); +} + +/* + * This is all a bit non-ideal... the trouble is that on Linux you + * need to call setpriority to affect the dynamic priority for + * non-realtime processes, but this requires the pid, which is not + * accessible from the pthread_t. We could get it by calling getpid + * once each thread has started, but then we would need a list of + * running threads etc etc. + * Also there is no such thing as IDLE priority on Linux. + * For the moment, map idle, low and normal process priorities to + * SCHED_OTHER, with the thread priority ignored for these classes. + * Map high priority processes to the lower half of the SCHED_RR + * range, and realtime to the upper half + */ + +// priority 1 to 10 where 5=normal, 1=low. If the handle is 0, sets the +// priority of the current thread +void juce_setThreadPriority (void* handle, int priority) throw() +{ + struct sched_param param; + int policy, maxp, minp, pri; + + if (handle == 0) + handle = (void*) pthread_self(); + + if (pthread_getschedparam ((pthread_t) handle, &policy, ¶m) == 0 + && policy != SCHED_OTHER) + { + minp = sched_get_priority_min(policy); + maxp = sched_get_priority_max(policy); + + pri = ((maxp - minp) / 2) * (priority - 1) / 9; + + if (param.__sched_priority >= (minp + (maxp - minp) / 2)) + // Realtime process priority + param.__sched_priority = minp + ((maxp - minp) / 2) + pri; + else + // High process priority + param.__sched_priority = minp + pri; + + param.sched_priority = jlimit (1, 127, 1 + (priority * 126) / 11); + + pthread_setschedparam ((pthread_t) handle, policy, ¶m); + } +} + +void Thread::setCurrentThreadAffinityMask (const uint32 affinityMask) throw() +{ +#if SUPPORT_AFFINITIES + cpu_set_t affinity; + CPU_ZERO (&affinity); + + for (int i = 0; i < 32; ++i) + if ((affinityMask & (1 << i)) != 0) + CPU_SET (i, &affinity); + + /* + N.B. If this line causes a compile error, then you've probably not got the latest + version of glibc installed. + + If you don't want to update your copy of glibc and don't care about cpu affinities, + then you can just disable all this stuff by removing the SUPPORT_AFFINITIES macro + from the linuxincludes.h file. + */ + sched_setaffinity (getpid(), sizeof (cpu_set_t), &affinity); + sched_yield(); + +#else + /* affinities aren't supported because either the appropriate header files weren't found, + or the SUPPORT_AFFINITIES macro was turned off in linuxheaders.h + */ + jassertfalse +#endif +} + +void Thread::yield() throw() +{ + sched_yield(); +} + +// sets the process to 0=low priority, 1=normal, 2=high, 3=realtime +void Process::setPriority (ProcessPriority prior) +{ + struct sched_param param; + int policy, maxp, minp; + + const int p = (int) prior; + + if (p <= 1) + policy = SCHED_OTHER; + else + policy = SCHED_RR; + + minp = sched_get_priority_min (policy); + maxp = sched_get_priority_max (policy); + + if (p < 2) + param.__sched_priority = 0; + else if (p == 2 ) + // Set to middle of lower realtime priority range + param.__sched_priority = minp + (maxp - minp) / 4; + else + // Set to middle of higher realtime priority range + param.__sched_priority = minp + (3 * (maxp - minp) / 4); + + pthread_setschedparam (pthread_self(), policy, ¶m); +} + +void Process::terminate() +{ + exit (0); +} + +bool JUCE_CALLTYPE juce_isRunningUnderDebugger() throw() +{ + static char testResult = 0; + + if (testResult == 0) + { + testResult = (char) ptrace (PT_TRACE_ME, 0, 0, 0); + + if (testResult >= 0) + { + ptrace (PT_DETACH, 0, (caddr_t) 1, 0); + testResult = 1; + } + } + + return testResult < 0; +} + +bool JUCE_CALLTYPE Process::isRunningUnderDebugger() throw() +{ + return juce_isRunningUnderDebugger(); +} + +void Process::raisePrivilege() +{ + // If running suid root, change effective user + // to root + if (geteuid() != 0 && getuid() == 0) + { + setreuid (geteuid(), getuid()); + setregid (getegid(), getgid()); + } +} + +void Process::lowerPrivilege() +{ + // If runing suid root, change effective user + // back to real user + if (geteuid() == 0 && getuid() != 0) + { + setreuid (geteuid(), getuid()); + setregid (getegid(), getgid()); + } +} + +#if JUCE_BUILD_GUI_CLASSES +void* Process::loadDynamicLibrary (const String& name) +{ + return dlopen ((const char*) name.toUTF8(), RTLD_LOCAL | RTLD_NOW); +} + +void Process::freeDynamicLibrary (void* handle) +{ + dlclose(handle); +} + +void* Process::getProcedureEntryPoint (void* libraryHandle, const String& procedureName) +{ + return dlsym (libraryHandle, (const char*) procedureName); +} +#endif + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_linux_Threads.cpp *********/ + +#if ! JUCE_ONLY_BUILD_CORE_LIBRARY + +/********* Start of inlined file: juce_linux_Audio.cpp *********/ + +#if JUCE_BUILD_GUI_CLASSES + +#if JUCE_ALSA + +/* Got an include error here? If so, you've either not got ALSA installed, or you've + not got your paths set up correctly to find its header files. + + The package you need to install to get ASLA support is "libasound2-dev". + + If you don't have the ALSA library and don't want to build Juce with audio support, + just disable the JUCE_ALSA flag in juce_Config.h +*/ +#include + +BEGIN_JUCE_NAMESPACE + +static const int maxNumChans = 64; + +static void getDeviceSampleRates (snd_pcm_t* handle, Array & rates) +{ + const int ratesToTry[] = { 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000, 0 }; + + snd_pcm_hw_params_t* hwParams; + snd_pcm_hw_params_alloca (&hwParams); + + for (int i = 0; ratesToTry[i] != 0; ++i) + { + if (snd_pcm_hw_params_any (handle, hwParams) >= 0 + && snd_pcm_hw_params_test_rate (handle, hwParams, ratesToTry[i], 0) == 0) + { + rates.add (ratesToTry[i]); + } + } +} + +static void getDeviceNumChannels (snd_pcm_t* handle, unsigned int* minChans, unsigned int* maxChans) +{ + snd_pcm_hw_params_t *params; + snd_pcm_hw_params_alloca (¶ms); + + if (snd_pcm_hw_params_any (handle, params) >= 0) + { + snd_pcm_hw_params_get_channels_min (params, minChans); + snd_pcm_hw_params_get_channels_max (params, maxChans); + } +} + +static void getDeviceProperties (const String& id, + unsigned int& minChansOut, + unsigned int& maxChansOut, + unsigned int& minChansIn, + unsigned int& maxChansIn, + Array & rates) +{ + snd_ctl_t* handle; + + if (snd_ctl_open (&handle, id.upToLastOccurrenceOf (T(","), false, false), SND_CTL_NONBLOCK) >= 0) + { + snd_pcm_info_t* info; + snd_pcm_info_alloca (&info); + + snd_pcm_info_set_stream (info, SND_PCM_STREAM_PLAYBACK); + snd_pcm_info_set_device (info, id.fromLastOccurrenceOf (T(","), false, false).getIntValue()); + snd_pcm_info_set_subdevice (info, 0); + + if (snd_ctl_pcm_info (handle, info) >= 0) + { + snd_pcm_t* pcmHandle; + if (snd_pcm_open (&pcmHandle, id, SND_PCM_STREAM_PLAYBACK, SND_PCM_ASYNC | SND_PCM_NONBLOCK ) >= 0) + { + getDeviceNumChannels (pcmHandle, &minChansOut, &maxChansOut); + getDeviceSampleRates (pcmHandle, rates); + + snd_pcm_close (pcmHandle); + } + } + + snd_pcm_info_set_stream (info, SND_PCM_STREAM_CAPTURE); + + if (snd_ctl_pcm_info (handle, info) >= 0) + { + snd_pcm_t* pcmHandle; + if (snd_pcm_open (&pcmHandle, id, SND_PCM_STREAM_CAPTURE, SND_PCM_ASYNC | SND_PCM_NONBLOCK ) >= 0) + { + getDeviceNumChannels (pcmHandle, &minChansIn, &maxChansIn); + + if (rates.size() == 0) + getDeviceSampleRates (pcmHandle, rates); + + snd_pcm_close (pcmHandle); + } + } + + snd_ctl_close (handle); + } +} + +class ALSADevice +{ +public: + ALSADevice (const String& deviceName, + const bool forInput) + : handle (0), + bitDepth (16), + numChannelsRunning (0), + isInput (forInput), + sampleFormat (AudioDataConverters::int16LE) + { + failed (snd_pcm_open (&handle, deviceName, + forInput ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, + SND_PCM_ASYNC)); + } + + ~ALSADevice() + { + if (handle != 0) + snd_pcm_close (handle); + } + + bool setParameters (unsigned int sampleRate, int numChannels, int bufferSize) + { + if (handle == 0) + return false; + + snd_pcm_hw_params_t* hwParams; + snd_pcm_hw_params_alloca (&hwParams); + + if (failed (snd_pcm_hw_params_any (handle, hwParams))) + return false; + + if (snd_pcm_hw_params_set_access (handle, hwParams, SND_PCM_ACCESS_RW_NONINTERLEAVED) >= 0) + isInterleaved = false; + else if (snd_pcm_hw_params_set_access (handle, hwParams, SND_PCM_ACCESS_RW_INTERLEAVED) >= 0) + isInterleaved = true; + else + { + jassertfalse + return false; + } + + const int formatsToTry[] = { SND_PCM_FORMAT_FLOAT_LE, 32, AudioDataConverters::float32LE, + SND_PCM_FORMAT_FLOAT_BE, 32, AudioDataConverters::float32BE, + SND_PCM_FORMAT_S32_LE, 32, AudioDataConverters::int32LE, + SND_PCM_FORMAT_S32_BE, 32, AudioDataConverters::int32BE, + SND_PCM_FORMAT_S24_LE, 24, AudioDataConverters::int24LE, + SND_PCM_FORMAT_S24_BE, 24, AudioDataConverters::int24BE, + SND_PCM_FORMAT_S16_LE, 16, AudioDataConverters::int16LE, + SND_PCM_FORMAT_S16_BE, 16, AudioDataConverters::int16BE }; + bitDepth = 0; + + for (int i = 0; i < numElementsInArray (formatsToTry); i += 3) + { + if (snd_pcm_hw_params_set_format (handle, hwParams, (_snd_pcm_format) formatsToTry [i]) >= 0) + { + bitDepth = formatsToTry [i + 1]; + sampleFormat = (AudioDataConverters::DataFormat) formatsToTry [i + 2]; + break; + } + } + + if (bitDepth == 0) + { + error = "device doesn't support a compatible PCM format"; + DBG (T("ALSA error: ") + error + T("\n")); + return false; + } + + int dir = 0; + unsigned int periods = 4; + snd_pcm_uframes_t samplesPerPeriod = bufferSize; + + if (failed (snd_pcm_hw_params_set_rate_near (handle, hwParams, &sampleRate, 0)) + || failed (snd_pcm_hw_params_set_channels (handle, hwParams, numChannels)) + || failed (snd_pcm_hw_params_set_periods_near (handle, hwParams, &periods, &dir)) + || failed (snd_pcm_hw_params_set_period_size_near (handle, hwParams, &samplesPerPeriod, &dir)) + || failed (snd_pcm_hw_params (handle, hwParams))) + { + return false; + } + + snd_pcm_sw_params_t* swParams; + snd_pcm_sw_params_alloca (&swParams); + + if (failed (snd_pcm_sw_params_current (handle, swParams)) + || failed (snd_pcm_sw_params_set_silence_threshold (handle, swParams, 0)) + || failed (snd_pcm_sw_params_set_silence_size (handle, swParams, 0)) + || failed (snd_pcm_sw_params_set_start_threshold (handle, swParams, samplesPerPeriod)) + || failed (snd_pcm_sw_params_set_stop_threshold (handle, swParams, INT_MAX)) + || failed (snd_pcm_sw_params (handle, swParams))) + { + return false; + } + + /* +#ifdef JUCE_DEBUG + // enable this to dump the config of the devices that get opened + snd_output_t* out; + snd_output_stdio_attach (&out, stderr, 0); + snd_pcm_hw_params_dump (hwParams, out); + snd_pcm_sw_params_dump (swParams, out); +#endif + */ + + numChannelsRunning = numChannels; + + return true; + } + + bool write (float** const data, const int numSamples) + { + if (isInterleaved) + { + scratch.ensureSize (sizeof (float) * numSamples * numChannelsRunning, false); + float* interleaved = (float*) scratch; + + AudioDataConverters::interleaveSamples ((const float**) data, interleaved, numSamples, numChannelsRunning); + AudioDataConverters::convertFloatToFormat (sampleFormat, interleaved, interleaved, numSamples * numChannelsRunning); + + snd_pcm_sframes_t num = snd_pcm_writei (handle, (void*) interleaved, numSamples); + + if (failed (num) && num != -EPIPE && num != -ESTRPIPE) + return false; + } + else + { + for (int i = 0; i < numChannelsRunning; ++i) + if (data[i] != 0) + AudioDataConverters::convertFloatToFormat (sampleFormat, data[i], data[i], numSamples); + + snd_pcm_sframes_t num = snd_pcm_writen (handle, (void**) data, numSamples); + + if (failed (num)) + { + if (num == -EPIPE) + { + if (failed (snd_pcm_prepare (handle))) + return false; + } + else if (num != -ESTRPIPE) + return false; + } + } + + return true; + } + + bool read (float** const data, const int numSamples) + { + if (isInterleaved) + { + scratch.ensureSize (sizeof (float) * numSamples * numChannelsRunning, false); + float* interleaved = (float*) scratch; + + snd_pcm_sframes_t num = snd_pcm_readi (handle, (void*) interleaved, numSamples); + + if (failed (num)) + { + if (num == -EPIPE) + { + if (failed (snd_pcm_prepare (handle))) + return false; + } + else if (num != -ESTRPIPE) + return false; + } + + AudioDataConverters::convertFormatToFloat (sampleFormat, interleaved, interleaved, numSamples * numChannelsRunning); + AudioDataConverters::deinterleaveSamples (interleaved, data, numSamples, numChannelsRunning); + } + else + { + snd_pcm_sframes_t num = snd_pcm_readn (handle, (void**) data, numSamples); + + if (failed (num) && num != -EPIPE && num != -ESTRPIPE) + return false; + + for (int i = 0; i < numChannelsRunning; ++i) + if (data[i] != 0) + AudioDataConverters::convertFormatToFloat (sampleFormat, data[i], data[i], numSamples); + } + + return true; + } + + juce_UseDebuggingNewOperator + + snd_pcm_t* handle; + String error; + int bitDepth, numChannelsRunning; + +private: + const bool isInput; + bool isInterleaved; + MemoryBlock scratch; + AudioDataConverters::DataFormat sampleFormat; + + bool failed (const int errorNum) + { + if (errorNum >= 0) + return false; + + error = snd_strerror (errorNum); + DBG (T("ALSA error: ") + error + T("\n")); + return true; + } +}; + +class ALSAThread : public Thread +{ +public: + ALSAThread (const String& deviceName_) + : Thread ("Juce ALSA"), + sampleRate (0), + bufferSize (0), + callback (0), + deviceName (deviceName_), + outputDevice (0), + inputDevice (0), + numCallbacks (0), + totalNumInputChannels (0), + totalNumOutputChannels (0) + { + zeromem (outputChannelData, sizeof (outputChannelData)); + zeromem (outputChannelDataForCallback, sizeof (outputChannelDataForCallback)); + zeromem (inputChannelData, sizeof (inputChannelData)); + zeromem (inputChannelDataForCallback, sizeof (inputChannelDataForCallback)); + + initialiseRatesAndChannels(); + } + + ~ALSAThread() + { + close(); + } + + void open (const BitArray& inputChannels, + const BitArray& outputChannels, + const double sampleRate_, + const int bufferSize_) + { + close(); + + error = String::empty; + sampleRate = sampleRate_; + bufferSize = bufferSize_; + currentInputChans.clear(); + currentOutputChans.clear(); + + numChannelsRunning = jmax (inputChannels.getHighestBit(), + outputChannels.getHighestBit()) + 1; + + numChannelsRunning = jmin (maxNumChans, jlimit ((int) minChansIn, + (int) maxChansIn, + numChannelsRunning)); + + if (inputChannels.getHighestBit() >= 0) + { + for (int i = 0; i < numChannelsRunning; ++i) + { + inputChannelData [i] = (float*) juce_calloc (sizeof (float) * bufferSize); + + if (inputChannels[i]) + { + inputChannelDataForCallback [totalNumInputChannels++] = inputChannelData [i]; + currentInputChans.setBit (i); + } + } + } + + if (outputChannels.getHighestBit() >= 0) + { + for (int i = 0; i < numChannelsRunning; ++i) + { + outputChannelData [i] = (float*) juce_calloc (sizeof (float) * bufferSize); + + if (outputChannels[i]) + { + outputChannelDataForCallback [totalNumOutputChannels++] = outputChannelData [i]; + currentOutputChans.setBit (i); + } + } + } + + if (totalNumOutputChannels > 0) + { + outputDevice = new ALSADevice (deviceName, false); + + if (outputDevice->error.isNotEmpty()) + { + error = outputDevice->error; + deleteAndZero (outputDevice); + return; + } + + if (! outputDevice->setParameters ((unsigned int) sampleRate, numChannelsRunning, bufferSize)) + { + error = outputDevice->error; + deleteAndZero (outputDevice); + return; + } + } + + if (totalNumInputChannels > 0) + { + inputDevice = new ALSADevice (deviceName, true); + + if (inputDevice->error.isNotEmpty()) + { + error = inputDevice->error; + deleteAndZero (inputDevice); + return; + } + + if (! inputDevice->setParameters ((unsigned int) sampleRate, numChannelsRunning, bufferSize)) + { + error = inputDevice->error; + deleteAndZero (inputDevice); + return; + } + } + + if (outputDevice == 0 && inputDevice == 0) + { + error = "no channels"; + return; + } + + if (outputDevice != 0 && inputDevice != 0) + { + snd_pcm_link (outputDevice->handle, inputDevice->handle); + } + + if (inputDevice != 0 && failed (snd_pcm_prepare (inputDevice->handle))) + return; + + if (outputDevice != 0 && failed (snd_pcm_prepare (outputDevice->handle))) + return; + + startThread (9); + + int count = 1000; + + while (numCallbacks == 0) + { + sleep (5); + + if (--count < 0 || ! isThreadRunning()) + { + error = "device didn't start"; + break; + } + } + } + + void close() + { + stopThread (6000); + + deleteAndZero (inputDevice); + deleteAndZero (outputDevice); + + for (int i = 0; i < maxNumChans; ++i) + { + juce_free (inputChannelData [i]); + juce_free (outputChannelData [i]); + } + + zeromem (outputChannelData, sizeof (outputChannelData)); + zeromem (outputChannelDataForCallback, sizeof (outputChannelDataForCallback)); + zeromem (inputChannelData, sizeof (inputChannelData)); + zeromem (inputChannelDataForCallback, sizeof (inputChannelDataForCallback)); + totalNumOutputChannels = 0; + totalNumInputChannels = 0; + numChannelsRunning = 0; + + numCallbacks = 0; + } + + void setCallback (AudioIODeviceCallback* const newCallback) throw() + { + const ScopedLock sl (callbackLock); + callback = newCallback; + } + + void run() + { + while (! threadShouldExit()) + { + if (inputDevice != 0) + { + jassert (numChannelsRunning >= inputDevice->numChannelsRunning); + + if (! inputDevice->read (inputChannelData, bufferSize)) + { + DBG ("ALSA: read failure"); + break; + } + } + + if (threadShouldExit()) + break; + + { + const ScopedLock sl (callbackLock); + ++numCallbacks; + + if (callback != 0) + { + callback->audioDeviceIOCallback ((const float**) inputChannelDataForCallback, + totalNumInputChannels, + outputChannelDataForCallback, + totalNumOutputChannels, + bufferSize); + } + else + { + for (int i = 0; i < totalNumOutputChannels; ++i) + zeromem (outputChannelDataForCallback[i], sizeof (float) * bufferSize); + } + } + + if (outputDevice != 0) + { + failed (snd_pcm_wait (outputDevice->handle, 2000)); + + if (threadShouldExit()) + break; + + failed (snd_pcm_avail_update (outputDevice->handle)); + + jassert (numChannelsRunning >= outputDevice->numChannelsRunning); + if (! outputDevice->write (outputChannelData, bufferSize)) + { + DBG ("ALSA: write failure"); + break; + } + } + } + } + + int getBitDepth() const throw() + { + if (outputDevice != 0) + return outputDevice->bitDepth; + + if (inputDevice != 0) + return inputDevice->bitDepth; + + return 16; + } + + juce_UseDebuggingNewOperator + + String error; + double sampleRate; + int bufferSize; + BitArray currentInputChans, currentOutputChans; + + Array sampleRates; + StringArray channelNamesOut, channelNamesIn; + AudioIODeviceCallback* callback; + +private: + + const String deviceName; + ALSADevice* outputDevice; + ALSADevice* inputDevice; + int numCallbacks; + + CriticalSection callbackLock; + + float* outputChannelData [maxNumChans]; + float* outputChannelDataForCallback [maxNumChans]; + int totalNumInputChannels; + float* inputChannelData [maxNumChans]; + float* inputChannelDataForCallback [maxNumChans]; + int totalNumOutputChannels; + int numChannelsRunning; + + unsigned int minChansOut, maxChansOut; + unsigned int minChansIn, maxChansIn; + + bool failed (const int errorNum) throw() + { + if (errorNum >= 0) + return false; + + error = snd_strerror (errorNum); + DBG (T("ALSA error: ") + error + T("\n")); + return true; + } + + void initialiseRatesAndChannels() throw() + { + sampleRates.clear(); + channelNamesOut.clear(); + channelNamesIn.clear(); + minChansOut = 0; + maxChansOut = 0; + minChansIn = 0; + maxChansIn = 0; + + getDeviceProperties (deviceName, minChansOut, maxChansOut, minChansIn, maxChansIn, sampleRates); + + unsigned int i; + for (i = 0; i < maxChansOut; ++i) + channelNamesOut.add (T("channel ") + String ((int) i + 1)); + + for (i = 0; i < maxChansIn; ++i) + channelNamesIn.add (T("channel ") + String ((int) i + 1)); + } +}; + +class ALSAAudioIODevice : public AudioIODevice +{ +public: + ALSAAudioIODevice (const String& deviceName, + const String& deviceId) + : AudioIODevice (deviceName, T("ALSA")), + isOpen_ (false), + isStarted (false), + internal (0) + { + internal = new ALSAThread (deviceId); + } + + ~ALSAAudioIODevice() + { + delete internal; + } + + const StringArray getOutputChannelNames() + { + return internal->channelNamesOut; + } + + const StringArray getInputChannelNames() + { + return internal->channelNamesIn; + } + + int getNumSampleRates() + { + return internal->sampleRates.size(); + } + + double getSampleRate (int index) + { + return internal->sampleRates [index]; + } + + int getNumBufferSizesAvailable() + { + return 50; + } + + int getBufferSizeSamples (int index) + { + int n = 16; + for (int i = 0; i < index; ++i) + n += n < 64 ? 16 + : (n < 512 ? 32 + : (n < 1024 ? 64 + : (n < 2048 ? 128 : 256))); + + return n; + } + + int getDefaultBufferSize() + { + return 512; + } + + const String open (const BitArray& inputChannels, + const BitArray& outputChannels, + double sampleRate, + int bufferSizeSamples) + { + close(); + + if (bufferSizeSamples <= 0) + bufferSizeSamples = getDefaultBufferSize(); + + if (sampleRate <= 0) + { + for (int i = 0; i < getNumSampleRates(); ++i) + { + if (getSampleRate (i) >= 44100) + { + sampleRate = getSampleRate (i); + break; + } + } + } + + internal->open (inputChannels, outputChannels, + sampleRate, bufferSizeSamples); + + isOpen_ = internal->error.isEmpty(); + return internal->error; + } + + void close() + { + stop(); + internal->close(); + isOpen_ = false; + } + + bool isOpen() + { + return isOpen_; + } + + int getCurrentBufferSizeSamples() + { + return internal->bufferSize; + } + + double getCurrentSampleRate() + { + return internal->sampleRate; + } + + int getCurrentBitDepth() + { + return internal->getBitDepth(); + } + + const BitArray getActiveOutputChannels() const + { + return internal->currentOutputChans; + } + + const BitArray getActiveInputChannels() const + { + return internal->currentInputChans; + } + + int getOutputLatencyInSamples() + { + return 0; + } + + int getInputLatencyInSamples() + { + return 0; + } + + void start (AudioIODeviceCallback* callback) + { + if (! isOpen_) + callback = 0; + + internal->setCallback (callback); + + if (callback != 0) + callback->audioDeviceAboutToStart (this); + + isStarted = (callback != 0); + } + + void stop() + { + AudioIODeviceCallback* const oldCallback = internal->callback; + + start (0); + + if (oldCallback != 0) + oldCallback->audioDeviceStopped(); + } + + bool isPlaying() + { + return isStarted && internal->error.isEmpty(); + } + + const String getLastError() + { + return internal->error; + } + +private: + bool isOpen_, isStarted; + ALSAThread* internal; +}; + +class ALSAAudioIODeviceType : public AudioIODeviceType +{ +public: + + ALSAAudioIODeviceType() + : AudioIODeviceType (T("ALSA")), + hasScanned (false) + { + } + + ~ALSAAudioIODeviceType() + { + } + + void scanForDevices() + { + hasScanned = true; + + names.clear(); + ids.clear(); + + snd_ctl_t* handle; + snd_ctl_card_info_t* info; + snd_ctl_card_info_alloca (&info); + + int cardNum = -1; + + while (ids.size() <= 24) + { + snd_card_next (&cardNum); + + if (cardNum < 0) + break; + + if (snd_ctl_open (&handle, T("hw:") + String (cardNum), SND_CTL_NONBLOCK) >= 0) + { + if (snd_ctl_card_info (handle, info) >= 0) + { + String cardId (snd_ctl_card_info_get_id (info)); + + if (cardId.removeCharacters (T("0123456789")).isEmpty()) + cardId = String (cardNum); + + int device = -1; + + for (;;) + { + if (snd_ctl_pcm_next_device (handle, &device) < 0 || device < 0) + break; + + String id, name; + id << "hw:" << cardId << ',' << device; + + if (testDevice (id)) + { + name << snd_ctl_card_info_get_name (info); + + if (name.isEmpty()) + name = id; + + if (device > 0) + name << " (" << (device + 1) << ')'; + + ids.add (id); + names.add (name); + } + } + } + + snd_ctl_close (handle); + } + } + } + + const StringArray getDeviceNames (const bool /*preferInputNames*/) const + { + jassert (hasScanned); // need to call scanForDevices() before doing this + + StringArray namesCopy (names); + namesCopy.removeDuplicates (true); + + return namesCopy; + } + + const String getDefaultDeviceName (const bool /*preferInputNames*/, + const int /*numInputChannelsNeeded*/, + const int /*numOutputChannelsNeeded*/) const + { + jassert (hasScanned); // need to call scanForDevices() before doing this + + return names[0]; + } + + AudioIODevice* createDevice (const String& deviceName) + { + jassert (hasScanned); // need to call scanForDevices() before doing this + + const int index = names.indexOf (deviceName); + + if (index >= 0) + return new ALSAAudioIODevice (deviceName, ids [index]); + + return 0; + } + + juce_UseDebuggingNewOperator + +private: + StringArray names, ids; + bool hasScanned; + + static bool testDevice (const String& id) + { + unsigned int minChansOut = 0, maxChansOut = 0; + unsigned int minChansIn = 0, maxChansIn = 0; + Array rates; + + getDeviceProperties (id, minChansOut, maxChansOut, minChansIn, maxChansIn, rates); + + DBG (T("ALSA device: ") + id + + T(" outs=") + String ((int) minChansOut) + T("-") + String ((int) maxChansOut) + + T(" ins=") + String ((int) minChansIn) + T("-") + String ((int) maxChansIn) + + T(" rates=") + String (rates.size())); + + return (maxChansOut > 0 || maxChansIn > 0) && rates.size() > 0; + } + + ALSAAudioIODeviceType (const ALSAAudioIODeviceType&); + const ALSAAudioIODeviceType& operator= (const ALSAAudioIODeviceType&); +}; + +AudioIODeviceType* juce_createDefaultAudioIODeviceType() +{ + return new ALSAAudioIODeviceType(); +} + +END_JUCE_NAMESPACE + +#else // if ALSA is turned off.. + +BEGIN_JUCE_NAMESPACE + +AudioIODeviceType* juce_createDefaultAudioIODeviceType() { return 0; } + +END_JUCE_NAMESPACE + +#endif + +#endif +/********* End of inlined file: juce_linux_Audio.cpp *********/ + +/********* Start of inlined file: juce_linux_AudioCDReader.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +AudioCDReader::AudioCDReader() + : AudioFormatReader (0, T("CD Audio")) +{ +} + +const StringArray AudioCDReader::getAvailableCDNames() +{ + StringArray names; + return names; +} + +AudioCDReader* AudioCDReader::createReaderForCD (const int index) +{ + return 0; +} + +AudioCDReader::~AudioCDReader() +{ +} + +void AudioCDReader::refreshTrackLengths() +{ +} + +bool AudioCDReader::read (int** destSamples, + int64 startSampleInFile, + int numSamples) +{ + return false; +} + +bool AudioCDReader::isCDStillPresent() const +{ + return false; +} + +int AudioCDReader::getNumTracks() const +{ + return 0; +} + +int AudioCDReader::getPositionOfTrackStart (int trackNum) const +{ + return 0; +} + +bool AudioCDReader::isTrackAudio (int trackNum) const +{ + return false; +} + +void AudioCDReader::enableIndexScanning (bool b) +{ +} + +int AudioCDReader::getLastIndex() const +{ + return 0; +} + +const Array AudioCDReader::findIndexesInTrack (const int trackNumber) +{ + return Array(); +} + +int AudioCDReader::getCDDBId() +{ + return 0; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_linux_AudioCDReader.cpp *********/ + +/********* Start of inlined file: juce_linux_FileChooser.cpp *********/ + +#if JUCE_BUILD_GUI_CLASSES + +BEGIN_JUCE_NAMESPACE + +void FileChooser::showPlatformDialog (OwnedArray& results, + const String& title, + const File& file, + const String& filters, + bool isDirectory, + bool isSave, + bool warnAboutOverwritingExistingFiles, + bool selectMultipleFiles, + FilePreviewComponent* previewComponent) +{ + //xxx ain't got one! + jassertfalse +} + +END_JUCE_NAMESPACE + +#endif +/********* End of inlined file: juce_linux_FileChooser.cpp *********/ + +/********* Start of inlined file: juce_linux_Fonts.cpp *********/ + +#if JUCE_BUILD_GUI_CLASSES + +/* Got a build error here? You'll need to install the freetype library... + + The name of the package to install is "libfreetype6-dev". +*/ +#include +#include FT_FREETYPE_H + +BEGIN_JUCE_NAMESPACE + +class FreeTypeFontFace +{ +public: + + enum FontStyle + { + Plain = 0, + Bold = 1, + Italic = 2 + }; + + struct FontNameIndex + { + String fileName; + int faceIndex; + }; + + FreeTypeFontFace (const String& familyName) + : hasSerif (false), + monospaced (false) + { + family = familyName; + } + + void setFileName (const String& name, + const int faceIndex, + FontStyle style) + { + if (names[(int) style].fileName.isEmpty()) + { + names[(int) style].fileName = name; + names[(int) style].faceIndex = faceIndex; + } + } + + const String& getFamilyName() const throw() + { + return family; + } + + const String& getFileName (int style, int* faceIndex) const throw() + { + *faceIndex = names [style].faceIndex; + return names[style].fileName; + } + + void setMonospaced (bool mono) { monospaced = mono; } + bool getMonospaced () const throw() { return monospaced; } + + void setSerif (const bool serif) { hasSerif = serif; } + bool getSerif () const throw() { return hasSerif; } + +private: + + String family; + FontNameIndex names[4]; + bool hasSerif, monospaced; +}; + +class FreeTypeInterface : public DeletedAtShutdown +{ +public: + + FreeTypeInterface() throw() + : lastFace (0), + lastBold (false), + lastItalic (false) + { + if (FT_Init_FreeType (&ftLib) != 0) + { + ftLib = 0; + DBG (T("Failed to initialize FreeType")); + } + + StringArray fontDirs; + fontDirs.addTokens (String (getenv ("JUCE_FONT_PATH")), T(";,"), 0); + fontDirs.removeEmptyStrings (true); + + if (fontDirs.size() == 0) + { + XmlDocument fontsConfig (File ("/etc/fonts/fonts.conf")); + XmlElement* const fontsInfo = fontsConfig.getDocumentElement(); + + if (fontsInfo != 0) + { + forEachXmlChildElementWithTagName (*fontsInfo, e, T("dir")) + { + fontDirs.add (e->getAllSubText().trim()); + } + + delete fontsInfo; + } + } + + if (fontDirs.size() == 0) + fontDirs.add ("/usr/X11R6/lib/X11/fonts"); + + for (int i = 0; i < fontDirs.size(); ++i) + enumerateFaces (fontDirs[i]); + } + + ~FreeTypeInterface() throw() + { + if (lastFace != 0) + FT_Done_Face (lastFace); + + if (ftLib != 0) + FT_Done_FreeType (ftLib); + + clearSingletonInstance(); + } + + FreeTypeFontFace* findOrCreate (const String& familyName, + const bool create = false) throw() + { + for (int i = 0; i < faces.size(); i++) + if (faces[i]->getFamilyName() == familyName) + return faces[i]; + + if (! create) + return NULL; + + FreeTypeFontFace* newFace = new FreeTypeFontFace (familyName); + faces.add (newFace); + + return newFace; + } + + // Enumerate all font faces available in a given directory + void enumerateFaces (const String& path) throw() + { + File dirPath (path); + if (path.isEmpty() || ! dirPath.isDirectory()) + return; + + DirectoryIterator di (dirPath, true); + + while (di.next()) + { + File possible (di.getFile()); + + if (possible.hasFileExtension (T("ttf")) + || possible.hasFileExtension (T("pfb")) + || possible.hasFileExtension (T("pcf"))) + { + FT_Face face; + int faceIndex = 0; + int numFaces = 0; + + do + { + if (FT_New_Face (ftLib, + possible.getFullPathName(), + faceIndex, + &face) == 0) + { + if (faceIndex == 0) + numFaces = face->num_faces; + + if ((face->face_flags & FT_FACE_FLAG_SCALABLE) != 0) + { + FreeTypeFontFace* const newFace = findOrCreate (face->family_name, true); + int style = (int) FreeTypeFontFace::Plain; + + if ((face->style_flags & FT_STYLE_FLAG_BOLD) != 0) + style |= (int) FreeTypeFontFace::Bold; + + if ((face->style_flags & FT_STYLE_FLAG_ITALIC) != 0) + style |= (int) FreeTypeFontFace::Italic; + + newFace->setFileName (possible.getFullPathName(), faceIndex, (FreeTypeFontFace::FontStyle) style); + + if ((face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) != 0) + newFace->setMonospaced (true); + else + newFace->setMonospaced (false); + + // Surely there must be a better way to do this? + if (String (face->family_name).containsIgnoreCase (T("Sans")) + || String (face->family_name).containsIgnoreCase (T("Verdana")) + || String (face->family_name).containsIgnoreCase (T("Arial"))) + { + newFace->setSerif (false); + } + else + { + newFace->setSerif (true); + } + } + + FT_Done_Face (face); + } + + ++faceIndex; + } + while (faceIndex < numFaces); + } + } + } + + // Create a FreeType face object for a given font + FT_Face createFT_Face (const String& fontName, + const bool bold, + const bool italic) throw() + { + FT_Face face = NULL; + + if (fontName == lastFontName && bold == lastBold && italic == lastItalic) + { + face = lastFace; + } + else + { + if (lastFace) + { + FT_Done_Face (lastFace); + lastFace = NULL; + } + + lastFontName = fontName; + lastBold = bold; + lastItalic = italic; + + FreeTypeFontFace* const ftFace = findOrCreate (fontName); + + if (ftFace != 0) + { + int style = (int) FreeTypeFontFace::Plain; + + if (bold) + style |= (int) FreeTypeFontFace::Bold; + + if (italic) + style |= (int) FreeTypeFontFace::Italic; + + int faceIndex; + String fileName (ftFace->getFileName (style, &faceIndex)); + + if (fileName.isEmpty()) + { + style ^= (int) FreeTypeFontFace::Bold; + + fileName = ftFace->getFileName (style, &faceIndex); + + if (fileName.isEmpty()) + { + style ^= (int) FreeTypeFontFace::Bold; + style ^= (int) FreeTypeFontFace::Italic; + + fileName = ftFace->getFileName (style, &faceIndex); + + if (! fileName.length()) + { + style ^= (int) FreeTypeFontFace::Bold; + fileName = ftFace->getFileName (style, &faceIndex); + } + } + } + + if (! FT_New_Face (ftLib, (const char*) fileName, faceIndex, &lastFace)) + { + face = lastFace; + + // If there isn't a unicode charmap then select the first one. + if (FT_Select_Charmap (face, ft_encoding_unicode)) + FT_Set_Charmap (face, face->charmaps[0]); + } + } + } + return face; + } + + bool addGlyph (FT_Face face, Typeface& dest, uint32 character) throw() + { + const unsigned int glyphIndex = FT_Get_Char_Index (face, character); + const float height = (float) (face->ascender - face->descender); + const float scaleX = 1.0f / height; + const float scaleY = -1.0f / height; + Path destShape; + + #define CONVERTX(val) (scaleX * (val).x) + #define CONVERTY(val) (scaleY * (val).y) + + if (FT_Load_Glyph (face, glyphIndex, FT_LOAD_NO_SCALE + | FT_LOAD_NO_BITMAP + | FT_LOAD_IGNORE_TRANSFORM) != 0 + || face->glyph->format != ft_glyph_format_outline) + { + return false; + } + + const FT_Outline* const outline = &face->glyph->outline; + const short* const contours = outline->contours; + const char* const tags = outline->tags; + FT_Vector* const points = outline->points; + + for (int c = 0; c < outline->n_contours; c++) + { + const int startPoint = (c == 0) ? 0 : contours [c - 1] + 1; + const int endPoint = contours[c]; + + for (int p = startPoint; p <= endPoint; p++) + { + const float x = CONVERTX (points[p]); + const float y = CONVERTY (points[p]); + + if (p == startPoint) + { + if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_Conic) + { + float x2 = CONVERTX (points [endPoint]); + float y2 = CONVERTY (points [endPoint]); + + if (FT_CURVE_TAG (tags[endPoint]) != FT_Curve_Tag_On) + { + x2 = (x + x2) * 0.5f; + y2 = (y + y2) * 0.5f; + } + + destShape.startNewSubPath (x2, y2); + } + else + { + destShape.startNewSubPath (x, y); + } + } + + if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_On) + { + if (p != startPoint) + destShape.lineTo (x, y); + } + else if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_Conic) + { + const int nextIndex = (p == endPoint) ? startPoint : p + 1; + float x2 = CONVERTX (points [nextIndex]); + float y2 = CONVERTY (points [nextIndex]); + + if (FT_CURVE_TAG (tags [nextIndex]) == FT_Curve_Tag_Conic) + { + x2 = (x + x2) * 0.5f; + y2 = (y + y2) * 0.5f; + } + else + { + ++p; + } + + destShape.quadraticTo (x, y, x2, y2); + } + else if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_Cubic) + { + if (p >= endPoint) + return false; + + const int next1 = p + 1; + const int next2 = (p == (endPoint - 1)) ? startPoint : p + 2; + + const float x2 = CONVERTX (points [next1]); + const float y2 = CONVERTY (points [next1]); + const float x3 = CONVERTX (points [next2]); + const float y3 = CONVERTY (points [next2]); + + if (FT_CURVE_TAG (tags[next1]) != FT_Curve_Tag_Cubic + || FT_CURVE_TAG (tags[next2]) != FT_Curve_Tag_On) + return false; + + destShape.cubicTo (x, y, x2, y2, x3, y3); + p += 2; + } + } + + destShape.closeSubPath(); + } + + dest.addGlyph (character, destShape, face->glyph->metrics.horiAdvance/height); + + if ((face->face_flags & FT_FACE_FLAG_KERNING) != 0) + addKerning (face, dest, character, glyphIndex); + + return true; + } + + void addKerning (FT_Face face, Typeface& dest, const uint32 character, const uint32 glyphIndex) throw() + { + const float height = (float) (face->ascender - face->descender); + + uint32 rightGlyphIndex; + uint32 rightCharCode = FT_Get_First_Char (face, &rightGlyphIndex); + + while (rightGlyphIndex != 0) + { + FT_Vector kerning; + + if (FT_Get_Kerning (face, glyphIndex, rightGlyphIndex, ft_kerning_unscaled, &kerning) == 0) + { + if (kerning.x != 0) + dest.addKerningPair (character, rightCharCode, kerning.x / height); + } + + rightCharCode = FT_Get_Next_Char (face, rightCharCode, &rightGlyphIndex); + } + } + + // Add a glyph to a font + bool addGlyphToFont (const uint32 character, + const tchar* fontName, bool bold, bool italic, + Typeface& dest) throw() + { + FT_Face face = createFT_Face (fontName, bold, italic); + + if (face != 0) + return addGlyph (face, dest, character); + + return false; + } + + // Create a Typeface object for given name/style + bool createTypeface (const String& fontName, + const bool bold, const bool italic, + Typeface& dest, + const bool addAllGlyphs) throw() + { + dest.clear(); + dest.setName (fontName); + dest.setBold (bold); + dest.setItalic (italic); + + FT_Face face = createFT_Face (fontName, bold, italic); + + if (face == 0) + { +#ifdef JUCE_DEBUG + String msg (T("Failed to create typeface: ")); + msg << fontName << " " << (bold ? 'B' : ' ') << (italic ? 'I' : ' '); + DBG (msg); +#endif + return face; + } + + const float height = (float) (face->ascender - face->descender); + + dest.setAscent (face->ascender / height); + dest.setDefaultCharacter (L' '); + + if (addAllGlyphs) + { + uint32 glyphIndex; + uint32 charCode = FT_Get_First_Char (face, &glyphIndex); + + while (glyphIndex != 0) + { + addGlyph (face, dest, charCode); + charCode = FT_Get_Next_Char (face, charCode, &glyphIndex); + } + } + + return true; + } + + void getFamilyNames (StringArray& familyNames) const throw() + { + for (int i = 0; i < faces.size(); i++) + familyNames.add (faces[i]->getFamilyName()); + } + + void getMonospacedNames (StringArray& monoSpaced) const throw() + { + for (int i = 0; i < faces.size(); i++) + if (faces[i]->getMonospaced()) + monoSpaced.add (faces[i]->getFamilyName()); + } + + void getSerifNames (StringArray& serif) const throw() + { + for (int i = 0; i < faces.size(); i++) + if (faces[i]->getSerif()) + serif.add (faces[i]->getFamilyName()); + } + + void getSansSerifNames (StringArray& sansSerif) const throw() + { + for (int i = 0; i < faces.size(); i++) + if (! faces[i]->getSerif()) + sansSerif.add (faces[i]->getFamilyName()); + } + + juce_DeclareSingleton_SingleThreaded_Minimal (FreeTypeInterface) + +private: + + FT_Library ftLib; + FT_Face lastFace; + String lastFontName; + bool lastBold, lastItalic; + OwnedArray faces; +}; + +juce_ImplementSingleton_SingleThreaded (FreeTypeInterface) + +void Typeface::initialiseTypefaceCharacteristics (const String& fontName, + bool bold, bool italic, + bool addAllGlyphsToFont) throw() +{ + FreeTypeInterface::getInstance() + ->createTypeface (fontName, bold, italic, *this, addAllGlyphsToFont); +} + +bool Typeface::findAndAddSystemGlyph (juce_wchar character) throw() +{ + return FreeTypeInterface::getInstance() + ->addGlyphToFont (character, getName(), isBold(), isItalic(), *this); +} + +const StringArray Font::findAllTypefaceNames() throw() +{ + StringArray s; + FreeTypeInterface::getInstance()->getFamilyNames (s); + s.sort (true); + return s; +} + +static const String pickBestFont (const StringArray& names, + const char* const choicesString) +{ + StringArray choices; + choices.addTokens (String (choicesString), T(","), 0); + choices.trim(); + choices.removeEmptyStrings(); + + int i, j; + for (j = 0; j < choices.size(); ++j) + if (names.contains (choices[j], true)) + return choices[j]; + + for (j = 0; j < choices.size(); ++j) + for (i = 0; i < names.size(); i++) + if (names[i].startsWithIgnoreCase (choices[j])) + return names[i]; + + for (j = 0; j < choices.size(); ++j) + for (i = 0; i < names.size(); i++) + if (names[i].containsIgnoreCase (choices[j])) + return names[i]; + + return names[0]; +} + +static const String linux_getDefaultSansSerifFontName() +{ + StringArray allFonts; + FreeTypeInterface::getInstance()->getSansSerifNames (allFonts); + + return pickBestFont (allFonts, "Verdana, Bitstream Vera Sans, Luxi Sans, Sans"); +} + +static const String linux_getDefaultSerifFontName() +{ + StringArray allFonts; + FreeTypeInterface::getInstance()->getSerifNames (allFonts); + + return pickBestFont (allFonts, "Bitstream Vera Serif, Times, Nimbus Roman, Serif"); +} + +static const String linux_getDefaultMonospacedFontName() +{ + StringArray allFonts; + FreeTypeInterface::getInstance()->getMonospacedNames (allFonts); + + return pickBestFont (allFonts, "Bitstream Vera Sans Mono, Courier, Sans Mono, Mono"); +} + +void Font::getDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed) throw() +{ + defaultSans = linux_getDefaultSansSerifFontName(); + defaultSerif = linux_getDefaultSerifFontName(); + defaultFixed = linux_getDefaultMonospacedFontName(); +} + +END_JUCE_NAMESPACE + +#endif +/********* End of inlined file: juce_linux_Fonts.cpp *********/ + +/********* Start of inlined file: juce_linux_Messaging.cpp *********/ + +#if JUCE_BUILD_GUI_CLASSES + +#include +#include +#include +#include +#include +#include + +BEGIN_JUCE_NAMESPACE + +#ifdef JUCE_DEBUG + #define JUCE_DEBUG_XERRORS 1 +#endif + +Display* display = 0; // This is also referenced from WindowDriver.cpp +static Window juce_messageWindowHandle = None; + +#define SpecialAtom "JUCESpecialAtom" +#define BroadcastAtom "JUCEBroadcastAtom" +#define SpecialCallbackAtom "JUCESpecialCallbackAtom" + +static Atom specialId; +static Atom broadcastId; +static Atom specialCallbackId; + +// This is referenced from WindowDriver.cpp +XContext improbableNumber; + +// Defined in WindowDriver.cpp +extern void juce_windowMessageReceive (XEvent* event); + +struct MessageThreadFuncCall +{ + MessageCallbackFunction* func; + void* parameter; + void* result; + CriticalSection lock; + WaitableEvent event; +}; + +static bool errorCondition = false; + +// (defined in another file to avoid problems including certain headers in this one) +extern bool juce_isRunningAsApplication(); + +// Usually happens when client-server connection is broken +static int ioErrorHandler (Display* display) +{ + DBG (T("ERROR: connection to X server broken.. terminating.")); + + errorCondition = true; + + if (! juce_isRunningAsApplication()) + Process::terminate(); + + return 0; +} + +// A protocol error has occurred +static int errorHandler (Display* display, XErrorEvent* event) +{ +#ifdef JUCE_DEBUG_XERRORS + char errorStr[64] = { 0 }; + char requestStr[64] = { 0 }; + + XGetErrorText (display, event->error_code, errorStr, 64); + + XGetErrorDatabaseText (display, + "XRequest", + (const char*) String (event->request_code), + "Unknown", + requestStr, + 64); + + DBG (T("ERROR: X returned ") + String (errorStr) + T(" for operation ") + String (requestStr)); +#endif + + return 0; +} + +static bool breakIn = false; + +// Breakin from keyboard +static void sig_handler (int sig) +{ + if (sig == SIGINT) + { + breakIn = true; + return; + } + + static bool reentrant = false; + + if (reentrant == false) + { + reentrant = true; + + // Illegal instruction + fflush (stdout); + Logger::outputDebugString ("ERROR: Program executed illegal instruction.. terminating"); + + errorCondition = true; + + if (juce_isRunningAsApplication()) + Process::terminate(); + } + else + { + if (juce_isRunningAsApplication()) + exit(0); + } +} + +void MessageManager::doPlatformSpecificInitialisation() +{ + // Initialise xlib for multiple thread support + if (! XInitThreads()) + { + // This is fatal! Print error and closedown + Logger::outputDebugString ("Failed to initialise xlib thread support."); + + if (juce_isRunningAsApplication()) + Process::terminate(); + } + + // This is called if the client/server connection is broken + XSetIOErrorHandler (ioErrorHandler); + + // This is called if a protocol error occurs + XSetErrorHandler (errorHandler); + + // Install signal handler for break-in + struct sigaction saction; + sigset_t maskSet; + sigemptyset (&maskSet); + saction.sa_handler = sig_handler; + saction.sa_mask = maskSet; + saction.sa_flags = 0; + sigaction (SIGINT, &saction, NULL); + +#ifndef _DEBUG + // Setup signal handlers for various fatal errors + sigaction (SIGILL, &saction, NULL); + sigaction (SIGBUS, &saction, NULL); + sigaction (SIGFPE, &saction, NULL); + sigaction (SIGSEGV, &saction, NULL); + sigaction (SIGSYS, &saction, NULL); +#endif + + String displayName (getenv ("DISPLAY")); + if (displayName.isEmpty()) + displayName = T(":0.0"); + + display = XOpenDisplay (displayName); + + if (display == 0) + { + // This is fatal! Print error and closedown + Logger::outputDebugString ("Failed to open the X display."); + + if (juce_isRunningAsApplication()) + Process::terminate(); + } + + // Get defaults for various properties + int screen = DefaultScreen (display); + Window root = RootWindow (display, screen); + Visual* visual = DefaultVisual (display, screen); + + // Create atoms for our ClientMessages (these cannot be deleted) + specialId = XInternAtom (display, SpecialAtom, false); + broadcastId = XInternAtom (display, BroadcastAtom, false); + specialCallbackId = XInternAtom (display, SpecialCallbackAtom, false); + + // Create a context to store user data associated with Windows we + // create in WindowDriver + improbableNumber = XUniqueContext(); + + // We're only interested in client messages for this window + // which are always sent + XSetWindowAttributes swa; + swa.event_mask = NoEventMask; + + // Create our message window (this will never be mapped) + juce_messageWindowHandle = XCreateWindow (display, root, + 0, 0, 1, 1, 0, 0, InputOnly, + visual, CWEventMask, &swa); +} + +void MessageManager::doPlatformSpecificShutdown() +{ + if (errorCondition == false) + { + XDestroyWindow (display, juce_messageWindowHandle); + XCloseDisplay (display); + } +} + +bool juce_postMessageToSystemQueue (void* message) +{ + if (errorCondition) + return false; + + XClientMessageEvent clientMsg; + clientMsg.display = display; + clientMsg.window = juce_messageWindowHandle; + clientMsg.type = ClientMessage; + clientMsg.format = 32; + clientMsg.message_type = specialId; +#if JUCE_64BIT + clientMsg.data.l[0] = (long) (0x00000000ffffffff & (((uint64) message) >> 32)); + clientMsg.data.l[1] = (long) (0x00000000ffffffff & (long) message); +#else + clientMsg.data.l[0] = (long) message; +#endif + + XSendEvent (display, juce_messageWindowHandle, false, + NoEventMask, (XEvent*) &clientMsg); + + XFlush (display); // This is necessary to ensure the event is delivered + + return true; +} + +void MessageManager::broadcastMessage (const String& value) throw() +{ +} + +void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* func, + void* parameter) +{ + void* retVal = 0; + + if (! errorCondition) + { + if (! isThisTheMessageThread()) + { + static MessageThreadFuncCall messageFuncCallContext; + + const ScopedLock sl (messageFuncCallContext.lock); + + XClientMessageEvent clientMsg; + clientMsg.display = display; + clientMsg.window = juce_messageWindowHandle; + clientMsg.type = ClientMessage; + clientMsg.format = 32; + clientMsg.message_type = specialCallbackId; +#if JUCE_64BIT + clientMsg.data.l[0] = (long) (0x00000000ffffffff & (((uint64) &messageFuncCallContext) >> 32)); + clientMsg.data.l[1] = (long) (0x00000000ffffffff & (uint64) &messageFuncCallContext); +#else + clientMsg.data.l[0] = (long) &messageFuncCallContext; +#endif + + messageFuncCallContext.func = func; + messageFuncCallContext.parameter = parameter; + + if (XSendEvent (display, juce_messageWindowHandle, false, NoEventMask, (XEvent*) &clientMsg) == 0) + return 0; + + XFlush (display); // This is necessary to ensure the event is delivered + + // Wait for it to complete before continuing + messageFuncCallContext.event.wait(); + + retVal = messageFuncCallContext.result; + } + else + { + // Just call the function directly + retVal = func (parameter); + } + } + + return retVal; +} + +bool juce_dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages) +{ + if (errorCondition) + return false; + + if (breakIn) + { + errorCondition = true; + + if (juce_isRunningAsApplication()) + Process::terminate(); + } + + if (returnIfNoPendingMessages && ! XPending (display)) + return false; + + XEvent evt; + XNextEvent (display, &evt); + + if (evt.type == ClientMessage && evt.xany.window == juce_messageWindowHandle) + { + XClientMessageEvent* const clientMsg = (XClientMessageEvent*) &evt; + + if (clientMsg->format != 32) + { + jassertfalse + DBG ("Error: juce_dispatchNextMessageOnSystemQueue received malformed client message."); + } + else + { + JUCE_TRY + { +#if JUCE_64BIT + void* const messagePtr + = (void*) ((0xffffffff00000000 & (((uint64) clientMsg->data.l[0]) << 32)) + | (clientMsg->data.l[1] & 0x00000000ffffffff)); +#else + void* const messagePtr = (void*) (clientMsg->data.l[0]); +#endif + + if (clientMsg->message_type == specialId) + { + MessageManager::getInstance()->deliverMessage (messagePtr); + } + else if (clientMsg->message_type == specialCallbackId) + { + MessageThreadFuncCall* const call = (MessageThreadFuncCall*) messagePtr; + MessageCallbackFunction* func = call->func; + call->result = (*func) (call->parameter); + call->event.signal(); + } + else if (clientMsg->message_type == broadcastId) + { +#if 0 + TCHAR buffer[8192]; + zeromem (buffer, sizeof (buffer)); + + if (GlobalGetAtomName ((ATOM) lParam, buffer, 8192) != 0) + mq->deliverBroadcastMessage (String (buffer)); +#endif + } + else + { + DBG ("Error: juce_dispatchNextMessageOnSystemQueue received unknown client message."); + } + } + JUCE_CATCH_ALL + } + } + else if (evt.xany.window != juce_messageWindowHandle) + { + juce_windowMessageReceive (&evt); + } + + return true; +} + +END_JUCE_NAMESPACE + +#endif +/********* End of inlined file: juce_linux_Messaging.cpp *********/ + +/********* Start of inlined file: juce_linux_Midi.cpp *********/ + +#if JUCE_BUILD_GUI_CLASSES + +#if JUCE_ALSA + +#include + +BEGIN_JUCE_NAMESPACE + +static snd_seq_t* iterateDevices (const bool forInput, + StringArray& deviceNamesFound, + const int deviceIndexToOpen) +{ + snd_seq_t* returnedHandle = 0; + snd_seq_t* seqHandle; + + if (snd_seq_open (&seqHandle, "default", forInput ? SND_SEQ_OPEN_INPUT + : SND_SEQ_OPEN_OUTPUT, 0) == 0) + { + snd_seq_system_info_t* systemInfo; + snd_seq_client_info_t* clientInfo; + + if (snd_seq_system_info_malloc (&systemInfo) == 0) + { + if (snd_seq_system_info (seqHandle, systemInfo) == 0 + && snd_seq_client_info_malloc (&clientInfo) == 0) + { + int numClients = snd_seq_system_info_get_cur_clients (systemInfo); + + while (--numClients >= 0 && returnedHandle == 0) + { + if (snd_seq_query_next_client (seqHandle, clientInfo) == 0) + { + snd_seq_port_info_t* portInfo; + if (snd_seq_port_info_malloc (&portInfo) == 0) + { + int numPorts = snd_seq_client_info_get_num_ports (clientInfo); + const int client = snd_seq_client_info_get_client (clientInfo); + + snd_seq_port_info_set_client (portInfo, client); + snd_seq_port_info_set_port (portInfo, -1); + + while (--numPorts >= 0) + { + if (snd_seq_query_next_port (seqHandle, portInfo) == 0 + && (snd_seq_port_info_get_capability (portInfo) + & (forInput ? SND_SEQ_PORT_CAP_READ + : SND_SEQ_PORT_CAP_WRITE)) != 0) + { + deviceNamesFound.add (snd_seq_client_info_get_name (clientInfo)); + + if (deviceNamesFound.size() == deviceIndexToOpen + 1) + { + const int sourcePort = snd_seq_port_info_get_port (portInfo); + const int sourceClient = snd_seq_client_info_get_client (clientInfo); + + if (sourcePort != -1) + { + snd_seq_set_client_name (seqHandle, + forInput ? "Juce Midi Input" + : "Juce Midi Output"); + + const int portId + = snd_seq_create_simple_port (seqHandle, + forInput ? "Juce Midi In Port" + : "Juce Midi Out Port", + forInput ? (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE) + : (SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ), + SND_SEQ_PORT_TYPE_MIDI_GENERIC); + + snd_seq_connect_from (seqHandle, portId, sourceClient, sourcePort); + + returnedHandle = seqHandle; + break; + } + } + } + } + + snd_seq_port_info_free (portInfo); + } + } + } + + snd_seq_client_info_free (clientInfo); + } + + snd_seq_system_info_free (systemInfo); + } + + if (returnedHandle == 0) + snd_seq_close (seqHandle); + } + + deviceNamesFound.appendNumbersToDuplicates (true, true); + + return returnedHandle; +} + +static snd_seq_t* createDevice (const bool forInput, + const String& deviceNameToOpen) +{ + snd_seq_t* seqHandle = 0; + + if (snd_seq_open (&seqHandle, "default", forInput ? SND_SEQ_OPEN_INPUT + : SND_SEQ_OPEN_OUTPUT, 0) == 0) + { + snd_seq_set_client_name (seqHandle, + (const char*) (forInput ? (deviceNameToOpen + T(" Input")) + : (deviceNameToOpen + T(" Output")))); + + const int portId + = snd_seq_create_simple_port (seqHandle, + forInput ? "in" + : "out", + forInput ? (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE) + : (SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ), + forInput ? SND_SEQ_PORT_TYPE_APPLICATION + : SND_SEQ_PORT_TYPE_MIDI_GENERIC); + + if (portId < 0) + { + snd_seq_close (seqHandle); + seqHandle = 0; + } + } + + return seqHandle; +} + +class MidiOutputDevice +{ +public: + MidiOutputDevice (MidiOutput* const midiOutput_, + snd_seq_t* const seqHandle_) + : + midiOutput (midiOutput_), + seqHandle (seqHandle_), + maxEventSize (16 * 1024) + { + jassert (seqHandle != 0 && midiOutput != 0); + snd_midi_event_new (maxEventSize, &midiParser); + } + + ~MidiOutputDevice() + { + snd_midi_event_free (midiParser); + snd_seq_close (seqHandle); + } + + void sendMessageNow (const MidiMessage& message) + { + if (message.getRawDataSize() > maxEventSize) + { + maxEventSize = message.getRawDataSize(); + snd_midi_event_free (midiParser); + snd_midi_event_new (maxEventSize, &midiParser); + } + + snd_seq_event_t event; + snd_seq_ev_clear (&event); + + snd_midi_event_encode (midiParser, + message.getRawData(), + message.getRawDataSize(), + &event); + + snd_midi_event_reset_encode (midiParser); + + snd_seq_ev_set_source (&event, 0); + snd_seq_ev_set_subs (&event); + snd_seq_ev_set_direct (&event); + + snd_seq_event_output_direct (seqHandle, &event); + } + + juce_UseDebuggingNewOperator + +private: + MidiOutput* const midiOutput; + snd_seq_t* const seqHandle; + snd_midi_event_t* midiParser; + int maxEventSize; +}; + +const StringArray MidiOutput::getDevices() +{ + StringArray devices; + iterateDevices (false, devices, -1); + return devices; +} + +int MidiOutput::getDefaultDeviceIndex() +{ + return 0; +} + +MidiOutput* MidiOutput::openDevice (int deviceIndex) +{ + MidiOutput* newDevice = 0; + + StringArray devices; + snd_seq_t* const handle = iterateDevices (false, devices, deviceIndex); + + if (handle != 0) + { + newDevice = new MidiOutput(); + newDevice->internal = new MidiOutputDevice (newDevice, handle); + } + + return newDevice; +} + +MidiOutput* MidiOutput::createNewDevice (const String& deviceName) +{ + MidiOutput* newDevice = 0; + + snd_seq_t* const handle = createDevice (false, deviceName); + + if (handle != 0) + { + newDevice = new MidiOutput(); + newDevice->internal = new MidiOutputDevice (newDevice, handle); + } + + return newDevice; +} + +MidiOutput::~MidiOutput() +{ + MidiOutputDevice* const device = (MidiOutputDevice*) internal; + delete device; +} + +void MidiOutput::reset() +{ +} + +bool MidiOutput::getVolume (float& leftVol, float& rightVol) +{ + return false; +} + +void MidiOutput::setVolume (float leftVol, float rightVol) +{ +} + +void MidiOutput::sendMessageNow (const MidiMessage& message) +{ + ((MidiOutputDevice*) internal)->sendMessageNow (message); +} + +class MidiInputThread : public Thread +{ +public: + MidiInputThread (MidiInput* const midiInput_, + snd_seq_t* const seqHandle_, + MidiInputCallback* const callback_) + : Thread (T("Juce MIDI Input")), + midiInput (midiInput_), + seqHandle (seqHandle_), + callback (callback_) + { + jassert (seqHandle != 0 && callback != 0 && midiInput != 0); + } + + ~MidiInputThread() + { + snd_seq_close (seqHandle); + } + + void run() + { + const int maxEventSize = 16 * 1024; + snd_midi_event_t* midiParser; + + if (snd_midi_event_new (maxEventSize, &midiParser) >= 0) + { + uint8* const buffer = (uint8*) juce_malloc (maxEventSize); + + const int numPfds = snd_seq_poll_descriptors_count (seqHandle, POLLIN); + struct pollfd* const pfd = (struct pollfd*) alloca (numPfds * sizeof (struct pollfd)); + + snd_seq_poll_descriptors (seqHandle, pfd, numPfds, POLLIN); + + while (! threadShouldExit()) + { + if (poll (pfd, numPfds, 500) > 0) + { + snd_seq_event_t* inputEvent = 0; + + snd_seq_nonblock (seqHandle, 1); + + do + { + if (snd_seq_event_input (seqHandle, &inputEvent) >= 0) + { + // xxx what about SYSEXes that are too big for the buffer? + const int numBytes = snd_midi_event_decode (midiParser, buffer, maxEventSize, inputEvent); + + snd_midi_event_reset_decode (midiParser); + + if (numBytes > 0) + { + const MidiMessage message ((const uint8*) buffer, + numBytes, + Time::getMillisecondCounter() * 0.001); + + callback->handleIncomingMidiMessage (midiInput, message); + } + + snd_seq_free_event (inputEvent); + } + } + while (snd_seq_event_input_pending (seqHandle, 0) > 0); + + snd_seq_free_event (inputEvent); + } + } + + snd_midi_event_free (midiParser); + juce_free (buffer); + } + }; + + juce_UseDebuggingNewOperator + +private: + MidiInput* const midiInput; + snd_seq_t* const seqHandle; + MidiInputCallback* const callback; +}; + +MidiInput::MidiInput (const String& name_) + : name (name_), + internal (0) +{ +} + +MidiInput::~MidiInput() +{ + stop(); + MidiInputThread* const thread = (MidiInputThread*) internal; + delete thread; +} + +void MidiInput::start() +{ + ((MidiInputThread*) internal)->startThread(); +} + +void MidiInput::stop() +{ + ((MidiInputThread*) internal)->stopThread (3000); +} + +int MidiInput::getDefaultDeviceIndex() +{ + return 0; +} + +const StringArray MidiInput::getDevices() +{ + StringArray devices; + iterateDevices (true, devices, -1); + return devices; +} + +MidiInput* MidiInput::openDevice (int deviceIndex, MidiInputCallback* callback) +{ + MidiInput* newDevice = 0; + + StringArray devices; + snd_seq_t* const handle = iterateDevices (true, devices, deviceIndex); + + if (handle != 0) + { + newDevice = new MidiInput (devices [deviceIndex]); + newDevice->internal = new MidiInputThread (newDevice, handle, callback); + } + + return newDevice; +} + +MidiInput* MidiInput::createNewDevice (const String& deviceName, MidiInputCallback* callback) +{ + MidiInput* newDevice = 0; + + snd_seq_t* const handle = createDevice (true, deviceName); + + if (handle != 0) + { + newDevice = new MidiInput (deviceName); + newDevice->internal = new MidiInputThread (newDevice, handle, callback); + } + + return newDevice; +} + +END_JUCE_NAMESPACE + +#else + +// (These are just stub functions if ALSA is unavailable...) + +BEGIN_JUCE_NAMESPACE + +const StringArray MidiOutput::getDevices() { return StringArray(); } +int MidiOutput::getDefaultDeviceIndex() { return 0; } +MidiOutput* MidiOutput::openDevice (int) { return 0; } +MidiOutput* MidiOutput::createNewDevice (const String&) { return 0; } +MidiOutput::~MidiOutput() {} +void MidiOutput::reset() {} +bool MidiOutput::getVolume (float&, float&) { return false; } +void MidiOutput::setVolume (float, float) {} +void MidiOutput::sendMessageNow (const MidiMessage&) {} + +MidiInput::MidiInput (const String& name_) + : name (name_), + internal (0) +{} + +MidiInput::~MidiInput() {} +void MidiInput::start() {} +void MidiInput::stop() {} +int MidiInput::getDefaultDeviceIndex() { return 0; } +const StringArray MidiInput::getDevices() { return StringArray(); } +MidiInput* MidiInput::openDevice (int, MidiInputCallback*) { return 0; } +MidiInput* MidiInput::createNewDevice (const String&, MidiInputCallback*) { return 0; } + +END_JUCE_NAMESPACE + +#endif + +#endif +/********* End of inlined file: juce_linux_Midi.cpp *********/ + +/********* Start of inlined file: juce_linux_WebBrowserComponent.cpp *********/ + +BEGIN_JUCE_NAMESPACE + +/* + Sorry.. This class isn't implemented on Linux! +*/ + +WebBrowserComponent::WebBrowserComponent() + : browser (0), + blankPageShown (false) +{ + setOpaque (true); +} + +WebBrowserComponent::~WebBrowserComponent() +{ +} + +void WebBrowserComponent::goToURL (const String& url, + const StringArray* headers, + const MemoryBlock* postData) +{ + lastURL = url; + + lastHeaders.clear(); + if (headers != 0) + lastHeaders = *headers; + + lastPostData.setSize (0); + if (postData != 0) + lastPostData = *postData; + + blankPageShown = false; + +} + +void WebBrowserComponent::stop() +{ +} + +void WebBrowserComponent::goBack() +{ + lastURL = String::empty; + blankPageShown = false; + +} + +void WebBrowserComponent::goForward() +{ + lastURL = String::empty; + +} + +void WebBrowserComponent::paint (Graphics& g) +{ + g.fillAll (Colours::white); +} + +void WebBrowserComponent::checkWindowAssociation() +{ +} + +void WebBrowserComponent::reloadLastURL() +{ + if (lastURL.isNotEmpty()) + { + goToURL (lastURL, &lastHeaders, &lastPostData); + lastURL = String::empty; + } +} + +void WebBrowserComponent::parentHierarchyChanged() +{ + checkWindowAssociation(); +} + +void WebBrowserComponent::moved() +{ +} + +void WebBrowserComponent::resized() +{ +} + +void WebBrowserComponent::visibilityChanged() +{ + checkWindowAssociation(); +} + +bool WebBrowserComponent::pageAboutToLoad (const String& url) +{ + return true; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_linux_WebBrowserComponent.cpp *********/ + +/********* Start of inlined file: juce_linux_Windowing.cpp *********/ + +#if JUCE_BUILD_GUI_CLASSES + +#include +#include +#include +#include +#include +#include +#include + +#if JUCE_USE_XINERAMA + /* If you're trying to use Xinerama, you'll need to install the "libxinerama-dev" package.. + */ + #include +#endif + +#if JUCE_USE_XSHM + #include + #include + #include +#endif + +#if JUCE_OPENGL + /* Got an include error here? + + If you want to install OpenGL support, the packages to get are "mesa-common-dev" + and "freeglut3-dev". + + Alternatively, you can turn off the JUCE_OPENGL flag in juce_Config.h if you + want to disable it. + */ + #include +#endif + +#undef KeyPress + +BEGIN_JUCE_NAMESPACE + +#define TAKE_FOCUS 0 +#define DELETE_WINDOW 1 + +#define SYSTEM_TRAY_REQUEST_DOCK 0 +#define SYSTEM_TRAY_BEGIN_MESSAGE 1 +#define SYSTEM_TRAY_CANCEL_MESSAGE 2 + +static const int repaintTimerPeriod = 1000 / 100; // 100 fps maximum + +static Atom wm_ChangeState = None; +static Atom wm_State = None; +static Atom wm_Protocols = None; +static Atom wm_ProtocolList [2] = { None, None }; +static Atom wm_ActiveWin = None; + +#define ourDndVersion 3 +static Atom XA_XdndAware = None; +static Atom XA_XdndEnter = None; +static Atom XA_XdndLeave = None; +static Atom XA_XdndPosition = None; +static Atom XA_XdndStatus = None; +static Atom XA_XdndDrop = None; +static Atom XA_XdndFinished = None; +static Atom XA_XdndSelection = None; +static Atom XA_XdndProxy = None; + +static Atom XA_XdndTypeList = None; +static Atom XA_XdndActionList = None; +static Atom XA_XdndActionDescription = None; +static Atom XA_XdndActionCopy = None; +static Atom XA_XdndActionMove = None; +static Atom XA_XdndActionLink = None; +static Atom XA_XdndActionAsk = None; +static Atom XA_XdndActionPrivate = None; +static Atom XA_JXSelectionWindowProperty = None; + +static Atom XA_MimeTextPlain = None; +static Atom XA_MimeTextUriList = None; +static Atom XA_MimeRootDrop = None; + +static XErrorHandler oldHandler = 0; +static int trappedErrorCode = 0; + +extern "C" int errorTrapHandler (Display* dpy, XErrorEvent* err) +{ + trappedErrorCode = err->error_code; + return 0; +} + +static void trapErrors() +{ + trappedErrorCode = 0; + oldHandler = XSetErrorHandler (errorTrapHandler); +} + +static bool untrapErrors() +{ + XSetErrorHandler (oldHandler); + return (trappedErrorCode == 0); +} + +static bool isActiveApplication = false; + +bool Process::isForegroundProcess() throw() +{ + return isActiveApplication; +} + +// (used in the messaging code, declared here for build reasons) +bool juce_isRunningAsApplication() +{ + return JUCEApplication::getInstance() != 0; +} + +// These are defined in juce_linux_Messaging.cpp +extern Display* display; +extern XContext improbableNumber; + +static const int eventMask = NoEventMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask + | EnterWindowMask | LeaveWindowMask | PointerMotionMask | KeymapStateMask + | ExposureMask | StructureNotifyMask | FocusChangeMask; + +static int pointerMap[5]; +static int lastMousePosX = 0, lastMousePosY = 0; + +enum MouseButtons +{ + NoButton = 0, + LeftButton = 1, + MiddleButton = 2, + RightButton = 3, + WheelUp = 4, + WheelDown = 5 +}; + +static void getMousePos (int& x, int& y, int& mouseMods) throw() +{ + Window root, child; + int winx, winy; + unsigned int mask; + + mouseMods = 0; + + if (XQueryPointer (display, + RootWindow (display, DefaultScreen (display)), + &root, &child, + &x, &y, &winx, &winy, &mask) == False) + { + // Pointer not on the default screen + x = y = -1; + } + else + { + if ((mask & Button1Mask) != 0) + mouseMods |= ModifierKeys::leftButtonModifier; + + if ((mask & Button2Mask) != 0) + mouseMods |= ModifierKeys::middleButtonModifier; + + if ((mask & Button3Mask) != 0) + mouseMods |= ModifierKeys::rightButtonModifier; + } +} + +static int AltMask = 0; +static int NumLockMask = 0; +static bool numLock = 0; +static bool capsLock = 0; +static char keyStates [32]; + +static void updateKeyStates (const int keycode, const bool press) throw() +{ + const int keybyte = keycode >> 3; + const int keybit = (1 << (keycode & 7)); + + if (press) + keyStates [keybyte] |= keybit; + else + keyStates [keybyte] &= ~keybit; +} + +static bool keyDown (const int keycode) throw() +{ + const int keybyte = keycode >> 3; + const int keybit = (1 << (keycode & 7)); + + return (keyStates [keybyte] & keybit) != 0; +} + +static const int extendedKeyModifier = 0x10000000; + +bool KeyPress::isKeyCurrentlyDown (const int keyCode) throw() +{ + int keysym; + + if (keyCode & extendedKeyModifier) + { + keysym = 0xff00 | (keyCode & 0xff); + } + else + { + keysym = keyCode; + + if (keysym == (XK_Tab & 0xff) + || keysym == (XK_Return & 0xff) + || keysym == (XK_Escape & 0xff) + || keysym == (XK_BackSpace & 0xff)) + { + keysym |= 0xff00; + } + } + + return keyDown (XKeysymToKeycode (display, keysym)); +} + +// Alt and Num lock are not defined by standard X +// modifier constants: check what they're mapped to +static void getModifierMapping() throw() +{ + const int altLeftCode = XKeysymToKeycode (display, XK_Alt_L); + const int numLockCode = XKeysymToKeycode (display, XK_Num_Lock); + + AltMask = 0; + NumLockMask = 0; + + XModifierKeymap* mapping = XGetModifierMapping (display); + + if (mapping) + { + for (int i = 0; i < 8; i++) + { + if (mapping->modifiermap [i << 1] == altLeftCode) + AltMask = 1 << i; + else if (mapping->modifiermap [i << 1] == numLockCode) + NumLockMask = 1 << i; + } + + XFreeModifiermap (mapping); + } +} + +static int currentModifiers = 0; + +void ModifierKeys::updateCurrentModifiers() throw() +{ + currentModifierFlags = currentModifiers; +} + +const ModifierKeys ModifierKeys::getCurrentModifiersRealtime() throw() +{ + int x, y, mouseMods; + getMousePos (x, y, mouseMods); + + currentModifiers &= ~ModifierKeys::allMouseButtonModifiers; + currentModifiers |= mouseMods; + + return ModifierKeys (currentModifiers); +} + +static void updateKeyModifiers (const int status) throw() +{ + currentModifiers &= ~(ModifierKeys::shiftModifier + | ModifierKeys::ctrlModifier + | ModifierKeys::altModifier); + + if (status & ShiftMask) + currentModifiers |= ModifierKeys::shiftModifier; + + if (status & ControlMask) + currentModifiers |= ModifierKeys::ctrlModifier; + + if (status & AltMask) + currentModifiers |= ModifierKeys::altModifier; + + numLock = ((status & NumLockMask) != 0); + capsLock = ((status & LockMask) != 0); +} + +static bool updateKeyModifiersFromSym (KeySym sym, const bool press) throw() +{ + int modifier = 0; + bool isModifier = true; + + switch (sym) + { + case XK_Shift_L: + case XK_Shift_R: + modifier = ModifierKeys::shiftModifier; + break; + + case XK_Control_L: + case XK_Control_R: + modifier = ModifierKeys::ctrlModifier; + break; + + case XK_Alt_L: + case XK_Alt_R: + modifier = ModifierKeys::altModifier; + break; + + case XK_Num_Lock: + if (press) + numLock = ! numLock; + + break; + + case XK_Caps_Lock: + if (press) + capsLock = ! capsLock; + + break; + + case XK_Scroll_Lock: + break; + + default: + isModifier = false; + break; + } + + if (modifier != 0) + { + if (press) + currentModifiers |= modifier; + else + currentModifiers &= ~modifier; + } + + return isModifier; +} + +#if JUCE_USE_XSHM +static bool isShmAvailable() throw() +{ + static bool isChecked = false; + static bool isAvailable = false; + + if (! isChecked) + { + isChecked = true; + + int major, minor; + Bool pixmaps; + + if (XShmQueryVersion (display, &major, &minor, &pixmaps)) + { + trapErrors(); + + XShmSegmentInfo segmentInfo; + zerostruct (segmentInfo); + XImage* xImage = XShmCreateImage (display, DefaultVisual (display, DefaultScreen (display)), + 24, ZPixmap, 0, &segmentInfo, 50, 50); + + if ((segmentInfo.shmid = shmget (IPC_PRIVATE, + xImage->bytes_per_line * xImage->height, + IPC_CREAT | 0777)) >= 0) + { + segmentInfo.shmaddr = (char*) shmat (segmentInfo.shmid, 0, 0); + + if (segmentInfo.shmaddr != (void*) -1) + { + segmentInfo.readOnly = False; + xImage->data = segmentInfo.shmaddr; + XSync (display, False); + + if (XShmAttach (display, &segmentInfo) != 0) + { + XSync (display, False); + XShmDetach (display, &segmentInfo); + + isAvailable = true; + } + } + + XFlush (display); + XDestroyImage (xImage); + + shmdt (segmentInfo.shmaddr); + } + + shmctl (segmentInfo.shmid, IPC_RMID, 0); + + isAvailable &= untrapErrors(); + } + } + + return isAvailable; +} +#endif + +class XBitmapImage : public Image +{ +public: + + XBitmapImage (const PixelFormat format_, const int w, const int h, + const bool clearImage, const bool is16Bit_) + : Image (format_, w, h), + is16Bit (is16Bit_) + { + jassert (format_ == RGB || format_ == ARGB); + + pixelStride = (format_ == RGB) ? 3 : 4; + lineStride = ((w * pixelStride + 3) & ~3); + + Visual* const visual = DefaultVisual (display, DefaultScreen (display)); + +#if JUCE_USE_XSHM + usingXShm = false; + + if ((! is16Bit) && isShmAvailable()) + { + zerostruct (segmentInfo); + + xImage = XShmCreateImage (display, visual, 24, ZPixmap, 0, &segmentInfo, w, h); + + if (xImage != 0) + { + if ((segmentInfo.shmid = shmget (IPC_PRIVATE, + xImage->bytes_per_line * xImage->height, + IPC_CREAT | 0777)) >= 0) + { + segmentInfo.shmaddr = (char*) shmat (segmentInfo.shmid, 0, 0); + + if (segmentInfo.shmaddr != (void*) -1) + { + segmentInfo.readOnly = False; + + xImage->data = segmentInfo.shmaddr; + imageData = (uint8*) segmentInfo.shmaddr; + + XSync (display, False); + + if (XShmAttach (display, &segmentInfo) != 0) + { + XSync (display, False); + usingXShm = true; + } + else + { + jassertfalse + } + } + else + { + shmctl (segmentInfo.shmid, IPC_RMID, 0); + } + } + } + } + + if (! usingXShm) +#endif + { + imageData = (uint8*) juce_malloc (lineStride * h); + + if (format_ == ARGB && clearImage) + zeromem (imageData, h * lineStride); + + xImage = (XImage*) juce_calloc (sizeof (XImage)); + + xImage->width = w; + xImage->height = h; + xImage->xoffset = 0; + xImage->format = ZPixmap; + xImage->data = (char*) imageData; + xImage->byte_order = ImageByteOrder (display); + xImage->bitmap_unit = BitmapUnit (display); + xImage->bitmap_bit_order = BitmapBitOrder (display); + xImage->bitmap_pad = 32; + xImage->depth = pixelStride * 8; + xImage->bytes_per_line = lineStride; + xImage->bits_per_pixel = pixelStride * 8; + xImage->red_mask = 0x00FF0000; + xImage->green_mask = 0x0000FF00; + xImage->blue_mask = 0x000000FF; + + if (is16Bit) + { + const int pixelStride = 2; + const int lineStride = ((w * pixelStride + 3) & ~3); + + xImage->data = (char*) juce_malloc (lineStride * h); + xImage->bitmap_pad = 16; + xImage->depth = pixelStride * 8; + xImage->bytes_per_line = lineStride; + xImage->bits_per_pixel = pixelStride * 8; + xImage->red_mask = visual->red_mask; + xImage->green_mask = visual->green_mask; + xImage->blue_mask = visual->blue_mask; + } + + if (! XInitImage (xImage)) + { + jassertfalse + } + } + } + + ~XBitmapImage() + { +#if JUCE_USE_XSHM + if (usingXShm) + { + XShmDetach (display, &segmentInfo); + + XFlush (display); + XDestroyImage (xImage); + + shmdt (segmentInfo.shmaddr); + shmctl (segmentInfo.shmid, IPC_RMID, 0); + } + else +#endif + { + juce_free (xImage->data); + xImage->data = 0; + XDestroyImage (xImage); + } + + if (! is16Bit) + imageData = 0; // to stop the base class freeing this (for the 16-bit version we want it to free it) + } + + void blitToWindow (Window window, int dx, int dy, int dw, int dh, int sx, int sy) + { + static GC gc = 0; + + if (gc == 0) + gc = DefaultGC (display, DefaultScreen (display)); + + if (is16Bit) + { + const uint32 rMask = xImage->red_mask; + const uint32 rShiftL = jmax (0, getShiftNeeded (rMask)); + const uint32 rShiftR = jmax (0, -getShiftNeeded (rMask)); + const uint32 gMask = xImage->green_mask; + const uint32 gShiftL = jmax (0, getShiftNeeded (gMask)); + const uint32 gShiftR = jmax (0, -getShiftNeeded (gMask)); + const uint32 bMask = xImage->blue_mask; + const uint32 bShiftL = jmax (0, getShiftNeeded (bMask)); + const uint32 bShiftR = jmax (0, -getShiftNeeded (bMask)); + + int ls, ps; + const uint8* const pixels = lockPixelDataReadOnly (0, 0, getWidth(), getHeight(), ls, ps); + + jassert (! isARGB()) + + for (int y = sy; y < sy + dh; ++y) + { + const uint8* p = pixels + y * ls + sx * ps; + + for (int x = sx; x < sx + dw; ++x) + { + const PixelRGB* const pixel = (const PixelRGB*) p; + p += ps; + + XPutPixel (xImage, x, y, + (((((uint32) pixel->getRed()) << rShiftL) >> rShiftR) & rMask) + | (((((uint32) pixel->getGreen()) << gShiftL) >> gShiftR) & gMask) + | (((((uint32) pixel->getBlue()) << bShiftL) >> bShiftR) & bMask)); + } + } + + releasePixelDataReadOnly (pixels); + } + + // blit results to screen. +#if JUCE_USE_XSHM + if (usingXShm) + XShmPutImage (display, (::Drawable) window, gc, xImage, sx, sy, dx, dy, dw, dh, False); + else +#endif + XPutImage (display, (::Drawable) window, gc, xImage, sx, sy, dx, dy, dw, dh); + } + + juce_UseDebuggingNewOperator + +private: + XImage* xImage; + const bool is16Bit; + +#if JUCE_USE_XSHM + XShmSegmentInfo segmentInfo; + bool usingXShm; +#endif + + static int getShiftNeeded (const uint32 mask) throw() + { + for (int i = 32; --i >= 0;) + if (((mask >> i) & 1) != 0) + return i - 7; + + jassertfalse + return 0; + } +}; + +#define checkMessageManagerIsLocked jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager()); + +class LinuxComponentPeer : public ComponentPeer +{ +public: + + LinuxComponentPeer (Component* const component, const int windowStyleFlags) + : ComponentPeer (component, windowStyleFlags), + windowH (0), + parentWindow (0), + wx (0), + wy (0), + ww (0), + wh (0), + taskbarImage (0), + fullScreen (false), + entered (false), + mapped (false) + { + // it's dangerous to create a window on a thread other than the message thread.. + checkMessageManagerIsLocked + + repainter = new LinuxRepaintManager (this); + + createWindow(); + + setTitle (component->getName()); + } + + ~LinuxComponentPeer() + { + // it's dangerous to delete a window on a thread other than the message thread.. + checkMessageManagerIsLocked + + deleteTaskBarIcon(); + + destroyWindow(); + + windowH = 0; + delete repainter; + } + + void* getNativeHandle() const + { + return (void*) windowH; + } + + static LinuxComponentPeer* getPeerFor (Window windowHandle) throw() + { + XPointer peer = 0; + + if (! XFindContext (display, (XID) windowHandle, improbableNumber, &peer)) + { + if (peer != 0 && ! ((LinuxComponentPeer*) peer)->isValidMessageListener()) + peer = 0; + } + + return (LinuxComponentPeer*) peer; + } + + void setVisible (bool shouldBeVisible) + { + if (shouldBeVisible) + XMapWindow (display, windowH); + else + XUnmapWindow (display, windowH); + } + + void setTitle (const String& title) + { + setWindowTitle (windowH, title); + } + + void setPosition (int x, int y) + { + setBounds (x, y, ww, wh, false); + } + + void setSize (int w, int h) + { + setBounds (wx, wy, w, h, false); + } + + void setBounds (int x, int y, int w, int h, const bool isNowFullScreen) + { + fullScreen = isNowFullScreen; + + if (windowH != 0) + { + const ComponentDeletionWatcher deletionChecker (component); + + wx = x; + wy = y; + ww = jmax (1, w); + wh = jmax (1, h); + + if (! mapped) + { + // Make sure the Window manager does what we want + XSizeHints* hints = XAllocSizeHints(); + hints->flags = USSize | USPosition; + hints->width = ww + windowBorder.getLeftAndRight(); + hints->height = wh + windowBorder.getTopAndBottom(); + hints->x = wx - windowBorder.getLeft(); + hints->y = wy - windowBorder.getTop(); + XSetWMNormalHints (display, windowH, hints); + XFree (hints); + } + + XMoveResizeWindow (display, windowH, + wx - windowBorder.getLeft(), + wy - windowBorder.getTop(), + ww + windowBorder.getLeftAndRight(), + wh + windowBorder.getTopAndBottom()); + + if (! deletionChecker.hasBeenDeleted()) + { + updateBorderSize(); + handleMovedOrResized(); + } + } + } + + void getBounds (int& x, int& y, int& w, int& h) const + { + x = wx; + y = wy; + w = ww; + h = wh; + } + + int getScreenX() const + { + return wx; + } + + int getScreenY() const + { + return wy; + } + + void relativePositionToGlobal (int& x, int& y) + { + x += wx; + y += wy; + } + + void globalPositionToRelative (int& x, int& y) + { + x -= wx; + y -= wy; + } + + void setMinimised (bool shouldBeMinimised) + { + if (shouldBeMinimised) + { + Window root = RootWindow (display, DefaultScreen (display)); + + XClientMessageEvent clientMsg; + clientMsg.display = display; + clientMsg.window = windowH; + clientMsg.type = ClientMessage; + clientMsg.format = 32; + clientMsg.message_type = wm_ChangeState; + clientMsg.data.l[0] = IconicState; + + XSendEvent (display, root, false, + SubstructureRedirectMask | SubstructureNotifyMask, + (XEvent*) &clientMsg); + } + else + { + setVisible (true); + } + } + + bool isMinimised() const + { + bool minimised = false; + + unsigned char* stateProp; + unsigned long nitems, bytesLeft; + Atom actualType; + int actualFormat; + + if (XGetWindowProperty (display, windowH, wm_State, 0, 64, False, + wm_State, &actualType, &actualFormat, &nitems, &bytesLeft, + &stateProp) == Success + && actualType == wm_State + && actualFormat == 32 + && nitems > 0) + { + if (((unsigned long*) stateProp)[0] == IconicState) + minimised = true; + + XFree (stateProp); + } + + return minimised; + } + + void setFullScreen (const bool shouldBeFullScreen) + { + Rectangle r (lastNonFullscreenBounds); // (get a copy of this before de-minimising) + + setMinimised (false); + + if (fullScreen != shouldBeFullScreen) + { + if (shouldBeFullScreen) + r = Desktop::getInstance().getMainMonitorArea(); + + if (! r.isEmpty()) + setBounds (r.getX(), r.getY(), r.getWidth(), r.getHeight(), shouldBeFullScreen); + + getComponent()->repaint(); + } + } + + bool isFullScreen() const + { + return fullScreen; + } + + bool isChildWindowOf (Window possibleParent) const + { + Window* windowList = 0; + uint32 windowListSize = 0; + Window parent, root; + + if (XQueryTree (display, windowH, &root, &parent, &windowList, &windowListSize) != 0) + { + if (windowList != 0) + XFree (windowList); + + return parent == possibleParent; + } + + return false; + } + + bool isFrontWindow() const + { + Window* windowList = 0; + uint32 windowListSize = 0; + bool result = false; + + Window parent, root = RootWindow (display, DefaultScreen (display)); + + if (XQueryTree (display, root, &root, &parent, &windowList, &windowListSize) != 0) + { + for (int i = windowListSize; --i >= 0;) + { + LinuxComponentPeer* const peer = LinuxComponentPeer::getPeerFor (windowList[i]); + + if (peer != 0) + { + result = (peer == this); + break; + } + } + } + + if (windowList != 0) + XFree (windowList); + + return result; + } + + bool contains (int x, int y, bool trueIfInAChildWindow) const + { + jassert (x >= 0 && y >= 0 && x < ww && y < wh); // should only be called for points that are actually inside the bounds + + if (((unsigned int) x) >= (unsigned int) ww + || ((unsigned int) y) >= (unsigned int) wh) + return false; + + bool inFront = false; + + for (int i = 0; i < Desktop::getInstance().getNumComponents(); ++i) + { + Component* const c = Desktop::getInstance().getComponent (i); + + if (inFront) + { + if (c->contains (x + wx - c->getScreenX(), + y + wy - c->getScreenY())) + { + return false; + } + } + else if (c == getComponent()) + { + inFront = true; + } + } + + if (trueIfInAChildWindow) + return true; + + ::Window root, child; + unsigned int bw, depth; + int wx, wy, w, h; + + if (! XGetGeometry (display, (::Drawable) windowH, &root, + &wx, &wy, (unsigned int*) &w, (unsigned int*) &h, + &bw, &depth)) + { + return false; + } + + if (! XTranslateCoordinates (display, windowH, windowH, x, y, &wx, &wy, &child)) + return false; + + return child == None; + } + + const BorderSize getFrameSize() const + { + return BorderSize(); + } + + bool setAlwaysOnTop (bool alwaysOnTop) + { + if (windowH != 0) + { + const bool wasVisible = component->isVisible(); + + if (wasVisible) + setVisible (false); // doesn't always seem to work if the window is visible when this is done.. + + XSetWindowAttributes swa; + swa.override_redirect = alwaysOnTop ? True : False; + + XChangeWindowAttributes (display, windowH, CWOverrideRedirect, &swa); + + if (wasVisible) + setVisible (true); + } + + return true; + } + + void toFront (bool makeActive) + { + if (makeActive) + { + setVisible (true); + grabFocus(); + } + + XEvent ev; + ev.xclient.type = ClientMessage; + ev.xclient.serial = 0; + ev.xclient.send_event = True; + ev.xclient.message_type = wm_ActiveWin; + ev.xclient.window = windowH; + ev.xclient.format = 32; + ev.xclient.data.l[0] = 2; + ev.xclient.data.l[1] = CurrentTime; + ev.xclient.data.l[2] = 0; + ev.xclient.data.l[3] = 0; + ev.xclient.data.l[4] = 0; + + XSendEvent (display, RootWindow (display, DefaultScreen (display)), + False, + SubstructureRedirectMask | SubstructureNotifyMask, + &ev); + + XSync (display, False); + + handleBroughtToFront(); + } + + void toBehind (ComponentPeer* other) + { + LinuxComponentPeer* const otherPeer = dynamic_cast (other); + jassert (otherPeer != 0); // wrong type of window? + + if (otherPeer != 0) + { + setMinimised (false); + + Window newStack[] = { otherPeer->windowH, windowH }; + + XRestackWindows (display, newStack, 2); + } + } + + bool isFocused() const + { + int revert; + Window focusedWindow = 0; + XGetInputFocus (display, &focusedWindow, &revert); + + return focusedWindow == windowH; + } + + void grabFocus() + { + XWindowAttributes atts; + + if (windowH != 0 + && XGetWindowAttributes (display, windowH, &atts) + && atts.map_state == IsViewable + && ! isFocused()) + { + XSetInputFocus (display, windowH, RevertToParent, CurrentTime); + isActiveApplication = true; + } + } + + void repaint (int x, int y, int w, int h) + { + if (Rectangle::intersectRectangles (x, y, w, h, + 0, 0, + getComponent()->getWidth(), + getComponent()->getHeight())) + { + repainter->repaint (x, y, w, h); + } + } + + void performAnyPendingRepaintsNow() + { + repainter->performAnyPendingRepaintsNow(); + } + + void setIcon (const Image& newIcon) + { + const int dataSize = newIcon.getWidth() * newIcon.getHeight() + 2; + uint32* const data = (uint32*) juce_malloc (dataSize); + + int index = 0; + data[index++] = newIcon.getWidth(); + data[index++] = newIcon.getHeight(); + + for (int y = 0; y < newIcon.getHeight(); ++y) + for (int x = 0; x < newIcon.getWidth(); ++x) + data[index++] = newIcon.getPixelAt (x, y).getARGB(); + + XChangeProperty (display, windowH, + XInternAtom (display, "_NET_WM_ICON", False), + XA_CARDINAL, 32, PropModeReplace, + (unsigned char*) data, dataSize); + + XSync (display, False); + + juce_free (data); + } + + void handleWindowMessage (XEvent* event) + { + switch (event->xany.type) + { + case 2: // 'KeyPress' + { + XKeyEvent* const keyEvent = (XKeyEvent*) &event->xkey; + updateKeyStates (keyEvent->keycode, true); + + char utf8 [64]; + zeromem (utf8, sizeof (utf8)); + KeySym sym; + XLookupString (keyEvent, utf8, sizeof (utf8), &sym, 0); + + const juce_wchar unicodeChar = *(const juce_wchar*) String::fromUTF8 ((const uint8*) utf8, sizeof (utf8) - 1); + int keyCode = (int) unicodeChar; + + if (keyCode < 0x20) + keyCode = XKeycodeToKeysym (display, keyEvent->keycode, + (currentModifiers & ModifierKeys::shiftModifier) != 0 ? 1 : 0); + + const int oldMods = currentModifiers; + bool keyPressed = false; + + const bool keyDownChange = (sym != NoSymbol) && ! updateKeyModifiersFromSym (sym, true); + + if ((sym & 0xff00) == 0xff00) + { + // Translate keypad + if (sym == XK_KP_Divide) + keyCode = XK_slash; + else if (sym == XK_KP_Multiply) + keyCode = XK_asterisk; + else if (sym == XK_KP_Subtract) + keyCode = XK_hyphen; + else if (sym == XK_KP_Add) + keyCode = XK_plus; + else if (sym == XK_KP_Enter) + keyCode = XK_Return; + else if (sym == XK_KP_Decimal) + keyCode = numLock ? XK_period : XK_Delete; + else if (sym == XK_KP_0) + keyCode = numLock ? XK_0 : XK_Insert; + else if (sym == XK_KP_1) + keyCode = numLock ? XK_1 : XK_End; + else if (sym == XK_KP_2) + keyCode = numLock ? XK_2 : XK_Down; + else if (sym == XK_KP_3) + keyCode = numLock ? XK_3 : XK_Page_Down; + else if (sym == XK_KP_4) + keyCode = numLock ? XK_4 : XK_Left; + else if (sym == XK_KP_5) + keyCode = XK_5; + else if (sym == XK_KP_6) + keyCode = numLock ? XK_6 : XK_Right; + else if (sym == XK_KP_7) + keyCode = numLock ? XK_7 : XK_Home; + else if (sym == XK_KP_8) + keyCode = numLock ? XK_8 : XK_Up; + else if (sym == XK_KP_9) + keyCode = numLock ? XK_9 : XK_Page_Up; + + switch (sym) + { + case XK_Left: + case XK_Right: + case XK_Up: + case XK_Down: + case XK_Page_Up: + case XK_Page_Down: + case XK_End: + case XK_Home: + case XK_Delete: + case XK_Insert: + keyPressed = true; + keyCode = (sym & 0xff) | extendedKeyModifier; + break; + case XK_Tab: + case XK_Return: + case XK_Escape: + case XK_BackSpace: + keyPressed = true; + keyCode &= 0xff; + break; + default: + { + if (sym >= XK_F1 && sym <= XK_F16) + { + keyPressed = true; + keyCode = (sym & 0xff) | extendedKeyModifier; + } + break; + } + } + } + + if (utf8[0] != 0 || ((sym & 0xff00) == 0 && sym >= 8)) + keyPressed = true; + + if (oldMods != currentModifiers) + handleModifierKeysChange(); + + if (keyDownChange) + handleKeyUpOrDown(); + + if (keyPressed) + handleKeyPress (keyCode, unicodeChar); + + break; + } + + case KeyRelease: + { + const XKeyEvent* const keyEvent = (const XKeyEvent*) &event->xkey; + updateKeyStates (keyEvent->keycode, false); + + KeySym sym = XKeycodeToKeysym (display, keyEvent->keycode, 0); + + const int oldMods = currentModifiers; + const bool keyDownChange = (sym != NoSymbol) && ! updateKeyModifiersFromSym (sym, false); + + if (oldMods != currentModifiers) + handleModifierKeysChange(); + + if (keyDownChange) + handleKeyUpOrDown(); + + break; + } + + case ButtonPress: + { + const XButtonPressedEvent* const buttonPressEvent = (const XButtonPressedEvent*) &event->xbutton; + + bool buttonMsg = false; + bool wheelUpMsg = false; + bool wheelDownMsg = false; + + const int map = pointerMap [buttonPressEvent->button - Button1]; + + if (map == LeftButton) + { + currentModifiers |= ModifierKeys::leftButtonModifier; + buttonMsg = true; + } + else if (map == RightButton) + { + currentModifiers |= ModifierKeys::rightButtonModifier; + buttonMsg = true; + } + else if (map == MiddleButton) + { + currentModifiers |= ModifierKeys::middleButtonModifier; + buttonMsg = true; + } + else if (map == WheelUp) + { + wheelUpMsg = true; + } + else if (map == WheelDown) + { + wheelDownMsg = true; + } + + updateKeyModifiers (buttonPressEvent->state); + + if (buttonMsg) + { + toFront (true); + handleMouseDown (buttonPressEvent->x, buttonPressEvent->y, + getEventTime (buttonPressEvent->time)); + } + else if (wheelUpMsg || wheelDownMsg) + { + handleMouseWheel (0, wheelDownMsg ? -84 : 84, + getEventTime (buttonPressEvent->time)); + } + + lastMousePosX = lastMousePosY = 0x100000; + break; + } + + case ButtonRelease: + { + const XButtonReleasedEvent* const buttonRelEvent = (const XButtonReleasedEvent*) &event->xbutton; + + const int oldModifiers = currentModifiers; + const int map = pointerMap [buttonRelEvent->button - Button1]; + + if (map == LeftButton) + currentModifiers &= ~ModifierKeys::leftButtonModifier; + else if (map == RightButton) + currentModifiers &= ~ModifierKeys::rightButtonModifier; + else if (map == MiddleButton) + currentModifiers &= ~ModifierKeys::middleButtonModifier; + + updateKeyModifiers (buttonRelEvent->state); + + handleMouseUp (oldModifiers, + buttonRelEvent->x, buttonRelEvent->y, + getEventTime (buttonRelEvent->time)); + + lastMousePosX = lastMousePosY = 0x100000; + break; + } + + case MotionNotify: + { + const XPointerMovedEvent* const movedEvent = (const XPointerMovedEvent*) &event->xmotion; + + updateKeyModifiers (movedEvent->state); + + int x, y, mouseMods; + getMousePos (x, y, mouseMods); + + if (lastMousePosX != x || lastMousePosY != y) + { + lastMousePosX = x; + lastMousePosY = y; + + if (parentWindow != 0 && (styleFlags & windowHasTitleBar) == 0) + { + Window wRoot = 0, wParent = 0; + Window* wChild = 0; + unsigned int numChildren; + XQueryTree (display, windowH, &wRoot, &wParent, &wChild, &numChildren); + + if (wParent != 0 + && wParent != windowH + && wParent != wRoot) + { + parentWindow = wParent; + updateBounds(); + x -= getScreenX(); + y -= getScreenY(); + } + else + { + parentWindow = 0; + x -= getScreenX(); + y -= getScreenY(); + } + } + else + { + x -= getScreenX(); + y -= getScreenY(); + } + + if ((currentModifiers & ModifierKeys::allMouseButtonModifiers) == 0) + handleMouseMove (x, y, getEventTime (movedEvent->time)); + else + handleMouseDrag (x, y, getEventTime (movedEvent->time)); + } + + break; + } + + case EnterNotify: + { + lastMousePosX = lastMousePosY = 0x100000; + const XEnterWindowEvent* const enterEvent = (const XEnterWindowEvent*) &event->xcrossing; + + if ((currentModifiers & ModifierKeys::allMouseButtonModifiers) == 0 + && ! entered) + { + updateKeyModifiers (enterEvent->state); + + handleMouseEnter (enterEvent->x, enterEvent->y, getEventTime (enterEvent->time)); + + entered = true; + } + + break; + } + + case LeaveNotify: + { + const XLeaveWindowEvent* const leaveEvent = (const XLeaveWindowEvent*) &event->xcrossing; + + // Suppress the normal leave if we've got a pointer grab, or if + // it's a bogus one caused by clicking a mouse button when running + // in a Window manager + if (((currentModifiers & ModifierKeys::allMouseButtonModifiers) == 0 + && leaveEvent->mode == NotifyNormal) + || leaveEvent->mode == NotifyUngrab) + { + updateKeyModifiers (leaveEvent->state); + + handleMouseExit (leaveEvent->x, leaveEvent->y, getEventTime (leaveEvent->time)); + + entered = false; + } + + break; + } + + case FocusIn: + { + isActiveApplication = true; + if (isFocused()) + handleFocusGain(); + + break; + } + + case FocusOut: + { + isActiveApplication = false; + if (! isFocused()) + handleFocusLoss(); + + break; + } + + case Expose: + { + // Batch together all pending expose events + XExposeEvent* exposeEvent = (XExposeEvent*) &event->xexpose; + XEvent nextEvent; + + if (exposeEvent->window != windowH) + { + Window child; + XTranslateCoordinates (display, exposeEvent->window, windowH, + exposeEvent->x, exposeEvent->y, &exposeEvent->x, &exposeEvent->y, + &child); + } + + repaint (exposeEvent->x, exposeEvent->y, + exposeEvent->width, exposeEvent->height); + + while (XEventsQueued (display, QueuedAfterFlush) > 0) + { + XPeekEvent (display, (XEvent*) &nextEvent); + if (nextEvent.type != Expose || nextEvent.xany.window != event->xany.window) + break; + + XNextEvent (display, (XEvent*) &nextEvent); + XExposeEvent* nextExposeEvent = (XExposeEvent*) &nextEvent.xexpose; + repaint (nextExposeEvent->x, nextExposeEvent->y, + nextExposeEvent->width, nextExposeEvent->height); + } + + break; + } + + case CirculateNotify: + case CreateNotify: + case DestroyNotify: + // Think we can ignore these + break; + + case ConfigureNotify: + { + updateBounds(); + updateBorderSize(); + handleMovedOrResized(); + + // if the native title bar is dragged, need to tell any active menus, etc. + if ((styleFlags & windowHasTitleBar) != 0 + && component->isCurrentlyBlockedByAnotherModalComponent()) + { + Component* const currentModalComp = Component::getCurrentlyModalComponent(); + + if (currentModalComp != 0) + currentModalComp->inputAttemptWhenModal(); + } + + XConfigureEvent* const confEvent = (XConfigureEvent*) &event->xconfigure; + + if (confEvent->window == windowH + && confEvent->above != 0 + && isFrontWindow()) + { + handleBroughtToFront(); + } + + break; + } + + case ReparentNotify: + case GravityNotify: + { + parentWindow = 0; + Window wRoot = 0; + Window* wChild = 0; + unsigned int numChildren; + XQueryTree (display, windowH, &wRoot, &parentWindow, &wChild, &numChildren); + + if (parentWindow == windowH || parentWindow == wRoot) + parentWindow = 0; + + updateBounds(); + updateBorderSize(); + handleMovedOrResized(); + break; + } + + case MapNotify: + mapped = true; + handleBroughtToFront(); + break; + + case UnmapNotify: + mapped = false; + break; + + case MappingNotify: + { + XMappingEvent* mappingEvent = (XMappingEvent*) &event->xmapping; + + if (mappingEvent->request != MappingPointer) + { + // Deal with modifier/keyboard mapping + XRefreshKeyboardMapping (mappingEvent); + getModifierMapping(); + } + + break; + } + + case ClientMessage: + { + const XClientMessageEvent* const clientMsg = (const XClientMessageEvent*) &event->xclient; + + if (clientMsg->message_type == wm_Protocols && clientMsg->format == 32) + { + const Atom atom = (Atom) clientMsg->data.l[0]; + + if (atom == wm_ProtocolList [TAKE_FOCUS]) + { + XWindowAttributes atts; + + if (clientMsg->window != 0 + && XGetWindowAttributes (display, clientMsg->window, &atts)) + { + if (atts.map_state == IsViewable) + XSetInputFocus (display, clientMsg->window, RevertToParent, clientMsg->data.l[1]); + } + } + else if (atom == wm_ProtocolList [DELETE_WINDOW]) + { + handleUserClosingWindow(); + } + } + else if (clientMsg->message_type == XA_XdndEnter) + { + handleDragAndDropEnter (clientMsg); + } + else if (clientMsg->message_type == XA_XdndLeave) + { + resetDragAndDrop(); + } + else if (clientMsg->message_type == XA_XdndPosition) + { + handleDragAndDropPosition (clientMsg); + } + else if (clientMsg->message_type == XA_XdndDrop) + { + handleDragAndDropDrop (clientMsg); + } + else if (clientMsg->message_type == XA_XdndStatus) + { + handleDragAndDropStatus (clientMsg); + } + else if (clientMsg->message_type == XA_XdndFinished) + { + resetDragAndDrop(); + } + + break; + } + + case SelectionNotify: + handleDragAndDropSelection (event); + break; + + case SelectionClear: + case SelectionRequest: + break; + + default: + break; + } + } + + void showMouseCursor (Cursor cursor) throw() + { + XDefineCursor (display, windowH, cursor); + } + + void setTaskBarIcon (const Image& image) + { + deleteTaskBarIcon(); + taskbarImage = image.createCopy(); + + Screen* const screen = XDefaultScreenOfDisplay (display); + const int screenNumber = XScreenNumberOfScreen (screen); + + char screenAtom[32]; + snprintf (screenAtom, sizeof (screenAtom), "_NET_SYSTEM_TRAY_S%d", screenNumber); + Atom selectionAtom = XInternAtom (display, screenAtom, false); + + XGrabServer (display); + Window managerWin = XGetSelectionOwner (display, selectionAtom); + + if (managerWin != None) + XSelectInput (display, managerWin, StructureNotifyMask); + + XUngrabServer (display); + XFlush (display); + + if (managerWin != None) + { + XEvent ev; + zerostruct (ev); + ev.xclient.type = ClientMessage; + ev.xclient.window = managerWin; + ev.xclient.message_type = XInternAtom (display, "_NET_SYSTEM_TRAY_OPCODE", False); + ev.xclient.format = 32; + ev.xclient.data.l[0] = CurrentTime; + ev.xclient.data.l[1] = SYSTEM_TRAY_REQUEST_DOCK; + ev.xclient.data.l[2] = windowH; + ev.xclient.data.l[3] = 0; + ev.xclient.data.l[4] = 0; + + XSendEvent (display, managerWin, False, NoEventMask, &ev); + XSync (display, False); + } + + // For older KDE's ... + long atomData = 1; + Atom trayAtom = XInternAtom (display, "KWM_DOCKWINDOW", false); + XChangeProperty (display, windowH, trayAtom, trayAtom, 32, PropModeReplace, (unsigned char*) &atomData, 1); + + // For more recent KDE's... + trayAtom = XInternAtom (display, "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR", false); + XChangeProperty (display, windowH, trayAtom, XA_WINDOW, 32, PropModeReplace, (unsigned char*) &windowH, 1); + } + + void deleteTaskBarIcon() + { + deleteAndZero (taskbarImage); + } + + const Image* getTaskbarIcon() const throw() { return taskbarImage; } + + juce_UseDebuggingNewOperator + + bool dontRepaint; + +private: + + class LinuxRepaintManager : public Timer + { + public: + LinuxRepaintManager (LinuxComponentPeer* const peer_) + : peer (peer_), + image (0), + lastTimeImageUsed (0) + { +#if JUCE_USE_XSHM + useARGBImagesForRendering = isShmAvailable(); + + if (useARGBImagesForRendering) + { + XShmSegmentInfo segmentinfo; + + XImage* const testImage + = XShmCreateImage (display, DefaultVisual (display, DefaultScreen (display)), + 24, ZPixmap, 0, &segmentinfo, 64, 64); + + useARGBImagesForRendering = (testImage->bits_per_pixel == 32); + XDestroyImage (testImage); + } +#endif + } + + ~LinuxRepaintManager() + { + delete image; + } + + void timerCallback() + { + if (! regionsNeedingRepaint.isEmpty()) + { + stopTimer(); + performAnyPendingRepaintsNow(); + } + else if (Time::getApproximateMillisecondCounter() > lastTimeImageUsed + 3000) + { + stopTimer(); + deleteAndZero (image); + } + } + + void repaint (int x, int y, int w, int h) + { + if (! isTimerRunning()) + startTimer (repaintTimerPeriod); + + regionsNeedingRepaint.add (x, y, w, h); + } + + void performAnyPendingRepaintsNow() + { + peer->clearMaskedRegion(); + + const Rectangle totalArea (regionsNeedingRepaint.getBounds()); + + if (! totalArea.isEmpty()) + { + if (image == 0 || image->getWidth() < totalArea.getWidth() + || image->getHeight() < totalArea.getHeight()) + { + delete image; + +#if JUCE_USE_XSHM + image = new XBitmapImage (useARGBImagesForRendering ? Image::ARGB + : Image::RGB, +#else + image = new XBitmapImage (Image::RGB, +#endif + (totalArea.getWidth() + 31) & ~31, + (totalArea.getHeight() + 31) & ~31, + false, + peer->depthIs16Bit); + } + + startTimer (repaintTimerPeriod); + + LowLevelGraphicsSoftwareRenderer context (*image); + + context.setOrigin (-totalArea.getX(), -totalArea.getY()); + + if (context.reduceClipRegion (regionsNeedingRepaint)) + peer->handlePaint (context); + + if (! peer->maskedRegion.isEmpty()) + regionsNeedingRepaint.subtract (peer->maskedRegion); + + for (RectangleList::Iterator i (regionsNeedingRepaint); i.next();) + { + const Rectangle& r = *i.getRectangle(); + + image->blitToWindow (peer->windowH, + r.getX(), r.getY(), r.getWidth(), r.getHeight(), + r.getX() - totalArea.getX(), r.getY() - totalArea.getY()); + } + } + + regionsNeedingRepaint.clear(); + + lastTimeImageUsed = Time::getApproximateMillisecondCounter(); + startTimer (repaintTimerPeriod); + } + + private: + LinuxComponentPeer* const peer; + XBitmapImage* image; + uint32 lastTimeImageUsed; + RectangleList regionsNeedingRepaint; + +#if JUCE_USE_XSHM + bool useARGBImagesForRendering; +#endif + LinuxRepaintManager (const LinuxRepaintManager&); + const LinuxRepaintManager& operator= (const LinuxRepaintManager&); + }; + + LinuxRepaintManager* repainter; + + friend class LinuxRepaintManager; + Window windowH, parentWindow; + int wx, wy, ww, wh; + Image* taskbarImage; + bool fullScreen, entered, mapped, depthIs16Bit; + BorderSize windowBorder; + + void removeWindowDecorations (Window wndH) + { + Atom hints = XInternAtom (display, "_MOTIF_WM_HINTS", True); + + if (hints != None) + { + typedef struct + { + unsigned long flags; + unsigned long functions; + unsigned long decorations; + ::INT32 input_mode; + unsigned long status; + } MotifWmHints; + + MotifWmHints motifHints; + zerostruct (motifHints); + motifHints.flags = 2; /* MWM_HINTS_DECORATIONS */ + motifHints.decorations = 0; + + XChangeProperty (display, wndH, hints, hints, 32, PropModeReplace, + (unsigned char*) &motifHints, 4); + } + + hints = XInternAtom (display, "_WIN_HINTS", True); + + if (hints != None) + { + long gnomeHints = 0; + + XChangeProperty (display, wndH, hints, hints, 32, PropModeReplace, + (unsigned char*) &gnomeHints, 1); + } + + hints = XInternAtom (display, "KWM_WIN_DECORATION", True); + + if (hints != None) + { + long kwmHints = 2; /*KDE_tinyDecoration*/ + + XChangeProperty (display, wndH, hints, hints, 32, PropModeReplace, + (unsigned char*) &kwmHints, 1); + } + + hints = XInternAtom (display, "_NET_WM_WINDOW_TYPE", True); + + if (hints != None) + { + long netHints [2]; + netHints[0] = XInternAtom (display, "_KDE_NET_WM_WINDOW_TYPE_OVERRIDE", True); + + if ((styleFlags & windowIsTemporary) != 0) + netHints[1] = XInternAtom (display, "_NET_WM_WINDOW_TYPE_MENU", True); + else + netHints[1] = XInternAtom (display, "_NET_WM_WINDOW_TYPE_NORMAL", True); + + XChangeProperty (display, wndH, hints, XA_ATOM, 32, PropModeReplace, + (unsigned char*) &netHints, 2); + } + } + + void addWindowButtons (Window wndH) + { + Atom hints = XInternAtom (display, "_MOTIF_WM_HINTS", True); + + if (hints != None) + { + typedef struct + { + unsigned long flags; + unsigned long functions; + unsigned long decorations; + ::INT32 input_mode; + unsigned long status; + } MotifWmHints; + + MotifWmHints motifHints; + zerostruct (motifHints); + + motifHints.flags = 1 | 2; /* MWM_HINTS_FUNCTIONS | MWM_HINTS_DECORATIONS */ + motifHints.decorations = 2 /* MWM_DECOR_BORDER */ | 8 /* MWM_DECOR_TITLE */ | 16; /* MWM_DECOR_MENU */ + + motifHints.functions = 4 /* MWM_FUNC_MOVE */; + + if ((styleFlags & windowHasCloseButton) != 0) + motifHints.functions |= 32; /* MWM_FUNC_CLOSE */ + + if ((styleFlags & windowHasMinimiseButton) != 0) + { + motifHints.functions |= 8; /* MWM_FUNC_MINIMIZE */ + motifHints.decorations |= 0x20; /* MWM_DECOR_MINIMIZE */ + } + + if ((styleFlags & windowHasMaximiseButton) != 0) + { + motifHints.functions |= 0x10; /* MWM_FUNC_MAXIMIZE */ + motifHints.decorations |= 0x40; /* MWM_DECOR_MAXIMIZE */ + } + + if ((styleFlags & windowIsResizable) != 0) + { + motifHints.functions |= 2; /* MWM_FUNC_RESIZE */ + motifHints.decorations |= 0x4; /* MWM_DECOR_RESIZEH */ + } + + XChangeProperty (display, wndH, hints, hints, 32, 0, (unsigned char*) &motifHints, 5); + } + + hints = XInternAtom (display, "_NET_WM_ALLOWED_ACTIONS", True); + + if (hints != None) + { + long netHints [6]; + int num = 0; + + netHints [num++] = XInternAtom (display, "_NET_WM_ACTION_RESIZE", (styleFlags & windowIsResizable) ? True : False); + netHints [num++] = XInternAtom (display, "_NET_WM_ACTION_FULLSCREEN", (styleFlags & windowHasMaximiseButton) ? True : False); + netHints [num++] = XInternAtom (display, "_NET_WM_ACTION_MINIMIZE", (styleFlags & windowHasMinimiseButton) ? True : False); + netHints [num++] = XInternAtom (display, "_NET_WM_ACTION_CLOSE", (styleFlags & windowHasCloseButton) ? True : False); + + XChangeProperty (display, wndH, hints, XA_ATOM, 32, PropModeReplace, + (unsigned char*) &netHints, num); + } + } + + void createWindow() + { + static bool atomsInitialised = false; + + if (! atomsInitialised) + { + atomsInitialised = true; + + wm_Protocols = XInternAtom (display, "WM_PROTOCOLS", 1); + wm_ProtocolList [TAKE_FOCUS] = XInternAtom (display, "WM_TAKE_FOCUS", 1); + wm_ProtocolList [DELETE_WINDOW] = XInternAtom (display, "WM_DELETE_WINDOW", 1); + wm_ChangeState = XInternAtom (display, "WM_CHANGE_STATE", 1); + wm_State = XInternAtom (display, "WM_STATE", 1); + wm_ActiveWin = XInternAtom (display, "_NET_ACTIVE_WINDOW", False); + + XA_XdndAware = XInternAtom (display, "XdndAware", 0); + XA_XdndEnter = XInternAtom (display, "XdndEnter", 0); + XA_XdndLeave = XInternAtom (display, "XdndLeave", 0); + XA_XdndPosition = XInternAtom (display, "XdndPosition", 0); + XA_XdndStatus = XInternAtom (display, "XdndStatus", 0); + XA_XdndDrop = XInternAtom (display, "XdndDrop", 0); + XA_XdndFinished = XInternAtom (display, "XdndFinished", 0); + XA_XdndSelection = XInternAtom (display, "XdndSelection", 0); + XA_XdndProxy = XInternAtom (display, "XdndProxy", 0); + + XA_XdndTypeList = XInternAtom (display, "XdndTypeList", 0); + XA_XdndActionList = XInternAtom (display, "XdndActionList", 0); + XA_XdndActionCopy = XInternAtom (display, "XdndActionCopy", 0); + XA_XdndActionMove = XInternAtom (display, "XdndActionMove", 0); + XA_XdndActionLink = XInternAtom (display, "XdndActionLink", 0); + XA_XdndActionAsk = XInternAtom (display, "XdndActionAsk", 0); + XA_XdndActionPrivate = XInternAtom (display, "XdndActionPrivate", 0); + XA_XdndActionDescription = XInternAtom (display, "XdndActionDescription", 0); + + XA_JXSelectionWindowProperty = XInternAtom (display, "JXSelectionWindowProperty", 0); + + XA_MimeTextPlain = XInternAtom (display, "text/plain", 0); + XA_MimeTextUriList = XInternAtom (display, "text/uri-list", 0); + XA_MimeRootDrop = XInternAtom (display, "application/x-rootwindow-drop", 0); + } + + resetDragAndDrop(); + + XA_OtherMime = XA_MimeTextPlain; // xxx why?? + allowedMimeTypeAtoms [0] = XA_MimeTextPlain; + allowedMimeTypeAtoms [1] = XA_OtherMime; + allowedMimeTypeAtoms [2] = XA_MimeTextUriList; + + allowedActions [0] = XA_XdndActionMove; + allowedActions [1] = XA_XdndActionCopy; + allowedActions [2] = XA_XdndActionLink; + allowedActions [3] = XA_XdndActionAsk; + allowedActions [4] = XA_XdndActionPrivate; + + // Get defaults for various properties + const int screen = DefaultScreen (display); + Window root = RootWindow (display, screen); + + // Attempt to create a 24-bit window on the default screen. If this is not + // possible then exit + XVisualInfo desiredVisual; + desiredVisual.screen = screen; + desiredVisual.depth = 24; + depthIs16Bit = false; + + int numVisuals; + XVisualInfo* visuals = XGetVisualInfo (display, VisualScreenMask | VisualDepthMask, + &desiredVisual, &numVisuals); + + if (numVisuals < 1 || visuals == 0) + { + XFree (visuals); + desiredVisual.depth = 16; + + visuals = XGetVisualInfo (display, VisualScreenMask | VisualDepthMask, + &desiredVisual, &numVisuals); + + if (numVisuals < 1 || visuals == 0) + { + Logger::outputDebugString ("ERROR: System doesn't support 24 or 16 bit RGB display.\n"); + Process::terminate(); + } + + depthIs16Bit = true; + } + + XFree (visuals); + + // Set up the window attributes + XSetWindowAttributes swa; + swa.border_pixel = 0; + swa.background_pixmap = None; + swa.colormap = DefaultColormap (display, screen); + swa.override_redirect = getComponent()->isAlwaysOnTop() ? True : False; + swa.event_mask = eventMask; + + Window wndH = XCreateWindow (display, root, + 0, 0, 1, 1, + 0, 0, InputOutput, (Visual*) CopyFromParent, + CWBorderPixel | CWColormap | CWBackPixmap | CWEventMask | CWOverrideRedirect, + &swa); + + XGrabButton (display, AnyButton, AnyModifier, wndH, False, + ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask | PointerMotionMask, + GrabModeAsync, GrabModeAsync, None, None); + + // Set the window context to identify the window handle object + if (XSaveContext (display, (XID) wndH, improbableNumber, (XPointer) this)) + { + // Failed + jassertfalse + Logger::outputDebugString ("Failed to create context information for window.\n"); + XDestroyWindow (display, wndH); + wndH = 0; + } + + // Set window manager hints + XWMHints* wmHints = XAllocWMHints(); + wmHints->flags = InputHint | StateHint; + wmHints->input = True; // Locally active input model + wmHints->initial_state = NormalState; + XSetWMHints (display, wndH, wmHints); + XFree (wmHints); + + if ((styleFlags & windowIsSemiTransparent) != 0) + { + //xxx + } + + if ((styleFlags & windowAppearsOnTaskbar) != 0) + { + //xxx + } + + //XSetTransientForHint (display, wndH, RootWindow (display, DefaultScreen (display))); + + if ((styleFlags & windowHasTitleBar) == 0) + removeWindowDecorations (wndH); + else + addWindowButtons (wndH); + + // Set window manager protocols + XChangeProperty (display, wndH, wm_Protocols, XA_ATOM, 32, PropModeReplace, + (unsigned char*) wm_ProtocolList, 2); + + // Set drag and drop flags + XChangeProperty (display, wndH, XA_XdndTypeList, XA_ATOM, 32, PropModeReplace, + (const unsigned char*) allowedMimeTypeAtoms, numElementsInArray (allowedMimeTypeAtoms)); + + XChangeProperty (display, wndH, XA_XdndActionList, XA_ATOM, 32, PropModeReplace, + (const unsigned char*) allowedActions, numElementsInArray (allowedActions)); + + XChangeProperty (display, wndH, XA_XdndActionDescription, XA_STRING, 8, PropModeReplace, + (const unsigned char*) "", 0); + + unsigned long dndVersion = ourDndVersion; + XChangeProperty (display, wndH, XA_XdndAware, XA_ATOM, 32, PropModeReplace, + (const unsigned char*) &dndVersion, 1); + + // Set window name + setWindowTitle (wndH, getComponent()->getName()); + + // Initialise the pointer and keyboard mapping + // This is not the same as the logical pointer mapping the X server uses: + // we don't mess with this. + static bool mappingInitialised = false; + + if (! mappingInitialised) + { + mappingInitialised = true; + + const int numButtons = XGetPointerMapping (display, 0, 0); + + if (numButtons == 2) + { + pointerMap[0] = LeftButton; + pointerMap[1] = RightButton; + pointerMap[2] = pointerMap[3] = pointerMap[4] = NoButton; + } + else if (numButtons >= 3) + { + pointerMap[0] = LeftButton; + pointerMap[1] = MiddleButton; + pointerMap[2] = RightButton; + + if (numButtons >= 5) + { + pointerMap[3] = WheelUp; + pointerMap[4] = WheelDown; + } + } + + getModifierMapping(); + } + + windowH = wndH; + } + + void destroyWindow() + { + XPointer handlePointer; + if (! XFindContext (display, (XID) windowH, improbableNumber, &handlePointer)) + XDeleteContext (display, (XID) windowH, improbableNumber); + + XDestroyWindow (display, windowH); + + // Wait for it to complete and then remove any events for this + // window from the event queue. + XSync (display, false); + + XEvent event; + while (XCheckWindowEvent (display, windowH, eventMask, &event) == True) + {} + } + + static int64 getEventTime (::Time t) throw() + { + static int64 eventTimeOffset = 0x12345678; + const int64 thisMessageTime = t; + + if (eventTimeOffset == 0x12345678) + eventTimeOffset = Time::currentTimeMillis() - thisMessageTime; + + return eventTimeOffset + thisMessageTime; + } + + static void setWindowTitle (Window xwin, const char* const title) throw() + { + XTextProperty nameProperty; + char* strings[] = { (char*) title }; + + if (XStringListToTextProperty (strings, 1, &nameProperty)) + { + XSetWMName (display, xwin, &nameProperty); + XSetWMIconName (display, xwin, &nameProperty); + + XFree (nameProperty.value); + } + } + + void updateBorderSize() + { + if ((styleFlags & windowHasTitleBar) == 0) + { + windowBorder = BorderSize (0); + } + else if (windowBorder.getTopAndBottom() == 0 && windowBorder.getLeftAndRight() == 0) + { + Atom hints = XInternAtom (display, "_NET_FRAME_EXTENTS", True); + + if (hints != None) + { + unsigned char* data = 0; + unsigned long nitems, bytesLeft; + Atom actualType; + int actualFormat; + + if (XGetWindowProperty (display, windowH, hints, 0, 4, False, + XA_CARDINAL, &actualType, &actualFormat, &nitems, &bytesLeft, + &data) == Success) + { + const unsigned long* const sizes = (const CARD32*) data; + + if (actualFormat == 32) + windowBorder = BorderSize ((int) sizes[2], (int) sizes[0], + (int) sizes[3], (int) sizes[1]); + + XFree (data); + } + } + } + } + + void updateBounds() + { + jassert (windowH != 0); + if (windowH != 0) + { + Window root, child; + unsigned int bw, depth; + + if (! XGetGeometry (display, (::Drawable) windowH, &root, + &wx, &wy, (unsigned int*) &ww, (unsigned int*) &wh, + &bw, &depth)) + { + wx = wy = ww = wh = 0; + } + else if (! XTranslateCoordinates (display, windowH, root, 0, 0, &wx, &wy, &child)) + { + wx = wy = 0; + } + } + } + + void resetDragAndDrop() + { + dragAndDropFiles.clear(); + lastDropX = lastDropY = -1; + dragAndDropCurrentMimeType = 0; + dragAndDropSourceWindow = 0; + srcMimeTypeAtomList.clear(); + } + + void sendDragAndDropMessage (XClientMessageEvent& msg) + { + msg.type = ClientMessage; + msg.display = display; + msg.window = dragAndDropSourceWindow; + msg.format = 32; + msg.data.l[0] = windowH; + + XSendEvent (display, dragAndDropSourceWindow, False, 0, (XEvent*) &msg); + } + + void sendDragAndDropStatus (const bool acceptDrop, Atom dropAction) + { + XClientMessageEvent msg; + zerostruct (msg); + msg.message_type = XA_XdndStatus; + msg.data.l[1] = (acceptDrop ? 1 : 0) | 2; // 2 indicates that we want to receive position messages + msg.data.l[4] = dropAction; + + sendDragAndDropMessage (msg); + } + + void sendDragAndDropLeave() + { + XClientMessageEvent msg; + zerostruct (msg); + msg.message_type = XA_XdndLeave; + sendDragAndDropMessage (msg); + } + + void sendDragAndDropFinish() + { + XClientMessageEvent msg; + zerostruct (msg); + msg.message_type = XA_XdndFinished; + sendDragAndDropMessage (msg); + } + + void handleDragAndDropStatus (const XClientMessageEvent* const clientMsg) + { + if ((clientMsg->data.l[1] & 1) == 0) + { + sendDragAndDropLeave(); + + if (dragAndDropFiles.size() > 0) + handleFileDragExit (dragAndDropFiles); + + dragAndDropFiles.clear(); + } + } + + void handleDragAndDropPosition (const XClientMessageEvent* const clientMsg) + { + if (dragAndDropSourceWindow == 0) + return; + + dragAndDropSourceWindow = clientMsg->data.l[0]; + + const int dropX = ((int) clientMsg->data.l[2] >> 16) - getScreenX(); + const int dropY = ((int) clientMsg->data.l[2] & 0xffff) - getScreenY(); + + if (lastDropX != dropX || lastDropY != dropY) + { + lastDropX = dropX; + lastDropY = dropY; + + dragAndDropTimestamp = clientMsg->data.l[3]; + + Atom targetAction = XA_XdndActionCopy; + + for (int i = numElementsInArray (allowedActions); --i >= 0;) + { + if ((Atom) clientMsg->data.l[4] == allowedActions[i]) + { + targetAction = allowedActions[i]; + break; + } + } + + sendDragAndDropStatus (true, targetAction); + + if (dragAndDropFiles.size() == 0) + updateDraggedFileList (clientMsg); + + if (dragAndDropFiles.size() > 0) + handleFileDragMove (dragAndDropFiles, dropX, dropY); + } + } + + void handleDragAndDropDrop (const XClientMessageEvent* const clientMsg) + { + if (dragAndDropFiles.size() == 0) + updateDraggedFileList (clientMsg); + + const StringArray files (dragAndDropFiles); + const int lastX = lastDropX, lastY = lastDropY; + + sendDragAndDropFinish(); + resetDragAndDrop(); + + if (files.size() > 0) + handleFileDragDrop (files, lastX, lastY); + } + + void handleDragAndDropEnter (const XClientMessageEvent* const clientMsg) + { + dragAndDropFiles.clear(); + srcMimeTypeAtomList.clear(); + + dragAndDropCurrentMimeType = 0; + const int dndCurrentVersion = (int) (clientMsg->data.l[1] & 0xff000000) >> 24; + + if (dndCurrentVersion < 3 || dndCurrentVersion > ourDndVersion) + { + dragAndDropSourceWindow = 0; + return; + } + + dragAndDropSourceWindow = clientMsg->data.l[0]; + + if ((clientMsg->data.l[1] & 1) != 0) + { + Atom actual; + int format; + unsigned long count = 0, remaining = 0; + unsigned char* data = 0; + + XGetWindowProperty (display, dragAndDropSourceWindow, XA_XdndTypeList, + 0, 0x8000000L, False, XA_ATOM, &actual, &format, + &count, &remaining, &data); + + if (data != 0) + { + if (actual == XA_ATOM && format == 32 && count != 0) + { + const unsigned long* const types = (const unsigned long*) data; + + for (unsigned int i = 0; i < count; ++i) + if (types[i] != None) + srcMimeTypeAtomList.add (types[i]); + } + + XFree (data); + } + } + + if (srcMimeTypeAtomList.size() == 0) + { + for (int i = 2; i < 5; ++i) + if (clientMsg->data.l[i] != None) + srcMimeTypeAtomList.add (clientMsg->data.l[i]); + + if (srcMimeTypeAtomList.size() == 0) + { + dragAndDropSourceWindow = 0; + return; + } + } + + for (int i = 0; i < srcMimeTypeAtomList.size() && dragAndDropCurrentMimeType == 0; ++i) + for (int j = 0; j < numElementsInArray (allowedMimeTypeAtoms); ++j) + if (srcMimeTypeAtomList[i] == allowedMimeTypeAtoms[j]) + dragAndDropCurrentMimeType = allowedMimeTypeAtoms[j]; + + handleDragAndDropPosition (clientMsg); + } + + void handleDragAndDropSelection (const XEvent* const evt) + { + dragAndDropFiles.clear(); + + if (evt->xselection.property != 0) + { + StringArray lines; + + { + MemoryBlock dropData; + + for (;;) + { + Atom actual; + uint8* data = 0; + unsigned long count = 0, remaining = 0; + int format = 0; + + if (XGetWindowProperty (display, evt->xany.window, evt->xselection.property, + dropData.getSize() / 4, 65536, 1, AnyPropertyType, &actual, + &format, &count, &remaining, &data) == Success) + { + dropData.append (data, count * format / 8); + XFree (data); + + if (remaining == 0) + break; + } + else + { + XFree (data); + break; + } + } + + lines.addLines (dropData.toString()); + } + + for (int i = 0; i < lines.size(); ++i) + dragAndDropFiles.add (URL::removeEscapeChars (lines[i].fromFirstOccurrenceOf (T("file://"), false, true))); + + dragAndDropFiles.trim(); + dragAndDropFiles.removeEmptyStrings(); + } + } + + void updateDraggedFileList (const XClientMessageEvent* const clientMsg) + { + dragAndDropFiles.clear(); + + if (dragAndDropSourceWindow != None + && dragAndDropCurrentMimeType != 0) + { + dragAndDropTimestamp = clientMsg->data.l[2]; + + XConvertSelection (display, + XA_XdndSelection, + dragAndDropCurrentMimeType, + XA_JXSelectionWindowProperty, + windowH, + dragAndDropTimestamp); + } + } + + StringArray dragAndDropFiles; + int dragAndDropTimestamp, lastDropX, lastDropY; + + Atom XA_OtherMime, dragAndDropCurrentMimeType; + Window dragAndDropSourceWindow; + + unsigned long allowedActions [5]; + unsigned long allowedMimeTypeAtoms [3]; + Array srcMimeTypeAtomList; +}; + +ComponentPeer* Component::createNewPeer (int styleFlags, void* /*nativeWindowToAttachTo*/) +{ + return new LinuxComponentPeer (this, styleFlags); +} + +// (this callback is hooked up in the messaging code) +void juce_windowMessageReceive (XEvent* event) +{ + if (event->xany.window != None) + { + LinuxComponentPeer* const peer = LinuxComponentPeer::getPeerFor (event->xany.window); + + const MessageManagerLock messLock; + + if (ComponentPeer::isValidPeer (peer)) + peer->handleWindowMessage (event); + } + else + { + switch (event->xany.type) + { + case KeymapNotify: + { + const XKeymapEvent* const keymapEvent = (const XKeymapEvent*) &event->xkeymap; + memcpy (keyStates, keymapEvent->key_vector, 32); + break; + } + + default: + break; + } + } +} + +void juce_updateMultiMonitorInfo (Array & monitorCoords, const bool /*clipToWorkArea*/) throw() +{ +#if JUCE_USE_XINERAMA + int major_opcode, first_event, first_error; + + if (XQueryExtension (display, "XINERAMA", &major_opcode, &first_event, &first_error) + && XineramaIsActive (display)) + { + int numMonitors = 0; + XineramaScreenInfo* const screens = XineramaQueryScreens (display, &numMonitors); + + if (screens != 0) + { + for (int i = numMonitors; --i >= 0;) + { + int index = screens[i].screen_number; + + if (index >= 0) + { + while (monitorCoords.size() < index) + monitorCoords.add (Rectangle (0, 0, 0, 0)); + + monitorCoords.set (index, Rectangle (screens[i].x_org, + screens[i].y_org, + screens[i].width, + screens[i].height)); + } + } + + XFree (screens); + } + } + + if (monitorCoords.size() == 0) +#endif + { + Atom hints = XInternAtom (display, "_NET_WORKAREA", True); + + if (hints != None) + { + const int numMonitors = ScreenCount (display); + + for (int i = 0; i < numMonitors; ++i) + { + Window root = RootWindow (display, i); + + unsigned long nitems, bytesLeft; + Atom actualType; + int actualFormat; + unsigned char* data = 0; + + if (XGetWindowProperty (display, root, hints, 0, 4, False, + XA_CARDINAL, &actualType, &actualFormat, &nitems, &bytesLeft, + &data) == Success) + { + const long* const position = (const long*) data; + + if (actualType == XA_CARDINAL && actualFormat == 32 && nitems == 4) + monitorCoords.add (Rectangle (position[0], position[1], + position[2], position[3])); + + XFree (data); + } + } + } + + if (monitorCoords.size() == 0) + { + monitorCoords.add (Rectangle (0, 0, + DisplayWidth (display, DefaultScreen (display)), + DisplayHeight (display, DefaultScreen (display)))); + } + } +} + +bool Desktop::canUseSemiTransparentWindows() throw() +{ + return false; +} + +void Desktop::getMousePosition (int& x, int& y) throw() +{ + int mouseMods; + getMousePos (x, y, mouseMods); +} + +void Desktop::setMousePosition (int x, int y) throw() +{ + Window root = RootWindow (display, DefaultScreen (display)); + XWarpPointer (display, None, root, 0, 0, 0, 0, x, y); +} + +static bool screenSaverAllowed = true; + +void Desktop::setScreenSaverEnabled (const bool isEnabled) throw() +{ + if (screenSaverAllowed != isEnabled) + { + screenSaverAllowed = isEnabled; + + typedef void (*tXScreenSaverSuspend) (Display*, Bool); + static tXScreenSaverSuspend xScreenSaverSuspend = 0; + + if (xScreenSaverSuspend == 0) + { + void* h = dlopen ("libXss.so", RTLD_GLOBAL | RTLD_NOW); + + if (h != 0) + xScreenSaverSuspend = (tXScreenSaverSuspend) dlsym (h, "XScreenSaverSuspend"); + } + + if (xScreenSaverSuspend != 0) + xScreenSaverSuspend (display, ! isEnabled); + } +} + +bool Desktop::isScreenSaverEnabled() throw() +{ + return screenSaverAllowed; +} + +void* juce_createMouseCursorFromImage (const Image& image, int hotspotX, int hotspotY) throw() +{ + Window root = RootWindow (display, DefaultScreen (display)); + const unsigned int imageW = image.getWidth(); + const unsigned int imageH = image.getHeight(); + unsigned int cursorW, cursorH; + + if (! XQueryBestCursor (display, root, imageW, imageH, &cursorW, &cursorH)) + return 0; + + Image im (Image::ARGB, cursorW, cursorH, true); + Graphics g (im); + + if (imageW > cursorW || imageH > cursorH) + { + hotspotX = (hotspotX * cursorW) / imageW; + hotspotY = (hotspotY * cursorH) / imageH; + + g.drawImageWithin (&image, 0, 0, imageW, imageH, + RectanglePlacement::xLeft | RectanglePlacement::yTop | RectanglePlacement::onlyReduceInSize, + false); + } + else + { + g.drawImageAt (&image, 0, 0); + } + + const int stride = (cursorW + 7) >> 3; + uint8* const maskPlane = (uint8*) juce_calloc (stride * cursorH); + uint8* const sourcePlane = (uint8*) juce_calloc (stride * cursorH); + + bool msbfirst = (BitmapBitOrder (display) == MSBFirst); + + for (int y = cursorH; --y >= 0;) + { + for (int x = cursorW; --x >= 0;) + { + const uint8 mask = (uint8) (1 << (msbfirst ? (7 - (x & 7)) : (x & 7))); + const int offset = y * stride + (x >> 3); + + const Colour c (im.getPixelAt (x, y)); + + if (c.getAlpha() >= 128) + maskPlane[offset] |= mask; + + if (c.getBrightness() >= 0.5f) + sourcePlane[offset] |= mask; + } + } + + Pixmap sourcePixmap = XCreatePixmapFromBitmapData (display, root, (char*) sourcePlane, cursorW, cursorH, 0xffff, 0, 1); + Pixmap maskPixmap = XCreatePixmapFromBitmapData (display, root, (char*) maskPlane, cursorW, cursorH, 0xffff, 0, 1); + + juce_free (maskPlane); + juce_free (sourcePlane); + + XColor white, black; + black.red = black.green = black.blue = 0; + white.red = white.green = white.blue = 0xffff; + + void* result = (void*) XCreatePixmapCursor (display, sourcePixmap, maskPixmap, &white, &black, hotspotX, hotspotY); + + XFreePixmap (display, sourcePixmap); + XFreePixmap (display, maskPixmap); + + return result; +} + +void juce_deleteMouseCursor (void* const cursorHandle, const bool) throw() +{ + if (cursorHandle != None) + XFreeCursor (display, (Cursor) cursorHandle); +} + +void* juce_createStandardMouseCursor (MouseCursor::StandardCursorType type) throw() +{ + unsigned int shape; + + switch (type) + { + case MouseCursor::NoCursor: + { + const Image im (Image::ARGB, 16, 16, true); + return juce_createMouseCursorFromImage (im, 0, 0); + } + + case MouseCursor::NormalCursor: + return (void*) None; // Use parent cursor + + case MouseCursor::DraggingHandCursor: + { + static unsigned char dragHandData[] = {71,73,70,56,57,97,16,0,16,0,145,2,0,0,0,0,255,255,255,0, + 0,0,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, + 16,0,0,2,52,148,47,0,200,185,16,130,90,12,74,139,107,84,123,39, + 132,117,151,116,132,146,248,60,209,138,98,22,203,114,34,236,37,52,77,217, + 247,154,191,119,110,240,193,128,193,95,163,56,60,234,98,135,2,0,59 }; + const int dragHandDataSize = 99; + + Image* const im = ImageFileFormat::loadFrom ((const char*) dragHandData, dragHandDataSize); + void* const dragHandCursor = juce_createMouseCursorFromImage (*im, 8, 7); + delete im; + + return dragHandCursor; + } + + case MouseCursor::CopyingCursor: + { + static unsigned char copyCursorData[] = {71,73,70,56,57,97,21,0,21,0,145,0,0,0,0,0,255,255,255,0, + 128,128,255,255,255,33,249,4,1,0,0,3,0,44,0,0,0,0,21,0, + 21,0,0,2,72,4,134,169,171,16,199,98,11,79,90,71,161,93,56,111, + 78,133,218,215,137,31,82,154,100,200,86,91,202,142,12,108,212,87,235,174, + 15,54,214,126,237,226,37,96,59,141,16,37,18,201,142,157,230,204,51,112, + 252,114,147,74,83,5,50,68,147,208,217,16,71,149,252,124,5,0,59,0,0 }; + const int copyCursorSize = 119; + + Image* const im = ImageFileFormat::loadFrom ((const char*) copyCursorData, copyCursorSize); + void* const copyCursor = juce_createMouseCursorFromImage (*im, 1, 3); + delete im; + + return copyCursor; + } + + case MouseCursor::WaitCursor: + shape = XC_watch; + break; + + case MouseCursor::IBeamCursor: + shape = XC_xterm; + break; + + case MouseCursor::PointingHandCursor: + shape = XC_hand2; + break; + + case MouseCursor::LeftRightResizeCursor: + shape = XC_sb_h_double_arrow; + break; + + case MouseCursor::UpDownResizeCursor: + shape = XC_sb_v_double_arrow; + break; + + case MouseCursor::UpDownLeftRightResizeCursor: + shape = XC_fleur; + break; + + case MouseCursor::TopEdgeResizeCursor: + shape = XC_top_side; + break; + + case MouseCursor::BottomEdgeResizeCursor: + shape = XC_bottom_side; + break; + + case MouseCursor::LeftEdgeResizeCursor: + shape = XC_left_side; + break; + + case MouseCursor::RightEdgeResizeCursor: + shape = XC_right_side; + break; + + case MouseCursor::TopLeftCornerResizeCursor: + shape = XC_top_left_corner; + break; + + case MouseCursor::TopRightCornerResizeCursor: + shape = XC_top_right_corner; + break; + + case MouseCursor::BottomLeftCornerResizeCursor: + shape = XC_bottom_left_corner; + break; + + case MouseCursor::BottomRightCornerResizeCursor: + shape = XC_bottom_right_corner; + break; + + case MouseCursor::CrosshairCursor: + shape = XC_crosshair; + break; + + default: + return (void*) None; // Use parent cursor + } + + return (void*) XCreateFontCursor (display, shape); +} + +void MouseCursor::showInWindow (ComponentPeer* peer) const throw() +{ + LinuxComponentPeer* const lp = dynamic_cast (peer); + + if (lp != 0) + lp->showMouseCursor ((Cursor) getHandle()); +} + +void MouseCursor::showInAllWindows() const throw() +{ + for (int i = ComponentPeer::getNumPeers(); --i >= 0;) + showInWindow (ComponentPeer::getPeer (i)); +} + +Image* juce_createIconForFile (const File& file) +{ + return 0; +} + +#if JUCE_OPENGL + +class WindowedGLContext : public OpenGLContext +{ +public: + WindowedGLContext (Component* const component, + const OpenGLPixelFormat& pixelFormat_, + GLXContext sharedContext) + : renderContext (0), + embeddedWindow (0), + pixelFormat (pixelFormat_) + { + jassert (component != 0); + LinuxComponentPeer* const peer = dynamic_cast (component->getTopLevelComponent()->getPeer()); + if (peer == 0) + return; + + XSync (display, False); + + GLint attribs [64]; + int n = 0; + attribs[n++] = GLX_RGBA; + attribs[n++] = GLX_DOUBLEBUFFER; + attribs[n++] = GLX_RED_SIZE; + attribs[n++] = pixelFormat.redBits; + attribs[n++] = GLX_GREEN_SIZE; + attribs[n++] = pixelFormat.greenBits; + attribs[n++] = GLX_BLUE_SIZE; + attribs[n++] = pixelFormat.blueBits; + attribs[n++] = GLX_ALPHA_SIZE; + attribs[n++] = pixelFormat.alphaBits; + attribs[n++] = GLX_DEPTH_SIZE; + attribs[n++] = pixelFormat.depthBufferBits; + attribs[n++] = GLX_STENCIL_SIZE; + attribs[n++] = pixelFormat.stencilBufferBits; + attribs[n++] = GLX_ACCUM_RED_SIZE; + attribs[n++] = pixelFormat.accumulationBufferRedBits; + attribs[n++] = GLX_ACCUM_GREEN_SIZE; + attribs[n++] = pixelFormat.accumulationBufferGreenBits; + attribs[n++] = GLX_ACCUM_BLUE_SIZE; + attribs[n++] = pixelFormat.accumulationBufferBlueBits; + attribs[n++] = GLX_ACCUM_ALPHA_SIZE; + attribs[n++] = pixelFormat.accumulationBufferAlphaBits; + + // xxx not sure how to do fullSceneAntiAliasingNumSamples on linux.. + + attribs[n++] = None; + + XVisualInfo* const bestVisual = glXChooseVisual (display, DefaultScreen (display), attribs); + + if (bestVisual == 0) + return; + + renderContext = glXCreateContext (display, bestVisual, sharedContext, GL_TRUE); + + Window windowH = (Window) peer->getNativeHandle(); + + Colormap colourMap = XCreateColormap (display, windowH, bestVisual->visual, AllocNone); + XSetWindowAttributes swa; + swa.colormap = colourMap; + swa.border_pixel = 0; + swa.event_mask = ExposureMask | StructureNotifyMask; + + embeddedWindow = XCreateWindow (display, windowH, + 0, 0, 1, 1, 0, + bestVisual->depth, + InputOutput, + bestVisual->visual, + CWBorderPixel | CWColormap | CWEventMask, + &swa); + + XSaveContext (display, (XID) embeddedWindow, improbableNumber, (XPointer) peer); + + XMapWindow (display, embeddedWindow); + XFreeColormap (display, colourMap); + + XFree (bestVisual); + XSync (display, False); + } + + ~WindowedGLContext() + { + makeInactive(); + + glXDestroyContext (display, renderContext); + + XUnmapWindow (display, embeddedWindow); + XDestroyWindow (display, embeddedWindow); + } + + bool makeActive() const throw() + { + jassert (renderContext != 0); + + return glXMakeCurrent (display, embeddedWindow, renderContext) + && XSync (display, False); + } + + bool makeInactive() const throw() + { + return (! isActive()) || glXMakeCurrent (display, None, 0); + } + + bool isActive() const throw() + { + return glXGetCurrentContext() == renderContext; + } + + const OpenGLPixelFormat getPixelFormat() const + { + return pixelFormat; + } + + void* getRawContext() const throw() + { + return renderContext; + } + + void updateWindowPosition (int x, int y, int w, int h, int) + { + XMoveResizeWindow (display, embeddedWindow, + x, y, jmax (1, w), jmax (1, h)); + } + + void swapBuffers() + { + glXSwapBuffers (display, embeddedWindow); + } + + bool setSwapInterval (const int numFramesPerSwap) + { + // xxx needs doing.. + return false; + } + + int getSwapInterval() const + { + // xxx needs doing.. + return 0; + } + + void repaint() + { + } + + juce_UseDebuggingNewOperator + + GLXContext renderContext; + +private: + Window embeddedWindow; + OpenGLPixelFormat pixelFormat; + + WindowedGLContext (const WindowedGLContext&); + const WindowedGLContext& operator= (const WindowedGLContext&); +}; + +OpenGLContext* OpenGLContext::createContextForWindow (Component* const component, + const OpenGLPixelFormat& pixelFormat, + const OpenGLContext* const contextToShareWith) +{ + WindowedGLContext* c = new WindowedGLContext (component, pixelFormat, + contextToShareWith != 0 ? (GLXContext) contextToShareWith->getRawContext() : 0); + + if (c->renderContext == 0) + deleteAndZero (c); + + return c; +} + +void juce_glViewport (const int w, const int h) +{ + glViewport (0, 0, w, h); +} + +void OpenGLPixelFormat::getAvailablePixelFormats (Component* component, + OwnedArray & results) +{ + results.add (new OpenGLPixelFormat()); // xxx +} + +#endif + +static void initClipboard (Window root, Atom* cutBuffers) throw() +{ + static bool init = false; + + if (! init) + { + init = true; + + // Make sure all cut buffers exist before use + for (int i = 0; i < 8; i++) + { + XChangeProperty (display, root, cutBuffers[i], + XA_STRING, 8, PropModeAppend, NULL, 0); + } + } +} + +// Clipboard implemented currently using cut buffers +// rather than the more powerful selection method +void SystemClipboard::copyTextToClipboard (const String& clipText) throw() +{ + Window root = RootWindow (display, DefaultScreen (display)); + Atom cutBuffers[8] = { XA_CUT_BUFFER0, XA_CUT_BUFFER1, XA_CUT_BUFFER2, XA_CUT_BUFFER3, + XA_CUT_BUFFER4, XA_CUT_BUFFER5, XA_CUT_BUFFER6, XA_CUT_BUFFER7 }; + + initClipboard (root, cutBuffers); + + XRotateWindowProperties (display, root, cutBuffers, 8, 1); + XChangeProperty (display, root, cutBuffers[0], + XA_STRING, 8, PropModeReplace, (const unsigned char*) (const char*) clipText, + clipText.length()); +} + +const String SystemClipboard::getTextFromClipboard() throw() +{ + const int bufSize = 64; // in words + String returnData; + int byteOffset = 0; + + Window root = RootWindow (display, DefaultScreen (display)); + + Atom cutBuffers[8] = { XA_CUT_BUFFER0, XA_CUT_BUFFER1, XA_CUT_BUFFER2, XA_CUT_BUFFER3, + XA_CUT_BUFFER4, XA_CUT_BUFFER5, XA_CUT_BUFFER6, XA_CUT_BUFFER7 }; + + initClipboard (root, cutBuffers); + + for (;;) + { + unsigned long bytesLeft = 0, nitems = 0; + unsigned char* clipData = 0; + int actualFormat = 0; + Atom actualType; + + if (XGetWindowProperty (display, root, cutBuffers[0], byteOffset >> 2, bufSize, + False, XA_STRING, &actualType, &actualFormat, &nitems, &bytesLeft, + &clipData) == Success) + { + if (actualType == XA_STRING && actualFormat == 8) + { + byteOffset += nitems; + returnData += String ((const char*) clipData, nitems); + } + else + { + bytesLeft = 0; + } + + if (clipData != 0) + XFree (clipData); + } + + if (bytesLeft == 0) + break; + } + + return returnData; +} + +bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, const bool canMoveFiles) +{ + jassertfalse // not implemented! + return false; +} + +bool DragAndDropContainer::performExternalDragDropOfText (const String& text) +{ + jassertfalse // not implemented! + return false; +} + +void SystemTrayIconComponent::setIconImage (const Image& newImage) +{ + if (! isOnDesktop ()) + addToDesktop (0); + + LinuxComponentPeer* const wp = dynamic_cast (getPeer()); + + if (wp != 0) + { + wp->setTaskBarIcon (newImage); + + setVisible (true); + toFront (false); + repaint(); + } +} + +void SystemTrayIconComponent::paint (Graphics& g) +{ + LinuxComponentPeer* const wp = dynamic_cast (getPeer()); + + if (wp != 0) + { + const Image* const image = wp->getTaskbarIcon(); + + if (image != 0) + g.drawImageAt (image, 0, 0, false); + } +} + +void SystemTrayIconComponent::setIconTooltip (const String& tooltip) +{ + // xxx not yet implemented! +} + +void PlatformUtilities::beep() +{ + fprintf (stdout, "\a"); + fflush (stdout); +} + +bool AlertWindow::showNativeDialogBox (const String& title, + const String& bodyText, + bool isOkCancel) +{ + // xxx this is supposed to pop up an alert! + Logger::outputDebugString (title + ": " + bodyText); + + // use a non-native one for the time being.. + if (isOkCancel) + return AlertWindow::showOkCancelBox (AlertWindow::NoIcon, title, bodyText); + else + AlertWindow::showMessageBox (AlertWindow::NoIcon, title, bodyText); + + return true; +} + +const int KeyPress::spaceKey = XK_space & 0xff; +const int KeyPress::returnKey = XK_Return & 0xff; +const int KeyPress::escapeKey = XK_Escape & 0xff; +const int KeyPress::backspaceKey = XK_BackSpace & 0xff; +const int KeyPress::leftKey = (XK_Left & 0xff) | extendedKeyModifier; +const int KeyPress::rightKey = (XK_Right & 0xff) | extendedKeyModifier; +const int KeyPress::upKey = (XK_Up & 0xff) | extendedKeyModifier; +const int KeyPress::downKey = (XK_Down & 0xff) | extendedKeyModifier; +const int KeyPress::pageUpKey = (XK_Page_Up & 0xff) | extendedKeyModifier; +const int KeyPress::pageDownKey = (XK_Page_Down & 0xff) | extendedKeyModifier; +const int KeyPress::endKey = (XK_End & 0xff) | extendedKeyModifier; +const int KeyPress::homeKey = (XK_Home & 0xff) | extendedKeyModifier; +const int KeyPress::insertKey = (XK_Insert & 0xff) | extendedKeyModifier; +const int KeyPress::deleteKey = (XK_Delete & 0xff) | extendedKeyModifier; +const int KeyPress::tabKey = XK_Tab & 0xff; +const int KeyPress::F1Key = (XK_F1 & 0xff) | extendedKeyModifier; +const int KeyPress::F2Key = (XK_F2 & 0xff) | extendedKeyModifier; +const int KeyPress::F3Key = (XK_F3 & 0xff) | extendedKeyModifier; +const int KeyPress::F4Key = (XK_F4 & 0xff) | extendedKeyModifier; +const int KeyPress::F5Key = (XK_F5 & 0xff) | extendedKeyModifier; +const int KeyPress::F6Key = (XK_F6 & 0xff) | extendedKeyModifier; +const int KeyPress::F7Key = (XK_F7 & 0xff) | extendedKeyModifier; +const int KeyPress::F8Key = (XK_F8 & 0xff) | extendedKeyModifier; +const int KeyPress::F9Key = (XK_F9 & 0xff) | extendedKeyModifier; +const int KeyPress::F10Key = (XK_F10 & 0xff) | extendedKeyModifier; +const int KeyPress::F11Key = (XK_F11 & 0xff) | extendedKeyModifier; +const int KeyPress::F12Key = (XK_F12 & 0xff) | extendedKeyModifier; +const int KeyPress::F13Key = (XK_F13 & 0xff) | extendedKeyModifier; +const int KeyPress::F14Key = (XK_F14 & 0xff) | extendedKeyModifier; +const int KeyPress::F15Key = (XK_F15 & 0xff) | extendedKeyModifier; +const int KeyPress::F16Key = (XK_F16 & 0xff) | extendedKeyModifier; +const int KeyPress::numberPad0 = (XK_KP_0 & 0xff) | extendedKeyModifier; +const int KeyPress::numberPad1 = (XK_KP_1 & 0xff) | extendedKeyModifier; +const int KeyPress::numberPad2 = (XK_KP_2 & 0xff) | extendedKeyModifier; +const int KeyPress::numberPad3 = (XK_KP_3 & 0xff) | extendedKeyModifier; +const int KeyPress::numberPad4 = (XK_KP_4 & 0xff) | extendedKeyModifier; +const int KeyPress::numberPad5 = (XK_KP_5 & 0xff) | extendedKeyModifier; +const int KeyPress::numberPad6 = (XK_KP_6 & 0xff) | extendedKeyModifier; +const int KeyPress::numberPad7 = (XK_KP_7 & 0xff)| extendedKeyModifier; +const int KeyPress::numberPad8 = (XK_KP_8 & 0xff)| extendedKeyModifier; +const int KeyPress::numberPad9 = (XK_KP_9 & 0xff)| extendedKeyModifier; +const int KeyPress::numberPadAdd = (XK_KP_Add & 0xff)| extendedKeyModifier; +const int KeyPress::numberPadSubtract = (XK_KP_Subtract & 0xff)| extendedKeyModifier; +const int KeyPress::numberPadMultiply = (XK_KP_Multiply & 0xff)| extendedKeyModifier; +const int KeyPress::numberPadDivide = (XK_KP_Divide & 0xff)| extendedKeyModifier; +const int KeyPress::numberPadSeparator = (XK_KP_Separator & 0xff)| extendedKeyModifier; +const int KeyPress::numberPadDecimalPoint = (XK_KP_Decimal & 0xff)| extendedKeyModifier; +const int KeyPress::numberPadEquals = (XK_KP_Equal & 0xff)| extendedKeyModifier; +const int KeyPress::numberPadDelete = (XK_KP_Delete & 0xff)| extendedKeyModifier; +const int KeyPress::playKey = (0xffeeff00) | extendedKeyModifier; +const int KeyPress::stopKey = (0xffeeff01) | extendedKeyModifier; +const int KeyPress::fastForwardKey = (0xffeeff02) | extendedKeyModifier; +const int KeyPress::rewindKey = (0xffeeff03) | extendedKeyModifier; + +END_JUCE_NAMESPACE + +#endif +/********* End of inlined file: juce_linux_Windowing.cpp *********/ + +#endif + +#endif + +//============================================================================== +#if JUCE_MAC + +/********* Start of inlined file: juce_mac_Files.cpp *********/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +BEGIN_JUCE_NAMESPACE + +/* + Note that a lot of methods that you'd expect to find in this file actually + live in juce_posix_SharedCode.cpp! +*/ + +/********* Start of inlined file: juce_posix_SharedCode.cpp *********/ +/* + This file contains posix routines that are common to both the Linux and Mac builds. + + It gets included directly in the cpp files for these platforms. +*/ + +CriticalSection::CriticalSection() throw() +{ + pthread_mutexattr_t atts; + pthread_mutexattr_init (&atts); + pthread_mutexattr_settype (&atts, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init (&internal, &atts); +} + +CriticalSection::~CriticalSection() throw() +{ + pthread_mutex_destroy (&internal); +} + +void CriticalSection::enter() const throw() +{ + pthread_mutex_lock (&internal); +} + +bool CriticalSection::tryEnter() const throw() +{ + return pthread_mutex_trylock (&internal) == 0; +} + +void CriticalSection::exit() const throw() +{ + pthread_mutex_unlock (&internal); +} + +struct EventStruct +{ + pthread_cond_t condition; + pthread_mutex_t mutex; + bool triggered; +}; + +WaitableEvent::WaitableEvent() throw() +{ + EventStruct* const es = new EventStruct(); + es->triggered = false; + + pthread_cond_init (&es->condition, 0); + pthread_mutex_init (&es->mutex, 0); + + internal = es; +} + +WaitableEvent::~WaitableEvent() throw() +{ + EventStruct* const es = (EventStruct*) internal; + + pthread_cond_destroy (&es->condition); + pthread_mutex_destroy (&es->mutex); + + delete es; +} + +bool WaitableEvent::wait (const int timeOutMillisecs) const throw() +{ + EventStruct* const es = (EventStruct*) internal; + + bool ok = true; + pthread_mutex_lock (&es->mutex); + + if (! es->triggered) + { + if (timeOutMillisecs < 0) + { + pthread_cond_wait (&es->condition, &es->mutex); + } + else + { + struct timespec time; + +#if JUCE_MAC + time.tv_sec = timeOutMillisecs / 1000; + time.tv_nsec = (timeOutMillisecs % 1000) * 1000000; + pthread_cond_timedwait_relative_np (&es->condition, &es->mutex, &time); +#else + struct timeval t; + int timeout = 0; + + gettimeofday (&t, 0); + + time.tv_sec = t.tv_sec + (timeOutMillisecs / 1000); + time.tv_nsec = (t.tv_usec + ((timeOutMillisecs % 1000) * 1000)) * 1000; + + while (time.tv_nsec >= 1000000000) + { + time.tv_nsec -= 1000000000; + time.tv_sec++; + } + + while (! timeout) + { + timeout = pthread_cond_timedwait (&es->condition, &es->mutex, &time); + + if (! timeout) + // Success + break; + + if (timeout == EINTR) + // Go round again + timeout = 0; + } +#endif + } + + ok = es->triggered; + } + + es->triggered = false; + + pthread_mutex_unlock (&es->mutex); + return ok; +} + +void WaitableEvent::signal() const throw() +{ + EventStruct* const es = (EventStruct*) internal; + + pthread_mutex_lock (&es->mutex); + es->triggered = true; + pthread_cond_broadcast (&es->condition); + pthread_mutex_unlock (&es->mutex); +} + +void WaitableEvent::reset() const throw() +{ + EventStruct* const es = (EventStruct*) internal; + + pthread_mutex_lock (&es->mutex); + es->triggered = false; + pthread_mutex_unlock (&es->mutex); +} + +void JUCE_CALLTYPE Thread::sleep (int millisecs) throw() +{ + struct timespec time; + time.tv_sec = millisecs / 1000; + time.tv_nsec = (millisecs % 1000) * 1000000; + nanosleep (&time, 0); +} + +const tchar File::separator = T('/'); +const tchar* File::separatorString = T("/"); + +bool juce_copyFile (const String& s, const String& d) throw(); + +static bool juce_stat (const String& fileName, struct stat& info) throw() +{ + return fileName.isNotEmpty() + && (stat (fileName.toUTF8(), &info) == 0); +} + +bool juce_isDirectory (const String& fileName) throw() +{ + struct stat info; + + return fileName.isEmpty() + || (juce_stat (fileName, info) + && ((info.st_mode & S_IFDIR) != 0)); +} + +bool juce_fileExists (const String& fileName, const bool dontCountDirectories) throw() +{ + if (fileName.isEmpty()) + return false; + + const char* const fileNameUTF8 = fileName.toUTF8(); + bool exists = access (fileNameUTF8, F_OK) == 0; + + if (exists && dontCountDirectories) + { + struct stat info; + const int res = stat (fileNameUTF8, &info); + + if (res == 0 && (info.st_mode & S_IFDIR) != 0) + exists = false; + } + + return exists; +} + +int64 juce_getFileSize (const String& fileName) throw() +{ + struct stat info; + return juce_stat (fileName, info) ? info.st_size : 0; +} + +bool juce_canWriteToFile (const String& fileName) throw() +{ + return access (fileName.toUTF8(), W_OK) == 0; +} + +bool juce_deleteFile (const String& fileName) throw() +{ + const char* const fileNameUTF8 = fileName.toUTF8(); + + if (juce_isDirectory (fileName)) + return rmdir (fileNameUTF8) == 0; + else + return remove (fileNameUTF8) == 0; +} + +bool juce_moveFile (const String& source, const String& dest) throw() +{ + if (rename (source.toUTF8(), dest.toUTF8()) == 0) + return true; + + if (juce_canWriteToFile (source) + && juce_copyFile (source, dest)) + { + if (juce_deleteFile (source)) + return true; + + juce_deleteFile (dest); + } + + return false; +} + +void juce_createDirectory (const String& fileName) throw() +{ + mkdir (fileName.toUTF8(), 0777); +} + +void* juce_fileOpen (const String& fileName, bool forWriting) throw() +{ + const char* const fileNameUTF8 = fileName.toUTF8(); + int flags = O_RDONLY; + + if (forWriting) + { + if (juce_fileExists (fileName, false)) + { + const int f = open (fileNameUTF8, O_RDWR, 00644); + + if (f != -1) + lseek (f, 0, SEEK_END); + + return (void*) f; + } + else + { + flags = O_RDWR + O_CREAT; + } + } + + return (void*) open (fileNameUTF8, flags, 00644); +} + +void juce_fileClose (void* handle) throw() +{ + if (handle != 0) + close ((int) (pointer_sized_int) handle); +} + +int juce_fileRead (void* handle, void* buffer, int size) throw() +{ + if (handle != 0) + return read ((int) (pointer_sized_int) handle, buffer, size); + + return 0; +} + +int juce_fileWrite (void* handle, const void* buffer, int size) throw() +{ + if (handle != 0) + return write ((int) (pointer_sized_int) handle, buffer, size); + + return 0; +} + +int64 juce_fileSetPosition (void* handle, int64 pos) throw() +{ + if (handle != 0 && lseek ((int) (pointer_sized_int) handle, pos, SEEK_SET) == pos) + return pos; + + return -1; +} + +int64 juce_fileGetPosition (void* handle) throw() +{ + if (handle != 0) + return lseek ((int) (pointer_sized_int) handle, 0, SEEK_CUR); + else + return -1; +} + +void juce_fileFlush (void* handle) throw() +{ + if (handle != 0) + fsync ((int) (pointer_sized_int) handle); +} + +// if this file doesn't exist, find a parent of it that does.. +static bool doStatFS (const File* file, struct statfs& result) throw() +{ + File f (*file); + + for (int i = 5; --i >= 0;) + { + if (f.exists()) + break; + + f = f.getParentDirectory(); + } + + return statfs (f.getFullPathName().toUTF8(), &result) == 0; +} + +int64 File::getBytesFreeOnVolume() const throw() +{ + int64 free_space = 0; + + struct statfs buf; + if (doStatFS (this, buf)) + // Note: this returns space available to non-super user + free_space = (int64) buf.f_bsize * (int64) buf.f_bavail; + + return free_space; +} + +const String juce_getVolumeLabel (const String& filenameOnVolume, + int& volumeSerialNumber) throw() +{ + // There is no equivalent on Linux + volumeSerialNumber = 0; + return String::empty; +} + +#if JUCE_64BIT + #define filedesc ((long long) internal) +#else + #define filedesc ((int) internal) +#endif + +InterProcessLock::InterProcessLock (const String& name_) throw() + : internal (0), + name (name_), + reentrancyLevel (0) +{ +#if JUCE_MAC + // (don't use getSpecialLocation() to avoid the temp folder being different for each app) + const File temp (File (T("~/Library/Caches/Juce")).getChildFile (name)); +#else + const File temp (File::getSpecialLocation (File::tempDirectory).getChildFile (name)); +#endif + + temp.create(); + + internal = (void*) open (temp.getFullPathName().toUTF8(), O_RDWR); +} + +InterProcessLock::~InterProcessLock() throw() +{ + while (reentrancyLevel > 0) + this->exit(); + + close (filedesc); +} + +bool InterProcessLock::enter (const int timeOutMillisecs) throw() +{ + if (internal == 0) + return false; + + if (reentrancyLevel != 0) + return true; + + const int64 endTime = Time::currentTimeMillis() + timeOutMillisecs; + + struct flock fl; + zerostruct (fl); + fl.l_whence = SEEK_SET; + fl.l_type = F_WRLCK; + + for (;;) + { + const int result = fcntl (filedesc, F_SETLK, &fl); + + if (result >= 0) + { + ++reentrancyLevel; + return true; + } + + if (errno != EINTR) + { + if (timeOutMillisecs == 0 + || (timeOutMillisecs > 0 && Time::currentTimeMillis() >= endTime)) + break; + + Thread::sleep (10); + } + } + + return false; +} + +void InterProcessLock::exit() throw() +{ + if (reentrancyLevel > 0 && internal != 0) + { + --reentrancyLevel; + + struct flock fl; + zerostruct (fl); + fl.l_whence = SEEK_SET; + fl.l_type = F_UNLCK; + + for (;;) + { + const int result = fcntl (filedesc, F_SETLKW, &fl); + + if (result >= 0 || errno != EINTR) + break; + } + } +} +/********* End of inlined file: juce_posix_SharedCode.cpp *********/ + +static File executableFile; + +void PlatformUtilities::copyToStr255 (Str255& d, const String& s) +{ + unsigned char* t = (unsigned char*) d; + t[0] = jmin (254, s.length()); + s.copyToBuffer ((char*) t + 1, 254); +} + +void PlatformUtilities::copyToStr63 (Str63& d, const String& s) +{ + unsigned char* t = (unsigned char*) d; + t[0] = jmin (62, s.length()); + s.copyToBuffer ((char*) t + 1, 62); +} + +const String PlatformUtilities::cfStringToJuceString (CFStringRef cfString) +{ + String result; + + if (cfString != 0) + { +#if JUCE_STRINGS_ARE_UNICODE + CFRange range = { 0, CFStringGetLength (cfString) }; + UniChar* const u = (UniChar*) juce_malloc (sizeof (UniChar) * (range.length + 1)); + + CFStringGetCharacters (cfString, range, u); + u[range.length] = 0; + + result = convertUTF16ToString (u); + + juce_free (u); +#else + const int len = CFStringGetLength (cfString); + char* buffer = (char*) juce_malloc (len + 1); + CFStringGetCString (cfString, buffer, len + 1, CFStringGetSystemEncoding()); + result = buffer; + juce_free (buffer); +#endif + } + + return result; +} + +CFStringRef PlatformUtilities::juceStringToCFString (const String& s) +{ +#if JUCE_STRINGS_ARE_UNICODE + const int len = s.length(); + const juce_wchar* t = (const juce_wchar*) s; + + UniChar* temp = (UniChar*) juce_malloc (sizeof (UniChar) * len + 4); + + for (int i = 0; i <= len; ++i) + temp[i] = t[i]; + + CFStringRef result = CFStringCreateWithCharacters (kCFAllocatorDefault, temp, len); + juce_free (temp); + + return result; + +#else + return CFStringCreateWithCString (kCFAllocatorDefault, + (const char*) s, + CFStringGetSystemEncoding()); +#endif +} + +const String PlatformUtilities::convertUTF16ToString (const UniChar* utf16) +{ + String s; + + while (*utf16 != 0) + s += (juce_wchar) *utf16++; + + return s; +} + +const String PlatformUtilities::convertToPrecomposedUnicode (const String& s) +{ + UnicodeMapping map; + + map.unicodeEncoding = CreateTextEncoding (kTextEncodingUnicodeDefault, + kUnicodeNoSubset, + kTextEncodingDefaultFormat); + + map.otherEncoding = CreateTextEncoding (kTextEncodingUnicodeDefault, + kUnicodeCanonicalCompVariant, + kTextEncodingDefaultFormat); + + map.mappingVersion = kUnicodeUseLatestMapping; + + UnicodeToTextInfo conversionInfo = 0; + String result; + + if (CreateUnicodeToTextInfo (&map, &conversionInfo) == noErr) + { + const int len = s.length(); + + UniChar* const tempIn = (UniChar*) juce_calloc (sizeof (UniChar) * len + 4); + UniChar* const tempOut = (UniChar*) juce_calloc (sizeof (UniChar) * len + 4); + + for (int i = 0; i <= len; ++i) + tempIn[i] = s[i]; + + ByteCount bytesRead = 0; + ByteCount outputBufferSize = 0; + + if (ConvertFromUnicodeToText (conversionInfo, + len * sizeof (UniChar), tempIn, + kUnicodeDefaultDirectionMask, + 0, 0, 0, 0, + len * sizeof (UniChar), &bytesRead, + &outputBufferSize, tempOut) == noErr) + { + result.preallocateStorage (bytesRead / sizeof (UniChar) + 2); + + tchar* t = const_cast ((const tchar*) result); + + int i; + for (i = 0; i < bytesRead / sizeof (UniChar); ++i) + t[i] = (tchar) tempOut[i]; + + t[i] = 0; + } + + juce_free (tempIn); + juce_free (tempOut); + + DisposeUnicodeToTextInfo (&conversionInfo); + } + + return result; +} + +const unsigned int macTimeToUnixTimeDiff = 0x7c25be90; + +static uint64 utcDateTimeToUnixTime (const UTCDateTime& d) throw() +{ + if (d.highSeconds == 0 && d.lowSeconds == 0 && d.fraction == 0) + return 0; + + return (((((uint64) d.highSeconds) << 32) | (uint64) d.lowSeconds) * 1000) + + ((d.fraction * 1000) >> 16) + - 2082844800000ll; +} + +static void unixTimeToUtcDateTime (uint64 t, UTCDateTime& d) throw() +{ + if (t != 0) + t += 2082844800000ll; + + d.highSeconds = (t / 1000) >> 32; + d.lowSeconds = (t / 1000) & (uint64) 0xffffffff; + d.fraction = ((t % 1000) << 16) / 1000; +} + +void juce_getFileTimes (const String& fileName, + int64& modificationTime, + int64& accessTime, + int64& creationTime) throw() +{ + modificationTime = 0; + accessTime = 0; + creationTime = 0; + + FSRef fileRef; + if (PlatformUtilities::makeFSRefFromPath (&fileRef, fileName)) + { + FSRefParam info; + zerostruct (info); + + info.ref = &fileRef; + info.whichInfo = kFSCatInfoAllDates; + + FSCatalogInfo catInfo; + info.catInfo = &catInfo; + + if (PBGetCatalogInfoSync (&info) == noErr) + { + creationTime = utcDateTimeToUnixTime (catInfo.createDate); + accessTime = utcDateTimeToUnixTime (catInfo.accessDate); + modificationTime = utcDateTimeToUnixTime (catInfo.contentModDate); + } + } +} + +bool juce_setFileTimes (const String& fileName, + int64 modificationTime, + int64 accessTime, + int64 creationTime) throw() +{ + FSRef fileRef; + if (PlatformUtilities::makeFSRefFromPath (&fileRef, fileName)) + { + FSRefParam info; + zerostruct (info); + + info.ref = &fileRef; + info.whichInfo = kFSCatInfoAllDates; + + FSCatalogInfo catInfo; + info.catInfo = &catInfo; + + if (PBGetCatalogInfoSync (&info) == noErr) + { + if (creationTime != 0) + unixTimeToUtcDateTime (creationTime, catInfo.createDate); + + if (modificationTime != 0) + unixTimeToUtcDateTime (modificationTime, catInfo.contentModDate); + + if (accessTime != 0) + unixTimeToUtcDateTime (accessTime, catInfo.accessDate); + + return PBSetCatalogInfoSync (&info) == noErr; + } + } + + return false; +} + +bool juce_setFileReadOnly (const String& fileName, bool isReadOnly) throw() +{ + const char* const fileNameUTF8 = fileName.toUTF8(); + + struct stat info; + const int res = stat (fileNameUTF8, &info); + + bool ok = false; + + if (res == 0) + { + info.st_mode &= 0777; // Just permissions + + if (isReadOnly) + info.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); + else + // Give everybody write permission? + info.st_mode |= S_IWUSR | S_IWGRP | S_IWOTH; + + ok = chmod (fileNameUTF8, info.st_mode) == 0; + } + + return ok; +} + +bool juce_copyFile (const String& src, const String& dst) throw() +{ + const File destFile (dst); + + if (! destFile.create()) + return false; + + FSRef srcRef, dstRef; + + if (! (PlatformUtilities::makeFSRefFromPath (&srcRef, src) + && PlatformUtilities::makeFSRefFromPath (&dstRef, dst))) + { + return false; + } + + int okForks = 0; + + CatPositionRec iter; + iter.initialize = 0; + HFSUniStr255 forkName; + + // can't just copy the data because this is a bloody Mac, so we need to copy each + // fork separately... + while (FSIterateForks (&srcRef, &iter, &forkName, 0, 0) == noErr) + { + SInt16 srcForkNum = 0, dstForkNum = 0; + OSErr err = FSOpenFork (&srcRef, forkName.length, forkName.unicode, fsRdPerm, &srcForkNum); + + if (err == noErr) + { + err = FSOpenFork (&dstRef, forkName.length, forkName.unicode, fsRdWrPerm, &dstForkNum); + + if (err == noErr) + { + MemoryBlock buf (32768); + SInt64 pos = 0; + + for (;;) + { + ByteCount bytesRead = 0; + err = FSReadFork (srcForkNum, fsFromStart, pos, buf.getSize(), (char*) buf, &bytesRead); + + if (bytesRead > 0) + { + err = FSWriteFork (dstForkNum, fsFromStart, pos, bytesRead, (const char*) buf, &bytesRead); + pos += bytesRead; + } + + if (err != noErr) + { + if (err == eofErr) + ++okForks; + + break; + } + } + + FSFlushFork (dstForkNum); + FSCloseFork (dstForkNum); + } + + FSCloseFork (srcForkNum); + } + } + + if (okForks > 0) // some files seem to be ok even if not all their forks get copied.. + { + // copy permissions.. + struct stat info; + if (juce_stat (src, info)) + chmod (dst.toUTF8(), info.st_mode & 0777); + + return true; + } + + return false; +} + +const StringArray juce_getFileSystemRoots() throw() +{ + StringArray s; + s.add (T("/")); + return s; +} + +static bool isFileOnDriveType (const File* const f, const char** types) throw() +{ + struct statfs buf; + + if (doStatFS (f, buf)) + { + const String type (buf.f_fstypename); + + while (*types != 0) + if (type.equalsIgnoreCase (*types++)) + return true; + } + + return false; +} + +bool File::isOnCDRomDrive() const throw() +{ + static const char* const cdTypes[] = { "cd9660", "cdfs", "cddafs", "udf", 0 }; + + return isFileOnDriveType (this, (const char**) cdTypes); +} + +bool File::isOnHardDisk() const throw() +{ + static const char* const nonHDTypes[] = { "nfs", "smbfs", "ramfs", 0 }; + + return ! (isOnCDRomDrive() || isFileOnDriveType (this, (const char**) nonHDTypes)); +} + +static bool juce_isHiddenFile (const String& path) throw() +{ + FSRef ref; + if (! PlatformUtilities::makeFSRefFromPath (&ref, path)) + return false; + + FSCatalogInfo info; + FSGetCatalogInfo (&ref, kFSCatInfoNodeFlags | kFSCatInfoFinderInfo, &info, 0, 0, 0); + + if ((info.nodeFlags & kFSNodeIsDirectoryBit) != 0) + return (((FolderInfo*) &info.finderInfo)->finderFlags & kIsInvisible) != 0; + + return (((FileInfo*) &info.finderInfo)->finderFlags & kIsInvisible) != 0; +} + +bool File::isHidden() const throw() +{ + return juce_isHiddenFile (getFullPathName()); +} + +const File File::getSpecialLocation (const SpecialLocationType type) +{ + const char* resultPath = 0; + + switch (type) + { + case userHomeDirectory: + resultPath = getenv ("HOME"); + + if (resultPath == 0) + { + struct passwd* const pw = getpwuid (getuid()); + if (pw != 0) + resultPath = pw->pw_dir; + } + + break; + + case userDocumentsDirectory: + resultPath = "~/Documents"; + break; + + case userDesktopDirectory: + resultPath = "~/Desktop"; + break; + + case userApplicationDataDirectory: + resultPath = "~/Library"; + break; + + case commonApplicationDataDirectory: + resultPath = "/Library"; + break; + + case globalApplicationsDirectory: + resultPath = "/Applications"; + break; + + case userMusicDirectory: + resultPath = "~/Music"; + break; + + case userMoviesDirectory: + resultPath = "~/Movies"; + break; + + case tempDirectory: + { + File tmp (T("~/Library/Caches/") + executableFile.getFileNameWithoutExtension()); + + tmp.createDirectory(); + return tmp.getFullPathName(); + } + + case currentExecutableFile: + return executableFile; + + case currentApplicationFile: + { + const File parent (executableFile.getParentDirectory()); + + return parent.getFullPathName().endsWithIgnoreCase (T("Contents/MacOS")) + ? parent.getParentDirectory().getParentDirectory() + : executableFile; + } + + default: + jassertfalse // unknown type? + break; + } + + if (resultPath != 0) + return File (PlatformUtilities::convertToPrecomposedUnicode (resultPath)); + + return File::nonexistent; +} + +void juce_setCurrentExecutableFileName (const String& filename) throw() +{ + executableFile = File::getCurrentWorkingDirectory() + .getChildFile (PlatformUtilities::convertToPrecomposedUnicode (filename)); +} + +void juce_setCurrentExecutableFileNameFromBundleId (const String& bundleId) throw() +{ + CFStringRef bundleIdStringRef = PlatformUtilities::juceStringToCFString (bundleId); + CFBundleRef bundleRef = CFBundleGetBundleWithIdentifier (bundleIdStringRef); + CFRelease (bundleIdStringRef); + + if (bundleRef != 0) + { + CFURLRef exeURLRef = CFBundleCopyExecutableURL (bundleRef); + + if (exeURLRef != 0) + { + CFStringRef pathStringRef = CFURLCopyFileSystemPath (exeURLRef, kCFURLPOSIXPathStyle); + CFRelease (exeURLRef); + + if (pathStringRef != 0) + { + juce_setCurrentExecutableFileName (PlatformUtilities::cfStringToJuceString (pathStringRef)); + CFRelease (pathStringRef); + } + } + } +} + +const File File::getCurrentWorkingDirectory() throw() +{ + char buf [2048]; + getcwd (buf, sizeof(buf)); + + return File (PlatformUtilities::convertToPrecomposedUnicode (buf)); +} + +bool File::setAsCurrentWorkingDirectory() const throw() +{ + return chdir (getFullPathName().toUTF8()) == 0; +} + +struct FindFileStruct +{ + String parentDir, wildCard; + DIR* dir; + + bool getNextMatch (String& result, bool* const isDir, bool* const isHidden, int64* const fileSize, + Time* const modTime, Time* const creationTime, bool* const isReadOnly) throw() + { + const char* const wildCardUTF8 = wildCard.toUTF8(); + + for (;;) + { + struct dirent* const de = readdir (dir); + + if (de == 0) + break; + + if (fnmatch (wildCardUTF8, de->d_name, 0) == 0) + { + result = PlatformUtilities::convertToPrecomposedUnicode (String::fromUTF8 ((const uint8*) de->d_name)); + + const String path (parentDir + result); + + if (isDir != 0 || fileSize != 0) + { + struct stat info; + const bool statOk = juce_stat (path, info); + + if (isDir != 0) + *isDir = path.isEmpty() || (statOk && ((info.st_mode & S_IFDIR) != 0)); + + if (isHidden != 0) + *isHidden = (de->d_name[0] == '.') + || juce_isHiddenFile (path); + + if (fileSize != 0) + *fileSize = statOk ? info.st_size : 0; + } + + if (modTime != 0 || creationTime != 0) + { + int64 m, a, c; + juce_getFileTimes (path, m, a, c); + + if (modTime != 0) + *modTime = m; + + if (creationTime != 0) + *creationTime = c; + } + + if (isReadOnly != 0) + *isReadOnly = ! juce_canWriteToFile (path); + + return true; + } + } + + return false; + } +}; + +// returns 0 on failure +void* juce_findFileStart (const String& directory, const String& wildCard, String& firstResultFile, + bool* isDir, bool* isHidden, int64* fileSize, Time* modTime, + Time* creationTime, bool* isReadOnly) throw() +{ + DIR* const d = opendir (directory.toUTF8()); + + if (d != 0) + { + FindFileStruct* const ff = new FindFileStruct(); + ff->parentDir = directory; + + if (!ff->parentDir.endsWithChar (File::separator)) + ff->parentDir += File::separator; + + ff->wildCard = wildCard; + ff->dir = d; + + if (ff->getNextMatch (firstResultFile, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly)) + { + return ff; + } + else + { + firstResultFile = String::empty; + isDir = false; + closedir (d); + delete ff; + } + } + + return 0; +} + +bool juce_findFileNext (void* handle, String& resultFile, + bool* isDir, bool* isHidden, int64* fileSize, Time* modTime, Time* creationTime, bool* isReadOnly) throw() +{ + FindFileStruct* const ff = (FindFileStruct*) handle; + + if (ff != 0) + return ff->getNextMatch (resultFile, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly); + + return false; +} + +void juce_findFileClose (void* handle) throw() +{ + FindFileStruct* const ff = (FindFileStruct*)handle; + + if (ff != 0) + { + closedir (ff->dir); + delete ff; + } +} + +bool juce_launchExecutable (const String& pathAndArguments) throw() +{ + char* const argv[4] = { "/bin/sh", "-c", (char*) (const char*) pathAndArguments, 0 }; + + const int cpid = fork(); + + if (cpid == 0) + { + // Child process + if (execve (argv[0], argv, 0) < 0) + exit (0); + } + else + { + if (cpid < 0) + return false; + } + + return true; +} + +bool juce_launchFile (const String& fileName, + const String& parameters) throw() +{ + bool ok = false; + + if (fileName.startsWithIgnoreCase (T("http:")) + || fileName.startsWithIgnoreCase (T("https:")) + || fileName.startsWithIgnoreCase (T("ftp:")) + || fileName.startsWithIgnoreCase (T("file:"))) + { + CFStringRef urlString = PlatformUtilities::juceStringToCFString (fileName); + + if (urlString != 0) + { + CFURLRef url = CFURLCreateWithString (kCFAllocatorDefault, + urlString, 0); + CFRelease (urlString); + + if (url != 0) + { + ok = (LSOpenCFURLRef (url, 0) == noErr); + CFRelease (url); + } + } + } + else + { + FSRef ref; + if (PlatformUtilities::makeFSRefFromPath (&ref, fileName)) + { + if (juce_isDirectory (fileName) && parameters.isNotEmpty()) + { + // if we're launching a bundled app with a document.. + StringArray docs; + docs.addTokens (parameters, true); + FSRef* docRefs = new FSRef [docs.size()]; + + for (int i = 0; i < docs.size(); ++i) + PlatformUtilities::makeFSRefFromPath (docRefs + i, docs[i]); + + LSLaunchFSRefSpec ors; + ors.appRef = &ref; + ors.numDocs = docs.size(); + ors.itemRefs = docRefs; + ors.passThruParams = 0; + ors.launchFlags = kLSLaunchDefaults; + ors.asyncRefCon = 0; + + FSRef actual; + ok = (LSOpenFromRefSpec (&ors, &actual) == noErr); + + delete docRefs; + } + else + { + if (parameters.isNotEmpty()) + ok = juce_launchExecutable (T("\"") + fileName + T("\" ") + parameters); + else + ok = (LSOpenFSRef (&ref, 0) == noErr); + } + } + } + + return ok; +} + +bool PlatformUtilities::makeFSSpecFromPath (FSSpec* fs, const String& path) +{ + FSRef ref; + + return makeFSRefFromPath (&ref, path) + && FSGetCatalogInfo (&ref, kFSCatInfoNone, 0, 0, fs, 0) == noErr; +} + +bool PlatformUtilities::makeFSRefFromPath (FSRef* destFSRef, const String& path) +{ + return FSPathMakeRef ((const UInt8*) path.toUTF8(), destFSRef, 0) == noErr; +} + +const String PlatformUtilities::makePathFromFSRef (FSRef* file) +{ + uint8 path [2048]; + zeromem (path, sizeof (path)); + + String result; + + if (FSRefMakePath (file, (UInt8*) path, sizeof (path) - 1) == noErr) + result = String::fromUTF8 (path); + + return PlatformUtilities::convertToPrecomposedUnicode (result); +} + +OSType PlatformUtilities::getTypeOfFile (const String& filename) +{ + FSRef fs; + if (makeFSRefFromPath (&fs, filename)) + { + LSItemInfoRecord info; + + if (LSCopyItemInfoForRef (&fs, kLSRequestTypeCreator, &info) == noErr) + return info.filetype; + } + + return 0; +} + +bool PlatformUtilities::isBundle (const String& filename) +{ + FSRef fs; + if (makeFSRefFromPath (&fs, filename)) + { + LSItemInfoRecord info; + + if (LSCopyItemInfoForRef (&fs, kLSItemInfoIsPackage, &info) == noErr) + return (info.flags & kLSItemInfoIsPackage) != 0; + } + + return false; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_mac_Files.cpp *********/ + +/********* Start of inlined file: juce_mac_NamedPipe.cpp *********/ + +#include +#include +#include + +// As well as being for the mac, this file is included by the linux build. + +#if JUCE_MAC + #include +#else + #include + #include + #include +#endif + +BEGIN_JUCE_NAMESPACE + +struct NamedPipeInternal +{ + String pipeInName, pipeOutName; + int pipeIn, pipeOut; + + bool volatile createdPipe, blocked, stopReadOperation; + + static void signalHandler (int) {} +}; + +void NamedPipe::cancelPendingReads() +{ + while (internal != 0 && ((NamedPipeInternal*) internal)->blocked) + { + NamedPipeInternal* const intern = (NamedPipeInternal*) internal; + + intern->stopReadOperation = true; + + char buffer [1] = { 0 }; + ::write (intern->pipeIn, buffer, 1); + + int timeout = 2000; + while (intern->blocked && --timeout >= 0) + sleep (2); + + intern->stopReadOperation = false; + } +} + +void NamedPipe::close() +{ + NamedPipeInternal* const intern = (NamedPipeInternal*) internal; + + if (intern != 0) + { + internal = 0; + + if (intern->pipeIn != -1) + ::close (intern->pipeIn); + + if (intern->pipeOut != -1) + ::close (intern->pipeOut); + + if (intern->createdPipe) + { + unlink (intern->pipeInName); + unlink (intern->pipeOutName); + } + + delete intern; + } +} + +bool NamedPipe::openInternal (const String& pipeName, const bool createPipe) +{ + close(); + + NamedPipeInternal* const intern = new NamedPipeInternal(); + internal = intern; + intern->createdPipe = createPipe; + intern->blocked = false; + intern->stopReadOperation = false; + + signal (SIGPIPE, NamedPipeInternal::signalHandler); + siginterrupt (SIGPIPE, 1); + + const String pipePath (T("/tmp/") + File::createLegalFileName (pipeName)); + + intern->pipeInName = pipePath + T("_in"); + intern->pipeOutName = pipePath + T("_out"); + intern->pipeIn = -1; + intern->pipeOut = -1; + + if (createPipe) + { + if ((mkfifo (intern->pipeInName, 0666) && errno != EEXIST) + || (mkfifo (intern->pipeOutName, 0666) && errno != EEXIST)) + { + delete intern; + internal = 0; + + return false; + } + } + + return true; +} + +int NamedPipe::read (void* destBuffer, int maxBytesToRead, int /*timeOutMilliseconds*/) +{ + int bytesRead = -1; + NamedPipeInternal* const intern = (NamedPipeInternal*) internal; + + if (intern != 0) + { + intern->blocked = true; + + if (intern->pipeIn == -1) + { + if (intern->createdPipe) + intern->pipeIn = ::open (intern->pipeInName, O_RDWR); + else + intern->pipeIn = ::open (intern->pipeOutName, O_RDWR); + + if (intern->pipeIn == -1) + { + intern->blocked = false; + return -1; + } + } + + bytesRead = 0; + + char* p = (char*) destBuffer; + + while (bytesRead < maxBytesToRead) + { + const int bytesThisTime = maxBytesToRead - bytesRead; + const int numRead = ::read (intern->pipeIn, p, bytesThisTime); + + if (numRead <= 0 || intern->stopReadOperation) + { + bytesRead = -1; + break; + } + + bytesRead += numRead; + p += bytesRead; + } + + intern->blocked = false; + } + + return bytesRead; +} + +int NamedPipe::write (const void* sourceBuffer, int numBytesToWrite, int timeOutMilliseconds) +{ + int bytesWritten = -1; + NamedPipeInternal* const intern = (NamedPipeInternal*) internal; + + if (intern != 0) + { + if (intern->pipeOut == -1) + { + if (intern->createdPipe) + intern->pipeOut = ::open (intern->pipeOutName, O_WRONLY); + else + intern->pipeOut = ::open (intern->pipeInName, O_WRONLY); + + if (intern->pipeOut == -1) + { + return -1; + } + } + + const char* p = (const char*) sourceBuffer; + bytesWritten = 0; + + const uint32 timeOutTime = Time::getMillisecondCounter() + timeOutMilliseconds; + + while (bytesWritten < numBytesToWrite + && (timeOutMilliseconds < 0 || Time::getMillisecondCounter() < timeOutTime)) + { + const int bytesThisTime = numBytesToWrite - bytesWritten; + const int numWritten = ::write (intern->pipeOut, p, bytesThisTime); + + if (numWritten <= 0) + { + bytesWritten = -1; + break; + } + + bytesWritten += numWritten; + p += bytesWritten; + } + } + + return bytesWritten; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_mac_NamedPipe.cpp *********/ + +/********* Start of inlined file: juce_mac_SystemStats.cpp *********/ + +#include +#include +#include +#include +#include + +BEGIN_JUCE_NAMESPACE + +static int64 highResTimerFrequency; + +#if JUCE_INTEL + +static void juce_getCpuVendor (char* const v) throw() +{ + int vendor[4]; + zerostruct (vendor); + int dummy = 0; + + asm ("mov %%ebx, %%esi \n\t" + "cpuid \n\t" + "xchg %%esi, %%ebx" + : "=a" (dummy), "=S" (vendor[0]), "=c" (vendor[2]), "=d" (vendor[1]) : "a" (0)); + + memcpy (v, vendor, 16); +} + +static unsigned int getCPUIDWord (unsigned int& familyModel, unsigned int& extFeatures) throw() +{ + unsigned int cpu = 0; + unsigned int ext = 0; + unsigned int family = 0; + unsigned int dummy = 0; + + asm ("mov %%ebx, %%esi \n\t" + "cpuid \n\t" + "xchg %%esi, %%ebx" + : "=a" (family), "=S" (ext), "=c" (dummy), "=d" (cpu) : "a" (1)); + + familyModel = family; + extFeatures = ext; + return cpu; +} + +struct CPUFlags +{ + bool hasMMX : 1; + bool hasSSE : 1; + bool hasSSE2 : 1; + bool has3DNow : 1; +}; + +static CPUFlags cpuFlags; + +#endif + +void Logger::outputDebugString (const String& text) throw() +{ + String withLineFeed (text + T("\n")); + const char* const utf8 = withLineFeed.toUTF8(); + fwrite (utf8, strlen (utf8), 1, stdout); +} + +void Logger::outputDebugPrintf (const tchar* format, ...) throw() +{ + String text; + va_list args; + va_start (args, format); + text.vprintf(format, args); + outputDebugString (text); +} + +int SystemStats::getMemorySizeInMegabytes() throw() +{ + long bytes; + if (Gestalt (gestaltPhysicalRAMSize, &bytes) == noErr) + return (int) (((unsigned long) bytes) / (1024 * 1024)); + + return 0; +} + +SystemStats::OperatingSystemType SystemStats::getOperatingSystemType() throw() +{ + return MacOSX; +} + +const String SystemStats::getOperatingSystemName() throw() +{ + return T("Mac OS X"); +} + +bool SystemStats::isOperatingSystem64Bit() throw() +{ +#if JUCE_64BIT + return true; +#else + //xxx not sure how to find this out?.. + return false; +#endif +} + +void SystemStats::initialiseStats() throw() +{ + static bool initialised = false; + + if (! initialised) + { + initialised = true; + + NSApplicationLoad(); + +#if JUCE_INTEL + { + unsigned int familyModel, extFeatures; + const unsigned int features = getCPUIDWord (familyModel, extFeatures); + + cpuFlags.hasMMX = ((features & (1 << 23)) != 0); + cpuFlags.hasSSE = ((features & (1 << 25)) != 0); + cpuFlags.hasSSE2 = ((features & (1 << 26)) != 0); + cpuFlags.has3DNow = ((extFeatures & (1 << 31)) != 0); + } +#endif + + highResTimerFrequency = (int64) AudioGetHostClockFrequency(); + +#if ! JUCE_ONLY_BUILD_CORE_LIBRARY + if (JUCEApplication::getInstance() != 0) + RegisterAppearanceClient(); +#endif + + TXNInitTextension (0, 0, kTXNWantMoviesMask | kTXNWantGraphicsMask); + + String s (SystemStats::getJUCEVersion()); + + rlimit lim; + getrlimit (RLIMIT_NOFILE, &lim); + lim.rlim_cur = lim.rlim_max = RLIM_INFINITY; + setrlimit (RLIMIT_NOFILE, &lim); + } +} + +bool SystemStats::hasMMX() throw() +{ +#if JUCE_INTEL + return cpuFlags.hasMMX; +#else + return false; +#endif +} + +bool SystemStats::hasSSE() throw() +{ +#if JUCE_INTEL + return cpuFlags.hasSSE; +#else + return false; +#endif +} + +bool SystemStats::hasSSE2() throw() +{ +#if JUCE_INTEL + return cpuFlags.hasSSE2; +#else + return false; +#endif +} + +bool SystemStats::has3DNow() throw() +{ +#if JUCE_INTEL + return cpuFlags.has3DNow; +#else + return false; +#endif +} + +const String SystemStats::getCpuVendor() throw() +{ +#if JUCE_INTEL + char v [16]; + juce_getCpuVendor (v); + return String (v, 16); +#else + return String::empty; +#endif +} + +int SystemStats::getCpuSpeedInMegaherz() throw() +{ + return GetCPUSpeed(); +} + +int SystemStats::getNumCpus() throw() +{ + return MPProcessors(); +} + +static int64 juce_getMicroseconds() throw() +{ + UnsignedWide t; + Microseconds (&t); + return (((int64) t.hi) << 32) | t.lo; +} + +uint32 juce_millisecondsSinceStartup() throw() +{ + return (uint32) (juce_getMicroseconds() / 1000); +} + +double Time::getMillisecondCounterHiRes() throw() +{ + // xxx might be more accurate to use a scaled AudioGetCurrentHostTime? + return juce_getMicroseconds() * 0.001; +} + +int64 Time::getHighResolutionTicks() throw() +{ + return (int64) AudioGetCurrentHostTime(); +} + +int64 Time::getHighResolutionTicksPerSecond() throw() +{ + return highResTimerFrequency; +} + +int64 SystemStats::getClockCycleCounter() throw() +{ + jassertfalse + return 0; +} + +bool Time::setSystemTimeToThisTime() const throw() +{ + jassertfalse + return false; +} + +int SystemStats::getPageSize() throw() +{ + jassertfalse + return 512; //xxx +} + +void PlatformUtilities::fpuReset() +{ +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_mac_SystemStats.cpp *********/ + +/********* Start of inlined file: juce_mac_Threads.cpp *********/ + +#include +#include +#include +#include +#include +#include + +BEGIN_JUCE_NAMESPACE + +/* + Note that a lot of methods that you'd expect to find in this file actually + live in juce_posix_SharedCode.cpp! +*/ + +void JUCE_API juce_threadEntryPoint (void*); + +void* threadEntryProc (void* userData) throw() +{ + juce_threadEntryPoint (userData); + return 0; +} + +void* juce_createThread (void* userData) throw() +{ + pthread_t handle = 0; + + if (pthread_create (&handle, 0, threadEntryProc, userData) == 0) + { + pthread_detach (handle); + return (void*) handle; + } + + return 0; +} + +void juce_killThread (void* handle) throw() +{ + if (handle != 0) + pthread_cancel ((pthread_t) handle); +} + +void juce_setCurrentThreadName (const String& /*name*/) throw() +{ +} + +int Thread::getCurrentThreadId() throw() +{ + return (int) pthread_self(); +} + +void juce_setThreadPriority (void* handle, int priority) throw() +{ + if (handle == 0) + handle = (void*) pthread_self(); + + struct sched_param param; + int policy; + pthread_getschedparam ((pthread_t) handle, &policy, ¶m); + param.sched_priority = jlimit (1, 127, 1 + (priority * 126) / 11); + pthread_setschedparam ((pthread_t) handle, policy, ¶m); +} + +void Thread::yield() throw() +{ + sched_yield(); +} + +void Thread::setCurrentThreadAffinityMask (const uint32 affinityMask) throw() +{ + // xxx + jassertfalse +} + +bool JUCE_CALLTYPE juce_isRunningUnderDebugger() throw() +{ + static char testResult = 0; + + if (testResult == 0) + { + struct kinfo_proc info; + int m[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid() }; + size_t sz = sizeof (info); + sysctl (m, 4, &info, &sz, 0, 0); + testResult = ((info.kp_proc.p_flag & P_TRACED) != 0) ? 1 : -1; + } + + return testResult > 0; +} + +bool JUCE_CALLTYPE Process::isRunningUnderDebugger() throw() +{ + return juce_isRunningUnderDebugger(); +} + +void Process::raisePrivilege() +{ + jassertfalse +} + +void Process::lowerPrivilege() +{ + jassertfalse +} + +void Process::terminate() +{ + ExitToShell(); +} + +void Process::setPriority (ProcessPriority p) +{ + // xxx +} + +void* Process::loadDynamicLibrary (const String& name) +{ + // xxx needs to use bundles + + FSSpec fs; + if (PlatformUtilities::makeFSSpecFromPath (&fs, name)) + { + CFragConnectionID connID; + Ptr mainPtr; + Str255 errorMessage; + Str63 nm; + PlatformUtilities::copyToStr63 (nm, name); + + const OSErr err = GetDiskFragment (&fs, 0, kCFragGoesToEOF, nm, kReferenceCFrag, &connID, &mainPtr, errorMessage); + if (err == noErr) + return (void*)connID; + } + + return 0; +} + +void Process::freeDynamicLibrary (void* handle) +{ + if (handle != 0) + CloseConnection ((CFragConnectionID*)&handle); +} + +void* Process::getProcedureEntryPoint (void* h, const String& procedureName) +{ + if (h != 0) + { + CFragSymbolClass cl; + Ptr ptr; + Str255 name; + PlatformUtilities::copyToStr255 (name, procedureName); + + if (FindSymbol ((CFragConnectionID) h, name, &ptr, &cl) == noErr) + { + return ptr; + } + } + + return 0; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_mac_Threads.cpp *********/ + +/********* Start of inlined file: juce_mac_Network.mm *********/ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +BEGIN_JUCE_NAMESPACE + +/********* Start of inlined file: juce_mac_HTTPStream.h *********/ +// (This file gets included by the mac + linux networking code) + +/** A HTTP input stream that uses sockets. +*/ +class JUCE_HTTPSocketStream +{ +public: + + JUCE_HTTPSocketStream() + : readPosition (0), + socketHandle (-1), + levelsOfRedirection (0), + timeoutSeconds (15) + { + } + + ~JUCE_HTTPSocketStream() + { + closeSocket(); + } + + bool open (const String& url, + const String& headers, + const MemoryBlock& postData, + const bool isPost, + URL::OpenStreamProgressCallback* callback, + void* callbackContext) + { + closeSocket(); + + String hostName, hostPath; + int hostPort; + + if (! decomposeURL (url, hostName, hostPath, hostPort)) + return false; + + struct hostent* const host + = gethostbyname ((const char*) hostName.toUTF8()); + + if (host == 0) + return false; + + struct sockaddr_in address; + zerostruct (address); + memcpy ((void*) &address.sin_addr, (const void*) host->h_addr, host->h_length); + address.sin_family = host->h_addrtype; + address.sin_port = htons (hostPort); + + socketHandle = socket (host->h_addrtype, SOCK_STREAM, 0); + + if (socketHandle == -1) + return false; + + int receiveBufferSize = 16384; + setsockopt (socketHandle, SOL_SOCKET, SO_RCVBUF, (char*) &receiveBufferSize, sizeof (receiveBufferSize)); + setsockopt (socketHandle, SOL_SOCKET, SO_KEEPALIVE, 0, 0); + +#if JUCE_MAC + setsockopt (socketHandle, SOL_SOCKET, SO_NOSIGPIPE, 0, 0); +#endif + + if (connect (socketHandle, (struct sockaddr*) &address, sizeof (address)) == -1) + { + closeSocket(); + return false; + } + + String proxyURL (getenv ("http_proxy")); + + if (! proxyURL.startsWithIgnoreCase (T("http://"))) + proxyURL = String::empty; + + const MemoryBlock requestHeader (createRequestHeader (hostName, hostPath, + proxyURL, url, + hostPort, + headers, postData, + isPost)); + + int totalHeaderSent = 0; + + while (totalHeaderSent < requestHeader.getSize()) + { + const int numToSend = jmin (1024, requestHeader.getSize() - totalHeaderSent); + + if (send (socketHandle, + ((const char*) requestHeader.getData()) + totalHeaderSent, + numToSend, 0) + != numToSend) + { + closeSocket(); + return false; + } + + totalHeaderSent += numToSend; + + if (callback != 0 && ! callback (callbackContext, totalHeaderSent, requestHeader.getSize())) + { + closeSocket(); + return false; + } + } + + const String responseHeader (readResponse()); + + if (responseHeader.isNotEmpty()) + { + //DBG (responseHeader); + + StringArray lines; + lines.addLines (responseHeader); + + // NB - using charToString() here instead of just T(" "), because that was + // causing a mysterious gcc internal compiler error... + const int statusCode = responseHeader.fromFirstOccurrenceOf (String::charToString (T(' ')), false, false) + .substring (0, 3) + .getIntValue(); + + //int contentLength = findHeaderItem (lines, T("Content-Length:")).getIntValue(); + //bool isChunked = findHeaderItem (lines, T("Transfer-Encoding:")).equalsIgnoreCase ("chunked"); + + String location (findHeaderItem (lines, T("Location:"))); + + if (statusCode >= 300 && statusCode < 400 + && location.isNotEmpty()) + { + if (! location.startsWithIgnoreCase (T("http://"))) + location = T("http://") + location; + + if (levelsOfRedirection++ < 3) + return open (location, headers, postData, isPost, callback, callbackContext); + } + else + { + levelsOfRedirection = 0; + return true; + } + } + + closeSocket(); + return false; + } + + int read (void* buffer, int bytesToRead) + { + fd_set readbits; + FD_ZERO (&readbits); + FD_SET (socketHandle, &readbits); + + struct timeval tv; + tv.tv_sec = timeoutSeconds; + tv.tv_usec = 0; + + if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0) + return 0; // (timeout) + + const int bytesRead = jmax (0, recv (socketHandle, buffer, bytesToRead, MSG_WAITALL)); + readPosition += bytesRead; + return bytesRead; + } + + int readPosition; + + juce_UseDebuggingNewOperator + +private: + int socketHandle, levelsOfRedirection; + const int timeoutSeconds; + + void closeSocket() + { + if (socketHandle >= 0) + close (socketHandle); + + socketHandle = -1; + } + + const MemoryBlock createRequestHeader (const String& hostName, + const String& hostPath, + const String& proxyURL, + const String& originalURL, + const int hostPort, + const String& headers, + const MemoryBlock& postData, + const bool isPost) + { + String header (isPost ? "POST " : "GET "); + + if (proxyURL.isEmpty()) + { + header << hostPath << " HTTP/1.0\r\nHost: " + << hostName << ':' << hostPort; + } + else + { + String proxyName, proxyPath; + int proxyPort; + + if (! decomposeURL (proxyURL, proxyName, proxyPath, proxyPort)) + return MemoryBlock(); + + header << originalURL << " HTTP/1.0\r\nHost: " + << proxyName << ':' << proxyPort; + + /* xxx needs finishing + const char* proxyAuth = getenv ("http_proxy_auth"); + if (proxyAuth != 0) + header << T("\r\nProxy-Authorization: ") << Base64Encode (proxyAuth); + */ + } + + header << "\r\nUser-Agent: JUCE/" + << JUCE_MAJOR_VERSION << '.' << JUCE_MINOR_VERSION + << "\r\nConnection: Close\r\nContent-Length: " + << postData.getSize() << "\r\n" + << headers << "\r\n"; + + MemoryBlock mb; + mb.append (header.toUTF8(), (int) strlen (header.toUTF8())); + mb.append (postData.getData(), postData.getSize()); + + return mb; + } + + const String readResponse() + { + int bytesRead = 0, numConsecutiveLFs = 0; + MemoryBlock buffer (1024, true); + + while (numConsecutiveLFs < 2 && bytesRead < 32768) + { + fd_set readbits; + FD_ZERO (&readbits); + FD_SET (socketHandle, &readbits); + + struct timeval tv; + tv.tv_sec = timeoutSeconds; + tv.tv_usec = 0; + + if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0) + return String::empty; // (timeout) + + buffer.ensureSize (bytesRead + 8, true); + char* const dest = (char*) buffer.getData() + bytesRead; + + if (recv (socketHandle, dest, 1, 0) == -1) + return String::empty; + + const char lastByte = *dest; + ++bytesRead; + + if (lastByte == '\n') + ++numConsecutiveLFs; + else if (lastByte != '\r') + numConsecutiveLFs = 0; + } + + const String header (String::fromUTF8 ((const uint8*) buffer.getData())); + + if (header.startsWithIgnoreCase (T("HTTP/"))) + return header.trimEnd(); + + return String::empty; + } + + static bool decomposeURL (const String& url, + String& host, String& path, int& port) + { + if (! url.startsWithIgnoreCase (T("http://"))) + return false; + + const int nextSlash = url.indexOfChar (7, '/'); + int nextColon = url.indexOfChar (7, ':'); + if (nextColon > nextSlash && nextSlash > 0) + nextColon = -1; + + if (nextColon >= 0) + { + host = url.substring (7, nextColon); + + if (nextSlash >= 0) + port = url.substring (nextColon + 1, nextSlash).getIntValue(); + else + port = url.substring (nextColon + 1).getIntValue(); + } + else + { + port = 80; + + if (nextSlash >= 0) + host = url.substring (7, nextSlash); + else + host = url.substring (7); + } + + if (nextSlash >= 0) + path = url.substring (nextSlash); + else + path = T("/"); + + return true; + } + + static const String findHeaderItem (const StringArray& lines, const String& itemName) + { + for (int i = 0; i < lines.size(); ++i) + if (lines[i].startsWithIgnoreCase (itemName)) + return lines[i].substring (itemName.length()).trim(); + + return String::empty; + } +}; + +bool juce_isOnLine() +{ + return true; +} + +void* juce_openInternetFile (const String& url, + const String& headers, + const MemoryBlock& postData, + const bool isPost, + URL::OpenStreamProgressCallback* callback, + void* callbackContext) +{ + JUCE_HTTPSocketStream* const s = new JUCE_HTTPSocketStream(); + + if (s->open (url, headers, postData, isPost, + callback, callbackContext)) + return s; + + delete s; + return 0; +} + +void juce_closeInternetFile (void* handle) +{ + JUCE_HTTPSocketStream* const s = (JUCE_HTTPSocketStream*) handle; + + if (s != 0) + delete s; +} + +int juce_readFromInternetFile (void* handle, void* buffer, int bytesToRead) +{ + JUCE_HTTPSocketStream* const s = (JUCE_HTTPSocketStream*) handle; + + if (s != 0) + return s->read (buffer, bytesToRead); + + return 0; +} + +int juce_seekInInternetFile (void* handle, int newPosition) +{ + JUCE_HTTPSocketStream* const s = (JUCE_HTTPSocketStream*) handle; + + if (s != 0) + return s->readPosition; + + return 0; +} + +/********* End of inlined file: juce_mac_HTTPStream.h *********/ + +static bool GetEthernetIterator (io_iterator_t* matchingServices) throw() +{ + mach_port_t masterPort; + + if (IOMasterPort (MACH_PORT_NULL, &masterPort) == KERN_SUCCESS) + { + CFMutableDictionaryRef dict = IOServiceMatching (kIOEthernetInterfaceClass); + + if (dict != 0) + { + CFMutableDictionaryRef propDict = CFDictionaryCreateMutable (kCFAllocatorDefault, + 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + if (propDict != 0) + { + CFDictionarySetValue (propDict, CFSTR (kIOPrimaryInterface), kCFBooleanTrue); + + CFDictionarySetValue (dict, CFSTR (kIOPropertyMatchKey), propDict); + CFRelease (propDict); + } + } + + return IOServiceGetMatchingServices (masterPort, dict, matchingServices) == KERN_SUCCESS; + } + + return false; +} + +int SystemStats::getMACAddresses (int64* addresses, int maxNum, const bool littleEndian) throw() +{ + int numResults = 0; + io_iterator_t it; + + if (GetEthernetIterator (&it)) + { + io_object_t i; + + while ((i = IOIteratorNext (it)) != 0) + { + io_object_t controller; + + if (IORegistryEntryGetParentEntry (i, kIOServicePlane, &controller) == KERN_SUCCESS) + { + CFTypeRef data = IORegistryEntryCreateCFProperty (controller, + CFSTR (kIOMACAddress), + kCFAllocatorDefault, + 0); + if (data != 0) + { + UInt8 addr [kIOEthernetAddressSize]; + zeromem (addr, sizeof (addr)); + + CFDataGetBytes ((CFDataRef) data, CFRangeMake (0, sizeof (addr)), addr); + CFRelease (data); + + int64 a = 0; + for (int i = 6; --i >= 0;) + a = (a << 8) | addr[i]; + + if (! littleEndian) + a = (int64) swapByteOrder ((uint64) a); + + if (numResults < maxNum) + { + *addresses++ = a; + ++numResults; + } + } + + IOObjectRelease (controller); + } + + IOObjectRelease (i); + } + + IOObjectRelease (it); + } + + return numResults; +} + +class AutoPool +{ +public: + AutoPool() { pool = [[NSAutoreleasePool alloc] init]; } + ~AutoPool() { [pool release]; } + +private: + NSAutoreleasePool* pool; +}; + +bool PlatformUtilities::launchEmailWithAttachments (const String& targetEmailAddress, + const String& emailSubject, + const String& bodyText, + const StringArray& filesToAttach) +{ + const AutoPool pool; + + String script; + script << "tell application \"Mail\"\r\n" + "set newMessage to make new outgoing message with properties {subject:\"" + << emailSubject.replace (T("\""), T("\\\"")) + << "\", content:\"" + << bodyText.replace (T("\""), T("\\\"")) + << "\" & return & return}\r\n" + "tell newMessage\r\n" + "set visible to true\r\n" + "set sender to \"sdfsdfsdfewf\"\r\n" + "make new to recipient at end of to recipients with properties {address:\"" + << targetEmailAddress + << "\"}\r\n"; + + for (int i = 0; i < filesToAttach.size(); ++i) + { + script << "tell content\r\n" + "make new attachment with properties {file name:\"" + << filesToAttach[i].replace (T("\""), T("\\\"")) + << "\"} at after the last paragraph\r\n" + "end tell\r\n"; + } + + script << "end tell\r\n" + "end tell\r\n"; + + NSAppleScript* s = [[NSAppleScript alloc] + initWithSource: [NSString stringWithUTF8String: (const char*) script.toUTF8()]]; + NSDictionary* error = 0; + const bool ok = [s executeAndReturnError: &error] != nil; + [s release]; + + return ok; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_mac_Network.mm *********/ + +#if ! JUCE_ONLY_BUILD_CORE_LIBRARY + +/********* Start of inlined file: juce_mac_AudioCDBurner.mm *********/ + +#if JUCE_USE_CDBURNER + +#import +#import + +BEGIN_JUCE_NAMESPACE + +END_JUCE_NAMESPACE + +@interface OpenDiskDevice : NSObject +{ + DRDevice* device; + + NSMutableArray* tracks; +} + +- (OpenDiskDevice*) initWithDevice: (DRDevice*) device; +- (void) dealloc; +- (bool) isDiskPresent; +- (int) getNumAvailableAudioBlocks; +- (void) addSourceTrack: (juce::AudioSource*) source numSamples: (int) numSamples_; +- (void) burn: (juce::AudioCDBurner::BurnProgressListener*) listener errorString: (juce::String*) error + ejectAfterwards: (bool) shouldEject isFake: (bool) peformFakeBurnForTesting; +@end + +@interface AudioTrackProducer : NSObject +{ + juce::AudioSource* source; + int readPosition, lengthInFrames; +} + +- (AudioTrackProducer*) init: (int) lengthInFrames; +- (AudioTrackProducer*) initWithAudioSource: (juce::AudioSource*) source numSamples: (int) lengthInSamples; +- (void) dealloc; +- (void) setupTrackProperties: (DRTrack*) track; + +- (void) cleanupTrackAfterBurn: (DRTrack*) track; +- (BOOL) cleanupTrackAfterVerification:(DRTrack*)track; +- (uint64_t) estimateLengthOfTrack:(DRTrack*)track; +- (BOOL) prepareTrack:(DRTrack*)track forBurn:(DRBurn*)burn + toMedia:(NSDictionary*)mediaInfo; +- (BOOL) prepareTrackForVerification:(DRTrack*)track; +- (uint32_t) produceDataForTrack:(DRTrack*)track intoBuffer:(char*)buffer + length:(uint32_t)bufferLength atAddress:(uint64_t)address + blockSize:(uint32_t)blockSize ioFlags:(uint32_t*)flags; +- (uint32_t) producePreGapForTrack:(DRTrack*)track + intoBuffer:(char*)buffer length:(uint32_t)bufferLength + atAddress:(uint64_t)address blockSize:(uint32_t)blockSize + ioFlags:(uint32_t*)flags; +- (BOOL) verifyDataForTrack:(DRTrack*)track inBuffer:(const char*)buffer + length:(uint32_t)bufferLength atAddress:(uint64_t)address + blockSize:(uint32_t)blockSize ioFlags:(uint32_t*)flags; +- (uint32_t) producePreGapForTrack:(DRTrack*)track + intoBuffer:(char*)buffer length:(uint32_t)bufferLength + atAddress:(uint64_t)address blockSize:(uint32_t)blockSize + ioFlags:(uint32_t*)flags; +@end + +@implementation OpenDiskDevice + +- (OpenDiskDevice*) initWithDevice: (DRDevice*) device_ +{ + [super init]; + + device = device_; + tracks = [[NSMutableArray alloc] init]; + return self; +} + +- (void) dealloc +{ + [tracks release]; + [super dealloc]; +} + +- (bool) isDiskPresent +{ + NSLog ([[device status] description]); + return [device isValid]; +} + +- (int) getNumAvailableAudioBlocks +{ + NSLog ([[device status] description]); + return [[[[device status] objectForKey: DRDeviceMediaInfoKey] + objectForKey: DRDeviceMediaBlocksFreeKey] intValue]; +} + +- (void) addSourceTrack: (juce::AudioSource*) source_ numSamples: (int) numSamples_ +{ + AudioTrackProducer* p = [[AudioTrackProducer alloc] initWithAudioSource: source_ numSamples: numSamples_]; + DRTrack* t = [[DRTrack alloc] initWithProducer: p]; + [p setupTrackProperties: t]; + + [tracks addObject: t]; + + [t release]; + [p release]; +} + +- (void) burn: (juce::AudioCDBurner::BurnProgressListener*) listener errorString: (juce::String*) error + ejectAfterwards: (bool) shouldEject isFake: (bool) peformFakeBurnForTesting +{ + DRBurn* burn = [DRBurn burnForDevice: device]; + + if (! [device acquireExclusiveAccess]) + { + *error = "Couldn't open or write to the CD device"; + return; + } + + [device acquireMediaReservation]; + + NSMutableDictionary* d = [[burn properties] mutableCopy]; + [d setObject: [NSNumber numberWithBool: peformFakeBurnForTesting] forKey: DRBurnTestingKey]; + [d setObject: [NSNumber numberWithBool: false] forKey: DRBurnVerifyDiscKey]; + [burn setProperties: d]; + [d release]; + + [burn writeLayout: tracks]; + + for (;;) + { + juce::Thread::sleep (300); + float progress = [[[burn status] objectForKey: DRStatusPercentCompleteKey] floatValue]; + + if (listener != 0 && listener->audioCDBurnProgress (progress)) + { + [burn abort]; + *error = "User cancelled the write operation"; + break; + } + + if ([[[burn status] objectForKey: DRStatusStateKey] isEqualTo: DRStatusStateFailed]) + { + *error = "Write operation failed"; + break; + } + else if ([[[burn status] objectForKey: DRStatusStateKey] isEqualTo: DRStatusStateDone]) + { + if (shouldEject) + [device openTray]; + + break; + } + NSString* err = (NSString*) [[[burn status] objectForKey: DRErrorStatusKey] + objectForKey: DRErrorStatusErrorStringKey]; + + if ([err length] > 0) + { + *error = juce::String::fromUTF8 ((juce::uint8*) [err UTF8String]); + break; + } + } + + [device releaseMediaReservation]; + [device releaseExclusiveAccess]; +} +@end + +@implementation AudioTrackProducer + +- (AudioTrackProducer*) init: (int) lengthInFrames_ +{ + lengthInFrames = lengthInFrames_; + readPosition = 0; + return self; +} + +- (void) setupTrackProperties: (DRTrack*) track +{ + NSMutableDictionary* p = [[track properties] mutableCopy]; + [p setObject: [DRMSF msfWithFrames: lengthInFrames] forKey: DRTrackLengthKey]; + [p setObject: [NSNumber numberWithUnsignedShort: 2048] forKey: DRBlockSizeKey]; + [p setObject: [NSNumber numberWithInt: 16] forKey: DRDataFormKey]; + [p setObject: [NSNumber numberWithInt: 8] forKey: DRBlockTypeKey]; + [p setObject: [NSNumber numberWithInt: 4] forKey: DRTrackModeKey]; + [p setObject: [NSNumber numberWithInt: 0] forKey: DRSessionFormatKey]; + [track setProperties: p]; + [p release]; +} + +- (AudioTrackProducer*) initWithAudioSource: (juce::AudioSource*) source_ numSamples: (int) lengthInSamples +{ + AudioTrackProducer* s = [self init: lengthInSamples / (44100 / 75)]; + + if (s != nil) + s->source = source_; + + return s; +} + +- (void) dealloc +{ + if (source != 0) + { + source->releaseResources(); + delete source; + } + + [super dealloc]; +} + +- (void) cleanupTrackAfterBurn: (DRTrack*) track +{ +} + +- (BOOL) cleanupTrackAfterVerification:(DRTrack*)track +{ + return true; +} + +- (uint64_t) estimateLengthOfTrack:(DRTrack*)track +{ + return lengthInFrames; +} + +- (BOOL) prepareTrack:(DRTrack*)track forBurn:(DRBurn*)burn + toMedia:(NSDictionary*)mediaInfo +{ + if (source != 0) + source->prepareToPlay (44100 / 75, 44100); + + readPosition = 0; + return true; +} + +- (BOOL) prepareTrackForVerification:(DRTrack*)track +{ + if (source != 0) + source->prepareToPlay (44100 / 75, 44100); + + return true; +} + +- (uint32_t) produceDataForTrack:(DRTrack*)track intoBuffer:(char*)buffer + length:(uint32_t)bufferLength atAddress:(uint64_t)address + blockSize:(uint32_t)blockSize ioFlags:(uint32_t*)flags +{ + if (source != 0) + { + const int numSamples = juce::jmin (bufferLength / 4, (lengthInFrames * (44100 / 75)) - readPosition); + + if (numSamples > 0) + { + juce::AudioSampleBuffer tempBuffer (2, numSamples); + + juce::AudioSourceChannelInfo info; + info.buffer = &tempBuffer; + info.startSample = 0; + info.numSamples = numSamples; + + source->getNextAudioBlock (info); + + juce::AudioDataConverters::convertFloatToInt16LE (tempBuffer.getSampleData (0), + buffer, numSamples, 4); + juce::AudioDataConverters::convertFloatToInt16LE (tempBuffer.getSampleData (1), + buffer + 2, numSamples, 4); + + readPosition += numSamples; + } + + return numSamples * 4; + } + + return 0; +} + +- (uint32_t) producePreGapForTrack:(DRTrack*)track + intoBuffer:(char*)buffer length:(uint32_t)bufferLength + atAddress:(uint64_t)address blockSize:(uint32_t)blockSize + ioFlags:(uint32_t*)flags +{ + zeromem (buffer, bufferLength); + return bufferLength; +} + +- (BOOL) verifyDataForTrack:(DRTrack*)track inBuffer:(const char*)buffer + length:(uint32_t)bufferLength atAddress:(uint64_t)address + blockSize:(uint32_t)blockSize ioFlags:(uint32_t*)flags +{ + return true; +} + +@end + +BEGIN_JUCE_NAMESPACE + +AudioCDBurner::AudioCDBurner (const int deviceIndex) + : internal (0) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + OpenDiskDevice* dev = [[OpenDiskDevice alloc] initWithDevice: [[DRDevice devices] objectAtIndex: deviceIndex]]; + + internal = (void*) dev; + [pool release]; +} + +AudioCDBurner::~AudioCDBurner() +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + OpenDiskDevice* dev = (OpenDiskDevice*) internal; + + if (dev != 0) + [dev release]; + + [pool release]; +} + +AudioCDBurner* AudioCDBurner::openDevice (const int deviceIndex) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + AudioCDBurner* b = new AudioCDBurner (deviceIndex); + + if (b->internal == 0) + deleteAndZero (b); + + [pool release]; + return b; +} + +static NSArray* findDiskBurnerDevices() +{ + NSMutableArray* results = [NSMutableArray array]; + NSArray* devs = [DRDevice devices]; + + if (devs != 0) + { + int num = [devs count]; + int i; + for (i = 0; i < num; ++i) + { + NSDictionary* dic = [[devs objectAtIndex: i] info]; + NSString* name = [dic valueForKey: DRDeviceProductNameKey]; + if (name != nil) + [results addObject: name]; + } + } + + return results; +} + +const StringArray AudioCDBurner::findAvailableDevices() +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSArray* names = findDiskBurnerDevices(); + StringArray s; + + for (int i = 0; i < [names count]; ++i) + s.add (String::fromUTF8 ((juce::uint8*) [[names objectAtIndex: i] UTF8String])); + + [pool release]; + return s; +} + +bool AudioCDBurner::isDiskPresent() const +{ + OpenDiskDevice* dev = (OpenDiskDevice*) internal; + + return dev != 0 && [dev isDiskPresent]; +} + +int AudioCDBurner::getNumAvailableAudioBlocks() const +{ + OpenDiskDevice* dev = (OpenDiskDevice*) internal; + + return [dev getNumAvailableAudioBlocks]; +} + +bool AudioCDBurner::addAudioTrack (AudioSource* source, int numSamps) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + OpenDiskDevice* dev = (OpenDiskDevice*) internal; + + if (dev != 0) + { + [dev addSourceTrack: source numSamples: numSamps]; + [pool release]; + return true; + } + + [pool release]; + return false; +} + +const String AudioCDBurner::burn (juce::AudioCDBurner::BurnProgressListener* listener, + const bool peformFakeBurnForTesting, + const bool ejectDiscAfterwards) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + juce::String error ("Couldn't open or write to the CD device"); + + OpenDiskDevice* dev = (OpenDiskDevice*) internal; + + if (dev != 0) + { + error = juce::String::empty; + [dev burn: listener + errorString: &error + ejectAfterwards: ejectDiscAfterwards + isFake: peformFakeBurnForTesting]; + } + + [pool release]; + return error; +} + +END_JUCE_NAMESPACE + +#endif +/********* End of inlined file: juce_mac_AudioCDBurner.mm *********/ + +/********* Start of inlined file: juce_mac_CoreAudio.cpp *********/ + +#include + +BEGIN_JUCE_NAMESPACE + +#ifndef JUCE_COREAUDIO_ERROR_LOGGING_ENABLED + #define JUCE_COREAUDIO_ERROR_LOGGING_ENABLED 1 +#endif + +#undef log +#if JUCE_COREAUDIO_LOGGING_ENABLED + #define log(a) Logger::writeToLog (a) +#else + #define log(a) +#endif + +#undef OK +#if JUCE_COREAUDIO_ERROR_LOGGING_ENABLED + static bool logAnyErrors_CoreAudio (const OSStatus err, const int lineNum) + { + if (err == noErr) + return true; + + Logger::writeToLog (T("CoreAudio error: ") + String (lineNum) + T(" - ") + String::toHexString ((int)err)); + jassertfalse + return false; + } + + #define OK(a) logAnyErrors_CoreAudio (a, __LINE__) +#else + #define OK(a) (a == noErr) +#endif + +static const int maxNumChans = 96; + +class CoreAudioInternal : public Timer +{ +public: + + CoreAudioInternal (AudioDeviceID id) + : deviceID (id), + started (false), + audioBuffer (0), + numInputChans (0), + numOutputChans (0), + callbacksAllowed (true), + numInputChannelInfos (0), + numOutputChannelInfos (0), + inputLatency (0), + outputLatency (0), + callback (0), + inputDevice (0), + isSlaveDevice (false) + { + sampleRate = 0; + bufferSize = 512; + + if (deviceID == 0) + { + error = TRANS("can't open device"); + } + else + { + updateDetailsFromDevice(); + + AudioDeviceAddPropertyListener (deviceID, + kAudioPropertyWildcardChannel, + kAudioPropertyWildcardSection, + kAudioPropertyWildcardPropertyID, + deviceListenerProc, this); + } + } + + ~CoreAudioInternal() + { + AudioDeviceRemovePropertyListener (deviceID, + kAudioPropertyWildcardChannel, + kAudioPropertyWildcardSection, + kAudioPropertyWildcardPropertyID, + deviceListenerProc); + + stop (false); + + juce_free (audioBuffer); + delete inputDevice; + } + + void setTempBufferSize (const int numChannels, const int numSamples) + { + juce_free (audioBuffer); + + audioBuffer = (float*) juce_calloc (32 + numChannels * numSamples * sizeof (float)); + + zeromem (tempInputBuffers, sizeof (tempInputBuffers)); + zeromem (tempOutputBuffers, sizeof (tempOutputBuffers)); + + int count = 0; + int i; + for (i = maxNumChans; --i >= 0;) + if (activeInputChans[i]) + tempInputBuffers[i] = audioBuffer + count++ * numSamples; + + for (i = maxNumChans; --i >= 0;) + if (activeOutputChans[i]) + tempOutputBuffers[i] = audioBuffer + count++ * numSamples; + } + + // returns the number of actual available channels + void fillInChannelInfo (bool input) + { + int chanNum = 0, activeChans = 0; + UInt32 size; + + if (OK (AudioDeviceGetPropertyInfo (deviceID, 0, input, kAudioDevicePropertyStreamConfiguration, &size, 0))) + { + AudioBufferList* const bufList = (AudioBufferList*) juce_calloc (size); + + if (OK (AudioDeviceGetProperty (deviceID, 0, input, kAudioDevicePropertyStreamConfiguration, &size, bufList))) + { + const int numStreams = bufList->mNumberBuffers; + + for (int i = 0; i < numStreams; ++i) + { + const AudioBuffer& b = bufList->mBuffers[i]; + + for (unsigned int j = 0; j < b.mNumberChannels; ++j) + { + if (input) + { + if (activeInputChans[chanNum]) + { + inputChannelInfo [activeChans].sourceChannelNum = chanNum; + inputChannelInfo [activeChans].streamNum = i; + inputChannelInfo [activeChans].dataOffsetSamples = j; + inputChannelInfo [activeChans].dataStrideSamples = b.mNumberChannels; + ++activeChans; + numInputChannelInfos = activeChans; + } + + inChanNames.add (T("input ") + String (chanNum + 1)); + } + else + { + if (activeOutputChans[chanNum]) + { + outputChannelInfo [activeChans].sourceChannelNum = chanNum; + outputChannelInfo [activeChans].streamNum = i; + outputChannelInfo [activeChans].dataOffsetSamples = j; + outputChannelInfo [activeChans].dataStrideSamples = b.mNumberChannels; + ++activeChans; + numOutputChannelInfos = activeChans; + } + + outChanNames.add (T("output ") + String (chanNum + 1)); + } + + ++chanNum; + } + } + } + + juce_free (bufList); + } + } + + void updateDetailsFromDevice() + { + stopTimer(); + + if (deviceID == 0) + return; + + const ScopedLock sl (callbackLock); + + Float64 sr; + UInt32 size = sizeof (Float64); + if (OK (AudioDeviceGetProperty (deviceID, 0, false, kAudioDevicePropertyNominalSampleRate, &size, &sr))) + sampleRate = sr; + + UInt32 framesPerBuf; + size = sizeof (framesPerBuf); + + if (OK (AudioDeviceGetProperty (deviceID, 0, false, kAudioDevicePropertyBufferFrameSize, &size, &framesPerBuf))) + { + bufferSize = framesPerBuf; + + if (bufferSize > 0) + setTempBufferSize (numInputChans + numOutputChans, bufferSize); + } + + bufferSizes.clear(); + + if (OK (AudioDeviceGetPropertyInfo (deviceID, 0, false, kAudioDevicePropertyBufferFrameSizeRange, &size, 0))) + { + AudioValueRange* ranges = (AudioValueRange*) juce_calloc (size); + + if (OK (AudioDeviceGetProperty (deviceID, 0, false, kAudioDevicePropertyBufferFrameSizeRange, &size, ranges))) + { + bufferSizes.add ((int) ranges[0].mMinimum); + + for (int i = 32; i < 8192; i += 32) + { + for (int j = size / sizeof (AudioValueRange); --j >= 0;) + { + if (i >= ranges[j].mMinimum && i <= ranges[j].mMaximum) + { + bufferSizes.addIfNotAlreadyThere (i); + break; + } + } + } + + if (bufferSize > 0) + bufferSizes.addIfNotAlreadyThere (bufferSize); + } + + juce_free (ranges); + } + + if (bufferSizes.size() == 0 && bufferSize > 0) + bufferSizes.add (bufferSize); + + sampleRates.clear(); + const double possibleRates[] = { 44100.0, 48000.0, 88200.0, 96000.0, 176400.0, 192000.0 }; + String rates; + + if (OK (AudioDeviceGetPropertyInfo (deviceID, 0, false, kAudioDevicePropertyAvailableNominalSampleRates, &size, 0))) + { + AudioValueRange* ranges = (AudioValueRange*) juce_calloc (size); + + if (OK (AudioDeviceGetProperty (deviceID, 0, false, kAudioDevicePropertyAvailableNominalSampleRates, &size, ranges))) + { + for (int i = 0; i < numElementsInArray (possibleRates); ++i) + { + bool ok = false; + + for (int j = size / sizeof (AudioValueRange); --j >= 0;) + if (possibleRates[i] >= ranges[j].mMinimum - 2 && possibleRates[i] <= ranges[j].mMaximum + 2) + ok = true; + + if (ok) + { + sampleRates.add (possibleRates[i]); + rates << possibleRates[i] << T(" "); + } + } + } + + juce_free (ranges); + } + + if (sampleRates.size() == 0 && sampleRate > 0) + { + sampleRates.add (sampleRate); + rates << sampleRate; + } + + log (T("sr: ") + rates); + + inputLatency = 0; + outputLatency = 0; + UInt32 lat; + size = sizeof (UInt32); + if (AudioDeviceGetProperty (deviceID, 0, true, kAudioDevicePropertyLatency, &size, &lat) == noErr) + inputLatency = (int) lat; + + if (AudioDeviceGetProperty (deviceID, 0, false, kAudioDevicePropertyLatency, &size, &lat) == noErr) + outputLatency = (int) lat; + + log (T("lat: ") + String (inputLatency) + T(" ") + String (outputLatency)); + + inChanNames.clear(); + outChanNames.clear(); + + zeromem (inputChannelInfo, sizeof (inputChannelInfo)); + zeromem (outputChannelInfo, sizeof (outputChannelInfo)); + + fillInChannelInfo (true); + fillInChannelInfo (false); + } + + const StringArray getSources (bool input) + { + StringArray s; + int num = 0; + OSType* types = getAllDataSourcesForDevice (deviceID, input, num); + + if (types != 0) + { + for (int i = 0; i < num; ++i) + { + AudioValueTranslation avt; + char buffer[256]; + + avt.mInputData = (void*) &(types[i]); + avt.mInputDataSize = sizeof (UInt32); + avt.mOutputData = buffer; + avt.mOutputDataSize = 256; + + UInt32 transSize = sizeof (avt); + if (OK (AudioDeviceGetProperty (deviceID, 0, input, kAudioDevicePropertyDataSourceNameForID, &transSize, &avt))) + { + DBG (buffer); + s.add (buffer); + } + } + + juce_free (types); + } + + return s; + } + + int getCurrentSourceIndex (bool input) const + { + OSType currentSourceID = 0; + UInt32 size = 0; + int result = -1; + + if (deviceID != 0 + && OK (AudioDeviceGetPropertyInfo (deviceID, 0, input, kAudioDevicePropertyDataSource, &size, 0))) + { + if (OK (AudioDeviceGetProperty (deviceID, 0, input, kAudioDevicePropertyDataSource, &size, ¤tSourceID))) + { + int num = 0; + OSType* const types = getAllDataSourcesForDevice (deviceID, input, num); + + if (types != 0) + { + for (int i = 0; i < num; ++i) + { + if (types[num] == currentSourceID) + { + result = i; + break; + } + } + + juce_free (types); + } + } + } + + return result; + } + + void setCurrentSourceIndex (int index, bool input) + { + if (deviceID != 0) + { + int num = 0; + OSType* types = getAllDataSourcesForDevice (deviceID, input, num); + + if (types != 0) + { + if (((unsigned int) index) < num) + { + OSType typeId = types[index]; + AudioDeviceSetProperty (deviceID, 0, 0, input, kAudioDevicePropertyDataSource, sizeof (typeId), &typeId); + } + + juce_free (types); + } + } + } + + const String reopen (const BitArray& inputChannels, + const BitArray& outputChannels, + double newSampleRate, + int bufferSizeSamples) + { + error = String::empty; + log ("CoreAudio reopen"); + callbacksAllowed = false; + stopTimer(); + + stop (false); + + activeInputChans = inputChannels; + activeOutputChans = outputChannels; + numInputChans = inputChannels.countNumberOfSetBits(); + numOutputChans = outputChannels.countNumberOfSetBits(); + + // set sample rate + Float64 sr = newSampleRate; + UInt32 size = sizeof (sr); + OK (AudioDeviceSetProperty (deviceID, 0, 0, false, kAudioDevicePropertyNominalSampleRate, size, &sr)); + OK (AudioDeviceSetProperty (deviceID, 0, 0, true, kAudioDevicePropertyNominalSampleRate, size, &sr)); + + // change buffer size + UInt32 framesPerBuf = bufferSizeSamples; + size = sizeof (framesPerBuf); + + OK (AudioDeviceSetProperty (deviceID, 0, 0, false, kAudioDevicePropertyBufferFrameSize, size, &framesPerBuf)); + OK (AudioDeviceSetProperty (deviceID, 0, 0, true, kAudioDevicePropertyBufferFrameSize, size, &framesPerBuf)); + + // wait for the changes to happen (on some devices) + int i = 30; + while (--i >= 0) + { + updateDetailsFromDevice(); + + if (sampleRate == newSampleRate && bufferSizeSamples == bufferSize) + break; + + Thread::sleep (100); + } + + if (i < 0) + error = "Couldn't change sample rate/buffer size"; + + if (sampleRates.size() == 0) + error = "Device has no available sample-rates"; + + if (bufferSizes.size() == 0) + error = "Device has no available buffer-sizes"; + + numInputChans = jmin (numInputChans, numInputChannelInfos); + numOutputChans = jmin (numOutputChans, numOutputChannelInfos); + + activeInputChans.setRange (inChanNames.size(), + activeInputChans.getHighestBit() + 1 - inChanNames.size(), + false); + + activeOutputChans.setRange (outChanNames.size(), + activeOutputChans.getHighestBit() + 1 - outChanNames.size(), + false); + + if (inputDevice != 0 && error.isEmpty()) + error = inputDevice->reopen (inputChannels, + outputChannels, + newSampleRate, + bufferSizeSamples); + + callbacksAllowed = true; + + return error; + } + + bool start (AudioIODeviceCallback* cb) + { + if (! started) + { + callback = 0; + + if (deviceID != 0) + { + if (OK (AudioDeviceAddIOProc (deviceID, audioIOProc, (void*) this))) + { + if (OK (AudioDeviceStart (deviceID, audioIOProc))) + { + started = true; + } + else + { + OK (AudioDeviceRemoveIOProc (deviceID, audioIOProc)); + } + } + } + } + + if (started) + { + const ScopedLock sl (callbackLock); + callback = cb; + } + + if (inputDevice != 0) + return started && inputDevice->start (cb); + else + return started; + } + + void stop (bool leaveInterruptRunning) + { + callbackLock.enter(); + callback = 0; + callbackLock.exit(); + + if (started + && (deviceID != 0) + && ! leaveInterruptRunning) + { + OK (AudioDeviceStop (deviceID, audioIOProc)); + OK (AudioDeviceRemoveIOProc (deviceID, audioIOProc)); + started = false; + + callbackLock.enter(); + callbackLock.exit(); + + // wait until it's definately stopped calling back.. + for (int i = 40; --i >= 0;) + { + Thread::sleep (50); + + UInt32 running = 0; + UInt32 size = sizeof (running); + OK (AudioDeviceGetProperty (deviceID, 0, false, kAudioDevicePropertyDeviceIsRunning, &size, &running)); + if (running == 0) + break; + } + + callbackLock.enter(); + callbackLock.exit(); + } + + if (inputDevice != 0) + inputDevice->stop (leaveInterruptRunning); + } + + double getSampleRate() const + { + return sampleRate; + } + + int getBufferSize() const + { + return bufferSize; + } + + void audioCallback (const AudioBufferList* inInputData, + AudioBufferList* outOutputData) + { + int i; + const ScopedLock sl (callbackLock); + + if (callback != 0) + { + if (inputDevice == 0) + { + for (i = numInputChans; --i >= 0;) + { + const CallbackDetailsForChannel& info = inputChannelInfo[i]; + float* dest = tempInputBuffers [info.sourceChannelNum]; + const float* src = ((const float*) inInputData->mBuffers[info.streamNum].mData) + + info.dataOffsetSamples; + const int stride = info.dataStrideSamples; + + if (stride != 0) // if this is zero, info is invalid + { + for (int j = bufferSize; --j >= 0;) + { + *dest++ = *src; + src += stride; + } + } + } + } + + if (! isSlaveDevice) + { + if (inputDevice == 0) + { + callback->audioDeviceIOCallback ((const float**) tempInputBuffers, + numInputChans, + tempOutputBuffers, + numOutputChans, + bufferSize); + } + else + { + jassert (inputDevice->bufferSize == bufferSize); + + callback->audioDeviceIOCallback ((const float**) inputDevice->tempInputBuffers, + inputDevice->numInputChans, + tempOutputBuffers, + numOutputChans, + bufferSize); + } + + for (i = numOutputChans; --i >= 0;) + { + const CallbackDetailsForChannel& info = outputChannelInfo[i]; + const float* src = tempOutputBuffers [info.sourceChannelNum]; + float* dest = ((float*) outOutputData->mBuffers[info.streamNum].mData) + + info.dataOffsetSamples; + const int stride = info.dataStrideSamples; + + if (stride != 0) // if this is zero, info is invalid + { + for (int j = bufferSize; --j >= 0;) + { + *dest = *src++; + dest += stride; + } + } + } + } + } + else + { + for (i = jmin (numOutputChans, numOutputChannelInfos); --i >= 0;) + { + const CallbackDetailsForChannel& info = outputChannelInfo[i]; + float* dest = ((float*) outOutputData->mBuffers[info.streamNum].mData) + + info.dataOffsetSamples; + const int stride = info.dataStrideSamples; + + if (stride != 0) // if this is zero, info is invalid + { + for (int j = bufferSize; --j >= 0;) + { + *dest = 0.0f; + dest += stride; + } + } + } + } + } + + // called by callbacks + void deviceDetailsChanged() + { + if (callbacksAllowed) + startTimer (100); + } + + void timerCallback() + { + stopTimer(); + log ("CoreAudio device changed callback"); + + const double oldSampleRate = sampleRate; + const int oldBufferSize = bufferSize; + updateDetailsFromDevice(); + + if (oldBufferSize != bufferSize || oldSampleRate != sampleRate) + { + callbacksAllowed = false; + stop (false); + updateDetailsFromDevice(); + callbacksAllowed = true; + } + } + + CoreAudioInternal* getRelatedDevice() const + { + UInt32 size = 0; + CoreAudioInternal* result = 0; + + if (deviceID != 0 + && AudioDeviceGetPropertyInfo (deviceID, 0, false, kAudioDevicePropertyRelatedDevices, &size, 0) == noErr + && size > 0) + { + AudioDeviceID* devs = (AudioDeviceID*) juce_calloc (size); + + if (OK (AudioDeviceGetProperty (deviceID, 0, false, kAudioDevicePropertyRelatedDevices, &size, devs))) + { + for (unsigned int i = 0; i < size / sizeof (AudioDeviceID); ++i) + { + if (devs[i] != deviceID && devs[i] != 0) + { + result = new CoreAudioInternal (devs[i]); + + if (result->error.isEmpty()) + { + const bool thisIsInput = inChanNames.size() > 0 && outChanNames.size() == 0; + const bool otherIsInput = result->inChanNames.size() > 0 && result->outChanNames.size() == 0; + + if (thisIsInput != otherIsInput) + break; + } + + deleteAndZero (result); + } + } + } + + juce_free (devs); + } + + return result; + } + + juce_UseDebuggingNewOperator + + String error; + int inputLatency, outputLatency; + BitArray activeInputChans, activeOutputChans; + StringArray inChanNames, outChanNames; + Array sampleRates; + Array bufferSizes; + AudioIODeviceCallback* callback; + + CoreAudioInternal* inputDevice; + bool isSlaveDevice; + +private: + CriticalSection callbackLock; + AudioDeviceID deviceID; + bool started; + double sampleRate; + int bufferSize; + float* audioBuffer; + int numInputChans, numOutputChans; + bool callbacksAllowed; + + struct CallbackDetailsForChannel + { + int sourceChannelNum; + int streamNum; + int dataOffsetSamples; + int dataStrideSamples; + }; + + int numInputChannelInfos, numOutputChannelInfos; + CallbackDetailsForChannel inputChannelInfo [maxNumChans]; + CallbackDetailsForChannel outputChannelInfo [maxNumChans]; + float* tempInputBuffers [maxNumChans]; + float* tempOutputBuffers [maxNumChans]; + + CoreAudioInternal (const CoreAudioInternal&); + const CoreAudioInternal& operator= (const CoreAudioInternal&); + + static OSStatus audioIOProc (AudioDeviceID inDevice, + const AudioTimeStamp* inNow, + const AudioBufferList* inInputData, + const AudioTimeStamp* inInputTime, + AudioBufferList* outOutputData, + const AudioTimeStamp* inOutputTime, + void* device) + { + ((CoreAudioInternal*) device)->audioCallback (inInputData, outOutputData); + return noErr; + } + + static OSStatus deviceListenerProc (AudioDeviceID inDevice, + UInt32 inLine, + Boolean isInput, + AudioDevicePropertyID inPropertyID, + void* inClientData) + { + CoreAudioInternal* const intern = (CoreAudioInternal*) inClientData; + + switch (inPropertyID) + { + case kAudioDevicePropertyBufferSize: + case kAudioDevicePropertyBufferFrameSize: + case kAudioDevicePropertyNominalSampleRate: + case kAudioDevicePropertyStreamFormat: + case kAudioDevicePropertyDeviceIsAlive: + intern->deviceDetailsChanged(); + break; + + case kAudioDevicePropertyBufferSizeRange: + case kAudioDevicePropertyVolumeScalar: + case kAudioDevicePropertyMute: + case kAudioDevicePropertyPlayThru: + case kAudioDevicePropertyDataSource: + case kAudioDevicePropertyDeviceIsRunning: + break; + } + + return noErr; + } + + static OSType* getAllDataSourcesForDevice (AudioDeviceID deviceID, const bool input, int& num) + { + OSType* types = 0; + UInt32 size = 0; + num = 0; + + if (deviceID != 0 + && OK (AudioDeviceGetPropertyInfo (deviceID, 0, input, kAudioDevicePropertyDataSources, &size, 0))) + { + types = (OSType*) juce_calloc (size); + + if (OK (AudioDeviceGetProperty (deviceID, 0, input, kAudioDevicePropertyDataSources, &size, types))) + { + num = size / sizeof (OSType); + } + else + { + juce_free (types); + types = 0; + } + } + + return types; + } +}; + +class CoreAudioIODevice : public AudioIODevice +{ +public: + CoreAudioIODevice (const String& deviceName, + AudioDeviceID deviceId1) + : AudioIODevice (deviceName, "CoreAudio"), + isOpen_ (false), + isStarted (false) + { + internal = 0; + + CoreAudioInternal* device = new CoreAudioInternal (deviceId1); + lastError = device->error; + + if (lastError.isNotEmpty()) + { + deleteAndZero (device); + } + else + { + CoreAudioInternal* secondDevice = device->getRelatedDevice(); + + if (secondDevice != 0) + { + if (device->inChanNames.size() > secondDevice->inChanNames.size()) + swapVariables (device, secondDevice); + + device->inputDevice = secondDevice; + secondDevice->isSlaveDevice = true; + } + } + + internal = device; + + AudioHardwareAddPropertyListener (kAudioPropertyWildcardPropertyID, + hardwareListenerProc, internal); + } + + ~CoreAudioIODevice() + { + AudioHardwareRemovePropertyListener (kAudioPropertyWildcardPropertyID, + hardwareListenerProc); + + delete internal; + } + + const StringArray getOutputChannelNames() + { + return internal->outChanNames; + } + + const StringArray getInputChannelNames() + { + if (internal->inputDevice != 0) + return internal->inputDevice->inChanNames; + else + return internal->inChanNames; + } + + int getNumSampleRates() + { + return internal->sampleRates.size(); + } + + double getSampleRate (int index) + { + return internal->sampleRates [index]; + } + + int getNumBufferSizesAvailable() + { + return internal->bufferSizes.size(); + } + + int getBufferSizeSamples (int index) + { + return internal->bufferSizes [index]; + } + + int getDefaultBufferSize() + { + for (int i = 0; i < getNumBufferSizesAvailable(); ++i) + if (getBufferSizeSamples(i) >= 512) + return getBufferSizeSamples(i); + + return 512; + } + + const String open (const BitArray& inputChannels, + const BitArray& outputChannels, + double sampleRate, + int bufferSizeSamples) + { + isOpen_ = true; + + if (bufferSizeSamples <= 0) + bufferSizeSamples = getDefaultBufferSize(); + + internal->reopen (inputChannels, outputChannels, sampleRate, bufferSizeSamples); + lastError = internal->error; + return lastError; + } + + void close() + { + isOpen_ = false; + } + + bool isOpen() + { + return isOpen_; + } + + int getCurrentBufferSizeSamples() + { + return internal != 0 ? internal->getBufferSize() : 512; + } + + double getCurrentSampleRate() + { + return internal != 0 ? internal->getSampleRate() : 0; + } + + int getCurrentBitDepth() + { + return 32; // no way to find out, so just assume it's high.. + } + + const BitArray getActiveOutputChannels() const + { + return internal != 0 ? internal->activeOutputChans : BitArray(); + } + + const BitArray getActiveInputChannels() const + { + BitArray chans; + + if (internal != 0) + { + chans = internal->activeInputChans; + + if (internal->inputDevice != 0) + chans.orWith (internal->inputDevice->activeInputChans); + } + + return chans; + } + + int getOutputLatencyInSamples() + { + if (internal == 0) + return 0; + + // this seems like a good guess at getting the latency right - comparing + // this with a round-trip measurement, it gets it to within a few millisecs + // for the built-in mac soundcard + return internal->outputLatency + internal->getBufferSize() * 2; + } + + int getInputLatencyInSamples() + { + if (internal == 0) + return 0; + + return internal->inputLatency + internal->getBufferSize() * 2; + } + + void start (AudioIODeviceCallback* callback) + { + if (internal != 0 && ! isStarted) + { + if (callback != 0) + callback->audioDeviceAboutToStart (this); + + isStarted = true; + internal->start (callback); + } + } + + void stop() + { + if (isStarted && internal != 0) + { + AudioIODeviceCallback* const lastCallback = internal->callback; + + isStarted = false; + internal->stop (true); + + if (lastCallback != 0) + lastCallback->audioDeviceStopped(); + } + } + + bool isPlaying() + { + if (internal->callback == 0) + isStarted = false; + + return isStarted; + } + + const String getLastError() + { + return lastError; + } + + juce_UseDebuggingNewOperator + +private: + CoreAudioInternal* internal; + bool isOpen_, isStarted; + String lastError; + + static OSStatus hardwareListenerProc (AudioHardwarePropertyID inPropertyID, void* inClientData) + { + CoreAudioInternal* const intern = (CoreAudioInternal*) inClientData; + + switch (inPropertyID) + { + case kAudioHardwarePropertyDevices: + intern->deviceDetailsChanged(); + break; + + case kAudioHardwarePropertyDefaultOutputDevice: + case kAudioHardwarePropertyDefaultInputDevice: + case kAudioHardwarePropertyDefaultSystemOutputDevice: + break; + } + + return noErr; + } + + CoreAudioIODevice (const CoreAudioIODevice&); + const CoreAudioIODevice& operator= (const CoreAudioIODevice&); +}; + +class CoreAudioIODeviceType : public AudioIODeviceType +{ +public: + + CoreAudioIODeviceType() + : AudioIODeviceType (T("CoreAudio")), + hasScanned (false) + { + } + + ~CoreAudioIODeviceType() + { + } + + void scanForDevices() + { + hasScanned = true; + + names.clear(); + ids.clear(); + + UInt32 size; + if (OK (AudioHardwareGetPropertyInfo (kAudioHardwarePropertyDevices, &size, 0))) + { + AudioDeviceID* const devs = (AudioDeviceID*) juce_calloc (size); + + if (OK (AudioHardwareGetProperty (kAudioHardwarePropertyDevices, &size, devs))) + { + static bool alreadyLogged = false; + const int num = size / sizeof (AudioDeviceID); + for (int i = 0; i < num; ++i) + { + char name[1024]; + size = sizeof (name); + if (OK (AudioDeviceGetProperty (devs[i], 0, false, kAudioDevicePropertyDeviceName, &size, name))) + { + const String nameString (String::fromUTF8 ((const uint8*) name, strlen (name))); + + if (! alreadyLogged) + log (T("CoreAudio device: ") + nameString); + + names.add (nameString); + ids.add (devs[i]); + } + } + + alreadyLogged = true; + } + + juce_free (devs); + } + } + + const StringArray getDeviceNames (const bool /*preferInputNames*/) const + { + jassert (hasScanned); // need to call scanForDevices() before doing this + + StringArray namesCopy (names); + namesCopy.removeDuplicates (true); + + return namesCopy; + } + + const String getDefaultDeviceName (const bool preferInputNames, + const int numInputChannelsNeeded, + const int numOutputChannelsNeeded) const + { + jassert (hasScanned); // need to call scanForDevices() before doing this + + String result (names[0]); + + AudioDeviceID deviceID; + UInt32 size = sizeof (deviceID); + + // if they're asking for any input channels at all, use the default input, so we + // get the built-in mic rather than the built-in output with no inputs.. + if (AudioHardwareGetProperty (numInputChannelsNeeded > 0 + ? kAudioHardwarePropertyDefaultInputDevice + : kAudioHardwarePropertyDefaultOutputDevice, + &size, &deviceID) == noErr) + { + for (int i = ids.size(); --i >= 0;) + if (ids[i] == deviceID) + result = names[i]; + } + + return result; + } + + AudioIODevice* createDevice (const String& deviceName) + { + jassert (hasScanned); // need to call scanForDevices() before doing this + + const int index = names.indexOf (deviceName); + + if (index >= 0) + return new CoreAudioIODevice (deviceName, ids [index]); + + return 0; + } + + juce_UseDebuggingNewOperator + +private: + StringArray names; + Array ids; + + bool hasScanned; + + CoreAudioIODeviceType (const CoreAudioIODeviceType&); + const CoreAudioIODeviceType& operator= (const CoreAudioIODeviceType&); +}; + +AudioIODeviceType* juce_createDefaultAudioIODeviceType() +{ + return new CoreAudioIODeviceType(); +} + +#undef log + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_mac_CoreAudio.cpp *********/ + +/********* Start of inlined file: juce_mac_CoreMidi.cpp *********/ +#include + +BEGIN_JUCE_NAMESPACE + +#undef log +#define log(a) Logger::writeToLog(a) + +static bool logAnyErrorsMidi (const OSStatus err, const int lineNum) +{ + if (err == noErr) + return true; + + log (T("CoreMidi error: ") + String (lineNum) + T(" - ") + String::toHexString ((int)err)); + jassertfalse + return false; +} + +#undef OK +#define OK(a) logAnyErrorsMidi(a, __LINE__) + +static const String getEndpointName (MIDIEndpointRef endpoint, bool isExternal) +{ + String result; + CFStringRef str = 0; + + MIDIObjectGetStringProperty (endpoint, kMIDIPropertyName, &str); + + if (str != 0) + { + result = PlatformUtilities::cfStringToJuceString (str); + CFRelease (str); + str = 0; + } + + MIDIEntityRef entity = 0; + MIDIEndpointGetEntity (endpoint, &entity); + + if (entity == 0) + return result; // probably virtual + + if (result.isEmpty()) + { + // endpoint name has zero length - try the entity + MIDIObjectGetStringProperty (entity, kMIDIPropertyName, &str); + + if (str != 0) + { + result += PlatformUtilities::cfStringToJuceString (str); + CFRelease (str); + str = 0; + } + } + + // now consider the device's name + MIDIDeviceRef device = 0; + MIDIEntityGetDevice (entity, &device); + if (device == 0) + return result; + + MIDIObjectGetStringProperty (device, kMIDIPropertyName, &str); + + if (str != 0) + { + const String s (PlatformUtilities::cfStringToJuceString (str)); + CFRelease (str); + + // if an external device has only one entity, throw away + // the endpoint name and just use the device name + if (isExternal && MIDIDeviceGetNumberOfEntities (device) < 2) + { + result = s; + } + else if (! result.startsWithIgnoreCase (s)) + { + // prepend the device name to the entity name + result = (s + T(" ") + result).trimEnd(); + } + } + + return result; +} + +static const String getConnectedEndpointName (MIDIEndpointRef endpoint) +{ + String result; + + // Does the endpoint have connections? + CFDataRef connections = 0; + int numConnections = 0; + + MIDIObjectGetDataProperty (endpoint, kMIDIPropertyConnectionUniqueID, &connections); + + if (connections != 0) + { + numConnections = CFDataGetLength (connections) / sizeof (MIDIUniqueID); + + if (numConnections > 0) + { + const SInt32* pid = reinterpret_cast (CFDataGetBytePtr (connections)); + + for (int i = 0; i < numConnections; ++i, ++pid) + { + MIDIUniqueID uid = EndianS32_BtoN (*pid); + MIDIObjectRef connObject; + MIDIObjectType connObjectType; + OSStatus err = MIDIObjectFindByUniqueID (uid, &connObject, &connObjectType); + + if (err == noErr) + { + String s; + + if (connObjectType == kMIDIObjectType_ExternalSource + || connObjectType == kMIDIObjectType_ExternalDestination) + { + // Connected to an external device's endpoint (10.3 and later). + s = getEndpointName (static_cast (connObject), true); + } + else + { + // Connected to an external device (10.2) (or something else, catch-all) + CFStringRef str = 0; + MIDIObjectGetStringProperty (connObject, kMIDIPropertyName, &str); + + if (str != 0) + { + s = PlatformUtilities::cfStringToJuceString (str); + CFRelease (str); + } + } + + if (s.isNotEmpty()) + { + if (result.isNotEmpty()) + result += (", "); + + result += s; + } + } + } + } + + CFRelease (connections); + } + + if (result.isNotEmpty()) + return result; + + // Here, either the endpoint had no connections, or we failed to obtain names for any of them. + return getEndpointName (endpoint, false); +} + +const StringArray MidiOutput::getDevices() +{ + StringArray s; + + const ItemCount num = MIDIGetNumberOfDestinations(); + for (ItemCount i = 0; i < num; ++i) + { + MIDIEndpointRef dest = MIDIGetDestination (i); + + if (dest != 0) + { + String name (getConnectedEndpointName (dest)); + + if (name.isEmpty()) + name = ""; + + s.add (name); + } + else + { + s.add (""); + } + } + + return s; +} + +int MidiOutput::getDefaultDeviceIndex() +{ + return 0; +} + +static MIDIClientRef globalMidiClient; +static bool hasGlobalClientBeenCreated = false; + +static bool makeSureClientExists() +{ + if (! hasGlobalClientBeenCreated) + { + String name (T("JUCE")); + + if (JUCEApplication::getInstance() != 0) + name = JUCEApplication::getInstance()->getApplicationName(); + + CFStringRef appName = PlatformUtilities::juceStringToCFString (name); + + hasGlobalClientBeenCreated = OK (MIDIClientCreate (appName, 0, 0, &globalMidiClient)); + CFRelease (appName); + } + + return hasGlobalClientBeenCreated; +} + +struct MidiPortAndEndpoint +{ + MIDIPortRef port; + MIDIEndpointRef endPoint; +}; + +MidiOutput* MidiOutput::openDevice (int index) +{ + MidiOutput* mo = 0; + + if (((unsigned int) index) < (unsigned int) MIDIGetNumberOfDestinations()) + { + MIDIEndpointRef endPoint = MIDIGetDestination (index); + + CFStringRef pname; + if (OK (MIDIObjectGetStringProperty (endPoint, kMIDIPropertyName, &pname))) + { + log (T("CoreMidi - opening out: ") + PlatformUtilities::cfStringToJuceString (pname)); + + if (makeSureClientExists()) + { + MIDIPortRef port; + + if (OK (MIDIOutputPortCreate (globalMidiClient, pname, &port))) + { + MidiPortAndEndpoint* mpe = new MidiPortAndEndpoint(); + mpe->port = port; + mpe->endPoint = endPoint; + + mo = new MidiOutput(); + mo->internal = (void*)mpe; + } + } + + CFRelease (pname); + } + } + + return mo; +} + +MidiOutput::~MidiOutput() +{ + MidiPortAndEndpoint* const mpe = (MidiPortAndEndpoint*)internal; + MIDIPortDispose (mpe->port); + delete mpe; +} + +void MidiOutput::reset() +{ +} + +bool MidiOutput::getVolume (float& leftVol, float& rightVol) +{ + return false; +} + +void MidiOutput::setVolume (float leftVol, float rightVol) +{ +} + +void MidiOutput::sendMessageNow (const MidiMessage& message) +{ + MidiPortAndEndpoint* const mpe = (MidiPortAndEndpoint*)internal; + + if (message.isSysEx()) + { + MIDIPacketList* const packets = (MIDIPacketList*) juce_malloc (32 + message.getRawDataSize()); + packets->numPackets = 1; + packets->packet[0].timeStamp = 0; + packets->packet[0].length = message.getRawDataSize(); + memcpy (packets->packet[0].data, message.getRawData(), message.getRawDataSize()); + + MIDISend (mpe->port, mpe->endPoint, packets); + juce_free (packets); + } + else + { + MIDIPacketList packets; + packets.numPackets = 1; + packets.packet[0].timeStamp = 0; + packets.packet[0].length = message.getRawDataSize(); + *(int*) (packets.packet[0].data) = *(const int*) message.getRawData(); + + MIDISend (mpe->port, mpe->endPoint, &packets); + } +} + +const StringArray MidiInput::getDevices() +{ + StringArray s; + + const ItemCount num = MIDIGetNumberOfSources(); + for (ItemCount i = 0; i < num; ++i) + { + MIDIEndpointRef source = MIDIGetSource (i); + + if (source != 0) + { + String name (getConnectedEndpointName (source)); + + if (name.isEmpty()) + name = ""; + + s.add (name); + } + else + { + s.add (""); + } + } + + return s; +} + +int MidiInput::getDefaultDeviceIndex() +{ + return 0; +} + +struct MidiPortAndCallback +{ + MidiInput* input; + MIDIPortRef port; + MIDIEndpointRef endPoint; + MidiInputCallback* callback; + MemoryBlock pendingData; + int pendingBytes; + double pendingDataTime; + bool active; +}; + +static CriticalSection callbackLock; +static VoidArray activeCallbacks; + +static void processSysex (MidiPortAndCallback* const mpe, const uint8*& d, int& size, const double time) +{ + if (*d == 0xf0) + { + mpe->pendingBytes = 0; + mpe->pendingDataTime = time; + } + + mpe->pendingData.ensureSize (mpe->pendingBytes + size, false); + uint8* totalMessage = (uint8*) mpe->pendingData.getData(); + + uint8* dest = totalMessage + mpe->pendingBytes; + + while (size > 0) + { + if (mpe->pendingBytes > 0 && *d >= 0x80) + { + if (*d >= 0xfa || *d == 0xf8) + { + mpe->callback->handleIncomingMidiMessage (mpe->input, MidiMessage (*d, time)); + ++d; + --size; + } + else + { + if (*d == 0xf7) + { + *dest++ = *d++; + mpe->pendingBytes++; + --size; + } + + break; + } + } + else + { + *dest++ = *d++; + mpe->pendingBytes++; + --size; + } + } + + if (totalMessage [mpe->pendingBytes - 1] == 0xf7) + { + mpe->callback->handleIncomingMidiMessage (mpe->input, MidiMessage (totalMessage, + mpe->pendingBytes, + mpe->pendingDataTime)); + mpe->pendingBytes = 0; + } + else + { + mpe->callback->handlePartialSysexMessage (mpe->input, + totalMessage, + mpe->pendingBytes, + mpe->pendingDataTime); + } +} + +static void midiInputProc (const MIDIPacketList* pktlist, + void* readProcRefCon, + void* srcConnRefCon) +{ + double time = Time::getMillisecondCounterHiRes() * 0.001; + const double originalTime = time; + + MidiPortAndCallback* const mpe = (MidiPortAndCallback*) readProcRefCon; + const ScopedLock sl (callbackLock); + + if (activeCallbacks.contains (mpe) && mpe->active) + { + const MIDIPacket* packet = &pktlist->packet[0]; + + for (unsigned int i = 0; i < pktlist->numPackets; ++i) + { + const uint8* d = (const uint8*) (packet->data); + int size = packet->length; + + while (size > 0) + { + time = originalTime; + + if (mpe->pendingBytes > 0 || d[0] == 0xf0) + { + processSysex (mpe, d, size, time); + } + else + { + int used = 0; + const MidiMessage m (d, size, used, 0, time); + + if (used <= 0) + { + jassertfalse // malformed midi message + break; + } + else + { + mpe->callback->handleIncomingMidiMessage (mpe->input, m); + } + + size -= used; + d += used; + } + } + + packet = MIDIPacketNext (packet); + } + } +} + +MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback) +{ + MidiInput* mi = 0; + + if (((unsigned int) index) < (unsigned int) MIDIGetNumberOfSources()) + { + MIDIEndpointRef endPoint = MIDIGetSource (index); + + if (endPoint != 0) + { + CFStringRef pname; + + if (OK (MIDIObjectGetStringProperty (endPoint, kMIDIPropertyName, &pname))) + { + log (T("CoreMidi - opening inp: ") + PlatformUtilities::cfStringToJuceString (pname)); + + if (makeSureClientExists()) + { + MIDIPortRef port; + + MidiPortAndCallback* const mpe = new MidiPortAndCallback(); + mpe->active = false; + + if (OK (MIDIInputPortCreate (globalMidiClient, pname, midiInputProc, mpe, &port))) + { + if (OK (MIDIPortConnectSource (port, endPoint, 0))) + { + mpe->port = port; + mpe->endPoint = endPoint; + mpe->callback = callback; + mpe->pendingBytes = 0; + mpe->pendingData.ensureSize (128); + + mi = new MidiInput (getDevices() [index]); + mpe->input = mi; + mi->internal = (void*) mpe; + + const ScopedLock sl (callbackLock); + activeCallbacks.add (mpe); + } + else + { + OK (MIDIPortDispose (port)); + delete mpe; + } + } + else + { + delete mpe; + } + } + } + + CFRelease (pname); + } + } + + return mi; +} + +MidiInput::MidiInput (const String& name_) + : name (name_) +{ +} + +MidiInput::~MidiInput() +{ + MidiPortAndCallback* const mpe = (MidiPortAndCallback*) internal; + mpe->active = false; + + callbackLock.enter(); + activeCallbacks.removeValue (mpe); + callbackLock.exit(); + + OK (MIDIPortDisconnectSource (mpe->port, mpe->endPoint)); + OK (MIDIPortDispose (mpe->port)); + delete mpe; +} + +void MidiInput::start() +{ + MidiPortAndCallback* const mpe = (MidiPortAndCallback*) internal; + const ScopedLock sl (callbackLock); + mpe->active = true; +} + +void MidiInput::stop() +{ + MidiPortAndCallback* const mpe = (MidiPortAndCallback*) internal; + const ScopedLock sl (callbackLock); + mpe->active = false; +} + +#undef log + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_mac_CoreMidi.cpp *********/ + +/********* Start of inlined file: juce_mac_FileChooser.cpp *********/ +#include +#include + +BEGIN_JUCE_NAMESPACE + +struct JuceNavInfo +{ + StringArray filters; + AEDesc defaultLocation; + bool defaultLocationValid; +}; + +static void pascal juceNavEventProc (NavEventCallbackMessage callbackSelector, + NavCBRecPtr callbackParms, + void *callBackUD) +{ + if (callbackSelector == kNavCBStart) + { + if (((JuceNavInfo*) callBackUD)->defaultLocationValid) + { + NavCustomControl (callbackParms->context, + kNavCtlSetLocation, + (void*) &((JuceNavInfo*) callBackUD)->defaultLocation); + } + + for (int i = Desktop::getInstance().getNumComponents(); --i >= 0;) + { + Component* const c = Desktop::getInstance().getComponent (i); + + if (c != 0 && c->isAlwaysOnTop() && c->isVisible()) + { + SetWindowGroup (callbackParms->window, + GetWindowGroup ((WindowRef) c->getWindowHandle())); + + break; + } + } + + BringToFront (callbackParms->window); + SelectWindow (callbackParms->window); + SetUserFocusWindow (callbackParms->window); + } +} + +static Boolean pascal juceNavFilterProc (AEDesc* theItem, + void*, + void* callBackUD, + NavFilterModes filterMode) +{ + // must return true if we don't understand the object + bool result = true; + + if (filterMode == kNavFilteringBrowserList) + { + AEDesc desc; + if (AECoerceDesc (theItem, typeFSRef, &desc) == noErr) + { + Size size = AEGetDescDataSize (&desc); + + if (size > 0) + { + void* data = juce_calloc (size); + + if (AEGetDescData (&desc, data, size) == noErr) + { + const String path (PlatformUtilities::makePathFromFSRef ((FSRef*) data)); + + if (path.isNotEmpty()) + { + const File file (path); + + if ((! file.isDirectory()) || PlatformUtilities::isBundle (path)) + { + const String filename (file.getFileName().toLowerCase()); + const char* const filenameUTF8 = filename.toUTF8(); + + const JuceNavInfo* const info = (const JuceNavInfo*) callBackUD; + + if (info != 0) + { + result = false; + + for (int i = info->filters.size(); --i >= 0;) + { + const String wildcard (info->filters[i].toLowerCase()); + + if (fnmatch (wildcard.toUTF8(), filenameUTF8, 0) == 0) + { + result = true; + break; + } + } + } + } + } + } + + juce_free (data); + } + + AEDisposeDesc (&desc); + } + } + + return result; +} + +void FileChooser::showPlatformDialog (OwnedArray& results, + const String& title, + const File& currentFileOrDirectory, + const String& filter, + bool selectsDirectory, + bool isSaveDialogue, + bool warnAboutOverwritingExistingFiles, + bool selectMultipleFiles, + FilePreviewComponent* extraInfoComponent) +{ + JuceNavInfo userInfo; + userInfo.filters.addTokens (filter.replaceCharacters (T(",:"), T(";;")), T(";"), 0); + userInfo.filters.trim(); + userInfo.filters.removeEmptyStrings(); + userInfo.defaultLocationValid = false; + void* const userInfoPtr = (void*) &userInfo; + + const int oldTimeBeforeWaitCursor = MessageManager::getInstance()->getTimeBeforeShowingWaitCursor(); + MessageManager::getInstance()->setTimeBeforeShowingWaitCursor (0); + + NavEventUPP eventProc = NewNavEventUPP (juceNavEventProc); + NavObjectFilterUPP filterProc = NewNavObjectFilterUPP (juceNavFilterProc); + + FSRef defaultRef; + + if ((currentFileOrDirectory.isOnHardDisk() + && PlatformUtilities::makeFSRefFromPath (&defaultRef, + currentFileOrDirectory.getFullPathName())) + || (currentFileOrDirectory.getParentDirectory().isOnHardDisk() + && PlatformUtilities::makeFSRefFromPath (&defaultRef, + currentFileOrDirectory.getParentDirectory().getFullPathName()))) + { + if (AECreateDesc (typeFSRef, &defaultRef, sizeof (defaultRef), &userInfo.defaultLocation) == noErr) + { + userInfo.defaultLocationValid = true; + } + } + + WindowRef lastFocused = GetUserFocusWindow(); + NavDialogCreationOptions options; + + if (NavGetDefaultDialogCreationOptions (&options) == noErr) + { + options.optionFlags |= kNavSelectDefaultLocation + | kNavSupportPackages + | kNavAllowPreviews; + + if (! warnAboutOverwritingExistingFiles) + options.optionFlags |= kNavDontConfirmReplacement; + + if (selectMultipleFiles) + options.optionFlags |= kNavAllowMultipleFiles; + + const String name (selectsDirectory ? TRANS("Choose folder") + : TRANS("Choose file")); + + options.clientName = PlatformUtilities::juceStringToCFString (name); + CFStringRef message = PlatformUtilities::juceStringToCFString (title); + + // nasty layout bug if the message text is set for a directory browser.. + if (selectsDirectory) + options.windowTitle = message; + else + options.message = message; + + NavDialogRef dialog = 0; + bool ok = false; + + if (selectsDirectory) + { + ok = (NavCreateChooseFolderDialog (&options, eventProc, 0, userInfoPtr, &dialog) == noErr); + } + else if (isSaveDialogue) + { + ok = (NavCreatePutFileDialog (&options, 0, 0, eventProc, userInfoPtr, &dialog) == noErr); + } + else + { + ok = (NavCreateGetFileDialog (&options, 0, eventProc, 0, filterProc, userInfoPtr, &dialog) == noErr); + } + + if (ok && (NavDialogRun (dialog) == noErr)) + { + NavReplyRecord reply; + if (NavDialogGetReply (dialog, &reply) == noErr) + { + if (reply.validRecord) + { + long count; + if (AECountItems (&(reply.selection), &count) == noErr + && count > 0) + { + AEKeyword theKeyword; + DescType actualType; + Size actualSize; + FSRef file; + + for (int i = 1; i <= count; ++i) + { + // Get a pointer to selected file + if (AEGetNthPtr (&(reply.selection), + i, + typeFSRef, + &theKeyword, + &actualType, + &file, + sizeof (file), + &actualSize) == noErr) + { + String result (PlatformUtilities::makePathFromFSRef (&file)); + + if (result.isNotEmpty() && isSaveDialogue && ! selectsDirectory) + { + CFStringRef saveName = NavDialogGetSaveFileName (dialog); + + result = File (result) + .getChildFile (PlatformUtilities::convertToPrecomposedUnicode (PlatformUtilities::cfStringToJuceString (saveName))) + .getFullPathName(); + } + + results.add (new File (result)); + } + } + } + } + + NavDisposeReply (&reply); + } + } + + if (dialog != 0) + NavDialogDispose (dialog); + + CFRelease (message); + CFRelease (options.clientName); + } + + if (userInfo.defaultLocationValid) + AEDisposeDesc (&userInfo.defaultLocation); + + DisposeNavEventUPP (eventProc); + DisposeNavObjectFilterUPP (filterProc); + + MessageManager::getInstance()->setTimeBeforeShowingWaitCursor (oldTimeBeforeWaitCursor); + + SetUserFocusWindow (lastFocused); +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_mac_FileChooser.cpp *********/ + +/********* Start of inlined file: juce_mac_Fonts.cpp *********/ + +#include + +BEGIN_JUCE_NAMESPACE + +static OSStatus pascal CubicMoveTo (const Float32Point *pt, + void* callBackDataPtr) +{ + Path* const p = (Path*) callBackDataPtr; + p->startNewSubPath (pt->x, pt->y); + + return noErr; +} + +static OSStatus pascal CubicLineTo (const Float32Point *pt, + void* callBackDataPtr) +{ + Path* const p = (Path*) callBackDataPtr; + p->lineTo (pt->x, pt->y); + + return noErr; +} + +static OSStatus pascal CubicCurveTo (const Float32Point *pt1, + const Float32Point *pt2, + const Float32Point *pt3, + void* callBackDataPtr) +{ + Path* const p = (Path*) callBackDataPtr; + p->cubicTo (pt1->x, pt1->y, + pt2->x, pt2->y, + pt3->x, pt3->y); + + return noErr; +} + +static OSStatus pascal CubicClosePath (void* callBackDataPtr) +{ + Path* const p = (Path*) callBackDataPtr; + p->closeSubPath(); + + return noErr; +} + +class ATSFontHelper +{ + ATSUFontID fontId; + ATSUStyle style; + + ATSCubicMoveToUPP moveToProc; + ATSCubicLineToUPP lineToProc; + ATSCubicCurveToUPP curveToProc; + ATSCubicClosePathUPP closePathProc; + + float totalSize, ascent; + + TextToUnicodeInfo encodingInfo; + +public: + String name; + bool isBold, isItalic; + float fontSize; + int refCount; + + ATSFontHelper (const String& name_, + const bool bold_, + const bool italic_, + const float size_) + : fontId (0), + name (name_), + isBold (bold_), + isItalic (italic_), + fontSize (size_), + refCount (1) + { + const char* const nameUtf8 = name_.toUTF8(); + + ATSUFindFontFromName (const_cast (nameUtf8), + strlen (nameUtf8), + kFontFullName, + kFontNoPlatformCode, + kFontNoScriptCode, + kFontNoLanguageCode, + &fontId); + + ATSUCreateStyle (&style); + + ATSUAttributeTag attTypes[] = { kATSUFontTag, + kATSUQDBoldfaceTag, + kATSUQDItalicTag, + kATSUSizeTag }; + + ByteCount attSizes[] = { sizeof (ATSUFontID), + sizeof (Boolean), + sizeof (Boolean), + sizeof (Fixed) }; + + Boolean bold = bold_, italic = italic_; + Fixed size = X2Fix (size_); + + ATSUAttributeValuePtr attValues[] = { &fontId, + &bold, + &italic, + &size }; + + ATSUSetAttributes (style, 4, attTypes, attSizes, attValues); + + moveToProc = NewATSCubicMoveToUPP (CubicMoveTo); + lineToProc = NewATSCubicLineToUPP (CubicLineTo); + curveToProc = NewATSCubicCurveToUPP (CubicCurveTo); + closePathProc = NewATSCubicClosePathUPP (CubicClosePath); + + ascent = 0.0f; + float kern, descent = 0.0f; + getPathAndKerning (T('N'), T('O'), 0, kern, &ascent, &descent); + totalSize = ascent + descent; + } + + ~ATSFontHelper() + { + ATSUDisposeStyle (style); + + DisposeATSCubicMoveToUPP (moveToProc); + DisposeATSCubicLineToUPP (lineToProc); + DisposeATSCubicCurveToUPP (curveToProc); + DisposeATSCubicClosePathUPP (closePathProc); + } + + bool getPathAndKerning (const juce_wchar char1, + const juce_wchar char2, + Path* path, + float& kerning, + float* ascent, + float* descent) + { + bool ok = false; + + UniChar buffer[4]; + buffer[0] = T(' '); + buffer[1] = char1; + buffer[2] = char2; + buffer[3] = 0; + + UniCharCount count = kATSUToTextEnd; + ATSUTextLayout layout; + OSStatus err = ATSUCreateTextLayoutWithTextPtr (buffer, + 0, + 2, + 2, + 1, + &count, + &style, + &layout); + if (err == noErr) + { + ATSUSetTransientFontMatching (layout, true); + + ATSLayoutRecord* layoutRecords; + ItemCount numRecords; + Fixed* deltaYs; + ItemCount numDeltaYs; + + ATSUDirectGetLayoutDataArrayPtrFromTextLayout (layout, + 0, + kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, + (void**) &layoutRecords, + &numRecords); + + ATSUDirectGetLayoutDataArrayPtrFromTextLayout (layout, + 0, + kATSUDirectDataBaselineDeltaFixedArray, + (void**) &deltaYs, + &numDeltaYs); + + if (numRecords > 2) + { + kerning = (float) (Fix2X (layoutRecords[2].realPos) + - Fix2X (layoutRecords[1].realPos)); + + if (ascent != 0) + { + ATSUTextMeasurement asc; + ByteCount actualSize; + + ATSUGetLineControl (layout, + 0, + kATSULineAscentTag, + sizeof (ATSUTextMeasurement), + &asc, + &actualSize); + + *ascent = (float) Fix2X (asc); + } + + if (descent != 0) + { + ATSUTextMeasurement desc; + ByteCount actualSize; + + ATSUGetLineControl (layout, + 0, + kATSULineDescentTag, + sizeof (ATSUTextMeasurement), + &desc, + &actualSize); + + *descent = (float) Fix2X (desc); + } + + if (path != 0) + { + OSStatus callbackResult; + + ok = (ATSUGlyphGetCubicPaths (style, + layoutRecords[1].glyphID, + moveToProc, + lineToProc, + curveToProc, + closePathProc, + (void*) path, + &callbackResult) == noErr); + + if (numDeltaYs > 0 && ok) + { + const float dy = (float) Fix2X (deltaYs[1]); + + path->applyTransform (AffineTransform::translation (0.0f, dy)); + } + } + else + { + ok = true; + } + } + + if (deltaYs != 0) + ATSUDirectReleaseLayoutDataArrayPtr (0, kATSUDirectDataBaselineDeltaFixedArray, + (void**) &deltaYs); + + if (layoutRecords != 0) + ATSUDirectReleaseLayoutDataArrayPtr (0, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, + (void**) &layoutRecords); + + ATSUDisposeTextLayout (layout); + } + + return kerning; + } + + float getAscent() + { + return ascent; + } + + float getTotalHeight() + { + return totalSize; + } + + juce_wchar getDefaultChar() + { + return 0; + } +}; + +class ATSFontHelperCache : public Timer, + public DeletedAtShutdown +{ + VoidArray cache; + +public: + ATSFontHelperCache() + { + } + + ~ATSFontHelperCache() + { + for (int i = cache.size(); --i >= 0;) + { + ATSFontHelper* const f = (ATSFontHelper*) cache.getUnchecked(i); + delete f; + } + + clearSingletonInstance(); + } + + ATSFontHelper* getFont (const String& name, + const bool bold, + const bool italic, + const float size = 1024) + { + for (int i = cache.size(); --i >= 0;) + { + ATSFontHelper* const f = (ATSFontHelper*) cache.getUnchecked(i); + + if (f->name == name + && f->isBold == bold + && f->isItalic == italic + && f->fontSize == size) + { + f->refCount++; + return f; + } + } + + ATSFontHelper* const f = new ATSFontHelper (name, bold, italic, size); + cache.add (f); + return f; + } + + void releaseFont (ATSFontHelper* f) + { + for (int i = cache.size(); --i >= 0;) + { + ATSFontHelper* const f2 = (ATSFontHelper*) cache.getUnchecked(i); + + if (f == f2) + { + f->refCount--; + + if (f->refCount == 0) + startTimer (5000); + + break; + } + } + } + + void timerCallback() + { + stopTimer(); + + for (int i = cache.size(); --i >= 0;) + { + ATSFontHelper* const f = (ATSFontHelper*) cache.getUnchecked(i); + + if (f->refCount == 0) + { + cache.remove (i); + delete f; + } + } + + if (cache.size() == 0) + delete this; + } + + juce_DeclareSingleton_SingleThreaded_Minimal (ATSFontHelperCache) +}; + +juce_ImplementSingleton_SingleThreaded (ATSFontHelperCache) + +void Typeface::initialiseTypefaceCharacteristics (const String& fontName, + bool bold, + bool italic, + bool addAllGlyphsToFont) throw() +{ + // This method is only safe to be called from the normal UI thread.. + jassert (MessageManager::getInstance()->isThisTheMessageThread()); + + ATSFontHelper* const helper = ATSFontHelperCache::getInstance() + ->getFont (fontName, bold, italic); + + clear(); + setAscent (helper->getAscent() / helper->getTotalHeight()); + setName (fontName); + setDefaultCharacter (helper->getDefaultChar()); + setBold (bold); + setItalic (italic); + + if (addAllGlyphsToFont) + { + //xxx + jassertfalse + } + + ATSFontHelperCache::getInstance()->releaseFont (helper); +} + +bool Typeface::findAndAddSystemGlyph (juce_wchar character) throw() +{ + // This method is only safe to be called from the normal UI thread.. + jassert (MessageManager::getInstance()->isThisTheMessageThread()); + + ATSFontHelper* const helper = ATSFontHelperCache::getInstance() + ->getFont (getName(), isBold(), isItalic()); + + Path path; + float width; + bool foundOne = false; + + if (helper->getPathAndKerning (character, T('I'), &path, width, 0, 0)) + { + path.applyTransform (AffineTransform::scale (1.0f / helper->getTotalHeight(), + 1.0f / helper->getTotalHeight())); + + addGlyph (character, path, width / helper->getTotalHeight()); + + for (int i = 0; i < glyphs.size(); ++i) + { + const TypefaceGlyphInfo* const g = (const TypefaceGlyphInfo*) glyphs.getUnchecked(i); + + float kerning; + if (helper->getPathAndKerning (character, g->getCharacter(), 0, kerning, 0, 0)) + { + kerning = (kerning - width) / helper->getTotalHeight(); + + if (kerning != 0) + addKerningPair (character, g->getCharacter(), kerning); + } + + if (helper->getPathAndKerning (g->getCharacter(), character, 0, kerning, 0, 0)) + { + kerning = kerning / helper->getTotalHeight() - g->width; + + if (kerning != 0) + addKerningPair (g->getCharacter(), character, kerning); + } + } + + foundOne = true; + } + + ATSFontHelperCache::getInstance()->releaseFont (helper); + return foundOne; +} + +const StringArray Font::findAllTypefaceNames() throw() +{ + StringArray names; + ATSFontIterator iter; + + if (ATSFontIteratorCreate (kATSFontContextGlobal, + 0, + 0, + kATSOptionFlagsRestrictedScope, + &iter) == noErr) + { + ATSFontRef font; + + while (ATSFontIteratorNext (iter, &font) == noErr) + { + CFStringRef name; + + if (ATSFontGetName (font, + kATSOptionFlagsDefault, + &name) == noErr) + { + const String nm (PlatformUtilities::cfStringToJuceString (name)); + + if (nm.isNotEmpty()) + names.add (nm); + + CFRelease (name); + } + } + + ATSFontIteratorRelease (&iter); + } + + // Use some totuous logic to eliminate bold/italic versions of fonts that we've already got + // a plain version of. This is only necessary because of Carbon's total lack of support + // for dealing with font families... + for (int j = names.size(); --j >= 0;) + { + const char* const endings[] = { " bold", " italic", " bold italic", " bolditalic", + " oblque", " bold oblique", " boldoblique" }; + + for (int i = 0; i < numElementsInArray (endings); ++i) + { + const String ending (endings[i]); + + if (names[j].endsWithIgnoreCase (ending)) + { + const String root (names[j].dropLastCharacters (ending.length()).trimEnd()); + + if (names.contains (root) + || names.contains (root + T(" plain"), true)) + { + names.remove (j); + break; + } + } + } + } + + names.sort (true); + return names; +} + +void Font::getDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed) throw() +{ + defaultSans = "Lucida Grande"; + defaultSerif = "Times New Roman"; + defaultFixed = "Monaco"; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_mac_Fonts.cpp *********/ + +/********* Start of inlined file: juce_mac_Messaging.cpp *********/ + +#include + +BEGIN_JUCE_NAMESPACE + +#undef Point + +static int kJUCEClass = FOUR_CHAR_CODE ('JUCE'); +const int kJUCEKind = 1; +const int kCallbackKind = 2; + +extern void juce_HandleProcessFocusChange(); +extern void juce_maximiseAllMinimisedWindows(); +extern void juce_InvokeMainMenuCommand (const HICommand& command); +extern void juce_MainMenuAboutToBeUsed(); + +static pascal OSStatus EventHandlerProc (EventHandlerCallRef, EventRef theEvent, void* userData) +{ + void* event = 0; + GetEventParameter (theEvent, 'mess', typeVoidPtr, 0, sizeof (void*), 0, &event); + + if (event != 0) + MessageManager::getInstance()->deliverMessage (event); + + return noErr; +} + +struct CallbackMessagePayload +{ + MessageCallbackFunction* function; + void* parameter; + void* volatile result; + bool volatile hasBeenExecuted; +}; + +static pascal OSStatus CallbackHandlerProc (EventHandlerCallRef, EventRef theEvent, void* userData) +{ + CallbackMessagePayload* pl = 0; + GetEventParameter (theEvent, 'mess', typeVoidPtr, 0, sizeof(pl), 0, &pl); + + if (pl != 0) + { + pl->result = (*pl->function) (pl->parameter); + pl->hasBeenExecuted = true; + } + + return noErr; +} + +static pascal OSStatus MouseClickHandlerProc (EventHandlerCallRef, EventRef theEvent, void* userData) +{ + ::Point where; + GetEventParameter (theEvent, kEventParamMouseLocation, typeQDPoint, 0, sizeof(::Point), 0, &where); + WindowRef window; + if (FindWindow (where, &window) == inMenuBar) + { + // turn off the wait cursor before going in here.. + const int oldTimeBeforeWaitCursor = MessageManager::getInstance()->getTimeBeforeShowingWaitCursor(); + MessageManager::getInstance()->setTimeBeforeShowingWaitCursor (0); + + if (Component::getCurrentlyModalComponent() != 0) + Component::getCurrentlyModalComponent()->inputAttemptWhenModal(); + + juce_MainMenuAboutToBeUsed(); + MenuSelect (where); + HiliteMenu (0); + + MessageManager::getInstance()->setTimeBeforeShowingWaitCursor (oldTimeBeforeWaitCursor); + return noErr; + } + + return eventNotHandledErr; +} + +static pascal OSErr QuitAppleEventHandler (const AppleEvent *appleEvt, AppleEvent* reply, long refcon) +{ + if (JUCEApplication::getInstance() != 0) + JUCEApplication::getInstance()->systemRequestedQuit(); + + return noErr; +} + +static pascal OSErr OpenDocEventHandler (const AppleEvent *appleEvt, AppleEvent* reply, long refcon) +{ + AEDescList docs; + StringArray files; + + if (AEGetParamDesc (appleEvt, keyDirectObject, typeAEList, &docs) == noErr) + { + long num; + if (AECountItems (&docs, &num) == noErr) + { + for (int i = 1; i <= num; ++i) + { + FSRef file; + AEKeyword keyword; + DescType type; + Size size; + + if (AEGetNthPtr (&docs, i, typeFSRef, &keyword, &type, + &file, sizeof (file), &size) == noErr) + { + const String path (PlatformUtilities::makePathFromFSRef (&file)); + + if (path.isNotEmpty()) + files.add (path.quoted()); + } + } + + if (files.size() > 0 + && JUCEApplication::getInstance() != 0) + { + JUCE_TRY + { + JUCEApplication::getInstance() + ->anotherInstanceStarted (files.joinIntoString (T(" "))); + } + JUCE_CATCH_ALL + } + } + + AEDisposeDesc (&docs); + }; + + return noErr; +} + +static pascal OSStatus AppEventHandlerProc (EventHandlerCallRef, EventRef theEvent, void* userData) +{ + const UInt32 eventClass = GetEventClass (theEvent); + + if (eventClass == kEventClassCommand) + { + HICommand command; + + if (GetEventParameter (theEvent, kEventParamHICommand, typeHICommand, 0, sizeof (command), 0, &command) == noErr + || GetEventParameter (theEvent, kEventParamDirectObject, typeHICommand, 0, sizeof (command), 0, &command) == noErr) + { + if (command.commandID == kHICommandQuit) + { + if (JUCEApplication::getInstance() != 0) + JUCEApplication::getInstance()->systemRequestedQuit(); + + return noErr; + } + else if (command.commandID == kHICommandMaximizeAll + || command.commandID == kHICommandMaximizeWindow + || command.commandID == kHICommandBringAllToFront) + { + juce_maximiseAllMinimisedWindows(); + return noErr; + } + else + { + juce_InvokeMainMenuCommand (command); + } + } + } + else if (eventClass == kEventClassApplication) + { + if (GetEventKind (theEvent) == kEventAppFrontSwitched) + { + juce_HandleProcessFocusChange(); + } + else if (GetEventKind (theEvent) == kEventAppShown) + { + // this seems to blank the windows, so we need to do a repaint.. + for (int i = Desktop::getInstance().getNumComponents(); --i >= 0;) + { + Component* const c = Desktop::getInstance().getComponent (i); + + if (c != 0) + c->repaint(); + } + } + } + + return eventNotHandledErr; +} + +static EventQueueRef mainQueue; +static EventHandlerRef juceEventHandler = 0; +static EventHandlerRef callbackEventHandler = 0; + +void MessageManager::doPlatformSpecificInitialisation() +{ + static bool initialised = false; + + if (! initialised) + { + initialised = true; + +#if MACOS_10_3_OR_EARLIER + // work-around for a bug in MacOS 10.2.. + ProcessSerialNumber junkPSN; + (void) GetCurrentProcess (&junkPSN); +#endif + + mainQueue = GetMainEventQueue(); + + // if we're linking a Juce app to one or more dynamic libraries, we'll need different values + // for this so each module doesn't interfere with the others. + UnsignedWide t; + Microseconds (&t); + kJUCEClass ^= t.lo; + } + + const EventTypeSpec type1 = { kJUCEClass, kJUCEKind }; + InstallApplicationEventHandler (NewEventHandlerUPP (EventHandlerProc), 1, &type1, 0, &juceEventHandler); + + const EventTypeSpec type2 = { kJUCEClass, kCallbackKind }; + InstallApplicationEventHandler (NewEventHandlerUPP (CallbackHandlerProc), 1, &type2, 0, &callbackEventHandler); + + // only do this stuff if we're running as an application rather than a library.. + if (JUCEApplication::getInstance() != 0) + { + const EventTypeSpec type3 = { kEventClassMouse, kEventMouseDown }; + InstallApplicationEventHandler (NewEventHandlerUPP (MouseClickHandlerProc), 1, &type3, 0, 0); + + const EventTypeSpec type4[] = { { kEventClassApplication, kEventAppShown }, + { kEventClassApplication, kEventAppFrontSwitched }, + { kEventClassCommand, kEventProcessCommand } }; + + InstallApplicationEventHandler (NewEventHandlerUPP (AppEventHandlerProc), 3, type4, 0, 0); + + AEInstallEventHandler (kCoreEventClass, kAEQuitApplication, + NewAEEventHandlerUPP (QuitAppleEventHandler), 0, false); + + AEInstallEventHandler (kCoreEventClass, kAEOpenDocuments, + NewAEEventHandlerUPP (OpenDocEventHandler), 0, false); + } +} + +void MessageManager::doPlatformSpecificShutdown() +{ + if (juceEventHandler != 0) + { + RemoveEventHandler (juceEventHandler); + juceEventHandler = 0; + } + + if (callbackEventHandler != 0) + { + RemoveEventHandler (callbackEventHandler); + callbackEventHandler = 0; + } +} + +bool juce_postMessageToSystemQueue (void* message) +{ + jassert (mainQueue == GetMainEventQueue()); + + EventRef event; + if (CreateEvent (0, kJUCEClass, kJUCEKind, 0, kEventAttributeUserEvent, &event) == noErr) + { + SetEventParameter (event, 'mess', typeVoidPtr, sizeof (void*), &message); + const bool ok = PostEventToQueue (mainQueue, event, kEventPriorityStandard) == noErr; + ReleaseEvent (event); + return ok; + } + + return false; +} + +void MessageManager::broadcastMessage (const String& value) throw() +{ +} + +void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* callback, + void* data) +{ + if (isThisTheMessageThread()) + { + return (*callback) (data); + } + else + { + jassert (mainQueue == GetMainEventQueue()); + + CallbackMessagePayload cmp; + cmp.function = callback; + cmp.parameter = data; + cmp.result = 0; + cmp.hasBeenExecuted = false; + + EventRef event; + if (CreateEvent (0, kJUCEClass, kCallbackKind, 0, kEventAttributeUserEvent, &event) == noErr) + { + void* v = &cmp; + SetEventParameter (event, 'mess', typeVoidPtr, sizeof (void*), &v); + + if (PostEventToQueue (mainQueue, event, kEventPriorityStandard) == noErr) + { + while (! cmp.hasBeenExecuted) + Thread::yield(); + + return cmp.result; + } + } + + return 0; + } +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_mac_Messaging.cpp *********/ + +/********* Start of inlined file: juce_mac_WebBrowserComponent.mm *********/ + +#include +#include +#include +#include +#include + +BEGIN_JUCE_NAMESPACE + +END_JUCE_NAMESPACE + +@interface DownloadClickDetector : NSObject +{ + juce::WebBrowserComponent* ownerComponent; +} + +- (DownloadClickDetector*) initWithOwner: (juce::WebBrowserComponent*) ownerComponent; + +- (void) webView: (WebView*) webView decidePolicyForNavigationAction: (NSDictionary*) actionInformation + request: (NSURLRequest*) request + frame: (WebFrame*) frame + decisionListener: (id) listener; +@end + +@implementation DownloadClickDetector + +- (DownloadClickDetector*) initWithOwner: (juce::WebBrowserComponent*) ownerComponent_ +{ + [super init]; + ownerComponent = ownerComponent_; + return self; +} + +- (void) webView: (WebView*) sender decidePolicyForNavigationAction: (NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id < WebPolicyDecisionListener >)listener +{ + NSURL* url = [actionInformation valueForKey: @"WebActionOriginalURLKey"]; + + if (ownerComponent->pageAboutToLoad (juce::String::fromUTF8 ((const juce::uint8*) [[url absoluteString] UTF8String]))) + [listener use]; + else + [listener ignore]; +} + +@end + +BEGIN_JUCE_NAMESPACE + +class WebBrowserComponentInternal : public Timer +{ +public: + WebBrowserComponentInternal (WebBrowserComponent* owner_) + : owner (owner_), + view (0), + webView (0) + { + HIWebViewCreate (&view); + + ComponentPeer* const peer = owner_->getPeer(); + jassert (peer != 0); + + if (view != 0 && peer != 0) + { + WindowRef parentWindow = (WindowRef) peer->getNativeHandle(); + + WindowAttributes attributes; + GetWindowAttributes (parentWindow, &attributes); + + HIViewRef parentView = 0; + + if ((attributes & kWindowCompositingAttribute) != 0) + { + HIViewRef root = HIViewGetRoot (parentWindow); + HIViewFindByID (root, kHIViewWindowContentID, &parentView); + + if (parentView == 0) + parentView = root; + } + else + { + GetRootControl (parentWindow, (ControlRef*) &parentView); + + if (parentView == 0) + CreateRootControl (parentWindow, (ControlRef*) &parentView); + } + + HIViewAddSubview (parentView, view); + updateBounds(); + show(); + + webView = HIWebViewGetWebView (view); + + clickListener = [[DownloadClickDetector alloc] initWithOwner: owner_]; + [webView setPolicyDelegate: clickListener]; + } + + startTimer (500); + } + + ~WebBrowserComponentInternal() + { + [webView setPolicyDelegate: nil]; + [clickListener release]; + + if (view != 0) + CFRelease (view); + } + + // Horrific bodge-workaround for the fact that the webview somehow hangs onto key + // focus when you pop up a new window, no matter what that window does to + // try to grab focus for itself. This catches such a situation and forces + // focus away from the webview, then back to the place it should be.. + void timerCallback() + { + WindowRef viewWindow = HIViewGetWindow (view); + WindowRef focusedWindow = GetUserFocusWindow(); + + if (focusedWindow != viewWindow) + { + if (HIViewSubtreeContainsFocus (view)) + { + HIViewAdvanceFocus (HIViewGetRoot (viewWindow), 0); + HIViewAdvanceFocus (HIViewGetRoot (focusedWindow), 0); + } + } + } + + void show() + { + HIViewSetVisible (view, true); + } + + void hide() + { + HIViewSetVisible (view, false); + } + + void goToURL (const String& url, + const StringArray* headers, + const MemoryBlock* postData) + { + char** headerNamesAsChars = 0; + char** headerValuesAsChars = 0; + int numHeaders = 0; + + if (headers != 0) + { + numHeaders = headers->size(); + + headerNamesAsChars = (char**) juce_malloc (sizeof (char*) * numHeaders); + headerValuesAsChars = (char**) juce_malloc (sizeof (char*) * numHeaders); + + int i; + for (i = 0; i < numHeaders; ++i) + { + const String headerName ((*headers)[i].upToFirstOccurrenceOf (T(":"), false, false).trim()); + headerNamesAsChars[i] = (char*) juce_calloc (headerName.copyToUTF8 (0)); + headerName.copyToUTF8 ((juce::uint8*) headerNamesAsChars[i]); + + const String headerValue ((*headers)[i].fromFirstOccurrenceOf (T(":"), false, false).trim()); + headerValuesAsChars[i] = (char*) juce_calloc (headerValue.copyToUTF8 (0)); + headerValue.copyToUTF8 ((juce::uint8*) headerValuesAsChars[i]); + } + } + + sendWebViewToURL ((const char*) url.toUTF8(), + (const char**) headerNamesAsChars, + (const char**) headerValuesAsChars, + numHeaders, + postData != 0 ? (const char*) postData->getData() : 0, + postData != 0 ? postData->getSize() : 0); + + for (int i = 0; i < numHeaders; ++i) + { + juce_free (headerNamesAsChars[i]); + juce_free (headerValuesAsChars[i]); + } + + juce_free (headerNamesAsChars); + juce_free (headerValuesAsChars); + } + + void goBack() + { + [webView goBack]; + } + + void goForward() + { + [webView goForward]; + } + + void stop() + { + [webView stopLoading: nil]; + } + + void updateBounds() + { + HIRect r; + r.origin.x = (float) owner->getScreenX() - owner->getTopLevelComponent()->getScreenX(); + r.origin.y = (float) owner->getScreenY() - owner->getTopLevelComponent()->getScreenY(); + r.size.width = (float) owner->getWidth(); + r.size.height = (float) owner->getHeight(); + HIViewSetFrame (view, &r); + } + +private: + WebBrowserComponent* const owner; + HIViewRef view; + WebView* webView; + DownloadClickDetector* clickListener; + + void sendWebViewToURL (const char* utf8URL, + const char** headerNames, + const char** headerValues, + int numHeaders, + const char* postData, + int postDataSize) + { + NSMutableURLRequest* r = [NSMutableURLRequest + requestWithURL: [NSURL URLWithString: [NSString stringWithUTF8String: utf8URL]] + cachePolicy: NSURLRequestUseProtocolCachePolicy + timeoutInterval: 30.0]; + + if (postDataSize > 0) + { + [ r setHTTPMethod: @"POST"]; + [ r setHTTPBody: [NSData dataWithBytes: postData length: postDataSize]]; + } + + int i; + for (i = 0; i < numHeaders; ++i) + { + [ r setValue: [NSString stringWithUTF8String: headerValues[i]] + forHTTPHeaderField: [NSString stringWithUTF8String: headerNames[i]]]; + } + + [[webView mainFrame] stopLoading ]; + [[webView mainFrame] loadRequest: r]; + } + + WebBrowserComponentInternal (const WebBrowserComponentInternal&); + const WebBrowserComponentInternal& operator= (const WebBrowserComponentInternal&); +}; + +WebBrowserComponent::WebBrowserComponent() + : browser (0), + associatedWindow (0), + blankPageShown (false) +{ + setOpaque (true); +} + +WebBrowserComponent::~WebBrowserComponent() +{ + deleteBrowser(); +} + +void WebBrowserComponent::goToURL (const String& url, + const StringArray* headers, + const MemoryBlock* postData) +{ + lastURL = url; + + lastHeaders.clear(); + if (headers != 0) + lastHeaders = *headers; + + lastPostData.setSize (0); + if (postData != 0) + lastPostData = *postData; + + blankPageShown = false; + + if (browser != 0) + browser->goToURL (url, headers, postData); +} + +void WebBrowserComponent::stop() +{ + if (browser != 0) + browser->stop(); +} + +void WebBrowserComponent::goBack() +{ + lastURL = String::empty; + blankPageShown = false; + + if (browser != 0) + browser->goBack(); +} + +void WebBrowserComponent::goForward() +{ + lastURL = String::empty; + + if (browser != 0) + browser->goForward(); +} + +void WebBrowserComponent::paint (Graphics& g) +{ + if (browser == 0) + g.fillAll (Colours::white); +} + +void WebBrowserComponent::checkWindowAssociation() +{ + void* const window = getWindowHandle(); + + if (window != associatedWindow + || (browser == 0 && window != 0)) + { + associatedWindow = window; + + deleteBrowser(); + createBrowser(); + } + + if (browser != 0) + { + if (associatedWindow != 0 && isShowing()) + { + browser->show(); + + if (blankPageShown) + goBack(); + } + else + { + if (! blankPageShown) + { + // when the component becomes invisible, some stuff like flash + // carries on playing audio, so we need to force it onto a blank + // page to avoid this.. + + blankPageShown = true; + browser->goToURL ("about:blank", 0, 0); + } + + browser->hide(); + } + } +} + +void WebBrowserComponent::createBrowser() +{ + deleteBrowser(); + + if (isShowing()) + { + WebInitForCarbon(); + browser = new WebBrowserComponentInternal (this); + reloadLastURL(); + } +} + +void WebBrowserComponent::deleteBrowser() +{ + deleteAndZero (browser); +} + +void WebBrowserComponent::reloadLastURL() +{ + if (lastURL.isNotEmpty()) + { + goToURL (lastURL, &lastHeaders, &lastPostData); + lastURL = String::empty; + } +} + +void WebBrowserComponent::updateBrowserPosition() +{ + if (getPeer() != 0 && browser != 0) + browser->updateBounds(); +} + +void WebBrowserComponent::parentHierarchyChanged() +{ + checkWindowAssociation(); +} + +void WebBrowserComponent::moved() +{ + updateBrowserPosition(); +} + +void WebBrowserComponent::resized() +{ + updateBrowserPosition(); +} + +void WebBrowserComponent::visibilityChanged() +{ + checkWindowAssociation(); +} + +bool WebBrowserComponent::pageAboutToLoad (const String& url) +{ + return true; +} + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_mac_WebBrowserComponent.mm *********/ + +/********* Start of inlined file: juce_mac_Windowing.cpp *********/ + +#include +#include +#include +#include +#include +#include + +#if JUCE_OPENGL +#include +#endif + +BEGIN_JUCE_NAMESPACE + +#undef Point + +const WindowRegionCode windowRegionToUse = kWindowContentRgn; + +static HIObjectClassRef viewClassRef = 0; +static CFStringRef juceHiViewClassNameCFString = 0; +static ComponentPeer* juce_currentMouseTrackingPeer = 0; + +static VoidArray keysCurrentlyDown; + +bool KeyPress::isKeyCurrentlyDown (const int keyCode) throw() +{ + if (keysCurrentlyDown.contains ((void*) keyCode)) + return true; + + if (keyCode >= 'A' && keyCode <= 'Z' + && keysCurrentlyDown.contains ((void*) (int) CharacterFunctions::toLowerCase ((tchar) keyCode))) + return true; + + if (keyCode >= 'a' && keyCode <= 'z' + && keysCurrentlyDown.contains ((void*) (int) CharacterFunctions::toUpperCase ((tchar) keyCode))) + return true; + + return false; +} + +static VoidArray minimisedWindows; + +static void setWindowMinimised (WindowRef ref, const bool isMinimised) +{ + if (isMinimised != minimisedWindows.contains (ref)) + CollapseWindow (ref, isMinimised); +} + +void juce_maximiseAllMinimisedWindows() +{ + const VoidArray minWin (minimisedWindows); + + for (int i = minWin.size(); --i >= 0;) + setWindowMinimised ((WindowRef) (minWin[i]), false); +} + +class HIViewComponentPeer; +static HIViewComponentPeer* currentlyFocusedPeer = 0; + +static int currentModifiers = 0; + +static void updateModifiers (EventRef theEvent) +{ + currentModifiers &= ~ (ModifierKeys::shiftModifier | ModifierKeys::ctrlModifier + | ModifierKeys::altModifier | ModifierKeys::commandModifier); + + UInt32 m; + + if (theEvent != 0) + GetEventParameter (theEvent, kEventParamKeyModifiers, typeUInt32, 0, sizeof(m), 0, &m); + else + m = GetCurrentEventKeyModifiers(); + + if ((m & (shiftKey | rightShiftKey)) != 0) + currentModifiers |= ModifierKeys::shiftModifier; + + if ((m & (controlKey | rightControlKey)) != 0) + currentModifiers |= ModifierKeys::ctrlModifier; + + if ((m & (optionKey | rightOptionKey)) != 0) + currentModifiers |= ModifierKeys::altModifier; + + if ((m & cmdKey) != 0) + currentModifiers |= ModifierKeys::commandModifier; +} + +void ModifierKeys::updateCurrentModifiers() throw() +{ + currentModifierFlags = currentModifiers; +} + +static int64 getEventTime (EventRef event) +{ + const int64 millis = (int64) (1000.0 * (event != 0 ? GetEventTime (event) + : GetCurrentEventTime())); + + static int64 offset = 0; + if (offset == 0) + offset = Time::currentTimeMillis() - millis; + + return offset + millis; +} + +class MacBitmapImage : public Image +{ +public: + + CGColorSpaceRef colourspace; + CGDataProviderRef provider; + + MacBitmapImage (const PixelFormat format_, + const int w, const int h, const bool clearImage) + : Image (format_, w, h) + { + jassert (format_ == RGB || format_ == ARGB); + + pixelStride = (format_ == RGB) ? 3 : 4; + + lineStride = (w * pixelStride + 3) & ~3; + const int imageSize = lineStride * h; + + if (clearImage) + imageData = (uint8*) juce_calloc (imageSize); + else + imageData = (uint8*) juce_malloc (imageSize); + + //colourspace = CGColorSpaceCreateWithName (kCGColorSpaceUserRGB); + + CMProfileRef prof; + CMGetSystemProfile (&prof); + colourspace = CGColorSpaceCreateWithPlatformColorSpace (prof); + provider = CGDataProviderCreateWithData (0, imageData, h * lineStride, 0); + CMCloseProfile (prof); + } + + MacBitmapImage::~MacBitmapImage() + { + CGDataProviderRelease (provider); + CGColorSpaceRelease (colourspace); + + juce_free (imageData); + imageData = 0; // to stop the base class freeing this + } + + void blitToContext (CGContextRef context, const float dx, const float dy) + { + CGImageRef tempImage = CGImageCreate (getWidth(), getHeight(), + 8, pixelStride << 3, lineStride, colourspace, +#if MACOS_10_3_OR_EARLIER || JUCE_BIG_ENDIAN + hasAlphaChannel() ? kCGImageAlphaPremultipliedFirst + : kCGImageAlphaNone, +#else + hasAlphaChannel() ? kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst + : kCGImageAlphaNone, +#endif + provider, 0, false, + kCGRenderingIntentDefault); + + HIRect r; + r.origin.x = dx; + r.origin.y = dy; + r.size.width = (float) getWidth(); + r.size.height = (float) getHeight(); + + HIViewDrawCGImage (context, &r, tempImage); + + CGImageRelease (tempImage); + } + + juce_UseDebuggingNewOperator +}; + +class MouseCheckTimer : private Timer, + private DeletedAtShutdown +{ +public: + MouseCheckTimer() + : lastX (0), + lastY (0) + { + lastPeerUnderMouse = 0; + resetMouseMoveChecker(); + +#if ! MACOS_10_2_OR_EARLIER + // Just putting this in here because it's a convenient object that'll get deleted at shutdown + CGDisplayRegisterReconfigurationCallback (&displayChangeCallback, 0); +#endif + } + + ~MouseCheckTimer() + { +#if ! MACOS_10_2_OR_EARLIER + CGDisplayRemoveReconfigurationCallback (&displayChangeCallback, 0); +#endif + + clearSingletonInstance(); + } + + juce_DeclareSingleton_SingleThreaded_Minimal (MouseCheckTimer) + + bool hasEverHadAMouseMove; + + void moved (HIViewComponentPeer* const peer) + { + if (hasEverHadAMouseMove) + startTimer (200); + + lastPeerUnderMouse = peer; + } + + void resetMouseMoveChecker() + { + hasEverHadAMouseMove = false; + startTimer (1000 / 16); + } + + void timerCallback(); + +private: + HIViewComponentPeer* lastPeerUnderMouse; + int lastX, lastY; + +#if ! MACOS_10_2_OR_EARLIER + static void displayChangeCallback (CGDirectDisplayID, CGDisplayChangeSummaryFlags flags, void*) + { + Desktop::getInstance().refreshMonitorSizes(); + } +#endif +}; + +juce_ImplementSingleton_SingleThreaded (MouseCheckTimer) + +#if JUCE_QUICKTIME +extern void OfferMouseClickToQuickTime (WindowRef window, ::Point where, long when, long modifiers, + Component* topLevelComp); +#endif + +class HIViewComponentPeer : public ComponentPeer, + private Timer +{ +public: + + HIViewComponentPeer (Component* const component, + const int windowStyleFlags, + HIViewRef viewToAttachTo) + : ComponentPeer (component, windowStyleFlags), + fullScreen (false), + isCompositingWindow (false), + windowRef (0), + viewRef (0) + { + repainter = new RepaintManager (this); + + eventHandlerRef = 0; + + if (viewToAttachTo != 0) + { + isSharedWindow = true; + } + else + { + isSharedWindow = false; + + WindowRef newWindow = createNewWindow (windowStyleFlags); + + GetRootControl (newWindow, (ControlRef*) &viewToAttachTo); + jassert (viewToAttachTo != 0); + + HIViewRef growBox = 0; + HIViewFindByID (HIViewGetRoot (newWindow), kHIViewWindowGrowBoxID, &growBox); + + if (growBox != 0) + HIGrowBoxViewSetTransparent (growBox, true); + } + + createNewHIView(); + + HIViewAddSubview (viewToAttachTo, viewRef); + HIViewSetVisible (viewRef, component->isVisible()); + + setTitle (component->getName()); + + if (component->isVisible() && ! isSharedWindow) + { + ShowWindow (windowRef); + ActivateWindow (windowRef, component->getWantsKeyboardFocus()); + } + } + + ~HIViewComponentPeer() + { + minimisedWindows.removeValue (windowRef); + + if (IsValidWindowPtr (windowRef)) + { + if (! isSharedWindow) + { + CFRelease (viewRef); + viewRef = 0; + + DisposeWindow (windowRef); + } + else + { + if (eventHandlerRef != 0) + RemoveEventHandler (eventHandlerRef); + + CFRelease (viewRef); + viewRef = 0; + } + + windowRef = 0; + } + + if (currentlyFocusedPeer == this) + currentlyFocusedPeer = 0; + + delete repainter; + } + + void* getNativeHandle() const + { + return windowRef; + } + + void setVisible (bool shouldBeVisible) + { + HIViewSetVisible (viewRef, shouldBeVisible); + + if ((! isSharedWindow) && IsValidWindowPtr (windowRef)) + { + if (shouldBeVisible) + ShowWindow (windowRef); + else + HideWindow (windowRef); + + resizeViewToFitWindow(); + + // If nothing else is focused, then grab the focus too + if (shouldBeVisible + && Component::getCurrentlyFocusedComponent() == 0 + && Process::isForegroundProcess()) + { + component->toFront (true); + } + } + } + + void setTitle (const String& title) + { + if ((! isSharedWindow) && IsValidWindowPtr (windowRef)) + { + CFStringRef t = PlatformUtilities::juceStringToCFString (title); + SetWindowTitleWithCFString (windowRef, t); + CFRelease (t); + } + } + + void setPosition (int x, int y) + { + if (isSharedWindow) + { + HIViewPlaceInSuperviewAt (viewRef, x, y); + } + else if (IsValidWindowPtr (windowRef)) + { + Rect r; + GetWindowBounds (windowRef, windowRegionToUse, &r); + r.right += x - r.left; + r.bottom += y - r.top; + r.left = x; + r.top = y; + SetWindowBounds (windowRef, windowRegionToUse, &r); + } + } + + void setSize (int w, int h) + { + w = jmax (0, w); + h = jmax (0, h); + + if (w != getComponent()->getWidth() + || h != getComponent()->getHeight()) + { + repainter->repaint (0, 0, w, h); + } + + if (isSharedWindow) + { + HIRect r; + HIViewGetFrame (viewRef, &r); + r.size.width = (float) w; + r.size.height = (float) h; + HIViewSetFrame (viewRef, &r); + } + else if (IsValidWindowPtr (windowRef)) + { + Rect r; + GetWindowBounds (windowRef, windowRegionToUse, &r); + r.right = r.left + w; + r.bottom = r.top + h; + SetWindowBounds (windowRef, windowRegionToUse, &r); + } + } + + void setBounds (int x, int y, int w, int h, const bool isNowFullScreen) + { + fullScreen = isNowFullScreen; + w = jmax (0, w); + h = jmax (0, h); + + if (w != getComponent()->getWidth() + || h != getComponent()->getHeight()) + { + repainter->repaint (0, 0, w, h); + } + + if (isSharedWindow) + { + HIRect r; + r.origin.x = (float) x; + r.origin.y = (float) y; + r.size.width = (float) w; + r.size.height = (float) h; + HIViewSetFrame (viewRef, &r); + } + else if (IsValidWindowPtr (windowRef)) + { + Rect r; + r.left = x; + r.top = y; + r.right = x + w; + r.bottom = y + h; + SetWindowBounds (windowRef, windowRegionToUse, &r); + } + } + + void getBounds (int& x, int& y, int& w, int& h, const bool global) const + { + HIRect hiViewPos; + HIViewGetFrame (viewRef, &hiViewPos); + + if (global) + { + HIViewRef content = 0; + HIViewFindByID (HIViewGetRoot (windowRef), kHIViewWindowContentID, &content); + HIPoint p = { 0.0f, 0.0f }; + HIViewConvertPoint (&p, viewRef, content); + + x = (int) p.x; + y = (int) p.y; + + if (IsValidWindowPtr (windowRef)) + { + Rect windowPos; + GetWindowBounds (windowRef, kWindowContentRgn, &windowPos); + + x += windowPos.left; + y += windowPos.top; + } + } + else + { + x = (int) hiViewPos.origin.x; + y = (int) hiViewPos.origin.y; + } + + w = (int) hiViewPos.size.width; + h = (int) hiViewPos.size.height; + } + + void getBounds (int& x, int& y, int& w, int& h) const + { + getBounds (x, y, w, h, ! isSharedWindow); + } + + int getScreenX() const + { + int x, y, w, h; + getBounds (x, y, w, h, true); + return x; + } + + int getScreenY() const + { + int x, y, w, h; + getBounds (x, y, w, h, true); + return y; + } + + void relativePositionToGlobal (int& x, int& y) + { + int wx, wy, ww, wh; + getBounds (wx, wy, ww, wh, true); + + x += wx; + y += wy; + } + + void globalPositionToRelative (int& x, int& y) + { + int wx, wy, ww, wh; + getBounds (wx, wy, ww, wh, true); + + x -= wx; + y -= wy; + } + + void setMinimised (bool shouldBeMinimised) + { + if (! isSharedWindow) + setWindowMinimised (windowRef, shouldBeMinimised); + } + + bool isMinimised() const + { + return minimisedWindows.contains (windowRef); + } + + void setFullScreen (bool shouldBeFullScreen) + { + if (! isSharedWindow) + { + Rectangle r (lastNonFullscreenBounds); + + setMinimised (false); + + if (fullScreen != shouldBeFullScreen) + { + if (shouldBeFullScreen) + r = Desktop::getInstance().getMainMonitorArea(); + + // (can't call the component's setBounds method because that'll reset our fullscreen flag) + if (r != getComponent()->getBounds() && ! r.isEmpty()) + setBounds (r.getX(), r.getY(), r.getWidth(), r.getHeight(), shouldBeFullScreen); + } + } + } + + bool isFullScreen() const + { + return fullScreen; + } + + bool contains (int x, int y, bool trueIfInAChildWindow) const + { + if (((unsigned int) x) >= (unsigned int) component->getWidth() + || ((unsigned int) y) >= (unsigned int) component->getHeight() + || ! IsValidWindowPtr (windowRef)) + return false; + + Rect r; + GetWindowBounds (windowRef, windowRegionToUse, &r); + + ::Point p; + p.h = r.left + x; + p.v = r.top + y; + + WindowRef ref2 = 0; + FindWindow (p, &ref2); + + if (windowRef != ref2) + return false; + + if (trueIfInAChildWindow) + return true; + + HIPoint p2; + p2.x = (float) x; + p2.y = (float) y; + HIViewRef hit; + + HIViewGetSubviewHit (viewRef, &p2, true, &hit); + return hit == 0 || hit == viewRef; + } + + const BorderSize getFrameSize() const + { + return BorderSize(); + } + + bool setAlwaysOnTop (bool alwaysOnTop) + { + // can't do this so return false and let the component create a new window + return false; + } + + void toFront (bool makeActiveWindow) + { + makeActiveWindow = makeActiveWindow + && component->isValidComponent() + && (component->getWantsKeyboardFocus() + || component->isCurrentlyModal()); + + if (windowRef != FrontWindow() + || (makeActiveWindow && ! IsWindowActive (windowRef)) + || ! Process::isForegroundProcess()) + { + if (! Process::isForegroundProcess()) + { + ProcessSerialNumber psn; + GetCurrentProcess (&psn); + SetFrontProcessWithOptions (&psn, kSetFrontProcessFrontWindowOnly); + } + + if (IsValidWindowPtr (windowRef)) + { + if (makeActiveWindow) + { + SelectWindow (windowRef); + SetUserFocusWindow (windowRef); + HIViewAdvanceFocus (viewRef, 0); + } + else + { + BringToFront (windowRef); + } + + handleBroughtToFront(); + } + } + } + + void toBehind (ComponentPeer* other) + { + HIViewComponentPeer* const otherWindow = dynamic_cast (other); + + if (other != 0 && windowRef != 0 && otherWindow->windowRef != 0) + { + if (windowRef == otherWindow->windowRef) + { + HIViewSetZOrder (viewRef, kHIViewZOrderBelow, otherWindow->viewRef); + } + else + { + SendBehind (windowRef, otherWindow->windowRef); + } + } + } + + void setIcon (const Image& /*newIcon*/) + { + // to do.. + } + + void viewFocusGain() + { + const MessageManagerLock messLock; + + if (currentlyFocusedPeer != this) + { + if (ComponentPeer::isValidPeer (currentlyFocusedPeer)) + currentlyFocusedPeer->handleFocusLoss(); + + currentlyFocusedPeer = this; + + handleFocusGain(); + } + } + + void viewFocusLoss() + { + if (currentlyFocusedPeer == this) + { + currentlyFocusedPeer = 0; + handleFocusLoss(); + } + } + + bool isFocused() const + { + return windowRef == GetUserFocusWindow() + && HIViewSubtreeContainsFocus (viewRef); + } + + void grabFocus() + { + if ((! isFocused()) && IsValidWindowPtr (windowRef)) + { + SetUserFocusWindow (windowRef); + HIViewAdvanceFocus (viewRef, 0); + } + } + + void repaint (int x, int y, int w, int h) + { + if (Rectangle::intersectRectangles (x, y, w, h, + 0, 0, + getComponent()->getWidth(), + getComponent()->getHeight())) + { + if ((getStyleFlags() & windowRepaintedExplictly) == 0) + { + if (isCompositingWindow) + { +#if MACOS_10_3_OR_EARLIER + RgnHandle rgn = NewRgn(); + SetRectRgn (rgn, x, y, x + w, y + h); + HIViewSetNeedsDisplayInRegion (viewRef, rgn, true); + DisposeRgn (rgn); +#else + HIRect r; + r.origin.x = x; + r.origin.y = y; + r.size.width = w; + r.size.height = h; + + HIViewSetNeedsDisplayInRect (viewRef, &r, true); +#endif + } + else + { + if (! isTimerRunning()) + startTimer (20); + } + } + + repainter->repaint (x, y, w, h); + } + } + + void timerCallback() + { + performAnyPendingRepaintsNow(); + } + + void performAnyPendingRepaintsNow() + { + stopTimer(); + + if (component->isVisible()) + { +#if MACOS_10_2_OR_EARLIER + if (! isCompositingWindow) + { + Rect w; + GetWindowBounds (windowRef, windowRegionToUse, &w); + + const int offsetInWindowX = component->getScreenX() - getScreenX(); + const int offsetInWindowY = component->getScreenY() - getScreenY(); + + for (RectangleList::Iterator i (repainter->getRegionsNeedingRepaint()); i.next();) + { + const Rectangle& r = *i.getRectangle(); + w.left = offsetInWindowX + r.getX(); + w.top = offsetInWindowY + r.getY(); + w.right = offsetInWindowX + r.getRight(); + w.bottom = offsetInWindowY + r.getBottom(); + InvalWindowRect (windowRef, &w); + } + } + else + { + EventRef theEvent; + + EventTypeSpec eventTypes[1]; + eventTypes[0].eventClass = kEventClassControl; + eventTypes[0].eventKind = kEventControlDraw; + + int n = 3; + while (--n >= 0 + && ReceiveNextEvent (1, eventTypes, kEventDurationNoWait, true, &theEvent) == noErr) + { + if (GetEventClass (theEvent) == kEventClassAppleEvent) + { + EventRecord eventRec; + if (ConvertEventRefToEventRecord (theEvent, &eventRec)) + AEProcessAppleEvent (&eventRec); + } + else + { + EventTargetRef theTarget = GetEventDispatcherTarget(); + SendEventToEventTarget (theEvent, theTarget); + } + + ReleaseEvent (theEvent); + } + } +#else + if (HIViewGetNeedsDisplay (viewRef) || repainter->isRepaintNeeded()) + HIViewRender (viewRef); +#endif + } + } + + juce_UseDebuggingNewOperator + + WindowRef windowRef; + HIViewRef viewRef; + +private: + EventHandlerRef eventHandlerRef; + bool fullScreen, isSharedWindow, isCompositingWindow; + StringArray dragAndDropFiles; + + class RepaintManager : public Timer + { +public: + RepaintManager (HIViewComponentPeer* const peer_) + : peer (peer_), + image (0) + { + } + + ~RepaintManager() + { + delete image; + } + + void timerCallback() + { + stopTimer(); + deleteAndZero (image); + } + + void repaint (int x, int y, int w, int h) + { + regionsNeedingRepaint.add (x, y, w, h); + } + + bool isRepaintNeeded() const throw() + { + return ! regionsNeedingRepaint.isEmpty(); + } + + void repaintAnyRemainingRegions() + { + // if any regions have been invaldated during the paint callback, + // we need to repaint them explicitly because the mac throws this + // stuff away + for (RectangleList::Iterator i (regionsNeedingRepaint); i.next();) + { + const Rectangle& r = *i.getRectangle(); + peer->repaint (r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + } + + void paint (CGContextRef cgContext, int x, int y, int w, int h) + { + if (w > 0 && h > 0) + { + bool refresh = false; + int imW = image != 0 ? image->getWidth() : 0; + int imH = image != 0 ? image->getHeight() : 0; + + if (imW < w || imH < h) + { + imW = jmin (peer->getComponent()->getWidth(), (w + 31) & ~31); + imH = jmin (peer->getComponent()->getHeight(), (h + 31) & ~31); + + delete image; + image = new MacBitmapImage (peer->getComponent()->isOpaque() ? Image::RGB + : Image::ARGB, + imW, imH, false); + + refresh = true; + } + else if (imageX > x || imageY > y + || imageX + imW < x + w + || imageY + imH < y + h) + { + refresh = true; + } + + if (refresh) + { + regionsNeedingRepaint.clear(); + regionsNeedingRepaint.addWithoutMerging (Rectangle (x, y, imW, imH)); + imageX = x; + imageY = y; + } + + LowLevelGraphicsSoftwareRenderer context (*image); + context.setOrigin (-imageX, -imageY); + + if (context.reduceClipRegion (regionsNeedingRepaint)) + { + regionsNeedingRepaint.clear(); + + if (! peer->getComponent()->isOpaque()) + { + for (RectangleList::Iterator i (*context.getRawClipRegion()); i.next();) + { + const Rectangle& r = *i.getRectangle(); + image->clear (r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + } + + regionsNeedingRepaint.clear(); + peer->clearMaskedRegion(); + peer->handlePaint (context); + } + else + { + regionsNeedingRepaint.clear(); + } + + if (! peer->maskedRegion.isEmpty()) + { + RectangleList total (Rectangle (x, y, w, h)); + total.subtract (peer->maskedRegion); + + CGRect* rects = (CGRect*) juce_malloc (sizeof (CGRect) * total.getNumRectangles()); + int n = 0; + + for (RectangleList::Iterator i (total); i.next();) + { + const Rectangle& r = *i.getRectangle(); + rects[n].origin.x = (int) r.getX(); + rects[n].origin.y = (int) r.getY(); + rects[n].size.width = roundFloatToInt (r.getWidth()); + rects[n++].size.height = roundFloatToInt (r.getHeight()); + } + + CGContextClipToRects (cgContext, rects, n); + juce_free (rects); + } + + if (peer->isSharedWindow) + { + CGRect clip; + clip.origin.x = x; + clip.origin.y = y; + clip.size.width = jmin (w, peer->getComponent()->getWidth() - x); + clip.size.height = jmin (h, peer->getComponent()->getHeight() - y); + + CGContextClipToRect (cgContext, clip); + } + + image->blitToContext (cgContext, imageX, imageY); + } + + startTimer (3000); + } + + private: + HIViewComponentPeer* const peer; + MacBitmapImage* image; + int imageX, imageY; + RectangleList regionsNeedingRepaint; + + RepaintManager (const RepaintManager&); + const RepaintManager& operator= (const RepaintManager&); + }; + + RepaintManager* repainter; + + friend class RepaintManager; + + static OSStatus handleFrameRepaintEvent (EventHandlerCallRef myHandler, + EventRef theEvent, + void* userData) + { + // don't draw the frame.. + return noErr; + } + + OSStatus handleKeyEvent (EventRef theEvent, juce_wchar textCharacter) + { + updateModifiers (theEvent); + + UniChar unicodeChars [4]; + zeromem (unicodeChars, sizeof (unicodeChars)); + GetEventParameter (theEvent, kEventParamKeyUnicodes, typeUnicodeText, 0, sizeof (unicodeChars), 0, unicodeChars); + + int keyCode = (int) (unsigned int) unicodeChars[0]; + + UInt32 rawKey = 0; + GetEventParameter (theEvent, kEventParamKeyCode, typeUInt32, 0, sizeof (UInt32), 0, &rawKey); + + if ((currentModifiers & ModifierKeys::ctrlModifier) != 0 + && keyCode >= 1 && keyCode <= 26) + { + keyCode += ('A' - 1); + } + else + { + static const int keyTranslations[] = + { + 0, 's', 'd', 'f', 'h', 'g', 'z', 'x', 'c', 'v', 0xa7, 'b', + 'q', 'w', 'e', 'r', 'y', 't', '1', '2', '3', '4', '6', '5', + '=', '9', '7', '-', '8', '0', ']', 'o', 'u', '[', 'i', 'p', + KeyPress::returnKey, 'l', 'j', '\'', 'k', ';', '\\', ',', '/', + 'n', 'm', '.', 0, KeyPress::spaceKey, '`', KeyPress::backspaceKey, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, KeyPress::numberPadDecimalPoint, + 0, KeyPress::numberPadMultiply, 0, KeyPress::numberPadAdd, + 0, KeyPress::numberPadDelete, 0, 0, 0, KeyPress::numberPadDivide, KeyPress::returnKey, + 0, KeyPress::numberPadSubtract, 0, 0, KeyPress::numberPadEquals, KeyPress::numberPad0, + KeyPress::numberPad1, KeyPress::numberPad2, KeyPress::numberPad3, + KeyPress::numberPad4, KeyPress::numberPad5, KeyPress::numberPad6, + KeyPress::numberPad7, 0, KeyPress::numberPad8, KeyPress::numberPad9, + 0, 0, 0, KeyPress::F5Key, KeyPress::F6Key, KeyPress::F7Key, KeyPress::F3Key, + KeyPress::F8Key, KeyPress::F9Key, 0, KeyPress::F11Key, 0, KeyPress::F13Key, + KeyPress::F16Key, KeyPress::F14Key, 0, KeyPress::F10Key, 0, KeyPress::F12Key, + 0, KeyPress::F15Key, 0, KeyPress::homeKey, KeyPress::pageUpKey, 0, KeyPress::F4Key, + KeyPress::endKey, KeyPress::F2Key, KeyPress::pageDownKey, KeyPress::F1Key, + KeyPress::leftKey, KeyPress::rightKey, KeyPress::downKey, KeyPress::upKey, 0 + }; + + if (((unsigned int) rawKey) < (unsigned int) numElementsInArray (keyTranslations) + && keyTranslations [rawKey] != 0) + { + keyCode = keyTranslations [rawKey]; + } + + if ((rawKey == 0 && textCharacter != 0) + || (CharacterFunctions::isLetterOrDigit ((juce_wchar) keyCode) + && CharacterFunctions::isLetterOrDigit (textCharacter))) // correction for azerty-type layouts.. + { + keyCode = CharacterFunctions::toLowerCase (textCharacter); + } + } + + if ((currentModifiers & (ModifierKeys::commandModifier | ModifierKeys::ctrlModifier)) != 0) + textCharacter = 0; + + static juce_wchar lastTextCharacter = 0; + + switch (GetEventKind (theEvent)) + { + case kEventRawKeyDown: + { + keysCurrentlyDown.addIfNotAlreadyThere ((void*) keyCode); + lastTextCharacter = textCharacter; + + const bool used1 = handleKeyUpOrDown(); + const bool used2 = handleKeyPress (keyCode, textCharacter); + + if (used1 || used2) + return noErr; + + break; + } + + case kEventRawKeyUp: + keysCurrentlyDown.removeValue ((void*) keyCode); + lastTextCharacter = 0; + if (handleKeyUpOrDown()) + return noErr; + + break; + + case kEventRawKeyRepeat: + if (handleKeyPress (keyCode, lastTextCharacter)) + return noErr; + + break; + + case kEventRawKeyModifiersChanged: + handleModifierKeysChange(); + break; + + default: + jassertfalse + break; + } + + return eventNotHandledErr; + } + + OSStatus handleTextInputEvent (EventRef theEvent) + { + UInt32 numBytesRequired = 0; + GetEventParameter (theEvent, kEventParamTextInputSendText, typeUnicodeText, 0, 0, &numBytesRequired, 0); + + MemoryBlock buffer (numBytesRequired, true); + UniChar* const uc = (UniChar*) buffer.getData(); + GetEventParameter (theEvent, kEventParamTextInputSendText, typeUnicodeText, 0, numBytesRequired, &numBytesRequired, uc); + + EventRef originalEvent; + GetEventParameter (theEvent, kEventParamTextInputSendKeyboardEvent, typeEventRef, 0, sizeof (originalEvent), 0, &originalEvent); + + OSStatus res = noErr; + for (int i = 0; i < numBytesRequired / sizeof (UniChar); ++i) + res = handleKeyEvent (originalEvent, (juce_wchar) uc[i]); + + return res; + } + + OSStatus handleMouseEvent (EventHandlerCallRef callRef, EventRef theEvent) + { + MouseCheckTimer::getInstance()->moved (this); + + ::Point where; + GetEventParameter (theEvent, kEventParamMouseLocation, typeQDPoint, 0, sizeof (::Point), 0, &where); + int x = where.h; + int y = where.v; + globalPositionToRelative (x, y); + + int64 time = getEventTime (theEvent); + + switch (GetEventKind (theEvent)) + { + case kEventMouseMoved: + MouseCheckTimer::getInstance()->hasEverHadAMouseMove = true; + updateModifiers (theEvent); + handleMouseMove (x, y, time); + break; + + case kEventMouseDragged: + updateModifiers (theEvent); + handleMouseDrag (x, y, time); + break; + + case kEventMouseDown: + { + if (! Process::isForegroundProcess()) + { + ProcessSerialNumber psn; + GetCurrentProcess (&psn); + SetFrontProcessWithOptions (&psn, kSetFrontProcessFrontWindowOnly); + + toFront (true); + } + +#if JUCE_QUICKTIME + { + long mods; + GetEventParameter (theEvent, kEventParamKeyModifiers, typeUInt32, 0, sizeof (mods), 0, &mods); + + ::Point where; + GetEventParameter (theEvent, kEventParamMouseLocation, typeQDPoint, 0, sizeof (::Point), 0, &where); + + OfferMouseClickToQuickTime (windowRef, where, EventTimeToTicks (GetEventTime (theEvent)), mods, component); + } +#endif + + if (component->isBroughtToFrontOnMouseClick() + && ! component->isCurrentlyBlockedByAnotherModalComponent()) + { + //ActivateWindow (windowRef, true); + SelectWindow (windowRef); + } + + EventMouseButton button; + GetEventParameter (theEvent, kEventParamMouseButton, typeMouseButton, 0, sizeof (EventMouseButton), 0, &button); + + // need to clear all these flags because sometimes the mac can swallow (right) mouse-up events and + // this makes a button get stuck down. Since there's no other way to tell what buttons are down, + // this is all I can think of doing about it.. + currentModifiers &= ~(ModifierKeys::leftButtonModifier | ModifierKeys::rightButtonModifier | ModifierKeys::middleButtonModifier); + + if (button == kEventMouseButtonPrimary) + currentModifiers |= ModifierKeys::leftButtonModifier; + else if (button == kEventMouseButtonSecondary) + currentModifiers |= ModifierKeys::rightButtonModifier; + else if (button == kEventMouseButtonTertiary) + currentModifiers |= ModifierKeys::middleButtonModifier; + + updateModifiers (theEvent); + + juce_currentMouseTrackingPeer = this; // puts the message dispatcher into mouse-tracking mode.. + handleMouseDown (x, y, time); + break; + } + + case kEventMouseUp: + { + const int oldModifiers = currentModifiers; + + EventMouseButton button; + GetEventParameter (theEvent, kEventParamMouseButton, typeMouseButton, 0, sizeof (EventMouseButton), 0, &button); + + if (button == kEventMouseButtonPrimary) + currentModifiers &= ~ModifierKeys::leftButtonModifier; + else if (button == kEventMouseButtonSecondary) + currentModifiers &= ~ModifierKeys::rightButtonModifier; + + updateModifiers (theEvent); + + juce_currentMouseTrackingPeer = 0; + handleMouseUp (oldModifiers, x, y, time); + break; + } + + case kEventMouseWheelMoved: + { + EventMouseWheelAxis axis; + GetEventParameter (theEvent, kEventParamMouseWheelAxis, typeMouseWheelAxis, 0, sizeof (axis), 0, &axis); + + SInt32 delta; + GetEventParameter (theEvent, kEventParamMouseWheelDelta, + typeLongInteger, 0, sizeof (delta), 0, &delta); + + updateModifiers (theEvent); + + handleMouseWheel (axis == kEventMouseWheelAxisX ? delta * 10 : 0, + axis == kEventMouseWheelAxisX ? 0 : delta * 10, + time); + + break; + } + } + + return noErr; + } + + void doDragDropEnter (EventRef theEvent) + { + updateDragAndDropFileList (theEvent); + + if (dragAndDropFiles.size() > 0) + { + int x, y; + component->getMouseXYRelative (x, y); + handleFileDragMove (dragAndDropFiles, x, y); + } + } + + void doDragDropMove (EventRef theEvent) + { + if (dragAndDropFiles.size() > 0) + { + int x, y; + component->getMouseXYRelative (x, y); + handleFileDragMove (dragAndDropFiles, x, y); + } + } + + void doDragDropExit (EventRef theEvent) + { + if (dragAndDropFiles.size() > 0) + handleFileDragExit (dragAndDropFiles); + } + + void doDragDrop (EventRef theEvent) + { + updateDragAndDropFileList (theEvent); + + if (dragAndDropFiles.size() > 0) + { + int x, y; + component->getMouseXYRelative (x, y); + handleFileDragDrop (dragAndDropFiles, x, y); + } + } + + void updateDragAndDropFileList (EventRef theEvent) + { + dragAndDropFiles.clear(); + + DragRef dragRef; + if (GetEventParameter (theEvent, kEventParamDragRef, typeDragRef, 0, sizeof (dragRef), 0, &dragRef) == noErr) + { + UInt16 numItems = 0; + if (CountDragItems (dragRef, &numItems) == noErr) + { + for (int i = 0; i < (int) numItems; ++i) + { + DragItemRef ref; + + if (GetDragItemReferenceNumber (dragRef, i + 1, &ref) == noErr) + { + const FlavorType flavorType = kDragFlavorTypeHFS; + + Size size = 0; + if (GetFlavorDataSize (dragRef, ref, flavorType, &size) == noErr) + { + void* data = juce_calloc (size); + + if (GetFlavorData (dragRef, ref, flavorType, data, &size, 0) == noErr) + { + HFSFlavor* f = (HFSFlavor*) data; + FSRef fsref; + + if (FSpMakeFSRef (&f->fileSpec, &fsref) == noErr) + { + const String path (PlatformUtilities::makePathFromFSRef (&fsref)); + + if (path.isNotEmpty()) + dragAndDropFiles.add (path); + } + } + + juce_free (data); + } + } + } + + dragAndDropFiles.trim(); + dragAndDropFiles.removeEmptyStrings(); + } + } + } + + void resizeViewToFitWindow() + { + HIRect r; + + if (isSharedWindow) + { + HIViewGetFrame (viewRef, &r); + r.size.width = (float) component->getWidth(); + r.size.height = (float) component->getHeight(); + } + else + { + r.origin.x = 0; + r.origin.y = 0; + + Rect w; + GetWindowBounds (windowRef, windowRegionToUse, &w); + + r.size.width = (float) (w.right - w.left); + r.size.height = (float) (w.bottom - w.top); + } + + HIViewSetFrame (viewRef, &r); + +#if MACOS_10_3_OR_EARLIER + component->repaint(); +#endif + } + + OSStatus hiViewDraw (EventRef theEvent) + { + CGContextRef context = 0; + GetEventParameter (theEvent, kEventParamCGContextRef, typeCGContextRef, 0, sizeof (CGContextRef), 0, &context); + + CGrafPtr oldPort; + CGrafPtr port = 0; + + if (context == 0) + { + GetEventParameter (theEvent, kEventParamGrafPort, typeGrafPtr, 0, sizeof (CGrafPtr), 0, &port); + + GetPort (&oldPort); + SetPort (port); + + if (port != 0) + QDBeginCGContext (port, &context); + + if (! isCompositingWindow) + { + Rect bounds; + GetWindowBounds (windowRef, windowRegionToUse, &bounds); + CGContextTranslateCTM (context, 0, bounds.bottom - bounds.top); + CGContextScaleCTM (context, 1.0, -1.0); + } + + if (isSharedWindow) + { + // NB - Had terrible problems trying to correctly get the position + // of this view relative to the window, and this seems wrong, but + // works better than any other method I've tried.. + HIRect hiViewPos; + HIViewGetFrame (viewRef, &hiViewPos); + CGContextTranslateCTM (context, hiViewPos.origin.x, hiViewPos.origin.y); + } + } + +#if MACOS_10_2_OR_EARLIER + RgnHandle rgn = 0; + GetEventParameter (theEvent, kEventParamRgnHandle, typeQDRgnHandle, 0, sizeof (RgnHandle), 0, &rgn); + + CGRect clip; + + // (avoid doing this in plugins because of some strange redraw bugs..) + if (rgn != 0 && JUCEApplication::getInstance() != 0) + { + Rect bounds; + GetRegionBounds (rgn, &bounds); + clip.origin.x = bounds.left; + clip.origin.y = bounds.top; + clip.size.width = bounds.right - bounds.left; + clip.size.height = bounds.bottom - bounds.top; + } + else + { + HIViewGetBounds (viewRef, &clip); + clip.origin.x = 0; + clip.origin.y = 0; + } +#else + CGRect clip (CGContextGetClipBoundingBox (context)); +#endif + + clip = CGRectIntegral (clip); + + if (clip.origin.x < 0) + { + clip.size.width += clip.origin.x; + clip.origin.x = 0; + } + + if (clip.origin.y < 0) + { + clip.size.height += clip.origin.y; + clip.origin.y = 0; + } + + if (! component->isOpaque()) + CGContextClearRect (context, clip); + + repainter->paint (context, + (int) clip.origin.x, (int) clip.origin.y, + (int) clip.size.width, (int) clip.size.height); + + if (port != 0) + { + CGContextFlush (context); + QDEndCGContext (port, &context); + + SetPort (oldPort); + } + + repainter->repaintAnyRemainingRegions(); + + return noErr; + } + + OSStatus handleWindowClassEvent (EventRef theEvent) + { + switch (GetEventKind (theEvent)) + { + case kEventWindowBoundsChanged: + resizeViewToFitWindow(); + break; // allow other handlers in the event chain to also get a look at the events + + case kEventWindowBoundsChanging: + if ((styleFlags & (windowIsResizable | windowHasTitleBar)) == (windowIsResizable | windowHasTitleBar)) + { + UInt32 atts = 0; + GetEventParameter (theEvent, kEventParamAttributes, typeUInt32, + 0, sizeof (UInt32), 0, &atts); + + if ((atts & (kWindowBoundsChangeUserDrag | kWindowBoundsChangeUserResize)) != 0) + { + if (component->isCurrentlyBlockedByAnotherModalComponent()) + { + Component* const modal = Component::getCurrentlyModalComponent(); + if (modal != 0) + { + static uint32 lastDragTime = 0; + const uint32 now = Time::currentTimeMillis(); + + if (now > lastDragTime + 1000) + { + lastDragTime = now; + modal->inputAttemptWhenModal(); + } + + const Rectangle currentRect (getComponent()->getBounds()); + Rect current; + current.left = currentRect.getX(); + current.top = currentRect.getY(); + current.right = currentRect.getRight(); + current.bottom = currentRect.getBottom(); + + // stop the window getting dragged.. + SetEventParameter (theEvent, kEventParamCurrentBounds, typeQDRectangle, + sizeof (Rect), ¤t); + + return noErr; + } + } + + if ((atts & kWindowBoundsChangeUserResize) != 0 + && constrainer != 0 && ! isSharedWindow) + { + Rect current; + GetEventParameter (theEvent, kEventParamCurrentBounds, typeQDRectangle, + 0, sizeof (Rect), 0, ¤t); + + int x = current.left; + int y = current.top; + int w = current.right - current.left; + int h = current.bottom - current.top; + + const Rectangle currentRect (getComponent()->getBounds()); + + constrainer->checkBounds (x, y, w, h, currentRect, + Desktop::getInstance().getAllMonitorDisplayAreas().getBounds(), + y != currentRect.getY() && y + h == currentRect.getBottom(), + x != currentRect.getX() && x + w == currentRect.getRight(), + y == currentRect.getY() && y + h != currentRect.getBottom(), + x == currentRect.getX() && x + w != currentRect.getRight()); + + current.left = x; + current.top = y; + current.right = x + w; + current.bottom = y + h; + + SetEventParameter (theEvent, kEventParamCurrentBounds, typeQDRectangle, + sizeof (Rect), ¤t); + + return noErr; + } + } + } + break; + + case kEventWindowFocusAcquired: + keysCurrentlyDown.clear(); + + if ((! isSharedWindow) || HIViewSubtreeContainsFocus (viewRef)) + viewFocusGain(); + + break; // allow other handlers in the event chain to also get a look at the events + + case kEventWindowFocusRelinquish: + keysCurrentlyDown.clear(); + viewFocusLoss(); + + break; // allow other handlers in the event chain to also get a look at the events + + case kEventWindowCollapsed: + minimisedWindows.addIfNotAlreadyThere (windowRef); + handleMovedOrResized(); + break; // allow other handlers in the event chain to also get a look at the events + + case kEventWindowExpanded: + minimisedWindows.removeValue (windowRef); + handleMovedOrResized(); + break; // allow other handlers in the event chain to also get a look at the events + + case kEventWindowShown: + break; // allow other handlers in the event chain to also get a look at the events + + case kEventWindowClose: + if (isSharedWindow) + break; // break to let the OS delete the window + + handleUserClosingWindow(); + return noErr; // avoids letting the OS to delete the window, we'll do that ourselves. + + default: + break; + } + + return eventNotHandledErr; + } + + OSStatus handleWindowEventForPeer (EventHandlerCallRef callRef, EventRef theEvent) + { + switch (GetEventClass (theEvent)) + { + case kEventClassMouse: + { + static HIViewComponentPeer* lastMouseDownPeer = 0; + + const UInt32 eventKind = GetEventKind (theEvent); + HIViewRef view = 0; + + if (eventKind == kEventMouseDragged) + { + view = viewRef; + } + else + { + HIViewGetViewForMouseEvent (HIViewGetRoot (windowRef), theEvent, &view); + + if (view != viewRef) + { + if ((eventKind == kEventMouseUp + || eventKind == kEventMouseExited) + && ComponentPeer::isValidPeer (lastMouseDownPeer)) + { + return lastMouseDownPeer->handleMouseEvent (callRef, theEvent); + } + + return eventNotHandledErr; + } + } + + if (eventKind == kEventMouseDown + || eventKind == kEventMouseDragged + || eventKind == kEventMouseEntered) + { + lastMouseDownPeer = this; + } + + return handleMouseEvent (callRef, theEvent); + } + break; + + case kEventClassWindow: + return handleWindowClassEvent (theEvent); + + case kEventClassKeyboard: + if (isFocused()) + return handleKeyEvent (theEvent, 0); + + break; + + case kEventClassTextInput: + if (isFocused()) + return handleTextInputEvent (theEvent); + + break; + + default: + break; + } + + return eventNotHandledErr; + } + + static pascal OSStatus handleWindowEvent (EventHandlerCallRef callRef, EventRef theEvent, void* userData) + { + MessageManager::delayWaitCursor(); + + HIViewComponentPeer* const peer = (HIViewComponentPeer*) userData; + + const MessageManagerLock messLock; + + if (ComponentPeer::isValidPeer (peer)) + return peer->handleWindowEventForPeer (callRef, theEvent); + + return eventNotHandledErr; + } + + static pascal OSStatus hiViewEventHandler (EventHandlerCallRef myHandler, EventRef theEvent, void* userData) + { + MessageManager::delayWaitCursor(); + + const UInt32 eventKind = GetEventKind (theEvent); + const UInt32 eventClass = GetEventClass (theEvent); + + if (eventClass == kEventClassHIObject) + { + switch (eventKind) + { + case kEventHIObjectConstruct: + { + void* data = juce_calloc (sizeof (void*)); + SetEventParameter (theEvent, kEventParamHIObjectInstance, + typeVoidPtr, sizeof (void*), &data); + + return noErr; + } + + case kEventHIObjectInitialize: + GetEventParameter (theEvent, 'peer', typeVoidPtr, 0, sizeof (void*), 0, (void**) userData); + return noErr; + + case kEventHIObjectDestruct: + juce_free (userData); + return noErr; + + default: + break; + } + } + else if (eventClass == kEventClassControl) + { + HIViewComponentPeer* const peer = *(HIViewComponentPeer**) userData; + const MessageManagerLock messLock; + + if (! ComponentPeer::isValidPeer (peer)) + return eventNotHandledErr; + + switch (eventKind) + { + case kEventControlDraw: + return peer->hiViewDraw (theEvent); + + case kEventControlBoundsChanged: + { + HIRect bounds; + HIViewGetBounds (peer->viewRef, &bounds); + peer->repaint (0, 0, roundFloatToInt (bounds.size.width), roundFloatToInt (bounds.size.height)); + + peer->handleMovedOrResized(); + return noErr; + } + + case kEventControlHitTest: + { + HIPoint where; + GetEventParameter (theEvent, kEventParamMouseLocation, typeHIPoint, 0, sizeof (HIPoint), 0, &where); + + HIRect bounds; + HIViewGetBounds (peer->viewRef, &bounds); + + ControlPartCode part = kControlNoPart; + + if (CGRectContainsPoint (bounds, where)) + part = 1; + + SetEventParameter (theEvent, kEventParamControlPart, typeControlPartCode, sizeof (ControlPartCode), &part); + return noErr; + } + break; + + case kEventControlSetFocusPart: + { + ControlPartCode desiredFocus; + if (GetEventParameter (theEvent, kEventParamControlPart, typeControlPartCode, 0, sizeof (ControlPartCode), 0, &desiredFocus) != noErr) + break; + + if (desiredFocus == kControlNoPart) + peer->viewFocusLoss(); + else + peer->viewFocusGain(); + + return noErr; + } + break; + + case kEventControlDragEnter: + { +#if MACOS_10_2_OR_EARLIER + enum { kEventParamControlWouldAcceptDrop = 'cldg' }; +#endif + Boolean accept = true; + SetEventParameter (theEvent, kEventParamControlWouldAcceptDrop, typeBoolean, sizeof (accept), &accept); + + peer->doDragDropEnter (theEvent); + return noErr; + } + + case kEventControlDragWithin: + peer->doDragDropMove (theEvent); + return noErr; + + case kEventControlDragLeave: + peer->doDragDropExit (theEvent); + return noErr; + + case kEventControlDragReceive: + peer->doDragDrop (theEvent); + return noErr; + + case kEventControlOwningWindowChanged: + return peer->ownerWindowChanged (theEvent); + +#if ! MACOS_10_2_OR_EARLIER + case kEventControlGetFrameMetrics: + { + CallNextEventHandler (myHandler, theEvent); + HIViewFrameMetrics metrics; + GetEventParameter (theEvent, kEventParamControlFrameMetrics, typeControlFrameMetrics, 0, sizeof (metrics), 0, &metrics); + metrics.top = metrics.bottom = 0; + SetEventParameter (theEvent, kEventParamControlFrameMetrics, typeControlFrameMetrics, sizeof (metrics), &metrics); + return noErr; + } +#endif + + case kEventControlInitialize: + { + UInt32 features = kControlSupportsDragAndDrop + | kControlSupportsFocus + | kControlHandlesTracking + | kControlSupportsEmbedding + | (1 << 8) /*kHIViewFeatureGetsFocusOnClick*/; + + SetEventParameter (theEvent, kEventParamControlFeatures, typeUInt32, sizeof (UInt32), &features); + return noErr; + } + + default: + break; + } + } + + return eventNotHandledErr; + } + + WindowRef createNewWindow (const int windowStyleFlags) + { + jassert (windowRef == 0); + + static ToolboxObjectClassRef customWindowClass = 0; + + if (customWindowClass == 0) + { + // Register our window class + const EventTypeSpec customTypes[] = { { kEventClassWindow, kEventWindowDrawFrame } }; + + UnsignedWide t; + Microseconds (&t); + const String randomString ((int) (t.lo & 0x7ffffff)); + const String juceWindowClassName (T("JUCEWindowClass_") + randomString); + CFStringRef juceWindowClassNameCFString = PlatformUtilities::juceStringToCFString (juceWindowClassName); + + RegisterToolboxObjectClass (juceWindowClassNameCFString, + 0, 1, customTypes, + NewEventHandlerUPP (handleFrameRepaintEvent), + 0, &customWindowClass); + + CFRelease (juceWindowClassNameCFString); + } + + Rect pos; + pos.left = getComponent()->getX(); + pos.top = getComponent()->getY(); + pos.right = getComponent()->getRight(); + pos.bottom = getComponent()->getBottom(); + + int attributes = kWindowStandardHandlerAttribute | kWindowCompositingAttribute; + if ((windowStyleFlags & windowHasDropShadow) == 0) + attributes |= kWindowNoShadowAttribute; + + if ((windowStyleFlags & windowIgnoresMouseClicks) != 0) + attributes |= kWindowIgnoreClicksAttribute; + +#if ! MACOS_10_3_OR_EARLIER + if ((windowStyleFlags & windowIsTemporary) != 0) + attributes |= kWindowDoesNotCycleAttribute; +#endif + + WindowRef newWindow = 0; + + if ((windowStyleFlags & windowHasTitleBar) == 0) + { + attributes |= kWindowCollapseBoxAttribute; + + WindowDefSpec customWindowSpec; + customWindowSpec.defType = kWindowDefObjectClass; + customWindowSpec.u.classRef = customWindowClass; + + CreateCustomWindow (&customWindowSpec, + ((windowStyleFlags & windowIsTemporary) != 0) ? kUtilityWindowClass : + (getComponent()->isAlwaysOnTop() ? kUtilityWindowClass + : kDocumentWindowClass), + attributes, + &pos, + &newWindow); + } + else + { + if ((windowStyleFlags & windowHasCloseButton) != 0) + attributes |= kWindowCloseBoxAttribute; + + if ((windowStyleFlags & windowHasMinimiseButton) != 0) + attributes |= kWindowCollapseBoxAttribute; + + if ((windowStyleFlags & windowHasMaximiseButton) != 0) + attributes |= kWindowFullZoomAttribute; + + if ((windowStyleFlags & windowIsResizable) != 0) + attributes |= kWindowResizableAttribute | kWindowLiveResizeAttribute; + + CreateNewWindow (kDocumentWindowClass, attributes, &pos, &newWindow); + } + + jassert (newWindow != 0); + if (newWindow != 0) + { + HideWindow (newWindow); + + SetAutomaticControlDragTrackingEnabledForWindow (newWindow, true); + + if (! getComponent()->isOpaque()) + SetWindowAlpha (newWindow, 0.9999999f); // to fool it into giving the window an alpha-channel + } + + return newWindow; + } + + OSStatus ownerWindowChanged (EventRef theEvent) + { + WindowRef newWindow = 0; + GetEventParameter (theEvent, kEventParamControlCurrentOwningWindow, typeWindowRef, 0, sizeof (newWindow), 0, &newWindow); + + if (windowRef != newWindow) + { + if (eventHandlerRef != 0) + { + RemoveEventHandler (eventHandlerRef); + eventHandlerRef = 0; + } + + windowRef = newWindow; + + if (windowRef != 0) + { + const EventTypeSpec eventTypes[] = + { + { kEventClassWindow, kEventWindowBoundsChanged }, + { kEventClassWindow, kEventWindowBoundsChanging }, + { kEventClassWindow, kEventWindowFocusAcquired }, + { kEventClassWindow, kEventWindowFocusRelinquish }, + { kEventClassWindow, kEventWindowCollapsed }, + { kEventClassWindow, kEventWindowExpanded }, + { kEventClassWindow, kEventWindowShown }, + { kEventClassWindow, kEventWindowClose }, + { kEventClassMouse, kEventMouseDown }, + { kEventClassMouse, kEventMouseUp }, + { kEventClassMouse, kEventMouseMoved }, + { kEventClassMouse, kEventMouseDragged }, + { kEventClassMouse, kEventMouseEntered }, + { kEventClassMouse, kEventMouseExited }, + { kEventClassMouse, kEventMouseWheelMoved }, + { kEventClassKeyboard, kEventRawKeyUp }, + { kEventClassKeyboard, kEventRawKeyRepeat }, + { kEventClassKeyboard, kEventRawKeyModifiersChanged }, + { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent } + }; + + static EventHandlerUPP handleWindowEventUPP = 0; + + if (handleWindowEventUPP == 0) + handleWindowEventUPP = NewEventHandlerUPP (handleWindowEvent); + + InstallWindowEventHandler (windowRef, handleWindowEventUPP, + GetEventTypeCount (eventTypes), eventTypes, + (void*) this, (EventHandlerRef*) &eventHandlerRef); + + WindowAttributes attributes; + GetWindowAttributes (windowRef, &attributes); + +#if MACOS_10_3_OR_EARLIER + isCompositingWindow = ((attributes & kWindowCompositingAttribute) != 0); +#else + isCompositingWindow = HIViewIsCompositingEnabled (viewRef); +#endif + + SetAutomaticControlDragTrackingEnabledForWindow (newWindow, true); + + MouseCheckTimer::getInstance()->resetMouseMoveChecker(); + } + } + + resizeViewToFitWindow(); + return noErr; + } + + void createNewHIView() + { + jassert (viewRef == 0); + + if (viewClassRef == 0) + { + // Register our HIView class + EventTypeSpec viewEvents[] = + { + { kEventClassHIObject, kEventHIObjectConstruct }, + { kEventClassHIObject, kEventHIObjectInitialize }, + { kEventClassHIObject, kEventHIObjectDestruct }, + { kEventClassControl, kEventControlInitialize }, + { kEventClassControl, kEventControlDraw }, + { kEventClassControl, kEventControlBoundsChanged }, + { kEventClassControl, kEventControlSetFocusPart }, + { kEventClassControl, kEventControlHitTest }, + { kEventClassControl, kEventControlDragEnter }, + { kEventClassControl, kEventControlDragLeave }, + { kEventClassControl, kEventControlDragWithin }, + { kEventClassControl, kEventControlDragReceive }, + { kEventClassControl, kEventControlOwningWindowChanged } + }; + + UnsignedWide t; + Microseconds (&t); + const String randomString ((int) (t.lo & 0x7ffffff)); + const String juceHiViewClassName (T("JUCEHIViewClass_") + randomString); + juceHiViewClassNameCFString = PlatformUtilities::juceStringToCFString (juceHiViewClassName); + + HIObjectRegisterSubclass (juceHiViewClassNameCFString, + kHIViewClassID, 0, + NewEventHandlerUPP (hiViewEventHandler), + GetEventTypeCount (viewEvents), + viewEvents, 0, + &viewClassRef); + } + + EventRef event; + CreateEvent (0, kEventClassHIObject, kEventHIObjectInitialize, GetCurrentEventTime(), kEventAttributeNone, &event); + + void* thisPointer = this; + SetEventParameter (event, 'peer', typeVoidPtr, sizeof (void*), &thisPointer); + + HIObjectCreate (juceHiViewClassNameCFString, event, (HIObjectRef*) &viewRef); + + SetControlDragTrackingEnabled (viewRef, true); + + if (isSharedWindow) + { + setBounds (component->getX(), component->getY(), + component->getWidth(), component->getHeight(), false); + } + } +}; + +bool juce_isHIViewCreatedByJuce (HIViewRef view) +{ + return juceHiViewClassNameCFString != 0 + && HIObjectIsOfClass ((HIObjectRef) view, juceHiViewClassNameCFString); +} + +bool juce_isWindowCreatedByJuce (WindowRef window) +{ + for (int i = ComponentPeer::getNumPeers(); --i >= 0;) + if (ComponentPeer::getPeer(i)->getNativeHandle() == window) + return true; + + return false; +} + +static void trackNextMouseEvent() +{ + UInt32 mods; + MouseTrackingResult result; + ::Point where; + + if (TrackMouseLocationWithOptions ((GrafPtr) -1, 0, 0.01, //kEventDurationForever, + &where, &mods, &result) != noErr + || ! ComponentPeer::isValidPeer (juce_currentMouseTrackingPeer)) + { + juce_currentMouseTrackingPeer = 0; + return; + } + + if (result == kMouseTrackingTimedOut) + return; + +#if MACOS_10_3_OR_EARLIER + const int x = where.h - juce_currentMouseTrackingPeer->getScreenX(); + const int y = where.v - juce_currentMouseTrackingPeer->getScreenY(); +#else + HIPoint p; + p.x = where.h; + p.y = where.v; + HIPointConvert (&p, kHICoordSpaceScreenPixel, 0, + kHICoordSpaceView, ((HIViewComponentPeer*) juce_currentMouseTrackingPeer)->viewRef); + const int x = p.x; + const int y = p.y; +#endif + + if (result == kMouseTrackingMouseDragged) + { + updateModifiers (0); + juce_currentMouseTrackingPeer->handleMouseDrag (x, y, getEventTime (0)); + + if (! ComponentPeer::isValidPeer (juce_currentMouseTrackingPeer)) + { + juce_currentMouseTrackingPeer = 0; + return; + } + } + else if (result == kMouseTrackingMouseUp + || result == kMouseTrackingUserCancelled + || result == kMouseTrackingMouseMoved) + { + ComponentPeer* const oldPeer = juce_currentMouseTrackingPeer; + juce_currentMouseTrackingPeer = 0; + + if (ComponentPeer::isValidPeer (oldPeer)) + { + const int oldModifiers = currentModifiers; + currentModifiers &= ~(ModifierKeys::leftButtonModifier | ModifierKeys::rightButtonModifier | ModifierKeys::middleButtonModifier); + updateModifiers (0); + + oldPeer->handleMouseUp (oldModifiers, x, y, getEventTime (0)); + } + } +} + +bool juce_dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages) +{ + if (juce_currentMouseTrackingPeer != 0) + trackNextMouseEvent(); + + EventRef theEvent; + + if (ReceiveNextEvent (0, 0, (returnIfNoPendingMessages) ? kEventDurationNoWait + : kEventDurationForever, + true, &theEvent) == noErr) + { + if (GetEventClass (theEvent) == kEventClassAppleEvent) + { + EventRecord eventRec; + if (ConvertEventRefToEventRecord (theEvent, &eventRec)) + AEProcessAppleEvent (&eventRec); + } + else + { + EventTargetRef theTarget = GetEventDispatcherTarget(); + SendEventToEventTarget (theEvent, theTarget); + } + + ReleaseEvent (theEvent); + return true; + } + + return false; +} + +ComponentPeer* Component::createNewPeer (int styleFlags, void* windowToAttachTo) +{ + return new HIViewComponentPeer (this, styleFlags, (HIViewRef) windowToAttachTo); +} + +void MouseCheckTimer::timerCallback() +{ + if (ModifierKeys::getCurrentModifiersRealtime().isAnyMouseButtonDown()) + return; + + if (Process::isForegroundProcess()) + { + bool stillOver = false; + int x = 0, y = 0, w = 0, h = 0; + int mx = 0, my = 0; + const bool validWindow = ComponentPeer::isValidPeer (lastPeerUnderMouse); + + if (validWindow) + { + lastPeerUnderMouse->getBounds (x, y, w, h, true); + Desktop::getMousePosition (mx, my); + + stillOver = (mx >= x && my >= y && mx < x + w && my < y + h); + + if (stillOver) + { + // check if it's over an embedded HIView + int rx = mx, ry = my; + lastPeerUnderMouse->globalPositionToRelative (rx, ry); + HIPoint hipoint; + hipoint.x = rx; + hipoint.y = ry; + + HIViewRef root; + GetRootControl ((WindowRef) lastPeerUnderMouse->getNativeHandle(), &root); + + HIViewRef hitview; + if (HIViewGetSubviewHit (root, &hipoint, true, &hitview) == noErr && hitview != 0) + { + stillOver = HIObjectIsOfClass ((HIObjectRef) hitview, juceHiViewClassNameCFString); + } + } + } + + if (! stillOver) + { + // mouse is outside our windows so set a normal cursor (only + // if we're running as an app, not a plugin) + if (JUCEApplication::getInstance() != 0) + SetThemeCursor (kThemeArrowCursor); + + if (validWindow) + lastPeerUnderMouse->handleMouseExit (mx - x, my - y, Time::currentTimeMillis()); + + if (hasEverHadAMouseMove) + stopTimer(); + } + + if ((! hasEverHadAMouseMove) && validWindow + && (mx != lastX || my != lastY)) + { + lastX = mx; + lastY = my; + + if (stillOver) + lastPeerUnderMouse->handleMouseMove (mx - x, my - y, Time::currentTimeMillis()); + } + } +} + +// called from juce_Messaging.cpp +void juce_HandleProcessFocusChange() +{ + keysCurrentlyDown.clear(); + + if (HIViewComponentPeer::isValidPeer (currentlyFocusedPeer)) + { + if (Process::isForegroundProcess()) + currentlyFocusedPeer->handleFocusGain(); + else + currentlyFocusedPeer->handleFocusLoss(); + } +} + +static bool performDrag (DragRef drag) +{ + EventRecord event; + event.what = mouseDown; + event.message = 0; + event.when = TickCount(); + + int x, y; + Desktop::getMousePosition (x, y); + event.where.h = x; + event.where.v = y; + + event.modifiers = GetCurrentKeyModifiers(); + + RgnHandle rgn = NewRgn(); + RgnHandle rgn2 = NewRgn(); + SetRectRgn (rgn, + event.where.h - 8, event.where.v - 8, + event.where.h + 8, event.where.v + 8); + CopyRgn (rgn, rgn2); + InsetRgn (rgn2, 1, 1); + DiffRgn (rgn, rgn2, rgn); + DisposeRgn (rgn2); + + bool result = TrackDrag (drag, &event, rgn) == noErr; + + DisposeRgn (rgn); + return result; +} + +bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, const bool canMoveFiles) +{ + for (int i = ComponentPeer::getNumPeers(); --i >= 0;) + ComponentPeer::getPeer (i)->performAnyPendingRepaintsNow(); + + DragRef drag; + bool result = false; + + if (NewDrag (&drag) == noErr) + { + for (int i = 0; i < files.size(); ++i) + { + HFSFlavor hfsData; + + if (PlatformUtilities::makeFSSpecFromPath (&hfsData.fileSpec, files[i])) + { + FInfo info; + if (FSpGetFInfo (&hfsData.fileSpec, &info) == noErr) + { + hfsData.fileType = info.fdType; + hfsData.fileCreator = info.fdCreator; + hfsData.fdFlags = info.fdFlags; + + AddDragItemFlavor (drag, i + 1, kDragFlavorTypeHFS, &hfsData, sizeof (hfsData), 0); + result = true; + } + } + } + + SetDragAllowableActions (drag, canMoveFiles ? kDragActionAll + : kDragActionCopy, false); + + if (result) + result = performDrag (drag); + + DisposeDrag (drag); + } + + return result; +} + +bool DragAndDropContainer::performExternalDragDropOfText (const String& text) +{ + jassertfalse // not implemented! + return false; +} + +bool Process::isForegroundProcess() throw() +{ + ProcessSerialNumber psn, front; + GetCurrentProcess (&psn); + GetFrontProcess (&front); + + Boolean b; + return (SameProcess (&psn, &front, &b) == noErr) && b; +} + +bool Desktop::canUseSemiTransparentWindows() throw() +{ + return true; +} + +void Desktop::getMousePosition (int& x, int& y) throw() +{ + CGrafPtr currentPort; + GetPort (¤tPort); + + if (! IsValidPort (currentPort)) + { + WindowRef front = FrontWindow(); + + if (front != 0) + { + SetPortWindowPort (front); + } + else + { + x = y = 0; + return; + } + } + + ::Point p; + GetMouse (&p); + LocalToGlobal (&p); + x = p.h; + y = p.v; + + SetPort (currentPort); +} + +void Desktop::setMousePosition (int x, int y) throw() +{ + // this rubbish needs to be done around the warp call, to avoid causing a + // bizarre glitch.. + CGAssociateMouseAndMouseCursorPosition (false); + CGSetLocalEventsSuppressionInterval (0); + + CGPoint pos = { x, y }; + CGWarpMouseCursorPosition (pos); + + CGAssociateMouseAndMouseCursorPosition (true); +} + +const ModifierKeys ModifierKeys::getCurrentModifiersRealtime() throw() +{ + return ModifierKeys (currentModifiers); +} + +class ScreenSaverDefeater : public Timer, + public DeletedAtShutdown +{ +public: + ScreenSaverDefeater() throw() + { + startTimer (10000); + timerCallback(); + } + + ~ScreenSaverDefeater() + { + } + + void timerCallback() + { + if (Process::isForegroundProcess()) + UpdateSystemActivity (UsrActivity); + } +}; + +static ScreenSaverDefeater* screenSaverDefeater = 0; + +void Desktop::setScreenSaverEnabled (const bool isEnabled) throw() +{ + if (screenSaverDefeater == 0) + screenSaverDefeater = new ScreenSaverDefeater(); +} + +bool Desktop::isScreenSaverEnabled() throw() +{ + return screenSaverDefeater == 0; +} + +void juce_updateMultiMonitorInfo (Array & monitorCoords, const bool clipToWorkArea) throw() +{ + int mainMonitorIndex = 0; + CGDirectDisplayID mainDisplayID = CGMainDisplayID(); + + CGDisplayCount count = 0; + CGDirectDisplayID disps [8]; + + if (CGGetOnlineDisplayList (numElementsInArray (disps), disps, &count) == noErr) + { + for (int i = 0; i < count; ++i) + { + if (mainDisplayID == disps[i]) + mainMonitorIndex = monitorCoords.size(); + + GDHandle hGDevice; + + if (clipToWorkArea + && DMGetGDeviceByDisplayID ((DisplayIDType) disps[i], &hGDevice, false) == noErr) + { + Rect rect; + GetAvailableWindowPositioningBounds (hGDevice, &rect); + + monitorCoords.add (Rectangle (rect.left, + rect.top, + rect.right - rect.left, + rect.bottom - rect.top)); + } + else + { + const CGRect r (CGDisplayBounds (disps[i])); + + monitorCoords.add (Rectangle ((int) r.origin.x, + (int) r.origin.y, + (int) r.size.width, + (int) r.size.height)); + } + } + } + + // make sure the first in the list is the main monitor + if (mainMonitorIndex > 0) + monitorCoords.swap (mainMonitorIndex, 0); + + jassert (monitorCoords.size() > 0); + + if (monitorCoords.size() == 0) + monitorCoords.add (Rectangle (0, 0, 1024, 768)); +} + +struct CursorWrapper +{ + Cursor* cursor; + ThemeCursor themeCursor; +}; + +void* juce_createMouseCursorFromImage (const Image& image, int hotspotX, int hotspotY) throw() +{ + const int maxW = 16; + const int maxH = 16; + + const Image* im = ℑ + Image* newIm = 0; + + if (image.getWidth() > maxW || image.getHeight() > maxH) + { + im = newIm = image.createCopy (maxW, maxH); + + hotspotX = (hotspotX * maxW) / image.getWidth(); + hotspotY = (hotspotY * maxH) / image.getHeight(); + } + + Cursor* const c = new Cursor(); + c->hotSpot.h = hotspotX; + c->hotSpot.v = hotspotY; + + for (int y = 0; y < maxH; ++y) + { + c->data[y] = 0; + c->mask[y] = 0; + + for (int x = 0; x < maxW; ++x) + { + const Colour pixelColour (im->getPixelAt (15 - x, y)); + + if (pixelColour.getAlpha() > 0.5f) + { + c->mask[y] |= (1 << x); + + if (pixelColour.getBrightness() < 0.5f) + c->data[y] |= (1 << x); + } + } + + c->data[y] = CFSwapInt16BigToHost (c->data[y]); + c->mask[y] = CFSwapInt16BigToHost (c->mask[y]); + } + + if (newIm != 0) + delete newIm; + + CursorWrapper* const cw = new CursorWrapper(); + cw->cursor = c; + cw->themeCursor = kThemeArrowCursor; + return (void*) cw; +} + +static void* cursorFromData (const unsigned char* data, const int size, int hx, int hy) throw() +{ + Image* const im = ImageFileFormat::loadFrom ((const char*) data, size); + jassert (im != 0); + void* curs = juce_createMouseCursorFromImage (*im, hx, hy); + delete im; + return curs; +} + +const unsigned int kSpecialNoCursor = 'nocr'; + +void* juce_createStandardMouseCursor (MouseCursor::StandardCursorType type) throw() +{ + ThemeCursor cursorId = kThemeArrowCursor; + + switch (type) + { + case MouseCursor::NormalCursor: + cursorId = kThemeArrowCursor; + break; + + case MouseCursor::NoCursor: + cursorId = kSpecialNoCursor; + break; + + case MouseCursor::DraggingHandCursor: + { + static const unsigned char cursData[] = {71,73,70,56,57,97,16,0,16,0,145,2,0,0,0,0,255,255,255,0, + 0,0,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, + 16,0,0,2,52,148,47,0,200,185,16,130,90,12,74,139,107,84,123,39, + 132,117,151,116,132,146,248,60,209,138,98,22,203,114,34,236,37,52,77,217, + 247,154,191,119,110,240,193,128,193,95,163,56,60,234,98,135,2,0,59 }; + const int cursDataSize = 99; + + return cursorFromData (cursData, cursDataSize, 8, 8); + } + break; + + case MouseCursor::CopyingCursor: + cursorId = kThemeCopyArrowCursor; + break; + + case MouseCursor::WaitCursor: + cursorId = kThemeWatchCursor; + break; + + case MouseCursor::IBeamCursor: + cursorId = kThemeIBeamCursor; + break; + + case MouseCursor::PointingHandCursor: + cursorId = kThemePointingHandCursor; + break; + + case MouseCursor::LeftRightResizeCursor: + case MouseCursor::LeftEdgeResizeCursor: + case MouseCursor::RightEdgeResizeCursor: + { + static const unsigned char cursData[] = {71,73,70,56,57,97,16,0,16,0,145,0,0,255,255,255,0,0,0,255, + 255,255,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, + 16,0,0,2,38,148,143,169,203,237,15,19,0,106,202,64,111,22,32,224, + 9,78,30,213,121,230,121,146,99,8,142,71,183,189,152,20,27,86,132,231, + 58,83,0,0,59 }; + const int cursDataSize = 85; + + return cursorFromData (cursData, cursDataSize, 8, 8); + } + + case MouseCursor::UpDownResizeCursor: + case MouseCursor::TopEdgeResizeCursor: + case MouseCursor::BottomEdgeResizeCursor: + { + static const unsigned char cursData[] = {71,73,70,56,57,97,16,0,16,0,145,0,0,255,255,255,0,0,0,255, + 255,255,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, + 16,0,0,2,38,148,111,128,187,16,202,90,152,48,10,55,169,189,192,245, + 106,121,27,34,142,201,99,158,224,86,154,109,216,61,29,155,105,180,61,190, + 121,84,0,0,59 }; + const int cursDataSize = 85; + + return cursorFromData (cursData, cursDataSize, 8, 8); + } + + case MouseCursor::TopLeftCornerResizeCursor: + case MouseCursor::BottomRightCornerResizeCursor: + { + static const unsigned char cursData[] = {71,73,70,56,57,97,16,0,16,0,145,0,0,255,255,255,0,0,0,255, + 255,255,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, + 16,0,0,2,43,132,15,162,187,16,255,18,99,14,202,217,44,158,213,221, + 237,9,225,38,94,35,73,5,31,42,170,108,106,174,112,43,195,209,91,185, + 104,174,131,208,77,66,28,10,0,59 }; + const int cursDataSize = 90; + + return cursorFromData (cursData, cursDataSize, 8, 8); + } + + case MouseCursor::TopRightCornerResizeCursor: + case MouseCursor::BottomLeftCornerResizeCursor: + { + static const unsigned char cursData[] = {71,73,70,56,57,97,16,0,16,0,145,0,0,255,255,255,0,0,0,255, + 255,255,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, + 16,0,0,2,45,148,127,160,11,232,16,98,108,14,65,73,107,194,122,223, + 92,65,141,216,145,134,162,153,221,25,128,73,166,62,173,16,203,237,188,94, + 120,46,237,105,239,123,48,80,157,2,0,59 }; + const int cursDataSize = 92; + + return cursorFromData (cursData, cursDataSize, 8, 8); + } + + case MouseCursor::UpDownLeftRightResizeCursor: + { + static const unsigned char cursData[] = {71,73,70,56,57,97,15,0,15,0,145,0,0,0,0,0,255,255,255,0, + 128,128,255,255,255,33,249,4,1,0,0,3,0,44,0,0,0,0,15,0, + 15,0,0,2,46,156,63,129,139,1,202,26,152,48,186,73,109,114,65,85, + 195,37,143,88,93,29,215,101,23,198,178,30,149,158,25,56,134,97,179,61, + 158,213,126,203,234,99,220,34,56,70,1,0,59,0,0 }; + const int cursDataSize = 93; + + return cursorFromData (cursData, cursDataSize, 7, 7); + } + + case MouseCursor::CrosshairCursor: + cursorId = kThemeCrossCursor; + break; + } + + CursorWrapper* cw = new CursorWrapper(); + cw->cursor = 0; + cw->themeCursor = cursorId; + + return (void*) cw; +} + +void juce_deleteMouseCursor (void* const cursorHandle, const bool isStandard) throw() +{ + CursorWrapper* const cw = (CursorWrapper*) cursorHandle; + + if (cw != 0) + { + delete cw->cursor; + delete cw; + } +} + +void MouseCursor::showInAllWindows() const throw() +{ + showInWindow (0); +} + +void MouseCursor::showInWindow (ComponentPeer*) const throw() +{ + const CursorWrapper* const cw = (CursorWrapper*) getHandle(); + + if (cw != 0) + { + static bool isCursorHidden = false; + static bool showingWaitCursor = false; + const bool shouldShowWaitCursor = (cw->themeCursor == kThemeWatchCursor); + const bool shouldHideCursor = (cw->themeCursor == kSpecialNoCursor); + + if (shouldShowWaitCursor != showingWaitCursor + && Process::isForegroundProcess()) + { + showingWaitCursor = shouldShowWaitCursor; + QDDisplayWaitCursor (shouldShowWaitCursor); + } + + if (shouldHideCursor != isCursorHidden) + { + isCursorHidden = shouldHideCursor; + + if (shouldHideCursor) + HideCursor(); + else + ShowCursor(); + } + + if (cw->cursor != 0) + SetCursor (cw->cursor); + else if (! (shouldShowWaitCursor || shouldHideCursor)) + SetThemeCursor (cw->themeCursor); + } +} + +Image* juce_createIconForFile (const File& file) +{ + return 0; +} + +class MainMenuHandler; +static MainMenuHandler* mainMenu = 0; + +class MainMenuHandler : private MenuBarModelListener, + private DeletedAtShutdown +{ +public: + MainMenuHandler() throw() + : currentModel (0) + { + } + + ~MainMenuHandler() throw() + { + setMenu (0); + + jassert (mainMenu == this); + mainMenu = 0; + } + + void setMenu (MenuBarModel* const newMenuBarModel) throw() + { + if (currentModel != newMenuBarModel) + { + if (currentModel != 0) + currentModel->removeListener (this); + + currentModel = newMenuBarModel; + + if (currentModel != 0) + currentModel->addListener (this); + + menuBarItemsChanged (0); + } + } + + void menuBarItemsChanged (MenuBarModel*) + { + ClearMenuBar(); + + if (currentModel != 0) + { + int menuId = 1000; + const StringArray menuNames (currentModel->getMenuBarNames()); + + for (int i = 0; i < menuNames.size(); ++i) + { + const PopupMenu menu (currentModel->getMenuForIndex (i, menuNames [i])); + + MenuRef m = createMenu (menu, menuNames [i], menuId, i); + + InsertMenu (m, 0); + CFRelease (m); + } + } + } + + void menuCommandInvoked (MenuBarModel*, const ApplicationCommandTarget::InvocationInfo& info) + { + MenuRef menu = 0; + MenuItemIndex index = 0; + GetIndMenuItemWithCommandID (0, info.commandID, 1, &menu, &index); + + if (menu != 0) + { + FlashMenuBar (GetMenuID (menu)); + FlashMenuBar (GetMenuID (menu)); + } + } + + void invoke (const int commandId, ApplicationCommandManager* const commandManager, const int topLevelIndex) const + { + if (currentModel != 0) + { + if (commandManager != 0) + { + ApplicationCommandTarget::InvocationInfo info (commandId); + info.invocationMethod = ApplicationCommandTarget::InvocationInfo::fromMenu; + + commandManager->invoke (info, true); + } + + currentModel->menuItemSelected (commandId, topLevelIndex); + } + } + + MenuBarModel* currentModel; + +private: + static MenuRef createMenu (const PopupMenu menu, + const String& menuName, + int& id, + const int topLevelIndex) + { + MenuRef m = 0; + + if (CreateNewMenu (id++, kMenuAttrAutoDisable, &m) == noErr) + { + CFStringRef name = PlatformUtilities::juceStringToCFString (menuName); + SetMenuTitleWithCFString (m, name); + CFRelease (name); + + PopupMenu::MenuItemIterator iter (menu); + + while (iter.next()) + { + MenuItemIndex index = 0; + + int flags = kMenuAttrAutoDisable | kMenuItemAttrIgnoreMeta | kMenuItemAttrNotPreviousAlternate; + if (! iter.isEnabled) + flags |= kMenuItemAttrDisabled; + + CFStringRef text = PlatformUtilities::juceStringToCFString (iter.itemName.upToFirstOccurrenceOf (T(""), false, true)); + + if (iter.isSeparator) + { + AppendMenuItemTextWithCFString (m, text, kMenuItemAttrSeparator, 0, &index); + } + else if (iter.isSectionHeader) + { + AppendMenuItemTextWithCFString (m, text, kMenuItemAttrSectionHeader, 0, &index); + } + else if (iter.subMenu != 0) + { + AppendMenuItemTextWithCFString (m, text, flags, id++, &index); + + MenuRef sub = createMenu (*iter.subMenu, iter.itemName, id, topLevelIndex); + SetMenuItemHierarchicalMenu (m, index, sub); + CFRelease (sub); + } + else + { + AppendMenuItemTextWithCFString (m, text, flags, iter.itemId, &index); + + if (iter.isTicked) + CheckMenuItem (m, index, true); + + SetMenuItemProperty (m, index, 'juce', 'apcm', sizeof (void*), &iter.commandManager); + SetMenuItemProperty (m, index, 'juce', 'topi', sizeof (int), &topLevelIndex); + + if (iter.commandManager != 0) + { + const Array keyPresses (iter.commandManager->getKeyMappings() + ->getKeyPressesAssignedToCommand (iter.itemId)); + + if (keyPresses.size() > 0) + { + const KeyPress& kp = keyPresses.getReference(0); + int mods = 0; + + if (kp.getModifiers().isShiftDown()) + mods |= kMenuShiftModifier; + if (kp.getModifiers().isCtrlDown()) + mods |= kMenuControlModifier; + if (kp.getModifiers().isAltDown()) + mods |= kMenuOptionModifier; + if (! kp.getModifiers().isCommandDown()) + mods |= kMenuNoCommandModifier; + + tchar keyCode = (tchar) kp.getKeyCode(); + + if (kp.getKeyCode() >= KeyPress::numberPad0 + && kp.getKeyCode() <= KeyPress::numberPad9) + { + keyCode = (tchar) ((T('0') - KeyPress::numberPad0) + kp.getKeyCode()); + } + + SetMenuItemCommandKey (m, index, true, 255); + + if (CharacterFunctions::isLetterOrDigit (keyCode) + || CharacterFunctions::indexOfChar (T(",.;/\\'[]=-+_<>?{}\":"), keyCode, false) >= 0) + { + SetMenuItemModifiers (m, index, mods); + SetMenuItemCommandKey (m, index, false, CharacterFunctions::toUpperCase (keyCode)); + } + else + { + const SInt16 glyph = getGlyphForKeyCode (kp.getKeyCode()); + + if (glyph != 0) + { + SetMenuItemModifiers (m, index, mods); + SetMenuItemKeyGlyph (m, index, glyph); + } + } + + // if we set the key glyph to be a text char, and enable virtual + // key triggering, it stops the menu automatically triggering the callback + ChangeMenuItemAttributes (m, index, kMenuItemAttrUseVirtualKey, 0); + } + } + } + + CFRelease (text); + } + } + + return m; + } + + static SInt16 getGlyphForKeyCode (const int keyCode) throw() + { + if (keyCode == KeyPress::spaceKey) + return kMenuSpaceGlyph; + else if (keyCode == KeyPress::returnKey) + return kMenuReturnGlyph; + else if (keyCode == KeyPress::escapeKey) + return kMenuEscapeGlyph; + else if (keyCode == KeyPress::backspaceKey) + return kMenuDeleteLeftGlyph; + else if (keyCode == KeyPress::leftKey) + return kMenuLeftArrowGlyph; + else if (keyCode == KeyPress::rightKey) + return kMenuRightArrowGlyph; + else if (keyCode == KeyPress::upKey) + return kMenuUpArrowGlyph; + else if (keyCode == KeyPress::downKey) + return kMenuDownArrowGlyph; + else if (keyCode == KeyPress::pageUpKey) + return kMenuPageUpGlyph; + else if (keyCode == KeyPress::pageDownKey) + return kMenuPageDownGlyph; + else if (keyCode == KeyPress::endKey) + return kMenuSoutheastArrowGlyph; + else if (keyCode == KeyPress::homeKey) + return kMenuNorthwestArrowGlyph; + else if (keyCode == KeyPress::deleteKey) + return kMenuDeleteRightGlyph; + else if (keyCode == KeyPress::tabKey) + return kMenuTabRightGlyph; + else if (keyCode == KeyPress::F1Key) + return kMenuF1Glyph; + else if (keyCode == KeyPress::F2Key) + return kMenuF2Glyph; + else if (keyCode == KeyPress::F3Key) + return kMenuF3Glyph; + else if (keyCode == KeyPress::F4Key) + return kMenuF4Glyph; + else if (keyCode == KeyPress::F5Key) + return kMenuF5Glyph; + else if (keyCode == KeyPress::F6Key) + return kMenuF6Glyph; + else if (keyCode == KeyPress::F7Key) + return kMenuF7Glyph; + else if (keyCode == KeyPress::F8Key) + return kMenuF8Glyph; + else if (keyCode == KeyPress::F9Key) + return kMenuF9Glyph; + else if (keyCode == KeyPress::F10Key) + return kMenuF10Glyph; + else if (keyCode == KeyPress::F11Key) + return kMenuF11Glyph; + else if (keyCode == KeyPress::F12Key) + return kMenuF12Glyph; + else if (keyCode == KeyPress::F13Key) + return kMenuF13Glyph; + else if (keyCode == KeyPress::F14Key) + return kMenuF14Glyph; + else if (keyCode == KeyPress::F15Key) + return kMenuF15Glyph; + + return 0; + } +}; + +void MenuBarModel::setMacMainMenu (MenuBarModel* newMenuBarModel) throw() +{ + if (getMacMainMenu() != newMenuBarModel) + { + if (newMenuBarModel == 0) + { + delete mainMenu; + jassert (mainMenu == 0); // should be zeroed in the destructor + } + else + { + if (mainMenu == 0) + mainMenu = new MainMenuHandler(); + + mainMenu->setMenu (newMenuBarModel); + } + } +} + +MenuBarModel* MenuBarModel::getMacMainMenu() throw() +{ + return mainMenu != 0 ? mainMenu->currentModel : 0; +} + +// these functions are called externally from the message handling code +void juce_MainMenuAboutToBeUsed() +{ + // force an update of the items just before the menu appears.. + if (mainMenu != 0) + mainMenu->menuBarItemsChanged (0); +} + +void juce_InvokeMainMenuCommand (const HICommand& command) +{ + if (mainMenu != 0) + { + ApplicationCommandManager* commandManager = 0; + int topLevelIndex = 0; + + if (GetMenuItemProperty (command.menu.menuRef, command.menu.menuItemIndex, + 'juce', 'apcm', sizeof (commandManager), 0, &commandManager) == noErr + && GetMenuItemProperty (command.menu.menuRef, command.menu.menuItemIndex, + 'juce', 'topi', sizeof (topLevelIndex), 0, &topLevelIndex) == noErr) + { + mainMenu->invoke (command.commandID, commandManager, topLevelIndex); + } + } +} + +void PlatformUtilities::beep() +{ + SysBeep (30); +} + +void SystemClipboard::copyTextToClipboard (const String& text) throw() +{ + ClearCurrentScrap(); + ScrapRef ref; + GetCurrentScrap (&ref); + + const int len = text.length(); + const int numBytes = sizeof (UniChar) * len; + UniChar* const temp = (UniChar*) juce_calloc (numBytes); + + for (int i = 0; i < len; ++i) + temp[i] = (UniChar) text[i]; + + PutScrapFlavor (ref, + kScrapFlavorTypeUnicode, + kScrapFlavorMaskNone, + numBytes, + temp); + + juce_free (temp); +} + +const String SystemClipboard::getTextFromClipboard() throw() +{ + String result; + + ScrapRef ref; + GetCurrentScrap (&ref); + Size size = 0; + + if (GetScrapFlavorSize (ref, kScrapFlavorTypeUnicode, &size) == noErr + && size > 0) + { + void* const data = juce_calloc (size + 8); + + if (GetScrapFlavorData (ref, kScrapFlavorTypeUnicode, &size, data) == noErr) + { + result = PlatformUtilities::convertUTF16ToString ((UniChar*) data); + } + + juce_free (data); + } + + return result; +} + +bool AlertWindow::showNativeDialogBox (const String& title, + const String& bodyText, + bool isOkCancel) +{ + Str255 tit, txt; + PlatformUtilities::copyToStr255 (tit, title); + PlatformUtilities::copyToStr255 (txt, bodyText); + + AlertStdAlertParamRec ar; + ar.movable = true; + ar.helpButton = false; + ar.filterProc = 0; + ar.defaultText = (const unsigned char*)-1; + ar.cancelText = (const unsigned char*)((isOkCancel) ? -1 : 0); + ar.otherText = 0; + ar.defaultButton = kAlertStdAlertOKButton; + ar.cancelButton = 0; + ar.position = kWindowDefaultPosition; + + SInt16 result; + StandardAlert (kAlertNoteAlert, tit, txt, &ar, &result); + return result == kAlertStdAlertOKButton; +} + +const int KeyPress::spaceKey = ' '; +const int KeyPress::returnKey = kReturnCharCode; +const int KeyPress::escapeKey = kEscapeCharCode; +const int KeyPress::backspaceKey = kBackspaceCharCode; +const int KeyPress::leftKey = kLeftArrowCharCode; +const int KeyPress::rightKey = kRightArrowCharCode; +const int KeyPress::upKey = kUpArrowCharCode; +const int KeyPress::downKey = kDownArrowCharCode; +const int KeyPress::pageUpKey = kPageUpCharCode; +const int KeyPress::pageDownKey = kPageDownCharCode; +const int KeyPress::endKey = kEndCharCode; +const int KeyPress::homeKey = kHomeCharCode; +const int KeyPress::deleteKey = kDeleteCharCode; +const int KeyPress::insertKey = -1; +const int KeyPress::tabKey = kTabCharCode; +const int KeyPress::F1Key = 0x10110; +const int KeyPress::F2Key = 0x10111; +const int KeyPress::F3Key = 0x10112; +const int KeyPress::F4Key = 0x10113; +const int KeyPress::F5Key = 0x10114; +const int KeyPress::F6Key = 0x10115; +const int KeyPress::F7Key = 0x10116; +const int KeyPress::F8Key = 0x10117; +const int KeyPress::F9Key = 0x10118; +const int KeyPress::F10Key = 0x10119; +const int KeyPress::F11Key = 0x1011a; +const int KeyPress::F12Key = 0x1011b; +const int KeyPress::F13Key = 0x1011c; +const int KeyPress::F14Key = 0x1011d; +const int KeyPress::F15Key = 0x1011e; +const int KeyPress::F16Key = 0x1011f; +const int KeyPress::numberPad0 = 0x30020; +const int KeyPress::numberPad1 = 0x30021; +const int KeyPress::numberPad2 = 0x30022; +const int KeyPress::numberPad3 = 0x30023; +const int KeyPress::numberPad4 = 0x30024; +const int KeyPress::numberPad5 = 0x30025; +const int KeyPress::numberPad6 = 0x30026; +const int KeyPress::numberPad7 = 0x30027; +const int KeyPress::numberPad8 = 0x30028; +const int KeyPress::numberPad9 = 0x30029; +const int KeyPress::numberPadAdd = 0x3002a; +const int KeyPress::numberPadSubtract = 0x3002b; +const int KeyPress::numberPadMultiply = 0x3002c; +const int KeyPress::numberPadDivide = 0x3002d; +const int KeyPress::numberPadSeparator = 0x3002e; +const int KeyPress::numberPadDecimalPoint = 0x3002f; +const int KeyPress::numberPadEquals = 0x30030; +const int KeyPress::numberPadDelete = 0x30031; +const int KeyPress::playKey = 0x30000; +const int KeyPress::stopKey = 0x30001; +const int KeyPress::fastForwardKey = 0x30002; +const int KeyPress::rewindKey = 0x30003; + +AppleRemoteDevice::AppleRemoteDevice() + : device (0), + queue (0), + remoteId (0) +{ +} + +AppleRemoteDevice::~AppleRemoteDevice() +{ + stop(); +} + +static io_object_t getAppleRemoteDevice() throw() +{ + CFMutableDictionaryRef dict = IOServiceMatching ("AppleIRController"); + + io_iterator_t iter = 0; + io_object_t iod = 0; + + if (IOServiceGetMatchingServices (kIOMasterPortDefault, dict, &iter) == kIOReturnSuccess + && iter != 0) + { + iod = IOIteratorNext (iter); + } + + IOObjectRelease (iter); + return iod; +} + +static bool createAppleRemoteInterface (io_object_t iod, void** device) throw() +{ + jassert (*device == 0); + io_name_t classname; + + if (IOObjectGetClass (iod, classname) == kIOReturnSuccess) + { + IOCFPlugInInterface** cfPlugInInterface = 0; + SInt32 score = 0; + + if (IOCreatePlugInInterfaceForService (iod, + kIOHIDDeviceUserClientTypeID, + kIOCFPlugInInterfaceID, + &cfPlugInInterface, + &score) == kIOReturnSuccess) + { + HRESULT hr = (*cfPlugInInterface)->QueryInterface (cfPlugInInterface, + CFUUIDGetUUIDBytes (kIOHIDDeviceInterfaceID), + device); + + (void) hr; + + (*cfPlugInInterface)->Release (cfPlugInInterface); + } + } + + return *device != 0; +} + +bool AppleRemoteDevice::start (const bool inExclusiveMode) throw() +{ + if (queue != 0) + return true; + + stop(); + + bool result = false; + io_object_t iod = getAppleRemoteDevice(); + + if (iod != 0) + { + if (createAppleRemoteInterface (iod, &device) && open (inExclusiveMode)) + result = true; + else + stop(); + + IOObjectRelease (iod); + } + + return result; +} + +void AppleRemoteDevice::stop() throw() +{ + if (queue != 0) + { + (*(IOHIDQueueInterface**) queue)->stop ((IOHIDQueueInterface**) queue); + (*(IOHIDQueueInterface**) queue)->dispose ((IOHIDQueueInterface**) queue); + (*(IOHIDQueueInterface**) queue)->Release ((IOHIDQueueInterface**) queue); + queue = 0; + } + + if (device != 0) + { + (*(IOHIDDeviceInterface**) device)->close ((IOHIDDeviceInterface**) device); + (*(IOHIDDeviceInterface**) device)->Release ((IOHIDDeviceInterface**) device); + device = 0; + } +} + +bool AppleRemoteDevice::isActive() const throw() +{ + return queue != 0; +} + +static void appleRemoteQueueCallback (void* const target, const IOReturn result, void*, void*) +{ + if (result == kIOReturnSuccess) + ((AppleRemoteDevice*) target)->handleCallbackInternal(); +} + +bool AppleRemoteDevice::open (const bool openInExclusiveMode) throw() +{ +#if ! MACOS_10_2_OR_EARLIER + Array cookies; + + CFArrayRef elements; + IOHIDDeviceInterface122** const device122 = (IOHIDDeviceInterface122**) device; + + if ((*device122)->copyMatchingElements (device122, 0, &elements) != kIOReturnSuccess) + return false; + + for (int i = 0; i < CFArrayGetCount (elements); ++i) + { + CFDictionaryRef element = (CFDictionaryRef) CFArrayGetValueAtIndex (elements, i); + + // get the cookie + CFTypeRef object = CFDictionaryGetValue (element, CFSTR (kIOHIDElementCookieKey)); + + if (object == 0 || CFGetTypeID (object) != CFNumberGetTypeID()) + continue; + + long number; + if (! CFNumberGetValue ((CFNumberRef) object, kCFNumberLongType, &number)) + continue; + + cookies.add ((int) number); + } + + CFRelease (elements); + + if ((*(IOHIDDeviceInterface**) device) + ->open ((IOHIDDeviceInterface**) device, + openInExclusiveMode ? kIOHIDOptionsTypeSeizeDevice + : kIOHIDOptionsTypeNone) == KERN_SUCCESS) + { + queue = (*(IOHIDDeviceInterface**) device)->allocQueue ((IOHIDDeviceInterface**) device); + + if (queue != 0) + { + (*(IOHIDQueueInterface**) queue)->create ((IOHIDQueueInterface**) queue, 0, 12); + + for (int i = 0; i < cookies.size(); ++i) + { + IOHIDElementCookie cookie = (IOHIDElementCookie) cookies.getUnchecked(i); + (*(IOHIDQueueInterface**) queue)->addElement ((IOHIDQueueInterface**) queue, cookie, 0); + } + + CFRunLoopSourceRef eventSource; + + if ((*(IOHIDQueueInterface**) queue) + ->createAsyncEventSource ((IOHIDQueueInterface**) queue, &eventSource) == KERN_SUCCESS) + { + if ((*(IOHIDQueueInterface**) queue)->setEventCallout ((IOHIDQueueInterface**) queue, + appleRemoteQueueCallback, this, 0) == KERN_SUCCESS) + { + CFRunLoopAddSource (CFRunLoopGetCurrent(), eventSource, kCFRunLoopDefaultMode); + + (*(IOHIDQueueInterface**) queue)->start ((IOHIDQueueInterface**) queue); + + return true; + } + } + } + } +#endif + + return false; +} + +void AppleRemoteDevice::handleCallbackInternal() +{ + int totalValues = 0; + AbsoluteTime nullTime = { 0, 0 }; + char cookies [12]; + int numCookies = 0; + + while (numCookies < numElementsInArray (cookies)) + { + IOHIDEventStruct e; + + if ((*(IOHIDQueueInterface**) queue)->getNextEvent ((IOHIDQueueInterface**) queue, &e, nullTime, 0) != kIOReturnSuccess) + break; + + if ((int) e.elementCookie == 19) + { + remoteId = e.value; + buttonPressed (switched, false); + } + else + { + totalValues += e.value; + cookies [numCookies++] = (char) (pointer_sized_int) e.elementCookie; + } + } + + cookies [numCookies++] = 0; + + static const char buttonPatterns[] = + { + 14, 7, 6, 5, 14, 7, 6, 5, 0, + 14, 8, 6, 5, 14, 8, 6, 5, 0, + 14, 12, 11, 6, 5, 0, + 14, 13, 11, 6, 5, 0, + 14, 9, 6, 5, 14, 9, 6, 5, 0, + 14, 10, 6, 5, 14, 10, 6, 5, 0, + 14, 6, 5, 4, 2, 0, + 14, 6, 5, 3, 2, 0, + 14, 6, 5, 14, 6, 5, 0, + 18, 14, 6, 5, 18, 14, 6, 5, 0, + 19, 0 + }; + + int buttonNum = (int) menuButton; + int i = 0; + + while (i < numElementsInArray (buttonPatterns)) + { + if (strcmp (cookies, buttonPatterns + i) == 0) + { + buttonPressed ((ButtonType) buttonNum, totalValues > 0); + break; + } + + i += strlen (buttonPatterns + i) + 1; + ++buttonNum; + } +} + +#if JUCE_OPENGL + +class WindowedGLContext : public OpenGLContext +{ +public: + WindowedGLContext (Component* const component, + const OpenGLPixelFormat& pixelFormat_, + AGLContext sharedContext) + : renderContext (0), + pixelFormat (pixelFormat_) + { + jassert (component != 0); + + HIViewComponentPeer* const peer = dynamic_cast (component->getTopLevelComponent()->getPeer()); + if (peer == 0) + return; + + GLint attribs [64]; + int n = 0; + attribs[n++] = AGL_RGBA; + attribs[n++] = AGL_DOUBLEBUFFER; + attribs[n++] = AGL_ACCELERATED; + attribs[n++] = AGL_RED_SIZE; + attribs[n++] = pixelFormat.redBits; + attribs[n++] = AGL_GREEN_SIZE; + attribs[n++] = pixelFormat.greenBits; + attribs[n++] = AGL_BLUE_SIZE; + attribs[n++] = pixelFormat.blueBits; + attribs[n++] = AGL_ALPHA_SIZE; + attribs[n++] = pixelFormat.alphaBits; + attribs[n++] = AGL_DEPTH_SIZE; + attribs[n++] = pixelFormat.depthBufferBits; + attribs[n++] = AGL_STENCIL_SIZE; + attribs[n++] = pixelFormat.stencilBufferBits; + attribs[n++] = AGL_ACCUM_RED_SIZE; + attribs[n++] = pixelFormat.accumulationBufferRedBits; + attribs[n++] = AGL_ACCUM_GREEN_SIZE; + attribs[n++] = pixelFormat.accumulationBufferGreenBits; + attribs[n++] = AGL_ACCUM_BLUE_SIZE; + attribs[n++] = pixelFormat.accumulationBufferBlueBits; + attribs[n++] = AGL_ACCUM_ALPHA_SIZE; + attribs[n++] = pixelFormat.accumulationBufferAlphaBits; + + // xxx not sure how to do fullSceneAntiAliasingNumSamples.. + + attribs[n++] = AGL_SAMPLE_BUFFERS_ARB; + attribs[n++] = 1; + attribs[n++] = AGL_SAMPLES_ARB; + attribs[n++] = 4; + attribs[n++] = AGL_CLOSEST_POLICY; + attribs[n++] = AGL_NO_RECOVERY; + attribs[n++] = AGL_NONE; + + renderContext = aglCreateContext (aglChoosePixelFormat (0, 0, attribs), + sharedContext); + + aglSetDrawable (renderContext, GetWindowPort (peer->windowRef)); + } + + ~WindowedGLContext() + { + makeInactive(); + aglSetDrawable (renderContext, 0); + aglDestroyContext (renderContext); + } + + bool makeActive() const throw() + { + jassert (renderContext != 0); + + return aglSetCurrentContext (renderContext); + } + + bool makeInactive() const throw() + { + return (! isActive()) || aglSetCurrentContext (0); + } + + bool isActive() const throw() + { + return aglGetCurrentContext() == renderContext; + } + + const OpenGLPixelFormat getPixelFormat() const + { + return pixelFormat; + } + + void* getRawContext() const throw() + { + return renderContext; + } + + void updateWindowPosition (int x, int y, int w, int h, int outerWindowHeight) + { + GLint bufferRect[4]; + bufferRect[0] = x; + bufferRect[1] = outerWindowHeight - (y + h); + bufferRect[2] = w; + bufferRect[3] = h; + + aglSetInteger (renderContext, AGL_BUFFER_RECT, bufferRect); + aglEnable (renderContext, AGL_BUFFER_RECT); + } + + void swapBuffers() + { + aglSwapBuffers (renderContext); + } + + bool setSwapInterval (const int numFramesPerSwap) + { + return aglSetInteger (renderContext, AGL_SWAP_INTERVAL, (const GLint*) &numFramesPerSwap); + } + + int getSwapInterval() const + { + GLint numFrames = 0; + aglGetInteger (renderContext, AGL_SWAP_INTERVAL, &numFrames); + return numFrames; + } + + void repaint() + { + } + + juce_UseDebuggingNewOperator + + AGLContext renderContext; + +private: + OpenGLPixelFormat pixelFormat; + + WindowedGLContext (const WindowedGLContext&); + const WindowedGLContext& operator= (const WindowedGLContext&); +}; + +OpenGLContext* OpenGLContext::createContextForWindow (Component* const component, + const OpenGLPixelFormat& pixelFormat, + const OpenGLContext* const contextToShareWith) +{ + WindowedGLContext* c = new WindowedGLContext (component, pixelFormat, + contextToShareWith != 0 ? (AGLContext) contextToShareWith->getRawContext() : 0); + + if (c->renderContext == 0) + deleteAndZero (c); + + return c; +} + +void juce_glViewport (const int w, const int h) +{ + glViewport (0, 0, w, h); +} + +static int getAGLAttribute (AGLPixelFormat p, const GLint attrib) +{ + GLint result = 0; + aglDescribePixelFormat (p, attrib, &result); + return result; +} + +void OpenGLPixelFormat::getAvailablePixelFormats (Component* /*component*/, + OwnedArray & results) +{ + GLint attribs [64]; + int n = 0; + attribs[n++] = AGL_RGBA; + attribs[n++] = AGL_DOUBLEBUFFER; + attribs[n++] = AGL_ACCELERATED; + attribs[n++] = AGL_NO_RECOVERY; + attribs[n++] = AGL_NONE; + + AGLPixelFormat p = aglChoosePixelFormat (0, 0, attribs); + + while (p != 0) + { + OpenGLPixelFormat* const pf = new OpenGLPixelFormat(); + pf->redBits = getAGLAttribute (p, AGL_RED_SIZE); + pf->greenBits = getAGLAttribute (p, AGL_GREEN_SIZE); + pf->blueBits = getAGLAttribute (p, AGL_BLUE_SIZE); + pf->alphaBits = getAGLAttribute (p, AGL_ALPHA_SIZE); + pf->depthBufferBits = getAGLAttribute (p, AGL_DEPTH_SIZE); + pf->stencilBufferBits = getAGLAttribute (p, AGL_STENCIL_SIZE); + pf->accumulationBufferRedBits = getAGLAttribute (p, AGL_ACCUM_RED_SIZE); + pf->accumulationBufferGreenBits = getAGLAttribute (p, AGL_ACCUM_GREEN_SIZE); + pf->accumulationBufferBlueBits = getAGLAttribute (p, AGL_ACCUM_BLUE_SIZE); + pf->accumulationBufferAlphaBits = getAGLAttribute (p, AGL_ACCUM_ALPHA_SIZE); + + results.add (pf); + + p = aglNextPixelFormat (p); + } +} + +#endif + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_mac_Windowing.cpp *********/ + +#endif + +#endif diff --git a/juce_amalgamated.h b/juce_amalgamated.h new file mode 100644 index 0000000000..17d2837bc4 --- /dev/null +++ b/juce_amalgamated.h @@ -0,0 +1,53316 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified 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. + + JUCE 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 JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +/* + ============================================================================== + + This header contains the entire Juce source tree, and can be #included in + all your source files. + + As well as including this in your files, you should also add juce_inline.cpp + to your project (or juce_inline.mm on the Mac). + + ============================================================================== +*/ + +#ifndef __ALL_JUCE_HEADERS_H__ +#define __ALL_JUCE_HEADERS_H__ + +#define DONT_AUTOLINK_TO_JUCE_LIBRARY 1 + +/********* Start of inlined file: juce.h *********/ +#ifndef __JUCE_JUCEHEADER__ +#define __JUCE_JUCEHEADER__ + +/* + This is the main JUCE header file that applications need to include. + +*/ + +// (this includes things that need defining outside of the JUCE namespace) + +/********* Start of inlined file: juce_StandardHeader.h *********/ +#ifndef __JUCE_STANDARDHEADER_JUCEHEADER__ +#define __JUCE_STANDARDHEADER_JUCEHEADER__ + +/** Current Juce version number. + + See also SystemStats::getJUCEVersion() for a string version. +*/ +#define JUCE_MAJOR_VERSION 1 +#define JUCE_MINOR_VERSION 46 + +/** Current Juce version number. + + Bits 16 to 32 = major version. + Bits 8 to 16 = minor version. + Bits 0 to 8 = point release (not currently used). + + See also SystemStats::getJUCEVersion() for a string version. +*/ +#define JUCE_VERSION ((JUCE_MAJOR_VERSION << 16) + (JUCE_MINOR_VERSION << 8)) + +/********* Start of inlined file: juce_Config.h *********/ +#ifndef __JUCE_CONFIG_JUCEHEADER__ +#define __JUCE_CONFIG_JUCEHEADER__ + +/* + This file contains macros that enable/disable various JUCE features. +*/ + +/** The name of the namespace that all Juce classes and functions will be + put inside. If this is not defined, no namespace will be used. +*/ +#ifndef JUCE_NAMESPACE + #define JUCE_NAMESPACE juce +#endif + +/** Normally, JUCE_DEBUG is set to 1 or 0 based on compiler and project settings, + but if you define this value, you can override this can force it to be true or + false. +*/ +#ifndef JUCE_FORCE_DEBUG + //#define JUCE_FORCE_DEBUG 1 +#endif + +/** If this flag is enabled, the the jassert and jassertfalse macros will + always use Logger::writeToLog() to write a message when an assertion happens. + + Enabling it will also leave this turned on in release builds. When it's disabled, + however, the jassert and jassertfalse macros will not be compiled in a + release build. + + @see jassert, jassertfalse, Logger +*/ +#ifndef JUCE_LOG_ASSERTIONS +// #define JUCE_LOG_ASSERTIONS 1 +#endif + +/** Comment out this macro if you haven't got the Steinberg ASIO SDK, without + which the ASIOAudioIODevice class can't be built. See the comments in the + ASIOAudioIODevice class's header file for more info about this. + + (This only affects a Win32 build) +*/ +#ifndef JUCE_ASIO + #define JUCE_ASIO 1 +#endif + +/** Comment out this macro to disable building of ALSA device support on Linux. +*/ +#ifndef JUCE_ALSA + #define JUCE_ALSA 1 +#endif + +/** Comment out this macro if you don't want to enable QuickTime or if you don't + have the SDK installed. + + If this flag is not enabled, the QuickTimeMovieComponent and QuickTimeAudioFormat + classes will be unavailable. + + On Windows, if you enable this, you'll need to have the QuickTime SDK + installed, and its header files will need to be on your include path. +*/ +#if ! (defined (JUCE_QUICKTIME) || defined (LINUX) || (defined (_WIN32) && ! defined (_MSC_VER))) + #define JUCE_QUICKTIME 1 +#endif + +/** Comment out this macro if you don't want to enable OpenGL or if you don't + have the appropriate headers and libraries available. If it's not enabled, the + OpenGLComponent class will be unavailable. +*/ +#ifndef JUCE_OPENGL + #define JUCE_OPENGL 1 +#endif + +/** These flags enable the Ogg-Vorbis and Flac audio formats. + + If you're not going to need either of these formats, turn off the flags to + avoid bloating your codebase with them. +*/ +#ifndef JUCE_USE_FLAC + #define JUCE_USE_FLAC 1 +#endif + +#ifndef JUCE_USE_OGGVORBIS + #define JUCE_USE_OGGVORBIS 1 +#endif + +/** This flag lets you enable support for CD-burning. You might want to disable + it to build without the MS SDK under windows. +*/ +#if (! defined (JUCE_USE_CDBURNER)) && ! (defined (_WIN32) && ! defined (_MSC_VER)) + #define JUCE_USE_CDBURNER 1 +#endif + +/** Enabling this macro means that all regions that get repainted will have a coloured + line drawn around them. + + This is handy if you're trying to optimise drawing, because it lets you easily see + when anything is being repainted unnecessarily. +*/ +#ifndef JUCE_ENABLE_REPAINT_DEBUGGING +// #define JUCE_ENABLE_REPAINT_DEBUGGING 1 +#endif + +/** Enable this under Linux to use Xinerama for multi-monitor support. +*/ +#ifndef JUCE_USE_XINERAMA + #define JUCE_USE_XINERAMA 1 +#endif + +/** Enable this under Linux to use XShm for faster shared-memory rendering. +*/ +#ifndef JUCE_USE_XSHM + #define JUCE_USE_XSHM 1 +#endif + +/** Enabling this builds support for VST audio plugins. + @see VSTPluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_AU +*/ +//#define JUCE_PLUGINHOST_VST 1 + +/** Enabling this builds support for AudioUnit audio plugins. + @see AudioUnitPluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_VST +*/ +#define JUCE_PLUGINHOST_AU 1 + +/** Disabling this will avoid linking to any UI code. This is handy for + writing command-line utilities, e.g. on linux boxes which don't have some + of the UI libraries installed. + + (On mac and windows, this won't generally make much difference to the build). +*/ +#ifndef JUCE_BUILD_GUI_CLASSES + #define JUCE_BUILD_GUI_CLASSES 1 +#endif + +/** Enable this to add extra memory-leak info to the new and delete operators. + + (Currently, this only affects Windows builds in debug mode). +*/ +#ifndef JUCE_CHECK_MEMORY_LEAKS + #define JUCE_CHECK_MEMORY_LEAKS 1 +#endif + +/** Enable this to turn on juce's internal catching of exceptions. + + Turning it off will avoid any exception catching. With it on, all exceptions + are passed to the JUCEApplication::unhandledException() callback for logging. +*/ +#ifndef JUCE_CATCH_UNHANDLED_EXCEPTIONS + #define JUCE_CATCH_UNHANDLED_EXCEPTIONS 1 +#endif + +/** If this macro is set, the Juce String class will use unicode as its + internal representation. If it isn't set, it'll use ANSI. +*/ +#ifndef JUCE_STRINGS_ARE_UNICODE + #define JUCE_STRINGS_ARE_UNICODE 1 +#endif + +#endif +/********* End of inlined file: juce_Config.h *********/ + +#ifdef JUCE_NAMESPACE + #define BEGIN_JUCE_NAMESPACE namespace JUCE_NAMESPACE { + #define END_JUCE_NAMESPACE } +#else + #define BEGIN_JUCE_NAMESPACE + #define END_JUCE_NAMESPACE +#endif + +// This sets up the JUCE_WIN32, JUCE_MAC, or JUCE_LINUX macros + +/********* Start of inlined file: juce_PlatformDefs.h *********/ +#ifndef __JUCE_PLATFORMDEFS_JUCEHEADER__ +#define __JUCE_PLATFORMDEFS_JUCEHEADER__ + +/* This file figures out which platform is being built, and defines some macros + that the rest of the code can use for OS-specific compilation. + + Macros that will be set here are: + + - One of JUCE_WIN32, JUCE_MAC or JUCE_LINUX. + - Either JUCE_32BIT or JUCE_64BIT, depending on the architecture. + - Either JUCE_LITTLE_ENDIAN or JUCE_BIG_ENDIAN. + - Either JUCE_INTEL or JUCE_PPC + - Either JUCE_GCC or JUCE_MSVC + + On the Mac, it also defines MACOS_10_2_OR_EARLIER if the build is targeting OSX10.2, + and MACOS_10_3_OR_EARLIER if it is targeting either 10.2 or 10.3 + + It also includes a set of macros for debug console output and assertions. + +*/ + +#if (defined (_WIN32) || defined (_WIN64)) + #define JUCE_WIN32 1 +#else + #ifdef LINUX + #define JUCE_LINUX 1 + #else + #define JUCE_MAC 1 + #endif +#endif + +#if JUCE_WIN32 + #ifdef _MSC_VER + #ifdef _WIN64 + #define JUCE_64BIT 1 + #else + #define JUCE_32BIT 1 + #endif + #endif + + #ifdef _DEBUG + #define JUCE_DEBUG 1 + #endif + + /** If defined, this indicates that the processor is little-endian. */ + #define JUCE_LITTLE_ENDIAN 1 + + #define JUCE_INTEL 1 +#endif + +#if JUCE_MAC + + #include + + #ifndef NDEBUG + #define JUCE_DEBUG 1 + #endif + + #ifdef __LITTLE_ENDIAN__ + #define JUCE_LITTLE_ENDIAN 1 + #else + #define JUCE_BIG_ENDIAN 1 + #endif + + #if defined (__ppc__) || defined (__ppc64__) + #define JUCE_PPC 1 + #else + #define JUCE_INTEL 1 + #endif + + #ifdef __LP64__ + #define JUCE_64BIT 1 + #else + #define JUCE_32BIT 1 + #endif + + #if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_3) + #define MACOS_10_2_OR_EARLIER 1 + #endif + + #if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_4) + #define MACOS_10_3_OR_EARLIER 1 + #endif +#endif + +#if JUCE_LINUX + + #ifdef _DEBUG + #define JUCE_DEBUG 1 + #endif + + // Allow override for big-endian Linux platforms + #ifndef JUCE_BIG_ENDIAN + #define JUCE_LITTLE_ENDIAN 1 + #endif + + #if defined (__LP64__) || defined (_LP64) + #define JUCE_64BIT 1 + #else + #define JUCE_32BIT 1 + #endif + + #define JUCE_INTEL 1 +#endif + +#ifdef JUCE_FORCE_DEBUG + #undef JUCE_DEBUG + + #if JUCE_FORCE_DEBUG + #define JUCE_DEBUG 1 + #endif +#endif + +// Compiler type macros. + +#ifdef __GNUC__ + #define JUCE_GCC 1 +#elif defined (_MSC_VER) + #define JUCE_MSVC 1 + + #if _MSC_VER >= 1400 + #define JUCE_USE_INTRINSICS 1 + #endif +#else + #error unknown compiler +#endif + +/** This macro defines the C calling convention used as the standard for Juce calls. */ +#if JUCE_MSVC + #define JUCE_CALLTYPE __stdcall +#else + #define JUCE_CALLTYPE +#endif + +// Debugging and assertion macros + +// (For info about JUCE_LOG_ASSERTIONS, have a look in juce_Config.h) +#if JUCE_LOG_ASSERTIONS + #define juce_LogCurrentAssertion juce_LogAssertion (__FILE__, __LINE__); +#elif defined (JUCE_DEBUG) + #define juce_LogCurrentAssertion fprintf (stderr, "JUCE Assertion failure in %s, line %d\n", __FILE__, __LINE__); +#else + #define juce_LogCurrentAssertion +#endif + +#ifdef JUCE_DEBUG + + // If debugging is enabled.. + + /** Writes a string to the standard error stream. + + This is only compiled in a debug build. + + @see Logger::outputDebugString + */ + #define DBG(dbgtext) Logger::outputDebugString (dbgtext); + + /** Printf's a string to the standard error stream. + + This is only compiled in a debug build. + + @see Logger::outputDebugString + */ + #define DBG_PRINTF(dbgprintf) Logger::outputDebugPrintf dbgprintf; + + // Assertions.. + + #if JUCE_WIN32 || DOXYGEN + + #if JUCE_USE_INTRINSICS + #pragma intrinsic (__debugbreak) + + /** This will try to break the debugger if one is currently hosting this app. + @see jassert() + */ + #define juce_breakDebugger __debugbreak(); + + #elif JUCE_GCC + /** This will try to break the debugger if one is currently hosting this app. + @see jassert() + */ + #define juce_breakDebugger asm("int $3"); + #else + /** This will try to break the debugger if one is currently hosting this app. + @see jassert() + */ + #define juce_breakDebugger { __asm int 3 } + #endif + #elif JUCE_MAC + #define juce_breakDebugger Debugger(); + #elif JUCE_LINUX + #define juce_breakDebugger kill (0, SIGTRAP); + #endif + + /** This will always cause an assertion failure. + + It is only compiled in a debug build, (unless JUCE_LOG_ASSERTIONS is enabled + in juce_Config.h). + + @see jassert() + */ + #define jassertfalse { juce_LogCurrentAssertion; if (JUCE_NAMESPACE::juce_isRunningUnderDebugger()) juce_breakDebugger; } + + /** Platform-independent assertion macro. + + This gets optimised out when not being built with debugging turned on. + + Be careful not to call any functions within its arguments that are vital to + the behaviour of the program, because these won't get called in the release + build. + + @see jassertfalse + */ + #define jassert(expression) { if (! (expression)) jassertfalse } + +#else + + // If debugging is disabled, these dummy debug and assertion macros are used.. + + #define DBG(dbgtext) + #define DBG_PRINTF(dbgprintf) + + #define jassertfalse { juce_LogCurrentAssertion } + + #if JUCE_LOG_ASSERTIONS + #define jassert(expression) { if (! (expression)) jassertfalse } + #else + #define jassert(a) { } + #endif + +#endif + +#ifndef DOXYGEN + template struct JuceStaticAssert; + template <> struct JuceStaticAssert { static void dummy() {} }; +#endif + +/** A compile-time assertion macro. + + If the expression parameter is false, the macro will cause a compile error. +*/ +#define static_jassert(expression) JuceStaticAssert::dummy(); + +#if JUCE_CATCH_UNHANDLED_EXCEPTIONS + + #define JUCE_TRY try + + /** Used in try-catch blocks, this macro will send exceptions to the JUCEApplication + object so they can be logged by the application if it wants to. + */ + #define JUCE_CATCH_EXCEPTION \ + catch (const std::exception& e) \ + { \ + JUCEApplication::sendUnhandledException (&e, __FILE__, __LINE__); \ + } \ + catch (...) \ + { \ + JUCEApplication::sendUnhandledException (0, __FILE__, __LINE__); \ + } + + #define JUCE_CATCH_ALL catch (...) {} + #define JUCE_CATCH_ALL_ASSERT catch (...) { jassertfalse } + +#else + + #define JUCE_TRY + #define JUCE_CATCH_EXCEPTION + #define JUCE_CATCH_ALL + #define JUCE_CATCH_ALL_ASSERT + +#endif + +// Macros for inlining. + +#if JUCE_MSVC + /** A platform-independent way of forcing an inline function. + + Use the syntax: @code + forcedinline void myfunction (int x) + @endcode + */ + #ifdef JUCE_DEBUG + #define forcedinline __forceinline + #else + #define forcedinline inline + #endif + + /** A platform-independent way of stopping the compiler inlining a function. + + Use the syntax: @code + juce_noinline void myfunction (int x) + @endcode + */ + #define juce_noinline + +#else + /** A platform-independent way of forcing an inline function. + + Use the syntax: @code + forcedinline void myfunction (int x) + @endcode + */ + #ifndef JUCE_DEBUG + #define forcedinline inline __attribute__((always_inline)) + #else + #define forcedinline inline + #endif + + /** A platform-independent way of stopping the compiler inlining a function. + + Use the syntax: @code + juce_noinline void myfunction (int x) + @endcode + */ + #define juce_noinline __attribute__((noinline)) + +#endif + +#endif // __JUCE_PLATFORMDEFS_JUCEHEADER__ +/********* End of inlined file: juce_PlatformDefs.h *********/ + +// Now we'll include any OS headers we need.. (at this point we are outside the Juce namespace). +#if JUCE_MSVC + #pragma warning (push) + #pragma warning (disable: 4514 4245 4100) +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#if JUCE_MAC || JUCE_LINUX + #include +#endif + +#if JUCE_USE_INTRINSICS + #include +#endif + +#if JUCE_MAC && ! MACOS_10_3_OR_EARLIER + #include +#endif + +#if JUCE_LINUX + #include +#endif + +#if JUCE_MSVC && JUCE_DEBUG + #include +#endif + +#if JUCE_MSVC + #pragma warning (pop) +#endif + +// DLL building settings on Win32 +#if JUCE_MSVC + #ifdef JUCE_DLL_BUILD + #define JUCE_API __declspec (dllexport) + #pragma warning (disable: 4251) + #elif defined (JUCE_DLL) + #define JUCE_API __declspec (dllimport) + #pragma warning (disable: 4251) + #endif +#endif + +#ifndef JUCE_API + /** This macro is added to all juce public class declarations. */ + #define JUCE_API +#endif + +/** This macro is added to all juce public function declarations. */ +#define JUCE_PUBLIC_FUNCTION JUCE_API JUCE_CALLTYPE + +// Now include some basics that are needed by most of the Juce classes... +BEGIN_JUCE_NAMESPACE + +extern bool JUCE_API JUCE_CALLTYPE juce_isRunningUnderDebugger() throw(); + +#if JUCE_LOG_ASSERTIONS + extern void JUCE_API juce_LogAssertion (const char* filename, const int lineNum) throw(); +#endif + +/********* Start of inlined file: juce_Memory.h *********/ +#ifndef __JUCE_MEMORY_JUCEHEADER__ +#define __JUCE_MEMORY_JUCEHEADER__ + +/* + This file defines the various juce_malloc(), juce_free() macros that should be used in + preference to the standard calls. +*/ + +#if defined (JUCE_DEBUG) && JUCE_MSVC && JUCE_CHECK_MEMORY_LEAKS + #ifndef JUCE_DLL + + // Win32 debug non-DLL versions.. + + /** This should be used instead of calling malloc directly. */ + #define juce_malloc(numBytes) _malloc_dbg (numBytes, _NORMAL_BLOCK, __FILE__, __LINE__) + /** This should be used instead of calling calloc directly. */ + #define juce_calloc(numBytes) _calloc_dbg (1, numBytes, _NORMAL_BLOCK, __FILE__, __LINE__) + /** This should be used instead of calling realloc directly. */ + #define juce_realloc(location, numBytes) _realloc_dbg (location, numBytes, _NORMAL_BLOCK, __FILE__, __LINE__) + /** This should be used instead of calling free directly. */ + #define juce_free(location) _free_dbg (location, _NORMAL_BLOCK) + + #else + + // Win32 debug DLL versions.. + + // For the DLL, we'll define some functions in the DLL that will be used for allocation - that + // way all juce calls in the DLL and in the host API will all use the same allocator. + extern JUCE_API void* juce_DebugMalloc (const int size, const char* file, const int line); + extern JUCE_API void* juce_DebugCalloc (const int size, const char* file, const int line); + extern JUCE_API void* juce_DebugRealloc (void* const block, const int size, const char* file, const int line); + extern JUCE_API void juce_DebugFree (void* const block); + + /** This should be used instead of calling malloc directly. */ + #define juce_malloc(numBytes) juce::juce_DebugMalloc (numBytes, __FILE__, __LINE__) + /** This should be used instead of calling calloc directly. */ + #define juce_calloc(numBytes) juce::juce_DebugCalloc (numBytes, __FILE__, __LINE__) + /** This should be used instead of calling realloc directly. */ + #define juce_realloc(location, numBytes) juce::juce_DebugRealloc (location, numBytes, __FILE__, __LINE__) + /** This should be used instead of calling free directly. */ + #define juce_free(location) juce::juce_DebugFree (location) + #endif + + #if ! defined (_AFXDLL) + /** This macro can be added to classes to add extra debugging information to the memory + allocated for them, so you can see the type of objects involved when there's a dump + of leaked objects at program shutdown. (Only works on win32 at the moment). + */ + #define juce_UseDebuggingNewOperator \ + static void* operator new (size_t sz) { void* const p = juce_malloc ((int) sz); return (p != 0) ? p : ::operator new (sz); } \ + static void* operator new (size_t sz, void* p) { return ::operator new (sz, p); } \ + static void operator delete (void* p) { juce_free (p); } + #endif + +#elif defined (JUCE_DLL) + + // Win32 DLL (release) versions.. + + // For the DLL, we'll define some functions in the DLL that will be used for allocation - that + // way all juce calls in the DLL and in the host API will all use the same allocator. + extern JUCE_API void* juce_Malloc (const int size); + extern JUCE_API void* juce_Calloc (const int size); + extern JUCE_API void* juce_Realloc (void* const block, const int size); + extern JUCE_API void juce_Free (void* const block); + + /** This should be used instead of calling malloc directly. */ + #define juce_malloc(numBytes) juce::juce_Malloc (numBytes) + /** This should be used instead of calling calloc directly. */ + #define juce_calloc(numBytes) juce::juce_Calloc (numBytes) + /** This should be used instead of calling realloc directly. */ + #define juce_realloc(location, numBytes) juce::juce_Realloc (location, numBytes) + /** This should be used instead of calling free directly. */ + #define juce_free(location) juce::juce_Free (location) + + #define juce_UseDebuggingNewOperator \ + static void* operator new (size_t sz) { void* const p = juce_malloc ((int) sz); return (p != 0) ? p : ::operator new (sz); } \ + static void* operator new (size_t sz, void* p) { return ::operator new (sz, p); } \ + static void operator delete (void* p) { juce_free (p); } + +#else + + // Mac, Linux and Win32 (release) versions.. + + /** This should be used instead of calling malloc directly. */ + #define juce_malloc(numBytes) malloc (numBytes) + /** This should be used instead of calling calloc directly. */ + #define juce_calloc(numBytes) calloc (1, numBytes) + /** This should be used instead of calling realloc directly. */ + #define juce_realloc(location, numBytes) realloc (location, numBytes) + /** This should be used instead of calling free directly. */ + #define juce_free(location) free (location) + +#endif + +/** This macro can be added to classes to add extra debugging information to the memory + allocated for them, so you can see the type of objects involved when there's a dump + of leaked objects at program shutdown. (Only works on win32 at the moment). + + Note that if you create a class that inherits from a class that uses this macro, + your class must also use the macro, otherwise you'll probably get compile errors + because of ambiguous new operators. + + Most of the JUCE classes use it, so see these for examples of where it should go. +*/ +#ifndef juce_UseDebuggingNewOperator + #define juce_UseDebuggingNewOperator +#endif + +#if JUCE_MSVC + /** This is a compiler-indenpendent way of declaring a variable as being thread-local. + + E.g. + @code + juce_ThreadLocal int myVariable; + @endcode + */ + #define juce_ThreadLocal __declspec(thread) +#else + #define juce_ThreadLocal __thread +#endif + +/** Clears a block of memory. */ +#define zeromem(memory, numBytes) memset (memory, 0, numBytes) + +/** Clears a reference to a local structure. */ +#define zerostruct(structure) memset (&structure, 0, sizeof (structure)) + +/** A handy macro that calls delete on a pointer if it's non-zero, and + then sets the pointer to null. +*/ +#define deleteAndZero(pointer) { delete (pointer); (pointer) = 0; } + +#endif // __JUCE_MEMORY_JUCEHEADER__ +/********* End of inlined file: juce_Memory.h *********/ + +/********* Start of inlined file: juce_MathsFunctions.h *********/ +#ifndef __JUCE_MATHSFUNCTIONS_JUCEHEADER__ +#define __JUCE_MATHSFUNCTIONS_JUCEHEADER__ + +/* + This file sets up some handy mathematical typdefs and functions. +*/ + +// Definitions for the int8, int16, int32, int64 and pointer_sized_int types. + +/** A platform-independent 8-bit signed integer type. */ +typedef signed char int8; +/** A platform-independent 8-bit unsigned integer type. */ +typedef unsigned char uint8; +/** A platform-independent 16-bit signed integer type. */ +typedef signed short int16; +/** A platform-independent 16-bit unsigned integer type. */ +typedef unsigned short uint16; +/** A platform-independent 32-bit signed integer type. */ +typedef signed int int32; +/** A platform-independent 32-bit unsigned integer type. */ +typedef unsigned int uint32; + +#if JUCE_MSVC + /** A platform-independent 64-bit integer type. */ + typedef __int64 int64; + /** A platform-independent 64-bit unsigned integer type. */ + typedef unsigned __int64 uint64; + /** A platform-independent macro for writing 64-bit literals, needed because + different compilers have different syntaxes for this. + + E.g. writing literal64bit (0x1000000000) will translate to 0x1000000000LL for + GCC, or 0x1000000000 for MSVC. + */ + #define literal64bit(longLiteral) ((__int64) longLiteral) +#else + /** A platform-independent 64-bit integer type. */ + typedef long long int64; + /** A platform-independent 64-bit unsigned integer type. */ + typedef unsigned long long uint64; + /** A platform-independent macro for writing 64-bit literals, needed because + different compilers have different syntaxes for this. + + E.g. writing literal64bit (0x1000000000) will translate to 0x1000000000LL for + GCC, or 0x1000000000 for MSVC. + */ + #define literal64bit(longLiteral) (longLiteral##LL) +#endif + +#if JUCE_64BIT + /** A signed integer type that's guaranteed to be large enough to hold a pointer without truncating it. */ + typedef int64 pointer_sized_int; + /** An unsigned integer type that's guaranteed to be large enough to hold a pointer without truncating it. */ + typedef uint64 pointer_sized_uint; +#elif _MSC_VER >= 1300 + /** A signed integer type that's guaranteed to be large enough to hold a pointer without truncating it. */ + typedef _W64 int pointer_sized_int; + /** An unsigned integer type that's guaranteed to be large enough to hold a pointer without truncating it. */ + typedef _W64 unsigned int pointer_sized_uint; +#else + /** A signed integer type that's guaranteed to be large enough to hold a pointer without truncating it. */ + typedef int pointer_sized_int; + /** An unsigned integer type that's guaranteed to be large enough to hold a pointer without truncating it. */ + typedef unsigned int pointer_sized_uint; +#endif + +/** A platform-independent unicode character type. */ +typedef wchar_t juce_wchar; + +// Some indispensible min/max functions + +/** Returns the larger of two values. */ +forcedinline int jmax (const int a, const int b) throw() { return (a < b) ? b : a; } +/** Returns the larger of two values. */ +forcedinline int64 jmax (const int64 a, const int64 b) throw() { return (a < b) ? b : a; } +/** Returns the larger of two values. */ +forcedinline float jmax (const float a, const float b) throw() { return (a < b) ? b : a; } +/** Returns the larger of two values. */ +forcedinline double jmax (const double a, const double b) throw() { return (a < b) ? b : a; } + +/** Returns the larger of three values. */ +inline int jmax (const int a, const int b, const int c) throw() { return (a < b) ? ((b < c) ? c : b) : ((a < c) ? c : a); } +/** Returns the larger of three values. */ +inline int64 jmax (const int64 a, const int64 b, const int64 c) throw() { return (a < b) ? ((b < c) ? c : b) : ((a < c) ? c : a); } +/** Returns the larger of three values. */ +inline float jmax (const float a, const float b, const float c) throw() { return (a < b) ? ((b < c) ? c : b) : ((a < c) ? c : a); } +/** Returns the larger of three values. */ +inline double jmax (const double a, const double b, const double c) throw() { return (a < b) ? ((b < c) ? c : b) : ((a < c) ? c : a); } + +/** Returns the larger of four values. */ +inline int jmax (const int a, const int b, const int c, const int d) throw() { return jmax (a, jmax (b, c, d)); } +/** Returns the larger of four values. */ +inline int64 jmax (const int64 a, const int64 b, const int64 c, const int64 d) throw() { return jmax (a, jmax (b, c, d)); } +/** Returns the larger of four values. */ +inline float jmax (const float a, const float b, const float c, const float d) throw() { return jmax (a, jmax (b, c, d)); } +/** Returns the larger of four values. */ +inline double jmax (const double a, const double b, const double c, const double d) throw() { return jmax (a, jmax (b, c, d)); } + +/** Returns the smaller of two values. */ +inline int jmin (const int a, const int b) throw() { return (a > b) ? b : a; } +/** Returns the smaller of two values. */ +inline int64 jmin (const int64 a, const int64 b) throw() { return (a > b) ? b : a; } +/** Returns the smaller of two values. */ +inline float jmin (const float a, const float b) throw() { return (a > b) ? b : a; } +/** Returns the smaller of two values. */ +inline double jmin (const double a, const double b) throw() { return (a > b) ? b : a; } + +/** Returns the smaller of three values. */ +inline int jmin (const int a, const int b, const int c) throw() { return (a > b) ? ((b > c) ? c : b) : ((a > c) ? c : a); } +/** Returns the smaller of three values. */ +inline int64 jmin (const int64 a, const int64 b, const int64 c) throw() { return (a > b) ? ((b > c) ? c : b) : ((a > c) ? c : a); } +/** Returns the smaller of three values. */ +inline float jmin (const float a, const float b, const float c) throw() { return (a > b) ? ((b > c) ? c : b) : ((a > c) ? c : a); } +/** Returns the smaller of three values. */ +inline double jmin (const double a, const double b, const double c) throw() { return (a > b) ? ((b > c) ? c : b) : ((a > c) ? c : a); } + +/** Returns the smaller of four values. */ +inline int jmin (const int a, const int b, const int c, const int d) throw() { return jmin (a, jmin (b, c, d)); } +/** Returns the smaller of four values. */ +inline int64 jmin (const int64 a, const int64 b, const int64 c, const int64 d) throw() { return jmin (a, jmin (b, c, d)); } +/** Returns the smaller of four values. */ +inline float jmin (const float a, const float b, const float c, const float d) throw() { return jmin (a, jmin (b, c, d)); } +/** Returns the smaller of four values. */ +inline double jmin (const double a, const double b, const double c, const double d) throw() { return jmin (a, jmin (b, c, d)); } + +/** Constrains a value to keep it within a given range. + + This will check that the specified value lies between the lower and upper bounds + specified, and if not, will return the nearest value that would be in-range. Effectively, + it's like calling jmax (lowerLimit, jmin (upperLimit, value)). + + Note that it expects that lowerLimit <= upperLimit. If this isn't true, + the results will be unpredictable. + + @param lowerLimit the minimum value to return + @param upperLimit the maximum value to return + @param valueToConstrain the value to try to return + @returns the closest value to valueToConstrain which lies between lowerLimit + and upperLimit (inclusive) + @see jlimit0To, jmin, jmax +*/ +template +inline Type jlimit (const Type lowerLimit, + const Type upperLimit, + const Type valueToConstrain) throw() +{ + jassert (lowerLimit <= upperLimit); // if these are in the wrong order, results are unpredictable.. + + return (valueToConstrain < lowerLimit) ? lowerLimit + : ((valueToConstrain > upperLimit) ? upperLimit + : valueToConstrain); +} + +/** Handy function to swap two values over. +*/ +template +inline void swapVariables (Type& variable1, Type& variable2) throw() +{ + const Type tempVal = variable1; + variable1 = variable2; + variable2 = tempVal; +} + +/** Handy macro for getting the number of elements in a simple const C array. + + E.g. + @code + static int myArray[] = { 1, 2, 3 }; + + int numElements = numElementsInArray (myArray) // returns 3 + @endcode +*/ +#define numElementsInArray(a) ((int) (sizeof (a) / sizeof ((a)[0]))) + +// Some useful maths functions that aren't always present with all compilers and build settings. + +#if JUCE_WIN32 || defined (DOXYGEN) + /** Using juce_hypot and juce_hypotf is easier than dealing with all the different + versions of these functions of various platforms and compilers. */ + forcedinline double juce_hypot (double a, double b) { return _hypot (a, b); } + + /** Using juce_hypot and juce_hypotf is easier than dealing with all the different + versions of these functions of various platforms and compilers. */ + forcedinline float juce_hypotf (float a, float b) { return (float) _hypot (a, b); } +#elif MACOS_10_2_OR_EARLIER + /** Using juce_hypot and juce_hypotf is easier than dealing with all the different + versions of these functions of various platforms and compilers. */ + forcedinline double juce_hypot (double a, double b) { return hypot (a, b); } + + /** Using juce_hypot and juce_hypotf is easier than dealing with all the different + versions of these functions of various platforms and compilers. */ + forcedinline float juce_hypotf (float a, float b) { return (float) hypot (a, b); } + forcedinline float sinf (const float a) { return (float) sin (a); } + forcedinline float cosf (const float a) { return (float) cos (a); } + forcedinline float tanf (const float a) { return (float) tan (a); } + forcedinline float atan2f (const float a, const float b) { return (float) atan2 (a, b); } + forcedinline float sqrtf (const float a) { return (float) sqrt (a); } + forcedinline float logf (const float a) { return (float) log (a); } + forcedinline float powf (const float a, const float b) { return (float) pow (a, b); } + forcedinline float expf (const float a) { return (float) exp (a); } +#else + /** Using juce_hypot and juce_hypotf is easier than dealing with all the different + versions of these functions of various platforms and compilers. */ + forcedinline double juce_hypot (double a, double b) { return hypot (a, b); } + + /** Using juce_hypot and juce_hypotf is easier than dealing with all the different + versions of these functions of various platforms and compilers. */ + forcedinline float juce_hypotf (float a, float b) { return hypotf (a, b); } +#endif + +inline int64 abs64 (const int64 n) throw() { return (n >= 0) ? n : -n; } + +/** A predefined value for Pi, at double-precision. + + @see float_Pi +*/ +const double double_Pi = 3.1415926535897932384626433832795; + +/** A predefined value for Pi, at sngle-precision. + + @see double_Pi +*/ +const float float_Pi = 3.14159265358979323846f; + +/** The isfinite() method seems to vary greatly between platforms, so this is a + platform-independent macro for it. +*/ +#if JUCE_LINUX + #define juce_isfinite(v) std::isfinite(v) +#elif JUCE_MAC + #if MACOS_10_2_OR_EARLIER + #define juce_isfinite(v) __isfinite(v) + #elif MACOS_10_3_OR_EARLIER + #ifdef isfinite + #define juce_isfinite(v) isfinite(v) + #else + // no idea why the isfinite macro is sometimes impossible to include, so just copy the built-in one.. + static __inline__ int juce_isfinite (double __x) { return __x == __x && __builtin_fabs (__x) != __builtin_inf(); } + #endif + #else + #define juce_isfinite(v) std::isfinite(v) + #endif +#elif JUCE_WIN32 && ! defined (isfinite) + #define juce_isfinite(v) _finite(v) +#else + #define juce_isfinite(v) isfinite(v) +#endif + +#endif // __JUCE_MATHSFUNCTIONS_JUCEHEADER__ +/********* End of inlined file: juce_MathsFunctions.h *********/ + +/********* Start of inlined file: juce_DataConversions.h *********/ +#ifndef __JUCE_DATACONVERSIONS_JUCEHEADER__ +#define __JUCE_DATACONVERSIONS_JUCEHEADER__ + +#if JUCE_USE_INTRINSICS + #pragma intrinsic (_byteswap_ulong) +#endif + +// Endianness conversions.. + +/** Swaps the byte-order in an integer from little to big-endianness or vice-versa. */ +forcedinline uint32 swapByteOrder (uint32 n) throw() +{ +#if JUCE_MAC + // Mac version + return CFSwapInt32 (n); +#elif JUCE_GCC + // Inpenetrable GCC version.. + asm("bswap %%eax" : "=a"(n) : "a"(n)); + return n; +#elif JUCE_USE_INTRINSICS + // Win32 intrinsics version.. + return _byteswap_ulong (n); +#else + // Win32 version.. + __asm { + mov eax, n + bswap eax + mov n, eax + } + return n; +#endif +} + +/** Swaps the byte-order of a 16-bit short. */ +inline uint16 swapByteOrder (const uint16 n) throw() +{ +#if JUCE_USE_INTRINSICSxxx // agh - the MS compiler has an internal error when you try to use this intrinsic! + // Win32 intrinsics version.. + return (uint16) _byteswap_ushort (n); +#else + return (uint16) ((n << 8) | (n >> 8)); +#endif +} + +inline uint64 swapByteOrder (const uint64 value) throw() +{ +#if JUCE_MAC + return CFSwapInt64 (value); +#elif JUCE_USE_INTRINSICS + return _byteswap_uint64 (value); +#else + return (((int64) swapByteOrder ((uint32) value)) << 32) + | swapByteOrder ((uint32) (value >> 32)); +#endif +} + +#if JUCE_LITTLE_ENDIAN + /** Swaps the byte order of a 16-bit int if the CPU is big-endian */ + inline uint16 swapIfBigEndian (const uint16 v) throw() { return v; } + /** Swaps the byte order of a 32-bit int if the CPU is big-endian */ + inline uint32 swapIfBigEndian (const uint32 v) throw() { return v; } + /** Swaps the byte order of a 64-bit int if the CPU is big-endian */ + inline uint64 swapIfBigEndian (const uint64 v) throw() { return v; } + + /** Swaps the byte order of a 16-bit int if the CPU is little-endian */ + inline uint16 swapIfLittleEndian (const uint16 v) throw() { return swapByteOrder (v); } + /** Swaps the byte order of a 32-bit int if the CPU is little-endian */ + inline uint32 swapIfLittleEndian (const uint32 v) throw() { return swapByteOrder (v); } + /** Swaps the byte order of a 64-bit int if the CPU is little-endian */ + inline uint64 swapIfLittleEndian (const uint64 v) throw() { return swapByteOrder (v); } + + /** Turns 4 bytes into a little-endian integer. */ + inline uint32 littleEndianInt (const char* const bytes) throw() { return *(uint32*) bytes; } + /** Turns 2 bytes into a little-endian integer. */ + inline uint16 littleEndianShort (const char* const bytes) throw() { return *(uint16*) bytes; } + + /** Turns 4 bytes into a big-endian integer. */ + inline uint32 bigEndianInt (const char* const bytes) throw() { return swapByteOrder (*(uint32*) bytes); } + /** Turns 2 bytes into a big-endian integer. */ + inline uint16 bigEndianShort (const char* const bytes) throw() { return swapByteOrder (*(uint16*) bytes); } + +#else + /** Swaps the byte order of a 16-bit int if the CPU is big-endian */ + inline uint16 swapIfBigEndian (const uint16 v) throw() { return swapByteOrder (v); } + /** Swaps the byte order of a 32-bit int if the CPU is big-endian */ + inline uint32 swapIfBigEndian (const uint32 v) throw() { return swapByteOrder (v); } + /** Swaps the byte order of a 64-bit int if the CPU is big-endian */ + inline uint64 swapIfBigEndian (const uint64 v) throw() { return swapByteOrder (v); } + + /** Swaps the byte order of a 16-bit int if the CPU is little-endian */ + inline uint16 swapIfLittleEndian (const uint16 v) throw() { return v; } + /** Swaps the byte order of a 32-bit int if the CPU is little-endian */ + inline uint32 swapIfLittleEndian (const uint32 v) throw() { return v; } + /** Swaps the byte order of a 64-bit int if the CPU is little-endian */ + inline uint64 swapIfLittleEndian (const uint64 v) throw() { return v; } + + /** Turns 4 bytes into a little-endian integer. */ + inline uint32 littleEndianInt (const char* const bytes) throw() { return swapByteOrder (*(uint32*) bytes); } + /** Turns 2 bytes into a little-endian integer. */ + inline uint16 littleEndianShort (const char* const bytes) throw() { return swapByteOrder (*(uint16*) bytes); } + + /** Turns 4 bytes into a big-endian integer. */ + inline uint32 bigEndianInt (const char* const bytes) throw() { return *(uint32*) bytes; } + /** Turns 2 bytes into a big-endian integer. */ + inline uint16 bigEndianShort (const char* const bytes) throw() { return *(uint16*) bytes; } +#endif + +/** Converts 3 little-endian bytes into a signed 24-bit value (which is sign-extended to 32 bits). */ +inline int littleEndian24Bit (const char* const bytes) throw() { return (((int) bytes[2]) << 16) | (((uint32) (uint8) bytes[1]) << 8) | ((uint32) (uint8) bytes[0]); } +/** Converts 3 big-endian bytes into a signed 24-bit value (which is sign-extended to 32 bits). */ +inline int bigEndian24Bit (const char* const bytes) throw() { return (((int) bytes[0]) << 16) | (((uint32) (uint8) bytes[1]) << 8) | ((uint32) (uint8) bytes[2]); } + +/** Copies a 24-bit number to 3 little-endian bytes. */ +inline void littleEndian24BitToChars (const int value, char* const destBytes) throw() { destBytes[0] = (char)(value & 0xff); destBytes[1] = (char)((value >> 8) & 0xff); destBytes[2] = (char)((value >> 16) & 0xff); } +/** Copies a 24-bit number to 3 big-endian bytes. */ +inline void bigEndian24BitToChars (const int value, char* const destBytes) throw() { destBytes[0] = (char)((value >> 16) & 0xff); destBytes[1] = (char)((value >> 8) & 0xff); destBytes[2] = (char)(value & 0xff); } + +/** Fast floating-point-to-integer conversion. + + This is faster than using the normal c++ cast to convert a double to an int, and + it will round the value to the nearest integer, rather than rounding it down + like the normal cast does. + + Note that this routine gets its speed at the expense of some accuracy, and when + rounding values whose floating point component is exactly 0.5, odd numbers and + even numbers will be rounded up or down differently. For a more accurate conversion, + see roundDoubleToIntAccurate(). +*/ +inline int roundDoubleToInt (const double value) throw() +{ + union { int asInt[2]; double asDouble; } n; + n.asDouble = value + 6755399441055744.0; + +#if JUCE_BIG_ENDIAN + return n.asInt [1]; +#else + return n.asInt [0]; +#endif +} + +/** Fast floating-point-to-integer conversion. + + This is a slightly slower and slightly more accurate version of roundDoubleToInt(). It works + fine for values above zero, but negative numbers are rounded the wrong way. +*/ +inline int roundDoubleToIntAccurate (const double value) throw() +{ + return roundDoubleToInt (value + 1.5e-8); +} + +/** Fast floating-point-to-integer conversion. + + This is faster than using the normal c++ cast to convert a float to an int, and + it will round the value to the nearest integer, rather than rounding it down + like the normal cast does. + + Note that this routine gets its speed at the expense of some accuracy, and when + rounding values whose floating point component is exactly 0.5, odd numbers and + even numbers will be rounded up or down differently. +*/ +inline int roundFloatToInt (const float value) throw() +{ + union { int asInt[2]; double asDouble; } n; + n.asDouble = value + 6755399441055744.0; + +#if JUCE_BIG_ENDIAN + return n.asInt [1]; +#else + return n.asInt [0]; +#endif +} + +#endif // __JUCE_DATACONVERSIONS_JUCEHEADER__ +/********* End of inlined file: juce_DataConversions.h *********/ + +/********* Start of inlined file: juce_Logger.h *********/ +#ifndef __JUCE_LOGGER_JUCEHEADER__ +#define __JUCE_LOGGER_JUCEHEADER__ + +/********* Start of inlined file: juce_String.h *********/ +#ifndef __JUCE_STRING_JUCEHEADER__ +#define __JUCE_STRING_JUCEHEADER__ + +/********* Start of inlined file: juce_CharacterFunctions.h *********/ +#ifndef __JUCE_CHARACTERFUNCTIONS_JUCEHEADER__ +#define __JUCE_CHARACTERFUNCTIONS_JUCEHEADER__ + +/* The String class can either use wchar_t unicode characters, or 8-bit characters + (in the default system encoding) as its internal representation. + + To use unicode, define the JUCE_STRINGS_ARE_UNICODE macro in juce_Config.h + + Be sure to use "tchar" for characters rather than "char", and always wrap string + literals in the T("abcd") macro, so that it all works nicely either way round. +*/ +#if JUCE_STRINGS_ARE_UNICODE + + #define JUCE_T(stringLiteral) (L##stringLiteral) + typedef juce_wchar tchar; + #define juce_tcharToWideChar(c) (c) + +#else + + #define JUCE_T(stringLiteral) (stringLiteral) + typedef char tchar; + #define juce_tcharToWideChar(c) ((juce_wchar) (unsigned char) (c)) + +#endif + +#if ! JUCE_DONT_DEFINE_MACROS + +/** The 'T' macro allows a literal string to be compiled using either 8-bit characters + or unicode. + + If you write your string literals in the form T("xyz"), this will either be compiled + as "xyz" for non-unicode builds, or L"xyz" for unicode builds, depending on whether the + JUCE_STRINGS_ARE_UNICODE macro has been set in juce_Config.h + + Because the 'T' symbol is occasionally used inside 3rd-party library headers which you + may need to include after juce.h, you can use the juce_withoutMacros.h file (in + the juce/src directory) to avoid defining this macro. See the comments in + juce_withoutMacros.h for more info. +*/ +#define T(stringLiteral) JUCE_T(stringLiteral) + +#endif + +/** + A set of methods for manipulating characters and character strings, with + duplicate methods to handle 8-bit and unicode characters. + + These are defined as wrappers around the basic C string handlers, to provide + a clean, cross-platform layer, (because various platforms differ in the + range of C library calls that they provide). + + @see String +*/ +class JUCE_API CharacterFunctions +{ +public: + static int length (const char* const s) throw(); + static int length (const juce_wchar* const s) throw(); + + static void copy (char* dest, const char* src, const int maxChars) throw(); + static void copy (juce_wchar* dest, const juce_wchar* src, const int maxChars) throw(); + + static void copy (juce_wchar* dest, const char* src, const int maxChars) throw(); + static void copy (char* dest, const juce_wchar* src, const int maxChars) throw(); + + static void append (char* dest, const char* src) throw(); + static void append (juce_wchar* dest, const juce_wchar* src) throw(); + + static int compare (const char* const s1, const char* const s2) throw(); + static int compare (const juce_wchar* s1, const juce_wchar* s2) throw(); + + static int compare (const char* const s1, const char* const s2, const int maxChars) throw(); + static int compare (const juce_wchar* s1, const juce_wchar* s2, int maxChars) throw(); + + static int compareIgnoreCase (const char* const s1, const char* const s2) throw(); + static int compareIgnoreCase (const juce_wchar* s1, const juce_wchar* s2) throw(); + + static int compareIgnoreCase (const char* const s1, const char* const s2, const int maxChars) throw(); + static int compareIgnoreCase (const juce_wchar* s1, const juce_wchar* s2, int maxChars) throw(); + + static const char* find (const char* const haystack, const char* const needle) throw(); + static const juce_wchar* find (const juce_wchar* haystack, const juce_wchar* const needle) throw(); + + static int indexOfChar (const char* const haystack, const char needle, const bool ignoreCase) throw(); + static int indexOfChar (const juce_wchar* const haystack, const juce_wchar needle, const bool ignoreCase) throw(); + + static int indexOfCharFast (const char* const haystack, const char needle) throw(); + static int indexOfCharFast (const juce_wchar* const haystack, const juce_wchar needle) throw(); + + static int getIntialSectionContainingOnly (const char* const text, const char* const allowedChars) throw(); + static int getIntialSectionContainingOnly (const juce_wchar* const text, const juce_wchar* const allowedChars) throw(); + + static int ftime (char* const dest, const int maxChars, const char* const format, const struct tm* const tm) throw(); + static int ftime (juce_wchar* const dest, const int maxChars, const juce_wchar* const format, const struct tm* const tm) throw(); + + static int getIntValue (const char* const s) throw(); + static int getIntValue (const juce_wchar* s) throw(); + + static int64 getInt64Value (const char* s) throw(); + static int64 getInt64Value (const juce_wchar* s) throw(); + + static double getDoubleValue (const char* const s) throw(); + static double getDoubleValue (const juce_wchar* const s) throw(); + + static char toUpperCase (const char character) throw(); + static juce_wchar toUpperCase (const juce_wchar character) throw(); + static void toUpperCase (char* s) throw(); + + static void toUpperCase (juce_wchar* s) throw(); + static bool isUpperCase (const char character) throw(); + static bool isUpperCase (const juce_wchar character) throw(); + + static char toLowerCase (const char character) throw(); + static juce_wchar toLowerCase (const juce_wchar character) throw(); + static void toLowerCase (char* s) throw(); + static void toLowerCase (juce_wchar* s) throw(); + static bool isLowerCase (const char character) throw(); + static bool isLowerCase (const juce_wchar character) throw(); + + static bool isWhitespace (const char character) throw(); + static bool isWhitespace (const juce_wchar character) throw(); + + static bool isDigit (const char character) throw(); + static bool isDigit (const juce_wchar character) throw(); + + static bool isLetter (const char character) throw(); + static bool isLetter (const juce_wchar character) throw(); + + static bool isLetterOrDigit (const char character) throw(); + static bool isLetterOrDigit (const juce_wchar character) throw(); + + /** Returns 0 to 16 for '0' to 'F", or -1 for characters that aren't a legel + hex digit. + */ + static int getHexDigitValue (const tchar digit) throw(); + + static int printf (char* const dest, const int maxLength, const char* const format, ...) throw(); + static int printf (juce_wchar* const dest, const int maxLength, const juce_wchar* const format, ...) throw(); + + static int vprintf (char* const dest, const int maxLength, const char* const format, va_list& args) throw(); + static int vprintf (juce_wchar* const dest, const int maxLength, const juce_wchar* const format, va_list& args) throw(); +}; + +#endif // __JUCE_CHARACTERFUNCTIONS_JUCEHEADER__ +/********* End of inlined file: juce_CharacterFunctions.h *********/ + +/** + The JUCE String class! + + Using a reference-counted internal representation, these strings are fast + and efficient, and there are methods to do just about any operation you'll ever + dream of. + + @see StringArray, StringPairArray +*/ +class JUCE_API String +{ +public: + + /** Creates an empty string. + + @see empty + */ + String() throw(); + + /** Creates a copy of another string. */ + String (const String& other) throw(); + + /** Creates a string from a zero-terminated text string. + + The string is assumed to be stored in the default system encoding. + */ + String (const char* const text) throw(); + + /** Creates a string from an string of characters. + + This will use up the the first maxChars characters of the string (or + less if the string is actually shorter) + */ + String (const char* const text, + const int maxChars) throw(); + + /** Creates a string from a zero-terminated unicode text string. */ + String (const juce_wchar* const unicodeText) throw(); + + /** Creates a string from a unicode text string. + + This will use up the the first maxChars characters of the string (or + less if the string is actually shorter) + */ + String (const juce_wchar* const unicodeText, + const int maxChars) throw(); + + /** Creates a string from a single character. */ + static const String charToString (const tchar character) throw(); + + /** Destructor. */ + ~String() throw(); + + /** This is an empty string that can be used whenever one is needed. + + It's better to use this than String() because it explains what's going on + and is more efficient. + */ + static const String empty; + + /** Generates a probably-unique 32-bit hashcode from this string. */ + int hashCode() const throw(); + + /** Generates a probably-unique 64-bit hashcode from this string. */ + int64 hashCode64() const throw(); + + /** Returns the number of characters in the string. */ + int length() const throw(); + + // Assignment and concatenation operators.. + + /** Replaces this string's contents with another string. */ + const String& operator= (const tchar* const other) throw(); + + /** Replaces this string's contents with another string. */ + const String& operator= (const String& other) throw(); + + /** Appends another string at the end of this one. */ + const String& operator+= (const tchar* const textToAppend) throw(); + /** Appends another string at the end of this one. */ + const String& operator+= (const String& stringToAppend) throw(); + /** Appends a character at the end of this string. */ + const String& operator+= (const char characterToAppend) throw(); + /** Appends a character at the end of this string. */ + const String& operator+= (const juce_wchar characterToAppend) throw(); + + /** Appends a string at the end of this one. + + @param textToAppend the string to add + @param maxCharsToTake the maximum number of characters to take from the string passed in + */ + void append (const tchar* const textToAppend, + const int maxCharsToTake) throw(); + + /** Appends a string at the end of this one. + @returns the concatenated string + */ + const String operator+ (const String& stringToAppend) const throw(); + + /** Appends a string at the end of this one. + @returns the concatenated string + */ + const String operator+ (const tchar* const textToAppend) const throw(); + + /** Appends a character at the end of this one. + @returns the concatenated string + */ + const String operator+ (const tchar characterToAppend) const throw(); + + /** Appends a character at the end of this string. */ + String& operator<< (const char n) throw(); + /** Appends a character at the end of this string. */ + String& operator<< (const juce_wchar n) throw(); + /** Appends another string at the end of this one. */ + String& operator<< (const char* const text) throw(); + /** Appends another string at the end of this one. */ + String& operator<< (const juce_wchar* const text) throw(); + /** Appends another string at the end of this one. */ + String& operator<< (const String& text) throw(); + + /** Appends a decimal number at the end of this string. */ + String& operator<< (const short number) throw(); + /** Appends a decimal number at the end of this string. */ + String& operator<< (const int number) throw(); + /** Appends a decimal number at the end of this string. */ + String& operator<< (const unsigned int number) throw(); + /** Appends a decimal number at the end of this string. */ + String& operator<< (const float number) throw(); + /** Appends a decimal number at the end of this string. */ + String& operator<< (const double number) throw(); + + // Comparison methods.. + + /** Returns true if the string contains no characters. + + Note that there's also an isNotEmpty() method to help write readable code. + */ + inline bool isEmpty() const throw() { return text->text[0] == 0; } + + /** Returns true if the string contains at least one character. + + Note that there's also an isEmpty() method to help write readable code. + */ + inline bool isNotEmpty() const throw() { return text->text[0] != 0; } + + /** Case-sensitive comparison with another string. */ + bool operator== (const String& other) const throw(); + /** Case-sensitive comparison with another string. */ + bool operator== (const tchar* const other) const throw(); + + /** Case-sensitive comparison with another string. */ + bool operator!= (const String& other) const throw(); + /** Case-sensitive comparison with another string. */ + bool operator!= (const tchar* const other) const throw(); + + /** Case-insensitive comparison with another string. */ + bool equalsIgnoreCase (const String& other) const throw(); + /** Case-insensitive comparison with another string. */ + bool equalsIgnoreCase (const tchar* const other) const throw(); + + /** Case-sensitive comparison with another string. */ + bool operator> (const String& other) const throw(); + /** Case-sensitive comparison with another string. */ + bool operator< (const tchar* const other) const throw(); + + /** Case-sensitive comparison with another string. */ + bool operator>= (const String& other) const throw(); + /** Case-sensitive comparison with another string. */ + bool operator<= (const tchar* const other) const throw(); + + /** Case-sensitive comparison with another string. + @returns 0 if the two strings are identical; negative if this string + comes before the other one alphabetically, or positive if it + comes after it. + */ + int compare (const tchar* const other) const throw(); + + /** Case-insensitive comparison with another string. + @returns 0 if the two strings are identical; negative if this string + comes before the other one alphabetically, or positive if it + comes after it. + */ + int compareIgnoreCase (const tchar* const other) const throw(); + + /** Lexicographic comparison with another string. + + The comparison used here is case-insensitive and ignores leading non-alphanumeric + characters, making it good for sorting human-readable strings. + + @returns 0 if the two strings are identical; negative if this string + comes before the other one alphabetically, or positive if it + comes after it. + */ + int compareLexicographically (const tchar* const other) const throw(); + + /** Tests whether the string begins with another string. + + Uses a case-sensitive comparison. + */ + bool startsWith (const tchar* const text) const throw(); + + /** Tests whether the string begins with a particular character. + + Uses a case-sensitive comparison. + */ + bool startsWithChar (const tchar character) const throw(); + + /** Tests whether the string begins with another string. + + Uses a case-insensitive comparison. + */ + bool startsWithIgnoreCase (const tchar* const text) const throw(); + + /** Tests whether the string ends with another string. + + Uses a case-sensitive comparison. + */ + bool endsWith (const tchar* const text) const throw(); + + /** Tests whether the string ends with a particular character. + + Uses a case-sensitive comparison. + */ + bool endsWithChar (const tchar character) const throw(); + + /** Tests whether the string ends with another string. + + Uses a case-insensitive comparison. + */ + bool endsWithIgnoreCase (const tchar* const text) const throw(); + + /** Tests whether the string contains another substring. + + Uses a case-sensitive comparison. + */ + bool contains (const tchar* const text) const throw(); + + /** Tests whether the string contains a particular character. + + Uses a case-sensitive comparison. + */ + bool containsChar (const tchar character) const throw(); + + /** Tests whether the string contains another substring. + + Uses a case-insensitive comparison. + */ + bool containsIgnoreCase (const tchar* const text) const throw(); + + /** Tests whether the string contains another substring as a distict word. + + @returns true if the string contains this word, surrounded by + non-alphanumeric characters + @see indexOfWholeWord, containsWholeWordIgnoreCase + */ + bool containsWholeWord (const tchar* const wordToLookFor) const throw(); + + /** Tests whether the string contains another substring as a distict word. + + @returns true if the string contains this word, surrounded by + non-alphanumeric characters + @see indexOfWholeWordIgnoreCase, containsWholeWord + */ + bool containsWholeWordIgnoreCase (const tchar* const wordToLookFor) const throw(); + + /** Finds an instance of another substring if it exists as a distict word. + + @returns if the string contains this word, surrounded by non-alphanumeric characters, + then this will return the index of the start of the substring. If it isn't + found, then it will return -1 + @see indexOfWholeWordIgnoreCase, containsWholeWord + */ + int indexOfWholeWord (const tchar* const wordToLookFor) const throw(); + + /** Finds an instance of another substring if it exists as a distict word. + + @returns if the string contains this word, surrounded by non-alphanumeric characters, + then this will return the index of the start of the substring. If it isn't + found, then it will return -1 + @see indexOfWholeWord, containsWholeWordIgnoreCase + */ + int indexOfWholeWordIgnoreCase (const tchar* const wordToLookFor) const throw(); + + /** Looks for any of a set of characters in the string. + + Uses a case-sensitive comparison. + + @returns true if the string contains any of the characters from + the string that is passed in. + */ + bool containsAnyOf (const tchar* const charactersItMightContain) const throw(); + + /** Looks for a set of characters in the string. + + Uses a case-sensitive comparison. + + @returns true if the all the characters in the string are also found in the + string that is passed in. + */ + bool containsOnly (const tchar* const charactersItMightContain) const throw(); + + /** Returns true if the string matches this simple wildcard expression. + + So for example String ("abcdef").matchesWildcard ("*DEF", true) would return true. + + This isn't a full-blown regex though! The only wildcard characters supported + are "*" and "?". It's mainly intended for filename pattern matching. + */ + bool matchesWildcard (const tchar* wildcard, const bool ignoreCase) const throw(); + + // Substring location methods.. + + /** Searches for a character inside this string. + + Uses a case-sensitive comparison. + + @returns the index of the first occurrence of the character in this + string, or -1 if it's not found. + */ + int indexOfChar (const tchar characterToLookFor) const throw(); + + /** Searches for a character inside this string. + + Uses a case-sensitive comparison. + + @param startIndex the index from which the search should proceed + @param characterToLookFor the character to look for + @returns the index of the first occurrence of the character in this + string, or -1 if it's not found. + */ + int indexOfChar (const int startIndex, const tchar characterToLookFor) const throw(); + + /** Returns the index of the first character that matches one of the characters + passed-in to this method. + + This scans the string, beginning from the startIndex supplied, and if it finds + a character that appears in the string charactersToLookFor, it returns its index. + + If none of these characters are found, it returns -1. + + If ignoreCase is true, the comparison will be case-insensitive. + + @see indexOfChar, lastIndexOfAnyOf + */ + int indexOfAnyOf (const tchar* const charactersToLookFor, + const int startIndex = 0, + const bool ignoreCase = false) const throw(); + + /** Searches for a substring within this string. + + Uses a case-sensitive comparison. + + @returns the index of the first occurrence of this substring, or -1 if it's not found. + */ + int indexOf (const tchar* const text) const throw(); + + /** Searches for a substring within this string. + + Uses a case-sensitive comparison. + + @param startIndex the index from which the search should proceed + @param textToLookFor the string to search for + @returns the index of the first occurrence of this substring, or -1 if it's not found. + */ + int indexOf (const int startIndex, + const tchar* const textToLookFor) const throw(); + + /** Searches for a substring within this string. + + Uses a case-insensitive comparison. + + @returns the index of the first occurrence of this substring, or -1 if it's not found. + */ + int indexOfIgnoreCase (const tchar* const textToLookFor) const throw(); + + /** Searches for a substring within this string. + + Uses a case-insensitive comparison. + + @param startIndex the index from which the search should proceed + @param textToLookFor the string to search for + @returns the index of the first occurrence of this substring, or -1 if it's not found. + */ + int indexOfIgnoreCase (const int startIndex, + const tchar* const textToLookFor) const throw(); + + /** Searches for a character inside this string (working backwards from the end of the string). + + Uses a case-sensitive comparison. + + @returns the index of the last occurrence of the character in this + string, or -1 if it's not found. + */ + int lastIndexOfChar (const tchar character) const throw(); + + /** Searches for a substring inside this string (working backwards from the end of the string). + + Uses a case-sensitive comparison. + + @returns the index of the start of the last occurrence of the + substring within this string, or -1 if it's not found. + */ + int lastIndexOf (const tchar* const textToLookFor) const throw(); + + /** Searches for a substring inside this string (working backwards from the end of the string). + + Uses a case-insensitive comparison. + + @returns the index of the start of the last occurrence of the + substring within this string, or -1 if it's not found. + */ + int lastIndexOfIgnoreCase (const tchar* const textToLookFor) const throw(); + + /** Returns the index of the last character in this string that matches one of the + characters passed-in to this method. + + This scans the string backwards, starting from its end, and if it finds + a character that appears in the string charactersToLookFor, it returns its index. + + If none of these characters are found, it returns -1. + + If ignoreCase is true, the comparison will be case-insensitive. + + @see lastIndexOf, indexOfAnyOf + */ + int lastIndexOfAnyOf (const tchar* const charactersToLookFor, + const bool ignoreCase = false) const throw(); + + // Substring extraction and manipulation methods.. + + /** Returns the character at this index in the string. + + No checks are made to see if the index is within a valid range, so be careful! + */ + inline const tchar& operator[] (const int index) const throw() { jassert (((unsigned int) index) <= (unsigned int) length()); return text->text [index]; } + + /** Returns a character from the string such that it can also be altered. + + This can be used as a way of easily changing characters in the string. + + Note that the index passed-in is not checked to see whether it's in-range, so + be careful when using this. + */ + tchar& operator[] (const int index) throw(); + + /** Returns the final character of the string. + + If the string is empty this will return 0. + */ + tchar getLastCharacter() const throw(); + + /** Returns a subsection of the string. + + If the range specified is beyond the limits of the string, as much as + possible is returned. + + @param startIndex the index of the start of the substring needed + @param endIndex all characters from startIndex up to (but not including) + this index are returned + @see fromFirstOccurrenceOf, dropLastCharacters, upToFirstOccurrenceOf + */ + const String substring (int startIndex, + int endIndex) const throw(); + + /** Returns a section of the string, starting from a given position. + + @param startIndex the first character to include. If this is beyond the end + of the string, an empty string is returned. If it is zero or + less, the whole string is returned. + @returns the substring from startIndex up to the end of the string + @see dropLastCharacters, fromFirstOccurrenceOf, upToFirstOccurrenceOf, fromLastOccurrenceOf + */ + const String substring (const int startIndex) const throw(); + + /** Returns a version of this string with a number of characters removed + from the end. + + @param numberToDrop the number of characters to drop from the end of the + string. If this is greater than the length of the string, + an empty string will be returned. If zero or less, the + original string will be returned. + @see substring, fromFirstOccurrenceOf, upToFirstOccurrenceOf, fromLastOccurrenceOf, getLastCharacter + */ + const String dropLastCharacters (const int numberToDrop) const throw(); + + /** Returns a section of the string starting from a given substring. + + This will search for the first occurrence of the given substring, and + return the section of the string starting from the point where this is + found (optionally not including the substring itself). + + e.g. for the string "123456", fromFirstOccurrenceOf ("34", true) would return "3456", and + fromFirstOccurrenceOf ("34", false) would return "56". + + If the substring isn't found, the method will return an empty string. + + If ignoreCase is true, the comparison will be case-insensitive. + + @see upToFirstOccurrenceOf, fromLastOccurrenceOf + */ + const String fromFirstOccurrenceOf (const tchar* const substringToStartFrom, + const bool includeSubStringInResult, + const bool ignoreCase) const throw(); + + /** Returns a section of the string starting from the last occurrence of a given substring. + + Similar to fromFirstOccurrenceOf(), but using the last occurrence of the substring, and + unlike fromFirstOccurrenceOf(), if the substring isn't found, this method will + return the whole of the original string. + + @see fromFirstOccurrenceOf, upToLastOccurrenceOf + */ + const String fromLastOccurrenceOf (const tchar* const substringToFind, + const bool includeSubStringInResult, + const bool ignoreCase) const throw(); + + /** Returns the start of this string, up to the first occurrence of a substring. + + This will search for the first occurrence of a given substring, and then + return a copy of the string, up to the position of this substring, + optionally including or excluding the substring itself in the result. + + e.g. for the string "123456", upTo ("34", false) would return "12", and + upTo ("34", true) would return "1234". + + If the substring isn't found, this will return the whole of the original string. + + @see upToLastOccurrenceOf, fromFirstOccurrenceOf + */ + const String upToFirstOccurrenceOf (const tchar* const substringToEndWith, + const bool includeSubStringInResult, + const bool ignoreCase) const throw(); + + /** Returns the start of this string, up to the last occurrence of a substring. + + Similar to upToFirstOccurrenceOf(), but this finds the last occurrence rather than the first. + + @see upToFirstOccurrenceOf, fromFirstOccurrenceOf + */ + const String upToLastOccurrenceOf (const tchar* substringToFind, + const bool includeSubStringInResult, + const bool ignoreCase) const throw(); + + /** Returns a copy of this string with any whitespace characters removed from the start and end. */ + const String trim() const throw(); + /** Returns a copy of this string with any whitespace characters removed from the start. */ + const String trimStart() const throw(); + /** Returns a copy of this string with any whitespace characters removed from the end. */ + const String trimEnd() const throw(); + + /** Returns an upper-case version of this string. */ + const String toUpperCase() const throw(); + + /** Returns an lower-case version of this string. */ + const String toLowerCase() const throw(); + + /** Replaces a sub-section of the string with another string. + + This will return a copy of this string, with a set of characters + from startIndex to startIndex + numCharsToReplace removed, and with + a new string inserted in their place. + + Note that this is a const method, and won't alter the string itself. + + @param startIndex the first character to remove. If this is beyond the bounds of the string, + it will be constrained to a valid range. + @param numCharactersToReplace the number of characters to remove. If zero or less, no + characters will be taken out. + @param stringToInsert the new string to insert at startIndex after the characters have been + removed. + */ + const String replaceSection (int startIndex, + int numCharactersToReplace, + const tchar* const stringToInsert) const throw(); + + /** Replaces all occurrences of a substring with another string. + + Returns a copy of this string, with any occurrences of stringToReplace + swapped for stringToInsertInstead. + + Note that this is a const method, and won't alter the string itself. + */ + const String replace (const tchar* const stringToReplace, + const tchar* const stringToInsertInstead, + const bool ignoreCase = false) const throw(); + + /** Returns a string with all occurrences of a character replaced with a different one. */ + const String replaceCharacter (const tchar characterToReplace, + const tchar characterToInsertInstead) const throw(); + + /** Replaces a set of characters with another set. + + Returns a string in which each character from charactersToReplace has been replaced + by the character at the equivalent position in newCharacters (so the two strings + passed in must be the same length). + + e.g. translate ("abc", "def") replaces 'a' with 'd', 'b' with 'e', etc. + + Note that this is a const method, and won't affect the string itself. + */ + const String replaceCharacters (const String& charactersToReplace, + const tchar* const charactersToInsertInstead) const throw(); + + /** Returns a version of this string that only retains a fixed set of characters. + + This will return a copy of this string, omitting any characters which are not + found in the string passed-in. + + e.g. for "1122334455", retainCharacters ("432") would return "223344" + + Note that this is a const method, and won't alter the string itself. + */ + const String retainCharacters (const tchar* const charactersToRetain) const throw(); + + /** Returns a version of this string with a set of characters removed. + + This will return a copy of this string, omitting any characters which are + found in the string passed-in. + + e.g. for "1122334455", removeCharacters ("432") would return "1155" + + Note that this is a const method, and won't alter the string itself. + */ + const String removeCharacters (const tchar* const charactersToRemove) const throw(); + + /** Returns a section from the start of the string that only contains a certain set of characters. + + This returns the leftmost section of the string, up to (and not including) the + first character that doesn't appear in the string passed in. + */ + const String initialSectionContainingOnly (const tchar* const permittedCharacters) const throw(); + + /** Returns a section from the start of the string that only contains a certain set of characters. + + This returns the leftmost section of the string, up to (and not including) the + first character that occurs in the string passed in. + */ + const String initialSectionNotContaining (const tchar* const charactersToStopAt) const throw(); + + /** Checks whether the string might be in quotation marks. + + @returns true if the string begins with a quote character (either a double or single quote). + It is also true if there is whitespace before the quote, but it doesn't check the end of the string. + @see unquoted, quoted + */ + bool isQuotedString() const throw(); + + /** Removes quotation marks from around the string, (if there are any). + + Returns a copy of this string with any quotes removed from its ends. Quotes that aren't + at the ends of the string are not affected. If there aren't any quotes, the original string + is returned. + + Note that this is a const method, and won't alter the string itself. + + @see isQuotedString, quoted + */ + const String unquoted() const throw(); + + /** Adds quotation marks around a string. + + This will return a copy of the string with a quote at the start and end, (but won't + add the quote if there's already one there, so it's safe to call this on strings that + may already have quotes around them). + + Note that this is a const method, and won't alter the string itself. + + @param quoteCharacter the character to add at the start and end + @see isQuotedString, unquoted + */ + const String quoted (const tchar quoteCharacter = JUCE_T('"')) const throw(); + + /** Writes text into this string, using printf style-arguments. + + This will replace the contents of the string with the output of this + formatted printf. + + @see formatted + */ + void printf (const tchar* const format, ...) throw(); + + /** Returns a string, created using arguments in the style of printf. + + This will return a string which is the result of a sprintf using the + arguments passed-in. + + @see printf, vprintf + */ + static const String formatted (const tchar* const format, ...) throw(); + + /** Writes text into this string, using a printf style, but taking a va_list argument. + + This will replace the contents of the string with the output of this + formatted printf. Used by other methods, this is public in case it's + useful for other purposes where you want to pass a va_list through directly. + + @see printf, formatted + */ + void vprintf (const tchar* const format, va_list& args) throw(); + + /** Creates a string which is a version of a string repeated and joined together. + + @param stringToRepeat the string to repeat + @param numberOfTimesToRepeat how many times to repeat it + */ + static const String repeatedString (const tchar* const stringToRepeat, + int numberOfTimesToRepeat) throw(); + + /** Creates a string from data in an unknown format. + + This looks at some binary data and tries to guess whether it's Unicode + or 8-bit characters, then returns a string that represents it correctly. + + Should be able to handle Unicode endianness correctly, by looking at + the first two bytes. + */ + static const String createStringFromData (const void* const data, + const int size) throw(); + + // Numeric conversions.. + + /** Creates a string containing this signed 32-bit integer as a decimal number. + + @see getIntValue, getFloatValue, getDoubleValue, toHexString + */ + explicit String (const int decimalInteger) throw(); + + /** Creates a string containing this unsigned 32-bit integer as a decimal number. + + @see getIntValue, getFloatValue, getDoubleValue, toHexString + */ + explicit String (const unsigned int decimalInteger) throw(); + + /** Creates a string containing this signed 16-bit integer as a decimal number. + + @see getIntValue, getFloatValue, getDoubleValue, toHexString + */ + explicit String (const short decimalInteger) throw(); + + /** Creates a string containing this unsigned 16-bit integer as a decimal number. + + @see getIntValue, getFloatValue, getDoubleValue, toHexString + */ + explicit String (const unsigned short decimalInteger) throw(); + + /** Creates a string containing this signed 64-bit integer as a decimal number. + + @see getLargeIntValue, getFloatValue, getDoubleValue, toHexString + */ + explicit String (const int64 largeIntegerValue) throw(); + + /** Creates a string containing this unsigned 64-bit integer as a decimal number. + + @see getLargeIntValue, getFloatValue, getDoubleValue, toHexString + */ + explicit String (const uint64 largeIntegerValue) throw(); + + /** Creates a string representing this floating-point number. + + @param floatValue the value to convert to a string + @param numberOfDecimalPlaces if this is > 0, it will format the number using that many + decimal places, and will not use exponent notation. If 0 or + less, it will use exponent notation if necessary. + @see getDoubleValue, getIntValue + */ + explicit String (const float floatValue, + const int numberOfDecimalPlaces = 0) throw(); + + /** Creates a string representing this floating-point number. + + @param doubleValue the value to convert to a string + @param numberOfDecimalPlaces if this is > 0, it will format the number using that many + decimal places, and will not use exponent notation. If 0 or + less, it will use exponent notation if necessary. + + @see getFloatValue, getIntValue + */ + explicit String (const double doubleValue, + const int numberOfDecimalPlaces = 0) throw(); + + /** Parses this string to find its numerical value (up to 32 bits in size). + + @returns the value of the string as a 32 bit signed base-10 integer. + @see getTrailingIntValue, getHexValue32, getHexValue64 + */ + int getIntValue() const throw(); + + /** Parses this string to find its numerical value (up to 64 bits in size). + + @returns the value of the string as a 64 bit signed base-10 integer. + */ + int64 getLargeIntValue() const throw(); + + /** Parses a decimal number from the end of the string. + + This will look for a value at the end of the string. + e.g. for "321 xyz654" it will return 654; for "2 3 4" it'll return 4. + + Negative numbers are not handled, so "xyz-5" returns 5. + + @see getIntValue + */ + int getTrailingIntValue() const throw(); + + /** Parses this string as a floating point number. + + @returns the value of the string as a 32-bit floating point value. + @see getDoubleValue + */ + float getFloatValue() const throw(); + + /** Parses this string as a floating point number. + + @returns the value of the string as a 64-bit floating point value. + @see getFloatValue + */ + double getDoubleValue() const throw(); + + /** Parses the string as a hexadecimal number. + + Non-hexadecimal characters in the string are ignored. + + If the string contains too many characters, then the lowest significant + digits are returned, e.g. "ffff12345678" would produce 0x12345678. + + @returns a 32-bit number which is the value of the string in hex. + */ + int getHexValue32() const throw(); + + /** Parses the string as a hexadecimal number. + + Non-hexadecimal characters in the string are ignored. + + If the string contains too many characters, then the lowest significant + digits are returned, e.g. "ffff1234567812345678" would produce 0x1234567812345678. + + @returns a 64-bit number which is the value of the string in hex. + */ + int64 getHexValue64() const throw(); + + /** Creates a string representing this 32-bit value in hexadecimal. */ + static const String toHexString (const int number) throw(); + + /** Creates a string representing this 64-bit value in hexadecimal. */ + static const String toHexString (const int64 number) throw(); + + /** Creates a string representing this 16-bit value in hexadecimal. */ + static const String toHexString (const short number) throw(); + + /** Creates a string containing a hex dump of a block of binary data. + + @param data the binary data to use as input + @param size how many bytes of data to use + @param groupSize how many bytes are grouped together before inserting a + space into the output. e.g. group size 0 has no spaces, + group size 1 looks like: "be a1 c2 ff", group size 2 looks + like "bea1 c2ff". + */ + static const String toHexString (const unsigned char* data, + const int size, + const int groupSize = 1) throw(); + + // Casting to character arrays.. + +#if JUCE_STRINGS_ARE_UNICODE + /** Returns a version of this string using the default 8-bit system encoding. + + Because it returns a reference to the string's internal data, the pointer + that is returned must not be stored anywhere, as it can be deleted whenever the + string changes. + */ + operator const char*() const throw(); + + /** Returns a unicode version of this string. + + Because it returns a reference to the string's internal data, the pointer + that is returned must not be stored anywhere, as it can be deleted whenever the + string changes. + */ + inline operator const juce_wchar*() const throw() { return text->text; } +#else + /** Returns a version of this string using the default 8-bit system encoding. + + Because it returns a reference to the string's internal data, the pointer + that is returned must not be stored anywhere, as it can be deleted whenever the + string changes. + */ + inline operator const char*() const throw() { return text->text; } + + /** Returns a unicode version of this string. + + Because it returns a reference to the string's internal data, the pointer + that is returned must not be stored anywhere, as it can be deleted whenever the + string changes. + */ + operator const juce_wchar*() const throw(); +#endif + + /** Copies the string to a buffer. + + @param destBuffer the place to copy it to + @param maxCharsToCopy the maximum number of characters to copy to the buffer, + not including the tailing zero, so this shouldn't be + larger than the size of your destination buffer - 1 + */ + void copyToBuffer (char* const destBuffer, + const int maxCharsToCopy) const throw(); + + /** Copies the string to a unicode buffer. + + @param destBuffer the place to copy it to + @param maxCharsToCopy the maximum number of characters to copy to the buffer, + not including the tailing zero, so this shouldn't be + larger than the size of your destination buffer - 1 + */ + void copyToBuffer (juce_wchar* const destBuffer, + const int maxCharsToCopy) const throw(); + + /** Copies the string to a buffer as UTF-8 characters. + + Returns the number of bytes copied to the buffer, including the terminating null + character. + + @param destBuffer the place to copy it to; if this is a null pointer, + the method just returns the number of bytes required + (including the terminating null character). + */ + int copyToUTF8 (uint8* const destBuffer) const throw(); + + /** Returns a pointer to a UTF-8 version of this string. + + Because it returns a reference to the string's internal data, the pointer + that is returned must not be stored anywhere, as it can be deleted whenever the + string changes. + */ + const char* toUTF8() const throw(); + + /** Creates a String from a UTF-8 encoded buffer. + + If the size is < 0, it'll keep reading until it hits a zero. + */ + static const String fromUTF8 (const uint8* const utf8buffer, + int bufferSizeBytes = -1) throw(); + + /** Increases the string's internally allocated storage. + + Although the string's contents won't be affected by this call, it will + increase the amount of memory allocated internally for the string to grow into. + + If you're about to make a large number of calls to methods such + as += or <<, it's more efficient to preallocate enough extra space + beforehand, so that these methods won't have to keep resizing the string + to append the extra characters. + + @param numCharsNeeded the number of characters to allocate storage for. If this + value is less than the currently allocated size, it will + have no effect. + */ + void preallocateStorage (const int numCharsNeeded) throw(); + + juce_UseDebuggingNewOperator // (adds debugging info to find leaked objects) + +private: + + struct InternalRefCountedStringHolder + { + int refCount; + int allocatedNumChars; + +#if JUCE_STRINGS_ARE_UNICODE + wchar_t text[1]; +#else + char text[1]; +#endif + }; + + InternalRefCountedStringHolder* text; + static InternalRefCountedStringHolder emptyString; + + // internal constructor that preallocates a certain amount of memory + String (const int numChars, const int dummyVariable) throw(); + + void deleteInternal() throw(); + void createInternal (const int numChars) throw(); + void createInternal (const tchar* const text, const tchar* const textEnd) throw(); + void appendInternal (const tchar* const text, const int numExtraChars) throw(); + void doubleToStringWithDecPlaces (double n, int numDecPlaces) throw(); + void dupeInternalIfMultiplyReferenced() throw(); +}; + +/** Global operator to allow a String to be appended to a string literal. + + This allows the use of expressions such as "abc" + String (x) + + @see String + */ +const String JUCE_PUBLIC_FUNCTION operator+ (const char* const string1, + const String& string2) throw(); + +/** Global operator to allow a String to be appended to a string literal. + + This allows the use of expressions such as "abc" + String (x) + + @see String + */ +const String JUCE_PUBLIC_FUNCTION operator+ (const juce_wchar* const string1, + const String& string2) throw(); + +#endif // __JUCE_STRING_JUCEHEADER__ +/********* End of inlined file: juce_String.h *********/ + +/** + Acts as an application-wide logging class. + + A subclass of Logger can be created and passed into the Logger::setCurrentLogger + method and this will then be used by all calls to writeToLog. + + The logger class also contains methods for writing messages to the debugger's + output stream. + + @see FileLogger +*/ +class JUCE_API Logger +{ +public: + + /** Destructor. */ + virtual ~Logger(); + + /** Sets the current logging class to use. + + Note that the object passed in won't be deleted when no longer needed. + A null pointer can be passed-in to disable any logging. + + If deleteOldLogger is set to true, the existing logger will be + deleted (if there is one). + */ + static void JUCE_CALLTYPE setCurrentLogger (Logger* const newLogger, + const bool deleteOldLogger = false); + + /** Writes a string to the current logger. + + This will pass the string to the logger's logMessage() method if a logger + has been set. + + @see logMessage + */ + static void JUCE_CALLTYPE writeToLog (const String& message); + + /** Writes a message to the standard error stream. + + This can be called directly, or by using the DBG() macro in + juce_PlatformDefs.h (which will avoid calling the method in non-debug builds). + */ + static void JUCE_CALLTYPE outputDebugString (const String& text) throw(); + + /** Writes a message to the standard error stream. + + This can be called directly, or by using the DBG_PRINTF() macro in + juce_PlatformDefs.h (which will avoid calling the method in non-debug builds). + */ + static void JUCE_CALLTYPE outputDebugPrintf (const tchar* format, ...) throw(); + +protected: + + Logger(); + + /** This is overloaded by subclasses to implement custom logging behaviour. + + @see setCurrentLogger + */ + virtual void logMessage (const String& message) = 0; +}; + +#endif // __JUCE_LOGGER_JUCEHEADER__ +/********* End of inlined file: juce_Logger.h *********/ + +END_JUCE_NAMESPACE + +#endif // __JUCE_STANDARDHEADER_JUCEHEADER__ +/********* End of inlined file: juce_StandardHeader.h *********/ + +BEGIN_JUCE_NAMESPACE + +#if JUCE_MSVC + // this is set explicitly in case the app is using a different packing size. + #pragma pack (push, 8) + #pragma warning (push) + #pragma warning (disable: 4786) // (old vc6 warning about long class names) +#endif + +#if JUCE_MAC + #pragma align=natural +#endif + +#define JUCE_PUBLIC_INCLUDES + +// this is where all the class header files get brought in.. + +/********* Start of inlined file: juce_core_includes.h *********/ +#ifndef __JUCE_JUCE_CORE_INCLUDES_INCLUDEFILES__ +#define __JUCE_JUCE_CORE_INCLUDES_INCLUDEFILES__ + +#ifndef __JUCE_ATOMIC_JUCEHEADER__ + +/********* Start of inlined file: juce_Atomic.h *********/ +#ifndef __JUCE_ATOMIC_JUCEHEADER__ +#define __JUCE_ATOMIC_JUCEHEADER__ + +// Atomic increment/decrement operations.. + +#if JUCE_MAC && ! DOXYGEN + + #if ! MACOS_10_3_OR_EARLIER + + forcedinline void atomicIncrement (int& variable) throw() { OSAtomicIncrement32 ((int32_t*) &variable); } + forcedinline int atomicIncrementAndReturn (int& variable) throw() { return OSAtomicIncrement32 ((int32_t*) &variable); } + forcedinline void atomicDecrement (int& variable) throw() { OSAtomicDecrement32 ((int32_t*) &variable); } + forcedinline int atomicDecrementAndReturn (int& variable) throw() { return OSAtomicDecrement32 ((int32_t*) &variable); } + #else + + forcedinline void atomicIncrement (int& variable) throw() { OTAtomicAdd32 (1, (SInt32*) &variable); } + forcedinline int atomicIncrementAndReturn (int& variable) throw() { return OTAtomicAdd32 (1, (SInt32*) &variable); } + forcedinline void atomicDecrement (int& variable) throw() { OTAtomicAdd32 (-1, (SInt32*) &variable); } + forcedinline int atomicDecrementAndReturn (int& variable) throw() { return OTAtomicAdd32 (-1, (SInt32*) &variable); } + #endif + +#elif JUCE_GCC + + #if JUCE_USE_GCC_ATOMIC_INTRINSICS + forcedinline void atomicIncrement (int& variable) throw() { __sync_add_and_fetch (&variable, 1); } + forcedinline int atomicIncrementAndReturn (int& variable) throw() { return __sync_add_and_fetch (&variable, 1); } + forcedinline void atomicDecrement (int& variable) throw() { __sync_add_and_fetch (&variable, -1); } + forcedinline int atomicDecrementAndReturn (int& variable) throw() { return __sync_add_and_fetch (&variable, -1); } + #else + + /** Increments an integer in a thread-safe way. */ + forcedinline void atomicIncrement (int& variable) throw() + { + __asm__ __volatile__ ( + #if JUCE_64BIT + "lock incl (%%rax)" + : + : "a" (&variable) + : "cc", "memory"); + #else + "lock incl %0" + : "=m" (variable) + : "m" (variable)); + #endif + } + + /** Increments an integer in a thread-safe way and returns the incremented value. */ + forcedinline int atomicIncrementAndReturn (int& variable) throw() + { + int result; + + __asm__ __volatile__ ( + #if JUCE_64BIT + "lock xaddl %%ebx, (%%rax) \n\ + incl %%ebx" + : "=b" (result) + : "a" (&variable), "b" (1) + : "cc", "memory"); + #else + "lock xaddl %%eax, (%%ecx) \n\ + incl %%eax" + : "=a" (result) + : "c" (&variable), "a" (1) + : "memory"); + #endif + + return result; + } + + /** Decrememts an integer in a thread-safe way. */ + forcedinline void atomicDecrement (int& variable) throw() + { + __asm__ __volatile__ ( + #if JUCE_64BIT + "lock decl (%%rax)" + : + : "a" (&variable) + : "cc", "memory"); + #else + "lock decl %0" + : "=m" (variable) + : "m" (variable)); + #endif + } + + /** Decrememts an integer in a thread-safe way and returns the incremented value. */ + forcedinline int atomicDecrementAndReturn (int& variable) throw() + { + int result; + + __asm__ __volatile__ ( + #if JUCE_64BIT + "lock xaddl %%ebx, (%%rax) \n\ + decl %%ebx" + : "=b" (result) + : "a" (&variable), "b" (-1) + : "cc", "memory"); + #else + "lock xaddl %%eax, (%%ecx) \n\ + decl %%eax" + : "=a" (result) + : "c" (&variable), "a" (-1) + : "memory"); + #endif + return result; + } + #endif + +#elif JUCE_USE_INTRINSICS + + #pragma intrinsic (_InterlockedIncrement) + #pragma intrinsic (_InterlockedDecrement) + + /** Increments an integer in a thread-safe way. */ + forcedinline void __fastcall atomicIncrement (int& variable) throw() + { + _InterlockedIncrement (reinterpret_cast (&variable)); + } + + /** Increments an integer in a thread-safe way and returns the incremented value. */ + forcedinline int __fastcall atomicIncrementAndReturn (int& variable) throw() + { + return _InterlockedIncrement (reinterpret_cast (&variable)); + } + + /** Decrememts an integer in a thread-safe way. */ + forcedinline void __fastcall atomicDecrement (int& variable) throw() + { + _InterlockedDecrement (reinterpret_cast (&variable)); + } + + /** Decrememts an integer in a thread-safe way and returns the incremented value. */ + forcedinline int __fastcall atomicDecrementAndReturn (int& variable) throw() + { + return _InterlockedDecrement (reinterpret_cast (&variable)); + } +#else + + /** Increments an integer in a thread-safe way. */ + forcedinline void __fastcall atomicIncrement (int& variable) throw() + { + __asm { + mov ecx, dword ptr [variable] + lock inc dword ptr [ecx] + } + } + + /** Increments an integer in a thread-safe way and returns the incremented value. */ + forcedinline int __fastcall atomicIncrementAndReturn (int& variable) throw() + { + int result; + + __asm { + mov ecx, dword ptr [variable] + mov eax, 1 + lock xadd dword ptr [ecx], eax + inc eax + mov result, eax + } + + return result; + } + + /** Decrememts an integer in a thread-safe way. */ + forcedinline void __fastcall atomicDecrement (int& variable) throw() + { + __asm { + mov ecx, dword ptr [variable] + lock dec dword ptr [ecx] + } + } + + /** Decrememts an integer in a thread-safe way and returns the incremented value. */ + forcedinline int __fastcall atomicDecrementAndReturn (int& variable) throw() + { + int result; + + __asm { + mov ecx, dword ptr [variable] + mov eax, -1 + lock xadd dword ptr [ecx], eax + dec eax + mov result, eax + } + + return result; + } +#endif + +#endif // __JUCE_ATOMIC_JUCEHEADER__ +/********* End of inlined file: juce_Atomic.h *********/ + +#endif +#ifndef __JUCE_DATACONVERSIONS_JUCEHEADER__ + +#endif +#ifndef __JUCE_FILELOGGER_JUCEHEADER__ + +/********* Start of inlined file: juce_FileLogger.h *********/ +#ifndef __JUCE_FILELOGGER_JUCEHEADER__ +#define __JUCE_FILELOGGER_JUCEHEADER__ + +/********* Start of inlined file: juce_File.h *********/ +#ifndef __JUCE_FILE_JUCEHEADER__ +#define __JUCE_FILE_JUCEHEADER__ + +/********* Start of inlined file: juce_OwnedArray.h *********/ +#ifndef __JUCE_OWNEDARRAY_JUCEHEADER__ +#define __JUCE_OWNEDARRAY_JUCEHEADER__ + +/********* Start of inlined file: juce_ArrayAllocationBase.h *********/ +#ifndef __JUCE_ARRAYALLOCATIONBASE_JUCEHEADER__ +#define __JUCE_ARRAYALLOCATIONBASE_JUCEHEADER__ + +/** The default size of chunk in which arrays increase their storage. + + Used by ArrayAllocationBase and its subclasses. +*/ +const int juceDefaultArrayGranularity = 8; + +/** + Implements some basic array storage allocation functions. + + This class isn't really for public use - it's used by the other + array classes, but might come in handy for some purposes. + + @see Array, OwnedArray, ReferenceCountedArray +*/ +template +class ArrayAllocationBase +{ +protected: + + /** Creates an empty array. + + @param granularity_ this is the size of increment by which the internal storage + will be increased. + */ + ArrayAllocationBase (const int granularity_) throw() + : elements (0), + numAllocated (0), + granularity (granularity_) + { + } + + /** Destructor. */ + ~ArrayAllocationBase() throw() + { + if (elements != 0) + juce_free (elements); + } + + /** Changes the amount of storage allocated. + + This will retain any data currently held in the array, and either add or + remove extra space at the end. + + @param numElements the number of elements that are needed + */ + void setAllocatedSize (const int numElements) throw() + { + if (numAllocated != numElements) + { + numAllocated = numElements; + + if (numElements > 0) + { + if (elements == 0) + elements = (ElementType*) juce_malloc (sizeof (ElementType) * numElements); + else + elements = (ElementType*) juce_realloc (elements, sizeof (ElementType) * numElements); + } + else + { + if (elements != 0) + { + juce_free (elements); + elements = 0; + } + } + } + } + + /** Increases the amount of storage allocated if it is less than a given amount. + + This will retain any data currently held in the array, but will add + extra space at the end to make sure there it's at least as big as the size + passed in. If it's already bigger, no action is taken. + + @param minNumElements the minimum number of elements that are needed + */ + void ensureAllocatedSize (int minNumElements) throw() + { + if (minNumElements > numAllocated) + { + // for arrays with small granularity that get big, start + // increasing the size in bigger jumps + if (minNumElements > (granularity << 6)) + { + minNumElements += (minNumElements / granularity); + if (minNumElements > (granularity << 8)) + minNumElements += granularity << 6; + else + minNumElements += granularity << 5; + } + + setAllocatedSize (granularity * (minNumElements / granularity + 1)); + } + } + + ElementType* elements; + int numAllocated, granularity; + +private: + ArrayAllocationBase (const ArrayAllocationBase&); + const ArrayAllocationBase& operator= (const ArrayAllocationBase&); +}; + +#endif // __JUCE_ARRAYALLOCATIONBASE_JUCEHEADER__ +/********* End of inlined file: juce_ArrayAllocationBase.h *********/ + +/********* Start of inlined file: juce_ElementComparator.h *********/ +#ifndef __JUCE_ELEMENTCOMPARATOR_JUCEHEADER__ +#define __JUCE_ELEMENTCOMPARATOR_JUCEHEADER__ + +/** + Sorts a range of elements in an array. + + The comparator object that is passed-in must define a public method with the following + signature: + @code + int compareElements (ElementType first, ElementType second); + @endcode + + ..and this method must return: + - a value of < 0 if the first comes before the second + - a value of 0 if the two objects are equivalent + - a value of > 0 if the second comes before the first + + To improve performance, the compareElements() method can be declared as static or const. + + @param comparator an object which defines a compareElements() method + @param array the array to sort + @param firstElement the index of the first element of the range to be sorted + @param lastElement the index of the last element in the range that needs + sorting (this is inclusive) + @param retainOrderOfEquivalentItems if true, the order of items that the + comparator deems the same will be maintained - this will be + a slower algorithm than if they are allowed to be moved around. + + @see sortArrayRetainingOrder +*/ +template +static void sortArray (ElementComparator& comparator, + ElementType* const array, + int firstElement, + int lastElement, + const bool retainOrderOfEquivalentItems) +{ + (void) comparator; // if you pass in an object with a static compareElements() method, this + // avoids getting warning messages about the parameter being unused + + if (lastElement > firstElement) + { + if (retainOrderOfEquivalentItems) + { + for (int i = firstElement; i < lastElement; ++i) + { + if (comparator.compareElements (array[i], array [i + 1]) > 0) + { + const ElementType temp = array [i]; + array [i] = array[i + 1]; + array [i + 1] = temp; + + if (i > firstElement) + i -= 2; + } + } + } + else + { + int fromStack[30], toStack[30]; + int stackIndex = 0; + + for (;;) + { + const int size = (lastElement - firstElement) + 1; + + if (size <= 8) + { + int j = lastElement; + int maxIndex; + + while (j > firstElement) + { + maxIndex = firstElement; + for (int k = firstElement + 1; k <= j; ++k) + if (comparator.compareElements (array[k], array [maxIndex]) > 0) + maxIndex = k; + + const ElementType temp = array [maxIndex]; + array [maxIndex] = array[j]; + array [j] = temp; + + --j; + } + } + else + { + const int mid = firstElement + (size >> 1); + ElementType temp = array [mid]; + array [mid] = array [firstElement]; + array [firstElement] = temp; + + int i = firstElement; + int j = lastElement + 1; + + for (;;) + { + while (++i <= lastElement + && comparator.compareElements (array[i], array [firstElement]) <= 0) + {} + + while (--j > firstElement + && comparator.compareElements (array[j], array [firstElement]) >= 0) + {} + + if (j < i) + break; + + temp = array[i]; + array[i] = array[j]; + array[j] = temp; + } + + temp = array [firstElement]; + array [firstElement] = array[j]; + array [j] = temp; + + if (j - 1 - firstElement >= lastElement - i) + { + if (firstElement + 1 < j) + { + fromStack [stackIndex] = firstElement; + toStack [stackIndex] = j - 1; + ++stackIndex; + } + + if (i < lastElement) + { + firstElement = i; + continue; + } + } + else + { + if (i < lastElement) + { + fromStack [stackIndex] = i; + toStack [stackIndex] = lastElement; + ++stackIndex; + } + + if (firstElement + 1 < j) + { + lastElement = j - 1; + continue; + } + } + } + + if (--stackIndex < 0) + break; + + jassert (stackIndex < numElementsInArray (fromStack)); + + firstElement = fromStack [stackIndex]; + lastElement = toStack [stackIndex]; + } + } + } +} + +/** + Searches a sorted array of elements, looking for the index at which a specified value + should be inserted for it to be in the correct order. + + The comparator object that is passed-in must define a public method with the following + signature: + @code + int compareElements (ElementType first, ElementType second); + @endcode + + ..and this method must return: + - a value of < 0 if the first comes before the second + - a value of 0 if the two objects are equivalent + - a value of > 0 if the second comes before the first + + To improve performance, the compareElements() method can be declared as static or const. + + @param comparator an object which defines a compareElements() method + @param array the array to search + @param newElement the value that is going to be inserted + @param firstElement the index of the first element to search + @param lastElement the index of the last element in the range (this is non-inclusive) +*/ +template +static int findInsertIndexInSortedArray (ElementComparator& comparator, + ElementType* const array, + const ElementType newElement, + int firstElement, + int lastElement) +{ + jassert (firstElement <= lastElement); + + (void) comparator; // if you pass in an object with a static compareElements() method, this + // avoids getting warning messages about the parameter being unused + + while (firstElement < lastElement) + { + if (comparator.compareElements (newElement, array [firstElement]) == 0) + { + ++firstElement; + break; + } + else + { + const int halfway = (firstElement + lastElement) >> 1; + + if (halfway == firstElement) + { + if (comparator.compareElements (newElement, array [halfway]) >= 0) + ++firstElement; + + break; + } + else if (comparator.compareElements (newElement, array [halfway]) >= 0) + { + firstElement = halfway; + } + else + { + lastElement = halfway; + } + } + } + + return firstElement; +} + +/** + A simple ElementComparator class that can be used to sort an array of + integer primitive objects. + + Example: @code + Array myArray; + + IntegerElementComparator sorter; + myArray.sort (sorter); + @endcode + + For floating point values, see the FloatElementComparator class instead. + + @see FloatElementComparator, ElementComparator +*/ +template +class IntegerElementComparator +{ +public: + static int compareElements (const ElementType first, + const ElementType second) throw() + { + return first - second; + } +}; + +/** + A simple ElementComparator class that can be used to sort an array of numeric + double or floating point primitive objects. + + Example: @code + Array myArray; + + FloatElementComparator sorter; + myArray.sort (sorter); + @endcode + + For integer values, see the IntegerElementComparator class instead. + + @see IntegerElementComparator, ElementComparator +*/ +template +class FloatElementComparator +{ +public: + static int compareElements (const ElementType first, + const ElementType second) throw() + { + return (first < second) ? -1 + : ((first == second) ? 0 + : 1); + } +}; + +#endif // __JUCE_ELEMENTCOMPARATOR_JUCEHEADER__ +/********* End of inlined file: juce_ElementComparator.h *********/ + +/********* Start of inlined file: juce_CriticalSection.h *********/ +#ifndef __JUCE_CRITICALSECTION_JUCEHEADER__ +#define __JUCE_CRITICALSECTION_JUCEHEADER__ + +/** + Prevents multiple threads from accessing shared objects at the same time. + + @see ScopedLock, Thread, InterProcessLock +*/ +class JUCE_API CriticalSection +{ +public: + + /** + Creates a CriticalSection object + */ + CriticalSection() throw(); + + /** Destroys a CriticalSection object. + + If the critical section is deleted whilst locked, its subsequent behaviour + is unpredictable. + */ + ~CriticalSection() throw(); + + /** Locks this critical section. + + If the lock is currently held by another thread, this will wait until it + becomes free. + + If the lock is already held by the caller thread, the method returns immediately. + + @see exit, ScopedLock + */ + void enter() const throw(); + + /** Attempts to lock this critical section without blocking. + + This method behaves identically to CriticalSection::enter, except that the caller thread + does not wait if the lock is currently held by another thread but returns false immediately. + + @returns false if the lock is currently held by another thread, true otherwise. + @see enter + */ + bool tryEnter() const throw(); + + /** Releases the lock. + + If the caller thread hasn't got the lock, this can have unpredictable results. + + If the enter() method has been called multiple times by the thread, each + call must be matched by a call to exit() before other threads will be allowed + to take over the lock. + + @see enter, ScopedLock + */ + void exit() const throw(); + + juce_UseDebuggingNewOperator + +private: + +#if JUCE_WIN32 + #if JUCE_64BIT + // To avoid including windows.h in the public Juce includes, we'll just allocate a + // block of memory here that's big enough to be used internally as a windows critical + // section object. + uint8 internal [44]; + #else + uint8 internal [24]; + #endif +#else + mutable pthread_mutex_t internal; +#endif + + CriticalSection (const CriticalSection&); + const CriticalSection& operator= (const CriticalSection&); +}; + +/** + A class that can be used in place of a real CriticalSection object. + + This is currently used by some templated array classes, and should get + optimised out by the compiler. + + @see Array, OwnedArray, ReferenceCountedArray +*/ +class JUCE_API DummyCriticalSection +{ +public: + forcedinline DummyCriticalSection() throw() {} + forcedinline ~DummyCriticalSection() throw() {} + + forcedinline void enter() const throw() {} + forcedinline void exit() const throw() {} +}; + +#endif // __JUCE_CRITICALSECTION_JUCEHEADER__ +/********* End of inlined file: juce_CriticalSection.h *********/ + +/** An array designed for holding objects. + + This holds a list of pointers to objects, and will automatically + delete the objects when they are removed from the array, or when the + array is itself deleted. + + Declare it in the form: OwnedArray + + ..and then add new objects, e.g. myOwnedArray.add (new MyObjectClass()); + + After adding objects, they are 'owned' by the array and will be deleted when + removed or replaced. + + To make all the array's methods thread-safe, pass in "CriticalSection" as the templated + TypeOfCriticalSectionToUse parameter, instead of the default DummyCriticalSection. + + @see Array, ReferenceCountedArray, StringArray, CriticalSection +*/ +template + +class OwnedArray : private ArrayAllocationBase +{ +public: + + /** Creates an empty array. + + @param granularity this is the size of increment by which the internal storage + used by the array will grow. Only change it from the default if you know the + array is going to be very big and needs to be able to grow efficiently. + + @see ArrayAllocationBase + */ + OwnedArray (const int granularity = juceDefaultArrayGranularity) throw() + : ArrayAllocationBase (granularity), + numUsed (0) + { + } + + /** Deletes the array and also deletes any objects inside it. + + To get rid of the array without deleting its objects, use its + clear (false) method before deleting it. + */ + ~OwnedArray() + { + clear (true); + } + + /** Clears the array, optionally deleting the objects inside it first. */ + void clear (const bool deleteObjects = true) + { + lock.enter(); + + if (deleteObjects) + { + while (numUsed > 0) + delete this->elements [--numUsed]; + } + + this->setAllocatedSize (0); + numUsed = 0; + lock.exit(); + } + + /** Returns the number of items currently in the array. + @see operator[] + */ + inline int size() const throw() + { + return numUsed; + } + + /** Returns a pointer to the object at this index in the array. + + If the index is out-of-range, this will return a null pointer, (and + it could be null anyway, because it's ok for the array to hold null + pointers as well as objects). + + @see getUnchecked + */ + inline ObjectClass* operator[] (const int index) const throw() + { + lock.enter(); + ObjectClass* const result = (((unsigned int) index) < (unsigned int) numUsed) + ? this->elements [index] + : (ObjectClass*) 0; + lock.exit(); + + return result; + } + + /** Returns a pointer to the object at this index in the array, without checking whether the index is in-range. + + This is a faster and less safe version of operator[] which doesn't check the index passed in, so + it can be used when you're sure the index if always going to be legal. + */ + inline ObjectClass* getUnchecked (const int index) const throw() + { + lock.enter(); + jassert (((unsigned int) index) < (unsigned int) numUsed); + ObjectClass* const result = this->elements [index]; + lock.exit(); + + return result; + } + + /** Returns a pointer to the first object in the array. + + This will return a null pointer if the array's empty. + @see getLast + */ + inline ObjectClass* getFirst() const throw() + { + lock.enter(); + ObjectClass* const result = (numUsed > 0) ? this->elements [0] + : (ObjectClass*) 0; + lock.exit(); + return result; + } + + /** Returns a pointer to the last object in the array. + + This will return a null pointer if the array's empty. + @see getFirst + */ + inline ObjectClass* getLast() const throw() + { + lock.enter(); + ObjectClass* const result = (numUsed > 0) ? this->elements [numUsed - 1] + : (ObjectClass*) 0; + lock.exit(); + + return result; + } + + /** Finds the index of an object which might be in the array. + + @param objectToLookFor the object to look for + @returns the index at which the object was found, or -1 if it's not found + */ + int indexOf (const ObjectClass* const objectToLookFor) const throw() + { + int result = -1; + + lock.enter(); + ObjectClass* const* e = this->elements; + + for (int i = numUsed; --i >= 0;) + { + if (objectToLookFor == *e) + { + result = (int) (e - this->elements); + break; + } + + ++e; + } + + lock.exit(); + return result; + } + + /** Returns true if the array contains a specified object. + + @param objectToLookFor the object to look for + @returns true if the object is in the array + */ + bool contains (const ObjectClass* const objectToLookFor) const throw() + { + lock.enter(); + + ObjectClass* const* e = this->elements; + int i = numUsed; + + while (i >= 4) + { + if (objectToLookFor == *e + || objectToLookFor == *++e + || objectToLookFor == *++e + || objectToLookFor == *++e) + { + lock.exit(); + return true; + } + + i -= 4; + ++e; + } + + while (i > 0) + { + if (objectToLookFor == *e) + { + lock.exit(); + return true; + } + + --i; + ++e; + } + + lock.exit(); + return false; + } + + /** Appends a new object to the end of the array. + + Note that the this object will be deleted by the OwnedArray when it + is removed, so be careful not to delete it somewhere else. + + Also be careful not to add the same object to the array more than once, + as this will obviously cause deletion of dangling pointers. + + @param newObject the new object to add to the array + @see set, insert, addIfNotAlreadyThere, addSorted + */ + void add (const ObjectClass* const newObject) throw() + { + lock.enter(); + this->ensureAllocatedSize (numUsed + 1); + this->elements [numUsed++] = const_cast (newObject); + lock.exit(); + } + + /** Inserts a new object into the array at the given index. + + Note that the this object will be deleted by the OwnedArray when it + is removed, so be careful not to delete it somewhere else. + + If the index is less than 0 or greater than the size of the array, the + element will be added to the end of the array. + Otherwise, it will be inserted into the array, moving all the later elements + along to make room. + + Be careful not to add the same object to the array more than once, + as this will obviously cause deletion of dangling pointers. + + @param indexToInsertAt the index at which the new element should be inserted + @param newObject the new object to add to the array + @see add, addSorted, addIfNotAlreadyThere, set + */ + void insert (int indexToInsertAt, + const ObjectClass* const newObject) throw() + { + if (indexToInsertAt >= 0) + { + lock.enter(); + + if (indexToInsertAt > numUsed) + indexToInsertAt = numUsed; + + this->ensureAllocatedSize (numUsed + 1); + + ObjectClass** const e = this->elements + indexToInsertAt; + const int numToMove = numUsed - indexToInsertAt; + + if (numToMove > 0) + memmove (e + 1, e, numToMove * sizeof (ObjectClass*)); + + *e = const_cast (newObject); + ++numUsed; + + lock.exit(); + } + else + { + add (newObject); + } + } + + /** Appends a new object at the end of the array as long as the array doesn't + already contain it. + + If the array already contains a matching object, nothing will be done. + + @param newObject the new object to add to the array + */ + void addIfNotAlreadyThere (const ObjectClass* const newObject) throw() + { + lock.enter(); + + if (! contains (newObject)) + add (newObject); + + lock.exit(); + } + + /** Replaces an object in the array with a different one. + + If the index is less than zero, this method does nothing. + If the index is beyond the end of the array, the new object is added to the end of the array. + + Be careful not to add the same object to the array more than once, + as this will obviously cause deletion of dangling pointers. + + @param indexToChange the index whose value you want to change + @param newObject the new value to set for this index. + @param deleteOldElement whether to delete the object that's being replaced with the new one + @see add, insert, remove + */ + void set (const int indexToChange, + const ObjectClass* const newObject, + const bool deleteOldElement = true) + { + if (indexToChange >= 0) + { + ObjectClass* toDelete = 0; + lock.enter(); + + if (indexToChange < numUsed) + { + if (deleteOldElement) + toDelete = this->elements [indexToChange]; + + if (toDelete == newObject) + toDelete = 0; + else + this->elements [indexToChange] = const_cast (newObject); + } + else + { + this->ensureAllocatedSize (numUsed + 1); + this->elements [numUsed++] = const_cast (newObject); + } + + lock.exit(); + + delete toDelete; + } + } + + /** Inserts a new object into the array assuming that the array is sorted. + + This will use a comparator to find the position at which the new object + should go. If the array isn't sorted, the behaviour of this + method will be unpredictable. + + @param comparator the comparator to use to compare the elements - see the sort method + for details about this object's structure + @param newObject the new object to insert to the array + @see add, sort, indexOfSorted + */ + template + void addSorted (ElementComparator& comparator, + ObjectClass* const newObject) throw() + { + (void) comparator; // if you pass in an object with a static compareElements() method, this + // avoids getting warning messages about the parameter being unused + lock.enter(); + insert (findInsertIndexInSortedArray (comparator, this->elements, newObject, 0, numUsed), newObject); + lock.exit(); + } + + /** Finds the index of an object in the array, assuming that the array is sorted. + + This will use a comparator to do a binary-chop to find the index of the given + element, if it exists. If the array isn't sorted, the behaviour of this + method will be unpredictable. + + @param comparator the comparator to use to compare the elements - see the sort() + method for details about the form this object should take + @param objectToLookFor the object to search for + @returns the index of the element, or -1 if it's not found + @see addSorted, sort + */ + template + int indexOfSorted (ElementComparator& comparator, + const ObjectClass* const objectToLookFor) const throw() + { + (void) comparator; // if you pass in an object with a static compareElements() method, this + // avoids getting warning messages about the parameter being unused + lock.enter(); + + int start = 0; + int end = numUsed; + + for (;;) + { + if (start >= end) + { + lock.exit(); + return -1; + } + else if (comparator.compareElements (objectToLookFor, this->elements [start]) == 0) + { + lock.exit(); + return start; + } + else + { + const int halfway = (start + end) >> 1; + + if (halfway == start) + { + lock.exit(); + return -1; + } + else if (comparator.compareElements (objectToLookFor, this->elements [halfway]) >= 0) + start = halfway; + else + end = halfway; + } + } + } + + /** Removes an object from the array. + + This will remove the object at a given index (optionally also + deleting it) and move back all the subsequent objects to close the gap. + If the index passed in is out-of-range, nothing will happen. + + @param indexToRemove the index of the element to remove + @param deleteObject whether to delete the object that is removed + @see removeObject, removeRange + */ + void remove (const int indexToRemove, + const bool deleteObject = true) + { + lock.enter(); + ObjectClass* toDelete = 0; + + if (((unsigned int) indexToRemove) < (unsigned int) numUsed) + { + ObjectClass** const e = this->elements + indexToRemove; + + if (deleteObject) + toDelete = *e; + + --numUsed; + const int numToShift = numUsed - indexToRemove; + + if (numToShift > 0) + memmove (e, e + 1, numToShift * sizeof (ObjectClass*)); + + if ((numUsed << 1) < this->numAllocated) + minimiseStorageOverheads(); + } + + lock.exit(); + + delete toDelete; + } + + /** Removes a specified object from the array. + + If the item isn't found, no action is taken. + + @param objectToRemove the object to try to remove + @param deleteObject whether to delete the object (if it's found) + @see remove, removeRange + */ + void removeObject (const ObjectClass* const objectToRemove, + const bool deleteObject = true) + { + lock.enter(); + ObjectClass** e = this->elements; + + for (int i = numUsed; --i >= 0;) + { + if (objectToRemove == *e) + { + remove ((int) (e - this->elements), deleteObject); + break; + } + + ++e; + } + + lock.exit(); + } + + /** Removes a range of objects from the array. + + This will remove a set of objects, starting from the given index, + and move any subsequent elements down to close the gap. + + If the range extends beyond the bounds of the array, it will + be safely clipped to the size of the array. + + @param startIndex the index of the first object to remove + @param numberToRemove how many objects should be removed + @param deleteObjects whether to delete the objects that get removed + @see remove, removeObject + */ + void removeRange (int startIndex, + const int numberToRemove, + const bool deleteObjects = true) + { + lock.enter(); + const int endIndex = jlimit (0, numUsed, startIndex + numberToRemove); + startIndex = jlimit (0, numUsed, startIndex); + + if (endIndex > startIndex) + { + if (deleteObjects) + { + for (int i = startIndex; i < endIndex; ++i) + { + delete this->elements [i]; + this->elements [i] = 0; // (in case one of the destructors accesses this array and hits a dangling pointer) + } + } + + const int rangeSize = endIndex - startIndex; + ObjectClass** e = this->elements + startIndex; + int numToShift = numUsed - endIndex; + numUsed -= rangeSize; + + while (--numToShift >= 0) + { + *e = e [rangeSize]; + ++e; + } + + if ((numUsed << 1) < this->numAllocated) + minimiseStorageOverheads(); + } + + lock.exit(); + } + + /** Removes the last n objects from the array. + + @param howManyToRemove how many objects to remove from the end of the array + @param deleteObjects whether to also delete the objects that are removed + @see remove, removeObject, removeRange + */ + void removeLast (int howManyToRemove = 1, + const bool deleteObjects = true) + { + lock.enter(); + + if (howManyToRemove >= numUsed) + { + clear (deleteObjects); + } + else + { + while (--howManyToRemove >= 0) + remove (numUsed - 1, deleteObjects); + } + + lock.exit(); + } + + /** Swaps a pair of objects in the array. + + If either of the indexes passed in is out-of-range, nothing will happen, + otherwise the two objects at these positions will be exchanged. + */ + void swap (const int index1, + const int index2) throw() + { + lock.enter(); + + if (((unsigned int) index1) < (unsigned int) numUsed + && ((unsigned int) index2) < (unsigned int) numUsed) + { + swapVariables (this->elements [index1], + this->elements [index2]); + } + + lock.exit(); + } + + /** Moves one of the objects to a different position. + + This will move the object to a specified index, shuffling along + any intervening elements as required. + + So for example, if you have the array { 0, 1, 2, 3, 4, 5 } then calling + move (2, 4) would result in { 0, 1, 3, 4, 2, 5 }. + + @param currentIndex the index of the object to be moved. If this isn't a + valid index, then nothing will be done + @param newIndex the index at which you'd like this object to end up. If this + is less than zero, it will be moved to the end of the array + */ + void move (const int currentIndex, + int newIndex) throw() + { + if (currentIndex != newIndex) + { + lock.enter(); + + if (((unsigned int) currentIndex) < (unsigned int) numUsed) + { + if (((unsigned int) newIndex) >= (unsigned int) numUsed) + newIndex = numUsed - 1; + + ObjectClass* const value = this->elements [currentIndex]; + + if (newIndex > currentIndex) + { + memmove (this->elements + currentIndex, + this->elements + currentIndex + 1, + (newIndex - currentIndex) * sizeof (ObjectClass*)); + } + else + { + memmove (this->elements + newIndex + 1, + this->elements + newIndex, + (currentIndex - newIndex) * sizeof (ObjectClass*)); + } + + this->elements [newIndex] = value; + } + + lock.exit(); + } + } + + /** This swaps the contents of this array with those of another array. + + If you need to exchange two arrays, this is vastly quicker than using copy-by-value + because it just swaps their internal pointers. + */ + template + void swapWithArray (OtherArrayType& otherArray) throw() + { + lock.enter(); + otherArray.lock.enter(); + swapVariables (this->numUsed, otherArray.numUsed); + swapVariables (this->elements, otherArray.elements); + swapVariables (this->numAllocated, otherArray.numAllocated); + otherArray.lock.exit(); + lock.exit(); + } + + /** Reduces the amount of storage being used by the array. + + Arrays typically allocate slightly more storage than they need, and after + removing elements, they may have quite a lot of unused space allocated. + This method will reduce the amount of allocated storage to a minimum. + */ + void minimiseStorageOverheads() throw() + { + lock.enter(); + + if (numUsed == 0) + { + this->setAllocatedSize (0); + } + else + { + const int newAllocation = this->granularity * (numUsed / this->granularity + 1); + + if (newAllocation < this->numAllocated) + this->setAllocatedSize (newAllocation); + } + + lock.exit(); + } + + /** Increases the array's internal storage to hold a minimum number of elements. + + Calling this before adding a large known number of elements means that + the array won't have to keep dynamically resizing itself as the elements + are added, and it'll therefore be more efficient. + */ + void ensureStorageAllocated (const int minNumElements) throw() + { + this->ensureAllocatedSize (minNumElements); + } + + /** Sorts the elements in the array. + + This will use a comparator object to sort the elements into order. The object + passed must have a method of the form: + @code + int compareElements (ElementType first, ElementType second); + @endcode + + ..and this method must return: + - a value of < 0 if the first comes before the second + - a value of 0 if the two objects are equivalent + - a value of > 0 if the second comes before the first + + To improve performance, the compareElements() method can be declared as static or const. + + @param comparator the comparator to use for comparing elements. + @param retainOrderOfEquivalentItems if this is true, then items + which the comparator says are equivalent will be + kept in the order in which they currently appear + in the array. This is slower to perform, but may + be important in some cases. If it's false, a faster + algorithm is used, but equivalent elements may be + rearranged. + @see sortArray, indexOfSorted + */ + template + void sort (ElementComparator& comparator, + const bool retainOrderOfEquivalentItems = false) const throw() + { + (void) comparator; // if you pass in an object with a static compareElements() method, this + // avoids getting warning messages about the parameter being unused + + lock.enter(); + sortArray (comparator, this->elements, 0, size() - 1, retainOrderOfEquivalentItems); + lock.exit(); + } + + /** Locks the array's CriticalSection. + + Of course if the type of section used is a DummyCriticalSection, this won't + have any effect. + + @see unlockArray + */ + void lockArray() const throw() + { + lock.enter(); + } + + /** Unlocks the array's CriticalSection. + + Of course if the type of section used is a DummyCriticalSection, this won't + have any effect. + + @see lockArray + */ + void unlockArray() const throw() + { + lock.exit(); + } + + juce_UseDebuggingNewOperator + +private: + int numUsed; + TypeOfCriticalSectionToUse lock; + + // disallow copy constructor and assignment + OwnedArray (const OwnedArray&); + const OwnedArray& operator= (const OwnedArray&); +}; + +#endif // __JUCE_OWNEDARRAY_JUCEHEADER__ +/********* End of inlined file: juce_OwnedArray.h *********/ + +/********* Start of inlined file: juce_Time.h *********/ +#ifndef __JUCE_TIME_JUCEHEADER__ +#define __JUCE_TIME_JUCEHEADER__ + +/********* Start of inlined file: juce_RelativeTime.h *********/ +#ifndef __JUCE_RELATIVETIME_JUCEHEADER__ +#define __JUCE_RELATIVETIME_JUCEHEADER__ + +/** A relative measure of time. + + The time is stored as a number of seconds, at double-precision floating + point accuracy, and may be positive or negative. + + If you need an absolute time, (i.e. a date + time), see the Time class. +*/ +class JUCE_API RelativeTime +{ +public: + + /** Creates a RelativeTime. + + @param seconds the number of seconds, which may be +ve or -ve. + @see milliseconds, minutes, hours, days, weeks + */ + explicit RelativeTime (const double seconds = 0.0) throw(); + + /** Copies another relative time. */ + RelativeTime (const RelativeTime& other) throw(); + + /** Copies another relative time. */ + const RelativeTime& operator= (const RelativeTime& other) throw(); + + /** Destructor. */ + ~RelativeTime() throw(); + + /** Creates a new RelativeTime object representing a number of milliseconds. + + @see minutes, hours, days, weeks + */ + static const RelativeTime milliseconds (const int milliseconds) throw(); + + /** Creates a new RelativeTime object representing a number of milliseconds. + + @see minutes, hours, days, weeks + */ + static const RelativeTime milliseconds (const int64 milliseconds) throw(); + + /** Creates a new RelativeTime object representing a number of minutes. + + @see milliseconds, hours, days, weeks + */ + static const RelativeTime minutes (const double numberOfMinutes) throw(); + + /** Creates a new RelativeTime object representing a number of hours. + + @see milliseconds, minutes, days, weeks + */ + static const RelativeTime hours (const double numberOfHours) throw(); + + /** Creates a new RelativeTime object representing a number of days. + + @see milliseconds, minutes, hours, weeks + */ + static const RelativeTime days (const double numberOfDays) throw(); + + /** Creates a new RelativeTime object representing a number of weeks. + + @see milliseconds, minutes, hours, days + */ + static const RelativeTime weeks (const double numberOfWeeks) throw(); + + /** Returns the number of milliseconds this time represents. + + @see milliseconds, inSeconds, inMinutes, inHours, inDays, inWeeks + */ + int64 inMilliseconds() const throw(); + + /** Returns the number of seconds this time represents. + + @see inMilliseconds, inMinutes, inHours, inDays, inWeeks + */ + double inSeconds() const throw() { return seconds; } + + /** Returns the number of minutes this time represents. + + @see inMilliseconds, inSeconds, inHours, inDays, inWeeks + */ + double inMinutes() const throw(); + + /** Returns the number of hours this time represents. + + @see inMilliseconds, inSeconds, inMinutes, inDays, inWeeks + */ + double inHours() const throw(); + + /** Returns the number of days this time represents. + + @see inMilliseconds, inSeconds, inMinutes, inHours, inWeeks + */ + double inDays() const throw(); + + /** Returns the number of weeks this time represents. + + @see inMilliseconds, inSeconds, inMinutes, inHours, inDays + */ + double inWeeks() const throw(); + + /** Returns a readable textual description of the time. + + The exact format of the string returned will depend on + the magnitude of the time - e.g. + + "1 min 4 secs", "1 hr 45 mins", "2 weeks 5 days", "140 ms" + + so that only the two most significant units are printed. + + The returnValueForZeroTime value is the result that is returned if the + length is zero. Depending on your application you might want to use this + to return something more relevant like "empty" or "0 secs", etc. + + @see inMilliseconds, inSeconds, inMinutes, inHours, inDays, inWeeks + */ + const String getDescription (const String& returnValueForZeroTime = JUCE_T("0")) const throw(); + + /** Compares two RelativeTimes. */ + bool operator== (const RelativeTime& other) const throw(); + /** Compares two RelativeTimes. */ + bool operator!= (const RelativeTime& other) const throw(); + + /** Compares two RelativeTimes. */ + bool operator> (const RelativeTime& other) const throw(); + /** Compares two RelativeTimes. */ + bool operator< (const RelativeTime& other) const throw(); + /** Compares two RelativeTimes. */ + bool operator>= (const RelativeTime& other) const throw(); + /** Compares two RelativeTimes. */ + bool operator<= (const RelativeTime& other) const throw(); + + /** Adds another RelativeTime to this one and returns the result. */ + const RelativeTime operator+ (const RelativeTime& timeToAdd) const throw(); + /** Subtracts another RelativeTime from this one and returns the result. */ + const RelativeTime operator- (const RelativeTime& timeToSubtract) const throw(); + + /** Adds a number of seconds to this RelativeTime and returns the result. */ + const RelativeTime operator+ (const double secondsToAdd) const throw(); + /** Subtracts a number of seconds from this RelativeTime and returns the result. */ + const RelativeTime operator- (const double secondsToSubtract) const throw(); + + /** Adds another RelativeTime to this one. */ + const RelativeTime& operator+= (const RelativeTime& timeToAdd) throw(); + /** Subtracts another RelativeTime from this one. */ + const RelativeTime& operator-= (const RelativeTime& timeToSubtract) throw(); + + /** Adds a number of seconds to this time. */ + const RelativeTime& operator+= (const double secondsToAdd) throw(); + + /** Subtracts a number of seconds from this time. */ + const RelativeTime& operator-= (const double secondsToSubtract) throw(); + + juce_UseDebuggingNewOperator + +private: + double seconds; +}; + +#endif // __JUCE_RELATIVETIME_JUCEHEADER__ +/********* End of inlined file: juce_RelativeTime.h *********/ + +/** + Holds an absolute date and time. + + Internally, the time is stored at millisecond precision. + + @see RelativeTime +*/ +class JUCE_API Time +{ +public: + + /** Creates a Time object. + + This default constructor creates a time of 1st January 1970, (which is + represented internally as 0ms). + + To create a time object representing the current time, use getCurrentTime(). + + @see getCurrentTime + */ + Time() throw(); + + /** Creates a copy of another Time object. */ + Time (const Time& other) throw(); + + /** Creates a time based on a number of milliseconds. + + The internal millisecond count is set to 0 (1st January 1970). To create a + time object set to the current time, use getCurrentTime(). + + @param millisecondsSinceEpoch the number of milliseconds since the unix + 'epoch' (midnight Jan 1st 1970). + @see getCurrentTime, currentTimeMillis + */ + Time (const int64 millisecondsSinceEpoch) throw(); + + /** Creates a time from a set of date components. + + The timezone is assumed to be whatever the system is using as its locale. + + @param year the year, in 4-digit format, e.g. 2004 + @param month the month, in the range 0 to 11 + @param day the day of the month, in the range 1 to 31 + @param hours hours in 24-hour clock format, 0 to 23 + @param minutes minutes 0 to 59 + @param seconds seconds 0 to 59 + @param milliseconds milliseconds 0 to 999 + */ + Time (const int year, + const int month, + const int day, + const int hours, + const int minutes, + const int seconds = 0, + const int milliseconds = 0) throw(); + + /** Destructor. */ + ~Time() throw(); + + /** Copies this time from another one. */ + const Time& operator= (const Time& other) throw(); + + /** Returns a Time object that is set to the current system time. + + @see currentTimeMillis + */ + static const Time JUCE_CALLTYPE getCurrentTime() throw(); + + /** Returns the time as a number of milliseconds. + + @returns the number of milliseconds this Time object represents, since + midnight jan 1st 1970. + @see getMilliseconds + */ + int64 toMilliseconds() const throw() { return millisSinceEpoch; } + + /** Returns the year. + + A 4-digit format is used, e.g. 2004. + */ + int getYear() const throw(); + + /** Returns the number of the month. + + The value returned is in the range 0 to 11. + @see getMonthName + */ + int getMonth() const throw(); + + /** Returns the name of the month. + + @param threeLetterVersion if true, it'll be a 3-letter abbreviation, e.g. "Jan"; if false + it'll return the long form, e.g. "January" + @see getMonth + */ + const String getMonthName (const bool threeLetterVersion) const throw(); + + /** Returns the day of the month. + + The value returned is in the range 1 to 31. + */ + int getDayOfMonth() const throw(); + + /** Returns the number of the day of the week. + + The value returned is in the range 0 to 6 (0 = sunday, 1 = monday, etc). + */ + int getDayOfWeek() const throw(); + + /** Returns the name of the weekday. + + @param threeLetterVersion if true, it'll return a 3-letter abbreviation, e.g. "Tue"; if + false, it'll return the full version, e.g. "Tuesday". + */ + const String getWeekdayName (const bool threeLetterVersion) const throw(); + + /** Returns the number of hours since midnight. + + This is in 24-hour clock format, in the range 0 to 23. + + @see getHoursInAmPmFormat, isAfternoon + */ + int getHours() const throw(); + + /** Returns true if the time is in the afternoon. + + So it returns true for "PM", false for "AM". + + @see getHoursInAmPmFormat, getHours + */ + bool isAfternoon() const throw(); + + /** Returns the hours in 12-hour clock format. + + This will return a value 1 to 12 - use isAfternoon() to find out + whether this is in the afternoon or morning. + + @see getHours, isAfternoon + */ + int getHoursInAmPmFormat() const throw(); + + /** Returns the number of minutes, 0 to 59. */ + int getMinutes() const throw(); + + /** Returns the number of seconds, 0 to 59. */ + int getSeconds() const throw(); + + /** Returns the number of milliseconds, 0 to 999. + + Unlike toMilliseconds(), this just returns the position within the + current second rather than the total number since the epoch. + + @see toMilliseconds + */ + int getMilliseconds() const throw(); + + /** Returns true if the local timezone uses a daylight saving correction. */ + bool isDaylightSavingTime() const throw(); + + /** Returns a 3-character string to indicate the local timezone. */ + const String getTimeZone() const throw(); + + /** Quick way of getting a string version of a date and time. + + For a more powerful way of formatting the date and time, see the formatted() method. + + @param includeDate whether to include the date in the string + @param includeTime whether to include the time in the string + @param includeSeconds if the time is being included, this provides an option not to include + the seconds in it + @param use24HourClock if the time is being included, sets whether to use am/pm or 24 + hour notation. + @see formatted + */ + const String toString (const bool includeDate, + const bool includeTime, + const bool includeSeconds = true, + const bool use24HourClock = false) const throw(); + + /** Converts this date/time to a string with a user-defined format. + + This uses the C strftime() function to format this time as a string. To save you + looking it up, these are the escape codes that strftime uses (other codes might + work on some platforms and not others, but these are the common ones): + + %a is replaced by the locale's abbreviated weekday name. + %A is replaced by the locale's full weekday name. + %b is replaced by the locale's abbreviated month name. + %B is replaced by the locale's full month name. + %c is replaced by the locale's appropriate date and time representation. + %d is replaced by the day of the month as a decimal number [01,31]. + %H is replaced by the hour (24-hour clock) as a decimal number [00,23]. + %I is replaced by the hour (12-hour clock) as a decimal number [01,12]. + %j is replaced by the day of the year as a decimal number [001,366]. + %m is replaced by the month as a decimal number [01,12]. + %M is replaced by the minute as a decimal number [00,59]. + %p is replaced by the locale's equivalent of either a.m. or p.m. + %S is replaced by the second as a decimal number [00,61]. + %U is replaced by the week number of the year (Sunday as the first day of the week) as a decimal number [00,53]. + %w is replaced by the weekday as a decimal number [0,6], with 0 representing Sunday. + %W is replaced by the week number of the year (Monday as the first day of the week) as a decimal number [00,53]. All days in a new year preceding the first Monday are considered to be in week 0. + %x is replaced by the locale's appropriate date representation. + %X is replaced by the locale's appropriate time representation. + %y is replaced by the year without century as a decimal number [00,99]. + %Y is replaced by the year with century as a decimal number. + %Z is replaced by the timezone name or abbreviation, or by no bytes if no timezone information exists. + %% is replaced by %. + + @see toString + */ + const String formatted (const tchar* const format) const throw(); + + /** Adds a RelativeTime to this time and returns the result. */ + const Time operator+ (const RelativeTime& delta) const throw() { return Time (millisSinceEpoch + delta.inMilliseconds()); } + + /** Subtracts a RelativeTime from this time and returns the result. */ + const Time operator- (const RelativeTime& delta) const throw() { return Time (millisSinceEpoch - delta.inMilliseconds()); } + + /** Returns the relative time difference between this time and another one. */ + const RelativeTime operator- (const Time& other) const throw() { return RelativeTime::milliseconds (millisSinceEpoch - other.millisSinceEpoch); } + + /** Compares two Time objects. */ + bool operator== (const Time& other) const throw() { return millisSinceEpoch == other.millisSinceEpoch; } + + /** Compares two Time objects. */ + bool operator!= (const Time& other) const throw() { return millisSinceEpoch != other.millisSinceEpoch; } + + /** Compares two Time objects. */ + bool operator< (const Time& other) const throw() { return millisSinceEpoch < other.millisSinceEpoch; } + + /** Compares two Time objects. */ + bool operator<= (const Time& other) const throw() { return millisSinceEpoch <= other.millisSinceEpoch; } + + /** Compares two Time objects. */ + bool operator> (const Time& other) const throw() { return millisSinceEpoch > other.millisSinceEpoch; } + + /** Compares two Time objects. */ + bool operator>= (const Time& other) const throw() { return millisSinceEpoch >= other.millisSinceEpoch; } + + /** Tries to set the computer's clock. + + @returns true if this succeeds, although depending on the system, the + application might not have sufficient privileges to do this. + */ + bool setSystemTimeToThisTime() const throw(); + + /** Returns the name of a day of the week. + + @param dayNumber the day, 0 to 6 (0 = sunday, 1 = monday, etc) + @param threeLetterVersion if true, it'll return a 3-letter abbreviation, e.g. "Tue"; if + false, it'll return the full version, e.g. "Tuesday". + */ + static const String getWeekdayName (int dayNumber, + const bool threeLetterVersion) throw(); + + /** Returns the name of one of the months. + + @param monthNumber the month, 0 to 11 + @param threeLetterVersion if true, it'll be a 3-letter abbreviation, e.g. "Jan"; if false + it'll return the long form, e.g. "January" + */ + static const String getMonthName (int monthNumber, + const bool threeLetterVersion) throw(); + + // Static methods for getting system timers directly.. + + /** Returns the current system time. + + Returns the number of milliseconds since midnight jan 1st 1970. + + Should be accurate to within a few millisecs, depending on platform, + hardware, etc. + */ + static int64 currentTimeMillis() throw(); + + /** Returns the number of millisecs since system startup. + + Should be accurate to within a few millisecs, depending on platform, + hardware, etc. + + @see getApproximateMillisecondCounter + */ + static uint32 getMillisecondCounter() throw(); + + /** Returns the number of millisecs since system startup. + + Same as getMillisecondCounter(), but returns a more accurate value, using + the high-res timer. + + @see getMillisecondCounter + */ + static double getMillisecondCounterHiRes() throw(); + + /** Waits until the getMillisecondCounter() reaches a given value. + + This will make the thread sleep as efficiently as it can while it's waiting. + */ + static void waitForMillisecondCounter (const uint32 targetTime) throw(); + + /** Less-accurate but faster version of getMillisecondCounter(). + + This will return the last value that getMillisecondCounter() returned, so doesn't + need to make a system call, but is less accurate - it shouldn't be more than + 100ms away from the correct time, though, so is still accurate enough for a + lot of purposes. + + @see getMillisecondCounter + */ + static uint32 getApproximateMillisecondCounter() throw(); + + // High-resolution timers.. + + /** Returns the current high-resolution counter's tick-count. + + This is a similar idea to getMillisecondCounter(), but with a higher + resolution. + + @see getHighResolutionTicksPerSecond, highResolutionTicksToSeconds, + secondsToHighResolutionTicks + */ + static int64 getHighResolutionTicks() throw(); + + /** Returns the resolution of the high-resolution counter in ticks per second. + + @see getHighResolutionTicks, highResolutionTicksToSeconds, + secondsToHighResolutionTicks + */ + static int64 getHighResolutionTicksPerSecond() throw(); + + /** Converts a number of high-resolution ticks into seconds. + + @see getHighResolutionTicks, getHighResolutionTicksPerSecond, + secondsToHighResolutionTicks + */ + static double highResolutionTicksToSeconds (const int64 ticks) throw(); + + /** Converts a number seconds into high-resolution ticks. + + @see getHighResolutionTicks, getHighResolutionTicksPerSecond, + highResolutionTicksToSeconds + */ + static int64 secondsToHighResolutionTicks (const double seconds) throw(); + +private: + + int64 millisSinceEpoch; +}; + +#endif // __JUCE_TIME_JUCEHEADER__ +/********* End of inlined file: juce_Time.h *********/ + +/********* Start of inlined file: juce_StringArray.h *********/ +#ifndef __JUCE_STRINGARRAY_JUCEHEADER__ +#define __JUCE_STRINGARRAY_JUCEHEADER__ + +/********* Start of inlined file: juce_VoidArray.h *********/ +#ifndef __JUCE_VOIDARRAY_JUCEHEADER__ +#define __JUCE_VOIDARRAY_JUCEHEADER__ + +/********* Start of inlined file: juce_Array.h *********/ +#ifndef __JUCE_ARRAY_JUCEHEADER__ +#define __JUCE_ARRAY_JUCEHEADER__ + +/** + Holds a list of primitive objects, such as ints, doubles, or pointers. + + Examples of arrays are: Array or Array + + Note that when holding pointers to objects, the array doesn't take any ownership + of the objects - for doing this, see the OwnedArray class or the ReferenceCountedArray class. + + If you're using a class or struct as the element type, it must be + capable of being copied or moved with a straightforward memcpy, rather than + needing construction and destruction code. + + For holding lists of strings, use the specialised class StringArray. + + To make all the array's methods thread-safe, pass in "CriticalSection" as the templated + TypeOfCriticalSectionToUse parameter, instead of the default DummyCriticalSection. + + @see OwnedArray, ReferenceCountedArray, StringArray, CriticalSection +*/ +template +class Array : private ArrayAllocationBase +{ +public: + + /** Creates an empty array. + + @param granularity this is the size of increment by which the internal storage + used by the array will grow. Only change it from the default if you know the + array is going to be very big and needs to be able to grow efficiently. + + @see ArrayAllocationBase + */ + Array (const int granularity = juceDefaultArrayGranularity) throw() + : ArrayAllocationBase (granularity), + numUsed (0) + { + } + + /** Creates a copy of another array. + @param other the array to copy + */ + Array (const Array& other) throw() + : ArrayAllocationBase (other.granularity) + { + other.lockArray(); + numUsed = other.numUsed; + this->setAllocatedSize (other.numUsed); + memcpy (this->elements, other.elements, numUsed * sizeof (ElementType)); + other.unlockArray(); + } + + /** Initalises from a null-terminated C array of values. + + @param values the array to copy from + */ + Array (const ElementType* values) throw() + : ArrayAllocationBase (juceDefaultArrayGranularity), + numUsed (0) + { + while (*values != 0) + add (*values++); + } + + /** Initalises from a C array of values. + + @param values the array to copy from + @param numValues the number of values in the array + */ + Array (const ElementType* values, int numValues) throw() + : ArrayAllocationBase (juceDefaultArrayGranularity), + numUsed (numValues) + { + this->setAllocatedSize (numValues); + memcpy (this->elements, values, numValues * sizeof (ElementType)); + } + + /** Destructor. */ + ~Array() throw() + { + } + + /** Copies another array. + @param other the array to copy + */ + const Array & operator= (const Array & other) throw() + { + if (this != &other) + { + other.lockArray(); + lock.enter(); + + this->granularity = other.granularity; + this->ensureAllocatedSize (other.size()); + numUsed = other.numUsed; + memcpy (this->elements, other.elements, this->numUsed * sizeof (ElementType)); + minimiseStorageOverheads(); + + lock.exit(); + other.unlockArray(); + } + + return *this; + } + + /** Compares this array to another one. + Two arrays are considered equal if they both contain the same set of + elements, in the same order. + @param other the other array to compare with + */ + template + bool operator== (const OtherArrayType& other) const throw() + { + lock.enter(); + + if (this->numUsed != other.numUsed) + { + lock.exit(); + return false; + } + + for (int i = numUsed; --i >= 0;) + { + if (this->elements [i] != other.elements [i]) + { + lock.exit(); + return false; + } + } + + lock.exit(); + return true; + } + + /** Compares this array to another one. + Two arrays are considered equal if they both contain the same set of + elements, in the same order. + @param other the other array to compare with + */ + template + bool operator!= (const OtherArrayType& other) const throw() + { + return ! operator== (other); + } + + /** Removes all elements from the array. + This will remove all the elements, and free any storage that the array is + using. To clear the array without freeing the storage, use the clearQuick() + method instead. + + @see clearQuick + */ + void clear() throw() + { + lock.enter(); + this->setAllocatedSize (0); + numUsed = 0; + lock.exit(); + } + + /** Removes all elements from the array without freeing the array's allocated storage. + + @see clear + */ + void clearQuick() throw() + { + lock.enter(); + numUsed = 0; + lock.exit(); + } + + /** Returns the current number of elements in the array. + */ + inline int size() const throw() + { + return numUsed; + } + + /** Returns one of the elements in the array. + If the index passed in is beyond the range of valid elements, this + will return zero. + + If you're certain that the index will always be a valid element, you + can call getUnchecked() instead, which is faster. + + @param index the index of the element being requested (0 is the first element in the array) + @see getUnchecked, getFirst, getLast + */ + inline ElementType operator[] (const int index) const throw() + { + lock.enter(); + const ElementType result = (((unsigned int) index) < (unsigned int) numUsed) + ? this->elements [index] + : ElementType(); + lock.exit(); + + return result; + } + + /** Returns one of the elements in the array, without checking the index passed in. + + Unlike the operator[] method, this will try to return an element without + checking that the index is within the bounds of the array, so should only + be used when you're confident that it will always be a valid index. + + @param index the index of the element being requested (0 is the first element in the array) + @see operator[], getFirst, getLast + */ + inline ElementType getUnchecked (const int index) const throw() + { + lock.enter(); + jassert (((unsigned int) index) < (unsigned int) numUsed); + const ElementType result = this->elements [index]; + lock.exit(); + + return result; + } + + /** Returns a direct reference to one of the elements in the array, without checking the index passed in. + + This is like getUnchecked, but returns a direct reference to the element, so that + you can alter it directly. Obviously this can be dangerous, so only use it when + absolutely necessary. + + @param index the index of the element being requested (0 is the first element in the array) + @see operator[], getFirst, getLast + */ + inline ElementType& getReference (const int index) const throw() + { + lock.enter(); + jassert (((unsigned int) index) < (unsigned int) numUsed); + ElementType& result = this->elements [index]; + lock.exit(); + return result; + } + + /** Returns the first element in the array, or 0 if the array is empty. + + @see operator[], getUnchecked, getLast + */ + inline ElementType getFirst() const throw() + { + lock.enter(); + const ElementType result = (numUsed > 0) ? this->elements [0] + : ElementType(); + lock.exit(); + + return result; + } + + /** Returns the last element in the array, or 0 if the array is empty. + + @see operator[], getUnchecked, getFirst + */ + inline ElementType getLast() const throw() + { + lock.enter(); + const ElementType result = (numUsed > 0) ? this->elements [numUsed - 1] + : ElementType(); + lock.exit(); + + return result; + } + + /** Finds the index of the first element which matches the value passed in. + + This will search the array for the given object, and return the index + of its first occurrence. If the object isn't found, the method will return -1. + + @param elementToLookFor the value or object to look for + @returns the index of the object, or -1 if it's not found + */ + int indexOf (const ElementType elementToLookFor) const throw() + { + int result = -1; + + lock.enter(); + const ElementType* e = this->elements; + + for (int i = numUsed; --i >= 0;) + { + if (elementToLookFor == *e) + { + result = (int) (e - this->elements); + break; + } + + ++e; + } + + lock.exit(); + return result; + } + + /** Returns true if the array contains at least one occurrence of an object. + + @param elementToLookFor the value or object to look for + @returns true if the item is found + */ + bool contains (const ElementType elementToLookFor) const throw() + { + lock.enter(); + + const ElementType* e = this->elements; + int num = numUsed; + + while (num >= 4) + { + if (*e == elementToLookFor + || *++e == elementToLookFor + || *++e == elementToLookFor + || *++e == elementToLookFor) + { + lock.exit(); + return true; + } + + num -= 4; + ++e; + } + + while (num > 0) + { + if (elementToLookFor == *e) + { + lock.exit(); + return true; + } + + --num; + ++e; + } + + lock.exit(); + return false; + } + + /** Appends a new element at the end of the array. + + @param newElement the new object to add to the array + @see set, insert, addIfNotAlreadyThere, addSorted, addArray + */ + void add (const ElementType newElement) throw() + { + lock.enter(); + this->ensureAllocatedSize (numUsed + 1); + this->elements [numUsed++] = newElement; + lock.exit(); + } + + /** Inserts a new element into the array at a given position. + + If the index is less than 0 or greater than the size of the array, the + element will be added to the end of the array. + Otherwise, it will be inserted into the array, moving all the later elements + along to make room. + + @param indexToInsertAt the index at which the new element should be + inserted (pass in -1 to add it to the end) + @param newElement the new object to add to the array + @see add, addSorted, set + */ + void insert (int indexToInsertAt, const ElementType newElement) throw() + { + lock.enter(); + this->ensureAllocatedSize (numUsed + 1); + + if (((unsigned int) indexToInsertAt) < (unsigned int) numUsed) + { + ElementType* const insertPos = this->elements + indexToInsertAt; + const int numberToMove = numUsed - indexToInsertAt; + + if (numberToMove > 0) + memmove (insertPos + 1, insertPos, numberToMove * sizeof (ElementType)); + + *insertPos = newElement; + ++numUsed; + } + else + { + this->elements [numUsed++] = newElement; + } + + lock.exit(); + } + + /** Inserts multiple copies of an element into the array at a given position. + + If the index is less than 0 or greater than the size of the array, the + element will be added to the end of the array. + Otherwise, it will be inserted into the array, moving all the later elements + along to make room. + + @param indexToInsertAt the index at which the new element should be inserted + @param newElement the new object to add to the array + @param numberOfTimesToInsertIt how many copies of the value to insert + @see insert, add, addSorted, set + */ + void insertMultiple (int indexToInsertAt, const ElementType newElement, + int numberOfTimesToInsertIt) throw() + { + if (numberOfTimesToInsertIt > 0) + { + lock.enter(); + this->ensureAllocatedSize (numUsed + numberOfTimesToInsertIt); + + if (((unsigned int) indexToInsertAt) < (unsigned int) numUsed) + { + ElementType* insertPos = this->elements + indexToInsertAt; + const int numberToMove = numUsed - indexToInsertAt; + + memmove (insertPos + numberOfTimesToInsertIt, insertPos, numberToMove * sizeof (ElementType)); + numUsed += numberOfTimesToInsertIt; + + while (--numberOfTimesToInsertIt >= 0) + *insertPos++ = newElement; + } + else + { + while (--numberOfTimesToInsertIt >= 0) + this->elements [numUsed++] = newElement; + } + + lock.exit(); + } + } + + /** Inserts an array of values into this array at a given position. + + If the index is less than 0 or greater than the size of the array, the + new elements will be added to the end of the array. + Otherwise, they will be inserted into the array, moving all the later elements + along to make room. + + @param indexToInsertAt the index at which the first new element should be inserted + @param newElements the new values to add to the array + @param numberOfElements how many items are in the array + @see insert, add, addSorted, set + */ + void insertArray (int indexToInsertAt, + const ElementType* newElements, + int numberOfElements) throw() + { + if (numberOfElements > 0) + { + lock.enter(); + this->ensureAllocatedSize (numUsed + numberOfElements); + + if (((unsigned int) indexToInsertAt) < (unsigned int) numUsed) + { + ElementType* insertPos = this->elements + indexToInsertAt; + const int numberToMove = numUsed - indexToInsertAt; + + memmove (insertPos + numberOfElements, insertPos, numberToMove * sizeof (ElementType)); + numUsed += numberOfElements; + + while (--numberOfElements >= 0) + *insertPos++ = *newElements++; + } + else + { + while (--numberOfElements >= 0) + this->elements [numUsed++] = *newElements++; + } + + lock.exit(); + } + } + + /** Appends a new element at the end of the array as long as the array doesn't + already contain it. + + If the array already contains an element that matches the one passed in, nothing + will be done. + + @param newElement the new object to add to the array + */ + void addIfNotAlreadyThere (const ElementType newElement) throw() + { + lock.enter(); + + if (! contains (newElement)) + add (newElement); + + lock.exit(); + } + + /** Replaces an element with a new value. + + If the index is less than zero, this method does nothing. + If the index is beyond the end of the array, the item is added to the end of the array. + + @param indexToChange the index whose value you want to change + @param newValue the new value to set for this index. + @see add, insert + */ + void set (const int indexToChange, + const ElementType newValue) throw() + { + jassert (indexToChange >= 0); + + if (indexToChange >= 0) + { + lock.enter(); + + if (indexToChange < numUsed) + { + this->elements [indexToChange] = newValue; + } + else + { + this->ensureAllocatedSize (numUsed + 1); + this->elements [numUsed++] = newValue; + } + + lock.exit(); + } + } + + /** Replaces an element with a new value without doing any bounds-checking. + + This just sets a value directly in the array's internal storage, so you'd + better make sure it's in range! + + @param indexToChange the index whose value you want to change + @param newValue the new value to set for this index. + @see set, getUnchecked + */ + void setUnchecked (const int indexToChange, + const ElementType newValue) throw() + { + lock.enter(); + jassert (((unsigned int) indexToChange) < (unsigned int) numUsed); + this->elements [indexToChange] = newValue; + lock.exit(); + } + + /** Adds elements from an array to the end of this array. + + @param elementsToAdd the array of elements to add + @param numElementsToAdd how many elements are in this other array + @see add + */ + void addArray (const ElementType* elementsToAdd, + int numElementsToAdd) throw() + { + lock.enter(); + + if (numElementsToAdd > 0) + { + this->ensureAllocatedSize (numUsed + numElementsToAdd); + + while (--numElementsToAdd >= 0) + this->elements [numUsed++] = *elementsToAdd++; + } + + lock.exit(); + } + + /** This swaps the contents of this array with those of another array. + + If you need to exchange two arrays, this is vastly quicker than using copy-by-value + because it just swaps their internal pointers. + */ + template + void swapWithArray (OtherArrayType& otherArray) throw() + { + lock.enter(); + otherArray.lock.enter(); + swapVariables (this->numUsed, otherArray.numUsed); + swapVariables (this->elements, otherArray.elements); + swapVariables (this->numAllocated, otherArray.numAllocated); + otherArray.lock.exit(); + lock.exit(); + } + + /** Adds elements from another array to the end of this array. + + @param arrayToAddFrom the array from which to copy the elements + @param startIndex the first element of the other array to start copying from + @param numElementsToAdd how many elements to add from the other array. If this + value is negative or greater than the number of available elements, + all available elements will be copied. + @see add + */ + template + void addArray (const OtherArrayType& arrayToAddFrom, + int startIndex = 0, + int numElementsToAdd = -1) throw() + { + arrayToAddFrom.lockArray(); + lock.enter(); + jassert (this != &arrayToAddFrom); + + if (startIndex < 0) + { + jassertfalse + startIndex = 0; + } + + if (numElementsToAdd < 0 || startIndex + numElementsToAdd > arrayToAddFrom.size()) + numElementsToAdd = arrayToAddFrom.size() - startIndex; + + this->addArray ((const ElementType*) (arrayToAddFrom.elements + startIndex), numElementsToAdd); + + lock.exit(); + arrayToAddFrom.unlockArray(); + } + + /** Inserts a new element into the array, assuming that the array is sorted. + + This will use a comparator to find the position at which the new element + should go. If the array isn't sorted, the behaviour of this + method will be unpredictable. + + @param comparator the comparator to use to compare the elements - see the sort() + method for details about the form this object should take + @param newElement the new element to insert to the array + @see add, sort + */ + template + void addSorted (ElementComparator& comparator, + const ElementType newElement) throw() + { + lock.enter(); + insert (findInsertIndexInSortedArray (comparator, this->elements, newElement, 0, numUsed), newElement); + lock.exit(); + } + + /** Finds the index of an element in the array, assuming that the array is sorted. + + This will use a comparator to do a binary-chop to find the index of the given + element, if it exists. If the array isn't sorted, the behaviour of this + method will be unpredictable. + + @param comparator the comparator to use to compare the elements - see the sort() + method for details about the form this object should take + @param elementToLookFor the element to search for + @returns the index of the element, or -1 if it's not found + @see addSorted, sort + */ + template + int indexOfSorted (ElementComparator& comparator, + const ElementType elementToLookFor) const throw() + { + (void) comparator; // if you pass in an object with a static compareElements() method, this + // avoids getting warning messages about the parameter being unused + lock.enter(); + + int start = 0; + int end = numUsed; + + for (;;) + { + if (start >= end) + { + lock.exit(); + return -1; + } + else if (comparator.compareElements (elementToLookFor, this->elements [start]) == 0) + { + lock.exit(); + return start; + } + else + { + const int halfway = (start + end) >> 1; + + if (halfway == start) + { + lock.exit(); + return -1; + } + else if (comparator.compareElements (elementToLookFor, this->elements [halfway]) >= 0) + start = halfway; + else + end = halfway; + } + } + } + + /** Removes an element from the array. + + This will remove the element at a given index, and move back + all the subsequent elements to close the gap. + If the index passed in is out-of-range, nothing will happen. + + @param indexToRemove the index of the element to remove + @returns the element that has been removed + @see removeValue, removeRange + */ + ElementType remove (const int indexToRemove) throw() + { + lock.enter(); + + if (((unsigned int) indexToRemove) < (unsigned int) numUsed) + { + --numUsed; + + ElementType* const e = this->elements + indexToRemove; + ElementType const removed = *e; + const int numberToShift = numUsed - indexToRemove; + + if (numberToShift > 0) + memmove (e, e + 1, numberToShift * sizeof (ElementType)); + + if ((numUsed << 1) < this->numAllocated) + minimiseStorageOverheads(); + + lock.exit(); + return removed; + } + else + { + lock.exit(); + return ElementType(); + } + } + + /** Removes an item from the array. + + This will remove the first occurrence of the given element from the array. + If the item isn't found, no action is taken. + + @param valueToRemove the object to try to remove + @see remove, removeRange + */ + void removeValue (const ElementType valueToRemove) throw() + { + lock.enter(); + ElementType* e = this->elements; + + for (int i = numUsed; --i >= 0;) + { + if (valueToRemove == *e) + { + remove ((int) (e - this->elements)); + break; + } + + ++e; + } + + lock.exit(); + } + + /** Removes a range of elements from the array. + + This will remove a set of elements, starting from the given index, + and move subsequent elements down to close the gap. + + If the range extends beyond the bounds of the array, it will + be safely clipped to the size of the array. + + @param startIndex the index of the first element to remove + @param numberToRemove how many elements should be removed + @see remove, removeValue + */ + void removeRange (int startIndex, + const int numberToRemove) throw() + { + lock.enter(); + const int endIndex = jlimit (0, numUsed, startIndex + numberToRemove); + startIndex = jlimit (0, numUsed, startIndex); + + if (endIndex > startIndex) + { + const int rangeSize = endIndex - startIndex; + ElementType* e = this->elements + startIndex; + int numToShift = numUsed - endIndex; + numUsed -= rangeSize; + + while (--numToShift >= 0) + { + *e = e [rangeSize]; + ++e; + } + + if ((numUsed << 1) < this->numAllocated) + minimiseStorageOverheads(); + } + + lock.exit(); + } + + /** Removes the last n elements from the array. + + @param howManyToRemove how many elements to remove from the end of the array + @see remove, removeValue, removeRange + */ + void removeLast (const int howManyToRemove = 1) throw() + { + lock.enter(); + numUsed = jmax (0, numUsed - howManyToRemove); + + if ((numUsed << 1) < this->numAllocated) + minimiseStorageOverheads(); + + lock.exit(); + } + + /** Removes any elements which are also in another array. + + @param otherArray the other array in which to look for elements to remove + @see removeValuesNotIn, remove, removeValue, removeRange + */ + template + void removeValuesIn (const OtherArrayType& otherArray) throw() + { + otherArray.lockArray(); + lock.enter(); + + if (this == &otherArray) + { + clear(); + } + else + { + if (otherArray.size() > 0) + { + for (int i = numUsed; --i >= 0;) + if (otherArray.contains (this->elements [i])) + remove (i); + } + } + + lock.exit(); + otherArray.unlockArray(); + } + + /** Removes any elements which are not found in another array. + + Only elements which occur in this other array will be retained. + + @param otherArray the array in which to look for elements NOT to remove + @see removeValuesIn, remove, removeValue, removeRange + */ + template + void removeValuesNotIn (const OtherArrayType& otherArray) throw() + { + otherArray.lockArray(); + lock.enter(); + + if (this != &otherArray) + { + if (otherArray.size() <= 0) + { + clear(); + } + else + { + for (int i = numUsed; --i >= 0;) + if (! otherArray.contains (this->elements [i])) + remove (i); + } + } + + lock.exit(); + otherArray.unlockArray(); + } + + /** Swaps over two elements in the array. + + This swaps over the elements found at the two indexes passed in. + If either index is out-of-range, this method will do nothing. + + @param index1 index of one of the elements to swap + @param index2 index of the other element to swap + */ + void swap (const int index1, + const int index2) throw() + { + lock.enter(); + + if (((unsigned int) index1) < (unsigned int) numUsed + && ((unsigned int) index2) < (unsigned int) numUsed) + { + swapVariables (this->elements [index1], + this->elements [index2]); + } + + lock.exit(); + } + + /** Moves one of the values to a different position. + + This will move the value to a specified index, shuffling along + any intervening elements as required. + + So for example, if you have the array { 0, 1, 2, 3, 4, 5 } then calling + move (2, 4) would result in { 0, 1, 3, 4, 2, 5 }. + + @param currentIndex the index of the value to be moved. If this isn't a + valid index, then nothing will be done + @param newIndex the index at which you'd like this value to end up. If this + is less than zero, the value will be moved to the end + of the array + */ + void move (const int currentIndex, + int newIndex) throw() + { + if (currentIndex != newIndex) + { + lock.enter(); + + if (((unsigned int) currentIndex) < (unsigned int) numUsed) + { + if (((unsigned int) newIndex) >= (unsigned int) numUsed) + newIndex = numUsed - 1; + + const ElementType value = this->elements [currentIndex]; + + if (newIndex > currentIndex) + { + memmove (this->elements + currentIndex, + this->elements + currentIndex + 1, + (newIndex - currentIndex) * sizeof (ElementType)); + } + else + { + memmove (this->elements + newIndex + 1, + this->elements + newIndex, + (currentIndex - newIndex) * sizeof (ElementType)); + } + + this->elements [newIndex] = value; + } + + lock.exit(); + } + } + + /** Reduces the amount of storage being used by the array. + + Arrays typically allocate slightly more storage than they need, and after + removing elements, they may have quite a lot of unused space allocated. + This method will reduce the amount of allocated storage to a minimum. + */ + void minimiseStorageOverheads() throw() + { + lock.enter(); + + if (numUsed == 0) + { + this->setAllocatedSize (0); + } + else + { + const int newAllocation = this->granularity * (numUsed / this->granularity + 1); + + if (newAllocation < this->numAllocated) + this->setAllocatedSize (newAllocation); + } + + lock.exit(); + } + + /** Increases the array's internal storage to hold a minimum number of elements. + + Calling this before adding a large known number of elements means that + the array won't have to keep dynamically resizing itself as the elements + are added, and it'll therefore be more efficient. + */ + void ensureStorageAllocated (const int minNumElements) throw() + { + this->ensureAllocatedSize (minNumElements); + } + + /** Sorts the elements in the array. + + This will use a comparator object to sort the elements into order. The object + passed must have a method of the form: + @code + int compareElements (ElementType first, ElementType second); + @endcode + + ..and this method must return: + - a value of < 0 if the first comes before the second + - a value of 0 if the two objects are equivalent + - a value of > 0 if the second comes before the first + + To improve performance, the compareElements() method can be declared as static or const. + + @param comparator the comparator to use for comparing elements. + @param retainOrderOfEquivalentItems if this is true, then items + which the comparator says are equivalent will be + kept in the order in which they currently appear + in the array. This is slower to perform, but may + be important in some cases. If it's false, a faster + algorithm is used, but equivalent elements may be + rearranged. + + @see addSorted, indexOfSorted, sortArray + */ + template + void sort (ElementComparator& comparator, + const bool retainOrderOfEquivalentItems = false) const throw() + { + (void) comparator; // if you pass in an object with a static compareElements() method, this + // avoids getting warning messages about the parameter being unused + lock.enter(); + sortArray (comparator, this->elements, 0, size() - 1, retainOrderOfEquivalentItems); + lock.exit(); + } + + /** Locks the array's CriticalSection. + + Of course if the type of section used is a DummyCriticalSection, this won't + have any effect. + + @see unlockArray + */ + void lockArray() const throw() + { + lock.enter(); + } + + /** Unlocks the array's CriticalSection. + + Of course if the type of section used is a DummyCriticalSection, this won't + have any effect. + + @see lockArray + */ + void unlockArray() const throw() + { + lock.exit(); + } + + juce_UseDebuggingNewOperator + +private: + int numUsed; + TypeOfCriticalSectionToUse lock; +}; + +#endif // __JUCE_ARRAY_JUCEHEADER__ +/********* End of inlined file: juce_Array.h *********/ + +/** + A typedef for an Array of void*'s. + + VoidArrays are used in various places throughout the library instead of + more strongly-typed arrays, to keep code-size low. +*/ +typedef Array VoidArray; + +#endif // __JUCE_VOIDARRAY_JUCEHEADER__ +/********* End of inlined file: juce_VoidArray.h *********/ + +#ifndef DOXYGEN + // (used in StringArray::appendNumbersToDuplicates) + static const tchar* const defaultPreNumberString = JUCE_T(" ("); + static const tchar* const defaultPostNumberString = JUCE_T(")"); +#endif + +/** + A special array for holding a list of strings. + + @see String, StringPairArray +*/ +class JUCE_API StringArray +{ +public: + + /** Creates an empty string array */ + StringArray() throw(); + + /** Creates a copy of another string array */ + StringArray (const StringArray& other) throw(); + + /** Creates a copy of an array of string literals. + + @param strings an array of strings to add. Null pointers in the array will be + treated as empty strings + @param numberOfStrings how many items there are in the array + */ + StringArray (const juce_wchar** const strings, + const int numberOfStrings) throw(); + + /** Creates a copy of an array of string literals. + + @param strings an array of strings to add. Null pointers in the array will be + treated as empty strings + @param numberOfStrings how many items there are in the array + */ + StringArray (const char** const strings, + const int numberOfStrings) throw(); + + /** Creates a copy of a null-terminated array of string literals. + + Each item from the array passed-in is added, until it encounters a null pointer, + at which point it stops. + */ + StringArray (const juce_wchar** const strings) throw(); + + /** Creates a copy of a null-terminated array of string literals. + + Each item from the array passed-in is added, until it encounters a null pointer, + at which point it stops. + */ + StringArray (const char** const strings) throw(); + + /** Destructor. */ + virtual ~StringArray() throw(); + + /** Copies the contents of another string array into this one */ + const StringArray& operator= (const StringArray& other) throw(); + + /** Compares two arrays. + + Comparisons are case-sensitive. + + @returns true only if the other array contains exactly the same strings in the same order + */ + bool operator== (const StringArray& other) const throw(); + + /** Compares two arrays. + + Comparisons are case-sensitive. + + @returns false if the other array contains exactly the same strings in the same order + */ + bool operator!= (const StringArray& other) const throw(); + + /** Returns the number of strings in the array */ + inline int size() const throw() { return strings.size(); }; + + /** Returns one of the strings from the array. + + If the index is out-of-range, an empty string is returned. + + Obviously the reference returned shouldn't be stored for later use, as the + string it refers to may disappear when the array changes. + */ + const String& operator[] (const int index) const throw(); + + /** Searches for a string in the array. + + The comparison will be case-insensitive if the ignoreCase parameter is true. + + @returns true if the string is found inside the array + */ + bool contains (const String& stringToLookFor, + const bool ignoreCase = false) const throw(); + + /** Searches for a string in the array. + + The comparison will be case-insensitive if the ignoreCase parameter is true. + + @param stringToLookFor the string to try to find + @param ignoreCase whether the comparison should be case-insensitive + @param startIndex the first index to start searching from + @returns the index of the first occurrence of the string in this array, + or -1 if it isn't found. + */ + int indexOf (const String& stringToLookFor, + const bool ignoreCase = false, + int startIndex = 0) const throw(); + + /** Appends a string at the end of the array. */ + void add (const String& stringToAdd) throw(); + + /** Inserts a string into the array. + + This will insert a string into the array at the given index, moving + up the other elements to make room for it. + If the index is less than zero or greater than the size of the array, + the new string will be added to the end of the array. + */ + void insert (const int index, + const String& stringToAdd) throw(); + + /** Adds a string to the array as long as it's not already in there. + + The search can optionally be case-insensitive. + */ + void addIfNotAlreadyThere (const String& stringToAdd, + const bool ignoreCase = false) throw(); + + /** Replaces one of the strings in the array with another one. + + If the index is higher than the array's size, the new string will be + added to the end of the array; if it's less than zero nothing happens. + */ + void set (const int index, + const String& newString) throw(); + + /** Appends some strings from another array to the end of this one. + + @param other the array to add + @param startIndex the first element of the other array to add + @param numElementsToAdd the maximum number of elements to add (if this is + less than zero, they are all added) + */ + void addArray (const StringArray& other, + int startIndex = 0, + int numElementsToAdd = -1) throw(); + + /** Breaks up a string into tokens and adds them to this array. + + This will tokenise the given string using whitespace characters as the + token delimiters, and will add these tokens to the end of the array. + + @returns the number of tokens added + */ + int addTokens (const tchar* const stringToTokenise, + const bool preserveQuotedStrings) throw(); + + /** Breaks up a string into tokens and adds them to this array. + + This will tokenise the given string (using the string passed in to define the + token delimiters), and will add these tokens to the end of the array. + + @param stringToTokenise the string to tokenise + @param breakCharacters a string of characters, any of which will be considered + to be a token delimiter. + @param quoteCharacters if this string isn't empty, it defines a set of characters + which are treated as quotes. Any text occurring + between quotes is not broken up into tokens. + @returns the number of tokens added + */ + int addTokens (const tchar* const stringToTokenise, + const tchar* breakCharacters, + const tchar* quoteCharacters) throw(); + + /** Breaks up a string into lines and adds them to this array. + + This breaks a string down into lines separated by \\n or \\r\\n, and adds each line + to the array. Line-break characters are omitted from the strings that are added to + the array. + */ + int addLines (const tchar* stringToBreakUp) throw(); + + /** Removes all elements from the array. */ + void clear() throw(); + + /** Removes a string from the array. + + If the index is out-of-range, no action will be taken. + */ + void remove (const int index) throw(); + + /** Finds a string in the array and removes it. + + This will remove the first occurrence of the given string from the array. The + comparison may be case-insensitive depending on the ignoreCase parameter. + */ + void removeString (const String& stringToRemove, + const bool ignoreCase = false) throw(); + + /** Removes any duplicated elements from the array. + + If any string appears in the array more than once, only the first occurrence of + it will be retained. + + @param ignoreCase whether to use a case-insensitive comparison + */ + void removeDuplicates (const bool ignoreCase) throw(); + + /** Removes empty strings from the array. + + @param removeWhitespaceStrings if true, strings that only contain whitespace + characters will also be removed + */ + void removeEmptyStrings (const bool removeWhitespaceStrings = true) throw(); + + /** Moves one of the strings to a different position. + + This will move the string to a specified index, shuffling along + any intervening elements as required. + + So for example, if you have the array { 0, 1, 2, 3, 4, 5 } then calling + move (2, 4) would result in { 0, 1, 3, 4, 2, 5 }. + + @param currentIndex the index of the value to be moved. If this isn't a + valid index, then nothing will be done + @param newIndex the index at which you'd like this value to end up. If this + is less than zero, the value will be moved to the end + of the array + */ + void move (const int currentIndex, int newIndex) throw(); + + /** Deletes any whitespace characters from the starts and ends of all the strings. */ + void trim() throw(); + + /** Adds numbers to the strings in the array, to make each string unique. + + This will add numbers to the ends of groups of similar strings. + e.g. if there are two "moose" strings, they will become "moose (1)" and "moose (2)" + + @param ignoreCaseWhenComparing whether the comparison used is case-insensitive + @param appendNumberToFirstInstance whether the first of a group of similar strings + also has a number appended to it. + @param preNumberString when adding a number, this string is added before the number + @param postNumberString this string is appended after any numbers that are added + */ + void appendNumbersToDuplicates (const bool ignoreCaseWhenComparing, + const bool appendNumberToFirstInstance, + const tchar* const preNumberString = defaultPreNumberString, + const tchar* const postNumberString = defaultPostNumberString) throw(); + + /** Joins the strings in the array together into one string. + + This will join a range of elements from the array into a string, separating + them with a given string. + + e.g. joinIntoString (",") will turn an array of "a" "b" and "c" into "a,b,c". + + @param separatorString the string to insert between all the strings + @param startIndex the first element to join + @param numberOfElements how many elements to join together. If this is less + than zero, all available elements will be used. + */ + const String joinIntoString (const String& separatorString, + int startIndex = 0, + int numberOfElements = -1) const throw(); + + /** Sorts the array into alphabetical order. + + @param ignoreCase if true, the comparisons used will be case-sensitive. + */ + void sort (const bool ignoreCase) throw(); + + /** Reduces the amount of storage being used by the array. + + Arrays typically allocate slightly more storage than they need, and after + removing elements, they may have quite a lot of unused space allocated. + This method will reduce the amount of allocated storage to a minimum. + */ + void minimiseStorageOverheads() throw(); + + juce_UseDebuggingNewOperator + +private: + VoidArray strings; +}; + +#endif // __JUCE_STRINGARRAY_JUCEHEADER__ +/********* End of inlined file: juce_StringArray.h *********/ + +/********* Start of inlined file: juce_MemoryBlock.h *********/ +#ifndef __JUCE_MEMORYBLOCK_JUCEHEADER__ +#define __JUCE_MEMORYBLOCK_JUCEHEADER__ + +/** + A class to hold a resizable block of raw data. + +*/ +class JUCE_API MemoryBlock +{ +public: + + /** Create an uninitialised block with 0 size. */ + MemoryBlock() throw(); + + /** Creates a memory block with a given initial size. + + @param initialSize the size of block to create + @param initialiseToZero whether to clear the memory or just leave it uninitialised + */ + MemoryBlock (const int initialSize, + const bool initialiseToZero = false) throw(); + + /** Creates a copy of another memory block. */ + MemoryBlock (const MemoryBlock& other) throw(); + + /** Creates a memory block using a copy of a block of data. + + @param dataToInitialiseFrom some data to copy into this block + @param sizeInBytes how much space to use + */ + MemoryBlock (const void* const dataToInitialiseFrom, + const int sizeInBytes) throw(); + + /** Destructor. */ + ~MemoryBlock() throw(); + + /** Copies another memory block onto this one. + + This block will be resized and copied to exactly match the other one. + */ + const MemoryBlock& operator= (const MemoryBlock& other) throw(); + + /** Compares two memory blocks. + + @returns true only if the two blocks are the same size and have identical contents. + */ + bool operator== (const MemoryBlock& other) const throw(); + + /** Compares two memory blocks. + + @returns true if the two blocks are different sizes or have different contents. + */ + bool operator!= (const MemoryBlock& other) const throw(); + + /** Returns a pointer to the data, casting it to any type of primitive data required. + + Note that the pointer returned will probably become invalid when the + block is resized. + */ + template + operator DataType*() const throw() { return (DataType*) data; } + + /** Returns a void pointer to the data. + + Note that the pointer returned will probably become invalid when the + block is resized. + */ + void* getData() const throw() { return data; } + + /** Returns a byte from the memory block. + + This returns a reference, so you can also use it to set a byte. + */ + char& operator[] (const int offset) const throw() { return data [offset]; } + + /** Returns the block's current allocated size, in bytes. */ + int getSize() const throw() { return size; } + + /** Resizes the memory block. + + This will try to keep as much of the block's current content as it can, + and can optionally be made to clear any new space that gets allocated at + the end of the block. + + @param newSize the new desired size for the block + @param initialiseNewSpaceToZero if the block gets enlarged, this determines + whether to clear the new section or just leave it + uninitialised + @see ensureSize + */ + void setSize (const int newSize, + const bool initialiseNewSpaceToZero = false) throw(); + + /** Increases the block's size only if it's smaller than a given size. + + @param minimumSize if the block is already bigger than this size, no action + will be taken; otherwise it will be increased to this size + @param initialiseNewSpaceToZero if the block gets enlarged, this determines + whether to clear the new section or just leave it + uninitialised + @see setSize + */ + void ensureSize (const int minimumSize, + const bool initialiseNewSpaceToZero = false) throw(); + + /** Fills the entire memory block with a repeated byte value. + + This is handy for clearing a block of memory to zero. + */ + void fillWith (const uint8 valueToUse) throw(); + + /** Adds another block of data to the end of this one. + + This block's size will be increased accordingly. + */ + void append (const void* const data, + const int numBytes) throw(); + + /** Copies data into this MemoryBlock from a memory address. + + @param srcData the memory location of the data to copy into this block + @param destinationOffset the offset in this block at which the data being copied should begin + @param numBytes how much to copy in (if this goes beyond the size of the memory block, + it will be clipped so not to do anything nasty) + */ + void copyFrom (const void* srcData, + int destinationOffset, + int numBytes) throw(); + + /** Copies data from this MemoryBlock to a memory address. + + @param destData the memory location to write to + @param sourceOffset the offset within this block from which the copied data will be read + @param numBytes how much to copy (if this extends beyond the limits of the memory block, + zeros will be used for that portion of the data) + */ + void copyTo (void* destData, + int sourceOffset, + int numBytes) const throw(); + + /** Chops out a section of the block. + + This will remove a section of the memory block and close the gap around it, + shifting any subsequent data downwards and reducing the size of the block. + + If the range specified goes beyond the size of the block, it will be clipped. + */ + void removeSection (int startByte, int numBytesToRemove) throw(); + + /** Attempts to parse the contents of the block as a zero-terminated string of 8-bit + characters in the system's default encoding. */ + const String toString() const throw(); + + /** Parses a string of hexadecimal numbers and writes this data into the memory block. + + The block will be resized to the number of valid bytes read from the string. + Non-hex characters in the string will be ignored. + + @see String::toHexString() + */ + void loadFromHexString (const String& sourceHexString) throw(); + + /** Sets a number of bits in the memory block, treating it as a long binary sequence. */ + void setBitRange (int bitRangeStart, + int numBits, + int binaryNumberToApply) throw(); + + /** Reads a number of bits from the memory block, treating it as one long binary sequence */ + int getBitRange (int bitRangeStart, + int numBitsToRead) const throw(); + + /** Returns a string of characters that represent the binary contents of this block. + + Uses a 64-bit encoding system to allow binary data to be turned into a string + of simple non-extended characters, e.g. for storage in XML. + + @see fromBase64Encoding + */ + const String toBase64Encoding() const throw(); + + /** Takes a string of encoded characters and turns it into binary data. + + The string passed in must have been created by to64BitEncoding(), and this + block will be resized to recreate the original data block. + + @see toBase64Encoding + */ + bool fromBase64Encoding (const String& encodedString) throw(); + + juce_UseDebuggingNewOperator + +private: + + char* data; + int size; +}; + +#endif // __JUCE_MEMORYBLOCK_JUCEHEADER__ +/********* End of inlined file: juce_MemoryBlock.h *********/ + +class FileInputStream; +class FileOutputStream; + +/** + Represents a local file or directory. + + This class encapsulates the absolute pathname of a file or directory, and + has methods for finding out about the file and changing its properties. + + To read or write to the file, there are methods for returning an input or + output stream. + + @see FileInputStream, FileOutputStream +*/ +class JUCE_API File +{ +public: + + /** Creates an (invalid) file object. + + The file is initially set to an empty path, so getFullPath() will return + an empty string, and comparing the file to File::nonexistent will return + true. + + You can use its operator= method to point it at a proper file. + */ + File() throw() {} + + /** Creates a file from an absolute path. + + If the path supplied is a relative path, it is taken to be relative + to the current working directory (see File::getCurrentWorkingDirectory()), + but this isn't a recommended way of creating a file, because you + never know what the CWD is going to be. + + On the Mac/Linux, the path can include "~" notation for referring to + user home directories. + */ + File (const String& path) throw(); + + /** Creates a copy of another file object. */ + File (const File& other) throw(); + + /** Destructor. */ + ~File() throw() {} + + /** Sets the file based on an absolute pathname. + + If the path supplied is a relative path, it is taken to be relative + to the current working directory (see File::getCurrentWorkingDirectory()), + but this isn't a recommended way of creating a file, because you + never know what the CWD is going to be. + + On the Mac/Linux, the path can include "~" notation for referring to + user home directories. + */ + const File& operator= (const String& newFilePath) throw(); + + /** Copies from another file object. */ + const File& operator= (const File& otherFile) throw(); + + /** This static constant is used for referring to an 'invalid' file. */ + static const File nonexistent; + + /** Checks whether the file actually exists. + + @returns true if the file exists, either as a file or a directory. + @see existsAsFile, isDirectory + */ + bool exists() const throw(); + + /** Checks whether the file exists and is a file rather than a directory. + + @returns true only if this is a real file, false if it's a directory + or doesn't exist + @see exists, isDirectory + */ + bool existsAsFile() const throw(); + + /** Checks whether the file is a directory that exists. + + @returns true only if the file is a directory which actually exists, so + false if it's a file or doesn't exist at all + @see exists, existsAsFile + */ + bool isDirectory() const throw(); + + /** Returns the size of the file in bytes. + + @returns the number of bytes in the file, or 0 if it doesn't exist. + */ + int64 getSize() const throw(); + + /** Utility function to convert a file size in bytes to a neat string description. + + So for example 100 would return "100 bytes", 2000 would return "2 KB", + 2000000 would produce "2 MB", etc. + */ + static const String descriptionOfSizeInBytes (const int64 bytes); + + /** Returns the complete, absolute path of this file. + + This includes the filename and all its parent folders. On Windows it'll + also include the drive letter prefix; on Mac or Linux it'll be a complete + path starting from the root folder. + + If you just want the file's name, you should use getFileName() or + getFileNameWithoutExtension(). + + @see getFileName, getRelativePathFrom + */ + const String& getFullPathName() const throw() { return fullPath; } + + /** Returns the last section of the pathname. + + Returns just the final part of the path - e.g. if the whole path + is "/moose/fish/foo.txt" this will return "foo.txt". + + For a directory, it returns the final part of the path - e.g. for the + directory "/moose/fish" it'll return "fish". + + If the filename begins with a dot, it'll return the whole filename, e.g. for + "/moose/.fish", it'll return ".fish" + + @see getFullPathName, getFileNameWithoutExtension + */ + const String getFileName() const throw(); + + /** Creates a relative path that refers to a file relatively to a given directory. + + e.g. File ("/moose/foo.txt").getRelativePathFrom ("/moose/fish/haddock") + would return "../../foo.txt". + + If it's not possible to navigate from one file to the other, an absolute + path is returned. If the paths are invalid, an empty string may also be + returned. + + @param directoryToBeRelativeTo the directory which the resultant string will + be relative to. If this is actually a file rather than + a directory, its parent directory will be used instead. + If it doesn't exist, it's assumed to be a directory. + @see getChildFile, isAbsolutePath + */ + const String getRelativePathFrom (const File& directoryToBeRelativeTo) const throw(); + + /** Returns the file's extension. + + Returns the file extension of this file, also including the dot. + + e.g. "/moose/fish/foo.txt" would return ".txt" + + @see hasFileExtension, withFileExtension, getFileNameWithoutExtension + */ + const String getFileExtension() const throw(); + + /** Checks whether the file has a given extension. + + @param extensionToTest the extension to look for - it doesn't matter whether or + not this string has a dot at the start, so ".wav" and "wav" + will have the same effect. The comparison used is + case-insensitve. + + @see getFileExtension, withFileExtension, getFileNameWithoutExtension + */ + bool hasFileExtension (const String& extensionToTest) const throw(); + + /** Returns a version of this file with a different file extension. + + e.g. File ("/moose/fish/foo.txt").withFileExtension ("html") returns "/moose/fish/foo.html" + + @param newExtension the new extension, either with or without a dot at the start (this + doesn't make any difference). To get remove a file's extension altogether, + pass an empty string into this function. + + @see getFileName, getFileExtension, hasFileExtension, getFileNameWithoutExtension + */ + const File withFileExtension (const String& newExtension) const throw(); + + /** Returns the last part of the filename, without its file extension. + + e.g. for "/moose/fish/foo.txt" this will return "foo". + + @see getFileName, getFileExtension, hasFileExtension, withFileExtension + */ + const String getFileNameWithoutExtension() const throw(); + + /** Returns a 32-bit hash-code that identifies this file. + + This is based on the filename. Obviously it's possible, although unlikely, that + two files will have the same hash-code. + */ + int hashCode() const throw(); + + /** Returns a 64-bit hash-code that identifies this file. + + This is based on the filename. Obviously it's possible, although unlikely, that + two files will have the same hash-code. + */ + int64 hashCode64() const throw(); + + /** Returns a file based on a relative path. + + This will find a child file or directory of the current object. + + e.g. + File ("/moose/fish").getChildFile ("foo.txt") will produce "/moose/fish/foo.txt". + File ("/moose/fish").getChildFile ("../foo.txt") will produce "/moose/foo.txt". + + If the string is actually an absolute path, it will be treated as such, e.g. + File ("/moose/fish").getChildFile ("/foo.txt") will produce "/foo.txt" + + @see getSiblingFile, getParentDirectory, getRelativePathFrom, isAChildOf + */ + const File getChildFile (String relativePath) const throw(); + + /** Returns a file which is in the same directory as this one. + + This is equivalent to getParentDirectory().getChildFile (name). + + @see getChildFile, getParentDirectory + */ + const File getSiblingFile (const String& siblingFileName) const throw(); + + /** Returns the directory that contains this file or directory. + + e.g. for "/moose/fish/foo.txt" this will return "/moose/fish". + */ + const File getParentDirectory() const throw(); + + /** Checks whether a file is somewhere inside a directory. + + Returns true if this file is somewhere inside a subdirectory of the directory + that is passed in. Neither file actually has to exist, because the function + just checks the paths for similarities. + + e.g. File ("/moose/fish/foo.txt").isAChildOf ("/moose") is true. + File ("/moose/fish/foo.txt").isAChildOf ("/moose/fish") is also true. + */ + bool isAChildOf (const File& potentialParentDirectory) const throw(); + + /** Chooses a filename relative to this one that doesn't already exist. + + If this file is a directory, this will return a child file of this + directory that doesn't exist, by adding numbers to a prefix and suffix until + it finds one that isn't already there. + + If the prefix + the suffix doesn't exist, it won't bother adding a number. + + e.g. File ("/moose/fish").getNonexistentChildFile ("foo", ".txt", true) might + return "/moose/fish/foo(2).txt" if there's already a file called "foo.txt". + + @param prefix the string to use for the filename before the number + @param suffix the string to add to the filename after the number + @param putNumbersInBrackets if true, this will create filenames in the + format "prefix(number)suffix", if false, it will leave the + brackets out. + */ + const File getNonexistentChildFile (const String& prefix, + const String& suffix, + bool putNumbersInBrackets = true) const throw(); + + /** Chooses a filename for a sibling file to this one that doesn't already exist. + + If this file doesn't exist, this will just return itself, otherwise it + will return an appropriate sibling that doesn't exist, e.g. if a file + "/moose/fish/foo.txt" exists, this might return "/moose/fish/foo(2).txt". + + @param putNumbersInBrackets whether to add brackets around the numbers that + get appended to the new filename. + */ + const File getNonexistentSibling (const bool putNumbersInBrackets = true) const throw(); + + /** Compares the pathnames for two files. */ + bool operator== (const File& otherFile) const throw(); + /** Compares the pathnames for two files. */ + bool operator!= (const File& otherFile) const throw(); + + /** Checks whether a file can be created or written to. + + @returns true if it's possible to create and write to this file. If the file + doesn't already exist, this will check its parent directory to + see if writing is allowed. + @see setReadOnly + */ + bool hasWriteAccess() const throw(); + + /** Changes the write-permission of a file or directory. + + @param shouldBeReadOnly whether to add or remove write-permission + @param applyRecursively if the file is a directory and this is true, it will + recurse through all the subfolders changing the permissions + of all files + @returns true if it manages to change the file's permissions. + @see hasWriteAccess + */ + bool setReadOnly (const bool shouldBeReadOnly, + const bool applyRecursively = false) const throw(); + + /** Returns true if this file is a hidden or system file. + + The criteria for deciding whether a file is hidden are platform-dependent. + */ + bool isHidden() const throw(); + + /** Returns the last modification time of this file. + + @returns the time, or an invalid time if the file doesn't exist. + @see setLastModificationTime, getLastAccessTime, getCreationTime + */ + const Time getLastModificationTime() const throw(); + + /** Returns the last time this file was accessed. + + @returns the time, or an invalid time if the file doesn't exist. + @see setLastAccessTime, getLastModificationTime, getCreationTime + */ + const Time getLastAccessTime() const throw(); + + /** Returns the time that this file was created. + + @returns the time, or an invalid time if the file doesn't exist. + @see getLastModificationTime, getLastAccessTime + */ + const Time getCreationTime() const throw(); + + /** Changes the modification time for this file. + + @param newTime the time to apply to the file + @returns true if it manages to change the file's time. + @see getLastModificationTime, setLastAccessTime, setCreationTime + */ + bool setLastModificationTime (const Time& newTime) const throw(); + + /** Changes the last-access time for this file. + + @param newTime the time to apply to the file + @returns true if it manages to change the file's time. + @see getLastAccessTime, setLastModificationTime, setCreationTime + */ + bool setLastAccessTime (const Time& newTime) const throw(); + + /** Changes the creation date for this file. + + @param newTime the time to apply to the file + @returns true if it manages to change the file's time. + @see getCreationTime, setLastModificationTime, setLastAccessTime + */ + bool setCreationTime (const Time& newTime) const throw(); + + /** Creates an empty file if it doesn't already exist. + + If the file that this object refers to doesn't exist, this will create a file + of zero size. + + If it already exists or is a directory, this method will do nothing. + + @returns true if the file has been created (or if it already existed). + @see createDirectory + */ + bool create() const throw(); + + /** Creates a new directory for this filename. + + This will try to create the file as a directory, and fill also create + any parent directories it needs in order to complete the operation. + + @returns true if the directory has been created successfully, (or if it + already existed beforehand). + @see create + */ + bool createDirectory() const throw(); + + /** Deletes a file. + + If this file is actually a directory, it may not be deleted correctly if it + contains files. See deleteRecursively() as a better way of deleting directories. + + @returns true if the file has been successfully deleted (or if it didn't exist to + begin with). + @see deleteRecursively + */ + bool deleteFile() const throw(); + + /** Deletes a file or directory and all its subdirectories. + + If this file is a directory, this will try to delete it and all its subfolders. If + it's just a file, it will just try to delete the file. + + @returns true if the file and all its subfolders have been successfully deleted + (or if it didn't exist to begin with). + @see deleteFile + */ + bool deleteRecursively() const throw(); + + /** Moves or renames a file. + + Tries to move a file to a different location. + If the target file already exists, this will attempt to delete it first, and + will fail if this can't be done. + + Note that the destination file isn't the directory to put it in, it's the actual + filename that you want the new file to have. + + @returns true if the operation succeeds + */ + bool moveFileTo (const File& targetLocation) const throw(); + + /** Copies a file. + + Tries to copy a file to a different location. + If the target file already exists, this will attempt to delete it first, and + will fail if this can't be done. + + @returns true if the operation succeeds + */ + bool copyFileTo (const File& targetLocation) const throw(); + + /** Copies a directory. + + Tries to copy an entire directory, recursively. + + If this file isn't a directory or if any target files can't be created, this + will return false. + + @param newDirectory the directory that this one should be copied to. Note that this + is the name of the actual directory to create, not the directory + into which the new one should be placed, so there must be enough + write privileges to create it if it doesn't exist. Any files inside + it will be overwritten by similarly named ones that are copied. + */ + bool copyDirectoryTo (const File& newDirectory) const throw(); + + /** Used in file searching, to specify whether to return files, directories, or both. + */ + enum TypesOfFileToFind + { + findDirectories = 1, /**< Use this flag to indicate that you want to find directories. */ + findFiles = 2, /**< Use this flag to indicate that you want to find files. */ + findFilesAndDirectories = 3, /**< Use this flag to indicate that you want to find both files and directories. */ + ignoreHiddenFiles = 4 /**< Add this flag to avoid returning any hidden files in the results. */ + }; + + /** Searches inside a directory for files matching a wildcard pattern. + + Assuming that this file is a directory, this method will search it + for either files or subdirectories whose names match a filename pattern. + + @param results an array to which File objects will be added for the + files that the search comes up with + @param whatToLookFor a value from the TypesOfFileToFind enum, specifying whether to + return files, directories, or both. If the ignoreHiddenFiles flag + is also added to this value, hidden files won't be returned + @param searchRecursively if true, all subdirectories will be recursed into to do + an exhaustive search + @param wildCardPattern the filename pattern to search for, e.g. "*.txt" + @returns the number of results that have been found + + @see getNumberOfChildFiles, DirectoryIterator + */ + int findChildFiles (OwnedArray& results, + const int whatToLookFor, + const bool searchRecursively, + const String& wildCardPattern = JUCE_T("*")) const throw(); + + /** Searches inside a directory and counts how many files match a wildcard pattern. + + Assuming that this file is a directory, this method will search it + for either files or subdirectories whose names match a filename pattern, + and will return the number of matches found. + + This isn't a recursive call, and will only search this directory, not + its children. + + @param whatToLookFor a value from the TypesOfFileToFind enum, specifying whether to + count files, directories, or both. If the ignoreHiddenFiles flag + is also added to this value, hidden files won't be counted + @param wildCardPattern the filename pattern to search for, e.g. "*.txt" + @returns the number of matches found + @see findChildFiles, DirectoryIterator + */ + int getNumberOfChildFiles (const int whatToLookFor, + const String& wildCardPattern = JUCE_T("*")) const throw(); + + /** Creates a stream to read from this file. + + @returns a stream that will read from this file (initially positioned at the + start of the file), or 0 if the file can't be opened for some reason + @see createOutputStream, loadFileAsData + */ + FileInputStream* createInputStream() const throw(); + + /** Creates a stream to write to this file. + + If the file exists, the stream that is returned will be positioned ready for + writing at the end of the file, so you might want to use deleteFile() first + to write to an empty file. + + @returns a stream that will write to this file (initially positioned at the + end of the file), or 0 if the file can't be opened for some reason + @see createInputStream, printf, appendData, appendText + */ + FileOutputStream* createOutputStream (const int bufferSize = 0x8000) const throw(); + + /** Loads a file's contents into memory as a block of binary data. + + Of course, trying to load a very large file into memory will blow up, so + it's better to check first. + + @param result the data block to which the file's contents should be appended - note + that if the memory block might already contain some data, you + might want to clear it first + @returns true if the file could all be read into memory + */ + bool loadFileAsData (MemoryBlock& result) const throw(); + + /** Reads a file into memory as a string. + + Attempts to load the entire file as a zero-terminated string. + + This makes use of InputStream::readEntireStreamAsString, which should + automatically cope with unicode/acsii file formats. + */ + const String loadFileAsString() const throw(); + + /** Writes text to the end of the file. + + This will try to do a printf to the file. + + @returns false if it can't write to the file for some reason + */ + bool printf (const tchar* format, ...) const throw(); + + /** Appends a block of binary data to the end of the file. + + This will try to write the given buffer to the end of the file. + + @returns false if it can't write to the file for some reason + */ + bool appendData (const void* const dataToAppend, + const int numberOfBytes) const throw(); + + /** Replaces this file's contents with a given block of data. + + This will delete the file and replace it with the given data. + + A nice feature of this method is that it's safe - instead of deleting + the file first and then re-writing it, it creates a new temporary file, + writes the data to that, and then moves the new file to replace the existing + file. This means that if the power gets pulled out or something crashes, + you're a lot less likely to end up with an empty file.. + + Returns true if the operation succeeds, or false if it fails. + + @see appendText + */ + bool replaceWithData (const void* const dataToWrite, + const int numberOfBytes) const throw(); + + /** Appends a string to the end of the file. + + This will try to append a text string to the file, as either 16-bit unicode + or 8-bit characters in the default system encoding. + + It can also write the 'ff fe' unicode header bytes before the text to indicate + the endianness of the file. + + Any single \\n characters in the string are replaced with \\r\\n before it is written. + + @see replaceWithText + */ + bool appendText (const String& textToAppend, + const bool asUnicode = false, + const bool writeUnicodeHeaderBytes = false) const throw(); + + /** Replaces this file's contents with a given text string. + + This will delete the file and replace it with the given text. + + A nice feature of this method is that it's safe - instead of deleting + the file first and then re-writing it, it creates a new temporary file, + writes the text to that, and then moves the new file to replace the existing + file. This means that if the power gets pulled out or something crashes, + you're a lot less likely to end up with an empty file.. + + For an explanation of the parameters here, see the appendText() method. + + Returns true if the operation succeeds, or false if it fails. + + @see appendText + */ + bool replaceWithText (const String& textToWrite, + const bool asUnicode = false, + const bool writeUnicodeHeaderBytes = false) const throw(); + + /** Creates a set of files to represent each file root. + + e.g. on Windows this will create files for "c:\", "d:\" etc according + to which ones are available. On the Mac/Linux, this will probably + just add a single entry for "/". + */ + static void findFileSystemRoots (OwnedArray& results) throw(); + + /** Finds the name of the drive on which this file lives. + + @returns the volume label of the drive, or an empty string if this isn't possible + */ + const String getVolumeLabel() const throw(); + + /** Returns the serial number of the volume on which this file lives. + + @returns the serial number, or zero if there's a problem doing this + */ + int getVolumeSerialNumber() const throw(); + + /** Returns the number of bytes free on the drive that this file lives on. + + @returns the number of bytes free, or 0 if there's a problem finding this out + */ + int64 getBytesFreeOnVolume() const throw(); + + /** Returns true if this file is on a CD or DVD drive. */ + bool isOnCDRomDrive() const throw(); + + /** Returns true if this file is on a hard disk. + + This will fail if it's a network drive, but will still be true for + removable hard-disks. + */ + bool isOnHardDisk() const throw(); + + /** Returns true if this file is on a removable disk drive. + + This might be a usb-drive, a CD-rom, or maybe a network drive. + */ + bool isOnRemovableDrive() const throw(); + + /** Launches the file as a process. + + - if the file is executable, this will run it. + + - if it's a document of some kind, it will launch the document with its + default viewer application. + + - if it's a folder, it will be opened in Explorer, Finder, or equivalent. + */ + bool startAsProcess (const String& parameters = String::empty) const throw(); + + /** A set of types of location that can be passed to the getSpecialLocation() method. + */ + enum SpecialLocationType + { + /** The user's home folder. This is the same as using File ("~"). */ + userHomeDirectory, + + /** The user's default documents folder. On Windows, this might be the user's + "My Documents" folder. On the Mac it'll be their "Documents" folder. Linux + doesn't tend to have one of these, so it might just return their home folder. + */ + userDocumentsDirectory, + + /** The folder that contains the user's desktop objects. */ + userDesktopDirectory, + + /** The folder in which applications store their persistent user-specific settings. + On Windows, this might be "\Documents and Settings\username\Application Data". + On the Mac, it might be "~/Library". If you're going to store your settings in here, + always create your own sub-folder to put them in, to avoid making a mess. + */ + userApplicationDataDirectory, + + /** An equivalent of the userApplicationDataDirectory folder that is shared by all users + of the computer, rather than just the current user. + + On the Mac it'll be "/Library", on Windows, it could be something like + "\Documents and Settings\All Users\Application Data". + + Depending on the setup, this folder may be read-only. + */ + commonApplicationDataDirectory, + + /** The folder that should be used for temporary files. + + Always delete them when you're finished, to keep the user's computer tidy! + */ + tempDirectory, + + /** Returns this application's executable file. + + If running as a plug-in or DLL, this will (where possible) be the DLL rather than the + host app. + + On the mac this will return the unix binary, not the package folder - see + currentApplicationFile for that. + */ + currentExecutableFile, + + /** Returns this application's location. + + If running as a plug-in or DLL, this will (where possible) be the DLL rather than the + host app. + + On the mac this will return the package folder (if it's in one), not the unix binary + that's inside it - compare with currentExecutableFile. + */ + currentApplicationFile, + + /** The directory in which applications normally get installed. + + So on windows, this would be something like "c:\program files", on the + Mac "/Applications", or "/usr" on linux. + */ + globalApplicationsDirectory, + + /** The most likely place where a user might store their music files. + */ + userMusicDirectory, + + /** The most likely place where a user might store their movie files. + */ + userMoviesDirectory, + }; + + /** Finds the location of a special type of file or directory, such as a home folder or + documents folder. + + @see SpecialLocationType + */ + static const File JUCE_CALLTYPE getSpecialLocation (const SpecialLocationType type); + + /** Returns a temporary file in the system's temp directory. + + This will try to return the name of a non-existent temp file. + + To get the temp folder, you can use getSpecialLocation (File::tempDirectory). + */ + static const File createTempFile (const String& fileNameEnding) throw(); + + /** Returns the current working directory. + + @see setAsCurrentWorkingDirectory + */ + static const File getCurrentWorkingDirectory() throw(); + + /** Sets the current working directory to be this file. + + For this to work the file must point to a valid directory. + + @returns true if the current directory has been changed. + @see getCurrentWorkingDirectory + */ + bool setAsCurrentWorkingDirectory() const throw(); + + /** The system-specific file separator character. + + On Windows, this will be '\', on Mac/Linux, it'll be '/' + */ + static const tchar separator; + + /** The system-specific file separator character, as a string. + + On Windows, this will be '\', on Mac/Linux, it'll be '/' + */ + static const tchar* separatorString; + + /** Removes illegal characters from a filename. + + This will return a copy of the given string after removing characters + that are not allowed in a legal filename, and possibly shortening the + string if it's too long. + + Because this will remove slashes, don't use it on an absolute pathname. + + @see createLegalPathName + */ + static const String createLegalFileName (const String& fileNameToFix) throw(); + + /** Removes illegal characters from a pathname. + + Similar to createLegalFileName(), but this won't remove slashes, so can + be used on a complete pathname. + + @see createLegalFileName + */ + static const String createLegalPathName (const String& pathNameToFix) throw(); + + /** Indicates whether filenames are case-sensitive on the current operating system. + */ + static bool areFileNamesCaseSensitive(); + + /** Returns true if the string seems to be a fully-specified absolute path. + */ + static bool isAbsolutePath (const String& path) throw(); + + juce_UseDebuggingNewOperator + +private: + + String fullPath; + + // internal way of contructing a file without checking the path + friend class DirectoryIterator; + File (const String&, int) throw(); + const String getPathUpToLastSlash() const throw(); +}; + +#endif // __JUCE_FILE_JUCEHEADER__ +/********* End of inlined file: juce_File.h *********/ + +/** + A simple implemenation of a Logger that writes to a file. + + @see Logger +*/ +class JUCE_API FileLogger : public Logger +{ +public: + + /** Creates a FileLogger for a given file. + + @param fileToWriteTo the file that to use - new messages will be appended + to the file. If the file doesn't exist, it will be created, + along with any parent directories that are needed. + @param welcomeMessage when opened, the logger will write a header to the log, along + with the current date and time, and this welcome message + @param maxInitialFileSizeBytes if this is zero or greater, then if the file already exists + but is larger than this number of bytes, then the start of the + file will be truncated to keep the size down. This prevents a log + file getting ridiculously large over time. The file will be truncated + at a new-line boundary. If this value is less than zero, no size limit + will be imposed; if it's zero, the file will always be deleted. Note that + the size is only checked once when this object is created - any logging + that is done later will be appended without any checking + */ + FileLogger (const File& fileToWriteTo, + const String& welcomeMessage, + const int maxInitialFileSizeBytes = 128 * 1024); + + /** Destructor. */ + ~FileLogger(); + + void logMessage (const String& message); + + /** Helper function to create a log file in the correct place for this platform. + + On Windows this will return a logger with a path such as: + c:\\Documents and Settings\\username\\Application Data\\[logFileSubDirectoryName]\\[logFileName] + + On the Mac it'll create something like: + ~/Library/Logs/[logFileName] + + The method might return 0 if the file can't be created for some reason. + + @param logFileSubDirectoryName if a subdirectory is needed, this is what it will be called - + it's best to use the something like the name of your application here. + @param logFileName the name of the file to create, e.g. "MyAppLog.txt". Don't just + call it "log.txt" because if it goes in a directory with logs + from other applications (as it will do on the Mac) then no-one + will know which one is yours! + @param welcomeMessage a message that will be written to the log when it's opened. + @param maxInitialFileSizeBytes (see the FileLogger constructor for more info on this) + */ + static FileLogger* createDefaultAppLogger (const String& logFileSubDirectoryName, + const String& logFileName, + const String& welcomeMessage, + const int maxInitialFileSizeBytes = 128 * 1024); + + juce_UseDebuggingNewOperator + +private: + File logFile; + CriticalSection logLock; + FileOutputStream* logStream; + + void trimFileSize (int maxFileSizeBytes) const; + + FileLogger (const FileLogger&); + const FileLogger& operator= (const FileLogger&); +}; + +#endif // __JUCE_FILELOGGER_JUCEHEADER__ +/********* End of inlined file: juce_FileLogger.h *********/ + +#endif +#ifndef __JUCE_INITIALISATION_JUCEHEADER__ + +/********* Start of inlined file: juce_Initialisation.h *********/ +#ifndef __JUCE_INITIALISATION_JUCEHEADER__ +#define __JUCE_INITIALISATION_JUCEHEADER__ + +/** Initialises Juce's GUI classes. + + If you're embedding Juce into an application that uses its own event-loop rather + than using the START_JUCE_APPLICATION macro, call this function before making any + Juce calls, to make sure things are initialised correctly. + + Note that if you're creating a Juce DLL for Windows, you may also need to call the + PlatformUtilities::setCurrentModuleInstanceHandle() method. + + @see shutdownJuce_GUI(), initialiseJuce_NonGUI() +*/ +void JUCE_PUBLIC_FUNCTION initialiseJuce_GUI(); + +/** Clears up any static data being used by Juce's GUI classes. + + If you're embedding Juce into an application that uses its own event-loop rather + than using the START_JUCE_APPLICATION macro, call this function in your shutdown + code to clean up any juce objects that might be lying around. + + @see initialiseJuce_GUI(), initialiseJuce_NonGUI() +*/ +void JUCE_PUBLIC_FUNCTION shutdownJuce_GUI(); + +/** Initialises the core parts of Juce. + + If you're embedding Juce into either a command-line program, call this function + at the start of your main() function to make sure that Juce is initialised correctly. + + Note that if you're creating a Juce DLL for Windows, you may also need to call the + PlatformUtilities::setCurrentModuleInstanceHandle() method. + + @see shutdownJuce_NonGUI, initialiseJuce_GUI +*/ +void JUCE_PUBLIC_FUNCTION initialiseJuce_NonGUI(); + +/** Clears up any static data being used by Juce's non-gui core classes. + + If you're embedding Juce into either a command-line program, call this function + at the end of your main() function if you want to make sure any Juce objects are + cleaned up correctly. + + @see initialiseJuce_NonGUI, initialiseJuce_GUI +*/ +void JUCE_PUBLIC_FUNCTION shutdownJuce_NonGUI(); + +#endif // __JUCE_INITIALISATION_JUCEHEADER__ +/********* End of inlined file: juce_Initialisation.h *********/ + +#endif +#ifndef __JUCE_LOGGER_JUCEHEADER__ + +#endif +#ifndef __JUCE_MATHSFUNCTIONS_JUCEHEADER__ + +#endif +#ifndef __JUCE_MEMORY_JUCEHEADER__ + +#endif +#ifndef __JUCE_PLATFORMDEFS_JUCEHEADER__ + +#endif +#ifndef __JUCE_RANDOM_JUCEHEADER__ + +/********* Start of inlined file: juce_Random.h *********/ +#ifndef __JUCE_RANDOM_JUCEHEADER__ +#define __JUCE_RANDOM_JUCEHEADER__ + +/** + A simple pseudo-random number generator. +*/ +class JUCE_API Random +{ +public: + + /** Creates a Random object based on a seed value. + + For a given seed value, the subsequent numbers generated by this object + will be predictable, so a good idea is to set this value based + on the time, e.g. + + new Random (Time::currentTimeMillis()) + */ + Random (const int64 seedValue) throw(); + + /** Destructor. */ + ~Random() throw(); + + /** Returns the next random 32 bit integer. + + @returns a random integer from the full range 0x80000000 to 0x7fffffff + */ + int nextInt() throw(); + + /** Returns the next random number, limited to a given range. + + @returns a random integer between 0 (inclusive) and maxValue (exclusive). + */ + int nextInt (const int maxValue) throw(); + + /** Returns the next 64-bit random number. + + @returns a random integer from the full range 0x8000000000000000 to 0x7fffffffffffffff + */ + int64 nextInt64() throw(); + + /** Returns the next random floating-point number. + + @returns a random value in the range 0 to 1.0 + */ + float nextFloat() throw(); + + /** Returns the next random floating-point number. + + @returns a random value in the range 0 to 1.0 + */ + double nextDouble() throw(); + + /** Returns the next random boolean value. + */ + bool nextBool() throw(); + + /** To avoid the overhead of having to create a new Random object whenever + you need a number, this is a shared application-wide object that + can be used. + + It's not thread-safe though, so threads should use their own Random object. + */ + static Random& getSystemRandom() throw(); + + /** Resets this Random object to a given seed value. */ + void setSeed (const int64 newSeed) throw(); + + juce_UseDebuggingNewOperator + +private: + int64 seed; +}; + +#endif // __JUCE_RANDOM_JUCEHEADER__ +/********* End of inlined file: juce_Random.h *********/ + +#endif +#ifndef __JUCE_RELATIVETIME_JUCEHEADER__ + +#endif +#ifndef __JUCE_SINGLETON_JUCEHEADER__ + +/********* Start of inlined file: juce_Singleton.h *********/ +#ifndef __JUCE_SINGLETON_JUCEHEADER__ +#define __JUCE_SINGLETON_JUCEHEADER__ + +/********* Start of inlined file: juce_ScopedLock.h *********/ +#ifndef __JUCE_SCOPEDLOCK_JUCEHEADER__ +#define __JUCE_SCOPEDLOCK_JUCEHEADER__ + +/** + Automatically locks and unlocks a CriticalSection object. + + Use one of these as a local variable to control access to a CriticalSection. + + e.g. @code + + CriticalSection myCriticalSection; + + for (;;) + { + const ScopedLock myScopedLock (myCriticalSection); + // myCriticalSection is now locked + + ...do some stuff... + + // myCriticalSection gets unlocked here. + } + @endcode + + @see CriticalSection, ScopedUnlock +*/ +class JUCE_API ScopedLock +{ +public: + + /** Creates a ScopedLock. + + As soon as it is created, this will lock the CriticalSection, and + when the ScopedLock object is deleted, the CriticalSection will + be unlocked. + + Make sure this object is created and deleted by the same thread, + otherwise there are no guarantees what will happen! Best just to use it + as a local stack object, rather than creating one with the new() operator. + */ + inline ScopedLock (const CriticalSection& lock) throw() : lock_ (lock) { lock.enter(); } + + /** Destructor. + + The CriticalSection will be unlocked when the destructor is called. + + Make sure this object is created and deleted by the same thread, + otherwise there are no guarantees what will happen! + */ + inline ~ScopedLock() throw() { lock_.exit(); } + +private: + + const CriticalSection& lock_; + + ScopedLock (const ScopedLock&); + const ScopedLock& operator= (const ScopedLock&); +}; + +/** + Automatically unlocks and re-locks a CriticalSection object. + + This is the reverse of a ScopedLock object - instead of locking the critical + section for the lifetime of this object, it unlocks it. + + Make sure you don't try to unlock critical sections that aren't actually locked! + + e.g. @code + + CriticalSection myCriticalSection; + + for (;;) + { + const ScopedLock myScopedLock (myCriticalSection); + // myCriticalSection is now locked + + ... do some stuff with it locked .. + + while (xyz) + { + ... do some stuff with it locked .. + + const ScopedUnlock unlocker (myCriticalSection); + + // myCriticalSection is now unlocked for the remainder of this block, + // and re-locked at the end. + + ...do some stuff with it unlocked ... + } + + // myCriticalSection gets unlocked here. + } + @endcode + + @see CriticalSection, ScopedLock +*/ +class ScopedUnlock +{ +public: + + /** Creates a ScopedUnlock. + + As soon as it is created, this will unlock the CriticalSection, and + when the ScopedLock object is deleted, the CriticalSection will + be re-locked. + + Make sure this object is created and deleted by the same thread, + otherwise there are no guarantees what will happen! Best just to use it + as a local stack object, rather than creating one with the new() operator. + */ + inline ScopedUnlock (const CriticalSection& lock) throw() : lock_ (lock) { lock.exit(); } + + /** Destructor. + + The CriticalSection will be unlocked when the destructor is called. + + Make sure this object is created and deleted by the same thread, + otherwise there are no guarantees what will happen! + */ + inline ~ScopedUnlock() throw() { lock_.enter(); } + +private: + + const CriticalSection& lock_; + + ScopedUnlock (const ScopedLock&); + const ScopedUnlock& operator= (const ScopedUnlock&); +}; + +#endif // __JUCE_SCOPEDLOCK_JUCEHEADER__ +/********* End of inlined file: juce_ScopedLock.h *********/ + +/** + Macro to declare member variables and methods for a singleton class. + + To use this, add the line juce_DeclareSingleton (MyClass, allowOnlyOneInstance) + to the class's definition. + + If allowOnlyOneInstance == true, it won't allow the object to be created + more than once in the process's lifetime. + + Then put a macro juce_ImplementSingleton (MyClass) along with the class's + implementation code. + + Clients can then call the static MyClass::getInstance() to get a pointer to the + singleton, or MyClass::getInstanceWithoutCreating() which may return 0 if no instance + is currently extant + + it's a very good idea to also add the call clearSingletonInstance() to the + destructor of the class, in case it is deleted by other means than deleteInstance() + + e.g. @code + + class MySingleton + { + public: + MySingleton() + { + } + + ~MySingleton() + { + // this ensures that no dangling pointers are left when the + // singleton is deleted. + clearSingletonInstance(); + } + + juce_DeclareSingleton (MySingleton, false) + }; + + juce_ImplementSingleton (MySingleton) + + // example of usage: + MySingleton* m = MySingleton::getInstance(); // creates the singleton if there isn't already one. + + ... + + MySingleton::deleteInstance(); // safely deletes the singleton (if it's been created). + + @endcode + + If you know that your object will only be created and deleted by a single thread, you + can use the slightly more efficient juce_DeclareSingleton_SingleThreaded() macro instead + of this one. + + @see juce_ImplementSingleton, juce_DeclareSingleton_SingleThreaded +*/ +#define juce_DeclareSingleton(classname, allowOnlyOneInstance) \ +\ + static classname* _singletonInstance; \ + static JUCE_NAMESPACE::CriticalSection _singletonLock; \ +\ + static classname* getInstance() \ + { \ + if (_singletonInstance == 0) \ + {\ + const JUCE_NAMESPACE::ScopedLock sl (_singletonLock); \ +\ + if (_singletonInstance == 0) \ + { \ + static bool alreadyInside = false; \ + static bool createdOnceAlready = false; \ +\ + const bool problem = alreadyInside || ((allowOnlyOneInstance) && createdOnceAlready); \ + jassert (! problem); \ + if (! problem) \ + { \ + createdOnceAlready = true; \ + alreadyInside = true; \ + classname* newObject = new classname(); /* (use a stack variable to avoid setting the newObject value before the class has finished its constructor) */ \ + alreadyInside = false; \ +\ + _singletonInstance = newObject; \ + } \ + } \ + } \ +\ + return _singletonInstance; \ + } \ +\ + static inline classname* getInstanceWithoutCreating() throw() \ + { \ + return _singletonInstance; \ + } \ +\ + static void deleteInstance() \ + { \ + const JUCE_NAMESPACE::ScopedLock sl (_singletonLock); \ + if (_singletonInstance != 0) \ + { \ + classname* const old = _singletonInstance; \ + _singletonInstance = 0; \ + delete old; \ + } \ + } \ +\ + void clearSingletonInstance() throw() \ + { \ + if (_singletonInstance == this) \ + _singletonInstance = 0; \ + } + +/** This is a counterpart to the juce_DeclareSingleton macro. + + After adding the juce_DeclareSingleton to the class definition, this macro has + to be used in the cpp file. +*/ +#define juce_ImplementSingleton(classname) \ +\ + classname* classname::_singletonInstance = 0; \ + JUCE_NAMESPACE::CriticalSection classname::_singletonLock; + +/** + Macro to declare member variables and methods for a singleton class. + + This is exactly the same as juce_DeclareSingleton, but doesn't use a critical + section to make access to it thread-safe. If you know that your object will + only ever be created or deleted by a single thread, then this is a + more efficient version to use. + + See the documentation for juce_DeclareSingleton for more information about + how to use it, the only difference being that you have to use + juce_ImplementSingleton_SingleThreaded instead of juce_ImplementSingleton. + + @see juce_ImplementSingleton_SingleThreaded, juce_DeclareSingleton, juce_DeclareSingleton_SingleThreaded_Minimal +*/ +#define juce_DeclareSingleton_SingleThreaded(classname, allowOnlyOneInstance) \ +\ + static classname* _singletonInstance; \ +\ + static classname* getInstance() \ + { \ + if (_singletonInstance == 0) \ + { \ + static bool alreadyInside = false; \ + static bool createdOnceAlready = false; \ +\ + const bool problem = alreadyInside || ((allowOnlyOneInstance) && createdOnceAlready); \ + jassert (! problem); \ + if (! problem) \ + { \ + createdOnceAlready = true; \ + alreadyInside = true; \ + classname* newObject = new classname(); /* (use a stack variable to avoid setting the newObject value before the class has finished its constructor) */ \ + alreadyInside = false; \ +\ + _singletonInstance = newObject; \ + } \ + } \ +\ + return _singletonInstance; \ + } \ +\ + static inline classname* getInstanceWithoutCreating() throw() \ + { \ + return _singletonInstance; \ + } \ +\ + static void deleteInstance() \ + { \ + if (_singletonInstance != 0) \ + { \ + classname* const old = _singletonInstance; \ + _singletonInstance = 0; \ + delete old; \ + } \ + } \ +\ + void clearSingletonInstance() throw() \ + { \ + if (_singletonInstance == this) \ + _singletonInstance = 0; \ + } + +/** + Macro to declare member variables and methods for a singleton class. + + This is like juce_DeclareSingleton_SingleThreaded, but doesn't do any checking + for recursion or repeated instantiation. It's intended for use as a lightweight + version of a singleton, where you're using it in very straightforward + circumstances and don't need the extra checking. + + Juce use the normal juce_ImplementSingleton_SingleThreaded as the counterpart + to this declaration, as you would with juce_DeclareSingleton_SingleThreaded. + + See the documentation for juce_DeclareSingleton for more information about + how to use it, the only difference being that you have to use + juce_ImplementSingleton_SingleThreaded instead of juce_ImplementSingleton. + + @see juce_ImplementSingleton_SingleThreaded, juce_DeclareSingleton +*/ +#define juce_DeclareSingleton_SingleThreaded_Minimal(classname) \ +\ + static classname* _singletonInstance; \ +\ + static classname* getInstance() \ + { \ + if (_singletonInstance == 0) \ + _singletonInstance = new classname(); \ +\ + return _singletonInstance; \ + } \ +\ + static inline classname* getInstanceWithoutCreating() throw() \ + { \ + return _singletonInstance; \ + } \ +\ + static void deleteInstance() \ + { \ + if (_singletonInstance != 0) \ + { \ + classname* const old = _singletonInstance; \ + _singletonInstance = 0; \ + delete old; \ + } \ + } \ +\ + void clearSingletonInstance() throw() \ + { \ + if (_singletonInstance == this) \ + _singletonInstance = 0; \ + } + +/** This is a counterpart to the juce_DeclareSingleton_SingleThreaded macro. + + After adding juce_DeclareSingleton_SingleThreaded or juce_DeclareSingleton_SingleThreaded_Minimal + to the class definition, this macro has to be used somewhere in the cpp file. +*/ +#define juce_ImplementSingleton_SingleThreaded(classname) \ +\ + classname* classname::_singletonInstance = 0; + +#endif // __JUCE_SINGLETON_JUCEHEADER__ +/********* End of inlined file: juce_Singleton.h *********/ + +#endif +#ifndef __JUCE_STANDARDHEADER_JUCEHEADER__ + +#endif +#ifndef __JUCE_SYSTEMSTATS_JUCEHEADER__ + +/********* Start of inlined file: juce_SystemStats.h *********/ +#ifndef __JUCE_SYSTEMSTATS_JUCEHEADER__ +#define __JUCE_SYSTEMSTATS_JUCEHEADER__ + +/** + Contains methods for finding out about the current hardware and OS configuration. +*/ +class JUCE_API SystemStats +{ +public: + + /** Returns the current version of JUCE, + + (just in case you didn't already know at compile-time.) + + See also the JUCE_VERSION, JUCE_MAJOR_VERSION and JUCE_MINOR_VERSION macros. + */ + static const String getJUCEVersion() throw(); + + /** The set of possible results of the getOperatingSystemType() method. + */ + enum OperatingSystemType + { + UnknownOS = 0, + + MacOSX = 0x1000, + Linux = 0x2000, + + Win95 = 0x4001, + Win98 = 0x4002, + WinNT351 = 0x4103, + WinNT40 = 0x4104, + Win2000 = 0x4105, + WinXP = 0x4106, + WinVista = 0x4107, + + Windows = 0x4000, /**< To test whether any version of Windows is running, + you can use the expression ((getOperatingSystemType() & Windows) != 0). */ + WindowsNT = 0x0100, /**< To test whether the platform is Windows NT or later (i.e. not Win95 or 98), + you can use the expression ((getOperatingSystemType() & WindowsNT) != 0). */ + }; + + /** Returns the type of operating system we're running on. + + @returns one of the values from the OSType enum. + @see getOperatingSystemName + */ + static OperatingSystemType getOperatingSystemType() throw(); + + /** Returns the name of the type of operating system we're running on. + + @returns a string describing the OS type. + @see getOperatingSystemType + */ + static const String getOperatingSystemName() throw(); + + /** Returns true if the OS is 64-bit, or false for a 32-bit OS. + */ + static bool isOperatingSystem64Bit() throw(); + + // CPU and memory information.. + + /** Returns the approximate CPU speed. + + @returns the speed in megahertz, e.g. 1500, 2500, 32000 (depending on + what year you're reading this...) + */ + static int getCpuSpeedInMegaherz() throw(); + + /** Returns a string to indicate the CPU vendor. + + Might not be known on some systems. + */ + static const String getCpuVendor() throw(); + + /** Checks whether Intel MMX instructions are available. */ + static bool hasMMX() throw(); + + /** Checks whether Intel SSE instructions are available. */ + static bool hasSSE() throw(); + + /** Checks whether Intel SSE2 instructions are available. */ + static bool hasSSE2() throw(); + + /** Checks whether AMD 3DNOW instructions are available. */ + static bool has3DNow() throw(); + + /** Returns the number of CPUs. + */ + static int getNumCpus() throw(); + + /** Returns a clock-cycle tick counter, if available. + + If the machine can do it, this will return a tick-count + where each tick is one cpu clock cycle - used for profiling + code. + + @returns the tick count, or zero if not available. + */ + static int64 getClockCycleCounter() throw(); + + /** Finds out how much RAM is in the machine. + + @returns the approximate number of megabytes of memory, or zero if + something goes wrong when finding out. + */ + static int getMemorySizeInMegabytes() throw(); + + /** Returns the system page-size. + + This is only used by programmers with beards. + */ + static int getPageSize() throw(); + + /** Returns a list of MAC addresses found on this machine. + + @param addresses an array into which the MAC addresses should be copied + @param maxNum the number of elements in this array + @param littleEndian the endianness of the numbers to return. Note that + the default values of this parameter are different on + Mac/PC to avoid breaking old software that was written + before this parameter was added (when the two systems + defaulted to using different endiannesses). In newer + software you probably want to specify an explicit value + for this. + @returns the number of MAC addresses that were found + */ + static int getMACAddresses (int64* addresses, int maxNum, +#if JUCE_MAC + const bool littleEndian = true) throw(); +#else + const bool littleEndian = false) throw(); +#endif + + // not-for-public-use platform-specific method gets called at startup to initialise things. + static void initialiseStats() throw(); +}; + +#endif // __JUCE_SYSTEMSTATS_JUCEHEADER__ +/********* End of inlined file: juce_SystemStats.h *********/ + +#endif +#ifndef __JUCE_TIME_JUCEHEADER__ + +#endif +#ifndef __JUCE_ARRAY_JUCEHEADER__ + +#endif +#ifndef __JUCE_ARRAYALLOCATIONBASE_JUCEHEADER__ + +#endif +#ifndef __JUCE_BITARRAY_JUCEHEADER__ + +/********* Start of inlined file: juce_BitArray.h *********/ +#ifndef __JUCE_BITARRAY_JUCEHEADER__ +#define __JUCE_BITARRAY_JUCEHEADER__ + +class MemoryBlock; + +/** + An array of on/off bits, also usable to store large binary integers. + + A BitArray acts like an arbitrarily large integer whose bits can be set or + cleared, and some basic mathematical operations can be done on the number as + a whole. +*/ +class JUCE_API BitArray +{ +public: + + /** Creates an empty BitArray */ + BitArray() throw(); + + /** Creates a BitArray containing an integer value in its low bits. + + The low 32 bits of the array are initialised with this value. + */ + BitArray (const unsigned int value) throw(); + + /** Creates a BitArray containing an integer value in its low bits. + + The low 32 bits of the array are initialised with the absolute value + passed in, and its sign is set to reflect the sign of the number. + */ + BitArray (const int value) throw(); + + /** Creates a BitArray containing an integer value in its low bits. + + The low 64 bits of the array are initialised with the absolute value + passed in, and its sign is set to reflect the sign of the number. + */ + BitArray (int64 value) throw(); + + /** Creates a copy of another BitArray. */ + BitArray (const BitArray& other) throw(); + + /** Destructor. */ + ~BitArray() throw(); + + /** Copies another BitArray onto this one. */ + const BitArray& operator= (const BitArray& other) throw(); + + /** Two arrays are the same if the same bits are set. */ + bool operator== (const BitArray& other) const throw(); + /** Two arrays are the same if the same bits are set. */ + bool operator!= (const BitArray& other) const throw(); + + /** Clears all bits in the BitArray to 0. */ + void clear() throw(); + + /** Clears a particular bit in the array. */ + void clearBit (const int bitNumber) throw(); + + /** Sets a specified bit to 1. + + If the bit number is high, this will grow the array to accomodate it. + */ + void setBit (const int bitNumber) throw(); + + /** Sets or clears a specified bit. */ + void setBit (const int bitNumber, + const bool shouldBeSet) throw(); + + /** Sets a range of bits to be either on or off. + + @param startBit the first bit to change + @param numBits the number of bits to change + @param shouldBeSet whether to turn these bits on or off + */ + void setRange (int startBit, + int numBits, + const bool shouldBeSet) throw(); + + /** Inserts a bit an a given position, shifting up any bits above it. */ + void insertBit (const int bitNumber, + const bool shouldBeSet) throw(); + + /** Returns the value of a specified bit in the array. + + If the index is out-of-range, the result will be false. + */ + bool operator[] (const int bit) const throw(); + + /** Returns true if no bits are set. */ + bool isEmpty() const throw(); + + /** Returns a range of bits in the array as an integer value. + + e.g. getBitRangeAsInt (0, 32) would return the lowest 32 bits. + + Asking for more than 32 bits isn't allowed (obviously). + */ + int getBitRangeAsInt (int startBit, int numBits) const throw(); + + /** Sets a range of bits in the array based on an integer value. + + Copies the given integer into the array, starting at startBit, + and only using up to numBits of the available bits. + */ + void setBitRangeAsInt (int startBit, int numBits, + unsigned int valueToSet) throw(); + + /** Performs a bitwise OR with another BitArray. + + The result ends up in this array. + */ + void orWith (const BitArray& other) throw(); + + /** Performs a bitwise AND with another BitArray. + + The result ends up in this array. + */ + void andWith (const BitArray& other) throw(); + + /** Performs a bitwise XOR with another BitArray. + + The result ends up in this array. + */ + void xorWith (const BitArray& other) throw(); + + /** Adds another BitArray's value to this one. + + Treating the two arrays as large positive integers, this + adds them up and puts the result in this array. + */ + void add (const BitArray& other) throw(); + + /** Subtracts another BitArray's value from this one. + + Treating the two arrays as large positive integers, this + subtracts them and puts the result in this array. + + Note that if the result should be negative, this won't be + handled correctly. + */ + void subtract (const BitArray& other) throw(); + + /** Multiplies another BitArray's value with this one. + + Treating the two arrays as large positive integers, this + multiplies them and puts the result in this array. + */ + void multiplyBy (const BitArray& other) throw(); + + /** Divides another BitArray's value into this one and also produces a remainder. + + Treating the two arrays as large positive integers, this + divides this value by the other, leaving the quotient in this + array, and the remainder is copied into the other BitArray passed in. + */ + void divideBy (const BitArray& divisor, BitArray& remainder) throw(); + + /** Returns the largest value that will divide both this value and the one + passed-in. + */ + const BitArray findGreatestCommonDivisor (BitArray other) const throw(); + + /** Performs a modulo operation on this value. + + The result is stored in this value. + */ + void modulo (const BitArray& divisor) throw(); + + /** Performs a combined exponent and modulo operation. + + This BitArray's value becomes (this ^ exponent) % modulus. + */ + void exponentModulo (const BitArray& exponent, const BitArray& modulus) throw(); + + /** Performs an inverse modulo on the value. + + i.e. the result is (this ^ -1) mod (modulus). + */ + void inverseModulo (const BitArray& modulus) throw(); + + /** Shifts a section of bits left or right. + + @param howManyBitsLeft how far to move the bits (+ve numbers shift it left, -ve numbers shift it right). + @param startBit the first bit to affect - if this is > 0, only bits above that index will be affected. + */ + void shiftBits (int howManyBitsLeft, + int startBit = 0) throw(); + + /** Does a signed comparison of two BitArrays. + + Return values are: + - 0 if the numbers are the same + - < 0 if this number is smaller than the other + - > 0 if this number is bigger than the other + */ + int compare (const BitArray& other) const throw(); + + /** Compares the magnitudes of two BitArrays, ignoring their signs. + + Return values are: + - 0 if the numbers are the same + - < 0 if this number is smaller than the other + - > 0 if this number is bigger than the other + */ + int compareAbsolute (const BitArray& other) const throw(); + + /** Returns true if the value is less than zero. + + @see setNegative, negate + */ + bool isNegative() const throw(); + + /** Changes the sign of the number to be positive or negative. + + @see isNegative, negate + */ + void setNegative (const bool shouldBeNegative) throw(); + + /** Inverts the sign of the number. + + @see isNegative, setNegative + */ + void negate() throw(); + + /** Counts the total number of set bits in the array. */ + int countNumberOfSetBits() const throw(); + + /** Looks for the index of the next set bit after a given starting point. + + searches from startIndex (inclusive) upwards for the first set bit, + and returns its index. + + If no set bits are found, it returns -1. + */ + int findNextSetBit (int startIndex = 0) const throw(); + + /** Looks for the index of the next clear bit after a given starting point. + + searches from startIndex (inclusive) upwards for the first clear bit, + and returns its index. + */ + int findNextClearBit (int startIndex = 0) const throw(); + + /** Returns the index of the highest set bit in the array. + + If the array is empty, this will return -1. + */ + int getHighestBit() const throw(); + + /** Sets a range of bits to random values. */ + void fillBitsRandomly (int startBit, int numBits) throw(); + + /** Turns this value into a random number less than the given value. */ + void createRandomNumber (const BitArray& maximumValue) throw(); + + /** Converts the array to a number string. + + Specify a base such as 2 (binary), 8 (octal), 10 (decimal), 16 (hex). + */ + const String toString (const int base) const throw(); + + /** Converts a number string to an array. + + Any non-valid characters will be ignored. + + Specify a base such as 2 (binary), 8 (octal), 10 (decimal), 16 (hex). + */ + void parseString (const String& text, + const int base) throw(); + + /** Turns the array into a block of binary data. + + The data is arranged as little-endian, so the first byte of data is the low 8 bits + of the array, and so on. + + @see loadFromMemoryBlock + */ + const MemoryBlock toMemoryBlock() const throw(); + + /** Copies a block of raw data onto this array. + + The data is arranged as little-endian, so the first byte of data is the low 8 bits + of the array, and so on. + + @see toMemoryBlock + */ + void loadFromMemoryBlock (const MemoryBlock& data) throw(); + + juce_UseDebuggingNewOperator + +private: + void ensureSize (const int numVals) throw(); + unsigned int* values; + int numValues, highestBit; + bool negative; +}; + +#endif // __JUCE_BITARRAY_JUCEHEADER__ +/********* End of inlined file: juce_BitArray.h *********/ + +#endif +#ifndef __JUCE_ELEMENTCOMPARATOR_JUCEHEADER__ + +#endif +#ifndef __JUCE_MEMORYBLOCK_JUCEHEADER__ + +#endif +#ifndef __JUCE_OWNEDARRAY_JUCEHEADER__ + +#endif +#ifndef __JUCE_PROPERTYSET_JUCEHEADER__ + +/********* Start of inlined file: juce_PropertySet.h *********/ +#ifndef __JUCE_PROPERTYSET_JUCEHEADER__ +#define __JUCE_PROPERTYSET_JUCEHEADER__ + +/********* Start of inlined file: juce_StringPairArray.h *********/ +#ifndef __JUCE_STRINGPAIRARRAY_JUCEHEADER__ +#define __JUCE_STRINGPAIRARRAY_JUCEHEADER__ + +/** + A container for holding a set of strings which are keyed by another string. + + @see StringArray +*/ +class JUCE_API StringPairArray +{ +public: + + /** Creates an empty array */ + StringPairArray (const bool ignoreCaseWhenComparingKeys = true) throw(); + + /** Creates a copy of another array */ + StringPairArray (const StringPairArray& other) throw(); + + /** Destructor. */ + ~StringPairArray() throw(); + + /** Copies the contents of another string array into this one */ + const StringPairArray& operator= (const StringPairArray& other) throw(); + + /** Compares two arrays. + + Comparisons are case-sensitive. + + @returns true only if the other array contains exactly the same strings with the same keys + */ + bool operator== (const StringPairArray& other) const throw(); + + /** Compares two arrays. + + Comparisons are case-sensitive. + + @returns false if the other array contains exactly the same strings with the same keys + */ + bool operator!= (const StringPairArray& other) const throw(); + + /** Finds the value corresponding to a key string. + + If no such key is found, this will just return an empty string. To check whether + a given key actually exists (because it might actually be paired with an empty string), use + the getAllKeys() method to obtain a list. + + Obviously the reference returned shouldn't be stored for later use, as the + string it refers to may disappear when the array changes. + + @see getValue + */ + const String& operator[] (const String& key) const throw(); + + /** Finds the value corresponding to a key string. + + If no such key is found, this will just return the value provided as a default. + + @see operator[] + */ + const String getValue (const String& key, const String& defaultReturnValue) const; + + /** Returns a list of all keys in the array. */ + const StringArray& getAllKeys() const throw() { return keys; } + + /** Returns a list of all values in the array. */ + const StringArray& getAllValues() const throw() { return values; } + + /** Returns the number of strings in the array */ + inline int size() const throw() { return keys.size(); }; + + /** Adds or amends a key/value pair. + + If a value already exists with this key, its value will be overwritten, + otherwise the key/value pair will be added to the array. + */ + void set (const String& key, + const String& value) throw(); + + /** Adds the items from another array to this one. + + This is equivalent to using set() to add each of the pairs from the other array. + */ + void addArray (const StringPairArray& other); + + /** Removes all elements from the array. */ + void clear() throw(); + + /** Removes a string from the array based on its key. + + If the key isn't found, nothing will happen. + */ + void remove (const String& key) throw(); + + /** Removes a string from the array based on its index. + + If the index is out-of-range, no action will be taken. + */ + void remove (const int index) throw(); + + /** Reduces the amount of storage being used by the array. + + Arrays typically allocate slightly more storage than they need, and after + removing elements, they may have quite a lot of unused space allocated. + This method will reduce the amount of allocated storage to a minimum. + */ + void minimiseStorageOverheads() throw(); + + juce_UseDebuggingNewOperator + +private: + StringArray keys, values; + bool ignoreCase; +}; + +#endif // __JUCE_STRINGPAIRARRAY_JUCEHEADER__ +/********* End of inlined file: juce_StringPairArray.h *********/ + +/********* Start of inlined file: juce_XmlElement.h *********/ +#ifndef __JUCE_XMLELEMENT_JUCEHEADER__ +#define __JUCE_XMLELEMENT_JUCEHEADER__ + +/********* Start of inlined file: juce_OutputStream.h *********/ +#ifndef __JUCE_OUTPUTSTREAM_JUCEHEADER__ +#define __JUCE_OUTPUTSTREAM_JUCEHEADER__ + +/********* Start of inlined file: juce_InputStream.h *********/ +#ifndef __JUCE_INPUTSTREAM_JUCEHEADER__ +#define __JUCE_INPUTSTREAM_JUCEHEADER__ + +/** The base class for streams that read data. + + Input and output streams are used throughout the library - subclasses can override + some or all of the virtual functions to implement their behaviour. + + @see OutputStream, MemoryInputStream, BufferedInputStream, FileInputStream +*/ +class JUCE_API InputStream +{ +public: + /** Destructor. */ + virtual ~InputStream() {} + + /** Returns the total number of bytes available for reading in this stream. + + Note that this is the number of bytes available from the start of the + stream, not from the current position. + + If the size of the stream isn't actually known, this may return -1. + */ + virtual int64 getTotalLength() = 0; + + /** Returns true if the stream has no more data to read. */ + virtual bool isExhausted() = 0; + + /** Reads a set of bytes from the stream into a memory buffer. + + This is the only read method that subclasses actually need to implement, as the + InputStream base class implements the other read methods in terms of this one (although + it's often more efficient for subclasses to implement them directly). + + @param destBuffer the destination buffer for the data + @param maxBytesToRead the maximum number of bytes to read - make sure the + memory block passed in is big enough to contain this + many bytes. + + @returns the actual number of bytes that were read, which may be less than + maxBytesToRead if the stream is exhausted before it gets that far + */ + virtual int read (void* destBuffer, + int maxBytesToRead) = 0; + + /** Reads a byte from the stream. + + If the stream is exhausted, this will return zero. + + @see OutputStream::writeByte + */ + virtual char readByte(); + + /** Reads a boolean from the stream. + + The bool is encoded as a single byte - 1 for true, 0 for false. + + If the stream is exhausted, this will return false. + + @see OutputStream::writeBool + */ + virtual bool readBool(); + + /** Reads two bytes from the stream as a little-endian 16-bit value. + + If the next two bytes read are byte1 and byte2, this returns + (byte1 | (byte2 << 8)). + + If the stream is exhausted partway through reading the bytes, this will return zero. + + @see OutputStream::writeShort, readShortBigEndian + */ + virtual short readShort(); + + /** Reads two bytes from the stream as a little-endian 16-bit value. + + If the next two bytes read are byte1 and byte2, this returns + (byte2 | (byte1 << 8)). + + If the stream is exhausted partway through reading the bytes, this will return zero. + + @see OutputStream::writeShortBigEndian, readShort + */ + virtual short readShortBigEndian(); + + /** Reads four bytes from the stream as a little-endian 32-bit value. + + If the next four bytes are byte1 to byte4, this returns + (byte1 | (byte2 << 8) | (byte3 << 16) | (byte4 << 24)). + + If the stream is exhausted partway through reading the bytes, this will return zero. + + @see OutputStream::writeInt, readIntBigEndian + */ + virtual int readInt(); + + /** Reads four bytes from the stream as a big-endian 32-bit value. + + If the next four bytes are byte1 to byte4, this returns + (byte4 | (byte3 << 8) | (byte2 << 16) | (byte1 << 24)). + + If the stream is exhausted partway through reading the bytes, this will return zero. + + @see OutputStream::writeIntBigEndian, readInt + */ + virtual int readIntBigEndian(); + + /** Reads eight bytes from the stream as a little-endian 64-bit value. + + If the next eight bytes are byte1 to byte8, this returns + (byte1 | (byte2 << 8) | (byte3 << 16) | (byte4 << 24) | (byte5 << 32) | (byte6 << 40) | (byte7 << 48) | (byte8 << 56)). + + If the stream is exhausted partway through reading the bytes, this will return zero. + + @see OutputStream::writeInt64, readInt64BigEndian + */ + virtual int64 readInt64(); + + /** Reads eight bytes from the stream as a big-endian 64-bit value. + + If the next eight bytes are byte1 to byte8, this returns + (byte8 | (byte7 << 8) | (byte6 << 16) | (byte5 << 24) | (byte4 << 32) | (byte3 << 40) | (byte2 << 48) | (byte1 << 56)). + + If the stream is exhausted partway through reading the bytes, this will return zero. + + @see OutputStream::writeInt64BigEndian, readInt64 + */ + virtual int64 readInt64BigEndian(); + + /** Reads four bytes as a 32-bit floating point value. + + The raw 32-bit encoding of the float is read from the stream as a little-endian int. + + If the stream is exhausted partway through reading the bytes, this will return zero. + + @see OutputStream::writeFloat, readDouble + */ + virtual float readFloat(); + + /** Reads four bytes as a 32-bit floating point value. + + The raw 32-bit encoding of the float is read from the stream as a big-endian int. + + If the stream is exhausted partway through reading the bytes, this will return zero. + + @see OutputStream::writeFloatBigEndian, readDoubleBigEndian + */ + virtual float readFloatBigEndian(); + + /** Reads eight bytes as a 64-bit floating point value. + + The raw 64-bit encoding of the double is read from the stream as a little-endian int64. + + If the stream is exhausted partway through reading the bytes, this will return zero. + + @see OutputStream::writeDouble, readFloat + */ + virtual double readDouble(); + + /** Reads eight bytes as a 64-bit floating point value. + + The raw 64-bit encoding of the double is read from the stream as a big-endian int64. + + If the stream is exhausted partway through reading the bytes, this will return zero. + + @see OutputStream::writeDoubleBigEndian, readFloatBigEndian + */ + virtual double readDoubleBigEndian(); + + /** Reads an encoded 32-bit number from the stream using a space-saving compressed format. + + For small values, this is more space-efficient than using readInt() and OutputStream::writeInt() + + The format used is: number of significant bytes + up to 4 bytes in little-endian order. + + @see OutputStream::writeCompressedInt() + */ + virtual int readCompressedInt(); + + /** Reads a string from the stream, up to the next linefeed or carriage return. + + The stream is treated as 8-bit characters encoded with the system's default encoding, + and this will read up to the next "\n" or "\r\n" or end-of-stream. + + After this call, the stream's position will be left pointing to the character + following the line-feed, but the linefeeds aren't included in the string that + is returned. + */ + virtual const String readNextLine(); + + /** Reads a zero-terminated string from the stream. + + This will read characters from the stream until it hits a zero character or + end-of-stream. + + @see OutputStream::writeString, readEntireStreamAsString + */ + virtual const String readString(); + + /** Tries to read the whole stream and turn it into a string. + + This will read from the stream's current position until the end-of-stream, and + will try to make an educated guess about whether it's unicode or an 8-bit encoding. + */ + virtual const String readEntireStreamAsString(); + + /** Reads from the stream and appends the data to a MemoryBlock. + + @param destBlock the block to append the data onto + @param maxNumBytesToRead if this is a positive value, it sets a limit to the number + of bytes that will be read - if it's negative, data + will be read until the stream is exhausted. + @returns the number of bytes that were added to the memory block + */ + virtual int readIntoMemoryBlock (MemoryBlock& destBlock, + int maxNumBytesToRead = -1); + + /** Returns the offset of the next byte that will be read from the stream. + + @see setPosition + */ + virtual int64 getPosition() = 0; + + /** Tries to move the current read position of the stream. + + The position is an absolute number of bytes from the stream's start. + + Some streams might not be able to do this, in which case they should do + nothing and return false. Others might be able to manage it by resetting + themselves and skipping to the correct position, although this is + obviously a bit slow. + + @returns true if the stream manages to reposition itself correctly + @see getPosition + */ + virtual bool setPosition (int64 newPosition) = 0; + + /** Reads and discards a number of bytes from the stream. + + Some input streams might implement this efficiently, but the base + class will just keep reading data until the requisite number of bytes + have been done. + */ + virtual void skipNextBytes (int64 numBytesToSkip); + + juce_UseDebuggingNewOperator + +protected: + + InputStream() throw() {} +}; + +#endif // __JUCE_INPUTSTREAM_JUCEHEADER__ +/********* End of inlined file: juce_InputStream.h *********/ + +/** + The base class for streams that write data to some kind of destination. + + Input and output streams are used throughout the library - subclasses can override + some or all of the virtual functions to implement their behaviour. + + @see InputStream, MemoryOutputStream, FileOutputStream +*/ +class JUCE_API OutputStream +{ +public: + /** Destructor. + + Some subclasses might want to do things like call flush() during their + destructors. + */ + virtual ~OutputStream(); + + /** If the stream is using a buffer, this will ensure it gets written + out to the destination. */ + virtual void flush() = 0; + + /** Tries to move the stream's output position. + + Not all streams will be able to seek to a new position - this will return + false if it fails to work. + + @see getPosition + */ + virtual bool setPosition (int64 newPosition) = 0; + + /** Returns the stream's current position. + + @see setPosition + */ + virtual int64 getPosition() = 0; + + /** Writes a block of data to the stream. + + When creating a subclass of OutputStream, this is the only write method + that needs to be overloaded - the base class has methods for writing other + types of data which use this to do the work. + + @returns false if the write operation fails for some reason + */ + virtual bool write (const void* dataToWrite, + int howManyBytes) = 0; + + /** Writes a single byte to the stream. + + @see InputStream::readByte + */ + virtual void writeByte (char byte); + + /** Writes a boolean to the stream. + + This is encoded as a byte - either 1 or 0. + + @see InputStream::readBool + */ + virtual void writeBool (bool boolValue); + + /** Writes a 16-bit integer to the stream in a little-endian byte order. + + This will write two bytes to the stream: (value & 0xff), then (value >> 8). + + @see InputStream::readShort + */ + virtual void writeShort (short value); + + /** Writes a 16-bit integer to the stream in a big-endian byte order. + + This will write two bytes to the stream: (value >> 8), then (value & 0xff). + + @see InputStream::readShortBigEndian + */ + virtual void writeShortBigEndian (short value); + + /** Writes a 32-bit integer to the stream in a little-endian byte order. + + @see InputStream::readInt + */ + virtual void writeInt (int value); + + /** Writes a 32-bit integer to the stream in a big-endian byte order. + + @see InputStream::readIntBigEndian + */ + virtual void writeIntBigEndian (int value); + + /** Writes a 64-bit integer to the stream in a little-endian byte order. + + @see InputStream::readInt64 + */ + virtual void writeInt64 (int64 value); + + /** Writes a 64-bit integer to the stream in a big-endian byte order. + + @see InputStream::readInt64BigEndian + */ + virtual void writeInt64BigEndian (int64 value); + + /** Writes a 32-bit floating point value to the stream. + + The binary 32-bit encoding of the float is written as a little-endian int. + + @see InputStream::readFloat + */ + virtual void writeFloat (float value); + + /** Writes a 32-bit floating point value to the stream. + + The binary 32-bit encoding of the float is written as a big-endian int. + + @see InputStream::readFloatBigEndian + */ + virtual void writeFloatBigEndian (float value); + + /** Writes a 64-bit floating point value to the stream. + + The eight raw bytes of the double value are written out as a little-endian 64-bit int. + + @see InputStream::readDouble + */ + virtual void writeDouble (double value); + + /** Writes a 64-bit floating point value to the stream. + + The eight raw bytes of the double value are written out as a big-endian 64-bit int. + + @see InputStream::readDoubleBigEndian + */ + virtual void writeDoubleBigEndian (double value); + + /** Writes a condensed encoding of a 32-bit integer. + + If you're storing a lot of integers which are unlikely to have very large values, + this can save a lot of space, because values under 0xff will only take up 2 bytes, + under 0xffff only 3 bytes, etc. + + The format used is: number of significant bytes + up to 4 bytes in little-endian order. + + @see InputStream::readCompressedInt + */ + virtual void writeCompressedInt (int value); + + /** Stores a string in the stream. + + This isn't the method to use if you're trying to append text to the end of a + text-file! It's intended for storing a string for later retrieval + by InputStream::readString. + + For appending text to a file, instead use writeText, printf, or operator<< + + @see InputStream::readString, writeText, printf, operator<< + */ + virtual void writeString (const String& text); + + /** Writes a string of text to the stream. + + It can either write it as 8-bit system-encoded characters, or as unicode, and + can also add unicode header bytes (0xff, 0xfe) to indicate the endianness (this + should only be done at the start of a file). + + The method also replaces '\\n' characters in the text with '\\r\\n'. + */ + virtual void writeText (const String& text, + const bool asUnicode, + const bool writeUnicodeHeaderBytes); + + /** Writes a string of text to the stream. + + @see writeText + */ + virtual void printf (const char* format, ...); + + /** Reads data from an input stream and writes it to this stream. + + @param source the stream to read from + @param maxNumBytesToWrite the number of bytes to read from the stream (if this is + less than zero, it will keep reading until the input + is exhausted) + */ + virtual int writeFromInputStream (InputStream& source, + int maxNumBytesToWrite); + + /** Writes a number to the stream as 8-bit characters in the default system encoding. */ + virtual OutputStream& operator<< (const int number); + + /** Writes a number to the stream as 8-bit characters in the default system encoding. */ + virtual OutputStream& operator<< (const double number); + + /** Writes a character to the stream. */ + virtual OutputStream& operator<< (const char character); + + /** Writes a null-terminated string to the stream. */ + virtual OutputStream& operator<< (const char* const text); + + /** Writes a null-terminated unicode text string to the stream, converting it + to 8-bit characters in the default system encoding. */ + virtual OutputStream& operator<< (const juce_wchar* const text); + + /** Writes a string to the stream as 8-bit characters in the default system encoding. */ + virtual OutputStream& operator<< (const String& text); + + juce_UseDebuggingNewOperator + +protected: + + OutputStream() throw(); +}; + +#endif // __JUCE_OUTPUTSTREAM_JUCEHEADER__ +/********* End of inlined file: juce_OutputStream.h *********/ + +/** A handy macro to make it easy to iterate all the child elements in an XmlElement. + + The parentXmlElement should be a reference to the parent XML, and the childElementVariableName + will be the name of a pointer to each child element. + + E.g. @code + XmlElement* myParentXml = createSomeKindOfXmlDocument(); + + forEachXmlChildElement (*myParentXml, child) + { + if (child->hasTagName ("FOO")) + doSomethingWithXmlElement (child); + } + + @endcode + + @see forEachXmlChildElementWithTagName +*/ +#define forEachXmlChildElement(parentXmlElement, childElementVariableName) \ +\ + for (XmlElement* childElementVariableName = (parentXmlElement).getFirstChildElement(); \ + childElementVariableName != 0; \ + childElementVariableName = childElementVariableName->getNextElement()) + +/** A macro that makes it easy to iterate all the child elements of an XmlElement + which have a specified tag. + + This does the same job as the forEachXmlChildElement macro, but only for those + elements that have a particular tag name. + + The parentXmlElement should be a reference to the parent XML, and the childElementVariableName + will be the name of a pointer to each child element. The requiredTagName is the + tag name to match. + + E.g. @code + XmlElement* myParentXml = createSomeKindOfXmlDocument(); + + forEachXmlChildElementWithTagName (*myParentXml, child, T("MYTAG")) + { + // the child object is now guaranteed to be a element.. + doSomethingWithMYTAGElement (child); + } + + @endcode + + @see forEachXmlChildElement +*/ +#define forEachXmlChildElementWithTagName(parentXmlElement, childElementVariableName, requiredTagName) \ +\ + for (XmlElement* childElementVariableName = (parentXmlElement).getChildByName (requiredTagName); \ + childElementVariableName != 0; \ + childElementVariableName = childElementVariableName->getNextElementWithTagName (requiredTagName)) + +/** Used to build a tree of elements representing an XML document. + + An XML document can be parsed into a tree of XmlElements, each of which + represents an XML tag structure, and which may itself contain other + nested elements. + + An XmlElement can also be converted back into a text document, and has + lots of useful methods for manipulating its attributes and sub-elements, + so XmlElements can actually be used as a handy general-purpose data + structure. + + Here's an example of parsing some elements: @code + // check we're looking at the right kind of document.. + if (myElement->hasTagName ("ANIMALS")) + { + // now we'll iterate its sub-elements looking for 'giraffe' elements.. + forEachXmlChildElement (*myElement, e) + { + if (e->hasTagName ("GIRAFFE")) + { + // found a giraffe, so use some of its attributes.. + + String giraffeName = e->getStringAttribute ("name"); + int giraffeAge = e->getIntAttribute ("age"); + bool isFriendly = e->getBoolAttribute ("friendly"); + } + } + } + @endcode + + And here's an example of how to create an XML document from scratch: @code + // create an outer node called "ANIMALS" + XmlElement animalsList ("ANIMALS"); + + for (int i = 0; i < numAnimals; ++i) + { + // create an inner element.. + XmlElement* giraffe = new XmlElement ("GIRAFFE"); + + giraffe->setAttribute ("name", "nigel"); + giraffe->setAttribute ("age", 10); + giraffe->setAttribute ("friendly", true); + + // ..and add our new element to the parent node + animalsList.addChildElement (giraffe); + } + + // now we can turn the whole thing into a text document.. + String myXmlDoc = animalsList.createDocument (String::empty); + @endcode + + @see XmlDocument +*/ +class JUCE_API XmlElement +{ +public: + + /** Creates an XmlElement with this tag name. */ + XmlElement (const String& tagName) throw(); + + /** Creates a (deep) copy of another element. */ + XmlElement (const XmlElement& other) throw(); + + /** Creates a (deep) copy of another element. */ + const XmlElement& operator= (const XmlElement& other) throw(); + + /** Deleting an XmlElement will also delete all its child elements. */ + ~XmlElement() throw(); + + /** Compares two XmlElements to see if they contain the same text and attiributes. + + The elements are only considered equivalent if they contain the same attiributes + with the same values, and have the same sub-nodes. + + @param other the other element to compare to + @param ignoreOrderOfAttributes if true, this means that two elements with the + same attributes in a different order will be + considered the same; if false, the attributes must + be in the same order as well + */ + bool isEquivalentTo (const XmlElement* const other, + const bool ignoreOrderOfAttributes) const throw(); + + /** Returns an XML text document that represents this element. + + The string returned can be parsed to recreate the same XmlElement that + was used to create it. + + @param dtdToUse the DTD to add to the document + @param allOnOneLine if true, this means that the document will not contain any + linefeeds, so it'll be smaller but not very easy to read. + @param includeXmlHeader whether to add the ", this would return + "MOOSE". + + @see hasTagName + */ + inline const String& getTagName() const throw() { return tagName; } + + /** Tests whether this element has a particular tag name. + + @param possibleTagName the tag name you're comparing it with + + @see getTagName + */ + bool hasTagName (const tchar* const possibleTagName) const throw(); + + /** Returns the number of XML attributes this element contains. + + E.g. for an element such as \, this would + return 2. + */ + int getNumAttributes() const throw(); + + /** Returns the name of one of the elements attributes. + + E.g. for an element such as \, then + getAttributeName(1) would return "antlers". + + @see getAttributeValue, getStringAttribute + */ + const String& getAttributeName (const int attributeIndex) const throw(); + + /** Returns the value of one of the elements attributes. + + E.g. for an element such as \, then + getAttributeName(1) would return "2". + + @see getAttributeName, getStringAttribute + */ + const String& getAttributeValue (const int attributeIndex) const throw(); + + // Attribute-handling methods.. + + /** Checks whether the element contains an attribute with a certain name. */ + bool hasAttribute (const tchar* const attributeName) const throw(); + + /** Returns the value of a named attribute. + + @param attributeName the name of the attribute to look up + @param defaultReturnValue a value to return if the element doesn't have an attribute + with this name + */ + const String getStringAttribute (const tchar* const attributeName, + const tchar* const defaultReturnValue = 0) const throw(); + + /** Compares the value of a named attribute with a value passed-in. + + @param attributeName the name of the attribute to look up + @param stringToCompareAgainst the value to compare it with + @param ignoreCase whether the comparison should be case-insensitive + @returns true if the value of the attribute is the same as the string passed-in; + false if it's different (or if no such attribute exists) + */ + bool compareAttribute (const tchar* const attributeName, + const tchar* const stringToCompareAgainst, + const bool ignoreCase = false) const throw(); + + /** Returns the value of a named attribute as an integer. + + This will try to find the attribute and convert it to an integer (using + the String::getIntValue() method). + + @param attributeName the name of the attribute to look up + @param defaultReturnValue a value to return if the element doesn't have an attribute + with this name + @see setAttribute (const tchar* const, int) + */ + int getIntAttribute (const tchar* const attributeName, + const int defaultReturnValue = 0) const throw(); + + /** Returns the value of a named attribute as floating-point. + + This will try to find the attribute and convert it to an integer (using + the String::getDoubleValue() method). + + @param attributeName the name of the attribute to look up + @param defaultReturnValue a value to return if the element doesn't have an attribute + with this name + @see setAttribute (const tchar* const, double) + */ + double getDoubleAttribute (const tchar* const attributeName, + const double defaultReturnValue = 0.0) const throw(); + + /** Returns the value of a named attribute as a boolean. + + This will try to find the attribute and interpret it as a boolean. To do this, + it'll return true if the value is "1", "true", "y", etc, or false for other + values. + + @param attributeName the name of the attribute to look up + @param defaultReturnValue a value to return if the element doesn't have an attribute + with this name + */ + bool getBoolAttribute (const tchar* const attributeName, + const bool defaultReturnValue = false) const throw(); + + /** Adds a named attribute to the element. + + If the element already contains an attribute with this name, it's value will + be updated to the new value. If there's no such attribute yet, a new one will + be added. + + Note that there are other setAttribute() methods that take integers, + doubles, etc. to make it easy to store numbers. + + @param attributeName the name of the attribute to set + @param newValue the value to set it to + @see removeAttribute + */ + void setAttribute (const tchar* const attributeName, + const String& newValue) throw(); + + /** Adds a named attribute to the element. + + If the element already contains an attribute with this name, it's value will + be updated to the new value. If there's no such attribute yet, a new one will + be added. + + Note that there are other setAttribute() methods that take integers, + doubles, etc. to make it easy to store numbers. + + @param attributeName the name of the attribute to set + @param newValue the value to set it to + */ + void setAttribute (const tchar* const attributeName, + const tchar* const newValue) throw(); + + /** Adds a named attribute to the element, setting it to an integer value. + + If the element already contains an attribute with this name, it's value will + be updated to the new value. If there's no such attribute yet, a new one will + be added. + + Note that there are other setAttribute() methods that take integers, + doubles, etc. to make it easy to store numbers. + + @param attributeName the name of the attribute to set + @param newValue the value to set it to + */ + void setAttribute (const tchar* const attributeName, + const int newValue) throw(); + + /** Adds a named attribute to the element, setting it to a floating-point value. + + If the element already contains an attribute with this name, it's value will + be updated to the new value. If there's no such attribute yet, a new one will + be added. + + Note that there are other setAttribute() methods that take integers, + doubles, etc. to make it easy to store numbers. + + @param attributeName the name of the attribute to set + @param newValue the value to set it to + */ + void setAttribute (const tchar* const attributeName, + const double newValue) throw(); + + /** Removes a named attribute from the element. + + @param attributeName the name of the attribute to remove + @see removeAllAttributes + */ + void removeAttribute (const tchar* const attributeName) throw(); + + /** Removes all attributes from this element. + */ + void removeAllAttributes() throw(); + + // Child element methods.. + + /** Returns the first of this element's sub-elements. + + see getNextElement() for an example of how to iterate the sub-elements. + + @see forEachXmlChildElement + */ + XmlElement* getFirstChildElement() const throw() { return firstChildElement; } + + /** Returns the next of this element's siblings. + + This can be used for iterating an element's sub-elements, e.g. + @code + XmlElement* child = myXmlDocument->getFirstChildElement(); + + while (child != 0) + { + ...do stuff with this child.. + + child = child->getNextElement(); + } + @endcode + + Note that when iterating the child elements, some of them might be + text elements as well as XML tags - use isTextElement() to work this + out. + + Also, it's much easier and neater to use this method indirectly via the + forEachXmlChildElement macro. + + @returns the sibling element that follows this one, or zero if this is the last + element in its parent + + @see getNextElement, isTextElement, forEachXmlChildElement + */ + inline XmlElement* getNextElement() const throw() { return nextElement; } + + /** Returns the next of this element's siblings which has the specified tag + name. + + This is like getNextElement(), but will scan through the list until it + finds an element with the given tag name. + + @see getNextElement, forEachXmlChildElementWithTagName + */ + XmlElement* getNextElementWithTagName (const tchar* const requiredTagName) const; + + /** Returns the number of sub-elements in this element. + + @see getChildElement + */ + int getNumChildElements() const throw(); + + /** Returns the sub-element at a certain index. + + It's not very efficient to iterate the sub-elements by index - see + getNextElement() for an example of how best to iterate. + + @returns the n'th child of this element, or 0 if the index is out-of-range + @see getNextElement, isTextElement, getChildByName + */ + XmlElement* getChildElement (const int index) const throw(); + + /** Returns the first sub-element with a given tag-name. + + @param tagNameToLookFor the tag name of the element you want to find + @returns the first element with this tag name, or 0 if none is found + @see getNextElement, isTextElement, getChildElement + */ + XmlElement* getChildByName (const tchar* const tagNameToLookFor) const throw(); + + /** Appends an element to this element's list of children. + + Child elements are deleted automatically when their parent is deleted, so + make sure the object that you pass in will not be deleted by anything else, + and make sure it's not already the child of another element. + + @see getFirstChildElement, getNextElement, getNumChildElements, + getChildElement, removeChildElement + */ + void addChildElement (XmlElement* const newChildElement) throw(); + + /** Inserts an element into this element's list of children. + + Child elements are deleted automatically when their parent is deleted, so + make sure the object that you pass in will not be deleted by anything else, + and make sure it's not already the child of another element. + + @param newChildNode the element to add + @param indexToInsertAt the index at which to insert the new element - if this is + below zero, it will be added to the end of the list + @see addChildElement, insertChildElement + */ + void insertChildElement (XmlElement* const newChildNode, + int indexToInsertAt) throw(); + + /** Replaces one of this element's children with another node. + + If the current element passed-in isn't actually a child of this element, + this will return false and the new one won't be added. Otherwise, the + existing element will be deleted, replaced with the new one, and it + will return true. + */ + bool replaceChildElement (XmlElement* const currentChildElement, + XmlElement* const newChildNode) throw(); + + /** Removes a child element. + + @param childToRemove the child to look for and remove + @param shouldDeleteTheChild if true, the child will be deleted, if false it'll + just remove it + */ + void removeChildElement (XmlElement* const childToRemove, + const bool shouldDeleteTheChild) throw(); + + /** Deletes all the child elements in the element. + + @see removeChildElement, deleteAllChildElementsWithTagName + */ + void deleteAllChildElements() throw(); + + /** Deletes all the child elements with a given tag name. + + @see removeChildElement + */ + void deleteAllChildElementsWithTagName (const tchar* const tagName) throw(); + + /** Returns true if the given element is a child of this one. */ + bool containsChildElement (const XmlElement* const possibleChild) const throw(); + + /** Recursively searches all sub-elements to find one that contains the specified + child element. + */ + XmlElement* findParentElementOf (const XmlElement* const elementToLookFor) throw(); + + /** Sorts the child elements using a comparator. + + This will use a comparator object to sort the elements into order. The object + passed must have a method of the form: + @code + int compareElements (const XmlElement* first, const XmlElement* second); + @endcode + + ..and this method must return: + - a value of < 0 if the first comes before the second + - a value of 0 if the two objects are equivalent + - a value of > 0 if the second comes before the first + + To improve performance, the compareElements() method can be declared as static or const. + + @param comparator the comparator to use for comparing elements. + @param retainOrderOfEquivalentItems if this is true, then items + which the comparator says are equivalent will be + kept in the order in which they currently appear + in the array. This is slower to perform, but may + be important in some cases. If it's false, a faster + algorithm is used, but equivalent elements may be + rearranged. + */ + template + void sortChildElements (ElementComparator& comparator, + const bool retainOrderOfEquivalentItems = false) throw() + { + const int num = getNumChildElements(); + + if (num > 1) + { + XmlElement** const elems = getChildElementsAsArray (num); + sortArray (comparator, elems, 0, num - 1, retainOrderOfEquivalentItems); + reorderChildElements (elems, num); + delete[] elems; + } + } + + /** Returns true if this element is a section of text. + + Elements can either be an XML tag element or a secton of text, so this + is used to find out what kind of element this one is. + + @see getAllText, addTextElement, deleteAllTextElements + */ + bool isTextElement() const throw(); + + /** Returns the text for a text element. + + Note that if you have an element like this: + + @codehello@endcode + + then calling getText on the "xyz" element won't return "hello", because that is + actually stored in a special text sub-element inside the xyz element. To get the + "hello" string, you could either call getText on the (unnamed) sub-element, or + use getAllSubText() to do this automatically. + + @see isTextElement, getAllSubText, getChildElementAllSubText + */ + const String getText() const throw(); + + /** Sets the text in a text element. + + Note that this is only a valid call if this element is a text element. If it's + not, then no action will be performed. + */ + void setText (const String& newText) throw(); + + /** Returns all the text from this element's child nodes. + + This iterates all the child elements and when it finds text elements, + it concatenates their text into a big string which it returns. + + E.g. @code hello there @endcode + if you called getAllSubText on the "xyz" element, it'd return "hello there". + + @see isTextElement, getChildElementAllSubText, getText, addTextElement + */ + const String getAllSubText() const throw(); + + /** Returns all the sub-text of a named child element. + + If there is a child element with the given tag name, this will return + all of its sub-text (by calling getAllSubText() on it). If there is + no such child element, this will return the default string passed-in. + + @see getAllSubText + */ + const String getChildElementAllSubText (const tchar* const childTagName, + const String& defaultReturnValue) const throw(); + + /** Appends a section of text to this element. + + @see isTextElement, getText, getAllSubText + */ + void addTextElement (const String& text) throw(); + + /** Removes all the text elements from this element. + + @see isTextElement, getText, getAllSubText, addTextElement + */ + void deleteAllTextElements() throw(); + + /** Creates a text element that can be added to a parent element. + */ + static XmlElement* createTextElement (const String& text) throw(); + + juce_UseDebuggingNewOperator + +private: + friend class XmlDocument; + + String tagName; + XmlElement* firstChildElement; + XmlElement* nextElement; + + struct XmlAttributeNode + { + XmlAttributeNode (const XmlAttributeNode& other) throw(); + XmlAttributeNode (const String& name, const String& value) throw(); + + String name, value; + XmlAttributeNode* next; + }; + + XmlAttributeNode* attributes; + + XmlElement (int) throw(); // for internal use + XmlElement (const tchar* const tagNameText, const int nameLen) throw(); + + void copyChildrenAndAttributesFrom (const XmlElement& other) throw(); + + void writeElementAsText (OutputStream& out, const int indentationLevel) const throw(); + + XmlElement** getChildElementsAsArray (const int) const throw(); + void reorderChildElements (XmlElement** const, const int) throw(); +}; + +#endif // __JUCE_XMLELEMENT_JUCEHEADER__ +/********* End of inlined file: juce_XmlElement.h *********/ + +/** + A set of named property values, which can be strings, integers, floating point, etc. + + Effectively, this just wraps a StringPairArray in an interface that makes it easier + to load and save types other than strings. + + See the PropertiesFile class for a subclass of this, which automatically broadcasts change + messages and saves/loads the list from a file. +*/ +class JUCE_API PropertySet +{ +public: + + /** Creates an empty PropertySet. + + @param ignoreCaseOfKeyNames if true, the names of properties are compared in a + case-insensitive way + */ + PropertySet (const bool ignoreCaseOfKeyNames = false) throw(); + + /** Creates a copy of another PropertySet. + */ + PropertySet (const PropertySet& other) throw(); + + /** Copies another PropertySet over this one. + */ + const PropertySet& operator= (const PropertySet& other) throw(); + + /** Destructor. */ + virtual ~PropertySet(); + + /** Returns one of the properties as a string. + + If the value isn't found in this set, then this will look for it in a fallback + property set (if you've specified one with the setFallbackPropertySet() method), + and if it can't find one there, it'll return the default value passed-in. + + @param keyName the name of the property to retrieve + @param defaultReturnValue a value to return if the named property doesn't actually exist + */ + const String getValue (const String& keyName, + const String& defaultReturnValue = String::empty) const throw(); + + /** Returns one of the properties as an integer. + + If the value isn't found in this set, then this will look for it in a fallback + property set (if you've specified one with the setFallbackPropertySet() method), + and if it can't find one there, it'll return the default value passed-in. + + @param keyName the name of the property to retrieve + @param defaultReturnValue a value to return if the named property doesn't actually exist + */ + int getIntValue (const String& keyName, + const int defaultReturnValue = 0) const throw(); + + /** Returns one of the properties as an double. + + If the value isn't found in this set, then this will look for it in a fallback + property set (if you've specified one with the setFallbackPropertySet() method), + and if it can't find one there, it'll return the default value passed-in. + + @param keyName the name of the property to retrieve + @param defaultReturnValue a value to return if the named property doesn't actually exist + */ + double getDoubleValue (const String& keyName, + const double defaultReturnValue = 0.0) const throw(); + + /** Returns one of the properties as an boolean. + + The result will be true if the string found for this key name can be parsed as a non-zero + integer. + + If the value isn't found in this set, then this will look for it in a fallback + property set (if you've specified one with the setFallbackPropertySet() method), + and if it can't find one there, it'll return the default value passed-in. + + @param keyName the name of the property to retrieve + @param defaultReturnValue a value to return if the named property doesn't actually exist + */ + bool getBoolValue (const String& keyName, + const bool defaultReturnValue = false) const throw(); + + /** Returns one of the properties as an XML element. + + The result will a new XMLElement object that the caller must delete. If may return 0 if the + key isn't found, or if the entry contains an string that isn't valid XML. + + If the value isn't found in this set, then this will look for it in a fallback + property set (if you've specified one with the setFallbackPropertySet() method), + and if it can't find one there, it'll return the default value passed-in. + + @param keyName the name of the property to retrieve + */ + XmlElement* getXmlValue (const String& keyName) const; + + /** Sets a named property as a string. + + @param keyName the name of the property to set. (This mustn't be an empty string) + @param value the new value to set it to + */ + void setValue (const String& keyName, const String& value) throw(); + + /** Sets a named property as a string. + + @param keyName the name of the property to set. (This mustn't be an empty string) + @param value the new value to set it to + */ + void setValue (const String& keyName, const tchar* const value) throw(); + + /** Sets a named property to an integer. + + @param keyName the name of the property to set. (This mustn't be an empty string) + @param value the new value to set it to + */ + void setValue (const String& keyName, const int value) throw(); + + /** Sets a named property to a double. + + @param keyName the name of the property to set. (This mustn't be an empty string) + @param value the new value to set it to + */ + void setValue (const String& keyName, const double value) throw(); + + /** Sets a named property to a boolean. + + @param keyName the name of the property to set. (This mustn't be an empty string) + @param value the new value to set it to + */ + void setValue (const String& keyName, const bool value) throw(); + + /** Sets a named property to an XML element. + + @param keyName the name of the property to set. (This mustn't be an empty string) + @param xml the new element to set it to. If this is zero, the value will be set to + an empty string + @see getXmlValue + */ + void setValue (const String& keyName, const XmlElement* const xml); + + /** Deletes a property. + + @param keyName the name of the property to delete. (This mustn't be an empty string) + */ + void removeValue (const String& keyName) throw(); + + /** Returns true if the properies include the given key. */ + bool containsKey (const String& keyName) const throw(); + + /** Removes all values. */ + void clear(); + + /** Returns the keys/value pair array containing all the properties. */ + StringPairArray& getAllProperties() throw() { return properties; } + + /** Returns the lock used when reading or writing to this set */ + const CriticalSection& getLock() const throw() { return lock; } + + /** Returns an XML element which encapsulates all the items in this property set. + + The string parameter is the tag name that should be used for the node. + + @see restoreFromXml + */ + XmlElement* createXml (const String& nodeName) const throw(); + + /** Reloads a set of properties that were previously stored as XML. + + The node passed in must have been created by the createXml() method. + + @see createXml + */ + void restoreFromXml (const XmlElement& xml) throw(); + + /** Sets up a second PopertySet that will be used to look up any values that aren't + set in this one. + + If you set this up to be a pointer to a second property set, then whenever one + of the getValue() methods fails to find an entry in this set, it will look up that + value in the fallback set, and if it finds it, it will return that. + + Make sure that you don't delete the fallback set while it's still being used by + another set! To remove the fallback set, just call this method with a null pointer. + + @see getFallbackPropertySet + */ + void setFallbackPropertySet (PropertySet* fallbackProperties) throw(); + + /** Returns the fallback property set. + @see setFallbackPropertySet + */ + PropertySet* getFallbackPropertySet() const throw() { return fallbackProperties; } + + juce_UseDebuggingNewOperator + +protected: + + /** Subclasses can override this to be told when one of the properies has been changed. + */ + virtual void propertyChanged(); + +private: + + StringPairArray properties; + PropertySet* fallbackProperties; + CriticalSection lock; + bool ignoreCaseOfKeys; +}; + +#endif // __JUCE_PROPERTYSET_JUCEHEADER__ +/********* End of inlined file: juce_PropertySet.h *********/ + +#endif +#ifndef __JUCE_REFERENCECOUNTEDARRAY_JUCEHEADER__ + +/********* Start of inlined file: juce_ReferenceCountedArray.h *********/ +#ifndef __JUCE_REFERENCECOUNTEDARRAY_JUCEHEADER__ +#define __JUCE_REFERENCECOUNTEDARRAY_JUCEHEADER__ + +/********* Start of inlined file: juce_ReferenceCountedObject.h *********/ +#ifndef __JUCE_REFERENCECOUNTEDOBJECT_JUCEHEADER__ +#define __JUCE_REFERENCECOUNTEDOBJECT_JUCEHEADER__ + +/** + Adds reference-counting to an object. + + To add reference-counting to a class, derive it from this class, and + use the ReferenceCountedObjectPtr class to point to it. + + e.g. @code + class MyClass : public ReferenceCountedObject + { + void foo(); + + // This is a neat way of declaring a typedef for a pointer class, + // rather than typing out the full templated name each time.. + typedef ReferenceCountedObjectPtr Ptr; + }; + + MyClass::Ptr p = new MyClass(); + MyClass::Ptr p2 = p; + p = 0; + p2->foo(); + @endcode + + Once a new ReferenceCountedObject has been assigned to a pointer, be + careful not to delete the object manually. + + @see ReferenceCountedObjectPtr, ReferenceCountedArray +*/ +class JUCE_API ReferenceCountedObject +{ +public: + + /** Increments the object's reference count. + + This is done automatically by the smart pointer, but is public just + in case it's needed for nefarious purposes. + */ + inline void incReferenceCount() throw() + { + atomicIncrement (refCounts); + + jassert (refCounts > 0); + } + + /** Decreases the object's reference count. + + If the count gets to zero, the object will be deleted. + */ + inline void decReferenceCount() throw() + { + jassert (refCounts > 0); + + if (atomicDecrementAndReturn (refCounts) == 0) + delete this; + } + + /** Returns the object's current reference count. */ + inline int getReferenceCount() const throw() + { + return refCounts; + } + +protected: + + /** Creates the reference-counted object (with an initial ref count of zero). */ + ReferenceCountedObject() + : refCounts (0) + { + } + + /** Destructor. */ + virtual ~ReferenceCountedObject() + { + // it's dangerous to delete an object that's still referenced by something else! + jassert (refCounts == 0); + } + +private: + + int refCounts; +}; + +/** + Used to point to an object of type ReferenceCountedObject. + + It's wise to use a typedef instead of typing out the templated name + each time - e.g. + + typedef ReferenceCountedObjectPtr MyClassPtr; + + @see ReferenceCountedObject, ReferenceCountedObjectArray +*/ +template +class ReferenceCountedObjectPtr +{ +public: + + /** Creates a pointer to a null object. */ + inline ReferenceCountedObjectPtr() throw() + : referencedObject (0) + { + } + + /** Creates a pointer to an object. + + This will increment the object's reference-count if it is non-null. + */ + inline ReferenceCountedObjectPtr (ReferenceCountedObjectClass* const refCountedObject) throw() + : referencedObject (refCountedObject) + { + if (refCountedObject != 0) + refCountedObject->incReferenceCount(); + } + + /** Copies another pointer. + + This will increment the object's reference-count (if it is non-null). + */ + inline ReferenceCountedObjectPtr (const ReferenceCountedObjectPtr& other) throw() + : referencedObject (other.referencedObject) + { + if (referencedObject != 0) + referencedObject->incReferenceCount(); + } + + /** Changes this pointer to point at a different object. + + The reference count of the old object is decremented, and it might be + deleted if it hits zero. The new object's count is incremented. + */ + const ReferenceCountedObjectPtr& operator= (const ReferenceCountedObjectPtr& other) + { + ReferenceCountedObjectClass* const newObject = other.referencedObject; + + if (newObject != referencedObject) + { + if (newObject != 0) + newObject->incReferenceCount(); + + ReferenceCountedObjectClass* const oldObject = referencedObject; + referencedObject = newObject; + + if (oldObject != 0) + oldObject->decReferenceCount(); + } + + return *this; + } + + /** Changes this pointer to point at a different object. + + The reference count of the old object is decremented, and it might be + deleted if it hits zero. The new object's count is incremented. + */ + const ReferenceCountedObjectPtr& operator= (ReferenceCountedObjectClass* const newObject) + { + if (referencedObject != newObject) + { + if (newObject != 0) + newObject->incReferenceCount(); + + ReferenceCountedObjectClass* const oldObject = referencedObject; + referencedObject = newObject; + + if (oldObject != 0) + oldObject->decReferenceCount(); + } + + return *this; + } + + /** Destructor. + + This will decrement the object's reference-count, and may delete it if it + gets to zero. + */ + inline ~ReferenceCountedObjectPtr() + { + if (referencedObject != 0) + referencedObject->decReferenceCount(); + } + + /** Returns the object that this pointer references. + + The pointer returned may be zero, of course. + */ + inline operator ReferenceCountedObjectClass*() const throw() + { + return referencedObject; + } + + /** Returns true if this pointer refers to the given object. */ + inline bool operator== (ReferenceCountedObjectClass* const object) const throw() + { + return referencedObject == object; + } + + /** Returns true if this pointer doesn't refer to the given object. */ + inline bool operator!= (ReferenceCountedObjectClass* const object) const throw() + { + return referencedObject != object; + } + + // the -> operator is called on the referenced object + inline ReferenceCountedObjectClass* operator->() const throw() + { + return referencedObject; + } + +private: + + ReferenceCountedObjectClass* referencedObject; +}; + +#endif // __JUCE_REFERENCECOUNTEDOBJECT_JUCEHEADER__ +/********* End of inlined file: juce_ReferenceCountedObject.h *********/ + +/** + Holds a list of objects derived from ReferenceCountedObject. + + A ReferenceCountedArray holds objects derived from ReferenceCountedObject, + and takes care of incrementing and decrementing their ref counts when they + are added and removed from the array. + + To make all the array's methods thread-safe, pass in "CriticalSection" as the templated + TypeOfCriticalSectionToUse parameter, instead of the default DummyCriticalSection. + + @see Array, OwnedArray, StringArray +*/ +template +class ReferenceCountedArray : private ArrayAllocationBase +{ +public: + + /** Creates an empty array. + + @param granularity this is the size of increment by which the internal storage + used by the array will grow. Only change it from the default if you know the + array is going to be very big and needs to be able to grow efficiently. + + @see ReferenceCountedObject, ArrayAllocationBase, Array, OwnedArray + */ + ReferenceCountedArray (const int granularity = juceDefaultArrayGranularity) throw() + : ArrayAllocationBase (granularity), + numUsed (0) + { + } + + /** Creates a copy of another array */ + ReferenceCountedArray (const ReferenceCountedArray& other) throw() + : ArrayAllocationBase (other.granularity), + numUsed (other.numUsed) + { + other.lockArray(); + this->setAllocatedSize (numUsed); + memcpy (this->elements, other.elements, numUsed * sizeof (ObjectClass*)); + + for (int i = numUsed; --i >= 0;) + if (this->elements[i] != 0) + this->elements[i]->incReferenceCount(); + + other.unlockArray(); + } + + /** Copies another array into this one. + + Any existing objects in this array will first be released. + */ + const ReferenceCountedArray& operator= (const ReferenceCountedArray& other) throw() + { + if (this != &other) + { + other.lockArray(); + lock.enter(); + + clear(); + + this->granularity = other.granularity; + this->ensureAllocatedSize (other.numUsed); + numUsed = other.numUsed; + memcpy (this->elements, other.elements, numUsed * sizeof (ObjectClass*)); + minimiseStorageOverheads(); + + for (int i = numUsed; --i >= 0;) + if (this->elements[i] != 0) + this->elements[i]->incReferenceCount(); + + lock.exit(); + other.unlockArray(); + } + + return *this; + } + + /** Destructor. + + Any objects in the array will be released, and may be deleted if not referenced from elsewhere. + */ + ~ReferenceCountedArray() + { + clear(); + } + + /** Removes all objects from the array. + + Any objects in the array that are not referenced from elsewhere will be deleted. + */ + void clear() + { + lock.enter(); + + while (numUsed > 0) + if (this->elements [--numUsed] != 0) + this->elements [numUsed]->decReferenceCount(); + + jassert (numUsed == 0); + this->setAllocatedSize (0); + + lock.exit(); + } + + /** Returns the current number of objects in the array. */ + inline int size() const throw() + { + return numUsed; + } + + /** Returns a pointer to the object at this index in the array. + + If the index is out-of-range, this will return a null pointer, (and + it could be null anyway, because it's ok for the array to hold null + pointers as well as objects). + + @see getUnchecked + */ + inline ObjectClass* operator[] (const int index) const throw() + { + lock.enter(); + ObjectClass* const result = (((unsigned int) index) < (unsigned int) numUsed) + ? this->elements [index] + : (ObjectClass*) 0; + lock.exit(); + return result; + } + + /** Returns a pointer to the object at this index in the array, without checking whether the index is in-range. + + This is a faster and less safe version of operator[] which doesn't check the index passed in, so + it can be used when you're sure the index if always going to be legal. + */ + inline ObjectClass* getUnchecked (const int index) const throw() + { + lock.enter(); + jassert (((unsigned int) index) < (unsigned int) numUsed); + ObjectClass* const result = this->elements [index]; + lock.exit(); + return result; + } + + /** Returns a pointer to the first object in the array. + + This will return a null pointer if the array's empty. + @see getLast + */ + inline ObjectClass* getFirst() const throw() + { + lock.enter(); + ObjectClass* const result = (numUsed > 0) ? this->elements [0] + : (ObjectClass*) 0; + lock.exit(); + + return result; + } + + /** Returns a pointer to the last object in the array. + + This will return a null pointer if the array's empty. + @see getFirst + */ + inline ObjectClass* getLast() const throw() + { + lock.enter(); + ObjectClass* const result = (numUsed > 0) ? this->elements [numUsed - 1] + : (ObjectClass*) 0; + lock.exit(); + + return result; + } + + /** Finds the index of the first occurrence of an object in the array. + + @param objectToLookFor the object to look for + @returns the index at which the object was found, or -1 if it's not found + */ + int indexOf (const ObjectClass* const objectToLookFor) const throw() + { + int result = -1; + + lock.enter(); + ObjectClass** e = this->elements; + + for (int i = numUsed; --i >= 0;) + { + if (objectToLookFor == *e) + { + result = (int) (e - this->elements); + break; + } + + ++e; + } + + lock.exit(); + return result; + } + + /** Returns true if the array contains a specified object. + + @param objectToLookFor the object to look for + @returns true if the object is in the array + */ + bool contains (const ObjectClass* const objectToLookFor) const throw() + { + lock.enter(); + ObjectClass** e = this->elements; + + for (int i = numUsed; --i >= 0;) + { + if (objectToLookFor == *e) + { + lock.exit(); + return true; + } + + ++e; + } + + lock.exit(); + return false; + } + + /** Appends a new object to the end of the array. + + This will increase the new object's reference count. + + @param newObject the new object to add to the array + @see set, insert, addIfNotAlreadyThere, addSorted, addArray + */ + void add (ObjectClass* const newObject) throw() + { + lock.enter(); + this->ensureAllocatedSize (numUsed + 1); + this->elements [numUsed++] = newObject; + + if (newObject != 0) + newObject->incReferenceCount(); + + lock.exit(); + } + + /** Inserts a new object into the array at the given index. + + If the index is less than 0 or greater than the size of the array, the + element will be added to the end of the array. + Otherwise, it will be inserted into the array, moving all the later elements + along to make room. + + This will increase the new object's reference count. + + @param indexToInsertAt the index at which the new element should be inserted + @param newObject the new object to add to the array + @see add, addSorted, addIfNotAlreadyThere, set + */ + void insert (int indexToInsertAt, + ObjectClass* const newObject) throw() + { + if (indexToInsertAt >= 0) + { + lock.enter(); + + if (indexToInsertAt > numUsed) + indexToInsertAt = numUsed; + + this->ensureAllocatedSize (numUsed + 1); + + ObjectClass** const e = this->elements + indexToInsertAt; + const int numToMove = numUsed - indexToInsertAt; + + if (numToMove > 0) + memmove (e + 1, e, numToMove * sizeof (ObjectClass*)); + + *e = newObject; + + if (newObject != 0) + newObject->incReferenceCount(); + + ++numUsed; + lock.exit(); + } + else + { + add (newObject); + } + } + + /** Appends a new object at the end of the array as long as the array doesn't + already contain it. + + If the array already contains a matching object, nothing will be done. + + @param newObject the new object to add to the array + */ + void addIfNotAlreadyThere (ObjectClass* const newObject) throw() + { + lock.enter(); + + if (! contains (newObject)) + add (newObject); + + lock.exit(); + } + + /** Replaces an object in the array with a different one. + + If the index is less than zero, this method does nothing. + If the index is beyond the end of the array, the new object is added to the end of the array. + + The object being added has its reference count increased, and if it's replacing + another object, then that one has its reference count decreased, and may be deleted. + + @param indexToChange the index whose value you want to change + @param newObject the new value to set for this index. + @see add, insert, remove + */ + void set (const int indexToChange, + ObjectClass* const newObject) + { + if (indexToChange >= 0) + { + lock.enter(); + + if (newObject != 0) + newObject->incReferenceCount(); + + if (indexToChange < numUsed) + { + if (this->elements [indexToChange] != 0) + this->elements [indexToChange]->decReferenceCount(); + + this->elements [indexToChange] = newObject; + } + else + { + this->ensureAllocatedSize (numUsed + 1); + this->elements [numUsed++] = newObject; + } + + lock.exit(); + } + } + + /** Adds elements from another array to the end of this array. + + @param arrayToAddFrom the array from which to copy the elements + @param startIndex the first element of the other array to start copying from + @param numElementsToAdd how many elements to add from the other array. If this + value is negative or greater than the number of available elements, + all available elements will be copied. + @see add + */ + void addArray (const ReferenceCountedArray& arrayToAddFrom, + int startIndex = 0, + int numElementsToAdd = -1) throw() + { + arrayToAddFrom.lockArray(); + lock.enter(); + + if (startIndex < 0) + { + jassertfalse + startIndex = 0; + } + + if (numElementsToAdd < 0 || startIndex + numElementsToAdd > arrayToAddFrom.size()) + numElementsToAdd = arrayToAddFrom.size() - startIndex; + + if (numElementsToAdd > 0) + { + this->ensureAllocatedSize (numUsed + numElementsToAdd); + + while (--numElementsToAdd >= 0) + add (arrayToAddFrom.getUnchecked (startIndex++)); + } + + lock.exit(); + arrayToAddFrom.unlockArray(); + } + + /** Inserts a new object into the array assuming that the array is sorted. + + This will use a comparator to find the position at which the new object + should go. If the array isn't sorted, the behaviour of this + method will be unpredictable. + + @param comparator the comparator object to use to compare the elements - see the + sort() method for details about this object's form + @param newObject the new object to insert to the array + @see add, sort + */ + template + void addSorted (ElementComparator& comparator, + ObjectClass* newObject) throw() + { + lock.enter(); + insert (findInsertIndexInSortedArray (comparator, this->elements, newObject, 0, numUsed), newObject); + lock.exit(); + } + + /** Removes an object from the array. + + This will remove the object at a given index and move back all the + subsequent objects to close the gap. + + If the index passed in is out-of-range, nothing will happen. + + The object that is removed will have its reference count decreased, + and may be deleted if not referenced from elsewhere. + + @param indexToRemove the index of the element to remove + @see removeObject, removeRange + */ + void remove (const int indexToRemove) + { + lock.enter(); + + if (((unsigned int) indexToRemove) < (unsigned int) numUsed) + { + ObjectClass** const e = this->elements + indexToRemove; + + if (*e != 0) + (*e)->decReferenceCount(); + + --numUsed; + const int numberToShift = numUsed - indexToRemove; + + if (numberToShift > 0) + memmove (e, e + 1, numberToShift * sizeof (ObjectClass*)); + + if ((numUsed << 1) < this->numAllocated) + minimiseStorageOverheads(); + } + + lock.exit(); + } + + /** Removes the first occurrence of a specified object from the array. + + If the item isn't found, no action is taken. If it is found, it is + removed and has its reference count decreased. + + @param objectToRemove the object to try to remove + @see remove, removeRange + */ + void removeObject (ObjectClass* const objectToRemove) + { + lock.enter(); + remove (indexOf (objectToRemove)); + lock.exit(); + } + + /** Removes a range of objects from the array. + + This will remove a set of objects, starting from the given index, + and move any subsequent elements down to close the gap. + + If the range extends beyond the bounds of the array, it will + be safely clipped to the size of the array. + + The objects that are removed will have their reference counts decreased, + and may be deleted if not referenced from elsewhere. + + @param startIndex the index of the first object to remove + @param numberToRemove how many objects should be removed + @see remove, removeObject + */ + void removeRange (const int startIndex, + const int numberToRemove) + { + lock.enter(); + + const int start = jlimit (0, numUsed, startIndex); + const int end = jlimit (0, numUsed, startIndex + numberToRemove); + + if (end > start) + { + int i; + for (i = start; i < end; ++i) + { + if (this->elements[i] != 0) + { + this->elements[i]->decReferenceCount(); + this->elements[i] = 0; // (in case one of the destructors accesses this array and hits a dangling pointer) + } + } + + const int rangeSize = end - start; + ObjectClass** e = this->elements + start; + i = numUsed - end; + numUsed -= rangeSize; + + while (--i >= 0) + { + *e = e [rangeSize]; + ++e; + } + + if ((numUsed << 1) < this->numAllocated) + minimiseStorageOverheads(); + } + + lock.exit(); + } + + /** Removes the last n objects from the array. + + The objects that are removed will have their reference counts decreased, + and may be deleted if not referenced from elsewhere. + + @param howManyToRemove how many objects to remove from the end of the array + @see remove, removeObject, removeRange + */ + void removeLast (int howManyToRemove = 1) + { + lock.enter(); + + if (howManyToRemove > numUsed) + howManyToRemove = numUsed; + + while (--howManyToRemove >= 0) + remove (numUsed - 1); + + lock.exit(); + } + + /** Swaps a pair of objects in the array. + + If either of the indexes passed in is out-of-range, nothing will happen, + otherwise the two objects at these positions will be exchanged. + */ + void swap (const int index1, + const int index2) throw() + { + lock.enter(); + + if (((unsigned int) index1) < (unsigned int) numUsed + && ((unsigned int) index2) < (unsigned int) numUsed) + { + swapVariables (this->elements [index1], + this->elements [index2]); + } + + lock.exit(); + } + + /** Moves one of the objects to a different position. + + This will move the object to a specified index, shuffling along + any intervening elements as required. + + So for example, if you have the array { 0, 1, 2, 3, 4, 5 } then calling + move (2, 4) would result in { 0, 1, 3, 4, 2, 5 }. + + @param currentIndex the index of the object to be moved. If this isn't a + valid index, then nothing will be done + @param newIndex the index at which you'd like this object to end up. If this + is less than zero, it will be moved to the end of the array + */ + void move (const int currentIndex, + int newIndex) throw() + { + if (currentIndex != newIndex) + { + lock.enter(); + + if (((unsigned int) currentIndex) < (unsigned int) numUsed) + { + if (((unsigned int) newIndex) >= (unsigned int) numUsed) + newIndex = numUsed - 1; + + ObjectClass* const value = this->elements [currentIndex]; + + if (newIndex > currentIndex) + { + memmove (this->elements + currentIndex, + this->elements + currentIndex + 1, + (newIndex - currentIndex) * sizeof (ObjectClass*)); + } + else + { + memmove (this->elements + newIndex + 1, + this->elements + newIndex, + (currentIndex - newIndex) * sizeof (ObjectClass*)); + } + + this->elements [newIndex] = value; + } + + lock.exit(); + } + } + + /** Compares this array to another one. + + @returns true only if the other array contains the same objects in the same order + */ + bool operator== (const ReferenceCountedArray& other) const throw() + { + other.lockArray(); + lock.enter(); + + bool result = numUsed == other.numUsed; + + if (result) + { + for (int i = numUsed; --i >= 0;) + { + if (this->elements [i] != other.elements [i]) + { + result = false; + break; + } + } + } + + lock.exit(); + other.unlockArray(); + + return result; + } + + /** Compares this array to another one. + + @see operator== + */ + bool operator!= (const ReferenceCountedArray& other) const throw() + { + return ! operator== (other); + } + + /** Sorts the elements in the array. + + This will use a comparator object to sort the elements into order. The object + passed must have a method of the form: + @code + int compareElements (ElementType first, ElementType second); + @endcode + + ..and this method must return: + - a value of < 0 if the first comes before the second + - a value of 0 if the two objects are equivalent + - a value of > 0 if the second comes before the first + + To improve performance, the compareElements() method can be declared as static or const. + + @param comparator the comparator to use for comparing elements. + @param retainOrderOfEquivalentItems if this is true, then items + which the comparator says are equivalent will be + kept in the order in which they currently appear + in the array. This is slower to perform, but may + be important in some cases. If it's false, a faster + algorithm is used, but equivalent elements may be + rearranged. + + @see sortArray + */ + template + void sort (ElementComparator& comparator, + const bool retainOrderOfEquivalentItems = false) const throw() + { + (void) comparator; // if you pass in an object with a static compareElements() method, this + // avoids getting warning messages about the parameter being unused + + lock.enter(); + sortArray (comparator, this->elements, 0, size() - 1, retainOrderOfEquivalentItems); + lock.exit(); + } + + /** Reduces the amount of storage being used by the array. + + Arrays typically allocate slightly more storage than they need, and after + removing elements, they may have quite a lot of unused space allocated. + This method will reduce the amount of allocated storage to a minimum. + */ + void minimiseStorageOverheads() throw() + { + lock.enter(); + + if (numUsed == 0) + { + this->setAllocatedSize (0); + } + else + { + const int newAllocation = this->granularity * (numUsed / this->granularity + 1); + + if (newAllocation < this->numAllocated) + this->setAllocatedSize (newAllocation); + } + + lock.exit(); + } + + /** Locks the array's CriticalSection. + + Of course if the type of section used is a DummyCriticalSection, this won't + have any effect. + + @see unlockArray + */ + void lockArray() const throw() + { + lock.enter(); + } + + /** Unlocks the array's CriticalSection. + + Of course if the type of section used is a DummyCriticalSection, this won't + have any effect. + + @see lockArray + */ + void unlockArray() const throw() + { + lock.exit(); + } + + juce_UseDebuggingNewOperator + +private: + int numUsed; + TypeOfCriticalSectionToUse lock; +}; + +#endif // __JUCE_REFERENCECOUNTEDARRAY_JUCEHEADER__ +/********* End of inlined file: juce_ReferenceCountedArray.h *********/ + +#endif +#ifndef __JUCE_REFERENCECOUNTEDOBJECT_JUCEHEADER__ + +#endif +#ifndef __JUCE_SORTEDSET_JUCEHEADER__ + +/********* Start of inlined file: juce_SortedSet.h *********/ +#ifndef __JUCE_SORTEDSET_JUCEHEADER__ +#define __JUCE_SORTEDSET_JUCEHEADER__ + +#if JUCE_MSVC + #pragma warning (push) + #pragma warning (disable: 4512) +#endif + +/** + Holds a set of unique primitive objects, such as ints or doubles. + + A set can only hold one item with a given value, so if for example it's a + set of integers, attempting to add the same integer twice will do nothing + the second time. + + Internally, the list of items is kept sorted (which means that whatever + kind of primitive type is used must support the ==, <, >, <= and >= operators + to determine the order), and searching the set for known values is very fast + because it uses a binary-chop method. + + Note that if you're using a class or struct as the element type, it must be + capable of being copied or moved with a straightforward memcpy, rather than + needing construction and destruction code. + + To make all the set's methods thread-safe, pass in "CriticalSection" as the templated + TypeOfCriticalSectionToUse parameter, instead of the default DummyCriticalSection. + + @see Array, OwnedArray, ReferenceCountedArray, StringArray, CriticalSection +*/ +template +class SortedSet : private ArrayAllocationBase +{ +public: + + /** Creates an empty set. + + @param granularity this is the size of increment by which the internal storage + used by the array will grow. Only change it from the default if you know the + array is going to be very big and needs to be able to grow efficiently. + + @see ArrayAllocationBase + */ + SortedSet (const int granularity = juceDefaultArrayGranularity) throw() + : ArrayAllocationBase (granularity), + numUsed (0) + { + } + + /** Creates a copy of another set. + @param other the set to copy + */ + SortedSet (const SortedSet& other) throw() + : ArrayAllocationBase (other.granularity) + { + other.lockSet(); + numUsed = other.numUsed; + setAllocatedSize (other.numUsed); + memcpy (this->elements, other.elements, numUsed * sizeof (ElementType)); + other.unlockSet(); + } + + /** Destructor. */ + ~SortedSet() throw() + { + } + + /** Copies another set over this one. + @param other the set to copy + */ + const SortedSet & operator= (const SortedSet & other) throw() + { + if (this != &other) + { + other.lockSet(); + lock.enter(); + + this->granularity = other.granularity; + ensureAllocatedSize (other.size()); + numUsed = other.numUsed; + memcpy (this->elements, other.elements, numUsed * sizeof (ElementType)); + minimiseStorageOverheads(); + + lock.exit(); + other.unlockSet(); + } + + return *this; + } + + /** Compares this set to another one. + + Two sets are considered equal if they both contain the same set of + elements. + + @param other the other set to compare with + */ + bool operator== (const SortedSet& other) const throw() + { + lock.enter(); + + if (numUsed != other.numUsed) + { + lock.exit(); + return false; + } + + for (int i = numUsed; --i >= 0;) + { + if (this->elements [i] != other.elements [i]) + { + lock.exit(); + return false; + } + } + + lock.exit(); + return true; + } + + /** Compares this set to another one. + + Two sets are considered equal if they both contain the same set of + elements. + + @param other the other set to compare with + */ + bool operator!= (const SortedSet& other) const throw() + { + return ! operator== (other); + } + + /** Removes all elements from the set. + + This will remove all the elements, and free any storage that the set is + using. To clear it without freeing the storage, use the clearQuick() + method instead. + + @see clearQuick + */ + void clear() throw() + { + lock.enter(); + this->setAllocatedSize (0); + numUsed = 0; + lock.exit(); + } + + /** Removes all elements from the set without freeing the array's allocated storage. + + @see clear + */ + void clearQuick() throw() + { + lock.enter(); + numUsed = 0; + lock.exit(); + } + + /** Returns the current number of elements in the set. + */ + inline int size() const throw() + { + return numUsed; + } + + /** Returns one of the elements in the set. + + If the index passed in is beyond the range of valid elements, this + will return zero. + + If you're certain that the index will always be a valid element, you + can call getUnchecked() instead, which is faster. + + @param index the index of the element being requested (0 is the first element in the set) + @see getUnchecked, getFirst, getLast + */ + inline ElementType operator[] (const int index) const throw() + { + lock.enter(); + const ElementType result = (((unsigned int) index) < (unsigned int) numUsed) + ? this->elements [index] + : (ElementType) 0; + lock.exit(); + + return result; + } + + /** Returns one of the elements in the set, without checking the index passed in. + Unlike the operator[] method, this will try to return an element without + checking that the index is within the bounds of the set, so should only + be used when you're confident that it will always be a valid index. + + @param index the index of the element being requested (0 is the first element in the set) + @see operator[], getFirst, getLast + */ + inline ElementType getUnchecked (const int index) const throw() + { + lock.enter(); + jassert (((unsigned int) index) < (unsigned int) numUsed); + const ElementType result = this->elements [index]; + lock.exit(); + + return result; + } + + /** Returns the first element in the set, or 0 if the set is empty. + + @see operator[], getUnchecked, getLast + */ + inline ElementType getFirst() const throw() + { + lock.enter(); + const ElementType result = (numUsed > 0) ? this->elements [0] + : (ElementType) 0; + lock.exit(); + + return result; + } + + /** Returns the last element in the set, or 0 if the set is empty. + + @see operator[], getUnchecked, getFirst + */ + inline ElementType getLast() const throw() + { + lock.enter(); + const ElementType result = (numUsed > 0) ? this->elements [numUsed - 1] + : (ElementType) 0; + lock.exit(); + + return result; + } + + /** Finds the index of the first element which matches the value passed in. + + This will search the set for the given object, and return the index + of its first occurrence. If the object isn't found, the method will return -1. + + @param elementToLookFor the value or object to look for + @returns the index of the object, or -1 if it's not found + */ + int indexOf (const ElementType elementToLookFor) const throw() + { + lock.enter(); + + int start = 0; + int end = numUsed; + + for (;;) + { + if (start >= end) + { + lock.exit(); + return -1; + } + else if (elementToLookFor == this->elements [start]) + { + lock.exit(); + return start; + } + else + { + const int halfway = (start + end) >> 1; + + if (halfway == start) + { + lock.exit(); + return -1; + } + else if (elementToLookFor >= this->elements [halfway]) + start = halfway; + else + end = halfway; + } + } + } + + /** Returns true if the set contains at least one occurrence of an object. + + @param elementToLookFor the value or object to look for + @returns true if the item is found + */ + bool contains (const ElementType elementToLookFor) const throw() + { + lock.enter(); + + int start = 0; + int end = numUsed; + + for (;;) + { + if (start >= end) + { + lock.exit(); + return false; + } + else if (elementToLookFor == this->elements [start]) + { + lock.exit(); + return true; + } + else + { + const int halfway = (start + end) >> 1; + + if (halfway == start) + { + lock.exit(); + return false; + } + else if (elementToLookFor >= this->elements [halfway]) + start = halfway; + else + end = halfway; + } + } + } + + /** Adds a new element to the set, (as long as it's not already in there). + + @param newElement the new object to add to the set + @see set, insert, addIfNotAlreadyThere, addSorted, addSet, addArray + */ + void add (const ElementType newElement) throw() + { + lock.enter(); + + int start = 0; + int end = numUsed; + + for (;;) + { + if (start >= end) + { + jassert (start <= end); + insertInternal (start, newElement); + break; + } + else if (newElement == this->elements [start]) + { + break; + } + else + { + const int halfway = (start + end) >> 1; + + if (halfway == start) + { + if (newElement >= this->elements [halfway]) + insertInternal (start + 1, newElement); + else + insertInternal (start, newElement); + + break; + } + else if (newElement >= this->elements [halfway]) + start = halfway; + else + end = halfway; + } + } + + lock.exit(); + } + + /** Adds elements from an array to this set. + + @param elementsToAdd the array of elements to add + @param numElementsToAdd how many elements are in this other array + @see add + */ + void addArray (const ElementType* elementsToAdd, + int numElementsToAdd) throw() + { + lock.enter(); + + while (--numElementsToAdd >= 0) + add (*elementsToAdd++); + + lock.exit(); + } + + /** Adds elements from another set to this one. + + @param setToAddFrom the set from which to copy the elements + @param startIndex the first element of the other set to start copying from + @param numElementsToAdd how many elements to add from the other set. If this + value is negative or greater than the number of available elements, + all available elements will be copied. + @see add + */ + template + void addSet (const OtherSetType& setToAddFrom, + int startIndex = 0, + int numElementsToAdd = -1) throw() + { + setToAddFrom.lockSet(); + lock.enter(); + jassert (this != &setToAddFrom); + + if (this != &setToAddFrom) + { + if (startIndex < 0) + { + jassertfalse + startIndex = 0; + } + + if (numElementsToAdd < 0 || startIndex + numElementsToAdd > setToAddFrom.size()) + numElementsToAdd = setToAddFrom.size() - startIndex; + + addArray (setToAddFrom.elements + startIndex, numElementsToAdd); + } + + lock.exit(); + setToAddFrom.unlockSet(); + } + + /** Removes an element from the set. + + This will remove the element at a given index. + If the index passed in is out-of-range, nothing will happen. + + @param indexToRemove the index of the element to remove + @returns the element that has been removed + @see removeValue, removeRange + */ + ElementType remove (const int indexToRemove) throw() + { + lock.enter(); + + if (((unsigned int) indexToRemove) < (unsigned int) numUsed) + { + --numUsed; + + ElementType* const e = this->elements + indexToRemove; + ElementType const removed = *e; + const int numberToShift = numUsed - indexToRemove; + + if (numberToShift > 0) + memmove (e, e + 1, numberToShift * sizeof (ElementType)); + + if ((numUsed << 1) < this->numAllocated) + minimiseStorageOverheads(); + + lock.exit(); + return removed; + } + else + { + lock.exit(); + return 0; + } + } + + /** Removes an item from the set. + + This will remove the given element from the set, if it's there. + + @param valueToRemove the object to try to remove + @see remove, removeRange + */ + void removeValue (const ElementType valueToRemove) throw() + { + lock.enter(); + remove (indexOf (valueToRemove)); + lock.exit(); + } + + /** Removes any elements which are also in another set. + + @param otherSet the other set in which to look for elements to remove + @see removeValuesNotIn, remove, removeValue, removeRange + */ + template + void removeValuesIn (const OtherSetType& otherSet) throw() + { + otherSet.lockSet(); + lock.enter(); + + if (this == &otherSet) + { + clear(); + } + else + { + if (otherSet.size() > 0) + { + for (int i = numUsed; --i >= 0;) + if (otherSet.contains (this->elements [i])) + remove (i); + } + } + + lock.exit(); + otherSet.unlockSet(); + } + + /** Removes any elements which are not found in another set. + + Only elements which occur in this other set will be retained. + + @param otherSet the set in which to look for elements NOT to remove + @see removeValuesIn, remove, removeValue, removeRange + */ + template + void removeValuesNotIn (const OtherSetType& otherSet) throw() + { + otherSet.lockSet(); + lock.enter(); + + if (this != &otherSet) + { + if (otherSet.size() <= 0) + { + clear(); + } + else + { + for (int i = numUsed; --i >= 0;) + if (! otherSet.contains (this->elements [i])) + remove (i); + } + } + + lock.exit(); + otherSet.lockSet(); + } + + /** Reduces the amount of storage being used by the set. + + Sets typically allocate slightly more storage than they need, and after + removing elements, they may have quite a lot of unused space allocated. + This method will reduce the amount of allocated storage to a minimum. + */ + void minimiseStorageOverheads() throw() + { + lock.enter(); + + if (numUsed == 0) + { + this->setAllocatedSize (0); + } + else + { + const int newAllocation = this->granularity * (numUsed / this->granularity + 1); + + if (newAllocation < this->numAllocated) + this->setAllocatedSize (newAllocation); + } + + lock.exit(); + } + + /** Locks the set's CriticalSection. + + Of course if the type of section used is a DummyCriticalSection, this won't + have any effect. + + @see unlockSet + */ + void lockSet() const throw() + { + lock.enter(); + } + + /** Unlocks the set's CriticalSection. + + Of course if the type of section used is a DummyCriticalSection, this won't + have any effect. + + @see lockSet + */ + void unlockSet() const throw() + { + lock.exit(); + } + + juce_UseDebuggingNewOperator + +private: + int numUsed; + TypeOfCriticalSectionToUse lock; + + void insertInternal (const int indexToInsertAt, const ElementType newElement) throw() + { + this->ensureAllocatedSize (numUsed + 1); + + ElementType* const insertPos = this->elements + indexToInsertAt; + const int numberToMove = numUsed - indexToInsertAt; + + if (numberToMove > 0) + memmove (insertPos + 1, insertPos, numberToMove * sizeof (ElementType)); + + *insertPos = newElement; + ++numUsed; + } +}; + +#if JUCE_MSVC + #pragma warning (pop) +#endif + +#endif // __JUCE_SORTEDSET_JUCEHEADER__ +/********* End of inlined file: juce_SortedSet.h *********/ + +#endif +#ifndef __JUCE_SPARSESET_JUCEHEADER__ + +/********* Start of inlined file: juce_SparseSet.h *********/ +#ifndef __JUCE_SPARSESET_JUCEHEADER__ +#define __JUCE_SPARSESET_JUCEHEADER__ + +/** + Holds a set of primitive values, storing them as a set of ranges. + + This container acts like a simple BitArray, but can efficiently hold large + continguous ranges of values. It's quite a specialised class, mostly useful + for things like keeping the set of selected rows in a listbox. + + The type used as a template paramter must be an integer type, such as int, short, + int64, etc. +*/ +template +class SparseSet +{ +public: + + /** Creates a new empty set. */ + SparseSet() throw() + { + } + + /** Creates a copy of another SparseSet. */ + SparseSet (const SparseSet& other) throw() + : values (other.values) + { + } + + /** Destructor. */ + ~SparseSet() throw() + { + } + + /** Clears the set. */ + void clear() throw() + { + values.clear(); + } + + /** Checks whether the set is empty. + + This is much quicker than using (size() == 0). + */ + bool isEmpty() const throw() + { + return values.size() == 0; + } + + /** Returns the number of values in the set. + + Because of the way the data is stored, this method can take longer if there + are a lot of items in the set. Use isEmpty() for a quick test of whether there + are any items. + */ + Type size() const throw() + { + Type num = 0; + + for (int i = 0; i < values.size(); i += 2) + num += values[i + 1] - values[i]; + + return num; + } + + /** Returns one of the values in the set. + + @param index the index of the value to retrieve, in the range 0 to (size() - 1). + @returns the value at this index, or 0 if it's out-of-range + */ + Type operator[] (int index) const throw() + { + for (int i = 0; i < values.size(); i += 2) + { + const Type s = values.getUnchecked(i); + const Type e = values.getUnchecked(i + 1); + + if (index < e - s) + return s + index; + + index -= e - s; + } + + return (Type) 0; + } + + /** Checks whether a particular value is in the set. */ + bool contains (const Type valueToLookFor) const throw() + { + bool on = false; + + for (int i = 0; i < values.size(); ++i) + { + if (values.getUnchecked(i) > valueToLookFor) + return on; + + on = ! on; + } + + return false; + } + + /** Returns the number of contiguous blocks of values. + + @see getRange + */ + int getNumRanges() const throw() + { + return values.size() >> 1; + } + + /** Returns one of the contiguous ranges of values stored. + + @param rangeIndex the index of the range to look up, between 0 + and (getNumRanges() - 1) + @param startValue on return, the value at the start of the range + @param numValues on return, the number of values in the range + + @see getTotalRange + */ + bool getRange (const int rangeIndex, + Type& startValue, + Type& numValues) const throw() + { + if (((unsigned int) rangeIndex) < (unsigned int) getNumRanges()) + { + startValue = values [rangeIndex << 1]; + numValues = values [(rangeIndex << 1) + 1] - startValue; + + return true; + } + + return false; + } + + /** Returns the lowest and highest values in the set. + + @see getRange + */ + bool getTotalRange (Type& lowestValue, + Type& highestValue) const throw() + { + if (values.size() > 0) + { + lowestValue = values.getUnchecked (0); + highestValue = values.getUnchecked (values.size() - 1); + return true; + } + + return false; + } + + /** Adds a range of contiguous values to the set. + + e.g. addRange (10, 4) will add (10, 11, 12, 13) to the set. + + @param firstValue the start of the range of values to add + @param numValuesToAdd how many values to add + */ + void addRange (const Type firstValue, + const Type numValuesToAdd) throw() + { + jassert (numValuesToAdd >= 0); + + if (numValuesToAdd > 0) + { + removeRange (firstValue, numValuesToAdd); + + IntegerElementComparator sorter; + values.addSorted (sorter, firstValue); + values.addSorted (sorter, firstValue + numValuesToAdd); + + simplify(); + } + } + + /** Removes a range of values from the set. + + e.g. removeRange (10, 4) will remove (10, 11, 12, 13) from the set. + + @param firstValue the start of the range of values to remove + @param numValuesToRemove how many values to remove + */ + void removeRange (const Type firstValue, + const Type numValuesToRemove) throw() + { + jassert (numValuesToRemove >= 0); + + if (numValuesToRemove > 0 + && firstValue < values.getLast()) + { + const bool onAtStart = contains (firstValue - 1); + Type lastValue = firstValue + numValuesToRemove; + + if (lastValue < firstValue) // possible if the signed arithmetic wraps around + lastValue = values.getLast(); + + const bool onAtEnd = contains (lastValue); + + for (int i = values.size(); --i >= 0;) + { + if (values.getUnchecked(i) >= firstValue + && values.getUnchecked(i) <= lastValue) + { + values.remove (i); + } + } + + IntegerElementComparator sorter; + + if (onAtStart) + values.addSorted (sorter, firstValue); + + if (onAtEnd) + values.addSorted (sorter, lastValue); + + simplify(); + } + } + + /** Does an XOR of the values in a given range. */ + void invertRange (const Type firstValue, + const Type numValues) + { + SparseSet newItems; + newItems.addRange (firstValue, numValues); + + int i; + for (i = getNumRanges(); --i >= 0;) + { + const int start = values [i << 1]; + const int end = values [(i << 1) + 1]; + + newItems.removeRange (start, end); + } + + removeRange (firstValue, numValues); + + for (i = newItems.getNumRanges(); --i >= 0;) + { + const int start = newItems.values [i << 1]; + const int end = newItems.values [(i << 1) + 1]; + + addRange (start, end); + } + } + + /** Checks whether any part of a given range overlaps any part of this one. */ + bool overlapsRange (const Type firstValue, + const Type numValues) throw() + { + jassert (numValues >= 0); + + if (numValues > 0) + { + for (int i = getNumRanges(); --i >= 0;) + { + if (firstValue >= values.getUnchecked ((i << 1) + 1)) + return false; + + if (firstValue + numValues > values.getUnchecked (i << 1)) + return true; + } + } + + return false; + } + + /** Checks whether the whole of a given range is contained within this one. */ + bool containsRange (const Type firstValue, + const Type numValues) throw() + { + jassert (numValues >= 0); + + if (numValues > 0) + { + for (int i = getNumRanges(); --i >= 0;) + { + if (firstValue >= values.getUnchecked ((i << 1) + 1)) + return false; + + if (firstValue >= values.getUnchecked (i << 1) + && firstValue + numValues <= values.getUnchecked ((i << 1) + 1)) + return true; + } + } + + return false; + } + + bool operator== (const SparseSet& other) throw() + { + return values == other.values; + } + + bool operator!= (const SparseSet& other) throw() + { + return values != other.values; + } + + juce_UseDebuggingNewOperator + +private: + // alternating start/end values of ranges of values that are present. + Array values; + + void simplify() throw() + { + jassert ((values.size() & 1) == 0); + + for (int i = values.size(); --i > 0;) + if (values.getUnchecked(i) == values.getUnchecked (i - 1)) + values.removeRange (i - 1, 2); + } +}; + +#endif // __JUCE_SPARSESET_JUCEHEADER__ +/********* End of inlined file: juce_SparseSet.h *********/ + +#endif +#ifndef __JUCE_VOIDARRAY_JUCEHEADER__ + +#endif +#ifndef __JUCE_INPUTSTREAM_JUCEHEADER__ + +#endif +#ifndef __JUCE_OUTPUTSTREAM_JUCEHEADER__ + +#endif +#ifndef __JUCE_DIRECTORYITERATOR_JUCEHEADER__ + +/********* Start of inlined file: juce_DirectoryIterator.h *********/ +#ifndef __JUCE_DIRECTORYITERATOR_JUCEHEADER__ +#define __JUCE_DIRECTORYITERATOR_JUCEHEADER__ + +/** + Searches through a the files in a directory, returning each file that is found. + + A DirectoryIterator will search through a directory and its subdirectories using + a wildcard filepattern match. + + If you may be finding a large number of files, this is better than + using File::findChildFiles() because it doesn't block while it finds them + all, and this is more memory-efficient. + + It can also guess how far it's got using a wildly inaccurate algorithm. +*/ +class JUCE_API DirectoryIterator +{ +public: + + /** Creates a DirectoryIterator for a given directory. + + After creating one of these, call its next() method to get the + first file - e.g. @code + + DirectoryIterator iter (File ("/animals/mooses"), true, "*.moose"); + + while (iter.next()) + { + File theFileItFound (iter.getFile()); + + ... etc + } + @endcode + + @param directory the directory to search in + @param isRecursive whether all the subdirectories should also be searched + @param wildCard the file pattern to match + @param whatToLookFor a value from the File::TypesOfFileToFind enum, specifying + whether to look for files, directories, or both. + */ + DirectoryIterator (const File& directory, + bool isRecursive, + const String& wildCard = JUCE_T("*"), + const int whatToLookFor = File::findFiles) throw(); + + /** Destructor. */ + ~DirectoryIterator() throw(); + + /** Call this to move the iterator along to the next file. + + @returns true if a file was found (you can then use getFile() to see what it was) - or + false if there are no more matching files. + */ + bool next() throw(); + + /** Returns the file that the iterator is currently pointing at. + + The result of this call is only valid after a call to next() has returned true. + */ + const File getFile() const throw(); + + /** Returns a guess of how far through the search the iterator has got. + + @returns a value 0.0 to 1.0 to show the progress, although this won't be + very accurate. + */ + float getEstimatedProgress() const throw(); + + juce_UseDebuggingNewOperator + +private: + OwnedArray filesFound; + OwnedArray dirsFound; + String wildCard; + int index; + const int whatToLookFor; + DirectoryIterator* subIterator; + + DirectoryIterator (const DirectoryIterator&); + const DirectoryIterator& operator= (const DirectoryIterator&); +}; + +#endif // __JUCE_DIRECTORYITERATOR_JUCEHEADER__ +/********* End of inlined file: juce_DirectoryIterator.h *********/ + +#endif +#ifndef __JUCE_FILE_JUCEHEADER__ + +#endif +#ifndef __JUCE_FILEINPUTSTREAM_JUCEHEADER__ + +/********* Start of inlined file: juce_FileInputStream.h *********/ +#ifndef __JUCE_FILEINPUTSTREAM_JUCEHEADER__ +#define __JUCE_FILEINPUTSTREAM_JUCEHEADER__ + +/** + An input stream that reads from a local file. + + @see InputStream, FileOutputStream, File::createInputStream +*/ +class JUCE_API FileInputStream : public InputStream +{ +public: + + /** Creates a FileInputStream. + + @param fileToRead the file to read from - if the file can't be accessed for some + reason, then the stream will just contain no data + */ + FileInputStream (const File& fileToRead); + + /** Destructor. */ + ~FileInputStream(); + + const File& getFile() const throw() { return file; } + + int64 getTotalLength(); + int read (void* destBuffer, int maxBytesToRead); + bool isExhausted(); + int64 getPosition(); + bool setPosition (int64 pos); + + juce_UseDebuggingNewOperator + +private: + File file; + void* fileHandle; + int64 currentPosition, totalSize; + bool needToSeek; +}; + +#endif // __JUCE_FILEINPUTSTREAM_JUCEHEADER__ +/********* End of inlined file: juce_FileInputStream.h *********/ + +#endif +#ifndef __JUCE_FILEOUTPUTSTREAM_JUCEHEADER__ + +/********* Start of inlined file: juce_FileOutputStream.h *********/ +#ifndef __JUCE_FILEOUTPUTSTREAM_JUCEHEADER__ +#define __JUCE_FILEOUTPUTSTREAM_JUCEHEADER__ + +/** + An output stream that writes into a local file. + + @see OutputStream, FileInputStream, File::createOutputStream +*/ +class JUCE_API FileOutputStream : public OutputStream +{ +public: + + /** Creates a FileOutputStream. + + If the file doesn't exist, it will first be created. If the file can't be + created or opened, the failedToOpen() method will return + true. + + If the file already exists when opened, the stream's write-postion will + be set to the end of the file. To overwrite an existing file, + use File::deleteFile() before opening the stream, or use setPosition(0) + after it's opened (although this won't truncate the file). + + It's better to use File::createOutputStream() to create one of these, rather + than using the class directly. + */ + FileOutputStream (const File& fileToWriteTo, + const int bufferSizeToUse = 16384); + + /** Destructor. */ + ~FileOutputStream(); + + /** Returns the file that this stream is writing to. + */ + const File& getFile() const throw() { return file; } + + /** Returns true if the stream couldn't be opened for some reason. + */ + bool failedToOpen() const throw() { return fileHandle == 0; } + + void flush(); + int64 getPosition(); + bool setPosition (int64 pos); + bool write (const void* data, int numBytes); + + juce_UseDebuggingNewOperator + +private: + File file; + void* fileHandle; + int64 currentPosition; + int bufferSize, bytesInBuffer; + char* buffer; +}; + +#endif // __JUCE_FILEOUTPUTSTREAM_JUCEHEADER__ +/********* End of inlined file: juce_FileOutputStream.h *********/ + +#endif +#ifndef __JUCE_FILESEARCHPATH_JUCEHEADER__ + +/********* Start of inlined file: juce_FileSearchPath.h *********/ +#ifndef __JUCE_FILESEARCHPATH_JUCEHEADER__ +#define __JUCE_FILESEARCHPATH_JUCEHEADER__ + +/** + Encapsulates a set of folders that make up a search path. + + @see File +*/ +class JUCE_API FileSearchPath +{ +public: + + /** Creates an empty search path. */ + FileSearchPath(); + + /** Creates a search path from a string of pathnames. + + The path can be semicolon- or comma-separated, e.g. + "/foo/bar;/foo/moose;/fish/moose" + + The separate folders are tokenised and added to the search path. + */ + FileSearchPath (const String& path); + + /** Creates a copy of another search path. */ + FileSearchPath (const FileSearchPath& other); + + /** Destructor. */ + ~FileSearchPath(); + + /** Uses a string containing a list of pathnames to re-initialise this list. + + This search path is cleared and the semicolon- or comma-separated folders + in this string are added instead. e.g. "/foo/bar;/foo/moose;/fish/moose" + */ + const FileSearchPath& operator= (const String& path); + + /** Returns the number of folders in this search path. + + @see operator[] + */ + int getNumPaths() const; + + /** Returns one of the folders in this search path. + + The file returned isn't guaranteed to actually be a valid directory. + + @see getNumPaths + */ + const File operator[] (const int index) const; + + /** Returns the search path as a semicolon-separated list of directories. */ + const String toString() const; + + /** Adds a new directory to the search path. + + The new directory is added to the end of the list if the insertIndex parameter is + less than zero, otherwise it is inserted at the given index. + */ + void add (const File& directoryToAdd, + const int insertIndex = -1); + + /** Adds a new directory to the search path if it's not already in there. */ + void addIfNotAlreadyThere (const File& directoryToAdd); + + /** Removes a directory from the search path. */ + void remove (const int indexToRemove); + + /** Merges another search path into this one. + + This will remove any duplicate directories. + */ + void addPath (const FileSearchPath& other); + + /** Removes any directories that are actually subdirectories of one of the other directories in the search path. + + If the search is intended to be recursive, there's no point having nested folders in the search + path, because they'll just get searched twice and you'll get duplicate results. + + e.g. if the path is "c:\abc\de;c:\abc", this method will simplify it to "c:\abc" + */ + void removeRedundantPaths(); + + /** Removes any directories that don't actually exist. */ + void removeNonExistentPaths(); + + /** Searches the path for a wildcard. + + This will search all the directories in the search path in order, adding any + matching files to the results array. + + @param results an array to append the results to + @param whatToLookFor a value from the File::TypesOfFileToFind enum, specifying whether to + return files, directories, or both. + @param searchRecursively whether to recursively search the subdirectories too + @param wildCardPattern a pattern to match against the filenames + @returns the number of files added to the array + @see File::findChildFiles + */ + int findChildFiles (OwnedArray& results, + const int whatToLookFor, + const bool searchRecursively, + const String& wildCardPattern = JUCE_T("*")) const; + + /** Finds out whether a file is inside one of the path's directories. + + This will return true if the specified file is a child of one of the + directories specified by this path. Note that this doesn't actually do any + searching or check that the files exist - it just looks at the pathnames + to work out whether the file would be inside a directory. + + @param fileToCheck the file to look for + @param checkRecursively if true, then this will return true if the file is inside a + subfolder of one of the path's directories (at any depth). If false + it will only return true if the file is actually a direct child + of one of the directories. + @see File::isAChildOf + + */ + bool isFileInPath (const File& fileToCheck, + const bool checkRecursively) const; + + juce_UseDebuggingNewOperator + +private: + StringArray directories; + + void init (const String& path); +}; + +#endif // __JUCE_FILESEARCHPATH_JUCEHEADER__ +/********* End of inlined file: juce_FileSearchPath.h *********/ + +#endif +#ifndef __JUCE_NAMEDPIPE_JUCEHEADER__ + +/********* Start of inlined file: juce_NamedPipe.h *********/ +#ifndef __JUCE_NAMEDPIPE_JUCEHEADER__ +#define __JUCE_NAMEDPIPE_JUCEHEADER__ + +/** + A cross-process pipe that can have data written to and read from it. + + Two or more processes can use these for inter-process communication. + + @see InterprocessConnection +*/ +class JUCE_API NamedPipe +{ +public: + + /** Creates a NamedPipe. */ + NamedPipe(); + + /** Destructor. */ + ~NamedPipe(); + + /** Tries to open a pipe that already exists. + + Returns true if it succeeds. + */ + bool openExisting (const String& pipeName); + + /** Tries to create a new pipe. + + Returns true if it succeeds. + */ + bool createNewPipe (const String& pipeName); + + /** Closes the pipe, if it's open. */ + void close(); + + /** True if the pipe is currently open. */ + bool isOpen() const throw(); + + /** Returns the last name that was used to try to open this pipe. */ + const String getName() const throw(); + + /** Reads data from the pipe. + + This will block until another thread has written enough data into the pipe to fill + the number of bytes specified, or until another thread calls the cancelPendingReads() + method. + + If the operation fails, it returns -1, otherwise, it will return the number of + bytes read. + + If timeOutMilliseconds is less than zero, it will wait indefinitely, otherwise + this is a maximum timeout for reading from the pipe. + */ + int read (void* destBuffer, int maxBytesToRead, int timeOutMilliseconds = 5000); + + /** Writes some data to the pipe. + + If the operation fails, it returns -1, otherwise, it will return the number of + bytes written. + */ + int write (const void* sourceBuffer, int numBytesToWrite, + int timeOutMilliseconds = 2000); + + /** If any threads are currently blocked on a read operation, this tells them to abort. + */ + void cancelPendingReads(); + + juce_UseDebuggingNewOperator + +private: + void* internal; + String currentPipeName; + + NamedPipe (const NamedPipe&); + const NamedPipe& operator= (const NamedPipe&); + + bool openInternal (const String& pipeName, const bool createPipe); +}; + +#endif // __JUCE_NAMEDPIPE_JUCEHEADER__ +/********* End of inlined file: juce_NamedPipe.h *********/ + +#endif +#ifndef __JUCE_BLOWFISH_JUCEHEADER__ + +/********* Start of inlined file: juce_BlowFish.h *********/ +#ifndef __JUCE_BLOWFISH_JUCEHEADER__ +#define __JUCE_BLOWFISH_JUCEHEADER__ + +/** + BlowFish encryption class. + +*/ +class JUCE_API BlowFish +{ +public: + + /** Creates an object that can encode/decode based on the specified key. */ + BlowFish (const uint8* keyData, int keyBytes); + + /** Destructor. */ + ~BlowFish(); + + /** Encrypts a pair of 32-bit integers. */ + void encrypt (uint32& data1, uint32& data2) const; + + /** Decrypts a pair of 32-bit integers. */ + void decrypt (uint32& data1, uint32& data2) const; + + juce_UseDebuggingNewOperator + +private: + uint32* p; + uint32* s[4]; + + uint32 F (uint32 x) const; +}; + +#endif // __JUCE_BLOWFISH_JUCEHEADER__ +/********* End of inlined file: juce_BlowFish.h *********/ + +#endif +#ifndef __JUCE_MD5_JUCEHEADER__ + +/********* Start of inlined file: juce_MD5.h *********/ +#ifndef __JUCE_MD5_JUCEHEADER__ +#define __JUCE_MD5_JUCEHEADER__ + +/** + MD5 checksum class. + + Create one of these with a block of source data or a string, and it calculates the + MD5 checksum of that data. + + You can then retrieve this checksum as a 16-byte block, or as a hex string. +*/ +class JUCE_API MD5 +{ +public: + + /** Creates a null MD5 object. */ + MD5(); + + /** Creates a copy of another MD5. */ + MD5 (const MD5& other); + + /** Copies another MD5. */ + const MD5& operator= (const MD5& other); + + /** Creates a checksum for a block of binary data. */ + MD5 (const MemoryBlock& data); + + /** Creates a checksum for a block of binary data. */ + MD5 (const char* data, const int numBytes); + + /** Creates a checksum for a string. */ + MD5 (const String& text); + + /** Creates a checksum for the input from a stream. + + This will read up to the given number of bytes from the stream, and produce the + checksum of that. If the number of bytes to read is negative, it'll read + until the stream is exhausted. + */ + MD5 (InputStream& input, int numBytesToRead = -1); + + /** Creates a checksum for a file. */ + MD5 (const File& file); + + /** Destructor. */ + ~MD5(); + + /** Returns the checksum as a 16-byte block of data. */ + const MemoryBlock getRawChecksumData() const; + + /** Returns the checksum as a 32-digit hex string. */ + const String toHexString() const; + + /** Compares this to another MD5. */ + bool operator== (const MD5& other) const; + + /** Compares this to another MD5. */ + bool operator!= (const MD5& other) const; + + juce_UseDebuggingNewOperator + +private: + uint8 result [16]; + + struct ProcessContext + { + uint8 buffer [64]; + uint32 state [4]; + uint32 count [2]; + + ProcessContext(); + + void processBlock (const uint8* const data, int dataSize); + void transform (const uint8* const buffer); + void finish (uint8* const result); + }; + + void processStream (InputStream& input, int numBytesToRead); +}; + +#endif // __JUCE_MD5_JUCEHEADER__ +/********* End of inlined file: juce_MD5.h *********/ + +#endif +#ifndef __JUCE_PRIMES_JUCEHEADER__ + +/********* Start of inlined file: juce_Primes.h *********/ +#ifndef __JUCE_PRIMES_JUCEHEADER__ +#define __JUCE_PRIMES_JUCEHEADER__ + +/** + Prime number creation class. + + This class contains static methods for generating and testing prime numbers. + + @see BitArray +*/ +class JUCE_API Primes +{ +public: + + /** Creates a random prime number with a given bit-length. + + The certainty parameter specifies how many iterations to use when testing + for primality. A safe value might be anything over about 20-30. + */ + static const BitArray createProbablePrime (int bitLength, + int certainty) throw(); + + /** Tests a number to see if it's prime. + + This isn't a bulletproof test, it uses a Miller-Rabin test to determine + whether the number is prime. + + The certainty parameter specifies how many iterations to use when testing - a + safe value might be anything over about 20-30. + */ + static bool isProbablyPrime (const BitArray& number, + int certainty) throw(); +}; + +#endif // __JUCE_PRIMES_JUCEHEADER__ +/********* End of inlined file: juce_Primes.h *********/ + +#endif +#ifndef __JUCE_RSAKEY_JUCEHEADER__ + +/********* Start of inlined file: juce_RSAKey.h *********/ +#ifndef __JUCE_RSAKEY_JUCEHEADER__ +#define __JUCE_RSAKEY_JUCEHEADER__ + +/** + RSA public/private key-pair encryption class. + + An object of this type makes up one half of a public/private RSA key pair. Use the + createKeyPair() method to create a matching pair for encoding/decoding. +*/ +class JUCE_API RSAKey +{ +public: + + /** Creates a null key object. + + Initialise a pair of objects for use with the createKeyPair() method. + */ + RSAKey() throw(); + + /** Loads a key from an encoded string representation. + + This reloads a key from a string created by the toString() method. + */ + RSAKey (const String& stringRepresentation) throw(); + + /** Destructor. */ + ~RSAKey() throw(); + + /** Turns the key into a string representation. + + This can be reloaded using the constructor that takes a string. + */ + const String toString() const throw(); + + /** Encodes or decodes a value. + + Call this on the public key object to encode some data, then use the matching + private key object to decode it. + + Returns false if the operation failed, e.g. if this object isn't a valid key. + */ + bool applyToValue (BitArray& value) const throw(); + + /** Creates a public/private key-pair. + + Each key will perform one-way encryption that can only be reversed by + using the other key. + + The numBits parameter specifies the size of key, e.g. 128, 256, 512 bit. Bigger + sizes are more secure, but this method will take longer to execute. + */ + static void createKeyPair (RSAKey& publicKey, + RSAKey& privateKey, + const int numBits) throw(); + + juce_UseDebuggingNewOperator + +protected: + BitArray part1, part2; +}; + +#endif // __JUCE_RSAKEY_JUCEHEADER__ +/********* End of inlined file: juce_RSAKey.h *********/ + +#endif +#ifndef __JUCE_SOCKET_JUCEHEADER__ + +/********* Start of inlined file: juce_Socket.h *********/ +#ifndef __JUCE_SOCKET_JUCEHEADER__ +#define __JUCE_SOCKET_JUCEHEADER__ + +/** + A wrapper for a streaming (TCP) socket. + + This allows low-level use of sockets; for an easier-to-use messaging layer on top of + sockets, you could also try the InterprocessConnection class. + + @see DatagramSocket, InterprocessConnection, InterprocessConnectionServer +*/ +class JUCE_API StreamingSocket +{ +public: + + /** Creates an uninitialised socket. + + To connect it, use the connect() method, after which you can read() or write() + to it. + + To wait for other sockets to connect to this one, the createListener() method + enters "listener" mode, and can be used to spawn new sockets for each connection + that comes along. + */ + StreamingSocket(); + + /** Destructor. */ + ~StreamingSocket(); + + /** Binds the socket to the specified local port. + + @returns true on success; false may indicate that another socket is already bound + on the same port + */ + bool bindToPort (const int localPortNumber); + + /** Tries to connect the socket to hostname:port. + + If timeOutMillisecs is 0, then this method will block until the operating system + rejects the connection (which could take a long time). + + @returns true if it succeeds. + @see isConnected + */ + bool connect (const String& remoteHostname, + const int remotePortNumber, + const int timeOutMillisecs = 3000); + + /** True if the socket is currently connected. */ + bool isConnected() const throw() { return connected; } + + /** Closes the connection. */ + void close(); + + /** Returns the name of the currently connected host. */ + const String& getHostName() const throw() { return hostName; } + + /** Returns the port number that's currently open. */ + int getPort() const throw() { return portNumber; } + + /** True if the socket is connected to this machine rather than over the network. */ + bool isLocal() const throw(); + + /** Waits until the socket is ready for reading or writing. + + If readyForReading is true, it will wait until the socket is ready for + reading; if false, it will wait until it's ready for writing. + + If the timeout is < 0, it will wait forever, or else will give up after + the specified time. + + If the socket is ready on return, this returns 1. If it times-out before + the socket becomes ready, it returns 0. If an error occurs, it returns -1. + */ + int waitUntilReady (const bool readyForReading, + const int timeoutMsecs) const; + + /** Reads bytes from the socket (blocking). + + Note that this method will block unless you have checked the socket is ready + for reading before calling it (see the waitUntilReady() method). + + @returns the number of bytes read, or -1 if there was an error. + */ + int read (void* destBuffer, const int maxBytesToRead); + + /** Writes bytes to the socket from a buffer. + + Note that this method will block unless you have checked the socket is ready + for writing before calling it (see the waitUntilReady() method). + + @returns the number of bytes written, or -1 if there was an error. + */ + int write (const void* sourceBuffer, const int numBytesToWrite); + + /** Puts this socket into "listener" mode. + + When in this mode, your thread can call waitForNextConnection() repeatedly, + which will spawn new sockets for each new connection, so that these can + be handled in parallel by other threads. + + This returns true if it manages to open the socket successfully. + + @see waitForNextConnection + */ + bool createListener (const int portNumber); + + /** When in "listener" mode, this waits for a connection and spawns it as a new + socket. + + The object that gets returned will be owned by the caller. + + This method can only be called after using createListener(). + + @see createListener + */ + StreamingSocket* waitForNextConnection() const; + + juce_UseDebuggingNewOperator + +private: + String hostName; + int volatile portNumber, handle; + bool connected, isListener; + + StreamingSocket (const String& hostname, const int portNumber, const int handle); + StreamingSocket (const StreamingSocket&); + const StreamingSocket& operator= (const StreamingSocket&); +}; + +/** + A wrapper for a datagram (UDP) socket. + + This allows low-level use of sockets; for an easier-to-use messaging layer on top of + sockets, you could also try the InterprocessConnection class. + + @see StreamingSocket, InterprocessConnection, InterprocessConnectionServer +*/ +class JUCE_API DatagramSocket +{ +public: + + /** + Creates an (uninitialised) datagram socket. + + The localPortNumber is the port on which to bind this socket. If this value is 0, + the port number is assigned by the operating system. + + To use the socket for sending, call the connect() method. This will not immediately + make a connection, but will save the destination you've provided. After this, you can + call read() or write(). + + To wait for other sockets to connect to this one, call waitForNextConnection(). + */ + DatagramSocket (const int localPortNumber); + + /** Destructor. */ + ~DatagramSocket(); + + /** Binds the socket to the specified local port. + + @returns true on success; false may indicate that another socket is already bound + on the same port + */ + bool bindToPort (const int localPortNumber); + + /** Tries to connect the socket to hostname:port. + + If timeOutMillisecs is 0, then this method will block until the operating system + rejects the connection (which could take a long time). + + @returns true if it succeeds. + @see isConnected + */ + bool connect (const String& remoteHostname, + const int remotePortNumber, + const int timeOutMillisecs = 3000); + + /** True if the socket is currently connected. */ + bool isConnected() const throw() { return connected; } + + /** Closes the connection. */ + void close(); + + /** Returns the name of the currently connected host. */ + const String& getHostName() const throw() { return hostName; } + + /** Returns the port number that's currently open. */ + int getPort() const throw() { return portNumber; } + + /** True if the socket is connected to this machine rather than over the network. */ + bool isLocal() const throw(); + + /** Waits until the socket is ready for reading or writing. + + If readyForReading is true, it will wait until the socket is ready for + reading; if false, it will wait until it's ready for writing. + + If the timeout is < 0, it will wait forever, or else will give up after + the specified time. + + If the socket is ready on return, this returns 1. If it times-out before + the socket becomes ready, it returns 0. If an error occurs, it returns -1. + */ + int waitUntilReady (const bool readyForReading, + const int timeoutMsecs) const; + + /** Reads bytes from the socket (blocking). + + Note that this method will block unless you have checked the socket is ready + for reading before calling it (see the waitUntilReady() method). + + @returns the number of bytes read, or -1 if there was an error. + */ + int read (void* destBuffer, const int maxBytesToRead); + + /** Writes bytes to the socket from a buffer. + + Note that this method will block unless you have checked the socket is ready + for writing before calling it (see the waitUntilReady() method). + + @returns the number of bytes written, or -1 if there was an error. + */ + int write (const void* sourceBuffer, const int numBytesToWrite); + + /** This waits for incoming data to be sent, and returns a socket that can be used + to read it. + + The object that gets returned is owned by the caller, and can't be used for + sending, but can be used to read the data. + */ + DatagramSocket* waitForNextConnection() const; + + juce_UseDebuggingNewOperator + +private: + String hostName; + int volatile portNumber, handle; + bool connected; + void* serverAddress; + + DatagramSocket (const String& hostname, const int portNumber, const int handle, const int localPortNumber); + DatagramSocket (const DatagramSocket&); + const DatagramSocket& operator= (const DatagramSocket&); +}; + +#endif // __JUCE_SOCKET_JUCEHEADER__ +/********* End of inlined file: juce_Socket.h *********/ + +#endif +#ifndef __JUCE_URL_JUCEHEADER__ + +/********* Start of inlined file: juce_URL.h *********/ +#ifndef __JUCE_URL_JUCEHEADER__ +#define __JUCE_URL_JUCEHEADER__ + +/** + Represents a URL and has a bunch of useful functions to manipulate it. + + This class can be used to launch URLs in browsers, and also to create + InputStreams that can read from remote http or ftp sources. +*/ +class JUCE_API URL +{ +public: + + /** Creates an empty URL. */ + URL() throw(); + + /** Creates a URL from a string. */ + URL (const String& url); + + /** Creates a copy of another URL. */ + URL (const URL& other); + + /** Destructor. */ + ~URL() throw(); + + /** Copies this URL from another one. */ + const URL& operator= (const URL& other); + + /** Returns a string version of the URL. + + If includeGetParameters is true and any parameters have been set with the + withParameter() method, then the string will have these appended on the + end and url-encoded. + */ + const String toString (const bool includeGetParameters) const; + + /** True if it seems to be valid. */ + bool isWellFormed() const; + + /** Returns a copy of this URL, with a GET parameter added to the end. + + Any control characters in the value will be encoded. + + e.g. calling "withParameter ("amount", "some fish") for the url "www.fish.com" + would produce a new url whose toString(true) method would return + "www.fish.com?amount=some+fish". + */ + const URL withParameter (const String& parameterName, + const String& parameterValue) const; + + /** Returns a copy of this URl, with a file-upload type parameter added to it. + + When performing a POST where one of your parameters is a binary file, this + lets you specify the file. + + Note that the filename is stored, but the file itself won't actually be read + until this URL is later used to create a network input stream. + */ + const URL withFileToUpload (const String& parameterName, + const File& fileToUpload, + const String& mimeType) const; + + /** Returns a set of all the parameters encoded into the url. + + E.g. for the url "www.fish.com?type=haddock&amount=some+fish", this array would + contain two pairs: "type" => "haddock" and "amount" => "some fish". + + The values returned will have been cleaned up to remove any escape characters. + + @see getNamedParameter, withParameter + */ + const StringPairArray& getParameters() const throw(); + + /** Returns the set of files that should be uploaded as part of a POST operation. + + This is the set of files that were added to the URL with the withFileToUpload() + method. + */ + const StringPairArray& getFilesToUpload() const throw(); + + /** Returns the set of mime types associated with each of the upload files. + */ + const StringPairArray& getMimeTypesOfUploadFiles() const throw(); + + /** Tries to launch the system's default browser to open the URL. + + Returns true if this seems to have worked. + */ + bool launchInDefaultBrowser() const; + + /** Takes a guess as to whether a string might be a valid website address. + + This isn't foolproof! + */ + static bool isProbablyAWebsiteURL (const String& possibleURL); + + /** Takes a guess as to whether a string might be a valid email address. + + This isn't foolproof! + */ + static bool isProbablyAnEmailAddress (const String& possibleEmailAddress); + + /** This callback function can be used by the createInputStream() method. + + It allows your app to receive progress updates during a lengthy POST operation. If you + want to continue the operation, this should return true, or false to abort. + */ + typedef bool (OpenStreamProgressCallback) (void* context, int bytesSent, int totalBytes); + + /** Attempts to open a stream that can read from this URL. + + @param usePostCommand if true, it will try to do use a http 'POST' to pass + the paramters, otherwise it'll encode them into the + URL and do a 'GET'. + @param progressCallback if this is non-zero, it lets you supply a callback function + to keep track of the operation's progress. This can be useful + for lengthy POST operations, so that you can provide user feedback. + @param progressCallbackContext if a callback is specified, this value will be passed to + the function + */ + InputStream* createInputStream (const bool usePostCommand, + OpenStreamProgressCallback* const progressCallback = 0, + void* const progressCallbackContext = 0) const; + + /** Tries to download the entire contents of this URL into a binary data block. + + If it succeeds, this will return true and append the data it read onto the end + of the memory block. + + @param destData the memory block to append the new data to + @param usePostCommand whether to use a POST command to get the data (uses + a GET command if this is false) + @see readEntireTextStream, readEntireXmlStream + */ + bool readEntireBinaryStream (MemoryBlock& destData, + const bool usePostCommand = false) const; + + /** Tries to download the entire contents of this URL as a string. + + If it fails, this will return an empty string, otherwise it will return the + contents of the downloaded file. If you need to distinguish between a read + operation that fails and one that returns an empty string, you'll need to use + a different method, such as readEntireBinaryStream(). + + @param usePostCommand whether to use a POST command to get the data (uses + a GET command if this is false) + @see readEntireBinaryStream, readEntireXmlStream + */ + const String readEntireTextStream (const bool usePostCommand = false) const; + + /** Tries to download the entire contents of this URL and parse it as XML. + + If it fails, or if the text that it reads can't be parsed as XML, this will + return 0. + + When it returns a valid XmlElement object, the caller is responsibile for deleting + this object when no longer needed. + + @param usePostCommand whether to use a POST command to get the data (uses + a GET command if this is false) + + @see readEntireBinaryStream, readEntireTextStream + */ + XmlElement* readEntireXmlStream (const bool usePostCommand = false) const; + + /** Adds escape sequences to a string to encode any characters that aren't + legal in a URL. + + E.g. any spaces will be replaced with "%20". + + This is the opposite of removeEscapeChars(). + + @see removeEscapeChars + */ + static const String addEscapeChars (const String& stringToAddEscapeCharsTo); + + /** Replaces any escape character sequences in a string with their original + character codes. + + E.g. any instances of "%20" will be replaced by a space. + + This is the opposite of addEscapeChars(). + + @see addEscapeChars + */ + static const String removeEscapeChars (const String& stringToRemoveEscapeCharsFrom); + + juce_UseDebuggingNewOperator + +private: + String url; + StringPairArray parameters, filesToUpload, mimeTypes; +}; + +#endif // __JUCE_URL_JUCEHEADER__ +/********* End of inlined file: juce_URL.h *********/ + +#endif +#ifndef __JUCE_BUFFEREDINPUTSTREAM_JUCEHEADER__ + +/********* Start of inlined file: juce_BufferedInputStream.h *********/ +#ifndef __JUCE_BUFFEREDINPUTSTREAM_JUCEHEADER__ +#define __JUCE_BUFFEREDINPUTSTREAM_JUCEHEADER__ + +/** Wraps another input stream, and reads from it using an intermediate buffer + + If you're using an input stream such as a file input stream, and making lots of + small read accesses to it, it's probably sensible to wrap it in one of these, + so that the source stream gets accessed in larger chunk sizes, meaning less + work for the underlying stream. +*/ +class JUCE_API BufferedInputStream : public InputStream +{ +public: + + /** Creates a BufferedInputStream from an input source. + + @param sourceStream the source stream to read from + @param bufferSize the size of reservoir to use to buffer the source + @param deleteSourceWhenDestroyed whether the sourceStream that is passed in should be + deleted by this object when it is itself deleted. + */ + BufferedInputStream (InputStream* const sourceStream, + const int bufferSize, + const bool deleteSourceWhenDestroyed) throw(); + + /** Destructor. + + This may also delete the source stream, if that option was chosen when the + buffered stream was created. + */ + ~BufferedInputStream() throw(); + + int64 getTotalLength(); + int64 getPosition(); + bool setPosition (int64 newPosition); + int read (void* destBuffer, int maxBytesToRead); + const String readString(); + bool isExhausted(); + + juce_UseDebuggingNewOperator + +private: + InputStream* const source; + const bool deleteSourceWhenDestroyed; + int bufferSize; + int64 position, lastReadPos, bufferStart, bufferOverlap; + char* buffer; + void ensureBuffered(); + + BufferedInputStream (const BufferedInputStream&); + const BufferedInputStream& operator= (const BufferedInputStream&); +}; + +#endif // __JUCE_BUFFEREDINPUTSTREAM_JUCEHEADER__ +/********* End of inlined file: juce_BufferedInputStream.h *********/ + +#endif +#ifndef __JUCE_FILEINPUTSOURCE_JUCEHEADER__ + +/********* Start of inlined file: juce_FileInputSource.h *********/ +#ifndef __JUCE_FILEINPUTSOURCE_JUCEHEADER__ +#define __JUCE_FILEINPUTSOURCE_JUCEHEADER__ + +/********* Start of inlined file: juce_InputSource.h *********/ +#ifndef __JUCE_INPUTSOURCE_JUCEHEADER__ +#define __JUCE_INPUTSOURCE_JUCEHEADER__ + +/** + A lightweight object that can create a stream to read some kind of resource. + + This may be used to refer to a file, or some other kind of source, allowing a + caller to create an input stream that can read from it when required. + + @see FileInputSource +*/ +class JUCE_API InputSource +{ +public: + + InputSource() throw() {} + + /** Destructor. */ + virtual ~InputSource() {} + + /** Returns a new InputStream to read this item. + + @returns an inputstream that the caller will delete, or 0 if + the filename isn't found. + */ + virtual InputStream* createInputStream() = 0; + + /** Returns a new InputStream to read an item, relative. + + @param relatedItemPath the relative pathname of the resource that is required + @returns an inputstream that the caller will delete, or 0 if + the item isn't found. + */ + virtual InputStream* createInputStreamFor (const String& relatedItemPath) = 0; + + /** Returns a hash code that uniquely represents this item. + */ + virtual int64 hashCode() const = 0; + + juce_UseDebuggingNewOperator +}; + +#endif // __JUCE_INPUTSOURCE_JUCEHEADER__ +/********* End of inlined file: juce_InputSource.h *********/ + +/** + A type of InputSource that represents a normal file. + + @see InputSource +*/ +class JUCE_API FileInputSource : public InputSource +{ +public: + + FileInputSource (const File& file) throw(); + ~FileInputSource(); + + InputStream* createInputStream(); + InputStream* createInputStreamFor (const String& relatedItemPath); + int64 hashCode() const; + + juce_UseDebuggingNewOperator + +private: + const File file; + + FileInputSource (const FileInputSource&); + const FileInputSource& operator= (const FileInputSource&); +}; + +#endif // __JUCE_FILEINPUTSOURCE_JUCEHEADER__ +/********* End of inlined file: juce_FileInputSource.h *********/ + +#endif +#ifndef __JUCE_GZIPCOMPRESSOROUTPUTSTREAM_JUCEHEADER__ + +/********* Start of inlined file: juce_GZIPCompressorOutputStream.h *********/ +#ifndef __JUCE_GZIPCOMPRESSOROUTPUTSTREAM_JUCEHEADER__ +#define __JUCE_GZIPCOMPRESSOROUTPUTSTREAM_JUCEHEADER__ + +/** + A stream which uses zlib to compress the data written into it. + + @see GZIPDecompressorInputStream +*/ +class JUCE_API GZIPCompressorOutputStream : public OutputStream +{ +public: + + /** Creates a compression stream. + + @param destStream the stream into which the compressed data should + be written + @param compressionLevel how much to compress the data, between 1 and 9, where + 1 is the fastest/lowest compression, and 9 is the + slowest/highest compression. Any value outside this range + indicates that a default compression level should be used. + @param deleteDestStreamWhenDestroyed whether or not to delete the destStream object when + this stream is destroyed + @param noWrap this is used internally by the ZipFile class + and should be ignored by user applications + */ + GZIPCompressorOutputStream (OutputStream* const destStream, + int compressionLevel = 0, + const bool deleteDestStreamWhenDestroyed = false, + const bool noWrap = false); + + /** Destructor. */ + ~GZIPCompressorOutputStream(); + + void flush(); + int64 getPosition(); + bool setPosition (int64 newPosition); + bool write (const void* destBuffer, int howMany); + + juce_UseDebuggingNewOperator + +private: + OutputStream* const destStream; + const bool deleteDestStream; + uint8* buffer; + void* helper; + bool doNextBlock(); + + GZIPCompressorOutputStream (const GZIPCompressorOutputStream&); + const GZIPCompressorOutputStream& operator= (const GZIPCompressorOutputStream&); +}; + +#endif // __JUCE_GZIPCOMPRESSOROUTPUTSTREAM_JUCEHEADER__ +/********* End of inlined file: juce_GZIPCompressorOutputStream.h *********/ + +#endif +#ifndef __JUCE_GZIPDECOMPRESSORINPUTSTREAM_JUCEHEADER__ + +/********* Start of inlined file: juce_GZIPDecompressorInputStream.h *********/ +#ifndef __JUCE_GZIPDECOMPRESSORINPUTSTREAM_JUCEHEADER__ +#define __JUCE_GZIPDECOMPRESSORINPUTSTREAM_JUCEHEADER__ + +/** + This stream will decompress a source-stream using zlib. + + Tip: if you're reading lots of small items from one of these streams, you + can increase the performance enormously by passing it through a + BufferedInputStream, so that it has to read larger blocks less often. + + @see GZIPCompressorOutputStream +*/ +class JUCE_API GZIPDecompressorInputStream : public InputStream +{ +public: + + /** Creates a decompressor stream. + + @param sourceStream the stream to read from + @param deleteSourceWhenDestroyed whether or not to delete the source stream + when this object is destroyed + @param noWrap this is used internally by the ZipFile class + and should be ignored by user applications + @param uncompressedStreamLength if the creator knows the length that the + uncompressed stream will be, then it can supply this + value, which will be returned by getTotalLength() + */ + GZIPDecompressorInputStream (InputStream* const sourceStream, + const bool deleteSourceWhenDestroyed, + const bool noWrap = false, + const int64 uncompressedStreamLength = -1); + + /** Destructor. */ + ~GZIPDecompressorInputStream(); + + int64 getPosition(); + bool setPosition (int64 pos); + int64 getTotalLength(); + bool isExhausted(); + int read (void* destBuffer, int maxBytesToRead); + + juce_UseDebuggingNewOperator + +private: + InputStream* const sourceStream; + const int64 uncompressedStreamLength; + const bool deleteSourceWhenDestroyed, noWrap; + bool isEof; + int activeBufferSize; + int64 originalSourcePos; + uint8* buffer; + void* helper; + + GZIPDecompressorInputStream (const GZIPDecompressorInputStream&); + const GZIPDecompressorInputStream& operator= (const GZIPDecompressorInputStream&); +}; + +#endif // __JUCE_GZIPDECOMPRESSORINPUTSTREAM_JUCEHEADER__ +/********* End of inlined file: juce_GZIPDecompressorInputStream.h *********/ + +#endif +#ifndef __JUCE_INPUTSOURCE_JUCEHEADER__ + +#endif +#ifndef __JUCE_MEMORYINPUTSTREAM_JUCEHEADER__ + +/********* Start of inlined file: juce_MemoryInputStream.h *********/ +#ifndef __JUCE_MEMORYINPUTSTREAM_JUCEHEADER__ +#define __JUCE_MEMORYINPUTSTREAM_JUCEHEADER__ + +/** + Allows a block of data and to be accessed as a stream. + + This can either be used to refer to a shared block of memory, or can make its + own internal copy of the data when the MemoryInputStream is created. +*/ +class JUCE_API MemoryInputStream : public InputStream +{ +public: + + /** Creates a MemoryInputStream. + + @param sourceData the block of data to use as the stream's source + @param sourceDataSize the number of bytes in the source data block + @param keepInternalCopyOfData if false, the stream will just keep a pointer to + the source data, so this data shouldn't be changed + for the lifetime of the stream; if this parameter is + true, the stream will make its own copy of the + data and use that. + */ + MemoryInputStream (const void* const sourceData, + const int sourceDataSize, + const bool keepInternalCopyOfData) throw(); + + /** Destructor. */ + ~MemoryInputStream() throw(); + + int64 getPosition(); + bool setPosition (int64 pos); + int64 getTotalLength(); + bool isExhausted(); + int read (void* destBuffer, int maxBytesToRead); + + juce_UseDebuggingNewOperator + +private: + const char* data; + int dataSize, position; + MemoryBlock internalCopy; +}; + +#endif // __JUCE_MEMORYINPUTSTREAM_JUCEHEADER__ +/********* End of inlined file: juce_MemoryInputStream.h *********/ + +#endif +#ifndef __JUCE_MEMORYOUTPUTSTREAM_JUCEHEADER__ + +/********* Start of inlined file: juce_MemoryOutputStream.h *********/ +#ifndef __JUCE_MEMORYOUTPUTSTREAM_JUCEHEADER__ +#define __JUCE_MEMORYOUTPUTSTREAM_JUCEHEADER__ + +/** Writes data to an internal memory buffer, which grows as required. + + The data that was written into the stream can then be accessed later as + a contiguous block of memory. +*/ +class JUCE_API MemoryOutputStream : public OutputStream +{ +public: + + /** Creates a memory stream ready for writing into. + + @param initialSize the intial amount of space to allocate for writing into + @param granularity the increments by which the internal storage will be increased + @param memoryBlockToWriteTo if this is non-zero, then this block will be used as the + place that the data gets stored. If it's zero, the stream + will allocate its own storage internally, which you can + access using getData() and getDataSize() + */ + MemoryOutputStream (const int initialSize = 256, + const int granularity = 256, + MemoryBlock* const memoryBlockToWriteTo = 0) throw(); + + /** Destructor. + + This will free any data that was written to it. + */ + ~MemoryOutputStream() throw(); + + /** Returns a pointer to the data that has been written to the stream. + + @see getDataSize + */ + const char* getData() throw(); + + /** Returns the number of bytes of data that have been written to the stream. + + @see getData + */ + int getDataSize() const throw(); + + /** Resets the stream, clearing any data that has been written to it so far. */ + void reset() throw(); + + void flush(); + bool write (const void* buffer, int howMany); + int64 getPosition(); + bool setPosition (int64 newPosition); + + juce_UseDebuggingNewOperator + +private: + MemoryBlock* data; + int position, size, blockSize; + bool ownsMemoryBlock; +}; + +#endif // __JUCE_MEMORYOUTPUTSTREAM_JUCEHEADER__ +/********* End of inlined file: juce_MemoryOutputStream.h *********/ + +#endif +#ifndef __JUCE_SUBREGIONSTREAM_JUCEHEADER__ + +/********* Start of inlined file: juce_SubregionStream.h *********/ +#ifndef __JUCE_SUBREGIONSTREAM_JUCEHEADER__ +#define __JUCE_SUBREGIONSTREAM_JUCEHEADER__ + +/** Wraps another input stream, and reads from a specific part of it. + + This lets you take a subsection of a stream and present it as an entire + stream in its own right. +*/ +class JUCE_API SubregionStream : public InputStream +{ +public: + + /** Creates a SubregionStream from an input source. + + @param sourceStream the source stream to read from + @param startPositionInSourceStream this is the position in the source stream that + corresponds to position 0 in this stream + @param lengthOfSourceStream this specifies the maximum number of bytes + from the source stream that will be passed through + by this stream. When the position of this stream + exceeds lengthOfSourceStream, it will the end-of-stream. + If the length passed in here is greater than the length + of the source stream (as returned by getTotalLength()), + then the smaller value will be used. + Passing a negative value for this parameter means it + will keep reading until the source's end-of-stream. + @param deleteSourceWhenDestroyed whether the sourceStream that is passed in should be + deleted by this object when it is itself deleted. + */ + SubregionStream (InputStream* const sourceStream, + const int64 startPositionInSourceStream, + const int64 lengthOfSourceStream, + const bool deleteSourceWhenDestroyed) throw(); + + /** Destructor. + + This may also delete the source stream, if that option was chosen when the + buffered stream was created. + */ + ~SubregionStream() throw(); + + int64 getTotalLength(); + int64 getPosition(); + bool setPosition (int64 newPosition); + int read (void* destBuffer, int maxBytesToRead); + bool isExhausted(); + + juce_UseDebuggingNewOperator + +private: + InputStream* const source; + const bool deleteSourceWhenDestroyed; + const int64 startPositionInSourceStream, lengthOfSourceStream; + + SubregionStream (const SubregionStream&); + const SubregionStream& operator= (const SubregionStream&); +}; + +#endif // __JUCE_SUBREGIONSTREAM_JUCEHEADER__ +/********* End of inlined file: juce_SubregionStream.h *********/ + +#endif +#ifndef __JUCE_PERFORMANCECOUNTER_JUCEHEADER__ + +/********* Start of inlined file: juce_PerformanceCounter.h *********/ +#ifndef __JUCE_PERFORMANCECOUNTER_JUCEHEADER__ +#define __JUCE_PERFORMANCECOUNTER_JUCEHEADER__ + +/** A timer for measuring performance of code and dumping the results to a file. + + e.g. @code + + PerformanceCounter pc ("fish", 50, "/temp/myfishlog.txt"); + + for (;;) + { + pc.start(); + + doSomethingFishy(); + + pc.stop(); + } + @endcode + + In this example, the time of each period between calling start/stop will be + measured and averaged over 50 runs, and the results printed to a file + every 50 times round the loop. +*/ +class JUCE_API PerformanceCounter +{ +public: + + /** Creates a PerformanceCounter object. + + @param counterName the name used when printing out the statistics + @param runsPerPrintout the number of start/stop iterations before calling + printStatistics() + @param loggingFile a file to dump the results to - if this is File::nonexistent, + the results are just written to the debugger output + */ + PerformanceCounter (const String& counterName, + int runsPerPrintout = 100, + const File& loggingFile = File::nonexistent); + + /** Destructor. */ + ~PerformanceCounter(); + + /** Starts timing. + + @see stop + */ + void start(); + + /** Stops timing and prints out the results. + + The number of iterations before doing a printout of the + results is set in the constructor. + + @see start + */ + void stop(); + + /** Dumps the current metrics to the debugger output and to a file. + + As well as using Logger::outputDebugString to print the results, + this will write then to the file specified in the constructor (if + this was valid). + */ + void printStatistics(); + + juce_UseDebuggingNewOperator + +private: + + String name; + int numRuns, runsPerPrint; + double totalTime; + int64 started; + File outputFile; +}; + +#endif // __JUCE_PERFORMANCECOUNTER_JUCEHEADER__ +/********* End of inlined file: juce_PerformanceCounter.h *********/ + +#endif +#ifndef __JUCE_PLATFORMUTILITIES_JUCEHEADER__ + +/********* Start of inlined file: juce_PlatformUtilities.h *********/ +#ifndef __JUCE_PLATFORMUTILITIES_JUCEHEADER__ +#define __JUCE_PLATFORMUTILITIES_JUCEHEADER__ + +/** + A collection of miscellaneous platform-specific utilities. + +*/ +class JUCE_API PlatformUtilities +{ +public: + + /** Plays the operating system's default alert 'beep' sound. */ + static void beep(); + + static bool launchEmailWithAttachments (const String& targetEmailAddress, + const String& emailSubject, + const String& bodyText, + const StringArray& filesToAttach); + +#if JUCE_MAC || DOXYGEN + + /** MAC ONLY - Turns a String into a pascal string. */ + static void copyToStr255 (Str255& d, const String& s); + /** MAC ONLY - Turns a String into a pascal string. */ + static void copyToStr63 (Str63& d, const String& s); + + /** MAC ONLY - Turns a Core CF String into a juce one. */ + static const String cfStringToJuceString (CFStringRef cfString); + + /** MAC ONLY - Turns a juce string into a Core CF one. */ + static CFStringRef juceStringToCFString (const String& s); + + /** MAC ONLY - Converts a UTF16 string to a Juce String. */ + static const String convertUTF16ToString (const UniChar* utf16); + + /** MAC ONLY - Turns a file path into an FSSpec, returning true if it succeeds. */ + static bool makeFSSpecFromPath (FSSpec* destFSSpec, const String& path); + + /** MAC ONLY - Turns a file path into an FSRef, returning true if it succeeds. */ + static bool makeFSRefFromPath (FSRef* destFSRef, const String& path); + + /** MAC ONLY - Turns an FSRef into a juce string path. */ + static const String makePathFromFSRef (FSRef* file); + + /** MAC ONLY - Converts any decomposed unicode characters in a string into + their precomposed equivalents. + */ + static const String convertToPrecomposedUnicode (const String& s); + + /** MAC ONLY - Gets the type of a file from the file's resources. */ + static OSType getTypeOfFile (const String& filename); + + /** MAC ONLY - Returns true if this file is actually a bundle. */ + static bool isBundle (const String& filename); + +#endif + +#if JUCE_WIN32 || DOXYGEN + + // Some registry helper functions: + + /** WIN32 ONLY - Returns a string from the registry. + + The path is a string for the entire path of a value in the registry, + e.g. "HKEY_CURRENT_USER\Software\foo\bar" + */ + static const String getRegistryValue (const String& regValuePath, + const String& defaultValue = String::empty); + + /** WIN32 ONLY - Sets a registry value as a string. + + This will take care of creating any groups needed to get to the given + registry value. + */ + static void setRegistryValue (const String& regValuePath, + const String& value); + + /** WIN32 ONLY - Returns true if the given value exists in the registry. */ + static bool registryValueExists (const String& regValuePath); + + /** WIN32 ONLY - Deletes a registry value. */ + static void deleteRegistryValue (const String& regValuePath); + + /** WIN32 ONLY - Deletes a registry key (which is registry-talk for 'folder'). */ + static void deleteRegistryKey (const String& regKeyPath); + + /** WIN32 ONLY - This returns the HINSTANCE of the current module. + + In a normal Juce application this will be set to the module handle + of the application executable. + + If you're writing a DLL using Juce and plan to use any Juce messaging or + windows, you'll need to make sure you use the setCurrentModuleInstanceHandle() + to set the correct module handle in your DllMain() function, because + the win32 system relies on the correct instance handle when opening windows. + */ + static void* JUCE_CALLTYPE getCurrentModuleInstanceHandle() throw(); + + /** WIN32 ONLY - Sets a new module handle to be used by the library. + + @see getCurrentModuleInstanceHandle() + */ + static void JUCE_CALLTYPE setCurrentModuleInstanceHandle (void* newHandle) throw(); + +#endif + + /** Clears the floating point unit's flags. + + Only has an effect under win32, currently. + */ + static void fpuReset(); + +#if JUCE_LINUX || DOXYGEN + +#endif +}; + +#if JUCE_MAC + +/** + A wrapper class for picking up events from an Apple IR remote control device. + + To use it, just create a subclass of this class, implementing the buttonPressed() + callback, then call start() and stop() to start or stop receiving events. +*/ +class JUCE_API AppleRemoteDevice +{ +public: + + AppleRemoteDevice(); + virtual ~AppleRemoteDevice(); + + /** The set of buttons that may be pressed. + @see buttonPressed + */ + enum ButtonType + { + menuButton = 0, /**< The menu button (if it's held for a short time). */ + playButton, /**< The play button. */ + plusButton, /**< The plus or volume-up button. */ + minusButton, /**< The minus or volume-down button. */ + rightButton, /**< The right button (if it's held for a short time). */ + leftButton, /**< The left button (if it's held for a short time). */ + rightButton_Long, /**< The right button (if it's held for a long time). */ + leftButton_Long, /**< The menu button (if it's held for a long time). */ + menuButton_Long, /**< The menu button (if it's held for a long time). */ + playButtonSleepMode, + switched + }; + + /** Override this method to receive the callback about a button press. + + The callback will happen on the application's message thread. + + Some buttons trigger matching up and down events, in which the isDown parameter + will be true and then false. Others only send a single event when the + button is pressed. + */ + virtual void buttonPressed (const ButtonType buttonId, const bool isDown) = 0; + + /** Starts the device running and responding to events. + + Returns true if it managed to open the device. + + @param inExclusiveMode if true, the remote will be grabbed exclusively for this app, + and will not be available to any other part of the system. If + false, it will be shared with other apps. + @see stop + */ + bool start (const bool inExclusiveMode) throw(); + + /** Stops the device running. + @see start + */ + void stop() throw(); + + /** Returns true if the device has been started successfully. + */ + bool isActive() const throw(); + + /** Returns the ID number of the remote, if it has sent one. + */ + int getRemoteId() const throw() { return remoteId; } + + juce_UseDebuggingNewOperator + + /** @internal */ + void handleCallbackInternal(); + +private: + void* device; + void* queue; + int remoteId; + + bool open (const bool openInExclusiveMode) throw(); +}; + +#endif + +#endif // __JUCE_PLATFORMUTILITIES_JUCEHEADER__ +/********* End of inlined file: juce_PlatformUtilities.h *********/ + +#endif +#ifndef __JUCE_UUID_JUCEHEADER__ + +/********* Start of inlined file: juce_Uuid.h *********/ +#ifndef __JUCE_UUID_JUCEHEADER__ +#define __JUCE_UUID_JUCEHEADER__ + +/** + A universally unique 128-bit identifier. + + This class generates very random unique numbers based on the system time + and MAC addresses if any are available. It's extremely unlikely that two identical + UUIDs would ever be created by chance. + + The class includes methods for saving the ID as a string or as raw binary data. +*/ +class JUCE_API Uuid +{ +public: + + /** Creates a new unique ID. */ + Uuid(); + + /** Destructor. */ + ~Uuid() throw(); + + /** Creates a copy of another UUID. */ + Uuid (const Uuid& other); + + /** Copies another UUID. */ + Uuid& operator= (const Uuid& other); + + /** Returns true if the ID is zero. */ + bool isNull() const throw(); + + /** Compares two UUIDs. */ + bool operator== (const Uuid& other) const; + + /** Compares two UUIDs. */ + bool operator!= (const Uuid& other) const; + + /** Returns a stringified version of this UUID. + + A Uuid object can later be reconstructed from this string using operator= or + the constructor that takes a string parameter. + + @returns a 32 character hex string. + */ + const String toString() const; + + /** Creates an ID from an encoded string version. + + @see toString + */ + Uuid (const String& uuidString); + + /** Copies from a stringified UUID. + + The string passed in should be one that was created with the toString() method. + */ + Uuid& operator= (const String& uuidString); + + /** Returns a pointer to the internal binary representation of the ID. + + This is an array of 16 bytes. To reconstruct a Uuid from its data, use + the constructor or operator= method that takes an array of uint8s. + */ + const uint8* getRawData() const throw() { return value.asBytes; } + + /** Creates a UUID from a 16-byte array. + + @see getRawData + */ + Uuid (const uint8* const rawData); + + /** Sets this UUID from 16-bytes of raw data. */ + Uuid& operator= (const uint8* const rawData); + + juce_UseDebuggingNewOperator + +private: + union + { + uint8 asBytes [16]; + int asInt[4]; + int64 asInt64[2]; + + } value; +}; + +#endif // __JUCE_UUID_JUCEHEADER__ +/********* End of inlined file: juce_Uuid.h *********/ + +#endif +#ifndef __JUCE_ZIPFILE_JUCEHEADER__ + +/********* Start of inlined file: juce_ZipFile.h *********/ +#ifndef __JUCE_ZIPFILE_JUCEHEADER__ +#define __JUCE_ZIPFILE_JUCEHEADER__ + +/** + Decodes a ZIP file from a stream. + + This can enumerate the items in a ZIP file and can create suitable stream objects + to read each one. +*/ +class JUCE_API ZipFile +{ +public: + + /** Creates a ZipFile for a given stream. + + @param inputStream the stream to read from + @param deleteStreamWhenDestroyed if set to true, the object passed-in + will be deleted when this ZipFile object is deleted + */ + ZipFile (InputStream* const inputStream, + const bool deleteStreamWhenDestroyed) throw(); + + /** Creates a ZipFile based for a file. */ + ZipFile (const File& file); + + /** Destructor. */ + ~ZipFile() throw(); + + /** + Contains information about one of the entries in a ZipFile. + + @see ZipFile::getEntry + */ + struct ZipEntry + { + /** The name of the file, which may also include a partial pathname. */ + String filename; + + /** The file's original size. */ + unsigned int uncompressedSize; + + /** The last time the file was modified. */ + Time fileTime; + }; + + /** Returns the number of items in the zip file. */ + int getNumEntries() const throw(); + + /** Returns a structure that describes one of the entries in the zip file. + + This may return zero if the index is out of range. + + @see ZipFile::ZipEntry + */ + const ZipEntry* getEntry (const int index) const throw(); + + /** Returns the index of the first entry with a given filename. + + This uses a case-sensitive comparison to look for a filename in the + list of entries. It might return -1 if no match is found. + + @see ZipFile::ZipEntry + */ + int getIndexOfFileName (const String& fileName) const throw(); + + /** Returns a structure that describes one of the entries in the zip file. + + This uses a case-sensitive comparison to look for a filename in the + list of entries. It might return 0 if no match is found. + + @see ZipFile::ZipEntry + */ + const ZipEntry* getEntry (const String& fileName) const throw(); + + /** Sorts the list of entries, based on the filename. + */ + void sortEntriesByFilename(); + + /** Creates a stream that can read from one of the zip file's entries. + + The stream that is returned must be deleted by the caller (and + zero might be returned if a stream can't be opened for some reason). + + The stream must not be used after the ZipFile object that created + has been deleted. + */ + InputStream* createStreamForEntry (const int index); + + /** Uncompresses all of the files in the zip file. + + This will expand all the entires into a target directory. The relative + paths of the entries are used. + + @param targetDirectory the root folder to uncompress to + @param shouldOverwriteFiles whether to overwrite existing files with similarly-named ones + */ + void uncompressTo (const File& targetDirectory, + const bool shouldOverwriteFiles = true); + + juce_UseDebuggingNewOperator + +private: + VoidArray entries; + friend class ZipInputStream; + CriticalSection lock; + InputStream* source; + File sourceFile; + bool isFromCustomStream, deleteStreamWhenDestroyed; + int numEntries, centralRecStart; + +#ifdef JUCE_DEBUG + int numOpenStreams; +#endif + + void init(); + int findEndOfZipEntryTable(); + + ZipFile (const ZipFile&); + const ZipFile& operator= (const ZipFile&); +}; + +#endif // __JUCE_ZIPFILE_JUCEHEADER__ +/********* End of inlined file: juce_ZipFile.h *********/ + +#endif +#ifndef __JUCE_CHARACTERFUNCTIONS_JUCEHEADER__ + +#endif +#ifndef __JUCE_LOCALISEDSTRINGS_JUCEHEADER__ + +/********* Start of inlined file: juce_LocalisedStrings.h *********/ +#ifndef __JUCE_LOCALISEDSTRINGS_JUCEHEADER__ +#define __JUCE_LOCALISEDSTRINGS_JUCEHEADER__ + +/** Used in the same way as the T(text) macro, this will attempt to translate a + string into a localised version using the LocalisedStrings class. + + @see LocalisedStrings +*/ +#define TRANS(stringLiteral) \ + LocalisedStrings::translateWithCurrentMappings (stringLiteral) + +/** + Used to convert strings to localised foreign-language versions. + + This is basically a look-up table of strings and their translated equivalents. + It can be loaded from a text file, so that you can supply a set of localised + versions of strings that you use in your app. + + To use it in your code, simply call the translate() method on each string that + might have foreign versions, and if none is found, the method will just return + the original string. + + The translation file should start with some lines specifying a description of + the language it contains, and also a list of ISO country codes where it might + be appropriate to use the file. After that, each line of the file should contain + a pair of quoted strings with an '=' sign. + + E.g. for a french translation, the file might be: + + @code + language: French + countries: fr be mc ch lu + + "hello" = "bonjour" + "goodbye" = "au revoir" + @endcode + + If the strings need to contain a quote character, they can use '\"' instead, and + if the first non-whitespace character on a line isn't a quote, then it's ignored, + (you can use this to add comments). + + Note that this is a singleton class, so don't create or destroy the object directly. + There's also a TRANS(text) macro defined to make it easy to use the this. + + E.g. @code + printSomething (TRANS("hello")); + @endcode + + This macro is used in the Juce classes themselves, so your application has a chance to + intercept and translate any internal Juce text strings that might be shown. (You can easily + get a list of all the messages by searching for the TRANS() macro in the Juce source + code). +*/ +class JUCE_API LocalisedStrings +{ +public: + + /** Creates a set of translations from the text of a translation file. + + When you create one of these, you can call setCurrentMappings() to make it + the set of mappings that the system's using. + */ + LocalisedStrings (const String& fileContents) throw(); + + /** Creates a set of translations from a file. + + When you create one of these, you can call setCurrentMappings() to make it + the set of mappings that the system's using. + */ + LocalisedStrings (const File& fileToLoad) throw(); + + /** Destructor. */ + ~LocalisedStrings() throw(); + + /** Selects the current set of mappings to be used by the system. + + The object you pass in will be automatically deleted when no longer needed, so + don't keep a pointer to it. You can also pass in zero to remove the current + mappings. + + See also the TRANS() macro, which uses the current set to do its translation. + + @see translateWithCurrentMappings + */ + static void setCurrentMappings (LocalisedStrings* newTranslations) throw(); + + /** Returns the currently selected set of mappings. + + This is the object that was last passed to setCurrentMappings(). It may + be 0 if none has been created. + */ + static LocalisedStrings* getCurrentMappings() throw(); + + /** Tries to translate a string using the currently selected set of mappings. + + If no mapping has been set, or if the mapping doesn't contain a translation + for the string, this will just return the original string. + + See also the TRANS() macro, which uses this method to do its translation. + + @see setCurrentMappings, getCurrentMappings + */ + static const String translateWithCurrentMappings (const String& text) throw(); + + /** Tries to translate a string using the currently selected set of mappings. + + If no mapping has been set, or if the mapping doesn't contain a translation + for the string, this will just return the original string. + + See also the TRANS() macro, which uses this method to do its translation. + + @see setCurrentMappings, getCurrentMappings + */ + static const String translateWithCurrentMappings (const char* text) throw(); + + /** Attempts to look up a string and return its localised version. + + If the string isn't found in the list, the original string will be returned. + */ + const String translate (const String& text) const throw(); + + /** Returns the name of the language specified in the translation file. + + This is specified in the file using a line starting with "language:", e.g. + @code + language: german + @endcode + */ + const String getLanguageName() const throw() { return languageName; } + + /** Returns the list of suitable country codes listed in the translation file. + + These is specified in the file using a line starting with "countries:", e.g. + @code + countries: fr be mc ch lu + @endcode + + The country codes are supposed to be 2-character ISO complient codes. + */ + const StringArray getCountryCodes() const throw() { return countryCodes; } + + juce_UseDebuggingNewOperator + +private: + String languageName; + StringArray countryCodes; + StringPairArray translations; + + void loadFromText (const String& fileContents) throw(); +}; + +#endif // __JUCE_LOCALISEDSTRINGS_JUCEHEADER__ +/********* End of inlined file: juce_LocalisedStrings.h *********/ + +#endif +#ifndef __JUCE_STRING_JUCEHEADER__ + +#endif +#ifndef __JUCE_STRINGARRAY_JUCEHEADER__ + +#endif +#ifndef __JUCE_STRINGPAIRARRAY_JUCEHEADER__ + +#endif +#ifndef __JUCE_XMLDOCUMENT_JUCEHEADER__ + +/********* Start of inlined file: juce_XmlDocument.h *********/ +#ifndef __JUCE_XMLDOCUMENT_JUCEHEADER__ +#define __JUCE_XMLDOCUMENT_JUCEHEADER__ + +/** + Parses a text-based XML document and creates an XmlElement object from it. + + The parser will parse DTDs to load external entities but won't + check the document for validity against the DTD. + + e.g. + @code + + XmlDocument myDocument (File ("myfile.xml")); + XmlElement* mainElement = myDocument.getDocumentElement(); + + if (mainElement == 0) + { + String error = myDocument.getLastParseError(); + } + else + { + ..use the element + } + + @endcode + + @see XmlElement +*/ +class JUCE_API XmlDocument +{ +public: + + /** Creates an XmlDocument from the xml text. + + The text doesn't actually get parsed until the getDocumentElement() method is + called. + */ + XmlDocument (const String& documentText) throw(); + + /** Creates an XmlDocument from a file. + + The text doesn't actually get parsed until the getDocumentElement() method is + called. + */ + XmlDocument (const File& file); + + /** Destructor. */ + ~XmlDocument() throw(); + + /** Creates an XmlElement object to represent the main document node. + + This method will do the actual parsing of the text, and if there's a + parse error, it may returns 0 (and you can find out the error using + the getLastParseError() method). + + @param onlyReadOuterDocumentElement if true, the parser will only read the + first section of the file, and will only + return the outer document element - this + allows quick checking of large files to + see if they contain the correct type of + tag, without having to parse the entire file + @returns a new XmlElement which the caller will need to delete, or null if + there was an error. + @see getLastParseError + */ + XmlElement* getDocumentElement (const bool onlyReadOuterDocumentElement = false); + + /** Returns the parsing error that occurred the last time getDocumentElement was called. + + @returns the error, or an empty string if there was no error. + */ + const String& getLastParseError() const throw(); + + /** Sets an input source object to use for parsing documents that reference external entities. + + If the document has been created from a file, this probably won't be needed, but + if you're parsing some text and there might be a DTD that references external + files, you may need to create a custom input source that can retrieve the + other files it needs. + + The object that is passed-in will be deleted automatically when no longer needed. + + @see InputSource + */ + void setInputSource (InputSource* const newSource) throw(); + + juce_UseDebuggingNewOperator + +private: + String originalText; + const tchar* input; + bool outOfData, errorOccurred; + + bool identifierLookupTable [128]; + String lastError, dtdText; + StringArray tokenisedDTD; + bool needToLoadDTD; + InputSource* inputSource; + + void setLastError (const String& desc, const bool carryOn) throw(); + void skipHeader() throw(); + void skipNextWhiteSpace() throw(); + tchar readNextChar() throw(); + XmlElement* readNextElement (const bool alsoParseSubElements) throw(); + void readChildElements (XmlElement* parent) throw(); + int findNextTokenLength() throw(); + void readQuotedString (String& result) throw(); + void readEntity (String& result) throw(); + + const String getFileContents (const String& filename) const; + const String expandEntity (const String& entity); + const String expandExternalEntity (const String& entity); + const String getParameterEntity (const String& entity); +}; + +#endif // __JUCE_XMLDOCUMENT_JUCEHEADER__ +/********* End of inlined file: juce_XmlDocument.h *********/ + +#endif +#ifndef __JUCE_XMLELEMENT_JUCEHEADER__ + +#endif +#ifndef __JUCE_CRITICALSECTION_JUCEHEADER__ + +#endif +#ifndef __JUCE_INTERPROCESSLOCK_JUCEHEADER__ + +/********* Start of inlined file: juce_InterProcessLock.h *********/ +#ifndef __JUCE_INTERPROCESSLOCK_JUCEHEADER__ +#define __JUCE_INTERPROCESSLOCK_JUCEHEADER__ + +/** + Acts as a critical section which processes can use to block each other. + + @see CriticalSection +*/ +class JUCE_API InterProcessLock +{ +public: + + /** Creates a lock object. + + @param name a name that processes will use to identify this lock object + */ + InterProcessLock (const String& name) throw(); + + /** Destructor. + + This will also release the lock if it's currently held by this process. + */ + ~InterProcessLock() throw(); + + /** Attempts to lock the critical section. + + @param timeOutMillisecs how many milliseconds to wait if the lock + is already held by another process - a value of + 0 will return immediately, negative values will wait + forever + @returns true if the lock could be gained within the timeout period, or + false if the timeout expired. + */ + bool enter (int timeOutMillisecs = -1) throw(); + + /** Releases the lock if it's currently held by this process. + */ + void exit() throw(); + + juce_UseDebuggingNewOperator + +private: + + void* internal; + String name; + int reentrancyLevel; + + InterProcessLock (const InterProcessLock&); + const InterProcessLock& operator= (const InterProcessLock&); +}; + +#endif // __JUCE_INTERPROCESSLOCK_JUCEHEADER__ +/********* End of inlined file: juce_InterProcessLock.h *********/ + +#endif +#ifndef __JUCE_PROCESS_JUCEHEADER__ + +/********* Start of inlined file: juce_Process.h *********/ +#ifndef __JUCE_PROCESS_JUCEHEADER__ +#define __JUCE_PROCESS_JUCEHEADER__ + +/** Represents the current executable's process. + + This contains methods for controlling the current application at the + process-level. + + @see Thread, JUCEApplication +*/ +class JUCE_API Process +{ +public: + + enum ProcessPriority + { + LowPriority = 0, + NormalPriority = 1, + HighPriority = 2, + RealtimePriority = 3 + }; + + /** Changes the current process's priority. + + @param priority the process priority, where + 0=low, 1=normal, 2=high, 3=realtime + */ + static void setPriority (const ProcessPriority priority); + + /** Kills the current process immediately. + + This is an emergency process terminator that kills the application + immediately - it's intended only for use only when something goes + horribly wrong. + + @see JUCEApplication::quit + */ + static void terminate(); + + /** Returns true if this application process is the one that the user is + currently using. + */ + static bool isForegroundProcess() throw(); + + /** Raises the current process's privilege level. + + Does nothing if this isn't supported by the current OS, or if process + privilege level is fixed. + */ + static void raisePrivilege(); + + /** Lowers the current process's privilege level. + + Does nothing if this isn't supported by the current OS, or if process + privilege level is fixed. + */ + static void lowerPrivilege(); + + /** Returns true if this process is being hosted by a debugger. + */ + static bool JUCE_CALLTYPE isRunningUnderDebugger() throw(); + + /** Loads a dynamically-linked library into the process's address space. + + @param pathOrFilename the platform-dependent name and search path + @returns a handle which can be used by getProcedureEntryPoint(), or + zero if it fails. + @see freeDynamicLibrary, getProcedureEntryPoint + */ + static void* loadDynamicLibrary (const String& pathOrFilename); + + /** Frees a dynamically-linked library. + + @param libraryHandle a handle created by loadDynamicLibrary + @see loadDynamicLibrary, getProcedureEntryPoint + */ + static void freeDynamicLibrary (void* libraryHandle); + + /** Finds a procedure call in a dynamically-linked library. + + @param libraryHandle a library handle returned by loadDynamicLibrary + @param procedureName the name of the procedure call to try to load + @returns a pointer to the function if found, or 0 if it fails + @see loadDynamicLibrary + */ + static void* getProcedureEntryPoint (void* libraryHandle, + const String& procedureName); + +}; + +#endif // __JUCE_PROCESS_JUCEHEADER__ +/********* End of inlined file: juce_Process.h *********/ + +#endif +#ifndef __JUCE_READWRITELOCK_JUCEHEADER__ + +/********* Start of inlined file: juce_ReadWriteLock.h *********/ +#ifndef __JUCE_READWRITELOCK_JUCEHEADER__ +#define __JUCE_READWRITELOCK_JUCEHEADER__ + +/********* Start of inlined file: juce_WaitableEvent.h *********/ +#ifndef __JUCE_WAITABLEEVENT_JUCEHEADER__ +#define __JUCE_WAITABLEEVENT_JUCEHEADER__ + +/** + Allows threads to wait for events triggered by other threads. + + A thread can call wait() on a WaitableObject, and this will suspend the + calling thread until another thread wakes it up by calling the signal() + method. +*/ +class JUCE_API WaitableEvent +{ +public: + + /** Creates a WaitableEvent object. */ + WaitableEvent() throw(); + + /** Destructor. + + If other threads are waiting on this object when it gets deleted, this + can cause nasty errors, so be careful! + */ + ~WaitableEvent() throw(); + + /** Suspends the calling thread until the event has been signalled. + + This will wait until the object's signal() method is called by another thread, + or until the timeout expires. + + After the event has been signalled, this method will return true and reset + the event. + + @param timeOutMilliseconds the maximum time to wait, in milliseconds. A negative + value will cause it to wait forever. + + @returns true if the object has been signalled, false if the timeout expires first. + @see signal, reset + */ + bool wait (const int timeOutMilliseconds = -1) const throw(); + + /** Wakes up any threads that are currently waiting on this object. + + If signal() is called when nothing is waiting, the next thread to call wait() + will return immediately and reset the signal. + + @see wait, reset + */ + void signal() const throw(); + + /** Resets the event to an unsignalled state. + + If it's not already signalled, this does nothing. + */ + void reset() const throw(); + + juce_UseDebuggingNewOperator + +private: + void* internal; + + WaitableEvent (const WaitableEvent&); + const WaitableEvent& operator= (const WaitableEvent&); +}; + +#endif // __JUCE_WAITABLEEVENT_JUCEHEADER__ +/********* End of inlined file: juce_WaitableEvent.h *********/ + +/** + A critical section that allows multiple simultaneous readers. + + Features of this type of lock are: + + - Multiple readers can hold the lock at the same time, but only one writer + can hold it at once. + - Writers trying to gain the lock will be blocked until all readers and writers + have released it + - Readers trying to gain the lock while a writer is waiting to acquire it will be + blocked until the writer has obtained and released it + - If a thread already has a read lock and tries to obtain a write lock, it will succeed if + there are no other readers + - If a thread already has the write lock and tries to obtain a read lock, this will succeed. + - Recursive locking is supported. + + @see ScopedReadLock, ScopedWriteLock, CriticalSection +*/ +class JUCE_API ReadWriteLock +{ +public: + + /** + Creates a ReadWriteLock object. + */ + ReadWriteLock() throw(); + + /** Destructor. + + If the object is deleted whilst locked, any subsequent behaviour + is unpredictable. + */ + ~ReadWriteLock() throw(); + + /** Locks this object for reading. + + Multiple threads can simulaneously lock the object for reading, but if another + thread has it locked for writing, then this will block until it releases the + lock. + + @see exitRead, ScopedReadLock + */ + void enterRead() const throw(); + + /** Releases the read-lock. + + If the caller thread hasn't got the lock, this can have unpredictable results. + + If the enterRead() method has been called multiple times by the thread, each + call must be matched by a call to exitRead() before other threads will be allowed + to take over the lock. + + @see enterRead, ScopedReadLock + */ + void exitRead() const throw(); + + /** Locks this object for writing. + + This will block until any other threads that have it locked for reading or + writing have released their lock. + + @see exitWrite, ScopedWriteLock + */ + void enterWrite() const throw(); + + /** Releases the write-lock. + + If the caller thread hasn't got the lock, this can have unpredictable results. + + If the enterWrite() method has been called multiple times by the thread, each + call must be matched by a call to exit() before other threads will be allowed + to take over the lock. + + @see enterWrite, ScopedWriteLock + */ + void exitWrite() const throw(); + + juce_UseDebuggingNewOperator + +private: + + CriticalSection accessLock; + WaitableEvent waitEvent; + mutable int numWaitingWriters, numWriters; + mutable int writerThreadId; + mutable Array readerThreads; + + ReadWriteLock (const ReadWriteLock&); + const ReadWriteLock& operator= (const ReadWriteLock&); +}; + +#endif // __JUCE_READWRITELOCK_JUCEHEADER__ +/********* End of inlined file: juce_ReadWriteLock.h *********/ + +#endif +#ifndef __JUCE_SCOPEDLOCK_JUCEHEADER__ + +#endif +#ifndef __JUCE_SCOPEDREADLOCK_JUCEHEADER__ + +/********* Start of inlined file: juce_ScopedReadLock.h *********/ +#ifndef __JUCE_SCOPEDREADLOCK_JUCEHEADER__ +#define __JUCE_SCOPEDREADLOCK_JUCEHEADER__ + +/** + Automatically locks and unlocks a ReadWriteLock object. + + Use one of these as a local variable to control access to a ReadWriteLock. + + e.g. @code + + ReadWriteLock myLock; + + for (;;) + { + const ScopedReadLock myScopedLock (myLock); + // myLock is now locked + + ...do some stuff... + + // myLock gets unlocked here. + } + @endcode + + @see ReadWriteLock, ScopedWriteLock +*/ +class JUCE_API ScopedReadLock +{ +public: + + /** Creates a ScopedReadLock. + + As soon as it is created, this will call ReadWriteLock::enterRead(), and + when the ScopedReadLock object is deleted, the ReadWriteLock will + be unlocked. + + Make sure this object is created and deleted by the same thread, + otherwise there are no guarantees what will happen! Best just to use it + as a local stack object, rather than creating one with the new() operator. + */ + inline ScopedReadLock (const ReadWriteLock& lock) throw() : lock_ (lock) { lock.enterRead(); } + + /** Destructor. + + The ReadWriteLock's exitRead() method will be called when the destructor is called. + + Make sure this object is created and deleted by the same thread, + otherwise there are no guarantees what will happen! + */ + inline ~ScopedReadLock() throw() { lock_.exitRead(); } + +private: + + const ReadWriteLock& lock_; + + ScopedReadLock (const ScopedReadLock&); + const ScopedReadLock& operator= (const ScopedReadLock&); +}; + +#endif // __JUCE_SCOPEDREADLOCK_JUCEHEADER__ +/********* End of inlined file: juce_ScopedReadLock.h *********/ + +#endif +#ifndef __JUCE_SCOPEDTRYLOCK_JUCEHEADER__ + +/********* Start of inlined file: juce_ScopedTryLock.h *********/ +#ifndef __JUCE_SCOPEDTRYLOCK_JUCEHEADER__ +#define __JUCE_SCOPEDTRYLOCK_JUCEHEADER__ + +/** + Automatically tries to lock and unlock a CriticalSection object. + + Use one of these as a local variable to control access to a CriticalSection. + + e.g. @code + + CriticalSection myCriticalSection; + + for (;;) + { + const ScopedTryLock myScopedTryLock (myCriticalSection); + + // Unlike using a ScopedLock, this may fail to actually get the lock, so you + // should test this with the isLocked() method before doing your thread-unsafe + // action.. + if (myScopedTryLock.isLocked()) + { + ...do some stuff... + } + else + { + ..our attempt at locking failed because another thread had already locked it.. + } + + // myCriticalSection gets unlocked here (if it was locked) + } + @endcode + + @see CriticalSection::tryEnter, ScopedLock, ScopedUnlock, ScopedReadLock +*/ +class JUCE_API ScopedTryLock +{ +public: + + /** Creates a ScopedTryLock. + + As soon as it is created, this will try to lock the CriticalSection, and + when the ScopedTryLock object is deleted, the CriticalSection will + be unlocked if the lock was successful. + + Make sure this object is created and deleted by the same thread, + otherwise there are no guarantees what will happen! Best just to use it + as a local stack object, rather than creating one with the new() operator. + */ + inline ScopedTryLock (const CriticalSection& lock) throw() : lock_ (lock), lockWasSuccessful (lock.tryEnter()) {} + + /** Destructor. + + The CriticalSection will be unlocked (if locked) when the destructor is called. + + Make sure this object is created and deleted by the same thread, + otherwise there are no guarantees what will happen! + */ + inline ~ScopedTryLock() throw() { if (lockWasSuccessful) lock_.exit(); } + + /** Lock state + + @return True if the CriticalSection is locked. + */ + bool isLocked() const throw() { return lockWasSuccessful; } + +private: + + const CriticalSection& lock_; + const bool lockWasSuccessful; + + ScopedTryLock (const ScopedTryLock&); + const ScopedTryLock& operator= (const ScopedTryLock&); +}; + +#endif // __JUCE_SCOPEDTRYLOCK_JUCEHEADER__ +/********* End of inlined file: juce_ScopedTryLock.h *********/ + +#endif +#ifndef __JUCE_SCOPEDWRITELOCK_JUCEHEADER__ + +/********* Start of inlined file: juce_ScopedWriteLock.h *********/ +#ifndef __JUCE_SCOPEDWRITELOCK_JUCEHEADER__ +#define __JUCE_SCOPEDWRITELOCK_JUCEHEADER__ + +/** + Automatically locks and unlocks a ReadWriteLock object. + + Use one of these as a local variable to control access to a ReadWriteLock. + + e.g. @code + + ReadWriteLock myLock; + + for (;;) + { + const ScopedWriteLock myScopedLock (myLock); + // myLock is now locked + + ...do some stuff... + + // myLock gets unlocked here. + } + @endcode + + @see ReadWriteLock, ScopedReadLock +*/ +class JUCE_API ScopedWriteLock +{ +public: + + /** Creates a ScopedWriteLock. + + As soon as it is created, this will call ReadWriteLock::enterWrite(), and + when the ScopedWriteLock object is deleted, the ReadWriteLock will + be unlocked. + + Make sure this object is created and deleted by the same thread, + otherwise there are no guarantees what will happen! Best just to use it + as a local stack object, rather than creating one with the new() operator. + */ + inline ScopedWriteLock (const ReadWriteLock& lock) throw() : lock_ (lock) { lock.enterWrite(); } + + /** Destructor. + + The ReadWriteLock's exitWrite() method will be called when the destructor is called. + + Make sure this object is created and deleted by the same thread, + otherwise there are no guarantees what will happen! + */ + inline ~ScopedWriteLock() throw() { lock_.exitWrite(); } + +private: + + const ReadWriteLock& lock_; + + ScopedWriteLock (const ScopedWriteLock&); + const ScopedWriteLock& operator= (const ScopedWriteLock&); +}; + +#endif // __JUCE_SCOPEDWRITELOCK_JUCEHEADER__ +/********* End of inlined file: juce_ScopedWriteLock.h *********/ + +#endif +#ifndef __JUCE_THREAD_JUCEHEADER__ + +/********* Start of inlined file: juce_Thread.h *********/ +#ifndef __JUCE_THREAD_JUCEHEADER__ +#define __JUCE_THREAD_JUCEHEADER__ + +/** + Encapsulates a thread. + + Subclasses derive from Thread and implement the run() method, in which they + do their business. The thread can then be started with the startThread() method + and controlled with various other methods. + + This class also contains some thread-related static methods, such + as sleep(), yield(), getCurrentThreadId() etc. + + @see CriticalSection, WaitableEvent, Process, ThreadWithProgressWindow, + MessageManagerLock +*/ +class JUCE_API Thread +{ +public: + + /** + Creates a thread. + + When first created, the thread is not running. Use the startThread() + method to start it. + */ + Thread (const String& threadName); + + /** Destructor. + + Deleting a Thread object that is running will only give the thread a + brief opportunity to stop itself cleanly, so it's recommended that you + should always call stopThread() with a decent timeout before deleting, + to avoid the thread being forcibly killed (which is a Bad Thing). + */ + virtual ~Thread(); + + /** Must be implemented to perform the thread's actual code. + + Remember that the thread must regularly check the threadShouldExit() + method whilst running, and if this returns true it should return from + the run() method as soon as possible to avoid being forcibly killed. + + @see threadShouldExit, startThread + */ + virtual void run() = 0; + + // Thread control functions.. + + /** Starts the thread running. + + This will start the thread's run() method. + (if it's already started, startThread() won't do anything). + + @see stopThread + */ + void startThread() throw(); + + /** Starts the thread with a given priority. + + Launches the thread with a given priority, where 0 = lowest, 10 = highest. + If the thread is already running, its priority will be changed. + + @see startThread, setPriority + */ + void startThread (const int priority) throw(); + + /** Attempts to stop the thread running. + + This method will cause the threadShouldExit() method to return true + and call notify() in case the thread is currently waiting. + + Hopefully the thread will then respond to this by exiting cleanly, and + the stopThread method will wait for a given time-period for this to + happen. + + If the thread is stuck and fails to respond after the time-out, it gets + forcibly killed, which is a very bad thing to happen, as it could still + be holding locks, etc. which are needed by other parts of your program. + + @param timeOutMilliseconds The number of milliseconds to wait for the + thread to finish before killing it by force. A negative + value in here will wait forever. + @see signalThreadShouldExit, threadShouldExit, waitForThreadToExit, isThreadRunning + */ + void stopThread (const int timeOutMilliseconds) throw(); + + /** Returns true if the thread is currently active */ + bool isThreadRunning() const throw(); + + /** Sets a flag to tell the thread it should stop. + + Calling this means that the threadShouldExit() method will then return true. + The thread should be regularly checking this to see whether it should exit. + + @see threadShouldExit + @see waitForThreadToExit + */ + void signalThreadShouldExit() throw(); + + /** Checks whether the thread has been told to stop running. + + Threads need to check this regularly, and if it returns true, they should + return from their run() method at the first possible opportunity. + + @see signalThreadShouldExit + */ + inline bool threadShouldExit() const throw() { return threadShouldExit_; } + + /** Waits for the thread to stop. + + This will waits until isThreadRunning() is false or until a timeout expires. + + @param timeOutMilliseconds the time to wait, in milliseconds. If this value + is less than zero, it will wait forever. + @returns true if the thread exits, or false if the timeout expires first. + */ + bool waitForThreadToExit (const int timeOutMilliseconds) const throw(); + + /** Changes the thread's priority. + + @param priority the new priority, in the range 0 (lowest) to 10 (highest). A priority + of 5 is normal. + */ + void setPriority (const int priority) throw(); + + /** Changes the priority of the caller thread. + + Similar to setPriority(), but this static method acts on the caller thread. + + @see setPriority + */ + static void setCurrentThreadPriority (const int priority) throw(); + + /** Sets the affinity mask for the thread. + + This will only have an effect next time the thread is started - i.e. if the + thread is already running when called, it'll have no effect. + + @see setCurrentThreadAffinityMask + */ + void setAffinityMask (const uint32 affinityMask) throw(); + + /** Changes the affinity mask for the caller thread. + + This will change the affinity mask for the thread that calls this static method. + + @see setAffinityMask + */ + static void setCurrentThreadAffinityMask (const uint32 affinityMask) throw(); + + // this can be called from any thread that needs to pause.. + static void JUCE_CALLTYPE sleep (int milliseconds) throw(); + + /** Yields the calling thread's current time-slot. */ + static void JUCE_CALLTYPE yield() throw(); + + /** Makes the thread wait for a notification. + + This puts the thread to sleep until either the timeout period expires, or + another thread calls the notify() method to wake it up. + + @returns true if the event has been signalled, false if the timeout expires. + */ + bool wait (const int timeOutMilliseconds) const throw(); + + /** Wakes up the thread. + + If the thread has called the wait() method, this will wake it up. + + @see wait + */ + void notify() const throw(); + + /** Returns an id that identifies the caller thread. + + To find the ID of a particular thread object, use getThreadId(). + + @returns a unique identifier that identifies the calling thread. + @see getThreadId + */ + static int getCurrentThreadId() throw(); + + /** Finds the thread object that is currently running. + + Note that the main UI thread (or other non-Juce threads) don't have a Thread + object associated with them, so this will return 0. + */ + static Thread* getCurrentThread() throw(); + + /** Returns the ID of this thread. + + That means the ID of this thread object - not of the thread that's calling the method. + + This can change when the thread is started and stopped, and will be invalid if the + thread's not actually running. + + @see getCurrentThreadId + */ + int getThreadId() const throw(); + + /** Returns the name of the thread. + + This is the name that gets set in the constructor. + */ + const String getThreadName() const throw() { return threadName_; } + + /** Returns the number of currently-running threads. + + @returns the number of Thread objects known to be currently running. + @see stopAllThreads + */ + static int getNumRunningThreads() throw(); + + /** Tries to stop all currently-running threads. + + This will attempt to stop all the threads known to be running at the moment. + */ + static void stopAllThreads (const int timeoutInMillisecs) throw(); + + juce_UseDebuggingNewOperator + +private: + const String threadName_; + void* volatile threadHandle_; + CriticalSection startStopLock; + WaitableEvent startSuspensionEvent_, defaultEvent_; + + int threadPriority_, threadId_; + uint32 affinityMask_; + bool volatile threadShouldExit_; + + friend void JUCE_API juce_threadEntryPoint (void*); + static void threadEntryPoint (Thread* thread) throw(); + + Thread (const Thread&); + const Thread& operator= (const Thread&); +}; + +#endif // __JUCE_THREAD_JUCEHEADER__ +/********* End of inlined file: juce_Thread.h *********/ + +#endif +#ifndef __JUCE_THREADPOOL_JUCEHEADER__ + +/********* Start of inlined file: juce_ThreadPool.h *********/ +#ifndef __JUCE_THREADPOOL_JUCEHEADER__ +#define __JUCE_THREADPOOL_JUCEHEADER__ + +class ThreadPool; +class ThreadPoolThread; + +/** + A task that is executed by a ThreadPool object. + + A ThreadPool keeps a list of ThreadPoolJob objects which are executed by + its threads. + + The runJob() method needs to be implemented to do the task, and if the code that + does the work takes a significant time to run, it must keep checking the shouldExit() + method to see if something is trying to interrupt the job. If shouldExit() returns + true, the runJob() method must return immediately. + + @see ThreadPool, Thread +*/ +class JUCE_API ThreadPoolJob +{ +public: + + /** Creates a thread pool job object. + + After creating your job, add it to a thread pool with ThreadPool::addJob(). + */ + ThreadPoolJob (const String& name); + + /** Destructor. */ + virtual ~ThreadPoolJob(); + + /** Returns the name of this job. + @see setJobName + */ + const String getJobName() const; + + /** Changes the job's name. + @see getJobName + */ + void setJobName (const String& newName); + + /** These are the values that can be returned by the runJob() method. + */ + enum JobStatus + { + jobHasFinished = 0, /**< indicates that the job has finished and can be + removed from the pool. */ + + jobHasFinishedAndShouldBeDeleted, /**< indicates that the job has finished and that it + should be automatically deleted by the pool. */ + + jobNeedsRunningAgain /**< indicates that the job would like to be called + again when a thread is free. */ + }; + + /** Peforms the actual work that this job needs to do. + + Your subclass must implement this method, in which is does its work. + + If the code in this method takes a significant time to run, it must repeatedly check + the shouldExit() method to see if something is trying to interrupt the job. + If shouldExit() ever returns true, the runJob() method must return immediately. + + If this method returns jobHasFinished, then the job will be removed from the pool + immediately. If it returns jobNeedsRunningAgain, then the job will be left in the + pool and will get a chance to run again as soon as a thread is free. + + @see shouldExit() + */ + virtual JobStatus runJob() = 0; + + /** Returns true if this job is currently running its runJob() method. */ + bool isRunning() const throw() { return isActive; } + + /** Returns true if something is trying to interrupt this job and make it stop. + + Your runJob() method must call this whenever it gets a chance, and if it ever + returns true, the runJob() method must return immediately. + + @see signalJobShouldExit() + */ + bool shouldExit() const throw() { return shouldStop; } + + /** Calling this will cause the shouldExit() method to return true, and the job + should (if it's been implemented correctly) stop as soon as possible. + + @see shouldExit() + */ + void signalJobShouldExit(); + + juce_UseDebuggingNewOperator + +private: + friend class ThreadPool; + friend class ThreadPoolThread; + String jobName; + ThreadPool* pool; + bool shouldStop, isActive, shouldBeDeleted; +}; + +/** + A set of threads that will run a list of jobs. + + When a ThreadPoolJob object is added to the ThreadPool's list, its run() method + will be called by the next pooled thread that becomes free. + + @see ThreadPoolJob, Thread +*/ +class JUCE_API ThreadPool +{ +public: + + /** Creates a thread pool. + + Once you've created a pool, you can give it some things to do with the addJob() + method. + + @param numberOfThreads the maximum number of actual threads to run. + @param startThreadsOnlyWhenNeeded if this is true, then no threads will be started + until there are some jobs to run. If false, then + all the threads will be fired-up immediately so that + they're ready for action + @param stopThreadsWhenNotUsedTimeoutMs if this timeout is > 0, then if any threads have been + inactive for this length of time, they will automatically + be stopped until more jobs come along and they're needed + */ + ThreadPool (const int numberOfThreads, + const bool startThreadsOnlyWhenNeeded = true, + const int stopThreadsWhenNotUsedTimeoutMs = 5000); + + /** Destructor. + + This will attempt to remove all the jobs before deleting, but if you want to + specify a timeout, you should call removeAllJobs() explicitly before deleting + the pool. + */ + ~ThreadPool(); + + /** Adds a job to the queue. + + Once a job has been added, then the next time a thread is free, it will run + the job's ThreadPoolJob::runJob() method. Depending on the return value of the + runJob() method, the pool will either remove the job from the pool or add it to + the back of the queue to be run again. + */ + void addJob (ThreadPoolJob* const job); + + /** Tries to remove a job from the pool. + + If the job isn't yet running, this will simply remove it. If it is running, it + will wait for it to finish. + + If the timeout period expires before the job finishes running, then the job will be + left in the pool and this will return false. It returns true if the job is sucessfully + stopped and removed. + + @param job the job to remove + @param interruptIfRunning if true, then if the job is currently busy, its + ThreadPoolJob::signalJobShouldExit() method will be called to try + to interrupt it. If false, then if the job will be allowed to run + until it stops normally (or the timeout expires) + @param timeOutMilliseconds the length of time this method should wait for the job to finish + before giving up and returning false + */ + bool removeJob (ThreadPoolJob* const job, + const bool interruptIfRunning, + const int timeOutMilliseconds); + + /** Tries clear all jobs from the pool. + + @param interruptRunningJobs if true, then all running jobs will have their ThreadPoolJob::signalJobShouldExit() + methods called to try to interrupt them + @param timeOutMilliseconds the length of time this method should wait for all the jobs to finish + before giving up and returning false + @returns true if all jobs are successfully stopped and removed; false if the timeout period + expires while waiting for them to stop + */ + bool removeAllJobs (const bool interruptRunningJobs, + const int timeOutMilliseconds); + + /** Returns the number of jobs currently running or queued. + */ + int getNumJobs() const throw(); + + /** Returns one of the jobs in the queue. + + Note that this can be a very volatile list as jobs might be continuously getting shifted + around in the list, and this method may return 0 if the index is currently out-of-range. + */ + ThreadPoolJob* getJob (const int index) const; + + /** Returns true if the given job is currently queued or running. + + @see isJobRunning() + */ + bool contains (const ThreadPoolJob* const job) const throw(); + + /** Returns true if the given job is currently being run by a thread. + */ + bool isJobRunning (const ThreadPoolJob* const job) const; + + /** Waits until a job has finished running and has been removed from the pool. + + This will wait until the job is no longer in the pool - i.e. until its + runJob() method returns ThreadPoolJob::jobHasFinished. + + If the timeout period expires before the job finishes, this will return false; + it returns true if the job has finished successfully. + */ + bool waitForJobToFinish (const ThreadPoolJob* const job, + const int timeOutMilliseconds) const; + + /** Returns a list of the names of all the jobs currently running or queued. + + If onlyReturnActiveJobs is true, only the ones currently running are returned. + */ + const StringArray getNamesOfAllJobs (const bool onlyReturnActiveJobs) const; + + /** Changes the priority of all the threads. + + This will call Thread::setPriority() for each thread in the pool. + */ + void setThreadPriorities (const int newPriority); + + juce_UseDebuggingNewOperator + +private: + const int numThreads, threadStopTimeout; + int priority; + Thread** threads; + VoidArray jobs; + + CriticalSection lock; + uint32 lastJobEndTime; + + friend class ThreadPoolThread; + bool runNextJob(); + + ThreadPool (const ThreadPool&); + const ThreadPool& operator= (const ThreadPool&); +}; + +#endif // __JUCE_THREADPOOL_JUCEHEADER__ +/********* End of inlined file: juce_ThreadPool.h *********/ + +#endif +#ifndef __JUCE_TIMESLICETHREAD_JUCEHEADER__ + +/********* Start of inlined file: juce_TimeSliceThread.h *********/ +#ifndef __JUCE_TIMESLICETHREAD_JUCEHEADER__ +#define __JUCE_TIMESLICETHREAD_JUCEHEADER__ + +/** + Used by the TimeSliceThread class. + + To register your class with a TimeSliceThread, derive from this class and + use the TimeSliceThread::addTimeSliceClient() method to add it to the list. + + Make sure you always call TimeSliceThread::removeTimeSliceClient() before + deleting your client! + + @see TimeSliceThread +*/ +class JUCE_API TimeSliceClient +{ +public: + /** Destructor. */ + virtual ~TimeSliceClient() {} + + /** Called back by a TimeSliceThread. + + When you register this class with it, a TimeSliceThread will repeatedly call + this method. + + The implementation of this method should use its time-slice to do something that's + quick - never block for longer than absolutely necessary. + + @returns Your method should return true if it needs more time, or false if it's + not too busy and doesn't need calling back urgently. If all the thread's + clients indicate that they're not busy, then it'll save CPU by sleeping for + up to half a second in between callbacks. You can force the TimeSliceThread + to wake up and poll again immediately by calling its notify() method. + */ + virtual bool useTimeSlice() = 0; +}; + +/** + A thread that keeps a list of clients, and calls each one in turn, giving them + all a chance to run some sort of short task. + + @see TimeSliceClient, Thread +*/ +class JUCE_API TimeSliceThread : public Thread +{ +public: + + /** + Creates a TimeSliceThread. + + When first created, the thread is not running. Use the startThread() + method to start it. + */ + TimeSliceThread (const String& threadName); + + /** Destructor. + + Deleting a Thread object that is running will only give the thread a + brief opportunity to stop itself cleanly, so it's recommended that you + should always call stopThread() with a decent timeout before deleting, + to avoid the thread being forcibly killed (which is a Bad Thing). + */ + ~TimeSliceThread(); + + /** Adds a client to the list. + + The client's callbacks will start immediately (possibly before the method + has returned). + */ + void addTimeSliceClient (TimeSliceClient* const client); + + /** Removes a client from the list. + + This method will make sure that all callbacks to the client have completely + finished before the method returns. + */ + void removeTimeSliceClient (TimeSliceClient* const client); + + /** Returns the number of registered clients. */ + int getNumClients() const throw(); + + /** Returns one of the registered clients. */ + TimeSliceClient* getClient (const int index) const throw(); + + /** @internal */ + void run(); + + juce_UseDebuggingNewOperator + +private: + CriticalSection callbackLock, listLock; + Array clients; + int index; + TimeSliceClient* clientBeingCalled; + bool clientsChanged; + + TimeSliceThread (const TimeSliceThread&); + const TimeSliceThread& operator= (const TimeSliceThread&); +}; + +#endif // __JUCE_TIMESLICETHREAD_JUCEHEADER__ +/********* End of inlined file: juce_TimeSliceThread.h *********/ + +#endif +#ifndef __JUCE_WAITABLEEVENT_JUCEHEADER__ + +#endif + +#endif +/********* End of inlined file: juce_core_includes.h *********/ + +// if you're compiling a command-line app, you might want to just include the core headers, +// so you can set this macro before including juce.h +#if ! JUCE_ONLY_BUILD_CORE_LIBRARY + +/********* Start of inlined file: juce_app_includes.h *********/ +#ifndef __JUCE_JUCE_APP_INCLUDES_INCLUDEFILES__ +#define __JUCE_JUCE_APP_INCLUDES_INCLUDEFILES__ + +#ifndef __JUCE_APPLICATION_JUCEHEADER__ + +/********* Start of inlined file: juce_Application.h *********/ +#ifndef __JUCE_APPLICATION_JUCEHEADER__ +#define __JUCE_APPLICATION_JUCEHEADER__ + +/********* Start of inlined file: juce_ApplicationCommandTarget.h *********/ +#ifndef __JUCE_APPLICATIONCOMMANDTARGET_JUCEHEADER__ +#define __JUCE_APPLICATIONCOMMANDTARGET_JUCEHEADER__ + +/********* Start of inlined file: juce_Component.h *********/ +#ifndef __JUCE_COMPONENT_JUCEHEADER__ +#define __JUCE_COMPONENT_JUCEHEADER__ + +/********* Start of inlined file: juce_MouseCursor.h *********/ +#ifndef __JUCE_MOUSECURSOR_JUCEHEADER__ +#define __JUCE_MOUSECURSOR_JUCEHEADER__ + +class Image; +class RefCountedMouseCursor; +class ComponentPeer; +class Component; + +/** + Represents a mouse cursor image. + + This object can either be used to represent one of the standard mouse + cursor shapes, or a custom one generated from an image. +*/ +class JUCE_API MouseCursor +{ +public: + + /** The set of available standard mouse cursors. */ + enum StandardCursorType + { + NoCursor = 0, /**< An invisible cursor. */ + NormalCursor, /**< The stardard arrow cursor. */ + + WaitCursor, /**< The normal hourglass or spinning-beachball 'busy' cursor. */ + IBeamCursor, /**< A vertical I-beam for positioning within text. */ + CrosshairCursor, /**< A pair of crosshairs. */ + CopyingCursor, /**< The normal arrow cursor, but with a "+" on it to indicate + that you're dragging a copy of something. */ + + PointingHandCursor, /**< A hand with a pointing finger, for clicking on web-links. */ + DraggingHandCursor, /**< An open flat hand for dragging heavy objects around. */ + + LeftRightResizeCursor, /**< An arrow pointing left and right. */ + UpDownResizeCursor, /**< an arrow pointing up and down. */ + UpDownLeftRightResizeCursor, /**< An arrow pointing up, down, left and right. */ + + TopEdgeResizeCursor, /**< A platform-specific cursor for resizing the top-edge of a window. */ + BottomEdgeResizeCursor, /**< A platform-specific cursor for resizing the bottom-edge of a window. */ + LeftEdgeResizeCursor, /**< A platform-specific cursor for resizing the left-edge of a window. */ + RightEdgeResizeCursor, /**< A platform-specific cursor for resizing the right-edge of a window. */ + TopLeftCornerResizeCursor, /**< A platform-specific cursor for resizing the top-left-corner of a window. */ + TopRightCornerResizeCursor, /**< A platform-specific cursor for resizing the top-right-corner of a window. */ + BottomLeftCornerResizeCursor, /**< A platform-specific cursor for resizing the bottom-left-corner of a window. */ + BottomRightCornerResizeCursor /**< A platform-specific cursor for resizing the bottom-right-corner of a window. */ + }; + + /** Creates the standard arrow cursor. */ + MouseCursor() throw(); + + /** Creates one of the standard mouse cursor */ + MouseCursor (const StandardCursorType type) throw(); + + /** Creates a custom cursor from an image. + + @param image the image to use for the cursor - if this is bigger than the + system can manage, it might get scaled down first, and might + also have to be turned to black-and-white if it can't do colour + cursors. + @param hotSpotX the x position of the cursor's hotspot within the image + @param hotSpotY the y position of the cursor's hotspot within the image + */ + MouseCursor (Image& image, + const int hotSpotX, + const int hotSpotY) throw(); + + /** Creates a copy of another cursor object. */ + MouseCursor (const MouseCursor& other) throw(); + + /** Copies this cursor from another object. */ + const MouseCursor& operator= (const MouseCursor& other) throw(); + + /** Destructor. */ + ~MouseCursor() throw(); + + /** Checks whether two mouse cursors are the same. + + For custom cursors, two cursors created from the same image won't be + recognised as the same, only MouseCursor objects that have been + copied from the same object. + */ + bool operator== (const MouseCursor& other) const throw(); + + /** Checks whether two mouse cursors are the same. + + For custom cursors, two cursors created from the same image won't be + recognised as the same, only MouseCursor objects that have been + copied from the same object. + */ + bool operator!= (const MouseCursor& other) const throw(); + + /** Makes the system show its default 'busy' cursor. + + This will turn the system cursor to an hourglass or spinning beachball + until the next time the mouse is moved, or hideWaitCursor() is called. + + This is handy if the message loop is about to block for a couple of + seconds while busy and you want to give the user feedback about this. + + @see MessageManager::setTimeBeforeShowingWaitCursor + */ + static void showWaitCursor() throw(); + + /** If showWaitCursor has been called, this will return the mouse to its + normal state. + + This will look at what component is under the mouse, and update the + cursor to be the correct one for that component. + + @see showWaitCursor + */ + static void hideWaitCursor() throw(); + + juce_UseDebuggingNewOperator + +private: + RefCountedMouseCursor* cursorHandle; + + friend class Component; + + void showInWindow (ComponentPeer* window) const throw(); + void showInAllWindows() const throw(); + + void* getHandle() const throw(); +}; + +#endif // __JUCE_MOUSECURSOR_JUCEHEADER__ +/********* End of inlined file: juce_MouseCursor.h *********/ + +/********* Start of inlined file: juce_MouseListener.h *********/ +#ifndef __JUCE_MOUSELISTENER_JUCEHEADER__ +#define __JUCE_MOUSELISTENER_JUCEHEADER__ + +/********* Start of inlined file: juce_MouseEvent.h *********/ +#ifndef __JUCE_MOUSEEVENT_JUCEHEADER__ +#define __JUCE_MOUSEEVENT_JUCEHEADER__ + +class Component; + +/********* Start of inlined file: juce_ModifierKeys.h *********/ +#ifndef __JUCE_MODIFIERKEYS_JUCEHEADER__ +#define __JUCE_MODIFIERKEYS_JUCEHEADER__ + +/** + Represents the state of the mouse buttons and modifier keys. + + This is used both by mouse events and by KeyPress objects to describe + the state of keys such as shift, control, alt, etc. + + @see KeyPress, MouseEvent::mods +*/ +class JUCE_API ModifierKeys +{ +public: + + /** Creates a ModifierKeys object from a raw set of flags. + + @param flags to represent the keys that are down + @see shiftModifier, ctrlModifier, altModifier, leftButtonModifier, + rightButtonModifier, commandModifier, popupMenuClickModifier + */ + ModifierKeys (const int flags = 0) throw(); + + /** Creates a copy of another object. */ + ModifierKeys (const ModifierKeys& other) throw(); + + /** Copies this object from another one. */ + const ModifierKeys& operator= (const ModifierKeys& other) throw(); + + /** Checks whether the 'command' key flag is set (or 'ctrl' on Windows/Linux). + + This is a platform-agnostic way of checking for the operating system's + preferred command-key modifier - so on the Mac it tests for the Apple key, on + Windows/Linux, it's actually checking for the CTRL key. + */ + inline bool isCommandDown() const throw() { return (flags & commandModifier) != 0; } + + /** Checks whether the user is trying to launch a pop-up menu. + + This checks for platform-specific modifiers that might indicate that the user + is following the operating system's normal method of showing a pop-up menu. + + So on Windows/Linux, this method is really testing for a right-click. + On the Mac, it tests for either the CTRL key being down, or a right-click. + */ + inline bool isPopupMenu() const throw() { return (flags & popupMenuClickModifier) != 0; } + + /** Checks whether the flag is set for the left mouse-button. */ + inline bool isLeftButtonDown() const throw() { return (flags & leftButtonModifier) != 0; } + + /** Checks whether the flag is set for the right mouse-button. + + Note that for detecting popup-menu clicks, you should be using isPopupMenu() instead, as + this is platform-independent (and makes your code more explanatory too). + */ + inline bool isRightButtonDown() const throw() { return (flags & rightButtonModifier) != 0; } + + inline bool isMiddleButtonDown() const throw() { return (flags & middleButtonModifier) != 0; } + + /** Tests for any of the mouse-button flags. */ + inline bool isAnyMouseButtonDown() const throw() { return (flags & allMouseButtonModifiers) != 0; } + + /** Tests for any of the modifier key flags. */ + inline bool isAnyModifierKeyDown() const throw() { return (flags & (shiftModifier | ctrlModifier | altModifier | commandModifier)) != 0; } + + /** Checks whether the shift key's flag is set. */ + inline bool isShiftDown() const throw() { return (flags & shiftModifier) != 0; } + + /** Checks whether the CTRL key's flag is set. + + Remember that it's better to use the platform-agnostic routines to test for command-key and + popup-menu modifiers. + + @see isCommandDown, isPopupMenu + */ + inline bool isCtrlDown() const throw() { return (flags & ctrlModifier) != 0; } + + /** Checks whether the shift key's flag is set. */ + inline bool isAltDown() const throw() { return (flags & altModifier) != 0; } + + /** Flags that represent the different keys. */ + enum Flags + { + /** Shift key flag. */ + shiftModifier = 1, + + /** CTRL key flag. */ + ctrlModifier = 2, + + /** ALT key flag. */ + altModifier = 4, + + /** Left mouse button flag. */ + leftButtonModifier = 16, + + /** Right mouse button flag. */ + rightButtonModifier = 32, + + /** Middle mouse button flag. */ + middleButtonModifier = 64, + +#if JUCE_MAC + /** Command key flag - on windows this is the same as the CTRL key flag. */ + commandModifier = 8, + + /** Popup menu flag - on windows this is the same as rightButtonModifier, on the + Mac it's the same as (rightButtonModifier | ctrlModifier). */ + popupMenuClickModifier = rightButtonModifier | ctrlModifier, +#else + /** Command key flag - on windows this is the same as the CTRL key flag. */ + commandModifier = ctrlModifier, + + /** Popup menu flag - on windows this is the same as rightButtonModifier, on the + Mac it's the same as (rightButtonModifier | ctrlModifier). */ + popupMenuClickModifier = rightButtonModifier, +#endif + + /** Represents a combination of all the shift, alt, ctrl and command key modifiers. */ + allKeyboardModifiers = shiftModifier | ctrlModifier | altModifier | commandModifier, + + /** Represents a combination of all the mouse buttons at once. */ + allMouseButtonModifiers = leftButtonModifier | rightButtonModifier | middleButtonModifier, + }; + + /** Returns the raw flags for direct testing. */ + inline int getRawFlags() const throw() { return flags; } + + /** Tests a combination of flags and returns true if any of them are set. */ + inline bool testFlags (const int flagsToTest) const throw() { return (flags & flagsToTest) != 0; } + + /** Creates a ModifierKeys object to represent the last-known state of the + keyboard and mouse buttons. + + @see getCurrentModifiersRealtime + */ + static const ModifierKeys getCurrentModifiers() throw(); + + /** Creates a ModifierKeys object to represent the current state of the + keyboard and mouse buttons. + + This isn't often needed and isn't recommended, but will actively check all the + mouse and key states rather than just returning their last-known state like + getCurrentModifiers() does. + + This is only needed in special circumstances for up-to-date modifier information + at times when the app's event loop isn't running normally. + */ + static const ModifierKeys getCurrentModifiersRealtime() throw(); + +private: + + int flags; + + static int currentModifierFlags; + + friend class ComponentPeer; + static void updateCurrentModifiers() throw(); +}; + +#endif // __JUCE_MODIFIERKEYS_JUCEHEADER__ +/********* End of inlined file: juce_ModifierKeys.h *********/ + +/** + Contains position and status information about a mouse event. + + @see MouseListener, Component::mouseMove, Component::mouseEnter, Component::mouseExit, + Component::mouseDown, Component::mouseUp, Component::mouseDrag +*/ +class JUCE_API MouseEvent +{ +public: + + /** Creates a MouseEvent. + + Normally an application will never need to use this. + + @param x the x position of the mouse, relative to the component that is passed-in + @param y the y position of the mouse, relative to the component that is passed-in + @param modifiers the key modifiers at the time of the event + @param originator the component that the mouse event applies to + @param eventTime the time the event happened + @param mouseDownX the x position of the corresponding mouse-down event (relative to the component that is passed-in). + If there isn't a corresponding mouse-down (e.g. for a mouse-move), this will just be + the same as the current mouse-x position. + @param mouseDownY the y position of the corresponding mouse-down event (relative to the component that is passed-in) + If there isn't a corresponding mouse-down (e.g. for a mouse-move), this will just be + the same as the current mouse-y position. + @param mouseDownTime the time at which the corresponding mouse-down event happened + If there isn't a corresponding mouse-down (e.g. for a mouse-move), this will just be + the same as the current mouse-event time. + @param numberOfClicks how many clicks, e.g. a double-click event will be 2, a triple-click will be 3, etc + @param mouseWasDragged whether the mouse has been dragged significantly since the previous mouse-down + */ + MouseEvent (const int x, const int y, + const ModifierKeys& modifiers, + Component* const originator, + const Time& eventTime, + const int mouseDownX, + const int mouseDownY, + const Time& mouseDownTime, + const int numberOfClicks, + const bool mouseWasDragged) throw(); + + /** Destructor. */ + ~MouseEvent() throw(); + + /** The x-position of the mouse when the event occurred. + + This value is relative to the top-left of the component to which the + event applies (as indicated by the MouseEvent::eventComponent field). + */ + int x; + + /** The y-position of the mouse when the event occurred. + + This value is relative to the top-left of the component to which the + event applies (as indicated by the MouseEvent::eventComponent field). + */ + int y; + + /** The key modifiers associated with the event. + + This will let you find out which mouse buttons were down, as well as which + modifier keys were held down. + + When used for mouse-up events, this will indicate the state of the mouse buttons + just before they were released, so that you can tell which button they let go of. + */ + ModifierKeys mods; + + /** The component that this event applies to. + + This is usually the component that the mouse was over at the time, but for mouse-drag + events the mouse could actually be over a different component and the events are + still sent to the component that the button was originally pressed on. + + The x and y member variables are relative to this component's position. + + If you use getEventRelativeTo() to retarget this object to be relative to a different + component, this pointer will be updated, but originalComponent remains unchanged. + + @see originalComponent + */ + Component* eventComponent; + + /** The component that the event first occurred on. + + If you use getEventRelativeTo() to retarget this object to be relative to a different + component, this value remains unchanged to indicate the first component that received it. + + @see eventComponent + */ + Component* originalComponent; + + /** The time that this mouse-event occurred. + */ + Time eventTime; + + /** Returns the x co-ordinate of the last place that a mouse was pressed. + + The co-ordinate is relative to the component specified in MouseEvent::component. + + @see getDistanceFromDragStart, getDistanceFromDragStartX, mouseWasClicked + */ + int getMouseDownX() const throw(); + + /** Returns the y co-ordinate of the last place that a mouse was pressed. + + The co-ordinate is relative to the component specified in MouseEvent::component. + + @see getDistanceFromDragStart, getDistanceFromDragStartX, mouseWasClicked + */ + int getMouseDownY() const throw(); + + /** Returns the straight-line distance between where the mouse is now and where it + was the last time the button was pressed. + + This is quite handy for things like deciding whether the user has moved far enough + for it to be considered a drag operation. + + @see getDistanceFromDragStartX + */ + int getDistanceFromDragStart() const throw(); + + /** Returns the difference between the mouse's current x postion and where it was + when the button was last pressed. + + @see getDistanceFromDragStart + */ + int getDistanceFromDragStartX() const throw(); + + /** Returns the difference between the mouse's current y postion and where it was + when the button was last pressed. + + @see getDistanceFromDragStart + */ + int getDistanceFromDragStartY() const throw(); + + /** Returns true if the mouse has just been clicked. + + Used in either your mouseUp() or mouseDrag() methods, this will tell you whether + the user has dragged the mouse more than a few pixels from the place where the + mouse-down occurred. + + Once they have dragged it far enough for this method to return false, it will continue + to return false until the mouse-up, even if they move the mouse back to the same + position where they originally pressed it. This means that it's very handy for + objects that can either be clicked on or dragged, as you can use it in the mouseDrag() + callback to ignore any small movements they might make while clicking. + + @returns true if the mouse wasn't dragged by more than a few pixels between + the last time the button was pressed and released. + */ + bool mouseWasClicked() const throw(); + + /** For a click event, the number of times the mouse was clicked in succession. + + So for example a double-click event will return 2, a triple-click 3, etc. + */ + int getNumberOfClicks() const throw() { return numberOfClicks; } + + /** Returns the time that the mouse button has been held down for. + + If called from a mouseDrag or mouseUp callback, this will return the + number of milliseconds since the corresponding mouseDown event occurred. + If called in other contexts, e.g. a mouseMove, then the returned value + may be 0 or an undefined value. + */ + int getLengthOfMousePress() const throw(); + + /** Returns the mouse x position of this event, in global screen co-ordinates. + + The co-ordinates are relative to the top-left of the main monitor. + + @see getMouseDownScreenX, Desktop::getMousePosition + */ + int getScreenX() const throw(); + + /** Returns the mouse y position of this event, in global screen co-ordinates. + + The co-ordinates are relative to the top-left of the main monitor. + + @see getMouseDownScreenY, Desktop::getMousePosition + */ + int getScreenY() const throw(); + + /** Returns the x co-ordinate at which the mouse button was last pressed. + + The co-ordinates are relative to the top-left of the main monitor. + + @see getScreenX, Desktop::getMousePosition + */ + int getMouseDownScreenX() const throw(); + + /** Returns the y co-ordinate at which the mouse button was last pressed. + + The co-ordinates are relative to the top-left of the main monitor. + + @see getScreenY, Desktop::getMousePosition + */ + int getMouseDownScreenY() const throw(); + + /** Creates a version of this event that is relative to a different component. + + The x and y positions of the event that is returned will have been + adjusted to be relative to the new component. + */ + const MouseEvent getEventRelativeTo (Component* const otherComponent) const throw(); + + /** Changes the application-wide setting for the double-click time limit. + + This is the maximum length of time between mouse-clicks for it to be + considered a double-click. It's used by the Component class. + + @see getDoubleClickTimeout, MouseListener::mouseDoubleClick + */ + static void setDoubleClickTimeout (const int timeOutMilliseconds) throw(); + + /** Returns the application-wide setting for the double-click time limit. + + This is the maximum length of time between mouse-clicks for it to be + considered a double-click. It's used by the Component class. + + @see setDoubleClickTimeout, MouseListener::mouseDoubleClick + */ + static int getDoubleClickTimeout() throw(); + + juce_UseDebuggingNewOperator + +private: + int mouseDownX, mouseDownY; + Time mouseDownTime; + int numberOfClicks; + bool wasMovedSinceMouseDown; +}; + +#endif // __JUCE_MOUSEEVENT_JUCEHEADER__ +/********* End of inlined file: juce_MouseEvent.h *********/ + +/** + A MouseListener can be registered with a component to receive callbacks + about mouse events that happen to that component. + + @see Component::addMouseListener, Component::removeMouseListener +*/ +class JUCE_API MouseListener +{ +public: + /** Destructor. */ + virtual ~MouseListener() {} + + /** Called when the mouse moves inside a component. + + If the mouse button isn't pressed and the mouse moves over a component, + this will be called to let the component react to this. + + A component will always get a mouseEnter callback before a mouseMove. + + @param e details about the position and status of the mouse event, including + the source component in which it occurred + @see mouseEnter, mouseExit, mouseDrag, contains + */ + virtual void mouseMove (const MouseEvent& e); + + /** Called when the mouse first enters a component. + + If the mouse button isn't pressed and the mouse moves into a component, + this will be called to let the component react to this. + + When the mouse button is pressed and held down while being moved in + or out of a component, no mouseEnter or mouseExit callbacks are made - only + mouseDrag messages are sent to the component that the mouse was originally + clicked on, until the button is released. + + @param e details about the position and status of the mouse event, including + the source component in which it occurred + @see mouseExit, mouseDrag, mouseMove, contains + */ + virtual void mouseEnter (const MouseEvent& e); + + /** Called when the mouse moves out of a component. + + This will be called when the mouse moves off the edge of this + component. + + If the mouse button was pressed, and it was then dragged off the + edge of the component and released, then this callback will happen + when the button is released, after the mouseUp callback. + + @param e details about the position and status of the mouse event, including + the source component in which it occurred + @see mouseEnter, mouseDrag, mouseMove, contains + */ + virtual void mouseExit (const MouseEvent& e); + + /** Called when a mouse button is pressed. + + The MouseEvent object passed in contains lots of methods for finding out + which button was pressed, as well as which modifier keys (e.g. shift, ctrl) + were held down at the time. + + Once a button is held down, the mouseDrag method will be called when the + mouse moves, until the button is released. + + @param e details about the position and status of the mouse event, including + the source component in which it occurred + @see mouseUp, mouseDrag, mouseDoubleClick, contains + */ + virtual void mouseDown (const MouseEvent& e); + + /** Called when the mouse is moved while a button is held down. + + When a mouse button is pressed inside a component, that component + receives mouseDrag callbacks each time the mouse moves, even if the + mouse strays outside the component's bounds. + + @param e details about the position and status of the mouse event, including + the source component in which it occurred + @see mouseDown, mouseUp, mouseMove, contains, setDragRepeatInterval + */ + virtual void mouseDrag (const MouseEvent& e); + + /** Called when a mouse button is released. + + A mouseUp callback is sent to the component in which a button was pressed + even if the mouse is actually over a different component when the + button is released. + + The MouseEvent object passed in contains lots of methods for finding out + which buttons were down just before they were released. + + @param e details about the position and status of the mouse event, including + the source component in which it occurred + @see mouseDown, mouseDrag, mouseDoubleClick, contains + */ + virtual void mouseUp (const MouseEvent& e); + + /** Called when a mouse button has been double-clicked on a component. + + The MouseEvent object passed in contains lots of methods for finding out + which button was pressed, as well as which modifier keys (e.g. shift, ctrl) + were held down at the time. + + @param e details about the position and status of the mouse event, including + the source component in which it occurred + @see mouseDown, mouseUp + */ + virtual void mouseDoubleClick (const MouseEvent& e); + + /** Called when the mouse-wheel is moved. + + This callback is sent to the component that the mouse is over when the + wheel is moved. + + If not overridden, the component will forward this message to its parent, so + that parent components can collect mouse-wheel messages that happen to + child components which aren't interested in them. + + @param e details about the position and status of the mouse event, including + the source component in which it occurred + @param wheelIncrementX the speed and direction of the horizontal scroll-wheel - a positive + value means the wheel has been pushed to the right, negative means it + was pushed to the left + @param wheelIncrementY the speed and direction of the vertical scroll-wheel - a positive + value means the wheel has been pushed upwards, negative means it + was pushed downwards + */ + virtual void mouseWheelMove (const MouseEvent& e, + float wheelIncrementX, + float wheelIncrementY); + +private: + // XXX Deprecated! The parameters for this method have changed to accommodate horizonatal scroll-wheels. + // This line is here to cause a syntax error if you're trying to use the old-style definition, so + // if that happens, update your code to use the new one above. + virtual int mouseWheelMove (const MouseEvent&, float) { return 0; } +}; + +#endif // __JUCE_MOUSELISTENER_JUCEHEADER__ +/********* End of inlined file: juce_MouseListener.h *********/ + +/********* Start of inlined file: juce_ComponentListener.h *********/ +#ifndef __JUCE_COMPONENTLISTENER_JUCEHEADER__ +#define __JUCE_COMPONENTLISTENER_JUCEHEADER__ + +class Component; + +/** + Gets informed about changes to a component's hierarchy or position. + + To monitor a component for changes, register a subclass of ComponentListener + with the component using Component::addComponentListener(). + + Be sure to deregister listeners before you delete them! + + @see Component::addComponentListener, Component::removeComponentListener +*/ +class JUCE_API ComponentListener +{ +public: + /** Destructor. */ + virtual ~ComponentListener() {} + + /** Called when the component's position or size changes. + + @param component the component that was moved or resized + @param wasMoved true if the component's top-left corner has just moved + @param wasResized true if the component's width or height has just changed + @see Component::setBounds, Component::resized, Component::moved + */ + virtual void componentMovedOrResized (Component& component, + bool wasMoved, + bool wasResized); + + /** Called when the component is brought to the top of the z-order. + + @param component the component that was moved + @see Component::toFront, Component::broughtToFront + */ + virtual void componentBroughtToFront (Component& component); + + /** Called when the component is made visible or invisible. + + @param component the component that changed + @see Component::setVisible + */ + virtual void componentVisibilityChanged (Component& component); + + /** Called when the component has children added or removed. + + @param component the component whose children were changed + @see Component::childrenChanged, Component::addChildComponent, + Component::removeChildComponent + */ + virtual void componentChildrenChanged (Component& component); + + /** Called to indicate that the component's parents have changed. + + When a component is added or removed from its parent, all of its children + will produce this notification (recursively - so all children of its + children will also be called as well). + + @param component the component that this listener is registered with + @see Component::parentHierarchyChanged + */ + virtual void componentParentHierarchyChanged (Component& component); + + /** Called when the component's name is changed. + + @see Component::setName, Component::getName + */ + virtual void componentNameChanged (Component& component); +}; + +#endif // __JUCE_COMPONENTLISTENER_JUCEHEADER__ +/********* End of inlined file: juce_ComponentListener.h *********/ + +/********* Start of inlined file: juce_KeyListener.h *********/ +#ifndef __JUCE_KEYLISTENER_JUCEHEADER__ +#define __JUCE_KEYLISTENER_JUCEHEADER__ + +/********* Start of inlined file: juce_KeyPress.h *********/ +#ifndef __JUCE_KEYPRESS_JUCEHEADER__ +#define __JUCE_KEYPRESS_JUCEHEADER__ + +/** + Represents a key press, including any modifier keys that are needed. + + E.g. a KeyPress might represent CTRL+C, SHIFT+ALT+H, Spacebar, Escape, etc. + + @see Component, KeyListener, Button::addShortcut, KeyPressMappingManager +*/ +class JUCE_API KeyPress +{ +public: + + /** Creates an (invalid) KeyPress. + + @see isValid + */ + KeyPress() throw(); + + /** Creates a KeyPress for a key and some modifiers. + + e.g. + CTRL+C would be: KeyPress ('c', ModifierKeys::ctrlModifier) + SHIFT+Escape would be: KeyPress (KeyPress::escapeKey, ModifierKeys::shiftModifier) + + @param keyCode a code that represents the key - this value must be + one of special constants listed in this class, or an + 8-bit character code such as a letter (case is ignored), + digit or a simple key like "," or ".". Note that this + isn't the same as the textCharacter parameter, so for example + a keyCode of 'a' and a shift-key modifier should have a + textCharacter value of 'A'. + @param modifiers the modifiers to associate with the keystroke + @param textCharacter the character that would be printed if someone typed + this keypress into a text editor. This value may be + null if the keypress is a non-printing character + @see getKeyCode, isKeyCode, getModifiers + */ + KeyPress (const int keyCode, + const ModifierKeys& modifiers, + const juce_wchar textCharacter) throw(); + + /** Creates a keypress with a keyCode but no modifiers or text character. + */ + KeyPress (const int keyCode) throw(); + + /** Creates a copy of another KeyPress. */ + KeyPress (const KeyPress& other) throw(); + + /** Copies this KeyPress from another one. */ + const KeyPress& operator= (const KeyPress& other) throw(); + + /** Compares two KeyPress objects. */ + bool operator== (const KeyPress& other) const throw(); + + /** Compares two KeyPress objects. */ + bool operator!= (const KeyPress& other) const throw(); + + /** Returns true if this is a valid KeyPress. + + A null keypress can be created by the default constructor, in case it's + needed. + */ + bool isValid() const throw() { return keyCode != 0; } + + /** Returns the key code itself. + + This will either be one of the special constants defined in this class, + or an 8-bit character code. + */ + int getKeyCode() const throw() { return keyCode; } + + /** Returns the key modifiers. + + @see ModifierKeys + */ + const ModifierKeys getModifiers() const throw() { return mods; } + + /** Returns the character that is associated with this keypress. + + This is the character that you'd expect to see printed if you press this + keypress in a text editor or similar component. + */ + juce_wchar getTextCharacter() const throw() { return textCharacter; } + + /** Checks whether the KeyPress's key is the same as the one provided, without checking + the modifiers. + + The values for key codes can either be one of the special constants defined in + this class, or an 8-bit character code. + + @see getKeyCode + */ + bool isKeyCode (const int keyCodeToCompare) const throw() { return keyCode == keyCodeToCompare; } + + /** Converts a textual key description to a KeyPress. + + This attempts to decode a textual version of a keypress, e.g. "CTRL + C" or "SPACE". + + This isn't designed to cope with any kind of input, but should be given the + strings that are created by the getTextDescription() method. + + If the string can't be parsed, the object returned will be invalid. + + @see getTextDescription + */ + static const KeyPress createFromDescription (const String& textVersion) throw(); + + /** Creates a textual description of the key combination. + + e.g. "CTRL + C" or "DELETE". + + To store a keypress in a file, use this method, along with createFromDescription() + to retrieve it later. + */ + const String getTextDescription() const throw(); + + /** Checks whether the user is currently holding down the keys that make up this + KeyPress. + + Note that this will return false if any extra modifier keys are + down - e.g. if the keypress is CTRL+X and the user is actually holding CTRL+ALT+x + then it will be false. + */ + bool isCurrentlyDown() const throw(); + + /** Checks whether a particular key is held down, irrespective of modifiers. + + The values for key codes can either be one of the special constants defined in + this class, or an 8-bit character code. + */ + static bool isKeyCurrentlyDown (int keyCode) throw(); + + // Key codes + // + // Note that the actual values of these are platform-specific and may change + // without warning, so don't store them anywhere as constants. For persisting/retrieving + // KeyPress objects, use getTextDescription() and createFromDescription() instead. + // + + static const int spaceKey; /**< key-code for the space bar */ + static const int escapeKey; /**< key-code for the escape key */ + static const int returnKey; /**< key-code for the return key*/ + static const int tabKey; /**< key-code for the tab key*/ + + static const int deleteKey; /**< key-code for the delete key (not backspace) */ + static const int backspaceKey; /**< key-code for the backspace key */ + static const int insertKey; /**< key-code for the insert key */ + + static const int upKey; /**< key-code for the cursor-up key */ + static const int downKey; /**< key-code for the cursor-down key */ + static const int leftKey; /**< key-code for the cursor-left key */ + static const int rightKey; /**< key-code for the cursor-right key */ + static const int pageUpKey; /**< key-code for the page-up key */ + static const int pageDownKey; /**< key-code for the page-down key */ + static const int homeKey; /**< key-code for the home key */ + static const int endKey; /**< key-code for the end key */ + + static const int F1Key; /**< key-code for the F1 key */ + static const int F2Key; /**< key-code for the F2 key */ + static const int F3Key; /**< key-code for the F3 key */ + static const int F4Key; /**< key-code for the F4 key */ + static const int F5Key; /**< key-code for the F5 key */ + static const int F6Key; /**< key-code for the F6 key */ + static const int F7Key; /**< key-code for the F7 key */ + static const int F8Key; /**< key-code for the F8 key */ + static const int F9Key; /**< key-code for the F9 key */ + static const int F10Key; /**< key-code for the F10 key */ + static const int F11Key; /**< key-code for the F11 key */ + static const int F12Key; /**< key-code for the F12 key */ + static const int F13Key; /**< key-code for the F13 key */ + static const int F14Key; /**< key-code for the F14 key */ + static const int F15Key; /**< key-code for the F15 key */ + static const int F16Key; /**< key-code for the F16 key */ + + static const int numberPad0; /**< key-code for the 0 on the numeric keypad. */ + static const int numberPad1; /**< key-code for the 1 on the numeric keypad. */ + static const int numberPad2; /**< key-code for the 2 on the numeric keypad. */ + static const int numberPad3; /**< key-code for the 3 on the numeric keypad. */ + static const int numberPad4; /**< key-code for the 4 on the numeric keypad. */ + static const int numberPad5; /**< key-code for the 5 on the numeric keypad. */ + static const int numberPad6; /**< key-code for the 6 on the numeric keypad. */ + static const int numberPad7; /**< key-code for the 7 on the numeric keypad. */ + static const int numberPad8; /**< key-code for the 8 on the numeric keypad. */ + static const int numberPad9; /**< key-code for the 9 on the numeric keypad. */ + + static const int numberPadAdd; /**< key-code for the add sign on the numeric keypad. */ + static const int numberPadSubtract; /**< key-code for the subtract sign on the numeric keypad. */ + static const int numberPadMultiply; /**< key-code for the multiply sign on the numeric keypad. */ + static const int numberPadDivide; /**< key-code for the divide sign on the numeric keypad. */ + static const int numberPadSeparator; /**< key-code for the comma on the numeric keypad. */ + static const int numberPadDecimalPoint; /**< key-code for the decimal point sign on the numeric keypad. */ + static const int numberPadEquals; /**< key-code for the equals key on the numeric keypad. */ + static const int numberPadDelete; /**< key-code for the delete key on the numeric keypad. */ + + static const int playKey; /**< key-code for a multimedia 'play' key, (not all keyboards will have one) */ + static const int stopKey; /**< key-code for a multimedia 'stop' key, (not all keyboards will have one) */ + static const int fastForwardKey; /**< key-code for a multimedia 'fast-forward' key, (not all keyboards will have one) */ + static const int rewindKey; /**< key-code for a multimedia 'rewind' key, (not all keyboards will have one) */ + + juce_UseDebuggingNewOperator + +private: + + int keyCode; + ModifierKeys mods; + juce_wchar textCharacter; +}; + +#endif // __JUCE_KEYPRESS_JUCEHEADER__ +/********* End of inlined file: juce_KeyPress.h *********/ + +class Component; + +/** + Receives callbacks when keys are pressed. + + You can add a key listener to a component to be informed when that component + gets key events. See the Component::addListener method for more details. + + @see KeyPress, Component::addKeyListener, KeyPressMappingManager +*/ +class JUCE_API KeyListener +{ +public: + /** Destructor. */ + virtual ~KeyListener() {} + + /** Called to indicate that a key has been pressed. + + If your implementation returns true, then the key event is considered to have + been consumed, and will not be passed on to any other components. If it returns + false, then the key will be passed to other components that might want to use it. + + @param key the keystroke, including modifier keys + @param originatingComponent the component that received the key event + @see keyStateChanged, Component::keyPressed + */ + virtual bool keyPressed (const KeyPress& key, + Component* originatingComponent) = 0; + + /** Called when any key is pressed or released. + + When this is called, classes that might be interested in + the state of one or more keys can use KeyPress::isKeyCurrentlyDown() to + check whether their key has changed. + + If your implementation returns true, then the key event is considered to have + been consumed, and will not be passed on to any other components. If it returns + false, then the key will be passed to other components that might want to use it. + + @param originatingComponent the component that received the key event + @see KeyPress, Component::keyStateChanged + */ + virtual bool keyStateChanged (Component* originatingComponent); + +}; + +#endif // __JUCE_KEYLISTENER_JUCEHEADER__ +/********* End of inlined file: juce_KeyListener.h *********/ + +/********* Start of inlined file: juce_KeyboardFocusTraverser.h *********/ +#ifndef __JUCE_KEYBOARDFOCUSTRAVERSER_JUCEHEADER__ +#define __JUCE_KEYBOARDFOCUSTRAVERSER_JUCEHEADER__ + +class Component; + +/** + Controls the order in which focus moves between components. + + The default algorithm used by this class to work out the order of traversal + is as follows: + - if two components both have an explicit focus order specified, then the + one with the lowest number comes first (see the Component::setExplicitFocusOrder() + method). + - any component with an explicit focus order greater than 0 comes before ones + that don't have an order specified. + - any unspecified components are traversed in a left-to-right, then top-to-bottom + order. + + If you need traversal in a more customised way, you can create a subclass + of KeyboardFocusTraverser that uses your own algorithm, and use + Component::createFocusTraverser() to create it. + + @see Component::setExplicitFocusOrder, Component::createFocusTraverser +*/ +class JUCE_API KeyboardFocusTraverser +{ +public: + KeyboardFocusTraverser(); + + /** Destructor. */ + virtual ~KeyboardFocusTraverser(); + + /** Returns the component that should be given focus after the specified one + when moving "forwards". + + The default implementation will return the next component which is to the + right of or below this one. + + This may return 0 if there's no suitable candidate. + */ + virtual Component* getNextComponent (Component* current); + + /** Returns the component that should be given focus after the specified one + when moving "backwards". + + The default implementation will return the next component which is to the + left of or above this one. + + This may return 0 if there's no suitable candidate. + */ + virtual Component* getPreviousComponent (Component* current); + + /** Returns the component that should receive focus be default within the given + parent component. + + The default implementation will just return the foremost child component that + wants focus. + + This may return 0 if there's no suitable candidate. + */ + virtual Component* getDefaultComponent (Component* parentComponent); +}; + +#endif // __JUCE_KEYBOARDFOCUSTRAVERSER_JUCEHEADER__ +/********* End of inlined file: juce_KeyboardFocusTraverser.h *********/ + +/********* Start of inlined file: juce_ImageEffectFilter.h *********/ +#ifndef __JUCE_IMAGEEFFECTFILTER_JUCEHEADER__ +#define __JUCE_IMAGEEFFECTFILTER_JUCEHEADER__ + +/********* Start of inlined file: juce_Graphics.h *********/ +#ifndef __JUCE_GRAPHICS_JUCEHEADER__ +#define __JUCE_GRAPHICS_JUCEHEADER__ + +/********* Start of inlined file: juce_Font.h *********/ +#ifndef __JUCE_FONT_JUCEHEADER__ +#define __JUCE_FONT_JUCEHEADER__ + +/********* Start of inlined file: juce_Typeface.h *********/ +#ifndef __JUCE_TYPEFACE_JUCEHEADER__ +#define __JUCE_TYPEFACE_JUCEHEADER__ + +/********* Start of inlined file: juce_Path.h *********/ +#ifndef __JUCE_PATH_JUCEHEADER__ +#define __JUCE_PATH_JUCEHEADER__ + +/********* Start of inlined file: juce_AffineTransform.h *********/ +#ifndef __JUCE_AFFINETRANSFORM_JUCEHEADER__ +#define __JUCE_AFFINETRANSFORM_JUCEHEADER__ + +/** + Represents a 2D affine-transformation matrix. + + An affine transformation is a transformation such as a rotation, scale, shear, + resize or translation. + + These are used for various 2D transformation tasks, e.g. with Path objects. + + @see Path, Point, Line +*/ +class JUCE_API AffineTransform +{ +public: + + /** Creates an identity transform. */ + AffineTransform() throw(); + + /** Creates a copy of another transform. */ + AffineTransform (const AffineTransform& other) throw(); + + /** Creates a transform from a set of raw matrix values. + + The resulting matrix is: + + (mat00 mat01 mat02) + (mat10 mat11 mat12) + ( 0 0 1 ) + */ + AffineTransform (const float mat00, const float mat01, const float mat02, + const float mat10, const float mat11, const float mat12) throw(); + + /** Copies from another AffineTransform object */ + const AffineTransform& operator= (const AffineTransform& other) throw(); + + /** Compares two transforms. */ + bool operator== (const AffineTransform& other) const throw(); + + /** Compares two transforms. */ + bool operator!= (const AffineTransform& other) const throw(); + + /** A ready-to-use identity transform, which you can use to append other + transformations to. + + e.g. @code + AffineTransform myTransform = AffineTransform::identity.rotated (.5f) + .scaled (2.0f); + + @endcode + */ + static const AffineTransform identity; + + /** Transforms a 2D co-ordinate using this matrix. */ + void transformPoint (float& x, + float& y) const throw(); + + /** Transforms a 2D co-ordinate using this matrix. */ + void transformPoint (double& x, + double& y) const throw(); + + /** Returns a new transform which is the same as this one followed by a translation. */ + const AffineTransform translated (const float deltaX, + const float deltaY) const throw(); + + /** Returns a new transform which is a translation. */ + static const AffineTransform translation (const float deltaX, + const float deltaY) throw(); + + /** Returns a transform which is the same as this one followed by a rotation. + + The rotation is specified by a number of radians to rotate clockwise, centred around + the origin (0, 0). + */ + const AffineTransform rotated (const float angleInRadians) const throw(); + + /** Returns a transform which is the same as this one followed by a rotation about a given point. + + The rotation is specified by a number of radians to rotate clockwise, centred around + the co-ordinates passed in. + */ + const AffineTransform rotated (const float angleInRadians, + const float pivotX, + const float pivotY) const throw(); + + /** Returns a new transform which is a rotation about (0, 0). */ + static const AffineTransform rotation (const float angleInRadians) throw(); + + /** Returns a new transform which is a rotation about a given point. */ + static const AffineTransform rotation (const float angleInRadians, + const float pivotX, + const float pivotY) throw(); + + /** Returns a transform which is the same as this one followed by a re-scaling. + + The scaling is centred around the origin (0, 0). + */ + const AffineTransform scaled (const float factorX, + const float factorY) const throw(); + + /** Returns a new transform which is a re-scale about the origin. */ + static const AffineTransform scale (const float factorX, + const float factorY) throw(); + + /** Returns a transform which is the same as this one followed by a shear. + + The shear is centred around the origin (0, 0). + */ + const AffineTransform sheared (const float shearX, + const float shearY) const throw(); + + /** Returns a matrix which is the inverse operation of this one. + + Some matrices don't have an inverse - in this case, the method will just return + an identity transform. + */ + const AffineTransform inverted() const throw(); + + /** Returns the result of concatenating another transformation after this one. */ + const AffineTransform followedBy (const AffineTransform& other) const throw(); + + /** Returns true if this transform has no effect on points. */ + bool isIdentity() const throw(); + + /** Returns true if this transform maps to a singularity - i.e. if it has no inverse. */ + bool isSingularity() const throw(); + + juce_UseDebuggingNewOperator + + /* The transform matrix is: + + (mat00 mat01 mat02) + (mat10 mat11 mat12) + ( 0 0 1 ) + */ + float mat00, mat01, mat02; + float mat10, mat11, mat12; + +private: + + const AffineTransform followedBy (const float mat00, const float mat01, const float mat02, + const float mat10, const float mat11, const float mat12) const throw(); +}; + +#endif // __JUCE_AFFINETRANSFORM_JUCEHEADER__ +/********* End of inlined file: juce_AffineTransform.h *********/ + +/********* Start of inlined file: juce_Point.h *********/ +#ifndef __JUCE_POINT_JUCEHEADER__ +#define __JUCE_POINT_JUCEHEADER__ + +/** + A pair of (x, y) co-ordinates. + + Uses 32-bit floating point accuracy. + + @see Line, Path, AffineTransform +*/ +class JUCE_API Point +{ +public: + + /** Creates a point with co-ordinates (0, 0). */ + Point() throw(); + + /** Creates a copy of another point. */ + Point (const Point& other) throw(); + + /** Creates a point from an (x, y) position. */ + Point (const float x, const float y) throw(); + + /** Copies this point from another one. + @see setXY + */ + const Point& operator= (const Point& other) throw(); + + /** Destructor. */ + ~Point() throw(); + + /** Returns the point's x co-ordinate. */ + inline float getX() const throw() { return x; } + + /** Returns the point's y co-ordinate. */ + inline float getY() const throw() { return y; } + + /** Changes the point's x and y co-ordinates. */ + void setXY (const float x, + const float y) throw(); + + /** Uses a transform to change the point's co-ordinates. + + @see AffineTransform::transformPoint + */ + void applyTransform (const AffineTransform& transform) throw(); + + juce_UseDebuggingNewOperator + +private: + float x, y; +}; + +#endif // __JUCE_POINT_JUCEHEADER__ +/********* End of inlined file: juce_Point.h *********/ + +/********* Start of inlined file: juce_Justification.h *********/ +#ifndef __JUCE_JUSTIFICATION_JUCEHEADER__ +#define __JUCE_JUSTIFICATION_JUCEHEADER__ + +/** + Represents a type of justification to be used when positioning graphical items. + + e.g. it indicates whether something should be placed top-left, top-right, + centred, etc. + + It is used in various places wherever this kind of information is needed. +*/ +class JUCE_API Justification +{ +public: + + /** Creates a Justification object using a combination of flags. */ + inline Justification (const int flags_) throw() : flags (flags_) {} + + /** Creates a copy of another Justification object. */ + Justification (const Justification& other) throw(); + + /** Copies another Justification object. */ + const Justification& operator= (const Justification& other) throw(); + + /** Returns the raw flags that are set for this Justification object. */ + inline int getFlags() const throw() { return flags; } + + /** Tests a set of flags for this object. + + @returns true if any of the flags passed in are set on this object. + */ + inline bool testFlags (const int flagsToTest) const throw() { return (flags & flagsToTest) != 0; } + + /** Returns just the flags from this object that deal with vertical layout. */ + int getOnlyVerticalFlags() const throw(); + + /** Returns just the flags from this object that deal with horizontal layout. */ + int getOnlyHorizontalFlags() const throw(); + + /** Adjusts the position of a rectangle to fit it into a space. + + The (x, y) position of the rectangle will be updated to position it inside the + given space according to the justification flags. + */ + void applyToRectangle (int& x, int& y, + const int w, const int h, + const int spaceX, const int spaceY, + const int spaceW, const int spaceH) const throw(); + + /** Flag values that can be combined and used in the constructor. */ + enum + { + + /** Indicates that the item should be aligned against the left edge of the available space. */ + left = 1, + + /** Indicates that the item should be aligned against the right edge of the available space. */ + right = 2, + + /** Indicates that the item should be placed in the centre between the left and right + sides of the available space. */ + horizontallyCentred = 4, + + /** Indicates that the item should be aligned against the top edge of the available space. */ + top = 8, + + /** Indicates that the item should be aligned against the bottom edge of the available space. */ + bottom = 16, + + /** Indicates that the item should be placed in the centre between the top and bottom + sides of the available space. */ + verticallyCentred = 32, + + /** Indicates that lines of text should be spread out to fill the maximum width + available, so that both margins are aligned vertically. + */ + horizontallyJustified = 64, + + /** Indicates that the item should be centred vertically and horizontally. + + This is equivalent to (horizontallyCentred | verticallyCentred) + */ + centred = 36, + + /** Indicates that the item should be centred vertically but placed on the left hand side. + + This is equivalent to (left | verticallyCentred) + */ + centredLeft = 33, + + /** Indicates that the item should be centred vertically but placed on the right hand side. + + This is equivalent to (right | verticallyCentred) + */ + centredRight = 34, + + /** Indicates that the item should be centred horizontally and placed at the top. + + This is equivalent to (horizontallyCentred | top) + */ + centredTop = 12, + + /** Indicates that the item should be centred horizontally and placed at the bottom. + + This is equivalent to (horizontallyCentred | bottom) + */ + centredBottom = 20, + + /** Indicates that the item should be placed in the top-left corner. + + This is equivalent to (left | top) + */ + topLeft = 9, + + /** Indicates that the item should be placed in the top-right corner. + + This is equivalent to (right | top) + */ + topRight = 10, + + /** Indicates that the item should be placed in the bottom-left corner. + + This is equivalent to (left | bottom) + */ + bottomLeft = 17, + + /** Indicates that the item should be placed in the bottom-left corner. + + This is equivalent to (right | bottom) + */ + bottomRight = 18 + }; + +private: + + int flags; +}; + +#endif // __JUCE_JUSTIFICATION_JUCEHEADER__ +/********* End of inlined file: juce_Justification.h *********/ + +/** + A path is a sequence of lines and curves that may either form a closed shape + or be open-ended. + + To use a path, you can create an empty one, then add lines and curves to it + to create shapes, then it can be rendered by a Graphics context or used + for geometric operations. + + e.g. @code + Path myPath; + + myPath.startNewSubPath (10.0f, 10.0f); // move the current position to (10, 10) + myPath.lineTo (100.0f, 200.0f); // draw a line from here to (100, 200) + myPath.quadraticTo (0.0f, 150.0f, 5.0f, 50.0f); // draw a curve that ends at (5, 50) + myPath.closeSubPath(); // close the subpath with a line back to (10, 10) + + // add an ellipse as well, which will form a second sub-path within the path.. + myPath.addEllipse (50.0f, 50.0f, 40.0f, 30.0f); + + // double the width of the whole thing.. + myPath.applyTransform (AffineTransform::scale (2.0f, 1.0f)); + + // and draw it to a graphics context with a 5-pixel thick outline. + g.strokePath (myPath, PathStrokeType (5.0f)); + + @endcode + + A path object can actually contain multiple sub-paths, which may themselves + be open or closed. + + @see PathFlatteningIterator, PathStrokeType, Graphics +*/ +class JUCE_API Path : private ArrayAllocationBase +{ +public: + + /** Creates an empty path. */ + Path() throw(); + + /** Creates a copy of another path. */ + Path (const Path& other) throw(); + + /** Destructor. */ + ~Path() throw(); + + /** Copies this path from another one. */ + const Path& operator= (const Path& other) throw(); + + /** Returns true if the path doesn't contain any lines or curves. */ + bool isEmpty() const throw(); + + /** Returns the smallest rectangle that contains all points within the path. + */ + void getBounds (float& x, float& y, + float& w, float& h) const throw(); + + /** Returns the smallest rectangle that contains all points within the path + after it's been transformed with the given tranasform matrix. + */ + void getBoundsTransformed (const AffineTransform& transform, + float& x, float& y, + float& w, float& h) const throw(); + + /** Checks whether a point lies within the path. + + This is only relevent for closed paths (see closeSubPath()), and + may produce false results if used on a path which has open sub-paths. + + The path's winding rule is taken into account by this method. + + @see closeSubPath, setUsingNonZeroWinding + */ + bool contains (const float x, + const float y) const throw(); + + /** Checks whether a line crosses the path. + + This will return positive if the line crosses any of the paths constituent + lines or curves. It doesn't take into account whether the line is inside + or outside the path, or whether the path is open or closed. + */ + bool intersectsLine (const float x1, const float y1, + const float x2, const float y2) throw(); + + /** Removes all lines and curves, resetting the path completely. */ + void clear() throw(); + + /** Begins a new subpath with a given starting position. + + This will move the path's current position to the co-ordinates passed in and + make it ready to draw lines or curves starting from this position. + + After adding whatever lines and curves are needed, you can either + close the current sub-path using closeSubPath() or call startNewSubPath() + to move to a new sub-path, leaving the old one open-ended. + + @see lineTo, quadraticTo, cubicTo, closeSubPath + */ + void startNewSubPath (const float startX, + const float startY) throw(); + + /** Closes a the current sub-path with a line back to its start-point. + + When creating a closed shape such as a triangle, don't use 3 lineTo() + calls - instead use two lineTo() calls, followed by a closeSubPath() + to join the final point back to the start. + + This ensures that closes shapes are recognised as such, and this is + important for tasks like drawing strokes, which needs to know whether to + draw end-caps or not. + + @see startNewSubPath, lineTo, quadraticTo, cubicTo, closeSubPath + */ + void closeSubPath() throw(); + + /** Adds a line from the shape's last position to a new end-point. + + This will connect the end-point of the last line or curve that was added + to a new point, using a straight line. + + See the class description for an example of how to add lines and curves to a path. + + @see startNewSubPath, quadraticTo, cubicTo, closeSubPath + */ + void lineTo (const float endX, + const float endY) throw(); + + /** Adds a quadratic bezier curve from the shape's last position to a new position. + + This will connect the end-point of the last line or curve that was added + to a new point, using a quadratic spline with one control-point. + + See the class description for an example of how to add lines and curves to a path. + + @see startNewSubPath, lineTo, cubicTo, closeSubPath + */ + void quadraticTo (const float controlPointX, + const float controlPointY, + const float endPointX, + const float endPointY) throw(); + + /** Adds a cubic bezier curve from the shape's last position to a new position. + + This will connect the end-point of the last line or curve that was added + to a new point, using a cubic spline with two control-points. + + See the class description for an example of how to add lines and curves to a path. + + @see startNewSubPath, lineTo, quadraticTo, closeSubPath + */ + void cubicTo (const float controlPoint1X, + const float controlPoint1Y, + const float controlPoint2X, + const float controlPoint2Y, + const float endPointX, + const float endPointY) throw(); + + /** Returns the last point that was added to the path by one of the drawing methods. + */ + const Point getCurrentPosition() const; + + /** Adds a rectangle to the path. + + The rectangle is added as a new sub-path. (Any currently open paths will be + left open). + + @see addRoundedRectangle, addTriangle + */ + void addRectangle (const float x, const float y, + const float w, const float h) throw(); + + /** Adds a rectangle with rounded corners to the path. + + The rectangle is added as a new sub-path. (Any currently open paths will be + left open). + + @see addRectangle, addTriangle + */ + void addRoundedRectangle (const float x, const float y, + const float w, const float h, + float cornerSize) throw(); + + /** Adds a rectangle with rounded corners to the path. + + The rectangle is added as a new sub-path. (Any currently open paths will be + left open). + + @see addRectangle, addTriangle + */ + void addRoundedRectangle (const float x, const float y, + const float w, const float h, + float cornerSizeX, + float cornerSizeY) throw(); + + /** Adds a triangle to the path. + + The triangle is added as a new closed sub-path. (Any currently open paths will be + left open). + + Note that whether the vertices are specified in clockwise or anticlockwise + order will affect how the triangle is filled when it overlaps other + shapes (the winding order setting will affect this of course). + */ + void addTriangle (const float x1, const float y1, + const float x2, const float y2, + const float x3, const float y3) throw(); + + /** Adds a quadrilateral to the path. + + The quad is added as a new closed sub-path. (Any currently open paths will be + left open). + + Note that whether the vertices are specified in clockwise or anticlockwise + order will affect how the quad is filled when it overlaps other + shapes (the winding order setting will affect this of course). + */ + void addQuadrilateral (const float x1, const float y1, + const float x2, const float y2, + const float x3, const float y3, + const float x4, const float y4) throw(); + + /** Adds an ellipse to the path. + + The shape is added as a new sub-path. (Any currently open paths will be + left open). + + @see addArc + */ + void addEllipse (const float x, const float y, + const float width, const float height) throw(); + + /** Adds an elliptical arc to the current path. + + Note that when specifying the start and end angles, the curve will be drawn either clockwise + or anti-clockwise according to whether the end angle is greater than the start. This means + that sometimes you may need to use values greater than 2*Pi for the end angle. + + @param x the left-hand edge of the rectangle in which the elliptical outline fits + @param y the top edge of the rectangle in which the elliptical outline fits + @param width the width of the rectangle in which the elliptical outline fits + @param height the height of the rectangle in which the elliptical outline fits + @param fromRadians the angle (clockwise) in radians at which to start the arc segment (where 0 is the + top-centre of the ellipse) + @param toRadians the angle (clockwise) in radians at which to end the arc segment (where 0 is the + top-centre of the ellipse). This angle can be greater than 2*Pi, so for example to + draw a curve clockwise from the 9 o'clock position to the 3 o'clock position via + 12 o'clock, you'd use 1.5*Pi and 2.5*Pi as the start and finish points. + @param startAsNewSubPath if true, the arc will begin a new subpath from its starting point; if false, + it will be added to the current sub-path, continuing from the current postition + + @see addCentredArc, arcTo, addPieSegment, addEllipse + */ + void addArc (const float x, const float y, + const float width, const float height, + const float fromRadians, + const float toRadians, + const bool startAsNewSubPath = false) throw(); + + /** Adds an arc which is centred at a given point, and can have a rotation specified. + + Note that when specifying the start and end angles, the curve will be drawn either clockwise + or anti-clockwise according to whether the end angle is greater than the start. This means + that sometimes you may need to use values greater than 2*Pi for the end angle. + + @param centreX the centre x of the ellipse + @param centreY the centre y of the ellipse + @param radiusX the horizontal radius of the ellipse + @param radiusY the vertical radius of the ellipse + @param rotationOfEllipse an angle by which the whole ellipse should be rotated about its centre, in radians (clockwise) + @param fromRadians the angle (clockwise) in radians at which to start the arc segment (where 0 is the + top-centre of the ellipse) + @param toRadians the angle (clockwise) in radians at which to end the arc segment (where 0 is the + top-centre of the ellipse). This angle can be greater than 2*Pi, so for example to + draw a curve clockwise from the 9 o'clock position to the 3 o'clock position via + 12 o'clock, you'd use 1.5*Pi and 2.5*Pi as the start and finish points. + @param startAsNewSubPath if true, the arc will begin a new subpath from its starting point; if false, + it will be added to the current sub-path, continuing from the current postition + + @see addArc, arcTo + */ + void addCentredArc (const float centreX, const float centreY, + const float radiusX, const float radiusY, + const float rotationOfEllipse, + const float fromRadians, + const float toRadians, + const bool startAsNewSubPath = false) throw(); + + /** Adds a "pie-chart" shape to the path. + + The shape is added as a new sub-path. (Any currently open paths will be + left open). + + Note that when specifying the start and end angles, the curve will be drawn either clockwise + or anti-clockwise according to whether the end angle is greater than the start. This means + that sometimes you may need to use values greater than 2*Pi for the end angle. + + @param x the left-hand edge of the rectangle in which the elliptical outline fits + @param y the top edge of the rectangle in which the elliptical outline fits + @param width the width of the rectangle in which the elliptical outline fits + @param height the height of the rectangle in which the elliptical outline fits + @param fromRadians the angle (clockwise) in radians at which to start the arc segment (where 0 is the + top-centre of the ellipse) + @param toRadians the angle (clockwise) in radians at which to end the arc segment (where 0 is the + top-centre of the ellipse) + @param innerCircleProportionalSize if this is > 0, then the pie will be drawn as a curved band around a hollow + ellipse at its centre, where this value indicates the inner ellipse's size with + respect to the outer one. + + @see addArc + */ + void addPieSegment (const float x, const float y, + const float width, const float height, + const float fromRadians, + const float toRadians, + const float innerCircleProportionalSize); + + /** Adds a line with a specified thickness. + + The line is added as a new closed sub-path. (Any currently open paths will be + left open). + + @see addArrow + */ + void addLineSegment (const float startX, const float startY, + const float endX, const float endY, + float lineThickness) throw(); + + /** Adds a line with an arrowhead on the end. + + The arrow is added as a new closed sub-path. (Any currently open paths will be + left open). + */ + void addArrow (const float startX, const float startY, + const float endX, const float endY, + float lineThickness, + float arrowheadWidth, + float arrowheadLength) throw(); + + /** Adds a star shape to the path. + + */ + void addStar (const float centreX, + const float centreY, + const int numberOfPoints, + const float innerRadius, + const float outerRadius, + const float startAngle = 0.0f); + + /** Adds a speech-bubble shape to the path. + + @param bodyX the left of the main body area of the bubble + @param bodyY the top of the main body area of the bubble + @param bodyW the width of the main body area of the bubble + @param bodyH the height of the main body area of the bubble + @param cornerSize the amount by which to round off the corners of the main body rectangle + @param arrowTipX the x position that the tip of the arrow should connect to + @param arrowTipY the y position that the tip of the arrow should connect to + @param whichSide the side to connect the arrow to: 0 = top, 1 = left, 2 = bottom, 3 = right + @param arrowPositionAlongEdgeProportional how far along the edge of the main rectangle the + arrow's base should be - this is a proportional distance between 0 and 1.0 + @param arrowWidth how wide the base of the arrow should be where it joins the main rectangle + */ + void addBubble (float bodyX, float bodyY, + float bodyW, float bodyH, + float cornerSize, + float arrowTipX, + float arrowTipY, + int whichSide, + float arrowPositionAlongEdgeProportional, + float arrowWidth); + + /** Adds another path to this one. + + The new path is added as a new sub-path. (Any currently open paths in this + path will be left open). + + @param pathToAppend the path to add + */ + void addPath (const Path& pathToAppend) throw(); + + /** Adds another path to this one, transforming it on the way in. + + The new path is added as a new sub-path, its points being transformed by the given + matrix before being added. + + @param pathToAppend the path to add + @param transformToApply an optional transform to apply to the incoming vertices + */ + void addPath (const Path& pathToAppend, + const AffineTransform& transformToApply) throw(); + + /** Swaps the contents of this path with another one. + + The internal data of the two paths is swapped over, so this is much faster than + copying it to a temp variable and back. + */ + void swapWithPath (Path& other); + + /** Applies a 2D transform to all the vertices in the path. + + @see AffineTransform, scaleToFit, getTransformToScaleToFit + */ + void applyTransform (const AffineTransform& transform) throw(); + + /** Rescales this path to make it fit neatly into a given space. + + This is effectively a quick way of calling + applyTransform (getTransformToScaleToFit (x, y, w, h, preserveProportions)) + + @param x the x position of the rectangle to fit the path inside + @param y the y position of the rectangle to fit the path inside + @param width the width of the rectangle to fit the path inside + @param height the height of the rectangle to fit the path inside + @param preserveProportions if true, it will fit the path into the space without altering its + horizontal/vertical scale ratio; if false, it will distort the + path to fill the specified ratio both horizontally and vertically + + @see applyTransform, getTransformToScaleToFit + */ + void scaleToFit (const float x, const float y, + const float width, const float height, + const bool preserveProportions) throw(); + + /** Returns a transform that can be used to rescale the path to fit into a given space. + + @param x the x position of the rectangle to fit the path inside + @param y the y position of the rectangle to fit the path inside + @param width the width of the rectangle to fit the path inside + @param height the height of the rectangle to fit the path inside + @param preserveProportions if true, it will fit the path into the space without altering its + horizontal/vertical scale ratio; if false, it will distort the + path to fill the specified ratio both horizontally and vertically + @param justificationType if the proportions are preseved, the resultant path may be smaller + than the available rectangle, so this describes how it should be + positioned within the space. + @returns an appropriate transformation + + @see applyTransform, scaleToFit + + */ + const AffineTransform getTransformToScaleToFit (const float x, const float y, + const float width, const float height, + const bool preserveProportions, + const Justification& justificationType = Justification::centred) const throw(); + + /** Creates a version of this path where all sharp corners have been replaced by curves. + + Wherever two lines meet at an angle, this will replace the corner with a curve + of the given radius. + */ + const Path createPathWithRoundedCorners (const float cornerRadius) const throw(); + + /** Changes the winding-rule to be used when filling the path. + + If set to true (which is the default), then the path uses a non-zero-winding rule + to determine which points are inside the path. If set to false, it uses an + alternate-winding rule. + + The winding-rule comes into play when areas of the shape overlap other + areas, and determines whether the overlapping regions are considered to be + inside or outside. + + Changing this value just sets a flag - it doesn't affect the contents of the + path. + + @see isUsingNonZeroWinding + */ + void setUsingNonZeroWinding (const bool isNonZeroWinding) throw(); + + /** Returns the flag that indicates whether the path should use a non-zero winding rule. + + The default for a new path is true. + + @see setUsingNonZeroWinding + */ + bool isUsingNonZeroWinding() const throw() { return useNonZeroWinding; } + + /** Iterates the lines and curves that a path contains. + + @see Path, PathFlatteningIterator + */ + class JUCE_API Iterator + { + public: + + Iterator (const Path& path); + ~Iterator(); + + /** Moves onto the next element in the path. + + If this returns false, there are no more elements. If it returns true, + the elementType variable will be set to the type of the current element, + and some of the x and y variables will be filled in with values. + */ + bool next(); + + enum PathElementType + { + startNewSubPath, /**< For this type, x1 and y1 will be set to indicate the first point in the subpath. */ + lineTo, /**< For this type, x1 and y1 indicate the end point of the line. */ + quadraticTo, /**< For this type, x1, y1, x2, y2 indicate the control point and endpoint of a quadratic curve. */ + cubicTo, /**< For this type, x1, y1, x2, y2, x3, y3 indicate the two control points and the endpoint of a cubic curve. */ + closePath /**< Indicates that the sub-path is being closed. None of the x or y values are valid in this case. */ + }; + + PathElementType elementType; + + float x1, y1, x2, y2, x3, y3; + + private: + const Path& path; + int index; + + Iterator (const Iterator&); + const Iterator& operator= (const Iterator&); + }; + + /** Loads a stored path from a data stream. + + The data in the stream must have been written using writePathToStream(). + + Note that this will append the stored path to whatever is currently in + this path, so you might need to call clear() beforehand. + + @see loadPathFromData, writePathToStream + */ + void loadPathFromStream (InputStream& source); + + /** Loads a stored path from a block of data. + + This is similar to loadPathFromStream(), but just reads from a block + of data. Useful if you're including stored shapes in your code as a + block of static data. + + @see loadPathFromStream, writePathToStream + */ + void loadPathFromData (const unsigned char* const data, + const int numberOfBytes) throw(); + + /** Stores the path by writing it out to a stream. + + After writing out a path, you can reload it using loadPathFromStream(). + + @see loadPathFromStream, loadPathFromData + */ + void writePathToStream (OutputStream& destination) const; + + /** Creates a string containing a textual representation of this path. + @see restoreFromString + */ + const String toString() const; + + /** Restores this path from a string that was created with the toString() method. + @see toString() + */ + void restoreFromString (const String& stringVersion); + + juce_UseDebuggingNewOperator + +private: + friend class PathFlatteningIterator; + friend class Path::Iterator; + int numElements; + float pathXMin, pathXMax, pathYMin, pathYMax; + bool useNonZeroWinding; + + static const float lineMarker; + static const float moveMarker; + static const float quadMarker; + static const float cubicMarker; + static const float closeSubPathMarker; +}; + +#endif // __JUCE_PATH_JUCEHEADER__ +/********* End of inlined file: juce_Path.h *********/ + +class Font; +class Typeface; + +/** + Stores information about the shape and kerning of one of the glyphs in a Typeface. + + @see Typeface, PositionedGlyph, GlyphArrangement +*/ +class JUCE_API TypefaceGlyphInfo +{ +public: + + /** Returns the path that describes the glyph's outline. + + This is normalised to a height of 1.0, and its origin is the + left-hand edge of the glyph's baseline. + */ + const Path& getPath() const throw() { return path; } + + /** Returns the unicode character that this glyph represents. */ + juce_wchar getCharacter() const throw() { return character; } + + bool isWhitespace() const throw() { return CharacterFunctions::isWhitespace (character); } + + /** Returns the distance to leave between this and a following character. + + The value returned is expressed as a proportion of the font's height. + */ + float getHorizontalSpacing (const juce_wchar subsequentCharacter) const throw(); + + /** Returns the typeface that this glyph belongs to. */ + Typeface* getTypeface() const throw() { return typeface; } + +private: + + friend class Typeface; + + struct KerningPair + { + juce_wchar character2; + float kerningAmount; + }; + + const juce_wchar character; + const Path path; + float width; + MemoryBlock kerningPairs; + Typeface* const typeface; + + TypefaceGlyphInfo (const juce_wchar character, + const Path& shape, + const float horizontalSeparation, + Typeface* const typeface) throw(); + ~TypefaceGlyphInfo() throw(); + + KerningPair& getKerningPair (const int index) const throw(); + int getNumKerningPairs() const throw(); + + void addKerningPair (const juce_wchar subsequentCharacter, + const float extraKerningAmount) throw(); + + const TypefaceGlyphInfo& operator= (const TypefaceGlyphInfo&); +}; + +/** + Represents a size-independent system font. + + A Font object represents a particular Typeface along with a specific size, + style, kerning, scale, etc, wheras the Typeface is just a generalised description + of the shapes of the glyphs and their properties. + +*/ +class JUCE_API Typeface : public ReferenceCountedObject +{ +public: + + /** Tries to load a named system font and to initialise all the glyphs + appropriately from it. + + @param faceName the name of the typeface, e.g. "Times" + @param bold whether to try to find a bold version of the font (may not always be available) + @param italic whether to try to find an italicised version of the font (may not always be available) + */ + Typeface (const String& faceName, + const bool bold, + const bool italic); + + /** Creates a copy of another typeface */ + Typeface (const Typeface& other); + + /** Destructor. */ + ~Typeface(); + + /** Copies another typeface over this one. */ + const Typeface& operator= (const Typeface& other) throw(); + + /** Returns a unique ID for the typeface. + + This is based on the name and style, so can be used to compare two Typeface objects. + */ + int hashCode() const throw() { return hash; } + + /** Returns the name of the typeface, e.g. "Times", "Verdana", etc */ + const String& getName() const throw() { return typefaceName; } + + /** Returns the font's ascent as a proportion of its height. */ + float getAscent() const throw() { return ascent; } + + /** Returns true if the font is flagged as being bold. */ + bool isBold() const throw() { return bold; } + + /** Returns true if the typeface's 'italic' flag is set. */ + bool isItalic() const throw() { return italic; } + + /** Finds the Path that describes the outline shape of a character. + + The height of the path is normalised to 1.0 (i.e. a distance of 1.0 is the + height of the font). + + This may return 0 if the typeface has no characters, but if the character + that is asked for is not found, it will first try to return a default + character instead. + */ + const Path* getOutlineForGlyph (const juce_wchar character) throw(); + + /** Tries to find the information describing a glyph for this character. + + If there isn't a glyph specifically for the character it will return + a default glyph instead; if the typeface is empty, it may return a null + pointer. + */ + const TypefaceGlyphInfo* getGlyph (const juce_wchar character) throw(); + + /** Deletes all the glyphs and kerning data fom the typeface. */ + void clear() throw(); + + /** Adds a glyph to the typeface. + + This is typically only called by the platform-specific code that generates + the typeface from a system font. + */ + void addGlyph (const juce_wchar character, + const Path& path, + const float horizontalSpacing) throw(); + + /** Adds a kerning distance to the typeface. + + The extra amount passed in is expressed as a proportion of the font's + height, normalised to 1.0. + + This is typically only called by the platform-specific code that generates + the typeface from a system font. + */ + void addKerningPair (const juce_wchar firstChar, + const juce_wchar secondChar, + const float extraAmount) throw(); + + /** Sets the typeface's name. + + This is typically only called by the platform-specific code that generates + the typeface from a system font. Calling this method won't actually affect + the underlying font being used. + */ + void setName (const String& name) throw(); + + /** Sets the font's ascent value, as a proportion of the font height. + + This is typically only called by the platform-specific code that generates + the typeface from a system font. + */ + void setAscent (const float newAscent) throw(); + + /** Sets the typeface's 'bold' flag. + + This is typically only called by the platform-specific code that generates + the typeface from a system font. + */ + void setBold (const bool shouldBeBold) throw(); + + /** Sets the typeface's 'italic' flag. + + This is typically only called by the platform-specific code that generates + the typeface from a system font. + */ + void setItalic (const bool shouldBeItalic) throw(); + + /** Changes the character index to use as the default character. + + This is the character that gets returned for characters which don't have a + glyph set for them. + */ + void setDefaultCharacter (const juce_wchar newDefaultCharacter) throw(); + + /** Creates a typeface from data created using Typeface::serialise(). + + This will attempt to load a compressed typeface that was created using + the Typeface::serialise() method. This is handy if you want to store + a typeface in your application as a binary blob, and use it without + having to actually install it on the computer. + + @see Typeface::serialise() + */ + Typeface (InputStream& serialisedTypefaceStream); + + /** Writes the typeface to a stream (using a proprietary format). + + This lets you save a typeface and reload it using the + Typeface::Typeface (InputStream&) constructor. The data's saved in + a compressed format. + + @see Typeface::Typeface (InputStream&) + */ + void serialise (OutputStream& outputStream); + + /** A handy typedef to make it easy to use ref counted pointers to this class. */ + typedef ReferenceCountedObjectPtr Ptr; + + juce_UseDebuggingNewOperator + +private: + VoidArray glyphs; + short lookupTable [128]; + + String typefaceName; + int hash; + float ascent; // as a proportion of the height + bool bold, italic, isFullyPopulated; + juce_wchar defaultCharacter; // the char to use if a matching glyph can't be found. + + Typeface() throw(); + void addGlyphCopy (const TypefaceGlyphInfo* const glyphInfoToCopy) throw(); + + friend class Font; + friend class TypefaceCache; + friend class FontGlyphAlphaMap; + + static const Ptr getTypefaceFor (const Font& font) throw(); + + // this is a platform-dependent method that will look for the given typeface + // and set up its kerning tables, etc. accordingly. + // If addAllGlyphsToFont is true, it should also add all the glyphs in the font + // to the typeface immediately, rather than having to add them later on-demand. + void initialiseTypefaceCharacteristics (const String& fontName, + bool bold, bool italic, + bool addAllGlyphsToFont) throw(); + + // platform-specific routine to look up and add a glyph to this typeface + bool findAndAddSystemGlyph (juce_wchar character) throw(); + + void updateHashCode() throw(); +}; + +#endif // __JUCE_TYPEFACE_JUCEHEADER__ +/********* End of inlined file: juce_Typeface.h *********/ + +/** + Represents a particular font, including its size, style, etc. + + Apart from the typeface to be used, a Font object also dictates whether + the font is bold, italic, underlined, how big it is, and its kerning and + horizontal scale factor. + + @see Typeface +*/ +class JUCE_API Font +{ +public: + + /** A combination of these values is used by the constructor to specify the + style of font to use. + */ + enum FontStyleFlags + { + plain = 0, /**< indicates a plain, non-bold, non-italic version of the font. @see setStyleFlags */ + bold = 1, /**< boldens the font. @see setStyleFlags */ + italic = 2, /**< finds an italic version of the font. @see setStyleFlags */ + underlined = 4 /**< underlines the font. @see setStyleFlags */ + }; + + /** Creates a sans-serif font in a given size. + + @param fontHeight the height in pixels (can be fractional) + @param styleFlags the style to use - this can be a combination of the + Font::bold, Font::italic and Font::underlined, or + just Font::plain for the normal style. + @see FontStyleFlags, getDefaultSansSerifFontName, setDefaultSansSerifFontName + */ + Font (const float fontHeight, + const int styleFlags = plain) throw(); + + /** Creates a font with a given typeface and parameters. + + @param typefaceName the name of the typeface to use + @param fontHeight the height in pixels (can be fractional) + @param styleFlags the style to use - this can be a combination of the + Font::bold, Font::italic and Font::underlined, or + just Font::plain for the normal style. + @see FontStyleFlags, getDefaultSansSerifFontName, setDefaultSansSerifFontName + */ + Font (const String& typefaceName, + const float fontHeight, + const int styleFlags) throw(); + + /** Creates a copy of another Font object. */ + Font (const Font& other) throw(); + + /** Creates a font based on a typeface. + + The font object stores its own internal copy of the typeface, so you can safely + delete the one passed in after calling this. + */ + Font (const Typeface& typeface) throw(); + + /** Creates a basic sans-serif font at a default height. + + You should use one of the other constructors for creating a font that you're planning + on drawing with - this constructor is here to help initialise objects before changing + the font's settings later. + */ + Font() throw(); + + /** Copies this font from another one. */ + const Font& operator= (const Font& other) throw(); + + bool operator== (const Font& other) const throw(); + bool operator!= (const Font& other) const throw(); + + /** Destructor. */ + ~Font() throw(); + + /** Changes the name of the typeface family. + + e.g. "Arial", "Courier", etc. + + If a suitable font isn't found on the machine, it'll just use a default instead. + */ + void setTypefaceName (const String& faceName) throw(); + + /** Returns the name of the typeface family that this font uses. + + e.g. "Arial", "Courier", etc. + */ + const String& getTypefaceName() const throw() { return typefaceName; } + + /** Returns a platform-specific font family that is recommended for sans-serif fonts. + + This is the typeface that will be used when a font is created without + specifying another name. + + @see setTypefaceName, getDefaultSerifFontName, getDefaultMonospacedFontName, + setDefaultSansSerifFontName + */ + static const String getDefaultSansSerifFontName() throw(); + + /** Returns a platform-specific font family that is recommended for serif fonts. + + @see setTypefaceName, getDefaultSansSerifFontName, getDefaultMonospacedFontName + */ + static const String getDefaultSerifFontName() throw(); + + /** Returns a platform-specific font family that is recommended for monospaced fonts. + + @see setTypefaceName, getDefaultSansSerifFontName, getDefaultSerifFontName + */ + static const String getDefaultMonospacedFontName() throw(); + + /** Changes the default sans-serif typeface family name. + + This changes the value that is returned by getDefaultSansSerifFontName(), so + changing this will change the default system font used. + + @see getDefaultSansSerifFontName + */ + static void setDefaultSansSerifFontName (const String& name) throw(); + + /** Returns the total height of this font. + + This is the maximum height, from the top of the ascent to the bottom of the + descenders. + + @see setHeight, setHeightWithoutChangingWidth, getAscent + */ + float getHeight() const throw() { return height; } + + /** Changes the font's height. + + @see getHeight, setHeightWithoutChangingWidth + */ + void setHeight (float newHeight) throw(); + + /** Changes the font's height without changing its width. + + This alters the horizontal scale to compensate for the change in height. + */ + void setHeightWithoutChangingWidth (float newHeight) throw(); + + /** Returns the height of the font above its baseline. + + This is the maximum height from the baseline to the top. + + @see getHeight, getDescent + */ + float getAscent() const throw(); + + /** Returns the amount that the font descends below its baseline. + + This is calculated as (getHeight() - getAscent()). + + @see getAscent, getHeight + */ + float getDescent() const throw(); + + /** Returns the font's style flags. + + This will return a bitwise-or'ed combination of values from the FontStyleFlags + enum, to describe whether the font is bold, italic, etc. + + @see FontStyleFlags + */ + int getStyleFlags() const throw() { return styleFlags; } + + /** Changes the font's style. + + @param newFlags a bitwise-or'ed combination of values from the FontStyleFlags + enum, to set the font's properties + @see FontStyleFlags + */ + void setStyleFlags (const int newFlags) throw(); + + /** Makes the font bold or non-bold. */ + void setBold (const bool shouldBeBold) throw(); + /** Returns true if the font is bold. */ + bool isBold() const throw(); + + /** Makes the font italic or non-italic. */ + void setItalic (const bool shouldBeItalic) throw(); + /** Returns true if the font is italic. */ + bool isItalic() const throw(); + + /** Makes the font underlined or non-underlined. */ + void setUnderline (const bool shouldBeUnderlined) throw(); + /** Returns true if the font is underlined. */ + bool isUnderlined() const throw(); + + /** Changes the font's horizontal scale factor. + + @param scaleFactor a value of 1.0 is the normal scale, less than this will be + narrower, greater than 1.0 will be stretched out. + */ + void setHorizontalScale (const float scaleFactor) throw(); + + /** Returns the font's horizontal scale. + + A value of 1.0 is the normal scale, less than this will be narrower, greater + than 1.0 will be stretched out. + + @see setHorizontalScale + */ + float getHorizontalScale() const throw() { return horizontalScale; } + + /** Changes the font's kerning. + + @param extraKerning a multiple of the font's height that will be added + to space between the characters. So a value of zero is + normal spacing, positive values spread the letters out, + negative values make them closer together. + */ + void setExtraKerningFactor (const float extraKerning) throw(); + + /** Returns the font's kerning. + + This is the extra space added between adjacent characters, as a proportion + of the font's height. + + A value of zero is normal spacing, positive values will spread the letters + out more, and negative values make them closer together. + */ + float getExtraKerningFactor() const throw() { return kerning; } + + /** Changes all the font's characteristics with one call. */ + void setSizeAndStyle (const float newHeight, + const int newStyleFlags, + const float newHorizontalScale, + const float newKerningAmount) throw(); + + /** Resets this font's characteristics. + + This is basically like saying "myFont = Font();", because it resets the + typeface, size, style, etc to a default state. Not very useful to most + people, its raison d'etre is to help the Graphics class be more efficient. + */ + void resetToDefaultState() throw(); + + /** Returns the total width of a string as it would be drawn using this font. + + For a more accurate floating-point result, use getStringWidthFloat(). + */ + int getStringWidth (const String& text) const throw(); + + /** Returns the total width of a string as it would be drawn using this font. + + @see getStringWidth + */ + float getStringWidthFloat (const String& text) const throw(); + + /** Returns the typeface used by this font. + + Note that the object returned may go out of scope if this font is deleted + or has its style changed. + */ + Typeface* getTypeface() const throw(); + + /** Creates an array of Font objects to represent all the fonts on the system. + + If you just need the names of the typefaces, you can also use + findAllTypefaceNames() instead. + + @param results the array to which new Font objects will be added. + */ + static void findFonts (OwnedArray& results) throw(); + + /** Returns a list of all the available typeface names. + + The names returned can be passed into setTypefaceName(). + + You can use this instead of findFonts() if you only need their names, and not + font objects. + */ + static const StringArray findAllTypefaceNames() throw(); + + /** Returns the name of the typeface to be used for rendering glyphs that aren't found + in the requested typeface. + */ + static const String getFallbackFontName() throw(); + + /** Sets the (platform-specific) name of the typeface to use to find glyphs that aren't + available in whatever font you're trying to use. + */ + static void setFallbackFontName (const String& name) throw(); + + juce_UseDebuggingNewOperator + +private: + + friend class FontGlyphAlphaMap; + friend class TypefaceCache; + + String typefaceName; + float height, horizontalScale, kerning; + mutable float ascent; + int styleFlags; + mutable Typeface::Ptr typeface; + + // platform-specific calls + static void getDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed) throw(); + + friend void JUCE_PUBLIC_FUNCTION initialiseJuce_GUI(); + static void initialiseDefaultFontNames() throw(); +}; + +#endif // __JUCE_FONT_JUCEHEADER__ +/********* End of inlined file: juce_Font.h *********/ + +/********* Start of inlined file: juce_Rectangle.h *********/ +#ifndef __JUCE_RECTANGLE_JUCEHEADER__ +#define __JUCE_RECTANGLE_JUCEHEADER__ + +/** + A rectangle, specified using integer co-ordinates. + + @see RectangleList, Path, Line, Point +*/ +class JUCE_API Rectangle +{ +public: + + /** Creates a rectangle of zero size. + + The default co-ordinates will be (0, 0, 0, 0). + */ + Rectangle() throw(); + + /** Creates a copy of another rectangle. */ + Rectangle (const Rectangle& other) throw(); + + /** Creates a rectangle with a given position and size. */ + Rectangle (const int x, const int y, + const int width, const int height) throw(); + + /** Creates a rectangle with a given size, and a position of (0, 0). */ + Rectangle (const int width, const int height) throw(); + + /** Destructor. */ + ~Rectangle() throw(); + + /** Returns the x co-ordinate of the rectangle's left-hand-side. */ + inline int getX() const throw() { return x; } + + /** Returns the y co-ordinate of the rectangle's top edge. */ + inline int getY() const throw() { return y; } + + /** Returns the width of the rectangle. */ + inline int getWidth() const throw() { return w; } + + /** Returns the height of the rectangle. */ + inline int getHeight() const throw() { return h; } + + /** Returns the x co-ordinate of the rectangle's right-hand-side. */ + inline int getRight() const throw() { return x + w; } + + /** Returns the y co-ordinate of the rectangle's bottom edge. */ + inline int getBottom() const throw() { return y + h; } + + /** Returns the x co-ordinate of the rectangle's centre. */ + inline int getCentreX() const throw() { return x + (w >> 1); } + + /** Returns the y co-ordinate of the rectangle's centre. */ + inline int getCentreY() const throw() { return y + (h >> 1); } + + /** Returns true if the rectangle's width and height are both zero or less */ + bool isEmpty() const throw(); + + /** Changes the position of the rectangle's top-left corner (leaving its size unchanged). */ + void setPosition (const int x, const int y) throw(); + + /** Changes the rectangle's size, leaving the position of its top-left corner unchanged. */ + void setSize (const int w, const int h) throw(); + + /** Changes all the rectangle's co-ordinates. */ + void setBounds (const int newX, const int newY, + const int newWidth, const int newHeight) throw(); + + /** Moves the rectangle's position by adding amount to its x and y co-ordinates. */ + void translate (const int deltaX, + const int deltaY) throw(); + + /** Returns a rectangle which is the same as this one moved by a given amount. */ + const Rectangle translated (const int deltaX, + const int deltaY) const throw(); + + /** Expands the rectangle by a given amount. + + Effectively, its new size is (x - deltaX, y - deltaY, w + deltaX * 2, h + deltaY * 2). + @see expanded, reduce, reduced + */ + void expand (const int deltaX, + const int deltaY) throw(); + + /** Returns a rectangle that is larger than this one by a given amount. + + Effectively, the rectangle returned is (x - deltaX, y - deltaY, w + deltaX * 2, h + deltaY * 2). + @see expand, reduce, reduced + */ + const Rectangle expanded (const int deltaX, + const int deltaY) const throw(); + + /** Shrinks the rectangle by a given amount. + + Effectively, its new size is (x + deltaX, y + deltaY, w - deltaX * 2, h - deltaY * 2). + @see reduced, expand, expanded + */ + void reduce (const int deltaX, + const int deltaY) throw(); + + /** Returns a rectangle that is smaller than this one by a given amount. + + Effectively, the rectangle returned is (x + deltaX, y + deltaY, w - deltaX * 2, h - deltaY * 2). + @see reduce, expand, expanded + */ + const Rectangle reduced (const int deltaX, + const int deltaY) const throw(); + + /** Returns true if the two rectangles are identical. */ + bool operator== (const Rectangle& other) const throw(); + + /** Returns true if the two rectangles are not identical. */ + bool operator!= (const Rectangle& other) const throw(); + + /** Returns true if this co-ordinate is inside the rectangle. */ + bool contains (const int x, const int y) const throw(); + + /** Returns true if this other rectangle is completely inside this one. */ + bool contains (const Rectangle& other) const throw(); + + /** Returns true if any part of another rectangle overlaps this one. */ + bool intersects (const Rectangle& other) const throw(); + + /** Returns the region that is the overlap between this and another rectangle. + + If the two rectangles don't overlap, the rectangle returned will be empty. + */ + const Rectangle getIntersection (const Rectangle& other) const throw(); + + /** Clips a rectangle so that it lies only within this one. + + This is a non-static version of intersectRectangles(). + + Returns false if the two regions didn't overlap. + */ + bool intersectRectangle (int& x, int& y, int& w, int& h) const throw(); + + /** Returns the smallest rectangle that contains both this one and the one + passed-in. + */ + const Rectangle getUnion (const Rectangle& other) const throw(); + + /** If this rectangle merged with another one results in a simple rectangle, this + will set this rectangle to the result, and return true. + + Returns false and does nothing to this rectangle if the two rectangles don't overlap, + or if they form a complex region. + */ + bool enlargeIfAdjacent (const Rectangle& other) throw(); + + /** If after removing another rectangle from this one the result is a simple rectangle, + this will set this object's bounds to be the result, and return true. + + Returns false and does nothing to this rectangle if the two rectangles don't overlap, + or if removing the other one would form a complex region. + */ + bool reduceIfPartlyContainedIn (const Rectangle& other) throw(); + + /** Static utility to intersect two sets of rectangular co-ordinates. + + Returns false if the two regions didn't overlap. + + @see intersectRectangle + */ + static bool intersectRectangles (int& x1, int& y1, int& w1, int& h1, + int x2, int y2, int w2, int h2) throw(); + + /** Creates a string describing this rectangle. + + The string will be of the form "x y width height", e.g. "100 100 400 200". + + Coupled with the fromString() method, this is very handy for things like + storing rectangles (particularly component positions) in XML attributes. + + @see fromString + */ + const String toString() const throw(); + + /** Parses a string containing a rectangle's details. + + The string should contain 4 integer tokens, in the form "x y width height". They + can be comma or whitespace separated. + + This method is intended to go with the toString() method, to form an easy way + of saving/loading rectangles as strings. + + @see toString + */ + static const Rectangle fromString (const String& stringVersion); + + juce_UseDebuggingNewOperator + +private: + friend class RectangleList; + int x, y, w, h; +}; + +#endif // __JUCE_RECTANGLE_JUCEHEADER__ +/********* End of inlined file: juce_Rectangle.h *********/ + +/********* Start of inlined file: juce_PathStrokeType.h *********/ +#ifndef __JUCE_PATHSTROKETYPE_JUCEHEADER__ +#define __JUCE_PATHSTROKETYPE_JUCEHEADER__ + +/** + Describes a type of stroke used to render a solid outline along a path. + + A PathStrokeType object can be used directly to create the shape of an outline + around a path, and is used by Graphics::strokePath to specify the type of + stroke to draw. + + @see Path, Graphics::strokePath +*/ +class JUCE_API PathStrokeType +{ +public: + + /** The type of shape to use for the corners between two adjacent line segments. */ + enum JointStyle + { + mitered, /**< Indicates that corners should be drawn with sharp joints. + Note that for angles that curve back on themselves, drawing a + mitre could require extending the point too far away from the + path, so a mitre limit is imposed and any corners that exceed it + are drawn as bevelled instead. */ + curved, /**< Indicates that corners should be drawn as rounded-off. */ + beveled /**< Indicates that corners should be drawn with a line flattening their + outside edge. */ + }; + + /** The type shape to use for the ends of lines. */ + enum EndCapStyle + { + butt, /**< Ends of lines are flat and don't extend beyond the end point. */ + square, /**< Ends of lines are flat, but stick out beyond the end point for half + the thickness of the stroke. */ + rounded /**< Ends of lines are rounded-off with a circular shape. */ + }; + + /** Creates a stroke type. + + @param strokeThickness the width of the line to use + @param jointStyle the type of joints to use for corners + @param endStyle the type of end-caps to use for the ends of open paths. + */ + PathStrokeType (const float strokeThickness, + const JointStyle jointStyle = mitered, + const EndCapStyle endStyle = butt) throw(); + + /** Createes a copy of another stroke type. */ + PathStrokeType (const PathStrokeType& other) throw(); + + /** Copies another stroke onto this one. */ + const PathStrokeType& operator= (const PathStrokeType& other) throw(); + + /** Destructor. */ + ~PathStrokeType() throw(); + + /** Applies this stroke type to a path and returns the resultant stroke as another Path. + + @param destPath the resultant stroked outline shape will be copied into this path. + Note that it's ok for the source and destination Paths to be + the same object, so you can easily turn a path into a stroked version + of itself. + @param sourcePath the path to use as the source + @param transform an optional transform to apply to the points from the source path + as they are being used + @param extraAccuracy if this is greater than 1.0, it will subdivide the path to + a higher resolution, which improved the quality if you'll later want + to enlarge the stroked path + + @see createDashedStroke + */ + void createStrokedPath (Path& destPath, + const Path& sourcePath, + const AffineTransform& transform = AffineTransform::identity, + const float extraAccuracy = 1.0f) const throw(); + + /** Applies this stroke type to a path, creating a dashed line. + + This is similar to createStrokedPath, but uses the array passed in to + break the stroke up into a series of dashes. + + @param destPath the resultant stroked outline shape will be copied into this path. + Note that it's ok for the source and destination Paths to be + the same object, so you can easily turn a path into a stroked version + of itself. + @param sourcePath the path to use as the source + @param dashLengths An array of alternating on/off lengths. E.g. { 2, 3, 4, 5 } will create + a line of length 2, then skip a length of 3, then add a line of length 4, + skip 5, and keep repeating this pattern. + @param numDashLengths The number of lengths in the dashLengths array. This should really be + an even number, otherwise the pattern will get out of step as it + repeats. + @param transform an optional transform to apply to the points from the source path + as they are being used + @param extraAccuracy if this is greater than 1.0, it will subdivide the path to + a higher resolution, which improved the quality if you'll later want + to enlarge the stroked path + */ + void createDashedStroke (Path& destPath, + const Path& sourcePath, + const float* dashLengths, + int numDashLengths, + const AffineTransform& transform = AffineTransform::identity, + const float extraAccuracy = 1.0f) const throw(); + + /** Returns the stroke thickness. */ + float getStrokeThickness() const throw() { return thickness; } + + /** Returns the joint style. */ + JointStyle getJointStyle() const throw() { return jointStyle; } + + /** Returns the end-cap style. */ + EndCapStyle getEndStyle() const throw() { return endStyle; } + + juce_UseDebuggingNewOperator + + /** Compares the stroke thickness, joint and end styles of two stroke types. */ + bool operator== (const PathStrokeType& other) const throw(); + + /** Compares the stroke thickness, joint and end styles of two stroke types. */ + bool operator!= (const PathStrokeType& other) const throw(); + +private: + + float thickness; + JointStyle jointStyle; + EndCapStyle endStyle; +}; + +#endif // __JUCE_PATHSTROKETYPE_JUCEHEADER__ +/********* End of inlined file: juce_PathStrokeType.h *********/ + +/********* Start of inlined file: juce_Line.h *********/ +#ifndef __JUCE_LINE_JUCEHEADER__ +#define __JUCE_LINE_JUCEHEADER__ + +/** + Represents a line, using 32-bit float co-ordinates. + + This class contains a bunch of useful methods for various geometric + tasks. + + @see Point, Rectangle, Path, Graphics::drawLine +*/ +class JUCE_API Line +{ +public: + + /** Creates a line, using (0, 0) as its start and end points. */ + Line() throw(); + + /** Creates a copy of another line. */ + Line (const Line& other) throw(); + + /** Creates a line based on the co-ordinates of its start and end points. */ + Line (const float startX, + const float startY, + const float endX, + const float endY) throw(); + + /** Creates a line from its start and end points. */ + Line (const Point& start, + const Point& end) throw(); + + /** Copies a line from another one. */ + const Line& operator= (const Line& other) throw(); + + /** Destructor. */ + ~Line() throw(); + + /** Returns the x co-ordinate of the line's start point. */ + inline float getStartX() const throw() { return startX; } + + /** Returns the y co-ordinate of the line's start point. */ + inline float getStartY() const throw() { return startY; } + + /** Returns the x co-ordinate of the line's end point. */ + inline float getEndX() const throw() { return endX; } + + /** Returns the y co-ordinate of the line's end point. */ + inline float getEndY() const throw() { return endY; } + + /** Returns the line's start point. */ + const Point getStart() const throw(); + + /** Returns the line's end point. */ + const Point getEnd() const throw(); + + /** Changes this line's start point */ + void setStart (const float newStartX, + const float newStartY) throw(); + + /** Changes this line's end point */ + void setEnd (const float newEndX, + const float newEndY) throw(); + + /** Changes this line's start point */ + void setStart (const Point& newStart) throw(); + + /** Changes this line's end point */ + void setEnd (const Point& newEnd) throw(); + + /** Applies an affine transform to the line's start and end points. */ + void applyTransform (const AffineTransform& transform) throw(); + + /** Returns the length of the line. */ + float getLength() const throw(); + + /** Returns true if the line's start and end x co-ordinates are the same. */ + bool isVertical() const throw(); + + /** Returns true if the line's start and end y co-ordinates are the same. */ + bool isHorizontal() const throw(); + + /** Returns the line's angle. + + This value is the number of radians clockwise from the 3 o'clock direction, + where the line's start point is considered to be at the centre. + */ + float getAngle() const throw(); + + /** Compares two lines. */ + bool operator== (const Line& other) const throw(); + + /** Compares two lines. */ + bool operator!= (const Line& other) const throw(); + + /** Finds the intersection between two lines. + + @param line the other line + @param intersectionX the x co-ordinate of the point where the lines meet (or + where they would meet if they were infinitely long) + the intersection (if the lines intersect). If the lines + are parallel, this will just be set to the position + of one of the line's endpoints. + @param intersectionY the y co-ordinate of the point where the lines meet + @returns true if the line segments intersect; false if they dont. Even if they + don't intersect, the intersection co-ordinates returned will still + be valid + */ + bool intersects (const Line& line, + float& intersectionX, + float& intersectionY) const throw(); + + /** Returns the location of the point which is a given distance along this line. + + @param distanceFromStart the distance to move along the line from its + start point. This value can be negative or longer + than the line itself + @see getPointAlongLineProportionally + */ + const Point getPointAlongLine (const float distanceFromStart) const throw(); + + /** Returns a point which is a certain distance along and to the side of this line. + + This effectively moves a given distance along the line, then another distance + perpendicularly to this, and returns the resulting position. + + @param distanceFromStart the distance to move along the line from its + start point. This value can be negative or longer + than the line itself + @param perpendicularDistance how far to move sideways from the line. If you're + looking along the line from its start towards its + end, then a positive value here will move to the + right, negative value move to the left. + */ + const Point getPointAlongLine (const float distanceFromStart, + const float perpendicularDistance) const throw(); + + /** Returns the location of the point which is a given distance along this line + proportional to the line's length. + + @param proportionOfLength the distance to move along the line from its + start point, in multiples of the line's length. + So a value of 0.0 will return the line's start point + and a value of 1.0 will return its end point. (This value + can be negative or greater than 1.0). + @see getPointAlongLine + */ + const Point getPointAlongLineProportionally (const float proportionOfLength) const throw(); + + /** Returns the smallest distance between this line segment and a given point. + + So if the point is close to the line, this will return the perpendicular + distance from the line; if the point is a long way beyond one of the line's + end-point's, it'll return the straight-line distance to the nearest end-point. + + @param x x position of the point to test + @param y y position of the point to test + @returns the point's distance from the line + @see getPositionAlongLineOfNearestPoint + */ + float getDistanceFromLine (const float x, + const float y) const throw(); + + /** Finds the point on this line which is nearest to a given point, and + returns its position as a proportional position along the line. + + @param x x position of the point to test + @param y y position of the point to test + @returns a value 0 to 1.0 which is the distance along this line from the + line's start to the point which is nearest to the point passed-in. To + turn this number into a position, use getPointAlongLineProportionally(). + @see getDistanceFromLine, getPointAlongLineProportionally + */ + float findNearestPointTo (const float x, + const float y) const throw(); + + /** Returns true if the given point lies above this line. + + The return value is true if the point's y coordinate is less than the y + coordinate of this line at the given x (assuming the line extends infinitely + in both directions). + */ + bool isPointAbove (const float x, const float y) const throw(); + + /** Returns a shortened copy of this line. + + This will chop off part of the start of this line by a certain amount, (leaving the + end-point the same), and return the new line. + */ + const Line withShortenedStart (const float distanceToShortenBy) const throw(); + + /** Returns a shortened copy of this line. + + This will chop off part of the end of this line by a certain amount, (leaving the + start-point the same), and return the new line. + */ + const Line withShortenedEnd (const float distanceToShortenBy) const throw(); + + /** Cuts off parts of this line to keep the parts that are either inside or + outside a path. + + Note that this isn't smart enough to cope with situations where the + line would need to be cut into multiple pieces to correctly clip against + a re-entrant shape. + + @param path the path to clip against + @param keepSectionOutsidePath if true, it's the section outside the path + that will be kept; if false its the section inside + the path + @returns true if the line was changed. + */ + bool clipToPath (const Path& path, + const bool keepSectionOutsidePath) throw(); + + juce_UseDebuggingNewOperator + +private: + float startX, startY, endX, endY; +}; + +#endif // __JUCE_LINE_JUCEHEADER__ +/********* End of inlined file: juce_Line.h *********/ + +/********* Start of inlined file: juce_Colours.h *********/ +#ifndef __JUCE_COLOURS_JUCEHEADER__ +#define __JUCE_COLOURS_JUCEHEADER__ + +/********* Start of inlined file: juce_Colour.h *********/ +#ifndef __JUCE_COLOUR_JUCEHEADER__ +#define __JUCE_COLOUR_JUCEHEADER__ + +/********* Start of inlined file: juce_PixelFormats.h *********/ +#ifndef __JUCE_PIXELFORMATS_JUCEHEADER__ +#define __JUCE_PIXELFORMATS_JUCEHEADER__ + +#if JUCE_MSVC + #pragma pack (push, 1) + #define PACKED +#elif JUCE_GCC + #define PACKED __attribute__((packed)) +#else + #define PACKED +#endif + +/** + Represents a 32-bit ARGB pixel with premultiplied alpha, and can perform compositing + operations with it. + + This is used internally by the imaging classes. + + @see PixelRGB +*/ +class JUCE_API PixelARGB +{ +public: + /** Creates a pixel without defining its colour. */ + PixelARGB() throw() {} + ~PixelARGB() throw() {} + + /** Creates a pixel from a 32-bit argb value. + */ + PixelARGB (const uint32 argb_) throw() + : argb (argb_) + { + } + + forcedinline uint32 getARGB() const throw() { return argb; } + forcedinline uint32 getRB() const throw() { return 0x00ff00ff & argb; } + forcedinline uint32 getAG() const throw() { return 0x00ff00ff & (argb >> 8); } + + forcedinline uint8 getAlpha() const throw() { return components.a; } + forcedinline uint8 getRed() const throw() { return components.r; } + forcedinline uint8 getGreen() const throw() { return components.g; } + forcedinline uint8 getBlue() const throw() { return components.b; } + + /** Blends another pixel onto this one. + + This takes into account the opacity of the pixel being overlaid, and blends + it accordingly. + */ + template + forcedinline void blend (const Pixel& src) throw() + { + uint32 sargb = src.getARGB(); + const uint32 alpha = 0x100 - (sargb >> 24); + + sargb += 0x00ff00ff & ((getRB() * alpha) >> 8); + sargb += 0xff00ff00 & (getAG() * alpha); + + argb = sargb; + } + + /** Blends another pixel onto this one, applying an extra multiplier to its opacity. + + The opacity of the pixel being overlaid is scaled by the extraAlpha factor before + being used, so this can blend semi-transparently from a PixelRGB argument. + */ + template + forcedinline void blend (const Pixel& src, uint32 extraAlpha) throw() + { + ++extraAlpha; + + uint32 sargb = ((extraAlpha * src.getAG()) & 0xff00ff00) + | (((extraAlpha * src.getRB()) >> 8) & 0x00ff00ff); + + const uint32 alpha = 0x100 - (sargb >> 24); + + sargb += 0x00ff00ff & ((getRB() * alpha) >> 8); + sargb += 0xff00ff00 & (getAG() * alpha); + + argb = sargb; + } + + /** Blends another pixel with this one, creating a colour that is somewhere + between the two, as specified by the amount. + */ + template + forcedinline void tween (const Pixel& src, const uint32 amount) throw() + { + uint32 drb = getRB(); + drb += (((src.getRB() - drb) * amount) >> 8); + drb &= 0x00ff00ff; + + uint32 dag = getAG(); + dag += (((src.getAG() - dag) * amount) >> 8); + dag &= 0x00ff00ff; + dag <<= 8; + + dag |= drb; + argb = dag; + } + + /** Copies another pixel colour over this one. + + This doesn't blend it - this colour is simply replaced by the other one. + */ + template + forcedinline void set (const Pixel& src) throw() + { + argb = src.getARGB(); + } + + /** Replaces the colour's alpha value with another one. */ + forcedinline void setAlpha (const uint8 newAlpha) throw() + { + components.a = newAlpha; + } + + /** Multiplies the colour's alpha value with another one. */ + forcedinline void multiplyAlpha (int multiplier) throw() + { + ++multiplier; + + argb = ((multiplier * getAG()) & 0xff00ff00) + | (((multiplier * getRB()) >> 8) & 0x00ff00ff); + } + + forcedinline void multiplyAlpha (const float multiplier) throw() + { + multiplyAlpha ((int) (multiplier * 256.0f)); + } + + /** Sets the pixel's colour from individual components. */ + void setARGB (const uint8 a, const uint8 r, const uint8 g, const uint8 b) throw() + { + components.b = b; + components.g = g; + components.r = r; + components.a = a; + } + + /** Premultiplies the pixel's RGB values by its alpha. */ + forcedinline void premultiply() throw() + { + const uint32 alpha = components.a; + + if (alpha < 0xff) + { + if (alpha == 0) + { + components.b = 0; + components.g = 0; + components.r = 0; + } + else + { + components.b = (uint8) ((components.b * alpha + 0x7f) >> 8); + components.g = (uint8) ((components.g * alpha + 0x7f) >> 8); + components.r = (uint8) ((components.r * alpha + 0x7f) >> 8); + } + } + } + + /** Unpremultiplies the pixel's RGB values. */ + forcedinline void unpremultiply() throw() + { + const uint32 alpha = components.a; + + if (alpha < 0xff) + { + if (alpha == 0) + { + components.b = 0; + components.g = 0; + components.r = 0; + } + else + { + components.b = (uint8) jmin (0xff, (components.b * 0xff) / alpha); + components.g = (uint8) jmin (0xff, (components.g * 0xff) / alpha); + components.r = (uint8) jmin (0xff, (components.r * 0xff) / alpha); + } + } + } + + forcedinline void desaturate() throw() + { + if (components.a < 0xff && components.a > 0) + { + const int newUnpremultipliedLevel = (0xff * ((int) components.r + (int) components.g + (int) components.b) / (3 * components.a)); + + components.r = components.g = components.b + = (uint8) ((newUnpremultipliedLevel * components.a + 0x7f) >> 8); + } + else + { + components.r = components.g = components.b + = (uint8) (((int) components.r + (int) components.g + (int) components.b) / 3); + } + } + +private: + + union + { + uint32 argb; + + struct + { +#if JUCE_BIG_ENDIAN + uint8 a : 8, r : 8, g : 8, b : 8; +#else + uint8 b, g, r, a; +#endif + } PACKED components; + }; + +} PACKED; + +/** + Represents a 24-bit RGB pixel, and can perform compositing operations on it. + + This is used internally by the imaging classes. + + @see PixelARGB +*/ +class JUCE_API PixelRGB +{ +public: + /** Creates a pixel without defining its colour. */ + PixelRGB() throw() {} + ~PixelRGB() throw() {} + + /** Creates a pixel from a 32-bit argb value. + + (The argb format is that used by PixelARGB) + */ + PixelRGB (const uint32 argb) throw() + { + r = (uint8) (argb >> 16); + g = (uint8) (argb >> 8); + b = (uint8) (argb); + } + + forcedinline uint32 getARGB() const throw() { return 0xff000000 | b | (g << 8) | (r << 16); } + forcedinline uint32 getRB() const throw() { return b | (uint32) (r << 16); } + forcedinline uint32 getAG() const throw() { return 0xff0000 | g; } + + forcedinline uint8 getAlpha() const throw() { return 0xff; } + forcedinline uint8 getRed() const throw() { return r; } + forcedinline uint8 getGreen() const throw() { return g; } + forcedinline uint8 getBlue() const throw() { return b; } + + /** Blends another pixel onto this one. + + This takes into account the opacity of the pixel being overlaid, and blends + it accordingly. + */ + forcedinline void blend (const PixelARGB& src) throw() + { + uint32 sargb = src.getARGB(); + const uint32 alpha = 0x100 - (sargb >> 24); + + sargb += 0x00ff00ff & ((getRB() * alpha) >> 8); + sargb += 0x0000ff00 & (g * alpha); + + r = (uint8) (sargb >> 16); + g = (uint8) (sargb >> 8); + b = (uint8) sargb; + } + + forcedinline void blend (const PixelRGB& src) throw() + { + set (src); + } + + /** Blends another pixel onto this one, applying an extra multiplier to its opacity. + + The opacity of the pixel being overlaid is scaled by the extraAlpha factor before + being used, so this can blend semi-transparently from a PixelRGB argument. + */ + template + forcedinline void blend (const Pixel& src, uint32 extraAlpha) throw() + { + ++extraAlpha; + const uint32 srb = (extraAlpha * src.getRB()) >> 8; + const uint32 sag = extraAlpha * src.getAG(); + uint32 sargb = (sag & 0xff00ff00) | (srb & 0x00ff00ff); + + const uint32 alpha = 0x100 - (sargb >> 24); + + sargb += 0x00ff00ff & ((getRB() * alpha) >> 8); + sargb += 0x0000ff00 & (g * alpha); + + b = (uint8) sargb; + g = (uint8) (sargb >> 8); + r = (uint8) (sargb >> 16); + } + + /** Blends another pixel with this one, creating a colour that is somewhere + between the two, as specified by the amount. + */ + template + forcedinline void tween (const Pixel& src, const uint32 amount) throw() + { + uint32 drb = getRB(); + drb += (((src.getRB() - drb) * amount) >> 8); + + uint32 dag = getAG(); + dag += (((src.getAG() - dag) * amount) >> 8); + + b = (uint8) drb; + g = (uint8) dag; + r = (uint8) (drb >> 16); + } + + /** Copies another pixel colour over this one. + + This doesn't blend it - this colour is simply replaced by the other one. + Because PixelRGB has no alpha channel, any alpha value in the source pixel + is thrown away. + */ + template + forcedinline void set (const Pixel& src) throw() + { + b = src.getBlue(); + g = src.getGreen(); + r = src.getRed(); + } + + /** This method is included for compatibility with the PixelARGB class. */ + forcedinline void setAlpha (const uint8) throw() {} + + /** Multiplies the colour's alpha value with another one. */ + forcedinline void multiplyAlpha (int) throw() {} + + /** Sets the pixel's colour from individual components. */ + void setARGB (const uint8, const uint8 r_, const uint8 g_, const uint8 b_) throw() + { + r = r_; + g = g_; + b = b_; + } + + /** Premultiplies the pixel's RGB values by its alpha. */ + forcedinline void premultiply() throw() {} + + /** Unpremultiplies the pixel's RGB values. */ + forcedinline void unpremultiply() throw() {} + + forcedinline void desaturate() throw() + { + r = g = b = (uint8) (((int) r + (int) g + (int) b) / 3); + } + +private: + +#if JUCE_MAC + uint8 r, g, b; +#else + uint8 b, g, r; +#endif + +} PACKED; + +#if JUCE_MSVC + #pragma pack (pop) +#endif + +#undef PACKED + +#endif // __JUCE_PIXELFORMATS_JUCEHEADER__ +/********* End of inlined file: juce_PixelFormats.h *********/ + +/** + Represents a colour, also including a transparency value. + + The colour is stored internally as unsigned 8-bit red, green, blue and alpha values. +*/ +class JUCE_API Colour +{ +public: + + /** Creates a transparent black colour. */ + Colour() throw(); + + /** Creates a copy of another Colour object. */ + Colour (const Colour& other) throw(); + + /** Creates a colour from a 32-bit ARGB value. + + The format of this number is: + ((alpha << 24) | (red << 16) | (green << 16) | blue). + + All components in the range 0x00 to 0xff. + An alpha of 0x00 is completely transparent, alpha of 0xff is opaque. + + @see getPixelARGB + */ + explicit Colour (const uint32 argb) throw(); + + /** Creates an opaque colour using 8-bit red, green and blue values */ + Colour (const uint8 red, + const uint8 green, + const uint8 blue) throw(); + + /** Creates a colour using 8-bit red, green, blue and alpha values. */ + Colour (const uint8 red, + const uint8 green, + const uint8 blue, + const uint8 alpha) throw(); + + /** Creates a colour from 8-bit red, green, and blue values, and a floating-point alpha. + + Alpha of 0.0 is transparent, alpha of 1.0f is opaque. + Values outside the valid range will be clipped. + */ + Colour (const uint8 red, + const uint8 green, + const uint8 blue, + const float alpha) throw(); + + /** Creates a colour using floating point hue, saturation and brightness values, and an 8-bit alpha. + + The floating point values must be between 0.0 and 1.0. + An alpha of 0x00 is completely transparent, alpha of 0xff is opaque. + Values outside the valid range will be clipped. + */ + Colour (const float hue, + const float saturation, + const float brightness, + const uint8 alpha) throw(); + + /** Creates a colour using floating point hue, saturation, brightness and alpha values. + + All values must be between 0.0 and 1.0. + Numbers outside the valid range will be clipped. + */ + Colour (const float hue, + const float saturation, + const float brightness, + const float alpha) throw(); + + /** Destructor. */ + ~Colour() throw(); + + /** Copies another Colour object. */ + const Colour& operator= (const Colour& other) throw(); + + /** Compares two colours. */ + bool operator== (const Colour& other) const throw(); + /** Compares two colours. */ + bool operator!= (const Colour& other) const throw(); + + /** Returns the red component of this colour. + + @returns a value between 0x00 and 0xff. + */ + uint8 getRed() const throw() { return argb.getRed(); } + + /** Returns the green component of this colour. + + @returns a value between 0x00 and 0xff. + */ + uint8 getGreen() const throw() { return argb.getGreen(); } + + /** Returns the blue component of this colour. + + @returns a value between 0x00 and 0xff. + */ + uint8 getBlue() const throw() { return argb.getBlue(); } + + /** Returns the red component of this colour as a floating point value. + + @returns a value between 0.0 and 1.0 + */ + float getFloatRed() const throw(); + + /** Returns the green component of this colour as a floating point value. + + @returns a value between 0.0 and 1.0 + */ + float getFloatGreen() const throw(); + + /** Returns the blue component of this colour as a floating point value. + + @returns a value between 0.0 and 1.0 + */ + float getFloatBlue() const throw(); + + /** Returns a premultiplied ARGB pixel object that represents this colour. + */ + const PixelARGB getPixelARGB() const throw(); + + /** Returns a 32-bit integer that represents this colour. + + The format of this number is: + ((alpha << 24) | (red << 16) | (green << 16) | blue). + */ + uint32 getARGB() const throw(); + + /** Returns the colour's alpha (opacity). + + Alpha of 0x00 is completely transparent, 0xff is completely opaque. + */ + uint8 getAlpha() const throw() { return argb.getAlpha(); } + + /** Returns the colour's alpha (opacity) as a floating point value. + + Alpha of 0.0 is completely transparent, 1.0 is completely opaque. + */ + float getFloatAlpha() const throw(); + + /** Returns true if this colour is completely opaque. + + Equivalent to (getAlpha() == 0xff). + */ + bool isOpaque() const throw(); + + /** Returns true if this colour is completely transparent. + + Equivalent to (getAlpha() == 0x00). + */ + bool isTransparent() const throw(); + + /** Returns a colour that's the same colour as this one, but with a new alpha value. */ + const Colour withAlpha (const uint8 newAlpha) const throw(); + + /** Returns a colour that's the same colour as this one, but with a new alpha value. */ + const Colour withAlpha (const float newAlpha) const throw(); + + /** Returns a colour that's the same colour as this one, but with a modified alpha value. + + The new colour's alpha will be this object's alpha multiplied by the value passed-in. + */ + const Colour withMultipliedAlpha (const float alphaMultiplier) const throw(); + + /** Returns a colour that is the result of alpha-compositing a new colour over this one. + + If the foreground colour is semi-transparent, it is blended onto this colour + accordingly. + */ + const Colour overlaidWith (const Colour& foregroundColour) const throw(); + + /** Returns the colour's hue component. + The value returned is in the range 0.0 to 1.0 + */ + float getHue() const throw(); + + /** Returns the colour's saturation component. + The value returned is in the range 0.0 to 1.0 + */ + float getSaturation() const throw(); + + /** Returns the colour's brightness component. + The value returned is in the range 0.0 to 1.0 + */ + float getBrightness() const throw(); + + /** Returns the colour's hue, saturation and brightness components all at once. + The values returned are in the range 0.0 to 1.0 + */ + void getHSB (float& hue, + float& saturation, + float& brightness) const throw(); + + /** Returns a copy of this colour with a different hue. */ + const Colour withHue (const float newHue) const throw(); + + /** Returns a copy of this colour with a different saturation. */ + const Colour withSaturation (const float newSaturation) const throw(); + + /** Returns a copy of this colour with a different brightness. + @see brighter, darker, withMultipliedBrightness + */ + const Colour withBrightness (const float newBrightness) const throw(); + + /** Returns a copy of this colour with it hue rotated. + + The new colour's hue is ((this->getHue() + amountToRotate) % 1.0) + + @see brighter, darker, withMultipliedBrightness + */ + const Colour withRotatedHue (const float amountToRotate) const throw(); + + /** Returns a copy of this colour with its saturation multiplied by the given value. + + The new colour's saturation is (this->getSaturation() * multiplier) + (the result is clipped to legal limits). + */ + const Colour withMultipliedSaturation (const float multiplier) const throw(); + + /** Returns a copy of this colour with its brightness multiplied by the given value. + + The new colour's saturation is (this->getBrightness() * multiplier) + (the result is clipped to legal limits). + */ + const Colour withMultipliedBrightness (const float amount) const throw(); + + /** Returns a brighter version of this colour. + + @param amountBrighter how much brighter to make it - a value from 0 to 1.0 where 0 is + unchanged, and higher values make it brighter + @see withMultipliedBrightness + */ + const Colour brighter (float amountBrighter = 0.4f) const throw(); + + /** Returns a darker version of this colour. + + @param amountDarker how much darker to make it - a value from 0 to 1.0 where 0 is + unchanged, and higher values make it darker + @see withMultipliedBrightness + */ + const Colour darker (float amountDarker = 0.4f) const throw(); + + /** Returns a colour that will be clearly visible against this colour. + + The amount parameter indicates how contrasting the new colour should + be, so e.g. Colours::black.contrasting (0.1f) will return a colour + that's just a little bit lighter; Colours::black.contrasting (1.0f) will + return white; Colours::white.contrasting (1.0f) will return black, etc. + */ + const Colour contrasting (const float amount = 1.0f) const throw(); + + /** Returns a colour that contrasts against two colours. + + Looks for a colour that contrasts with both of the colours passed-in. + + Handy for things like choosing a highlight colour in text editors, etc. + */ + static const Colour contrasting (const Colour& colour1, + const Colour& colour2) throw(); + + /** Returns an opaque shade of grey. + + @param brightness the level of grey to return - 0 is black, 1.0 is white + */ + static const Colour greyLevel (const float brightness) throw(); + + /** Returns a stringified version of this colour. + + The string can be turned back into a colour using the fromString() method. + */ + const String toString() const throw(); + + /** Reads the colour from a string that was created with toString(). + */ + static const Colour fromString (const String& encodedColourString); + + juce_UseDebuggingNewOperator + +private: + PixelARGB argb; +}; + +#endif // __JUCE_COLOUR_JUCEHEADER__ +/********* End of inlined file: juce_Colour.h *********/ + +/** + Contains a set of predefined named colours (mostly standard HTML colours) + + @see Colour, Colours::greyLevel +*/ +class Colours +{ +public: + static JUCE_API const Colour + + transparentBlack, /**< ARGB = 0x00000000 */ + transparentWhite, /**< ARGB = 0x00ffffff */ + + black, /**< ARGB = 0xff000000 */ + white, /**< ARGB = 0xffffffff */ + blue, /**< ARGB = 0xff0000ff */ + grey, /**< ARGB = 0xff808080 */ + green, /**< ARGB = 0xff008000 */ + red, /**< ARGB = 0xffff0000 */ + yellow, /**< ARGB = 0xffffff00 */ + + aliceblue, antiquewhite, aqua, aquamarine, + azure, beige, bisque, blanchedalmond, + blueviolet, brown, burlywood, cadetblue, + chartreuse, chocolate, coral, cornflowerblue, + cornsilk, crimson, cyan, darkblue, + darkcyan, darkgoldenrod, darkgrey, darkgreen, + darkkhaki, darkmagenta, darkolivegreen, darkorange, + darkorchid, darkred, darksalmon, darkseagreen, + darkslateblue, darkslategrey, darkturquoise, darkviolet, + deeppink, deepskyblue, dimgrey, dodgerblue, + firebrick, floralwhite, forestgreen, fuchsia, + gainsboro, gold, goldenrod, greenyellow, + honeydew, hotpink, indianred, indigo, + ivory, khaki, lavender, lavenderblush, + lemonchiffon, lightblue, lightcoral, lightcyan, + lightgoldenrodyellow, lightgreen, lightgrey, lightpink, + lightsalmon, lightseagreen, lightskyblue, lightslategrey, + lightsteelblue, lightyellow, lime, limegreen, + linen, magenta, maroon, mediumaquamarine, + mediumblue, mediumorchid, mediumpurple, mediumseagreen, + mediumslateblue, mediumspringgreen, mediumturquoise, mediumvioletred, + midnightblue, mintcream, mistyrose, navajowhite, + navy, oldlace, olive, olivedrab, + orange, orangered, orchid, palegoldenrod, + palegreen, paleturquoise, palevioletred, papayawhip, + peachpuff, peru, pink, plum, + powderblue, purple, rosybrown, royalblue, + saddlebrown, salmon, sandybrown, seagreen, + seashell, sienna, silver, skyblue, + slateblue, slategrey, snow, springgreen, + steelblue, tan, teal, thistle, + tomato, turquoise, violet, wheat, + whitesmoke, yellowgreen; + + /** Attempts to look up a string in the list of known colour names, and return + the appropriate colour. + + A non-case-sensitive search is made of the list of predefined colours, and + if a match is found, that colour is returned. If no match is found, the + colour passed in as the defaultColour parameter is returned. + */ + static JUCE_API const Colour findColourForName (const String& colourName, + const Colour& defaultColour); + +private: + + // this isn't a class you should ever instantiate - it's just here for the + // static values in it. + Colours(); +}; + +#endif // __JUCE_COLOURS_JUCEHEADER__ +/********* End of inlined file: juce_Colours.h *********/ + +/********* Start of inlined file: juce_SolidColourBrush.h *********/ +#ifndef __JUCE_SOLIDCOLOURBRUSH_JUCEHEADER__ +#define __JUCE_SOLIDCOLOURBRUSH_JUCEHEADER__ + +/********* Start of inlined file: juce_Brush.h *********/ +#ifndef __JUCE_BRUSH_JUCEHEADER__ +#define __JUCE_BRUSH_JUCEHEADER__ + +class Path; +class AffineTransform; +class LowLevelGraphicsContext; +class Image; +class Graphics; + +/** + A brush is used to fill areas with colours, patterns, or images. + + The Graphics class has an idea of a current brush which it uses to render + shapes, rectangles, lines, text, etc. + + This is the base class - there are subclasses for useful types of fill pattern, + and applications can define their own brushes too. + + @see Graphics::setBrush, SolidColourBrush, GradientBrush, ImageBrush +*/ +class JUCE_API Brush +{ +protected: + + /** Creates a Brush. + + (Nothing much happens in the base class). + */ + Brush() throw(); + +public: + /** Destructor. */ + virtual ~Brush() throw(); + + /** Creates a copy of whatever class of Brush this is. */ + virtual Brush* createCopy() const throw() = 0; + + /** Does whatever is relevent to transform the geometry of this brush. */ + virtual void applyTransform (const AffineTransform& transform) throw() = 0; + + /** Does whatever is relevent to change the opacity of this brush. */ + virtual void multiplyOpacity (const float multiple) throw() = 0; + + /** Must return true if this brush won't draw any pixels. */ + virtual bool isInvisible() const throw() = 0; + + virtual void paintPath (LowLevelGraphicsContext& context, + const Path& path, const AffineTransform& transform) throw() = 0; + + virtual void paintRectangle (LowLevelGraphicsContext& context, + int x, int y, int w, int h) throw() = 0; + + virtual void paintAlphaChannel (LowLevelGraphicsContext& context, + const Image& alphaChannelImage, int imageX, int imageY, + int x, int y, int w, int h) throw() = 0; + + virtual void paintVerticalLine (LowLevelGraphicsContext& context, + int x, float y1, float y2) throw(); + + virtual void paintHorizontalLine (LowLevelGraphicsContext& context, + int y, float x1, float x2) throw(); + + virtual void paintLine (LowLevelGraphicsContext& context, + float x1, float y1, float x2, float y2) throw(); + +private: + + Brush (const Brush&); + const Brush& operator= (const Brush&); +}; + +#endif // __JUCE_BRUSH_JUCEHEADER__ +/********* End of inlined file: juce_Brush.h *********/ + +/** + A Brush that fills its area with a solid (or semi-transparent) colour. + + An application won't normally need to use this class directly, as drawing + with solid colours is taken care of automatically by the Graphics class + (it actually uses one of these brushes internally when you set the colour + with the Graphics::setColour() method). + + @see Brush, Graphics::setBrush, GradientBrush, ImageBrush +*/ +class JUCE_API SolidColourBrush : public Brush +{ +public: + + /** Creates a SolidColourBrush to draw with the given colour. + + The colour can be changed later with the setColour() method. + */ + SolidColourBrush (const Colour& colour) throw(); + + /** Creates a SolidColourBrush set to black. + + The colour can be changed later with the setColour() method. + */ + SolidColourBrush() throw(); + + /** Destructor. */ + ~SolidColourBrush() throw(); + + /** Returns the colour currently being used. */ + const Colour& getColour() const throw() { return colour; } + + /** Sets the colour to use for drawing. */ + void setColour (const Colour& newColour) throw() { colour = newColour; } + + Brush* createCopy() const throw(); + + void applyTransform (const AffineTransform& transform) throw(); + + bool isInvisible() const throw(); + + void multiplyOpacity (const float multiple) throw(); + + void paintPath (LowLevelGraphicsContext& context, + const Path& path, const AffineTransform& transform) throw(); + + void paintRectangle (LowLevelGraphicsContext& context, + int x, int y, int w, int h) throw(); + + void paintAlphaChannel (LowLevelGraphicsContext& context, + const Image& alphaChannelImage, int imageX, int imageY, + int x, int y, int w, int h) throw(); + + void paintVerticalLine (LowLevelGraphicsContext& context, + int x, float y1, float y2) throw(); + + void paintHorizontalLine (LowLevelGraphicsContext& context, + int y, float x1, float x2) throw(); + + void paintLine (LowLevelGraphicsContext& context, + float x1, float y1, float x2, float y2) throw(); + + juce_UseDebuggingNewOperator + +private: + Colour colour; + + SolidColourBrush (const SolidColourBrush&); + const SolidColourBrush& operator= (const SolidColourBrush&); +}; + +#endif // __JUCE_SOLIDCOLOURBRUSH_JUCEHEADER__ +/********* End of inlined file: juce_SolidColourBrush.h *********/ + +/********* Start of inlined file: juce_RectanglePlacement.h *********/ +#ifndef __JUCE_RECTANGLEPLACEMENT_JUCEHEADER__ +#define __JUCE_RECTANGLEPLACEMENT_JUCEHEADER__ + +/** + Defines the method used to postion some kind of rectangular object within + a rectangular viewport. + + Although similar to Justification, this is more specific, and has some extra + options. +*/ +class JUCE_API RectanglePlacement +{ +public: + + /** Creates a RectanglePlacement object using a combination of flags. */ + inline RectanglePlacement (const int flags_) throw() : flags (flags_) {} + + /** Creates a copy of another Justification object. */ + RectanglePlacement (const RectanglePlacement& other) throw(); + + /** Copies another Justification object. */ + const RectanglePlacement& operator= (const RectanglePlacement& other) throw(); + + /** Flag values that can be combined and used in the constructor. */ + enum + { + + /** Indicates that the source rectangle's left edge should be aligned with the left edge of the target rectangle. */ + xLeft = 1, + + /** Indicates that the source rectangle's right edge should be aligned with the right edge of the target rectangle. */ + xRight = 2, + + /** Indicates that the source should be placed in the centre between the left and right + sides of the available space. */ + xMid = 4, + + /** Indicates that the source's top edge should be aligned with the top edge of the + destination rectangle. */ + yTop = 8, + + /** Indicates that the source's bottom edge should be aligned with the bottom edge of the + destination rectangle. */ + yBottom = 16, + + /** Indicates that the source should be placed in the centre between the top and bottom + sides of the available space. */ + yMid = 32, + + /** If this flag is set, then the source rectangle will be resized to completely fill + the destination rectangle, and all other flags are ignored. + */ + stretchToFit = 64, + + /** If this flag is set, then the source rectangle will be resized so that it is the + minimum size to completely fill the destination rectangle. This means that some + of the source rectangle may fall outside the destination. + + If this flag is not set, the source will be given the maximum size at which none + of it falls outside the destination rectangle. + */ + fillDestination = 128, + + /** Indicates that the source rectangle can be reduced in size if required, but should + never be made larger than its original size. + */ + onlyReduceInSize = 256, + + /** Indicates that the source rectangle can be enlarged if required, but should + never be made smaller than its original size. + */ + onlyIncreaseInSize = 512, + + /** Indicates that the source rectangle's size should be left unchanged. + */ + doNotResize = (onlyIncreaseInSize | onlyReduceInSize), + + /** A shorthand value that is equivalent to (xMid | yMid). */ + centred = 4 + 32 + }; + + /** Returns the raw flags that are set for this object. */ + inline int getFlags() const throw() { return flags; } + + /** Tests a set of flags for this object. + + @returns true if any of the flags passed in are set on this object. + */ + inline bool testFlags (const int flagsToTest) const throw() { return (flags & flagsToTest) != 0; } + + /** Adjusts the position and size of a rectangle to fit it into a space. + + The source rectangle co-ordinates will be adjusted so that they fit into + the destination rectangle based on this object's flags. + */ + void applyTo (double& sourceX, + double& sourceY, + double& sourceW, + double& sourceH, + const double destinationX, + const double destinationY, + const double destinationW, + const double destinationH) const throw(); + + /** Returns the transform that should be applied to these source co-ordinates to fit them + into the destination rectangle using the current flags. + */ + const AffineTransform getTransformToFit (float sourceX, + float sourceY, + float sourceW, + float sourceH, + const float destinationX, + const float destinationY, + const float destinationW, + const float destinationH) const throw(); + +private: + + int flags; +}; + +#endif // __JUCE_RECTANGLEPLACEMENT_JUCEHEADER__ +/********* End of inlined file: juce_RectanglePlacement.h *********/ + +class LowLevelGraphicsContext; +class Image; +class RectangleList; + +/** + A graphics context, used for drawing a component or image. + + When a Component needs painting, a Graphics context is passed to its + Component::paint() method, and this you then call methods within this + object to actually draw the component's content. + + A Graphics can also be created from an image, to allow drawing directly onto + that image. + + @see Component::paint +*/ +class JUCE_API Graphics +{ +public: + + /** Creates a Graphics object to draw directly onto the given image. + + The graphics object that is created will be set up to draw onto the image, + with the context's clipping area being the entire size of the image, and its + origin being the image's origin. To draw into a subsection of an image, use the + reduceClipRegion() and setOrigin() methods. + + Obviously you shouldn't delete the image before this context is deleted. + */ + Graphics (Image& imageToDrawOnto) throw(); + + /** Destructor. */ + ~Graphics() throw(); + + /** Changes the current drawing colour. + + This sets the colour that will now be used for drawing operations - it also + sets the opacity to that of the colour passed-in. + + If a brush is being used when this method is called, the brush will be deselected, + and any subsequent drawing will be done with a solid colour brush instead. + + @see setOpacity, setBrush, getColour + */ + void setColour (const Colour& newColour) throw(); + + /** Returns the colour that's currently being used. + + This will return the last colour set by setColour(), even if the colour's not + currently being used for drawing because a brush has been selected instead. + + @see setColour + */ + const Colour& getCurrentColour() const throw(); + + /** Changes the opacity to use with the current colour. + + If a solid colour is being used for drawing, this changes its opacity (and this + will be reflected by calls to the getColour() method). + + A value of 0.0 is completely transparent, 1.0 is completely opaque. + */ + void setOpacity (const float newOpacity) throw(); + + /** Changes the current brush to use for drawing. + + If a null pointer is passed in, the context will revert to using a solid + colour for drawing (using the last colour set by setColour()). + + If a brush is passed in, a copy of it will be used for subsequent drawing + operations until setColour() or setBrush() is called. + + @see SolidColourBrush, GradientBrush, ImageBrush, Brush + */ + void setBrush (const Brush* const newBrush) throw(); + + /** Changes the font to use for subsequent text-drawing functions. + + Note there's also a setFont (float, int) method to quickly change the size and + style of the current font. + + @see drawSingleLineText, drawMultiLineText, drawTextAsPath, drawText, drawFittedText + */ + void setFont (const Font& newFont) throw(); + + /** Changes the size and style of the currently-selected font. + + This is a convenient shortcut that changes the context's current font to a + different size or style. The typeface won't be changed. + + @see Font + */ + void setFont (const float newFontHeight, + const int fontStyleFlags = Font::plain) throw(); + + /** Returns the font that's currently being used for text operations. + + @see setFont + */ + const Font& getCurrentFont() const throw(); + + /** Draws a one-line text string. + + This will use the current colour (or brush) to fill the text. The font is the last + one specified by setFont(). + + @param text the string to draw + @param startX the position to draw the left-hand edge of the text + @param baselineY the position of the text's baseline + @see drawMultiLineText, drawText, drawFittedText, GlyphArrangement::addLineOfText + */ + void drawSingleLineText (const String& text, + const int startX, + const int baselineY) const throw(); + + /** Draws text across multiple lines. + + This will break the text onto a new line where there's a new-line or + carriage-return character, or at a word-boundary when the text becomes wider + than the size specified by the maximumLineWidth parameter. + + @see setFont, drawSingleLineText, drawFittedText, GlyphArrangement::addJustifiedText + */ + void drawMultiLineText (const String& text, + const int startX, + const int baselineY, + const int maximumLineWidth) const throw(); + + /** Renders a string of text as a vector path. + + This allows a string to be transformed with an arbitrary AffineTransform and + rendered using the current colour/brush. It's much slower than the normal text methods + but more accurate. + + @see setFont + */ + void drawTextAsPath (const String& text, + const AffineTransform& transform) const throw(); + + /** Draws a line of text within a specified rectangle. + + The text will be positioned within the rectangle based on the justification + flags passed-in. If the string is too long to fit inside the rectangle, it will + either be truncated or will have ellipsis added to its end (if the useEllipsesIfTooBig + flag is true). + + @see drawSingleLineText, drawFittedText, drawMultiLineText, GlyphArrangement::addJustifiedText + */ + void drawText (const String& text, + const int x, + const int y, + const int width, + const int height, + const Justification& justificationType, + const bool useEllipsesIfTooBig) const throw(); + + /** Tries to draw a text string inside a given space. + + This does its best to make the given text readable within the specified rectangle, + so it useful for labelling things. + + If the text is too big, it'll be squashed horizontally or broken over multiple lines + if the maximumLinesToUse value allows this. If the text just won't fit into the space, + it'll cram as much as possible in there, and put some ellipsis at the end to show that + it's been truncated. + + A Justification parameter lets you specify how the text is laid out within the rectangle, + both horizontally and vertically. + + The minimumHorizontalScale parameter specifies how much the text can be squashed horizontally + to try to squeeze it into the space. If you don't want any horizontal scaling to occur, you + can set this value to 1.0f. + + @see GlyphArrangement::addFittedText + */ + void drawFittedText (const String& text, + const int x, + const int y, + const int width, + const int height, + const Justification& justificationFlags, + const int maximumNumberOfLines, + const float minimumHorizontalScale = 0.7f) const throw(); + + /** Fills the context's entire clip region with the current colour or brush. + + (See also the fillAll (const Colour&) method which is a quick way of filling + it with a given colour). + */ + void fillAll() const throw(); + + /** Fills the context's entire clip region with a given colour. + + This leaves the context's current colour and brush unchanged, it just + uses the specified colour temporarily. + */ + void fillAll (const Colour& colourToUse) const throw(); + + /** Fills a rectangle with the current colour or brush. + + @see drawRect, fillRoundedRectangle + */ + void fillRect (int x, + int y, + int width, + int height) const throw(); + + /** Fills a rectangle with the current colour or brush. */ + void fillRect (const Rectangle& rectangle) const throw(); + + /** Fills a rectangle with the current colour or brush. + + This uses sub-pixel positioning so is slower than the fillRect method which + takes integer co-ordinates. + */ + void fillRect (const float x, + const float y, + const float width, + const float height) const throw(); + + /** Uses the current colour or brush to fill a rectangle with rounded corners. + + @see drawRoundedRectangle, Path::addRoundedRectangle + */ + void fillRoundedRectangle (const float x, + const float y, + const float width, + const float height, + const float cornerSize) const throw(); + + /** Uses the current colour or brush to fill a rectangle with rounded corners. + + @see drawRoundedRectangle, Path::addRoundedRectangle + */ + void fillRoundedRectangle (const Rectangle& rectangle, + const float cornerSize) const throw(); + + /** Fills a rectangle with a checkerboard pattern, alternating between two colours. + */ + void fillCheckerBoard (int x, int y, + int width, int height, + const int checkWidth, + const int checkHeight, + const Colour& colour1, + const Colour& colour2) const throw(); + + /** Draws four lines to form a rectangular outline, using the current colour or brush. + + The lines are drawn inside the given rectangle, and greater line thicknesses + extend inwards. + + @see fillRect + */ + void drawRect (const int x, + const int y, + const int width, + const int height, + const int lineThickness = 1) const throw(); + + /** Draws four lines to form a rectangular outline, using the current colour or brush. + + The lines are drawn inside the given rectangle, and greater line thicknesses + extend inwards. + + @see fillRect + */ + void drawRect (const float x, + const float y, + const float width, + const float height, + const float lineThickness = 1.0f) const throw(); + + /** Draws four lines to form a rectangular outline, using the current colour or brush. + + The lines are drawn inside the given rectangle, and greater line thicknesses + extend inwards. + + @see fillRect + */ + void drawRect (const Rectangle& rectangle, + const int lineThickness = 1) const throw(); + + /** Uses the current colour or brush to draw the outline of a rectangle with rounded corners. + + @see fillRoundedRectangle, Path::addRoundedRectangle + */ + void drawRoundedRectangle (const float x, + const float y, + const float width, + const float height, + const float cornerSize, + const float lineThickness) const throw(); + + /** Uses the current colour or brush to draw the outline of a rectangle with rounded corners. + + @see fillRoundedRectangle, Path::addRoundedRectangle + */ + void drawRoundedRectangle (const Rectangle& rectangle, + const float cornerSize, + const float lineThickness) const throw(); + + /** Draws a 3D raised (or indented) bevel using two colours. + + The bevel is drawn inside the given rectangle, and greater bevel thicknesses + extend inwards. + + The top-left colour is used for the top- and left-hand edges of the + bevel; the bottom-right colour is used for the bottom- and right-hand + edges. + */ + void drawBevel (const int x, + const int y, + const int width, + const int height, + const int bevelThickness, + const Colour& topLeftColour = Colours::white, + const Colour& bottomRightColour = Colours::black, + const bool useGradient = true) const throw(); + + /** Draws a pixel using the current colour or brush. + */ + void setPixel (int x, int y) const throw(); + + /** Fills an ellipse with the current colour or brush. + + The ellipse is drawn to fit inside the given rectangle. + + @see drawEllipse, Path::addEllipse + */ + void fillEllipse (const float x, + const float y, + const float width, + const float height) const throw(); + + /** Draws an elliptical stroke using the current colour or brush. + + @see fillEllipse, Path::addEllipse + */ + void drawEllipse (const float x, + const float y, + const float width, + const float height, + const float lineThickness) const throw(); + + /** Draws a line between two points. + + The line is 1 pixel wide and drawn with the current colour or brush. + */ + void drawLine (float startX, + float startY, + float endX, + float endY) const throw(); + + /** Draws a line between two points with a given thickness. + + @see Path::addLineSegment + */ + void drawLine (const float startX, + const float startY, + const float endX, + const float endY, + const float lineThickness) const throw(); + + /** Draws a line between two points. + + The line is 1 pixel wide and drawn with the current colour or brush. + */ + void drawLine (const Line& line) const throw(); + + /** Draws a line between two points with a given thickness. + + @see Path::addLineSegment + */ + void drawLine (const Line& line, + const float lineThickness) const throw(); + + /** Draws a dashed line using a custom set of dash-lengths. + + @param startX the line's start x co-ordinate + @param startY the line's start y co-ordinate + @param endX the line's end x co-ordinate + @param endY the line's end y co-ordinate + @param dashLengths a series of lengths to specify the on/off lengths - e.g. + { 4, 5, 6, 7 } will draw a line of 4 pixels, skip 5 pixels, + draw 6 pixels, skip 7 pixels, and then repeat. + @param numDashLengths the number of elements in the array (this must be an even number). + @param lineThickness the thickness of the line to draw + @see PathStrokeType::createDashedStroke + */ + void drawDashedLine (const float startX, + const float startY, + const float endX, + const float endY, + const float* const dashLengths, + const int numDashLengths, + const float lineThickness = 1.0f) const throw(); + + /** Draws a vertical line of pixels at a given x position. + + The x position is an integer, but the top and bottom of the line can be sub-pixel + positions, and these will be anti-aliased if necessary. + */ + void drawVerticalLine (const int x, float top, float bottom) const throw(); + + /** Draws a horizontal line of pixels at a given y position. + + The y position is an integer, but the left and right ends of the line can be sub-pixel + positions, and these will be anti-aliased if necessary. + */ + void drawHorizontalLine (const int y, float left, float right) const throw(); + + /** Fills a path using the currently selected colour or brush. + */ + void fillPath (const Path& path, + const AffineTransform& transform = AffineTransform::identity) const throw(); + + /** Draws a path's outline using the currently selected colour or brush. + */ + void strokePath (const Path& path, + const PathStrokeType& strokeType, + const AffineTransform& transform = AffineTransform::identity) const throw(); + + /** Draws a line with an arrowhead. + + @param startX the line's start x co-ordinate + @param startY the line's start y co-ordinate + @param endX the line's end x co-ordinate (the tip of the arrowhead) + @param endY the line's end y co-ordinate (the tip of the arrowhead) + @param lineThickness the thickness of the line + @param arrowheadWidth the width of the arrow head (perpendicular to the line) + @param arrowheadLength the length of the arrow head (along the length of the line) + */ + void drawArrow (const float startX, + const float startY, + const float endX, + const float endY, + const float lineThickness, + const float arrowheadWidth, + const float arrowheadLength) const throw(); + + /** Types of rendering quality that can be specified when drawing images. + + @see blendImage, Graphics::setImageResamplingQuality + */ + enum ResamplingQuality + { + lowResamplingQuality = 0, /**< Just uses a nearest-neighbour algorithm for resampling. */ + mediumResamplingQuality = 1, /**< Uses bilinear interpolation for upsampling and area-averaging for downsampling. */ + highResamplingQuality = 2 /**< Uses bicubic interpolation for upsampling and area-averaging for downsampling. */ + }; + + /** Changes the quality that will be used when resampling images. + + By default a Graphics object will be set to mediumRenderingQuality. + + @see Graphics::drawImage, Graphics::drawImageTransformed, Graphics::drawImageWithin + */ + void setImageResamplingQuality (const ResamplingQuality newQuality) throw(); + + /** Draws an image. + + This will draw the whole of an image, positioning its top-left corner at the + given co-ordinates, and keeping its size the same. This is the simplest image + drawing method - the others give more control over the scaling and clipping + of the images. + + Images are composited using the context's current opacity, so if you + don't want it to be drawn semi-transparently, be sure to call setOpacity (1.0f) + (or setColour() with an opaque colour) before drawing images. + */ + void drawImageAt (const Image* const imageToDraw, + const int topLeftX, + const int topLeftY, + const bool fillAlphaChannelWithCurrentBrush = false) const throw(); + + /** Draws part of an image, rescaling it to fit in a given target region. + + The specified area of the source image is rescaled and drawn to fill the + specifed destination rectangle. + + Images are composited using the context's current opacity, so if you + don't want it to be drawn semi-transparently, be sure to call setOpacity (1.0f) + (or setColour() with an opaque colour) before drawing images. + + @param imageToDraw the image to overlay + @param destX the left of the destination rectangle + @param destY the top of the destination rectangle + @param destWidth the width of the destination rectangle + @param destHeight the height of the destination rectangle + @param sourceX the left of the rectangle to copy from the source image + @param sourceY the top of the rectangle to copy from the source image + @param sourceWidth the width of the rectangle to copy from the source image + @param sourceHeight the height of the rectangle to copy from the source image + @param fillAlphaChannelWithCurrentBrush if true, then instead of drawing the source image's pixels, + the source image's alpha channel is used as a mask with + which to fill the destination using the current colour + or brush. (If the source is has no alpha channel, then + it will just fill the target with a solid rectangle) + @see setImageResamplingQuality, drawImageAt, drawImageWithin, fillAlphaMap + */ + void drawImage (const Image* const imageToDraw, + int destX, + int destY, + int destWidth, + int destHeight, + int sourceX, + int sourceY, + int sourceWidth, + int sourceHeight, + const bool fillAlphaChannelWithCurrentBrush = false) const throw(); + + /** Draws part of an image, having applied an affine transform to it. + + This lets you throw the image around in some wacky ways, rotate it, shear, + scale it, etc. + + A clipping subregion is specified within the source image and no pixels + outside this region will be used. + + Images are composited using the context's current opacity, so if you + don't want it to be drawn semi-transparently, be sure to call setOpacity (1.0f) + (or setColour() with an opaque colour) before drawing images. + + If fillAlphaChannelWithCurrentBrush is set to true, then the image's RGB channels + are ignored and it is filled with the current brush, masked by its alpha channel. + + @see setImageResamplingQuality, drawImage + */ + void drawImageTransformed (const Image* const imageToDraw, + int sourceClipX, + int sourceClipY, + int sourceClipWidth, + int sourceClipHeight, + const AffineTransform& transform, + const bool fillAlphaChannelWithCurrentBrush) const throw(); + + /** Draws an image to fit within a designated rectangle. + + If the image is too big or too small for the space, it will be rescaled + to fit as nicely as it can do without affecting its aspect ratio. It will + then be placed within the target rectangle according to the justification flags + specified. + + @param imageToDraw the source image to draw + @param destX top-left of the target rectangle to fit it into + @param destY top-left of the target rectangle to fit it into + @param destWidth size of the target rectangle to fit the image into + @param destHeight size of the target rectangle to fit the image into + @param placementWithinTarget this specifies how the image should be positioned + within the target rectangle - see the RectanglePlacement + class for more details about this. + @param fillAlphaChannelWithCurrentBrush if true, then instead of drawing the image, just its + alpha channel will be used as a mask with which to + draw with the current brush or colour. This is + similar to fillAlphaMap(), and see also drawImage() + @see setImageResamplingQuality, drawImage, drawImageTransformed, drawImageAt, RectanglePlacement + */ + void drawImageWithin (const Image* const imageToDraw, + const int destX, + const int destY, + const int destWidth, + const int destHeight, + const RectanglePlacement& placementWithinTarget, + const bool fillAlphaChannelWithCurrentBrush) const throw(); + + /** Returns the position of the bounding box for the current clipping region. + + @see getClipRegion, clipRegionIntersects + */ + const Rectangle getClipBounds() const throw(); + + /** Checks whether a rectangle overlaps the context's clipping region. + + If this returns false, no part of the given area can be drawn onto, so this + method can be used to optimise a component's paint() method, by letting it + avoid drawing complex objects that aren't within the region being repainted. + */ + bool clipRegionIntersects (const int x, const int y, const int width, const int height) const throw(); + + /** Intersects the current clipping region with another region. + + @returns true if the resulting clipping region is non-zero in size + @see setOrigin, clipRegionIntersects, getClipLeft, getClipRight, getClipWidth, getClipHeight + */ + bool reduceClipRegion (const int x, const int y, + const int width, const int height) throw(); + + /** Intersects the current clipping region with a rectangle list region. + + @returns true if the resulting clipping region is non-zero in size + @see setOrigin, clipRegionIntersects, getClipLeft, getClipRight, getClipWidth, getClipHeight + */ + bool reduceClipRegion (const RectangleList& clipRegion) throw(); + + /** Excludes a rectangle to stop it being drawn into. */ + void excludeClipRegion (const int x, const int y, + const int width, const int height) throw(); + + /** Returns true if no drawing can be done because the clip region is zero. */ + bool isClipEmpty() const throw(); + + /** Saves the current graphics state on an internal stack. + + To restore the state, use restoreState(). + */ + void saveState() throw(); + + /** Restores a graphics state that was previously saved with saveState(). + */ + void restoreState() throw(); + + /** Moves the position of the context's origin. + + This changes the position that the context considers to be (0, 0) to + the specified position. + + So if you call setOrigin (100, 100), then the position that was previously + referred to as (100, 100) will subsequently be considered to be (0, 0). + + @see reduceClipRegion + */ + void setOrigin (const int newOriginX, + const int newOriginY) throw(); + + /** Resets the current colour, brush, and font to default settings. */ + void resetToDefaultState() throw(); + + /** Returns true if this context is drawing to a vector-based device, such as a printer. */ + bool isVectorDevice() const throw(); + + juce_UseDebuggingNewOperator + + /** Create a graphics that uses a given low-level renderer. + + For internal use only. + + NB. The context will NOT be deleted by this object when it is deleted. + */ + Graphics (LowLevelGraphicsContext* const internalContext) throw(); + + /** @internal */ + LowLevelGraphicsContext* getInternalContext() const throw() { return context; } + +private: + + LowLevelGraphicsContext* const context; + const bool ownsContext; + + struct GraphicsState + { + GraphicsState() throw(); + GraphicsState (const GraphicsState&) throw(); + ~GraphicsState() throw(); + + Colour colour; + Brush* brush; + Font font; + ResamplingQuality quality; + }; + + GraphicsState* state; + OwnedArray stateStack; + bool saveStatePending; + + void saveStateIfPending() throw(); + + const Graphics& operator= (const Graphics& other); + Graphics (const Graphics&); +}; + +#endif // __JUCE_GRAPHICS_JUCEHEADER__ +/********* End of inlined file: juce_Graphics.h *********/ + +/** + A graphical effect filter that can be applied to components. + + An ImageEffectFilter can be applied to the image that a component + paints before it hits the screen. + + This is used for adding effects like shadows, blurs, etc. + + @see Component::setComponentEffect +*/ +class JUCE_API ImageEffectFilter +{ +public: + + /** Overridden to render the effect. + + The implementation of this method must use the image that is passed in + as its source, and should render its output to the graphics context passed in. + + @param sourceImage the image that the source component has just rendered with + its paint() method. The image may or may not have an alpha + channel, depending on whether the component is opaque. + @param destContext the graphics context to use to draw the resultant image. + */ + virtual void applyEffect (Image& sourceImage, + Graphics& destContext) = 0; + + /** Destructor. */ + virtual ~ImageEffectFilter() {} + +}; + +#endif // __JUCE_IMAGEEFFECTFILTER_JUCEHEADER__ +/********* End of inlined file: juce_ImageEffectFilter.h *********/ + +/********* Start of inlined file: juce_RectangleList.h *********/ +#ifndef __JUCE_RECTANGLELIST_JUCEHEADER__ +#define __JUCE_RECTANGLELIST_JUCEHEADER__ + +/** + Maintains a set of rectangles as a complex region. + + This class allows a set of rectangles to be treated as a solid shape, and can + add and remove rectangular sections of it, and simplify overlapping or + adjacent rectangles. + + @see Rectangle +*/ +class JUCE_API RectangleList +{ +public: + + /** Creates an empty RectangleList */ + RectangleList() throw(); + + /** Creates a copy of another list */ + RectangleList (const RectangleList& other) throw(); + + /** Creates a list containing just one rectangle. */ + RectangleList (const Rectangle& rect) throw(); + + /** Copies this list from another one. */ + const RectangleList& operator= (const RectangleList& other) throw(); + + /** Destructor. */ + ~RectangleList() throw(); + + /** Returns true if the region is empty. */ + bool isEmpty() const throw(); + + /** Returns the number of rectangles in the list. */ + int getNumRectangles() const throw() { return rects.size(); } + + /** Returns one of the rectangles at a particular index. + + @returns the rectangle at the index, or an empty rectangle if the + index is out-of-range. + */ + const Rectangle getRectangle (const int index) const throw(); + + /** Removes all rectangles to leave an empty region. */ + void clear() throw(); + + /** Merges a new rectangle into the list. + + The rectangle being added will first be clipped to remove any parts of it + that overlap existing rectangles in the list. + */ + void add (const int x, const int y, + const int w, const int h) throw(); + + /** Merges a new rectangle into the list. + + The rectangle being added will first be clipped to remove any parts of it + that overlap existing rectangles in the list, and adjacent rectangles will be + merged into it. + */ + void add (const Rectangle& rect) throw(); + + /** Dumbly adds a rectangle to the list without checking for overlaps. + + This simply adds the rectangle to the end, it doesn't merge it or remove + any overlapping bits. + */ + void addWithoutMerging (const Rectangle& rect) throw(); + + /** Merges another rectangle list into this one. + + Any overlaps between the two lists will be clipped, so that the result is + the union of both lists. + */ + void add (const RectangleList& other) throw(); + + /** Removes a rectangular region from the list. + + Any rectangles in the list which overlap this will be clipped and subdivided + if necessary. + */ + void subtract (const Rectangle& rect) throw(); + + /** Removes all areas in another RectangleList from this one. + + Any rectangles in the list which overlap this will be clipped and subdivided + if necessary. + */ + void subtract (const RectangleList& otherList) throw(); + + /** Removes any areas of the region that lie outside a given rectangle. + + Any rectangles in the list which overlap this will be clipped and subdivided + if necessary. + + Returns true if the resulting region is not empty, false if it is empty. + + @see getIntersectionWith + */ + bool clipTo (const Rectangle& rect) throw(); + + /** Removes any areas of the region that lie outside a given rectangle list. + + Any rectangles in this object which overlap the specified list will be clipped + and subdivided if necessary. + + Returns true if the resulting region is not empty, false if it is empty. + + @see getIntersectionWith + */ + bool clipTo (const RectangleList& other) throw(); + + /** Creates a region which is the result of clipping this one to a given rectangle. + + Unlike the other clipTo method, this one doesn't affect this object - it puts the + resulting region into the list whose reference is passed-in. + + Returns true if the resulting region is not empty, false if it is empty. + + @see clipTo + */ + bool getIntersectionWith (const Rectangle& rect, RectangleList& destRegion) const throw(); + + /** Swaps the contents of this and another list. + + This swaps their internal pointers, so is hugely faster than using copy-by-value + to swap them. + */ + void swapWith (RectangleList& otherList) throw(); + + /** Checks whether the region contains a given point. + + @returns true if the point lies within one of the rectangles in the list + */ + bool containsPoint (const int x, const int y) const throw(); + + /** Checks whether the region contains the whole of a given rectangle. + + @returns true all parts of the rectangle passed in lie within the region + defined by this object + @see intersectsRectangle, containsPoint + */ + bool containsRectangle (const Rectangle& rectangleToCheck) const throw(); + + /** Checks whether the region contains any part of a given rectangle. + + @returns true if any part of the rectangle passed in lies within the region + defined by this object + @see containsRectangle + */ + bool intersectsRectangle (const Rectangle& rectangleToCheck) const throw(); + + /** Checks whether this region intersects any part of another one. + + @see intersectsRectangle + */ + bool intersects (const RectangleList& other) const throw(); + + /** Returns the smallest rectangle that can enclose the whole of this region. */ + const Rectangle getBounds() const throw(); + + /** Optimises the list into a minimum number of constituent rectangles. + + This will try to combine any adjacent rectangles into larger ones where + possible, to simplify lists that might have been fragmented by repeated + add/subtract calls. + */ + void consolidate() throw(); + + /** Adds an x and y value to all the co-ordinates. */ + void offsetAll (const int dx, const int dy) throw(); + + /** Creates a Path object to represent this region. */ + const Path toPath() const throw(); + + /** An iterator for accessing all the rectangles in a RectangleList. */ + class Iterator + { + public: + + Iterator (const RectangleList& list) throw(); + ~Iterator() throw(); + + /** Advances to the next rectangle, and returns true if it's not finished. + + Call this before using getRectangle() to find the rectangle that was returned. + */ + bool next() throw(); + + /** Returns the current rectangle. */ + const Rectangle* getRectangle() const throw() { return current; } + + juce_UseDebuggingNewOperator + + private: + const Rectangle* current; + const RectangleList& owner; + int index; + + Iterator (const Iterator&); + const Iterator& operator= (const Iterator&); + }; + + juce_UseDebuggingNewOperator + +private: + friend class Iterator; + Array rects; +}; + +#endif // __JUCE_RECTANGLELIST_JUCEHEADER__ +/********* End of inlined file: juce_RectangleList.h *********/ + +/********* Start of inlined file: juce_BorderSize.h *********/ +#ifndef __JUCE_BORDERSIZE_JUCEHEADER__ +#define __JUCE_BORDERSIZE_JUCEHEADER__ + +/** + Specifies a set of gaps to be left around the sides of a rectangle. + + This is basically the size of the spaces at the top, bottom, left and right of + a rectangle. It's used by various component classes to specify borders. + + @see Rectangle +*/ +class JUCE_API BorderSize +{ +public: + + /** Creates a null border. + + All sizes are left as 0. + */ + BorderSize() throw(); + + /** Creates a copy of another border. */ + BorderSize (const BorderSize& other) throw(); + + /** Creates a border with the given gaps. */ + BorderSize (const int topGap, + const int leftGap, + const int bottomGap, + const int rightGap) throw(); + + /** Creates a border with the given gap on all sides. */ + BorderSize (const int allGaps) throw(); + + /** Destructor. */ + ~BorderSize() throw(); + + /** Returns the gap that should be left at the top of the region. */ + int getTop() const throw() { return top; } + + /** Returns the gap that should be left at the top of the region. */ + int getLeft() const throw() { return left; } + + /** Returns the gap that should be left at the top of the region. */ + int getBottom() const throw() { return bottom; } + + /** Returns the gap that should be left at the top of the region. */ + int getRight() const throw() { return right; } + + /** Returns the sum of the top and bottom gaps. */ + int getTopAndBottom() const throw() { return top + bottom; } + + /** Returns the sum of the left and right gaps. */ + int getLeftAndRight() const throw() { return left + right; } + + /** Changes the top gap. */ + void setTop (const int newTopGap) throw(); + + /** Changes the left gap. */ + void setLeft (const int newLeftGap) throw(); + + /** Changes the bottom gap. */ + void setBottom (const int newBottomGap) throw(); + + /** Changes the right gap. */ + void setRight (const int newRightGap) throw(); + + /** Returns a rectangle with these borders removed from it. */ + const Rectangle subtractedFrom (const Rectangle& original) const throw(); + + /** Removes this border from a given rectangle. */ + void subtractFrom (Rectangle& rectangle) const throw(); + + /** Returns a rectangle with these borders added around it. */ + const Rectangle addedTo (const Rectangle& original) const throw(); + + /** Adds this border around a given rectangle. */ + void addTo (Rectangle& original) const throw(); + + bool operator== (const BorderSize& other) const throw(); + bool operator!= (const BorderSize& other) const throw(); + + juce_UseDebuggingNewOperator + +private: + int top, left, bottom, right; +}; + +#endif // __JUCE_BORDERSIZE_JUCEHEADER__ +/********* End of inlined file: juce_BorderSize.h *********/ + +/********* Start of inlined file: juce_ComponentPeer.h *********/ +#ifndef __JUCE_COMPONENTPEER_JUCEHEADER__ +#define __JUCE_COMPONENTPEER_JUCEHEADER__ + +class Component; +class Graphics; + +/********* Start of inlined file: juce_MessageListener.h *********/ +#ifndef __JUCE_MESSAGELISTENER_JUCEHEADER__ +#define __JUCE_MESSAGELISTENER_JUCEHEADER__ + +/********* Start of inlined file: juce_Message.h *********/ +#ifndef __JUCE_MESSAGE_JUCEHEADER__ +#define __JUCE_MESSAGE_JUCEHEADER__ + +class MessageListener; + +/** The base class for objects that can be delivered to a MessageListener. + + The simplest Message object contains a few integer and pointer parameters + that the user can set, and this is enough for a lot of purposes. For passing more + complex data, subclasses of Message can also be used. + + @see MessageListener, MessageManager, ActionListener, ChangeListener +*/ +class JUCE_API Message +{ +public: + + /** Creates an uninitialised message. + + The class's variables will also be left uninitialised. + */ + Message() throw(); + + /** Creates a message object, filling in the member variables. + + The corresponding public member variables will be set from the parameters + passed in. + */ + Message (const int intParameter1, + const int intParameter2, + const int intParameter3, + void* const pointerParameter) throw(); + + /** Destructor. */ + virtual ~Message() throw(); + + // These values can be used for carrying simple data that the application needs to + // pass around. For more complex messages, just create a subclass. + + int intParameter1; /**< user-defined integer value. */ + int intParameter2; /**< user-defined integer value. */ + int intParameter3; /**< user-defined integer value. */ + void* pointerParameter; /**< user-defined pointer value. */ + + juce_UseDebuggingNewOperator + +private: + friend class MessageListener; + friend class MessageManager; + MessageListener* messageRecipient; + + Message (const Message&); + const Message& operator= (const Message&); +}; + +#endif // __JUCE_MESSAGE_JUCEHEADER__ +/********* End of inlined file: juce_Message.h *********/ + +/** + MessageListener subclasses can post and receive Message objects. + + @see Message, MessageManager, ActionListener, ChangeListener +*/ +class JUCE_API MessageListener +{ +protected: + + /** Creates a MessageListener. */ + MessageListener() throw(); + +public: + + /** Destructor. + + When a MessageListener is deleted, it removes itself from a global list + of registered listeners, so that the isValidMessageListener() method + will no longer return true. + */ + virtual ~MessageListener(); + + /** This is the callback method that receives incoming messages. + + This is called by the MessageManager from its dispatch loop. + + @see postMessage + */ + virtual void handleMessage (const Message& message) = 0; + + /** Sends a message to the message queue, for asynchronous delivery to this listener + later on. + + This method can be called safely by any thread. + + @param message the message object to send - this will be deleted + automatically by the message queue, so don't keep any + references to it after calling this method. + @see handleMessage + */ + void postMessage (Message* const message) const throw(); + + /** Checks whether this MessageListener has been deleted. + + Although not foolproof, this method is safe to call on dangling or null + pointers. A list of active MessageListeners is kept internally, so this + checks whether the object is on this list or not. + + Note that it's possible to get a false-positive here, if an object is + deleted and another is subsequently created that happens to be at the + exact same memory location, but I can't think of a good way of avoiding + this. + */ + bool isValidMessageListener() const throw(); +}; + +#endif // __JUCE_MESSAGELISTENER_JUCEHEADER__ +/********* End of inlined file: juce_MessageListener.h *********/ + +class ComponentBoundsConstrainer; +class ComponentDeletionWatcher; + +/** + The base class for window objects that wrap a component as a real operating + system object. + + This is an abstract base class - the platform specific code contains default + implementations of it that create and manage windows. + + @see Component::createNewPeer +*/ +class JUCE_API ComponentPeer : public MessageListener +{ +public: + + /** A combination of these flags is passed to the ComponentPeer constructor. */ + enum StyleFlags + { + windowAppearsOnTaskbar = (1 << 0), /**< Indicates that the window should have a corresponding + entry on the taskbar (ignored on MacOSX) */ + windowIsTemporary = (1 << 1), /**< Indicates that the window is a temporary popup, like a menu, + tooltip, etc. */ + windowIgnoresMouseClicks = (1 << 2), /**< Indicates that the window should let mouse clicks pass + through it (may not be possible on some platforms). */ + windowHasTitleBar = (1 << 3), /**< Indicates that the window should have a normal OS-specific + title bar and frame\. if not specified, the window will be + borderless. */ + windowIsResizable = (1 << 4), /**< Indicates that the window should let mouse clicks pass + through it (may not be possible on some platforms). */ + windowHasMinimiseButton = (1 << 5), /**< Indicates that if the window has a title bar, it should have a + minimise button on it. */ + windowHasMaximiseButton = (1 << 6), /**< Indicates that if the window has a title bar, it should have a + maximise button on it. */ + windowHasCloseButton = (1 << 7), /**< Indicates that if the window has a title bar, it should have a + close button on it. */ + windowHasDropShadow = (1 << 8), /**< Indicates that the window should have a drop-shadow (this may + not be possible on all platforms). */ + windowRepaintedExplictly = (1 << 9), /**< Not intended for public use - this tells a window not to + do its own repainting, but only to repaint when the + performAnyPendingRepaintsNow() method is called. */ + windowIsSemiTransparent = (1 << 31) /**< Not intended for public use - makes a window transparent. */ + + }; + + /** Creates a peer. + + The component is the one that we intend to represent, and the style flags are + a combination of the values in the StyleFlags enum + */ + ComponentPeer (Component* const component, + const int styleFlags) throw(); + + /** Destructor. */ + virtual ~ComponentPeer(); + + /** Returns the component being represented by this peer. */ + Component* getComponent() const throw() { return component; } + + /** Returns the set of style flags that were set when the window was created. + + @see Component::addToDesktop + */ + int getStyleFlags() const throw() { return styleFlags; } + + /** Returns the raw handle to whatever kind of window is being used. + + On windows, this is probably a HWND, on the mac, it's likely to be a WindowRef, + but rememeber there's no guarantees what you'll get back. + */ + virtual void* getNativeHandle() const = 0; + + /** Shows or hides the window. */ + virtual void setVisible (bool shouldBeVisible) = 0; + + /** Changes the title of the window. */ + virtual void setTitle (const String& title) = 0; + + /** Moves the window without changing its size. + + If the native window is contained in another window, then the co-ordinates are + relative to the parent window's origin, not the screen origin. + + This should result in a callback to handleMovedOrResized(). + */ + virtual void setPosition (int x, int y) = 0; + + /** Resizes the window without changing its position. + + This should result in a callback to handleMovedOrResized(). + */ + virtual void setSize (int w, int h) = 0; + + /** Moves and resizes the window. + + If the native window is contained in another window, then the co-ordinates are + relative to the parent window's origin, not the screen origin. + + This should result in a callback to handleMovedOrResized(). + */ + virtual void setBounds (int x, int y, int w, int h, const bool isNowFullScreen) = 0; + + /** Returns the current position and size of the window. + + If the native window is contained in another window, then the co-ordinates are + relative to the parent window's origin, not the screen origin. + */ + virtual void getBounds (int& x, int& y, int& w, int& h) const = 0; + + /** Returns the x-position of this window, relative to the screen's origin. */ + virtual int getScreenX() const = 0; + + /** Returns the y-position of this window, relative to the screen's origin. */ + virtual int getScreenY() const = 0; + + /** Converts a position relative to the top-left of this component to screen co-ordinates. */ + virtual void relativePositionToGlobal (int& x, int& y) = 0; + + /** Converts a screen co-ordinate to a position relative to the top-left of this component. */ + virtual void globalPositionToRelative (int& x, int& y) = 0; + + /** Minimises the window. */ + virtual void setMinimised (bool shouldBeMinimised) = 0; + + /** True if the window is currently minimised. */ + virtual bool isMinimised() const = 0; + + /** Enable/disable fullscreen mode for the window. */ + virtual void setFullScreen (bool shouldBeFullScreen) = 0; + + /** True if the window is currently full-screen. */ + virtual bool isFullScreen() const = 0; + + /** Sets the size to restore to if fullscreen mode is turned off. */ + void setNonFullScreenBounds (const Rectangle& newBounds) throw(); + + /** Returns the size to restore to if fullscreen mode is turned off. */ + const Rectangle& getNonFullScreenBounds() const throw(); + + /** Attempts to change the icon associated with this window. + */ + virtual void setIcon (const Image& newIcon) = 0; + + /** Sets a constrainer to use if the peer can resize itself. + + The constrainer won't be deleted by this object, so the caller must manage its lifetime. + */ + void setConstrainer (ComponentBoundsConstrainer* const newConstrainer) throw(); + + /** Returns the current constrainer, if one has been set. */ + ComponentBoundsConstrainer* getConstrainer() const throw() { return constrainer; } + + /** Checks if a point is in the window. + + Coordinates are relative to the top-left of this window. If trueIfInAChildWindow + is false, then this returns false if the point is actually inside a child of this + window. + */ + virtual bool contains (int x, int y, bool trueIfInAChildWindow) const = 0; + + /** Returns the size of the window frame that's around this window. + + Whether or not the window has a normal window frame depends on the flags + that were set when the window was created by Component::addToDesktop() + */ + virtual const BorderSize getFrameSize() const = 0; + + /** This is called when the window's bounds change. + + A peer implementation must call this when the window is moved and resized, so that + this method can pass the message on to the component. + */ + void handleMovedOrResized(); + + /** This is called if the screen resolution changes. + + A peer implementation must call this if the monitor arrangement changes or the available + screen size changes. + */ + void handleScreenSizeChange(); + + /** This is called to repaint the component into the given context. */ + void handlePaint (LowLevelGraphicsContext& contextToPaintTo); + + /** Sets this window to either be always-on-top or normal. + + Some kinds of window might not be able to do this, so should return false. + */ + virtual bool setAlwaysOnTop (bool alwaysOnTop) = 0; + + /** Brings the window to the top, optionally also giving it focus. */ + virtual void toFront (bool makeActive) = 0; + + /** Moves the window to be just behind another one. */ + virtual void toBehind (ComponentPeer* other) = 0; + + /** Called when the window is brought to the front, either by the OS or by a call + to toFront(). + */ + void handleBroughtToFront(); + + /** True if the window has the keyboard focus. */ + virtual bool isFocused() const = 0; + + /** Tries to give the window keyboard focus. */ + virtual void grabFocus() = 0; + + /** Called when the window gains keyboard focus. */ + void handleFocusGain(); + /** Called when the window loses keyboard focus. */ + void handleFocusLoss(); + + Component* getLastFocusedSubcomponent() const throw(); + + /** Called when a key is pressed. + + For keycode info, see the KeyPress class. + Returns true if the keystroke was used. + */ + bool handleKeyPress (const int keyCode, + const juce_wchar textCharacter); + + /** Called whenever a key is pressed or released. + Returns true if the keystroke was used. + */ + bool handleKeyUpOrDown(); + + /** Called whenever a modifier key is pressed or released. */ + void handleModifierKeysChange(); + + /** Invalidates a region of the window to be repainted asynchronously. */ + virtual void repaint (int x, int y, int w, int h) = 0; + + /** This can be called (from the message thread) to cause the immediate redrawing + of any areas of this window that need repainting. + + You shouldn't ever really need to use this, it's mainly for special purposes + like supporting audio plugins where the host's event loop is out of our control. + */ + virtual void performAnyPendingRepaintsNow() = 0; + + void handleMouseEnter (int x, int y, const int64 time); + void handleMouseMove (int x, int y, const int64 time); + void handleMouseDown (int x, int y, const int64 time); + void handleMouseDrag (int x, int y, const int64 time); + void handleMouseUp (const int oldModifiers, int x, int y, const int64 time); + void handleMouseExit (int x, int y, const int64 time); + void handleMouseWheel (const int amountX, const int amountY, const int64 time); + + /** Causes a mouse-move callback to be made asynchronously. */ + void sendFakeMouseMove() throw(); + + void handleUserClosingWindow(); + + void handleFileDragMove (const StringArray& files, int x, int y); + void handleFileDragExit (const StringArray& files); + void handleFileDragDrop (const StringArray& files, int x, int y); + + /** Resets the masking region. + + The subclass should call this every time it's about to call the handlePaint + method. + + @see addMaskedRegion + */ + void clearMaskedRegion() throw(); + + /** Adds a rectangle to the set of areas not to paint over. + + A component can call this on its peer during its paint() method, to signal + that the painting code should ignore a given region. The reason + for this is to stop embedded windows (such as OpenGL) getting painted over. + + The masked region is cleared each time before a paint happens, so a component + will have to make sure it calls this every time it's painted. + */ + void addMaskedRegion (int x, int y, int w, int h) throw(); + + /** Returns the number of currently-active peers. + + @see getPeer + */ + static int getNumPeers() throw(); + + /** Returns one of the currently-active peers. + + @see getNumPeers + */ + static ComponentPeer* getPeer (const int index) throw(); + + /** Checks if this peer object is valid. + + @see getNumPeers + */ + static bool isValidPeer (const ComponentPeer* const peer) throw(); + + juce_UseDebuggingNewOperator + +protected: + Component* const component; + const int styleFlags; + RectangleList maskedRegion; + Rectangle lastNonFullscreenBounds; + uint32 lastPaintTime; + ComponentBoundsConstrainer* constrainer; + + static void updateCurrentModifiers() throw(); + + /** @internal */ + void handleMessage (const Message& message); + +private: + + Component* lastFocusedComponent; + ComponentDeletionWatcher* dragAndDropTargetComponent; + Component* lastDragAndDropCompUnderMouse; + bool fakeMouseMessageSent : 1, isWindowMinimised : 1; + + friend class Component; + static ComponentPeer* getPeerFor (const Component* const component) throw(); + + void setLastDragDropTarget (Component* comp); + + ComponentPeer (const ComponentPeer&); + const ComponentPeer& operator= (const ComponentPeer&); +}; + +#endif // __JUCE_COMPONENTPEER_JUCEHEADER__ +/********* End of inlined file: juce_ComponentPeer.h *********/ + +class LookAndFeel; + +/** + The base class for all JUCE user-interface objects. + +*/ +class JUCE_API Component : public MouseListener, + protected MessageListener +{ +public: + + /** Creates a component. + + To get it to actually appear, you'll also need to: + - Either add it to a parent component or use the addToDesktop() method to + make it a desktop window + - Set its size and position to something sensible + - Use setVisible() to make it visible + + And for it to serve any useful purpose, you'll need to write a + subclass of Component or use one of the other types of component from + the library. + */ + Component() throw(); + + /** Destructor. + + Note that when a component is deleted, any child components it might + contain are NOT deleted unless you explicitly call deleteAllChildren() first. + */ + virtual ~Component(); + + /** Creates a component, setting its name at the same time. + + @see getName, setName + */ + Component (const String& componentName) throw(); + + /** Returns the name of this component. + + @see setName + */ + const String& getName() const throw() { return componentName_; } + + /** Sets the name of this component. + + When the name changes, all registered ComponentListeners will receive a + ComponentListener::componentNameChanged() callback. + + @see getName + */ + virtual void setName (const String& newName); + + /** Checks whether this Component object has been deleted. + + This will check whether this object is still a valid component, or whether + it's been deleted. + + It's safe to call this on null or dangling pointers, but note that there is a + small risk if another new (but different) component has been created at the + same memory address which this one occupied, this methods can return a + false positive. + */ + bool isValidComponent() const throw(); + + /** Makes the component visible or invisible. + + This method will show or hide the component. + Note that components default to being non-visible when first created. + Also note that visible components won't be seen unless all their parent components + are also visible. + + This method will call visibilityChanged() and also componentVisibilityChanged() + for any component listeners that are interested in this component. + + @param shouldBeVisible whether to show or hide the component + @see isVisible, isShowing, visibilityChanged, ComponentListener::componentVisibilityChanged + */ + virtual void setVisible (bool shouldBeVisible); + + /** Tests whether the component is visible or not. + + this doesn't necessarily tell you whether this comp is actually on the screen + because this depends on whether all the parent components are also visible - use + isShowing() to find this out. + + @see isShowing, setVisible + */ + bool isVisible() const throw() { return flags.visibleFlag; } + + /** Called when this component's visiblility changes. + + @see setVisible, isVisible + */ + virtual void visibilityChanged(); + + /** Tests whether this component and all its parents are visible. + + @returns true only if this component and all its parents are visible. + @see isVisible + */ + bool isShowing() const throw(); + + /** Makes a component invisible using a groovy fade-out and animated zoom effect. + + To do this, this function will cunningly: + - take a snapshot of the component as it currently looks + - call setVisible(false) on the component + - replace it with a special component that will continue drawing the + snapshot, animating it and gradually making it more transparent + - when it's gone, the special component will also be deleted + + As soon as this method returns, the component can be safely removed and deleted + leaving the proxy to do the fade-out, so it's even ok to call this in a + component's destructor. + + Passing non-zero x and y values will cause the ghostly component image to + also whizz off by this distance while fading out. If the scale factor is + not 1.0, it will also zoom from the component's current size to this new size. + + One thing to be careful about is that the parent component must be able to cope + with this unknown component type being added to it. + */ + void fadeOutComponent (const int lengthOfFadeOutInMilliseconds, + const int deltaXToMove = 0, + const int deltaYToMove = 0, + const float scaleFactorAtEnd = 1.0f); + + /** Makes this component appear as a window on the desktop. + + Note that before calling this, you should make sure that the component's opacity is + set correctly using setOpaque(). If the component is non-opaque, the windowing + system will try to create a special transparent window for it, which will generally take + a lot more CPU to operate (and might not even be possible on some platforms). + + If the component is inside a parent component at the time this method is called, it + will be first be removed from that parent. Likewise if a component on the desktop + is subsequently added to another component, it'll be removed from the desktop. + + @param windowStyleFlags a combination of the flags specified in the + ComponentPeer::StyleFlags enum, which define the + window's characteristics. + @param nativeWindowToAttachTo this allows an OS object to be passed-in as the window + in which the juce component should place itself. On Windows, + this would be a HWND, a HIViewRef on the Mac. Not necessarily + supported on all platforms, and best left as 0 unless you know + what you're doing + @see removeFromDesktop, isOnDesktop, userTriedToCloseWindow, + getPeer, ComponentPeer::setMinimised, ComponentPeer::StyleFlags, + ComponentPeer::getStyleFlags, ComponentPeer::setFullScreen + */ + virtual void addToDesktop (int windowStyleFlags, + void* nativeWindowToAttachTo = 0); + + /** If the component is currently showing on the desktop, this will hide it. + + You can also use setVisible() to hide a desktop window temporarily, but + removeFromDesktop() will free any system resources that are being used up. + + @see addToDesktop, isOnDesktop + */ + void removeFromDesktop(); + + /** Returns true if this component is currently showing on the desktop. + + @see addToDesktop, removeFromDesktop + */ + bool isOnDesktop() const throw(); + + /** Returns the heavyweight window that contains this component. + + If this component is itself on the desktop, this will return the window + object that it is using. Otherwise, it will return the window of + its top-level parent component. + + This may return 0 if there isn't a desktop component. + + @see addToDesktop, isOnDesktop + */ + ComponentPeer* getPeer() const throw(); + + /** For components on the desktop, this is called if the system wants to close the window. + + This is a signal that either the user or the system wants the window to close. The + default implementation of this method will trigger an assertion to warn you that your + component should do something about it, but you can override this to ignore the event + if you want. + */ + virtual void userTriedToCloseWindow(); + + /** Called for a desktop component which has just been minimised or un-minimised. + + This will only be called for components on the desktop. + + @see getPeer, ComponentPeer::setMinimised, ComponentPeer::isMinimised + */ + virtual void minimisationStateChanged (bool isNowMinimised); + + /** Brings the component to the front of its siblings. + + If some of the component's siblings have had their 'always-on-top' flag set, + then they will still be kept in front of this one (unless of course this + one is also 'always-on-top'). + + @param shouldAlsoGainFocus if true, this will also try to assign keyboard focus + to the component (see grabKeyboardFocus() for more details) + @see toBack, toBehind, setAlwaysOnTop + */ + void toFront (const bool shouldAlsoGainFocus); + + /** Changes this component's z-order to be at the back of all its siblings. + + If the component is set to be 'always-on-top', it will only be moved to the + back of the other other 'always-on-top' components. + + @see toFront, toBehind, setAlwaysOnTop + */ + void toBack(); + + /** Changes this component's z-order so that it's just behind another component. + + @see toFront, toBack + */ + void toBehind (Component* const other); + + /** Sets whether the component should always be kept at the front of its siblings. + + @see isAlwaysOnTop + */ + void setAlwaysOnTop (const bool shouldStayOnTop); + + /** Returns true if this component is set to always stay in front of its siblings. + + @see setAlwaysOnTop + */ + bool isAlwaysOnTop() const throw(); + + /** Returns the x co-ordinate of the component's left edge. + + This is a distance in pixels from the left edge of the component's parent. + + @see getScreenX + */ + inline int getX() const throw() { return bounds_.getX(); } + + /** Returns the y co-ordinate of the top of this component. + + This is a distance in pixels from the top edge of the component's parent. + + @see getScreenY + */ + inline int getY() const throw() { return bounds_.getY(); } + + /** Returns the component's width in pixels. */ + inline int getWidth() const throw() { return bounds_.getWidth(); } + + /** Returns the component's height in pixels. */ + inline int getHeight() const throw() { return bounds_.getHeight(); } + + /** Returns the x co-ordinate of the component's right-hand edge. + + This is a distance in pixels from the left edge of the component's parent. + */ + int getRight() const throw() { return bounds_.getRight(); } + + /** Returns the y co-ordinate of the bottom edge of this component. + + This is a distance in pixels from the top edge of the component's parent. + */ + int getBottom() const throw() { return bounds_.getBottom(); } + + /** Returns this component's bounding box. + + The rectangle returned is relative to the top-left of the component's parent. + */ + const Rectangle& getBounds() const throw() { return bounds_; } + + /** Returns the region of this component that's not obscured by other, opaque components. + + The RectangleList that is returned represents the area of this component + which isn't covered by opaque child components. + + If includeSiblings is true, it will also take into account any siblings + that may be overlapping the component. + */ + void getVisibleArea (RectangleList& result, + const bool includeSiblings) const; + + /** Returns this component's x co-ordinate relative the the screen's top-left origin. + + @see getX, relativePositionToGlobal + */ + int getScreenX() const throw(); + + /** Returns this component's y co-ordinate relative the the screen's top-left origin. + + @see getY, relativePositionToGlobal + */ + int getScreenY() const throw(); + + /** Converts a position relative to this component's top-left into a screen co-ordinate. + + @see globalPositionToRelative, relativePositionToOtherComponent + */ + void relativePositionToGlobal (int& x, int& y) const throw(); + + /** Converts a screen co-ordinate into a position relative to this component's top-left. + + @see relativePositionToGlobal, relativePositionToOtherComponent + */ + void globalPositionToRelative (int& x, int& y) const throw(); + + /** Converts a position relative to this component's top-left into a position + relative to another component's top-left. + + @see relativePositionToGlobal, globalPositionToRelative + */ + void relativePositionToOtherComponent (const Component* const targetComponent, + int& x, int& y) const throw(); + + /** Moves the component to a new position. + + Changes the component's top-left position (without changing its size). + The position is relative to the top-left of the component's parent. + + If the component actually moves, this method will make a synchronous call to moved(). + + @see setBounds, ComponentListener::componentMovedOrResized + */ + void setTopLeftPosition (const int x, const int y); + + /** Moves the component to a new position. + + Changes the position of the component's top-right corner (keeping it the same size). + The position is relative to the top-left of the component's parent. + + If the component actually moves, this method will make a synchronous call to moved(). + */ + void setTopRightPosition (const int x, const int y); + + /** Changes the size of the component. + + A synchronous call to resized() will be occur if the size actually changes. + */ + void setSize (const int newWidth, const int newHeight); + + /** Changes the component's position and size. + + The co-ordinates are relative to the top-left of the component's parent, or relative + to the origin of the screen is the component is on the desktop. + + If this method changes the component's top-left position, it will make a synchronous + call to moved(). If it changes the size, it will also make a call to resized(). + + @see setTopLeftPosition, setSize, ComponentListener::componentMovedOrResized + */ + void setBounds (int x, int y, int width, int height); + + /** Changes the component's position and size. + + @see setBounds + */ + void setBounds (const Rectangle& newBounds); + + /** Changes the component's position and size in terms of fractions of its parent's size. + + The values are factors of the parent's size, so for example + setBoundsRelative (0.2f, 0.2f, 0.5f, 0.5f) would give it half the + width and height of the parent, with its top-left position 20% of + the way across and down the parent. + */ + void setBoundsRelative (const float proportionalX, const float proportionalY, + const float proportionalWidth, const float proportionalHeight); + + /** Changes the component's position and size based on the amount of space to leave around it. + + This will position the component within its parent, leaving the specified number of + pixels around each edge. + */ + void setBoundsInset (const BorderSize& borders); + + /** Positions the component within a given rectangle, keeping its proportions + unchanged. + + If onlyReduceInSize is false, the component will be resized to fill as much of the + rectangle as possible without changing its aspect ratio (the component's + current size is used to determine its aspect ratio, so a zero-size component + won't work here). If onlyReduceInSize is true, it will only be resized if it's + too big to fit inside the rectangle. + + It will then be positioned within the rectangle according to the justification flags + specified. + */ + void setBoundsToFit (int x, int y, int width, int height, + const Justification& justification, + const bool onlyReduceInSize); + + /** Changes the position of the component's centre. + + Leaves the component's size unchanged, but sets the position of its centre + relative to its parent's top-left. + */ + void setCentrePosition (const int x, const int y); + + /** Changes the position of the component's centre. + + Leaves the position unchanged, but positions its centre relative to its + parent's size. E.g. setCentreRelative (0.5f, 0.5f) would place it centrally in + its parent. + */ + void setCentreRelative (const float x, const float y); + + /** Changes the component's size and centres it within its parent. + + After changing the size, the component will be moved so that it's + centred within its parent. + */ + void centreWithSize (const int width, const int height); + + /** Returns a proportion of the component's width. + + This is a handy equivalent of (getWidth() * proportion). + */ + int proportionOfWidth (const float proportion) const throw(); + + /** Returns a proportion of the component's height. + + This is a handy equivalent of (getHeight() * proportion). + */ + int proportionOfHeight (const float proportion) const throw(); + + /** Returns the width of the component's parent. + + If the component has no parent (i.e. if it's on the desktop), this will return + the width of the screen. + */ + int getParentWidth() const throw(); + + /** Returns the height of the component's parent. + + If the component has no parent (i.e. if it's on the desktop), this will return + the height of the screen. + */ + int getParentHeight() const throw(); + + /** Returns the screen co-ordinates of the monitor that contains this component. + + If there's only one monitor, this will return its size - if there are multiple + monitors, it will return the area of the monitor that contains the component's + centre. + */ + const Rectangle getParentMonitorArea() const throw(); + + /** Returns the number of child components that this component contains. + + @see getChildComponent, getIndexOfChildComponent + */ + int getNumChildComponents() const throw(); + + /** Returns one of this component's child components, by it index. + + The component with index 0 is at the back of the z-order, the one at the + front will have index (getNumChildComponents() - 1). + + If the index is out-of-range, this will return a null pointer. + + @see getNumChildComponents, getIndexOfChildComponent + */ + Component* getChildComponent (const int index) const throw(); + + /** Returns the index of this component in the list of child components. + + A value of 0 means it is first in the list (i.e. behind all other components). Higher + values are further towards the front. + + Returns -1 if the component passed-in is not a child of this component. + + @see getNumChildComponents, getChildComponent, addChildComponent, toFront, toBack, toBehind + */ + int getIndexOfChildComponent (const Component* const child) const throw(); + + /** Adds a child component to this one. + + @param child the new component to add. If the component passed-in is already + the child of another component, it'll first be removed from that. + + @param zOrder The index in the child-list at which this component should be inserted. + A value of -1 will insert it in front of the others, 0 is the back. + @see removeChildComponent, addAndMakeVisible, getChild, + ComponentListener::componentChildrenChanged + */ + void addChildComponent (Component* const child, + int zOrder = -1); + + /** Adds a child component to this one, and also makes the child visible if it isn't. + + Quite a useful function, this is just the same as calling addChildComponent() + followed by setVisible (true) on the child. + */ + void addAndMakeVisible (Component* const child, + int zOrder = -1); + + /** Removes one of this component's child-components. + + If the child passed-in isn't actually a child of this component (either because + it's invalid or is the child of a different parent), then nothing is done. + + Note that removing a child will not delete it! + + @see addChildComponent, ComponentListener::componentChildrenChanged + */ + void removeChildComponent (Component* const childToRemove); + + /** Removes one of this component's child-components by index. + + This will return a pointer to the component that was removed, or null if + the index was out-of-range. + + Note that removing a child will not delete it! + + @see addChildComponent, ComponentListener::componentChildrenChanged + */ + Component* removeChildComponent (const int childIndexToRemove); + + /** Removes all this component's children. + + Note that this won't delete them! To do that, use deleteAllChildren() instead. + */ + void removeAllChildren(); + + /** Removes all this component's children, and deletes them. + + @see removeAllChildren + */ + void deleteAllChildren(); + + /** Returns the component which this component is inside. + + If this is the highest-level component or hasn't yet been added to + a parent, this will return null. + */ + Component* getParentComponent() const throw() { return parentComponent_; } + + /** Searches the parent components for a component of a specified class. + + For example findParentComponentOfClass \() would return the first parent + component that can be dynamically cast to a MyComp, or will return 0 if none + of the parents are suitable. + + N.B. The dummy parameter is needed to work around a VC6 compiler bug. + */ + template + TargetClass* findParentComponentOfClass (TargetClass* const dummyParameter = 0) const + { + (void) dummyParameter; + Component* p = parentComponent_; + while (p != 0) + { + TargetClass* target = dynamic_cast (p); + if (target != 0) + return target; + + p = p->parentComponent_; + } + + return 0; + } + + /** Returns the highest-level component which contains this one or its parents. + + This will search upwards in the parent-hierarchy from this component, until it + finds the highest one that doesn't have a parent (i.e. is on the desktop or + not yet added to a parent), and will return that. + */ + Component* getTopLevelComponent() const throw(); + + /** Checks whether a component is anywhere inside this component or its children. + + This will recursively check through this components children to see if the + given component is anywhere inside. + */ + bool isParentOf (const Component* possibleChild) const throw(); + + /** Called to indicate that the component's parents have changed. + + When a component is added or removed from its parent, this method will + be called on all of its children (recursively - so all children of its + children will also be called as well). + + Subclasses can override this if they need to react to this in some way. + + @see getParentComponent, isShowing, ComponentListener::componentParentHierarchyChanged + */ + virtual void parentHierarchyChanged(); + + /** Subclasses can use this callback to be told when children are added or removed. + + @see parentHierarchyChanged + */ + virtual void childrenChanged(); + + /** Tests whether a given point inside the component. + + Overriding this method allows you to create components which only intercept + mouse-clicks within a user-defined area. + + This is called to find out whether a particular x, y co-ordinate is + considered to be inside the component or not, and is used by methods such + as contains() and getComponentAt() to work out which component + the mouse is clicked on. + + Components with custom shapes will probably want to override it to perform + some more complex hit-testing. + + The default implementation of this method returns either true or false, + depending on the value that was set by calling setInterceptsMouseClicks() (true + is the default return value). + + Note that the hit-test region is not related to the opacity with which + areas of a component are painted. + + Applications should never call hitTest() directly - instead use the + contains() method, because this will also test for occlusion by the + component's parent. + + Note that for components on the desktop, this method will be ignored, because it's + not always possible to implement this behaviour on all platforms. + + @param x the x co-ordinate to test, relative to the left hand edge of this + component. This value is guaranteed to be greater than or equal to + zero, and less than the component's width + @param y the y co-ordinate to test, relative to the top edge of this + component. This value is guaranteed to be greater than or equal to + zero, and less than the component's height + @returns true if the click is considered to be inside the component + @see setInterceptsMouseClicks, contains + */ + virtual bool hitTest (int x, int y); + + /** Changes the default return value for the hitTest() method. + + Setting this to false is an easy way to make a component pass its mouse-clicks + through to the components behind it. + + When a component is created, the default setting for this is true. + + @param allowClicksOnThisComponent if true, hitTest() will always return true; if false, it will + return false (or true for child components if allowClicksOnChildComponents + is true) + @param allowClicksOnChildComponents if this is true and allowClicksOnThisComponent is false, then child + components can be clicked on as normal but clicks on this component pass + straight through; if this is false and allowClicksOnThisComponent + is false, then neither this component nor any child components can + be clicked on + @see hitTest, getInterceptsMouseClicks + */ + void setInterceptsMouseClicks (const bool allowClicksOnThisComponent, + const bool allowClicksOnChildComponents) throw(); + + /** Retrieves the current state of the mouse-click interception flags. + + On return, the two parameters are set to the state used in the last call to + setInterceptsMouseClicks(). + + @see setInterceptsMouseClicks + */ + void getInterceptsMouseClicks (bool& allowsClicksOnThisComponent, + bool& allowsClicksOnChildComponents) const throw(); + + /** Returns true if a given point lies within this component or one of its children. + + Never override this method! Use hitTest to create custom hit regions. + + @param x the x co-ordinate to test, relative to this component's left hand edge. + @param y the y co-ordinate to test, relative to this component's top edge. + @returns true if the point is within the component's hit-test area, but only if + that part of the component isn't clipped by its parent component. Note + that this won't take into account any overlapping sibling components + which might be in the way - for that, see reallyContains() + @see hitTest, reallyContains, getComponentAt + */ + virtual bool contains (int x, int y); + + /** Returns true if a given point lies in this component, taking any overlapping + siblings into account. + + @param x the x co-ordinate to test, relative to this component's left hand edge. + @param y the y co-ordinate to test, relative to this component's top edge. + @param returnTrueIfWithinAChild if the point actually lies within a child of this + component, this determines the value that will + be returned. + + @see contains, getComponentAt + */ + bool reallyContains (int x, int y, + const bool returnTrueIfWithinAChild); + + /** Returns the component at a certain point within this one. + + @param x the x co-ordinate to test, relative to this component's left hand edge. + @param y the y co-ordinate to test, relative to this component's top edge. + @returns the component that is at this position - which may be 0, this component, + or one of its children. Note that overlapping siblings that might actually + be in the way are not taken into account by this method - to account for these, + instead call getComponentAt on the top-level parent of this component. + @see hitTest, contains, reallyContains + */ + Component* getComponentAt (const int x, const int y); + + /** Marks the whole component as needing to be redrawn. + + Calling this will not do any repainting immediately, but will mark the component + as 'dirty'. At some point in the near future the operating system will send a paint + message, which will redraw all the dirty regions of all components. + There's no guarantee about how soon after calling repaint() the redraw will actually + happen, and other queued events may be delivered before a redraw is done. + + If the setBufferedToImage() method has been used to cause this component + to use a buffer, the repaint() call will invalidate the component's buffer. + + To redraw just a subsection of the component rather than the whole thing, + use the repaint (int, int, int, int) method. + + @see paint + */ + void repaint() throw(); + + /** Marks a subsection of this component as needing to be redrawn. + + Calling this will not do any repainting immediately, but will mark the given region + of the component as 'dirty'. At some point in the near future the operating system + will send a paint message, which will redraw all the dirty regions of all components. + There's no guarantee about how soon after calling repaint() the redraw will actually + happen, and other queued events may be delivered before a redraw is done. + + The region that is passed in will be clipped to keep it within the bounds of this + component. + + @see repaint() + */ + void repaint (const int x, const int y, + const int width, const int height) throw(); + + /** Makes the component use an internal buffer to optimise its redrawing. + + Setting this flag to true will cause the component to allocate an + internal buffer into which it paints itself, so that when asked to + redraw itself, it can use this buffer rather than actually calling the + paint() method. + + The buffer is kept until the repaint() method is called directly on + this component (or until it is resized), when the image is invalidated + and then redrawn the next time the component is painted. + + Note that only the drawing that happens within the component's paint() + method is drawn into the buffer, it's child components are not buffered, and + nor is the paintOverChildren() method. + + @see repaint, paint, createComponentSnapshot + */ + void setBufferedToImage (const bool shouldBeBuffered) throw(); + + /** Generates a snapshot of part of this component. + + This will return a new Image, the size of the rectangle specified, + containing a snapshot of the specified area of the component and all + its children. + + The image may or may not have an alpha-channel, depending on whether the + image is opaque or not. + + If the clipImageToComponentBounds parameter is true and the area is greater than + the size of the component, it'll be clipped. If clipImageToComponentBounds is false + then parts of the component beyond its bounds can be drawn. + + The caller is responsible for deleting the image that is returned. + + @see paintEntireComponent + */ + Image* createComponentSnapshot (const Rectangle& areaToGrab, + const bool clipImageToComponentBounds = true); + + /** Draws this component and all its subcomponents onto the specified graphics + context. + + You should very rarely have to use this method, it's simply there in case you need + to draw a component with a custom graphics context for some reason, e.g. for + creating a snapshot of the component. + + It calls paint(), paintOverChildren() and recursively calls paintEntireComponent() + on its children in order to render the entire tree. + + The graphics context may be left in an undefined state after this method returns, + so you may need to reset it if you're going to use it again. + */ + void paintEntireComponent (Graphics& context); + + /** Adds an effect filter to alter the component's appearance. + + When a component has an effect filter set, then this is applied to the + results of its paint() method. There are a few preset effects, such as + a drop-shadow or glow, but they can be user-defined as well. + + The effect that is passed in will not be deleted by the component - the + caller must take care of deleting it. + + To remove an effect from a component, pass a null pointer in as the parameter. + + @see ImageEffectFilter, DropShadowEffect, GlowEffect + */ + void setComponentEffect (ImageEffectFilter* const newEffect); + + /** Returns the current component effect. + + @see setComponentEffect + */ + ImageEffectFilter* getComponentEffect() const throw() { return effect_; } + + /** Finds the appropriate look-and-feel to use for this component. + + If the component hasn't had a look-and-feel explicitly set, this will + return the parent's look-and-feel, or just the default one if there's no + parent. + + @see setLookAndFeel, lookAndFeelChanged + */ + LookAndFeel& getLookAndFeel() const throw(); + + /** Sets the look and feel to use for this component. + + This will also change the look and feel for any child components that haven't + had their look set explicitly. + + The object passed in will not be deleted by the component, so it's the caller's + responsibility to manage it. It may be used at any time until this component + has been deleted. + + Calling this method will also invoke the sendLookAndFeelChange() method. + + @see getLookAndFeel, lookAndFeelChanged + */ + void setLookAndFeel (LookAndFeel* const newLookAndFeel); + + /** Called to let the component react to a change in the look-and-feel setting. + + When the look-and-feel is changed for a component, this will be called in + all its child components, recursively. + + It can also be triggered manually by the sendLookAndFeelChange() method, in case + an application uses a LookAndFeel class that might have changed internally. + + @see sendLookAndFeelChange, getLookAndFeel + */ + virtual void lookAndFeelChanged(); + + /** Calls the lookAndFeelChanged() method in this component and all its children. + + This will recurse through the children and their children, calling lookAndFeelChanged() + on them all. + + @see lookAndFeelChanged + */ + void sendLookAndFeelChange(); + + /** Indicates whether any parts of the component might be transparent. + + Components that always paint all of their contents with solid colour and + thus completely cover any components behind them should use this method + to tell the repaint system that they are opaque. + + This information is used to optimise drawing, because it means that + objects underneath opaque windows don't need to be painted. + + By default, components are considered transparent, unless this is used to + make it otherwise. + + @see isOpaque, getVisibleArea + */ + void setOpaque (const bool shouldBeOpaque) throw(); + + /** Returns true if no parts of this component are transparent. + + @returns the value that was set by setOpaque, (the default being false) + @see setOpaque + */ + bool isOpaque() const throw(); + + /** Indicates whether the component should be brought to the front when clicked. + + Setting this flag to true will cause the component to be brought to the front + when the mouse is clicked somewhere inside it or its child components. + + Note that a top-level desktop window might still be brought to the front by the + operating system when it's clicked, depending on how the OS works. + + By default this is set to false. + + @see setMouseClickGrabsKeyboardFocus + */ + void setBroughtToFrontOnMouseClick (const bool shouldBeBroughtToFront) throw(); + + /** Indicates whether the component should be brought to the front when clicked-on. + + @see setBroughtToFrontOnMouseClick + */ + bool isBroughtToFrontOnMouseClick() const throw(); + + // Keyboard focus methods + + /** Sets a flag to indicate whether this component needs keyboard focus or not. + + By default components aren't actually interested in gaining the + focus, but this method can be used to turn this on. + + See the grabKeyboardFocus() method for details about the way a component + is chosen to receive the focus. + + @see grabKeyboardFocus, getWantsKeyboardFocus + */ + void setWantsKeyboardFocus (const bool wantsFocus) throw(); + + /** Returns true if the component is interested in getting keyboard focus. + + This returns the flag set by setWantsKeyboardFocus(). The default + setting is false. + + @see setWantsKeyboardFocus + */ + bool getWantsKeyboardFocus() const throw(); + + /** Chooses whether a click on this component automatically grabs the focus. + + By default this is set to true, but you might want a component which can + be focused, but where you don't want the user to be able to affect it directly + by clicking. + */ + void setMouseClickGrabsKeyboardFocus (const bool shouldGrabFocus); + + /** Returns the last value set with setMouseClickGrabsKeyboardFocus(). + + See setMouseClickGrabsKeyboardFocus() for more info. + */ + bool getMouseClickGrabsKeyboardFocus() const throw(); + + /** Tries to give keyboard focus to this component. + + When the user clicks on a component or its grabKeyboardFocus() + method is called, the following procedure is used to work out which + component should get it: + + - if the component that was clicked on actually wants focus (as indicated + by calling getWantsKeyboardFocus), it gets it. + - if the component itself doesn't want focus, it will try to pass it + on to whichever of its children is the default component, as determined by + KeyboardFocusTraverser::getDefaultComponent() + - if none of its children want focus at all, it will pass it up to its + parent instead, unless it's a top-level component without a parent, + in which case it just takes the focus itself. + + @see setWantsKeyboardFocus, getWantsKeyboardFocus, hasKeyboardFocus, + getCurrentlyFocusedComponent, focusGained, focusLost, + keyPressed, keyStateChanged + */ + void grabKeyboardFocus(); + + /** Returns true if this component currently has the keyboard focus. + + @param trueIfChildIsFocused if this is true, then the method returns true if + either this component or any of its children (recursively) + have the focus. If false, the method only returns true if + this component has the focus. + + @see grabKeyboardFocus, setWantsKeyboardFocus, getCurrentlyFocusedComponent, + focusGained, focusLost + */ + bool hasKeyboardFocus (const bool trueIfChildIsFocused) const throw(); + + /** Returns the component that currently has the keyboard focus. + + @returns the focused component, or null if nothing is focused. + */ + static Component* JUCE_CALLTYPE getCurrentlyFocusedComponent() throw(); + + /** Tries to move the keyboard focus to one of this component's siblings. + + This will try to move focus to either the next or previous component. (This + is the method that is used when shifting focus by pressing the tab key). + + Components for which getWantsKeyboardFocus() returns false are not looked at. + + @param moveToNext if true, the focus will move forwards; if false, it will + move backwards + @see grabKeyboardFocus, setFocusContainer, setWantsKeyboardFocus + */ + void moveKeyboardFocusToSibling (const bool moveToNext); + + /** Creates a KeyboardFocusTraverser object to use to determine the logic by + which focus should be passed from this component. + + The default implementation of this method will return a default + KeyboardFocusTraverser if this component is a focus container (as determined + by the setFocusContainer() method). If the component isn't a focus + container, then it will recursively ask its parents for a KeyboardFocusTraverser. + + If you overrride this to return a custom KeyboardFocusTraverser, then + this component and all its sub-components will use the new object to + make their focusing decisions. + + The method should return a new object, which the caller is required to + delete when no longer needed. + */ + virtual KeyboardFocusTraverser* createFocusTraverser(); + + /** Returns the focus order of this component, if one has been specified. + + By default components don't have a focus order - in that case, this + will return 0. Lower numbers indicate that the component will be + earlier in the focus traversal order. + + To change the order, call setExplicitFocusOrder(). + + The focus order may be used by the KeyboardFocusTraverser class as part of + its algorithm for deciding the order in which components should be traversed. + See the KeyboardFocusTraverser class for more details on this. + + @see moveKeyboardFocusToSibling, createFocusTraverser, KeyboardFocusTraverser + */ + int getExplicitFocusOrder() const throw(); + + /** Sets the index used in determining the order in which focusable components + should be traversed. + + A value of 0 or less is taken to mean that no explicit order is wanted, and + that traversal should use other factors, like the component's position. + + @see getExplicitFocusOrder, moveKeyboardFocusToSibling + */ + void setExplicitFocusOrder (const int newFocusOrderIndex) throw(); + + /** Indicates whether this component is a parent for components that can have + their focus traversed. + + This flag is used by the default implementation of the createFocusTraverser() + method, which uses the flag to find the first parent component (of the currently + focused one) which wants to be a focus container. + + So using this method to set the flag to 'true' causes this component to + act as the top level within which focus is passed around. + + @see isFocusContainer, createFocusTraverser, moveKeyboardFocusToSibling + */ + void setFocusContainer (const bool isFocusContainer) throw(); + + /** Returns true if this component has been marked as a focus container. + + See setFocusContainer() for more details. + + @see setFocusContainer, moveKeyboardFocusToSibling, createFocusTraverser + */ + bool isFocusContainer() const throw(); + + /** Returns true if the component (and all its parents) are enabled. + + Components are enabled by default, and can be disabled with setEnabled(). Exactly + what difference this makes to the component depends on the type. E.g. buttons + and sliders will choose to draw themselves differently, etc. + + Note that if one of this component's parents is disabled, this will always + return false, even if this component itself is enabled. + + @see setEnabled, enablementChanged + */ + bool isEnabled() const throw(); + + /** Enables or disables this component. + + Disabling a component will also cause all of its child components to become + disabled. + + Similarly, enabling a component which is inside a disabled parent + component won't make any difference until the parent is re-enabled. + + @see isEnabled, enablementChanged + */ + void setEnabled (const bool shouldBeEnabled); + + /** Callback to indicate that this component has been enabled or disabled. + + This can be triggered by one of the component's parent components + being enabled or disabled, as well as changes to the component itself. + + The default implementation of this method does nothing; your class may + wish to repaint itself or something when this happens. + + @see setEnabled, isEnabled + */ + virtual void enablementChanged(); + + /** Changes the mouse cursor shape to use when the mouse is over this component. + + Note that the cursor set by this method can be overridden by the getMouseCursor + method. + + @see MouseCursor + */ + void setMouseCursor (const MouseCursor& cursorType) throw(); + + /** Returns the mouse cursor shape to use when the mouse is over this component. + + The default implementation will return the cursor that was set by setCursor() + but can be overridden for more specialised purposes, e.g. returning different + cursors depending on the mouse position. + + @see MouseCursor + */ + virtual const MouseCursor getMouseCursor(); + + /** Forces the current mouse cursor to be updated. + + If you're overriding the getMouseCursor() method to control which cursor is + displayed, then this will only be checked each time the user moves the mouse. So + if you want to force the system to check that the cursor being displayed is + up-to-date (even if the mouse is just sitting there), call this method. + + This isn't needed if you're only using setMouseCursor(). + */ + void updateMouseCursor() const throw(); + + /** Components can override this method to draw their content. + + The paint() method gets called when a region of a component needs redrawing, + either because the component's repaint() method has been called, or because + something has happened on the screen that means a section of a window needs + to be redrawn. + + Any child components will draw themselves over whatever this method draws. If + you need to paint over the top of your child components, you can also implement + the paintOverChildren() method to do this. + + If you want to cause a component to redraw itself, this is done asynchronously - + calling the repaint() method marks a region of the component as "dirty", and the + paint() method will automatically be called sometime later, by the message thread, + to paint any bits that need refreshing. In Juce (and almost all modern UI frameworks), + you never redraw something synchronously. + + You should never need to call this method directly - to take a snapshot of the + component you could use createComponentSnapshot() or paintEntireComponent(). + + @param g the graphics context that must be used to do the drawing operations. + @see repaint, paintOverChildren, Graphics + */ + virtual void paint (Graphics& g); + + /** Components can override this method to draw over the top of their children. + + For most drawing operations, it's better to use the normal paint() method, + but if you need to overlay something on top of the children, this can be + used. + + @see paint, Graphics + */ + virtual void paintOverChildren (Graphics& g); + + /** Called when the mouse moves inside this component. + + If the mouse button isn't pressed and the mouse moves over a component, + this will be called to let the component react to this. + + A component will always get a mouseEnter callback before a mouseMove. + + @param e details about the position and status of the mouse event + @see mouseEnter, mouseExit, mouseDrag, contains + */ + virtual void mouseMove (const MouseEvent& e); + + /** Called when the mouse first enters this component. + + If the mouse button isn't pressed and the mouse moves into a component, + this will be called to let the component react to this. + + When the mouse button is pressed and held down while being moved in + or out of a component, no mouseEnter or mouseExit callbacks are made - only + mouseDrag messages are sent to the component that the mouse was originally + clicked on, until the button is released. + + If you're writing a component that needs to repaint itself when the mouse + enters and exits, it might be quicker to use the setRepaintsOnMouseActivity() + method. + + @param e details about the position and status of the mouse event + @see mouseExit, mouseDrag, mouseMove, contains + */ + virtual void mouseEnter (const MouseEvent& e); + + /** Called when the mouse moves out of this component. + + This will be called when the mouse moves off the edge of this + component. + + If the mouse button was pressed, and it was then dragged off the + edge of the component and released, then this callback will happen + when the button is released, after the mouseUp callback. + + If you're writing a component that needs to repaint itself when the mouse + enters and exits, it might be quicker to use the setRepaintsOnMouseActivity() + method. + + @param e details about the position and status of the mouse event + @see mouseEnter, mouseDrag, mouseMove, contains + */ + virtual void mouseExit (const MouseEvent& e); + + /** Called when a mouse button is pressed while it's over this component. + + The MouseEvent object passed in contains lots of methods for finding out + which button was pressed, as well as which modifier keys (e.g. shift, ctrl) + were held down at the time. + + Once a button is held down, the mouseDrag method will be called when the + mouse moves, until the button is released. + + @param e details about the position and status of the mouse event + @see mouseUp, mouseDrag, mouseDoubleClick, contains + */ + virtual void mouseDown (const MouseEvent& e); + + /** Called when the mouse is moved while a button is held down. + + When a mouse button is pressed inside a component, that component + receives mouseDrag callbacks each time the mouse moves, even if the + mouse strays outside the component's bounds. + + If you want to be able to drag things off the edge of a component + and have the component scroll when you get to the edges, the + beginDragAutoRepeat() method might be useful. + + @param e details about the position and status of the mouse event + @see mouseDown, mouseUp, mouseMove, contains, beginDragAutoRepeat + */ + virtual void mouseDrag (const MouseEvent& e); + + /** Called when a mouse button is released. + + A mouseUp callback is sent to the component in which a button was pressed + even if the mouse is actually over a different component when the + button is released. + + The MouseEvent object passed in contains lots of methods for finding out + which buttons were down just before they were released. + + @param e details about the position and status of the mouse event + @see mouseDown, mouseDrag, mouseDoubleClick, contains + */ + virtual void mouseUp (const MouseEvent& e); + + /** Called when a mouse button has been double-clicked in this component. + + The MouseEvent object passed in contains lots of methods for finding out + which button was pressed, as well as which modifier keys (e.g. shift, ctrl) + were held down at the time. + + For altering the time limit used to detect double-clicks, + see MouseEvent::setDoubleClickTimeout. + + @param e details about the position and status of the mouse event + @see mouseDown, mouseUp, MouseEvent::setDoubleClickTimeout, + MouseEvent::getDoubleClickTimeout + */ + virtual void mouseDoubleClick (const MouseEvent& e); + + /** Called when the mouse-wheel is moved. + + This callback is sent to the component that the mouse is over when the + wheel is moved. + + If not overridden, the component will forward this message to its parent, so + that parent components can collect mouse-wheel messages that happen to + child components which aren't interested in them. + + @param e details about the position and status of the mouse event + @param wheelIncrementX the speed and direction of the horizontal scroll-wheel - a positive + value means the wheel has been pushed to the right, negative means it + was pushed to the left + @param wheelIncrementY the speed and direction of the vertical scroll-wheel - a positive + value means the wheel has been pushed upwards, negative means it + was pushed downwards + */ + virtual void mouseWheelMove (const MouseEvent& e, + float wheelIncrementX, + float wheelIncrementY); + + /** Ensures that a non-stop stream of mouse-drag events will be sent during the + next mouse-drag operation. + + This allows you to make sure that mouseDrag() events sent continuously, even + when the mouse isn't moving. This can be useful for things like auto-scrolling + components when the mouse is near an edge. + + Call this method during a mouseDown() or mouseDrag() callback, specifying the + minimum interval between consecutive mouse drag callbacks. The callbacks + will continue until the mouse is released, and then the interval will be reset, + so you need to make sure it's called every time you begin a drag event. If it + is called when the mouse isn't actually being pressed, it will apply to the next + mouse-drag operation that happens. + + Passing an interval of 0 or less will cancel the auto-repeat. + + @see mouseDrag + */ + static void beginDragAutoRepeat (const int millisecondIntervalBetweenCallbacks); + + /** Causes automatic repaints when the mouse enters or exits this component. + + If turned on, then when the mouse enters/exits, or when the button is pressed/released + on the component, it will trigger a repaint. + + This is handy for things like buttons that need to draw themselves differently when + the mouse moves over them, and it avoids having to override all the different mouse + callbacks and call repaint(). + + @see mouseEnter, mouseExit, mouseDown, mouseUp + */ + void setRepaintsOnMouseActivity (const bool shouldRepaint) throw(); + + /** Registers a listener to be told when mouse events occur in this component. + + If you need to get informed about mouse events in a component but can't or + don't want to override its methods, you can attach any number of listeners + to the component, and these will get told about the events in addition to + the component's own callbacks being called. + + Note that a MouseListener can also be attached to more than one component. + + @param newListener the listener to register + @param wantsEventsForAllNestedChildComponents if true, the listener will receive callbacks + for events that happen to any child component + within this component, including deeply-nested + child components. If false, it will only be + told about events that this component handles. + @see MouseListener, removeMouseListener + */ + void addMouseListener (MouseListener* const newListener, + const bool wantsEventsForAllNestedChildComponents) throw(); + + /** Deregisters a mouse listener. + + @see addMouseListener, MouseListener + */ + void removeMouseListener (MouseListener* const listenerToRemove) throw(); + + /** Adds a listener that wants to hear about keypresses that this component receives. + + The listeners that are registered with a component are called by its keyPressed() or + keyStateChanged() methods (assuming these haven't been overridden to do something else). + + If you add an object as a key listener, be careful to remove it when the object + is deleted, or the component will be left with a dangling pointer. + + @see keyPressed, keyStateChanged, removeKeyListener + */ + void addKeyListener (KeyListener* const newListener) throw(); + + /** Removes a previously-registered key listener. + + @see addKeyListener + */ + void removeKeyListener (KeyListener* const listenerToRemove) throw(); + + /** Called when a key is pressed. + + When a key is pressed, the component that has the keyboard focus will have this + method called. Remember that a component will only be given the focus if its + setWantsKeyboardFocus() method has been used to enable this. + + If your implementation returns true, the event will be consumed and not passed + on to any other listeners. If it returns false, the key will be passed to any + KeyListeners that have been registered with this component. As soon as one of these + returns true, the process will stop, but if they all return false, the event will + be passed upwards to this component's parent, and so on. + + The default implementation of this method does nothing and returns false. + + @see keyStateChanged, getCurrentlyFocusedComponent, addKeyListener + */ + virtual bool keyPressed (const KeyPress& key); + + /** Called when a key is pressed or released. + + Whenever a key on the keyboard is pressed or released (including modifier keys + like shift and ctrl), this method will be called on the component that currently + has the keyboard focus. Remember that a component will only be given the focus if + its setWantsKeyboardFocus() method has been used to enable this. + + If your implementation returns true, the event will be consumed and not passed + on to any other listeners. If it returns false, then any KeyListeners that have + been registered with this component will have their keyStateChanged methods called. + As soon as one of these returns true, the process will stop, but if they all return + false, the event will be passed upwards to this component's parent, and so on. + + The default implementation of this method does nothing and returns false. + + To find out which keys are up or down at any time, see the KeyPress::isKeyCurrentlyDown() + method. + + @see keyPressed, KeyPress, getCurrentlyFocusedComponent, addKeyListener + */ + virtual bool keyStateChanged(); + + /** Called when a modifier key is pressed or released. + + Whenever the shift, control, alt or command keys are pressed or released, + this method will be called on the component that currently has the keyboard focus. + Remember that a component will only be given the focus if its setWantsKeyboardFocus() + method has been used to enable this. + + The default implementation of this method actually calls its parent's modifierKeysChanged + method, so that focused components which aren't interested in this will give their + parents a chance to act on the event instead. + + @see keyStateChanged, ModifierKeys + */ + virtual void modifierKeysChanged (const ModifierKeys& modifiers); + + /** Enumeration used by the focusChanged() and focusLost() methods. */ + enum FocusChangeType + { + focusChangedByMouseClick, /**< Means that the user clicked the mouse to change focus. */ + focusChangedByTabKey, /**< Means that the user pressed the tab key to move the focus. */ + focusChangedDirectly /**< Means that the focus was changed by a call to grabKeyboardFocus(). */ + }; + + /** Called to indicate that this component has just acquired the keyboard focus. + + @see focusLost, setWantsKeyboardFocus, getCurrentlyFocusedComponent, hasKeyboardFocus + */ + virtual void focusGained (FocusChangeType cause); + + /** Called to indicate that this component has just lost the keyboard focus. + + @see focusGained, setWantsKeyboardFocus, getCurrentlyFocusedComponent, hasKeyboardFocus + */ + virtual void focusLost (FocusChangeType cause); + + /** Called to indicate that one of this component's children has been focused or unfocused. + + Essentially this means that the return value of a call to hasKeyboardFocus (true) has + changed. It happens when focus moves from one of this component's children (at any depth) + to a component that isn't contained in this one, (or vice-versa). + + @see focusGained, setWantsKeyboardFocus, getCurrentlyFocusedComponent, hasKeyboardFocus + */ + virtual void focusOfChildComponentChanged (FocusChangeType cause); + + /** Returns true if the mouse is currently over this component. + + If the mouse isn't over the component, this will return false, even if the + mouse is currently being dragged - so you can use this in your mouseDrag + method to find out whether it's really over the component or not. + + Note that when the mouse button is being held down, then the only component + for which this method will return true is the one that was originally + clicked on. + + @see isMouseButtonDown. isMouseOverOrDragging, mouseDrag + */ + bool isMouseOver() const throw(); + + /** Returns true if the mouse button is currently held down in this component. + + Note that this is a test to see whether the mouse is being pressed in this + component, so it'll return false if called on component A when the mouse + is actually being dragged in component B. + + @see isMouseButtonDownAnywhere, isMouseOver, isMouseOverOrDragging + */ + bool isMouseButtonDown() const throw(); + + /** True if the mouse is over this component, or if it's being dragged in this component. + + This is a handy equivalent to (isMouseOver() || isMouseButtonDown()). + + @see isMouseOver, isMouseButtonDown, isMouseButtonDownAnywhere + */ + bool isMouseOverOrDragging() const throw(); + + /** Returns true if a mouse button is currently down. + + Unlike isMouseButtonDown, this will test the current state of the + buttons without regard to which component (if any) it has been + pressed in. + + @see isMouseButtonDown, ModifierKeys + */ + static bool JUCE_CALLTYPE isMouseButtonDownAnywhere() throw(); + + /** Returns the mouse's current position, relative to this component. + + The co-ordinates are relative to the component's top-left corner. + */ + void getMouseXYRelative (int& x, int& y) const throw(); + + /** Returns the component that's currently underneath the mouse. + + @returns the component or 0 if there isn't one. + @see contains, getComponentAt + */ + static Component* JUCE_CALLTYPE getComponentUnderMouse() throw(); + + /** Allows the mouse to move beyond the edges of the screen. + + Calling this method when the mouse button is currently pressed inside this component + will remove the cursor from the screen and allow the mouse to (seem to) move beyond + the edges of the screen. + + This means that the co-ordinates returned to mouseDrag() will be unbounded, and this + can be used for things like custom slider controls or dragging objects around, where + movement would be otherwise be limited by the mouse hitting the edges of the screen. + + The unbounded mode is automatically turned off when the mouse button is released, or + it can be turned off explicitly by calling this method again. + + @param shouldUnboundedMovementBeEnabled whether to turn this mode on or off + @param keepCursorVisibleUntilOffscreen if set to false, the cursor will immediately be + hidden; if true, it will only be hidden when it + is moved beyond the edge of the screen + */ + void enableUnboundedMouseMovement (bool shouldUnboundedMovementBeEnabled, + bool keepCursorVisibleUntilOffscreen = false) throw(); + + /** Called when this component's size has been changed. + + A component can implement this method to do things such as laying out its + child components when its width or height changes. + + The method is called synchronously as a result of the setBounds or setSize + methods, so repeatedly changing a components size will repeatedly call its + resized method (unlike things like repainting, where multiple calls to repaint + are coalesced together). + + If the component is a top-level window on the desktop, its size could also + be changed by operating-system factors beyond the application's control. + + @see moved, setSize + */ + virtual void resized(); + + /** Called when this component's position has been changed. + + This is called when the position relative to its parent changes, not when + its absolute position on the screen changes (so it won't be called for + all child components when a parent component is moved). + + The method is called synchronously as a result of the setBounds, setTopLeftPosition + or any of the other repositioning methods, and like resized(), it will be + called each time those methods are called. + + If the component is a top-level window on the desktop, its position could also + be changed by operating-system factors beyond the application's control. + + @see resized, setBounds + */ + virtual void moved(); + + /** Called when one of this component's children is moved or resized. + + If the parent wants to know about changes to its immediate children (not + to children of its children), this is the method to override. + + @see moved, resized, parentSizeChanged + */ + virtual void childBoundsChanged (Component* child); + + /** Called when this component's immediate parent has been resized. + + If the component is a top-level window, this indicates that the screen size + has changed. + + @see childBoundsChanged, moved, resized + */ + virtual void parentSizeChanged(); + + /** Called when this component has been moved to the front of its siblings. + + The component may have been brought to the front by the toFront() method, or + by the operating system if it's a top-level window. + + @see toFront + */ + virtual void broughtToFront(); + + /** Adds a listener to be told about changes to the component hierarchy or position. + + Component listeners get called when this component's size, position or children + change - see the ComponentListener class for more details. + + @param newListener the listener to register - if this is already registered, it + will be ignored. + @see ComponentListener, removeComponentListener + */ + void addComponentListener (ComponentListener* const newListener) throw(); + + /** Removes a component listener. + + @see addComponentListener + */ + void removeComponentListener (ComponentListener* const listenerToRemove) throw(); + + /** Dispatches a numbered message to this component. + + This is a quick and cheap way of allowing simple asynchronous messages to + be sent to components. It's also safe, because if the component that you + send the message to is a null or dangling pointer, this won't cause an error. + + The command ID is later delivered to the component's handleCommandMessage() method by + the application's message queue. + + @see handleCommandMessage + */ + void postCommandMessage (const int commandId) throw(); + + /** Called to handle a command that was sent by postCommandMessage(). + + This is called by the message thread when a command message arrives, and + the component can override this method to process it in any way it needs to. + + @see postCommandMessage + */ + virtual void handleCommandMessage (int commandId); + + /** Runs a component modally, waiting until the loop terminates. + + This method first makes the component visible, brings it to the front and + gives it the keyboard focus. + + It then runs a loop, dispatching messages from the system message queue, but + blocking all mouse or keyboard messages from reaching any components other + than this one and its children. + + This loop continues until the component's exitModalState() method is called (or + the component is deleted), and then this method returns, returning the value + passed into exitModalState(). + + @see enterModalState, exitModalState, isCurrentlyModal, getCurrentlyModalComponent, + isCurrentlyBlockedByAnotherModalComponent, MessageManager::dispatchNextMessage + */ + int runModalLoop(); + + /** Puts the component into a modal state. + + This makes the component modal, so that messages are blocked from reaching + any components other than this one and its children, but unlike runModalLoop(), + this method returns immediately. + + If takeKeyboardFocus is true, the component will use grabKeyboardFocus() to + get the focus, which is usually what you'll want it to do. If not, it will leave + the focus unchanged. + + @see exitModalState, runModalLoop + */ + void enterModalState (const bool takeKeyboardFocus = true); + + /** Ends a component's modal state. + + If this component is currently modal, this will turn of its modalness, and return + a value to the runModalLoop() method that might have be running its modal loop. + + @see runModalLoop, enterModalState, isCurrentlyModal + */ + void exitModalState (const int returnValue); + + /** Returns true if this component is the modal one. + + It's possible to have nested modal components, e.g. a pop-up dialog box + that launches another pop-up, but this will only return true for + the one at the top of the stack. + + @see getCurrentlyModalComponent + */ + bool isCurrentlyModal() const throw(); + + /** Returns the component that is currently modal. + + @returns the modal component, or null if no components are modal + @see runModalLoop, isCurrentlyModal + */ + static Component* JUCE_CALLTYPE getCurrentlyModalComponent() throw(); + + /** Checks whether there's a modal component somewhere that's stopping this one + from receiving messages. + + If there is a modal component, its canModalEventBeSentToComponent() method + will be called to see if it will still allow this component to receive events. + + @see runModalLoop, getCurrentlyModalComponent + */ + bool isCurrentlyBlockedByAnotherModalComponent() const throw(); + + /** When a component is modal, this callback allows it to choose which other + components can still receive events. + + When a modal component is active and the user clicks on a non-modal component, + this method is called on the modal component, and if it returns true, the + event is allowed to reach its target. If it returns false, the event is blocked + and the inputAttemptWhenModal() callback is made. + + It called by the isCurrentlyBlockedByAnotherModalComponent() method. The default + implementation just returns false in all cases. + */ + virtual bool canModalEventBeSentToComponent (const Component* targetComponent); + + /** Called when the user tries to click on a component that is blocked by another + modal component. + + When a component is modal and the user clicks on one of the other components, + the modal component will receive this callback. + + The default implementation of this method will play a beep, and bring the currently + modal component to the front, but it can be overridden to do other tasks. + + @see isCurrentlyBlockedByAnotherModalComponent, canModalEventBeSentToComponent + */ + virtual void inputAttemptWhenModal(); + + /** Returns one of the component's properties as a string. + + @param keyName the name of the property to retrieve + @param useParentComponentIfNotFound if this is true and the key isn't present in this component's + properties, then it will check whether the parent component has + the key. + @param defaultReturnValue a value to return if the named property doesn't actually exist + */ + const String getComponentProperty (const String& keyName, + const bool useParentComponentIfNotFound, + const String& defaultReturnValue = String::empty) const throw(); + + /** Returns one of the properties as an integer. + + @param keyName the name of the property to retrieve + @param useParentComponentIfNotFound if this is true and the key isn't present in this component's + properties, then it will check whether the parent component has + the key. + @param defaultReturnValue a value to return if the named property doesn't actually exist + */ + int getComponentPropertyInt (const String& keyName, + const bool useParentComponentIfNotFound, + const int defaultReturnValue = 0) const throw(); + + /** Returns one of the properties as an double. + + @param keyName the name of the property to retrieve + @param useParentComponentIfNotFound if this is true and the key isn't present in this component's + properties, then it will check whether the parent component has + the key. + @param defaultReturnValue a value to return if the named property doesn't actually exist + */ + double getComponentPropertyDouble (const String& keyName, + const bool useParentComponentIfNotFound, + const double defaultReturnValue = 0.0) const throw(); + + /** Returns one of the properties as an boolean. + + The result will be true if the string found for this key name can be parsed as a non-zero + integer. + + @param keyName the name of the property to retrieve + @param useParentComponentIfNotFound if this is true and the key isn't present in this component's + properties, then it will check whether the parent component has + the key. + @param defaultReturnValue a value to return if the named property doesn't actually exist + */ + bool getComponentPropertyBool (const String& keyName, + const bool useParentComponentIfNotFound, + const bool defaultReturnValue = false) const throw(); + + /** Returns one of the properties as an colour. + + @param keyName the name of the property to retrieve + @param useParentComponentIfNotFound if this is true and the key isn't present in this component's + properties, then it will check whether the parent component has + the key. + @param defaultReturnValue a colour to return if the named property doesn't actually exist + */ + const Colour getComponentPropertyColour (const String& keyName, + const bool useParentComponentIfNotFound, + const Colour& defaultReturnValue = Colours::black) const throw(); + + /** Sets a named property as a string. + + @param keyName the name of the property to set. (This mustn't be an empty string) + @param value the new value to set it to + @see removeComponentProperty + */ + void setComponentProperty (const String& keyName, const String& value) throw(); + + /** Sets a named property to an integer. + + @param keyName the name of the property to set. (This mustn't be an empty string) + @param value the new value to set it to + @see removeComponentProperty + */ + void setComponentProperty (const String& keyName, const int value) throw(); + + /** Sets a named property to a double. + + @param keyName the name of the property to set. (This mustn't be an empty string) + @param value the new value to set it to + @see removeComponentProperty + */ + void setComponentProperty (const String& keyName, const double value) throw(); + + /** Sets a named property to a boolean. + + @param keyName the name of the property to set. (This mustn't be an empty string) + @param value the new value to set it to + @see removeComponentProperty + */ + void setComponentProperty (const String& keyName, const bool value) throw(); + + /** Sets a named property to a colour. + + @param keyName the name of the property to set. (This mustn't be an empty string) + @param newColour the new colour to set it to + @see removeComponentProperty + */ + void setComponentProperty (const String& keyName, const Colour& newColour) throw(); + + /** Deletes a named component property. + + @param keyName the name of the property to delete. (This mustn't be an empty string) + @see setComponentProperty, getComponentProperty + */ + void removeComponentProperty (const String& keyName) throw(); + + /** Returns the complete set of properties that have been set for this component. + + If no properties have been set, this will return a null pointer. + + @see getComponentProperty, setComponentProperty + */ + PropertySet* getComponentProperties() const throw() { return propertySet_; } + + /** Looks for a colour that has been registered with the given colour ID number. + + If a colour has been set for this ID number using setColour(), then it is + returned. If none has been set, the method will try calling the component's + LookAndFeel class's findColour() method. If none has been registered with the + look-and-feel either, it will just return black. + + The colour IDs for various purposes are stored as enums in the components that + they are relevent to - for an example, see Slider::ColourIds, + Label::ColourIds, TextEditor::ColourIds, TreeView::ColourIds, etc. + + @see setColour, isColourSpecified, colourChanged, LookAndFeel::findColour, LookAndFeel::setColour + */ + const Colour findColour (const int colourId, const bool inheritFromParent = false) const throw(); + + /** Registers a colour to be used for a particular purpose. + + Changing a colour will cause a synchronous callback to the colourChanged() + method, which your component can override if it needs to do something when + colours are altered. + + For more details about colour IDs, see the comments for findColour(). + + @see findColour, isColourSpecified, colourChanged, LookAndFeel::findColour, LookAndFeel::setColour + */ + void setColour (const int colourId, const Colour& colour); + + /** If a colour has been set with setColour(), this will remove it. + + This allows you to make a colour revert to its default state. + */ + void removeColour (const int colourId); + + /** Returns true if the specified colour ID has been explicitly set for this + component using the setColour() method. + */ + bool isColourSpecified (const int colourId) const throw(); + + /** This looks for any colours that have been specified for this component, + and copies them to the specified target component. + */ + void copyAllExplicitColoursTo (Component& target) const throw(); + + /** This method is called when a colour is changed by the setColour() method. + + @see setColour, findColour + */ + virtual void colourChanged(); + + /** Returns the underlying native window handle for this component. + + This is platform-dependent and strictly for power-users only! + */ + void* getWindowHandle() const throw(); + + /** When created, each component is given a number to uniquely identify it. + + The number is incremented each time a new component is created, so it's a more + unique way of identifying a component than using its memory location (which + may be reused after the component is deleted, of course). + */ + uint32 getComponentUID() const throw() { return componentUID; } + + juce_UseDebuggingNewOperator + +private: + + friend class ComponentPeer; + friend class InternalDragRepeater; + + static Component* currentlyFocusedComponent; + static Component* componentUnderMouse; + + String componentName_; + Component* parentComponent_; + uint32 componentUID; + Rectangle bounds_; + unsigned short numDeepMouseListeners; + Array childComponentList_; + LookAndFeel* lookAndFeel_; + MouseCursor cursor_; + ImageEffectFilter* effect_; + Image* bufferedImage_; + VoidArray* mouseListeners_; + VoidArray* keyListeners_; + VoidArray* componentListeners_; + PropertySet* propertySet_; + + struct ComponentFlags + { + bool hasHeavyweightPeerFlag : 1; + bool visibleFlag : 1; + bool opaqueFlag : 1; + bool ignoresMouseClicksFlag : 1; + bool allowChildMouseClicksFlag : 1; + bool wantsFocusFlag : 1; + bool isFocusContainerFlag : 1; + bool dontFocusOnMouseClickFlag : 1; + bool alwaysOnTopFlag : 1; + bool bufferToImageFlag : 1; + bool bringToFrontOnClickFlag : 1; + bool repaintOnMouseActivityFlag : 1; + bool draggingFlag : 1; + bool mouseOverFlag : 1; + bool mouseInsideFlag : 1; + bool currentlyModalFlag : 1; + bool isDisabledFlag : 1; + bool childCompFocusedFlag : 1; +#ifdef JUCE_DEBUG + bool isInsidePaintCall : 1; +#endif + }; + + union + { + uint32 componentFlags_; + ComponentFlags flags; + }; + + void internalMouseEnter (int x, int y, const int64 time); + void internalMouseExit (int x, int y, const int64 time); + void internalMouseDown (int x, int y); + void internalMouseUp (const int oldModifiers, int x, int y, const int64 time); + void internalMouseDrag (int x, int y, const int64 time); + void internalMouseMove (int x, int y, const int64 time); + void internalMouseWheel (const int intAmountX, const int intAmountY, const int64 time); + void internalBroughtToFront(); + void internalFocusGain (const FocusChangeType cause); + void internalFocusLoss (const FocusChangeType cause); + void internalChildFocusChange (FocusChangeType cause); + void internalModalInputAttempt(); + void internalModifierKeysChanged(); + void internalChildrenChanged(); + void internalHierarchyChanged(); + void internalUpdateMouseCursor (const bool forcedUpdate) throw(); + void sendMovedResizedMessages (const bool wasMoved, const bool wasResized); + void repaintParent() throw(); + void sendFakeMouseMove() const; + void takeKeyboardFocus (const FocusChangeType cause); + void grabFocusInternal (const FocusChangeType cause, const bool canTryParent = true); + static void giveAwayFocus(); + void sendEnablementChangeMessage(); + static void* runModalLoopCallback (void*); + void subtractObscuredRegions (RectangleList& result, + const int deltaX, const int deltaY, + const Rectangle& clipRect, + const Component* const compToAvoid) const throw(); + void clipObscuredRegions (Graphics& g, const Rectangle& clipRect, + const int deltaX, const int deltaY) const throw(); + + // how much of the component is not off the edges of its parents + const Rectangle getUnclippedArea() const; + void sendVisibilityChangeMessage(); + + // This is included here just to cause a compile error if your code is still handling + // drag-and-drop with this method. If so, just update it to use the new FileDragAndDropTarget + // class, which is easy (just make your class inherit from FileDragAndDropTarget, and + // implement its methods instead of this Component method). + virtual void filesDropped (const StringArray&, int, int) {} + + // components aren't allowed to have copy constructors, as this would mess up parent + // hierarchies. You might need to give your subclasses a private dummy constructor like + // this one to avoid compiler warnings. + Component (const Component&); + + const Component& operator= (const Component&); + +protected: + /** @internal */ + virtual void internalRepaint (int x, int y, int w, int h); + + virtual ComponentPeer* createNewPeer (int styleFlags, void* nativeWindowToAttachTo); + + /** Overridden from the MessageListener parent class. + + You can override this if you really need to, but be sure to pass your unwanted messages up + to this base class implementation, as the Component class needs to send itself messages + to work properly. + */ + void handleMessage (const Message&); +}; + +#endif // __JUCE_COMPONENT_JUCEHEADER__ +/********* End of inlined file: juce_Component.h *********/ + +/********* Start of inlined file: juce_ApplicationCommandInfo.h *********/ +#ifndef __JUCE_APPLICATIONCOMMANDINFO_JUCEHEADER__ +#define __JUCE_APPLICATIONCOMMANDINFO_JUCEHEADER__ + +/********* Start of inlined file: juce_ApplicationCommandID.h *********/ +#ifndef __JUCE_APPLICATIONCOMMANDID_JUCEHEADER__ +#define __JUCE_APPLICATIONCOMMANDID_JUCEHEADER__ + +/** A type used to hold the unique ID for an application command. + + This is a numeric type, so it can be stored as an integer. + + @see ApplicationCommandInfo, ApplicationCommandManager, + ApplicationCommandTarget, KeyPressMappingSet +*/ +typedef int CommandID; + +/** A set of general-purpose application command IDs. + + Because these commands are likely to be used in most apps, they're defined + here to help different apps to use the same numeric values for them. + + Of course you don't have to use these, but some of them are used internally by + Juce - e.g. the quit ID is recognised as a command by the JUCEApplication class. + + @see ApplicationCommandInfo, ApplicationCommandManager, + ApplicationCommandTarget, KeyPressMappingSet +*/ +namespace StandardApplicationCommandIDs +{ + /** This command ID should be used to send a "Quit the App" command. + + This command is recognised by the JUCEApplication class, so if it is invoked + and no other ApplicationCommandTarget handles the event first, the JUCEApplication + object will catch it and call JUCEApplication::systemRequestedQuit(). + */ + static const CommandID quit = 0x1001; + + /** The command ID that should be used to send a "Delete" command. */ + static const CommandID del = 0x1002; + + /** The command ID that should be used to send a "Cut" command. */ + static const CommandID cut = 0x1003; + + /** The command ID that should be used to send a "Copy to clipboard" command. */ + static const CommandID copy = 0x1004; + + /** The command ID that should be used to send a "Paste from clipboard" command. */ + static const CommandID paste = 0x1005; + + /** The command ID that should be used to send a "Select all" command. */ + static const CommandID selectAll = 0x1006; + + /** The command ID that should be used to send a "Deselect all" command. */ + static const CommandID deselectAll = 0x1007; +} + +#endif // __JUCE_APPLICATIONCOMMANDID_JUCEHEADER__ +/********* End of inlined file: juce_ApplicationCommandID.h *********/ + +/** + Holds information describing an application command. + + This object is used to pass information about a particular command, such as its + name, description and other usage flags. + + When an ApplicationCommandTarget is asked to provide information about the commands + it can perform, this is the structure gets filled-in to describe each one. + + @see ApplicationCommandTarget, ApplicationCommandTarget::getCommandInfo(), + ApplicationCommandManager +*/ +struct JUCE_API ApplicationCommandInfo +{ + + ApplicationCommandInfo (const CommandID commandID) throw(); + + /** Sets a number of the structures values at once. + + The meanings of each of the parameters is described below, in the appropriate + member variable's description. + */ + void setInfo (const String& shortName, + const String& description, + const String& categoryName, + const int flags) throw(); + + /** An easy way to set or remove the isDisabled bit in the structure's flags field. + + If isActive is true, the flags member has the isDisabled bit cleared; if isActive + is false, the bit is set. + */ + void setActive (const bool isActive) throw(); + + /** An easy way to set or remove the isTicked bit in the structure's flags field. + */ + void setTicked (const bool isTicked) throw(); + + /** Handy method for adding a keypress to the defaultKeypresses array. + + This is just so you can write things like: + @code + myinfo.addDefaultKeypress (T('s'), ModifierKeys::commandModifier); + @endcode + instead of + @code + myinfo.defaultKeypresses.add (KeyPress (T('s'), ModifierKeys::commandModifier)); + @endcode + */ + void addDefaultKeypress (const int keyCode, + const ModifierKeys& modifiers) throw(); + + /** The command's unique ID number. + */ + CommandID commandID; + + /** A short name to describe the command. + + This should be suitable for use in menus, on buttons that trigger the command, etc. + + You can use the setInfo() method to quickly set this and some of the command's + other properties. + */ + String shortName; + + /** A longer description of the command. + + This should be suitable for use in contexts such as a KeyMappingEditorComponent or + pop-up tooltip describing what the command does. + + You can use the setInfo() method to quickly set this and some of the command's + other properties. + */ + String description; + + /** A named category that the command fits into. + + You can give your commands any category you like, and these will be displayed in + contexts such as the KeyMappingEditorComponent, where the category is used to group + commands together. + + You can use the setInfo() method to quickly set this and some of the command's + other properties. + */ + String categoryName; + + /** A list of zero or more keypresses that should be used as the default keys for + this command. + + Methods such as KeyPressMappingSet::resetToDefaultMappings() will use the keypresses in + this list to initialise the default set of key-to-command mappings. + + @see addDefaultKeypress + */ + Array defaultKeypresses; + + /** Flags describing the ways in which this command should be used. + + A bitwise-OR of these values is stored in the ApplicationCommandInfo::flags + variable. + */ + enum CommandFlags + { + /** Indicates that the command can't currently be performed. + + The ApplicationCommandTarget::getCommandInfo() method must set this flag if it's + not currently permissable to perform the command. If the flag is set, then + components that trigger the command, e.g. PopupMenu, may choose to grey-out the + command or show themselves as not being enabled. + + @see ApplicationCommandInfo::setActive + */ + isDisabled = 1 << 0, + + /** Indicates that the command should have a tick next to it on a menu. + + If your command is shown on a menu and this is set, it'll show a tick next to + it. Other components such as buttons may also use this flag to indicate that it + is a value that can be toggled, and is currently in the 'on' state. + + @see ApplicationCommandInfo::setTicked + */ + isTicked = 1 << 1, + + /** If this flag is present, then when a KeyPressMappingSet invokes the command, + it will call the command twice, once on key-down and again on key-up. + + @see ApplicationCommandTarget::InvocationInfo + */ + wantsKeyUpDownCallbacks = 1 << 2, + + /** If this flag is present, then a KeyMappingEditorComponent will not display the + command in its list. + */ + hiddenFromKeyEditor = 1 << 3, + + /** If this flag is present, then a KeyMappingEditorComponent will display the + command in its list, but won't allow the assigned keypress to be changed. + */ + readOnlyInKeyEditor = 1 << 4, + + /** If this flag is present and the command is invoked from a keypress, then any + buttons or menus that are also connected to the command will not flash to + indicate that they've been triggered. + */ + dontTriggerVisualFeedback = 1 << 5 + }; + + /** A bitwise-OR of the values specified in the CommandFlags enum. + + You can use the setInfo() method to quickly set this and some of the command's + other properties. + */ + int flags; +}; + +#endif // __JUCE_APPLICATIONCOMMANDINFO_JUCEHEADER__ +/********* End of inlined file: juce_ApplicationCommandInfo.h *********/ + +/** + A command target publishes a list of command IDs that it can perform. + + An ApplicationCommandManager despatches commands to targets, which must be + able to provide information about what commands they can handle. + + To create a target, you'll need to inherit from this class, implementing all of + its pure virtual methods. + + For info about how a target is chosen to receive a command, see + ApplicationCommandManager::getFirstCommandTarget(). + + @see ApplicationCommandManager, ApplicationCommandInfo +*/ +class JUCE_API ApplicationCommandTarget +{ +public: + + /** Creates a command target. */ + ApplicationCommandTarget(); + + /** Destructor. */ + virtual ~ApplicationCommandTarget(); + + /** + */ + struct JUCE_API InvocationInfo + { + + InvocationInfo (const CommandID commandID) throw(); + + /** The UID of the command that should be performed. */ + CommandID commandID; + + /** The command's flags. + + See ApplicationCommandInfo for a description of these flag values. + */ + int commandFlags; + + /** The types of context in which the command might be called. */ + enum InvocationMethod + { + direct = 0, /**< The command is being invoked directly by a piece of code. */ + fromKeyPress, /**< The command is being invoked by a key-press. */ + fromMenu, /**< The command is being invoked by a menu selection. */ + fromButton /**< The command is being invoked by a button click. */ + }; + + /** The type of event that triggered this command. */ + InvocationMethod invocationMethod; + + /** If triggered by a keypress or menu, this will be the component that had the + keyboard focus at the time. + + If triggered by a button, it may be set to that component, or it may be null. + */ + Component* originatingComponent; + + /** The keypress that was used to invoke it. + + Note that this will be an invalid keypress if the command was invoked + by some other means than a keyboard shortcut. + */ + KeyPress keyPress; + + /** True if the callback is being invoked when the key is pressed, + false if the key is being released. + + @see KeyPressMappingSet::addCommand() + */ + bool isKeyDown; + + /** If the key is being released, this indicates how long it had been held + down for. + + (Only relevant if isKeyDown is false.) + */ + int millisecsSinceKeyPressed; + }; + + /** This must return the next target to try after this one. + + When a command is being sent, and the first target can't handle + that command, this method is used to determine the next target that should + be tried. + + It may return 0 if it doesn't know of another target. + + If your target is a Component, you would usually use the findFirstTargetParentComponent() + method to return a parent component that might want to handle it. + + @see invoke + */ + virtual ApplicationCommandTarget* getNextCommandTarget() = 0; + + /** This must return a complete list of commands that this target can handle. + + Your target should add all the command IDs that it handles to the array that is + passed-in. + */ + virtual void getAllCommands (Array & commands) = 0; + + /** This must provide details about one of the commands that this target can perform. + + This will be called with one of the command IDs that the target provided in its + getAllCommands() methods. + + It should fill-in all appropriate fields of the ApplicationCommandInfo structure with + suitable information about the command. (The commandID field will already have been filled-in + by the caller). + + The easiest way to set the info is using the ApplicationCommandInfo::setInfo() method to + set all the fields at once. + + If the command is currently inactive for some reason, this method must use + ApplicationCommandInfo::setActive() to make that clear, (or it should set the isDisabled + bit of the ApplicationCommandInfo::flags field). + + Any default key-presses for the command should be appended to the + ApplicationCommandInfo::defaultKeypresses field. + + Note that if you change something that affects the status of the commands + that would be returned by this method (e.g. something that makes some commands + active or inactive), you should call ApplicationCommandManager::commandStatusChanged() + to cause the manager to refresh its status. + */ + virtual void getCommandInfo (const CommandID commandID, + ApplicationCommandInfo& result) = 0; + + /** This must actually perform the specified command. + + If this target is able to perform the command specified by the commandID field of the + InvocationInfo structure, then it should do so, and must return true. + + If it can't handle this command, it should return false, which tells the caller to pass + the command on to the next target in line. + + @see invoke, ApplicationCommandManager::invoke + */ + virtual bool perform (const InvocationInfo& info) = 0; + + /** Makes this target invoke a command. + + Your code can call this method to invoke a command on this target, but normally + you'd call it indirectly via ApplicationCommandManager::invoke() or + ApplicationCommandManager::invokeDirectly(). + + If this target can perform the given command, it will call its perform() method to + do so. If not, then getNextCommandTarget() will be used to determine the next target + to try, and the command will be passed along to it. + + @param invocationInfo this must be correctly filled-in, describing the context for + the invocation. + @param asynchronously if false, the command will be performed before this method returns. + If true, a message will be posted so that the command will be performed + later on the message thread, and this method will return immediately. + @see perform, ApplicationCommandManager::invoke + */ + bool invoke (const InvocationInfo& invocationInfo, + const bool asynchronously); + + /** Invokes a given command directly on this target. + + This is just an easy way to call invoke() without having to fill out the InvocationInfo + structure. + */ + bool invokeDirectly (const CommandID commandID, + const bool asynchronously); + + /** Searches this target and all subsequent ones for the first one that can handle + the specified command. + + This will use getNextCommandTarget() to determine the chain of targets to try + after this one. + */ + ApplicationCommandTarget* getTargetForCommand (const CommandID commandID); + + /** Checks whether this command can currently be performed by this target. + + This will return true only if a call to getCommandInfo() doesn't set the + isDisabled flag to indicate that the command is inactive. + */ + bool isCommandActive (const CommandID commandID); + + /** If this object is a Component, this method will seach upwards in its current + UI hierarchy for the next parent component that implements the + ApplicationCommandTarget class. + + If your target is a Component, this is a very handy method to use in your + getNextCommandTarget() implementation. + */ + ApplicationCommandTarget* findFirstTargetParentComponent(); + + juce_UseDebuggingNewOperator + +private: + // (for async invocation of commands) + class CommandTargetMessageInvoker : public MessageListener + { + public: + CommandTargetMessageInvoker (ApplicationCommandTarget* const owner); + ~CommandTargetMessageInvoker(); + + void handleMessage (const Message& message); + + private: + ApplicationCommandTarget* const owner; + + CommandTargetMessageInvoker (const CommandTargetMessageInvoker&); + const CommandTargetMessageInvoker& operator= (const CommandTargetMessageInvoker&); + }; + + CommandTargetMessageInvoker* messageInvoker; + + friend class CommandTargetMessageInvoker; + bool tryToInvoke (const InvocationInfo& info, const bool async); +}; + +#endif // __JUCE_APPLICATIONCOMMANDTARGET_JUCEHEADER__ +/********* End of inlined file: juce_ApplicationCommandTarget.h *********/ + +/********* Start of inlined file: juce_ActionListener.h *********/ +#ifndef __JUCE_ACTIONLISTENER_JUCEHEADER__ +#define __JUCE_ACTIONLISTENER_JUCEHEADER__ + +/** + Receives callbacks to indicate that some kind of event has occurred. + + Used by various classes, e.g. buttons when they are pressed, to tell listeners + about something that's happened. + + @see ActionListenerList, ActionBroadcaster, ChangeListener +*/ +class JUCE_API ActionListener +{ +public: + /** Destructor. */ + virtual ~ActionListener() {} + + /** Overridden by your subclass to receive the callback. + + @param message the string that was specified when the event was triggered + by a call to ActionListenerList::sendActionMessage() + */ + virtual void actionListenerCallback (const String& message) = 0; +}; + +#endif // __JUCE_ACTIONLISTENER_JUCEHEADER__ +/********* End of inlined file: juce_ActionListener.h *********/ + +/** + An instance of this class is used to specify initialisation and shutdown + code for the application. + + An application that wants to run in the JUCE framework needs to declare a + subclass of JUCEApplication and implement its various pure virtual methods. + + It then needs to use the START_JUCE_APPLICATION macro somewhere in a cpp file + to declare an instance of this class and generate a suitable platform-specific + main() function. + + e.g. @code + class MyJUCEApp : public JUCEApplication + { + // NEVER put objects inside a JUCEApplication class - only use pointers to + // objects, which you must create in the initialise() method. + MyApplicationWindow* myMainWindow; + + public: + MyJUCEApp() + : myMainWindow (0) + { + // never create any Juce objects in the constructor - do all your initialisation + // in the initialise() method. + } + + ~MyJUCEApp() + { + // all your shutdown code must have already been done in the shutdown() method - + // nothing should happen in this destructor. + } + + void initialise (const String& commandLine) + { + myMainWindow = new MyApplicationWindow(); + myMainWindow->setBounds (100, 100, 400, 500); + myMainWindow->setVisible (true); + } + + void shutdown() + { + delete myMainWindow; + } + + const String getApplicationName() + { + return T("Super JUCE-o-matic"); + } + + const String getApplicationVersion() + { + return T("1.0"); + } + }; + + // this creates wrapper code to actually launch the app properly. + START_JUCE_APPLICATION (MyJUCEApp) + @endcode + + Because this object will be created before Juce has properly initialised, you must + NEVER add any member variable objects that will be automatically constructed. Likewise + don't put ANY code in the constructor that could call Juce functions. Any objects that + you want to add to the class must be pointers, which you should instantiate during the + initialise() method, and delete in the shutdown() method. + + @see MessageManager, DeletedAtShutdown +*/ +class JUCE_API JUCEApplication : public ApplicationCommandTarget, + private ActionListener +{ +protected: + + /** Constructs a JUCE app object. + + If subclasses implement a constructor or destructor, they shouldn't call any + JUCE code in there - put your startup/shutdown code in initialise() and + shutdown() instead. + */ + JUCEApplication(); + +public: + /** Destructor. + + If subclasses implement a constructor or destructor, they shouldn't call any + JUCE code in there - put your startup/shutdown code in initialise() and + shutdown() instead. + */ + virtual ~JUCEApplication(); + + /** Returns the global instance of the application object being run. */ + static JUCEApplication* getInstance() throw(); + + /** Called when the application starts. + + This will be called once to let the application do whatever initialisation + it needs, create its windows, etc. + + After the method returns, the normal event-dispatch loop will be run, + until the quit() method is called, at which point the shutdown() + method will be called to let the application clear up anything it needs + to delete. + + If during the initialise() method, the application decides not to start-up + after all, it can just call the quit() method and the event loop won't be run. + + @param commandLineParameters the line passed in does not include the + name of the executable, just the parameter list. + @see shutdown, quit + */ + virtual void initialise (const String& commandLineParameters) = 0; + + /** Returns true if the application hasn't yet completed its initialise() method + and entered the main event loop. + + This is handy for things like splash screens to know when the app's up-and-running + properly. + */ + bool isInitialising() const throw(); + + /* Called to allow the application to clear up before exiting. + + After JUCEApplication::quit() has been called, the event-dispatch loop will + terminate, and this method will get called to allow the app to sort itself + out. + + Be careful that nothing happens in this method that might rely on messages + being sent, or any kind of window activity, because the message loop is no + longer running at this point. + + @see DeletedAtShutdown + */ + virtual void shutdown() = 0; + + /** Returns the application's name. + + An application must implement this to name itself. + */ + virtual const String getApplicationName() = 0; + + /** Returns the application's version number. + + An application can implement this to give itself a version. + (The default implementation of this just returns an empty string). + */ + virtual const String getApplicationVersion(); + + /** Checks whether multiple instances of the app are allowed. + + If you application class returns true for this, more than one instance is + permitted to run (except on the Mac where this isn't possible). + + If it's false, the second instance won't start, but it you will still get a + callback to anotherInstanceStarted() to tell you about this - which + gives you a chance to react to what the user was trying to do. + */ + virtual bool moreThanOneInstanceAllowed(); + + /** Indicates that the user has tried to start up another instance of the app. + + This will get called even if moreThanOneInstanceAllowed() is false. + */ + virtual void anotherInstanceStarted (const String& commandLine); + + /** Called when the operating system is trying to close the application. + + The default implementation of this method is to call quit(), but it may + be overloaded to ignore the request or do some other special behaviour + instead. For example, you might want to offer the user the chance to save + their changes before quitting, and give them the chance to cancel. + + If you want to send a quit signal to your app, this is the correct method + to call, because it means that requests that come from the system get handled + in the same way as those from your own application code. So e.g. you'd + call this method from a "quit" item on a menu bar. + */ + virtual void systemRequestedQuit(); + + /** If any unhandled exceptions make it through to the message dispatch loop, this + callback will be triggered, in case you want to log them or do some other + type of error-handling. + + If the type of exception is derived from the std::exception class, the pointer + passed-in will be valid. If the exception is of unknown type, this pointer + will be null. + */ + virtual void unhandledException (const std::exception* e, + const String& sourceFilename, + const int lineNumber); + + /** Signals that the main message loop should stop and the application should terminate. + + This isn't synchronous, it just posts a quit message to the main queue, and + when this message arrives, the message loop will stop, the shutdown() method + will be called, and the app will exit. + + Note that this will cause an unconditional quit to happen, so if you need an + extra level before this, e.g. to give the user the chance to save their work + and maybe cancel the quit, you'll need to handle this in the systemRequestedQuit() + method - see that method's help for more info. + + @param useMaximumForce if this is true, the process will be forcibly killed + before leaving the WinMain or main() function, which can + be useful if threads might have got stuck somehow. + @see MessageManager, DeletedAtShutdown + */ + static void quit (const bool useMaximumForce = false); + + /** Sets the value that should be returned as the application's exit code when the + app quits. + + This is the value that's returned by the main() function. Normally you'd leave this + as 0 unless you want to indicate an error code. + + @see getApplicationReturnValue + */ + void setApplicationReturnValue (const int newReturnValue) throw(); + + /** Returns the value that has been set as the application's exit code. + @see setApplicationReturnValue + */ + int getApplicationReturnValue() const throw() { return appReturnValue; } + + // These are used by the START_JUCE_APPLICATION() macro and aren't for public use. + + /** @internal */ + static int main (String& commandLine, JUCEApplication* const newApp); + /** @internal */ + static int main (int argc, char* argv[], JUCEApplication* const newApp); + + /** @internal */ + static void sendUnhandledException (const std::exception* const e, + const char* const sourceFile, + const int lineNumber); + + /** @internal */ + ApplicationCommandTarget* getNextCommandTarget(); + /** @internal */ + void getCommandInfo (const CommandID commandID, ApplicationCommandInfo& result); + /** @internal */ + void getAllCommands (Array & commands); + /** @internal */ + bool perform (const InvocationInfo& info); + /** @internal */ + void actionListenerCallback (const String& message); + +private: + + int appReturnValue; + bool stillInitialising; + + static int shutdownAppAndClearUp (const bool useMaximumForce); +}; + +#endif // __JUCE_APPLICATION_JUCEHEADER__ +/********* End of inlined file: juce_Application.h *********/ + +#endif +#ifndef __JUCE_APPLICATIONCOMMANDID_JUCEHEADER__ + +#endif +#ifndef __JUCE_APPLICATIONCOMMANDINFO_JUCEHEADER__ + +#endif +#ifndef __JUCE_APPLICATIONCOMMANDMANAGER_JUCEHEADER__ + +/********* Start of inlined file: juce_ApplicationCommandManager.h *********/ +#ifndef __JUCE_APPLICATIONCOMMANDMANAGER_JUCEHEADER__ +#define __JUCE_APPLICATIONCOMMANDMANAGER_JUCEHEADER__ + +/********* Start of inlined file: juce_AsyncUpdater.h *********/ +#ifndef __JUCE_ASYNCUPDATER_JUCEHEADER__ +#define __JUCE_ASYNCUPDATER_JUCEHEADER__ + +/** + Has a callback method that is triggered asynchronously. + + This object allows an asynchronous callback function to be triggered, for + tasks such as coalescing multiple updates into a single callback later on. + + Basically, one or more calls to the triggerAsyncUpdate() will result in the + message thread calling handleAsyncUpdate() as soon as it can. +*/ +class JUCE_API AsyncUpdater +{ +public: + + /** Creates an AsyncUpdater object. */ + AsyncUpdater() throw(); + + /** Destructor. + + If there are any pending callbacks when the object is deleted, these are lost. + */ + virtual ~AsyncUpdater(); + + /** Causes the callback to be triggered at a later time. + + This method returns immediately, having made sure that a callback + to the handleAsyncUpdate() method will occur as soon as possible. + + If an update callback is already pending but hasn't happened yet, calls + to this method will be ignored. + + It's thread-safe to call this method from any number of threads without + needing to worry about locking. + */ + void triggerAsyncUpdate() throw(); + + /** This will stop any pending updates from happening. + + If called after triggerAsyncUpdate() and before the handleAsyncUpdate() + callback happens, this will cancel the handleAsyncUpdate() callback. + */ + void cancelPendingUpdate() throw(); + + /** If an update has been triggered and is pending, this will invoke it + synchronously. + + Use this as a kind of "flush" operation - if an update is pending, the + handleAsyncUpdate() method will be called immediately; if no update is + pending, then nothing will be done. + */ + void handleUpdateNowIfNeeded(); + + /** Called back to do whatever your class needs to do. + + This method is called by the message thread at the next convenient time + after the triggerAsyncUpdate() method has been called. + */ + virtual void handleAsyncUpdate() = 0; + +private: + + class AsyncUpdaterInternal : public MessageListener + { + public: + AsyncUpdaterInternal() throw() {} + ~AsyncUpdaterInternal() {} + + void handleMessage (const Message&); + + AsyncUpdater* owner; + + private: + AsyncUpdaterInternal (const AsyncUpdaterInternal&); + const AsyncUpdaterInternal& operator= (const AsyncUpdaterInternal&); + }; + + AsyncUpdaterInternal internalAsyncHandler; + bool asyncMessagePending; +}; + +#endif // __JUCE_ASYNCUPDATER_JUCEHEADER__ +/********* End of inlined file: juce_AsyncUpdater.h *********/ + +/********* Start of inlined file: juce_Desktop.h *********/ +#ifndef __JUCE_DESKTOP_JUCEHEADER__ +#define __JUCE_DESKTOP_JUCEHEADER__ + +/********* Start of inlined file: juce_DeletedAtShutdown.h *********/ +#ifndef __JUCE_DELETEDATSHUTDOWN_JUCEHEADER__ +#define __JUCE_DELETEDATSHUTDOWN_JUCEHEADER__ + +/** + Classes derived from this will be automatically deleted when the application exits. + + After JUCEApplication::shutdown() has been called, any objects derived from + DeletedAtShutdown which are still in existence will be deleted in the reverse + order to that in which they were created. + + So if you've got a singleton and don't want to have to explicitly delete it, just + inherit from this and it'll be taken care of. +*/ +class JUCE_API DeletedAtShutdown +{ +protected: + /** Creates a DeletedAtShutdown object. */ + DeletedAtShutdown() throw(); + + /** Destructor. + + It's ok to delete these objects explicitly - it's only the ones left + dangling at the end that will be deleted automatically. + */ + virtual ~DeletedAtShutdown(); + +public: + /** Deletes all extant objects. + + This shouldn't be used by applications, as it's called automatically + in the shutdown code of the JUCEApplication class. + */ + static void deleteAll(); + +private: + DeletedAtShutdown (const DeletedAtShutdown&); + const DeletedAtShutdown& operator= (const DeletedAtShutdown&); +}; + +#endif // __JUCE_DELETEDATSHUTDOWN_JUCEHEADER__ +/********* End of inlined file: juce_DeletedAtShutdown.h *********/ + +/********* Start of inlined file: juce_Timer.h *********/ +#ifndef __JUCE_TIMER_JUCEHEADER__ +#define __JUCE_TIMER_JUCEHEADER__ + +class InternalTimerThread; + +/** + Repeatedly calls a user-defined method at a specified time interval. + + A Timer's timerCallback() method will be repeatedly called at a given + interval. Initially when a Timer object is created, they will do nothing + until the startTimer() method is called, then the message thread will + start calling it back until stopTimer() is called. + + The time interval isn't guaranteed to be precise to any more than maybe + 10-20ms, and the intervals may end up being much longer than requested if the + system is busy. Because it's the message thread that is doing the callbacks, + any messages that take a significant amount of time to process will block + all the timers for that period. + + If you need to have a single callback that is shared by multiple timers with + different frequencies, then the MultiTimer class allows you to do that - its + structure is very similar to the Timer class, but contains multiple timers + internally, each one identified by an ID number. + + @see MultiTimer +*/ +class JUCE_API Timer +{ +protected: + + /** Creates a Timer. + + When created, the timer is stopped, so use startTimer() to get it going. + */ + Timer() throw(); + + /** Creates a copy of another timer. + + Note that this timer won't be started, even if the one you're copying + is running. + */ + Timer (const Timer& other) throw(); + +public: + + /** Destructor. */ + virtual ~Timer(); + + /** The user-defined callback routine that actually gets called periodically. + + It's perfectly ok to call startTimer() or stopTimer() from within this + callback to change the subsequent intervals. + */ + virtual void timerCallback() = 0; + + /** Starts the timer and sets the length of interval required. + + If the timer is already started, this will reset it, so the + time between calling this method and the next timer callback + will not be less than the interval length passed in. + + @param intervalInMilliseconds the interval to use (any values less than 1 will be + rounded up to 1) + */ + void startTimer (const int intervalInMilliseconds) throw(); + + /** Stops the timer. + + No more callbacks will be made after this method returns. + + If this is called from a different thread, any callbacks that may + be currently executing may be allowed to finish before the method + returns. + */ + void stopTimer() throw(); + + /** Checks if the timer has been started. + + @returns true if the timer is running. + */ + bool isTimerRunning() const throw() { return periodMs > 0; } + + /** Returns the timer's interval. + + @returns the timer's interval in milliseconds if it's running, or 0 if it's not. + */ + int getTimerInterval() const throw() { return periodMs; } + +private: + friend class InternalTimerThread; + int countdownMs, periodMs; + Timer* previous; + Timer* next; + + const Timer& operator= (const Timer&); +}; + +#endif // __JUCE_TIMER_JUCEHEADER__ +/********* End of inlined file: juce_Timer.h *********/ + +/** + Classes can implement this interface and register themselves with the Desktop class + to receive callbacks when the currently focused component changes. + + @see Desktop::addFocusChangeListener, Desktop::removeFocusChangeListener +*/ +class JUCE_API FocusChangeListener +{ +public: + /** Destructor. */ + virtual ~FocusChangeListener() {} + + /** Callback to indicate that the currently focused component has changed. */ + virtual void globalFocusChanged (Component* focusedComponent) = 0; +}; + +/** + Describes and controls aspects of the computer's desktop. + +*/ +class JUCE_API Desktop : private DeletedAtShutdown, + private Timer, + private AsyncUpdater +{ +public: + + /** There's only one dektop object, and this method will return it. + */ + static Desktop& JUCE_CALLTYPE getInstance() throw(); + + /** Returns a list of the positions of all the monitors available. + + The first rectangle in the list will be the main monitor area. + + If clippedToWorkArea is true, it will exclude any areas like the taskbar on Windows, + or the menu bar on Mac. If clippedToWorkArea is false, the entire monitor area is returned. + */ + const RectangleList getAllMonitorDisplayAreas (const bool clippedToWorkArea = true) const throw(); + + /** Returns the position and size of the main monitor. + + If clippedToWorkArea is true, it will exclude any areas like the taskbar on Windows, + or the menu bar on Mac. If clippedToWorkArea is false, the entire monitor area is returned. + */ + const Rectangle getMainMonitorArea (const bool clippedToWorkArea = true) const throw(); + + /** Returns the position and size of the monitor which contains this co-ordinate. + + If none of the monitors contains the point, this will just return the + main monitor. + + If clippedToWorkArea is true, it will exclude any areas like the taskbar on Windows, + or the menu bar on Mac. If clippedToWorkArea is false, the entire monitor area is returned. + */ + const Rectangle getMonitorAreaContaining (int x, int y, const bool clippedToWorkArea = true) const throw(); + + /** Returns the mouse position. + + The co-ordinates are relative to the top-left of the main monitor. + */ + static void getMousePosition (int& x, int& y) throw(); + + /** Makes the mouse pointer jump to a given location. + + The co-ordinates are relative to the top-left of the main monitor. + */ + static void setMousePosition (int x, int y) throw(); + + /** Returns the last position at which a mouse button was pressed. + */ + static void getLastMouseDownPosition (int& x, int& y) throw(); + + /** Returns the number of times the mouse button has been clicked since the + app started. + + Each mouse-down event increments this number by 1. + */ + static int getMouseButtonClickCounter() throw(); + + /** This lets you prevent the screensaver from becoming active. + + Handy if you're running some sort of presentation app where having a screensaver + appear would be annoying. + + Pass false to disable the screensaver, and true to re-enable it. (Note that this + won't enable a screensaver unless the user has actually set one up). + + The disablement will only happen while the Juce application is the foreground + process - if another task is running in front of it, then the screensaver will + be unaffected. + + @see isScreenSaverEnabled + */ + static void setScreenSaverEnabled (const bool isEnabled) throw(); + + /** Returns true if the screensaver has not been turned off. + + This will return the last value passed into setScreenSaverEnabled(). Note that + it won't tell you whether the user is actually using a screen saver, just + whether this app is deliberately preventing one from running. + + @see setScreenSaverEnabled + */ + static bool isScreenSaverEnabled() throw(); + + /** Registers a MouseListener that will receive all mouse events that occur on + any component. + + @see removeGlobalMouseListener + */ + void addGlobalMouseListener (MouseListener* const listener) throw(); + + /** Unregisters a MouseListener that was added with the addGlobalMouseListener() + method. + + @see addGlobalMouseListener + */ + void removeGlobalMouseListener (MouseListener* const listener) throw(); + + /** Registers a MouseListener that will receive a callback whenever the focused + component changes. + */ + void addFocusChangeListener (FocusChangeListener* const listener) throw(); + + /** Unregisters a listener that was added with addFocusChangeListener(). */ + void removeFocusChangeListener (FocusChangeListener* const listener) throw(); + + /** Returns the number of components that are currently active as top-level + desktop windows. + + @see getComponent, Component::addToDesktop + */ + int getNumComponents() const throw(); + + /** Returns one of the top-level desktop window components. + + The index is from 0 to getNumComponents() - 1. This could return 0 if the + index is out-of-range. + + @see getNumComponents, Component::addToDesktop + */ + Component* getComponent (const int index) const throw(); + + /** Finds the component at a given screen location. + + This will drill down into top-level windows to find the child component at + the given position. + + Returns 0 if the co-ordinates are inside a non-Juce window. + */ + Component* findComponentAt (const int screenX, + const int screenY) const; + + juce_UseDebuggingNewOperator + + /** Tells this object to refresh its idea of what the screen resolution is. + + (Called internally by the native code). + */ + void refreshMonitorSizes() throw(); + + /** True if the OS supports semitransparent windows */ + static bool canUseSemiTransparentWindows() throw(); + +private: + + friend class Component; + friend class ComponentPeer; + SortedSet mouseListeners, focusListeners; + VoidArray desktopComponents; + + friend class DeletedAtShutdown; + friend class TopLevelWindowManager; + Desktop() throw(); + ~Desktop() throw(); + + Array monitorCoordsClipped, monitorCoordsUnclipped; + int lastMouseX, lastMouseY; + + void timerCallback(); + void sendMouseMove(); + void resetTimer() throw(); + + int getNumDisplayMonitors() const throw(); + const Rectangle getDisplayMonitorCoordinates (const int index, const bool clippedToWorkArea) const throw(); + + void addDesktopComponent (Component* const c) throw(); + void removeDesktopComponent (Component* const c) throw(); + void componentBroughtToFront (Component* const c) throw(); + + void triggerFocusCallback() throw(); + void handleAsyncUpdate(); + + Desktop (const Desktop&); + const Desktop& operator= (const Desktop&); +}; + +#endif // __JUCE_DESKTOP_JUCEHEADER__ +/********* End of inlined file: juce_Desktop.h *********/ + +class KeyPressMappingSet; +class ApplicationCommandManagerListener; + +/** + One of these objects holds a list of all the commands your app can perform, + and despatches these commands when needed. + + Application commands are a good way to trigger actions in your app, e.g. "Quit", + "Copy", "Paste", etc. Menus, buttons and keypresses can all be given commands + to invoke automatically, which means you don't have to handle the result of a menu + or button click manually. Commands are despatched to ApplicationCommandTarget objects + which can choose which events they want to handle. + + This architecture also allows for nested ApplicationCommandTargets, so that for example + you could have two different objects, one inside the other, both of which can respond to + a "delete" command. Depending on which one has focus, the command will be sent to the + appropriate place, regardless of whether it was triggered by a menu, keypress or some other + method. + + To set up your app to use commands, you'll need to do the following: + + - Create a global ApplicationCommandManager to hold the list of all possible + commands. (This will also manage a set of key-mappings for them). + + - Make some of your UI components (or other objects) inherit from ApplicationCommandTarget. + This allows the object to provide a list of commands that it can perform, and + to handle them. + + - Register each type of command using ApplicationCommandManager::registerAllCommandsForTarget(), + or ApplicationCommandManager::registerCommand(). + + - If you want key-presses to trigger your commands, use the ApplicationCommandManager::getKeyMappings() + method to access the key-mapper object, which you will need to register as a key-listener + in whatever top-level component you're using. See the KeyPressMappingSet class for more help + about setting this up. + + - Use methods such as PopupMenu::addCommandItem() or Button::setCommandToTrigger() to + cause these commands to be invoked automatically. + + - Commands can be invoked directly by your code using ApplicationCommandManager::invokeDirectly(). + + When a command is invoked, the ApplicationCommandManager will try to choose the best + ApplicationCommandTarget to receive the specified command. To do this it will use the + current keyboard focus to see which component might be interested, and will search the + component hierarchy for those that also implement the ApplicationCommandTarget interface. + If an ApplicationCommandTarget isn't interested in the command that is being invoked, then + the next one in line will be tried (see the ApplicationCommandTarget::getNextCommandTarget() + method), and so on until ApplicationCommandTarget::getNextCommandTarget() returns 0. At this + point if the command still hasn't been performed, it will be passed to the current + JUCEApplication object (which is itself an ApplicationCommandTarget). + + To exert some custom control over which ApplicationCommandTarget is chosen to invoke a command, + you can override the ApplicationCommandManager::getFirstCommandTarget() method and choose + the object yourself. + + @see ApplicationCommandTarget, ApplicationCommandInfo +*/ +class JUCE_API ApplicationCommandManager : private AsyncUpdater, + private FocusChangeListener +{ +public: + + /** Creates an ApplicationCommandManager. + + Once created, you'll need to register all your app's commands with it, using + ApplicationCommandManager::registerAllCommandsForTarget() or + ApplicationCommandManager::registerCommand(). + */ + ApplicationCommandManager(); + + /** Destructor. + + Make sure that you don't delete this if pointers to it are still being used by + objects such as PopupMenus or Buttons. + */ + virtual ~ApplicationCommandManager(); + + /** Clears the current list of all commands. + + Note that this will also clear the contents of the KeyPressMappingSet. + */ + void clearCommands(); + + /** Adds a command to the list of registered commands. + + @see registerAllCommandsForTarget + */ + void registerCommand (const ApplicationCommandInfo& newCommand); + + /** Adds all the commands that this target publishes to the manager's list. + + This will use ApplicationCommandTarget::getAllCommands() and ApplicationCommandTarget::getCommandInfo() + to get details about all the commands that this target can do, and will call + registerCommand() to add each one to the manger's list. + + @see registerCommand + */ + void registerAllCommandsForTarget (ApplicationCommandTarget* target); + + /** Removes the command with a specified ID. + + Note that this will also remove any key mappings that are mapped to the command. + */ + void removeCommand (const CommandID commandID); + + /** This should be called to tell the manager that one of its registered commands may have changed + its active status. + + Because the command manager only finds out whether a command is active or inactive by querying + the current ApplicationCommandTarget, this is used to tell it that things may have changed. It + allows things like buttons to update their enablement, etc. + + This method will cause an asynchronous call to ApplicationCommandManagerListener::applicationCommandListChanged() + for any registered listeners. + */ + void commandStatusChanged(); + + /** Returns the number of commands that have been registered. + + @see registerCommand + */ + int getNumCommands() const throw() { return commands.size(); } + + /** Returns the details about one of the registered commands. + + The index is between 0 and (getNumCommands() - 1). + */ + const ApplicationCommandInfo* getCommandForIndex (const int index) const throw() { return commands [index]; } + + /** Returns the details about a given command ID. + + This will search the list of registered commands for one with the given command + ID number, and return its associated info. If no matching command is found, this + will return 0. + */ + const ApplicationCommandInfo* getCommandForID (const CommandID commandID) const throw(); + + /** Returns the name field for a command. + + An empty string is returned if no command with this ID has been registered. + @see getDescriptionOfCommand + */ + const String getNameOfCommand (const CommandID commandID) const throw(); + + /** Returns the description field for a command. + + An empty string is returned if no command with this ID has been registered. If the + command has no description, this will return its short name field instead. + + @see getNameOfCommand + */ + const String getDescriptionOfCommand (const CommandID commandID) const throw(); + + /** Returns the list of categories. + + This will go through all registered commands, and return a list of all the distict + categoryName values from their ApplicationCommandInfo structure. + + @see getCommandsInCategory() + */ + const StringArray getCommandCategories() const throw(); + + /** Returns a list of all the command UIDs in a particular category. + + @see getCommandCategories() + */ + const Array getCommandsInCategory (const String& categoryName) const throw(); + + /** Returns the manager's internal set of key mappings. + + This object can be used to edit the keypresses. To actually link this object up + to invoke commands when a key is pressed, see the comments for the KeyPressMappingSet + class. + + @see KeyPressMappingSet + */ + KeyPressMappingSet* getKeyMappings() const throw() { return keyMappings; } + + /** Invokes the given command directly, sending it to the default target. + + This is just an easy way to call invoke() without having to fill out the InvocationInfo + structure. + */ + bool invokeDirectly (const CommandID commandID, + const bool asynchronously); + + /** Sends a command to the default target. + + This will choose a target using getFirstCommandTarget(), and send the specified command + to it using the ApplicationCommandTarget::invoke() method. This means that if the + first target can't handle the command, it will be passed on to targets further down the + chain (see ApplicationCommandTarget::invoke() for more info). + + @param invocationInfo this must be correctly filled-in, describing the context for + the invocation. + @param asynchronously if false, the command will be performed before this method returns. + If true, a message will be posted so that the command will be performed + later on the message thread, and this method will return immediately. + + @see ApplicationCommandTarget::invoke + */ + bool invoke (const ApplicationCommandTarget::InvocationInfo& invocationInfo, + const bool asynchronously); + + /** Chooses the ApplicationCommandTarget to which a command should be sent. + + Whenever the manager needs to know which target a command should be sent to, it calls + this method to determine the first one to try. + + By default, this method will return the target that was set by calling setFirstCommandTarget(). + If no target is set, it will return the result of findDefaultComponentTarget(). + + If you need to make sure all commands go via your own custom target, then you can + either use setFirstCommandTarget() to specify a single target, or override this method + if you need more complex logic to choose one. + + It may return 0 if no targets are available. + + @see getTargetForCommand, invoke, invokeDirectly + */ + virtual ApplicationCommandTarget* getFirstCommandTarget (const CommandID commandID); + + /** Sets a target to be returned by getFirstCommandTarget(). + + If this is set to 0, then getFirstCommandTarget() will by default return the + result of findDefaultComponentTarget(). + + If you use this to set a target, make sure you call setFirstCommandTarget (0) before + deleting the target object. + */ + void setFirstCommandTarget (ApplicationCommandTarget* const newTarget) throw(); + + /** Tries to find the best target to use to perform a given command. + + This will call getFirstCommandTarget() to find the preferred target, and will + check whether that target can handle the given command. If it can't, then it'll use + ApplicationCommandTarget::getNextCommandTarget() to find the next one to try, and + so on until no more are available. + + If no targets are found that can perform the command, this method will return 0. + + If a target is found, then it will get the target to fill-in the upToDateInfo + structure with the latest info about that command, so that the caller can see + whether the command is disabled, ticked, etc. + */ + ApplicationCommandTarget* getTargetForCommand (const CommandID commandID, + ApplicationCommandInfo& upToDateInfo); + + /** Registers a listener that will be called when various events occur. */ + void addListener (ApplicationCommandManagerListener* const listener) throw(); + + /** Deregisters a previously-added listener. */ + void removeListener (ApplicationCommandManagerListener* const listener) throw(); + + /** Looks for a suitable command target based on which Components have the keyboard focus. + + This is used by the default implementation of ApplicationCommandTarget::getFirstCommandTarget(), + but is exposed here in case it's useful. + + It tries to pick the best ApplicationCommandTarget by looking at focused components, top level + windows, etc., and using the findTargetForComponent() method. + */ + static ApplicationCommandTarget* findDefaultComponentTarget(); + + /** Examines this component and all its parents in turn, looking for the first one + which is a ApplicationCommandTarget. + + Returns the first ApplicationCommandTarget that it finds, or 0 if none of them implement + that class. + */ + static ApplicationCommandTarget* findTargetForComponent (Component* component); + + juce_UseDebuggingNewOperator + +private: + + OwnedArray commands; + SortedSet listeners; + KeyPressMappingSet* keyMappings; + ApplicationCommandTarget* firstTarget; + + void sendListenerInvokeCallback (const ApplicationCommandTarget::InvocationInfo& info) const; + void handleAsyncUpdate(); + void globalFocusChanged (Component*); + + // xxx this is just here to cause a compile error in old code that hasn't been changed to use the new + // version of this method. + virtual short getFirstCommandTarget() { return 0; } +}; + +/** + A listener that receives callbacks from an ApplicationCommandManager when + commands are invoked or the command list is changed. + + @see ApplicationCommandManager::addListener, ApplicationCommandManager::removeListener + +*/ +class JUCE_API ApplicationCommandManagerListener +{ +public: + + /** Destructor. */ + virtual ~ApplicationCommandManagerListener() {} + + /** Called when an app command is about to be invoked. */ + virtual void applicationCommandInvoked (const ApplicationCommandTarget::InvocationInfo& info) = 0; + + /** Called when commands are registered or deregistered from the + command manager, or when commands are made active or inactive. + + Note that if you're using this to watch for changes to whether a command is disabled, + you'll need to make sure that ApplicationCommandManager::commandStatusChanged() is called + whenever the status of your command might have changed. + */ + virtual void applicationCommandListChanged() = 0; +}; + +#endif // __JUCE_APPLICATIONCOMMANDMANAGER_JUCEHEADER__ +/********* End of inlined file: juce_ApplicationCommandManager.h *********/ + +#endif +#ifndef __JUCE_APPLICATIONCOMMANDTARGET_JUCEHEADER__ + +#endif +#ifndef __JUCE_APPLICATIONPROPERTIES_JUCEHEADER__ + +/********* Start of inlined file: juce_ApplicationProperties.h *********/ +#ifndef __JUCE_APPLICATIONPROPERTIES_JUCEHEADER__ +#define __JUCE_APPLICATIONPROPERTIES_JUCEHEADER__ + +/********* Start of inlined file: juce_PropertiesFile.h *********/ +#ifndef __JUCE_PROPERTIESFILE_JUCEHEADER__ +#define __JUCE_PROPERTIESFILE_JUCEHEADER__ + +/********* Start of inlined file: juce_ChangeBroadcaster.h *********/ +#ifndef __JUCE_CHANGEBROADCASTER_JUCEHEADER__ +#define __JUCE_CHANGEBROADCASTER_JUCEHEADER__ + +/********* Start of inlined file: juce_ChangeListenerList.h *********/ +#ifndef __JUCE_CHANGELISTENERLIST_JUCEHEADER__ +#define __JUCE_CHANGELISTENERLIST_JUCEHEADER__ + +/********* Start of inlined file: juce_ChangeListener.h *********/ +#ifndef __JUCE_CHANGELISTENER_JUCEHEADER__ +#define __JUCE_CHANGELISTENER_JUCEHEADER__ + +/** + Receives callbacks about changes to some kind of object. + + Many objects use a ChangeListenerList to keep a set of listeners which they + will inform when something changes. A subclass of ChangeListener + is used to receive these callbacks. + + Note that the major difference between an ActionListener and a ChangeListener + is that for a ChangeListener, multiple changes will be coalesced into fewer + callbacks, but ActionListeners perform one callback for every event posted. + + @see ChangeListenerList, ChangeBroadcaster, ActionListener +*/ +class JUCE_API ChangeListener +{ +public: + /** Destructor. */ + virtual ~ChangeListener() {} + + /** Overridden by your subclass to receive the callback. + + @param objectThatHasChanged the value that was passed to the + ChangeListenerList::sendChangeMessage() method + */ + virtual void changeListenerCallback (void* objectThatHasChanged) = 0; +}; + +#endif // __JUCE_CHANGELISTENER_JUCEHEADER__ +/********* End of inlined file: juce_ChangeListener.h *********/ + +/** + A set of ChangeListeners. + + Listeners can be added and removed from the list, and change messages can be + broadcast to all the listeners. + + @see ChangeListener, ChangeBroadcaster +*/ +class JUCE_API ChangeListenerList : public MessageListener +{ +public: + + /** Creates an empty list. */ + ChangeListenerList() throw(); + + /** Destructor. */ + ~ChangeListenerList() throw(); + + /** Adds a listener to the list. + + (Trying to add a listener that's already on the list will have no effect). + */ + void addChangeListener (ChangeListener* const listener) throw(); + + /** Removes a listener from the list. + + If the listener isn't on the list, this won't have any effect. + */ + void removeChangeListener (ChangeListener* const listener) throw(); + + /** Removes all listeners from the list. */ + void removeAllChangeListeners() throw(); + + /** Posts an asynchronous change message to all the listeners. + + If a message has already been sent and hasn't yet been delivered, this + method won't send another - in this way it coalesces multiple frequent + changes into fewer actual callbacks to the ChangeListeners. Contrast this + with the ActionListener, which posts a new event for every call to its + sendActionMessage() method. + + Only listeners which are on the list when the change event is delivered + will receive the event - and this may include listeners that weren't on + the list when the change message was sent. + + @param objectThatHasChanged this pointer is passed to the + ChangeListener::changeListenerCallback() method, + and can be any value the application needs + @see sendSynchronousChangeMessage + */ + void sendChangeMessage (void* objectThatHasChanged) throw(); + + /** This will synchronously callback all the ChangeListeners. + + Use this if you need to synchronously force a call to all the + listeners' ChangeListener::changeListenerCallback() methods. + */ + void sendSynchronousChangeMessage (void* objectThatHasChanged); + + /** If a change message has been sent but not yet dispatched, this will + use sendSynchronousChangeMessage() to make the callback immediately. + */ + void dispatchPendingMessages(); + + /** @internal */ + void handleMessage (const Message&); + + juce_UseDebuggingNewOperator + +private: + SortedSet listeners; + CriticalSection lock; + void* lastChangedObject; + bool messagePending; + + ChangeListenerList (const ChangeListenerList&); + const ChangeListenerList& operator= (const ChangeListenerList&); +}; + +#endif // __JUCE_CHANGELISTENERLIST_JUCEHEADER__ +/********* End of inlined file: juce_ChangeListenerList.h *********/ + +/** Manages a list of ChangeListeners, and can send them messages. + + To quickly add methods to your class that can add/remove change + listeners and broadcast to them, you can derive from this. + + @see ChangeListenerList, ChangeListener +*/ +class JUCE_API ChangeBroadcaster +{ +public: + + /** Creates an ChangeBroadcaster. */ + ChangeBroadcaster() throw(); + + /** Destructor. */ + virtual ~ChangeBroadcaster(); + + /** Adds a listener to the list. + + (Trying to add a listener that's already on the list will have no effect). + */ + void addChangeListener (ChangeListener* const listener) throw(); + + /** Removes a listener from the list. + + If the listener isn't on the list, this won't have any effect. + */ + void removeChangeListener (ChangeListener* const listener) throw(); + + /** Removes all listeners from the list. */ + void removeAllChangeListeners() throw(); + + /** Broadcasts a change message to all the registered listeners. + + The message will be delivered asynchronously by the event thread, so this + method will not directly call any of the listeners. For a synchronous + message, use sendSynchronousChangeMessage(). + + @see ChangeListenerList::sendActionMessage + */ + void sendChangeMessage (void* objectThatHasChanged) throw(); + + /** Sends a synchronous change message to all the registered listeners. + + @see ChangeListenerList::sendSynchronousChangeMessage + */ + void sendSynchronousChangeMessage (void* objectThatHasChanged); + + /** If a change message has been sent but not yet dispatched, this will + use sendSynchronousChangeMessage() to make the callback immediately. + */ + void dispatchPendingMessages(); + +private: + + ChangeListenerList changeListenerList; + + ChangeBroadcaster (const ChangeBroadcaster&); + const ChangeBroadcaster& operator= (const ChangeBroadcaster&); +}; + +#endif // __JUCE_CHANGEBROADCASTER_JUCEHEADER__ +/********* End of inlined file: juce_ChangeBroadcaster.h *********/ + +/** Wrapper on a file that stores a list of key/value data pairs. + + Useful for storing application settings, etc. See the PropertySet class for + the interfaces that read and write values. + + Not designed for very large amounts of data, as it keeps all the values in + memory and writes them out to disk lazily when they are changed. + + Because this class derives from ChangeBroadcaster, ChangeListeners can be registered + with it, and these will be signalled when a value changes. + + @see PropertySet +*/ +class JUCE_API PropertiesFile : public PropertySet, + public ChangeBroadcaster, + private Timer +{ +public: + + enum FileFormatOptions + { + ignoreCaseOfKeyNames = 1, + storeAsBinary = 2, + storeAsCompressedBinary = 4, + storeAsXML = 8 + }; + + /** + Creates a PropertiesFile object. + + @param file the file to use + @param millisecondsBeforeSaving if this is zero or greater, then after a value + is changed, the object will wait for this amount + of time and then save the file. If zero, the file + will be written to disk immediately on being changed + (which might be slow, as it'll re-write synchronously + each time a value-change method is called). If it is + less than zero, the file won't be saved until + save() or saveIfNeeded() are explicitly called. + @param options a combination of the flags in the FileFormatOptions + enum, which specify the type of file to save, and other + options. + */ + PropertiesFile (const File& file, + const int millisecondsBeforeSaving, + const int options) throw(); + + /** Destructor. + + When deleted, the file will first call saveIfNeeded() to flush any changes to disk. + */ + ~PropertiesFile(); + + /** This will flush all the values to disk if they've changed since the last + time they were saved. + + Returns false if it fails to write to the file for some reason (maybe because + it's read-only or the directory doesn't exist or something). + + @see save + */ + bool saveIfNeeded(); + + /** This will force a write-to-disk of the current values, regardless of whether + anything has changed since the last save. + + Returns false if it fails to write to the file for some reason (maybe because + it's read-only or the directory doesn't exist or something). + + @see saveIfNeeded + */ + bool save(); + + /** Returns true if the properties have been altered since the last time they were + saved. + */ + bool needsToBeSaved() const throw(); + + /** Returns the file that's being used. */ + const File getFile() const throw(); + + /** Handy utility to create a properties file in whatever the standard OS-specific + location is for these things. + + This uses getDefaultAppSettingsFile() to decide what file to create, then + creates a PropertiesFile object with the specified properties. See + getDefaultAppSettingsFile() and the class's constructor for descriptions of + what the parameters do. + + @see getDefaultAppSettingsFile + */ + static PropertiesFile* createDefaultAppPropertiesFile (const String& applicationName, + const String& fileNameSuffix, + const String& folderName, + const bool commonToAllUsers, + const int millisecondsBeforeSaving, + const int propertiesFileOptions); + + /** Handy utility to choose a file in the standard OS-dependent location for application + settings files. + + So on a Mac, this will return a file called: + ~/Library/Preferences/[folderName]/[applicationName].[fileNameSuffix] + + On Windows it'll return something like: + C:\\Documents and Settings\\username\\Application Data\\[folderName]\\[applicationName].[fileNameSuffix] + + On Linux it'll return + ~/.[folderName]/[applicationName].[fileNameSuffix] + + If you pass an empty string as the folder name, it'll use the app name for this (or + omit the folder name on the Mac). + + If commonToAllUsers is true, then this will return the same file for all users of the + computer, regardless of the current user. If it is false, the file will be specific to + only the current user. Use this to choose whether you're saving settings that are common + or user-specific. + */ + static const File getDefaultAppSettingsFile (const String& applicationName, + const String& fileNameSuffix, + const String& folderName, + const bool commonToAllUsers); + + juce_UseDebuggingNewOperator + +protected: + virtual void propertyChanged(); + +private: + + File file; + int timerInterval; + const int options; + bool needsWriting; + + void timerCallback(); + + PropertiesFile (const PropertiesFile&); + const PropertiesFile& operator= (const PropertiesFile&); +}; + +#endif // __JUCE_PROPERTIESFILE_JUCEHEADER__ +/********* End of inlined file: juce_PropertiesFile.h *********/ + +/** + Manages a collection of properties. + + This is a slightly higher-level wrapper for PropertiesFile, which can be used + as a singleton. + + It holds two different PropertiesFile objects internally, one for user-specific + settings (stored in your user directory), and one for settings that are common to + all users (stored in a folder accessible to all users). + + The class manages the creation of these files on-demand, allowing access via the + getUserSettings() and getCommonSettings() methods. It also has a few handy + methods like testWriteAccess() to check that the files can be saved. + + If you're using one of these as a singleton, then your app's start-up code should + first of all call setStorageParameters() to tell it the parameters to use to create + the properties files. + + @see PropertiesFile +*/ +class JUCE_API ApplicationProperties : public DeletedAtShutdown +{ +public: + + /** + Creates an ApplicationProperties object. + + Before using it, you must call setStorageParameters() to give it the info + it needs to create the property files. + */ + ApplicationProperties() throw(); + + /** Destructor. + */ + ~ApplicationProperties(); + + juce_DeclareSingleton (ApplicationProperties, false) + + /** Gives the object the information it needs to create the appropriate properties files. + + See the comments for PropertiesFile::createDefaultAppPropertiesFile() for more + info about how these parameters are used. + */ + void setStorageParameters (const String& applicationName, + const String& fileNameSuffix, + const String& folderName, + const int millisecondsBeforeSaving, + const int propertiesFileOptions) throw(); + + /** Tests whether the files can be successfully written to, and can show + an error message if not. + + Returns true if none of the tests fail. + + @param testUserSettings if true, the user settings file will be tested + @param testCommonSettings if true, the common settings file will be tested + @param showWarningDialogOnFailure if true, the method will show a helpful error + message box if either of the tests fail + */ + bool testWriteAccess (const bool testUserSettings, + const bool testCommonSettings, + const bool showWarningDialogOnFailure); + + /** Returns the user settings file. + + The first time this is called, it will create and load the properties file. + + Note that when you search the user PropertiesFile for a value that it doesn't contain, + the common settings are used as a second-chance place to look. This is done via the + PropertySet::setFallbackPropertySet() method - by default the common settings are set + to the fallback for the user settings. + + @see getCommonSettings + */ + PropertiesFile* getUserSettings() throw(); + + /** Returns the common settings file. + + The first time this is called, it will create and load the properties file. + + @param returnUserPropsIfReadOnly if this is true, and the common properties file is + read-only (e.g. because the user doesn't have permission to write + to shared files), then this will return the user settings instead, + (like getUserSettings() would do). This is handy if you'd like to + write a value to the common settings, but if that's no possible, + then you'd rather write to the user settings than none at all. + If returnUserPropsIfReadOnly is false, this method will always return + the common settings, even if any changes to them can't be saved. + @see getUserSettings + */ + PropertiesFile* getCommonSettings (const bool returnUserPropsIfReadOnly) throw(); + + /** Saves both files if they need to be saved. + + @see PropertiesFile::saveIfNeeded + */ + bool saveIfNeeded(); + + /** Flushes and closes both files if they are open. + + This flushes any pending changes to disk with PropertiesFile::saveIfNeeded() + and closes both files. They will then be re-opened the next time getUserSettings() + or getCommonSettings() is called. + */ + void closeFiles(); + + juce_UseDebuggingNewOperator + +private: + + PropertiesFile* userProps; + PropertiesFile* commonProps; + + String appName, fileSuffix, folderName; + int msBeforeSaving, options; + int commonSettingsAreReadOnly; + + ApplicationProperties (const ApplicationProperties&); + const ApplicationProperties& operator= (const ApplicationProperties&); + + void openFiles() throw(); +}; + +#endif // __JUCE_APPLICATIONPROPERTIES_JUCEHEADER__ +/********* End of inlined file: juce_ApplicationProperties.h *********/ + +#endif +#ifndef __JUCE_DELETEDATSHUTDOWN_JUCEHEADER__ + +#endif +#ifndef __JUCE_PROPERTIESFILE_JUCEHEADER__ + +#endif +#ifndef __JUCE_SYSTEMCLIPBOARD_JUCEHEADER__ + +/********* Start of inlined file: juce_SystemClipboard.h *********/ +#ifndef __JUCE_SYSTEMCLIPBOARD_JUCEHEADER__ +#define __JUCE_SYSTEMCLIPBOARD_JUCEHEADER__ + +/** + Handles reading/writing to the system's clipboard. +*/ +class JUCE_API SystemClipboard +{ +public: + /** Copies a string of text onto the clipboard */ + static void copyTextToClipboard (const String& text) throw(); + + /** Gets the current clipboard's contents. + + Obviously this might have come from another app, so could contain + anything.. + */ + static const String getTextFromClipboard() throw(); +}; + +#endif // __JUCE_SYSTEMCLIPBOARD_JUCEHEADER__ +/********* End of inlined file: juce_SystemClipboard.h *********/ + +#endif +#ifndef __JUCE_MIDIBUFFER_JUCEHEADER__ + +/********* Start of inlined file: juce_MidiBuffer.h *********/ +#ifndef __JUCE_MIDIBUFFER_JUCEHEADER__ +#define __JUCE_MIDIBUFFER_JUCEHEADER__ + +/********* Start of inlined file: juce_MidiMessage.h *********/ +#ifndef __JUCE_MIDIMESSAGE_JUCEHEADER__ +#define __JUCE_MIDIMESSAGE_JUCEHEADER__ + +/** + Encapsulates a MIDI message. + + @see MidiMessageSequence, MidiOutput, MidiInput +*/ +class JUCE_API MidiMessage +{ +public: + + /** Creates a 3-byte short midi message. + + @param byte1 message byte 1 + @param byte2 message byte 2 + @param byte3 message byte 3 + @param timeStamp the time to give the midi message - this value doesn't + use any particular units, so will be application-specific + */ + MidiMessage (const int byte1, + const int byte2, + const int byte3, + const double timeStamp = 0) throw(); + + /** Creates a 2-byte short midi message. + + @param byte1 message byte 1 + @param byte2 message byte 2 + @param timeStamp the time to give the midi message - this value doesn't + use any particular units, so will be application-specific + */ + MidiMessage (const int byte1, + const int byte2, + const double timeStamp = 0) throw(); + + /** Creates a 1-byte short midi message. + + @param byte1 message byte 1 + @param timeStamp the time to give the midi message - this value doesn't + use any particular units, so will be application-specific + */ + MidiMessage (const int byte1, + const double timeStamp = 0) throw(); + + /** Creates a midi message from a block of data. */ + MidiMessage (const uint8* const data, + const int dataSize, + const double timeStamp = 0) throw(); + + /** Reads the next midi message from some data. + + This will read as many bytes from a data stream as it needs to make a + complete message, and will return the number of bytes it used. This lets + you read a sequence of midi messages from a file or stream. + + @param data the data to read from + @param size the maximum number of bytes it's allowed to read + @param numBytesUsed returns the number of bytes that were actually needed + @param lastStatusByte in a sequence of midi messages, the initial byte + can be dropped from a message if it's the same as the + first byte of the previous message, so this lets you + supply the byte to use if the first byte of the message + has in fact been dropped. + @param timeStamp the time to give the midi message - this value doesn't + use any particular units, so will be application-specific + */ + MidiMessage (const uint8* data, + int size, + int& numBytesUsed, + uint8 lastStatusByte, + double timeStamp = 0) throw(); + + /** Creates a copy of another midi message. */ + MidiMessage (const MidiMessage& other) throw(); + + /** Creates a copy of another midi message, with a different timestamp. */ + MidiMessage (const MidiMessage& other, + const double newTimeStamp) throw(); + + /** Destructor. */ + ~MidiMessage() throw(); + + /** Copies this message from another one. */ + const MidiMessage& operator= (const MidiMessage& other) throw(); + + /** Returns a pointer to the raw midi data. + + @see getRawDataSize + */ + uint8* getRawData() const throw() { return data; } + + /** Returns the number of bytes of data in the message. + + @see getRawData + */ + int getRawDataSize() const throw() { return size; } + + /** Returns the timestamp associated with this message. + + The units for the timestamp will be application-specific. + + @see setTimeStamp, addToTimeStamp + */ + double getTimeStamp() const throw() { return timeStamp; } + + /** Changes the message's associated timestamp. + + The units for the timestamp will be application-specific. + + @see addToTimeStamp, getTimeStamp + */ + void setTimeStamp (const double newTimestamp) throw() { timeStamp = newTimestamp; } + + /** Adds a value to the message's timestamp. + + The units for the timestamp will be application-specific. + */ + void addToTimeStamp (const double delta) throw() { timeStamp += delta; } + + /** Returns the midi channel associated with the message. + + @returns a value 1 to 16 if the message has a channel, or 0 if it hasn't (e.g. + if it's a sysex) + @see isForChannel, setChannel + */ + int getChannel() const throw(); + + /** Returns true if the message applies to the given midi channel. + + @param channelNumber the channel number to look for, in the range 1 to 16 + @see getChannel, setChannel + */ + bool isForChannel (const int channelNumber) const throw(); + + /** Changes the message's midi channel. + + This won't do anything for non-channel messages like sysexes. + + @param newChannelNumber the channel number to change it to, in the range 1 to 16 + */ + void setChannel (const int newChannelNumber) throw(); + + /** Returns true if this is a system-exclusive message. + */ + bool isSysEx() const throw(); + + /** Returns a pointer to the sysex data inside the message. + + If this event isn't a sysex event, it'll return 0. + + @see getSysExDataSize + */ + const uint8* getSysExData() const throw(); + + /** Returns the size of the sysex data. + + This value excludes the 0xf0 header byte and the 0xf7 at the end. + + @see getSysExData + */ + int getSysExDataSize() const throw(); + + /** Returns true if this message is a 'key-down' event. + + This will return false for a note-on event with a velocity of 0. + + @see isNoteOff, getNoteNumber, getVelocity, noteOn + */ + bool isNoteOn() const throw(); + + /** Creates a key-down message (using a floating-point velocity). + + @param channel the midi channel, in the range 1 to 16 + @param noteNumber the key number, 0 to 127 + @param velocity in the range 0 to 1.0 + @see isNoteOn + */ + static const MidiMessage noteOn (const int channel, + const int noteNumber, + const float velocity) throw(); + + /** Creates a key-down message (using an integer velocity). + + @param channel the midi channel, in the range 1 to 16 + @param noteNumber the key number, 0 to 127 + @param velocity in the range 0 to 127 + @see isNoteOn + */ + static const MidiMessage noteOn (const int channel, + const int noteNumber, + const uint8 velocity) throw(); + + /** Returns true if this message is a 'key-up' event. + + This will also return true for a note-on event with a velocity of 0. + + @see isNoteOn, getNoteNumber, getVelocity, noteOff + */ + bool isNoteOff() const throw(); + + /** Creates a key-up message. + + @param channel the midi channel, in the range 1 to 16 + @param noteNumber the key number, 0 to 127 + @see isNoteOff + */ + static const MidiMessage noteOff (const int channel, + const int noteNumber) throw(); + + /** Returns true if this message is a 'key-down' or 'key-up' event. + + @see isNoteOn, isNoteOff + */ + bool isNoteOnOrOff() const throw(); + + /** Returns the midi note number for note-on and note-off messages. + + If the message isn't a note-on or off, the value returned will be + meaningless. + + @see isNoteOff, getMidiNoteName, getMidiNoteInHertz, setNoteNumber + */ + int getNoteNumber() const throw(); + + /** Changes the midi note number of a note-on or note-off message. + + If the message isn't a note on or off, this will do nothing. + */ + void setNoteNumber (const int newNoteNumber) throw(); + + /** Returns the velocity of a note-on or note-off message. + + The value returned will be in the range 0 to 127. + + If the message isn't a note-on or off event, it will return 0. + + @see getFloatVelocity + */ + uint8 getVelocity() const throw(); + + /** Returns the velocity of a note-on or note-off message. + + The value returned will be in the range 0 to 1.0 + + If the message isn't a note-on or off event, it will return 0. + + @see getVelocity, setVelocity + */ + float getFloatVelocity() const throw(); + + /** Changes the velocity of a note-on or note-off message. + + If the message isn't a note on or off, this will do nothing. + + @param newVelocity the new velocity, in the range 0 to 1.0 + @see getFloatVelocity, multiplyVelocity + */ + void setVelocity (const float newVelocity) throw(); + + /** Multiplies the velocity of a note-on or note-off message by a given amount. + + If the message isn't a note on or off, this will do nothing. + + @param scaleFactor the value by which to multiply the velocity + @see setVelocity + */ + void multiplyVelocity (const float scaleFactor) throw(); + + /** Returns true if the message is a program (patch) change message. + + @see getProgramChangeNumber, getGMInstrumentName + */ + bool isProgramChange() const throw(); + + /** Returns the new program number of a program change message. + + If the message isn't a program change, the value returned will be + nonsense. + + @see isProgramChange, getGMInstrumentName + */ + int getProgramChangeNumber() const throw(); + + /** Creates a program-change message. + + @param channel the midi channel, in the range 1 to 16 + @param programNumber the midi program number, 0 to 127 + @see isProgramChange, getGMInstrumentName + */ + static const MidiMessage programChange (const int channel, + const int programNumber) throw(); + + /** Returns true if the message is a pitch-wheel move. + + @see getPitchWheelValue, pitchWheel + */ + bool isPitchWheel() const throw(); + + /** Returns the pitch wheel position from a pitch-wheel move message. + + The value returned is a 14-bit number from 0 to 0x3fff, indicating the wheel position. + If called for messages which aren't pitch wheel events, the number returned will be + nonsense. + + @see isPitchWheel + */ + int getPitchWheelValue() const throw(); + + /** Creates a pitch-wheel move message. + + @param channel the midi channel, in the range 1 to 16 + @param position the wheel position, in the range 0 to 16383 + @see isPitchWheel + */ + static const MidiMessage pitchWheel (const int channel, + const int position) throw(); + + /** Returns true if the message is an aftertouch event. + + For aftertouch events, use the getNoteNumber() method to find out the key + that it applies to, and getAftertouchValue() to find out the amount. Use + getChannel() to find out the channel. + + @see getAftertouchValue, getNoteNumber + */ + bool isAftertouch() const throw(); + + /** Returns the amount of aftertouch from an aftertouch messages. + + The value returned is in the range 0 to 127, and will be nonsense for messages + other than aftertouch messages. + + @see isAftertouch + */ + int getAfterTouchValue() const throw(); + + /** Creates an aftertouch message. + + @param channel the midi channel, in the range 1 to 16 + @param noteNumber the key number, 0 to 127 + @param aftertouchAmount the amount of aftertouch, 0 to 127 + @see isAftertouch + */ + static const MidiMessage aftertouchChange (const int channel, + const int noteNumber, + const int aftertouchAmount) throw(); + + /** Returns true if the message is a channel-pressure change event. + + This is like aftertouch, but common to the whole channel rather than a specific + note. Use getChannelPressureValue() to find out the pressure, and getChannel() + to find out the channel. + + @see channelPressureChange + */ + bool isChannelPressure() const throw(); + + /** Returns the pressure from a channel pressure change message. + + @returns the pressure, in the range 0 to 127 + @see isChannelPressure, channelPressureChange + */ + int getChannelPressureValue() const throw(); + + /** Creates a channel-pressure change event. + + @param channel the midi channel: 1 to 16 + @param pressure the pressure, 0 to 127 + @see isChannelPressure + */ + static const MidiMessage channelPressureChange (const int channel, + const int pressure) throw(); + + /** Returns true if this is a midi controller message. + + @see getControllerNumber, getControllerValue, controllerEvent + */ + bool isController() const throw(); + + /** Returns the controller number of a controller message. + + The name of the controller can be looked up using the getControllerName() method. + + Note that the value returned is invalid for messages that aren't controller changes. + + @see isController, getControllerName, getControllerValue + */ + int getControllerNumber() const throw(); + + /** Returns the controller value from a controller message. + + A value 0 to 127 is returned to indicate the new controller position. + + Note that the value returned is invalid for messages that aren't controller changes. + + @see isController, getControllerNumber + */ + int getControllerValue() const throw(); + + /** Creates a controller message. + + @param channel the midi channel, in the range 1 to 16 + @param controllerType the type of controller + @param value the controller value + @see isController + */ + static const MidiMessage controllerEvent (const int channel, + const int controllerType, + const int value) throw(); + + /** Checks whether this message is an all-notes-off message. + + @see allNotesOff + */ + bool isAllNotesOff() const throw(); + + /** Checks whether this message is an all-sound-off message. + + @see allSoundOff + */ + bool isAllSoundOff() const throw(); + + /** Creates an all-notes-off message. + + @param channel the midi channel, in the range 1 to 16 + @see isAllNotesOff + */ + static const MidiMessage allNotesOff (const int channel) throw(); + + /** Creates an all-sound-off message. + + @param channel the midi channel, in the range 1 to 16 + @see isAllSoundOff + */ + static const MidiMessage allSoundOff (const int channel) throw(); + + /** Creates an all-controllers-off message. + + @param channel the midi channel, in the range 1 to 16 + */ + static const MidiMessage allControllersOff (const int channel) throw(); + + /** Returns true if this event is a meta-event. + + Meta-events are things like tempo changes, track names, etc. + + @see getMetaEventType, isTrackMetaEvent, isEndOfTrackMetaEvent, + isTextMetaEvent, isTrackNameEvent, isTempoMetaEvent, isTimeSignatureMetaEvent, + isKeySignatureMetaEvent, isMidiChannelMetaEvent + */ + bool isMetaEvent() const throw(); + + /** Returns a meta-event's type number. + + If the message isn't a meta-event, this will return -1. + + @see isMetaEvent, isTrackMetaEvent, isEndOfTrackMetaEvent, + isTextMetaEvent, isTrackNameEvent, isTempoMetaEvent, isTimeSignatureMetaEvent, + isKeySignatureMetaEvent, isMidiChannelMetaEvent + */ + int getMetaEventType() const throw(); + + /** Returns a pointer to the data in a meta-event. + + @see isMetaEvent, getMetaEventLength + */ + const uint8* getMetaEventData() const throw(); + + /** Returns the length of the data for a meta-event. + + @see isMetaEvent, getMetaEventData + */ + int getMetaEventLength() const throw(); + + /** Returns true if this is a 'track' meta-event. */ + bool isTrackMetaEvent() const throw(); + + /** Returns true if this is an 'end-of-track' meta-event. */ + bool isEndOfTrackMetaEvent() const throw(); + + /** Creates an end-of-track meta-event. + + @see isEndOfTrackMetaEvent + */ + static const MidiMessage endOfTrack() throw(); + + /** Returns true if this is an 'track name' meta-event. + + You can use the getTextFromTextMetaEvent() method to get the track's name. + */ + bool isTrackNameEvent() const throw(); + + /** Returns true if this is a 'text' meta-event. + + @see getTextFromTextMetaEvent + */ + bool isTextMetaEvent() const throw(); + + /** Returns the text from a text meta-event. + + @see isTextMetaEvent + */ + const String getTextFromTextMetaEvent() const throw(); + + /** Returns true if this is a 'tempo' meta-event. + + @see getTempoMetaEventTickLength, getTempoSecondsPerQuarterNote + */ + bool isTempoMetaEvent() const throw(); + + /** Returns the tick length from a tempo meta-event. + + @param timeFormat the 16-bit time format value from the midi file's header. + @returns the tick length (in seconds). + @see isTempoMetaEvent + */ + double getTempoMetaEventTickLength (const short timeFormat) const throw(); + + /** Calculates the seconds-per-quarter-note from a tempo meta-event. + + @see isTempoMetaEvent, getTempoMetaEventTickLength + */ + double getTempoSecondsPerQuarterNote() const throw(); + + /** Creates a tempo meta-event. + + @see isTempoMetaEvent + */ + static const MidiMessage tempoMetaEvent (const int microsecondsPerQuarterNote) throw(); + + /** Returns true if this is a 'time-signature' meta-event. + + @see getTimeSignatureInfo + */ + bool isTimeSignatureMetaEvent() const throw(); + + /** Returns the time-signature values from a time-signature meta-event. + + @see isTimeSignatureMetaEvent + */ + void getTimeSignatureInfo (int& numerator, + int& denominator) const throw(); + + /** Creates a time-signature meta-event. + + @see isTimeSignatureMetaEvent + */ + static const MidiMessage timeSignatureMetaEvent (const int numerator, + const int denominator) throw(); + + /** Returns true if this is a 'key-signature' meta-event. + + @see getKeySignatureNumberOfSharpsOrFlats + */ + bool isKeySignatureMetaEvent() const throw(); + + /** Returns the key from a key-signature meta-event. + + @see isKeySignatureMetaEvent + */ + int getKeySignatureNumberOfSharpsOrFlats() const throw(); + + /** Returns true if this is a 'channel' meta-event. + + A channel meta-event specifies the midi channel that should be used + for subsequent meta-events. + + @see getMidiChannelMetaEventChannel + */ + bool isMidiChannelMetaEvent() const throw(); + + /** Returns the channel number from a channel meta-event. + + @returns the channel, in the range 1 to 16. + @see isMidiChannelMetaEvent + */ + int getMidiChannelMetaEventChannel() const throw(); + + /** Creates a midi channel meta-event. + + @param channel the midi channel, in the range 1 to 16 + @see isMidiChannelMetaEvent + */ + static const MidiMessage midiChannelMetaEvent (const int channel) throw(); + + /** Returns true if this is an active-sense message. */ + bool isActiveSense() const throw(); + + /** Returns true if this is a midi start event. + + @see midiStart + */ + bool isMidiStart() const throw(); + + /** Creates a midi start event. */ + static const MidiMessage midiStart() throw(); + + /** Returns true if this is a midi continue event. + + @see midiContinue + */ + bool isMidiContinue() const throw(); + + /** Creates a midi continue event. */ + static const MidiMessage midiContinue() throw(); + + /** Returns true if this is a midi stop event. + + @see midiStop + */ + bool isMidiStop() const throw(); + + /** Creates a midi stop event. */ + static const MidiMessage midiStop() throw(); + + /** Returns true if this is a midi clock event. + + @see midiClock, songPositionPointer + */ + bool isMidiClock() const throw(); + + /** Creates a midi clock event. */ + static const MidiMessage midiClock() throw(); + + /** Returns true if this is a song-position-pointer message. + + @see getSongPositionPointerMidiBeat, songPositionPointer + */ + bool isSongPositionPointer() const throw(); + + /** Returns the midi beat-number of a song-position-pointer message. + + @see isSongPositionPointer, songPositionPointer + */ + int getSongPositionPointerMidiBeat() const throw(); + + /** Creates a song-position-pointer message. + + The position is a number of midi beats from the start of the song, where 1 midi + beat is 6 midi clocks, and there are 24 midi clocks in a quarter-note. So there + are 4 midi beats in a quarter-note. + + @see isSongPositionPointer, getSongPositionPointerMidiBeat + */ + static const MidiMessage songPositionPointer (const int positionInMidiBeats) throw(); + + /** Returns true if this is a quarter-frame midi timecode message. + + @see quarterFrame, getQuarterFrameSequenceNumber, getQuarterFrameValue + */ + bool isQuarterFrame() const throw(); + + /** Returns the sequence number of a quarter-frame midi timecode message. + + This will be a value between 0 and 7. + + @see isQuarterFrame, getQuarterFrameValue, quarterFrame + */ + int getQuarterFrameSequenceNumber() const throw(); + + /** Returns the value from a quarter-frame message. + + This will be the lower nybble of the message's data-byte, a value + between 0 and 15 + */ + int getQuarterFrameValue() const throw(); + + /** Creates a quarter-frame MTC message. + + @param sequenceNumber a value 0 to 7 for the upper nybble of the message's data byte + @param value a value 0 to 15 for the lower nybble of the message's data byte + */ + static const MidiMessage quarterFrame (const int sequenceNumber, + const int value) throw(); + + /** SMPTE timecode types. + + Used by the getFullFrameParameters() and fullFrame() methods. + */ + enum SmpteTimecodeType + { + fps24 = 0, + fps25 = 1, + fps30drop = 2, + fps30 = 3 + }; + + /** Returns true if this is a full-frame midi timecode message. + */ + bool isFullFrame() const throw(); + + /** Extracts the timecode information from a full-frame midi timecode message. + + You should only call this on messages where you've used isFullFrame() to + check that they're the right kind. + */ + void getFullFrameParameters (int& hours, + int& minutes, + int& seconds, + int& frames, + SmpteTimecodeType& timecodeType) const throw(); + + /** Creates a full-frame MTC message. + */ + static const MidiMessage fullFrame (const int hours, + const int minutes, + const int seconds, + const int frames, + SmpteTimecodeType timecodeType); + + /** Types of MMC command. + + @see isMidiMachineControlMessage, getMidiMachineControlCommand, midiMachineControlCommand + */ + enum MidiMachineControlCommand + { + mmc_stop = 1, + mmc_play = 2, + mmc_deferredplay = 3, + mmc_fastforward = 4, + mmc_rewind = 5, + mmc_recordStart = 6, + mmc_recordStop = 7, + mmc_pause = 9 + }; + + /** Checks whether this is an MMC message. + + If it is, you can use the getMidiMachineControlCommand() to find out its type. + */ + bool isMidiMachineControlMessage() const throw(); + + /** For an MMC message, this returns its type. + + Make sure it's actually an MMC message with isMidiMachineControlMessage() before + calling this method. + */ + MidiMachineControlCommand getMidiMachineControlCommand() const throw(); + + /** Creates an MMC message. + */ + static const MidiMessage midiMachineControlCommand (MidiMachineControlCommand command); + + /** Checks whether this is an MMC "goto" message. + + If it is, the parameters passed-in are set to the time that the message contains. + + @see midiMachineControlGoto + */ + bool isMidiMachineControlGoto (int& hours, + int& minutes, + int& seconds, + int& frames) const throw(); + + /** Creates an MMC "goto" message. + + This messages tells the device to go to a specific frame. + + @see isMidiMachineControlGoto + */ + static const MidiMessage midiMachineControlGoto (int hours, + int minutes, + int seconds, + int frames); + + /** Creates a master-volume change message. + + @param volume the volume, 0 to 1.0 + */ + static const MidiMessage masterVolume (const float volume) throw(); + + /** Creates a system-exclusive message. + + The data passed in is wrapped with header and tail bytes of 0xf0 and 0xf7. + */ + static const MidiMessage createSysExMessage (const uint8* sysexData, + const int dataSize) throw(); + + /** Reads a midi variable-length integer. + + @param data the data to read the number from + @param numBytesUsed on return, this will be set to the number of bytes that were read + */ + static int readVariableLengthVal (const uint8* data, + int& numBytesUsed) throw(); + + /** Based on the first byte of a short midi message, this uses a lookup table + to return the message length (either 1, 2, or 3 bytes). + + The value passed in must be 0x80 or higher. + */ + static int getMessageLengthFromFirstByte (const uint8 firstByte) throw(); + + /** Returns the name of a midi note number. + + E.g "C", "D#", etc. + + @param noteNumber the midi note number, 0 to 127 + @param useSharps if true, sharpened notes are used, e.g. "C#", otherwise + they'll be flattened, e.g. "Db" + @param includeOctaveNumber if true, the octave number will be appended to the string, + e.g. "C#4" + @param octaveNumForMiddleC if an octave number is being appended, this indicates the + number that will be used for middle C's octave + + @see getMidiNoteInHertz + */ + static const String getMidiNoteName (int noteNumber, + bool useSharps, + bool includeOctaveNumber, + int octaveNumForMiddleC) throw(); + + /** Returns the frequency of a midi note number. + + @see getMidiNoteName + */ + static const double getMidiNoteInHertz (int noteNumber) throw(); + + /** Returns the standard name of a GM instrument. + + @param midiInstrumentNumber the program number 0 to 127 + @see getProgramChangeNumber + */ + static const String getGMInstrumentName (int midiInstrumentNumber) throw(); + + /** Returns the name of a bank of GM instruments. + + @param midiBankNumber the bank, 0 to 15 + */ + static const String getGMInstrumentBankName (int midiBankNumber) throw(); + + /** Returns the standard name of a channel 10 percussion sound. + + @param midiNoteNumber the key number, 35 to 81 + */ + static const String getRhythmInstrumentName (int midiNoteNumber) throw(); + + /** Returns the name of a controller type number. + + @see getControllerNumber + */ + static const String getControllerName (int controllerNumber) throw(); + + juce_UseDebuggingNewOperator + +private: + double timeStamp; + uint8* data; + int message, size; +}; + +#endif // __JUCE_MIDIMESSAGE_JUCEHEADER__ +/********* End of inlined file: juce_MidiMessage.h *********/ + +/** + Holds a sequence of time-stamped midi events. + + Analogous to the AudioSampleBuffer, this holds a set of midi events with + integer time-stamps. The buffer is kept sorted in order of the time-stamps. + + @see MidiMessage +*/ +class JUCE_API MidiBuffer : private ArrayAllocationBase +{ +public: + + /** Creates an empty MidiBuffer. */ + MidiBuffer() throw(); + + /** Creates a copy of another MidiBuffer. */ + MidiBuffer (const MidiBuffer& other) throw(); + + /** Makes a copy of another MidiBuffer. */ + const MidiBuffer& operator= (const MidiBuffer& other) throw(); + + /** Destructor */ + ~MidiBuffer() throw(); + + /** Removes all events from the buffer. */ + void clear() throw(); + + /** Removes all events between two times from the buffer. + + All events for which (start <= event position < start + numSamples) will + be removed. + */ + void clear (const int start, + const int numSamples) throw(); + + /** Returns true if the buffer is empty. + + To actually retrieve the events, use a MidiBuffer::Iterator object + */ + bool isEmpty() const throw(); + + /** Counts the number of events in the buffer. + + This is actually quite a slow operation, as it has to iterate through all + the events, so you might prefer to call isEmpty() if that's all you need + to know. + */ + int getNumEvents() const throw(); + + /** Adds an event to the buffer. + + The sample number will be used to determine the position of the event in + the buffer, which is always kept sorted. The MidiMessage's timestamp is + ignored. + + If an event is added whose sample position is the same as one or more events + already in the buffer, the new event will be placed after the existing ones. + + To retrieve events, use a MidiBuffer::Iterator object + */ + void addEvent (const MidiMessage& midiMessage, + const int sampleNumber) throw(); + + /** Adds an event to the buffer from raw midi data. + + The sample number will be used to determine the position of the event in + the buffer, which is always kept sorted. + + If an event is added whose sample position is the same as one or more events + already in the buffer, the new event will be placed after the existing ones. + + The event data will be inspected to calculate the number of bytes in length that + the midi event really takes up, so maxBytesOfMidiData may be longer than the data + that actually gets stored. E.g. if you pass in a note-on and a length of 4 bytes, + it'll actually only store 3 bytes. If the midi data is invalid, it might not + add an event at all. + + To retrieve events, use a MidiBuffer::Iterator object + */ + void addEvent (const uint8* const rawMidiData, + const int maxBytesOfMidiData, + const int sampleNumber) throw(); + + /** Adds some events from another buffer to this one. + + @param otherBuffer the buffer containing the events you want to add + @param startSample the lowest sample number in the source buffer for which + events should be added. Any source events whose timestamp is + less than this will be ignored + @param numSamples the valid range of samples from the source buffer for which + events should be added - i.e. events in the source buffer whose + timestamp is greater than or equal to (startSample + numSamples) + will be ignored. If this value is less than 0, all events after + startSample will be taken. + @param sampleDeltaToAdd a value which will be added to the source timestamps of the events + that are added to this buffer + */ + void addEvents (const MidiBuffer& otherBuffer, + const int startSample, + const int numSamples, + const int sampleDeltaToAdd) throw(); + + /** Returns the sample number of the first event in the buffer. + + If the buffer's empty, this will just return 0. + */ + int getFirstEventTime() const throw(); + + /** Returns the sample number of the last event in the buffer. + + If the buffer's empty, this will just return 0. + */ + int getLastEventTime() const throw(); + + /** + Used to iterate through the events in a MidiBuffer. + + Note that altering the buffer while an iterator is using it isn't a + safe operation. + + @see MidiBuffer + */ + class Iterator + { + public: + + /** Creates an Iterator for this MidiBuffer. */ + Iterator (const MidiBuffer& buffer) throw(); + + /** Destructor. */ + ~Iterator() throw(); + + /** Repositions the iterator so that the next event retrieved will be the first + one whose sample position is at greater than or equal to the given position. + */ + void setNextSamplePosition (const int samplePosition) throw(); + + /** Retrieves a copy of the next event from the buffer. + + @param result on return, this will be the message (the MidiMessage's timestamp + is not set) + @param samplePosition on return, this will be the position of the event + @returns true if an event was found, or false if the iterator has reached + the end of the buffer + */ + bool getNextEvent (MidiMessage& result, + int& samplePosition) throw(); + + /** Retrieves the next event from the buffer. + + @param midiData on return, this pointer will be set to a block of data containing + the midi message. Note that to make it fast, this is a pointer + directly into the MidiBuffer's internal data, so is only valid + temporarily until the MidiBuffer is altered. + @param numBytesOfMidiData on return, this is the number of bytes of data used by the + midi message + @param samplePosition on return, this will be the position of the event + @returns true if an event was found, or false if the iterator has reached + the end of the buffer + */ + bool getNextEvent (const uint8* &midiData, + int& numBytesOfMidiData, + int& samplePosition) throw(); + + juce_UseDebuggingNewOperator + + private: + const MidiBuffer& buffer; + const uint8* data; + + Iterator (const Iterator&); + const Iterator& operator= (const Iterator&); + }; + + juce_UseDebuggingNewOperator + +private: + friend class MidiBuffer::Iterator; + int bytesUsed; + + uint8* findEventAfter (uint8* d, const int samplePosition) const throw(); +}; + +#endif // __JUCE_MIDIBUFFER_JUCEHEADER__ +/********* End of inlined file: juce_MidiBuffer.h *********/ + +#endif +#ifndef __JUCE_MIDIFILE_JUCEHEADER__ + +/********* Start of inlined file: juce_MidiFile.h *********/ +#ifndef __JUCE_MIDIFILE_JUCEHEADER__ +#define __JUCE_MIDIFILE_JUCEHEADER__ + +/********* Start of inlined file: juce_MidiMessageSequence.h *********/ +#ifndef __JUCE_MIDIMESSAGESEQUENCE_JUCEHEADER__ +#define __JUCE_MIDIMESSAGESEQUENCE_JUCEHEADER__ + +/** + A sequence of timestamped midi messages. + + This allows the sequence to be manipulated, and also to be read from and + written to a standard midi file. + + @see MidiMessage, MidiFile +*/ +class JUCE_API MidiMessageSequence +{ +public: + + /** Creates an empty midi sequence object. */ + MidiMessageSequence(); + + /** Creates a copy of another sequence. */ + MidiMessageSequence (const MidiMessageSequence& other); + + /** Replaces this sequence with another one. */ + const MidiMessageSequence& operator= (const MidiMessageSequence& other); + + /** Destructor. */ + ~MidiMessageSequence(); + + /** Structure used to hold midi events in the sequence. + + These structures act as 'handles' on the events as they are moved about in + the list, and make it quick to find the matching note-offs for note-on events. + + @see MidiMessageSequence::getEventPointer + */ + class MidiEventHolder + { + public: + + /** Destructor. */ + ~MidiEventHolder(); + + /** The message itself, whose timestamp is used to specify the event's time. + */ + MidiMessage message; + + /** The matching note-off event (if this is a note-on event). + + If this isn't a note-on, this pointer will be null. + + Use the MidiMessageSequence::updateMatchedPairs() method to keep these + note-offs up-to-date after events have been moved around in the sequence + or deleted. + */ + MidiEventHolder* noteOffObject; + + juce_UseDebuggingNewOperator + + private: + friend class MidiMessageSequence; + MidiEventHolder (const MidiMessage& message); + }; + + /** Clears the sequence. */ + void clear(); + + /** Returns the number of events in the sequence. */ + int getNumEvents() const; + + /** Returns a pointer to one of the events. */ + MidiEventHolder* getEventPointer (const int index) const; + + /** Returns the time of the note-up that matches the note-on at this index. + + If the event at this index isn't a note-on, it'll just return 0. + + @see MidiMessageSequence::MidiEventHolder::noteOffObject + */ + double getTimeOfMatchingKeyUp (const int index) const; + + /** Returns the index of the note-up that matches the note-on at this index. + + If the event at this index isn't a note-on, it'll just return -1. + + @see MidiMessageSequence::MidiEventHolder::noteOffObject + */ + int getIndexOfMatchingKeyUp (const int index) const; + + /** Returns the index of an event. */ + int getIndexOf (MidiEventHolder* const event) const; + + /** Returns the index of the first event on or after the given timestamp. + + If the time is beyond the end of the sequence, this will return the + number of events. + */ + int getNextIndexAtTime (const double timeStamp) const; + + /** Returns the timestamp of the first event in the sequence. + + @see getEndTime + */ + double getStartTime() const; + + /** Returns the timestamp of the last event in the sequence. + + @see getStartTime + */ + double getEndTime() const; + + /** Returns the timestamp of the event at a given index. + + If the index is out-of-range, this will return 0.0 + */ + double getEventTime (const int index) const; + + /** Inserts a midi message into the sequence. + + The index at which the new message gets inserted will depend on its timestamp, + because the sequence is kept sorted. + + Remember to call updateMatchedPairs() after adding note-on events. + + @param newMessage the new message to add (an internal copy will be made) + @param timeAdjustment an optional value to add to the timestamp of the message + that will be inserted + @see updateMatchedPairs + */ + void addEvent (const MidiMessage& newMessage, + double timeAdjustment = 0); + + /** Deletes one of the events in the sequence. + + Remember to call updateMatchedPairs() after removing events. + + @param index the index of the event to delete + @param deleteMatchingNoteUp whether to also remove the matching note-off + if the event you're removing is a note-on + */ + void deleteEvent (const int index, + const bool deleteMatchingNoteUp); + + /** Merges another sequence into this one. + + Remember to call updateMatchedPairs() after using this method. + + @param other the sequence to add from + @param timeAdjustmentDelta an amount to add to the timestamps of the midi events + as they are read from the other sequence + @param firstAllowableDestTime events will not be added if their time is earlier + than this time. (This is after their time has been adjusted + by the timeAdjustmentDelta) + @param endOfAllowableDestTimes events will not be added if their time is equal to + or greater than this time. (This is after their time has + been adjusted by the timeAdjustmentDelta) + */ + void addSequence (const MidiMessageSequence& other, + double timeAdjustmentDelta, + double firstAllowableDestTime, + double endOfAllowableDestTimes); + + /** Makes sure all the note-on and note-off pairs are up-to-date. + + Call this after moving messages about or deleting/adding messages, and it + will scan the list and make sure all the note-offs in the MidiEventHolder + structures are pointing at the correct ones. + */ + void updateMatchedPairs(); + + /** Copies all the messages for a particular midi channel to another sequence. + + @param channelNumberToExtract the midi channel to look for, in the range 1 to 16 + @param destSequence the sequence that the chosen events should be copied to + @param alsoIncludeMetaEvents if true, any meta-events (which don't apply to a specific + channel) will also be copied across. + @see extractSysExMessages + */ + void extractMidiChannelMessages (const int channelNumberToExtract, + MidiMessageSequence& destSequence, + const bool alsoIncludeMetaEvents) const; + + /** Copies all midi sys-ex messages to another sequence. + + @param destSequence this is the sequence to which any sys-exes in this sequence + will be added + @see extractMidiChannelMessages + */ + void extractSysExMessages (MidiMessageSequence& destSequence) const; + + /** Removes any messages in this sequence that have a specific midi channel. + + @param channelNumberToRemove the midi channel to look for, in the range 1 to 16 + */ + void deleteMidiChannelMessages (const int channelNumberToRemove); + + /** Removes any sys-ex messages from this sequence. + */ + void deleteSysExMessages(); + + /** Adds an offset to the timestamps of all events in the sequence. + + @param deltaTime the amount to add to each timestamp. + */ + void addTimeToMessages (const double deltaTime); + + /** Scans through the sequence to determine the state of any midi controllers at + a given time. + + This will create a sequence of midi controller changes that can be + used to set all midi controllers to the state they would be in at the + specified time within this sequence. + + As well as controllers, it will also recreate the midi program number + and pitch bend position. + + @param channelNumber the midi channel to look for, in the range 1 to 16. Controllers + for other channels will be ignored. + @param time the time at which you want to find out the state - there are + no explicit units for this time measurement, it's the same units + as used for the timestamps of the messages + @param resultMessages an array to which midi controller-change messages will be added. This + will be the minimum number of controller changes to recreate the + state at the required time. + */ + void createControllerUpdatesForTime (const int channelNumber, + const double time, + OwnedArray& resultMessages); + + juce_UseDebuggingNewOperator + + /** @internal */ + static int compareElements (const MidiMessageSequence::MidiEventHolder* const first, + const MidiMessageSequence::MidiEventHolder* const second) throw(); + +private: + + friend class MidiComparator; + friend class MidiFile; + OwnedArray list; + + void sort(); +}; + +#endif // __JUCE_MIDIMESSAGESEQUENCE_JUCEHEADER__ +/********* End of inlined file: juce_MidiMessageSequence.h *********/ + +/** + Reads/writes standard midi format files. + + To read a midi file, create a MidiFile object and call its readFrom() method. You + can then get the individual midi tracks from it using the getTrack() method. + + To write a file, create a MidiFile object, add some MidiMessageSequence objects + to it using the addTrack() method, and then call its writeTo() method to stream + it out. + + @see MidiMessageSequence +*/ +class JUCE_API MidiFile +{ +public: + + /** Creates an empty MidiFile object. + */ + MidiFile() throw(); + + /** Destructor. */ + ~MidiFile() throw(); + + /** Returns the number of tracks in the file. + + @see getTrack, addTrack + */ + int getNumTracks() const throw(); + + /** Returns a pointer to one of the tracks in the file. + + @returns a pointer to the track, or 0 if the index is out-of-range + @see getNumTracks, addTrack + */ + const MidiMessageSequence* getTrack (const int index) const throw(); + + /** Adds a midi track to the file. + + This will make its own internal copy of the sequence that is passed-in. + + @see getNumTracks, getTrack + */ + void addTrack (const MidiMessageSequence& trackSequence) throw(); + + /** Removes all midi tracks from the file. + + @see getNumTracks + */ + void clear() throw(); + + /** Returns the raw time format code that will be written to a stream. + + After reading a midi file, this method will return the time-format that + was read from the file's header. It can be changed using the setTicksPerQuarterNote() + or setSmpteTimeFormat() methods. + + If the value returned is positive, it indicates the number of midi ticks + per quarter-note - see setTicksPerQuarterNote(). + + It it's negative, the upper byte indicates the frames-per-second (but negative), and + the lower byte is the number of ticks per frame - see setSmpteTimeFormat(). + */ + short getTimeFormat() const throw(); + + /** Sets the time format to use when this file is written to a stream. + + If this is called, the file will be written as bars/beats using the + specified resolution, rather than SMPTE absolute times, as would be + used if setSmpteTimeFormat() had been called instead. + + @param ticksPerQuarterNote e.g. 96, 960 + @see setSmpteTimeFormat + */ + void setTicksPerQuarterNote (const int ticksPerQuarterNote) throw(); + + /** Sets the time format to use when this file is written to a stream. + + If this is called, the file will be written using absolute times, rather + than bars/beats as would be the case if setTicksPerBeat() had been called + instead. + + @param framesPerSecond must be 24, 25, 29 or 30 + @param subframeResolution the sub-second resolution, e.g. 4 (midi time code), + 8, 10, 80 (SMPTE bit resolution), or 100. For millisecond + timing, setSmpteTimeFormat (25, 40) + @see setTicksPerBeat + */ + void setSmpteTimeFormat (const int framesPerSecond, + const int subframeResolution) throw(); + + /** Makes a list of all the tempo-change meta-events from all tracks in the midi file. + + Useful for finding the positions of all the tempo changes in a file. + + @param tempoChangeEvents a list to which all the events will be added + */ + void findAllTempoEvents (MidiMessageSequence& tempoChangeEvents) const; + + /** Makes a list of all the time-signature meta-events from all tracks in the midi file. + + Useful for finding the positions of all the tempo changes in a file. + + @param timeSigEvents a list to which all the events will be added + */ + void findAllTimeSigEvents (MidiMessageSequence& timeSigEvents) const; + + /** Returns the latest timestamp in any of the tracks. + + (Useful for finding the length of the file). + */ + double getLastTimestamp() const; + + /** Reads a midi file format stream. + + After calling this, you can get the tracks that were read from the file by using the + getNumTracks() and getTrack() methods. + + The timestamps of the midi events in the tracks will represent their positions in + terms of midi ticks. To convert them to seconds, use the convertTimestampTicksToSeconds() + method. + + @returns true if the stream was read successfully + */ + bool readFrom (InputStream& sourceStream); + + /** Writes the midi tracks as a standard midi file. + + @returns true if the operation succeeded. + */ + bool writeTo (OutputStream& destStream); + + /** Converts the timestamp of all the midi events from midi ticks to seconds. + + This will use the midi time format and tempo/time signature info in the + tracks to convert all the timestamps to absolute values in seconds. + */ + void convertTimestampTicksToSeconds(); + + juce_UseDebuggingNewOperator + + /** @internal */ + static int compareElements (const MidiMessageSequence::MidiEventHolder* const first, + const MidiMessageSequence::MidiEventHolder* const second) throw(); + +private: + MidiMessageSequence* tracks [128]; + short numTracks, timeFormat; + + MidiFile (const MidiFile&); + const MidiFile& operator= (const MidiFile&); + + void readNextTrack (const char* data, int size); + void writeTrack (OutputStream& mainOut, const int trackNum); +}; + +#endif // __JUCE_MIDIFILE_JUCEHEADER__ +/********* End of inlined file: juce_MidiFile.h *********/ + +#endif +#ifndef __JUCE_MIDIKEYBOARDSTATE_JUCEHEADER__ + +/********* Start of inlined file: juce_MidiKeyboardState.h *********/ +#ifndef __JUCE_MIDIKEYBOARDSTATE_JUCEHEADER__ +#define __JUCE_MIDIKEYBOARDSTATE_JUCEHEADER__ + +class MidiKeyboardState; + +/** + Receives events from a MidiKeyboardState object. + + @see MidiKeyboardState +*/ +class JUCE_API MidiKeyboardStateListener +{ +public: + + MidiKeyboardStateListener() throw() {} + virtual ~MidiKeyboardStateListener() {} + + /** Called when one of the MidiKeyboardState's keys is pressed. + + This will be called synchronously when the state is either processing a + buffer in its MidiKeyboardState::processNextMidiBuffer() method, or + when a note is being played with its MidiKeyboardState::noteOn() method. + + Note that this callback could happen from an audio callback thread, so be + careful not to block, and avoid any UI activity in the callback. + */ + virtual void handleNoteOn (MidiKeyboardState* source, + int midiChannel, int midiNoteNumber, float velocity) = 0; + + /** Called when one of the MidiKeyboardState's keys is released. + + This will be called synchronously when the state is either processing a + buffer in its MidiKeyboardState::processNextMidiBuffer() method, or + when a note is being played with its MidiKeyboardState::noteOff() method. + + Note that this callback could happen from an audio callback thread, so be + careful not to block, and avoid any UI activity in the callback. + */ + virtual void handleNoteOff (MidiKeyboardState* source, + int midiChannel, int midiNoteNumber) = 0; +}; + +/** + Represents a piano keyboard, keeping track of which keys are currently pressed. + + This object can parse a stream of midi events, using them to update its idea + of which keys are pressed for each individiual midi channel. + + When keys go up or down, it can broadcast these events to listener objects. + + It also allows key up/down events to be triggered with its noteOn() and noteOff() + methods, and midi messages for these events will be merged into the + midi stream that gets processed by processNextMidiBuffer(). +*/ +class JUCE_API MidiKeyboardState +{ +public: + + MidiKeyboardState(); + ~MidiKeyboardState(); + + /** Resets the state of the object. + + All internal data for all the channels is reset, but no events are sent as a + result. + + If you want to release any keys that are currently down, and to send out note-up + midi messages for this, use the allNotesOff() method instead. + */ + void reset(); + + /** Returns true if the given midi key is currently held down for the given midi channel. + + The channel number must be between 1 and 16. If you want to see if any notes are + on for a range of channels, use the isNoteOnForChannels() method. + */ + bool isNoteOn (const int midiChannel, const int midiNoteNumber) const throw(); + + /** Returns true if the given midi key is currently held down on any of a set of midi channels. + + The channel mask has a bit set for each midi channel you want to test for - bit + 0 = midi channel 1, bit 1 = midi channel 2, etc. + + If a note is on for at least one of the specified channels, this returns true. + */ + bool isNoteOnForChannels (const int midiChannelMask, const int midiNoteNumber) const throw(); + + /** Turns a specified note on. + + This will cause a suitable midi note-on event to be injected into the midi buffer during the + next call to processNextMidiBuffer(). + + It will also trigger a synchronous callback to the listeners to tell them that the key has + gone down. + */ + void noteOn (const int midiChannel, const int midiNoteNumber, const float velocity); + + /** Turns a specified note off. + + This will cause a suitable midi note-off event to be injected into the midi buffer during the + next call to processNextMidiBuffer(). + + It will also trigger a synchronous callback to the listeners to tell them that the key has + gone up. + + But if the note isn't acutally down for the given channel, this method will in fact do nothing. + */ + void noteOff (const int midiChannel, const int midiNoteNumber); + + /** This will turn off any currently-down notes for the given midi channel. + + If you pass 0 for the midi channel, it will in fact turn off all notes on all channels. + + Calling this method will make calls to noteOff(), so can trigger synchronous callbacks + and events being added to the midi stream. + */ + void allNotesOff (const int midiChannel); + + /** Looks at a key-up/down event and uses it to update the state of this object. + + To process a buffer full of midi messages, use the processNextMidiBuffer() method + instead. + */ + void processNextMidiEvent (const MidiMessage& message); + + /** Scans a midi stream for up/down events and adds its own events to it. + + This will look for any up/down events and use them to update the internal state, + synchronously making suitable callbacks to the listeners. + + If injectIndirectEvents is true, then midi events to produce the recent noteOn() + and noteOff() calls will be added into the buffer. + + Only the section of the buffer whose timestamps are between startSample and + (startSample + numSamples) will be affected, and any events added will be placed + between these times. + + If you're going to use this method, you'll need to keep calling it regularly for + it to work satisfactorily. + + To process a single midi event at a time, use the processNextMidiEvent() method + instead. + */ + void processNextMidiBuffer (MidiBuffer& buffer, + const int startSample, + const int numSamples, + const bool injectIndirectEvents); + + /** Registers a listener for callbacks when keys go up or down. + + @see removeListener + */ + void addListener (MidiKeyboardStateListener* const listener) throw(); + + /** Deregisters a listener. + + @see addListener + */ + void removeListener (MidiKeyboardStateListener* const listener) throw(); + + juce_UseDebuggingNewOperator + +private: + CriticalSection lock; + uint16 noteStates [128]; + MidiBuffer eventsToAdd; + VoidArray listeners; + + void noteOnInternal (const int midiChannel, const int midiNoteNumber, const float velocity); + void noteOffInternal (const int midiChannel, const int midiNoteNumber); + + MidiKeyboardState (const MidiKeyboardState&); + const MidiKeyboardState& operator= (const MidiKeyboardState&); +}; + +#endif // __JUCE_MIDIKEYBOARDSTATE_JUCEHEADER__ +/********* End of inlined file: juce_MidiKeyboardState.h *********/ + +#endif +#ifndef __JUCE_MIDIMESSAGE_JUCEHEADER__ + +#endif +#ifndef __JUCE_MIDIMESSAGECOLLECTOR_JUCEHEADER__ + +/********* Start of inlined file: juce_MidiMessageCollector.h *********/ +#ifndef __JUCE_MIDIMESSAGECOLLECTOR_JUCEHEADER__ +#define __JUCE_MIDIMESSAGECOLLECTOR_JUCEHEADER__ + +/********* Start of inlined file: juce_MidiInput.h *********/ +#ifndef __JUCE_MIDIINPUT_JUCEHEADER__ +#define __JUCE_MIDIINPUT_JUCEHEADER__ + +class MidiInput; + +/** + Receives midi messages from a midi input device. + + This class is overridden to handle incoming midi messages. See the MidiInput + class for more details. + + @see MidiInput +*/ +class JUCE_API MidiInputCallback +{ +public: + /** Destructor. */ + virtual ~MidiInputCallback() {} + + /** Receives an incoming message. + + A MidiInput object will call this method when a midi event arrives. It'll be + called on a high-priority system thread, so avoid doing anything time-consuming + in here, and avoid making any UI calls. You might find the MidiBuffer class helpful + for queueing incoming messages for use later. + + @param source the MidiInput object that generated the message + @param message the incoming message. The message's timestamp is set to a value + equivalent to (Time::getMillisecondCounter() / 1000.0) to specify the + time when the message arrived. + */ + virtual void handleIncomingMidiMessage (MidiInput* source, + const MidiMessage& message) = 0; + + /** Notification sent each time a packet of a multi-packet sysex message arrives. + + If a long sysex message is broken up into multiple packets, this callback is made + for each packet that arrives until the message is finished, at which point + the normal handleIncomingMidiMessage() callback will be made with the entire + message. + + The message passed in will contain the start of a sysex, but won't be finished + with the terminating 0xf7 byte. + */ + virtual void handlePartialSysexMessage (MidiInput* source, + const uint8* messageData, + const int numBytesSoFar, + const double timestamp) + { + // (this bit is just to avoid compiler warnings about unused variables) + (void) source; (void) messageData; (void) numBytesSoFar; (void) timestamp; + } +}; + +/** + Represents a midi input device. + + To create one of these, use the static getDevices() method to find out what inputs are + available, and then use the openDevice() method to try to open one. + + @see MidiOutput +*/ +class JUCE_API MidiInput +{ +public: + + /** Returns a list of the available midi input devices. + + You can open one of the devices by passing its index into the + openDevice() method. + + @see getDefaultDeviceIndex, openDevice + */ + static const StringArray getDevices(); + + /** Returns the index of the default midi input device to use. + + This refers to the index in the list returned by getDevices(). + */ + static int getDefaultDeviceIndex(); + + /** Tries to open one of the midi input devices. + + This will return a MidiInput object if it manages to open it. You can then + call start() and stop() on this device, and delete it when no longer needed. + + If the device can't be opened, this will return a null pointer. + + @param deviceIndex the index of a device from the list returned by getDevices() + @param callback the object that will receive the midi messages from this device. + + @see MidiInputCallback, getDevices + */ + static MidiInput* openDevice (int deviceIndex, + MidiInputCallback* callback); + +#if JUCE_LINUX || DOXYGEN + /** LINUX ONLY - This will try to create a new midi input device. + + This will attempt to create a new midi input device with the specified name, + for other apps to connect to. + + Returns 0 if a device can't be created. + + @param deviceName the name to use for the new device + @param callback the object that will receive the midi messages from this device. + */ + static MidiInput* createNewDevice (const String& deviceName, + MidiInputCallback* callback); +#endif + + /** Destructor. */ + ~MidiInput(); + + /** Returns the name of this device. + */ + const String getName() const throw() { return name; } + + /** Allows you to set a custom name for the device, in case you don't like the name + it was given when created. + */ + void setName (const String& newName) throw() { name = newName; } + + /** Starts the device running. + + After calling this, the device will start sending midi messages to the + MidiInputCallback object that was specified when the openDevice() method + was called. + + @see stop + */ + void start(); + + /** Stops the device running. + + @see start + */ + void stop(); + + juce_UseDebuggingNewOperator + +private: + String name; + void* internal; + + MidiInput (const String& name); + MidiInput (const MidiInput&); +}; + +#endif // __JUCE_MIDIINPUT_JUCEHEADER__ +/********* End of inlined file: juce_MidiInput.h *********/ + +/** + Collects incoming realtime MIDI messages and turns them into blocks suitable for + processing by a block-based audio callback. + + The class can also be used as either a MidiKeyboardStateListener or a MidiInputCallback + so it can easily use a midi input or keyboard component as its source. + + @see MidiMessage, MidiInput +*/ +class JUCE_API MidiMessageCollector : public MidiKeyboardStateListener, + public MidiInputCallback +{ +public: + + /** Creates a MidiMessageCollector. */ + MidiMessageCollector(); + + /** Destructor. */ + ~MidiMessageCollector(); + + /** Clears any messages from the queue. + + You need to call this method before starting to use the collector, so that + it knows the correct sample rate to use. + */ + void reset (const double sampleRate); + + /** Takes an incoming real-time message and adds it to the queue. + + The message's timestamp is taken, and it will be ready for retrieval as part + of the block returned by the next call to removeNextBlockOfMessages(). + + This method is fully thread-safe when overlapping calls are made with + removeNextBlockOfMessages(). + */ + void addMessageToQueue (const MidiMessage& message); + + /** Removes all the pending messages from the queue as a buffer. + + This will also correct the messages' timestamps to make sure they're in + the range 0 to numSamples - 1. + + This call should be made regularly by something like an audio processing + callback, because the time that it happens is used in calculating the + midi event positions. + + This method is fully thread-safe when overlapping calls are made with + addMessageToQueue(). + */ + void removeNextBlockOfMessages (MidiBuffer& destBuffer, + const int numSamples); + + /** @internal */ + void handleNoteOn (MidiKeyboardState* source, int midiChannel, int midiNoteNumber, float velocity); + /** @internal */ + void handleNoteOff (MidiKeyboardState* source, int midiChannel, int midiNoteNumber); + /** @internal */ + void handleIncomingMidiMessage (MidiInput* source, const MidiMessage& message); + + juce_UseDebuggingNewOperator + +private: + double lastCallbackTime; + CriticalSection midiCallbackLock; + MidiBuffer incomingMessages; + double sampleRate; + + MidiMessageCollector (const MidiMessageCollector&); + const MidiMessageCollector& operator= (const MidiMessageCollector&); +}; + +#endif // __JUCE_MIDIMESSAGECOLLECTOR_JUCEHEADER__ +/********* End of inlined file: juce_MidiMessageCollector.h *********/ + +#endif +#ifndef __JUCE_MIDIMESSAGESEQUENCE_JUCEHEADER__ + +#endif +#ifndef __JUCE_AUDIODATACONVERTERS_JUCEHEADER__ + +/********* Start of inlined file: juce_AudioDataConverters.h *********/ +#ifndef __JUCE_AUDIODATACONVERTERS_JUCEHEADER__ +#define __JUCE_AUDIODATACONVERTERS_JUCEHEADER__ + +/** + A set of routines to convert buffers of 32-bit floating point data to and from + various integer formats. + +*/ +class JUCE_API AudioDataConverters +{ +public: + + static void convertFloatToInt16LE (const float* source, void* dest, int numSamples, const int destBytesPerSample = 2); + static void convertFloatToInt16BE (const float* source, void* dest, int numSamples, const int destBytesPerSample = 2); + + static void convertFloatToInt24LE (const float* source, void* dest, int numSamples, const int destBytesPerSample = 3); + static void convertFloatToInt24BE (const float* source, void* dest, int numSamples, const int destBytesPerSample = 3); + + static void convertFloatToInt32LE (const float* source, void* dest, int numSamples, const int destBytesPerSample = 4); + static void convertFloatToInt32BE (const float* source, void* dest, int numSamples, const int destBytesPerSample = 4); + + static void convertFloatToFloat32LE (const float* source, void* dest, int numSamples, const int destBytesPerSample = 4); + static void convertFloatToFloat32BE (const float* source, void* dest, int numSamples, const int destBytesPerSample = 4); + + static void convertInt16LEToFloat (const void* source, float* dest, int numSamples, const int srcBytesPerSample = 2); + static void convertInt16BEToFloat (const void* source, float* dest, int numSamples, const int srcBytesPerSample = 2); + + static void convertInt24LEToFloat (const void* source, float* dest, int numSamples, const int srcBytesPerSample = 3); + static void convertInt24BEToFloat (const void* source, float* dest, int numSamples, const int srcBytesPerSample = 3); + + static void convertInt32LEToFloat (const void* source, float* dest, int numSamples, const int srcBytesPerSample = 4); + static void convertInt32BEToFloat (const void* source, float* dest, int numSamples, const int srcBytesPerSample = 4); + + static void convertFloat32LEToFloat (const void* source, float* dest, int numSamples, const int srcBytesPerSample = 4); + static void convertFloat32BEToFloat (const void* source, float* dest, int numSamples, const int srcBytesPerSample = 4); + + enum DataFormat + { + int16LE, + int16BE, + int24LE, + int24BE, + int32LE, + int32BE, + float32LE, + float32BE, + }; + + static void convertFloatToFormat (const DataFormat destFormat, + const float* source, void* dest, int numSamples); + + static void convertFormatToFloat (const DataFormat sourceFormat, + const void* source, float* dest, int numSamples); + + static void interleaveSamples (const float** source, float* dest, + const int numSamples, const int numChannels); + + static void deinterleaveSamples (const float* source, float** dest, + const int numSamples, const int numChannels); +}; + +#endif // __JUCE_AUDIODATACONVERTERS_JUCEHEADER__ +/********* End of inlined file: juce_AudioDataConverters.h *********/ + +#endif +#ifndef __JUCE_AUDIOSAMPLEBUFFER_JUCEHEADER__ + +/********* Start of inlined file: juce_AudioSampleBuffer.h *********/ +#ifndef __JUCE_AUDIOSAMPLEBUFFER_JUCEHEADER__ +#define __JUCE_AUDIOSAMPLEBUFFER_JUCEHEADER__ + +class AudioFormatReader; +class AudioFormatWriter; +const int maxNumAudioSampleBufferChannels = 32; + +/** + A multi-channel buffer of 32-bit floating point audio samples. + +*/ +class JUCE_API AudioSampleBuffer +{ +public: + + /** Creates a buffer with a specified number of channels and samples. + + The contents of the buffer will initially be undefined, so use clear() to + set all the samples to zero. + + The buffer will allocate its memory internally, and this will be released + when the buffer is deleted. + */ + AudioSampleBuffer (const int numChannels, + const int numSamples) throw(); + + /** Creates a buffer using a pre-allocated block of memory. + + Note that if the buffer is resized or its number of channels is changed, it + will re-allocate memory internally and copy the existing data to this new area, + so it will then stop directly addressing this memory. + + @param dataToReferTo a pre-allocated array containing pointers to the data + for each channel that should be used by this buffer. The + buffer will only refer to this memory, it won't try to delete + it when the buffer is deleted or resized. + @param numChannels the number of channels to use - this must correspond to the + number of elements in the array passed in + @param numSamples the number of samples to use - this must correspond to the + size of the arrays passed in + */ + AudioSampleBuffer (float** dataToReferTo, + const int numChannels, + const int numSamples) throw(); + + /** Copies another buffer. + + This buffer will make its own copy of the other's data, unless the buffer was created + using an external data buffer, in which case boths buffers will just point to the same + shared block of data. + */ + AudioSampleBuffer (const AudioSampleBuffer& other) throw(); + + /** Copies another buffer onto this one. + + This buffer's size will be changed to that of the other buffer. + */ + const AudioSampleBuffer& operator= (const AudioSampleBuffer& other) throw(); + + /** Destructor. + + This will free any memory allocated by the buffer. + */ + virtual ~AudioSampleBuffer() throw(); + + /** Returns the number of channels of audio data that this buffer contains. + + @see getSampleData + */ + int getNumChannels() const throw() { return numChannels; } + + /** Returns the number of samples allocated in each of the buffer's channels. + + @see getSampleData + */ + int getNumSamples() const throw() { return size; } + + /** Returns a pointer to a sample in one of the buffer's channels. + + For speed, this doesn't check whether the channel and sample number + are legal, so be careful when using it! + */ + float* getSampleData (const int channelNumber, + const int sampleOffset = 0) const throw(); + + /** Chages the buffer's size or number of channels. + + This can expand or contract the buffer's length, and add or remove channels. + + If keepExistingContent is true, it will try to preserve as much of the + old data as it can in the new buffer. + + If clearExtraSpace is true, then any extra channels or space that is + allocated will be also be cleared. If false, then this space is left + uninitialised. + + If avoidReallocating is true, then changing the buffer's size won't reduce the + amount of memory that is currently allocated (but it will still increase it if + the new size is bigger than the amount it currently has). If this is false, then + a new allocation will be done so that the buffer uses takes up the minimum amount + of memory that it needs. + */ + void setSize (const int newNumChannels, + const int newNumSamples, + const bool keepExistingContent = false, + const bool clearExtraSpace = false, + const bool avoidReallocating = false) throw(); + + /** Makes this buffer point to a pre-allocated set of channel data arrays. + + There's also a constructor that lets you specify arrays like this, but this + lets you change the channels dynamically. + + Note that if the buffer is resized or its number of channels is changed, it + will re-allocate memory internally and copy the existing data to this new area, + so it will then stop directly addressing this memory. + + @param dataToReferTo a pre-allocated array containing pointers to the data + for each channel that should be used by this buffer. The + buffer will only refer to this memory, it won't try to delete + it when the buffer is deleted or resized. + @param numChannels the number of channels to use - this must correspond to the + number of elements in the array passed in + @param numSamples the number of samples to use - this must correspond to the + size of the arrays passed in + */ + void setDataToReferTo (float** dataToReferTo, + const int numChannels, + const int numSamples) throw(); + + /** Clears all the samples in all channels. */ + void clear() throw(); + + /** Clears a specified region of all the channels. + + For speed, this doesn't check whether the channel and sample number + are in-range, so be careful! + */ + void clear (const int startSample, + const int numSamples) throw(); + + /** Clears a specified region of just one channel. + + For speed, this doesn't check whether the channel and sample number + are in-range, so be careful! + */ + void clear (const int channel, + const int startSample, + const int numSamples) throw(); + + /** Applies a gain multiple to a region of one channel. + + For speed, this doesn't check whether the channel and sample number + are in-range, so be careful! + */ + void applyGain (const int channel, + const int startSample, + int numSamples, + const float gain) throw(); + + /** Applies a gain multiple to a region of all the channels. + + For speed, this doesn't check whether the sample numbers + are in-range, so be careful! + */ + void applyGain (const int startSample, + const int numSamples, + const float gain) throw(); + + /** Applies a range of gains to a region of a channel. + + The gain that is applied to each sample will vary from + startGain on the first sample to endGain on the last Sample, + so it can be used to do basic fades. + + For speed, this doesn't check whether the sample numbers + are in-range, so be careful! + */ + void applyGainRamp (const int channel, + const int startSample, + int numSamples, + float startGain, + float endGain) throw(); + + /** Adds samples from another buffer to this one. + + @param destChannel the channel within this buffer to add the samples to + @param destStartSample the start sample within this buffer's channel + @param source the source buffer to add from + @param sourceChannel the channel within the source buffer to read from + @param sourceStartSample the offset within the source buffer's channel to start reading samples from + @param numSamples the number of samples to process + @param gainToApplyToSource an optional gain to apply to the source samples before they are + added to this buffer's samples + + @see copyFrom + */ + void addFrom (const int destChannel, + const int destStartSample, + const AudioSampleBuffer& source, + const int sourceChannel, + const int sourceStartSample, + int numSamples, + const float gainToApplyToSource = 1.0f) throw(); + + /** Adds samples from an array of floats to one of the channels. + + @param destChannel the channel within this buffer to add the samples to + @param destStartSample the start sample within this buffer's channel + @param source the source data to use + @param numSamples the number of samples to process + @param gainToApplyToSource an optional gain to apply to the source samples before they are + added to this buffer's samples + + @see copyFrom + */ + void addFrom (const int destChannel, + const int destStartSample, + const float* source, + int numSamples, + const float gainToApplyToSource = 1.0f) throw(); + + /** Adds samples from an array of floats, applying a gain ramp to them. + + @param destChannel the channel within this buffer to add the samples to + @param destStartSample the start sample within this buffer's channel + @param source the source data to use + @param numSamples the number of samples to process + @param startGain the gain to apply to the first sample (this is multiplied with + the source samples before they are added to this buffer) + @param endGain the gain to apply to the final sample. The gain is linearly + interpolated between the first and last samples. + */ + void addFromWithRamp (const int destChannel, + const int destStartSample, + const float* source, + int numSamples, + float startGain, + float endGain) throw(); + + /** Copies samples from another buffer to this one. + + @param destChannel the channel within this buffer to copy the samples to + @param destStartSample the start sample within this buffer's channel + @param source the source buffer to read from + @param sourceChannel the channel within the source buffer to read from + @param sourceStartSample the offset within the source buffer's channel to start reading samples from + @param numSamples the number of samples to process + + @see addFrom + */ + void copyFrom (const int destChannel, + const int destStartSample, + const AudioSampleBuffer& source, + const int sourceChannel, + const int sourceStartSample, + int numSamples) throw(); + + /** Copies samples from an array of floats into one of the channels. + + @param destChannel the channel within this buffer to copy the samples to + @param destStartSample the start sample within this buffer's channel + @param source the source buffer to read from + @param numSamples the number of samples to process + + @see addFrom + */ + void copyFrom (const int destChannel, + const int destStartSample, + const float* source, + int numSamples) throw(); + + /** Finds the highest and lowest sample values in a given range. + + @param channel the channel to read from + @param startSample the start sample within the channel + @param numSamples the number of samples to check + @param minVal on return, the lowest value that was found + @param maxVal on return, the highest value that was found + */ + void findMinMax (const int channel, + const int startSample, + int numSamples, + float& minVal, + float& maxVal) const throw(); + + /** Finds the highest absolute sample value within a region of a channel. + */ + float getMagnitude (const int channel, + const int startSample, + const int numSamples) const throw(); + + /** Finds the highest absolute sample value within a region on all channels. + */ + float getMagnitude (const int startSample, + const int numSamples) const throw(); + + /** Returns the root mean squared level for a region of a channel. + */ + float getRMSLevel (const int channel, + const int startSample, + const int numSamples) const throw(); + + /** Fills a section of the buffer using an AudioReader as its source. + + This will convert the reader's fixed- or floating-point data to + the buffer's floating-point format, and will try to intelligently + cope with mismatches between the number of channels in the reader + and the buffer. + + @see writeToAudioWriter + */ + void readFromAudioReader (AudioFormatReader* reader, + const int startSample, + const int numSamples, + const int readerStartSample, + const bool useReaderLeftChan, + const bool useReaderRightChan) throw(); + + /** Writes a section of this buffer to an audio writer. + + This saves you having to mess about with channels or floating/fixed + point conversion. + + @see readFromAudioReader + */ + void writeToAudioWriter (AudioFormatWriter* writer, + const int startSample, + const int numSamples) const throw(); + + juce_UseDebuggingNewOperator + +private: + int numChannels, size, allocatedBytes; + float* channels [maxNumAudioSampleBufferChannels + 1]; + float* allocatedData; +}; + +#endif // __JUCE_AUDIOSAMPLEBUFFER_JUCEHEADER__ +/********* End of inlined file: juce_AudioSampleBuffer.h *********/ + +#endif +#ifndef __JUCE_IIRFILTER_JUCEHEADER__ + +/********* Start of inlined file: juce_IIRFilter.h *********/ +#ifndef __JUCE_IIRFILTER_JUCEHEADER__ +#define __JUCE_IIRFILTER_JUCEHEADER__ + +/** + An IIR filter that can perform low, high, or band-pass filtering on an + audio signal. + + @see IIRFilterAudioSource +*/ +class JUCE_API IIRFilter +{ +public: + + /** Creates a filter. + + Initially the filter is inactive, so will have no effect on samples that + you process with it. Use the appropriate method to turn it into the type + of filter needed. + */ + IIRFilter() throw(); + + /** Creates a copy of another filter. */ + IIRFilter (const IIRFilter& other) throw(); + + /** Destructor. */ + ~IIRFilter() throw(); + + /** Resets the filter's processing pipeline, ready to start a new stream of data. + + Note that this clears the processing state, but the type of filter and + its coefficients aren't changed. To put a filter into an inactive state, use + the makeInactive() method. + */ + void reset() throw(); + + /** Performs the filter operation on the given set of samples. + */ + void processSamples (float* const samples, + const int numSamples) throw(); + + /** Sets the filter up to act as a low-pass filter. + */ + void makeLowPass (const double sampleRate, + const double frequency) throw(); + + /** Sets the filter up to act as a high-pass filter. + */ + void makeHighPass (const double sampleRate, + const double frequency) throw(); + + /** Sets the filter up to act as a low-pass shelf filter with variable Q and gain. + + The gain is a scale factor that the low frequencies are multiplied by, so values + greater than 1.0 will boost the low frequencies, values less than 1.0 will + attenuate them. + */ + void makeLowShelf (const double sampleRate, + const double cutOffFrequency, + const double Q, + const float gainFactor) throw(); + + /** Sets the filter up to act as a high-pass shelf filter with variable Q and gain. + + The gain is a scale factor that the high frequencies are multiplied by, so values + greater than 1.0 will boost the high frequencies, values less than 1.0 will + attenuate them. + */ + void makeHighShelf (const double sampleRate, + const double cutOffFrequency, + const double Q, + const float gainFactor) throw(); + + /** Sets the filter up to act as a band pass filter centred around a + frequency, with a variable Q and gain. + + The gain is a scale factor that the centre frequencies are multiplied by, so + values greater than 1.0 will boost the centre frequencies, values less than + 1.0 will attenuate them. + */ + void makeBandPass (const double sampleRate, + const double centreFrequency, + const double Q, + const float gainFactor) throw(); + + /** Clears the filter's coefficients so that it becomes inactive. + */ + void makeInactive() throw(); + + /** Makes this filter duplicate the set-up of another one. + */ + void copyCoefficientsFrom (const IIRFilter& other) throw(); + + juce_UseDebuggingNewOperator + +protected: + CriticalSection processLock; + + void setCoefficients (double c1, double c2, double c3, + double c4, double c5, double c6) throw(); + + bool active; + float coefficients[6]; + float x1, x2, y1, y2; + + // (use the copyCoefficientsFrom() method instead of this operator) + const IIRFilter& operator= (const IIRFilter&); +}; + +#endif // __JUCE_IIRFILTER_JUCEHEADER__ +/********* End of inlined file: juce_IIRFilter.h *********/ + +#endif +#ifndef __JUCE_AUDIOPLAYHEAD_JUCEHEADER__ + +/********* Start of inlined file: juce_AudioPlayHead.h *********/ +#ifndef __JUCE_AUDIOPLAYHEAD_JUCEHEADER__ +#define __JUCE_AUDIOPLAYHEAD_JUCEHEADER__ + +/** + A subclass of AudioPlayHead can supply information about the position and + status of a moving play head during audio playback. + + One of these can be supplied to an AudioProcessor object so that it can find + out about the position of the audio that it is rendering. + + @see AudioProcessor::setPlayHead, AudioProcessor::getPlayHead +*/ +class JUCE_API AudioPlayHead +{ +protected: + + AudioPlayHead() {} + +public: + virtual ~AudioPlayHead() {} + + /** Frame rate types. */ + enum FrameRateType + { + fps24 = 0, + fps25 = 1, + fps2997 = 2, + fps30 = 3, + fps2997drop = 4, + fps30drop = 5, + fpsUnknown = 99 + }; + + /** This structure is filled-in by the AudioPlayHead::getCurrentPosition() method. + */ + struct CurrentPositionInfo + { + /** The tempo in BPM */ + double bpm; + + /** Time signature numerator, e.g. the 3 of a 3/4 time sig */ + int timeSigNumerator; + /** Time signature denominator, e.g. the 4 of a 3/4 time sig */ + int timeSigDenominator; + + /** The current play position, in seconds from the start of the edit. */ + double timeInSeconds; + + /** For timecode, the position of the start of the edit, in seconds from 00:00:00:00. */ + double editOriginTime; + + /** The current play position in pulses-per-quarter-note. + + This is the number of quarter notes since the edit start. + */ + double ppqPosition; + + /** The position of the start of the last bar, in pulses-per-quarter-note. + + This is the number of quarter notes from the start of the edit to the + start of the current bar. + + Note - this value may be unavailable on some hosts, e.g. Pro-Tools. If + it's not available, the value will be 0. + */ + double ppqPositionOfLastBarStart; + + /** The video frame rate, if applicable. */ + FrameRateType frameRate; + + /** True if the transport is currently playing. */ + bool isPlaying; + + /** True if the transport is currently recording. + + (When isRecording is true, then isPlaying will also be true). + */ + bool isRecording; + }; + + /** Fills-in the given structure with details about the transport's + position at the start of the current processing block. + */ + virtual bool getCurrentPosition (CurrentPositionInfo& result) = 0; +}; + +#endif // __JUCE_AUDIOPLAYHEAD_JUCEHEADER__ +/********* End of inlined file: juce_AudioPlayHead.h *********/ + +#endif +#ifndef __JUCE_AUDIOPROCESSOR_JUCEHEADER__ + +/********* Start of inlined file: juce_AudioProcessor.h *********/ +#ifndef __JUCE_AUDIOPROCESSOR_JUCEHEADER__ +#define __JUCE_AUDIOPROCESSOR_JUCEHEADER__ + +/********* Start of inlined file: juce_AudioProcessorEditor.h *********/ +#ifndef __JUCE_AUDIOPROCESSOREDITOR_JUCEHEADER__ +#define __JUCE_AUDIOPROCESSOREDITOR_JUCEHEADER__ + +class AudioProcessor; + +/** + Base class for the component that acts as the GUI for an AudioProcessor. + + Derive your editor component from this class, and create an instance of it + by overriding the AudioProcessor::createEditor() method. + + @see AudioProcessor, GenericAudioProcessorEditor +*/ +class JUCE_API AudioProcessorEditor : public Component +{ +protected: + + /** Creates an editor for the specified processor. + */ + AudioProcessorEditor (AudioProcessor* const owner); + +public: + /** Destructor. */ + ~AudioProcessorEditor(); + + /** Returns a pointer to the processor that this editor represents. */ + AudioProcessor* getAudioProcessor() const throw() { return owner; } + +private: + + AudioProcessor* const owner; +}; + +#endif // __JUCE_AUDIOPROCESSOREDITOR_JUCEHEADER__ +/********* End of inlined file: juce_AudioProcessorEditor.h *********/ + +/********* Start of inlined file: juce_AudioProcessorListener.h *********/ +#ifndef __JUCE_AUDIOPROCESSORLISTENER_JUCEHEADER__ +#define __JUCE_AUDIOPROCESSORLISTENER_JUCEHEADER__ + +class AudioProcessor; + +/** + Base class for listeners that want to know about changes to an AudioProcessor. + + Use AudioProcessor::addListener() to register your listener with an AudioProcessor. + + @see AudioProcessor +*/ +class JUCE_API AudioProcessorListener +{ +public: + + /** Destructor. */ + virtual ~AudioProcessorListener() {} + + /** Receives a callback when a parameter is changed. + + IMPORTANT NOTE: this will be called synchronously when a parameter changes, and + many audio processors will change their parameter during their audio callback. + This means that not only has your handler code got to be completely thread-safe, + but it's also got to be VERY fast, and avoid blocking. If you need to handle + this event on your message thread, use this callback to trigger an AsyncUpdater + or ChangeBroadcaster which you can respond to on the message thread. + */ + virtual void audioProcessorParameterChanged (AudioProcessor* processor, + int parameterIndex, + float newValue) = 0; + + /** Called to indicate that something else in the plugin has changed, like its + program, number of parameters, etc. + + IMPORTANT NOTE: this will be called synchronously, and many audio processors will + call it during their audio callback. This means that not only has your handler code + got to be completely thread-safe, but it's also got to be VERY fast, and avoid + blocking. If you need to handle this event on your message thread, use this callback + to trigger an AsyncUpdater or ChangeBroadcaster which you can respond to later on the + message thread. + */ + virtual void audioProcessorChanged (AudioProcessor* processor) = 0; + + /** Indicates that a parameter change gesture has started. + + E.g. if the user is dragging a slider, this would be called when they first + press the mouse button, and audioProcessorParameterChangeGestureEnd would be + called when they release it. + + IMPORTANT NOTE: this will be called synchronously, and many audio processors will + call it during their audio callback. This means that not only has your handler code + got to be completely thread-safe, but it's also got to be VERY fast, and avoid + blocking. If you need to handle this event on your message thread, use this callback + to trigger an AsyncUpdater or ChangeBroadcaster which you can respond to later on the + message thread. + + @see audioProcessorParameterChangeGestureEnd + */ + virtual void audioProcessorParameterChangeGestureBegin (AudioProcessor* processor, + int parameterIndex); + + /** Indicates that a parameter change gesture has finished. + + E.g. if the user is dragging a slider, this would be called when they release + the mouse button. + + IMPORTANT NOTE: this will be called synchronously, and many audio processors will + call it during their audio callback. This means that not only has your handler code + got to be completely thread-safe, but it's also got to be VERY fast, and avoid + blocking. If you need to handle this event on your message thread, use this callback + to trigger an AsyncUpdater or ChangeBroadcaster which you can respond to later on the + message thread. + + @see audioPluginParameterChangeGestureStart + */ + virtual void audioProcessorParameterChangeGestureEnd (AudioProcessor* processor, + int parameterIndex); +}; + +#endif // __JUCE_AUDIOPROCESSORLISTENER_JUCEHEADER__ +/********* End of inlined file: juce_AudioProcessorListener.h *********/ + +/** + Base class for audio processing filters or plugins. + + This is intended to act as a base class of audio filter that is general enough to + be wrapped as a VST, AU, RTAS, etc, or used internally. + + It is also used by the plugin hosting code as the wrapper around an instance + of a loaded plugin. + + Derive your filter class from this base class, and if you're building a plugin, + you should implement a global function called createPluginFilter() which creates + and returns a new instance of your subclass. +*/ +class JUCE_API AudioProcessor +{ +protected: + + /** Constructor. + + You can also do your initialisation tasks in the initialiseFilterInfo() + call, which will be made after this object has been created. + */ + AudioProcessor(); + +public: + /** Destructor. */ + virtual ~AudioProcessor(); + + /** Returns the name of this processor. + */ + virtual const String getName() const = 0; + + /** Called before playback starts, to let the filter prepare itself. + + The sample rate is the target sample rate, and will remain constant until + playback stops. + + The estimatedSamplesPerBlock value is a HINT about the typical number of + samples that will be processed for each callback, but isn't any kind + of guarantee. The actual block sizes that the host uses may be different + each time the callback happens, and may be more or less than this value. + */ + virtual void prepareToPlay (double sampleRate, + int estimatedSamplesPerBlock) = 0; + + /** Called after playback has stopped, to let the filter free up any resources it + no longer needs. + */ + virtual void releaseResources() = 0; + + /** Renders the next block. + + When this method is called, the buffer contains a number of channels which is + at least as great as the maximum number of input and output channels that + this filter is using. It will be filled with the filter's input data and + should be replaced with the filter's output. + + So for example if your filter has 2 input channels and 4 output channels, then + the buffer will contain 4 channels, the first two being filled with the + input data. Your filter should read these, do its processing, and replace + the contents of all 4 channels with its output. + + Or if your filter has 5 inputs and 2 outputs, the buffer will have 5 channels, + all filled with data, and your filter should overwrite the first 2 of these + with its output. But be VERY careful not to write anything to the last 3 + channels, as these might be mapped to memory that the host assumes is read-only! + + Note that if you have more outputs than inputs, then only those channels that + correspond to an input channel are guaranteed to contain sensible data - e.g. + in the case of 2 inputs and 4 outputs, the first two channels contain the input, + but the last two channels may contain garbage, so you should be careful not to + let this pass through without being overwritten or cleared. + + Also note that the buffer may have more channels than are strictly necessary, + but your should only read/write from the ones that your filter is supposed to + be using. + + The number of samples in these buffers is NOT guaranteed to be the same for every + callback, and may be more or less than the estimated value given to prepareToPlay(). + Your code must be able to cope with variable-sized blocks, or you're going to get + clicks and crashes! + + If the filter is receiving a midi input, then the midiMessages array will be filled + with the midi messages for this block. Each message's timestamp will indicate the + message's time, as a number of samples from the start of the block. + + Any messages left in the midi buffer when this method has finished are assumed to + be the filter's midi output. This means that your filter should be careful to + clear any incoming messages from the array if it doesn't want them to be passed-on. + + Be very careful about what you do in this callback - it's going to be called by + the audio thread, so any kind of interaction with the UI is absolutely + out of the question. If you change a parameter in here and need to tell your UI to + update itself, the best way is probably to inherit from a ChangeBroadcaster, let + the UI components register as listeners, and then call sendChangeMessage() inside the + processBlock() method to send out an asynchronous message. You could also use + the AsyncUpdater class in a similar way. + */ + virtual void processBlock (AudioSampleBuffer& buffer, + MidiBuffer& midiMessages) = 0; + + /** Returns the current AudioPlayHead object that should be used to find + out the state and position of the playhead. + + You can call this from your processBlock() method, and use the AudioPlayHead + object to get the details about the time of the start of the block currently + being processed. + + If the host hasn't supplied a playhead object, this will return 0. + */ + AudioPlayHead* getPlayHead() const throw() { return playHead; } + + /** Returns the current sample rate. + + This can be called from your processBlock() method - it's not guaranteed + to be valid at any other time, and may return 0 if it's unknown. + */ + double getSampleRate() const throw() { return sampleRate; } + + /** Returns the current typical block size that is being used. + + This can be called from your processBlock() method - it's not guaranteed + to be valid at any other time. + + Remember it's not the ONLY block size that may be used when calling + processBlock, it's just the normal one. The actual block sizes used may be + larger or smaller than this, and will vary between successive calls. + */ + int getBlockSize() const throw() { return blockSize; } + + /** Returns the number of input channels that the host will be sending the filter. + + If writing a plugin, your JucePluginCharacteristics.h file should specify the + number of channels that your filter would prefer to have, and this method lets + you know how many the host is actually using. + + Note that this method is only valid during or after the prepareToPlay() + method call. Until that point, the number of channels will be unknown. + */ + int getNumInputChannels() const throw() { return numInputChannels; } + + /** Returns the number of output channels that the host will be sending the filter. + + If writing a plugin, your JucePluginCharacteristics.h file should specify the + number of channels that your filter would prefer to have, and this method lets + you know how many the host is actually using. + + Note that this method is only valid during or after the prepareToPlay() + method call. Until that point, the number of channels will be unknown. + */ + int getNumOutputChannels() const throw() { return numOutputChannels; } + + /** Returns the name of one of the input channels, as returned by the host. + + The host might not supply very useful names for channels, and this might be + something like "1", "2", "left", "right", etc. + */ + virtual const String getInputChannelName (const int channelIndex) const = 0; + + /** Returns the name of one of the output channels, as returned by the host. + + The host might not supply very useful names for channels, and this might be + something like "1", "2", "left", "right", etc. + */ + virtual const String getOutputChannelName (const int channelIndex) const = 0; + + /** Returns true if the specified channel is part of a stereo pair with its neighbour. */ + virtual bool isInputChannelStereoPair (int index) const = 0; + + /** Returns true if the specified channel is part of a stereo pair with its neighbour. */ + virtual bool isOutputChannelStereoPair (int index) const = 0; + + /** This returns the number of samples delay that the filter imposes on the audio + passing through it. + + The host will call this to find the latency - the filter itself should set this value + by calling setLatencySamples() as soon as it can during its initialisation. + */ + int getLatencySamples() const throw() { return latencySamples; } + + /** The filter should call this to set the number of samples delay that it introduces. + + The filter should call this as soon as it can during initialisation, and can call it + later if the value changes. + */ + void setLatencySamples (const int newLatency); + + /** Returns true if the processor wants midi messages. */ + virtual bool acceptsMidi() const = 0; + + /** Returns true if the processor produces midi messages. */ + virtual bool producesMidi() const = 0; + + /** This returns a critical section that will automatically be locked while the host + is calling the processBlock() method. + + Use it from your UI or other threads to lock access to variables that are used + by the process callback, but obviously be careful not to keep it locked for + too long, because that could cause stuttering playback. If you need to do something + that'll take a long time and need the processing to stop while it happens, use the + suspendProcessing() method instead. + + @see suspendProcessing + */ + const CriticalSection& getCallbackLock() const throw() { return callbackLock; } + + /** Enables and disables the processing callback. + + If you need to do something time-consuming on a thread and would like to make sure + the audio processing callback doesn't happen until you've finished, use this + to disable the callback and re-enable it again afterwards. + + E.g. + @code + void loadNewPatch() + { + suspendProcessing (true); + + ..do something that takes ages.. + + suspendProcessing (false); + } + @endcode + + If the host tries to make an audio callback while processing is suspended, the + filter will return an empty buffer, but won't block the audio thread like it would + do if you use the getCallbackLock() critical section to synchronise access. + + @see getCallbackLock + */ + void suspendProcessing (const bool shouldBeSuspended); + + /** Returns true if processing is currently suspended. + @see suspendProcessing + */ + bool isSuspended() const throw() { return suspended; } + + /** Returns true if the processor is being run in an offline mode for rendering. + + If the processor is being run live on realtime signals, this returns false. + If the mode is unknown, this will assume it's realtime and return false. + + This value may be unreliable until the prepareToPlay() method has been called, + and could change each time prepareToPlay() is called. + + @see setNonRealtime() + */ + bool isNonRealtime() const throw() { return nonRealtime; } + + /** Called by the host to tell this processor whether it's being used in a non-realime + capacity for offline rendering or bouncing. + + Whatever value is passed-in will be + */ + void setNonRealtime (const bool isNonRealtime) throw(); + + /** Creates the filter's UI. + + This can return 0 if you want a UI-less filter, in which case the host may create + a generic UI that lets the user twiddle the parameters directly. + + If you do want to pass back a component, the component should be created and set to + the correct size before returning it. + + Remember not to do anything silly like allowing your filter to keep a pointer to + the component that gets created - it could be deleted later without any warning, which + would make your pointer into a dangler. Use the getActiveEditor() method instead. + + The correct way to handle the connection between an editor component and its + filter is to use something like a ChangeBroadcaster so that the editor can + register itself as a listener, and be told when a change occurs. This lets them + safely unregister themselves when they are deleted. + + Here are a few things to bear in mind when writing an editor: + + - Initially there won't be an editor, until the user opens one, or they might + not open one at all. Your filter mustn't rely on it being there. + - An editor object may be deleted and a replacement one created again at any time. + - It's safe to assume that an editor will be deleted before its filter. + */ + virtual AudioProcessorEditor* createEditor() = 0; + + /** Returns the active editor, if there is one. + + Bear in mind this can return 0, even if an editor has previously been + opened. + */ + AudioProcessorEditor* getActiveEditor() const throw() { return activeEditor; } + + /** Returns the active editor, or if there isn't one, it will create one. + + This may call createEditor() internally to create the component. + */ + AudioProcessorEditor* createEditorIfNeeded(); + + /** This must return the correct value immediately after the object has been + created, and mustn't change the number of parameters later. + */ + virtual int getNumParameters() = 0; + + /** Returns the name of a particular parameter. */ + virtual const String getParameterName (int parameterIndex) = 0; + + /** Called by the host to find out the value of one of the filter's parameters. + + The host will expect the value returned to be between 0 and 1.0. + + This could be called quite frequently, so try to make your code efficient. + It's also likely to be called by non-UI threads, so the code in here should + be thread-aware. + */ + virtual float getParameter (int parameterIndex) = 0; + + /** Returns the value of a parameter as a text string. */ + virtual const String getParameterText (int parameterIndex) = 0; + + /** The host will call this method to change the value of one of the filter's parameters. + + The host may call this at any time, including during the audio processing + callback, so the filter has to process this very fast and avoid blocking. + + If you want to set the value of a parameter internally, e.g. from your + editor component, then don't call this directly - instead, use the + setParameterNotifyingHost() method, which will also send a message to + the host telling it about the change. If the message isn't sent, the host + won't be able to automate your parameters properly. + + The value passed will be between 0 and 1.0. + */ + virtual void setParameter (int parameterIndex, + float newValue) = 0; + + /** Your filter can call this when it needs to change one of its parameters. + + This could happen when the editor or some other internal operation changes + a parameter. This method will call the setParameter() method to change the + value, and will then send a message to the host telling it about the change. + + Note that to make sure the host correctly handles automation, you should call + the beginParameterChangeGesture() and endParameterChangeGesture() methods to + tell the host when the user has started and stopped changing the parameter. + */ + void setParameterNotifyingHost (int parameterIndex, + float newValue); + + /** Returns true if the host can automate this parameter. + + By default, this returns true for all parameters. + */ + virtual bool isParameterAutomatable (int parameterIndex) const; + + /** Sends a signal to the host to tell it that the user is about to start changing this + parameter. + + This allows the host to know when a parameter is actively being held by the user, and + it may use this information to help it record automation. + + If you call this, it must be matched by a later call to endParameterChangeGesture(). + */ + void beginParameterChangeGesture (int parameterIndex); + + /** Tells the host that the user has finished changing this parameter. + + This allows the host to know when a parameter is actively being held by the user, and + it may use this information to help it record automation. + + A call to this method must follow a call to beginParameterChangeGesture(). + */ + void endParameterChangeGesture (int parameterIndex); + + /** The filter can call this when something (apart from a parameter value) has changed. + + It sends a hint to the host that something like the program, number of parameters, + etc, has changed, and that it should update itself. + */ + void updateHostDisplay(); + + /** Returns the number of preset programs the filter supports. + + The value returned must be valid as soon as this object is created, and + must not change over its lifetime. + + This value shouldn't be less than 1. + */ + virtual int getNumPrograms() = 0; + + /** Returns the number of the currently active program. + */ + virtual int getCurrentProgram() = 0; + + /** Called by the host to change the current program. + */ + virtual void setCurrentProgram (int index) = 0; + + /** Must return the name of a given program. */ + virtual const String getProgramName (int index) = 0; + + /** Called by the host to rename a program. + */ + virtual void changeProgramName (int index, const String& newName) = 0; + + /** The host will call this method when it wants to save the filter's internal state. + + This must copy any info about the filter's state into the block of memory provided, + so that the host can store this and later restore it using setStateInformation(). + + Note that there's also a getCurrentProgramStateInformation() method, which only + stores the current program, not the state of the entire filter. + + See also the helper function copyXmlToBinary() for storing settings as XML. + + @see getCurrentProgramStateInformation + */ + virtual void getStateInformation (JUCE_NAMESPACE::MemoryBlock& destData) = 0; + + /** The host will call this method if it wants to save the state of just the filter's + current program. + + Unlike getStateInformation, this should only return the current program's state. + + Not all hosts support this, and if you don't implement it, the base class + method just calls getStateInformation() instead. If you do implement it, be + sure to also implement getCurrentProgramStateInformation. + + @see getStateInformation, setCurrentProgramStateInformation + */ + virtual void getCurrentProgramStateInformation (JUCE_NAMESPACE::MemoryBlock& destData); + + /** This must restore the filter's state from a block of data previously created + using getStateInformation(). + + Note that there's also a setCurrentProgramStateInformation() method, which tries + to restore just the current program, not the state of the entire filter. + + See also the helper function getXmlFromBinary() for loading settings as XML. + + @see setCurrentProgramStateInformation + */ + virtual void setStateInformation (const void* data, int sizeInBytes) = 0; + + /** The host will call this method if it wants to restore the state of just the filter's + current program. + + Not all hosts support this, and if you don't implement it, the base class + method just calls setStateInformation() instead. If you do implement it, be + sure to also implement getCurrentProgramStateInformation. + + @see setStateInformation, getCurrentProgramStateInformation + */ + virtual void setCurrentProgramStateInformation (const void* data, int sizeInBytes); + + /** Adds a listener that will be called when an aspect of this processor changes. */ + void addListener (AudioProcessorListener* const newListener) throw(); + + /** Removes a previously added listener. */ + void removeListener (AudioProcessorListener* const listenerToRemove) throw(); + + /** Not for public use - this is called before deleting an editor component. */ + void editorBeingDeleted (AudioProcessorEditor* const editor) throw(); + + /** Not for public use - this is called to initialise the processor. */ + void setPlayHead (AudioPlayHead* const newPlayHead) throw(); + + /** Not for public use - this is called to initialise the processor before playing. */ + void setPlayConfigDetails (const int numIns, const int numOuts, + const double sampleRate, + const int blockSize) throw(); + + juce_UseDebuggingNewOperator + +protected: + + /** Helper function that just converts an xml element into a binary blob. + + Use this in your filter's getStateInformation() method if you want to + store its state as xml. + + Then use getXmlFromBinary() to reverse this operation and retrieve the XML + from a binary blob. + */ + static void copyXmlToBinary (const XmlElement& xml, + JUCE_NAMESPACE::MemoryBlock& destData); + + /** Retrieves an XML element that was stored as binary with the copyXmlToBinary() method. + + This might return 0 if the data's unsuitable or corrupted. Otherwise it will return + an XmlElement object that the caller must delete when no longer needed. + */ + static XmlElement* getXmlFromBinary (const void* data, + const int sizeInBytes); + + /** @internal */ + AudioPlayHead* playHead; + + /** @internal */ + void sendParamChangeMessageToListeners (const int parameterIndex, const float newValue); + +private: + VoidArray listeners; + AudioProcessorEditor* activeEditor; + double sampleRate; + int blockSize, numInputChannels, numOutputChannels, latencySamples; + bool suspended, nonRealtime; + CriticalSection callbackLock, listenerLock; + +#ifdef JUCE_DEBUG + BitArray changingParams; +#endif + + AudioProcessor (const AudioProcessor&); + const AudioProcessor& operator= (const AudioProcessor&); +}; + +#endif // __JUCE_AUDIOPROCESSOR_JUCEHEADER__ +/********* End of inlined file: juce_AudioProcessor.h *********/ + +#endif +#ifndef __JUCE_AUDIOPROCESSOREDITOR_JUCEHEADER__ + +#endif +#ifndef __JUCE_AUDIOPROCESSORGRAPH_JUCEHEADER__ + +/********* Start of inlined file: juce_AudioProcessorGraph.h *********/ +#ifndef __JUCE_AUDIOPROCESSORGRAPH_JUCEHEADER__ +#define __JUCE_AUDIOPROCESSORGRAPH_JUCEHEADER__ + +/********* Start of inlined file: juce_AudioPluginFormatManager.h *********/ +#ifndef __JUCE_AUDIOPLUGINFORMATMANAGER_JUCEHEADER__ +#define __JUCE_AUDIOPLUGINFORMATMANAGER_JUCEHEADER__ + +/********* Start of inlined file: juce_AudioPluginFormat.h *********/ +#ifndef __JUCE_AUDIOPLUGINFORMAT_JUCEHEADER__ +#define __JUCE_AUDIOPLUGINFORMAT_JUCEHEADER__ + +/********* Start of inlined file: juce_AudioPluginInstance.h *********/ +#ifndef __JUCE_AUDIOPLUGININSTANCE_JUCEHEADER__ +#define __JUCE_AUDIOPLUGININSTANCE_JUCEHEADER__ + +/********* Start of inlined file: juce_PluginDescription.h *********/ +#ifndef __JUCE_PLUGINDESCRIPTION_JUCEHEADER__ +#define __JUCE_PLUGINDESCRIPTION_JUCEHEADER__ + +/** + A small class to represent some facts about a particular type of plugin. + + This class is for storing and managing the details about a plugin without + actually having to load an instance of it. + + A KnownPluginList contains a list of PluginDescription objects. + + @see KnownPluginList +*/ +class JUCE_API PluginDescription +{ +public: + + PluginDescription() throw(); + PluginDescription (const PluginDescription& other) throw(); + const PluginDescription& operator= (const PluginDescription& other) throw(); + ~PluginDescription() throw(); + + /** The name of the plugin. */ + String name; + + /** The plugin format, e.g. "VST", "AudioUnit", etc. + */ + String pluginFormatName; + + /** A category, such as "Dynamics", "Reverbs", etc. + */ + String category; + + /** The manufacturer. */ + String manufacturerName; + + /** The version. This string doesn't have any particular format. */ + String version; + + /** The binary module file containing the plugin. */ + File file; + + /** The last time the plugin file was changed. + This is handy when scanning for new or changed plugins. + */ + Time lastFileModTime; + + /** A unique ID for the plugin. + + Note that this might not be unique between formats, e.g. a VST and some + other format might actually have the same id. + + @see createIdentifierString + */ + int uid; + + /** True if the plugin identifies itself as a synthesiser. */ + bool isInstrument; + + /** The number of inputs. */ + int numInputChannels; + + /** The number of outputs. */ + int numOutputChannels; + + /** Returns true if the two descriptions refer the the same plugin. + + This isn't quite as simple as them just having the same file (because of + shell plugins). + */ + bool isDuplicateOf (const PluginDescription& other) const; + + /** Returns a string that can be saved and used to uniquely identify the + plugin again. + + This contains less info than the XML encoding, and is independent of the + plugin's file location, so can be used to store a plugin ID for use + across different machines. + */ + const String createIdentifierString() const throw(); + + /** Creates an XML object containing these details. + + @see loadFromXml + */ + XmlElement* createXml() const; + + /** Reloads the info in this structure from an XML record that was previously + saved with createXML(). + + Returns true if the XML was a valid plugin description. + */ + bool loadFromXml (const XmlElement& xml); + + juce_UseDebuggingNewOperator +}; + +#endif // __JUCE_PLUGINDESCRIPTION_JUCEHEADER__ +/********* End of inlined file: juce_PluginDescription.h *********/ + +/** + Base class for an active instance of a plugin. + + This derives from the AudioProcessor class, and adds some extra functionality + that helps when wrapping dynamically loaded plugins. + + @see AudioProcessor, AudioPluginFormat +*/ +class JUCE_API AudioPluginInstance : public AudioProcessor +{ +public: + + /** Destructor. + + Make sure that you delete any UI components that belong to this plugin before + deleting the plugin. + */ + virtual ~AudioPluginInstance(); + + /** Fills-in the appropriate parts of this plugin description object. + */ + virtual void fillInPluginDescription (PluginDescription& description) const = 0; + + juce_UseDebuggingNewOperator + +protected: + AudioPluginInstance(); + + AudioPluginInstance (const AudioPluginInstance&); + const AudioPluginInstance& operator= (const AudioPluginInstance&); +}; + +#endif // __JUCE_AUDIOPLUGININSTANCE_JUCEHEADER__ +/********* End of inlined file: juce_AudioPluginInstance.h *********/ + +class PluginDescription; + +/** + The base class for a type of plugin format, such as VST, AudioUnit, LADSPA, etc. + + Use the static getNumFormats() and getFormat() calls to find the types + of format that are available. +*/ +class JUCE_API AudioPluginFormat +{ +public: + + /** Destructor. */ + virtual ~AudioPluginFormat(); + + /** Returns the format name. + + E.g. "VST", "AudioUnit", etc. + */ + virtual const String getName() const = 0; + + /** This tries to create descriptions for all the plugin types available in + a binary module file. + + The file will be some kind of DLL or bundle. + + Normally there will only be one type returned, but some plugins + (e.g. VST shells) can use a single DLL to create a set of different plugin + subtypes, so in that case, each subtype is returned as a separate object. + */ + virtual void findAllTypesForFile (OwnedArray & results, + const File& file) = 0; + + /** Tries to recreate a type from a previously generated PluginDescription. + + @see PluginDescription::createInstance + */ + virtual AudioPluginInstance* createInstanceFromDescription (const PluginDescription& desc) = 0; + + /** Should do a quick check to see if this file or directory might be a plugin of + this format. + + This is for searching for potential files, so it shouldn't actually try to + load the plugin or do anything time-consuming. + */ + virtual bool fileMightContainThisPluginType (const File& file) = 0; + + /** Returns the typical places to look for this kind of plugin. + + Note that if this returns no paths, it means that the format can't be scanned-for + (i.e. it's an internal format that doesn't live in files) + */ + virtual const FileSearchPath getDefaultLocationsToSearch() = 0; + + juce_UseDebuggingNewOperator + +protected: + AudioPluginFormat() throw(); + + AudioPluginFormat (const AudioPluginFormat&); + const AudioPluginFormat& operator= (const AudioPluginFormat&); +}; + +#endif // __JUCE_AUDIOPLUGINFORMAT_JUCEHEADER__ +/********* End of inlined file: juce_AudioPluginFormat.h *********/ + +/** + This maintains a list of known AudioPluginFormats. + + @see AudioPluginFormat +*/ +class JUCE_API AudioPluginFormatManager : public DeletedAtShutdown +{ +public: + + AudioPluginFormatManager() throw(); + + /** Destructor. */ + ~AudioPluginFormatManager() throw(); + + juce_DeclareSingleton_SingleThreaded (AudioPluginFormatManager, false); + + /** Adds any formats that it knows about, e.g. VST. + */ + void addDefaultFormats(); + + /** Returns the number of types of format that are available. + + Use getFormat() to get one of them. + */ + int getNumFormats() throw(); + + /** Returns one of the available formats. + + @see getNumFormats + */ + AudioPluginFormat* getFormat (const int index) throw(); + + /** Adds a format to the list. + + The object passed in will be owned and deleted by the manager. + */ + void addFormat (AudioPluginFormat* const format) throw(); + + /** Tries to load the type for this description, by trying all the formats + that this manager knows about. + + The caller is responsible for deleting the object that is returned. + + If it can't load the plugin, it returns 0 and leaves a message in the + errorMessage string. + */ + AudioPluginInstance* createPluginInstance (const PluginDescription& description, + String& errorMessage) const; + + juce_UseDebuggingNewOperator + +private: + OwnedArray formats; + + AudioPluginFormatManager (const AudioPluginFormatManager&); + const AudioPluginFormatManager& operator= (const AudioPluginFormatManager&); +}; + +#endif // __JUCE_AUDIOPLUGINFORMATMANAGER_JUCEHEADER__ +/********* End of inlined file: juce_AudioPluginFormatManager.h *********/ + +/********* Start of inlined file: juce_KnownPluginList.h *********/ +#ifndef __JUCE_KNOWNPLUGINLIST_JUCEHEADER__ +#define __JUCE_KNOWNPLUGINLIST_JUCEHEADER__ + +/********* Start of inlined file: juce_PopupMenu.h *********/ +#ifndef __JUCE_POPUPMENU_JUCEHEADER__ +#define __JUCE_POPUPMENU_JUCEHEADER__ + +/********* Start of inlined file: juce_PopupMenuCustomComponent.h *********/ +#ifndef __JUCE_POPUPMENUCUSTOMCOMPONENT_JUCEHEADER__ +#define __JUCE_POPUPMENUCUSTOMCOMPONENT_JUCEHEADER__ + +/** A user-defined copmonent that can appear inside one of the rows of a popup menu. + + @see PopupMenu::addCustomItem +*/ +class JUCE_API PopupMenuCustomComponent : public Component +{ +public: + /** Destructor. */ + ~PopupMenuCustomComponent(); + + /** Chooses the size that this component would like to have. + + Note that the size which this method returns isn't necessarily the one that + the menu will give it, as it will be stretched to fit the other items in + the menu. + */ + virtual void getIdealSize (int& idealWidth, + int& idealHeight) = 0; + + /** Dismisses the menu indicating that this item has been chosen. + + This will cause the menu to exit from its modal state, returning + this item's id as the result. + */ + void triggerMenuItem(); + + /** Returns true if this item should be highlighted because the mouse is + over it. + + You can call this method in your paint() method to find out whether + to draw a highlight. + */ + bool isItemHighlighted() const throw() { return isHighlighted; } + +protected: + /** Constructor. + + If isTriggeredAutomatically is true, then the menu will automatically detect + a click on this component and use that to trigger it. If it's false, then it's + up to your class to manually trigger the item if it wants to. + */ + PopupMenuCustomComponent (const bool isTriggeredAutomatically = true); + +private: + friend class MenuItemInfo; + friend class MenuItemComponent; + friend class PopupMenuWindow; + int refCount_; + bool isHighlighted, isTriggeredAutomatically; + + PopupMenuCustomComponent (const PopupMenuCustomComponent&); + const PopupMenuCustomComponent& operator= (const PopupMenuCustomComponent&); +}; + +#endif // __JUCE_POPUPMENUCUSTOMCOMPONENT_JUCEHEADER__ +/********* End of inlined file: juce_PopupMenuCustomComponent.h *********/ + +/** Creates and displays a popup-menu. + + To show a popup-menu, you create one of these, add some items to it, then + call its show() method, which returns the id of the item the user selects. + + E.g. @code + void MyWidget::mouseDown (const MouseEvent& e) + { + PopupMenu m; + m.addItem (1, "item 1"); + m.addItem (2, "item 2"); + + const int result = m.show(); + + if (result == 0) + { + // user dismissed the menu without picking anything + } + else if (result == 1) + { + // user picked item 1 + } + else if (result == 2) + { + // user picked item 2 + } + } + @endcode + + Submenus are easy too: @code + + void MyWidget::mouseDown (const MouseEvent& e) + { + PopupMenu subMenu; + subMenu.addItem (1, "item 1"); + subMenu.addItem (2, "item 2"); + + PopupMenu mainMenu; + mainMenu.addItem (3, "item 3"); + mainMenu.addSubMenu ("other choices", subMenu); + + const int result = m.show(); + + ...etc + } + @endcode +*/ +class JUCE_API PopupMenu +{ +public: + + /** Creates an empty popup menu. */ + PopupMenu() throw(); + + /** Creates a copy of another menu. */ + PopupMenu (const PopupMenu& other) throw(); + + /** Destructor. */ + ~PopupMenu() throw(); + + /** Copies this menu from another one. */ + const PopupMenu& operator= (const PopupMenu& other) throw(); + + /** Resets the menu, removing all its items. */ + void clear() throw(); + + /** Appends a new text item for this menu to show. + + @param itemResultId the number that will be returned from the show() method + if the user picks this item. The value should never be + zero, because that's used to indicate that the user didn't + select anything. + @param itemText the text to show. + @param isActive if false, the item will be shown 'greyed-out' and can't be + picked + @param isTicked if true, the item will be shown with a tick next to it + @param iconToUse if this is non-zero, it should be an image that will be + displayed to the left of the item. This method will take its + own copy of the image passed-in, so there's no need to keep + it hanging around. + + @see addSeparator, addColouredItem, addCustomItem, addSubMenu + */ + void addItem (const int itemResultId, + const String& itemText, + const bool isActive = true, + const bool isTicked = false, + const Image* const iconToUse = 0) throw(); + + /** Adds an item that represents one of the commands in a command manager object. + + @param commandManager the manager to use to trigger the command and get information + about it + @param commandID the ID of the command + @param displayName if this is non-empty, then this string will be used instead of + the command's registered name + */ + void addCommandItem (ApplicationCommandManager* commandManager, + const int commandID, + const String& displayName = String::empty) throw(); + + /** Appends a text item with a special colour. + + This is the same as addItem(), but specifies a colour to use for the + text, which will override the default colours that are used by the + current look-and-feel. See addItem() for a description of the parameters. + */ + void addColouredItem (const int itemResultId, + const String& itemText, + const Colour& itemTextColour, + const bool isActive = true, + const bool isTicked = false, + const Image* const iconToUse = 0) throw(); + + /** Appends a custom menu item. + + This will add a user-defined component to use as a menu item. The component + passed in will be deleted by this menu when it's no longer needed. + + @see PopupMenuCustomComponent + */ + void addCustomItem (const int itemResultId, + PopupMenuCustomComponent* const customComponent) throw(); + + /** Appends a custom menu item that can't be used to trigger a result. + + This will add a user-defined component to use as a menu item. Unlike the + addCustomItem() method that takes a PopupMenuCustomComponent, this version + can't trigger a result from it, so doesn't take a menu ID. It also doesn't + delete the component when it's finished, so it's the caller's responsibility + to manage the component that is passed-in. + + if triggerMenuItemAutomaticallyWhenClicked is true, the menu itself will handle + detection of a mouse-click on your component, and use that to trigger the + menu ID specified in itemResultId. If this is false, the menu item can't + be triggered, so itemResultId is not used. + + @see PopupMenuCustomComponent + */ + void addCustomItem (const int itemResultId, + Component* customComponent, + int idealWidth, int idealHeight, + const bool triggerMenuItemAutomaticallyWhenClicked) throw(); + + /** Appends a sub-menu. + + If the menu that's passed in is empty, it will appear as an inactive item. + */ + void addSubMenu (const String& subMenuName, + const PopupMenu& subMenu, + const bool isActive = true, + Image* const iconToUse = 0) throw(); + + /** Appends a separator to the menu, to help break it up into sections. + + The menu class is smart enough not to display separators at the top or bottom + of the menu, and it will replace mutliple adjacent separators with a single + one, so your code can be quite free and easy about adding these, and it'll + always look ok. + */ + void addSeparator() throw(); + + /** Adds a non-clickable text item to the menu. + + This is a bold-font items which can be used as a header to separate the items + into named groups. + */ + void addSectionHeader (const String& title) throw(); + + /** Returns the number of items that the menu currently contains. + + (This doesn't count separators). + */ + int getNumItems() const throw(); + + /** Returns true if the menu contains a command item that triggers the given command. */ + bool containsCommandItem (const int commandID) const throw(); + + /** Returns true if the menu contains any items that can be used. */ + bool containsAnyActiveItems() const throw(); + + /** Displays the menu and waits for the user to pick something. + + This will display the menu modally, and return the ID of the item that the + user picks. If they click somewhere off the menu to get rid of it without + choosing anything, this will return 0. + + The current location of the mouse will be used as the position to show the + menu - to explicitly set the menu's position, use showAt() instead. Depending + on where this point is on the screen, the menu will appear above, below or + to the side of the point. + + @param itemIdThatMustBeVisible if you set this to the ID of one of the menu items, + then when the menu first appears, it will make sure + that this item is visible. So if the menu has too many + items to fit on the screen, it will be scrolled to a + position where this item is visible. + @param minimumWidth a minimum width for the menu, in pixels. It may be wider + than this if some items are too long to fit. + @param maximumNumColumns if there are too many items to fit on-screen in a single + vertical column, the menu may be laid out as a series of + columns - this is the maximum number allowed. To use the + default value for this (probably about 7), you can pass + in zero. + @param standardItemHeight if this is non-zero, it will be used as the standard + height for menu items (apart from custom items) + @see showAt + */ + int show (const int itemIdThatMustBeVisible = 0, + const int minimumWidth = 0, + const int maximumNumColumns = 0, + const int standardItemHeight = 0); + + /** Displays the menu at a specific location. + + This is the same as show(), but uses a specific location (in global screen + co-ordinates) rather than the current mouse position. + + @see show() + */ + int showAt (const int screenX, + const int screenY, + const int itemIdThatMustBeVisible = 0, + const int minimumWidth = 0, + const int maximumNumColumns = 0, + const int standardItemHeight = 0); + + /** Displays the menu as if it's attached to a component such as a button. + + This is similar to showAt(), but will position it next to the given component, e.g. + so that the menu's edge is aligned with that of the component. This is intended for + things like buttons that trigger a pop-up menu. + */ + int showAt (Component* componentToAttachTo, + const int itemIdThatMustBeVisible = 0, + const int minimumWidth = 0, + const int maximumNumColumns = 0, + const int standardItemHeight = 0); + + /** Closes any menus that are currently open. + + This might be useful if you have a situation where your window is being closed + by some means other than a user action, and you'd like to make sure that menus + aren't left hanging around. + */ + static void JUCE_CALLTYPE dismissAllActiveMenus() throw(); + + /** Specifies a look-and-feel for the menu and any sub-menus that it has. + + This can be called before show() if you need a customised menu. Be careful + not to delete the LookAndFeel object before the menu has been deleted. + */ + void setLookAndFeel (LookAndFeel* const newLookAndFeel) throw(); + + /** A set of colour IDs to use to change the colour of various aspects of the menu. + + These constants can be used either via the LookAndFeel::setColour() + method for the look and feel that is set for this menu with setLookAndFeel() + + @see setLookAndFeel, LookAndFeel::setColour, LookAndFeel::findColour + */ + enum ColourIds + { + backgroundColourId = 0x1000700, /**< The colour to fill the menu's background with. */ + textColourId = 0x1000600, /**< The colour for normal menu item text, (unless the + colour is specified when the item is added). */ + headerTextColourId = 0x1000601, /**< The colour for section header item text (see the + addSectionHeader() method). */ + highlightedBackgroundColourId = 0x1000900, /**< The colour to fill the background of the currently + highlighted menu item. */ + highlightedTextColourId = 0x1000800, /**< The colour to use for the text of the currently + highlighted item. */ + }; + + /** + Allows you to iterate through the items in a pop-up menu, and examine + their properties. + + To use this, just create one and repeatedly call its next() method. When this + returns true, all the member variables of the iterator are filled-out with + information describing the menu item. When it returns false, the end of the + list has been reached. + */ + class JUCE_API MenuItemIterator + { + public: + + /** Creates an iterator that will scan through the items in the specified + menu. + + Be careful not to add any items to a menu while it is being iterated, + or things could get out of step. + */ + MenuItemIterator (const PopupMenu& menu) throw(); + + /** Destructor. */ + ~MenuItemIterator() throw(); + + /** Returns true if there is another item, and sets up all this object's + member variables to reflect that item's properties. + */ + bool next() throw(); + + String itemName; + const PopupMenu* subMenu; + int itemId; + bool isSeparator; + bool isTicked; + bool isEnabled; + bool isCustomComponent; + bool isSectionHeader; + const Colour* customColour; + const Image* customImage; + ApplicationCommandManager* commandManager; + + juce_UseDebuggingNewOperator + + private: + const PopupMenu& menu; + int index; + + MenuItemIterator (const MenuItemIterator&); + const MenuItemIterator& operator= (const MenuItemIterator&); + }; + + juce_UseDebuggingNewOperator + +private: + friend class PopupMenuWindow; + friend class MenuItemIterator; + VoidArray items; + LookAndFeel* lookAndFeel; + bool separatorPending; + + void addSeparatorIfPending(); + + int showMenu (const int x, const int y, const int w, const int h, + const int itemIdThatMustBeVisible, + const int minimumWidth, + const int maximumNumColumns, + const int standardItemHeight, + const bool alignToRectangle, + Component* const componentAttachedTo) throw(); + + friend class MenuBarComponent; + Component* createMenuComponent (const int x, const int y, const int w, const int h, + const int itemIdThatMustBeVisible, + const int minimumWidth, + const int maximumNumColumns, + const int standardItemHeight, + const bool alignToRectangle, + Component* menuBarComponent, + ApplicationCommandManager** managerOfChosenCommand, + Component* const componentAttachedTo) throw(); +}; + +#endif // __JUCE_POPUPMENU_JUCEHEADER__ +/********* End of inlined file: juce_PopupMenu.h *********/ + +/** + Manages a list of plugin types. + + This can be easily edited, saved and loaded, and used to create instances of + the plugin types in it. + + @see PluginListComponent +*/ +class JUCE_API KnownPluginList : public ChangeBroadcaster +{ +public: + + /** Creates an empty list. + */ + KnownPluginList(); + + /** Destructor. */ + ~KnownPluginList(); + + /** Clears the list. */ + void clear(); + + /** Returns the number of types currently in the list. + @see getType + */ + int getNumTypes() const throw() { return types.size(); } + + /** Returns one of the types. + @see getNumTypes + */ + PluginDescription* getType (const int index) const throw() { return types [index]; } + + /** Looks for a type in the list which comes from this file. + */ + PluginDescription* getTypeForFile (const File& file) const throw(); + + /** Looks for a type in the list which matches a plugin type ID. + + The identifierString parameter must have been created by + PluginDescription::createIdentifierString(). + */ + PluginDescription* getTypeForIdentifierString (const String& identifierString) const throw(); + + /** Adds a type manually from its description. */ + bool addType (const PluginDescription& type); + + /** Removes a type. */ + void removeType (const int index) throw(); + + /** Looks for all types that can be loaded from a given file, and adds them + to the list. + + If dontRescanIfAlreadyInList is true, then the file will only be loaded and + re-tested if it's not already in the list, or if the file's modification + time has changed since the list was created. If dontRescanIfAlreadyInList is + false, the file will always be reloaded and tested. + + Returns true if any new types were added, and all the types found in this + file (even if it was already known and hasn't been re-scanned) get returned + in the array. + */ + bool scanAndAddFile (const File& possiblePluginFile, + const bool dontRescanIfAlreadyInList, + OwnedArray & typesFound); + + /** Returns true if the specified file is already known about and if it + hasn't been modified since our entry was created. + */ + bool isListingUpToDate (const File& possiblePluginFile) const throw(); + + /** Scans and adds a bunch of files that might have been dragged-and-dropped. + + If any types are found in the files, their descriptions are returned in the array. + */ + void scanAndAddDragAndDroppedFiles (const StringArray& filenames, + OwnedArray & typesFound); + + /** Sort methods used to change the order of the plugins in the list. + */ + enum SortMethod + { + defaultOrder = 0, + sortAlphabetically, + sortByCategory, + sortByManufacturer, + sortByFileSystemLocation + }; + + /** Adds all the plugin types to a popup menu so that the user can select one. + + Depending on the sort method, it may add sub-menus for categories, + manufacturers, etc. + + Use getIndexChosenByMenu() to find out the type that was chosen. + */ + void addToMenu (PopupMenu& menu, + const SortMethod sortMethod) const; + + /** Converts a menu item index that has been chosen into its index in this list. + + Returns -1 if it's not an ID that was used. + + @see addToMenu + */ + int getIndexChosenByMenu (const int menuResultCode) const; + + /** Sorts the list. */ + void sort (const SortMethod method); + + /** Creates some XML that can be used to store the state of this list. + */ + XmlElement* createXml() const; + + /** Recreates the state of this list from its stored XML format. + */ + void recreateFromXml (const XmlElement& xml); + + juce_UseDebuggingNewOperator + +private: + OwnedArray types; + + KnownPluginList (const KnownPluginList&); + const KnownPluginList& operator= (const KnownPluginList&); +}; + +#endif // __JUCE_KNOWNPLUGINLIST_JUCEHEADER__ +/********* End of inlined file: juce_KnownPluginList.h *********/ + +/** + A type of AudioProcessor which plays back a graph of other AudioProcessors. + + Use one of these objects if you want to wire-up a set of AudioProcessors + and play back the result. + + Processors can be added to the graph as "nodes" using addNode(), and once + added, you can connect any of their input or output channels to other + nodes using addConnection(). + + To play back a graph through an audio device, you might want to use an + AudioProcessorPlayer object. +*/ +class JUCE_API AudioProcessorGraph : public AudioProcessor, + public AsyncUpdater +{ +public: + + /** Creates an empty graph. + */ + AudioProcessorGraph(); + + /** Destructor. + + Any processor objects that have been added to the graph will also be deleted. + */ + ~AudioProcessorGraph(); + + /** Represents one of the nodes, or processors, in an AudioProcessorGraph. + + To create a node, call AudioProcessorGraph::addNode(). + */ + class Node : public ReferenceCountedObject + { + public: + /** Destructor. + */ + ~Node(); + + /** The ID number assigned to this node. + + This is assigned by the graph that owns it, and can't be changed. + */ + const uint32 id; + + /** The actual processor object that this node represents. + */ + AudioProcessor* const processor; + + /** A set of user-definable properties that are associated with this node. + + This can be used to attach values to the node for whatever purpose seems + useful. For example, you might store an x and y position if your application + is displaying the nodes on-screen. + */ + PropertySet properties; + + /** A convenient typedef for referring to a pointer to a node object. + */ + typedef ReferenceCountedObjectPtr Ptr; + + juce_UseDebuggingNewOperator + + private: + friend class AudioProcessorGraph; + + bool isPrepared; + + Node (const uint32 id, AudioProcessor* const processor) throw(); + + void prepare (const double sampleRate, const int blockSize, AudioProcessorGraph* const graph); + void unprepare(); + + Node (const Node&); + const Node& operator= (const Node&); + }; + + /** Represents a connection between two channels of two nodes in an AudioProcessorGraph. + + To create a connection, use AudioProcessorGraph::addConnection(). + */ + struct Connection + { + public: + + /** The ID number of the node which is the input source for this connection. + @see AudioProcessorGraph::getNodeForId + */ + uint32 sourceNodeId; + + /** The index of the output channel of the source node from which this + connection takes its data. + + If this value is the special number AudioProcessorGraph::midiChannelIndex, then + it is referring to the source node's midi output. Otherwise, it is the zero-based + index of an audio output channel in the source node. + */ + int sourceChannelIndex; + + /** The ID number of the node which is the destination for this connection. + @see AudioProcessorGraph::getNodeForId + */ + uint32 destNodeId; + + /** The index of the input channel of the destination node to which this + connection delivers its data. + + If this value is the special number AudioProcessorGraph::midiChannelIndex, then + it is referring to the destination node's midi input. Otherwise, it is the zero-based + index of an audio input channel in the destination node. + */ + int destChannelIndex; + + juce_UseDebuggingNewOperator + + private: + }; + + /** Deletes all nodes and connections from this graph. + + Any processor objects in the graph will be deleted. + */ + void clear(); + + /** Returns the number of nodes in the graph. */ + int getNumNodes() const throw() { return nodes.size(); } + + /** Returns a pointer to one of the nodes in the graph. + + This will return 0 if the index is out of range. + @see getNodeForId + */ + Node* getNode (const int index) const throw() { return nodes [index]; } + + /** Searches the graph for a node with the given ID number and returns it. + + If no such node was found, this returns 0. + @see getNode + */ + Node* getNodeForId (const uint32 nodeId) const throw(); + + /** Adds a node to the graph. + + This creates a new node in the graph, for the specified processor. Once you have + added a processor to the graph, the graph owns it and will delete it later when + it is no longer needed. + + The optional nodeId parameter lets you specify an ID to use for the node, but + if the value is already in use, this new node will overwrite the old one. + + If this succeeds, it returns a pointer to the newly-created node. + */ + Node* addNode (AudioProcessor* const newProcessor, + uint32 nodeId = 0); + + /** Deletes a node within the graph which has the specified ID. + + This will also delete any connections that are attached to this node. + */ + bool removeNode (const uint32 nodeId); + + /** Returns the number of connections in the graph. */ + int getNumConnections() const throw() { return connections.size(); } + + /** Returns a pointer to one of the connections in the graph. */ + const Connection* getConnection (const int index) const throw() { return connections [index]; } + + /** Searches for a connection between some specified channels. + + If no such connection is found, this returns 0. + */ + const Connection* getConnectionBetween (const uint32 sourceNodeId, + const int sourceChannelIndex, + const uint32 destNodeId, + const int destChannelIndex) const throw(); + + /** Returns true if there is a connection between any of the channels of + two specified nodes. + */ + bool isConnected (const uint32 possibleSourceNodeId, + const uint32 possibleDestNodeId) const throw(); + + /** Returns true if it would be legal to connect the specified points. + */ + bool canConnect (const uint32 sourceNodeId, const int sourceChannelIndex, + const uint32 destNodeId, const int destChannelIndex) const throw(); + + /** Attempts to connect two specified channels of two nodes. + + If this isn't allowed (e.g. because you're trying to connect a midi channel + to an audio one or other such nonsense), then it'll return 0. + */ + bool addConnection (const uint32 sourceNodeId, const int sourceChannelIndex, + const uint32 destNodeId, const int destChannelIndex); + + /** Deletes the connection with the specified index. + + Returns true if a connection was actually deleted. + */ + void removeConnection (const int index); + + /** Deletes any connection between two specified points. + + Returns true if a connection was actually deleted. + */ + bool removeConnection (const uint32 sourceNodeId, const int sourceChannelIndex, + const uint32 destNodeId, const int destChannelIndex); + + /** Removes all connections from the specified node. + */ + bool disconnectNode (const uint32 nodeId); + + /** Performs a sanity checks of all the connections. + + This might be useful if some of the processors are doing things like changing + their channel counts, which could render some connections obsolete. + */ + bool removeIllegalConnections(); + + /** A special number that represents the midi channel of a node. + + This is used as a channel index value if you want to refer to the midi input + or output instead of an audio channel. + */ + static const int midiChannelIndex; + + /** A special type of AudioProcessor that can live inside an AudioProcessorGraph + in order to use the audio that comes into and out of the graph itself. + + If you create an AudioGraphIOProcessor in "input" mode, it will act as a + node in the graph which delivers the audio that is coming into the parent + graph. This allows you to stream the data to other nodes and process the + incoming audio. + + Likewise, one of these in "output" mode can be sent data which it will add to + the sum of data being sent to the graph's output. + + @see AudioProcessorGraph + */ + class AudioGraphIOProcessor : public AudioPluginInstance + { + public: + /** Specifies the mode in which this processor will operate. + */ + enum IODeviceType + { + audioInputNode, /**< In this mode, the processor has output channels + representing all the audio input channels that are + coming into its parent audio graph. */ + audioOutputNode, /**< In this mode, the processor has input channels + representing all the audio output channels that are + going out of its parent audio graph. */ + midiInputNode, /**< In this mode, the processor has a midi output which + delivers the same midi data that is arriving at its + parent graph. */ + midiOutputNode /**< In this mode, the processor has a midi input and + any data sent to it will be passed out of the parent + graph. */ + }; + + /** Returns the mode of this processor. */ + IODeviceType getType() const throw() { return type; } + + /** Returns the parent graph to which this processor belongs, or 0 if it + hasn't yet been added to one. */ + AudioProcessorGraph* getParentGraph() const throw() { return graph; } + + /** True if this is an audio or midi input. */ + bool isInput() const throw(); + /** True if this is an audio or midi output. */ + bool isOutput() const throw(); + + AudioGraphIOProcessor (const IODeviceType type); + ~AudioGraphIOProcessor(); + + const String getName() const; + void fillInPluginDescription (PluginDescription& d) const; + + void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock); + void releaseResources(); + void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages); + + const String getInputChannelName (const int channelIndex) const; + const String getOutputChannelName (const int channelIndex) const; + bool isInputChannelStereoPair (int index) const; + bool isOutputChannelStereoPair (int index) const; + bool acceptsMidi() const; + bool producesMidi() const; + + AudioProcessorEditor* createEditor(); + + int getNumParameters(); + const String getParameterName (int); + float getParameter (int); + const String getParameterText (int); + void setParameter (int, float); + + int getNumPrograms(); + int getCurrentProgram(); + void setCurrentProgram (int); + const String getProgramName (int); + void changeProgramName (int, const String&); + + void getStateInformation (JUCE_NAMESPACE::MemoryBlock& destData); + void setStateInformation (const void* data, int sizeInBytes); + + /** @internal */ + void setParentGraph (AudioProcessorGraph* const graph) throw(); + + juce_UseDebuggingNewOperator + + private: + const IODeviceType type; + AudioProcessorGraph* graph; + + AudioGraphIOProcessor (const AudioGraphIOProcessor&); + const AudioGraphIOProcessor& operator= (const AudioGraphIOProcessor&); + }; + + // AudioProcessor methods: + + const String getName() const; + + void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock); + void releaseResources(); + void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages); + + const String getInputChannelName (const int channelIndex) const; + const String getOutputChannelName (const int channelIndex) const; + bool isInputChannelStereoPair (int index) const; + bool isOutputChannelStereoPair (int index) const; + + bool acceptsMidi() const; + bool producesMidi() const; + + AudioProcessorEditor* createEditor() { return 0; } + + int getNumParameters() { return 0; } + const String getParameterName (int) { return String::empty; } + float getParameter (int) { return 0; } + const String getParameterText (int) { return String::empty; } + void setParameter (int, float) { } + + int getNumPrograms() { return 0; } + int getCurrentProgram() { return 0; } + void setCurrentProgram (int) { } + const String getProgramName (int) { return String::empty; } + void changeProgramName (int, const String&) { } + + void getStateInformation (JUCE_NAMESPACE::MemoryBlock& destData); + void setStateInformation (const void* data, int sizeInBytes); + + /** @internal */ + void handleAsyncUpdate(); + + juce_UseDebuggingNewOperator + +private: + ReferenceCountedArray nodes; + OwnedArray connections; + int lastNodeId; + AudioSampleBuffer renderingBuffers; + OwnedArray midiBuffers; + + CriticalSection renderLock; + VoidArray renderingOps; + + friend class AudioGraphIOProcessor; + AudioSampleBuffer* currentAudioIOBuffer; + MidiBuffer* currentMidiIOBuffer; + + void clearRenderingSequence(); + void buildRenderingSequence(); + + bool isAnInputTo (const uint32 possibleInputId, + const uint32 possibleDestinationId, + const int recursionCheck) const throw(); + + AudioProcessorGraph (const AudioProcessorGraph&); + const AudioProcessorGraph& operator= (const AudioProcessorGraph&); +}; + +#endif // __JUCE_AUDIOPROCESSORGRAPH_JUCEHEADER__ +/********* End of inlined file: juce_AudioProcessorGraph.h *********/ + +#endif +#ifndef __JUCE_AUDIOPROCESSORLISTENER_JUCEHEADER__ + +#endif +#ifndef __JUCE_AUDIOPROCESSORPLAYER_JUCEHEADER__ + +/********* Start of inlined file: juce_AudioProcessorPlayer.h *********/ +#ifndef __JUCE_AUDIOPROCESSORPLAYER_JUCEHEADER__ +#define __JUCE_AUDIOPROCESSORPLAYER_JUCEHEADER__ + +/********* Start of inlined file: juce_AudioIODevice.h *********/ +#ifndef __JUCE_AUDIOIODEVICE_JUCEHEADER__ +#define __JUCE_AUDIOIODEVICE_JUCEHEADER__ + +class AudioIODevice; + +/** + One of these is passed to an AudioIODevice object to stream the audio data + in and out. + + The AudioIODevice will repeatedly call this class's audioDeviceIOCallback() + method on its own high-priority audio thread, when it needs to send or receive + the next block of data. + + @see AudioIODevice, AudioDeviceManager +*/ +class JUCE_API AudioIODeviceCallback +{ +public: + /** Destructor. */ + virtual ~AudioIODeviceCallback() {} + + /** Processes a block of incoming and outgoing audio data. + + The subclass's implementation should use the incoming audio for whatever + purposes it needs to, and must fill all the output channels with the next + block of output data before returning. + + The channel data is arranged with the same array indices as the channel name + array returned by AudioIODevice::getOutputChannelNames(), but those channels + that aren't specified in AudioIODevice::open() will have a null pointer for their + associated channel, so remember to check for this. + + @param inputChannelData a set of arrays containing the audio data for each + incoming channel - this data is valid until the function + returns. Some members of the array may be null pointers, if + that channel wasn't enabled when the audio device was + opened (see AudioIODevice::open()) + @param totalNumInputChannels the total number of pointers to channel data in + the inputChannelData array. Note that not all of these + channels may be active, so some may be null pointers + @param outputChannelData a set of arrays which need to be filled with the data + that should be sent to each outgoing channel of the device. + As for the input array, some of these pointers may be null, if + those channels weren't enabled when the audio device was + opened. The contents of the array are undefined, so the + callback function must fill all the channels with zeros if + it wants to output silence - not doing this could cause quite + an unpleasant noise! + @param totalNumOutputChannels the total number of pointers to channel data in + the outputChannelData array. Note that not all of these + channels may be active, so some may be null pointers + @param numSamples the number of samples in each channel of the input and + output arrays. The number of samples will depend on the + audio device's buffer size and will usually remain constant, + although this isn't guaranteed, so make sure your code can + cope with reasonable changes in the buffer size from one + callback to the next. + */ + virtual void audioDeviceIOCallback (const float** inputChannelData, + int totalNumInputChannels, + float** outputChannelData, + int totalNumOutputChannels, + int numSamples) = 0; + + /** Called to indicate that the device is about to start calling back. + + This will be called just before the audio callbacks begin, either when this + callback has just been added to an audio device, or after the device has been + restarted because of a sample-rate or block-size change. + + You can use this opportunity to find out the sample rate and block size + that the device is going to use by calling the AudioIODevice::getCurrentSampleRate() + and AudioIODevice::getCurrentBufferSizeSamples() on the supplied pointer. + + @param device the audio IO device that will be used to drive the callback. + Note that if you're going to store this this pointer, it is + only valid until the next time that audioDeviceStopped is called. + */ + virtual void audioDeviceAboutToStart (AudioIODevice* device) = 0; + + /** Called to indicate that the device has stopped. + */ + virtual void audioDeviceStopped() = 0; +}; + +/** + Base class for an audio device with synchoronised input and output channels. + + Subclasses of this are used to implement different protocols such as DirectSound, + ASIO, CoreAudio, etc. + + To create one of these, you'll need to use the AudioIODeviceType class - see the + documentation for that class for more info. + + For an easier way of managing audio devices and their settings, have a look at the + AudioDeviceManager class. + + @see AudioIODeviceType, AudioDeviceManager +*/ +class JUCE_API AudioIODevice +{ +public: + /** Destructor. */ + virtual ~AudioIODevice(); + + /** Returns the device's name, (as set in the constructor). */ + const String& getName() const throw() { return name; } + + /** Returns the type of the device. + + E.g. "CoreAudio", "ASIO", etc. - this comes from the AudioIODeviceType that created it. + */ + const String& getTypeName() const throw() { return typeName; } + + /** Returns the names of all the available output channels on this device. + To find out which of these are currently in use, call getActiveOutputChannels(). + */ + virtual const StringArray getOutputChannelNames() = 0; + + /** Returns the names of all the available input channels on this device. + To find out which of these are currently in use, call getActiveInputChannels(). + */ + virtual const StringArray getInputChannelNames() = 0; + + /** Returns the number of sample-rates this device supports. + + To find out which rates are available on this device, use this method to + find out how many there are, and getSampleRate() to get the rates. + + @see getSampleRate + */ + virtual int getNumSampleRates() = 0; + + /** Returns one of the sample-rates this device supports. + + To find out which rates are available on this device, use getNumSampleRates() to + find out how many there are, and getSampleRate() to get the individual rates. + + The sample rate is set by the open() method. + + (Note that for DirectSound some rates might not work, depending on combinations + of i/o channels that are being opened). + + @see getNumSampleRates + */ + virtual double getSampleRate (int index) = 0; + + /** Returns the number of sizes of buffer that are available. + + @see getBufferSizeSamples, getDefaultBufferSize + */ + virtual int getNumBufferSizesAvailable() = 0; + + /** Returns one of the possible buffer-sizes. + + @param index the index of the buffer-size to use, from 0 to getNumBufferSizesAvailable() - 1 + @returns a number of samples + @see getNumBufferSizesAvailable, getDefaultBufferSize + */ + virtual int getBufferSizeSamples (int index) = 0; + + /** Returns the default buffer-size to use. + + @returns a number of samples + @see getNumBufferSizesAvailable, getBufferSizeSamples + */ + virtual int getDefaultBufferSize() = 0; + + /** Tries to open the device ready to play. + + @param inputChannels a BitArray in which a set bit indicates that the corresponding + input channel should be enabled + @param outputChannels a BitArray in which a set bit indicates that the corresponding + output channel should be enabled + @param sampleRate the sample rate to try to use - to find out which rates are + available, see getNumSampleRates() and getSampleRate() + @param bufferSizeSamples the size of i/o buffer to use - to find out the available buffer + sizes, see getNumBufferSizesAvailable() and getBufferSizeSamples() + @returns an error description if there's a problem, or an empty string if it succeeds in + opening the device + @see close + */ + virtual const String open (const BitArray& inputChannels, + const BitArray& outputChannels, + double sampleRate, + int bufferSizeSamples) = 0; + + /** Closes and releases the device if it's open. */ + virtual void close() = 0; + + /** Returns true if the device is still open. + + A device might spontaneously close itself if something goes wrong, so this checks if + it's still open. + */ + virtual bool isOpen() = 0; + + /** Starts the device actually playing. + + This must be called after the device has been opened. + + @param callback the callback to use for streaming the data. + @see AudioIODeviceCallback, open + */ + virtual void start (AudioIODeviceCallback* callback) = 0; + + /** Stops the device playing. + + Once a device has been started, this will stop it. Any pending calls to the + callback class will be flushed before this method returns. + */ + virtual void stop() = 0; + + /** Returns true if the device is still calling back. + + The device might mysteriously stop, so this checks whether it's + still playing. + */ + virtual bool isPlaying() = 0; + + /** Returns the last error that happened if anything went wrong. */ + virtual const String getLastError() = 0; + + /** Returns the buffer size that the device is currently using. + + If the device isn't actually open, this value doesn't really mean much. + */ + virtual int getCurrentBufferSizeSamples() = 0; + + /** Returns the sample rate that the device is currently using. + + If the device isn't actually open, this value doesn't really mean much. + */ + virtual double getCurrentSampleRate() = 0; + + /** Returns the device's current physical bit-depth. + + If the device isn't actually open, this value doesn't really mean much. + */ + virtual int getCurrentBitDepth() = 0; + + /** Returns a mask showing which of the available output channels are currently + enabled. + @see getOutputChannelNames + */ + virtual const BitArray getActiveOutputChannels() const = 0; + + /** Returns a mask showing which of the available input channels are currently + enabled. + @see getInputChannelNames + */ + virtual const BitArray getActiveInputChannels() const = 0; + + /** Returns the device's output latency. + + This is the delay in samples between a callback getting a block of data, and + that data actually getting played. + */ + virtual int getOutputLatencyInSamples() = 0; + + /** Returns the device's input latency. + + This is the delay in samples between some audio actually arriving at the soundcard, + and the callback getting passed this block of data. + */ + virtual int getInputLatencyInSamples() = 0; + + /** True if this device can show a pop-up control panel for editing its settings. + + This is generally just true of ASIO devices. If true, you can call showControlPanel() + to display it. + */ + virtual bool hasControlPanel() const; + + /** Shows a device-specific control panel if there is one. + + This should only be called for devices which return true from hasControlPanel(). + */ + virtual bool showControlPanel(); + +protected: + /** Creates a device, setting its name and type member variables. */ + AudioIODevice (const String& deviceName, + const String& typeName); + + /** @internal */ + String name, typeName; +}; + +#endif // __JUCE_AUDIOIODEVICE_JUCEHEADER__ +/********* End of inlined file: juce_AudioIODevice.h *********/ + +/** + An AudioIODeviceCallback object which streams audio through an AudioProcessor. + + To use one of these, just make it the callback used by your AudioIODevice, and + give it a processor to use by calling setProcessor(). + + It's also a MidiInputCallback, so you can connect it to both an audio and midi + input to send both streams through the processor. + + @see AudioProcessor, AudioProcessorGraph +*/ +class JUCE_API AudioProcessorPlayer : public AudioIODeviceCallback, + public MidiInputCallback +{ +public: + + /** + */ + AudioProcessorPlayer(); + + /** Destructor. */ + virtual ~AudioProcessorPlayer(); + + /** Sets the processor that should be played. + + The processor that is passed in will not be deleted or owned by this object. + To stop anything playing, pass in 0 to this method. + */ + void setProcessor (AudioProcessor* const processorToPlay); + + /** Returns the current audio processor that is being played. + */ + AudioProcessor* getCurrentProcessor() const throw() { return processor; } + + /** Returns a midi message collector that you can pass midi messages to if you + want them to be injected into the midi stream that is being sent to the + processor. + */ + MidiMessageCollector& getMidiMessageCollector() throw() { return messageCollector; } + + /** @internal */ + void audioDeviceIOCallback (const float** inputChannelData, + int totalNumInputChannels, + float** outputChannelData, + int totalNumOutputChannels, + int numSamples); + /** @internal */ + void audioDeviceAboutToStart (AudioIODevice* device); + /** @internal */ + void audioDeviceStopped(); + /** @internal */ + void handleIncomingMidiMessage (MidiInput* source, const MidiMessage& message); + + juce_UseDebuggingNewOperator + +private: + AudioProcessor* processor; + CriticalSection lock; + double sampleRate; + int blockSize; + bool isPrepared; + + int numInputChans, numOutputChans; + float* channels [128]; + float* outputChans [128]; + const float* inputChans [128]; + AudioSampleBuffer tempBuffer; + + MidiBuffer incomingMidi; + MidiMessageCollector messageCollector; + + AudioProcessorPlayer (const AudioProcessorPlayer&); + const AudioProcessorPlayer& operator= (const AudioProcessorPlayer&); +}; + +#endif // __JUCE_AUDIOPROCESSORPLAYER_JUCEHEADER__ +/********* End of inlined file: juce_AudioProcessorPlayer.h *********/ + +#endif +#ifndef __JUCE_GENERICAUDIOPROCESSOREDITOR_JUCEHEADER__ + +/********* Start of inlined file: juce_GenericAudioProcessorEditor.h *********/ +#ifndef __JUCE_GENERICAUDIOPROCESSOREDITOR_JUCEHEADER__ +#define __JUCE_GENERICAUDIOPROCESSOREDITOR_JUCEHEADER__ + +/********* Start of inlined file: juce_PropertyPanel.h *********/ +#ifndef __JUCE_PROPERTYPANEL_JUCEHEADER__ +#define __JUCE_PROPERTYPANEL_JUCEHEADER__ + +/********* Start of inlined file: juce_PropertyComponent.h *********/ +#ifndef __JUCE_PROPERTYCOMPONENT_JUCEHEADER__ +#define __JUCE_PROPERTYCOMPONENT_JUCEHEADER__ + +class EditableProperty; + +/** + A base class for a component that goes in a PropertyPanel and displays one of + an item's properties. + + Subclasses of this are used to display a property in various forms, e.g. a + ChoicePropertyComponent shows its value as a combo box; a SliderPropertyComponent + shows its value as a slider; a TextPropertyComponent as a text box, etc. + + A subclass must implement the refresh() method which will be called to tell the + component to update itself, and is also responsible for calling this it when the + item that it refers to is changed. + + @see PropertyPanel, TextPropertyComponent, SliderPropertyComponent, + ChoicePropertyComponent, ButtonPropertyComponent, BooleanPropertyComponent +*/ +class JUCE_API PropertyComponent : public Component +{ +public: + + /** Creates a PropertyComponent. + + @param propertyName the name is stored as this component's name, and is + used as the name displayed next to this component in + a property panel + @param preferredHeight the height that the component should be given - some + items may need to be larger than a normal row height. + This value can also be set if a subclass changes the + preferredHeight member variable. + */ + PropertyComponent (const String& propertyName, + const int preferredHeight = 25); + + /** Destructor. */ + ~PropertyComponent(); + + /** Returns this item's preferred height. + + This value is specified either in the constructor or by a subclass changing the + preferredHeight member variable. + */ + int getPreferredHeight() const throw() { return preferredHeight; } + + /** Updates the property component if the item it refers to has changed. + + A subclass must implement this method, and other objects may call it to + force it to refresh itself. + + The subclass should be economical in the amount of work is done, so for + example it should check whether it really needs to do a repaint rather than + just doing one every time this method is called, as it may be called when + the value being displayed hasn't actually changed. + */ + virtual void refresh() = 0; + + /** The default paint method fills the background and draws a label for the + item's name. + + @see LookAndFeel::drawPropertyComponentBackground(), LookAndFeel::drawPropertyComponentLabel() + */ + void paint (Graphics& g); + + /** The default resize method positions any child component to the right of this + one, based on the look and feel's default label size. + */ + void resized(); + + /** By default, this just repaints the component. */ + void enablementChanged(); + + juce_UseDebuggingNewOperator + +protected: + /** Used by the PropertyPanel to determine how high this component needs to be. + + A subclass can update this value in its constructor but shouldn't alter it later + as changes won't necessarily be picked up. + */ + int preferredHeight; +}; + +#endif // __JUCE_PROPERTYCOMPONENT_JUCEHEADER__ +/********* End of inlined file: juce_PropertyComponent.h *********/ + +/********* Start of inlined file: juce_Viewport.h *********/ +#ifndef __JUCE_VIEWPORT_JUCEHEADER__ +#define __JUCE_VIEWPORT_JUCEHEADER__ + +/********* Start of inlined file: juce_ScrollBar.h *********/ +#ifndef __JUCE_SCROLLBAR_JUCEHEADER__ +#define __JUCE_SCROLLBAR_JUCEHEADER__ + +/********* Start of inlined file: juce_Button.h *********/ +#ifndef __JUCE_BUTTON_JUCEHEADER__ +#define __JUCE_BUTTON_JUCEHEADER__ + +/********* Start of inlined file: juce_TooltipWindow.h *********/ +#ifndef __JUCE_TOOLTIPWINDOW_JUCEHEADER__ +#define __JUCE_TOOLTIPWINDOW_JUCEHEADER__ + +/********* Start of inlined file: juce_TooltipClient.h *********/ +#ifndef __JUCE_TOOLTIPCLIENT_JUCEHEADER__ +#define __JUCE_TOOLTIPCLIENT_JUCEHEADER__ + +/** + Components that want to use pop-up tooltips should implement this interface. + + A TooltipWindow will wait for the mouse to hover over a component that + implements the TooltipClient interface, and when it finds one, it will display + the tooltip returned by its getTooltip() method. + + @see TooltipWindow, SettableTooltipClient +*/ +class JUCE_API TooltipClient +{ +public: + /** Destructor. */ + virtual ~TooltipClient() {} + + /** Returns the string that this object wants to show as its tooltip. */ + virtual const String getTooltip() = 0; +}; + +/** + An implementation of TooltipClient that stores the tooltip string and a method + for changing it. + + This makes it easy to add a tooltip to a custom component, by simply adding this + as a base class and calling setTooltip(). + + Many of the Juce widgets already use this as a base class to implement their + tooltips. + + @see TooltipClient, TooltipWindow +*/ +class JUCE_API SettableTooltipClient : public TooltipClient +{ +public: + + /** Destructor. */ + virtual ~SettableTooltipClient() {} + + virtual void setTooltip (const String& newTooltip) { tooltipString = newTooltip; } + + virtual const String getTooltip() { return tooltipString; } + + juce_UseDebuggingNewOperator + +protected: + String tooltipString; +}; + +#endif // __JUCE_TOOLTIPCLIENT_JUCEHEADER__ +/********* End of inlined file: juce_TooltipClient.h *********/ + +/** + A window that displays a pop-up tooltip when the mouse hovers over another component. + + To enable tooltips in your app, just create a single instance of a TooltipWindow + object. + + The TooltipWindow object will then stay invisible, waiting until the mouse + hovers for the specified length of time - it will then see if it's currently + over a component which implements the TooltipClient interface, and if so, + it will make itself visible to show the tooltip in the appropriate place. + + @see TooltipClient, SettableTooltipClient +*/ +class JUCE_API TooltipWindow : public Component, + private Timer +{ +public: + + /** Creates a tooltip window. + + Make sure your app only creates one instance of this class, otherwise you'll + get multiple overlaid tooltips appearing. The window will initially be invisible + and will make itself visible when it needs to display a tip. + + To change the style of tooltips, see the LookAndFeel class for its tooltip + methods. + + @param parentComponent if set to 0, the TooltipWindow will appear on the desktop, + otherwise the tooltip will be added to the given parent + component. + @param millisecondsBeforeTipAppears the time for which the mouse has to stay still + before a tooltip will be shown + + @see TooltipClient, LookAndFeel::drawTooltip, LookAndFeel::getTooltipSize + */ + TooltipWindow (Component* parentComponent = 0, + const int millisecondsBeforeTipAppears = 700); + + /** Destructor. */ + ~TooltipWindow(); + + /** A set of colour IDs to use to change the colour of various aspects of the tooltip. + + These constants can be used either via the Component::setColour(), or LookAndFeel::setColour() + methods. + + @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour + */ + enum ColourIds + { + backgroundColourId = 0x1001b00, /**< The colour to fill the background with. */ + textColourId = 0x1001c00, /**< The colour to use for the text. */ + outlineColourId = 0x1001c10 /**< The colour to use to draw an outline around the tooltip. */ + }; + + juce_UseDebuggingNewOperator + +private: + + const int millisecondsBeforeTipAppears; + int mouseX, mouseY, mouseClicks; + unsigned int lastMouseMoveTime, lastHideTime; + Component* lastComponentUnderMouse; + bool changedCompsSinceShown; + String tip; + + void paint (Graphics& g); + void mouseEnter (const MouseEvent& e); + void timerCallback(); + + void showFor (Component* const c); + + TooltipWindow (const TooltipWindow&); + const TooltipWindow& operator= (const TooltipWindow&); +}; + +#endif // __JUCE_TOOLTIPWINDOW_JUCEHEADER__ +/********* End of inlined file: juce_TooltipWindow.h *********/ + +class Button; + +/** + Used to receive callbacks when a button is clicked. + + @see Button::addButtonListener, Button::removeButtonListener +*/ +class JUCE_API ButtonListener +{ +public: + /** Destructor. */ + virtual ~ButtonListener() {} + + /** Called when the button is clicked. */ + virtual void buttonClicked (Button* button) = 0; + + /** Called when the button's state changes. */ + virtual void buttonStateChanged (Button*) {} +}; + +/** + A base class for buttons. + + This contains all the logic for button behaviours such as enabling/disabling, + responding to shortcut keystrokes, auto-repeating when held down, toggle-buttons + and radio groups, etc. + + @see TextButton, DrawableButton, ToggleButton +*/ +class JUCE_API Button : public Component, + public SettableTooltipClient, + public ApplicationCommandManagerListener, + private KeyListener +{ +protected: + + /** Creates a button. + + @param buttonName the text to put in the button (the component's name is also + initially set to this string, but these can be changed later + using the setName() and setButtonText() methods) + */ + Button (const String& buttonName); + +public: + /** Destructor. */ + virtual ~Button(); + + /** Changes the button's text. + + @see getButtonText + */ + void setButtonText (const String& newText) throw(); + + /** Returns the text displayed in the button. + + @see setButtonText + */ + const String getButtonText() const throw() { return text; } + + /** Returns true if the button is currently being held down by the mouse. + + @see isOver + */ + bool isDown() const throw(); + + /** Returns true if the mouse is currently over the button. + + This will be also be true if the mouse is being held down. + + @see isDown + */ + bool isOver() const throw(); + + /** A button has an on/off state associated with it, and this changes that. + + By default buttons are 'off' and for simple buttons that you click to perform + an action you won't change this. Toggle buttons, however will want to + change their state when turned on or off. + + @param shouldBeOn whether to set the button's toggle state to be on or + off. If it's a member of a button group, this will + always try to turn it on, and to turn off any other + buttons in the group + @param sendChangeNotification if true, a callback will be made to clicked(); if false + the button will be repainted but no notification will + be sent + @see getToggleState, setRadioGroupId + */ + void setToggleState (const bool shouldBeOn, + const bool sendChangeNotification); + + /** Returns true if the button in 'on'. + + By default buttons are 'off' and for simple buttons that you click to perform + an action you won't change this. Toggle buttons, however will want to + change their state when turned on or off. + + @see setToggleState + */ + bool getToggleState() const throw() { return isOn; } + + /** This tells the button to automatically flip the toggle state when + the button is clicked. + + If set to true, then before the clicked() callback occurs, the toggle-state + of the button is flipped. + */ + void setClickingTogglesState (const bool shouldToggle) throw(); + + /** Returns true if this button is set to be an automatic toggle-button. + + This returns the last value that was passed to setClickingTogglesState(). + */ + bool getClickingTogglesState() const throw(); + + /** Enables the button to act as a member of a mutually-exclusive group + of 'radio buttons'. + + If the group ID is set to a non-zero number, then this button will + act as part of a group of buttons with the same ID, only one of + which can be 'on' at the same time. Note that when it's part of + a group, clicking a toggle-button that's 'on' won't turn it off. + + To find other buttons with the same ID, this button will search through + its sibling components for ToggleButtons, so all the buttons for a + particular group must be placed inside the same parent component. + + Set the group ID back to zero if you want it to act as a normal toggle + button again. + + @see getRadioGroupId + */ + void setRadioGroupId (const int newGroupId); + + /** Returns the ID of the group to which this button belongs. + + (See setRadioGroupId() for an explanation of this). + */ + int getRadioGroupId() const throw() { return radioGroupId; } + + /** Registers a listener to receive events when this button's state changes. + + If the listener is already registered, this will not register it again. + + @see removeButtonListener + */ + void addButtonListener (ButtonListener* const newListener) throw(); + + /** Removes a previously-registered button listener + + @see addButtonListener + */ + void removeButtonListener (ButtonListener* const listener) throw(); + + /** Causes the button to act as if it's been clicked. + + This will asynchronously make the button draw itself going down and up, and + will then call back the clicked() method as if mouse was clicked on it. + + @see clicked + */ + virtual void triggerClick(); + + /** Sets a command ID for this button to automatically invoke when it's clicked. + + When the button is pressed, it will use the given manager to trigger the + command ID. + + Obviously be careful that the ApplicationCommandManager doesn't get deleted + before this button is. To disable the command triggering, call this method and + pass 0 for the parameters. + + If generateTooltip is true, then the button's tooltip will be automatically + generated based on the name of this command and its current shortcut key. + + @see addShortcut, getCommandID + */ + void setCommandToTrigger (ApplicationCommandManager* commandManagerToUse, + const int commandID, + const bool generateTooltip); + + /** Returns the command ID that was set by setCommandToTrigger(). + */ + int getCommandID() const throw() { return commandID; } + + /** Assigns a shortcut key to trigger the button. + + The button registers itself with its top-level parent component for keypresses. + + Note that a different way of linking buttons to keypresses is by using the + setKeyPressToTrigger() method to invoke a command - the difference being that + setting a shortcut allows the button to be temporarily linked to a keypress + only while it's on-screen. + + @see clearShortcuts + */ + void addShortcut (const KeyPress& key); + + /** Removes all key shortcuts that had been set for this button. + + @see addShortcut + */ + void clearShortcuts(); + + /** Returns true if the given keypress is a shortcut for this button. + + @see addShortcut + */ + bool isRegisteredForShortcut (const KeyPress& key) const throw(); + + /** Sets an auto-repeat speed for the button when it is held down. + + (Auto-repeat is disabled by default). + + @param initialDelayInMillisecs how long to wait after the mouse is pressed before + triggering the next click. If this is zero, auto-repeat + is disabled + @param repeatDelayInMillisecs the frequently subsequent repeated clicks should be + triggered + @param minimumDelayInMillisecs if this is greater than 0, the auto-repeat speed will + get faster, the longer the button is held down, up to the + minimum interval specified here + */ + void setRepeatSpeed (const int initialDelayInMillisecs, + const int repeatDelayInMillisecs, + const int minimumDelayInMillisecs = -1) throw(); + + /** Sets whether the button click should happen when the mouse is pressed or released. + + By default the button is only considered to have been clicked when the mouse is + released, but setting this to true will make it call the clicked() method as soon + as the button is pressed. + + This is useful if the button is being used to show a pop-up menu, as it allows + the click to be used as a drag onto the menu. + */ + void setTriggeredOnMouseDown (const bool isTriggeredOnMouseDown) throw(); + + /** Returns the number of milliseconds since the last time the button + went into the 'down' state. + */ + uint32 getMillisecondsSinceButtonDown() const throw(); + + /** (overridden from Component to do special stuff). */ + void setVisible (bool shouldBeVisible); + + /** Sets the tooltip for this button. + + @see TooltipClient, TooltipWindow + */ + void setTooltip (const String& newTooltip); + + // (implementation of the TooltipClient method) + const String getTooltip(); + + /** A combination of these flags are used by setConnectedEdges(). + */ + enum ConnectedEdgeFlags + { + ConnectedOnLeft = 1, + ConnectedOnRight = 2, + ConnectedOnTop = 4, + ConnectedOnBottom = 8 + }; + + /** Hints about which edges of the button might be connected to adjoining buttons. + + The value passed in is a bitwise combination of any of the values in the + ConnectedEdgeFlags enum. + + E.g. if you are placing two buttons adjacent to each other, you could use this to + indicate which edges are touching, and the LookAndFeel might choose to drawn them + without rounded corners on the edges that connect. It's only a hint, so the + LookAndFeel can choose to ignore it if it's not relevent for this type of + button. + */ + void setConnectedEdges (const int connectedEdgeFlags) throw(); + + /** Returns the set of flags passed into setConnectedEdges(). */ + int getConnectedEdgeFlags() const throw() { return connectedEdgeFlags; } + + /** Indicates whether the button adjoins another one on its left edge. + @see setConnectedEdges + */ + bool isConnectedOnLeft() const throw() { return (connectedEdgeFlags & ConnectedOnLeft) != 0; } + + /** Indicates whether the button adjoins another one on its right edge. + @see setConnectedEdges + */ + bool isConnectedOnRight() const throw() { return (connectedEdgeFlags & ConnectedOnRight) != 0; } + + /** Indicates whether the button adjoins another one on its top edge. + @see setConnectedEdges + */ + bool isConnectedOnTop() const throw() { return (connectedEdgeFlags & ConnectedOnTop) != 0; } + + /** Indicates whether the button adjoins another one on its bottom edge. + @see setConnectedEdges + */ + bool isConnectedOnBottom() const throw() { return (connectedEdgeFlags & ConnectedOnBottom) != 0; } + + /** Used by setState(). */ + enum ButtonState + { + buttonNormal, + buttonOver, + buttonDown + }; + + /** Can be used to force the button into a particular state. + + This only changes the button's appearance, it won't trigger a click, or stop any mouse-clicks + from happening. + + The state that you set here will only last until it is automatically changed when the mouse + enters or exits the button, or the mouse-button is pressed or released. + */ + void setState (const ButtonState newState); + + juce_UseDebuggingNewOperator + +protected: + + /** This method is called when the button has been clicked. + + Subclasses can override this to perform whatever they actions they need + to do. + + Alternatively, a ButtonListener can be added to the button, and these listeners + will be called when the click occurs. + + @see triggerClick + */ + virtual void clicked(); + + /** This method is called when the button has been clicked. + + By default it just calls clicked(), but you might want to override it to handle + things like clicking when a modifier key is pressed, etc. + + @see ModifierKeys + */ + virtual void clicked (const ModifierKeys& modifiers); + + /** Subclasses should override this to actually paint the button's contents. + + It's better to use this than the paint method, because it gives you information + about the over/down state of the button. + + @param g the graphics context to use + @param isMouseOverButton true if the button is either in the 'over' or + 'down' state + @param isButtonDown true if the button should be drawn in the 'down' position + */ + virtual void paintButton (Graphics& g, + bool isMouseOverButton, + bool isButtonDown) = 0; + + /** Called when the button's up/down/over state changes. + + Subclasses can override this if they need to do something special when the button + goes up or down. + + @see isDown, isOver + */ + virtual void buttonStateChanged(); + + /** @internal */ + virtual void internalClickCallback (const ModifierKeys& modifiers); + /** @internal */ + void handleCommandMessage (int commandId); + /** @internal */ + void mouseEnter (const MouseEvent& e); + /** @internal */ + void mouseExit (const MouseEvent& e); + /** @internal */ + void mouseDown (const MouseEvent& e); + /** @internal */ + void mouseDrag (const MouseEvent& e); + /** @internal */ + void mouseUp (const MouseEvent& e); + /** @internal */ + bool keyPressed (const KeyPress& key); + /** @internal */ + bool keyPressed (const KeyPress& key, Component* originatingComponent); + /** @internal */ + bool keyStateChanged (Component* originatingComponent); + /** @internal */ + void paint (Graphics& g); + /** @internal */ + void parentHierarchyChanged(); + /** @internal */ + void focusGained (FocusChangeType cause); + /** @internal */ + void focusLost (FocusChangeType cause); + /** @internal */ + void enablementChanged(); + /** @internal */ + void applicationCommandInvoked (const ApplicationCommandTarget::InvocationInfo&); + /** @internal */ + void applicationCommandListChanged(); + +private: + + Array shortcuts; + Component* keySource; + String text; + SortedSet buttonListeners; + + friend class InternalButtonRepeatTimer; + Timer* repeatTimer; + uint32 buttonPressTime, lastTimeCallbackTime; + ApplicationCommandManager* commandManagerToUse; + int autoRepeatDelay, autoRepeatSpeed, autoRepeatMinimumDelay; + int radioGroupId, commandID, connectedEdgeFlags; + ButtonState buttonState; + + bool isOn : 1; + bool clickTogglesState : 1; + bool needsToRelease : 1; + bool needsRepainting : 1; + bool isKeyDown : 1; + bool triggerOnMouseDown : 1; + bool generateTooltip : 1; + + void repeatTimerCallback() throw(); + Timer& getRepeatTimer() throw(); + + ButtonState updateState (const MouseEvent* const e) throw(); + bool isShortcutPressed() const throw(); + void turnOffOtherButtonsInGroup (const bool sendChangeNotification); + + void flashButtonState() throw(); + void sendClickMessage (const ModifierKeys& modifiers); + void sendStateMessage(); + + Button (const Button&); + const Button& operator= (const Button&); +}; + +#endif // __JUCE_BUTTON_JUCEHEADER__ +/********* End of inlined file: juce_Button.h *********/ + +class ScrollBar; + +/** + A class for receiving events from a ScrollBar. + + You can register a ScrollBarListener with a ScrollBar using the ScrollBar::addListener() + method, and it will be called when the bar's position changes. + + @see ScrollBar::addListener, ScrollBar::removeListener +*/ +class JUCE_API ScrollBarListener +{ +public: + /** Destructor. */ + virtual ~ScrollBarListener() {} + + /** Called when a ScrollBar is moved. + + @param scrollBarThatHasMoved the bar that has moved + @param newRangeStart the new range start of this bar + */ + virtual void scrollBarMoved (ScrollBar* scrollBarThatHasMoved, + const double newRangeStart) = 0; +}; + +/** + A scrollbar component. + + To use a scrollbar, set up its total range using the setRangeLimits() method - this + sets the range of values it can represent. Then you can use setCurrentRange() to + change the position and size of the scrollbar's 'thumb'. + + Registering a ScrollBarListener with the scrollbar will allow you to find out when + the user moves it, and you can use the getCurrentRangeStart() to find out where + they moved it to. + + The scrollbar will adjust its own visibility according to whether its thumb size + allows it to actually be scrolled. + + For most purposes, it's probably easier to use a ViewportContainer or ListBox + instead of handling a scrollbar directly. + + @see ScrollBarListener +*/ +class JUCE_API ScrollBar : public Component, + public AsyncUpdater, + private Timer +{ +public: + + /** Creates a Scrollbar. + + @param isVertical whether it should be a vertical or horizontal bar + @param buttonsAreVisible whether to show the up/down or left/right buttons + */ + ScrollBar (const bool isVertical, + const bool buttonsAreVisible = true); + + /** Destructor. */ + ~ScrollBar(); + + /** Returns true if the scrollbar is vertical, false if it's horizontal. */ + bool isVertical() const throw() { return vertical; } + + /** Changes the scrollbar's direction. + + You'll also need to resize the bar appropriately - this just changes its internal + layout. + + @param shouldBeVertical true makes it vertical; false makes it horizontal. + */ + void setOrientation (const bool shouldBeVertical) throw(); + + /** Shows or hides the scrollbar's buttons. */ + void setButtonVisibility (const bool buttonsAreVisible); + + /** Tells the scrollbar whether to make itself invisible when not needed. + + The default behaviour is for a scrollbar to become invisible when the thumb + fills the whole of its range (i.e. when it can't be moved). Setting this + value to false forces the bar to always be visible. + */ + void setAutoHide (const bool shouldHideWhenFullRange); + + /** Sets the minimum and maximum values that the bar will move between. + + The bar's thumb will always be constrained so that the top of the thumb + will be >= minimum, and the bottom of the thumb <= maximum. + + @see setCurrentRange + */ + void setRangeLimits (const double minimum, + const double maximum) throw(); + + /** Returns the lower value that the thumb can be set to. + + This is the value set by setRangeLimits(). + */ + double getMinimumRangeLimit() const throw() { return minimum; } + + /** Returns the upper value that the thumb can be set to. + + This is the value set by setRangeLimits(). + */ + double getMaximumRangeLimit() const throw() { return maximum; } + + /** Changes the position of the scrollbar's 'thumb'. + + This sets both the position and size of the thumb - to just set the position without + changing the size, you can use setCurrentRangeStart(). + + If this method call actually changes the scrollbar's position, it will trigger an + asynchronous call to ScrollBarListener::scrollBarMoved() for all the listeners that + are registered. + + @param newStart the top (or left) of the thumb, in the range + getMinimumRangeLimit() <= newStart <= getMaximumRangeLimit(). If the + value is beyond these limits, it will be clipped. + @param newSize the size of the thumb, such that + getMinimumRangeLimit() <= newStart + newSize <= getMaximumRangeLimit(). If the + size is beyond these limits, it will be clipped. + @see setCurrentRangeStart, getCurrentRangeStart, getCurrentRangeSize + */ + void setCurrentRange (double newStart, + double newSize) throw(); + + /** Moves the bar's thumb position. + + This will move the thumb position without changing the thumb size. Note + that the maximum thumb start position is (getMaximumRangeLimit() - getCurrentRangeSize()). + + If this method call actually changes the scrollbar's position, it will trigger an + asynchronous call to ScrollBarListener::scrollBarMoved() for all the listeners that + are registered. + + @see setCurrentRange + */ + void setCurrentRangeStart (double newStart) throw(); + + /** Returns the position of the top of the thumb. + + @see setCurrentRangeStart + */ + double getCurrentRangeStart() const throw() { return rangeStart; } + + /** Returns the current size of the thumb. + + @see setCurrentRange + */ + double getCurrentRangeSize() const throw() { return rangeSize; } + + /** Sets the amount by which the up and down buttons will move the bar. + + The value here is in terms of the total range, and is added or subtracted + from the thumb position when the user clicks an up/down (or left/right) button. + */ + void setSingleStepSize (const double newSingleStepSize) throw(); + + /** Moves the scrollbar by a number of single-steps. + + This will move the bar by a multiple of its single-step interval (as + specified using the setSingleStepSize() method). + + A positive value here will move the bar down or to the right, a negative + value moves it up or to the left. + */ + void moveScrollbarInSteps (const int howManySteps) throw(); + + /** Moves the scroll bar up or down in pages. + + This will move the bar by a multiple of its current thumb size, effectively + doing a page-up or down. + + A positive value here will move the bar down or to the right, a negative + value moves it up or to the left. + */ + void moveScrollbarInPages (const int howManyPages) throw(); + + /** Scrolls to the top (or left). + + This is the same as calling setCurrentRangeStart (getMinimumRangeLimit()); + */ + void scrollToTop() throw(); + + /** Scrolls to the bottom (or right). + + This is the same as calling setCurrentRangeStart (getMaximumRangeLimit() - getCurrentRangeSize()); + */ + void scrollToBottom() throw(); + + /** Changes the delay before the up and down buttons autorepeat when they are held + down. + + For an explanation of what the parameters are for, see Button::setRepeatSpeed(). + + @see Button::setRepeatSpeed + */ + void setButtonRepeatSpeed (const int initialDelayInMillisecs, + const int repeatDelayInMillisecs, + const int minimumDelayInMillisecs = -1) throw(); + + /** A set of colour IDs to use to change the colour of various aspects of the component. + + These constants can be used either via the Component::setColour(), or LookAndFeel::setColour() + methods. + + @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour + */ + enum ColourIds + { + backgroundColourId = 0x1000300, /**< The background colour of the scrollbar. */ + thumbColourId = 0x1000400, /**< A base colour to use for the thumb. The look and feel will probably use variations on this colour. */ + trackColourId = 0x1000401 /**< A base colour to use for the slot area of the bar. The look and feel will probably use variations on this colour. */ + }; + + /** Registers a listener that will be called when the scrollbar is moved. */ + void addListener (ScrollBarListener* const listener) throw(); + + /** Deregisters a previously-registered listener. */ + void removeListener (ScrollBarListener* const listener) throw(); + + /** @internal */ + bool keyPressed (const KeyPress& key); + /** @internal */ + void mouseWheelMove (const MouseEvent& e, float wheelIncrementX, float wheelIncrementY); + /** @internal */ + void lookAndFeelChanged(); + /** @internal */ + void handleAsyncUpdate(); + /** @internal */ + void mouseDown (const MouseEvent& e); + /** @internal */ + void mouseDrag (const MouseEvent& e); + /** @internal */ + void mouseUp (const MouseEvent& e); + /** @internal */ + void paint (Graphics& g); + /** @internal */ + void resized(); + + juce_UseDebuggingNewOperator + +private: + + double minimum, maximum; + double rangeStart, rangeSize; + double singleStepSize, dragStartRange; + int thumbAreaStart, thumbAreaSize, thumbStart, thumbSize; + int dragStartMousePos, lastMousePos; + int initialDelayInMillisecs, repeatDelayInMillisecs, minimumDelayInMillisecs; + bool vertical, isDraggingThumb, alwaysVisible; + Button* upButton; + Button* downButton; + SortedSet listeners; + + void updateThumbPosition() throw(); + void timerCallback(); + + ScrollBar (const ScrollBar&); + const ScrollBar& operator= (const ScrollBar&); +}; + +#endif // __JUCE_SCROLLBAR_JUCEHEADER__ +/********* End of inlined file: juce_ScrollBar.h *********/ + +/** + A Viewport is used to contain a larger child component, and allows the child + to be automatically scrolled around. + + To use a Viewport, just create one and set the component that goes inside it + using the setViewedComponent() method. When the child component changes size, + the Viewport will adjust its scrollbars accordingly. + + A subclass of the viewport can be created which will receive calls to its + visibleAreaChanged() method when the subcomponent changes position or size. + +*/ +class JUCE_API Viewport : public Component, + private ComponentListener, + private ScrollBarListener +{ +public: + + /** Creates a Viewport. + + The viewport is initially empty - use the setViewedComponent() method to + add a child component for it to manage. + */ + Viewport (const String& componentName = String::empty); + + /** Destructor. */ + ~Viewport(); + + /** Sets the component that this viewport will contain and scroll around. + + This will add the given component to this Viewport and position it at + (0, 0). + + (Don't add or remove any child components directly using the normal + Component::addChildComponent() methods). + + @param newViewedComponent the component to add to this viewport (this pointer + may be null). The component passed in will be deleted + by the Viewport when it's no longer needed + @see getViewedComponent + */ + void setViewedComponent (Component* const newViewedComponent); + + /** Returns the component that's currently being used inside the Viewport. + + @see setViewedComponent + */ + Component* getViewedComponent() const throw() { return contentComp; } + + /** Changes the position of the viewed component. + + The inner component will be moved so that the pixel at the top left of + the viewport will be the pixel at position (xPixelsOffset, yPixelsOffset) + within the inner component. + + This will update the scrollbars and might cause a call to visibleAreaChanged(). + + @see getViewPositionX, getViewPositionY, setViewPositionProportionately + */ + void setViewPosition (const int xPixelsOffset, + const int yPixelsOffset); + + /** Changes the view position as a proportion of the distance it can move. + + The values here are from 0.0 to 1.0 - where (0, 0) would put the + visible area in the top-left, and (1, 1) would put it as far down and + to the right as it's possible to go whilst keeping the child component + on-screen. + */ + void setViewPositionProportionately (const double proportionX, + const double proportionY); + + /** Returns the position within the child component of the top-left of its visible area. + @see getViewWidth, setViewPosition + */ + int getViewPositionX() const throw() { return lastVX; } + + /** Returns the position within the child component of the top-left of its visible area. + @see getViewHeight, setViewPosition + */ + int getViewPositionY() const throw() { return lastVY; } + + /** Returns the width of the visible area of the child component. + + This may be less than the width of this Viewport if there's a vertical scrollbar + or if the child component is itself smaller. + */ + int getViewWidth() const throw() { return lastVW; } + + /** Returns the height of the visible area of the child component. + + This may be less than the height of this Viewport if there's a horizontal scrollbar + or if the child component is itself smaller. + */ + int getViewHeight() const throw() { return lastVH; } + + /** Returns the width available within this component for the contents. + + This will be the width of the viewport component minus the width of a + vertical scrollbar (if visible). + */ + int getMaximumVisibleWidth() const throw(); + + /** Returns the height available within this component for the contents. + + This will be the height of the viewport component minus the space taken up + by a horizontal scrollbar (if visible). + */ + int getMaximumVisibleHeight() const throw(); + + /** Callback method that is called when the visible area changes. + + This will be called when the visible area is moved either be scrolling or + by calls to setViewPosition(), etc. + */ + virtual void visibleAreaChanged (int visibleX, int visibleY, + int visibleW, int visibleH); + + /** Turns scrollbars on or off. + + If set to false, the scrollbars won't ever appear. When true (the default) + they will appear only when needed. + */ + void setScrollBarsShown (const bool showVerticalScrollbarIfNeeded, + const bool showHorizontalScrollbarIfNeeded); + + /** True if the vertical scrollbar is enabled. + @see setScrollBarsShown + */ + bool isVerticalScrollBarShown() const throw() { return showVScrollbar; } + + /** True if the horizontal scrollbar is enabled. + @see setScrollBarsShown + */ + bool isHorizontalScrollBarShown() const throw() { return showHScrollbar; } + + /** Changes the width of the scrollbars. + + If this isn't specified, the default width from the LookAndFeel class will be used. + + @see LookAndFeel::getDefaultScrollbarWidth + */ + void setScrollBarThickness (const int thickness); + + /** Returns the thickness of the scrollbars. + + @see setScrollBarThickness + */ + int getScrollBarThickness() const throw(); + + /** Changes the distance that a single-step click on a scrollbar button + will move the viewport. + */ + void setSingleStepSizes (const int stepX, const int stepY); + + /** Shows or hides the buttons on any scrollbars that are used. + + @see ScrollBar::setButtonVisibility + */ + void setScrollBarButtonVisibility (const bool buttonsVisible); + + /** Returns a pointer to the scrollbar component being used. + + Handy if you need to customise the bar somehow. + */ + ScrollBar* getVerticalScrollBar() const throw() { return verticalScrollBar; } + + /** Returns a pointer to the scrollbar component being used. + + Handy if you need to customise the bar somehow. + */ + ScrollBar* getHorizontalScrollBar() const throw() { return horizontalScrollBar; } + + juce_UseDebuggingNewOperator + + /** @internal */ + void resized(); + /** @internal */ + void scrollBarMoved (ScrollBar* scrollBarThatHasMoved, const double newRangeStart); + /** @internal */ + void mouseWheelMove (const MouseEvent& e, float wheelIncrementX, float wheelIncrementY); + /** @internal */ + bool keyPressed (const KeyPress& key); + /** @internal */ + void componentMovedOrResized (Component& component, bool wasMoved, bool wasResized); + /** @internal */ + bool useMouseWheelMoveIfNeeded (const MouseEvent& e, float wheelIncrementX, float wheelIncrementY); + +private: + Component* contentComp; + int lastVX, lastVY, lastVW, lastVH; + int scrollBarThickness; + int singleStepX, singleStepY; + bool showHScrollbar, showVScrollbar; + Component* contentHolder; + ScrollBar* verticalScrollBar; + ScrollBar* horizontalScrollBar; + + void updateVisibleRegion(); + Viewport (const Viewport&); + const Viewport& operator= (const Viewport&); +}; + +#endif // __JUCE_VIEWPORT_JUCEHEADER__ +/********* End of inlined file: juce_Viewport.h *********/ + +/** + A panel that holds a list of PropertyComponent objects. + + This panel displays a list of PropertyComponents, and allows them to be organised + into collapsible sections. + + To use, simply create one of these and add your properties to it with addProperties() + or addSection(). + + @see PropertyComponent +*/ +class JUCE_API PropertyPanel : public Component +{ +public: + + /** Creates an empty property panel. */ + PropertyPanel(); + + /** Destructor. */ + ~PropertyPanel(); + + /** Deletes all property components from the panel. + */ + void clear(); + + /** Adds a set of properties to the panel. + + The components in the list will be owned by this object and will be automatically + deleted later on when no longer needed. + + These properties are added without them being inside a named section. If you + want them to be kept together in a collapsible section, use addSection() instead. + */ + void addProperties (const Array & newPropertyComponents); + + /** Adds a set of properties to the panel. + + These properties are added at the bottom of the list, under a section heading with + a plus/minus button that allows it to be opened and closed. + + The components in the list will be owned by this object and will be automatically + deleted later on when no longer needed. + + To add properies without them being in a section, use addProperties(). + */ + void addSection (const String& sectionTitle, + const Array & newPropertyComponents, + const bool shouldSectionInitiallyBeOpen = true); + + /** Calls the refresh() method of all PropertyComponents in the panel */ + void refreshAll() const; + + /** Returns a list of all the names of sections in the panel. + + These are the sections that have been added with addSection(). + */ + const StringArray getSectionNames() const; + + /** Returns true if the section at this index is currently open. + + The index is from 0 up to the number of items returned by getSectionNames(). + */ + bool isSectionOpen (const int sectionIndex) const; + + /** Opens or closes one of the sections. + + The index is from 0 up to the number of items returned by getSectionNames(). + */ + void setSectionOpen (const int sectionIndex, const bool shouldBeOpen); + + /** Saves the current state of open/closed sections so it can be restored later. + + The caller is responsible for deleting the object that is returned. + + To restore this state, use restoreOpennessState(). + + @see restoreOpennessState + */ + XmlElement* getOpennessState() const; + + /** Restores a previously saved arrangement of open/closed sections. + + This will try to restore a snapshot of the panel's state that was created by + the getOpennessState() method. If any of the sections named in the original + XML aren't present, they will be ignored. + + @see getOpennessState + */ + void restoreOpennessState (const XmlElement& newState); + + /** Sets a message to be displayed when there are no properties in the panel. + + The default message is "nothing selected". + */ + void setMessageWhenEmpty (const String& newMessage); + + /** Returns the message that is displayed when there are no properties. + @see setMessageWhenEmpty + */ + const String& getMessageWhenEmpty() const throw(); + + /** @internal */ + void paint (Graphics& g); + /** @internal */ + void resized(); + + juce_UseDebuggingNewOperator + +private: + Viewport* viewport; + Component* propertyHolderComponent; + String messageWhenEmpty; + + void updatePropHolderLayout() const; + void updatePropHolderLayout (const int width) const; +}; + +#endif // __JUCE_PROPERTYPANEL_JUCEHEADER__ +/********* End of inlined file: juce_PropertyPanel.h *********/ + +/** + A type of UI component that displays the parameters of an AudioProcessor as + a simple list of sliders. + + This can be used for showing an editor for a processor that doesn't supply + its own custom editor. + + @see AudioProcessor +*/ +class JUCE_API GenericAudioProcessorEditor : public AudioProcessorEditor +{ +public: + + GenericAudioProcessorEditor (AudioProcessor* const owner); + ~GenericAudioProcessorEditor(); + + void paint (Graphics& g); + void resized(); + + juce_UseDebuggingNewOperator + +private: + PropertyPanel* panel; +}; + +#endif // __JUCE_GENERICAUDIOPROCESSOREDITOR_JUCEHEADER__ +/********* End of inlined file: juce_GenericAudioProcessorEditor.h *********/ + +#endif +#ifndef __JUCE_AUDIOFORMATREADERSOURCE_JUCEHEADER__ + +/********* Start of inlined file: juce_AudioFormatReaderSource.h *********/ +#ifndef __JUCE_AUDIOFORMATREADERSOURCE_JUCEHEADER__ +#define __JUCE_AUDIOFORMATREADERSOURCE_JUCEHEADER__ + +/********* Start of inlined file: juce_PositionableAudioSource.h *********/ +#ifndef __JUCE_POSITIONABLEAUDIOSOURCE_JUCEHEADER__ +#define __JUCE_POSITIONABLEAUDIOSOURCE_JUCEHEADER__ + +/********* Start of inlined file: juce_AudioSource.h *********/ +#ifndef __JUCE_AUDIOSOURCE_JUCEHEADER__ +#define __JUCE_AUDIOSOURCE_JUCEHEADER__ + +/** + Used by AudioSource::getNextAudioBlock(). +*/ +struct JUCE_API AudioSourceChannelInfo +{ + /** The destination buffer to fill with audio data. + + When the AudioSource::getNextAudioBlock() method is called, the active section + of this buffer should be filled with whatever output the source produces. + + Only the samples specified by the startSample and numSamples members of this structure + should be affected by the call. + + The contents of the buffer when it is passed to the the AudioSource::getNextAudioBlock() + method can be treated as the input if the source is performing some kind of filter operation, + but should be cleared if this is not the case - the clearActiveBufferRegion() is + a handy way of doing this. + + The number of channels in the buffer could be anything, so the AudioSource + must cope with this in whatever way is appropriate for its function. + */ + AudioSampleBuffer* buffer; + + /** The first sample in the buffer from which the callback is expected + to write data. */ + int startSample; + + /** The number of samples in the buffer which the callback is expected to + fill with data. */ + int numSamples; + + /** Convenient method to clear the buffer if the source is not producing any data. */ + void clearActiveBufferRegion() const + { + if (buffer != 0) + buffer->clear (startSample, numSamples); + } +}; + +/** + Base class for objects that can produce a continuous stream of audio. + + @see AudioFormatReaderSource, ResamplingAudioSource +*/ +class JUCE_API AudioSource +{ +protected: + + /** Creates an AudioSource. */ + AudioSource() throw() {} + +public: + /** Destructor. */ + virtual ~AudioSource() {} + + /** Tells the source to prepare for playing. + + The source can use this opportunity to initialise anything it needs to. + + Note that this method could be called more than once in succession without + a matching call to releaseResources(), so make sure your code is robust and + can handle that kind of situation. + + @param samplesPerBlockExpected the number of samples that the source + will be expected to supply each time its + getNextAudioBlock() method is called. This + number may vary slightly, because it will be dependent + on audio hardware callbacks, and these aren't + guaranteed to always use a constant block size, so + the source should be able to cope with small variations. + @param sampleRate the sample rate that the output will be used at - this + is needed by sources such as tone generators. + @see releaseResources, getNextAudioBlock + */ + virtual void prepareToPlay (int samplesPerBlockExpected, + double sampleRate) = 0; + + /** Allows the source to release anything it no longer needs after playback has stopped. + + This will be called when the source is no longer going to have its getNextAudioBlock() + method called, so it should release any spare memory, etc. that it might have + allocated during the prepareToPlay() call. + + Note that there's no guarantee that prepareToPlay() will actually have been called before + releaseResources(), and it may be called more than once in succession, so make sure your + code is robust and doesn't make any assumptions about when it will be called. + + @see prepareToPlay, getNextAudioBlock + */ + virtual void releaseResources() = 0; + + /** Called repeatedly to fetch subsequent blocks of audio data. + + After calling the prepareToPlay() method, this callback will be made each + time the audio playback hardware (or whatever other destination the audio + data is going to) needs another block of data. + + It will generally be called on a high-priority system thread, or possibly even + an interrupt, so be careful not to do too much work here, as that will cause + audio glitches! + + @see AudioSourceChannelInfo, prepareToPlay, releaseResources + */ + virtual void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) = 0; +}; + +#endif // __JUCE_AUDIOSOURCE_JUCEHEADER__ +/********* End of inlined file: juce_AudioSource.h *********/ + +/** + A type of AudioSource which can be repositioned. + + The basic AudioSource just streams continuously with no idea of a current + time or length, so the PositionableAudioSource is used for a finite stream + that has a current read position. + + @see AudioSource, AudioTransportSource +*/ +class JUCE_API PositionableAudioSource : public AudioSource +{ +protected: + + /** Creates the PositionableAudioSource. */ + PositionableAudioSource() throw() {} + +public: + /** Destructor */ + ~PositionableAudioSource() {} + + /** Tells the stream to move to a new position. + + Calling this indicates that the next call to AudioSource::getNextAudioBlock() + should return samples from this position. + + Note that this may be called on a different thread to getNextAudioBlock(), + so the subclass should make sure it's synchronised. + */ + virtual void setNextReadPosition (int newPosition) = 0; + + /** Returns the position from which the next block will be returned. + + @see setNextReadPosition + */ + virtual int getNextReadPosition() const = 0; + + /** Returns the total length of the stream (in samples). */ + virtual int getTotalLength() const = 0; + + /** Returns true if this source is actually playing in a loop. */ + virtual bool isLooping() const = 0; +}; + +#endif // __JUCE_POSITIONABLEAUDIOSOURCE_JUCEHEADER__ +/********* End of inlined file: juce_PositionableAudioSource.h *********/ + +/********* Start of inlined file: juce_AudioFormatReader.h *********/ +#ifndef __JUCE_AUDIOFORMATREADER_JUCEHEADER__ +#define __JUCE_AUDIOFORMATREADER_JUCEHEADER__ + +class AudioFormat; + +/** + Reads samples from an audio file stream. + + A subclass that reads a specific type of audio format will be created by + an AudioFormat object. + + @see AudioFormat, AudioFormatWriter +*/ +class JUCE_API AudioFormatReader +{ +protected: + + /** Creates an AudioFormatReader object. + + @param sourceStream the stream to read from - this will be deleted + by this object when it is no longer needed. (Some + specialised readers might not use this parameter and + can leave it as 0). + @param formatName the description that will be returned by the getFormatName() + method + */ + AudioFormatReader (InputStream* const sourceStream, + const String& formatName); + +public: + /** Destructor. */ + virtual ~AudioFormatReader(); + + /** Returns a description of what type of format this is. + + E.g. "AIFF" + */ + const String getFormatName() const throw() { return formatName; } + + /** Reads samples from the stream. + + @param destSamples an array of buffers into which the sample data for each + channel will be written. + If the format is fixed-point, each channel will be written + as an array of 32-bit signed integers using the full + range -0x80000000 to 0x7fffffff, regardless of the source's + bit-depth. If it is a floating-point format, you should cast + the resulting array to a (float**) to get the values (in the + range -1.0 to 1.0 or beyond) + If the format is stereo, then destSamples[0] is the left channel + data, and destSamples[1] is the right channel. + The array passed in should be zero-terminated, and it's ok to + pass in an array with a different number of channels than + the number in the stream, so if you pass in an array with only + one channel and the stream is stereo, the reader will + put a merged sum of the stereo channels into the single + destination channel. + @param startSample the offset into the audio stream from which the samples + should be read, as a number of samples from the start of the + stream. It's ok for this to be beyond the start or end of the + available data - any samples that can't be read will be padded + with zeros. + @param numSamples the number of samples to read. If this is greater than the + number of samples available, the result will be padded with + zeros + @returns true if the operation succeeded, false if there was an error. Note + that reading sections of data beyond the extent of the stream isn't an + error - the reader should just return zeros for these regions + @see readMaxLevels + */ + virtual bool read (int** destSamples, + int64 startSample, + int numSamples) = 0; + + /** Finds the highest and lowest sample levels from a section of the audio stream. + + This will read a block of samples from the stream, and measure the + highest and lowest sample levels from the channels in that section, returning + these as normalised floating-point levels. + + @param startSample the offset into the audio stream to start reading from. It's + ok for this to be beyond the start or end of the stream. + @param numSamples how many samples to read + @param lowestLeft on return, this is the lowest absolute sample from the left channel + @param highestLeft on return, this is the highest absolute sample from the left channel + @param lowestRight on return, this is the lowest absolute sample from the right + channel (if there is one) + @param highestRight on return, this is the highest absolute sample from the right + channel (if there is one) + @see read + */ + virtual void readMaxLevels (int64 startSample, + int64 numSamples, + float& lowestLeft, + float& highestLeft, + float& lowestRight, + float& highestRight); + + /** Scans the source looking for a sample whose magnitude is in a specified range. + + This will read from the source, either forwards or backwards between two sample + positions, until it finds a sample whose magnitude lies between two specified levels. + + If it finds a suitable sample, it returns its position; if not, it will return -1. + + There's also a minimumConsecutiveSamples setting to help avoid spikes or zero-crossing + points when you're searching for a continuous range of samples + + @param startSample the first sample to look at + @param numSamplesToSearch the number of samples to scan. If this value is negative, + the search will go backwards + @param magnitudeRangeMinimum the lowest magnitude (inclusive) that is considered a hit, from 0 to 1.0 + @param magnitudeRangeMaximum the highest magnitude (inclusive) that is considered a hit, from 0 to 1.0 + @param minimumConsecutiveSamples if this is > 0, the method will only look for a sequence + of this many consecutive samples, all of which lie + within the target range. When it finds such a sequence, + it returns the position of the first in-range sample + it found (i.e. the earliest one if scanning forwards, the + latest one if scanning backwards) + */ + int64 searchForLevel (int64 startSample, + int64 numSamplesToSearch, + const double magnitudeRangeMinimum, + const double magnitudeRangeMaximum, + const int minimumConsecutiveSamples); + + /** The sample-rate of the stream. */ + double sampleRate; + + /** The number of bits per sample, e.g. 16, 24, 32. */ + unsigned int bitsPerSample; + + /** The total number of samples in the audio stream. */ + int64 lengthInSamples; + + /** The total number of channels in the audio stream. */ + unsigned int numChannels; + + /** Indicates whether the data is floating-point or fixed. */ + bool usesFloatingPointData; + + /** A set of metadata values that the reader has pulled out of the stream. + + Exactly what these values are depends on the format, so you can + check out the format implementation code to see what kind of stuff + they understand. + */ + StringPairArray metadataValues; + + /** The input stream, for use by subclasses. */ + InputStream* input; + + juce_UseDebuggingNewOperator + +private: + String formatName; + + AudioFormatReader (const AudioFormatReader&); + const AudioFormatReader& operator= (const AudioFormatReader&); +}; + +#endif // __JUCE_AUDIOFORMATREADER_JUCEHEADER__ +/********* End of inlined file: juce_AudioFormatReader.h *********/ + +/** + A type of AudioSource that will read from an AudioFormatReader. + + @see PositionableAudioSource, AudioTransportSource, BufferingAudioSource +*/ +class JUCE_API AudioFormatReaderSource : public PositionableAudioSource +{ +public: + + /** Creates an AudioFormatReaderSource for a given reader. + + @param sourceReader the reader to use as the data source + @param deleteReaderWhenThisIsDeleted if true, the reader passed-in will be deleted + when this object is deleted; if false it will be + left up to the caller to manage its lifetime + */ + AudioFormatReaderSource (AudioFormatReader* const sourceReader, + const bool deleteReaderWhenThisIsDeleted); + + /** Destructor. */ + ~AudioFormatReaderSource(); + + /** Toggles loop-mode. + + If set to true, it will continuously loop the input source. If false, + it will just emit silence after the source has finished. + + @see isLooping + */ + void setLooping (const bool shouldLoop) throw(); + + /** Returns whether loop-mode is turned on or not. */ + bool isLooping() const { return looping; } + + /** Returns the reader that's being used. */ + AudioFormatReader* getAudioFormatReader() const throw() { return reader; } + + /** Implementation of the AudioSource method. */ + void prepareToPlay (int samplesPerBlockExpected, double sampleRate); + + /** Implementation of the AudioSource method. */ + void releaseResources(); + + /** Implementation of the AudioSource method. */ + void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill); + + /** Implements the PositionableAudioSource method. */ + void setNextReadPosition (int newPosition); + + /** Implements the PositionableAudioSource method. */ + int getNextReadPosition() const; + + /** Implements the PositionableAudioSource method. */ + int getTotalLength() const; + + juce_UseDebuggingNewOperator + +private: + AudioFormatReader* reader; + bool deleteReader; + + int volatile nextPlayPos; + bool volatile looping; + + void readBufferSection (int start, int length, AudioSampleBuffer& buffer, int startSample); + + AudioFormatReaderSource (const AudioFormatReaderSource&); + const AudioFormatReaderSource& operator= (const AudioFormatReaderSource&); +}; + +#endif // __JUCE_AUDIOFORMATREADERSOURCE_JUCEHEADER__ +/********* End of inlined file: juce_AudioFormatReaderSource.h *********/ + +#endif +#ifndef __JUCE_AUDIOSOURCE_JUCEHEADER__ + +#endif +#ifndef __JUCE_AUDIOSOURCEPLAYER_JUCEHEADER__ + +/********* Start of inlined file: juce_AudioSourcePlayer.h *********/ +#ifndef __JUCE_AUDIOSOURCEPLAYER_JUCEHEADER__ +#define __JUCE_AUDIOSOURCEPLAYER_JUCEHEADER__ + +/** + Wrapper class to continuously stream audio from an audio source to an + AudioIODevice. + + This object acts as an AudioIODeviceCallback, so can be attached to an + output device, and will stream audio from an AudioSource. +*/ +class JUCE_API AudioSourcePlayer : public AudioIODeviceCallback +{ +public: + + /** Creates an empty AudioSourcePlayer. */ + AudioSourcePlayer(); + + /** Destructor. + + Make sure this object isn't still being used by an AudioIODevice before + deleting it! + */ + virtual ~AudioSourcePlayer(); + + /** Changes the current audio source to play from. + + If the source passed in is already being used, this method will do nothing. + If the source is not null, its prepareToPlay() method will be called + before it starts being used for playback. + + If there's another source currently playing, its releaseResources() method + will be called after it has been swapped for the new one. + + @param newSource the new source to use - this will NOT be deleted + by this object when no longer needed, so it's the + caller's responsibility to manage it. + */ + void setSource (AudioSource* newSource); + + /** Returns the source that's playing. + + May return 0 if there's no source. + */ + AudioSource* getCurrentSource() const throw() { return source; } + + /** Implementation of the AudioIODeviceCallback method. */ + void audioDeviceIOCallback (const float** inputChannelData, + int totalNumInputChannels, + float** outputChannelData, + int totalNumOutputChannels, + int numSamples); + + /** Implementation of the AudioIODeviceCallback method. */ + void audioDeviceAboutToStart (AudioIODevice* device); + + /** Implementation of the AudioIODeviceCallback method. */ + void audioDeviceStopped(); + + juce_UseDebuggingNewOperator + +private: + + CriticalSection readLock; + AudioSource* source; + double sampleRate; + int bufferSize; + float* channels [128]; + float* outputChans [128]; + const float* inputChans [128]; + AudioSampleBuffer tempBuffer; + + AudioSourcePlayer (const AudioSourcePlayer&); + const AudioSourcePlayer& operator= (const AudioSourcePlayer&); +}; + +#endif // __JUCE_AUDIOSOURCEPLAYER_JUCEHEADER__ +/********* End of inlined file: juce_AudioSourcePlayer.h *********/ + +#endif +#ifndef __JUCE_AUDIOTRANSPORTSOURCE_JUCEHEADER__ + +/********* Start of inlined file: juce_AudioTransportSource.h *********/ +#ifndef __JUCE_AUDIOTRANSPORTSOURCE_JUCEHEADER__ +#define __JUCE_AUDIOTRANSPORTSOURCE_JUCEHEADER__ + +/********* Start of inlined file: juce_BufferingAudioSource.h *********/ +#ifndef __JUCE_BUFFERINGAUDIOSOURCE_JUCEHEADER__ +#define __JUCE_BUFFERINGAUDIOSOURCE_JUCEHEADER__ + +/** + An AudioSource which takes another source as input, and buffers it using a thread. + + Create this as a wrapper around another thread, and it will read-ahead with + a background thread to smooth out playback. You can either create one of these + directly, or use it indirectly using an AudioTransportSource. + + @see PositionableAudioSource, AudioTransportSource +*/ +class JUCE_API BufferingAudioSource : public PositionableAudioSource +{ +public: + + /** Creates a BufferingAudioSource. + + @param source the input source to read from + @param deleteSourceWhenDeleted if true, then the input source object will + be deleted when this object is deleted + @param numberOfSamplesToBuffer the size of buffer to use for reading ahead + */ + BufferingAudioSource (PositionableAudioSource* source, + const bool deleteSourceWhenDeleted, + int numberOfSamplesToBuffer); + + /** Destructor. + + The input source may be deleted depending on whether the deleteSourceWhenDeleted + flag was set in the constructor. + */ + ~BufferingAudioSource(); + + /** Implementation of the AudioSource method. */ + void prepareToPlay (int samplesPerBlockExpected, double sampleRate); + + /** Implementation of the AudioSource method. */ + void releaseResources(); + + /** Implementation of the AudioSource method. */ + void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill); + + /** Implements the PositionableAudioSource method. */ + void setNextReadPosition (int newPosition); + + /** Implements the PositionableAudioSource method. */ + int getNextReadPosition() const; + + /** Implements the PositionableAudioSource method. */ + int getTotalLength() const { return source->getTotalLength(); } + + /** Implements the PositionableAudioSource method. */ + bool isLooping() const { return source->isLooping(); } + + juce_UseDebuggingNewOperator + +private: + + PositionableAudioSource* source; + bool deleteSourceWhenDeleted; + int numberOfSamplesToBuffer; + AudioSampleBuffer buffer; + CriticalSection bufferStartPosLock; + int volatile bufferValidStart, bufferValidEnd, nextPlayPos; + bool wasSourceLooping; + double volatile sampleRate; + + friend class SharedBufferingAudioSourceThread; + bool readNextBufferChunk(); + void readBufferSection (int start, int length, int bufferOffset); + + BufferingAudioSource (const BufferingAudioSource&); + const BufferingAudioSource& operator= (const BufferingAudioSource&); +}; + +#endif // __JUCE_BUFFERINGAUDIOSOURCE_JUCEHEADER__ +/********* End of inlined file: juce_BufferingAudioSource.h *********/ + +/********* Start of inlined file: juce_ResamplingAudioSource.h *********/ +#ifndef __JUCE_RESAMPLINGAUDIOSOURCE_JUCEHEADER__ +#define __JUCE_RESAMPLINGAUDIOSOURCE_JUCEHEADER__ + +/** + A type of AudioSource that takes an input source and changes its sample rate. + + @see AudioSource +*/ +class JUCE_API ResamplingAudioSource : public AudioSource +{ +public: + + /** Creates a ResamplingAudioSource for a given input source. + + @param inputSource the input source to read from + @param deleteInputWhenDeleted if true, the input source will be deleted when + this object is deleted + */ + ResamplingAudioSource (AudioSource* const inputSource, + const bool deleteInputWhenDeleted); + + /** Destructor. */ + ~ResamplingAudioSource(); + + /** Changes the resampling ratio. + + (This value can be changed at any time, even while the source is running). + + @param samplesInPerOutputSample if set to 1.0, the input is passed through; higher + values will speed it up; lower values will slow it + down. The ratio must be greater than 0 + */ + void setResamplingRatio (const double samplesInPerOutputSample); + + /** Returns the current resampling ratio. + + This is the value that was set by setResamplingRatio(). + */ + double getResamplingRatio() const throw() { return ratio; } + + void prepareToPlay (int samplesPerBlockExpected, double sampleRate); + void releaseResources(); + void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill); + + juce_UseDebuggingNewOperator + +private: + AudioSource* const input; + const bool deleteInputWhenDeleted; + double ratio, lastRatio; + AudioSampleBuffer buffer; + int bufferPos, sampsInBuffer; + double subSampleOffset; + double coefficients[6]; + CriticalSection ratioLock; + + void setFilterCoefficients (double c1, double c2, double c3, double c4, double c5, double c6); + void createLowPass (const double proportionalRate); + + struct FilterState + { + double x1, x2, y1, y2; + }; + + FilterState filterStates[2]; + void resetFilters(); + + void applyFilter (float* samples, int num, FilterState& fs); + + ResamplingAudioSource (const ResamplingAudioSource&); + const ResamplingAudioSource& operator= (const ResamplingAudioSource&); +}; + +#endif // __JUCE_RESAMPLINGAUDIOSOURCE_JUCEHEADER__ +/********* End of inlined file: juce_ResamplingAudioSource.h *********/ + +/** + An AudioSource that takes a PositionableAudioSource and allows it to be + played, stopped, started, etc. + + This can also be told use a buffer and background thread to read ahead, and + if can correct for different sample-rates. + + You may want to use one of these along with an AudioSourcePlayer and AudioIODevice + to control playback of an audio file. + + @see AudioSource, AudioSourcePlayer +*/ +class JUCE_API AudioTransportSource : public PositionableAudioSource, + public ChangeBroadcaster +{ +public: + + /** Creates an AudioTransportSource. + + After creating one of these, use the setSource() method to select an input source. + */ + AudioTransportSource(); + + /** Destructor. */ + ~AudioTransportSource(); + + /** Sets the reader that is being used as the input source. + + This will stop playback, reset the position to 0 and change to the new reader. + + The source passed in will not be deleted by this object, so must be managed by + the caller. + + @param newSource the new input source to use. This may be zero + @param readAheadBufferSize a size of buffer to use for reading ahead. If this + is zero, no reading ahead will be done; if it's + greater than zero, a BufferingAudioSource will be used + to do the reading-ahead + @param sourceSampleRateToCorrectFor if this is non-zero, it specifies the sample + rate of the source, and playback will be sample-rate + adjusted to maintain playback at the correct pitch. If + this is 0, no sample-rate adjustment will be performed + */ + void setSource (PositionableAudioSource* const newSource, + int readAheadBufferSize = 0, + double sourceSampleRateToCorrectFor = 0.0); + + /** Changes the current playback position in the source stream. + + The next time the getNextAudioBlock() method is called, this + is the time from which it'll read data. + + @see getPosition + */ + void setPosition (double newPosition); + + /** Returns the position that the next data block will be read from + + This is a time in seconds. + */ + double getCurrentPosition() const; + + /** Returns true if the player has stopped because its input stream ran out of data. + */ + bool hasStreamFinished() const throw() { return inputStreamEOF; } + + /** Starts playing (if a source has been selected). + + If it starts playing, this will send a message to any ChangeListeners + that are registered with this object. + */ + void start(); + + /** Stops playing. + + If it's actually playing, this will send a message to any ChangeListeners + that are registered with this object. + */ + void stop(); + + /** Returns true if it's currently playing. */ + bool isPlaying() const throw() { return playing; } + + /** Changes the gain to apply to the output. + + @param newGain a factor by which to multiply the outgoing samples, + so 1.0 = 0dB, 0.5 = -6dB, 2.0 = 6dB, etc. + */ + void setGain (const float newGain) throw(); + + /** Returns the current gain setting. + + @see setGain + */ + float getGain() const throw() { return gain; } + + /** Implementation of the AudioSource method. */ + void prepareToPlay (int samplesPerBlockExpected, double sampleRate); + + /** Implementation of the AudioSource method. */ + void releaseResources(); + + /** Implementation of the AudioSource method. */ + void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill); + + /** Implements the PositionableAudioSource method. */ + void setNextReadPosition (int newPosition); + + /** Implements the PositionableAudioSource method. */ + int getNextReadPosition() const; + + /** Implements the PositionableAudioSource method. */ + int getTotalLength() const; + + /** Implements the PositionableAudioSource method. */ + bool isLooping() const; + + juce_UseDebuggingNewOperator + +private: + PositionableAudioSource* source; + ResamplingAudioSource* resamplerSource; + BufferingAudioSource* bufferingSource; + PositionableAudioSource* positionableSource; + AudioSource* masterSource; + + CriticalSection callbackLock; + float volatile gain, lastGain; + bool volatile playing, stopped; + double sampleRate, sourceSampleRate; + int blockSize, readAheadBufferSize; + bool isPrepared, inputStreamEOF; + + AudioTransportSource (const AudioTransportSource&); + const AudioTransportSource& operator= (const AudioTransportSource&); +}; + +#endif // __JUCE_AUDIOTRANSPORTSOURCE_JUCEHEADER__ +/********* End of inlined file: juce_AudioTransportSource.h *********/ + +#endif +#ifndef __JUCE_BUFFERINGAUDIOSOURCE_JUCEHEADER__ + +#endif +#ifndef __JUCE_CHANNELREMAPPINGAUDIOSOURCE_JUCEHEADER__ + +/********* Start of inlined file: juce_ChannelRemappingAudioSource.h *********/ +#ifndef __JUCE_CHANNELREMAPPINGAUDIOSOURCE_JUCEHEADER__ +#define __JUCE_CHANNELREMAPPINGAUDIOSOURCE_JUCEHEADER__ + +/** + An AudioSource that takes the audio from another source, and re-maps its + input and output channels to a different arrangement. + + You can use this to increase or decrease the number of channels that an + audio source uses, or to re-order those channels. + + Call the reset() method before using it to set up a default mapping, and then + the setInputChannelMapping() and setOutputChannelMapping() methods to + create an appropriate mapping, otherwise no channels will be connected and + it'll produce silence. + + @see AudioSource +*/ +class ChannelRemappingAudioSource : public AudioSource +{ +public: + + /** Creates a remapping source that will pass on audio from the given input. + + @param source the input source to use. Make sure that this doesn't + get deleted before the ChannelRemappingAudioSource object + @param deleteSourceWhenDeleted if true, the input source will be deleted + when this object is deleted, if false, the caller is + responsible for its deletion + */ + ChannelRemappingAudioSource (AudioSource* const source, + const bool deleteSourceWhenDeleted); + + /** Destructor. */ + ~ChannelRemappingAudioSource(); + + /** Specifies a number of channels that this audio source must produce from its + getNextAudioBlock() callback. + */ + void setNumberOfChannelsToProduce (const int requiredNumberOfChannels) throw(); + + /** Clears any mapped channels. + + After this, no channels are mapped, so this object will produce silence. Create + some mappings with setInputChannelMapping() and setOutputChannelMapping(). + */ + void clearAllMappings() throw(); + + /** Creates an input channel mapping. + + When the getNextAudioBlock() method is called, the data in channel sourceChannelIndex of the incoming + data will be sent to destChannelIndex of our input source. + + @param destChannelIndex the index of an input channel in our input audio source (i.e. the + source specified when this object was created). + @param sourceChannelIndex the index of the input channel in the incoming audio data buffer + during our getNextAudioBlock() callback + */ + void setInputChannelMapping (const int destChannelIndex, + const int sourceChannelIndex) throw(); + + /** Creates an output channel mapping. + + When the getNextAudioBlock() method is called, the data returned in channel sourceChannelIndex by + our input audio source will be copied to channel destChannelIndex of the final buffer. + + @param sourceChannelIndex the index of an output channel coming from our input audio source + (i.e. the source specified when this object was created). + @param destChannelIndex the index of the output channel in the incoming audio data buffer + during our getNextAudioBlock() callback + */ + void setOutputChannelMapping (const int sourceChannelIndex, + const int destChannelIndex) throw(); + + /** Returns the channel from our input that will be sent to channel inputChannelIndex of + our input audio source. + */ + int getRemappedInputChannel (const int inputChannelIndex) const throw(); + + /** Returns the output channel to which channel outputChannelIndex of our input audio + source will be sent to. + */ + int getRemappedOutputChannel (const int outputChannelIndex) const throw(); + + /** Returns an XML object to encapsulate the state of the mappings. + + @see restoreFromXml + */ + XmlElement* createXml() const throw(); + + /** Restores the mappings from an XML object created by createXML(). + + @see createXml + */ + void restoreFromXml (const XmlElement& e) throw(); + + void prepareToPlay (int samplesPerBlockExpected, double sampleRate); + void releaseResources(); + void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill); + + juce_UseDebuggingNewOperator + +private: + int requiredNumberOfChannels; + Array remappedInputs, remappedOutputs; + + AudioSource* const source; + const bool deleteSourceWhenDeleted; + + AudioSampleBuffer buffer; + AudioSourceChannelInfo remappedInfo; + + CriticalSection lock; + + ChannelRemappingAudioSource (const ChannelRemappingAudioSource&); + const ChannelRemappingAudioSource& operator= (const ChannelRemappingAudioSource&); +}; + +#endif // __JUCE_CHANNELREMAPPINGAUDIOSOURCE_JUCEHEADER__ +/********* End of inlined file: juce_ChannelRemappingAudioSource.h *********/ + +#endif +#ifndef __JUCE_IIRFILTERAUDIOSOURCE_JUCEHEADER__ + +/********* Start of inlined file: juce_IIRFilterAudioSource.h *********/ +#ifndef __JUCE_IIRFILTERAUDIOSOURCE_JUCEHEADER__ +#define __JUCE_IIRFILTERAUDIOSOURCE_JUCEHEADER__ + +/** + An AudioSource that performs an IIR filter on another source. +*/ +class JUCE_API IIRFilterAudioSource : public AudioSource +{ +public: + + /** Creates a IIRFilterAudioSource for a given input source. + + @param inputSource the input source to read from + @param deleteInputWhenDeleted if true, the input source will be deleted when + this object is deleted + */ + IIRFilterAudioSource (AudioSource* const inputSource, + const bool deleteInputWhenDeleted); + + /** Destructor. */ + ~IIRFilterAudioSource(); + + /** Changes the filter to use the same parameters as the one being passed in. + */ + void setFilterParameters (const IIRFilter& newSettings); + + void prepareToPlay (int samplesPerBlockExpected, double sampleRate); + void releaseResources(); + void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill); + + juce_UseDebuggingNewOperator + +private: + + AudioSource* const input; + const bool deleteInputWhenDeleted; + OwnedArray iirFilters; + + IIRFilterAudioSource (const IIRFilterAudioSource&); + const IIRFilterAudioSource& operator= (const IIRFilterAudioSource&); +}; + +#endif // __JUCE_IIRFILTERAUDIOSOURCE_JUCEHEADER__ +/********* End of inlined file: juce_IIRFilterAudioSource.h *********/ + +#endif +#ifndef __JUCE_MIXERAUDIOSOURCE_JUCEHEADER__ + +/********* Start of inlined file: juce_MixerAudioSource.h *********/ +#ifndef __JUCE_MIXERAUDIOSOURCE_JUCEHEADER__ +#define __JUCE_MIXERAUDIOSOURCE_JUCEHEADER__ + +/** + An AudioSource that mixes together the output of a set of other AudioSources. + + Input sources can be added and removed while the mixer is running as long as their + prepareToPlay() and releaseResources() methods are called before and after adding + them to the mixer. +*/ +class JUCE_API MixerAudioSource : public AudioSource +{ +public: + + /** Creates a MixerAudioSource. + */ + MixerAudioSource(); + + /** Destructor. */ + ~MixerAudioSource(); + + /** Adds an input source to the mixer. + + If the mixer is running you'll need to make sure that the input source + is ready to play by calling its prepareToPlay() method before adding it. + If the mixer is stopped, then its input sources will be automatically + prepared when the mixer's prepareToPlay() method is called. + + @param newInput the source to add to the mixer + @param deleteWhenRemoved if true, then this source will be deleted when + the mixer is deleted or when removeAllInputs() is + called (unless the source is previously removed + with the removeInputSource method) + */ + void addInputSource (AudioSource* newInput, + const bool deleteWhenRemoved); + + /** Removes an input source. + + If the mixer is running, this will remove the source but not call its + releaseResources() method, so the caller might want to do this manually. + + @param input the source to remove + @param deleteSource whether to delete this source after it's been removed + */ + void removeInputSource (AudioSource* input, + const bool deleteSource); + + /** Removes all the input sources. + + If the mixer is running, this will remove the sources but not call their + releaseResources() method, so the caller might want to do this manually. + + Any sources which were added with the deleteWhenRemoved flag set will be + deleted by this method. + */ + void removeAllInputs(); + + /** Implementation of the AudioSource method. + + This will call prepareToPlay() on all its input sources. + */ + void prepareToPlay (int samplesPerBlockExpected, double sampleRate); + + /** Implementation of the AudioSource method. + + This will call releaseResources() on all its input sources. + */ + void releaseResources(); + + /** Implementation of the AudioSource method. */ + void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill); + + juce_UseDebuggingNewOperator + +private: + + VoidArray inputs; + BitArray inputsToDelete; + CriticalSection lock; + AudioSampleBuffer tempBuffer; + double currentSampleRate; + int bufferSizeExpected; + + MixerAudioSource (const MixerAudioSource&); + const MixerAudioSource& operator= (const MixerAudioSource&); +}; + +#endif // __JUCE_MIXERAUDIOSOURCE_JUCEHEADER__ +/********* End of inlined file: juce_MixerAudioSource.h *********/ + +#endif +#ifndef __JUCE_POSITIONABLEAUDIOSOURCE_JUCEHEADER__ + +#endif +#ifndef __JUCE_RESAMPLINGAUDIOSOURCE_JUCEHEADER__ + +#endif +#ifndef __JUCE_TONEGENERATORAUDIOSOURCE_JUCEHEADER__ + +/********* Start of inlined file: juce_ToneGeneratorAudioSource.h *********/ +#ifndef __JUCE_TONEGENERATORAUDIOSOURCE_JUCEHEADER__ +#define __JUCE_TONEGENERATORAUDIOSOURCE_JUCEHEADER__ + +/** + A simple AudioSource that generates a sine wave. + +*/ +class JUCE_API ToneGeneratorAudioSource : public AudioSource +{ +public: + + /** Creates a ToneGeneratorAudioSource. */ + ToneGeneratorAudioSource(); + + /** Destructor. */ + ~ToneGeneratorAudioSource(); + + /** Sets the signal's amplitude. */ + void setAmplitude (const float newAmplitude); + + /** Sets the signal's frequency. */ + void setFrequency (const double newFrequencyHz); + + /** Implementation of the AudioSource method. */ + void prepareToPlay (int samplesPerBlockExpected, double sampleRate); + + /** Implementation of the AudioSource method. */ + void releaseResources(); + + /** Implementation of the AudioSource method. */ + void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill); + + juce_UseDebuggingNewOperator + +private: + + double frequency, sampleRate; + double currentPhase, phasePerSample; + float amplitude; + + ToneGeneratorAudioSource (const ToneGeneratorAudioSource&); + const ToneGeneratorAudioSource& operator= (const ToneGeneratorAudioSource&); +}; + +#endif // __JUCE_TONEGENERATORAUDIOSOURCE_JUCEHEADER__ +/********* End of inlined file: juce_ToneGeneratorAudioSource.h *********/ + +#endif +#ifndef __JUCE_AUDIODEVICEMANAGER_JUCEHEADER__ + +/********* Start of inlined file: juce_AudioDeviceManager.h *********/ +#ifndef __JUCE_AUDIODEVICEMANAGER_JUCEHEADER__ +#define __JUCE_AUDIODEVICEMANAGER_JUCEHEADER__ + +/********* Start of inlined file: juce_AudioIODeviceType.h *********/ +#ifndef __JUCE_AUDIOIODEVICETYPE_JUCEHEADER__ +#define __JUCE_AUDIOIODEVICETYPE_JUCEHEADER__ + +/** + Represents a type of audio driver, such as DirectSound, ASIO, CoreAudio, etc. + + To get a list of available audio driver types, use the createDeviceTypes() + method. Each of the objects returned can then be used to list the available + devices of that type. E.g. + @code + OwnedArray types; + AudioIODeviceType::createDeviceTypes (types); + + for (int i = 0; i < types.size(); ++i) + { + String typeName (types[i]->getTypeName()); // This will be things like "DirectSound", "CoreAudio", etc. + + types[i]->scanForDevices(); // This must be called before getting the list of devices + + String deviceNames (types[i]->getDeviceNames()); // This will now return a list of available devices of this type + + for (int j = 0; j < deviceNames.size(); ++j) + { + AudioIODevice* device = types[i]->createDevice (deviceNames [j]); + + ... + } + } + @endcode + + For an easier way of managing audio devices and their settings, have a look at the + AudioDeviceManager class. + + @see AudioIODevice, AudioDeviceManager +*/ +class JUCE_API AudioIODeviceType +{ +public: + + /** Creates a list of available types. + + This will add a set of new AudioIODeviceType objects to the specified list, to + represent each available types of device. + + The objects that are created should be managed by the caller (the OwnedArray + will delete them when the array is itself deleted). + + When created, the objects are uninitialised, so you should call scanForDevices() + on each one before getting its list of devices. + */ + static void createDeviceTypes (OwnedArray & list); + + /** Returns the name of this type of driver that this object manages. + + This will be something like "DirectSound", "ASIO", "CoreAudio", "ALSA", etc. + */ + const String& getTypeName() const throw() { return typeName; } + + /** Refreshes the object's cached list of known devices. + + This must be called at least once before calling getDeviceNames() or any of + the other device creation methods. + */ + virtual void scanForDevices() = 0; + + /** Returns the list of available devices of this type. + + The scanForDevices() method must have been called to create this list. + + @param preferInputNames only really used by DirectSound where devices are split up + into inputs and outputs, this indicates whether to use + the input or output name to refer to a pair of devices. + */ + virtual const StringArray getDeviceNames (const bool preferInputNames = false) const = 0; + + /** Returns the name of the default device. + + This will be one of the names from the getDeviceNames() list. + + @param preferInputNames only really used by DirectSound where devices are split up + into inputs and outputs, this indicates whether to use + the input or output name to refer to a pair of devices. + @param numInputChannelsNeeded the number of input channels the user is expecting to need - this + may be used to help decide which device would be most suitable + @param numOutputChannelsNeeded the number of output channels the user is expecting to need - this + may be used to help decide which device would be most suitable + */ + virtual const String getDefaultDeviceName (const bool preferInputNames, + const int numInputChannelsNeeded, + const int numOutputChannelsNeeded) const = 0; + + /** Creates one of the devices of this type. + + The deviceName must be one of the strings returned by getDeviceNames(), and + scanForDevices() must have been called before this method is used. + */ + virtual AudioIODevice* createDevice (const String& deviceName) = 0; + + /** Destructor. */ + virtual ~AudioIODeviceType(); + +protected: + AudioIODeviceType (const tchar* const typeName); + +private: + String typeName; + + AudioIODeviceType (const AudioIODeviceType&); + const AudioIODeviceType& operator= (const AudioIODeviceType&); +}; + +#endif // __JUCE_AUDIOIODEVICETYPE_JUCEHEADER__ +/********* End of inlined file: juce_AudioIODeviceType.h *********/ + +/********* Start of inlined file: juce_MidiOutput.h *********/ +#ifndef __JUCE_MIDIOUTPUT_JUCEHEADER__ +#define __JUCE_MIDIOUTPUT_JUCEHEADER__ + +/** + Represents a midi output device. + + To create one of these, use the static getDevices() method to find out what + outputs are available, then use the openDevice() method to try to open one. + + @see MidiInput +*/ +class JUCE_API MidiOutput : private Thread +{ +public: + + /** Returns a list of the available midi output devices. + + You can open one of the devices by passing its index into the + openDevice() method. + + @see getDefaultDeviceIndex, openDevice + */ + static const StringArray getDevices(); + + /** Returns the index of the default midi output device to use. + + This refers to the index in the list returned by getDevices(). + */ + static int getDefaultDeviceIndex(); + + /** Tries to open one of the midi output devices. + + This will return a MidiOutput object if it manages to open it. You can then + send messages to this device, and delete it when no longer needed. + + If the device can't be opened, this will return a null pointer. + + @param deviceIndex the index of a device from the list returned by getDevices() + @see getDevices + */ + static MidiOutput* openDevice (int deviceIndex); + +#if JUCE_LINUX || DOXYGEN + /** LINUX ONLY - This will try to create a new midi output device. + + This will attempt to create a new midi output device that other apps can connect + to and use as their midi input. + + Returns 0 if a device can't be created. + + @param deviceName the name to use for the new device + */ + static MidiOutput* createNewDevice (const String& deviceName); +#endif + + /** Destructor. */ + ~MidiOutput(); + + /** Makes this device output a midi message. + + @see MidiMessage + */ + void sendMessageNow (const MidiMessage& message); + + /** Sends a midi reset to the device. */ + void reset(); + + /** Returns the current volume setting for this device. */ + bool getVolume (float& leftVol, + float& rightVol); + + /** Changes the overall volume for this device. */ + void setVolume (float leftVol, + float rightVol); + + /** This lets you supply a block of messages that will be sent out at some point + in the future. + + The MidiOutput class has an internal thread that can send out timestamped + messages - this appends a set of messages to its internal buffer, ready for + sending. + + This will only work if you've already started the thread with startBackgroundThread(). + + A time is supplied, at which the block of messages should be sent. This time uses + the same time base as Time::getMillisecondCounter(), and must be in the future. + + The samplesPerSecondForBuffer parameter indicates the number of samples per second + used by the MidiBuffer. Each event in a MidiBuffer has a sample position, and the + samplesPerSecondForBuffer value is needed to convert this sample position to a + real time. + */ + void sendBlockOfMessages (const MidiBuffer& buffer, + const double millisecondCounterToStartAt, + double samplesPerSecondForBuffer) throw(); + + /** Gets rid of any midi messages that had been added by sendBlockOfMessages(). + */ + void clearAllPendingMessages() throw(); + + /** Starts up a background thread so that the device can send blocks of data. + + Call this to get the device ready, before using sendBlockOfMessages(). + */ + void startBackgroundThread() throw(); + + /** Stops the background thread, and clears any pending midi events. + + @see startBackgroundThread + */ + void stopBackgroundThread() throw(); + + juce_UseDebuggingNewOperator + +private: + void* internal; + + struct PendingMessage + { + PendingMessage (const uint8* const data, const int len, const double sampleNumber) throw(); + + MidiMessage message; + PendingMessage* next; + + juce_UseDebuggingNewOperator + }; + + CriticalSection lock; + PendingMessage* firstMessage; + + MidiOutput() throw(); + MidiOutput (const MidiOutput&); + + void run(); +}; + +#endif // __JUCE_MIDIOUTPUT_JUCEHEADER__ +/********* End of inlined file: juce_MidiOutput.h *********/ + +/********* Start of inlined file: juce_ComboBox.h *********/ +#ifndef __JUCE_COMBOBOX_JUCEHEADER__ +#define __JUCE_COMBOBOX_JUCEHEADER__ + +/********* Start of inlined file: juce_Label.h *********/ +#ifndef __JUCE_LABEL_JUCEHEADER__ +#define __JUCE_LABEL_JUCEHEADER__ + +/********* Start of inlined file: juce_ComponentDeletionWatcher.h *********/ +#ifndef __JUCE_COMPONENTDELETIONWATCHER_JUCEHEADER__ +#define __JUCE_COMPONENTDELETIONWATCHER_JUCEHEADER__ + +/** + Object for monitoring a component, and later testing whether it's still valid. + + Slightly obscure, this one, but it's used internally for making sure that + after some callbacks, a component hasn't been deleted. It's more reliable than + just using isValidComponent(), which can provide false-positives if a new + component is created at the same memory location as an old one. +*/ +class JUCE_API ComponentDeletionWatcher +{ +public: + + /** Creates a watcher for a given component. + + The component must be valid at the time it's passed in. + */ + ComponentDeletionWatcher (const Component* const componentToWatch) throw(); + + /** Destructor. */ + ~ComponentDeletionWatcher() throw(); + + /** Returns true if the component has been deleted since the time that this + object was created. + */ + bool hasBeenDeleted() const throw(); + + /** Returns the component that's being watched, or null if it has been deleted. */ + const Component* getComponent() const throw(); + + juce_UseDebuggingNewOperator + +private: + const Component* const componentToWatch; + const uint32 componentUID; + + ComponentDeletionWatcher (const ComponentDeletionWatcher&); + const ComponentDeletionWatcher& operator= (const ComponentDeletionWatcher&); +}; + +#endif // __JUCE_COMPONENTDELETIONWATCHER_JUCEHEADER__ +/********* End of inlined file: juce_ComponentDeletionWatcher.h *********/ + +/********* Start of inlined file: juce_TextEditor.h *********/ +#ifndef __JUCE_TEXTEDITOR_JUCEHEADER__ +#define __JUCE_TEXTEDITOR_JUCEHEADER__ + +/********* Start of inlined file: juce_UndoManager.h *********/ +#ifndef __JUCE_UNDOMANAGER_JUCEHEADER__ +#define __JUCE_UNDOMANAGER_JUCEHEADER__ + +/********* Start of inlined file: juce_UndoableAction.h *********/ +#ifndef __JUCE_UNDOABLEACTION_JUCEHEADER__ +#define __JUCE_UNDOABLEACTION_JUCEHEADER__ + +/** + Used by the UndoManager class to store an action which can be done + and undone. + + @see UndoManager +*/ +class JUCE_API UndoableAction +{ +protected: + /** Creates an action. */ + UndoableAction() throw() {} + +public: + /** Destructor. */ + virtual ~UndoableAction() {} + + /** Overridden by a subclass to perform the action. + + This method is called by the UndoManager, and shouldn't be used directly by + applications. + + Be careful not to make any calls in a perform() method that could call + recursively back into the UndoManager::perform() method + + @returns true if the action could be performed. + @see UndoManager::perform + */ + virtual bool perform() = 0; + + /** Overridden by a subclass to undo the action. + + This method is called by the UndoManager, and shouldn't be used directly by + applications. + + Be careful not to make any calls in an undo() method that could call + recursively back into the UndoManager::perform() method + + @returns true if the action could be undone without any errors. + @see UndoManager::perform + */ + virtual bool undo() = 0; + + /** Returns a value to indicate how much memory this object takes up. + + Because the UndoManager keeps a list of UndoableActions, this is used + to work out how much space each one will take up, so that the UndoManager + can work out how many to keep. + + The default value returned here is 10 - units are arbitrary and + don't have to be accurate. + + @see UndoManager::getNumberOfUnitsTakenUpByStoredCommands, + UndoManager::setMaxNumberOfStoredUnits + */ + virtual int getSizeInUnits() { return 10; } +}; + +#endif // __JUCE_UNDOABLEACTION_JUCEHEADER__ +/********* End of inlined file: juce_UndoableAction.h *********/ + +/** + Manages a list of undo/redo commands. + + An UndoManager object keeps a list of past actions and can use these actions + to move backwards and forwards through an undo history. + + To use it, create subclasses of UndoableAction which perform all the + actions you need, then when you need to actually perform an action, create one + and pass it to the UndoManager's perform() method. + + The manager also uses the concept of 'transactions' to group the actions + together - all actions performed between calls to beginNewTransaction() are + grouped together and are all undone/redone as a group. + + The UndoManager is a ChangeBroadcaster, so listeners can register to be told + when actions are performed or undone. + + @see UndoableAction +*/ +class JUCE_API UndoManager : public ChangeBroadcaster +{ +public: + + /** Creates an UndoManager. + + @param maxNumberOfUnitsToKeep each UndoableAction object returns a value + to indicate how much storage it takes up + (UndoableAction::getSizeInUnits()), so this + lets you specify the maximum total number of + units that the undomanager is allowed to + keep in memory before letting the older actions + drop off the end of the list. + @param minimumTransactionsToKeep this specifies the minimum number of transactions + that will be kept, even if this involves exceeding + the amount of space specified in maxNumberOfUnitsToKeep + */ + UndoManager (const int maxNumberOfUnitsToKeep = 30000, + const int minimumTransactionsToKeep = 30); + + /** Destructor. */ + ~UndoManager(); + + /** Deletes all stored actions in the list. */ + void clearUndoHistory(); + + /** Returns the current amount of space to use for storing UndoableAction objects. + + @see setMaxNumberOfStoredUnits + */ + int getNumberOfUnitsTakenUpByStoredCommands() const; + + /** Sets the amount of space that can be used for storing UndoableAction objects. + + @param maxNumberOfUnitsToKeep each UndoableAction object returns a value + to indicate how much storage it takes up + (UndoableAction::getSizeInUnits()), so this + lets you specify the maximum total number of + units that the undomanager is allowed to + keep in memory before letting the older actions + drop off the end of the list. + @param minimumTransactionsToKeep this specifies the minimum number of transactions + that will be kept, even if this involves exceeding + the amount of space specified in maxNumberOfUnitsToKeep + @see getNumberOfUnitsTakenUpByStoredCommands + */ + void setMaxNumberOfStoredUnits (const int maxNumberOfUnitsToKeep, + const int minimumTransactionsToKeep); + + /** Performs an action and adds it to the undo history list. + + @param action the action to perform - this will be deleted by the UndoManager + when no longer needed + @param actionName if this string is non-empty, the current transaction will be + given this name; if it's empty, the current transaction name will + be left unchanged. See setCurrentTransactionName() + @returns true if the command succeeds - see UndoableAction::perform + @see beginNewTransaction + */ + bool perform (UndoableAction* const action, + const String& actionName = String::empty); + + /** Starts a new group of actions that together will be treated as a single transaction. + + All actions that are passed to the perform() method between calls to this + method are grouped together and undone/redone together by a single call to + undo() or redo(). + + @param actionName a description of the transaction that is about to be + performed + */ + void beginNewTransaction (const String& actionName = String::empty); + + /** Changes the name stored for the current transaction. + + Each transaction is given a name when the beginNewTransaction() method is + called, but this can be used to change that name without starting a new + transaction. + */ + void setCurrentTransactionName (const String& newName); + + /** Returns true if there's at least one action in the list to undo. + + @see getUndoDescription, undo, canRedo + */ + bool canUndo() const; + + /** Returns the description of the transaction that would be next to get undone. + + The description returned is the one that was passed into beginNewTransaction + before the set of actions was performed. + + @see undo + */ + const String getUndoDescription() const; + + /** Tries to roll-back the last transaction. + + @returns true if the transaction can be undone, and false if it fails, or + if there aren't any transactions to undo + */ + bool undo(); + + /** Tries to roll-back any actions that were added to the current transaction. + + This will perform an undo() only if there are some actions in the undo list + that were added after the last call to beginNewTransaction(). + + This is useful because it lets you call beginNewTransaction(), then + perform an operation which may or may not actually perform some actions, and + then call this method to get rid of any actions that might have been done + without it rolling back the previous transaction if nothing was actually + done. + + @returns true if any actions were undone. + */ + bool undoCurrentTransactionOnly(); + + /** Returns a list of the UndoableAction objects that have been performed during the + transaction that is currently open. + + Effectively, this is the list of actions that would be undone if undoCurrentTransactionOnly() + were to be called now. + + The first item in the list is the earliest action performed. + */ + void getActionsInCurrentTransaction (Array & actionsFound) const; + + /** Returns true if there's at least one action in the list to redo. + + @see getRedoDescription, redo, canUndo + */ + bool canRedo() const; + + /** Returns the description of the transaction that would be next to get redone. + + The description returned is the one that was passed into beginNewTransaction + before the set of actions was performed. + + @see redo + */ + const String getRedoDescription() const; + + /** Tries to redo the last transaction that was undone. + + @returns true if the transaction can be redone, and false if it fails, or + if there aren't any transactions to redo + */ + bool redo(); + + juce_UseDebuggingNewOperator + +private: + + OwnedArray > transactions; + StringArray transactionNames; + String currentTransactionName; + int totalUnitsStored, maxNumUnitsToKeep, minimumTransactionsToKeep, nextIndex; + bool newTransaction, reentrancyCheck; + + // disallow copy constructor + UndoManager (const UndoManager&); + const UndoManager& operator= (const UndoManager&); +}; + +#endif // __JUCE_UNDOMANAGER_JUCEHEADER__ +/********* End of inlined file: juce_UndoManager.h *********/ + +class TextEditor; +class TextHolderComponent; + +/** + Receives callbacks from a TextEditor component when it changes. + + @see TextEditor::addListener +*/ +class JUCE_API TextEditorListener +{ +public: + /** Destructor. */ + virtual ~TextEditorListener() {} + + /** Called when the user changes the text in some way. */ + virtual void textEditorTextChanged (TextEditor& editor) = 0; + + /** Called when the user presses the return key. */ + virtual void textEditorReturnKeyPressed (TextEditor& editor) = 0; + + /** Called when the user presses the escape key. */ + virtual void textEditorEscapeKeyPressed (TextEditor& editor) = 0; + + /** Called when the text editor loses focus. */ + virtual void textEditorFocusLost (TextEditor& editor) = 0; +}; + +/** + A component containing text that can be edited. + + A TextEditor can either be in single- or multi-line mode, and supports mixed + fonts and colours. + + @see TextEditorListener, Label +*/ +class JUCE_API TextEditor : public Component, + public SettableTooltipClient +{ +public: + + /** Creates a new, empty text editor. + + @param componentName the name to pass to the component for it to use as its name + @param passwordCharacter if this is not zero, this character will be used as a replacement + for all characters that are drawn on screen - e.g. to create + a password-style textbox containing circular blobs instead of text, + you could set this value to 0x25cf, which is the unicode character + for a black splodge (not all fonts include this, though), or 0x2022, + which is a bullet (probably the best choice for linux). + */ + TextEditor (const String& componentName = String::empty, + const tchar passwordCharacter = 0); + + /** Destructor. */ + virtual ~TextEditor(); + + /** Puts the editor into either multi- or single-line mode. + + By default, the editor will be in single-line mode, so use this if you need a multi-line + editor. + + See also the setReturnKeyStartsNewLine() method, which will also need to be turned + on if you want a multi-line editor with line-breaks. + + @see isMultiLine, setReturnKeyStartsNewLine + */ + void setMultiLine (const bool shouldBeMultiLine, + const bool shouldWordWrap = true); + + /** Returns true if the editor is in multi-line mode. + */ + bool isMultiLine() const throw(); + + /** Changes the behaviour of the return key. + + If set to true, the return key will insert a new-line into the text; if false + it will trigger a call to the TextEditorListener::textEditorReturnKeyPressed() + method. By default this is set to false, and when true it will only insert + new-lines when in multi-line mode (see setMultiLine()). + */ + void setReturnKeyStartsNewLine (const bool shouldStartNewLine); + + /** Returns the value set by setReturnKeyStartsNewLine(). + + See setReturnKeyStartsNewLine() for more info. + */ + bool getReturnKeyStartsNewLine() const throw() { return returnKeyStartsNewLine; } + + /** Indicates whether the tab key should be accepted and used to input a tab character, + or whether it gets ignored. + + By default the tab key is ignored, so that it can be used to switch keyboard focus + between components. + */ + void setTabKeyUsedAsCharacter (const bool shouldTabKeyBeUsed) throw(); + + /** Returns true if the tab key is being used for input. + @see setTabKeyUsedAsCharacter + */ + bool isTabKeyUsedAsCharacter() const throw() { return tabKeyUsed; } + + /** Changes the editor to read-only mode. + + By default, the text editor is not read-only. If you're making it read-only, you + might also want to call setCaretVisible (false) to get rid of the caret. + + The text can still be highlighted and copied when in read-only mode. + + @see isReadOnly, setCaretVisible + */ + void setReadOnly (const bool shouldBeReadOnly); + + /** Returns true if the editor is in read-only mode. + */ + bool isReadOnly() const throw(); + + /** Makes the caret visible or invisible. + + By default the caret is visible. + + @see setCaretColour, setCaretPosition + */ + void setCaretVisible (const bool shouldBeVisible) throw(); + + /** Returns true if the caret is enabled. + @see setCaretVisible + */ + bool isCaretVisible() const throw() { return caretVisible; } + + /** Enables/disables a vertical scrollbar. + + (This only applies when in multi-line mode). When the text gets too long to fit + in the component, a scrollbar can appear to allow it to be scrolled. Even when + this is enabled, the scrollbar will be hidden unless it's needed. + + By default the scrollbar is enabled. + */ + void setScrollbarsShown (bool shouldBeEnabled) throw(); + + /** Returns true if scrollbars are enabled. + @see setScrollbarsShown + */ + bool areScrollbarsShown() const throw() { return scrollbarVisible; } + + /** Changes the password character used to disguise the text. + + @param passwordCharacter if this is not zero, this character will be used as a replacement + for all characters that are drawn on screen - e.g. to create + a password-style textbox containing circular blobs instead of text, + you could set this value to 0x25cf, which is the unicode character + for a black splodge (not all fonts include this, though), or 0x2022, + which is a bullet (probably the best choice for linux). + */ + void setPasswordCharacter (const tchar passwordCharacter) throw(); + + /** Returns the current password character. + @see setPasswordCharacter +l */ + tchar getPasswordCharacter() const throw() { return passwordCharacter; } + + /** Allows a right-click menu to appear for the editor. + + (This defaults to being enabled). + + If enabled, right-clicking (or command-clicking on the Mac) will pop up a menu + of options such as cut/copy/paste, undo/redo, etc. + */ + void setPopupMenuEnabled (const bool menuEnabled) throw(); + + /** Returns true if the right-click menu is enabled. + @see setPopupMenuEnabled + */ + bool isPopupMenuEnabled() const throw() { return popupMenuEnabled; } + + /** Returns true if a popup-menu is currently being displayed. + */ + bool isPopupMenuCurrentlyActive() const throw() { return menuActive; } + + /** A set of colour IDs to use to change the colour of various aspects of the editor. + + These constants can be used either via the Component::setColour(), or LookAndFeel::setColour() + methods. + + @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour + */ + enum ColourIds + { + backgroundColourId = 0x1000200, /**< The colour to use for the text component's background - this can be + transparent if necessary. */ + + textColourId = 0x1000201, /**< The colour that will be used when text is added to the editor. Note + that because the editor can contain multiple colours, calling this + method won't change the colour of existing text - to do that, call + applyFontToAllText() after calling this method.*/ + + highlightColourId = 0x1000202, /**< The colour with which to fill the background of highlighted sections of + the text - this can be transparent if you don't want to show any + highlighting.*/ + + highlightedTextColourId = 0x1000203, /**< The colour with which to draw the text in highlighted sections. */ + + caretColourId = 0x1000204, /**< The colour with which to draw the caret. */ + + outlineColourId = 0x1000205, /**< If this is non-transparent, it will be used to draw a box around + the edge of the component. */ + + focusedOutlineColourId = 0x1000206, /**< If this is non-transparent, it will be used to draw a box around + the edge of the component when it has focus. */ + + shadowColourId = 0x1000207, /**< If this is non-transparent, it'll be used to draw an inner shadow + around the edge of the editor. */ + }; + + /** Sets the font to use for newly added text. + + This will change the font that will be used next time any text is added or entered + into the editor. It won't change the font of any existing text - to do that, use + applyFontToAllText() instead. + + @see applyFontToAllText + */ + void setFont (const Font& newFont) throw(); + + /** Applies a font to all the text in the editor. + + This will also set the current font to use for any new text that's added. + + @see setFont + */ + void applyFontToAllText (const Font& newFont); + + /** Returns the font that's currently being used for new text. + + @see setFont + */ + const Font getFont() const throw(); + + /** If set to true, focusing on the editor will highlight all its text. + + (Set to false by default). + + This is useful for boxes where you expect the user to re-enter all the + text when they focus on the component, rather than editing what's already there. + */ + void setSelectAllWhenFocused (const bool b) throw(); + + /** Sets limits on the characters that can be entered. + + @param maxTextLength if this is > 0, it sets a maximum length limit; if 0, no + limit is set + @param allowedCharacters if this is non-empty, then only characters that occur in + this string are allowed to be entered into the editor. + */ + void setInputRestrictions (const int maxTextLength, + const String& allowedCharacters = String::empty) throw(); + + /** When the text editor is empty, it can be set to display a message. + + This is handy for things like telling the user what to type in the box - the + string is only displayed, it's not taken to actually be the contents of + the editor. + */ + void setTextToShowWhenEmpty (const String& text, const Colour& colourToUse) throw(); + + /** Changes the size of the scrollbars that are used. + + Handy if you need smaller scrollbars for a small text box. + */ + void setScrollBarThickness (const int newThicknessPixels); + + /** Shows or hides the buttons on any scrollbars that are used. + + @see ScrollBar::setButtonVisibility + */ + void setScrollBarButtonVisibility (const bool buttonsVisible); + + /** Registers a listener to be told when things happen to the text. + + @see removeListener + */ + void addListener (TextEditorListener* const newListener) throw(); + + /** Deregisters a listener. + + @see addListener + */ + void removeListener (TextEditorListener* const listenerToRemove) throw(); + + /** Returns the entire contents of the editor. */ + const String getText() const throw(); + + /** Returns a section of the contents of the editor. */ + const String getTextSubstring (const int startCharacter, const int endCharacter) const throw(); + + /** Returns true if there are no characters in the editor. + + This is more efficient than calling getText().isEmpty(). + */ + bool isEmpty() const throw(); + + /** Sets the entire content of the editor. + + This will clear the editor and insert the given text (using the current text colour + and font). You can set the current text colour using + @code setColour (TextEditor::textColourId, ...); + @endcode + + @param newText the text to add + @param sendTextChangeMessage if true, this will cause a change message to + be sent to all the listeners. + @see insertText + */ + void setText (const String& newText, + const bool sendTextChangeMessage = true); + + /** Inserts some text at the current cursor position. + + If a section of the text is highlighted, it will be replaced by + this string, otherwise it will be inserted. + + To delete a section of text, you can use setHighlightedRegion() to + highlight it, and call insertTextAtCursor (String::empty). + + @see setCaretPosition, getCaretPosition, setHighlightedRegion + */ + void insertTextAtCursor (String textToInsert); + + /** Deletes all the text from the editor. */ + void clear(); + + /** Deletes the currently selected region, and puts it on the clipboard. + + @see copy, paste, SystemClipboard + */ + void cut(); + + /** Copies any currently selected region to the clipboard. + + @see cut, paste, SystemClipboard + */ + void copy(); + + /** Pastes the contents of the clipboard into the editor at the cursor position. + + @see cut, copy, SystemClipboard + */ + void paste(); + + /** Moves the caret to be in front of a given character. + + @see getCaretPosition + */ + void setCaretPosition (const int newIndex) throw(); + + /** Returns the current index of the caret. + + @see setCaretPosition + */ + int getCaretPosition() const throw(); + + /** Selects a section of the text. + */ + void setHighlightedRegion (int startIndex, + int numberOfCharactersToHighlight) throw(); + + /** Returns the first character that is selected. + + If nothing is selected, this will still return a character index, but getHighlightedRegionLength() + will return 0. + + @see setHighlightedRegion, getHighlightedRegionLength + */ + int getHighlightedRegionStart() const throw() { return selectionStart; } + + /** Returns the number of characters that are selected. + + @see setHighlightedRegion, getHighlightedRegionStart + */ + int getHighlightedRegionLength() const throw() { return jmax (0, selectionEnd - selectionStart); } + + /** Returns the section of text that is currently selected. */ + const String getHighlightedText() const throw(); + + /** Finds the index of the character at a given position. + + The co-ordinates are relative to the component's top-left. + */ + int getTextIndexAt (const int x, const int y) throw(); + + /** Returns the total width of the text, as it is currently laid-out. + + This may be larger than the size of the TextEditor, and can change when + the TextEditor is resized or the text changes. + */ + int getTextWidth() const throw(); + + /** Returns the maximum height of the text, as it is currently laid-out. + + This may be larger than the size of the TextEditor, and can change when + the TextEditor is resized or the text changes. + */ + int getTextHeight() const throw(); + + /** Changes the size of the gap at the top and left-edge of the editor. + + By default there's a gap of 4 pixels. + */ + void setIndents (const int newLeftIndent, const int newTopIndent) throw(); + + /** Changes the size of border left around the edge of the component. + + @see getBorder + */ + void setBorder (const BorderSize& border) throw(); + + /** Returns the size of border around the edge of the component. + + @see setBorder + */ + const BorderSize getBorder() const throw(); + + /** Used to disable the auto-scrolling which keeps the cursor visible. + + If true (the default), the editor will scroll when the cursor moves offscreen. If + set to false, it won't. + */ + void setScrollToShowCursor (const bool shouldScrollToShowCursor) throw(); + + /** @internal */ + void paint (Graphics& g); + /** @internal */ + void paintOverChildren (Graphics& g); + /** @internal */ + void mouseDown (const MouseEvent& e); + /** @internal */ + void mouseUp (const MouseEvent& e); + /** @internal */ + void mouseDrag (const MouseEvent& e); + /** @internal */ + void mouseDoubleClick (const MouseEvent& e); + /** @internal */ + void mouseWheelMove (const MouseEvent& e, float wheelIncrementX, float wheelIncrementY); + /** @internal */ + bool keyPressed (const KeyPress& key); + /** @internal */ + bool keyStateChanged(); + /** @internal */ + void focusGained (FocusChangeType cause); + /** @internal */ + void focusLost (FocusChangeType cause); + /** @internal */ + void resized(); + /** @internal */ + void enablementChanged(); + /** @internal */ + void colourChanged(); + + juce_UseDebuggingNewOperator + +protected: + + /** This adds the items to the popup menu. + + By default it adds the cut/copy/paste items, but you can override this if + you need to replace these with your own items. + + If you want to add your own items to the existing ones, you can override this, + call the base class's addPopupMenuItems() method, then append your own items. + + When the menu has been shown, performPopupMenuAction() will be called to + perform the item that the user has chosen. + + The default menu items will be added using item IDs in the range + 0x7fff0000 - 0x7fff1000, so you should avoid those values for your own + menu IDs. + + If this was triggered by a mouse-click, the mouseClickEvent parameter will be + a pointer to the info about it, or may be null if the menu is being triggered + by some other means. + + @see performPopupMenuAction, setPopupMenuEnabled, isPopupMenuEnabled + */ + virtual void addPopupMenuItems (PopupMenu& menuToAddTo, + const MouseEvent* mouseClickEvent); + + /** This is called to perform one of the items that was shown on the popup menu. + + If you've overridden addPopupMenuItems(), you should also override this + to perform the actions that you've added. + + If you've overridden addPopupMenuItems() but have still left the default items + on the menu, remember to call the superclass's performPopupMenuAction() + so that it can perform the default actions if that's what the user clicked on. + + @see addPopupMenuItems, setPopupMenuEnabled, isPopupMenuEnabled + */ + virtual void performPopupMenuAction (const int menuItemID); + + /** Scrolls the minimum distance needed to get the caret into view. */ + void scrollToMakeSureCursorIsVisible() throw(); + + /** @internal */ + void moveCaret (int newCaretPos) throw(); + + /** @internal */ + void moveCursorTo (const int newPosition, const bool isSelecting) throw(); + + /** Used internally to dispatch a text-change message. */ + void textChanged() throw(); + + /** Counts the number of characters in the text. + + This is quicker than getting the text as a string if you just need to know + the length. + */ + int getTotalNumChars() throw(); + + /** Begins a new transaction in the UndoManager. + */ + void newTransaction() throw(); + + /** Used internally to trigger an undo or redo. */ + void doUndoRedo (const bool isRedo); + + /** Can be overridden to intercept return key presses directly */ + virtual void returnPressed(); + + /** Can be overridden to intercept escape key presses directly */ + virtual void escapePressed(); + + /** @internal */ + void handleCommandMessage (int commandId); + +private: + + Viewport* viewport; + TextHolderComponent* textHolder; + BorderSize borderSize; + + bool readOnly : 1; + bool multiline : 1; + bool wordWrap : 1; + bool returnKeyStartsNewLine : 1; + bool caretVisible : 1; + bool popupMenuEnabled : 1; + bool selectAllTextWhenFocused : 1; + bool scrollbarVisible : 1; + bool wasFocused : 1; + bool caretFlashState : 1; + bool keepCursorOnScreen : 1; + bool tabKeyUsed : 1; + bool menuActive : 1; + + UndoManager undoManager; + float cursorX, cursorY, cursorHeight; + int maxTextLength; + int selectionStart, selectionEnd; + int leftIndent, topIndent; + unsigned int lastTransactionTime; + Font currentFont; + int totalNumChars, caretPosition; + VoidArray sections; + String textToShowWhenEmpty; + Colour colourForTextWhenEmpty; + tchar passwordCharacter; + + enum + { + notDragging, + draggingSelectionStart, + draggingSelectionEnd + } dragType; + + String allowedCharacters; + SortedSet listeners; + + friend class TextEditorInsertAction; + friend class TextEditorRemoveAction; + + void coalesceSimilarSections() throw(); + void splitSection (const int sectionIndex, const int charToSplitAt) throw(); + + void clearInternal (UndoManager* const um) throw(); + + void insert (const String& text, + const int insertIndex, + const Font& font, + const Colour& colour, + UndoManager* const um, + const int caretPositionToMoveTo) throw(); + + void reinsert (const int insertIndex, + const VoidArray& sections) throw(); + + void remove (const int startIndex, + int endIndex, + UndoManager* const um, + const int caretPositionToMoveTo) throw(); + + void getCharPosition (const int index, + float& x, float& y, + float& lineHeight) const throw(); + + int indexAtPosition (const float x, + const float y) throw(); + + int findWordBreakAfter (const int position) const throw(); + int findWordBreakBefore (const int position) const throw(); + + friend class TextHolderComponent; + friend class TextEditorViewport; + void drawContent (Graphics& g); + void updateTextHolderSize() throw(); + float getWordWrapWidth() const throw(); + void timerCallbackInt(); + void repaintCaret(); + void repaintText (int textStartIndex, int textEndIndex); + + TextEditor (const TextEditor&); + const TextEditor& operator= (const TextEditor&); +}; + +#endif // __JUCE_TEXTEDITOR_JUCEHEADER__ +/********* End of inlined file: juce_TextEditor.h *********/ + +class Label; + +/** + A class for receiving events from a Label. + + You can register a LabelListener with a Label using the Label::addListener() + method, and it will be called when the text of the label changes, either because + of a call to Label::setText() or by the user editing the text (if the label is + editable). + + @see Label::addListener, Label::removeListener +*/ +class JUCE_API LabelListener +{ +public: + /** Destructor. */ + virtual ~LabelListener() {} + + /** Called when a Label's text has changed. + */ + virtual void labelTextChanged (Label* labelThatHasChanged) = 0; +}; + +/** + A component that displays a text string, and can optionally become a text + editor when clicked. +*/ +class JUCE_API Label : public Component, + public SettableTooltipClient, + protected TextEditorListener, + private ComponentListener, + private AsyncUpdater +{ +public: + + /** Creates a Label. + + @param componentName the name to give the component + @param labelText the text to show in the label + */ + Label (const String& componentName, + const String& labelText); + + /** Destructor. */ + ~Label(); + + /** Changes the label text. + + If broadcastChangeMessage is true and the new text is different to the current + text, then the class will broadcast a change message to any LabelListeners that + are registered. + */ + void setText (const String& newText, + const bool broadcastChangeMessage); + + /** Returns the label's current text. + + @param returnActiveEditorContents if this is true and the label is currently + being edited, then this method will return the + text as it's being shown in the editor. If false, + then the value returned here won't be updated until + the user has finished typing and pressed the return + key. + */ + const String getText (const bool returnActiveEditorContents = false) const throw(); + + /** Changes the font to use to draw the text. + + @see getFont + */ + void setFont (const Font& newFont) throw(); + + /** Returns the font currently being used. + + @see setFont + */ + const Font& getFont() const throw(); + + /** A set of colour IDs to use to change the colour of various aspects of the label. + + These constants can be used either via the Component::setColour(), or LookAndFeel::setColour() + methods. + + Note that you can also use the constants from TextEditor::ColourIds to change the + colour of the text editor that is opened when a label is editable. + + @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour + */ + enum ColourIds + { + backgroundColourId = 0x1000280, /**< The background colour to fill the label with. */ + textColourId = 0x1000281, /**< The colour for the text. */ + outlineColourId = 0x1000282 /**< An optional colour to use to draw a border around the label. + Leave this transparent to not have an outline. */ + }; + + /** Sets the style of justification to be used for positioning the text. + + (The default is Justification::centredLeft) + */ + void setJustificationType (const Justification& justification) throw(); + + /** Returns the type of justification, as set in setJustificationType(). */ + const Justification getJustificationType() const throw() { return justification; } + + /** Makes this label "stick to" another component. + + This will cause the label to follow another component around, staying + either to its left or above it. + + @param owner the component to follow + @param onLeft if true, the label will stay on the left of its component; if + false, it will stay above it. + */ + void attachToComponent (Component* owner, + const bool onLeft); + + /** If this label has been attached to another component using attachToComponent, this + returns the other component. + + Returns 0 if the label is not attached. + */ + Component* getAttachedComponent() const throw() { return ownerComponent; } + + /** If the label is attached to the left of another component, this returns true. + + Returns false if the label is above the other component. This is only relevent if + attachToComponent() has been called. + */ + bool isAttachedOnLeft() const throw() { return leftOfOwnerComp; } + + /** Registers a listener that will be called when the label's text changes. */ + void addListener (LabelListener* const listener) throw(); + + /** Deregisters a previously-registered listener. */ + void removeListener (LabelListener* const listener) throw(); + + /** Makes the label turn into a TextEditor when clicked. + + By default this is turned off. + + If turned on, then single- or double-clicking will turn the label into + an editor. If the user then changes the text, then the ChangeBroadcaster + base class will be used to send change messages to any listeners that + have registered. + + If the user changes the text, the textWasEdited() method will be called + afterwards, and subclasses can override this if they need to do anything + special. + + @param editOnSingleClick if true, just clicking once on the label will start editing the text + @param editOnDoubleClick if true, a double-click is needed to start editing + @param lossOfFocusDiscardsChanges if true, clicking somewhere else while the text is being + edited will discard any changes; if false, then this will + commit the changes. + @see showEditor, setEditorColours, TextEditor + */ + void setEditable (const bool editOnSingleClick, + const bool editOnDoubleClick = false, + const bool lossOfFocusDiscardsChanges = false) throw(); + + /** Returns true if this option was set using setEditable(). */ + bool isEditableOnSingleClick() const throw() { return editSingleClick; } + + /** Returns true if this option was set using setEditable(). */ + bool isEditableOnDoubleClick() const throw() { return editDoubleClick; } + + /** Returns true if this option has been set in a call to setEditable(). */ + bool doesLossOfFocusDiscardChanges() const throw() { return lossOfFocusDiscardsChanges; } + + /** Returns true if the user can edit this label's text. */ + bool isEditable() const throw() { return editSingleClick || editDoubleClick; } + + /** Makes the editor appear as if the label had been clicked by the user. + + @see textWasEdited, setEditable + */ + void showEditor(); + + /** Hides the editor if it was being shown. + + @param discardCurrentEditorContents if true, the label's text will be + reset to whatever it was before the editor + was shown; if false, the current contents of the + editor will be used to set the label's text + before it is hidden. + */ + void hideEditor (const bool discardCurrentEditorContents); + + /** Returns true if the editor is currently focused and active. */ + bool isBeingEdited() const throw(); + + juce_UseDebuggingNewOperator + +protected: + /** @internal */ + void paint (Graphics& g); + /** @internal */ + void resized(); + /** @internal */ + void mouseUp (const MouseEvent& e); + /** @internal */ + void mouseDoubleClick (const MouseEvent& e); + /** @internal */ + void componentMovedOrResized (Component& component, bool wasMoved, bool wasResized); + /** @internal */ + void componentParentHierarchyChanged (Component& component); + /** @internal */ + void componentVisibilityChanged (Component& component); + /** @internal */ + void inputAttemptWhenModal(); + /** @internal */ + void focusGained (FocusChangeType); + /** @internal */ + void enablementChanged(); + /** @internal */ + KeyboardFocusTraverser* createFocusTraverser(); + /** @internal */ + void textEditorTextChanged (TextEditor& editor); + /** @internal */ + void textEditorReturnKeyPressed (TextEditor& editor); + /** @internal */ + void textEditorEscapeKeyPressed (TextEditor& editor); + /** @internal */ + void textEditorFocusLost (TextEditor& editor); + /** @internal */ + void handleAsyncUpdate(); + /** @internal */ + void colourChanged(); + + /** Creates the TextEditor component that will be used when the user has clicked on the label. + + Subclasses can override this if they need to customise this component in some way. + */ + virtual TextEditor* createEditorComponent(); + + /** Called after the user changes the text. + */ + virtual void textWasEdited(); + +private: + String text; + Font font; + Justification justification; + TextEditor* editor; + SortedSet listeners; + Component* ownerComponent; + ComponentDeletionWatcher* deletionWatcher; + + bool editSingleClick : 1; + bool editDoubleClick : 1; + bool lossOfFocusDiscardsChanges : 1; + bool leftOfOwnerComp : 1; + + bool updateFromTextEditorContents(); + + Label (const Label&); + const Label& operator= (const Label&); +}; + +#endif // __JUCE_LABEL_JUCEHEADER__ +/********* End of inlined file: juce_Label.h *********/ + +class ComboBox; + +/** + A class for receiving events from a ComboBox. + + You can register a ComboBoxListener with a ComboBox using the ComboBox::addListener() + method, and it will be called when the selected item in the box changes. + + @see ComboBox::addListener, ComboBox::removeListener +*/ +class JUCE_API ComboBoxListener +{ +public: + /** Destructor. */ + virtual ~ComboBoxListener() {} + + /** Called when a ComboBox has its selected item changed. + */ + virtual void comboBoxChanged (ComboBox* comboBoxThatHasChanged) = 0; +}; + +/** + A component that lets the user choose from a drop-down list of choices. + + The combo-box has a list of text strings, each with an associated id number, + that will be shown in the drop-down list when the user clicks on the component. + + The currently selected choice is displayed in the combo-box, and this can + either be read-only text, or editable. + + To find out when the user selects a different item or edits the text, you + can register a ComboBoxListener to receive callbacks. + + @see ComboBoxListener +*/ +class JUCE_API ComboBox : public Component, + public SettableTooltipClient, + private LabelListener, + private AsyncUpdater +{ +public: + + /** Creates a combo-box. + + On construction, the text field will be empty, so you should call the + setSelectedId() or setText() method to choose the initial value before + displaying it. + + @param componentName the name to set for the component (see Component::setName()) + */ + ComboBox (const String& componentName); + + /** Destructor. */ + ~ComboBox(); + + /** Sets whether the test in the combo-box is editable. + + The default state for a new ComboBox is non-editable, and can only be changed + by choosing from the drop-down list. + */ + void setEditableText (const bool isEditable); + + /** Returns true if the text is directly editable. + @see setEditableText + */ + bool isTextEditable() const throw(); + + /** Sets the style of justification to be used for positioning the text. + + The default is Justification::centredLeft. The text is displayed using a + Label component inside the ComboBox. + */ + void setJustificationType (const Justification& justification) throw(); + + /** Returns the current justification for the text box. + @see setJustificationType + */ + const Justification getJustificationType() const throw(); + + /** Adds an item to be shown in the drop-down list. + + @param newItemText the text of the item to show in the list + @param newItemId an associated ID number that can be set or retrieved - see + getSelectedId() and setSelectedId() + @see setItemEnabled, addSeparator, addSectionHeading, removeItem, getNumItems, getItemText, getItemId + */ + void addItem (const String& newItemText, + const int newItemId) throw(); + + /** Adds a separator line to the drop-down list. + + This is like adding a separator to a popup menu. See PopupMenu::addSeparator(). + */ + void addSeparator() throw(); + + /** Adds a heading to the drop-down list, so that you can group the items into + different sections. + + The headings are indented slightly differently to set them apart from the + items on the list, and obviously can't be selected. You might want to add + separators between your sections too. + + @see addItem, addSeparator + */ + void addSectionHeading (const String& headingName) throw(); + + /** This allows items in the drop-down list to be selectively disabled. + + When you add an item, it's enabled by default, but you can call this + method to change its status. + + If you disable an item which is already selected, this won't change the + current selection - it just stops the user choosing that item from the list. + */ + void setItemEnabled (const int itemId, + const bool isEnabled) throw(); + + /** Changes the text for an existing item. + */ + void changeItemText (const int itemId, + const String& newText) throw(); + + /** Removes all the items from the drop-down list. + + If this call causes the content to be cleared, then a change-message + will be broadcast unless dontSendChangeMessage is true. + + @see addItem, removeItem, getNumItems + */ + void clear (const bool dontSendChangeMessage = false); + + /** Returns the number of items that have been added to the list. + + Note that this doesn't include headers or separators. + */ + int getNumItems() const throw(); + + /** Returns the text for one of the items in the list. + + Note that this doesn't include headers or separators. + + @param index the item's index from 0 to (getNumItems() - 1) + */ + const String getItemText (const int index) const throw(); + + /** Returns the ID for one of the items in the list. + + Note that this doesn't include headers or separators. + + @param index the item's index from 0 to (getNumItems() - 1) + */ + int getItemId (const int index) const throw(); + + /** Returns the ID of the item that's currently shown in the box. + + If no item is selected, or if the text is editable and the user + has entered something which isn't one of the items in the list, then + this will return 0. + + @see setSelectedId, getSelectedItemIndex, getText + */ + int getSelectedId() const throw(); + + /** Sets one of the items to be the current selection. + + This will set the ComboBox's text to that of the item that matches + this ID. + + @param newItemId the new item to select + @param dontSendChangeMessage if set to true, this method won't trigger a + change notification + @see getSelectedId, setSelectedItemIndex, setText + */ + void setSelectedId (const int newItemId, + const bool dontSendChangeMessage = false) throw(); + + /** Returns the index of the item that's currently shown in the box. + + If no item is selected, or if the text is editable and the user + has entered something which isn't one of the items in the list, then + this will return -1. + + @see setSelectedItemIndex, getSelectedId, getText + */ + int getSelectedItemIndex() const throw(); + + /** Sets one of the items to be the current selection. + + This will set the ComboBox's text to that of the item at the given + index in the list. + + @param newItemIndex the new item to select + @param dontSendChangeMessage if set to true, this method won't trigger a + change notification + @see getSelectedItemIndex, setSelectedId, setText + */ + void setSelectedItemIndex (const int newItemIndex, + const bool dontSendChangeMessage = false) throw(); + + /** Returns the text that is currently shown in the combo-box's text field. + + If the ComboBox has editable text, then this text may have been edited + by the user; otherwise it will be one of the items from the list, or + possibly an empty string if nothing was selected. + + @see setText, getSelectedId, getSelectedItemIndex + */ + const String getText() const throw(); + + /** Sets the contents of the combo-box's text field. + + The text passed-in will be set as the current text regardless of whether + it is one of the items in the list. If the current text isn't one of the + items, then getSelectedId() will return -1, otherwise it wil return + the approriate ID. + + @param newText the text to select + @param dontSendChangeMessage if set to true, this method won't trigger a + change notification + @see getText + */ + void setText (const String& newText, + const bool dontSendChangeMessage = false) throw(); + + /** Programmatically opens the text editor to allow the user to edit the current item. + + This is the same effect as when the box is clicked-on. + @see Label::showEditor(); + */ + void showEditor(); + + /** Registers a listener that will be called when the box's content changes. */ + void addListener (ComboBoxListener* const listener) throw(); + + /** Deregisters a previously-registered listener. */ + void removeListener (ComboBoxListener* const listener) throw(); + + /** Sets a message to display when there is no item currently selected. + + @see getTextWhenNothingSelected + */ + void setTextWhenNothingSelected (const String& newMessage) throw(); + + /** Returns the text that is shown when no item is selected. + + @see setTextWhenNothingSelected + */ + const String getTextWhenNothingSelected() const throw(); + + /** Sets the message to show when there are no items in the list, and the user clicks + on the drop-down box. + + By default it just says "no choices", but this lets you change it to something more + meaningful. + */ + void setTextWhenNoChoicesAvailable (const String& newMessage) throw(); + + /** Returns the text shown when no items have been added to the list. + @see setTextWhenNoChoicesAvailable + */ + const String getTextWhenNoChoicesAvailable() const throw(); + + /** Gives the ComboBox a tooltip. */ + void setTooltip (const String& newTooltip); + + /** A set of colour IDs to use to change the colour of various aspects of the combo box. + + These constants can be used either via the Component::setColour(), or LookAndFeel::setColour() + methods. + + To change the colours of the menu that pops up + + @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour + */ + enum ColourIds + { + backgroundColourId = 0x1000b00, /**< The background colour to fill the box with. */ + textColourId = 0x1000a00, /**< The colour for the text in the box. */ + outlineColourId = 0x1000c00, /**< The colour for an outline around the box. */ + buttonColourId = 0x1000d00, /**< The base colour for the button (a LookAndFeel class will probably use variations on this). */ + arrowColourId = 0x1000e00, /**< The colour for the arrow shape that pops up the menu */ + }; + + /** @internal */ + void labelTextChanged (Label*); + /** @internal */ + void enablementChanged(); + /** @internal */ + void colourChanged(); + /** @internal */ + void focusGained (Component::FocusChangeType cause); + /** @internal */ + void focusLost (Component::FocusChangeType cause); + /** @internal */ + void handleAsyncUpdate(); + /** @internal */ + const String getTooltip() { return label->getTooltip(); } + /** @internal */ + void mouseDown (const MouseEvent&); + /** @internal */ + void mouseDrag (const MouseEvent&); + /** @internal */ + void mouseUp (const MouseEvent&); + /** @internal */ + void lookAndFeelChanged(); + /** @internal */ + void paint (Graphics&); + /** @internal */ + void resized(); + /** @internal */ + bool keyStateChanged(); + /** @internal */ + bool keyPressed (const KeyPress&); + + juce_UseDebuggingNewOperator + +private: + struct ItemInfo + { + String name; + int itemId; + bool isEnabled : 1, isHeading : 1; + + bool isSeparator() const throw(); + bool isRealItem() const throw(); + }; + + OwnedArray items; + int currentIndex; + bool isButtonDown; + bool separatorPending; + bool menuActive; + SortedSet listeners; + Label* label; + String textWhenNothingSelected, noChoicesMessage; + + void showPopup(); + + ItemInfo* getItemForId (const int itemId) const throw(); + ItemInfo* getItemForIndex (const int index) const throw(); + + ComboBox (const ComboBox&); + const ComboBox& operator= (const ComboBox&); +}; + +#endif // __JUCE_COMBOBOX_JUCEHEADER__ +/********* End of inlined file: juce_ComboBox.h *********/ + +/** + Manages the state of some audio and midi i/o devices. + + This class keeps tracks of a currently-selected audio device, through + with which it continuously streams data from an audio callback, as well as + one or more midi inputs. + + The idea is that your application will create one global instance of this object, + and let it take care of creating and deleting specific types of audio devices + internally. So when the device is changed, your callbacks will just keep running + without having to worry about this. + + The manager can save and reload all of its device settings as XML, which + makes it very easy for you to save and reload the audio setup of your + application. + + And to make it easy to let the user change its settings, there's a component + to do just that - the AudioDeviceSelectorComponent class, which contains a set of + device selection/sample-rate/latency controls. + + To use an AudioDeviceManager, create one, and use initialise() to set it up. Then + call setAudioCallback() to register your audio callback with it, and use that to process + your audio data. + + The manager also acts as a handy hub for incoming midi messages, allowing a + listener to register for messages from either a specific midi device, or from whatever + the current default midi input device is. The listener then doesn't have to worry about + re-registering with different midi devices if they are changed or deleted. + + And yet another neat trick is that amount of CPU time being used is measured and + available with the getCpuUsage() method. + + The AudioDeviceManager is a ChangeBroadcaster, and will send a change message to + listeners whenever one of its settings is changed. + + @see AudioDeviceSelectorComponent, AudioIODevice, AudioIODeviceType +*/ +class JUCE_API AudioDeviceManager : public ChangeBroadcaster +{ +public: + + /** Creates a default AudioDeviceManager. + + Initially no audio device will be selected. You should call the initialise() method + and register an audio callback with setAudioCallback() before it'll be able to + actually make any noise. + */ + AudioDeviceManager(); + + /** Destructor. */ + ~AudioDeviceManager(); + + /** Opens a set of audio devices ready for use. + + This will attempt to open either a default audio device, or one that was + previously saved as XML. + + @param numInputChannelsNeeded a minimum number of input channels needed + by your app. + @param numOutputChannelsNeeded a minimum number of output channels to open + @param savedState either a previously-saved state that was produced + by createStateXml(), or 0 if you want the manager + to choose the best device to open. + @param selectDefaultDeviceOnFailure if true, then if the device specified in the XML + fails to open, then a default device will be used + instead. If false, then on failure, no device is + opened. + @param preferredDefaultDeviceName if this is not empty, and there's a device with this + name, then that will be used as the default device + (assuming that there wasn't one specified in the XML). + The string can actually be a simple wildcard, containing "*" + and "?" characters + + @returns an error message if anything went wrong, or an empty string if it worked ok. + */ + const String initialise (const int numInputChannelsNeeded, + const int numOutputChannelsNeeded, + const XmlElement* const savedState, + const bool selectDefaultDeviceOnFailure, + const String& preferredDefaultDeviceName = String::empty); + + /** Returns some XML representing the current state of the manager. + + This stores the current device, its samplerate, block size, etc, and + can be restored later with initialise(). + */ + XmlElement* createStateXml() const; + + /** Returns a list of the audio devices that can be used. + + On Windows, this will include both DSound and ASIO devices if they are available. On + the Mac, it'll be a list of the CoreAudio devices. + + These names are used by setAudioDevice() when changing devices. + */ + const StringArray getAvailableAudioDeviceNames() const; + + /** Rescans the list of known audio devices, in case it's changed. */ + void refreshDeviceList() const; + + /** Sets a flag to indicate that when listing audio device names, it should treat them + as inputs rather than outputs. + + This only really applies to DirectSound, where input and output devices are + separate. On ASIO and CoreAudio this makes no difference. + + @see getAvailableAudioDeviceNames + */ + void setInputDeviceNamesUsed (const bool useInputNames); + + /** Just adds the list of device names to a combo box. + + The only reason this is in this class is so that it can divide DSound + and ASIO devices into labelled sections, which makes it look much neater. + */ + void addDeviceNamesToComboBox (ComboBox& combo) const; + + /** Changes the audio device that should be used. + + If deviceName is empty or not a valid name returned by getAvailableAudioDeviceNames(), + it will disable the current device. + + @param deviceName the name of the device you want to use (or an empty string to + deselect the current device) + @param blockSizeToUse the samples-per-block you want to use, or 0 to use a default + value + @param sampleRateToUse the target sample-rate, or 0 to use a default that the device + is capable of + @param inputChans which of the audio device's input channels to open - pass 0 to + open as many of the the first ones as are needed for the number + of input channels that the app has requested + @param outputChans which of the audio device's input channels to open - pass 0 to + open as many of the the first ones as are needed for the number + of output channels that the app has requested + @param treatAsChosenDevice if this is true and if the device opens correctly, these new + settings will be taken as having been explicitly chosen by the + user, and the next time createStateXml() is called, these settings + will be returned. If it's false, then the device is treated as a + temporary or default device, and a call to createStateXml() will + return either the last settings that were made with treatAsChosenDevice + as true, or the last XML settings that were passed into initialise(). + + @returns an error message if anything went wrong, or an empty string if it worked ok. + */ + const String setAudioDevice (const String& deviceName, + int blockSizeToUse, + double sampleRateToUse, + const BitArray* inputChans, + const BitArray* outputChans, + const bool treatAsChosenDevice); + + /** Closes the currently-open device. + + You can call restartLastAudioDevice() later to reopen it in the same state + that it was just in. + */ + void closeAudioDevice(); + + /** Tries to reload the last audio device that was running. + + Note that this only reloads the last device that was running before + closeAudioDevice() was called - it doesn't reload any kind of saved-state, + and can only be called after a device has been opened with SetAudioDevice(). + + If a device is already open, this call will do nothing. + */ + void restartLastAudioDevice(); + + /** Returns the name of the currently selected audio device. + + This will be an empty string if none is active. + */ + const String getCurrentAudioDeviceName() const; + + /** Returns the currently-active audio device. */ + AudioIODevice* getCurrentAudioDevice() const throw() { return currentAudioDevice; } + + /** Returns the set of audio input channels currently being used. + + To select different channels, use setInputChannels(), or call setAudioDevice() to + reopen the device with a different set of channels. + */ + const BitArray getInputChannels() const throw() { return inputChannels; } + + /** Changes the active set of input channels. + + @param newEnabledChannels the set of channels to enable + @param treatAsChosenDevice if this is true and if the device opens correctly, these new + settings will be taken as having been explicitly chosen by the + user, and the next time createStateXml() is called, these settings + will be returned. If it's false, then the device is treated as a + temporary or default device, and a call to createStateXml() will + return either the last settings that were made with treatAsChosenDevice + as true, or the last XML settings that were passed into initialise(). + @see getInputChannels + */ + void setInputChannels (const BitArray& newEnabledChannels, + const bool treatAsChosenDevice); + + /** Returns the set of audio output channels currently being used. + + To select different channels, use setOutputChannels(), or call setAudioDevice() to + reopen the device with a different set of channels. + */ + const BitArray getOutputChannels() const throw() { return outputChannels; } + + /** Changes the active set of output channels. + + @param newEnabledChannels the set of channels to enable + @param treatAsChosenDevice if this is true and if the device opens correctly, these new + settings will be taken as having been explicitly chosen by the + user, and the next time createStateXml() is called, these settings + will be returned. If it's false, then the device is treated as a + temporary or default device, and a call to createStateXml() will + return either the last settings that were made with treatAsChosenDevice + as true, or the last XML settings that were passed into initialise(). + @see getOutputChannels + */ + void setOutputChannels (const BitArray& newEnabledChannels, + const bool treatAsChosenDevice); + + /** Gives the manager an audio callback to use. + + The manager will redirect callbacks from whatever audio device is currently + in use to this callback object. + + You can pass 0 in here to stop callbacks being made. + */ + void setAudioCallback (AudioIODeviceCallback* newCallback); + + /** Returns the average proportion of available CPU being spent inside the audio callbacks. + + Returns a value between 0 and 1.0 + */ + double getCpuUsage() const; + + /** Enables or disables a midi input device. + + The list of devices can be obtained with the MidiInput::getDevices() method. + + Any incoming messages from enabled input devices will be forwarded on to all the + listeners that have been registered with the addMidiInputCallback() method. They + can either register for messages from a particular device, or from just the + "default" midi input. + + Routing the midi input via an AudioDeviceManager means that when a listener + registers for the default midi input, this default device can be changed by the + manager without the listeners having to know about it or re-register. + + It also means that a listener can stay registered for a midi input that is disabled + or not present, so that when the input is re-enabled, the listener will start + receiving messages again. + + @see addMidiInputCallback, isMidiInputEnabled + */ + void setMidiInputEnabled (const String& midiInputDeviceName, + const bool enabled); + + /** Returns true if a given midi input device is being used. + + @see setMidiInputEnabled + */ + bool isMidiInputEnabled (const String& midiInputDeviceName) const; + + /** Registers a listener for callbacks when midi events arrive from a midi input. + + The device name can be empty to indicate that it wants events from whatever the + current "default" device is. Or it can be the name of one of the midi input devices + (see MidiInput::getDevices() for the names). + + Only devices which are enabled (see the setMidiInputEnabled() method) will have their + events forwarded on to listeners. + */ + void addMidiInputCallback (const String& midiInputDeviceName, + MidiInputCallback* callback); + + /** Removes a listener that was previously registered with addMidiInputCallback(). + */ + void removeMidiInputCallback (MidiInputCallback* callback); + + /** Sets a midi output device to use as the default. + + The list of devices can be obtained with the MidiOutput::getDevices() method. + + The specified device will be opened automatically and can be retrieved with the + getDefaultMidiOutput() method. + + Pass in an empty string to deselect all devices. For the default device, you + can use MidiOutput::getDevices() [MidiOutput::getDefaultDeviceIndex()]. + + @see getDefaultMidiOutput, getDefaultMidiOutputName + */ + void setDefaultMidiOutput (const String& deviceName); + + /** Returns the name of the default midi output. + + @see setDefaultMidiOutput, getDefaultMidiOutput + */ + const String getDefaultMidiOutputName() const throw() { return defaultMidiOutputName; } + + /** Returns the current default midi output device. + + If no device has been selected, or the device can't be opened, this will + return 0. + + @see getDefaultMidiOutputName + */ + MidiOutput* getDefaultMidiOutput() const throw() { return defaultMidiOutput; } + + juce_UseDebuggingNewOperator + +private: + + OwnedArray availableDeviceTypes; + + AudioIODevice* currentAudioDevice; + AudioIODeviceCallback* currentCallback; + int numInputChansNeeded, numOutputChansNeeded; + BitArray inputChannels, outputChannels; + XmlElement* lastExplicitSettings; + mutable bool listNeedsScanning; + bool useInputNames; + + OwnedArray enabledMidiInputs; + Array midiCallbacks; + Array midiCallbackDevices; + String defaultMidiOutputName; + MidiOutput* defaultMidiOutput; + CriticalSection audioCallbackLock, midiCallbackLock; + + double cpuUsageMs, timeToCpuScale; + + class CallbackHandler : public AudioIODeviceCallback, + public MidiInputCallback + { + public: + AudioDeviceManager* owner; + + void audioDeviceIOCallback (const float** inputChannelData, + int totalNumInputChannels, + float** outputChannelData, + int totalNumOutputChannels, + int numSamples); + + void audioDeviceAboutToStart (AudioIODevice*); + + void audioDeviceStopped(); + + void handleIncomingMidiMessage (MidiInput* source, const MidiMessage& message); + }; + + CallbackHandler callbackHandler; + String lastRunningDevice; + int lastRunningBlockSize; + double lastRunningSampleRate; + BitArray lastRunningIns, lastRunningOuts; + + friend class CallbackHandler; + + void audioDeviceIOCallbackInt (const float** inputChannelData, + int totalNumInputChannels, + float** outputChannelData, + int totalNumOutputChannels, + int numSamples); + void audioDeviceAboutToStartInt (AudioIODevice* const device); + void audioDeviceStoppedInt(); + + void handleIncomingMidiMessageInt (MidiInput* source, const MidiMessage& message); + + const String restartDevice (int blockSizeToUse, double sampleRateToUse, + const BitArray& ins, const BitArray& outs); + void stopDevice(); + + void updateXml(); + + AudioDeviceManager (const AudioDeviceManager&); + const AudioDeviceManager& operator= (const AudioDeviceManager&); +}; + +#endif // __JUCE_AUDIODEVICEMANAGER_JUCEHEADER__ +/********* End of inlined file: juce_AudioDeviceManager.h *********/ + +#endif +#ifndef __JUCE_AUDIOIODEVICE_JUCEHEADER__ + +#endif +#ifndef __JUCE_AUDIOIODEVICETYPE_JUCEHEADER__ + +#endif +#ifndef __JUCE_MIDIINPUT_JUCEHEADER__ + +#endif +#ifndef __JUCE_MIDIOUTPUT_JUCEHEADER__ + +#endif +#ifndef __JUCE_SAMPLER_JUCEHEADER__ + +/********* Start of inlined file: juce_Sampler.h *********/ +#ifndef __JUCE_SAMPLER_JUCEHEADER__ +#define __JUCE_SAMPLER_JUCEHEADER__ + +/********* Start of inlined file: juce_Synthesiser.h *********/ +#ifndef __JUCE_SYNTHESISER_JUCEHEADER__ +#define __JUCE_SYNTHESISER_JUCEHEADER__ + +/** + Describes one of the sounds that a Synthesiser can play. + + A synthesiser can contain one or more sounds, and a sound can choose which + midi notes and channels can trigger it. + + The SynthesiserSound is a passive class that just describes what the sound is - + the actual audio rendering for a sound is done by a SynthesiserVoice. This allows + more than one SynthesiserVoice to play the same sound at the same time. + + @see Synthesiser, SynthesiserVoice +*/ +class JUCE_API SynthesiserSound : public ReferenceCountedObject +{ +protected: + + SynthesiserSound(); + +public: + /** Destructor. */ + virtual ~SynthesiserSound(); + + /** Returns true if this sound should be played when a given midi note is pressed. + + The Synthesiser will use this information when deciding which sounds to trigger + for a given note. + */ + virtual bool appliesToNote (const int midiNoteNumber) = 0; + + /** Returns true if the sound should be triggered by midi events on a given channel. + + The Synthesiser will use this information when deciding which sounds to trigger + for a given note. + */ + virtual bool appliesToChannel (const int midiChannel) = 0; + + /** + */ + typedef ReferenceCountedObjectPtr Ptr; + + juce_UseDebuggingNewOperator +}; + +/** + Represents a voice that a Synthesiser can use to play a SynthesiserSound. + + A voice plays a single sound at a time, and a synthesiser holds an array of + voices so that it can play polyphonically. + + @see Synthesiser, SynthesiserSound +*/ +class JUCE_API SynthesiserVoice +{ +public: + + /** Creates a voice. */ + SynthesiserVoice(); + + /** Destructor. */ + virtual ~SynthesiserVoice(); + + /** Returns the midi note that this voice is currently playing. + + Returns a value less than 0 if no note is playing. + */ + int getCurrentlyPlayingNote() const throw() { return currentlyPlayingNote; } + + /** Returns the sound that this voice is currently playing. + + Returns 0 if it's not playing. + */ + const SynthesiserSound::Ptr getCurrentlyPlayingSound() const throw() { return currentlyPlayingSound; } + + /** Must return true if this voice object is capable of playing the given sound. + + If there are different classes of sound, and different classes of voice, a voice can + choose which ones it wants to take on. + + A typical implementation of this method may just return true if there's only one type + of voice and sound, or it might check the type of the sound object passed-in and + see if it's one that it understands. + */ + virtual bool canPlaySound (SynthesiserSound* sound) = 0; + + /** Called to start a new note. + + This will be called during the rendering callback, so must be fast and thread-safe. + */ + virtual void startNote (const int midiNoteNumber, + const float velocity, + SynthesiserSound* sound, + const int currentPitchWheelPosition) = 0; + + /** Called to stop a note. + + This will be called during the rendering callback, so must be fast and thread-safe. + + If allowTailOff is false or the voice doesn't want to tail-off, then it must stop all + sound immediately, and must call clearCurrentNote() to reset the state of this voice + and allow the synth to reassign it another sound. + + If allowTailOff is true and the voice decides to do a tail-off, then it's allowed to + begin fading out its sound, and it can stop playing until it's finished. As soon as it + finishes playing (during the rendering callback), it must make sure that it calls + clearCurrentNote(). + */ + virtual void stopNote (const bool allowTailOff) = 0; + + /** Called to let the voice know that the pitch wheel has been moved. + + This will be called during the rendering callback, so must be fast and thread-safe. + */ + virtual void pitchWheelMoved (const int newValue) = 0; + + /** Called to let the voice know that a midi controller has been moved. + + This will be called during the rendering callback, so must be fast and thread-safe. + */ + virtual void controllerMoved (const int controllerNumber, + const int newValue) = 0; + + /** Renders the next block of data for this voice. + + The output audio data must be added to the current contents of the buffer provided. + Only the region of the buffer between startSample and (startSample + numSamples) + should be altered by this method. + + If the voice is currently silent, it should just return without doing anything. + + If the sound that the voice is playing finishes during the course of this rendered + block, it must call clearCurrentNote(), to tell the synthesiser that it has finished. + + The size of the blocks that are rendered can change each time it is called, and may + involve rendering as little as 1 sample at a time. In between rendering callbacks, + the voice's methods will be called to tell it about note and controller events. + */ + virtual void renderNextBlock (AudioSampleBuffer& outputBuffer, + int startSample, + int numSamples) = 0; + + /** Returns true if the voice is currently playing a sound which is mapped to the given + midi channel. + + If it's not currently playing, this will return false. + */ + bool isPlayingChannel (const int midiChannel) const; + + /** Changes the voice's reference sample rate. + + The rate is set so that subclasses know the output rate and can set their pitch + accordingly. + + This method is called by the synth, and subclasses can access the current rate with + the currentSampleRate member. + */ + void setCurrentPlaybackSampleRate (const double newRate); + + juce_UseDebuggingNewOperator + +protected: + + /** Returns the current target sample rate at which rendering is being done. + + This is available for subclasses so they can pitch things correctly. + */ + double getSampleRate() const throw() { return currentSampleRate; } + + /** Resets the state of this voice after a sound has finished playing. + + The subclass must call this when it finishes playing a note and becomes available + to play new ones. + + It must either call it in the stopNote() method, or if the voice is tailing off, + then it should call it later during the renderNextBlock method, as soon as it + finishes its tail-off. + + It can also be called at any time during the render callback if the sound happens + to have finished, e.g. if it's playing a sample and the sample finishes. + */ + void clearCurrentNote(); + +private: + + friend class Synthesiser; + + double currentSampleRate; + int currentlyPlayingNote; + uint32 noteOnTime; + SynthesiserSound::Ptr currentlyPlayingSound; +}; + +/** + Base class for a musical device that can play sounds. + + To create a synthesiser, you'll need to create a subclass of SynthesiserSound + to describe each sound available to your synth, and a subclass of SynthesiserVoice + which can play back one of these sounds. + + Then you can use the addVoice() and addSound() methods to give the synthesiser a + set of sounds, and a set of voices it can use to play them. If you only give it + one voice it will be monophonic - the more voices it has, the more polyphony it'll + have available. + + Then repeatedly call the renderNextBlock() method to produce the audio. Any midi + events that go in will be scanned for note on/off messages, and these are used to + start and stop the voices playing the appropriate sounds. + + While it's playing, you can also cause notes to be triggered by calling the noteOn(), + noteOff() and other controller methods. + + Before rendering, be sure to call the setCurrentPlaybackSampleRate() to tell it + what the target playback rate is. This value is passed on to the voices so that + they can pitch their output correctly. +*/ +class JUCE_API Synthesiser +{ +public: + + /** Creates a new synthesiser. + + You'll need to add some sounds and voices before it'll make any sound.. + */ + Synthesiser(); + + /** Destructor. */ + virtual ~Synthesiser(); + + /** Deletes all voices. */ + void clearVoices(); + + /** Returns the number of voices that have been added. */ + int getNumVoices() const throw() { return voices.size(); } + + /** Returns one of the voices that have been added. */ + SynthesiserVoice* getVoice (const int index) const throw(); + + /** Adds a new voice to the synth. + + All the voices should be the same class of object and are treated equally. + + The object passed in will be managed by the synthesiser, which will delete + it later on when no longer needed. The caller should not retain a pointer to the + voice. + */ + void addVoice (SynthesiserVoice* const newVoice); + + /** Deletes one of the voices. */ + void removeVoice (const int index); + + /** Deletes all sounds. */ + void clearSounds(); + + /** Returns the number of sounds that have been added to the synth. */ + int getNumSounds() const throw() { return sounds.size(); } + + /** Returns one of the sounds. */ + SynthesiserSound* getSound (const int index) const throw() { return sounds [index]; } + + /** Adds a new sound to the synthesiser. + + The object passed in is reference counted, so will be deleted when it is removed + from the synthesiser, and when no voices are still using it. + */ + void addSound (const SynthesiserSound::Ptr& newSound); + + /** Removes and deletes one of the sounds. */ + void removeSound (const int index); + + /** If set to true, then the synth will try to take over an existing voice if + it runs out and needs to play another note. + + The value of this boolean is passed into findFreeVoice(), so the result will + depend on the implementation of this method. + */ + void setNoteStealingEnabled (const bool shouldStealNotes); + + /** Returns true if note-stealing is enabled. + @see setNoteStealingEnabled + */ + bool isNoteStealingEnabled() const throw() { return shouldStealNotes; } + + /** Triggers a note-on event. + + The default method here will find all the sounds that want to be triggered by + this note/channel. For each sound, it'll try to find a free voice, and use the + voice to start playing the sound. + + Subclasses might want to override this if they need a more complex algorithm. + + This method will be called automatically according to the midi data passed into + renderNextBlock(), but may be called explicitly too. + */ + virtual void noteOn (const int midiChannel, + const int midiNoteNumber, + const float velocity); + + /** Triggers a note-off event. + + This will turn off any voices that are playing a sound for the given note/channel. + + If allowTailOff is true, the voices will be allowed to fade out the notes gracefully + (if they can do). If this is false, the notes will all be cut off immediately. + + This method will be called automatically according to the midi data passed into + renderNextBlock(), but may be called explicitly too. + */ + virtual void noteOff (const int midiChannel, + const int midiNoteNumber, + const bool allowTailOff); + + /** Turns off all notes. + + This will turn off any voices that are playing a sound on the given midi channel. + + If midiChannel is 0 or less, then all voices will be turned off, regardless of + which channel they're playing. + + If allowTailOff is true, the voices will be allowed to fade out the notes gracefully + (if they can do). If this is false, the notes will all be cut off immediately. + + This method will be called automatically according to the midi data passed into + renderNextBlock(), but may be called explicitly too. + */ + virtual void allNotesOff (const int midiChannel, + const bool allowTailOff); + + /** Sends a pitch-wheel message. + + This will send a pitch-wheel message to any voices that are playing sounds on + the given midi channel. + + This method will be called automatically according to the midi data passed into + renderNextBlock(), but may be called explicitly too. + + @param midiChannel the midi channel for the event + @param wheelValue the wheel position, from 0 to 0x3fff, as returned by MidiMessage::getPitchWheelValue() + */ + virtual void handlePitchWheel (const int midiChannel, + const int wheelValue); + + /** Sends a midi controller message. + + This will send a midi controller message to any voices that are playing sounds on + the given midi channel. + + This method will be called automatically according to the midi data passed into + renderNextBlock(), but may be called explicitly too. + + @param midiChannel the midi channel for the event + @param controllerNumber the midi controller type, as returned by MidiMessage::getControllerNumber() + @param controllerValue the midi controller value, between 0 and 127, as returned by MidiMessage::getControllerValue() + */ + virtual void handleController (const int midiChannel, + const int controllerNumber, + const int controllerValue); + + /** Tells the synthesiser what the sample rate is for the audio it's being used to + render. + + This value is propagated to the voices so that they can use it to render the correct + pitches. + */ + void setCurrentPlaybackSampleRate (const double sampleRate); + + /** Creates the next block of audio output. + + This will process the next numSamples of data from all the voices, and add that output + to the audio block supplied, starting from the offset specified. Note that the + data will be added to the current contents of the buffer, so you should clear it + before calling this method if necessary. + + The midi events in the inputMidi buffer are parsed for note and controller events, + and these are used to trigger the voices. Note that the startSample offset applies + both to the audio output buffer and the midi input buffer, so any midi events + with timestamps outside the specified region will be ignored. + */ + void renderNextBlock (AudioSampleBuffer& outputAudio, + const MidiBuffer& inputMidi, + int startSample, + int numSamples); + + juce_UseDebuggingNewOperator + +protected: + + /** This is used to control access to the rendering callback and the note trigger methods. */ + CriticalSection lock; + + OwnedArray voices; + ReferenceCountedArray sounds; + + /** The last pitch-wheel values for each midi channel. */ + int lastPitchWheelValues [16]; + + /** Searches through the voices to find one that's not currently playing, and which + can play the given sound. + + Returns 0 if all voices are busy and stealing isn't enabled. + + This can be overridden to implement custom voice-stealing algorithms. + */ + virtual SynthesiserVoice* findFreeVoice (SynthesiserSound* soundToPlay, + const bool stealIfNoneAvailable) const; + + /** Starts a specified voice playing a particular sound. + + You'll probably never need to call this, it's used internally by noteOn(), but + may be needed by subclasses for custom behaviours. + */ + void startVoice (SynthesiserVoice* const voice, + SynthesiserSound* const sound, + const int midiChannel, + const int midiNoteNumber, + const float velocity); + + /** xxx Temporary method here to cause a compiler error - note the new parameters for this method. */ + int findFreeVoice (const bool) const { return 0; } + +private: + double sampleRate; + uint32 lastNoteOnCounter; + bool shouldStealNotes; + + Synthesiser (const Synthesiser&); + const Synthesiser& operator= (const Synthesiser&); +}; + +#endif // __JUCE_SYNTHESISER_JUCEHEADER__ +/********* End of inlined file: juce_Synthesiser.h *********/ + +/** + A subclass of SynthesiserSound that represents a sampled audio clip. + + This is a pretty basic sampler, and just attempts to load the whole audio stream + into memory. + + To use it, create a Synthesiser, add some SamplerVoice objects to it, then + give it some SampledSound objects to play. + + @see SamplerVoice, Synthesiser, SynthesiserSound +*/ +class JUCE_API SamplerSound : public SynthesiserSound +{ +public: + + /** Creates a sampled sound from an audio reader. + + This will attempt to load the audio from the source into memory and store + it in this object. + + @param name a name for the sample + @param source the audio to load. This object can be safely deleted by the + caller after this constructor returns + @param midiNotes the set of midi keys that this sound should be played on. This + is used by the SynthesiserSound::appliesToNote() method + @param midiNoteForNormalPitch the midi note at which the sample should be played + with its natural rate. All other notes will be pitched + up or down relative to this one + @param attackTimeSecs the attack (fade-in) time, in seconds + @param releaseTimeSecs the decay (fade-out) time, in seconds + @param maxSampleLengthSeconds a maximum length of audio to read from the audio + source, in seconds + */ + SamplerSound (const String& name, + AudioFormatReader& source, + const BitArray& midiNotes, + const int midiNoteForNormalPitch, + const double attackTimeSecs, + const double releaseTimeSecs, + const double maxSampleLengthSeconds); + + /** Destructor. */ + ~SamplerSound(); + + /** Returns the sample's name */ + const String& getName() const throw() { return name; } + + /** Returns the audio sample data. + This could be 0 if there was a problem loading it. + */ + AudioSampleBuffer* getAudioData() const throw() { return data; } + + bool appliesToNote (const int midiNoteNumber); + bool appliesToChannel (const int midiChannel); + + juce_UseDebuggingNewOperator + +private: + friend class SamplerVoice; + + String name; + AudioSampleBuffer* data; + double sourceSampleRate; + BitArray midiNotes; + int length, attackSamples, releaseSamples; + int midiRootNote; +}; + +/** + A subclass of SynthesiserVoice that can play a SamplerSound. + + To use it, create a Synthesiser, add some SamplerVoice objects to it, then + give it some SampledSound objects to play. + + @see SamplerSound, Synthesiser, SynthesiserVoice +*/ +class JUCE_API SamplerVoice : public SynthesiserVoice +{ +public: + + /** Creates a SamplerVoice. + */ + SamplerVoice(); + + /** Destructor. */ + ~SamplerVoice(); + + bool canPlaySound (SynthesiserSound* sound); + + void startNote (const int midiNoteNumber, + const float velocity, + SynthesiserSound* sound, + const int currentPitchWheelPosition); + + void stopNote (const bool allowTailOff); + + void pitchWheelMoved (const int newValue); + void controllerMoved (const int controllerNumber, + const int newValue); + + void renderNextBlock (AudioSampleBuffer& outputBuffer, int startSample, int numSamples); + + juce_UseDebuggingNewOperator + +private: + double pitchRatio; + double sourceSamplePosition; + float lgain, rgain, attackReleaseLevel, attackDelta, releaseDelta; + bool isInAttack, isInRelease; +}; + +#endif // __JUCE_SAMPLER_JUCEHEADER__ +/********* End of inlined file: juce_Sampler.h *********/ + +#endif +#ifndef __JUCE_SYNTHESISER_JUCEHEADER__ + +#endif +#ifndef __JUCE_AUDIOUNITPLUGINFORMAT_JUCEHEADER__ + +/********* Start of inlined file: juce_AudioUnitPluginFormat.h *********/ +#ifndef __JUCE_AUDIOUNITPLUGINFORMAT_JUCEHEADER__ +#define __JUCE_AUDIOUNITPLUGINFORMAT_JUCEHEADER__ + +#if JUCE_PLUGINHOST_AU && JUCE_MAC + +/** + Implements a plugin format manager for AudioUnits. +*/ +class JUCE_API AudioUnitPluginFormat : public AudioPluginFormat +{ +public: + + AudioUnitPluginFormat(); + ~AudioUnitPluginFormat(); + + const String getName() const { return "AudioUnit"; } + void findAllTypesForFile (OwnedArray & results, const File& file); + AudioPluginInstance* createInstanceFromDescription (const PluginDescription& desc); + bool fileMightContainThisPluginType (const File& file); + const FileSearchPath getDefaultLocationsToSearch(); + + juce_UseDebuggingNewOperator + +private: + AudioUnitPluginFormat (const AudioUnitPluginFormat&); + const AudioUnitPluginFormat& operator= (const AudioUnitPluginFormat&); +}; + +#endif + +#endif // __JUCE_AUDIOUNITPLUGINFORMAT_JUCEHEADER__ +/********* End of inlined file: juce_AudioUnitPluginFormat.h *********/ + +#endif +#ifndef __JUCE_DIRECTXPLUGINFORMAT_JUCEHEADER__ + +/********* Start of inlined file: juce_DirectXPluginFormat.h *********/ +#ifndef __JUCE_DIRECTXPLUGINFORMAT_JUCEHEADER__ +#define __JUCE_DIRECTXPLUGINFORMAT_JUCEHEADER__ + +#if JUCE_PLUGINHOST_DX && JUCE_WIN32 + +// Sorry, this file is just a placeholder at the moment!... + +/** + Implements a plugin format manager for DirectX plugins. +*/ +class JUCE_API DirectXPluginFormat : public AudioPluginFormat +{ +public: + + DirectXPluginFormat(); + ~DirectXPluginFormat(); + + const String getName() const { return "DirectX"; } + void findAllTypesForFile (OwnedArray & results, const File& file); + AudioPluginInstance* createInstanceFromDescription (const PluginDescription& desc); + bool fileMightContainThisPluginType (const File& file); + const FileSearchPath getDefaultLocationsToSearch(); + + juce_UseDebuggingNewOperator + +private: + DirectXPluginFormat (const DirectXPluginFormat&); + const DirectXPluginFormat& operator= (const DirectXPluginFormat&); +}; + +#endif + +#endif // __JUCE_DIRECTXPLUGINFORMAT_JUCEHEADER__ +/********* End of inlined file: juce_DirectXPluginFormat.h *********/ + +#endif +#ifndef __JUCE_LADSPAPLUGINFORMAT_JUCEHEADER__ + +/********* Start of inlined file: juce_LADSPAPluginFormat.h *********/ +#ifndef __JUCE_LADSPAPLUGINFORMAT_JUCEHEADER__ +#define __JUCE_LADSPAPLUGINFORMAT_JUCEHEADER__ + +#if JUCE_PLUGINHOST_LADSPA && JUCE_LINUX + +// Sorry, this file is just a placeholder at the moment!... + +/** + Implements a plugin format manager for DirectX plugins. +*/ +class JUCE_API LADSPAPluginFormat : public AudioPluginFormat +{ +public: + + LADSPAPluginFormat(); + ~LADSPAPluginFormat(); + + const String getName() const { return "LADSPA"; } + void findAllTypesForFile (OwnedArray & results, const File& file); + AudioPluginInstance* createInstanceFromDescription (const PluginDescription& desc); + bool fileMightContainThisPluginType (const File& file); + const FileSearchPath getDefaultLocationsToSearch(); + + juce_UseDebuggingNewOperator + +private: + LADSPAPluginFormat (const LADSPAPluginFormat&); + const LADSPAPluginFormat& operator= (const LADSPAPluginFormat&); +}; + +#endif + +#endif // __JUCE_LADSPAPLUGINFORMAT_JUCEHEADER__ +/********* End of inlined file: juce_LADSPAPluginFormat.h *********/ + +#endif +#ifndef __JUCE_VSTPLUGINFORMAT_JUCEHEADER__ + +/********* Start of inlined file: juce_VSTPluginFormat.h *********/ +#ifndef __JUCE_VSTPLUGINFORMAT_JUCEHEADER__ +#define __JUCE_VSTPLUGINFORMAT_JUCEHEADER__ + +#if JUCE_PLUGINHOST_VST + +/** + Implements a plugin format manager for VSTs. +*/ +class JUCE_API VSTPluginFormat : public AudioPluginFormat +{ +public: + + VSTPluginFormat(); + ~VSTPluginFormat(); + + const String getName() const { return "VST"; } + void findAllTypesForFile (OwnedArray & results, const File& file); + AudioPluginInstance* createInstanceFromDescription (const PluginDescription& desc); + bool fileMightContainThisPluginType (const File& file); + const FileSearchPath getDefaultLocationsToSearch(); + + juce_UseDebuggingNewOperator + +private: + VSTPluginFormat (const VSTPluginFormat&); + const VSTPluginFormat& operator= (const VSTPluginFormat&); +}; + +#endif +#endif // __JUCE_VSTPLUGINFORMAT_JUCEHEADER__ +/********* End of inlined file: juce_VSTPluginFormat.h *********/ + +#endif +#ifndef __JUCE_AUDIOPLUGINFORMAT_JUCEHEADER__ + +#endif +#ifndef __JUCE_AUDIOPLUGINFORMATMANAGER_JUCEHEADER__ + +#endif +#ifndef __JUCE_AUDIOPLUGININSTANCE_JUCEHEADER__ + +#endif +#ifndef __JUCE_KNOWNPLUGINLIST_JUCEHEADER__ + +#endif +#ifndef __JUCE_PLUGINDESCRIPTION_JUCEHEADER__ + +#endif +#ifndef __JUCE_PLUGINDIRECTORYSCANNER_JUCEHEADER__ + +/********* Start of inlined file: juce_PluginDirectoryScanner.h *********/ +#ifndef __JUCE_PLUGINDIRECTORYSCANNER_JUCEHEADER__ +#define __JUCE_PLUGINDIRECTORYSCANNER_JUCEHEADER__ + +/** + Scans a directory for plugins, and adds them to a KnownPluginList. + + To use one of these, create it and call scanNextFile() repeatedly, until + it returns false. +*/ +class JUCE_API PluginDirectoryScanner +{ +public: + + /** + Creates a scanner. + + @param listToAddResultsTo this will get the new types added to it. + @param formatToLookFor this is the type of format that you want to look for + @param directoriesToSearch the path to search + @param searchRecursively true to search recursively + @param deadMansPedalFile if this isn't File::nonexistent, then it will + be used as a file to store the names of any plugins + that crash during initialisation. If there are + any plugins listed in it, then these will always + be scanned after all other possible files have + been tried - in this way, even if there's a few + dodgy plugins in your path, then a couple of rescans + will still manage to find all the proper plugins. + It's probably best to choose a file in the user's + application data directory (alongside your app's + settings file) for this. The file format it uses + is just a list of filenames of the modules that + failed. + */ + PluginDirectoryScanner (KnownPluginList& listToAddResultsTo, + AudioPluginFormat& formatToLookFor, + FileSearchPath directoriesToSearch, + const bool searchRecursively, + const File& deadMansPedalFile); + + /** Destructor. */ + ~PluginDirectoryScanner(); + + /** Tries the next likely-looking file. + + If dontRescanIfAlreadyInList is true, then the file will only be loaded and + re-tested if it's not already in the list, or if the file's modification + time has changed since the list was created. If dontRescanIfAlreadyInList is + false, the file will always be reloaded and tested. + + Returns false when there are no more files to try. + */ + bool scanNextFile (const bool dontRescanIfAlreadyInList); + + /** Returns the file that will be scanned during the next call to scanNextFile(). + + This is handy if you want to show the user which file is currently getting + scanned. + */ + const File getNextPluginFileThatWillBeScanned() const throw(); + + /** Returns the estimated progress, between 0 and 1. + */ + float getProgress() const { return progress; } + + /** This returns a list of all the filenames of things that looked like being + a plugin file, but which failed to open for some reason. + */ + const StringArray& getFailedFiles() const throw() { return failedFiles; } + + juce_UseDebuggingNewOperator + +private: + KnownPluginList& list; + AudioPluginFormat& format; + OwnedArray filesToScan; + File deadMansPedalFile; + StringArray failedFiles; + int nextIndex; + float progress; + + void recursiveFileSearch (const File& dir, const bool recursive); + const StringArray getDeadMansPedalFile() throw(); + void setDeadMansPedalFile (const StringArray& newContents) throw(); + + PluginDirectoryScanner (const PluginDirectoryScanner&); + const PluginDirectoryScanner& operator= (const PluginDirectoryScanner&); +}; + +#endif // __JUCE_PLUGINDIRECTORYSCANNER_JUCEHEADER__ +/********* End of inlined file: juce_PluginDirectoryScanner.h *********/ + +#endif +#ifndef __JUCE_PLUGINLISTCOMPONENT_JUCEHEADER__ + +/********* Start of inlined file: juce_PluginListComponent.h *********/ +#ifndef __JUCE_PLUGINLISTCOMPONENT_JUCEHEADER__ +#define __JUCE_PLUGINLISTCOMPONENT_JUCEHEADER__ + +/********* Start of inlined file: juce_ListBox.h *********/ +#ifndef __JUCE_LISTBOX_JUCEHEADER__ +#define __JUCE_LISTBOX_JUCEHEADER__ + +class ListViewport; + +/** + A subclass of this is used to drive a ListBox. + + @see ListBox +*/ +class JUCE_API ListBoxModel +{ +public: + + /** Destructor. */ + virtual ~ListBoxModel() {} + + /** This has to return the number of items in the list. + + @see ListBox::getNumRows() + */ + virtual int getNumRows() = 0; + + /** This method must be implemented to draw a row of the list. + */ + virtual void paintListBoxItem (int rowNumber, + Graphics& g, + int width, int height, + bool rowIsSelected) = 0; + + /** This is used to create or update a custom component to go in a row of the list. + + Any row may contain a custom component, or can just be drawn with the paintListBoxItem() method + and handle mouse clicks with listBoxItemClicked(). + + This method will be called whenever a custom component might need to be updated - e.g. + when the table is changed, or TableListBox::updateContent() is called. + + If you don't need a custom component for the specified row, then return 0. + + If you do want a custom component, and the existingComponentToUpdate is null, then + this method must create a suitable new component and return it. + + If the existingComponentToUpdate is non-null, it will be a pointer to a component previously created + by this method. In this case, the method must either update it to make sure it's correctly representing + the given row (which may be different from the one that the component was created for), or it can + delete this component and return a new one. + + The component that your method returns will be deleted by the ListBox when it is no longer needed. + */ + virtual Component* refreshComponentForRow (int rowNumber, bool isRowSelected, + Component* existingComponentToUpdate); + + /** This can be overridden to react to the user clicking on a row. + + @see listBoxItemDoubleClicked + */ + virtual void listBoxItemClicked (int row, const MouseEvent& e); + + /** This can be overridden to react to the user double-clicking on a row. + + @see listBoxItemClicked + */ + virtual void listBoxItemDoubleClicked (int row, const MouseEvent& e); + + /** This can be overridden to react to the user double-clicking on a part of the list where + there are no rows. + + @see listBoxItemClicked + */ + virtual void backgroundClicked(); + + /** Override this to be informed when rows are selected or deselected. + + This will be called whenever a row is selected or deselected. If a range of + rows is selected all at once, this will just be called once for that event. + + @param lastRowSelected the last row that the user selected. If no + rows are currently selected, this may be -1. + */ + virtual void selectedRowsChanged (int lastRowSelected); + + /** Override this to be informed when the delete key is pressed. + + If no rows are selected when they press the key, this won't be called. + + @param lastRowSelected the last row that had been selected when they pressed the + key - if there are multiple selections, this might not be + very useful + */ + virtual void deleteKeyPressed (int lastRowSelected); + + /** Override this to be informed when the return key is pressed. + + If no rows are selected when they press the key, this won't be called. + + @param lastRowSelected the last row that had been selected when they pressed the + key - if there are multiple selections, this might not be + very useful + */ + virtual void returnKeyPressed (int lastRowSelected); + + /** Override this to be informed when the list is scrolled. + + This might be caused by the user moving the scrollbar, or by programmatic changes + to the list position. + */ + virtual void listWasScrolled(); + + /** To allow rows from your list to be dragged-and-dropped, implement this method. + + If this returns a non-empty name then when the user drags a row, the listbox will + try to find a DragAndDropContainer in its parent hierarchy, and will use it to trigger + a drag-and-drop operation, using this string as the source description, with the listbox + itself as the source component. + + @see DragAndDropContainer::startDragging + */ + virtual const String getDragSourceDescription (const SparseSet& currentlySelectedRows); +}; + +/** + A list of items that can be scrolled vertically. + + To create a list, you'll need to create a subclass of ListBoxModel. This can + either paint each row of the list and respond to events via callbacks, or for + more specialised tasks, it can supply a custom component to fill each row. + + @see ComboBox, TableListBox +*/ +class JUCE_API ListBox : public Component, + public SettableTooltipClient +{ +public: + + /** Creates a ListBox. + + The model pointer passed-in can be null, in which case you can set it later + with setModel(). + */ + ListBox (const String& componentName, + ListBoxModel* const model); + + /** Destructor. */ + ~ListBox(); + + /** Changes the current data model to display. */ + void setModel (ListBoxModel* const newModel); + + /** Returns the current list model. */ + ListBoxModel* getModel() const throw() { return model; } + + /** Causes the list to refresh its content. + + Call this when the number of rows in the list changes, or if you want it + to call refreshComponentForRow() on all the row components. + + Be careful not to call it from a different thread, though, as it's not + thread-safe. + */ + void updateContent(); + + /** Turns on multiple-selection of rows. + + By default this is disabled. + + When your row component gets clicked you'll need to call the + selectRowsBasedOnModifierKeys() method to tell the list that it's been + clicked and to get it to do the appropriate selection based on whether + the ctrl/shift keys are held down. + */ + void setMultipleSelectionEnabled (bool shouldBeEnabled); + + /** Makes the list react to mouse moves by selecting the row that the mouse if over. + + This function is here primarily for the ComboBox class to use, but might be + useful for some other purpose too. + */ + void setMouseMoveSelectsRows (bool shouldSelect); + + /** Selects a row. + + If the row is already selected, this won't do anything. + + @param rowNumber the row to select + @param dontScrollToShowThisRow if true, the list's position won't change; if false and + the selected row is off-screen, it'll scroll to make + sure that row is on-screen + @param deselectOthersFirst if true and there are multiple selections, these will + first be deselected before this item is selected + @see isRowSelected, selectRowsBasedOnModifierKeys, flipRowSelection, deselectRow, + deselectAllRows, selectRangeOfRows + */ + void selectRow (const int rowNumber, + bool dontScrollToShowThisRow = false, + bool deselectOthersFirst = true); + + /** Selects a set of rows. + + This will add these rows to the current selection, so you might need to + clear the current selection first with deselectAllRows() + + @param firstRow the first row to select (inclusive) + @param lastRow the last row to select (inclusive) + */ + void selectRangeOfRows (int firstRow, + int lastRow); + + /** Deselects a row. + + If it's not currently selected, this will do nothing. + + @see selectRow, deselectAllRows + */ + void deselectRow (const int rowNumber); + + /** Deselects any currently selected rows. + + @see deselectRow + */ + void deselectAllRows(); + + /** Selects or deselects a row. + + If the row's currently selected, this deselects it, and vice-versa. + */ + void flipRowSelection (const int rowNumber); + + /** Returns a sparse set indicating the rows that are currently selected. + + @see setSelectedRows + */ + const SparseSet getSelectedRows() const; + + /** Sets the rows that should be selected, based on an explicit set of ranges. + + If sendNotificationEventToModel is true, the ListBoxModel::selectedRowsChanged() + method will be called. If it's false, no notification will be sent to the model. + + @see getSelectedRows + */ + void setSelectedRows (const SparseSet& setOfRowsToBeSelected, + const bool sendNotificationEventToModel = true); + + /** Checks whether a row is selected. + */ + bool isRowSelected (const int rowNumber) const; + + /** Returns the number of rows that are currently selected. + + @see getSelectedRow, isRowSelected, getLastRowSelected + */ + int getNumSelectedRows() const; + + /** Returns the row number of a selected row. + + This will return the row number of the Nth selected row. The row numbers returned will + be sorted in order from low to high. + + @param index the index of the selected row to return, (from 0 to getNumSelectedRows() - 1) + @returns the row number, or -1 if the index was out of range or if there aren't any rows + selected + @see getNumSelectedRows, isRowSelected, getLastRowSelected + */ + int getSelectedRow (const int index = 0) const; + + /** Returns the last row that the user selected. + + This isn't the same as the highest row number that is currently selected - if the user + had multiply-selected rows 10, 5 and then 6 in that order, this would return 6. + + If nothing is selected, it will return -1. + */ + int getLastRowSelected() const; + + /** Multiply-selects rows based on the modifier keys. + + If no modifier keys are down, this will select the given row and + deselect any others. + + If the ctrl (or command on the Mac) key is down, it'll flip the + state of the selected row. + + If the shift key is down, it'll select up to the given row from the + last row selected. + + @see selectRow + */ + void selectRowsBasedOnModifierKeys (const int rowThatWasClickedOn, + const ModifierKeys& modifiers); + + /** Scrolls the list to a particular position. + + The proportion is between 0 and 1.0, so 0 scrolls to the top of the list, + 1.0 scrolls to the bottom. + + If the total number of rows all fit onto the screen at once, then this + method won't do anything. + + @see getVerticalPosition + */ + void setVerticalPosition (const double newProportion); + + /** Returns the current vertical position as a proportion of the total. + + This can be used in conjunction with setVerticalPosition() to save and restore + the list's position. It returns a value in the range 0 to 1. + + @see setVerticalPosition + */ + double getVerticalPosition() const; + + /** Scrolls if necessary to make sure that a particular row is visible. + */ + void scrollToEnsureRowIsOnscreen (const int row); + + /** Returns a pointer to the scrollbar. + + (Unlikely to be useful for most people). + */ + ScrollBar* getVerticalScrollBar() const throw(); + + /** Returns a pointer to the scrollbar. + + (Unlikely to be useful for most people). + */ + ScrollBar* getHorizontalScrollBar() const throw(); + + /** Finds the row index that contains a given x,y position. + + The position is relative to the ListBox's top-left. + + If no row exists at this position, the method will return -1. + + @see getComponentForRowNumber + */ + int getRowContainingPosition (const int x, const int y) const throw(); + + /** Finds a row index that would be the most suitable place to insert a new + item for a given position. + + This is useful when the user is e.g. dragging and dropping onto the listbox, + because it lets you easily choose the best position to insert the item that + they drop, based on where they drop it. + + If the position is out of range, this will return -1. If the position is + beyond the end of the list, it will return getNumRows() to indicate the end + of the list. + + @see getComponentForRowNumber + */ + int getInsertionIndexForPosition (const int x, const int y) const throw(); + + /** Returns the position of one of the rows, relative to the top-left of + the listbox. + + This may be off-screen, and the range of the row number that is passed-in is + not checked to see if it's a valid row. + */ + const Rectangle getRowPosition (const int rowNumber, + const bool relativeToComponentTopLeft) const throw(); + + /** Finds the row component for a given row in the list. + + The component returned will have been created using createRowComponent(). + + If the component for this row is off-screen or if the row is out-of-range, + this will return 0. + + @see getRowContainingPosition + */ + Component* getComponentForRowNumber (const int rowNumber) const throw(); + + /** Returns the row number that the given component represents. + + If the component isn't one of the list's rows, this will return -1. + */ + int getRowNumberOfComponent (Component* const rowComponent) const throw(); + + /** Returns the width of a row (which may be less than the width of this component + if there's a scrollbar). + */ + int getVisibleRowWidth() const throw(); + + /** Sets the height of each row in the list. + + The default height is 22 pixels. + + @see getRowHeight + */ + void setRowHeight (const int newHeight); + + /** Returns the height of a row in the list. + + @see setRowHeight + */ + int getRowHeight() const throw() { return rowHeight; } + + /** Returns the number of rows actually visible. + + This is the number of whole rows which will fit on-screen, so the value might + be more than the actual number of rows in the list. + */ + int getNumRowsOnScreen() const throw(); + + /** A set of colour IDs to use to change the colour of various aspects of the label. + + These constants can be used either via the Component::setColour(), or LookAndFeel::setColour() + methods. + + @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour + */ + enum ColourIds + { + backgroundColourId = 0x1002800, /**< The background colour to fill the list with. + Make this transparent if you don't want the background to be filled. */ + outlineColourId = 0x1002810, /**< An optional colour to use to draw a border around the list. + Make this transparent to not have an outline. */ + textColourId = 0x1002820 /**< The preferred colour to use for drawing text in the listbox. */ + }; + + /** Sets the thickness of a border that will be drawn around the box. + + To set the colour of the outline, use @code setColour (ListBox::outlineColourId, colourXYZ); @endcode + @see outlineColourId + */ + void setOutlineThickness (const int outlineThickness); + + /** Returns the thickness of outline that will be drawn around the listbox. + + @see setOutlineColour + */ + int getOutlineThickness() const throw() { return outlineThickness; } + + /** Sets a component that the list should use as a header. + + This will position the given component at the top of the list, maintaining the + height of the component passed-in, but rescaling it horizontally to match the + width of the items in the listbox. + + The component will be deleted when setHeaderComponent() is called with a + different component, or when the listbox is deleted. + */ + void setHeaderComponent (Component* const newHeaderComponent); + + /** Changes the width of the rows in the list. + + This can be used to make the list's row components wider than the list itself - the + width of the rows will be either the width of the list or this value, whichever is + greater, and if the rows become wider than the list, a horizontal scrollbar will + appear. + + The default value for this is 0, which means that the rows will always + be the same width as the list. + */ + void setMinimumContentWidth (const int newMinimumWidth); + + /** Returns the space currently available for the row items, taking into account + borders, scrollbars, etc. + */ + int getVisibleContentWidth() const throw(); + + /** Repaints one of the rows. + + This is a lightweight alternative to calling updateContent, and just causes a + repaint of the row's area. + */ + void repaintRow (const int rowNumber) throw(); + + /** This fairly obscure method creates an image that just shows the currently + selected row components. + + It's a handy method for doing drag-and-drop, as it can be passed to the + DragAndDropContainer for use as the drag image. + + Note that it will make the row components temporarily invisible, so if you're + using custom components this could affect them if they're sensitive to that + sort of thing. + + @see Component::createComponentSnapshot + */ + Image* createSnapshotOfSelectedRows(); + + /** Returns the viewport that this ListBox uses. + + You may need to use this to change parameters such as whether scrollbars + are shown, etc. + */ + Viewport* getViewport() const throw(); + + /** @internal */ + bool keyPressed (const KeyPress& key); + /** @internal */ + bool keyStateChanged(); + /** @internal */ + void paint (Graphics& g); + /** @internal */ + void paintOverChildren (Graphics& g); + /** @internal */ + void resized(); + /** @internal */ + void visibilityChanged(); + /** @internal */ + void mouseWheelMove (const MouseEvent& e, float wheelIncrementX, float wheelIncrementY); + /** @internal */ + void mouseMove (const MouseEvent&); + /** @internal */ + void mouseExit (const MouseEvent&); + /** @internal */ + void mouseUp (const MouseEvent&); + /** @internal */ + void colourChanged(); + + juce_UseDebuggingNewOperator + +private: + + friend class ListViewport; + friend class TableListBox; + ListBoxModel* model; + ListViewport* viewport; + Component* headerComponent; + int totalItems, rowHeight, minimumRowWidth; + int outlineThickness; + int lastMouseX, lastMouseY, lastRowSelected; + bool mouseMoveSelects, multipleSelection, hasDoneInitialUpdate; + SparseSet selected; + + void selectRowInternal (const int rowNumber, + bool dontScrollToShowThisRow, + bool deselectOthersFirst, + bool isMouseClick); + + ListBox (const ListBox&); + const ListBox& operator= (const ListBox&); +}; + +#endif // __JUCE_LISTBOX_JUCEHEADER__ +/********* End of inlined file: juce_ListBox.h *********/ + +/********* Start of inlined file: juce_TextButton.h *********/ +#ifndef __JUCE_TEXTBUTTON_JUCEHEADER__ +#define __JUCE_TEXTBUTTON_JUCEHEADER__ + +/** + A button that uses the standard lozenge-shaped background with a line of + text on it. + + @see Button, DrawableButton +*/ +class JUCE_API TextButton : public Button +{ +public: + + /** Creates a TextButton. + + @param buttonName the text to put in the button (the component's name is also + initially set to this string, but these can be changed later + using the setName() and setButtonText() methods) + @param toolTip an optional string to use as a toolip + + @see Button + */ + TextButton (const String& buttonName, + const String& toolTip = String::empty); + + /** Destructor. */ + ~TextButton(); + + /** A set of colour IDs to use to change the colour of various aspects of the button. + + These constants can be used either via the Component::setColour(), or LookAndFeel::setColour() + methods. + + @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour + */ + enum ColourIds + { + buttonColourId = 0x1000100, /**< The colour used to fill the button shape (when the button is toggled + 'off'). The look-and-feel class might re-interpret this to add + effects, etc. */ + buttonOnColourId = 0x1000101, /**< The colour used to fill the button shape (when the button is toggled + 'on'). The look-and-feel class might re-interpret this to add + effects, etc. */ + textColourId = 0x1000102 /**< The colour to use for the button's text. */ + }; + + /** Resizes the button to fit neatly around its current text. + + If newHeight is >= 0, the button's height will be changed to this + value. If it's less than zero, its height will be unaffected. + */ + void changeWidthToFitText (const int newHeight = -1); + + /** This can be overridden to use different fonts than the default one. + + Note that you'll need to set the font's size appropriately, too. + */ + virtual const Font getFont(); + + juce_UseDebuggingNewOperator + +protected: + /** @internal */ + void paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown); + /** @internal */ + void colourChanged(); + +private: + TextButton (const TextButton&); + const TextButton& operator= (const TextButton&); +}; + +#endif // __JUCE_TEXTBUTTON_JUCEHEADER__ +/********* End of inlined file: juce_TextButton.h *********/ + +/** + A component displaying a list of plugins, with options to scan for them, + add, remove and sort them. +*/ +class JUCE_API PluginListComponent : public Component, + public ListBoxModel, + public ChangeListener, + public ButtonListener, + public Timer +{ +public: + + /** + Creates the list component. + + For info about the deadMansPedalFile, see the PluginDirectoryScanner constructor. + + The properties file, if supplied, is used to store the user's last search paths. + */ + PluginListComponent (KnownPluginList& listToRepresent, + const File& deadMansPedalFile, + PropertiesFile* const propertiesToUse); + + /** Destructor. */ + ~PluginListComponent(); + + /** @internal */ + void resized(); + /** @internal */ + bool isInterestedInFileDrag (const StringArray& files); + /** @internal */ + void filesDropped (const StringArray& files, int, int); + /** @internal */ + int getNumRows(); + /** @internal */ + void paintListBoxItem (int row, Graphics& g, int width, int height, bool rowIsSelected); + /** @internal */ + void deleteKeyPressed (int lastRowSelected); + /** @internal */ + void buttonClicked (Button* b); + /** @internal */ + void changeListenerCallback (void*); + /** @internal */ + void timerCallback(); + + juce_UseDebuggingNewOperator + +private: + KnownPluginList& list; + File deadMansPedalFile; + ListBox* listBox; + TextButton* optionsButton; + PropertiesFile* propertiesToUse; + int typeToScan; + + void scanFor (AudioPluginFormat* format); + + PluginListComponent (const PluginListComponent&); + const PluginListComponent& operator= (const PluginListComponent&); +}; + +#endif // __JUCE_PLUGINLISTCOMPONENT_JUCEHEADER__ +/********* End of inlined file: juce_PluginListComponent.h *********/ + +#endif +#ifndef __JUCE_AIFFAUDIOFORMAT_JUCEHEADER__ + +/********* Start of inlined file: juce_AiffAudioFormat.h *********/ +#ifndef __JUCE_AIFFAUDIOFORMAT_JUCEHEADER__ +#define __JUCE_AIFFAUDIOFORMAT_JUCEHEADER__ + +/********* Start of inlined file: juce_AudioFormat.h *********/ +#ifndef __JUCE_AUDIOFORMAT_JUCEHEADER__ +#define __JUCE_AUDIOFORMAT_JUCEHEADER__ + +/********* Start of inlined file: juce_AudioFormatWriter.h *********/ +#ifndef __JUCE_AUDIOFORMATWRITER_JUCEHEADER__ +#define __JUCE_AUDIOFORMATWRITER_JUCEHEADER__ + +/** + Writes samples to an audio file stream. + + A subclass that writes a specific type of audio format will be created by + an AudioFormat object. + + After creating one of these with the AudioFormat::createWriterFor() method + you can call its write() method to store the samples, and then delete it. + + @see AudioFormat, AudioFormatReader +*/ +class JUCE_API AudioFormatWriter +{ +protected: + + /** Creates an AudioFormatWriter object. + + @param destStream the stream to write to - this will be deleted + by this object when it is no longer needed + @param formatName the description that will be returned by the getFormatName() + method + @param sampleRate the sample rate to use - the base class just stores + this value, it doesn't do anything with it + @param numberOfChannels the number of channels to write - the base class just stores + this value, it doesn't do anything with it + @param bitsPerSample the bit depth of the stream - the base class just stores + this value, it doesn't do anything with it + */ + AudioFormatWriter (OutputStream* const destStream, + const String& formatName, + const double sampleRate, + const unsigned int numberOfChannels, + const unsigned int bitsPerSample); + +public: + /** Destructor. */ + virtual ~AudioFormatWriter(); + + /** Returns a description of what type of format this is. + + E.g. "AIFF file" + */ + const String getFormatName() const throw() { return formatName; } + + /** Writes a set of samples to the audio stream. + + Note that if you're trying to write the contents of an AudioSampleBuffer, you + can use AudioSampleBuffer::writeToAudioWriter(). + + @param samplesToWrite an array of arrays containing the sample data for + each channel to write. This is a zero-terminated + array of arrays, and can contain a different number + of channels than the actual stream uses, and the + writer should do its best to cope with this. + If the format is fixed-point, each channel will be formatted + as an array of signed integers using the full 32-bit + range -0x80000000 to 0x7fffffff, regardless of the source's + bit-depth. If it is a floating-point format, you should treat + the arrays as arrays of floats, and just cast it to an (int**) + to pass it into the method. + @param numSamples the number of samples to write + */ + virtual bool write (const int** samplesToWrite, + int numSamples) = 0; + + /** Reads a section of samples from an AudioFormatReader, and writes these to + the output. + + This will take care of any floating-point conversion that's required to convert + between the two formats. It won't deal with sample-rate conversion, though. + + @returns false if it can't read or write properly during the operation + */ + bool writeFromAudioReader (AudioFormatReader& reader, + int64 startSample, + int numSamplesToRead); + + /** Reads some samples from an AudioSource, and writes these to the output. + + The source must already have been initialised with the AudioSource::prepareToPlay() method + + @param source the source to read from + @param numSamplesToRead total number of samples to read and write + @param samplesPerBlock the maximum number of samples to fetch from the source + @returns false if it can't read or write properly during the operation + */ + bool writeFromAudioSource (AudioSource& source, + int numSamplesToRead, + const int samplesPerBlock = 2048); + + /** Returns the sample rate being used. */ + double getSampleRate() const throw() { return sampleRate; } + + /** Returns the number of channels being written. */ + int getNumChannels() const throw() { return numChannels; } + + /** Returns the bit-depth of the data being written. */ + int getBitsPerSample() const throw() { return bitsPerSample; } + + /** Returns true if it's a floating-point format, false if it's fixed-point. */ + bool isFloatingPoint() const throw() { return usesFloatingPointData; } + + juce_UseDebuggingNewOperator + +protected: + /** The sample rate of the stream. */ + double sampleRate; + + /** The number of channels being written to the stream. */ + unsigned int numChannels; + + /** The bit depth of the file. */ + unsigned int bitsPerSample; + + /** True if it's a floating-point format, false if it's fixed-point. */ + bool usesFloatingPointData; + + /** The output stream for Use by subclasses. */ + OutputStream* output; + +private: + String formatName; +}; + +#endif // __JUCE_AUDIOFORMATWRITER_JUCEHEADER__ +/********* End of inlined file: juce_AudioFormatWriter.h *********/ + +/** + Subclasses of AudioFormat are used to read and write different audio + file formats. + + @see AudioFormatReader, AudioFormatWriter, WavAudioFormat, AiffAudioFormat +*/ +class JUCE_API AudioFormat +{ +public: + + /** Destructor. */ + virtual ~AudioFormat(); + + /** Returns the name of this format. + + e.g. "WAV file" or "AIFF file" + */ + const String& getFormatName() const; + + /** Returns all the file extensions that might apply to a file of this format. + + The first item will be the one that's preferred when creating a new file. + + So for a wav file this might just return ".wav"; for an AIFF file it might + return two items, ".aif" and ".aiff" + */ + const StringArray& getFileExtensions() const; + + /** Returns true if this the given file can be read by this format. + + Subclasses shouldn't do too much work here, just check the extension or + file type. The base class implementation just checks the file's extension + against one of the ones that was registered in the constructor. + */ + virtual bool canHandleFile (const File& fileToTest); + + /** Returns a set of sample rates that the format can read and write. */ + virtual const Array getPossibleSampleRates() = 0; + + /** Returns a set of bit depths that the format can read and write. */ + virtual const Array getPossibleBitDepths() = 0; + + /** Returns true if the format can do 2-channel audio. */ + virtual bool canDoStereo() = 0; + + /** Returns true if the format can do 1-channel audio. */ + virtual bool canDoMono() = 0; + + /** Returns true if the format uses compressed data. */ + virtual bool isCompressed(); + + /** Returns a list of different qualities that can be used when writing. + + Non-compressed formats will just return an empty array, but for something + like Ogg-Vorbis or MP3, it might return a list of bit-rates, etc. + + When calling createWriterFor(), an index from this array is passed in to + tell the format which option is required. + */ + virtual const StringArray getQualityOptions(); + + /** Tries to create an object that can read from a stream containing audio + data in this format. + + The reader object that is returned can be used to read from the stream, and + should then be deleted by the caller. + + @param sourceStream the stream to read from - the AudioFormatReader object + that is returned will delete this stream when it no longer + needs it. + @param deleteStreamIfOpeningFails if no reader can be created, this determines whether this method + should delete the stream object that was passed-in. (If a valid + reader is returned, it will always be in charge of deleting the + stream, so this parameter is ignored) + @see AudioFormatReader + */ + virtual AudioFormatReader* createReaderFor (InputStream* sourceStream, + const bool deleteStreamIfOpeningFails) = 0; + + /** Tries to create an object that can write to a stream with this audio format. + + The writer object that is returned can be used to write to the stream, and + should then be deleted by the caller. + + If the stream can't be created for some reason (e.g. the parameters passed in + here aren't suitable), this will return 0. + + @param streamToWriteTo the stream that the data will go to - this will be + deleted by the AudioFormatWriter object when it's no longer + needed. If no AudioFormatWriter can be created by this method, + the stream will NOT be deleted, so that the caller can re-use it + to try to open a different format, etc + @param sampleRateToUse the sample rate for the file, which must be one of the ones + returned by getPossibleSampleRates() + @param numberOfChannels the number of channels - this must be either 1 or 2, and + the choice will depend on the results of canDoMono() and + canDoStereo() + @param bitsPerSample the bits per sample to use - this must be one of the values + returned by getPossibleBitDepths() + @param metadataValues a set of metadata values that the writer should try to write + to the stream. Exactly what these are depends on the format, + and the subclass doesn't actually have to do anything with + them if it doesn't want to. Have a look at the specific format + implementation classes to see possible values that can be + used + @param qualityOptionIndex the index of one of compression qualities returned by the + getQualityOptions() method. If there aren't any quality options + for this format, just pass 0 in this parameter, as it'll be + ignored + @see AudioFormatWriter + */ + virtual AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo, + double sampleRateToUse, + unsigned int numberOfChannels, + int bitsPerSample, + const StringPairArray& metadataValues, + int qualityOptionIndex) = 0; + +protected: + /** Creates an AudioFormat object. + + @param formatName this sets the value that will be returned by getFormatName() + @param fileExtensions a zero-terminated list of file extensions - this is what will + be returned by getFileExtension() + */ + AudioFormat (const String& formatName, + const tchar** const fileExtensions); + +private: + + String formatName; + StringArray fileExtensions; +}; + +#endif // __JUCE_AUDIOFORMAT_JUCEHEADER__ +/********* End of inlined file: juce_AudioFormat.h *********/ + +/** + Reads and Writes AIFF format audio files. + + @see AudioFormat +*/ +class JUCE_API AiffAudioFormat : public AudioFormat +{ +public: + + /** Creates an format object. */ + AiffAudioFormat(); + + /** Destructor. */ + ~AiffAudioFormat(); + + const Array getPossibleSampleRates(); + const Array getPossibleBitDepths(); + bool canDoStereo(); + bool canDoMono(); +#if JUCE_MAC + bool canHandleFile (const File& fileToTest); +#endif + + AudioFormatReader* createReaderFor (InputStream* sourceStream, + const bool deleteStreamIfOpeningFails); + + AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo, + double sampleRateToUse, + unsigned int numberOfChannels, + int bitsPerSample, + const StringPairArray& metadataValues, + int qualityOptionIndex); + + juce_UseDebuggingNewOperator +}; + +#endif // __JUCE_AIFFAUDIOFORMAT_JUCEHEADER__ +/********* End of inlined file: juce_AiffAudioFormat.h *********/ + +#endif +#ifndef __JUCE_AUDIOCDBURNER_JUCEHEADER__ + +/********* Start of inlined file: juce_AudioCDBurner.h *********/ +#ifndef __JUCE_AUDIOCDBURNER_JUCEHEADER__ +#define __JUCE_AUDIOCDBURNER_JUCEHEADER__ + +/** +*/ +class AudioCDBurner +{ +public: + + /** Returns a list of available optical drives. + + Use openDevice() to open one of the items from this list. + */ + static const StringArray findAvailableDevices(); + + /** Tries to open one of the optical drives. + + The deviceIndex is an index into the array returned by findAvailableDevices(). + */ + static AudioCDBurner* openDevice (const int deviceIndex); + + /** Destructor. */ + ~AudioCDBurner(); + + /** Returns true if there's a writable disk in the drive. + */ + bool isDiskPresent() const; + + /** Returns the number of free blocks on the disk. + + There are 75 blocks per second, at 44100Hz. + */ + int getNumAvailableAudioBlocks() const; + + /** Adds a track to be written. + + The source passed-in here will be kept by this object, and it will + be used and deleted at some point in the future, either during the + burn() method or when this AudioCDBurner object is deleted. Your caller + method shouldn't keep a reference to it or use it again after passing + it in here. + */ + bool addAudioTrack (AudioSource* source, int numSamples); + + /** + + Return true to cancel the current burn operation + */ + class BurnProgressListener + { + public: + BurnProgressListener() throw() {} + virtual ~BurnProgressListener() {} + + /** Called at intervals to report on the progress of the AudioCDBurner. + + To cancel the burn, return true from this. + */ + virtual bool audioCDBurnProgress (float proportionComplete) = 0; + }; + + const String burn (BurnProgressListener* listener, + const bool ejectDiscAfterwards, + const bool peformFakeBurnForTesting); + + juce_UseDebuggingNewOperator + +private: + AudioCDBurner (const int deviceIndex); + + void* internal; +}; + +#endif // __JUCE_AUDIOCDBURNER_JUCEHEADER__ +/********* End of inlined file: juce_AudioCDBurner.h *********/ + +#endif +#ifndef __JUCE_AUDIOCDREADER_JUCEHEADER__ + +/********* Start of inlined file: juce_AudioCDReader.h *********/ +#ifndef __JUCE_AUDIOCDREADER_JUCEHEADER__ +#define __JUCE_AUDIOCDREADER_JUCEHEADER__ + +#if JUCE_MAC + +#endif + +/** + A type of AudioFormatReader that reads from an audio CD. + + One of these can be used to read a CD as if it's one big audio stream. Use the + getPositionOfTrackStart() method to find where the individual tracks are + within the stream. + + @see AudioFormatReader +*/ +class JUCE_API AudioCDReader : public AudioFormatReader +{ +public: + + /** Returns a list of names of Audio CDs currently available for reading. + + If there's a CD drive but no CD in it, this might return an empty list, or + possibly a device that can be opened but which has no tracks, depending + on the platform. + + @see createReaderForCD + */ + static const StringArray getAvailableCDNames(); + + /** Tries to create an AudioFormatReader that can read from an Audio CD. + + @param index the index of one of the available CDs - use getAvailableCDNames() + to find out how many there are. + @returns a new AudioCDReader object, or 0 if it couldn't be created. The + caller will be responsible for deleting the object returned. + */ + static AudioCDReader* createReaderForCD (const int index); + + /** Destructor. */ + ~AudioCDReader(); + + /** Implementation of the AudioFormatReader method. */ + bool read (int** destSamples, + int64 startSampleInFile, + int numSamples); + + /** Checks whether the CD has been removed from the drive. + */ + bool isCDStillPresent() const; + + /** Returns the total number of tracks (audio + data). + */ + int getNumTracks() const; + + /** Finds the sample offset of the start of a track. + + @param trackNum the track number, where 0 is the first track. + */ + int getPositionOfTrackStart (int trackNum) const; + + /** Returns true if a given track is an audio track. + + @param trackNum the track number, where 0 is the first track. + */ + bool isTrackAudio (int trackNum) const; + + /** Refreshes the object's table of contents. + + If the disc has been ejected and a different one put in since this + object was created, this will cause it to update its idea of how many tracks + there are, etc. + */ + void refreshTrackLengths(); + + /** Enables scanning for indexes within tracks. + + @see getLastIndex + */ + void enableIndexScanning (bool enabled); + + /** Returns the index number found during the last read() call. + + Index scanning is turned off by default - turn it on with enableIndexScanning(). + + Then when the read() method is called, if it comes across an index within that + block, the index number is stored and returned by this method. + + Some devices might not support indexes, of course. + + (If you don't know what CD indexes are, it's unlikely you'll ever need them). + + @see enableIndexScanning + */ + int getLastIndex() const; + + /** Scans a track to find the position of any indexes within it. + + @param trackNumber the track to look in, where 0 is the first track on the disc + @returns an array of sample positions of any index points found (not including + the index that marks the start of the track) + */ + const Array findIndexesInTrack (const int trackNumber); + + /** Returns the CDDB id number for the CD. + + It's not a great way of identifying a disc, but it's traditional. + */ + int getCDDBId(); + + /** Tries to eject the disk. + + Of course this might not be possible, if some other process is using it. + */ + void ejectDisk(); + + juce_UseDebuggingNewOperator + +private: + +#if JUCE_MAC + File volumeDir; + OwnedArray tracks; + Array trackStartSamples; + int currentReaderTrack; + AudioFormatReader* reader; + AudioCDReader (const File& volume); +public: + static int compareElements (const File* const, const File* const) throw(); +private: + +#elif JUCE_WIN32 + int numTracks; + int trackStarts[100]; + bool audioTracks [100]; + void* handle; + bool indexingEnabled; + int lastIndex, firstFrameInBuffer, samplesInBuffer; + MemoryBlock buffer; + AudioCDReader (void* handle); + int getIndexAt (int samplePos); + +#elif JUCE_LINUX + AudioCDReader(); +#endif + + AudioCDReader (const AudioCDReader&); + const AudioCDReader& operator= (const AudioCDReader&); +}; + +#endif // __JUCE_AUDIOCDREADER_JUCEHEADER__ +/********* End of inlined file: juce_AudioCDReader.h *********/ + +#endif +#ifndef __JUCE_AUDIOFORMAT_JUCEHEADER__ + +#endif +#ifndef __JUCE_AUDIOFORMATMANAGER_JUCEHEADER__ + +/********* Start of inlined file: juce_AudioFormatManager.h *********/ +#ifndef __JUCE_AUDIOFORMATMANAGER_JUCEHEADER__ +#define __JUCE_AUDIOFORMATMANAGER_JUCEHEADER__ + +/** + A class for keeping a list of available audio formats, and for deciding which + one to use to open a given file. + + You can either use this class as a singleton object, or create instances of it + yourself. Once created, use its registerFormat() method to tell it which + formats it should use. + + @see AudioFormat +*/ +class JUCE_API AudioFormatManager +{ +public: + + /** Creates an empty format manager. + + Before it'll be any use, you'll need to call registerFormat() with all the + formats you want it to be able to recognise. + */ + AudioFormatManager(); + + /** Destructor. */ + ~AudioFormatManager(); + + juce_DeclareSingleton (AudioFormatManager, false); + + /** Adds a format to the manager's list of available file types. + + The object passed-in will be deleted by this object, so don't keep a pointer + to it! + + If makeThisTheDefaultFormat is true, then the getDefaultFormat() method will + return this one when called. + */ + void registerFormat (AudioFormat* newFormat, + const bool makeThisTheDefaultFormat); + + /** Handy method to make it easy to register the formats that come with Juce. + + Currently, this will add WAV and AIFF to the list. + */ + void registerBasicFormats(); + + /** Clears the list of known formats. */ + void clearFormats(); + + /** Returns the number of currently registered file formats. */ + int getNumKnownFormats() const; + + /** Returns one of the registered file formats. */ + AudioFormat* getKnownFormat (const int index) const; + + /** Looks for which of the known formats is listed as being for a given file + extension. + + The extension may have a dot before it, so e.g. ".wav" or "wav" are both ok. + */ + AudioFormat* findFormatForFileExtension (const String& fileExtension) const; + + /** Returns the format which has been set as the default one. + + You can set a format as being the default when it is registered. It's useful + when you want to write to a file, because the best format may change between + platforms, e.g. AIFF is preferred on the Mac, WAV on Windows. + + If none has been set as the default, this method will just return the first + one in the list. + */ + AudioFormat* getDefaultFormat() const; + + /** Returns a set of wildcards for file-matching that contains the extensions for + all known formats. + + E.g. if might return "*.wav;*.aiff" if it just knows about wavs and aiffs. + */ + const String getWildcardForAllFormats() const; + + /** Searches through the known formats to try to create a suitable reader for + this file. + + If none of the registered formats can open the file, it'll return 0. If it + returns a reader, it's the caller's responsibility to delete the reader. + */ + AudioFormatReader* createReaderFor (const File& audioFile); + + /** Searches through the known formats to try to create a suitable reader for + this stream. + + The stream object that is passed-in will be deleted by this method or by the + reader that is returned, so the caller should not keep any references to it. + + The stream that is passed-in must be capable of being repositioned so + that all the formats can have a go at opening it. + + If none of the registered formats can open the stream, it'll return 0. If it + returns a reader, it's the caller's responsibility to delete the reader. + */ + AudioFormatReader* createReaderFor (InputStream* audioFileStream); + + juce_UseDebuggingNewOperator + +private: + VoidArray knownFormats; + int defaultFormatIndex; +}; + +#endif // __JUCE_AUDIOFORMATMANAGER_JUCEHEADER__ +/********* End of inlined file: juce_AudioFormatManager.h *********/ + +#endif +#ifndef __JUCE_AUDIOFORMATREADER_JUCEHEADER__ + +#endif +#ifndef __JUCE_AUDIOFORMATWRITER_JUCEHEADER__ + +#endif +#ifndef __JUCE_AUDIOSUBSECTIONREADER_JUCEHEADER__ + +/********* Start of inlined file: juce_AudioSubsectionReader.h *********/ +#ifndef __JUCE_AUDIOSUBSECTIONREADER_JUCEHEADER__ +#define __JUCE_AUDIOSUBSECTIONREADER_JUCEHEADER__ + +/** + This class is used to wrap an AudioFormatReader and only read from a + subsection of the file. + + So if you have a reader which can read a 1000 sample file, you could wrap it + in one of these to only access, e.g. samples 100 to 200, and any samples + outside that will come back as 0. Accessing sample 0 from this reader will + actually read the first sample from the other's subsection, which might + be at a non-zero position. + + @see AudioFormatReader +*/ +class JUCE_API AudioSubsectionReader : public AudioFormatReader +{ +public: + + /** Creates a AudioSubsectionReader for a given data source. + + @param sourceReader the source reader from which we'll be taking data + @param subsectionStartSample the sample within the source reader which will be + mapped onto sample 0 for this reader. + @param subsectionLength the number of samples from the source that will + make up the subsection. If this reader is asked for + any samples beyond this region, it will return zero. + @param deleteSourceWhenDeleted if true, the sourceReader object will be deleted when + this object is deleted. + */ + AudioSubsectionReader (AudioFormatReader* const sourceReader, + const int64 subsectionStartSample, + const int64 subsectionLength, + const bool deleteSourceWhenDeleted); + + /** Destructor. */ + ~AudioSubsectionReader(); + + bool read (int** destSamples, + int64 startSample, + int numSamples); + + void readMaxLevels (int64 startSample, + int64 numSamples, + float& lowestLeft, + float& highestLeft, + float& lowestRight, + float& highestRight); + + juce_UseDebuggingNewOperator + +private: + AudioFormatReader* const source; + int64 startSample, length; + const bool deleteSourceWhenDeleted; + + AudioSubsectionReader (const AudioSubsectionReader&); + const AudioSubsectionReader& operator= (const AudioSubsectionReader&); +}; + +#endif // __JUCE_AUDIOSUBSECTIONREADER_JUCEHEADER__ +/********* End of inlined file: juce_AudioSubsectionReader.h *********/ + +#endif +#ifndef __JUCE_AUDIOTHUMBNAIL_JUCEHEADER__ + +/********* Start of inlined file: juce_AudioThumbnail.h *********/ +#ifndef __JUCE_AUDIOTHUMBNAIL_JUCEHEADER__ +#define __JUCE_AUDIOTHUMBNAIL_JUCEHEADER__ + +class AudioThumbnailCache; + +/** + Makes it easy to quickly draw scaled views of the waveform shape of an + audio file. + + To use this class, just create an AudioThumbNail class for the file you want + to draw, call setSource to tell it which file or resource to use, then call + drawChannel() to draw it. + + The class will asynchronously scan the wavefile to create its scaled-down view, + so you should make your UI repaint itself as this data comes in. To do this, the + AudioThumbnail is a ChangeBroadcaster, and will broadcast a message when its + listeners should repaint themselves. + + The thumbnail stores an internal low-res version of the wave data, and this can + be loaded and saved to avoid having to scan the file again. + + @see AudioThumbnailCache +*/ +class JUCE_API AudioThumbnail : public ChangeBroadcaster, + public TimeSliceClient, + private Timer +{ +public: + + /** Creates an audio thumbnail. + + @param sourceSamplesPerThumbnailSample when creating a stored, low-res version + of the audio data, this is the scale at which it should be done + @param formatManagerToUse the audio format manager that is used to open the file + @param cacheToUse an instance of an AudioThumbnailCache - this provides a background + thread and storage that is used to by the thumbnail, and the cache + object can be shared between multiple thumbnails + */ + AudioThumbnail (const int sourceSamplesPerThumbnailSample, + AudioFormatManager& formatManagerToUse, + AudioThumbnailCache& cacheToUse); + + /** Destructor. */ + ~AudioThumbnail(); + + /** Specifies the file or stream that contains the audio file. + + For a file, just call + @code + setSource (new FileInputSource (file)) + @endcode + + You can pass a zero in here to clear the thumbnail. + + The source that is passed in will be deleted by this object when it is no + longer needed + */ + void setSource (InputSource* const newSource); + + /** Reloads the low res thumbnail data from an input stream. + + The thumb will automatically attempt to reload itself from its + AudioThumbnailCache. + */ + void loadFrom (InputStream& input); + + /** Saves the low res thumbnail data to an output stream. + + The thumb will automatically attempt to save itself to its + AudioThumbnailCache after it finishes scanning the wave file. + */ + void saveTo (OutputStream& output) const; + + /** Returns the number of channels in the file. + */ + int getNumChannels() const throw(); + + /** Returns the length of the audio file. + */ + double getTotalLength() const throw(); + + /** Renders the waveform shape for a channel. + + The waveform will be drawn within the specified rectangle, where startTime + and endTime specify the times within the audio file that should be positioned + at the left and right edges of the rectangle. + + The waveform will be scaled vertically so that a full-volume sample will fill + the rectangle vertically, but you can also specify an extra vertical scale factor + with the verticalZoomFactor parameter. + */ + void drawChannel (Graphics& g, + int x, int y, int w, int h, + double startTime, + double endTime, + int channelNum, + const float verticalZoomFactor); + + /** Returns true if the low res preview is fully generated. + */ + bool isFullyLoaded() const throw(); + + /** @internal */ + bool useTimeSlice(); + /** @internal */ + void timerCallback(); + + juce_UseDebuggingNewOperator + +private: + AudioFormatManager& formatManagerToUse; + AudioThumbnailCache& cache; + InputSource* source; + + CriticalSection readerLock; + AudioFormatReader* reader; + + MemoryBlock data, cachedLevels; + int orginalSamplesPerThumbnailSample; + + int numChannelsCached, numSamplesCached; + double cachedStart, cachedTimePerPixel; + bool cacheNeedsRefilling; + + void clear(); + + AudioFormatReader* createReader() const; + + void generateSection (AudioFormatReader& reader, + int64 startSample, + int numSamples); + + char* getChannelData (int channel) const; + + void refillCache (const int numSamples, + double startTime, + const double timePerPixel); + + friend class AudioThumbnailCache; + + // true if it needs more callbacks from the readNextBlockFromAudioFile() method + bool initialiseFromAudioFile (AudioFormatReader& reader); + + // returns true if more needs to be read + bool readNextBlockFromAudioFile (AudioFormatReader& reader); +}; + +#endif // __JUCE_AUDIOTHUMBNAIL_JUCEHEADER__ +/********* End of inlined file: juce_AudioThumbnail.h *********/ + +#endif +#ifndef __JUCE_AUDIOTHUMBNAILCACHE_JUCEHEADER__ + +/********* Start of inlined file: juce_AudioThumbnailCache.h *********/ +#ifndef __JUCE_AUDIOTHUMBNAILCACHE_JUCEHEADER__ +#define __JUCE_AUDIOTHUMBNAILCACHE_JUCEHEADER__ + +struct ThumbnailCacheEntry; + +/** + An instance of this class is used to manage multiple AudioThumbnail objects. + + The cache runs a single background thread that is shared by all the thumbnails + that need it, and it maintains a set of low-res previews in memory, to avoid + having to re-scan audio files too often. + + @see AudioThumbnail +*/ +class JUCE_API AudioThumbnailCache : public TimeSliceThread +{ +public: + + /** Creates a cache object. + + The maxNumThumbsToStore parameter lets you specify how many previews should + be kept in memory at once. + */ + AudioThumbnailCache (const int maxNumThumbsToStore); + + /** Destructor. */ + ~AudioThumbnailCache(); + + /** Clears out any stored thumbnails. + */ + void clear(); + + /** Reloads the specified thumb if this cache contains the appropriate stored + data. + + This is called automatically by the AudioThumbnail class, so you shouldn't + normally need to call it directly. + */ + bool loadThumb (AudioThumbnail& thumb, const int64 hashCode); + + /** Stores the cachable data from the specified thumb in this cache. + + This is called automatically by the AudioThumbnail class, so you shouldn't + normally need to call it directly. + */ + void storeThumb (const AudioThumbnail& thumb, const int64 hashCode); + + juce_UseDebuggingNewOperator + +private: + + OwnedArray thumbs; + int maxNumThumbsToStore; + + friend class AudioThumbnail; + void addThumbnail (AudioThumbnail* const thumb); + void removeThumbnail (AudioThumbnail* const thumb); +}; + +#endif // __JUCE_AUDIOTHUMBNAILCACHE_JUCEHEADER__ +/********* End of inlined file: juce_AudioThumbnailCache.h *********/ + +#endif +#ifndef __JUCE_FLACAUDIOFORMAT_JUCEHEADER__ + +/********* Start of inlined file: juce_FlacAudioFormat.h *********/ +#ifndef __JUCE_FLACAUDIOFORMAT_JUCEHEADER__ +#define __JUCE_FLACAUDIOFORMAT_JUCEHEADER__ + +#if JUCE_USE_FLAC || defined (DOXYGEN) + +/** + Reads and writes the lossless-compression FLAC audio format. + + To compile this, you'll need to set the JUCE_USE_FLAC flag in juce_Config.h, + and make sure your include search path and library search path are set up to find + the FLAC header files and static libraries. + + @see AudioFormat +*/ +class JUCE_API FlacAudioFormat : public AudioFormat +{ +public: + + FlacAudioFormat(); + ~FlacAudioFormat(); + + const Array getPossibleSampleRates(); + const Array getPossibleBitDepths(); + bool canDoStereo(); + bool canDoMono(); + bool isCompressed(); + + AudioFormatReader* createReaderFor (InputStream* sourceStream, + const bool deleteStreamIfOpeningFails); + + AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo, + double sampleRateToUse, + unsigned int numberOfChannels, + int bitsPerSample, + const StringPairArray& metadataValues, + int qualityOptionIndex); + + juce_UseDebuggingNewOperator +}; + +#endif +#endif // __JUCE_FLACAUDIOFORMAT_JUCEHEADER__ +/********* End of inlined file: juce_FlacAudioFormat.h *********/ + +#endif +#ifndef __JUCE_OGGVORBISAUDIOFORMAT_JUCEHEADER__ + +/********* Start of inlined file: juce_OggVorbisAudioFormat.h *********/ +#ifndef __JUCE_OGGVORBISAUDIOFORMAT_JUCEHEADER__ +#define __JUCE_OGGVORBISAUDIOFORMAT_JUCEHEADER__ + +#if JUCE_USE_OGGVORBIS || defined (DOXYGEN) + +/** + Reads and writes the Ogg-Vorbis audio format. + + To compile this, you'll need to set the JUCE_USE_OGGVORBIS flag in juce_Config.h, + and make sure your include search path and library search path are set up to find + the Vorbis and Ogg header files and static libraries. + + @see AudioFormat, +*/ +class JUCE_API OggVorbisAudioFormat : public AudioFormat +{ +public: + + OggVorbisAudioFormat(); + ~OggVorbisAudioFormat(); + + const Array getPossibleSampleRates(); + const Array getPossibleBitDepths(); + bool canDoStereo(); + bool canDoMono(); + bool isCompressed(); + const StringArray getQualityOptions(); + + /** Tries to estimate the quality level of an ogg file based on its size. + + If it can't read the file for some reason, this will just return 1 (medium quality), + otherwise it will return the approximate quality setting that would have been used + to create the file. + + @see getQualityOptions + */ + int estimateOggFileQuality (const File& source); + + AudioFormatReader* createReaderFor (InputStream* sourceStream, + const bool deleteStreamIfOpeningFails); + + AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo, + double sampleRateToUse, + unsigned int numberOfChannels, + int bitsPerSample, + const StringPairArray& metadataValues, + int qualityOptionIndex); + + juce_UseDebuggingNewOperator +}; + +#endif +#endif // __JUCE_OGGVORBISAUDIOFORMAT_JUCEHEADER__ +/********* End of inlined file: juce_OggVorbisAudioFormat.h *********/ + +#endif +#ifndef __JUCE_QUICKTIMEAUDIOFORMAT_JUCEHEADER__ + +/********* Start of inlined file: juce_QuickTimeAudioFormat.h *********/ +#ifndef __JUCE_QUICKTIMEAUDIOFORMAT_JUCEHEADER__ +#define __JUCE_QUICKTIMEAUDIOFORMAT_JUCEHEADER__ + +#if JUCE_QUICKTIME + +/** + Uses QuickTime to read the audio track a movie or media file. + + As well as QuickTime movies, this should also manage to open other audio + files that quicktime can understand, like mp3, m4a, etc. + + @see AudioFormat +*/ +class JUCE_API QuickTimeAudioFormat : public AudioFormat +{ +public: + + /** Creates a format object. */ + QuickTimeAudioFormat(); + + /** Destructor. */ + ~QuickTimeAudioFormat(); + + const Array getPossibleSampleRates(); + const Array getPossibleBitDepths(); + bool canDoStereo(); + bool canDoMono(); + + AudioFormatReader* createReaderFor (InputStream* sourceStream, + const bool deleteStreamIfOpeningFails); + + AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo, + double sampleRateToUse, + unsigned int numberOfChannels, + int bitsPerSample, + const StringPairArray& metadataValues, + int qualityOptionIndex); + + juce_UseDebuggingNewOperator +}; + +#endif +#endif // __JUCE_QUICKTIMEAUDIOFORMAT_JUCEHEADER__ +/********* End of inlined file: juce_QuickTimeAudioFormat.h *********/ + +#endif +#ifndef __JUCE_WAVAUDIOFORMAT_JUCEHEADER__ + +/********* Start of inlined file: juce_WavAudioFormat.h *********/ +#ifndef __JUCE_WAVAUDIOFORMAT_JUCEHEADER__ +#define __JUCE_WAVAUDIOFORMAT_JUCEHEADER__ + +/** + Reads and Writes WAV format audio files. + + @see AudioFormat +*/ +class JUCE_API WavAudioFormat : public AudioFormat +{ +public: + + /** Creates a format object. */ + WavAudioFormat(); + + /** Destructor. */ + ~WavAudioFormat(); + + /** Metadata property name used by wav readers and writers for adding + a BWAV chunk to the file. + + @see AudioFormatReader::metadataValues, createWriterFor + */ + static const tchar* const bwavDescription; + + /** Metadata property name used by wav readers and writers for adding + a BWAV chunk to the file. + + @see AudioFormatReader::metadataValues, createWriterFor + */ + static const tchar* const bwavOriginator; + + /** Metadata property name used by wav readers and writers for adding + a BWAV chunk to the file. + + @see AudioFormatReader::metadataValues, createWriterFor + */ + static const tchar* const bwavOriginatorRef; + + /** Metadata property name used by wav readers and writers for adding + a BWAV chunk to the file. + + Date format is: yyyy-mm-dd + + @see AudioFormatReader::metadataValues, createWriterFor + */ + static const tchar* const bwavOriginationDate; + + /** Metadata property name used by wav readers and writers for adding + a BWAV chunk to the file. + + Time format is: hh-mm-ss + + @see AudioFormatReader::metadataValues, createWriterFor + */ + static const tchar* const bwavOriginationTime; + + /** Metadata property name used by wav readers and writers for adding + a BWAV chunk to the file. + + This is the number of samples from the start of an edit that the + file is supposed to begin at. Seems like an obvious mistake to + only allow a file to occur in an edit once, but that's the way + it is.. + + @see AudioFormatReader::metadataValues, createWriterFor + */ + static const tchar* const bwavTimeReference; + + /** Metadata property name used by wav readers and writers for adding + a BWAV chunk to the file. + + This is a + + @see AudioFormatReader::metadataValues, createWriterFor + */ + static const tchar* const bwavCodingHistory; + + /** Utility function to fill out the appropriate metadata for a BWAV file. + + This just makes it easier than using the property names directly, and it + fills out the time and date in the right format. + */ + static const StringPairArray createBWAVMetadata (const String& description, + const String& originator, + const String& originatorRef, + const Time& dateAndTime, + const int64 timeReferenceSamples, + const String& codingHistory); + + const Array getPossibleSampleRates(); + const Array getPossibleBitDepths(); + bool canDoStereo(); + bool canDoMono(); + + AudioFormatReader* createReaderFor (InputStream* sourceStream, + const bool deleteStreamIfOpeningFails); + + AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo, + double sampleRateToUse, + unsigned int numberOfChannels, + int bitsPerSample, + const StringPairArray& metadataValues, + int qualityOptionIndex); + + juce_UseDebuggingNewOperator +}; + +#endif // __JUCE_WAVAUDIOFORMAT_JUCEHEADER__ +/********* End of inlined file: juce_WavAudioFormat.h *********/ + +#endif +#ifndef __JUCE_ACTIONBROADCASTER_JUCEHEADER__ + +/********* Start of inlined file: juce_ActionBroadcaster.h *********/ +#ifndef __JUCE_ACTIONBROADCASTER_JUCEHEADER__ +#define __JUCE_ACTIONBROADCASTER_JUCEHEADER__ + +/********* Start of inlined file: juce_ActionListenerList.h *********/ +#ifndef __JUCE_ACTIONLISTENERLIST_JUCEHEADER__ +#define __JUCE_ACTIONLISTENERLIST_JUCEHEADER__ + +/** + A set of ActionListeners. + + Listeners can be added and removed from the list, and messages can be + broadcast to all the listeners. + + @see ActionListener, ActionBroadcaster +*/ +class JUCE_API ActionListenerList : public MessageListener +{ +public: + + /** Creates an empty list. */ + ActionListenerList() throw(); + + /** Destructor. */ + ~ActionListenerList() throw(); + + /** Adds a listener to the list. + + (Trying to add a listener that's already on the list will have no effect). + */ + void addActionListener (ActionListener* const listener) throw(); + + /** Removes a listener from the list. + + If the listener isn't on the list, this won't have any effect. + */ + void removeActionListener (ActionListener* const listener) throw(); + + /** Removes all listeners from the list. */ + void removeAllActionListeners() throw(); + + /** Broadcasts a message to all the registered listeners. + + This sends the message asynchronously. + + If a listener is on the list when this method is called but is removed from + the list before the message arrives, it won't receive the message. Similarly + listeners that are added to the list after the message is sent but before it + arrives won't get the message either. + */ + void sendActionMessage (const String& message) const; + + /** @internal */ + void handleMessage (const Message&); + + juce_UseDebuggingNewOperator + +private: + SortedSet actionListeners_; + CriticalSection actionListenerLock_; + + ActionListenerList (const ActionListenerList&); + const ActionListenerList& operator= (const ActionListenerList&); +}; + +#endif // __JUCE_ACTIONLISTENERLIST_JUCEHEADER__ +/********* End of inlined file: juce_ActionListenerList.h *********/ + +/** Manages a list of ActionListeners, and can send them messages. + + To quickly add methods to your class that can add/remove action + listeners and broadcast to them, you can derive from this. + + @see ActionListenerList, ActionListener +*/ +class JUCE_API ActionBroadcaster +{ +public: + + /** Creates an ActionBroadcaster. */ + ActionBroadcaster() throw(); + + /** Destructor. */ + virtual ~ActionBroadcaster(); + + /** Adds a listener to the list. + + (Trying to add a listener that's already on the list will have no effect). + */ + void addActionListener (ActionListener* const listener); + + /** Removes a listener from the list. + + If the listener isn't on the list, this won't have any effect. + */ + void removeActionListener (ActionListener* const listener); + + /** Removes all listeners from the list. */ + void removeAllActionListeners(); + + /** Broadcasts a message to all the registered listeners. + + @see ActionListenerList::sendActionMessage + */ + void sendActionMessage (const String& message) const; + +private: + + ActionListenerList actionListenerList; + + ActionBroadcaster (const ActionBroadcaster&); + const ActionBroadcaster& operator= (const ActionBroadcaster&); +}; + +#endif // __JUCE_ACTIONBROADCASTER_JUCEHEADER__ +/********* End of inlined file: juce_ActionBroadcaster.h *********/ + +#endif +#ifndef __JUCE_ACTIONLISTENER_JUCEHEADER__ + +#endif +#ifndef __JUCE_ACTIONLISTENERLIST_JUCEHEADER__ + +#endif +#ifndef __JUCE_ASYNCUPDATER_JUCEHEADER__ + +#endif +#ifndef __JUCE_CHANGEBROADCASTER_JUCEHEADER__ + +#endif +#ifndef __JUCE_CHANGELISTENER_JUCEHEADER__ + +#endif +#ifndef __JUCE_CHANGELISTENERLIST_JUCEHEADER__ + +#endif +#ifndef __JUCE_INTERPROCESSCONNECTION_JUCEHEADER__ + +/********* Start of inlined file: juce_InterprocessConnection.h *********/ +#ifndef __JUCE_INTERPROCESSCONNECTION_JUCEHEADER__ +#define __JUCE_INTERPROCESSCONNECTION_JUCEHEADER__ + +class InterprocessConnectionServer; + +/** + Manages a simple two-way messaging connection to another process, using either + a socket or a named pipe as the transport medium. + + To connect to a waiting socket or an open pipe, use the connectToSocket() or + connectToPipe() methods. If this succeeds, messages can be sent to the other end, + and incoming messages will result in a callback via the messageReceived() + method. + + To open a pipe and wait for another client to connect to it, use the createPipe() + method. + + To act as a socket server and create connections for one or more client, see the + InterprocessConnectionServer class. + + @see InterprocessConnectionServer, Socket, NamedPipe +*/ +class JUCE_API InterprocessConnection : public Thread, + private MessageListener +{ +public: + + /** Creates a connection. + + Connections are created manually, connecting them with the connectToSocket() + or connectToPipe() methods, or they are created automatically by a InterprocessConnectionServer + when a client wants to connect. + + @param callbacksOnMessageThread if true, callbacks to the connectionMade(), + connectionLost() and messageReceived() methods will + always be made using the message thread; if false, + these will be called immediately on the connection's + own thread. + @param magicMessageHeaderNumber a magic number to use in the header to check the + validity of the data blocks being sent and received. This + can be any number, but the sender and receiver must obviously + use matching values or they won't recognise each other. + */ + InterprocessConnection (const bool callbacksOnMessageThread = true, + const uint32 magicMessageHeaderNumber = 0xf2b49e2c); + + /** Destructor. */ + ~InterprocessConnection(); + + /** Tries to connect this object to a socket. + + For this to work, the machine on the other end needs to have a InterprocessConnectionServer + object waiting to receive client connections on this port number. + + @param hostName the host computer, either a network address or name + @param portNumber the socket port number to try to connect to + @param timeOutMillisecs how long to keep trying before giving up + @returns true if the connection is established successfully + @see Socket + */ + bool connectToSocket (const String& hostName, + const int portNumber, + const int timeOutMillisecs); + + /** Tries to connect the object to an existing named pipe. + + For this to work, another process on the same computer must already have opened + an InterprocessConnection object and used createPipe() to create a pipe for this + to connect to. + + You can optionally specify a timeout length to be passed to the NamedPipe::read() method. + + @returns true if it connects successfully. + @see createPipe, NamedPipe + */ + bool connectToPipe (const String& pipeName, + const int pipeReceiveMessageTimeoutMs = -1); + + /** Tries to create a new pipe for other processes to connect to. + + This creates a pipe with the given name, so that other processes can use + connectToPipe() to connect to the other end. + + You can optionally specify a timeout length to be passed to the NamedPipe::read() method. + + If another process is already using this pipe, this will fail and return false. + */ + bool createPipe (const String& pipeName, + const int pipeReceiveMessageTimeoutMs = -1); + + /** Disconnects and closes any currently-open sockets or pipes. */ + void disconnect(); + + /** True if a socket or pipe is currently active. */ + bool isConnected() const; + + /** Returns the socket that this connection is using (or null if it uses a pipe). */ + StreamingSocket* getSocket() const throw() { return socket; } + + /** Returns the pipe that this connection is using (or null if it uses a socket). */ + NamedPipe* getPipe() const throw() { return pipe; } + + /** Returns the name of the machine at the other end of this connection. + + This will return an empty string if the other machine isn't known for + some reason. + */ + const String getConnectedHostName() const; + + /** Tries to send a message to the other end of this connection. + + This will fail if it's not connected, or if there's some kind of write error. If + it succeeds, the connection object at the other end will receive the message by + a callback to its messageReceived() method. + + @see messageReceived + */ + bool sendMessage (const MemoryBlock& message); + + /** Called when the connection is first connected. + + If the connection was created with the callbacksOnMessageThread flag set, then + this will be called on the message thread; otherwise it will be called on a server + thread. + */ + virtual void connectionMade() = 0; + + /** Called when the connection is broken. + + If the connection was created with the callbacksOnMessageThread flag set, then + this will be called on the message thread; otherwise it will be called on a server + thread. + */ + virtual void connectionLost() = 0; + + /** Called when a message arrives. + + When the object at the other end of this connection sends us a message with sendMessage(), + this callback is used to deliver it to us. + + If the connection was created with the callbacksOnMessageThread flag set, then + this will be called on the message thread; otherwise it will be called on a server + thread. + + @see sendMessage + */ + virtual void messageReceived (const MemoryBlock& message) = 0; + + juce_UseDebuggingNewOperator + +private: + CriticalSection pipeAndSocketLock; + StreamingSocket* socket; + NamedPipe* pipe; + bool callbackConnectionState; + const bool useMessageThread; + const uint32 magicMessageHeader; + int pipeReceiveMessageTimeout; + + friend class InterprocessConnectionServer; + + void initialiseWithSocket (StreamingSocket* const socket_); + void initialiseWithPipe (NamedPipe* const pipe_); + + void handleMessage (const Message& message); + + void connectionMadeInt(); + void connectionLostInt(); + void deliverDataInt (const MemoryBlock& data); + + bool readNextMessageInt(); + void run(); + + InterprocessConnection (const InterprocessConnection&); + const InterprocessConnection& operator= (const InterprocessConnection&); +}; + +#endif // __JUCE_INTERPROCESSCONNECTION_JUCEHEADER__ +/********* End of inlined file: juce_InterprocessConnection.h *********/ + +#endif +#ifndef __JUCE_INTERPROCESSCONNECTIONSERVER_JUCEHEADER__ + +/********* Start of inlined file: juce_InterprocessConnectionServer.h *********/ +#ifndef __JUCE_INTERPROCESSCONNECTIONSERVER_JUCEHEADER__ +#define __JUCE_INTERPROCESSCONNECTIONSERVER_JUCEHEADER__ + +/** + An object that waits for client sockets to connect to a port on this host, and + creates InterprocessConnection objects for each one. + + To use this, create a class derived from it which implements the createConnectionObject() + method, so that it creates suitable connection objects for each client that tries + to connect. + + @see InterprocessConnection +*/ +class JUCE_API InterprocessConnectionServer : private Thread +{ +public: + + /** Creates an uninitialised server object. + */ + InterprocessConnectionServer(); + + /** Destructor. */ + ~InterprocessConnectionServer(); + + /** Starts an internal thread which listens on the given port number. + + While this is running, in another process tries to connect with the + InterprocessConnection::connectToSocket() method, this object will call + createConnectionObject() to create a connection to that client. + + Use stop() to stop the thread running. + + @see createConnectionObject, stop + */ + bool beginWaitingForSocket (const int portNumber); + + /** Terminates the listener thread, if it's active. + + @see beginWaitingForSocket + */ + void stop(); + +protected: + /** Creates a suitable connection object for a client process that wants to + connect to this one. + + This will be called by the listener thread when a client process tries + to connect, and must return a new InterprocessConnection object that will + then run as this end of the connection. + + @see InterprocessConnection + */ + virtual InterprocessConnection* createConnectionObject() = 0; + +public: + + juce_UseDebuggingNewOperator + +private: + StreamingSocket* volatile socket; + + void run(); + + InterprocessConnectionServer (const InterprocessConnectionServer&); + const InterprocessConnectionServer& operator= (const InterprocessConnectionServer&); +}; + +#endif // __JUCE_INTERPROCESSCONNECTIONSERVER_JUCEHEADER__ +/********* End of inlined file: juce_InterprocessConnectionServer.h *********/ + +#endif +#ifndef __JUCE_MESSAGE_JUCEHEADER__ + +#endif +#ifndef __JUCE_MESSAGELISTENER_JUCEHEADER__ + +#endif +#ifndef __JUCE_MESSAGEMANAGER_JUCEHEADER__ + +/********* Start of inlined file: juce_MessageManager.h *********/ +#ifndef __JUCE_MESSAGEMANAGER_JUCEHEADER__ +#define __JUCE_MESSAGEMANAGER_JUCEHEADER__ + +class Thread; +class InternalTimerThread; + +/** See MessageManager::callFunctionOnMessageThread() for use of this function type +*/ +typedef void* (MessageCallbackFunction) (void* userData); + +/** Delivers Message objects to MessageListeners, and handles the event-dispatch loop. + + @see Message, MessageListener, MessageManagerLock, JUCEApplication +*/ +class JUCE_API MessageManager : private DeletedAtShutdown, + private Timer +{ +public: + + /** Returns the global instance of the MessageManager. */ + static MessageManager* getInstance() throw(); + + /** Synchronously dispatches up to a certain number of messages from the queue. + + This will return when the queue becomes empty, or when the given number of + messages has been sent. + */ + void dispatchPendingMessages (int maxNumberOfMessagesToDispatch = 1000); + + /** Synchronously sends the next pending message. + + This must only be called by the message-thread. + + @param returnImmediatelyIfNoMessages if false, it will block indefinitely until a message + needs dispatching. If true, then if no messages are + pending, it will return immediately. + @param wasAMessageDispatched if this is non-zero, it will be set to true or false + depending on whether a message was actually sent or + not. + @returns false if the thing that's calling it should stop calling - i.e. if the + app is trying to quit. + */ + bool dispatchNextMessage (const bool returnImmediatelyIfNoMessages = false, + bool* const wasAMessageDispatched = 0); + + /** Calls a function using the message-thread. + + This can be used by any thread to cause this function to be called-back + by the message thread. If it's the message-thread that's calling this method, + then the function will just be called; if another thread is calling, a message + will be posted to the queue, and this method will block until that message + is delivered, the function is called, and the result is returned. + + Be careful not to cause any deadlocks with this! It's easy to do - e.g. if the caller + thread has a critical section locked, which an unrelated message callback then tries to lock + before the message thread gets round to processing this callback. + + @param callback the function to call - its signature must be @code + void* myCallbackFunction (void*) @endcode + @param userData a user-defined pointer that will be passed to the function that gets called + @returns the value that the callback function returns. + @see MessageManagerLock + */ + void* callFunctionOnMessageThread (MessageCallbackFunction* callback, + void* userData); + + /** Returns true if the caller-thread is the message thread. */ + bool isThisTheMessageThread() const throw(); + + /** Called to tell the manager which thread is the one that's running the dispatch loop. + + (Best to ignore this method unless you really know what you're doing..) + @see getCurrentMessageThread + */ + void setCurrentMessageThread (const int threadId) throw(); + + /** Returns the ID of the current message thread, as set by setCurrentMessageThread(). + + (Best to ignore this method unless you really know what you're doing..) + @see setCurrentMessageThread + */ + int getCurrentMessageThread() const throw() { return messageThreadId; } + + /** Returns true if the caller thread has currenltly got the message manager locked. + + see the MessageManagerLock class for more info about this. + + This will be true if the caller is the message thread, because that automatically + gains a lock while a message is being dispatched. + */ + bool currentThreadHasLockedMessageManager() const throw(); + + /** Sends a message to all other JUCE applications that are running. + + @param messageText the string that will be passed to the actionListenerCallback() + method of the broadcast listeners in the other app. + @see registerBroadcastListener, ActionListener + */ + static void broadcastMessage (const String& messageText) throw(); + + /** Registers a listener to get told about broadcast messages. + + The actionListenerCallback() callback's string parameter + is the message passed into broadcastMessage(). + + @see broadcastMessage + */ + void registerBroadcastListener (ActionListener* listener) throw(); + + /** Deregisters a broadcast listener. */ + void deregisterBroadcastListener (ActionListener* listener) throw(); + + /** Sets a time-limit for the app to be 'busy' before an hourglass cursor will be shown. + + @param millisecs how long before the cursor is shown (the default time is 500ms). If the + value is 0 or less, the wait cursor will never be shown (although on the + Mac the system might still decide to show it after a while). + @see MouseCursor::showWaitCursor + */ + void setTimeBeforeShowingWaitCursor (const int millisecs) throw(); + + /** Returns the time-out before the 'busy' cursor is shown when the app is busy. + + @see setTimeBeforeShowingWaitCursor, MouseCursor::showWaitCursor + */ + int getTimeBeforeShowingWaitCursor() const throw(); + + /** Tells the message manager that the system isn't locked-up, even if the message + loop isn't active. + + Used internally, this is handy when an OS enters its own modal loop. + */ + static void delayWaitCursor() throw(); + + /** Returns true if JUCEApplication::quit() has been called. */ + bool hasQuitMessageBeenPosted() const throw(); + + /** @internal */ + void deliverMessage (void*); + /** @internal */ + void deliverBroadcastMessage (const String&); + /** @internal */ + void timerCallback(); + + juce_UseDebuggingNewOperator + +private: + MessageManager() throw(); + ~MessageManager() throw(); + + friend class MessageListener; + friend class ChangeBroadcaster; + friend class ActionBroadcaster; + static MessageManager* instance; + + SortedSet messageListeners; + ActionListenerList* broadcastListeners; + + friend class JUCEApplication; + bool quitMessagePosted, quitMessageReceived, useMaximumForceWhenQuitting; + + int messageThreadId; + int volatile messageCounter, lastMessageCounter, isInMessageDispatcher; + bool volatile needToGetRidOfWaitCursor; + int volatile timeBeforeWaitCursor; + unsigned int lastActivityCheckOkTime; + + bool runDispatchLoop(); + void postMessageToQueue (Message* const message); + void postQuitMessage (const bool useMaximumForce); + + static void doPlatformSpecificInitialisation(); + static void doPlatformSpecificShutdown(); + + friend class InternalTimerThread; + static void inactivityCheckCallback() throw(); + void inactivityCheckCallbackInt() throw(); + + friend class MessageManagerLock; + CriticalSection messageDispatchLock; + int currentLockingThreadId; + + MessageManager (const MessageManager&); + const MessageManager& operator= (const MessageManager&); +}; + +/** Used to make sure that the calling thread has exclusive access to the message loop. + + Because it's not thread-safe to call any of the Component or other UI classes + from threads other than the message thread, one of these objects can be used to + lock the message loop and allow this to be done. The message thread will be + suspended for the lifetime of the MessageManagerLock object, so create one on + the stack like this: @code + void MyThread::run() + { + someData = 1234; + + const MessageManagerLock mmLock; + // the event loop will now be locked so it's safe to make a few calls.. + + myComponent->setBounds (newBounds); + myComponent->repaint(); + + // ..the event loop will now be unlocked as the MessageManagerLock goes out of scope + } + @endcode + + Obviously be careful not to create one of these and leave it lying around, or + your app will grind to a halt! + + Another caveat is that using this in conjunction with other CriticalSections + can create lots of interesting ways of producing a deadlock! In particular, if + your message thread calls stopThread() for a thread that uses these locks, + you'll get an (occasional) deadlock.. + + @see MessageManager, MessageManager::currentThreadHasLockedMessageManager +*/ +class JUCE_API MessageManagerLock +{ +public: + + /** Tries to acquire a lock on the message manager. + + If this constructor + When this constructor returns, the message manager will have finished processing the + last message and will not send another message until this MessageManagerLock is + deleted. + + If the current thread already has the lock, nothing will be done, so it's perfectly + safe to create these locks recursively. + */ + MessageManagerLock() throw(); + + /** Releases the current thread's lock on the message manager. + + Make sure this object is created and deleted by the same thread, + otherwise there are no guarantees what will happen! + */ + ~MessageManagerLock() throw(); + + /** Tries to acquire a lock on the message manager. + + This does the same thing as the normal constructor, but while it's waiting to get + the lock, it checks the specified thread to see if it has been given the + Thread::signalThreadShouldExit() signal. If this happens, then it will return + without gaining the lock. + + To find out whether the lock was successful, call lockWasGained(). If this is + false, your thread is being told to die, so you'd better get out of there. + + If the current thread already has the lock, nothing will be done, so it's perfectly + safe to create these locks recursively. + + E.g. + @code + void run() + { + ... + + while (! threadShouldExit()) + { + MessageManagerLock mml (Thread::getCurrentThread()); + + if (! mml.lockWasGained) + return; // another thread is trying to kill us! + + ..do some locked stuff here.. + } + + ..and now the MM is now unlocked.. + } + @endcode + + */ + MessageManagerLock (Thread* const threadToCheckForExitSignal) throw(); + + /** Returns true if the lock was successfully acquired. + + (See the constructor that takes a Thread for more info). + */ + bool lockWasGained() const throw() { return locked; } + +private: + int lastLockingThreadId; + bool locked; +}; + +#endif // __JUCE_MESSAGEMANAGER_JUCEHEADER__ +/********* End of inlined file: juce_MessageManager.h *********/ + +#endif +#ifndef __JUCE_MULTITIMER_JUCEHEADER__ + +/********* Start of inlined file: juce_MultiTimer.h *********/ +#ifndef __JUCE_MULTITIMER_JUCEHEADER__ +#define __JUCE_MULTITIMER_JUCEHEADER__ + +/** + A type of timer class that can run multiple timers with different frequencies, + all of which share a single callback. + + This class is very similar to the Timer class, but allows you run multiple + separate timers, where each one has a unique ID number. The methods in this + class are exactly equivalent to those in Timer, but with the addition of + this ID number. + + To use it, you need to create a subclass of MultiTimer, implementing the + timerCallback() method. Then you can start timers with startTimer(), and + each time the callback is triggered, it passes in the ID of the timer that + caused it. + + @see Timer +*/ +class JUCE_API MultiTimer +{ +protected: + + /** Creates a MultiTimer. + + When created, no timers are running, so use startTimer() to start things off. + */ + MultiTimer() throw(); + + /** Creates a copy of another timer. + + Note that this timer will not contain any running timers, even if the one you're + copying from was running. + */ + MultiTimer (const MultiTimer& other) throw(); + +public: + + /** Destructor. */ + virtual ~MultiTimer(); + + /** The user-defined callback routine that actually gets called by each of the + timers that are running. + + It's perfectly ok to call startTimer() or stopTimer() from within this + callback to change the subsequent intervals. + */ + virtual void timerCallback (const int timerId) = 0; + + /** Starts a timer and sets the length of interval required. + + If the timer is already started, this will reset it, so the + time between calling this method and the next timer callback + will not be less than the interval length passed in. + + @param timerId a unique Id number that identifies the timer to + start. This is the id that will be passed back + to the timerCallback() method when this timer is + triggered + @param intervalInMilliseconds the interval to use (any values less than 1 will be + rounded up to 1) + */ + void startTimer (const int timerId, const int intervalInMilliseconds) throw(); + + /** Stops a timer. + + If a timer has been started with the given ID number, it will be cancelled. + No more callbacks will be made for the specified timer after this method returns. + + If this is called from a different thread, any callbacks that may + be currently executing may be allowed to finish before the method + returns. + */ + void stopTimer (const int timerId) throw(); + + /** Checks whether a timer has been started for a specified ID. + + @returns true if a timer with the given ID is running. + */ + bool isTimerRunning (const int timerId) const throw(); + + /** Returns the interval for a specified timer ID. + + @returns the timer's interval in milliseconds if it's running, or 0 if it's no timer + is running for the ID number specified. + */ + int getTimerInterval (const int timerId) const throw(); + +private: + CriticalSection timerListLock; + VoidArray timers; + + const MultiTimer& operator= (const MultiTimer&); +}; + +#endif // __JUCE_MULTITIMER_JUCEHEADER__ +/********* End of inlined file: juce_MultiTimer.h *********/ + +#endif +#ifndef __JUCE_TIMER_JUCEHEADER__ + +#endif +#ifndef __JUCE_BRUSH_JUCEHEADER__ + +#endif +#ifndef __JUCE_GRADIENTBRUSH_JUCEHEADER__ + +/********* Start of inlined file: juce_GradientBrush.h *********/ +#ifndef __JUCE_GRADIENTBRUSH_JUCEHEADER__ +#define __JUCE_GRADIENTBRUSH_JUCEHEADER__ + +/********* Start of inlined file: juce_ColourGradient.h *********/ +#ifndef __JUCE_COLOURGRADIENT_JUCEHEADER__ +#define __JUCE_COLOURGRADIENT_JUCEHEADER__ + +/** + Structure used to define a colour gradient for painting areas. + + @see GradientBrush +*/ +class JUCE_API ColourGradient +{ +public: + + /** Creates a gradient object. + + (x1, y1) is the location to draw with colour1. Likewise (x2, y2) is where + colour2 should be. In between them there's a gradient. + + If isRadial is true, the colours form a circular gradient with (x1, y1) at + its centre. + + The alpha transparencies of the colours are used, so note that + if you blend from transparent to a solid colour, the RGB of the transparent + colour will become visible in parts of the gradient. e.g. blending + from Colour::transparentBlack to Colours::white will produce a + muddy grey colour midway, but Colour::transparentWhite to Colours::white + will be white all the way across. + + @see ColourGradient + */ + ColourGradient (const Colour& colour1, + const float x1, + const float y1, + const Colour& colour2, + const float x2, + const float y2, + const bool isRadial) throw(); + + /** Creates an uninitialised gradient. + + If you use this constructor instead of the other one, be sure to set all the + object's public member variables before using it! + */ + ColourGradient() throw(); + + /** Destructor */ + ~ColourGradient() throw(); + + /** Removes any colours that have been added. + + This will also remove any start and end colours, so the gradient won't work. You'll + need to add more colours with addColour(). + */ + void clearColours() throw(); + + /** Adds a colour at a point along the length of the gradient. + + This allows the gradient to go through a spectrum of colours, instead of just a + start and end colour. + + @param proportionAlongGradient a value between 0 and 1.0, which is the proportion + of the distance along the line between the two points + at which the colour should occur. + @param colour the colour that should be used at this point + */ + void addColour (const double proportionAlongGradient, + const Colour& colour) throw(); + + /** Multiplies the alpha value of all the colours by the given scale factor */ + void multiplyOpacity (const float multiplier) throw(); + + /** Returns the number of colour-stops that have been added. */ + int getNumColours() const throw(); + + /** Returns the position along the length of the gradient of the colour with this index. + + The index is from 0 to getNumColours() - 1. The return value will be between 0.0 and 1.0 + */ + double getColourPosition (const int index) const throw(); + + /** Returns the colour that was added with a given index. + + The index is from 0 to getNumColours() - 1. The return value will be between 0.0 and 1.0 + */ + const Colour getColour (const int index) const throw(); + + /** Creates a set of interpolated premultiplied ARGB values. + + The caller must delete the array that is returned using juce_free(). + */ + PixelARGB* createLookupTable (int& numEntries) const throw(); + + /** Returns true if all colours are opaque. */ + bool isOpaque() const throw(); + + /** Returns true if all colours are completely transparent. */ + bool isInvisible() const throw(); + + float x1; + float y1; + + float x2; + float y2; + + /** If true, the gradient should be filled circularly, centred around + (x1, y1), with (x2, y2) defining a point on the circumference. + + If false, the gradient is linear between the two points. + */ + bool isRadial; + + /** A transform to apply to the resultant gradient shape */ + AffineTransform transform; + + juce_UseDebuggingNewOperator + +private: + Array colours; +}; + +#endif // __JUCE_COLOURGRADIENT_JUCEHEADER__ +/********* End of inlined file: juce_ColourGradient.h *********/ + +/** + A Brush that fills areas with a colour gradient. + + The gradient can either be linear or circular. + + @see Brush, Graphics::setBrush, SolidColourBrush, ImageBrush +*/ +class JUCE_API GradientBrush : public Brush +{ +public: + + /** Creates a gradient brush, ready for use in Graphics::setBrush(). + + (x1, y1) is the location relative to the origin of the Graphics context, + at which the colour should be colour1. Likewise for (x2, y2) and colour2. + + If isRadial is true, the colours form a circular gradient with (x1, y1) at + its centre. + + The alpha transparencies of the colours are used, so the brush + need not be completely opaque. Note that this means that if you + blend from transparent to a solid colour, the RGB of the transparent + colour will become visible in parts of the gradient. e.g. blending + from Colour::transparentBlack to Colours::white will produce a + grey colour, but Colour::transparentWhite to Colours::white will be + white all the way across. + + @see ColourGradient + */ + GradientBrush (const Colour& colour1, + const float x1, + const float y1, + const Colour& colour2, + const float x2, + const float y2, + const bool isRadial) throw(); + + /** Creates a gradient brush from a ColourGradient object. + */ + GradientBrush (const ColourGradient& gradient) throw(); + + /** Destructor. */ + ~GradientBrush() throw(); + + Brush* createCopy() const throw(); + + void applyTransform (const AffineTransform& transform) throw(); + + void multiplyOpacity (const float multiple) throw(); + + bool isInvisible() const throw(); + + void paintPath (LowLevelGraphicsContext& context, + const Path& path, const AffineTransform& transform) throw(); + + void paintRectangle (LowLevelGraphicsContext& context, + int x, int y, int w, int h) throw(); + + void paintAlphaChannel (LowLevelGraphicsContext& context, + const Image& alphaChannelImage, int imageX, int imageY, + int x, int y, int w, int h) throw(); + + juce_UseDebuggingNewOperator + +protected: + ColourGradient gradient; + +private: + GradientBrush (const GradientBrush&); + const GradientBrush& operator= (const GradientBrush&); +}; + +#endif // __JUCE_GRADIENTBRUSH_JUCEHEADER__ +/********* End of inlined file: juce_GradientBrush.h *********/ + +#endif +#ifndef __JUCE_IMAGEBRUSH_JUCEHEADER__ + +/********* Start of inlined file: juce_ImageBrush.h *********/ +#ifndef __JUCE_IMAGEBRUSH_JUCEHEADER__ +#define __JUCE_IMAGEBRUSH_JUCEHEADER__ + +/********* Start of inlined file: juce_Image.h *********/ +#ifndef __JUCE_IMAGE_JUCEHEADER__ +#define __JUCE_IMAGE_JUCEHEADER__ + +/** + Holds a fixed-size bitmap. + + The image is stored in either 24-bit RGB or 32-bit premultiplied-ARGB format. + + To draw into an image, create a Graphics object for it. + e.g. @code + + // create a transparent 500x500 image.. + Image myImage (Image::RGB, 500, 500, true); + + Graphics g (myImage); + g.setColour (Colours::red); + g.fillEllipse (20, 20, 300, 200); // draws a red ellipse in our image. + @endcode + + Other useful ways to create an image are with the ImageCache class, or the + ImageFileFormat, which provides a way to load common image files. + + @see Graphics, ImageFileFormat, ImageCache, ImageConvolutionKernel +*/ +class JUCE_API Image +{ +public: + + enum PixelFormat + { + RGB, /**<< each pixel is a 3-byte packed RGB colour value. For byte order, see the PixelRGB class. */ + ARGB, /**<< each pixel is a 4-byte ARGB premultiplied colour value. For byte order, see the PixelARGB class. */ + SingleChannel /**<< each pixel is a 1-byte alpha channel value. */ + }; + + /** Creates an in-memory image with a specified size and format. + + @param format the number of colour channels in the image + @param imageWidth the desired width of the image, in pixels - this value must be + greater than zero (otherwise a width of 1 will be used) + @param imageHeight the desired width of the image, in pixels - this value must be + greater than zero (otherwise a height of 1 will be used) + @param clearImage if true, the image will initially be cleared to black or transparent + black. If false, the image may contain random data, and the + user will have to deal with this + */ + Image (const PixelFormat format, + const int imageWidth, + const int imageHeight, + const bool clearImage); + + /** Creates a copy of another image. + + @see createCopy + */ + Image (const Image& other); + + /** Destructor. */ + virtual ~Image(); + + /** Returns the image's width (in pixels). */ + int getWidth() const throw() { return imageWidth; } + + /** Returns the image's height (in pixels). */ + int getHeight() const throw() { return imageHeight; } + + /** Returns the image's pixel format. */ + PixelFormat getFormat() const throw() { return format; } + + /** True if the image's format is ARGB. */ + bool isARGB() const throw() { return format == ARGB; } + + /** True if the image's format is RGB. */ + bool isRGB() const throw() { return format == RGB; } + + /** True if the image contains an alpha-channel. */ + bool hasAlphaChannel() const throw() { return format != RGB; } + + /** Clears a section of the image with a given colour. + + This won't do any alpha-blending - it just sets all pixels in the image to + the given colour (which may be non-opaque if the image has an alpha channel). + */ + virtual void clear (int x, int y, int w, int h, + const Colour& colourToClearTo = Colour (0x00000000)); + + /** Returns a new image that's a copy of this one. + + A new size for the copied image can be specified, or values less than + zero can be passed-in to use the image's existing dimensions. + + It's up to the caller to delete the image when no longer needed. + */ + virtual Image* createCopy (int newWidth = -1, + int newHeight = -1, + const Graphics::ResamplingQuality quality = Graphics::mediumResamplingQuality) const; + + /** Returns the colour of one of the pixels in the image. + + If the co-ordinates given are beyond the image's boundaries, this will + return Colours::transparentBlack. + + (0, 0) is the image's top-left corner. + + @see getAlphaAt, setPixelAt, blendPixelAt + */ + virtual const Colour getPixelAt (const int x, const int y) const; + + /** Sets the colour of one of the image's pixels. + + If the co-ordinates are beyond the image's boundaries, then nothing will + happen. + + Note that unlike blendPixelAt(), this won't do any alpha-blending, it'll + just replace the existing pixel with the given one. The colour's opacity + will be ignored if this image doesn't have an alpha-channel. + + (0, 0) is the image's top-left corner. + + @see blendPixelAt + */ + virtual void setPixelAt (const int x, const int y, const Colour& colour); + + /** Changes the opacity of a pixel. + + This only has an effect if the image has an alpha channel and if the + given co-ordinates are inside the image's boundary. + + The multiplier must be in the range 0 to 1.0, and the current alpha + at the given co-ordinates will be multiplied by this value. + + @see getAlphaAt, setPixelAt + */ + virtual void multiplyAlphaAt (const int x, const int y, const float multiplier); + + /** Changes the overall opacity of the image. + + This will multiply the alpha value of each pixel in the image by the given + amount (limiting the resulting alpha values between 0 and 255). This allows + you to make an image more or less transparent. + + If the image doesn't have an alpha channel, this won't have any effect. + */ + virtual void multiplyAllAlphas (const float amountToMultiplyBy); + + /** Changes all the colours to be shades of grey, based on their current luminosity. + */ + virtual void desaturate(); + + /** Locks some of the pixels in the image so they can be read and written to. + + This returns a pointer to some memory containing the pixels in the given + rectangle. It also returns values for the line and pixel stride used within + the data. The format of the pixel data is the same as that of this image. + + When you've finished reading and changing the data, you must call + releasePixelDataReadWrite() to give the pixels back to the image. + + For images that are stored in memory, this method may just return a direct + pointer to the image's data, but other types of image may be stored elsewhere, + e.g. in video memory, and if so, this lockPixelDataReadWrite() and + releasePixelDataReadWrite() may need to create a temporary copy in main memory. + + If you only need read-access to the pixel data, use lockPixelDataReadOnly() + instead. + + @see releasePixelDataReadWrite, lockPixelDataReadOnly + */ + virtual uint8* lockPixelDataReadWrite (int x, int y, int w, int h, int& lineStride, int& pixelStride); + + /** Releases a block of memory that was locked with lockPixelDataReadWrite(). + */ + virtual void releasePixelDataReadWrite (void* sourceData); + + /** Locks some of the pixels in the image so they can be read. + + This returns a pointer to some memory containing the pixels in the given + rectangle. It also returns values for the line and pixel stride used within + the data. The format of the pixel data is the same as that of this image. + + When you've finished reading the data, you must call releasePixelDataReadOnly() + to let the image free the memory if necessary. + + For images that are stored in memory, this method may just return a direct + pointer to the image's data, but other types of image may be stored elsewhere, + e.g. in video memory, and if so, this lockPixelDataReadWrite() and + releasePixelDataReadWrite() may need to create a temporary copy in main memory. + + If you only need to read and write the pixel data, use lockPixelDataReadWrite() + instead. + + @see releasePixelDataReadOnly, lockPixelDataReadWrite + */ + virtual const uint8* lockPixelDataReadOnly (int x, int y, int w, int h, int& lineStride, int& pixelStride) const; + + /** Releases a block of memory that was locked with lockPixelDataReadOnly(). + */ + virtual void releasePixelDataReadOnly (const void* sourceData) const; + + /** Copies some pixel values to a rectangle of the image. + + The format of the pixel data must match that of the image itself, and the + rectangle supplied must be within the image's bounds. + */ + virtual void setPixelData (int destX, int destY, int destW, int destH, + const uint8* sourcePixelData, int sourceLineStride); + + /** Copies a section of the image to somewhere else within itself. + */ + virtual void moveImageSection (int destX, int destY, + int sourceX, int sourceY, + int width, int height); + + /** Creates a RectangleList containing rectangles for all non-transparent pixels + of the image. + + @param result the list that will have the area added to it + @param alphaThreshold for a semi-transparent image, any pixels whose alpha is + above this level will be considered opaque + */ + void createSolidAreaMask (RectangleList& result, + const float alphaThreshold = 0.5f) const; + + juce_UseDebuggingNewOperator + + /** Creates a context suitable for drawing onto this image. + + Don't call this method directly! It's used internally by the Graphics class. + */ + virtual LowLevelGraphicsContext* createLowLevelContext(); + +protected: + const PixelFormat format; + const int imageWidth, imageHeight; + + /** Used internally so that subclasses can call a constructor that doesn't allocate memory */ + Image (const PixelFormat format, + const int imageWidth, + const int imageHeight); + + int pixelStride, lineStride; + uint8* imageData; + +private: + + const Image& operator= (const Image&); +}; + +#endif // __JUCE_IMAGE_JUCEHEADER__ +/********* End of inlined file: juce_Image.h *********/ + +/** + A Brush that fills areas with tiled repetitions of an image. + + @see Brush, Graphics::setBrush, SolidColourBrush, GradientBrush +*/ +class JUCE_API ImageBrush : public Brush +{ +public: + + /* Creates an image brush, ready for use in Graphics::setBrush(). + + (x, y) is an anchor point for the top-left of the image + A reference to the image passed in will be kept, so don't delete + it within the lifetime of this object + */ + ImageBrush (Image* const image, + const int anchorX, + const int anchorY, + const float opacity) throw(); + + /** Destructor. */ + ~ImageBrush() throw(); + + Brush* createCopy() const throw(); + + void applyTransform (const AffineTransform& transform) throw(); + + void multiplyOpacity (const float multiple) throw(); + + bool isInvisible() const throw(); + + void paintPath (LowLevelGraphicsContext& context, + const Path& path, const AffineTransform& transform) throw(); + + void paintRectangle (LowLevelGraphicsContext& context, + int x, int y, int w, int h) throw(); + + void paintAlphaChannel (LowLevelGraphicsContext& context, + const Image& alphaChannelImage, int imageX, int imageY, + int x, int y, int w, int h) throw(); + + juce_UseDebuggingNewOperator + +protected: + Image* image; + int anchorX, anchorY; + float opacity; + +private: + ImageBrush (const ImageBrush&); + const ImageBrush& operator= (const ImageBrush&); + + void getStartXY (int& x, int& y) const throw(); +}; + +#endif // __JUCE_IMAGEBRUSH_JUCEHEADER__ +/********* End of inlined file: juce_ImageBrush.h *********/ + +#endif +#ifndef __JUCE_SOLIDCOLOURBRUSH_JUCEHEADER__ + +#endif +#ifndef __JUCE_COLOUR_JUCEHEADER__ + +#endif +#ifndef __JUCE_COLOURGRADIENT_JUCEHEADER__ + +#endif +#ifndef __JUCE_COLOURS_JUCEHEADER__ + +#endif +#ifndef __JUCE_PIXELFORMATS_JUCEHEADER__ + +#endif +#ifndef __JUCE_FONT_JUCEHEADER__ + +#endif +#ifndef __JUCE_GLYPHARRANGEMENT_JUCEHEADER__ + +/********* Start of inlined file: juce_GlyphArrangement.h *********/ +#ifndef __JUCE_GLYPHARRANGEMENT_JUCEHEADER__ +#define __JUCE_GLYPHARRANGEMENT_JUCEHEADER__ + +/** + An glyph from a particular font, with a particular size, style, + typeface and position. + + @see GlyphArrangement, Font +*/ +class JUCE_API PositionedGlyph +{ +public: + + /** Returns the character the glyph represents. */ + juce_wchar getCharacter() const throw() { return glyphInfo->getCharacter(); } + /** Checks whether the glyph is actually empty. */ + bool isWhitespace() const throw() { return CharacterFunctions::isWhitespace (glyphInfo->getCharacter()); } + + /** Returns the position of the glyph's left-hand edge. */ + float getLeft() const throw() { return x; } + /** Returns the position of the glyph's right-hand edge. */ + float getRight() const throw() { return x + w; } + /** Returns the y position of the glyph's baseline. */ + float getBaselineY() const throw() { return y; } + /** Returns the y position of the top of the glyph. */ + float getTop() const throw() { return y - fontAscent; } + /** Returns the y position of the bottom of the glyph. */ + float getBottom() const throw() { return y + fontHeight - fontAscent; } + + /** Shifts the glyph's position by a relative amount. */ + void moveBy (const float deltaX, + const float deltaY) throw(); + + /** Draws the glyph into a graphics context. */ + void draw (const Graphics& g) const throw(); + + /** Draws the glyph into a graphics context, with an extra transform applied to it. */ + void draw (const Graphics& g, const AffineTransform& transform) const throw(); + + /** Returns the path for this glyph. + + @param path the glyph's outline will be appended to this path + */ + void createPath (Path& path) const throw(); + + /** Checks to see if a point lies within this glyph. */ + bool hitTest (float x, float y) const throw(); + + juce_UseDebuggingNewOperator + +private: + + friend class GlyphArrangement; + float x, y, w; + float fontHeight, fontAscent, fontHorizontalScale; + bool isUnderlined; + const TypefaceGlyphInfo* glyphInfo; + + PositionedGlyph() throw(); +}; + +/** + A set of glyphs, each with a position. + + You can create a GlyphArrangement, text to it and then draw it onto a + graphics context. It's used internally by the text methods in the + Graphics class, but can be used directly if more control is needed. + + @see Font, PositionedGlyph +*/ +class JUCE_API GlyphArrangement +{ +public: + + /** Creates an empty arrangement. */ + GlyphArrangement() throw(); + + /** Takes a copy of another arrangement. */ + GlyphArrangement (const GlyphArrangement& other) throw(); + + /** Copies another arrangement onto this one. + + To add another arrangement without clearing this one, use addGlyphArrangement(). + */ + const GlyphArrangement& operator= (const GlyphArrangement& other) throw(); + + /** Destructor. */ + ~GlyphArrangement() throw(); + + /** Returns the total number of glyphs in the arrangement. */ + int getNumGlyphs() const throw() { return numGlyphs; } + + /** Returns one of the glyphs from the arrangement. + + @param index the glyph's index, from 0 to (getNumGlyphs() - 1). Be + careful not to pass an out-of-range index here, as it + doesn't do any bounds-checking. + */ + PositionedGlyph& getGlyph (const int index) const throw(); + + /** Clears all text from the arrangement and resets it. + */ + void clear() throw(); + + /** Appends a line of text to the arrangement. + + This will add the text as a single line, where x is the left-hand edge of the + first character, and y is the position for the text's baseline. + + If the text contains new-lines or carriage-returns, this will ignore them - use + addJustifiedText() to add multi-line arrangements. + */ + void addLineOfText (const Font& font, + const String& text, + const float x, + const float y) throw(); + + /** Adds a line of text, truncating it if it's wider than a specified size. + + This is the same as addLineOfText(), but if the line's width exceeds the value + specified in maxWidthPixels, it will be truncated using either ellipsis (i.e. dots: "..."), + if useEllipsis is true, or if this is false, it will just drop any subsequent characters. + */ + void addCurtailedLineOfText (const Font& font, + const String& text, + float x, + const float y, + const float maxWidthPixels, + const bool useEllipsis) throw(); + + /** Adds some multi-line text, breaking lines at word-boundaries if they are too wide. + + This will add text to the arrangement, breaking it into new lines either where there + is a new-line or carriage-return character in the text, or where a line's width + exceeds the value set in maxLineWidth. + + Each line that is added will be laid out using the flags set in horizontalLayout, so + the lines can be left- or right-justified, or centred horizontally in the space + between x and (x + maxLineWidth). + + The y co-ordinate is the position of the baseline of the first line of text - subsequent + lines will be placed below it, separated by a distance of font.getHeight(). + */ + void addJustifiedText (const Font& font, + const String& text, + float x, float y, + const float maxLineWidth, + const Justification& horizontalLayout) throw(); + + /** Tries to fit some text withing a given space. + + This does its best to make the given text readable within the specified rectangle, + so it useful for labelling things. + + If the text is too big, it'll be squashed horizontally or broken over multiple lines + if the maximumLinesToUse value allows this. If the text just won't fit into the space, + it'll cram as much as possible in there, and put some ellipsis at the end to show that + it's been truncated. + + A Justification parameter lets you specify how the text is laid out within the rectangle, + both horizontally and vertically. + + @see Graphics::drawFittedText + */ + void addFittedText (const Font& font, + const String& text, + float x, float y, + float width, float height, + const Justification& layout, + int maximumLinesToUse, + const float minimumHorizontalScale = 0.7f) throw(); + + /** Appends another glyph arrangement to this one. */ + void addGlyphArrangement (const GlyphArrangement& other) throw(); + + /** Draws this glyph arrangement to a graphics context. + + This uses cached bitmaps so is much faster than the draw (Graphics&, const AffineTransform&) + method, which renders the glyphs as filled vectors. + */ + void draw (const Graphics& g) const throw(); + + /** Draws this glyph arrangement to a graphics context. + + This renders the paths as filled vectors, so is far slower than the draw (Graphics&) + method for non-transformed arrangements. + */ + void draw (const Graphics& g, const AffineTransform& transform) const throw(); + + /** Converts the set of glyphs into a path. + + @param path the glyphs' outlines will be appended to this path + */ + void createPath (Path& path) const throw(); + + /** Looks for a glyph that contains the given co-ordinate. + + @returns the index of the glyph, or -1 if none were found. + */ + int findGlyphIndexAt (float x, float y) const throw(); + + /** Finds the smallest rectangle that will enclose a subset of the glyphs. + + @param startIndex the first glyph to test + @param numGlyphs the number of glyphs to include; if this is < 0, all glyphs after + startIndex will be included + @param left on return, the leftmost co-ordinate of the rectangle + @param top on return, the top co-ordinate of the rectangle + @param right on return, the rightmost co-ordinate of the rectangle + @param bottom on return, the bottom co-ordinate of the rectangle + @param includeWhitespace if true, the extent of any whitespace characters will also + be taken into account + */ + void getBoundingBox (int startIndex, + int numGlyphs, + float& left, + float& top, + float& right, + float& bottom, + const bool includeWhitespace) const throw(); + + /** Shifts a set of glyphs by a given amount. + + @param startIndex the first glyph to transform + @param numGlyphs the number of glyphs to move; if this is < 0, all glyphs after + startIndex will be used + @param deltaX the amount to add to their x-positions + @param deltaY the amount to add to their y-positions + */ + void moveRangeOfGlyphs (int startIndex, int numGlyphs, + const float deltaX, + const float deltaY) throw(); + + /** Removes a set of glyphs from the arrangement. + + @param startIndex the first glyph to remove + @param numGlyphs the number of glyphs to remove; if this is < 0, all glyphs after + startIndex will be deleted + */ + void removeRangeOfGlyphs (int startIndex, int numGlyphs) throw(); + + /** Expands or compresses a set of glyphs horizontally. + + @param startIndex the first glyph to transform + @param numGlyphs the number of glyphs to stretch; if this is < 0, all glyphs after + startIndex will be used + @param horizontalScaleFactor how much to scale their horizontal width by + */ + void stretchRangeOfGlyphs (int startIndex, int numGlyphs, + const float horizontalScaleFactor) throw(); + + /** Justifies a set of glyphs within a given space. + + This moves the glyphs as a block so that the whole thing is located within the + given rectangle with the specified layout. + + If the Justification::horizontallyJustified flag is specified, each line will + be stretched out to fill the specified width. + */ + void justifyGlyphs (const int startIndex, const int numGlyphs, + const float x, + const float y, + const float width, + const float height, + const Justification& justification) throw(); + + juce_UseDebuggingNewOperator + +private: + int numGlyphs, numAllocated; + PositionedGlyph* glyphs; + + void ensureNumGlyphsAllocated (int minGlyphs) throw(); + void removeLast() throw(); + void appendEllipsis (const Font& font, const float maxXPixels) throw(); + + void incGlyphRefCount (const int index) const throw(); + void decGlyphRefCount (const int index) const throw(); + + void spreadOutLine (const int start, const int numGlyphs, const float targetWidth) throw(); +}; + +#endif // __JUCE_GLYPHARRANGEMENT_JUCEHEADER__ +/********* End of inlined file: juce_GlyphArrangement.h *********/ + +#endif +#ifndef __JUCE_TEXTLAYOUT_JUCEHEADER__ + +/********* Start of inlined file: juce_TextLayout.h *********/ +#ifndef __JUCE_TEXTLAYOUT_JUCEHEADER__ +#define __JUCE_TEXTLAYOUT_JUCEHEADER__ + +class Graphics; + +/** + A laid-out arrangement of text. + + You can add text in different fonts to a TextLayout object, then call its + layout() method to word-wrap it into lines. The layout can then be drawn + using a graphics context. + + It's handy if you've got a message to display, because you can format it, + measure the extent of the layout, and then create a suitably-sized window + to show it in. + + @see Font, Graphics::drawFittedText, GlyphArrangement +*/ +class JUCE_API TextLayout +{ +public: + + /** Creates an empty text layout. + + Text can then be appended using the appendText() method. + */ + TextLayout() throw(); + + /** Creates a copy of another layout object. */ + TextLayout (const TextLayout& other) throw(); + + /** Creates a text layout from an initial string and font. */ + TextLayout (const String& text, const Font& font) throw(); + + /** Destructor. */ + ~TextLayout() throw(); + + /** Copies another layout onto this one. */ + const TextLayout& operator= (const TextLayout& layoutToCopy) throw(); + + /** Clears the layout, removing all its text. */ + void clear() throw(); + + /** Adds a string to the end of the arrangement. + + The string will be broken onto new lines wherever it contains + carriage-returns or linefeeds. After adding it, you can call layout() + to wrap long lines into a paragraph and justify it. + */ + void appendText (const String& textToAppend, + const Font& fontToUse) throw(); + + /** Replaces all the text with a new string. + + This is equivalent to calling clear() followed by appendText(). + */ + void setText (const String& newText, + const Font& fontToUse) throw(); + + /** Breaks the text up to form a paragraph with the given width. + + @param maximumWidth any text wider than this will be split + across multiple lines + @param justification how the lines are to be laid-out horizontally + @param attemptToBalanceLineLengths if true, it will try to split the lines at a + width that keeps all the lines of text at a + similar length - this is good when you're displaying + a short message and don't want it to get split + onto two lines with only a couple of words on + the second line, which looks untidy. + */ + void layout (int maximumWidth, + const Justification& justification, + const bool attemptToBalanceLineLengths) throw(); + + /** Returns the overall width of the entire text layout. */ + int getWidth() const throw(); + + /** Returns the overall height of the entire text layout. */ + int getHeight() const throw(); + + /** Returns the total number of lines of text. */ + int getNumLines() const throw() { return totalLines; } + + /** Returns the width of a particular line of text. + + @param lineNumber the line, from 0 to (getNumLines() - 1) + */ + int getLineWidth (const int lineNumber) const throw(); + + /** Renders the text at a specified position using a graphics context. + */ + void draw (Graphics& g, + const int topLeftX, + const int topLeftY) const throw(); + + /** Renders the text within a specified rectangle using a graphics context. + + The justification flags dictate how the block of text should be positioned + within the rectangle. + */ + void drawWithin (Graphics& g, + int x, int y, int w, int h, + const Justification& layoutFlags) const throw(); + + juce_UseDebuggingNewOperator + +private: + VoidArray tokens; + int totalLines; +}; + +#endif // __JUCE_TEXTLAYOUT_JUCEHEADER__ +/********* End of inlined file: juce_TextLayout.h *********/ + +#endif +#ifndef __JUCE_TYPEFACE_JUCEHEADER__ + +#endif +#ifndef __JUCE_EDGETABLE_JUCEHEADER__ + +/********* Start of inlined file: juce_EdgeTable.h *********/ +#ifndef __JUCE_EDGETABLE_JUCEHEADER__ +#define __JUCE_EDGETABLE_JUCEHEADER__ + +class Path; + +static const int juce_edgeTableDefaultEdgesPerLine = 10; + +/** + A table of horizontal scan-line segments - used for rasterising Paths. + + @see Path, Graphics +*/ +class JUCE_API EdgeTable +{ +public: + + /** Indicates the quality at which the edge table should be generated. + + Higher values will have better quality anti-aliasing, but will take + longer to generate the edge table and to render it. + */ + enum OversamplingLevel + { + Oversampling_none = 0, /**< No vertical anti-aliasing at all. */ + Oversampling_4times = 2, /**< Anti-aliased with 4 levels of grey - good enough for normal use. */ + Oversampling_16times = 4, /**< Anti-aliased with 16 levels of grey - very good quality but slower. */ + Oversampling_256times = 8 /**< Anti-aliased with 256 levels of grey - best quality, but too slow for + normal user-interface use. */ + }; + + /** Creates an empty edge table ready to have paths added. + + A table is created with a fixed vertical size, and only sections of paths + which lie within their range will be added to the table. + + @param topY the lowest y co-ordinate that the table can contain + @param height the number of horizontal lines it can contain + @param verticalOversampling the amount of oversampling used for anti-aliasing + @param expectedEdgesPerLine used to optimise the table's internal data usage - it's not + worth changing this except for very special purposes + */ + EdgeTable (const int topY, + const int height, + const OversamplingLevel verticalOversampling = Oversampling_4times, + const int expectedEdgesPerLine = juce_edgeTableDefaultEdgesPerLine) throw(); + + /** Creates a copy of another edge table. */ + EdgeTable (const EdgeTable& other) throw(); + + /** Copies from another edge table. */ + const EdgeTable& operator= (const EdgeTable& other) throw(); + + /** Destructor. */ + ~EdgeTable() throw(); + + /** Adds edges to the table for a path. + + This will add horizontal lines to the edge table for any parts of the path + which lie within the vertical bounds for which this table was created. + + @param path the path to add + @param transform an optional transform to apply to the path while it's + being added + */ + void addPath (const Path& path, + const AffineTransform& transform) throw(); + + /** Reduces the amount of space the table has allocated. + + This will shrink the table down to use as little memory as possible - useful for + read-only tables that get stored and re-used for rendering. + */ + void optimiseTable() throw(); + + /** Iterates the lines in the table, for rendering. + + This function will iterate each line in the table, and call a user-defined class + to render each pixel or continuous line of pixels that the table contains. + + @param iterationCallback this templated class must contain the following methods: + @code + inline void setEdgeTableYPos (int y); + inline void handleEdgeTablePixel (int x, int alphaLevel) const; + inline void handleEdgeTableLine (int x, int width, int alphaLevel) const; + @endcode + (these don't necessarily have to be 'const', but it might help it go faster) + @param clipLeft the left-hand edge of the rectangle which should be iterated + @param clipTop the top edge of the rectangle which should be iterated + @param clipRight the right-hand edge of the rectangle which should be iterated + @param clipBottom the bottom edge of the rectangle which should be iterated + @param subPixelXOffset a fraction of 1 pixel by which to shift the table rightwards, in the range 0 to 255 + */ + template + void iterate (EdgeTableIterationCallback& iterationCallback, + const int clipLeft, + int clipTop, + const int clipRight, + int clipBottom, + const int subPixelXOffset) const + { + if (clipTop < top) + clipTop = top; + + if (clipBottom > top + height) + clipBottom = top + height; + + const int* singleLine = table + lineStrideElements + * ((clipTop - top) << (int) oversampling); + + int mergedLineAllocation = 128; + MemoryBlock temp (mergedLineAllocation * (2 * sizeof (int))); + int* mergedLine = (int*) temp.getData(); + + const int timesOverSampling = 1 << (int) oversampling; + + for (int y = clipTop; y < clipBottom; ++y) + { + int numMergedPoints = 0; + + // sort all the oversampled lines into a single merged line ready to draw.. + for (int over = timesOverSampling; --over >= 0;) + { + const int* l = singleLine; + singleLine += lineStrideElements; + + int num = *l; + jassert (num >= 0); + + if (num > 0) + { + if (numMergedPoints + num >= mergedLineAllocation) + { + mergedLineAllocation = (numMergedPoints + num + 0x100) & ~0xff; + temp.setSize (mergedLineAllocation * (2 * sizeof (int)), false); + mergedLine = (int*) temp.getData(); + } + + while (--num >= 0) + { + const int x = *++l; + const int winding = *++l; + + int n = numMergedPoints << 1; + + while (n > 0) + { + const int cx = mergedLine [n - 2]; + + if (cx <= x) + break; + + mergedLine [n] = cx; + --n; + mergedLine [n + 2] = mergedLine [n]; + --n; + } + + mergedLine [n] = x; + mergedLine [n + 1] = winding; + + ++numMergedPoints; + } + } + } + + if (--numMergedPoints > 0) + { + const int* line = mergedLine; + int x = subPixelXOffset + *line; + int level = *++line; + int levelAccumulator = 0; + + iterationCallback.setEdgeTableYPos (y); + + while (--numMergedPoints >= 0) + { + const int endX = subPixelXOffset + *++line; + jassert (endX >= x); + + const int absLevel = abs (level); + int endOfRun = (endX >> 8); + + if (endOfRun == (x >> 8)) + { + // small segment within the same pixel, so just save it for the next + // time round.. + levelAccumulator += (endX - x) * absLevel; + } + else + { + // plot the fist pixel of this segment, including any accumulated + // levels from smaller segments that haven't been drawn yet + levelAccumulator += (0xff - (x & 0xff)) * absLevel; + + levelAccumulator >>= 8; + if (levelAccumulator > 0xff) + levelAccumulator = 0xff; + + x >>= 8; + + if (x >= clipRight) + { + levelAccumulator = 0; + break; + } + + if (x >= clipLeft && x < clipRight && levelAccumulator > 0) + iterationCallback.handleEdgeTablePixel (x, levelAccumulator); + + if (++x >= clipRight) + { + levelAccumulator = 0; + break; + } + + // if there's a segment of solid pixels, do it all in one go.. + if (absLevel > 0 && endOfRun > x) + { + if (x < clipLeft) + x = clipLeft; + + if (endOfRun > clipRight) + endOfRun = clipRight; + + const int numPix = endOfRun - x; + + if (numPix > 0) + iterationCallback.handleEdgeTableLine (x, numPix, + jmin (absLevel, 0xff)); + } + + // save the bit at the end to be drawn next time round the loop. + levelAccumulator = (endX & 0xff) * absLevel; + } + + level += *++line; + x = endX; + } + + if (levelAccumulator > 0) + { + levelAccumulator >>= 8; + if (levelAccumulator > 0xff) + levelAccumulator = 0xff; + + x >>= 8; + if (x >= clipLeft && x < clipRight) + iterationCallback.handleEdgeTablePixel (x, levelAccumulator); + } + } + } + } + + juce_UseDebuggingNewOperator + +private: + // table line format: number of points; point0 x, point0 levelDelta, point1 x, point1 levelDelta, etc + int* table; + int top, height, maxEdgesPerLine, lineStrideElements; + OversamplingLevel oversampling; + + // this will assume that the y co-ord is within bounds, and will avoid checking + // this for speed. + void addEdgePoint (const int x, const int y, const int winding) throw(); + + void remapTableForNumEdges (const int newNumEdgesPerLine) throw(); +}; + +#endif // __JUCE_EDGETABLE_JUCEHEADER__ +/********* End of inlined file: juce_EdgeTable.h *********/ + +#endif +#ifndef __JUCE_GRAPHICS_JUCEHEADER__ + +#endif +#ifndef __JUCE_JUSTIFICATION_JUCEHEADER__ + +#endif +#ifndef __JUCE_LOWLEVELGRAPHICSCONTEXT_JUCEHEADER__ + +/********* Start of inlined file: juce_LowLevelGraphicsContext.h *********/ +#ifndef __JUCE_LOWLEVELGRAPHICSCONTEXT_JUCEHEADER__ +#define __JUCE_LOWLEVELGRAPHICSCONTEXT_JUCEHEADER__ + +/** + Interface class for graphics context objects, used internally by the Graphics class. + + Users are not supposed to create instances of this class directly - do your drawing + via the Graphics object instead. + + It's a base class for different types of graphics context, that may perform software-based + or OS-accelerated rendering. + + E.g. the LowLevelGraphicsSoftwareRenderer renders onto an image in memory, but other + subclasses could render directly to a windows HDC, a Quartz context, or an OpenGL + context. +*/ +class JUCE_API LowLevelGraphicsContext +{ +protected: + + LowLevelGraphicsContext(); + +public: + virtual ~LowLevelGraphicsContext(); + + /** Returns true if this device is vector-based, e.g. a printer. */ + virtual bool isVectorDevice() const = 0; + + /** Moves the origin to a new position. + + The co-ords are relative to the current origin, and indicate the new position + of (0, 0). + */ + virtual void setOrigin (int x, int y) = 0; + + /** Cliping co-ords are relative to the origin. */ + virtual bool reduceClipRegion (int x, int y, int w, int h) = 0; + + /** Cliping co-ords are relative to the origin. */ + virtual bool reduceClipRegion (const RectangleList& clipRegion) = 0; + + /** Cliping co-ords are relative to the origin. */ + virtual void excludeClipRegion (int x, int y, int w, int h) = 0; + + virtual void saveState() = 0; + virtual void restoreState() = 0; + + virtual bool clipRegionIntersects (int x, int y, int w, int h) = 0; + virtual const Rectangle getClipBounds() const = 0; + virtual bool isClipEmpty() const = 0; + + virtual void fillRectWithColour (int x, int y, int w, int h, const Colour& colour, const bool replaceExistingContents) = 0; + virtual void fillRectWithGradient (int x, int y, int w, int h, const ColourGradient& gradient) = 0; + + virtual void fillPathWithColour (const Path& path, const AffineTransform& transform, const Colour& colour, EdgeTable::OversamplingLevel quality) = 0; + virtual void fillPathWithGradient (const Path& path, const AffineTransform& transform, const ColourGradient& gradient, EdgeTable::OversamplingLevel quality) = 0; + virtual void fillPathWithImage (const Path& path, const AffineTransform& transform, + const Image& image, int imageX, int imageY, float alpha, EdgeTable::OversamplingLevel quality) = 0; + + virtual void fillAlphaChannelWithColour (const Image& alphaImage, int alphaImageX, int alphaImageY, const Colour& colour) = 0; + virtual void fillAlphaChannelWithGradient (const Image& alphaImage, int alphaImageX, int alphaImageY, const ColourGradient& gradient) = 0; + virtual void fillAlphaChannelWithImage (const Image& alphaImage, int alphaImageX, int alphaImageY, + const Image& fillerImage, int fillerImageX, int fillerImageY, float alpha) = 0; + + virtual void blendImage (const Image& sourceImage, + int destX, int destY, int destW, int destH, int sourceX, int sourceY, + float alpha) = 0; + + virtual void blendImageRescaling (const Image& sourceImage, + int destX, int destY, int destW, int destH, + int sourceX, int sourceY, int sourceW, int sourceH, + float alpha, const Graphics::ResamplingQuality quality) = 0; + + virtual void blendImageWarping (const Image& sourceImage, + int srcClipX, int srcClipY, int srcClipW, int srcClipH, + const AffineTransform& transform, + float alpha, const Graphics::ResamplingQuality quality) = 0; + + virtual void drawLine (double x1, double y1, double x2, double y2, const Colour& colour) = 0; + + virtual void drawVerticalLine (const int x, double top, double bottom, const Colour& col) = 0; + virtual void drawHorizontalLine (const int y, double left, double right, const Colour& col) = 0; +}; + +#endif // __JUCE_LOWLEVELGRAPHICSCONTEXT_JUCEHEADER__ +/********* End of inlined file: juce_LowLevelGraphicsContext.h *********/ + +#endif +#ifndef __JUCE_LOWLEVELGRAPHICSPOSTSCRIPTRENDERER_JUCEHEADER__ + +/********* Start of inlined file: juce_LowLevelGraphicsPostScriptRenderer.h *********/ +#ifndef __JUCE_LOWLEVELGRAPHICSPOSTSCRIPTRENDERER_JUCEHEADER__ +#define __JUCE_LOWLEVELGRAPHICSPOSTSCRIPTRENDERER_JUCEHEADER__ + +/** + An implementation of LowLevelGraphicsContext that turns the drawing operations + into a PostScript document. + +*/ +class JUCE_API LowLevelGraphicsPostScriptRenderer : public LowLevelGraphicsContext +{ +public: + + LowLevelGraphicsPostScriptRenderer (OutputStream& resultingPostScript, + const String& documentTitle, + const int totalWidth, + const int totalHeight); + + ~LowLevelGraphicsPostScriptRenderer(); + + bool isVectorDevice() const; + void setOrigin (int x, int y); + + bool reduceClipRegion (int x, int y, int w, int h); + bool reduceClipRegion (const RectangleList& clipRegion); + void excludeClipRegion (int x, int y, int w, int h); + + void saveState(); + void restoreState(); + + bool clipRegionIntersects (int x, int y, int w, int h); + const Rectangle getClipBounds() const; + bool isClipEmpty() const; + + void fillRectWithColour (int x, int y, int w, int h, const Colour& colour, const bool replaceExistingContents); + void fillRectWithGradient (int x, int y, int w, int h, const ColourGradient& gradient); + + void fillPathWithColour (const Path& path, const AffineTransform& transform, const Colour& colour, EdgeTable::OversamplingLevel quality); + void fillPathWithGradient (const Path& path, const AffineTransform& transform, const ColourGradient& gradient, EdgeTable::OversamplingLevel quality); + void fillPathWithImage (const Path& path, const AffineTransform& transform, + const Image& image, int imageX, int imageY, float alpha, EdgeTable::OversamplingLevel quality); + + void fillAlphaChannelWithColour (const Image& alphaImage, int imageX, int imageY, const Colour& colour); + void fillAlphaChannelWithGradient (const Image& alphaImage, int imageX, int imageY, const ColourGradient& gradient); + void fillAlphaChannelWithImage (const Image& alphaImage, int alphaImageX, int alphaImageY, + const Image& fillerImage, int fillerImageX, int fillerImageY, float alpha); + + void blendImage (const Image& sourceImage, int destX, int destY, int destW, int destH, + int sourceX, int sourceY, float alpha); + + void blendImageRescaling (const Image& sourceImage, int destX, int destY, int destW, int destH, + int sourceX, int sourceY, int sourceW, int sourceH, + float alpha, const Graphics::ResamplingQuality quality); + + void blendImageWarping (const Image& sourceImage, int srcClipX, int srcClipY, int srcClipW, int srcClipH, + const AffineTransform& transform, + float alpha, const Graphics::ResamplingQuality quality); + + void drawLine (double x1, double y1, double x2, double y2, const Colour& colour); + + void drawVerticalLine (const int x, double top, double bottom, const Colour& col); + void drawHorizontalLine (const int x, double top, double bottom, const Colour& col); + + juce_UseDebuggingNewOperator + +protected: + + OutputStream& out; + RectangleList* clip; + int totalWidth, totalHeight, xOffset, yOffset; + bool needToClip; + Colour lastColour; + + struct SavedState + { + SavedState (RectangleList* const clip, const int xOffset, const int yOffset); + ~SavedState(); + + RectangleList* clip; + const int xOffset, yOffset; + + private: + SavedState (const SavedState&); + const SavedState& operator= (const SavedState&); + }; + + OwnedArray stateStack; + + void writeClip(); + void writeColour (const Colour& colour); + void writePath (const Path& path) const; + void writeXY (const float x, const float y) const; + void writeTransform (const AffineTransform& trans) const; + void writeImage (const Image& im, const int sx, const int sy, const int maxW, const int maxH) const; + + LowLevelGraphicsPostScriptRenderer (const LowLevelGraphicsPostScriptRenderer& other); + const LowLevelGraphicsPostScriptRenderer& operator= (const LowLevelGraphicsPostScriptRenderer&); +}; + +#endif // __JUCE_LOWLEVELGRAPHICSPOSTSCRIPTRENDERER_JUCEHEADER__ +/********* End of inlined file: juce_LowLevelGraphicsPostScriptRenderer.h *********/ + +#endif +#ifndef __JUCE_LOWLEVELGRAPHICSSOFTWARERENDERER_JUCEHEADER__ + +/********* Start of inlined file: juce_LowLevelGraphicsSoftwareRenderer.h *********/ +#ifndef __JUCE_LOWLEVELGRAPHICSSOFTWARERENDERER_JUCEHEADER__ +#define __JUCE_LOWLEVELGRAPHICSSOFTWARERENDERER_JUCEHEADER__ + +/** + A lowest-common-denominator implementation of LowLevelGraphicsContext that does all + its rendering in memory. + + User code is not supposed to create instances of this class directly - do all your + rendering via the Graphics class instead. +*/ +class JUCE_API LowLevelGraphicsSoftwareRenderer : public LowLevelGraphicsContext +{ +public: + + LowLevelGraphicsSoftwareRenderer (Image& imageToRenderOn); + ~LowLevelGraphicsSoftwareRenderer(); + + bool isVectorDevice() const; + + void setOrigin (int x, int y); + + bool reduceClipRegion (int x, int y, int w, int h); + bool reduceClipRegion (const RectangleList& clipRegion); + void excludeClipRegion (int x, int y, int w, int h); + + void saveState(); + void restoreState(); + + bool clipRegionIntersects (int x, int y, int w, int h); + const Rectangle getClipBounds() const; + bool isClipEmpty() const; + + void fillRectWithColour (int x, int y, int w, int h, const Colour& colour, const bool replaceExistingContents); + void fillRectWithGradient (int x, int y, int w, int h, const ColourGradient& gradient); + + void fillPathWithColour (const Path& path, const AffineTransform& transform, const Colour& colour, EdgeTable::OversamplingLevel quality); + void fillPathWithGradient (const Path& path, const AffineTransform& transform, const ColourGradient& gradient, EdgeTable::OversamplingLevel quality); + void fillPathWithImage (const Path& path, const AffineTransform& transform, + const Image& image, int imageX, int imageY, float alpha, EdgeTable::OversamplingLevel quality); + + void fillAlphaChannelWithColour (const Image& alphaImage, int imageX, int imageY, const Colour& colour); + void fillAlphaChannelWithGradient (const Image& alphaImage, int imageX, int imageY, const ColourGradient& gradient); + void fillAlphaChannelWithImage (const Image& alphaImage, int alphaImageX, int alphaImageY, + const Image& fillerImage, int fillerImageX, int fillerImageY, float alpha); + + void blendImage (const Image& sourceImage, int destX, int destY, int destW, int destH, + int sourceX, int sourceY, float alpha); + + void blendImageRescaling (const Image& sourceImage, int destX, int destY, int destW, int destH, + int sourceX, int sourceY, int sourceW, int sourceH, + float alpha, const Graphics::ResamplingQuality quality); + + void blendImageWarping (const Image& sourceImage, int srcClipX, int srcClipY, int srcClipW, int srcClipH, + const AffineTransform& transform, + float alpha, const Graphics::ResamplingQuality quality); + + void drawLine (double x1, double y1, double x2, double y2, const Colour& colour); + + void drawVerticalLine (const int x, double top, double bottom, const Colour& col); + void drawHorizontalLine (const int x, double top, double bottom, const Colour& col); + + RectangleList* getRawClipRegion() throw() { return clip; } + + juce_UseDebuggingNewOperator + +protected: + + Image& image; + RectangleList* clip; + int xOffset, yOffset; + + struct SavedState + { + SavedState (RectangleList* const clip, const int xOffset, const int yOffset); + ~SavedState(); + + RectangleList* clip; + const int xOffset, yOffset; + + private: + SavedState (const SavedState&); + const SavedState& operator= (const SavedState&); + }; + + OwnedArray stateStack; + + void drawVertical (const int x, const double top, const double bottom, const Colour& col); + void drawHorizontal (const int y, const double top, const double bottom, const Colour& col); + + bool getPathBounds (int clipX, int clipY, int clipW, int clipH, + const Path& path, const AffineTransform& transform, + int& x, int& y, int& w, int& h) const; + + void clippedFillRectWithColour (const Rectangle& clipRect, int x, int y, int w, int h, const Colour& colour, const bool replaceExistingContents); + + void clippedFillPathWithColour (int clipX, int clipY, int clipW, int clipH, const Path& path, const AffineTransform& transform, const Colour& colour, EdgeTable::OversamplingLevel quality); + void clippedFillPathWithGradient (int clipX, int clipY, int clipW, int clipH, const Path& path, const AffineTransform& transform, const ColourGradient& gradient, EdgeTable::OversamplingLevel quality); + void clippedFillPathWithImage (int clipX, int clipY, int clipW, int clipH, const Path& path, const AffineTransform& transform, + const Image& image, int imageX, int imageY, float alpha, EdgeTable::OversamplingLevel quality); + + void clippedFillAlphaChannelWithColour (int clipX, int clipY, int clipW, int clipH, const Image& alphaImage, int alphaImageX, int alphaImageY, const Colour& colour); + void clippedFillAlphaChannelWithGradient (int clipX, int clipY, int clipW, int clipH, const Image& alphaImage, int alphaImageX, int alphaImageY, const ColourGradient& gradient); + void clippedFillAlphaChannelWithImage (int clipX, int clipY, int clipW, int clipH, const Image& alphaImage, int alphaImageX, int alphaImageY, + const Image& fillerImage, int fillerImageX, int fillerImageY, float alpha); + + void clippedBlendImage (int clipX, int clipY, int clipW, int clipH, const Image& sourceImage, + int destX, int destY, int destW, int destH, int sourceX, int sourceY, + float alpha); + + void clippedBlendImageWarping (int clipX, int clipY, int clipW, int clipH, const Image& sourceImage, + int srcClipX, int srcClipY, int srcClipW, int srcClipH, + const AffineTransform& transform, + float alpha, const Graphics::ResamplingQuality quality); + + void clippedDrawLine (int clipX, int clipY, int clipW, int clipH, double x1, double y1, double x2, double y2, const Colour& colour); + + void clippedDrawVerticalLine (int clipX, int clipY, int clipW, int clipH, const int x, double top, double bottom, const Colour& col); + void clippedDrawHorizontalLine (int clipX, int clipY, int clipW, int clipH, const int x, double top, double bottom, const Colour& col); + + LowLevelGraphicsSoftwareRenderer (const LowLevelGraphicsSoftwareRenderer& other); + const LowLevelGraphicsSoftwareRenderer& operator= (const LowLevelGraphicsSoftwareRenderer&); +}; + +#endif // __JUCE_LOWLEVELGRAPHICSSOFTWARERENDERER_JUCEHEADER__ +/********* End of inlined file: juce_LowLevelGraphicsSoftwareRenderer.h *********/ + +#endif +#ifndef __JUCE_RECTANGLEPLACEMENT_JUCEHEADER__ + +#endif +#ifndef __JUCE_AFFINETRANSFORM_JUCEHEADER__ + +#endif +#ifndef __JUCE_BORDERSIZE_JUCEHEADER__ + +#endif +#ifndef __JUCE_LINE_JUCEHEADER__ + +#endif +#ifndef __JUCE_PATH_JUCEHEADER__ + +#endif +#ifndef __JUCE_PATHITERATOR_JUCEHEADER__ + +/********* Start of inlined file: juce_PathIterator.h *********/ +#ifndef __JUCE_PATHITERATOR_JUCEHEADER__ +#define __JUCE_PATHITERATOR_JUCEHEADER__ + +/** + Flattens a Path object into a series of straight-line sections. + + Use one of these to iterate through a Path object, and it will convert + all the curves into line sections so it's easy to render or perform + geometric operations on. + + @see Path +*/ +class JUCE_API PathFlatteningIterator +{ +public: + + /** Creates a PathFlatteningIterator. + + After creation, use the next() method to initialise the fields in the + object with the first line's position. + + @param path the path to iterate along + @param transform a transform to apply to each point in the path being iterated + @param tolerence the amount by which the curves are allowed to deviate from the + lines into which they are being broken down - a higher tolerence + is a bit faster, but less smooth. + */ + PathFlatteningIterator (const Path& path, + const AffineTransform& transform = AffineTransform::identity, + float tolerence = 9.0f) throw(); + + /** Destructor. */ + ~PathFlatteningIterator() throw(); + + /** Fetches the next line segment from the path. + + This will update the member variables x1, y1, x2, y2, subPathIndex and closesSubPath + so that they describe the new line segment. + + @returns false when there are no more lines to fetch. + */ + bool next() throw(); + + /** The x position of the start of the current line segment. */ + float x1; + /** The y position of the start of the current line segment. */ + float y1; + /** The x position of the end of the current line segment. */ + float x2; + /** The y position of the end of the current line segment. */ + float y2; + + /** Indicates whether the current line segment is closing a sub-path. + + If the current line is the one that connects the end of a sub-path + back to the start again, this will be true. + */ + bool closesSubPath; + + /** The index of the current line within the current sub-path. + + E.g. you can use this to see whether the line is the first one in the + subpath by seeing if it's 0. + */ + int subPathIndex; + + /** Returns true if the current segment is the last in the current sub-path. */ + bool isLastInSubpath() const throw() { return stackPos == stackBase; } + + juce_UseDebuggingNewOperator + +private: + const Path& path; + const AffineTransform transform; + float* points; + float tolerence, subPathCloseX, subPathCloseY; + bool isIdentityTransform; + + float* stackBase; + float* stackPos; + int index, stackSize; + + PathFlatteningIterator (const PathFlatteningIterator&); + const PathFlatteningIterator& operator= (const PathFlatteningIterator&); +}; + +#endif // __JUCE_PATHITERATOR_JUCEHEADER__ +/********* End of inlined file: juce_PathIterator.h *********/ + +#endif +#ifndef __JUCE_PATHSTROKETYPE_JUCEHEADER__ + +#endif +#ifndef __JUCE_POINT_JUCEHEADER__ + +#endif +#ifndef __JUCE_POSITIONEDRECTANGLE_JUCEHEADER__ + +/********* Start of inlined file: juce_PositionedRectangle.h *********/ +#ifndef __JUCE_POSITIONEDRECTANGLE_JUCEHEADER__ +#define __JUCE_POSITIONEDRECTANGLE_JUCEHEADER__ + +/** + A rectangle whose co-ordinates can be defined in terms of absolute or + proportional distances. + + Designed mainly for storing component positions, this gives you a lot of + control over how each co-ordinate is stored, either as an absolute position, + or as a proportion of the size of a parent rectangle. + + It also allows you to define the anchor points by which the rectangle is + positioned, so for example you could specify that the top right of the + rectangle should be an absolute distance from its parent's bottom-right corner. + + This object can be stored as a string, which takes the form "x y w h", including + symbols like '%' and letters to indicate the anchor point. See its toString() + method for more info. + + Example usage: + @code + class MyComponent + { + void resized() + { + // this will set the child component's x to be 20% of our width, its y + // to be 30, its width to be 150, and its height to be 50% of our + // height.. + const PositionedRectangle pos1 ("20% 30 150 50%"); + pos1.applyToComponent (*myChildComponent1); + + // this will inset the child component with a gap of 10 pixels + // around each of its edges.. + const PositionedRectangle pos2 ("10 10 20M 20M"); + pos2.applyToComponent (*myChildComponent2); + } + }; + @endcode +*/ +class JUCE_API PositionedRectangle +{ +public: + + /** Creates an empty rectangle with all co-ordinates set to zero. + + The default anchor point is top-left; the default + */ + PositionedRectangle() throw(); + + /** Initialises a PositionedRectangle from a saved string version. + + The string must be in the format generated by toString(). + */ + PositionedRectangle (const String& stringVersion) throw(); + + /** Creates a copy of another PositionedRectangle. */ + PositionedRectangle (const PositionedRectangle& other) throw(); + + /** Copies another PositionedRectangle. */ + const PositionedRectangle& operator= (const PositionedRectangle& other) throw(); + + /** Destructor. */ + ~PositionedRectangle() throw(); + + /** Returns a string version of this position, from which it can later be + re-generated. + + The format is four co-ordinates, "x y w h". + + - If a co-ordinate is absolute, it is stored as an integer, e.g. "100". + - If a co-ordinate is proportional to its parent's width or height, it is stored + as a percentage, e.g. "80%". + - If the X or Y co-ordinate is relative to the parent's right or bottom edge, the + number has "R" appended to it, e.g. "100R" means a distance of 100 pixels from + the parent's right-hand edge. + - If the X or Y co-ordinate is relative to the parent's centre, the number has "C" + appended to it, e.g. "-50C" would be 50 pixels left of the parent's centre. + - If the X or Y co-ordinate should be anchored at the component's right or bottom + edge, then it has "r" appended to it. So "-50Rr" would mean that this component's + right-hand edge should be 50 pixels left of the parent's right-hand edge. + - If the X or Y co-ordinate should be anchored at the component's centre, then it + has "c" appended to it. So "-50Rc" would mean that this component's + centre should be 50 pixels left of the parent's right-hand edge. "40%c" means that + this component's centre should be placed 40% across the parent's width. + - If it's a width or height that should use the parentSizeMinusAbsolute mode, then + the number has "M" appended to it. + + To reload a stored string, use the constructor that takes a string parameter. + */ + const String toString() const throw(); + + /** Calculates the absolute position, given the size of the space that + it should go in. + + This will work out any proportional distances and sizes relative to the + target rectangle, and will return the absolute position. + + @see applyToComponent + */ + const Rectangle getRectangle (const Rectangle& targetSpaceToBeRelativeTo) const throw(); + + /** Same as getRectangle(), but returning the values as doubles rather than ints. + */ + void getRectangleDouble (const Rectangle& targetSpaceToBeRelativeTo, + double& x, + double& y, + double& width, + double& height) const throw(); + + /** This sets the bounds of the given component to this position. + + This is equivalent to writing: + @code + comp.setBounds (getRectangle (Rectangle (0, 0, comp.getParentWidth(), comp.getParentHeight()))); + @endcode + + @see getRectangle, updateFromComponent + */ + void applyToComponent (Component& comp) const throw(); + + /** Updates this object's co-ordinates to match the given rectangle. + + This will set all co-ordinates based on the given rectangle, re-calculating + any proportional distances, and using the current anchor points. + + So for example if the x co-ordinate mode is currently proportional, this will + re-calculate x based on the rectangle's relative position within the target + rectangle's width. + + If the target rectangle's width or height are zero then it may not be possible + to re-calculate some proportional co-ordinates. In this case, those co-ordinates + will not be changed. + */ + void updateFrom (const Rectangle& newPosition, + const Rectangle& targetSpaceToBeRelativeTo) throw(); + + /** Same functionality as updateFrom(), but taking doubles instead of ints. + */ + void updateFromDouble (const double x, const double y, + const double width, const double height, + const Rectangle& targetSpaceToBeRelativeTo) throw(); + + /** Updates this object's co-ordinates to match the bounds of this component. + + This is equivalent to calling updateFrom() with the component's bounds and + it parent size. + + If the component doesn't currently have a parent, then proportional co-ordinates + might not be updated because it would need to know the parent's size to do the + maths for this. + */ + void updateFromComponent (const Component& comp) throw(); + + /** Specifies the point within the rectangle, relative to which it should be positioned. */ + enum AnchorPoint + { + anchorAtLeftOrTop = 1 << 0, /**< The x or y co-ordinate specifies where the left or top edge of the rectangle should be. */ + anchorAtRightOrBottom = 1 << 1, /**< The x or y co-ordinate specifies where the right or bottom edge of the rectangle should be. */ + anchorAtCentre = 1 << 2 /**< The x or y co-ordinate specifies where the centre of the rectangle should be. */ + }; + + /** Specifies how an x or y co-ordinate should be interpreted. */ + enum PositionMode + { + absoluteFromParentTopLeft = 1 << 3, /**< The x or y co-ordinate specifies an absolute distance from the parent's top or left edge. */ + absoluteFromParentBottomRight = 1 << 4, /**< The x or y co-ordinate specifies an absolute distance from the parent's bottom or right edge. */ + absoluteFromParentCentre = 1 << 5, /**< The x or y co-ordinate specifies an absolute distance from the parent's centre. */ + proportionOfParentSize = 1 << 6 /**< The x or y co-ordinate specifies a proportion of the parent's width or height, measured from the parent's top or left. */ + }; + + /** Specifies how the width or height should be interpreted. */ + enum SizeMode + { + absoluteSize = 1 << 0, /**< The width or height specifies an absolute size. */ + parentSizeMinusAbsolute = 1 << 1, /**< The width or height is an amount that should be subtracted from the parent's width or height. */ + proportionalSize = 1 << 2, /**< The width or height specifies a proportion of the parent's width or height. */ + }; + + /** Sets all options for all co-ordinates. + + This requires a reference rectangle to be specified, because if you're changing any + of the modes from proportional to absolute or vice-versa, then it'll need to convert + the co-ordinates, and will need to know the parent size so it can calculate this. + */ + void setModes (const AnchorPoint xAnchorMode, + const PositionMode xPositionMode, + const AnchorPoint yAnchorMode, + const PositionMode yPositionMode, + const SizeMode widthMode, + const SizeMode heightMode, + const Rectangle& targetSpaceToBeRelativeTo) throw(); + + /** Returns the anchoring mode for the x co-ordinate. + To change any of the modes, use setModes(). + */ + AnchorPoint getAnchorPointX() const throw(); + + /** Returns the positioning mode for the x co-ordinate. + To change any of the modes, use setModes(). + */ + PositionMode getPositionModeX() const throw(); + + /** Returns the raw x co-ordinate. + + If the x position mode is absolute, then this will be the absolute value. If it's + proportional, then this will be a fractional proportion, where 1.0 means the full + width of the parent space. + */ + double getX() const throw() { return x; } + + /** Sets the raw value of the x co-ordinate. + + See getX() for the meaning of this value. + */ + void setX (const double newX) throw() { x = newX; } + + /** Returns the anchoring mode for the y co-ordinate. + To change any of the modes, use setModes(). + */ + AnchorPoint getAnchorPointY() const throw(); + + /** Returns the positioning mode for the y co-ordinate. + To change any of the modes, use setModes(). + */ + PositionMode getPositionModeY() const throw(); + + /** Returns the raw y co-ordinate. + + If the y position mode is absolute, then this will be the absolute value. If it's + proportional, then this will be a fractional proportion, where 1.0 means the full + height of the parent space. + */ + double getY() const throw() { return y; } + + /** Sets the raw value of the y co-ordinate. + + See getY() for the meaning of this value. + */ + void setY (const double newY) throw() { y = newY; } + + /** Returns the mode used to calculate the width. + To change any of the modes, use setModes(). + */ + SizeMode getWidthMode() const throw(); + + /** Returns the raw width value. + + If the width mode is absolute, then this will be the absolute value. If the mode is + proportional, then this will be a fractional proportion, where 1.0 means the full + width of the parent space. + */ + double getWidth() const throw() { return w; } + + /** Sets the raw width value. + + See getWidth() for the details about what this value means. + */ + void setWidth (const double newWidth) throw() { w = newWidth; } + + /** Returns the mode used to calculate the height. + To change any of the modes, use setModes(). + */ + SizeMode getHeightMode() const throw(); + + /** Returns the raw height value. + + If the height mode is absolute, then this will be the absolute value. If the mode is + proportional, then this will be a fractional proportion, where 1.0 means the full + height of the parent space. + */ + double getHeight() const throw() { return h; } + + /** Sets the raw height value. + + See getHeight() for the details about what this value means. + */ + void setHeight (const double newHeight) throw() { h = newHeight; } + + /** If the size and position are constance, and wouldn't be affected by changes + in the parent's size, then this will return true. + */ + bool isPositionAbsolute() const throw(); + + /** Compares two objects. */ + const bool operator== (const PositionedRectangle& other) const throw(); + + /** Compares two objects. */ + const bool operator!= (const PositionedRectangle& other) const throw(); + + juce_UseDebuggingNewOperator + +private: + double x, y, w, h; + uint8 xMode, yMode, wMode, hMode; + + void addPosDescription (String& result, const uint8 mode, const double value) const throw(); + void addSizeDescription (String& result, const uint8 mode, const double value) const throw(); + void decodePosString (const String& s, uint8& mode, double& value) throw(); + void decodeSizeString (const String& s, uint8& mode, double& value) throw(); + void applyPosAndSize (double& xOut, double& wOut, const double x, const double w, + const uint8 xMode, const uint8 wMode, + const int parentPos, const int parentSize) const throw(); + void updatePosAndSize (double& xOut, double& wOut, double x, const double w, + const uint8 xMode, const uint8 wMode, + const int parentPos, const int parentSize) const throw(); +}; + +#endif // __JUCE_POSITIONEDRECTANGLE_JUCEHEADER__ +/********* End of inlined file: juce_PositionedRectangle.h *********/ + +#endif +#ifndef __JUCE_RECTANGLE_JUCEHEADER__ + +#endif +#ifndef __JUCE_RECTANGLELIST_JUCEHEADER__ + +#endif +#ifndef __JUCE_IMAGE_JUCEHEADER__ + +#endif +#ifndef __JUCE_IMAGECACHE_JUCEHEADER__ + +/********* Start of inlined file: juce_ImageCache.h *********/ +#ifndef __JUCE_IMAGECACHE_JUCEHEADER__ +#define __JUCE_IMAGECACHE_JUCEHEADER__ + +/** + A global cache of images that have been loaded from files or memory. + + If you're loading an image and may need to use the image in more than one + place, this is used to allow the same image to be shared rather than loading + multiple copies into memory. + + Another advantage is that after images are released, they will be kept in + memory for a few seconds before it is actually deleted, so if you're repeatedly + loading/deleting the same image, it'll reduce the chances of having to reload it + each time. + + @see Image, ImageFileFormat +*/ +class JUCE_API ImageCache : private DeletedAtShutdown, + private Timer +{ +public: + + /** Loads an image from a file, (or just returns the image if it's already cached). + + If the cache already contains an image that was loaded from this file, + that image will be returned. Otherwise, this method will try to load the + file, add it to the cache, and return it. + + It's very important not to delete the image that is returned - instead use + the ImageCache::release() method. + + Also, remember that the image returned is shared, so drawing into it might + affect other things that are using it! + + @param file the file to try to load + @returns the image, or null if it there was an error loading it + @see release, getFromMemory, getFromCache, ImageFileFormat::loadFrom + */ + static Image* getFromFile (const File& file); + + /** Loads an image from an in-memory image file, (or just returns the image if it's already cached). + + If the cache already contains an image that was loaded from this block of memory, + that image will be returned. Otherwise, this method will try to load the + file, add it to the cache, and return it. + + It's very important not to delete the image that is returned - instead use + the ImageCache::release() method. + + Also, remember that the image returned is shared, so drawing into it might + affect other things that are using it! + + @param imageData the block of memory containing the image data + @param dataSize the data size in bytes + @returns the image, or null if it there was an error loading it + @see release, getFromMemory, getFromCache, ImageFileFormat::loadFrom + */ + static Image* getFromMemory (const void* imageData, + const int dataSize); + + /** Releases an image that was previously created by the ImageCache. + + If an image has been returned by the getFromFile() or getFromMemory() methods, + it mustn't be deleted directly, but should be released with this method + instead. + + @see getFromFile, getFromMemory + */ + static void release (Image* const imageToRelease); + + /** Checks whether an image is in the cache or not. + + @returns true if the image is currently in the cache + */ + static bool isImageInCache (Image* const imageToLookFor); + + /** Increments the reference-count for a cached image. + + If the image isn't in the cache, this method won't do anything. + */ + static void incReferenceCount (Image* const image); + + /** Checks the cache for an image with a particular hashcode. + + If there's an image in the cache with this hashcode, it will be returned, + otherwise it will return zero. + + If an image is returned, it must be released with the release() method + when no longer needed, to maintain the correct reference counts. + + @param hashCode the hash code that would have been associated with the + image by addImageToCache() + @see addImageToCache + */ + static Image* getFromHashCode (const int64 hashCode); + + /** Adds an image to the cache with a user-defined hash-code. + + After calling this, responsibilty for deleting the image will be taken + by the ImageCache. + + The image will be initially be given a reference count of 1, so call + the release() method to delete it. + + @param image the image to add + @param hashCode the hash-code to associate with it + @see getFromHashCode + */ + static void addImageToCache (Image* const image, + const int64 hashCode); + + /** Changes the amount of time before an unused image will be removed from the cache. + + By default this is about 5 seconds. + */ + static void setCacheTimeout (const int millisecs); + + juce_UseDebuggingNewOperator + +private: + + CriticalSection lock; + VoidArray images; + + ImageCache() throw(); + ImageCache (const ImageCache&); + const ImageCache& operator= (const ImageCache&); + ~ImageCache(); + + void timerCallback(); +}; + +#endif // __JUCE_IMAGECACHE_JUCEHEADER__ +/********* End of inlined file: juce_ImageCache.h *********/ + +#endif +#ifndef __JUCE_IMAGECONVOLUTIONKERNEL_JUCEHEADER__ + +/********* Start of inlined file: juce_ImageConvolutionKernel.h *********/ +#ifndef __JUCE_IMAGECONVOLUTIONKERNEL_JUCEHEADER__ +#define __JUCE_IMAGECONVOLUTIONKERNEL_JUCEHEADER__ + +/** + Represents a filter kernel to use in convoluting an image. + + @see Image::applyConvolution +*/ +class JUCE_API ImageConvolutionKernel +{ +public: + + /** Creates an empty convulution kernel. + + @param size the length of each dimension of the kernel, so e.g. if the size + is 5, it will create a 5x5 kernel + */ + ImageConvolutionKernel (const int size) throw(); + + /** Destructor. */ + ~ImageConvolutionKernel() throw(); + + /** Resets all values in the kernel to zero. + */ + void clear() throw(); + + /** Sets the value of a specific cell in the kernel. + + The x and y parameters must be in the range 0 < x < getKernelSize(). + + @see setOverallSum + */ + void setKernelValue (const int x, + const int y, + const float value) throw(); + + /** Rescales all values in the kernel to make the total add up to a fixed value. + + This will multiply all values in the kernel by (desiredTotalSum / currentTotalSum). + */ + void setOverallSum (const float desiredTotalSum) throw(); + + /** Multiplies all values in the kernel by a value. */ + void rescaleAllValues (const float multiplier) throw(); + + /** Intialises the kernel for a gaussian blur. + + @param blurRadius this may be larger or smaller than the kernel's actual + size but this will obviously be wasteful or clip at the + edges. Ideally the kernel should be just larger than + (blurRadius * 2). + */ + void createGaussianBlur (const float blurRadius) throw(); + + /** Returns the size of the kernel. + + E.g. if it's a 3x3 kernel, this returns 3. + */ + int getKernelSize() const throw() { return size; } + + /** Returns a 2-dimensional array of the kernel's values. + + The size of each dimension of the array will be getKernelSize(). + */ + float** getValues() const throw() { return values; } + + /** Applies the kernel to an image. + + @param destImage the image that will receive the resultant convoluted pixels. + @param sourceImage an optional source image to read from - if this is 0, then the + destination image will be used as the source. If an image is + specified, it must be exactly the same size and type as the destination + image. + @param x the region of the image to apply the filter to + @param y the region of the image to apply the filter to + @param width the region of the image to apply the filter to + @param height the region of the image to apply the filter to + */ + void applyToImage (Image& destImage, + const Image* sourceImage, + int x, + int y, + int width, + int height) const; + + juce_UseDebuggingNewOperator + +private: + float** values; + int size; + + // no reason not to implement these one day.. + ImageConvolutionKernel (const ImageConvolutionKernel&); + const ImageConvolutionKernel& operator= (const ImageConvolutionKernel&); +}; + +#endif // __JUCE_IMAGECONVOLUTIONKERNEL_JUCEHEADER__ +/********* End of inlined file: juce_ImageConvolutionKernel.h *********/ + +#endif +#ifndef __JUCE_IMAGEFILEFORMAT_JUCEHEADER__ + +/********* Start of inlined file: juce_ImageFileFormat.h *********/ +#ifndef __JUCE_IMAGEFILEFORMAT_JUCEHEADER__ +#define __JUCE_IMAGEFILEFORMAT_JUCEHEADER__ + +/** + Base-class for codecs that can read and write image file formats such + as PNG, JPEG, etc. + + This class also contains static methods to make it easy to load images + from files, streams or from memory. + + @see Image, ImageCache +*/ +class JUCE_API ImageFileFormat +{ +protected: + + /** Creates an ImageFormat. */ + ImageFileFormat() throw() {} + +public: + /** Destructor. */ + virtual ~ImageFileFormat() throw() {} + + /** Returns a description of this file format. + + E.g. "JPEG", "PNG" + */ + virtual const String getFormatName() = 0; + + /** Returns true if the given stream seems to contain data that this format + understands. + + The format class should only read the first few bytes of the stream and sniff + for header bytes that it understands. + + @see decodeImage + */ + virtual bool canUnderstand (InputStream& input) = 0; + + /** Tries to decode and return an image from the given stream. + + This will be called for an image format after calling its canUnderStand() method + to see if it can handle the stream. + + @param input the stream to read the data from. The stream will be positioned + at the start of the image data (but this may not necessarily + be position 0) + @returns the image that was decoded, or 0 if it fails. It's the + caller's responsibility to delete this image when no longer needed. + @see loadFrom + */ + virtual Image* decodeImage (InputStream& input) = 0; + + /** Attempts to write an image to a stream. + + To specify extra information like encoding quality, there will be appropriate parameters + in the subclasses of the specific file types. + + @returns true if it nothing went wrong. + */ + virtual bool writeImageToStream (const Image& sourceImage, + OutputStream& destStream) = 0; + + /** Tries the built-in decoders to see if it can find one to read this stream. + + There are currently built-in decoders for PNG, JPEG and GIF formats. + + The object that is returned should not be deleted by the caller. + + @see canUnderstand, decodeImage, loadFrom + */ + static ImageFileFormat* findImageFormatForStream (InputStream& input); + + /** Tries to load an image from a stream. + + This will use the findImageFormatForStream() method to locate a suitable + codec, and use that to load the image. + + @returns the image that was decoded, or 0 if it fails to load one. It's the + caller's responsibility to delete this image when no longer needed. + */ + static Image* loadFrom (InputStream& input); + + /** Tries to load an image from a file. + + This will use the findImageFormatForStream() method to locate a suitable + codec, and use that to load the image. + + @returns the image that was decoded, or 0 if it fails to load one. It's the + caller's responsibility to delete this image when no longer needed. + */ + static Image* loadFrom (const File& file); + + /** Tries to load an image from a block of raw image data. + + This will use the findImageFormatForStream() method to locate a suitable + codec, and use that to load the image. + + @returns the image that was decoded, or 0 if it fails to load one. It's the + caller's responsibility to delete this image when no longer needed. + */ + static Image* loadFrom (const void* rawData, + const int numBytesOfData); + +}; + +/** + A type of ImageFileFormat for reading and writing PNG files. + + @see ImageFileFormat, JPEGImageFormat +*/ +class PNGImageFormat : public ImageFileFormat +{ +public: + + PNGImageFormat() throw(); + ~PNGImageFormat() throw(); + + const String getFormatName(); + bool canUnderstand (InputStream& input); + + Image* decodeImage (InputStream& input); + + bool writeImageToStream (const Image& sourceImage, OutputStream& destStream); +}; + +/** + A type of ImageFileFormat for reading and writing JPEG files. + + @see ImageFileFormat, PNGImageFormat +*/ +class JPEGImageFormat : public ImageFileFormat +{ +public: + + JPEGImageFormat() throw(); + ~JPEGImageFormat() throw(); + + /** Specifies the quality to be used when writing a JPEG file. + + @param newQuality a value 0 to 1.0, where 0 is low quality, 1.0 is best, or + any negative value is "default" quality + */ + void setQuality (const float newQuality); + + const String getFormatName(); + + bool canUnderstand (InputStream& input); + + Image* decodeImage (InputStream& input); + + bool writeImageToStream (const Image& sourceImage, OutputStream& destStream); + +private: + float quality; +}; + +#endif // __JUCE_IMAGEFILEFORMAT_JUCEHEADER__ +/********* End of inlined file: juce_ImageFileFormat.h *********/ + +#endif +#ifndef __JUCE_DRAWABLE_JUCEHEADER__ + +/********* Start of inlined file: juce_Drawable.h *********/ +#ifndef __JUCE_DRAWABLE_JUCEHEADER__ +#define __JUCE_DRAWABLE_JUCEHEADER__ + +/** + The base class for objects which can draw themselves, e.g. polygons, images, etc. + + @see DrawableComposite, DrawableImage, DrawablePath, DrawableText +*/ +class JUCE_API Drawable +{ +protected: + + /** The base class can't be instantiated directly. + + @see DrawableComposite, DrawableImage, DrawablePath, DrawableText + */ + Drawable(); + +public: + /** Destructor. */ + virtual ~Drawable(); + + /** Creates a deep copy of this Drawable object. + + Use this to create a new copy of this and any sub-objects in the tree. + */ + virtual Drawable* createCopy() const = 0; + + /** Renders this Drawable object. + + This is the main rendering method you should call to render a Drawable. + + @see drawWithin + */ + virtual void draw (Graphics& g, + const AffineTransform& transform = AffineTransform::identity) const = 0; + + /** Renders the Drawable at a given offset within the Graphics context. + + The co-ordinates passed-in are used to translate the object relative to its own + origin before drawing it - this is basically a quick way of saying: + + @code + draw (g, AffineTransform::translation (x, y)). + @endcode + */ + void drawAt (Graphics& g, + const float x, + const float y) const; + + /** Renders the Drawable within a rectangle, scaling it to fit neatly inside without + changing its aspect-ratio. + + The object can placed arbitrarily within the rectangle based on a Justification type, + and can either be made as big as possible, or just reduced to fit. + + @param g the graphics context to render onto + @param destX top-left of the target rectangle to fit it into + @param destY top-left of the target rectangle to fit it into + @param destWidth size of the target rectangle to fit the image into + @param destHeight size of the target rectangle to fit the image into + @param placement defines the alignment and rescaling to use to fit + this object within the target rectangle. + */ + void drawWithin (Graphics& g, + const int destX, + const int destY, + const int destWidth, + const int destHeight, + const RectanglePlacement& placement) const; + + /** Returns the smallest rectangle that can contain this Drawable object. + + Co-ordinates are relative to the object's own origin. + */ + virtual void getBounds (float& x, float& y, float& width, float& height) const = 0; + + /** Returns true if the given point is somewhere inside this Drawable. + + Co-ordinates are relative to the object's own origin. + */ + virtual bool hitTest (float x, float y) const = 0; + + /** Returns the name given to this drawable. + @see setName + */ + const String& getName() const throw() { return name; } + + /** Assigns a name to this drawable. */ + void setName (const String& newName) throw() { name = newName; } + + /** Tries to turn some kind of image file into a drawable. + + The data could be an image that the ImageFileFormat class understands, or it + could be SVG. + */ + static Drawable* createFromImageData (const void* data, const int numBytes); + + /** Tries to turn a stream containing some kind of image data into a drawable. + + The data could be an image that the ImageFileFormat class understands, or it + could be SVG. + */ + static Drawable* createFromImageDataStream (InputStream& dataSource); + + /** Tries to turn a file containing some kind of image data into a drawable. + + The data could be an image that the ImageFileFormat class understands, or it + could be SVG. + */ + static Drawable* createFromImageFile (const File& file); + + /** Attempts to parse an SVG (Scalable Vector Graphics) document, and to turn this + into a Drawable tree. + + The object returned must be deleted by the caller. If something goes wrong + while parsing, it may return 0. + + SVG is a pretty large and complex spec, and this doesn't aim to be a full + implementation, but it can return the basic vector objects. + */ + static Drawable* createFromSVG (const XmlElement& svgDocument); + + juce_UseDebuggingNewOperator + +private: + Drawable (const Drawable&); + const Drawable& operator= (const Drawable&); + + String name; +}; + +#endif // __JUCE_DRAWABLE_JUCEHEADER__ +/********* End of inlined file: juce_Drawable.h *********/ + +#endif +#ifndef __JUCE_DRAWABLECOMPOSITE_JUCEHEADER__ + +/********* Start of inlined file: juce_DrawableComposite.h *********/ +#ifndef __JUCE_DRAWABLECOMPOSITE_JUCEHEADER__ +#define __JUCE_DRAWABLECOMPOSITE_JUCEHEADER__ + +/** + A drawable object which acts as a container for a set of other Drawables. + + @see Drawable +*/ +class JUCE_API DrawableComposite : public Drawable +{ +public: + + /** Creates a composite Drawable. + */ + DrawableComposite(); + + /** Destructor. */ + virtual ~DrawableComposite(); + + /** Adds a new sub-drawable to this one. + + This passes in a Drawable pointer for this object to look after. To add a copy + of a drawable, use the form of this method that takes a Drawable reference instead. + + @param drawable the object to add - this will be deleted automatically + when no longer needed, so the caller mustn't keep any + pointers to it. + @param transform the transform to apply to this drawable when it's being + drawn + @param index where to insert it in the list of drawables. 0 is the back, + -1 is the front, or any value from 0 and getNumDrawables() + can be used + @see removeDrawable + */ + void insertDrawable (Drawable* drawable, + const AffineTransform& transform = AffineTransform::identity, + const int index = -1); + + /** Adds a new sub-drawable to this one. + + This takes a copy of a Drawable and adds it to this object. To pass in a Drawable + for this object to look after, use the form of this method that takes a Drawable + pointer instead. + + @param drawable the object to add - an internal copy will be made of this object + @param transform the transform to apply to this drawable when it's being + drawn + @param index where to insert it in the list of drawables. 0 is the back, + -1 is the front, or any value from 0 and getNumDrawables() + can be used + @see removeDrawable + */ + void insertDrawable (const Drawable& drawable, + const AffineTransform& transform = AffineTransform::identity, + const int index = -1); + + /** Deletes one of the Drawable objects. + + @param index the index of the drawable to delete, between 0 + and (getNumDrawables() - 1). + @see insertDrawable, getNumDrawables + */ + void removeDrawable (const int index); + + /** Returns the number of drawables contained inside this one. + + @see getDrawable + */ + int getNumDrawables() const throw() { return drawables.size(); } + + /** Returns one of the drawables that are contained in this one. + + Each drawable also has a transform associated with it - you can use getDrawableTransform() + to find it. + + The pointer returned is managed by this object and will be deleted when no longer + needed, so be careful what you do with it. + + @see getNumDrawables + */ + Drawable* getDrawable (const int index) const throw() { return drawables [index]; } + + /** Returns the transform that applies to one of the drawables that are contained in this one. + + The pointer returned is managed by this object and will be deleted when no longer + needed, so be careful what you do with it. + + @see getNumDrawables + */ + const AffineTransform* getDrawableTransform (const int index) const throw() { return transforms [index]; } + + /** Brings one of the Drawables to the front. + + @param index the index of the drawable to move, between 0 + and (getNumDrawables() - 1). + @see insertDrawable, getNumDrawables + */ + void bringToFront (const int index); + + /** @internal */ + void draw (Graphics& g, const AffineTransform& transform) const; + /** @internal */ + void getBounds (float& x, float& y, float& width, float& height) const; + /** @internal */ + bool hitTest (float x, float y) const; + /** @internal */ + Drawable* createCopy() const; + + juce_UseDebuggingNewOperator + +private: + OwnedArray drawables; + OwnedArray transforms; + + DrawableComposite (const DrawableComposite&); + const DrawableComposite& operator= (const DrawableComposite&); +}; + +#endif // __JUCE_DRAWABLECOMPOSITE_JUCEHEADER__ +/********* End of inlined file: juce_DrawableComposite.h *********/ + +#endif +#ifndef __JUCE_DRAWABLEIMAGE_JUCEHEADER__ + +/********* Start of inlined file: juce_DrawableImage.h *********/ +#ifndef __JUCE_DRAWABLEIMAGE_JUCEHEADER__ +#define __JUCE_DRAWABLEIMAGE_JUCEHEADER__ + +/** + A drawable object which is a bitmap image. + + @see Drawable +*/ +class JUCE_API DrawableImage : public Drawable +{ +public: + + DrawableImage(); + + /** Destructor. */ + virtual ~DrawableImage(); + + /** Sets the image that this drawable will render. + + An internal copy is made of the image passed-in. If you want to provide an + image that this object can take charge of without needing to create a copy, + use the other setImage() method. + */ + void setImage (const Image& imageToCopy); + + /** Sets the image that this drawable will render. + + An internal copy of this will not be made, so the caller mustn't delete + the image while it's still being used by this object. + + A good way to use this is with the ImageCache - if you create an image + with ImageCache and pass it in here with releaseWhenNotNeeded = true, then + it'll be released neatly with its reference count being decreased. + + @param imageToUse the image to render + @param releaseWhenNotNeeded if false, a simple pointer is kept to the image; if true, + then the image will be deleted when this object no longer + needs it - unless the image was created by the ImageCache, + in which case it will be released with ImageCache::release(). + */ + void setImage (Image* imageToUse, + const bool releaseWhenNotNeeded); + + /** Returns the current image. */ + Image* getImage() const throw() { return image; } + + /** Clears (and possibly deletes) the currently set image. */ + void clearImage(); + + /** Sets the opacity to use when drawing the image. */ + void setOpacity (const float newOpacity); + + /** Returns the image's opacity. */ + float getOpacity() const throw() { return opacity; } + + /** Sets a colour to draw over the image's alpha channel. + + By default this is transparent so isn't drawn, but if you set a non-transparent + colour here, then it will be overlaid on the image, using the image's alpha + channel as a mask. + + This is handy for doing things like darkening or lightening an image by overlaying + it with semi-transparent black or white. + */ + void setOverlayColour (const Colour& newOverlayColour); + + /** Returns the overlay colour. */ + const Colour& getOverlayColour() const throw() { return overlayColour; } + + /** @internal */ + void draw (Graphics& g, const AffineTransform& transform) const; + /** @internal */ + void getBounds (float& x, float& y, float& width, float& height) const; + /** @internal */ + bool hitTest (float x, float y) const; + /** @internal */ + Drawable* createCopy() const; + + juce_UseDebuggingNewOperator + +private: + Image* image; + bool canDeleteImage; + float opacity; + Colour overlayColour; + + DrawableImage (const DrawableImage&); + const DrawableImage& operator= (const DrawableImage&); +}; + +#endif // __JUCE_DRAWABLEIMAGE_JUCEHEADER__ +/********* End of inlined file: juce_DrawableImage.h *********/ + +#endif +#ifndef __JUCE_DRAWABLEPATH_JUCEHEADER__ + +/********* Start of inlined file: juce_DrawablePath.h *********/ +#ifndef __JUCE_DRAWABLEPATH_JUCEHEADER__ +#define __JUCE_DRAWABLEPATH_JUCEHEADER__ + +/** + A drawable object which renders a filled or outlined shape. + + @see Drawable +*/ +class JUCE_API DrawablePath : public Drawable +{ +public: + + /** Creates a DrawablePath. + */ + DrawablePath(); + + /** Destructor. */ + virtual ~DrawablePath(); + + /** Changes the path that will be drawn. + + @see setSolidFill, setOutline + */ + void setPath (const Path& newPath); + + /** Returns the current path. */ + const Path& getPath() const throw() { return path; } + + /** Sets a colour to fill the path with. + + This colour is used to fill the path - if you don't want the path to be + filled (e.g. if you're just drawing an outline), set this colour to be + transparent. + + @see setPath, setOutline + */ + void setSolidFill (const Colour& newColour); + + /** Sets a custom brush to use to fill the path. + + @see setSolidFill + */ + void setFillBrush (const Brush& newBrush); + + /** Returns the brush currently being used to fill the shape. */ + Brush* getCurrentBrush() const throw() { return fillBrush; } + + /** Changes the properties of the outline that will be drawn around the path. + + If the thickness value is 0, no outline will be drawn. If one is drawn, the + colour passed-in here will be used for it. + + @see setPath, setSolidFill + */ + void setOutline (const float thickness, + const Colour& outlineColour); + + /** Changes the properties of the outline that will be drawn around the path. + + If the stroke type has 0 thickness, no outline will be drawn. + + @see setPath, setSolidFill + */ + void setOutline (const PathStrokeType& strokeType, + const Brush& strokeBrush); + + /** Returns the current outline style. */ + const PathStrokeType& getOutlineStroke() const throw() { return strokeType; } + + /** Returns the brush currently being used to draw the outline. */ + Brush* getOutlineBrush() const throw() { return strokeBrush; } + + /** @internal */ + void draw (Graphics& g, const AffineTransform& transform) const; + /** @internal */ + void getBounds (float& x, float& y, float& width, float& height) const; + /** @internal */ + bool hitTest (float x, float y) const; + /** @internal */ + Drawable* createCopy() const; + + juce_UseDebuggingNewOperator + +private: + Path path, outline; + Brush* fillBrush; + Brush* strokeBrush; + PathStrokeType strokeType; + + void updateOutline(); + + DrawablePath (const DrawablePath&); + const DrawablePath& operator= (const DrawablePath&); +}; + +#endif // __JUCE_DRAWABLEPATH_JUCEHEADER__ +/********* End of inlined file: juce_DrawablePath.h *********/ + +#endif +#ifndef __JUCE_DRAWABLETEXT_JUCEHEADER__ + +/********* Start of inlined file: juce_DrawableText.h *********/ +#ifndef __JUCE_DRAWABLETEXT_JUCEHEADER__ +#define __JUCE_DRAWABLETEXT_JUCEHEADER__ + +/** + A drawable object which renders a line of text. + + @see Drawable +*/ +class JUCE_API DrawableText : public Drawable +{ +public: + + /** Creates a DrawableText object. */ + DrawableText(); + + /** Destructor. */ + virtual ~DrawableText(); + + /** Sets the block of text to render */ + void setText (const GlyphArrangement& newText); + + /** Sets a single line of text to render. + + This is a convenient method of adding a single line - for + more complex text, use the setText() that takes a + GlyphArrangement instead. + */ + void setText (const String& newText, const Font& fontToUse); + + /** Returns the text arrangement that was set with setText(). */ + const GlyphArrangement& getText() const throw() { return text; } + + /** Sets the colour of the text. */ + void setColour (const Colour& newColour); + + /** Returns the current text colour. */ + const Colour& getColour() const throw() { return colour; } + + /** @internal */ + void draw (Graphics& g, const AffineTransform& transform) const; + /** @internal */ + void getBounds (float& x, float& y, float& width, float& height) const; + /** @internal */ + bool hitTest (float x, float y) const; + /** @internal */ + Drawable* createCopy() const; + + juce_UseDebuggingNewOperator + +private: + GlyphArrangement text; + Colour colour; + + DrawableText (const DrawableText&); + const DrawableText& operator= (const DrawableText&); +}; + +#endif // __JUCE_DRAWABLETEXT_JUCEHEADER__ +/********* End of inlined file: juce_DrawableText.h *********/ + +#endif +#ifndef __JUCE_COMPONENT_JUCEHEADER__ + +#endif +#ifndef __JUCE_COMPONENTDELETIONWATCHER_JUCEHEADER__ + +#endif +#ifndef __JUCE_COMPONENTLISTENER_JUCEHEADER__ + +#endif +#ifndef __JUCE_DESKTOP_JUCEHEADER__ + +#endif +#ifndef __JUCE_ARROWBUTTON_JUCEHEADER__ + +/********* Start of inlined file: juce_ArrowButton.h *********/ +#ifndef __JUCE_ARROWBUTTON_JUCEHEADER__ +#define __JUCE_ARROWBUTTON_JUCEHEADER__ + +/********* Start of inlined file: juce_DropShadowEffect.h *********/ +#ifndef __JUCE_DROPSHADOWEFFECT_JUCEHEADER__ +#define __JUCE_DROPSHADOWEFFECT_JUCEHEADER__ + +/** + An effect filter that adds a drop-shadow behind the image's content. + + (This will only work on images/components that aren't opaque, of course). + + When added to a component, this effect will draw a soft-edged + shadow based on what gets drawn inside it. The shadow will also + be applied to the component's children. + + For speed, this doesn't use a proper gaussian blur, but cheats by + using a simple bilinear filter. If you need a really high-quality + shadow, check out ImageConvolutionKernel::createGaussianBlur() + + @see Component::setComponentEffect +*/ +class JUCE_API DropShadowEffect : public ImageEffectFilter +{ +public: + + /** Creates a default drop-shadow effect. + + To customise the shadow's appearance, use the setShadowProperties() + method. + */ + DropShadowEffect(); + + /** Destructor. */ + ~DropShadowEffect(); + + /** Sets up parameters affecting the shadow's appearance. + + @param newRadius the (approximate) radius of the blur used + @param newOpacity the opacity with which the shadow is rendered + @param newShadowOffsetX allows the shadow to be shifted in relation to the + component's contents + @param newShadowOffsetY allows the shadow to be shifted in relation to the + component's contents + */ + void setShadowProperties (const float newRadius, + const float newOpacity, + const int newShadowOffsetX, + const int newShadowOffsetY); + + /** @internal */ + void applyEffect (Image& sourceImage, Graphics& destContext); + + juce_UseDebuggingNewOperator + +private: + int offsetX, offsetY; + float radius, opacity; +}; + +#endif // __JUCE_DROPSHADOWEFFECT_JUCEHEADER__ +/********* End of inlined file: juce_DropShadowEffect.h *********/ + +/** + A button with an arrow in it. + + @see Button +*/ +class JUCE_API ArrowButton : public Button +{ +public: + + /** Creates an ArrowButton. + + @param buttonName the name to give the button + @param arrowDirection the direction the arrow should point in, where 0.0 is + pointing right, 0.25 is down, 0.5 is left, 0.75 is up + @param arrowColour the colour to use for the arrow + */ + ArrowButton (const String& buttonName, + float arrowDirection, + const Colour& arrowColour); + + /** Destructor. */ + ~ArrowButton(); + + juce_UseDebuggingNewOperator + +protected: + /** @internal */ + void paintButton (Graphics& g, + bool isMouseOverButton, + bool isButtonDown); + + /** @internal */ + void buttonStateChanged(); + +private: + + Colour colour; + DropShadowEffect shadow; + Path path; + int offset; + + ArrowButton (const ArrowButton&); + const ArrowButton& operator= (const ArrowButton&); +}; + +#endif // __JUCE_ARROWBUTTON_JUCEHEADER__ +/********* End of inlined file: juce_ArrowButton.h *********/ + +#endif +#ifndef __JUCE_BUTTON_JUCEHEADER__ + +#endif +#ifndef __JUCE_DRAWABLEBUTTON_JUCEHEADER__ + +/********* Start of inlined file: juce_DrawableButton.h *********/ +#ifndef __JUCE_DRAWABLEBUTTON_JUCEHEADER__ +#define __JUCE_DRAWABLEBUTTON_JUCEHEADER__ + +/** + A button that displays a Drawable. + + Up to three Drawable objects can be given to this button, to represent the + 'normal', 'over' and 'down' states. + + @see Button +*/ +class JUCE_API DrawableButton : public Button +{ +public: + + enum ButtonStyle + { + ImageFitted, /**< The button will just display the images, but will resize and centre them to fit inside it. */ + ImageRaw, /**< The button will just display the images in their normal size and position. + This leaves it up to the caller to make sure the images are the correct size and position for the button. */ + ImageAboveTextLabel, /**< Draws the button as a text label across the bottom with the image resized and scaled to fit above it. */ + ImageOnButtonBackground /**< Draws the button as a standard rounded-rectangle button with the image on top. */ + }; + + /** Creates a DrawableButton. + + After creating one of these, use setImages() to specify the drawables to use. + + @param buttonName the name to give the component + @param buttonStyle the layout to use + + @see ButtonStyle, setButtonStyle, setImages + */ + DrawableButton (const String& buttonName, + const ButtonStyle buttonStyle); + + /** Destructor. */ + ~DrawableButton(); + + /** Sets up the images to draw for the various button states. + + The button will keep its own internal copies of these drawables. + + @param normalImage the thing to draw for the button's 'normal' state. An internal copy + will be made of the object passed-in if it is non-zero. + @param overImage the thing to draw for the button's 'over' state - if this is + zero, the button's normal image will be used when the mouse is + over it. An internal copy will be made of the object passed-in + if it is non-zero. + @param downImage the thing to draw for the button's 'down' state - if this is + zero, the 'over' image will be used instead (or the normal image + as a last resort). An internal copy will be made of the object + passed-in if it is non-zero. + @param disabledImage an image to draw when the button is disabled. If this is zero, + the normal image will be drawn with a reduced opacity instead. + An internal copy will be made of the object passed-in if it is + non-zero. + @param normalImageOn same as the normalImage, but this is used when the button's toggle + state is 'on'. If this is 0, the normal image is used instead + @param overImageOn same as the overImage, but this is used when the button's toggle + state is 'on'. If this is 0, the normalImageOn is drawn instead + @param downImageOn same as the downImage, but this is used when the button's toggle + state is 'on'. If this is 0, the overImageOn is drawn instead + @param disabledImageOn same as the disabledImage, but this is used when the button's toggle + state is 'on'. If this is 0, the normal image will be drawn instead + with a reduced opacity + */ + void setImages (const Drawable* normalImage, + const Drawable* overImage = 0, + const Drawable* downImage = 0, + const Drawable* disabledImage = 0, + const Drawable* normalImageOn = 0, + const Drawable* overImageOn = 0, + const Drawable* downImageOn = 0, + const Drawable* disabledImageOn = 0); + + /** Changes the button's style. + + @see ButtonStyle + */ + void setButtonStyle (const ButtonStyle newStyle); + + /** Changes the button's background colours. + + The toggledOffColour is the colour to use when the button's toggle state + is off, and toggledOnColour when it's on. + + For an ImageOnly or ImageAboveTextLabel style, the background colour is + used to fill the background of the component. + + For an ImageOnButtonBackground style, the colour is used to draw the + button's lozenge shape and exactly how the colour's used will depend + on the LookAndFeel. + */ + void setBackgroundColours (const Colour& toggledOffColour, + const Colour& toggledOnColour); + + /** Returns the current background colour being used. + + @see setBackgroundColour + */ + const Colour& getBackgroundColour() const throw(); + + /** Gives the button an optional amount of space around the edge of the drawable. + + This will only apply to ImageFitted or ImageRaw styles, it won't affect the + ones on a button background. If the button is too small for the given gap, a + smaller gap will be used. + + By default there's a gap of about 3 pixels. + */ + void setEdgeIndent (const int numPixelsIndent); + + /** Returns the image that the button is currently displaying. */ + const Drawable* getCurrentImage() const throw(); + const Drawable* getNormalImage() const throw(); + const Drawable* getOverImage() const throw(); + const Drawable* getDownImage() const throw(); + + juce_UseDebuggingNewOperator + +protected: + /** @internal */ + void paintButton (Graphics& g, + bool isMouseOverButton, + bool isButtonDown); + +private: + + ButtonStyle style; + Drawable* normalImage; + Drawable* overImage; + Drawable* downImage; + Drawable* disabledImage; + Drawable* normalImageOn; + Drawable* overImageOn; + Drawable* downImageOn; + Drawable* disabledImageOn; + Colour backgroundOff, backgroundOn; + int edgeIndent; + + void deleteImages(); + DrawableButton (const DrawableButton&); + const DrawableButton& operator= (const DrawableButton&); +}; + +#endif // __JUCE_DRAWABLEBUTTON_JUCEHEADER__ +/********* End of inlined file: juce_DrawableButton.h *********/ + +#endif +#ifndef __JUCE_HYPERLINKBUTTON_JUCEHEADER__ + +/********* Start of inlined file: juce_HyperlinkButton.h *********/ +#ifndef __JUCE_HYPERLINKBUTTON_JUCEHEADER__ +#define __JUCE_HYPERLINKBUTTON_JUCEHEADER__ + +/** + A button showing an underlined weblink, that will launch the link + when it's clicked. + + @see Button +*/ +class JUCE_API HyperlinkButton : public Button +{ +public: + + /** Creates a HyperlinkButton. + + @param linkText the text that will be displayed in the button - this is + also set as the Component's name, but the text can be + changed later with the Button::getButtonText() method + @param linkURL the URL to launch when the user clicks the button + */ + HyperlinkButton (const String& linkText, + const URL& linkURL); + + /** Destructor. */ + ~HyperlinkButton(); + + /** Changes the font to use for the text. + + If resizeToMatchComponentHeight is true, the font's height will be adjusted + to match the size of the component. + */ + void setFont (const Font& newFont, + const bool resizeToMatchComponentHeight, + const Justification& justificationType = Justification::horizontallyCentred); + + /** A set of colour IDs to use to change the colour of various aspects of the link. + + These constants can be used either via the Component::setColour(), or LookAndFeel::setColour() + methods. + + @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour + */ + enum ColourIds + { + textColourId = 0x1001f00, /**< The colour to use for the URL text. */ + }; + + /** Changes the URL that the button will trigger. */ + void setURL (const URL& newURL) throw(); + + /** Returns the URL that the button will trigger. */ + const URL& getURL() const throw() { return url; } + + /** Resizes the button horizontally to fit snugly around the text. + + This won't affect the button's height. + */ + void changeWidthToFitText(); + + juce_UseDebuggingNewOperator + +protected: + /** @internal */ + void clicked(); + /** @internal */ + void colourChanged(); + /** @internal */ + void paintButton (Graphics& g, + bool isMouseOverButton, + bool isButtonDown); + +private: + URL url; + Font font; + bool resizeFont; + Justification justification; + + const Font getFontToUse() const; + + HyperlinkButton (const HyperlinkButton&); + const HyperlinkButton& operator= (const HyperlinkButton&); +}; + +#endif // __JUCE_HYPERLINKBUTTON_JUCEHEADER__ +/********* End of inlined file: juce_HyperlinkButton.h *********/ + +#endif +#ifndef __JUCE_IMAGEBUTTON_JUCEHEADER__ + +/********* Start of inlined file: juce_ImageButton.h *********/ +#ifndef __JUCE_IMAGEBUTTON_JUCEHEADER__ +#define __JUCE_IMAGEBUTTON_JUCEHEADER__ + +/** + As the title suggests, this is a button containing an image. + + The colour and transparency of the image can be set to vary when the + button state changes. + + @see Button, ShapeButton, TextButton +*/ +class JUCE_API ImageButton : public Button +{ +public: + + /** Creates an ImageButton. + + Use setImage() to specify the image to use. The colours and opacities that + are specified here can be changed later using setDrawingOptions(). + + @param name the name to give the component + */ + ImageButton (const String& name); + + /** Destructor. */ + ~ImageButton(); + + /** Sets up the images to draw in various states. + + Important! Bear in mind that if you pass the same image in for more than one of + these parameters, this button will delete it (or release from the ImageCache) + multiple times! + + @param resizeButtonNowToFitThisImage if true, the button will be immediately + resized to the same dimensions as the normal image + @param rescaleImagesWhenButtonSizeChanges if true, the image will be rescaled to fit the + button when the button's size changes + @param preserveImageProportions if true then any rescaling of the image to fit + the button will keep the image's x and y proportions + correct - i.e. it won't distort its shape, although + this might create gaps around the edges + @param normalImage the image to use when the button is in its normal state. The + image passed in will be deleted (or released if it + was created by the ImageCache class) when the + button no longer needs it. + @param imageOpacityWhenNormal the opacity to use when drawing the normal image. + @param overlayColourWhenNormal an overlay colour to use to fill the alpha channel of the + normal image - if this colour is transparent, no overlay + will be drawn. The overlay will be drawn over the top of the + image, so you can basically add a solid or semi-transparent + colour to the image to brighten or darken it + @param overImage the image to use when the mouse is over the button. If + you want to use the same image as was set in the normalImage + parameter, this value can be 0. As for normalImage, it + will be deleted or released by the button when no longer + needed + @param imageOpacityWhenOver the opacity to use when drawing the image when the mouse + is over the button + @param overlayColourWhenOver an overlay colour to use to fill the alpha channel of the + image when the mouse is over - if this colour is transparent, + no overlay will be drawn + @param downImage an image to use when the button is pressed down. If set + to zero, the 'over' image will be drawn instead (or the + normal image if there isn't an 'over' image either). This + image will be deleted or released by the button when no + longer needed + @param imageOpacityWhenDown the opacity to use when drawing the image when the button + is pressed + @param overlayColourWhenDown an overlay colour to use to fill the alpha channel of the + image when the button is pressed down - if this colour is + transparent, no overlay will be drawn + @param hitTestAlphaThreshold if set to zero, the mouse is considered to be over the button + whenever it's inside the button's bounding rectangle. If + set to values higher than 0, the mouse will only be + considered to be over the image when the value of the + image's alpha channel at that position is greater than + this level. + */ + void setImages (const bool resizeButtonNowToFitThisImage, + const bool rescaleImagesWhenButtonSizeChanges, + const bool preserveImageProportions, + Image* const normalImage, + const float imageOpacityWhenNormal, + const Colour& overlayColourWhenNormal, + Image* const overImage, + const float imageOpacityWhenOver, + const Colour& overlayColourWhenOver, + Image* const downImage, + const float imageOpacityWhenDown, + const Colour& overlayColourWhenDown, + const float hitTestAlphaThreshold = 0.0f); + + /** Returns the currently set 'normal' image. */ + Image* getNormalImage() const throw(); + + /** Returns the image that's drawn when the mouse is over the button. + + If an 'over' image has been set, this will return it; otherwise it'll + just return the normal image. + */ + Image* getOverImage() const throw(); + + /** Returns the image that's drawn when the button is held down. + + If a 'down' image has been set, this will return it; otherwise it'll + return the 'over' image or normal image, depending on what's available. + */ + Image* getDownImage() const throw(); + + juce_UseDebuggingNewOperator + +protected: + /** @internal */ + bool hitTest (int x, int y); + /** @internal */ + void paintButton (Graphics& g, + bool isMouseOverButton, + bool isButtonDown); + +private: + + bool scaleImageToFit, preserveProportions; + unsigned char alphaThreshold; + int imageX, imageY, imageW, imageH; + Image* normalImage; + Image* overImage; + Image* downImage; + float normalOpacity, overOpacity, downOpacity; + Colour normalOverlay, overOverlay, downOverlay; + + Image* getCurrentImage() const; + void deleteImages(); + + ImageButton (const ImageButton&); + const ImageButton& operator= (const ImageButton&); +}; + +#endif // __JUCE_IMAGEBUTTON_JUCEHEADER__ +/********* End of inlined file: juce_ImageButton.h *********/ + +#endif +#ifndef __JUCE_SHAPEBUTTON_JUCEHEADER__ + +/********* Start of inlined file: juce_ShapeButton.h *********/ +#ifndef __JUCE_SHAPEBUTTON_JUCEHEADER__ +#define __JUCE_SHAPEBUTTON_JUCEHEADER__ + +/** + A button that contains a filled shape. + + @see Button, ImageButton, TextButton, ArrowButton +*/ +class JUCE_API ShapeButton : public Button +{ +public: + + /** Creates a ShapeButton. + + @param name a name to give the component - see Component::setName() + @param normalColour the colour to fill the shape with when the mouse isn't over + @param overColour the colour to use when the mouse is over the shape + @param downColour the colour to use when the button is in the pressed-down state + */ + ShapeButton (const String& name, + const Colour& normalColour, + const Colour& overColour, + const Colour& downColour); + + /** Destructor. */ + ~ShapeButton(); + + /** Sets the shape to use. + + @param newShape the shape to use + @param resizeNowToFitThisShape if true, the button will be resized to fit the shape's bounds + @param maintainShapeProportions if true, the shape's proportions will be kept fixed when + the button is resized + @param hasDropShadow if true, the button will be given a drop-shadow effect + */ + void setShape (const Path& newShape, + const bool resizeNowToFitThisShape, + const bool maintainShapeProportions, + const bool hasDropShadow); + + /** Set the colours to use for drawing the shape. + + @param normalColour the colour to fill the shape with when the mouse isn't over + @param overColour the colour to use when the mouse is over the shape + @param downColour the colour to use when the button is in the pressed-down state + */ + void setColours (const Colour& normalColour, + const Colour& overColour, + const Colour& downColour); + + /** Sets up an outline to draw around the shape. + + @param outlineColour the colour to use + @param outlineStrokeWidth the thickness of line to draw + */ + void setOutline (const Colour& outlineColour, + const float outlineStrokeWidth); + + juce_UseDebuggingNewOperator + +protected: + /** @internal */ + void paintButton (Graphics& g, + bool isMouseOverButton, + bool isButtonDown); + +private: + Colour normalColour, overColour, downColour, outlineColour; + DropShadowEffect shadow; + Path shape; + bool maintainShapeProportions; + float outlineWidth; + + ShapeButton (const ShapeButton&); + const ShapeButton& operator= (const ShapeButton&); +}; + +#endif // __JUCE_SHAPEBUTTON_JUCEHEADER__ +/********* End of inlined file: juce_ShapeButton.h *********/ + +#endif +#ifndef __JUCE_TEXTBUTTON_JUCEHEADER__ + +#endif +#ifndef __JUCE_TOGGLEBUTTON_JUCEHEADER__ + +/********* Start of inlined file: juce_ToggleButton.h *********/ +#ifndef __JUCE_TOGGLEBUTTON_JUCEHEADER__ +#define __JUCE_TOGGLEBUTTON_JUCEHEADER__ + +/** + A button that can be toggled on/off. + + All buttons can be toggle buttons, but this lets you create one of the + standard ones which has a tick-box and a text label next to it. + + @see Button, DrawableButton, TextButton +*/ +class JUCE_API ToggleButton : public Button +{ +public: + + /** Creates a ToggleButton. + + @param buttonText the text to put in the button (the component's name is also + initially set to this string, but these can be changed later + using the setName() and setButtonText() methods) + */ + ToggleButton (const String& buttonText); + + /** Destructor. */ + ~ToggleButton(); + + /** Resizes the button to fit neatly around its current text. + + The button's height won't be affected, only its width. + */ + void changeWidthToFitText(); + + /** A set of colour IDs to use to change the colour of various aspects of the button. + + These constants can be used either via the Component::setColour(), or LookAndFeel::setColour() + methods. + + @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour + */ + enum ColourIds + { + textColourId = 0x1006501 /**< The colour to use for the button's text. */ + }; + + juce_UseDebuggingNewOperator + +protected: + /** @internal */ + void paintButton (Graphics& g, + bool isMouseOverButton, + bool isButtonDown); + + /** @internal */ + void colourChanged(); + +private: + + ToggleButton (const ToggleButton&); + const ToggleButton& operator= (const ToggleButton&); +}; + +#endif // __JUCE_TOGGLEBUTTON_JUCEHEADER__ +/********* End of inlined file: juce_ToggleButton.h *********/ + +#endif +#ifndef __JUCE_TOOLBARBUTTON_JUCEHEADER__ + +/********* Start of inlined file: juce_ToolbarButton.h *********/ +#ifndef __JUCE_TOOLBARBUTTON_JUCEHEADER__ +#define __JUCE_TOOLBARBUTTON_JUCEHEADER__ + +/********* Start of inlined file: juce_ToolbarItemComponent.h *********/ +#ifndef __JUCE_TOOLBARITEMCOMPONENT_JUCEHEADER__ +#define __JUCE_TOOLBARITEMCOMPONENT_JUCEHEADER__ + +/********* Start of inlined file: juce_Toolbar.h *********/ +#ifndef __JUCE_TOOLBAR_JUCEHEADER__ +#define __JUCE_TOOLBAR_JUCEHEADER__ + +/********* Start of inlined file: juce_DragAndDropContainer.h *********/ +#ifndef __JUCE_DRAGANDDROPCONTAINER_JUCEHEADER__ +#define __JUCE_DRAGANDDROPCONTAINER_JUCEHEADER__ + +/********* Start of inlined file: juce_DragAndDropTarget.h *********/ +#ifndef __JUCE_DRAGANDDROPTARGET_JUCEHEADER__ +#define __JUCE_DRAGANDDROPTARGET_JUCEHEADER__ + +/** + Components derived from this class can have things dropped onto them by a DragAndDropContainer. + + To create a component that can receive things drag-and-dropped by a DragAndDropContainer, + derive your component from this class, and make sure that it is somewhere inside a + DragAndDropContainer component. + + Note: If all that you need to do is to respond to files being drag-and-dropped from + the operating system onto your component, you don't need any of these classes: instead + see the FileDragAndDropTarget class. + + @see DragAndDropContainer, FileDragAndDropTarget +*/ +class JUCE_API DragAndDropTarget +{ +public: + /** Destructor. */ + virtual ~DragAndDropTarget() {} + + /** Callback to check whether this target is interested in the type of object being + dragged. + + @param sourceDescription the description string passed into DragAndDropContainer::startDragging() + @returns true if this component wants to receive the other callbacks regarging this + type of object; if it returns false, no other callbacks will be made. + */ + virtual bool isInterestedInDragSource (const String& sourceDescription, + Component* sourceComponent) = 0; + + /** Callback to indicate that something is being dragged over this component. + + This gets called when the user moves the mouse into this component while dragging + something. + + Use this callback as a trigger to make your component repaint itself to give the + user feedback about whether the item can be dropped here or not. + + @param sourceDescription the description string passed into DragAndDropContainer::startDragging() + @param sourceComponent the component passed into DragAndDropContainer::startDragging() + @param x the mouse x position, relative to this component + @param y the mouse y position, relative to this component + @see itemDragExit + */ + virtual void itemDragEnter (const String& sourceDescription, + Component* sourceComponent, + int x, + int y); + + /** Callback to indicate that the user is dragging something over this component. + + This gets called when the user moves the mouse over this component while dragging + something. Normally overriding itemDragEnter() and itemDragExit() are enough, but + this lets you know what happens in-between. + + @param sourceDescription the description string passed into DragAndDropContainer::startDragging() + @param sourceComponent the component passed into DragAndDropContainer::startDragging() + @param x the mouse x position, relative to this component + @param y the mouse y position, relative to this component + */ + virtual void itemDragMove (const String& sourceDescription, + Component* sourceComponent, + int x, + int y); + + /** Callback to indicate that something has been dragged off the edge of this component. + + This gets called when the user moves the mouse out of this component while dragging + something. + + If you've used itemDragEnter() to repaint your component and give feedback, use this + as a signal to repaint it in its normal state. + + @param sourceDescription the description string passed into DragAndDropContainer::startDragging() + @param sourceComponent the component passed into DragAndDropContainer::startDragging() + @see itemDragEnter + */ + virtual void itemDragExit (const String& sourceDescription, + Component* sourceComponent); + + /** Callback to indicate that the user has dropped something onto this component. + + When the user drops an item this get called, and you can use the description to + work out whether your object wants to deal with it or not. + + Note that after this is called, the itemDragExit method may not be called, so you should + clean up in here if there's anything you need to do when the drag finishes. + + @param sourceDescription the description string passed into DragAndDropContainer::startDragging() + @param sourceComponent the component passed into DragAndDropContainer::startDragging() + @param x the mouse x position, relative to this component + @param y the mouse y position, relative to this component + */ + virtual void itemDropped (const String& sourceDescription, + Component* sourceComponent, + int x, + int y) = 0; + + /** Overriding this allows the target to tell the drag container whether to + draw the drag image while the cursor is over it. + + By default it returns true, but if you return false, then the normal drag + image will not be shown when the cursor is over this target. + */ + virtual bool shouldDrawDragImageWhenOver(); +}; + +#endif // __JUCE_DRAGANDDROPTARGET_JUCEHEADER__ +/********* End of inlined file: juce_DragAndDropTarget.h *********/ + +/** + Enables drag-and-drop behaviour for a component and all its sub-components. + + For a component to be able to make or receive drag-and-drop events, one of its parent + components must derive from this class. It's probably best for the top-level + component to implement it. + + Then to start a drag operation, any sub-component can just call the startDragging() + method, and this object will take over, tracking the mouse and sending appropriate + callbacks to any child components derived from DragAndDropTarget which the mouse + moves over. + + Note: If all that you need to do is to respond to files being drag-and-dropped from + the operating system onto your component, you don't need any of these classes: you can do this + simply by overriding Component::filesDropped(). + + @see DragAndDropTarget +*/ +class JUCE_API DragAndDropContainer +{ +public: + + /** Creates a DragAndDropContainer. + + The object that derives from this class must also be a Component. + */ + DragAndDropContainer(); + + /** Destructor. */ + virtual ~DragAndDropContainer(); + + /** Begins a drag-and-drop operation. + + This starts a drag-and-drop operation - call it when the user drags the + mouse in your drag-source component, and this object will track mouse + movements until the user lets go of the mouse button, and will send + appropriate messages to DragAndDropTarget objects that the mouse moves + over. + + findParentDragContainerFor() is a handy method to call to find the + drag container to use for a component. + + @param sourceDescription a string to use as the description of the thing being + dragged - this will be passed to the objects that might be + dropped-onto so they can decide if they want to handle it or + not + @param sourceComponent the component that is being dragged + @param dragImage the image to drag around underneath the mouse. If this is + zero, a snapshot of the sourceComponent will be used instead. An + image passed-in will be deleted by this object when no longer + needed. + @param allowDraggingToOtherJuceWindows if true, the dragged component will appear as a desktop + window, and can be dragged to DragAndDropTargets that are the + children of components other than this one. + */ + void startDragging (const String& sourceDescription, + Component* sourceComponent, + Image* dragImage = 0, + const bool allowDraggingToOtherJuceWindows = false); + + /** Returns true if something is currently being dragged. */ + bool isDragAndDropActive() const; + + /** Returns the description of the thing that's currently being dragged. + + If nothing's being dragged, this will return an empty string, otherwise it's the + string that was passed into startDragging(). + + @see startDragging + */ + const String getCurrentDragDescription() const; + + /** Utility to find the DragAndDropContainer for a given Component. + + This will search up this component's parent hierarchy looking for the first + parent component which is a DragAndDropContainer. + + It's useful when a component wants to call startDragging but doesn't know + the DragAndDropContainer it should to use. + + Obviously this may return 0 if it doesn't find a suitable component. + */ + static DragAndDropContainer* findParentDragContainerFor (Component* childComponent); + + /** This performs a synchronous drag-and-drop of a set of files to some external + application. + + You can call this function in response to a mouseDrag callback, and it will + block, running its own internal message loop and tracking the mouse, while it + uses a native operating system drag-and-drop operation to move or copy some + files to another application. + + @param files a list of filenames to drag + @param canMoveFiles if true, the app that receives the files is allowed to move the files to a new location + (if this is appropriate). If false, the receiver is expected to make a copy of them. + @returns true if the files were successfully dropped somewhere, or false if it + was interrupted + @see performExternalDragDropOfText + */ + static bool performExternalDragDropOfFiles (const StringArray& files, const bool canMoveFiles); + + /** This performs a synchronous drag-and-drop of a block of text to some external + application. + + You can call this function in response to a mouseDrag callback, and it will + block, running its own internal message loop and tracking the mouse, while it + uses a native operating system drag-and-drop operation to move or copy some + text to another application. + + @param text the text to copy + @returns true if the text was successfully dropped somewhere, or false if it + was interrupted + @see performExternalDragDropOfFiles + */ + static bool performExternalDragDropOfText (const String& text); + + juce_UseDebuggingNewOperator + +protected: + /** Override this if you want to be able to perform an external drag a set of files + when the user drags outside of this container component. + + This method will be called when a drag operation moves outside the Juce-based window, + and if you want it to then perform a file drag-and-drop, add the filenames you want + to the array passed in, and return true. + + @param dragSourceDescription the description passed into the startDrag() call when the drag began + @param dragSourceComponent the component passed into the startDrag() call when the drag began + @param files on return, the filenames you want to drag + @param canMoveFiles on return, true if it's ok for the receiver to move the files; false if + it must make a copy of them (see the performExternalDragDropOfFiles() + method) + @see performExternalDragDropOfFiles + */ + virtual bool shouldDropFilesWhenDraggedExternally (const String& dragSourceDescription, + Component* dragSourceComponent, + StringArray& files, + bool& canMoveFiles); + +private: + friend class DragImageComponent; + Component* dragImageComponent; + String currentDragDesc; +}; + +#endif // __JUCE_DRAGANDDROPCONTAINER_JUCEHEADER__ +/********* End of inlined file: juce_DragAndDropContainer.h *********/ + +/********* Start of inlined file: juce_ComponentAnimator.h *********/ +#ifndef __JUCE_COMPONENTANIMATOR_JUCEHEADER__ +#define __JUCE_COMPONENTANIMATOR_JUCEHEADER__ + +/** + Animates a set of components, moving it to a new position. + + To use this, create a ComponentAnimator, and use its animateComponent() method + to tell it to move components to destination positions. Any number of + components can be animated by one ComponentAnimator object (if you've got a + lot of components to move, it's much more efficient to share a single animator + than to have many animators running at once). + + You'll need to make sure the animator object isn't deleted before it finishes + moving the components. +*/ +class JUCE_API ComponentAnimator : private Timer +{ +public: + + /** Creates a ComponentAnimator. */ + ComponentAnimator(); + + /** Destructor. */ + ~ComponentAnimator(); + + /** Starts a component moving from its current position to a specified position. + + If the component is already in the middle of an animation, that will be abandoned, + and a new animation will begin, moving the component from its current location. + + The start and end speed parameters let you apply some acceleration to the component's + movement. + + @param component the component to move + @param finalPosition the destination position and size to move it to + @param millisecondsToSpendMoving how long, in milliseconds, it should take + to arrive at its destination + @param startSpeed a value to indicate the relative start speed of the + animation. If this is 0, the component will start + by accelerating from rest; higher values mean that it + will have an initial speed greater than zero. If the + value if greater than 1, it will decelerate towards the + middle of its journey. To move the component at a constant + rate for its entire animation, set both the start and + end speeds to 1.0 + @param endSpeed a relative speed at which the component should be moving + when the animation finishes. If this is 0, the component + will decelerate to a standstill at its final position; higher + values mean the component will still be moving when it stops. + To move the component at a constant rate for its entire + animation, set both the start and end speeds to 1.0 + */ + void animateComponent (Component* const component, + const Rectangle& finalPosition, + const int millisecondsToSpendMoving, + const double startSpeed = 1.0, + const double endSpeed = 1.0); + + /** Stops a component if it's currently being animated. + + If moveComponentToItsFinalPosition is true, then the component will + be immediately moved to its destination position and size. If false, it will be + left in whatever location it currently occupies. + */ + void cancelAnimation (Component* const component, + const bool moveComponentToItsFinalPosition); + + /** Clears all of the active animations. + + If moveComponentsToTheirFinalPositions is true, all the components will + be immediately set to their final positions. If false, they will be + left in whatever locations they currently occupy. + */ + void cancelAllAnimations (const bool moveComponentsToTheirFinalPositions); + + /** Returns the destination position for a component. + + If the component is being animated, this will return the target position that + was specified when animateComponent() was called. + + If the specified component isn't currently being animated, this method will just + return its current position. + */ + const Rectangle getComponentDestination (Component* const component); + + juce_UseDebuggingNewOperator + +private: + VoidArray tasks; + uint32 lastTime; + + void* findTaskFor (Component* const component) const; + void timerCallback(); +}; + +#endif // __JUCE_COMPONENTANIMATOR_JUCEHEADER__ +/********* End of inlined file: juce_ComponentAnimator.h *********/ + +class ToolbarItemComponent; +class ToolbarItemFactory; + +/** + A toolbar component. + + A toolbar contains a horizontal or vertical strip of ToolbarItemComponents, + and looks after their order and layout. + + Items (icon buttons or other custom components) are added to a toolbar using a + ToolbarItemFactory - each type of item is given a unique ID number, and a + toolbar might contain more than one instance of a particular item type. + + Toolbars can be interactively customised, allowing the user to drag the items + around, and to drag items onto or off the toolbar, using the ToolbarItemPalette + component as a source of new items. + + @see ToolbarItemFactory, ToolbarItemComponent, ToolbarItemPalette +*/ +class JUCE_API Toolbar : public Component, + public DragAndDropContainer, + public DragAndDropTarget, + private ButtonListener +{ +public: + + /** Creates an empty toolbar component. + + To add some icons or other components to your toolbar, you'll need to + create a ToolbarItemFactory class that can create a suitable set of + ToolbarItemComponents. + + @see ToolbarItemFactory, ToolbarItemComponents + */ + Toolbar(); + + /** Destructor. + + Any items on the bar will be deleted when the toolbar is deleted. + */ + ~Toolbar(); + + /** Changes the bar's orientation. + @see isVertical + */ + void setVertical (const bool shouldBeVertical); + + /** Returns true if the bar is set to be vertical, or false if it's horizontal. + + You can change the bar's orientation with setVertical(). + */ + bool isVertical() const throw() { return vertical; } + + /** Returns the depth of the bar. + + If the bar is horizontal, this will return its height; if it's vertical, it + will return its width. + + @see getLength + */ + int getThickness() const throw(); + + /** Returns the length of the bar. + + If the bar is horizontal, this will return its width; if it's vertical, it + will return its height. + + @see getThickness + */ + int getLength() const throw(); + + /** Deletes all items from the bar. + */ + void clear(); + + /** Adds an item to the toolbar. + + The factory's ToolbarItemFactory::createItem() will be called by this method + to create the component that will actually be added to the bar. + + The new item will be inserted at the specified index (if the index is -1, it + will be added to the right-hand or bottom end of the bar). + + Once added, the component will be automatically deleted by this object when it + is no longer needed. + + @see ToolbarItemFactory + */ + void addItem (ToolbarItemFactory& factory, + const int itemId, + const int insertIndex = -1); + + /** Deletes one of the items from the bar. + */ + void removeToolbarItem (const int itemIndex); + + /** Returns the number of items currently on the toolbar. + + @see getItemId, getItemComponent + */ + int getNumItems() const throw(); + + /** Returns the ID of the item with the given index. + + If the index is less than zero or greater than the number of items, + this will return 0. + + @see getNumItems + */ + int getItemId (const int itemIndex) const throw(); + + /** Returns the component being used for the item with the given index. + + If the index is less than zero or greater than the number of items, + this will return 0. + + @see getNumItems + */ + ToolbarItemComponent* getItemComponent (const int itemIndex) const throw(); + + /** Clears this toolbar and adds to it the default set of items that the specified + factory creates. + + @see ToolbarItemFactory::getDefaultItemSet + */ + void addDefaultItems (ToolbarItemFactory& factoryToUse); + + /** Options for the way items should be displayed. + @see setStyle, getStyle + */ + enum ToolbarItemStyle + { + iconsOnly, /**< Means that the toolbar should just contain icons. */ + iconsWithText, /**< Means that the toolbar should have text labels under each icon. */ + textOnly /**< Means that the toolbar only display text labels for each item. */ + }; + + /** Returns the toolbar's current style. + @see ToolbarItemStyle, setStyle + */ + ToolbarItemStyle getStyle() const throw() { return toolbarStyle; } + + /** Changes the toolbar's current style. + @see ToolbarItemStyle, getStyle, ToolbarItemComponent::setStyle + */ + void setStyle (const ToolbarItemStyle& newStyle); + + /** Flags used by the showCustomisationDialog() method. */ + enum CustomisationFlags + { + allowIconsOnlyChoice = 1, /**< If this flag is specified, the customisation dialog can + show the "icons only" option on its choice of toolbar styles. */ + allowIconsWithTextChoice = 2, /**< If this flag is specified, the customisation dialog can + show the "icons with text" option on its choice of toolbar styles. */ + allowTextOnlyChoice = 4, /**< If this flag is specified, the customisation dialog can + show the "text only" option on its choice of toolbar styles. */ + showResetToDefaultsButton = 8, /**< If this flag is specified, the customisation dialog can + show a button to reset the toolbar to its default set of items. */ + + allCustomisationOptionsEnabled = (allowIconsOnlyChoice | allowIconsWithTextChoice | allowTextOnlyChoice | showResetToDefaultsButton) + }; + + /** Pops up a modal dialog box that allows this toolbar to be customised by the user. + + The dialog contains a ToolbarItemPalette and various controls for editing other + aspects of the toolbar. This method will block and run the dialog box modally, + returning when the user closes it. + + The factory is used to determine the set of items that will be shown on the + palette. + + The optionFlags parameter is a bitwise-or of values from the CustomisationFlags + enum. + + @see ToolbarItemPalette + */ + void showCustomisationDialog (ToolbarItemFactory& factory, + const int optionFlags = allCustomisationOptionsEnabled); + + /** Turns on or off the toolbar's editing mode, in which its items can be + rearranged by the user. + + (In most cases it's easier just to use showCustomisationDialog() instead of + trying to enable editing directly). + + @see ToolbarItemPalette + */ + void setEditingActive (const bool editingEnabled); + + /** A set of colour IDs to use to change the colour of various aspects of the toolbar. + + These constants can be used either via the Component::setColour(), or LookAndFeel::setColour() + methods. + + @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour + */ + enum ColourIds + { + backgroundColourId = 0x1003200, /**< A colour to use to fill the toolbar's background. For + more control over this, override LookAndFeel::paintToolbarBackground(). */ + separatorColourId = 0x1003210, /**< A colour to use to draw the separator lines. */ + + buttonMouseOverBackgroundColourId = 0x1003220, /**< A colour used to paint the background of buttons when the mouse is + over them. */ + buttonMouseDownBackgroundColourId = 0x1003230, /**< A colour used to paint the background of buttons when the mouse is + held down on them. */ + + labelTextColourId = 0x1003240, /**< A colour to use for drawing the text under buttons + when the style is set to iconsWithText or textOnly. */ + + editingModeOutlineColourId = 0x1003250 /**< A colour to use for an outline around buttons when + the customisation dialog is active and the mouse moves over them. */ + }; + + /** Returns a string that represents the toolbar's current set of items. + + This lets you later restore the same item layout using restoreFromString(). + + @see restoreFromString + */ + const String toString() const; + + /** Restores a set of items that was previously stored in a string by the toString() + method. + + The factory object is used to create any item components that are needed. + + @see toString + */ + bool restoreFromString (ToolbarItemFactory& factoryToUse, + const String& savedVersion); + + /** @internal */ + void paint (Graphics& g); + /** @internal */ + void resized(); + /** @internal */ + void buttonClicked (Button*); + /** @internal */ + void mouseDown (const MouseEvent&); + /** @internal */ + bool isInterestedInDragSource (const String&, Component*); + /** @internal */ + void itemDragMove (const String&, Component*, int, int); + /** @internal */ + void itemDragExit (const String&, Component*); + /** @internal */ + void itemDropped (const String&, Component*, int, int); + /** @internal */ + void updateAllItemPositions (const bool animate); + /** @internal */ + static ToolbarItemComponent* createItem (ToolbarItemFactory&, const int itemId); + + juce_UseDebuggingNewOperator + +private: + Button* missingItemsButton; + bool vertical, isEditingActive; + ToolbarItemStyle toolbarStyle; + ComponentAnimator animator; + + friend class ItemDragAndDropOverlayComponent; + static const tchar* const toolbarDragDescriptor; + + void addItemInternal (ToolbarItemFactory& factory, const int itemId, const int insertIndex); + + ToolbarItemComponent* getNextActiveComponent (int index, const int delta) const; + + Toolbar (const Toolbar&); + const Toolbar& operator= (const Toolbar&); +}; + +#endif // __JUCE_TOOLBAR_JUCEHEADER__ +/********* End of inlined file: juce_Toolbar.h *********/ + +class ItemDragAndDropOverlayComponent; + +/** + A component that can be used as one of the items in a Toolbar. + + Each of the items on a toolbar must be a component derived from ToolbarItemComponent, + and these objects are always created by a ToolbarItemFactory - see the ToolbarItemFactory + class for further info about creating them. + + The ToolbarItemComponent class is actually a button, but can be used to hold non-button + components too. To do this, set the value of isBeingUsedAsAButton to false when + calling the constructor, and override contentAreaChanged(), in which you can position + any sub-components you need to add. + + To add basic buttons without writing a special subclass, have a look at the + ToolbarButton class. + + @see ToolbarButton, Toolbar, ToolbarItemFactory +*/ +class JUCE_API ToolbarItemComponent : public Button +{ +public: + + /** Constructor. + + @param itemId the ID of the type of toolbar item which this represents + @param labelText the text to display if the toolbar's style is set to + Toolbar::iconsWithText or Toolbar::textOnly + @param isBeingUsedAsAButton set this to false if you don't want the button + to draw itself with button over/down states when the mouse + moves over it or clicks + */ + ToolbarItemComponent (const int itemId, + const String& labelText, + const bool isBeingUsedAsAButton); + + /** Destructor. */ + ~ToolbarItemComponent(); + + /** Returns the item type ID that this component represents. + This value is in the constructor. + */ + int getItemId() const throw() { return itemId; } + + /** Returns the toolbar that contains this component, or 0 if it's not currently + inside one. + */ + Toolbar* getToolbar() const; + + /** Returns true if this component is currently inside a toolbar which is vertical. + @see Toolbar::isVertical + */ + bool isToolbarVertical() const; + + /** Returns the current style setting of this item. + + Styles are listed in the Toolbar::ToolbarItemStyle enum. + @see setStyle, Toolbar::getStyle + */ + Toolbar::ToolbarItemStyle getStyle() const throw() { return toolbarStyle; } + + /** Changes the current style setting of this item. + + Styles are listed in the Toolbar::ToolbarItemStyle enum, and are automatically updated + by the toolbar that holds this item. + + @see setStyle, Toolbar::setStyle + */ + virtual void setStyle (const Toolbar::ToolbarItemStyle& newStyle); + + /** Returns the area of the component that should be used to display the button image or + other contents of the item. + + This content area may change when the item's style changes, and may leave a space around the + edge of the component where the text label can be shown. + + @see contentAreaChanged + */ + const Rectangle getContentArea() const throw() { return contentArea; } + + /** This method must return the size criteria for this item, based on a given toolbar + size and orientation. + + The preferredSize, minSize and maxSize values must all be set by your implementation + method. If the toolbar is horizontal, these will be the width of the item; for a vertical + toolbar, they refer to the item's height. + + The preferredSize is the size that the component would like to be, and this must be + between the min and max sizes. For a fixed-size item, simply set all three variables to + the same value. + + The toolbarThickness parameter tells you the depth of the toolbar - the same as calling + Toolbar::getThickness(). + + The isToolbarVertical parameter tells you whether the bar is oriented horizontally or + vertically. + */ + virtual bool getToolbarItemSizes (int toolbarThickness, + bool isToolbarVertical, + int& preferredSize, + int& minSize, + int& maxSize) = 0; + + /** Your subclass should use this method to draw its content area. + + The graphics object that is passed-in will have been clipped and had its origin + moved to fit the content area as specified get getContentArea(). The width and height + parameters are the width and height of the content area. + + If the component you're writing isn't a button, you can just do nothing in this method. + */ + virtual void paintButtonArea (Graphics& g, + int width, int height, + bool isMouseOver, bool isMouseDown) = 0; + + /** Callback to indicate that the content area of this item has changed. + + This might be because the component was resized, or because the style changed and + the space needed for the text label is different. + + See getContentArea() for a description of what the area is. + */ + virtual void contentAreaChanged (const Rectangle& newBounds) = 0; + + /** Editing modes. + These are used by setEditingMode(), but will be rarely needed in user code. + */ + enum ToolbarEditingMode + { + normalMode = 0, /**< Means that the component is active, inside a toolbar. */ + editableOnToolbar, /**< Means that the component is on a toolbar, but the toolbar is in + customisation mode, and the items can be dragged around. */ + editableOnPalette /**< Means that the component is on an new-item palette, so it can be + dragged onto a toolbar to add it to that bar.*/ + }; + + /** Changes the editing mode of this component. + + This is used by the ToolbarItemPalette and related classes for making the items draggable, + and is unlikely to be of much use in end-user-code. + */ + void setEditingMode (const ToolbarEditingMode newMode); + + /** Returns the current editing mode of this component. + + This is used by the ToolbarItemPalette and related classes for making the items draggable, + and is unlikely to be of much use in end-user-code. + */ + ToolbarEditingMode getEditingMode() const throw() { return mode; } + + /** @internal */ + void paintButton (Graphics& g, bool isMouseOver, bool isMouseDown); + /** @internal */ + void resized(); + + juce_UseDebuggingNewOperator + +private: + friend class Toolbar; + friend class ItemDragAndDropOverlayComponent; + const int itemId; + ToolbarEditingMode mode; + Toolbar::ToolbarItemStyle toolbarStyle; + Component* overlayComp; + int dragOffsetX, dragOffsetY; + bool isActive, isBeingDragged, isBeingUsedAsAButton; + Rectangle contentArea; + + ToolbarItemComponent (const ToolbarItemComponent&); + const ToolbarItemComponent& operator= (const ToolbarItemComponent&); +}; + +#endif // __JUCE_TOOLBARITEMCOMPONENT_JUCEHEADER__ +/********* End of inlined file: juce_ToolbarItemComponent.h *********/ + +/** + A type of button designed to go on a toolbar. + + This simple button can have two Drawable objects specified - one for normal + use and another one (optionally) for the button's "on" state if it's a + toggle button. + + @see Toolbar, ToolbarItemFactory, ToolbarItemComponent, Drawable, Button +*/ +class JUCE_API ToolbarButton : public ToolbarItemComponent +{ +public: + + /** Creates a ToolbarButton. + + @param itemId the ID for this toolbar item type. This is passed through to the + ToolbarItemComponent constructor + @param labelText the text to display on the button (if the toolbar is using a style + that shows text labels). This is passed through to the + ToolbarItemComponent constructor + @param normalImage a drawable object that the button should use as its icon. The object + that is passed-in here will be kept by this object and will be + deleted when no longer needed or when this button is deleted. + @param toggledOnImage a drawable object that the button can use as its icon if the button + is in a toggled-on state (see the Button::getToggleState() method). If + 0 is passed-in here, then the normal image will be used instead, regardless + of the toggle state. The object that is passed-in here will be kept by + this object and will be deleted when no longer needed or when this button + is deleted. + */ + ToolbarButton (const int itemId, + const String& labelText, + Drawable* const normalImage, + Drawable* const toggledOnImage); + + /** Destructor. */ + ~ToolbarButton(); + + /** @internal */ + bool getToolbarItemSizes (int toolbarDepth, bool isToolbarVertical, int& preferredSize, + int& minSize, int& maxSize); + /** @internal */ + void paintButtonArea (Graphics& g, int width, int height, bool isMouseOver, bool isMouseDown); + /** @internal */ + void contentAreaChanged (const Rectangle& newBounds); + + juce_UseDebuggingNewOperator + +private: + Drawable* const normalImage; + Drawable* const toggledOnImage; + + ToolbarButton (const ToolbarButton&); + const ToolbarButton& operator= (const ToolbarButton&); +}; + +#endif // __JUCE_TOOLBARBUTTON_JUCEHEADER__ +/********* End of inlined file: juce_ToolbarButton.h *********/ + +#endif +#ifndef __JUCE_DROPSHADOWEFFECT_JUCEHEADER__ + +#endif +#ifndef __JUCE_GLOWEFFECT_JUCEHEADER__ + +/********* Start of inlined file: juce_GlowEffect.h *********/ +#ifndef __JUCE_GLOWEFFECT_JUCEHEADER__ +#define __JUCE_GLOWEFFECT_JUCEHEADER__ + +/** + A component effect that adds a coloured blur around the component's contents. + + (This will only work on non-opaque components). + + @see Component::setComponentEffect, DropShadowEffect +*/ +class JUCE_API GlowEffect : public ImageEffectFilter +{ +public: + + /** Creates a default 'glow' effect. + + To customise its appearance, use the setGlowProperties() method. + */ + GlowEffect(); + + /** Destructor. */ + ~GlowEffect(); + + /** Sets the glow's radius and colour. + + The radius is how large the blur should be, and the colour is + used to render it (for a less intense glow, lower the colour's + opacity). + */ + void setGlowProperties (const float newRadius, + const Colour& newColour); + + /** @internal */ + void applyEffect (Image& sourceImage, Graphics& destContext); + + juce_UseDebuggingNewOperator + +private: + float radius; + Colour colour; +}; + +#endif // __JUCE_GLOWEFFECT_JUCEHEADER__ +/********* End of inlined file: juce_GlowEffect.h *********/ + +#endif +#ifndef __JUCE_IMAGEEFFECTFILTER_JUCEHEADER__ + +#endif +#ifndef __JUCE_REDUCEOPACITYEFFECT_JUCEHEADER__ + +/********* Start of inlined file: juce_ReduceOpacityEffect.h *********/ +#ifndef __JUCE_REDUCEOPACITYEFFECT_JUCEHEADER__ +#define __JUCE_REDUCEOPACITYEFFECT_JUCEHEADER__ + +/** + An effect filter that reduces the image's opacity. + + This can be used to make a component (and its child components) more + transparent. + + @see Component::setComponentEffect +*/ +class JUCE_API ReduceOpacityEffect : public ImageEffectFilter +{ +public: + + /** Creates the effect object. + + The opacity of the component to which the effect is applied will be + scaled by the given factor (in the range 0 to 1.0f). + */ + ReduceOpacityEffect (const float opacity = 1.0f); + + /** Destructor. */ + ~ReduceOpacityEffect(); + + /** Sets how much to scale the component's opacity. + + @param newOpacity should be between 0 and 1.0f + */ + void setOpacity (const float newOpacity); + + /** @internal */ + void applyEffect (Image& sourceImage, Graphics& destContext); + + juce_UseDebuggingNewOperator + +private: + float opacity; +}; + +#endif // __JUCE_REDUCEOPACITYEFFECT_JUCEHEADER__ +/********* End of inlined file: juce_ReduceOpacityEffect.h *********/ + +#endif +#ifndef __JUCE_KEYBOARDFOCUSTRAVERSER_JUCEHEADER__ + +#endif +#ifndef __JUCE_KEYLISTENER_JUCEHEADER__ + +#endif +#ifndef __JUCE_KEYMAPPINGEDITORCOMPONENT_JUCEHEADER__ + +/********* Start of inlined file: juce_KeyMappingEditorComponent.h *********/ +#ifndef __JUCE_KEYMAPPINGEDITORCOMPONENT_JUCEHEADER__ +#define __JUCE_KEYMAPPINGEDITORCOMPONENT_JUCEHEADER__ + +/********* Start of inlined file: juce_KeyPressMappingSet.h *********/ +#ifndef __JUCE_KEYPRESSMAPPINGSET_JUCEHEADER__ +#define __JUCE_KEYPRESSMAPPINGSET_JUCEHEADER__ + +/** + Manages and edits a list of keypresses, which it uses to invoke the appropriate + command in a ApplicationCommandManager. + + Normally, you won't actually create a KeyPressMappingSet directly, because + each ApplicationCommandManager contains its own KeyPressMappingSet, so typically + you'd create yourself an ApplicationCommandManager, and call its + ApplicationCommandManager::getKeyMappings() method to get a pointer to its + KeyPressMappingSet. + + For one of these to actually use keypresses, you'll need to add it as a KeyListener + to the top-level component for which you want to handle keystrokes. So for example: + + @code + class MyMainWindow : public Component + { + ApplicationCommandManager* myCommandManager; + + public: + MyMainWindow() + { + myCommandManager = new ApplicationCommandManager(); + + // first, make sure the command manager has registered all the commands that its + // targets can perform.. + myCommandManager->registerAllCommandsForTarget (myCommandTarget1); + myCommandManager->registerAllCommandsForTarget (myCommandTarget2); + + // this will use the command manager to initialise the KeyPressMappingSet with + // the default keypresses that were specified when the targets added their commands + // to the manager. + myCommandManager->getKeyMappings()->resetToDefaultMappings(); + + // having set up the default key-mappings, you might now want to load the last set + // of mappings that the user configured. + myCommandManager->getKeyMappings()->restoreFromXml (lastSavedKeyMappingsXML); + + // Now tell our top-level window to send any keypresses that arrive to the + // KeyPressMappingSet, which will use them to invoke the appropriate commands. + addKeyListener (myCommandManager->getKeyMappings()); + } + + ... + } + @endcode + + KeyPressMappingSet derives from ChangeBroadcaster so that interested parties can + register to be told when a command or mapping is added, removed, etc. + + There's also a UI component called KeyMappingEditorComponent that can be used + to easily edit the key mappings. + + @see Component::addKeyListener(), KeyMappingEditorComponent, ApplicationCommandManager +*/ +class JUCE_API KeyPressMappingSet : public KeyListener, + public ChangeBroadcaster, + public FocusChangeListener +{ +public: + + /** Creates a KeyPressMappingSet for a given command manager. + + Normally, you won't actually create a KeyPressMappingSet directly, because + each ApplicationCommandManager contains its own KeyPressMappingSet, so the + best thing to do is to create your ApplicationCommandManager, and use the + ApplicationCommandManager::getKeyMappings() method to access its mappings. + + When a suitable keypress happens, the manager's invoke() method will be + used to invoke the appropriate command. + + @see ApplicationCommandManager + */ + KeyPressMappingSet (ApplicationCommandManager* const commandManager) throw(); + + /** Creates an copy of a KeyPressMappingSet. */ + KeyPressMappingSet (const KeyPressMappingSet& other) throw(); + + /** Destructor. */ + ~KeyPressMappingSet(); + + ApplicationCommandManager* getCommandManager() const throw() { return commandManager; } + + /** Returns a list of keypresses that are assigned to a particular command. + + @param commandID the command's ID + */ + const Array getKeyPressesAssignedToCommand (const CommandID commandID) const throw(); + + /** Assigns a keypress to a command. + + If the keypress is already assigned to a different command, it will first be + removed from that command, to avoid it triggering multiple functions. + + @param commandID the ID of the command that you want to add a keypress to. If + this is 0, the keypress will be removed from anything that it + was previously assigned to, but not re-assigned + @param newKeyPress the new key-press + @param insertIndex if this is less than zero, the key will be appended to the + end of the list of keypresses; otherwise the new keypress will + be inserted into the existing list at this index + */ + void addKeyPress (const CommandID commandID, + const KeyPress& newKeyPress, + int insertIndex = -1) throw(); + + /** Reset all mappings to the defaults, as dictated by the ApplicationCommandManager. + + @see resetToDefaultMapping + */ + void resetToDefaultMappings() throw(); + + /** Resets all key-mappings to the defaults for a particular command. + + @see resetToDefaultMappings + */ + void resetToDefaultMapping (const CommandID commandID) throw(); + + /** Removes all keypresses that are assigned to any commands. */ + void clearAllKeyPresses() throw(); + + /** Removes all keypresses that are assigned to a particular command. */ + void clearAllKeyPresses (const CommandID commandID) throw(); + + /** Removes one of the keypresses that are assigned to a command. + + See the getKeyPressesAssignedToCommand() for the list of keypresses to + which the keyPressIndex refers. + */ + void removeKeyPress (const CommandID commandID, + const int keyPressIndex) throw(); + + /** Removes a keypress from any command that it may be assigned to. + */ + void removeKeyPress (const KeyPress& keypress) throw(); + + /** Returns true if the given command is linked to this key. */ + bool containsMapping (const CommandID commandID, + const KeyPress& keyPress) const throw(); + + /** Looks for a command that corresponds to a keypress. + + @returns the UID of the command or 0 if none was found + */ + CommandID findCommandForKeyPress (const KeyPress& keyPress) const throw(); + + /** Tries to recreate the mappings from a previously stored state. + + The XML passed in must have been created by the createXml() method. + + If the stored state makes any reference to commands that aren't + currently available, these will be ignored. + + If the set of mappings being loaded was a set of differences (using createXml (true)), + then this will call resetToDefaultMappings() and then merge the saved mappings + on top. If the saved set was created with createXml (false), then this method + will first clear all existing mappings and load the saved ones as a complete set. + + @returns true if it manages to load the XML correctly + @see createXml + */ + bool restoreFromXml (const XmlElement& xmlVersion); + + /** Creates an XML representation of the current mappings. + + This will produce a lump of XML that can be later reloaded using + restoreFromXml() to recreate the current mapping state. + + The object that is returned must be deleted by the caller. + + @param saveDifferencesFromDefaultSet if this is false, then all keypresses + will be saved into the XML. If it's true, then the XML will + only store the differences between the current mappings and + the default mappings you'd get from calling resetToDefaultMappings(). + The advantage of saving a set of differences from the default is that + if you change the default mappings (in a new version of your app, for + example), then these will be merged into a user's saved preferences. + + @see restoreFromXml + */ + XmlElement* createXml (const bool saveDifferencesFromDefaultSet) const; + + /** @internal */ + bool keyPressed (const KeyPress& key, Component* originatingComponent); + /** @internal */ + bool keyStateChanged (Component* originatingComponent); + /** @internal */ + void globalFocusChanged (Component* focusedComponent); + + juce_UseDebuggingNewOperator + +private: + + ApplicationCommandManager* commandManager; + + struct CommandMapping + { + CommandID commandID; + Array keypresses; + bool wantsKeyUpDownCallbacks; + }; + + OwnedArray mappings; + + struct KeyPressTime + { + KeyPress key; + uint32 timeWhenPressed; + }; + + OwnedArray keysDown; + + void handleMessage (const Message& message); + + void invokeCommand (const CommandID commandID, + const KeyPress& keyPress, + const bool isKeyDown, + const int millisecsSinceKeyPressed, + Component* const originatingComponent) const; + + const KeyPressMappingSet& operator= (const KeyPressMappingSet&); +}; + +#endif // __JUCE_KEYPRESSMAPPINGSET_JUCEHEADER__ +/********* End of inlined file: juce_KeyPressMappingSet.h *********/ + +/********* Start of inlined file: juce_TreeView.h *********/ +#ifndef __JUCE_TREEVIEW_JUCEHEADER__ +#define __JUCE_TREEVIEW_JUCEHEADER__ + +class TreeView; + +/** + An item in a treeview. + + A TreeViewItem can either be a leaf-node in the tree, or it can contain its + own sub-items. + + To implement an item that contains sub-items, override the itemOpennessChanged() + method so that when it is opened, it adds the new sub-items to itself using the + addSubItem method. Depending on the nature of the item it might choose to only + do this the first time it's opened, or it might want to refresh itself each time. + It also has the option of deleting its sub-items when it is closed, or leaving them + in place. +*/ +class JUCE_API TreeViewItem +{ +public: + + /** Constructor. */ + TreeViewItem(); + + /** Destructor. */ + virtual ~TreeViewItem(); + + /** Returns the number of sub-items that have been added to this item. + + Note that this doesn't mean much if the node isn't open. + + @see getSubItem, mightContainSubItems, addSubItem + */ + int getNumSubItems() const throw(); + + /** Returns one of the item's sub-items. + + Remember that the object returned might get deleted at any time when its parent + item is closed or refreshed, depending on the nature of the items you're using. + + @see getNumSubItems + */ + TreeViewItem* getSubItem (const int index) const throw(); + + /** Removes any sub-items. */ + void clearSubItems(); + + /** Adds a sub-item. + + @param newItem the object to add to the item's sub-item list. Once added, these can be + found using getSubItem(). When the items are later removed with + removeSubItem() (or when this item is deleted), they will be deleted. + @param insertPosition the index which the new item should have when it's added. If this + value is less than 0, the item will be added to the end of the list. + */ + void addSubItem (TreeViewItem* const newItem, + const int insertPosition = -1); + + /** Removes one of the sub-items. + + @param index the item to remove + @param deleteItem if true, the item that is removed will also be deleted. + */ + void removeSubItem (const int index, + const bool deleteItem = true); + + /** Returns the TreeView to which this item belongs. */ + TreeView* getOwnerView() const throw() { return ownerView; } + + /** Returns the item within which this item is contained. */ + TreeViewItem* getParentItem() const throw() { return parentItem; } + + /** True if this item is currently open in the treeview. */ + bool isOpen() const throw(); + + /** Opens or closes the item. + + When opened or closed, the item's itemOpennessChanged() method will be called, + and a subclass should use this callback to create and add any sub-items that + it needs to. + + @see itemOpennessChanged, mightContainSubItems + */ + void setOpen (const bool shouldBeOpen); + + /** True if this item is currently selected. + + Use this when painting the node, to decide whether to draw it as selected or not. + */ + bool isSelected() const throw(); + + /** Selects or deselects the item. + + This will cause a callback to itemSelectionChanged() + */ + void setSelected (const bool shouldBeSelected, + const bool deselectOtherItemsFirst); + + /** Returns the rectangle that this item occupies. + + If relativeToTreeViewTopLeft is true, the co-ordinates are relative to the + top-left of the TreeView comp, so this will depend on the scroll-position of + the tree. If false, it is relative to the top-left of the topmost item in the + tree (so this would be unaffected by scrolling the view). + */ + const Rectangle getItemPosition (const bool relativeToTreeViewTopLeft) const throw(); + + /** Sends a signal to the treeview to make it refresh itself. + + Call this if your items have changed and you want the tree to update to reflect + this. + */ + void treeHasChanged() const throw(); + + /** Returns the row number of this item in the tree. + + The row number of an item will change according to which items are open. + + @see TreeView::getNumRowsInTree(), TreeView::getItemOnRow() + */ + int getRowNumberInTree() const throw(); + + /** Changes whether lines are drawn to connect any sub-items to this item. + + By default, line-drawing is turned on. + */ + void setLinesDrawnForSubItems (const bool shouldDrawLines) throw(); + + /** Tells the tree whether this item can potentially be opened. + + If your item could contain sub-items, this should return true; if it returns + false then the tree will not try to open the item. This determines whether or + not the item will be drawn with a 'plus' button next to it. + */ + virtual bool mightContainSubItems() = 0; + + /** Returns a string to uniquely identify this item. + + If you're planning on using the TreeView::getOpennessState() method, then + these strings will be used to identify which nodes are open. The string + should be unique amongst the item's sibling items, but it's ok for there + to be duplicates at other levels of the tree. + + If you're not going to store the state, then it's ok not to bother implementing + this method. + */ + virtual const String getUniqueName() const; + + /** Called when an item is opened or closed. + + When setOpen() is called and the item has specified that it might + have sub-items with the mightContainSubItems() method, this method + is called to let the item create or manage its sub-items. + + So when this is called with isNowOpen set to true (i.e. when the item is being + opened), a subclass might choose to use clearSubItems() and addSubItem() to + refresh its sub-item list. + + When this is called with isNowOpen set to false, the subclass might want + to use clearSubItems() to save on space, or it might choose to leave them, + depending on the nature of the tree. + + You could also use this callback as a trigger to start a background process + which asynchronously creates sub-items and adds them, if that's more + appropriate for the task in hand. + + @see mightContainSubItems + */ + virtual void itemOpennessChanged (bool isNowOpen); + + /** Must return the width required by this item. + + If your item needs to have a particular width in pixels, return that value; if + you'd rather have it just fill whatever space is available in the treeview, + return -1. + + If all your items return -1, no horizontal scrollbar will be shown, but if any + items have fixed widths and extend beyond the width of the treeview, a + scrollbar will appear. + + Each item can be a different width, but if they change width, you should call + treeHasChanged() to update the tree. + */ + virtual int getItemWidth() const { return -1; } + + /** Must return the height required by this item. + + This is the height in pixels that the item will take up. Items in the tree + can be different heights, but if they change height, you should call + treeHasChanged() to update the tree. + */ + virtual int getItemHeight() const { return 20; } + + /** Creates a component that will be used to represent this item. + + You don't have to implement this method - if it returns 0 then no component + will be used for the item, and you can just draw it using the paintItem() + callback. But if you do return a component, it will be positioned in the + treeview so that it can be used to represent this item. + + The component returned will be managed by the treeview, so always return + a new component, and don't keep a reference to it, as the treeview will + delete it later when it goes off the screen or is no longer needed. Also + bear in mind that if the component keeps a reference to the item that + created it, that item could be deleted before the component. Its position + and size will be completely managed by the tree, so don't attempt to move it + around. + + Something you may want to do with your component is to give it a pointer to + the TreeView that created it. This is perfectly safe, and there's no danger + of it becoming a dangling pointer because the TreeView will always delete + the component before it is itself deleted. + + As long as you stick to these rules you can return whatever kind of + component you like. It's most useful if you're doing things like drag-and-drop + of items, or want to use a Label component to edit item names, etc. + */ + virtual Component* createItemComponent() { return 0; } + + /** Draws the item's contents. + + You can choose to either implement this method and draw each item, or you + can use createItemComponent() to create a component that will represent the + item. + + If all you need in your tree is to be able to draw the items and detect when + the user selects or double-clicks one of them, it's probably enough to + use paintItem(), itemClicked() and itemDoubleClicked(). If you need more + complicated interactions, you may need to use createItemComponent() instead. + + @param g the graphics context to draw into + @param width the width of the area available for drawing + @param height the height of the area available for drawing + */ + virtual void paintItem (Graphics& g, int width, int height); + + /** Called when the user clicks on this item. + + If you're using createItemComponent() to create a custom component for the + item, the mouse-clicks might not make it through to the treeview, but this + is how you find out about clicks when just drawing each item individually. + + The associated mouse-event details are passed in, so you can find out about + which button, where it was, etc. + + @see itemDoubleClicked + */ + virtual void itemClicked (const MouseEvent& e); + + /** Called when the user double-clicks on this item. + + If you're using createItemComponent() to create a custom component for the + item, the mouse-clicks might not make it through to the treeview, but this + is how you find out about clicks when just drawing each item individually. + + The associated mouse-event details are passed in, so you can find out about + which button, where it was, etc. + + If not overridden, the base class method here will open or close the item as + if the 'plus' button had been clicked. + + @see itemClicked + */ + virtual void itemDoubleClicked (const MouseEvent& e); + + /** Called when the item is selected or deselected. + + Use this if you want to do something special when the item's selectedness + changes. By default it'll get repainted when this happens. + */ + virtual void itemSelectionChanged (bool isNowSelected); + + /** To allow items from your treeview to be dragged-and-dropped, implement this method. + + If this returns a non-empty name then when the user drags an item, the treeview will + try to find a DragAndDropContainer in its parent hierarchy, and will use it to trigger + a drag-and-drop operation, using this string as the source description, with the treeview + itself as the source component. + + If you need more complex drag-and-drop behaviour, you can use custom components for + the items, and use those to trigger the drag. + + @see DragAndDropContainer::startDragging + */ + virtual const String getDragSourceDescription(); + + juce_UseDebuggingNewOperator + +private: + TreeView* ownerView; + TreeViewItem* parentItem; + OwnedArray subItems; + int y, itemHeight, totalHeight, itemWidth, totalWidth; + int uid; + bool selected : 1; + bool redrawNeeded : 1; + bool drawLinesInside : 1; + unsigned int openness : 2; + + friend class TreeView; + friend class TreeViewContentComponent; + + void updatePositions (int newY); + int getIndentX() const throw(); + void setOwnerView (TreeView* const newOwner) throw(); + void paintRecursively (Graphics& g, int width); + TreeViewItem* findItemRecursively (int y) throw(); + TreeViewItem* getDeepestOpenParentItem() throw(); + void restoreFromXml (const XmlElement& e); + XmlElement* createXmlOpenness() const; + bool isLastOfSiblings() const throw(); + TreeViewItem* getTopLevelItem() throw(); + int getNumRows() const throw(); + TreeViewItem* getItemOnRow (int index) throw(); + void deselectAllRecursively(); + int countSelectedItemsRecursively() const throw(); + TreeViewItem* getSelectedItemWithIndex (int index) throw(); + TreeViewItem* getNextVisibleItem (const bool recurse) const throw(); + + TreeViewItem (const TreeViewItem&); + const TreeViewItem& operator= (const TreeViewItem&); +}; + +/** + A tree-view component. + + Use one of these to hold and display a structure of TreeViewItem objects. + +*/ +class JUCE_API TreeView : public Component, + public SettableTooltipClient, + private AsyncUpdater +{ +public: + + /** Creates an empty treeview. + + Once you've got a treeview component, you'll need to give it something to + display, using the setRootItem() method. + */ + TreeView (const String& componentName = String::empty); + + /** Destructor. */ + ~TreeView(); + + /** Sets the item that is displayed in the treeview. + + A tree has a single root item which contains as many sub-items as it needs. If + you want the tree to contain a number of root items, you should still use a single + root item above these, but hide it using setRootItemVisible(). + + You can pass in 0 to this method to clear the tree and remove its current root item. + + The object passed in will not be deleted by the treeview, it's up to the caller + to delete it when no longer needed. BUT make absolutely sure that you don't delete + this item until you've removed it from the tree, either by calling setRootItem (0), + or by deleting the tree first. + */ + void setRootItem (TreeViewItem* const newRootItem); + + /** Returns the tree's root item. + + This will be the last object passed to setRootItem(), or 0 if none has been set. + */ + TreeViewItem* getRootItem() const throw() { return rootItem; } + + /** Changes whether the tree's root item is shown or not. + + If the root item is hidden, only its sub-items will be shown in the treeview - this + lets you make the tree look as if it's got many root items. If it's hidden, this call + will also make sure the root item is open (otherwise the treeview would look empty). + */ + void setRootItemVisible (const bool shouldBeVisible); + + /** Returns true if the root item is visible. + + @see setRootItemVisible + */ + bool isRootItemVisible() const throw() { return rootItemVisible; } + + /** Sets whether items are open or closed by default. + + Normally, items are closed until the user opens them, but you can use this + to make them default to being open until explicitly closed. + + @see areItemsOpenByDefault + */ + void setDefaultOpenness (const bool isOpenByDefault); + + /** Returns true if the tree's items default to being open. + + @see setDefaultOpenness + */ + bool areItemsOpenByDefault() const throw() { return defaultOpenness; } + + /** This sets a flag to indicate that the tree can be used for multi-selection. + + You can always select multiple items internally by calling the + TreeViewItem::setSelected() method, but this flag indicates whether the user + is allowed to multi-select by clicking on the tree. + + By default it is disabled. + + @see isMultiSelectEnabled + */ + void setMultiSelectEnabled (const bool canMultiSelect); + + /** Returns whether multi-select has been enabled for the tree. + + @see setMultiSelectEnabled + */ + bool isMultiSelectEnabled() const throw() { return multiSelectEnabled; } + + /** Deselects any items that are currently selected. */ + void clearSelectedItems(); + + /** Returns the number of items that are currently selected. + + @see getSelectedItem, clearSelectedItems + */ + int getNumSelectedItems() const throw(); + + /** Returns one of the selected items in the tree. + + @param index the index, 0 to (getNumSelectedItems() - 1) + */ + TreeViewItem* getSelectedItem (const int index) const throw(); + + /** Returns the number of rows the tree is using. + + This will depend on which items are open. + + @see TreeViewItem::getRowNumberInTree() + */ + int getNumRowsInTree() const; + + /** Returns the item on a particular row of the tree. + + If the index is out of range, this will return 0. + + @see getNumRowsInTree, TreeViewItem::getRowNumberInTree() + */ + TreeViewItem* getItemOnRow (int index) const; + + /** Tries to scroll the tree so that this item is on-screen somewhere. */ + void scrollToKeepItemVisible (TreeViewItem* item); + + /** Returns the treeview's Viewport object. */ + Viewport* getViewport() const throw() { return viewport; } + + /** Returns the number of pixels by which each nested level of the tree is indented. + @see setIndentSize + */ + int getIndentSize() const throw() { return indentSize; } + + /** Changes the distance by which each nested level of the tree is indented. + @see getIndentSize + */ + void setIndentSize (const int newIndentSize); + + /** Saves the current state of open/closed nodes so it can be restored later. + + This takes a snapshot of which nodes have been explicitly opened or closed, + and records it as XML. To identify node objects it uses the + TreeViewItem::getUniqueName() method to create named paths. This + means that the same state of open/closed nodes can be restored to a + completely different instance of the tree, as long as it contains nodes + whose unique names are the same. + + The caller is responsible for deleting the object that is returned. + + @param alsoIncludeScrollPosition if this is true, the state will also + include information about where the + tree has been scrolled to vertically, + so this can also be restored + @see restoreOpennessState + */ + XmlElement* getOpennessState (const bool alsoIncludeScrollPosition) const; + + /** Restores a previously saved arrangement of open/closed nodes. + + This will try to restore a snapshot of the tree's state that was created by + the getOpennessState() method. If any of the nodes named in the original + XML aren't present in this tree, they will be ignored. + + @see getOpennessState + */ + void restoreOpennessState (const XmlElement& newState); + + /** A set of colour IDs to use to change the colour of various aspects of the treeview. + + These constants can be used either via the Component::setColour(), or LookAndFeel::setColour() + methods. + + @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour + */ + enum ColourIds + { + backgroundColourId = 0x1000500, /**< A background colour to fill the component with. */ + linesColourId = 0x1000501 /**< The colour to draw the lines with.*/ + }; + + /** @internal */ + void paint (Graphics& g); + /** @internal */ + void resized(); + /** @internal */ + bool keyPressed (const KeyPress& key); + /** @internal */ + void colourChanged(); + + juce_UseDebuggingNewOperator + +private: + friend class TreeViewItem; + friend class TreeViewContentComponent; + Viewport* viewport; + CriticalSection nodeAlterationLock; + TreeViewItem* rootItem; + int indentSize; + bool defaultOpenness : 1; + bool needsRecalculating : 1; + bool rootItemVisible : 1; + bool multiSelectEnabled : 1; + + void itemsChanged() throw(); + void handleAsyncUpdate(); + void moveSelectedRow (int delta); + + TreeView (const TreeView&); + const TreeView& operator= (const TreeView&); +}; + +#endif // __JUCE_TREEVIEW_JUCEHEADER__ +/********* End of inlined file: juce_TreeView.h *********/ + +/** + A component to allow editing of the keymaps stored by a KeyPressMappingSet + object. + + @see KeyPressMappingSet +*/ +class JUCE_API KeyMappingEditorComponent : public Component, + public TreeViewItem, + public ChangeListener, + private ButtonListener +{ +public: + + /** Creates a KeyMappingEditorComponent. + + @param mappingSet this is the set of mappings to display and + edit. Make sure the mappings object is not + deleted before this component! + @param showResetToDefaultButton if true, then at the bottom of the + list, the component will include a 'reset to + defaults' button. + */ + KeyMappingEditorComponent (KeyPressMappingSet* const mappingSet, + const bool showResetToDefaultButton); + + /** Destructor. */ + virtual ~KeyMappingEditorComponent(); + + /** Sets up the colours to use for parts of the component. + + @param mainBackground colour to use for most of the background + @param textColour colour to use for the text + */ + void setColours (const Colour& mainBackground, + const Colour& textColour); + + /** Returns the KeyPressMappingSet that this component is acting upon. + */ + KeyPressMappingSet* getMappings() const throw() { return mappings; } + + /** Can be overridden if some commands need to be excluded from the list. + + By default this will use the KeyPressMappingSet's shouldCommandBeVisibleInEditor() + method to decide what to return, but you can override it to handle special cases. + */ + virtual bool shouldCommandBeIncluded (const CommandID commandID); + + /** Can be overridden to indicate that some commands are shown as read-only. + + By default this will use the KeyPressMappingSet's shouldCommandBeReadOnlyInEditor() + method to decide what to return, but you can override it to handle special cases. + */ + virtual bool isCommandReadOnly (const CommandID commandID); + + /** This can be overridden to let you change the format of the string used + to describe a keypress. + + This is handy if you're using non-standard KeyPress objects, e.g. for custom + keys that are triggered by something else externally. If you override the + method, be sure to let the base class's method handle keys you're not + interested in. + */ + virtual const String getDescriptionForKeyPress (const KeyPress& key); + + /** @internal */ + void parentHierarchyChanged(); + /** @internal */ + void resized(); + /** @internal */ + void changeListenerCallback (void*); + /** @internal */ + bool mightContainSubItems(); + /** @internal */ + const String getUniqueName() const; + /** @internal */ + void buttonClicked (Button* button); + + juce_UseDebuggingNewOperator + +private: + + KeyPressMappingSet* mappings; + TreeView* tree; + friend class KeyMappingTreeViewItem; + friend class KeyCategoryTreeViewItem; + friend class KeyMappingItemComponent; + friend class KeyMappingChangeButton; + Colour backgroundColour, textColour; + TextButton* resetButton; + + void assignNewKey (const CommandID commandID, int index); + + KeyMappingEditorComponent (const KeyMappingEditorComponent&); + const KeyMappingEditorComponent& operator= (const KeyMappingEditorComponent&); +}; + +#endif // __JUCE_KEYMAPPINGEDITORCOMPONENT_JUCEHEADER__ +/********* End of inlined file: juce_KeyMappingEditorComponent.h *********/ + +#endif +#ifndef __JUCE_KEYPRESS_JUCEHEADER__ + +#endif +#ifndef __JUCE_KEYPRESSMAPPINGSET_JUCEHEADER__ + +#endif +#ifndef __JUCE_MODIFIERKEYS_JUCEHEADER__ + +#endif +#ifndef __JUCE_MENUBARCOMPONENT_JUCEHEADER__ + +/********* Start of inlined file: juce_MenuBarComponent.h *********/ +#ifndef __JUCE_MENUBARCOMPONENT_JUCEHEADER__ +#define __JUCE_MENUBARCOMPONENT_JUCEHEADER__ + +/********* Start of inlined file: juce_MenuBarModel.h *********/ +#ifndef __JUCE_MENUBARMODEL_JUCEHEADER__ +#define __JUCE_MENUBARMODEL_JUCEHEADER__ + +class MenuBarModel; + +/** + A class to receive callbacks when a MenuBarModel changes. + + @see MenuBarModel::addListener, MenuBarModel::removeListener, MenuBarModel::menuItemsChanged +*/ +class JUCE_API MenuBarModelListener +{ +public: + /** Destructor. */ + virtual ~MenuBarModelListener() {} + + /** This callback is made when items are changed in the menu bar model. + */ + virtual void menuBarItemsChanged (MenuBarModel* menuBarModel) = 0; + + /** This callback is made when an application command is invoked that + is represented by one of the items in the menu bar model. + */ + virtual void menuCommandInvoked (MenuBarModel* menuBarModel, + const ApplicationCommandTarget::InvocationInfo& info) = 0; +}; + +/** + A class for controlling MenuBar components. + + This class is used to tell a MenuBar what menus to show, and to respond + to a menu being selected. + + @see MenuBarModelListener, MenuBarComponent, PopupMenu +*/ +class JUCE_API MenuBarModel : private AsyncUpdater, + private ApplicationCommandManagerListener +{ +public: + + MenuBarModel() throw(); + + /** Destructor. */ + virtual ~MenuBarModel(); + + /** Call this when some of your menu items have changed. + + This method will cause a callback to any MenuBarListener objects that + are registered with this model. + + If this model is displaying items from an ApplicationCommandManager, you + can use the setApplicationCommandManagerToWatch() method to cause + change messages to be sent automatically when the ApplicationCommandManager + is changed. + + @see addListener, removeListener, MenuBarListener + */ + void menuItemsChanged(); + + /** Tells the menu bar to listen to the specified command manager, and to update + itself when the commands change. + + This will also allow it to flash a menu name when a command from that menu + is invoked using a keystroke. + */ + void setApplicationCommandManagerToWatch (ApplicationCommandManager* const manager) throw(); + + /** Registers a listener for callbacks when the menu items in this model change. + + The listener object will get callbacks when this object's menuItemsChanged() + method is called. + + @see removeListener + */ + void addListener (MenuBarModelListener* const listenerToAdd) throw(); + + /** Removes a listener. + + @see addListener + */ + void removeListener (MenuBarModelListener* const listenerToRemove) throw(); + + /** This method must return a list of the names of the menus. */ + virtual const StringArray getMenuBarNames() = 0; + + /** This should return the popup menu to display for a given top-level menu. + + @param topLevelMenuIndex the index of the top-level menu to show + @param menuName the name of the top-level menu item to show + */ + virtual const PopupMenu getMenuForIndex (int topLevelMenuIndex, + const String& menuName) = 0; + + /** This is called when a menu item has been clicked on. + + @param menuItemID the item ID of the PopupMenu item that was selected + @param topLevelMenuIndex the index of the top-level menu from which the item was + chosen (just in case you've used duplicate ID numbers + on more than one of the popup menus) + */ + virtual void menuItemSelected (int menuItemID, + int topLevelMenuIndex) = 0; + +#if JUCE_MAC || DOXYGEN + /** MAC ONLY - Sets the model that is currently being shown as the main + menu bar at the top of the screen on the Mac. + + You can pass 0 to stop the current model being displayed. Be careful + not to delete a model while it is being used. + */ + static void setMacMainMenu (MenuBarModel* newMenuBarModel) throw(); + + /** MAC ONLY - Returns the menu model that is currently being shown as + the main menu bar. + */ + static MenuBarModel* getMacMainMenu() throw(); +#endif + + /** @internal */ + void applicationCommandInvoked (const ApplicationCommandTarget::InvocationInfo& info); + /** @internal */ + void applicationCommandListChanged(); + /** @internal */ + void handleAsyncUpdate(); + + juce_UseDebuggingNewOperator + +private: + ApplicationCommandManager* manager; + SortedSet listeners; + + MenuBarModel (const MenuBarModel&); + const MenuBarModel& operator= (const MenuBarModel&); +}; + +#endif // __JUCE_MENUBARMODEL_JUCEHEADER__ +/********* End of inlined file: juce_MenuBarModel.h *********/ + +/** + A menu bar component. + + @see MenuBarModel +*/ +class JUCE_API MenuBarComponent : public Component, + private MenuBarModelListener, + private Timer +{ +public: + + /** Creates a menu bar. + + @param model the model object to use to control this bar. You can + pass 0 into this if you like, and set the model later + using the setModel() method + */ + MenuBarComponent (MenuBarModel* const model); + + /** Destructor. */ + ~MenuBarComponent(); + + /** Changes the model object to use to control the bar. + + This can be 0, in which case the bar will be empty. Don't delete the object + that is passed-in while it's still being used by this MenuBar. + */ + void setModel (MenuBarModel* const newModel); + + /** Pops up one of the menu items. + + This lets you manually open one of the menus - it could be triggered by a + key shortcut, for example. + */ + void showMenu (const int menuIndex); + + /** @internal */ + void paint (Graphics& g); + /** @internal */ + void resized(); + /** @internal */ + void mouseEnter (const MouseEvent& e); + /** @internal */ + void mouseExit (const MouseEvent& e); + /** @internal */ + void mouseDown (const MouseEvent& e); + /** @internal */ + void mouseDrag (const MouseEvent& e); + /** @internal */ + void mouseUp (const MouseEvent& e); + /** @internal */ + void mouseMove (const MouseEvent& e); + /** @internal */ + void inputAttemptWhenModal(); + /** @internal */ + void handleCommandMessage (int commandId); + /** @internal */ + bool keyPressed (const KeyPress& key); + /** @internal */ + void menuBarItemsChanged (MenuBarModel* menuBarModel); + /** @internal */ + void menuCommandInvoked (MenuBarModel* menuBarModel, + const ApplicationCommandTarget::InvocationInfo& info); + + juce_UseDebuggingNewOperator + +private: + MenuBarModel* model; + + StringArray menuNames; + Array xPositions; + int itemUnderMouse, currentPopupIndex, topLevelIndexClicked, indexToShowAgain; + int lastMouseX, lastMouseY; + bool inModalState; + Component* currentPopup; + + int getItemAt (int x, int y); + void updateItemUnderMouse (const int x, const int y); + void hideCurrentMenu(); + void timerCallback(); + void repaintMenuItem (int index); + + MenuBarComponent (const MenuBarComponent&); + const MenuBarComponent& operator= (const MenuBarComponent&); +}; + +#endif // __JUCE_MENUBARCOMPONENT_JUCEHEADER__ +/********* End of inlined file: juce_MenuBarComponent.h *********/ + +#endif +#ifndef __JUCE_MENUBARMODEL_JUCEHEADER__ + +#endif +#ifndef __JUCE_POPUPMENU_JUCEHEADER__ + +#endif +#ifndef __JUCE_POPUPMENUCUSTOMCOMPONENT_JUCEHEADER__ + +#endif +#ifndef __JUCE_COMPONENTDRAGGER_JUCEHEADER__ + +/********* Start of inlined file: juce_ComponentDragger.h *********/ +#ifndef __JUCE_COMPONENTDRAGGER_JUCEHEADER__ +#define __JUCE_COMPONENTDRAGGER_JUCEHEADER__ + +/********* Start of inlined file: juce_ComponentBoundsConstrainer.h *********/ +#ifndef __JUCE_COMPONENTBOUNDSCONSTRAINER_JUCEHEADER__ +#define __JUCE_COMPONENTBOUNDSCONSTRAINER_JUCEHEADER__ + +/** + A class that imposes restrictions on a Component's size or position. + + This is used by classes such as ResizableCornerComponent, + ResizableBorderComponent and ResizableWindow. + + The base class can impose some basic size and position limits, but you can + also subclass this for custom uses. + + @see ResizableCornerComponent, ResizableBorderComponent, ResizableWindow +*/ +class JUCE_API ComponentBoundsConstrainer +{ +public: + + /** When first created, the object will not impose any restrictions on the components. */ + ComponentBoundsConstrainer() throw(); + + /** Destructor. */ + virtual ~ComponentBoundsConstrainer(); + + /** Imposes a minimum width limit. */ + void setMinimumWidth (const int minimumWidth) throw(); + + /** Returns the current minimum width. */ + int getMinimumWidth() const throw() { return minW; } + + /** Imposes a maximum width limit. */ + void setMaximumWidth (const int maximumWidth) throw(); + + /** Returns the current maximum width. */ + int getMaximumWidth() const throw() { return maxW; } + + /** Imposes a minimum height limit. */ + void setMinimumHeight (const int minimumHeight) throw(); + + /** Returns the current minimum height. */ + int getMinimumHeight() const throw() { return minH; } + + /** Imposes a maximum height limit. */ + void setMaximumHeight (const int maximumHeight) throw(); + + /** Returns the current maximum height. */ + int getMaximumHeight() const throw() { return maxH; } + + /** Imposes a minimum width and height limit. */ + void setMinimumSize (const int minimumWidth, + const int minimumHeight) throw(); + + /** Imposes a maximum width and height limit. */ + void setMaximumSize (const int maximumWidth, + const int maximumHeight) throw(); + + /** Set all the maximum and minimum dimensions. */ + void setSizeLimits (const int minimumWidth, + const int minimumHeight, + const int maximumWidth, + const int maximumHeight) throw(); + + /** Sets the amount by which the component is allowed to go off-screen. + + The values indicate how many pixels must remain on-screen when dragged off + one of its parent's edges, so e.g. if minimumWhenOffTheTop is set to 10, then + when the component goes off the top of the screen, its y-position will be + clipped so that there are always at least 10 pixels on-screen. In other words, + the lowest y-position it can take would be (10 - the component's height). + + If you pass 0 or less for one of these amounts, the component is allowed + to move beyond that edge completely, with no restrictions at all. + + If you pass a very large number (i.e. larger that the dimensions of the + component itself), then the component won't be allowed to overlap that + edge at all. So e.g. setting minimumWhenOffTheLeft to 0xffffff will mean that + the component will bump into the left side of the screen and go no further. + */ + void setMinimumOnscreenAmounts (const int minimumWhenOffTheTop, + const int minimumWhenOffTheLeft, + const int minimumWhenOffTheBottom, + const int minimumWhenOffTheRight) throw(); + + /** Specifies a width-to-height ratio that the resizer should always maintain. + + If the value is 0, no aspect ratio is enforced. If it's non-zero, the width + will always be maintained as this multiple of the height. + + @see setResizeLimits + */ + void setFixedAspectRatio (const double widthOverHeight) throw(); + + /** Returns the aspect ratio that was set with setFixedAspectRatio(). + + If no aspect ratio is being enforced, this will return 0. + */ + double getFixedAspectRatio() const throw(); + + /** This callback changes the given co-ordinates to impose whatever the current + constraints are set to be. + + @param x the x position that should be examined and adjusted + @param y the y position that should be examined and adjusted + @param w the width that should be examined and adjusted + @param h the height that should be examined and adjusted + @param previousBounds the component's current size + @param limits the region in which the component can be positioned + @param isStretchingTop whether the top edge of the component is being resized + @param isStretchingLeft whether the left edge of the component is being resized + @param isStretchingBottom whether the bottom edge of the component is being resized + @param isStretchingRight whether the right edge of the component is being resized + */ + virtual void checkBounds (int& x, int& y, int& w, int& h, + const Rectangle& previousBounds, + const Rectangle& limits, + const bool isStretchingTop, + const bool isStretchingLeft, + const bool isStretchingBottom, + const bool isStretchingRight); + + /** This callback happens when the resizer is about to start dragging. */ + virtual void resizeStart(); + + /** This callback happens when the resizer has finished dragging. */ + virtual void resizeEnd(); + + /** Checks the given bounds, and then sets the component to the corrected size. */ + void setBoundsForComponent (Component* const component, + int x, int y, int w, int h, + const bool isStretchingTop, + const bool isStretchingLeft, + const bool isStretchingBottom, + const bool isStretchingRight); + + /** Called by setBoundsForComponent() to apply a new constrained size to a + component. + + By default this just calls setBounds(), but it virtual in case it's needed for + extremely cunning purposes. + */ + virtual void applyBoundsToComponent (Component* component, + int x, int y, int w, int h); + + juce_UseDebuggingNewOperator + +private: + int minW, maxW, minH, maxH; + int minOffTop, minOffLeft, minOffBottom, minOffRight; + double aspectRatio; + + ComponentBoundsConstrainer (const ComponentBoundsConstrainer&); + const ComponentBoundsConstrainer& operator= (const ComponentBoundsConstrainer&); +}; + +#endif // __JUCE_COMPONENTBOUNDSCONSTRAINER_JUCEHEADER__ +/********* End of inlined file: juce_ComponentBoundsConstrainer.h *********/ + +/** + An object to take care of the logic for dragging components around with the mouse. + + Very easy to use - in your mouseDown() callback, call startDraggingComponent(), + then in your mouseDrag() callback, call dragComponent(). + + When starting a drag, you can give it a ComponentBoundsConstrainer to use + to limit the component's position and keep it on-screen. + + e.g. @code + class MyDraggableComp + { + ComponentDragger myDragger; + + void mouseDown (const MouseEvent& e) + { + myDragger.startDraggingComponent (this, 0); + } + + void mouseDrag (const MouseEvent& e) + { + myDragger.dragComponent (this, e); + } + }; + @endcode +*/ +class JUCE_API ComponentDragger +{ +public: + + /** Creates a ComponentDragger. */ + ComponentDragger(); + + /** Destructor. */ + virtual ~ComponentDragger(); + + /** Call this from your component's mouseDown() method, to prepare for dragging. + + @param componentToDrag the component that you want to drag + @param constrainer a constrainer object to use to keep the component + from going offscreen + @see dragComponent + */ + void startDraggingComponent (Component* const componentToDrag, + ComponentBoundsConstrainer* constrainer); + + /** Call this from your mouseDrag() callback to move the component. + + This will move the component, but will first check the validity of the + component's new position using the checkPosition() method, which you + can override if you need to enforce special positioning limits on the + component. + + @param componentToDrag the component that you want to drag + @param e the current mouse-drag event + @see dragComponent + */ + void dragComponent (Component* const componentToDrag, + const MouseEvent& e); + + juce_UseDebuggingNewOperator + +private: + ComponentBoundsConstrainer* constrainer; + int originalX, originalY; +}; + +#endif // __JUCE_COMPONENTDRAGGER_JUCEHEADER__ +/********* End of inlined file: juce_ComponentDragger.h *********/ + +#endif +#ifndef __JUCE_DRAGANDDROPCONTAINER_JUCEHEADER__ + +#endif +#ifndef __JUCE_DRAGANDDROPTARGET_JUCEHEADER__ + +#endif +#ifndef __JUCE_FILEDRAGANDDROPTARGET_JUCEHEADER__ + +/********* Start of inlined file: juce_FileDragAndDropTarget.h *********/ +#ifndef __JUCE_FILEDRAGANDDROPTARGET_JUCEHEADER__ +#define __JUCE_FILEDRAGANDDROPTARGET_JUCEHEADER__ + +/** + Components derived from this class can have files dropped onto them by an external application. + + @see DragAndDropContainer +*/ +class JUCE_API FileDragAndDropTarget +{ +public: + /** Destructor. */ + virtual ~FileDragAndDropTarget() {} + + /** Callback to check whether this target is interested in the set of files being offered. + + Note that this will be called repeatedly when the user is dragging the mouse around over your + component, so don't do anything time-consuming in here, like opening the files to have a look + inside them! + + @param files the set of (absolute) pathnames of the files that the user is dragging + @returns true if this component wants to receive the other callbacks regarging this + type of object; if it returns false, no other callbacks will be made. + */ + virtual bool isInterestedInFileDrag (const StringArray& files) = 0; + + /** Callback to indicate that some files are being dragged over this component. + + This gets called when the user moves the mouse into this component while dragging. + + Use this callback as a trigger to make your component repaint itself to give the + user feedback about whether the files can be dropped here or not. + + @param files the set of (absolute) pathnames of the files that the user is dragging + @param x the mouse x position, relative to this component + @param y the mouse y position, relative to this component + */ + virtual void fileDragEnter (const StringArray& files, int x, int y); + + /** Callback to indicate that the user is dragging some files over this component. + + This gets called when the user moves the mouse over this component while dragging. + Normally overriding itemDragEnter() and itemDragExit() are enough, but + this lets you know what happens in-between. + + @param files the set of (absolute) pathnames of the files that the user is dragging + @param x the mouse x position, relative to this component + @param y the mouse y position, relative to this component + */ + virtual void fileDragMove (const StringArray& files, int x, int y); + + /** Callback to indicate that the mouse has moved away from this component. + + This gets called when the user moves the mouse out of this component while dragging + the files. + + If you've used fileDragEnter() to repaint your component and give feedback, use this + as a signal to repaint it in its normal state. + + @param files the set of (absolute) pathnames of the files that the user is dragging + */ + virtual void fileDragExit (const StringArray& files); + + /** Callback to indicate that the user has dropped the files onto this component. + + When the user drops the files, this get called, and you can use the files in whatever + way is appropriate. + + Note that after this is called, the fileDragExit method may not be called, so you should + clean up in here if there's anything you need to do when the drag finishes. + + @param files the set of (absolute) pathnames of the files that the user is dragging + @param x the mouse x position, relative to this component + @param y the mouse y position, relative to this component + */ + virtual void filesDropped (const StringArray& files, int x, int y) = 0; +}; + +#endif // __JUCE_FILEDRAGANDDROPTARGET_JUCEHEADER__ +/********* End of inlined file: juce_FileDragAndDropTarget.h *********/ + +#endif +#ifndef __JUCE_LASSOCOMPONENT_JUCEHEADER__ + +/********* Start of inlined file: juce_LassoComponent.h *********/ +#ifndef __JUCE_LASSOCOMPONENT_JUCEHEADER__ +#define __JUCE_LASSOCOMPONENT_JUCEHEADER__ + +/********* Start of inlined file: juce_SelectedItemSet.h *********/ +#ifndef __JUCE_SELECTEDITEMSET_JUCEHEADER__ +#define __JUCE_SELECTEDITEMSET_JUCEHEADER__ + +/** Manages a list of selectable items. + + Use one of these to keep a track of things that the user has highlighted, like + icons or things in a list. + + The class is templated so that you can use it to hold either a set of pointers + to objects, or a set of ID numbers or handles, for cases where each item may + not always have a corresponding object. + + To be informed when items are selected/deselected, register a ChangeListener with + this object. + + @see SelectableObject +*/ +template +class JUCE_API SelectedItemSet : public ChangeBroadcaster +{ +public: + + /** Creates an empty set. */ + SelectedItemSet() + { + } + + /** Creates a set based on an array of items. */ + SelectedItemSet (const Array & items) + : selectedItems (items) + { + } + + /** Creates a copy of another set. */ + SelectedItemSet (const SelectedItemSet& other) + : selectedItems (other.selectedItems) + { + } + + /** Creates a copy of another set. */ + const SelectedItemSet& operator= (const SelectedItemSet& other) + { + if (selectedItems != other.selectedItems) + { + selectedItems = other.selectedItems; + changed(); + } + + return *this; + } + + /** Destructor. */ + ~SelectedItemSet() + { + } + + /** Clears any other currently selected items, and selects this item. + + If this item is already the only thing selected, no change notification + will be sent out. + + @see addToSelection, addToSelectionBasedOnModifiers + */ + void selectOnly (SelectableItemType item) + { + if (isSelected (item)) + { + for (int i = selectedItems.size(); --i >= 0;) + { + if (selectedItems.getUnchecked(i) != item) + { + deselect (selectedItems.getUnchecked(i)); + i = jmin (i, selectedItems.size()); + } + } + } + else + { + deselectAll(); + changed(); + + selectedItems.add (item); + itemSelected (item); + } + } + + /** Selects an item. + + If the item is already selected, no change notification will be sent out. + + @see selectOnly, addToSelectionBasedOnModifiers + */ + void addToSelection (SelectableItemType item) + { + if (! isSelected (item)) + { + changed(); + + selectedItems.add (item); + itemSelected (item); + } + } + + /** Selects or deselects an item. + + This will use the modifier keys to decide whether to deselect other items + first. + + So if the shift key is held down, the item will be added without deselecting + anything (same as calling addToSelection() ) + + If no modifiers are down, the current selection will be cleared first (same + as calling selectOnly() ) + + If the ctrl (or command on the Mac) key is held down, the item will be toggled - + so it'll be added to the set unless it's already there, in which case it'll be + deselected. + + If the items that you're selecting can also be dragged, you may need to use the + addToSelectionOnMouseDown() and addToSelectionOnMouseUp() calls to handle the + subtleties of this kind of usage. + + @see selectOnly, addToSelection, addToSelectionOnMouseDown, addToSelectionOnMouseUp + */ + void addToSelectionBasedOnModifiers (SelectableItemType item, + const ModifierKeys& modifiers) + { + if (modifiers.isShiftDown()) + { + addToSelection (item); + } + else if (modifiers.isCommandDown()) + { + if (isSelected (item)) + deselect (item); + else + addToSelection (item); + } + else + { + selectOnly (item); + } + } + + /** Selects or deselects items that can also be dragged, based on a mouse-down event. + + If you call addToSelectionOnMouseDown() at the start of your mouseDown event, + and then call addToSelectionOnMouseUp() at the end of your mouseUp event, this + makes it easy to handle multiple-selection of sets of objects that can also + be dragged. + + For example, if you have several items already selected, and you click on + one of them (without dragging), then you'd expect this to deselect the other, and + just select the item you clicked on. But if you had clicked on this item and + dragged it, you'd have expected them all to stay selected. + + When you call this method, you'll need to store the boolean result, because the + addToSelectionOnMouseUp() method will need to be know this value. + + @see addToSelectionOnMouseUp, addToSelectionBasedOnModifiers + */ + bool addToSelectionOnMouseDown (SelectableItemType item, + const ModifierKeys& modifiers) + { + if (isSelected (item)) + { + return ! modifiers.isPopupMenu(); + } + else + { + addToSelectionBasedOnModifiers (item, modifiers); + return false; + } + } + + /** Selects or deselects items that can also be dragged, based on a mouse-up event. + + Call this during a mouseUp callback, when you have previously called the + addToSelectionOnMouseDown() method during your mouseDown event. + + See addToSelectionOnMouseDown() for more info + + @param item the item to select (or deselect) + @param modifiers the modifiers from the mouse-up event + @param wasItemDragged true if your item was dragged during the mouse click + @param resultOfMouseDownSelectMethod this is the boolean return value that came + back from the addToSelectionOnMouseDown() call that you + should have made during the matching mouseDown event + */ + void addToSelectionOnMouseUp (SelectableItemType item, + const ModifierKeys& modifiers, + const bool wasItemDragged, + const bool resultOfMouseDownSelectMethod) + { + if (resultOfMouseDownSelectMethod && ! wasItemDragged) + addToSelectionBasedOnModifiers (item, modifiers); + } + + /** Deselects an item. */ + void deselect (SelectableItemType item) + { + const int i = selectedItems.indexOf (item); + + if (i >= 0) + { + changed(); + itemDeselected (selectedItems.remove (i)); + } + } + + /** Deselects all items. */ + void deselectAll() + { + if (selectedItems.size() > 0) + { + changed(); + + for (int i = selectedItems.size(); --i >= 0;) + { + itemDeselected (selectedItems.remove (i)); + i = jmin (i, selectedItems.size()); + } + } + } + + /** Returns the number of currently selected items. + + @see getSelectedItem + */ + int getNumSelected() const throw() + { + return selectedItems.size(); + } + + /** Returns one of the currently selected items. + + Returns 0 if the index is out-of-range. + + @see getNumSelected + */ + SelectableItemType getSelectedItem (const int index) const throw() + { + return selectedItems [index]; + } + + /** True if this item is currently selected. */ + bool isSelected (const SelectableItemType item) const throw() + { + return selectedItems.contains (item); + } + + const Array & getItemArray() const throw() { return selectedItems; } + + /** Can be overridden to do special handling when an item is selected. + + For example, if the item is an object, you might want to call it and tell + it that it's being selected. + */ + virtual void itemSelected (SelectableItemType item) {} + + /** Can be overridden to do special handling when an item is deselected. + + For example, if the item is an object, you might want to call it and tell + it that it's being deselected. + */ + virtual void itemDeselected (SelectableItemType item) {} + + /** Used internally, but can be called to force a change message to be sent to the ChangeListeners. + */ + void changed (const bool synchronous = false) + { + if (synchronous) + sendSynchronousChangeMessage (this); + else + sendChangeMessage (this); + } + + juce_UseDebuggingNewOperator + +private: + Array selectedItems; +}; + +#endif // __JUCE_SELECTEDITEMSET_JUCEHEADER__ +/********* End of inlined file: juce_SelectedItemSet.h *********/ + +/** + A class used by the LassoComponent to manage the things that it selects. + + This allows the LassoComponent to find out which items are within the lasso, + and to change the list of selected items. + + @see LassoComponent, SelectedItemSet +*/ +template +class LassoSource +{ +public: + /** Destructor. */ + virtual ~LassoSource() {} + + /** Returns the set of items that lie within a given lassoable region. + + Your implementation of this method must find all the relevent items that lie + within the given rectangle. and add them to the itemsFound array. + + The co-ordinates are relative to the top-left of the lasso component's parent + component. (i.e. they are the same as the size and position of the lasso + component itself). + */ + virtual void findLassoItemsInArea (Array & itemsFound, + int x, int y, int width, int height) = 0; + + /** Returns the SelectedItemSet that the lasso should update. + + This set will be continuously updated by the LassoComponent as it gets + dragged around, so make sure that you've got a ChangeListener attached to + the set so that your UI objects will know when the selection changes and + be able to update themselves appropriately. + */ + virtual SelectedItemSet & getLassoSelection() = 0; +}; + +/** + A component that acts as a rectangular selection region, which you drag with + the mouse to select groups of objects (in conjunction with a SelectedItemSet). + + To use one of these: + + - In your mouseDown or mouseDrag event, add the LassoComponent to your parent + component, and call its beginLasso() method, giving it a + suitable LassoSource object that it can use to find out which items are in + the active area. + + - Each time your parent component gets a mouseDrag event, call dragLasso() + to update the lasso's position - it will use its LassoSource to calculate and + update the current selection. + + - After the drag has finished and you get a mouseUp callback, you should call + endLasso() to clean up. This will make the lasso component invisible, and you + can remove it from the parent component, or delete it. + + The class takes into account the modifier keys that are being held down while + the lasso is being dragged, so if shift is pressed, then any lassoed items will + be added to the original selection; if ctrl or command is pressed, they will be + xor'ed with any previously selected items. + + @see LassoSource, SelectedItemSet +*/ +template +class LassoComponent : public Component +{ +public: + + /** Creates a Lasso component. + + The fill colour is used to fill the lasso'ed rectangle, and the outline + colour is used to draw a line around its edge. + */ + LassoComponent (const int outlineThickness_ = 1) + : source (0), + outlineThickness (outlineThickness_) + { + } + + /** Destructor. */ + ~LassoComponent() + { + } + + /** Call this in your mouseDown event, to initialise a drag. + + Pass in a suitable LassoSource object which the lasso will use to find + the items and change the selection. + + After using this method to initialise the lasso, repeatedly call dragLasso() + in your component's mouseDrag callback. + + @see dragLasso, endLasso, LassoSource + */ + void beginLasso (const MouseEvent& e, + LassoSource * const lassoSource) + { + jassert (source == 0); // this suggests that you didn't call endLasso() after the last drag... + jassert (lassoSource != 0); // the source can't be null! + jassert (getParentComponent() != 0); // you need to add this to a parent component for it to work! + + source = lassoSource; + + if (lassoSource != 0) + originalSelection = lassoSource->getLassoSelection().getItemArray(); + + setSize (0, 0); + } + + /** Call this in your mouseDrag event, to update the lasso's position. + + This must be repeatedly calling when the mouse is dragged, after you've + first initialised the lasso with beginLasso(). + + This method takes into account the modifier keys that are being held down, so + if shift is pressed, then the lassoed items will be added to any that were + previously selected; if ctrl or command is pressed, then they will be xor'ed + with previously selected items. + + @see beginLasso, endLasso + */ + void dragLasso (const MouseEvent& e) + { + if (source != 0) + { + const int x1 = e.getMouseDownX(); + const int y1 = e.getMouseDownY(); + + setBounds (jmin (x1, e.x), jmin (y1, e.y), abs (e.x - x1), abs (e.y - y1)); + setVisible (true); + + Array itemsInLasso; + source->findLassoItemsInArea (itemsInLasso, getX(), getY(), getWidth(), getHeight()); + + if (e.mods.isShiftDown()) + { + itemsInLasso.removeValuesIn (originalSelection); // to avoid duplicates + itemsInLasso.addArray (originalSelection); + } + else if (e.mods.isCommandDown() || e.mods.isAltDown()) + { + Array originalMinusNew (originalSelection); + originalMinusNew.removeValuesIn (itemsInLasso); + + itemsInLasso.removeValuesIn (originalSelection); + itemsInLasso.addArray (originalMinusNew); + } + + source->getLassoSelection() = SelectedItemSet (itemsInLasso); + } + } + + /** Call this in your mouseUp event, after the lasso has been dragged. + + @see beginLasso, dragLasso + */ + void endLasso() + { + source = 0; + originalSelection.clear(); + setVisible (false); + } + + /** A set of colour IDs to use to change the colour of various aspects of the label. + + These constants can be used either via the Component::setColour(), or LookAndFeel::setColour() + methods. + + Note that you can also use the constants from TextEditor::ColourIds to change the + colour of the text editor that is opened when a label is editable. + + @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour + */ + enum ColourIds + { + lassoFillColourId = 0x1000440, /**< The colour to fill the lasso rectangle with. */ + lassoOutlineColourId = 0x1000441, /**< The colour to draw the outline with. */ + }; + + /** @internal */ + void paint (Graphics& g) + { + g.fillAll (findColour (lassoFillColourId)); + + g.setColour (findColour (lassoOutlineColourId)); + g.drawRect (0, 0, getWidth(), getHeight(), outlineThickness); + + // this suggests that you've left a lasso comp lying around after the + // mouse drag has finished.. Be careful to call endLasso() when you get a + // mouse-up event. + jassert (isMouseButtonDownAnywhere()); + } + + /** @internal */ + bool hitTest (int x, int y) { return false; } + + juce_UseDebuggingNewOperator + +private: + Array originalSelection; + LassoSource * source; + int outlineThickness; +}; + +#endif // __JUCE_LASSOCOMPONENT_JUCEHEADER__ +/********* End of inlined file: juce_LassoComponent.h *********/ + +#endif +#ifndef __JUCE_MOUSECURSOR_JUCEHEADER__ + +#endif +#ifndef __JUCE_MOUSEEVENT_JUCEHEADER__ + +#endif +#ifndef __JUCE_MOUSEHOVERDETECTOR_JUCEHEADER__ + +/********* Start of inlined file: juce_MouseHoverDetector.h *********/ +#ifndef __JUCE_MOUSEHOVERDETECTOR_JUCEHEADER__ +#define __JUCE_MOUSEHOVERDETECTOR_JUCEHEADER__ + +/** + Monitors a component for mouse activity, and triggers a callback + when the mouse hovers in one place for a specified length of time. + + To use a hover-detector, just create one and call its setHoverComponent() + method to start it watching a component. You can call setHoverComponent (0) + to make it inactive. + + (Be careful not to delete a component that's being monitored without first + stopping or deleting the hover detector). +*/ +class JUCE_API MouseHoverDetector +{ +public: + + /** Creates a hover detector. + + Initially the object is inactive, and you need to tell it which component + to monitor, using the setHoverComponent() method. + + @param hoverTimeMillisecs the number of milliseconds for which the mouse + needs to stay still before the mouseHovered() method + is invoked. You can change this setting later with + the setHoverTimeMillisecs() method + */ + MouseHoverDetector (const int hoverTimeMillisecs = 400); + + /** Destructor. */ + virtual ~MouseHoverDetector(); + + /** Changes the time for which the mouse has to stay still before it's considered + to be hovering. + */ + void setHoverTimeMillisecs (const int newTimeInMillisecs); + + /** Changes the component that's being monitored for hovering. + + Be careful not to delete a component that's being monitored without first + stopping or deleting the hover detector. + */ + void setHoverComponent (Component* const newSourceComponent); + +protected: + + /** Called back when the mouse hovers. + + After the mouse has stayed still over the component for the length of time + specified by setHoverTimeMillisecs(), this method will be invoked. + + When the mouse is first moved after this callback has occurred, the + mouseMovedAfterHover() method will be called. + + @param mouseX the mouse's X position relative to the component being monitored + @param mouseY the mouse's Y position relative to the component being monitored + */ + virtual void mouseHovered (int mouseX, + int mouseY) = 0; + + /** Called when the mouse is moved away after just having hovered. */ + virtual void mouseMovedAfterHover() = 0; + +private: + + class JUCE_API HoverDetectorInternal : public MouseListener, + public Timer + { + public: + MouseHoverDetector* owner; + int lastX, lastY; + + void timerCallback(); + void mouseEnter (const MouseEvent&); + void mouseExit (const MouseEvent&); + void mouseDown (const MouseEvent&); + void mouseUp (const MouseEvent&); + void mouseMove (const MouseEvent&); + void mouseWheelMove (const MouseEvent&, float, float); + + } internalTimer; + + friend class HoverDetectorInternal; + + Component* source; + int hoverTimeMillisecs; + bool hasJustHovered; + + void hoverTimerCallback(); + void checkJustHoveredCallback(); +}; + +#endif // __JUCE_MOUSEHOVERDETECTOR_JUCEHEADER__ +/********* End of inlined file: juce_MouseHoverDetector.h *********/ + +#endif +#ifndef __JUCE_MOUSELISTENER_JUCEHEADER__ + +#endif +#ifndef __JUCE_TOOLTIPCLIENT_JUCEHEADER__ + +#endif +#ifndef __JUCE_COMBOBOX_JUCEHEADER__ + +#endif +#ifndef __JUCE_LABEL_JUCEHEADER__ + +#endif +#ifndef __JUCE_LISTBOX_JUCEHEADER__ + +#endif +#ifndef __JUCE_PROGRESSBAR_JUCEHEADER__ + +/********* Start of inlined file: juce_ProgressBar.h *********/ +#ifndef __JUCE_PROGRESSBAR_JUCEHEADER__ +#define __JUCE_PROGRESSBAR_JUCEHEADER__ + +/** + A progress bar component. + + To use this, just create one and make it visible. It'll run its own timer + to keep an eye on a variable that you give it, and will automatically + redraw itself when the variable changes. + + For an easy way of running a background task with a dialog box showing its + progress, see the ThreadWithProgressWindow class. + + @see ThreadWithProgressWindow +*/ +class JUCE_API ProgressBar : public Component, + public SettableTooltipClient, + private Timer +{ +public: + + /** Creates a ProgressBar. + + @param progress pass in a reference to a double that you're going to + update with your task's progress. The ProgressBar will + monitor the value of this variable and will redraw itself + when the value changes. The range is from 0 to 1.0. Obviously + you'd better be careful not to delete this variable while the + ProgressBar still exists! + */ + ProgressBar (double& progress); + + /** Destructor. */ + ~ProgressBar(); + + /** Turns the percentage display on or off. + + By default this is on, and the progress bar will display a text string showing + its current percentage. + */ + void setPercentageDisplay (const bool shouldDisplayPercentage); + + /** Gives the progress bar a string to display inside it. + + If you call this, it will turn off the percentage display. + @see setPercentageDisplay + */ + void setTextToDisplay (const String& text); + + /** A set of colour IDs to use to change the colour of various aspects of the bar. + + These constants can be used either via the Component::setColour(), or LookAndFeel::setColour() + methods. + + @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour + */ + enum ColourIds + { + backgroundColourId = 0x1001900, /**< The background colour, behind the bar. */ + foregroundColourId = 0x1001a00, /**< The colour to use to draw the bar itself. LookAndFeel + classes will probably use variations on this colour. */ + }; + + juce_UseDebuggingNewOperator + +protected: + /** @internal */ + void paint (Graphics& g); + /** @internal */ + void lookAndFeelChanged(); + /** @internal */ + void visibilityChanged(); + /** @internal */ + void colourChanged(); + +private: + double& progress; + double currentValue; + bool displayPercentage; + String displayedMessage, currentMessage; + + void timerCallback(); + + ProgressBar (const ProgressBar&); + const ProgressBar& operator= (const ProgressBar&); +}; + +#endif // __JUCE_PROGRESSBAR_JUCEHEADER__ +/********* End of inlined file: juce_ProgressBar.h *********/ + +#endif +#ifndef __JUCE_SLIDER_JUCEHEADER__ + +/********* Start of inlined file: juce_Slider.h *********/ +#ifndef __JUCE_SLIDER_JUCEHEADER__ +#define __JUCE_SLIDER_JUCEHEADER__ + +/********* Start of inlined file: juce_SliderListener.h *********/ +#ifndef __JUCE_SLIDERLISTENER_JUCEHEADER__ +#define __JUCE_SLIDERLISTENER_JUCEHEADER__ + +class Slider; + +/** + A class for receiving callbacks from a Slider. + + To be told when a slider's value changes, you can register a SliderListener + object using Slider::addListener(). + + @see Slider::addListener, Slider::removeListener +*/ +class JUCE_API SliderListener +{ +public: + + /** Destructor. */ + virtual ~SliderListener() {} + + /** Called when the slider's value is changed. + + This may be caused by dragging it, or by typing in its text entry box, + or by a call to Slider::setValue(). + + You can find out the new value using Slider::getValue(). + + @see Slider::valueChanged + */ + virtual void sliderValueChanged (Slider* slider) = 0; + + /** Called when the slider is about to be dragged. + + This is called when a drag begins, then it's followed by multiple calls + to sliderValueChanged(), and then sliderDragEnded() is called after the + user lets go. + + @see sliderDragEnded, Slider::startedDragging + */ + virtual void sliderDragStarted (Slider* slider); + + /** Called after a drag operation has finished. + + @see sliderDragStarted, Slider::stoppedDragging + */ + virtual void sliderDragEnded (Slider* slider); +}; + +#endif // __JUCE_SLIDERLISTENER_JUCEHEADER__ +/********* End of inlined file: juce_SliderListener.h *********/ + +/** + A slider control for changing a value. + + The slider can be horizontal, vertical, or rotary, and can optionally have + a text-box inside it to show an editable display of the current value. + + To use it, create a Slider object and use the setSliderStyle() method + to set up the type you want. To set up the text-entry box, use setTextBoxStyle(). + + To define the values that it can be set to, see the setRange() and setValue() methods. + + There are also lots of custom tweaks you can do by subclassing and overriding + some of the virtual methods, such as changing the scaling, changing the format of + the text display, custom ways of limiting the values, etc. + + You can register SliderListeners with a slider, which will be informed when the value + changes, or a subclass can override valueChanged() to be informed synchronously. + + @see SliderListener +*/ +class JUCE_API Slider : public Component, + public SettableTooltipClient, + private AsyncUpdater, + private ButtonListener, + private LabelListener +{ +public: + + /** Creates a slider. + + When created, you'll need to set up the slider's style and range with setSliderStyle(), + setRange(), etc. + */ + Slider (const String& componentName); + + /** Destructor. */ + ~Slider(); + + /** The types of slider available. + + @see setSliderStyle, setRotaryParameters + */ + enum SliderStyle + { + LinearHorizontal, /**< A traditional horizontal slider. */ + LinearVertical, /**< A traditional vertical slider. */ + LinearBar, /**< A horizontal bar slider with the text label drawn on top of it. */ + Rotary, /**< A rotary control that you move by dragging the mouse in a circular motion, like a knob. + @see setRotaryParameters */ + RotaryHorizontalDrag, /**< A rotary control that you move by dragging the mouse left-to-right. + @see setRotaryParameters */ + RotaryVerticalDrag, /**< A rotary control that you move by dragging the mouse up-and-down. + @see setRotaryParameters */ + IncDecButtons, /**< A pair of buttons that increment or decrement the slider's value by the increment set in setRange(). */ + + TwoValueHorizontal, /**< A horizontal slider that has two thumbs instead of one, so it can show a minimum and maximum value. + @see setMinValue, setMaxValue */ + TwoValueVertical, /**< A vertical slider that has two thumbs instead of one, so it can show a minimum and maximum value. + @see setMinValue, setMaxValue */ + + ThreeValueHorizontal, /**< A horizontal slider that has three thumbs instead of one, so it can show a minimum and maximum + value, with the current value being somewhere between them. + @see setMinValue, setMaxValue */ + ThreeValueVertical, /**< A vertical slider that has three thumbs instead of one, so it can show a minimum and maximum + value, with the current value being somewhere between them. + @see setMinValue, setMaxValue */ + }; + + /** Changes the type of slider interface being used. + + @param newStyle the type of interface + @see setRotaryParameters, setVelocityBasedMode, + */ + void setSliderStyle (const SliderStyle newStyle); + + /** Returns the slider's current style. + + @see setSliderStyle + */ + SliderStyle getSliderStyle() const throw() { return style; } + + /** Changes the properties of a rotary slider. + + @param startAngleRadians the angle (in radians, clockwise from the top) at which + the slider's minimum value is represented + @param endAngleRadians the angle (in radians, clockwise from the top) at which + the slider's maximum value is represented. This must be + greater than startAngleRadians + @param stopAtEnd if true, then when the slider is dragged around past the + minimum or maximum, it'll stop there; if false, it'll wrap + back to the opposite value + */ + void setRotaryParameters (const float startAngleRadians, + const float endAngleRadians, + const bool stopAtEnd); + + /** Sets the distance the mouse has to move to drag the slider across + the full extent of its range. + + This only applies when in modes like RotaryHorizontalDrag, where it's using + relative mouse movements to adjust the slider. + */ + void setMouseDragSensitivity (const int distanceForFullScaleDrag); + + /** Changes the way the the mouse is used when dragging the slider. + + If true, this will turn on velocity-sensitive dragging, so that + the faster the mouse moves, the bigger the movement to the slider. This + helps when making accurate adjustments if the slider's range is quite large. + + If false, the slider will just try to snap to wherever the mouse is. + */ + void setVelocityBasedMode (const bool isVelocityBased) throw(); + + /** Changes aspects of the scaling used when in velocity-sensitive mode. + + These apply when you've used setVelocityBasedMode() to turn on velocity mode, + or if you're holding down ctrl. + + @param sensitivity higher values than 1.0 increase the range of acceleration used + @param threshold the minimum number of pixels that the mouse needs to move for it + to be treated as a movement + @param offset values greater than 0.0 increase the minimum speed that will be used when + the threshold is reached + */ + void setVelocityModeParameters (const double sensitivity = 1.0, + const int threshold = 1.0, + const double offset = 0.0, + const bool userCanPressKeyToSwapMode = true) throw(); + + /** Sets up a skew factor to alter the way values are distributed. + + You may want to use a range of values on the slider where more accuracy + is required towards one end of the range, so this will logarithmically + spread the values across the length of the slider. + + If the factor is < 1.0, the lower end of the range will fill more of the + slider's length; if the factor is > 1.0, the upper end of the range + will be expanded instead. A factor of 1.0 doesn't skew it at all. + + To set the skew position by using a mid-point, use the setSkewFactorFromMidPoint() + method instead. + + @see getSkewFactor, setSkewFactorFromMidPoint + */ + void setSkewFactor (const double factor) throw(); + + /** Sets up a skew factor to alter the way values are distributed. + + This allows you to specify the slider value that should appear in the + centre of the slider's visible range. + + @see setSkewFactor, getSkewFactor + */ + void setSkewFactorFromMidPoint (const double sliderValueToShowAtMidPoint) throw(); + + /** Returns the current skew factor. + + See setSkewFactor for more info. + + @see setSkewFactor, setSkewFactorFromMidPoint + */ + double getSkewFactor() const throw() { return skewFactor; } + + /** Used by setIncDecButtonsMode(). + */ + enum IncDecButtonMode + { + incDecButtonsNotDraggable, + incDecButtonsDraggable_AutoDirection, + incDecButtonsDraggable_Horizontal, + incDecButtonsDraggable_Vertical + }; + + /** When the style is IncDecButtons, this lets you turn on a mode where the mouse + can be dragged on the buttons to drag the values. + + By default this is turned off. When enabled, clicking on the buttons still works + them as normal, but by holding down the mouse on a button and dragging it a little + distance, it flips into a mode where the value can be dragged. The drag direction can + either be set explicitly to be vertical or horizontal, or can be set to + incDecButtonsDraggable_AutoDirection so that it depends on whether the buttons + are side-by-side or above each other. + */ + void setIncDecButtonsMode (const IncDecButtonMode mode); + + /** The position of the slider's text-entry box. + + @see setTextBoxStyle + */ + enum TextEntryBoxPosition + { + NoTextBox, /**< Doesn't display a text box. */ + TextBoxLeft, /**< Puts the text box to the left of the slider, vertically centred. */ + TextBoxRight, /**< Puts the text box to the right of the slider, vertically centred. */ + TextBoxAbove, /**< Puts the text box above the slider, horizontally centred. */ + TextBoxBelow /**< Puts the text box below the slider, horizontally centred. */ + }; + + /** Changes the location and properties of the text-entry box. + + @param newPosition where it should go (or NoTextBox to not have one at all) + @param isReadOnly if true, it's a read-only display + @param textEntryBoxWidth the width of the text-box in pixels. Make sure this leaves enough + room for the slider as well! + @param textEntryBoxHeight the height of the text-box in pixels. Make sure this leaves enough + room for the slider as well! + + @see setTextBoxIsEditable, getValueFromText, getTextFromValue + */ + void setTextBoxStyle (const TextEntryBoxPosition newPosition, + const bool isReadOnly, + const int textEntryBoxWidth, + const int textEntryBoxHeight); + + /** Returns the status of the text-box. + @see setTextBoxStyle + */ + const TextEntryBoxPosition getTextBoxPosition() const throw() { return textBoxPos; } + + /** Returns the width used for the text-box. + @see setTextBoxStyle + */ + int getTextBoxWidth() const throw() { return textBoxWidth; } + + /** Returns the height used for the text-box. + @see setTextBoxStyle + */ + int getTextBoxHeight() const throw() { return textBoxHeight; } + + /** Makes the text-box editable. + + By default this is true, and the user can enter values into the textbox, + but it can be turned off if that's not suitable. + + @see setTextBoxStyle, getValueFromText, getTextFromValue + */ + void setTextBoxIsEditable (const bool shouldBeEditable) throw(); + + /** Returns true if the text-box is read-only. + @see setTextBoxStyle + */ + bool isTextBoxEditable() const throw() { return editableText; } + + /** If the text-box is editable, this will give it the focus so that the user can + type directly into it. + + This is basically the effect as the user clicking on it. + */ + void showTextBox(); + + /** If the text-box currently has focus and is being edited, this resets it and takes keyboard + focus away from it. + + @param discardCurrentEditorContents if true, the slider's value will be left + unchanged; if false, the current contents of the + text editor will be used to set the slider position + before it is hidden. + */ + void hideTextBox (const bool discardCurrentEditorContents); + + /** Changes the slider's current value. + + This will trigger a callback to SliderListener::sliderValueChanged() for any listeners + that are registered, and will synchronously call the valueChanged() method in case subclasses + want to handle it. + + @param newValue the new value to set - this will be restricted by the + minimum and maximum range, and will be snapped to the + nearest interval if one has been set + @param sendUpdateMessage if false, a change to the value will not trigger a call to + any SliderListeners or the valueChanged() method + @param sendMessageSynchronously if true, then a call to the SliderListeners will be made + synchronously; if false, it will be asynchronous + */ + void setValue (double newValue, + const bool sendUpdateMessage = true, + const bool sendMessageSynchronously = false); + + /** Returns the slider's current value. */ + double getValue() const throw(); + + /** Sets the limits that the slider's value can take. + + @param newMinimum the lowest value allowed + @param newMaximum the highest value allowed + @param newInterval the steps in which the value is allowed to increase - if this + is not zero, the value will always be (newMinimum + (newInterval * an integer)). + */ + void setRange (const double newMinimum, + const double newMaximum, + const double newInterval = 0); + + /** Returns the current maximum value. + @see setRange + */ + double getMaximum() const throw() { return maximum; } + + /** Returns the current minimum value. + @see setRange + */ + double getMinimum() const throw() { return minimum; } + + /** Returns the current step-size for values. + @see setRange + */ + double getInterval() const throw() { return interval; } + + /** For a slider with two or three thumbs, this returns the lower of its values. + + For a two-value slider, the values are controlled with getMinValue() and getMaxValue(). + A slider with three values also uses the normal getValue() and setValue() methods to + control the middle value. + + @see setMinValue, getMaxValue, TwoValueHorizontal, TwoValueVertical, ThreeValueHorizontal, ThreeValueVertical + */ + double getMinValue() const throw(); + + /** For a slider with two or three thumbs, this sets the lower of its values. + + This will trigger a callback to SliderListener::sliderValueChanged() for any listeners + that are registered, and will synchronously call the valueChanged() method in case subclasses + want to handle it. + + @param newValue the new value to set - this will be restricted by the + minimum and maximum range, and the max value (in a two-value + slider) or the mid value (in a three-value slider), and + will be snapped to the nearest interval if one has been set. + @param sendUpdateMessage if false, a change to the value will not trigger a call to + any SliderListeners or the valueChanged() method + @param sendMessageSynchronously if true, then a call to the SliderListeners will be made + synchronously; if false, it will be asynchronous + @see getMinValue, setMaxValue, setValue + */ + void setMinValue (double newValue, + const bool sendUpdateMessage = true, + const bool sendMessageSynchronously = false); + + /** For a slider with two or three thumbs, this returns the higher of its values. + + For a two-value slider, the values are controlled with getMinValue() and getMaxValue(). + A slider with three values also uses the normal getValue() and setValue() methods to + control the middle value. + + @see getMinValue, TwoValueHorizontal, TwoValueVertical, ThreeValueHorizontal, ThreeValueVertical + */ + double getMaxValue() const throw(); + + /** For a slider with two or three thumbs, this sets the lower of its values. + + This will trigger a callback to SliderListener::sliderValueChanged() for any listeners + that are registered, and will synchronously call the valueChanged() method in case subclasses + want to handle it. + + @param newValue the new value to set - this will be restricted by the + minimum and maximum range, and the max value (in a two-value + slider) or the mid value (in a three-value slider), and + will be snapped to the nearest interval if one has been set. + @param sendUpdateMessage if false, a change to the value will not trigger a call to + any SliderListeners or the valueChanged() method + @param sendMessageSynchronously if true, then a call to the SliderListeners will be made + synchronously; if false, it will be asynchronous + @see getMaxValue, setMinValue, setValue + */ + void setMaxValue (double newValue, + const bool sendUpdateMessage = true, + const bool sendMessageSynchronously = false); + + /** Adds a listener to be called when this slider's value changes. */ + void addListener (SliderListener* const listener) throw(); + + /** Removes a previously-registered listener. */ + void removeListener (SliderListener* const listener) throw(); + + /** This lets you choose whether double-clicking moves the slider to a given position. + + By default this is turned off, but it's handy if you want a double-click to act + as a quick way of resetting a slider. Just pass in the value you want it to + go to when double-clicked. + + @see getDoubleClickReturnValue + */ + void setDoubleClickReturnValue (const bool isDoubleClickEnabled, + const double valueToSetOnDoubleClick) throw(); + + /** Returns the values last set by setDoubleClickReturnValue() method. + + Sets isEnabled to true if double-click is enabled, and returns the value + that was set. + + @see setDoubleClickReturnValue + */ + double getDoubleClickReturnValue (bool& isEnabled) const throw(); + + /** Tells the slider whether to keep sending change messages while the user + is dragging the slider. + + If set to true, a change message will only be sent when the user has + dragged the slider and let go. If set to false (the default), then messages + will be continuously sent as they drag it while the mouse button is still + held down. + */ + void setChangeNotificationOnlyOnRelease (const bool onlyNotifyOnRelease) throw(); + + /** This lets you change whether the slider thumb jumps to the mouse position + when you click. + + By default, this is true. If it's false, then the slider moves with relative + motion when you drag it. + + This only applies to linear bars, and won't affect two- or three- value + sliders. + */ + void setSliderSnapsToMousePosition (const bool shouldSnapToMouse) throw(); + + /** If enabled, this gives the slider a pop-up bubble which appears while the + slider is being dragged. + + This can be handy if your slider doesn't have a text-box, so that users can + see the value just when they're changing it. + + If you pass a component as the parentComponentToUse parameter, the pop-up + bubble will be added as a child of that component when it's needed. If you + pass 0, the pop-up will be placed on the desktop instead (note that it's a + transparent window, so if you're using an OS that can't do transparent windows + you'll have to add it to a parent component instead). + */ + void setPopupDisplayEnabled (const bool isEnabled, + Component* const parentComponentToUse) throw(); + + /** If this is set to true, then right-clicking on the slider will pop-up + a menu to let the user change the way it works. + + By default this is turned off, but when turned on, the menu will include + things like velocity sensitivity, and for rotary sliders, whether they + use a linear or rotary mouse-drag to move them. + */ + void setPopupMenuEnabled (const bool menuEnabled) throw(); + + /** This can be used to stop the mouse scroll-wheel from moving the slider. + + By default it's enabled. + */ + void setScrollWheelEnabled (const bool enabled) throw(); + + /** Callback to indicate that the user is about to start dragging the slider. + + @see SliderListener::sliderDragStarted + */ + virtual void startedDragging(); + + /** Callback to indicate that the user has just stopped dragging the slider. + + @see SliderListener::sliderDragEnded + */ + virtual void stoppedDragging(); + + /** Callback to indicate that the user has just moved the slider. + + @see SliderListener::sliderValueChanged + */ + virtual void valueChanged(); + + /** Callback to indicate that the user has just moved the slider. + Note - the valueChanged() method has changed its format and now no longer has + any parameters. Update your code to use the new version. + This version has been left here with an int as its return value to cause + a syntax error if you've got existing code that uses the old version. + */ + virtual int valueChanged (double) { jassertfalse; return 0; } + + /** Subclasses can override this to convert a text string to a value. + + When the user enters something into the text-entry box, this method is + called to convert it to a value. + + The default routine just tries to convert it to a double. + + @see getTextFromValue + */ + virtual double getValueFromText (const String& text); + + /** Turns the slider's current value into a text string. + + Subclasses can override this to customise the formatting of the text-entry box. + + The default implementation just turns the value into a string, using + a number of decimal places based on the range interval. If a suffix string + has been set using setTextValueSuffix(), this will be appended to the text. + + @see getValueFromText + */ + virtual const String getTextFromValue (double value); + + /** Sets a suffix to append to the end of the numeric value when it's displayed as + a string. + + This is used by the default implementation of getTextFromValue(), and is just + appended to the numeric value. For more advanced formatting, you can override + getTextFromValue() and do something else. + */ + void setTextValueSuffix (const String& suffix); + + /** Allows a user-defined mapping of distance along the slider to its value. + + The default implementation for this performs the skewing operation that + can be set up in the setSkewFactor() method. Override it if you need + some kind of custom mapping instead, but make sure you also implement the + inverse function in valueToProportionOfLength(). + + @param proportion a value 0 to 1.0, indicating a distance along the slider + @returns the slider value that is represented by this position + @see valueToProportionOfLength + */ + virtual double proportionOfLengthToValue (double proportion); + + /** Allows a user-defined mapping of value to the position of the slider along its length. + + The default implementation for this performs the skewing operation that + can be set up in the setSkewFactor() method. Override it if you need + some kind of custom mapping instead, but make sure you also implement the + inverse function in proportionOfLengthToValue(). + + @param value a valid slider value, between the range of values specified in + setRange() + @returns a value 0 to 1.0 indicating the distance along the slider that + represents this value + @see proportionOfLengthToValue + */ + virtual double valueToProportionOfLength (double value); + + /** Returns the X or Y coordinate of a value along the slider's length. + + If the slider is horizontal, this will be the X coordinate of the given + value, relative to the left of the slider. If it's vertical, then this will + be the Y coordinate, relative to the top of the slider. + + If the slider is rotary, this will throw an assertion and return 0. If the + value is out-of-range, it will be constrained to the length of the slider. + */ + float getPositionOfValue (const double value); + + /** This can be overridden to allow the slider to snap to user-definable values. + + If overridden, it will be called when the user tries to move the slider to + a given position, and allows a subclass to sanity-check this value, possibly + returning a different value to use instead. + + @param attemptedValue the value the user is trying to enter + @param userIsDragging true if the user is dragging with the mouse; false if + they are entering the value using the text box + @returns the value to use instead + */ + virtual double snapValue (double attemptedValue, const bool userIsDragging); + + /** This can be called to force the text box to update its contents. + + (Not normally needed, as this is done automatically). + */ + void updateText(); + + /** True if the slider moves horizontally. */ + bool isHorizontal() const throw(); + /** True if the slider moves vertically. */ + bool isVertical() const throw(); + + /** A set of colour IDs to use to change the colour of various aspects of the slider. + + These constants can be used either via the Component::setColour(), or LookAndFeel::setColour() + methods. + + @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour + */ + enum ColourIds + { + backgroundColourId = 0x1001200, /**< A colour to use to fill the slider's background. */ + thumbColourId = 0x1001300, /**< The colour to draw the thumb with. It's up to the look + and feel class how this is used. */ + trackColourId = 0x1001310, /**< The colour to draw the groove that the thumb moves along. */ + rotarySliderFillColourId = 0x1001311, /**< For rotary sliders, this colour fills the outer curve. */ + rotarySliderOutlineColourId = 0x1001312, /**< For rotary sliders, this colour is used to draw the outer curve's outline. */ + + textBoxTextColourId = 0x1001400, /**< The colour for the text in the text-editor box used for editing the value. */ + textBoxBackgroundColourId = 0x1001500, /**< The background colour for the text-editor box. */ + textBoxHighlightColourId = 0x1001600, /**< The text highlight colour for the text-editor box. */ + textBoxOutlineColourId = 0x1001700 /**< The colour to use for a border around the text-editor box. */ + }; + + juce_UseDebuggingNewOperator + +protected: + /** @internal */ + void labelTextChanged (Label*); + /** @internal */ + void paint (Graphics& g); + /** @internal */ + void resized(); + /** @internal */ + void mouseDown (const MouseEvent& e); + /** @internal */ + void mouseUp (const MouseEvent& e); + /** @internal */ + void mouseDrag (const MouseEvent& e); + /** @internal */ + void mouseDoubleClick (const MouseEvent& e); + /** @internal */ + void mouseWheelMove (const MouseEvent& e, float wheelIncrementX, float wheelIncrementY); + /** @internal */ + void modifierKeysChanged (const ModifierKeys& modifiers); + /** @internal */ + void buttonClicked (Button* button); + /** @internal */ + void lookAndFeelChanged(); + /** @internal */ + void enablementChanged(); + /** @internal */ + void focusOfChildComponentChanged (FocusChangeType cause); + /** @internal */ + void handleAsyncUpdate(); + /** @internal */ + void colourChanged(); + +private: + SortedSet listeners; + double currentValue, valueMin, valueMax; + double minimum, maximum, interval, doubleClickReturnValue; + double valueWhenLastDragged, valueOnMouseDown, skewFactor, lastAngle; + double velocityModeSensitivity, velocityModeOffset, minMaxDiff; + int velocityModeThreshold; + float rotaryStart, rotaryEnd; + int numDecimalPlaces, mouseXWhenLastDragged, mouseYWhenLastDragged; + int sliderRegionStart, sliderRegionSize; + int sliderBeingDragged; + int pixelsForFullDragExtent; + Rectangle sliderRect; + String textSuffix; + + SliderStyle style; + TextEntryBoxPosition textBoxPos; + int textBoxWidth, textBoxHeight; + IncDecButtonMode incDecButtonMode; + + bool editableText : 1, doubleClickToValue : 1; + bool isVelocityBased : 1, userKeyOverridesVelocity : 1, rotaryStop : 1; + bool incDecButtonsSideBySide : 1, sendChangeOnlyOnRelease : 1, popupDisplayEnabled : 1; + bool menuEnabled : 1, menuShown : 1, mouseWasHidden : 1, incDecDragged : 1; + bool scrollWheelEnabled : 1, snapsToMousePos : 1; + Font font; + Label* valueBox; + Button* incButton; + Button* decButton; + Component* popupDisplay; + Component* parentForPopupDisplay; + + float getLinearSliderPos (const double value); + void restoreMouseIfHidden(); + void sendDragStart(); + void sendDragEnd(); + double constrainedValue (double value) const throw(); + void triggerChangeMessage (const bool synchronous); + bool incDecDragDirectionIsHorizontal() const throw(); + + Slider (const Slider&); + const Slider& operator= (const Slider&); +}; + +#endif // __JUCE_SLIDER_JUCEHEADER__ +/********* End of inlined file: juce_Slider.h *********/ + +#endif +#ifndef __JUCE_SLIDERLISTENER_JUCEHEADER__ + +#endif +#ifndef __JUCE_TABLEHEADERCOMPONENT_JUCEHEADER__ + +/********* Start of inlined file: juce_TableHeaderComponent.h *********/ +#ifndef __JUCE_TABLEHEADERCOMPONENT_JUCEHEADER__ +#define __JUCE_TABLEHEADERCOMPONENT_JUCEHEADER__ + +class TableHeaderComponent; + +/** + Receives events from a TableHeaderComponent when columns are resized, moved, etc. + + You can register one of these objects for table events using TableHeaderComponent::addListener() + and TableHeaderComponent::removeListener(). + + @see TableHeaderComponent +*/ +class JUCE_API TableHeaderListener +{ +public: + + TableHeaderListener() {} + + /** Destructor. */ + virtual ~TableHeaderListener() {} + + /** This is called when some of the table's columns are added, removed, hidden, + or rearranged. + */ + virtual void tableColumnsChanged (TableHeaderComponent* tableHeader) = 0; + + /** This is called when one or more of the table's columns are resized. + */ + virtual void tableColumnsResized (TableHeaderComponent* tableHeader) = 0; + + /** This is called when the column by which the table should be sorted is changed. + */ + virtual void tableSortOrderChanged (TableHeaderComponent* tableHeader) = 0; + + /** This is called when the user begins or ends dragging one of the columns around. + + When the user starts dragging a column, this is called with the ID of that + column. When they finish dragging, it is called again with 0 as the ID. + */ + virtual void tableColumnDraggingChanged (TableHeaderComponent* tableHeader, + int columnIdNowBeingDragged); +}; + +/** + A component that displays a strip of column headings for a table, and allows these + to be resized, dragged around, etc. + + This is just the component that goes at the top of a table. You can use it + directly for custom components, or to create a simple table, use the + TableListBox class. + + To use one of these, create it and use addColumn() to add all the columns that you need. + Each column must be given a unique ID number that's used to refer to it. + + @see TableListBox, TableHeaderListener +*/ +class JUCE_API TableHeaderComponent : public Component, + private AsyncUpdater +{ +public: + + /** Creates an empty table header. + */ + TableHeaderComponent(); + + /** Destructor. */ + ~TableHeaderComponent(); + + /** A combination of these flags are passed into the addColumn() method to specify + the properties of a column. + */ + enum ColumnPropertyFlags + { + visible = 1, /**< If this is set, the column will be shown; if not, it will be hidden until the user enables it with the pop-up menu. */ + resizable = 2, /**< If this is set, the column can be resized by dragging it. */ + draggable = 4, /**< If this is set, the column can be dragged around to change its order in the table. */ + appearsOnColumnMenu = 8, /**< If this is set, the column will be shown on the pop-up menu allowing it to be hidden/shown. */ + sortable = 16, /**< If this is set, then clicking on the column header will set it to be the sort column, and clicking again will reverse the order. */ + sortedForwards = 32, /**< If this is set, the column is currently the one by which the table is sorted (forwards). */ + sortedBackwards = 64, /**< If this is set, the column is currently the one by which the table is sorted (backwards). */ + + /** This set of default flags is used as the default parameter value in addColumn(). */ + defaultFlags = (visible | resizable | draggable | appearsOnColumnMenu | sortable), + + /** A quick way of combining flags for a column that's not resizable. */ + notResizable = (visible | draggable | appearsOnColumnMenu | sortable), + + /** A quick way of combining flags for a column that's not resizable or sortable. */ + notResizableOrSortable = (visible | draggable | appearsOnColumnMenu), + + /** A quick way of combining flags for a column that's not sortable. */ + notSortable = (visible | resizable | draggable | appearsOnColumnMenu) + }; + + /** Adds a column to the table. + + This will add a column, and asynchronously call the tableColumnsChanged() method of any + registered listeners. + + @param columnName the name of the new column. It's ok to have two or more columns with the same name + @param columnId an ID for this column. The ID can be any number apart from 0, but every column must have + a unique ID. This is used to identify the column later on, after the user may have + changed the order that they appear in + @param width the initial width of the column, in pixels + @param maximumWidth a maximum width that the column can take when the user is resizing it. This only applies + if the 'resizable' flag is specified for this column + @param minimumWidth a minimum width that the column can take when the user is resizing it. This only applies + if the 'resizable' flag is specified for this column + @param propertyFlags a combination of some of the values from the ColumnPropertyFlags enum, to define the + properties of this column + @param insertIndex the index at which the column should be added. A value of 0 puts it at the start (left-hand side) + and -1 puts it at the end (right-hand size) of the table. Note that the index the index within + all columns, not just the index amongst those that are currently visible + */ + void addColumn (const String& columnName, + const int columnId, + const int width, + const int minimumWidth = 30, + const int maximumWidth = -1, + const int propertyFlags = defaultFlags, + const int insertIndex = -1); + + /** Removes a column with the given ID. + + If there is such a column, this will asynchronously call the tableColumnsChanged() method of any + registered listeners. + */ + void removeColumn (const int columnIdToRemove); + + /** Deletes all columns from the table. + + If there are any columns to remove, this will asynchronously call the tableColumnsChanged() method of any + registered listeners. + */ + void removeAllColumns(); + + /** Returns the number of columns in the table. + + If onlyCountVisibleColumns is true, this will return the number of visible columns; otherwise it'll + return the total number of columns, including hidden ones. + + @see isColumnVisible + */ + int getNumColumns (const bool onlyCountVisibleColumns) const throw(); + + /** Returns the name for a column. + @see setColumnName + */ + const String getColumnName (const int columnId) const throw(); + + /** Changes the name of a column. */ + void setColumnName (const int columnId, const String& newName); + + /** Moves a column to a different index in the table. + + @param columnId the column to move + @param newVisibleIndex the target index for it, from 0 to the number of columns currently visible. + */ + void moveColumn (const int columnId, int newVisibleIndex); + + /** Changes the width of a column. + + This will cause an asynchronous callback to the tableColumnsResized() method of any registered listeners. + */ + void setColumnWidth (const int columnId, const int newWidth); + + /** Shows or hides a column. + + This can cause an asynchronous callback to the tableColumnsChanged() method of any registered listeners. + @see isColumnVisible + */ + void setColumnVisible (const int columnId, const bool shouldBeVisible); + + /** Returns true if this column is currently visible. + @see setColumnVisible + */ + bool isColumnVisible (const int columnId) const; + + /** Changes the column which is the sort column. + + This can cause an asynchronous callback to the tableSortOrderChanged() method of any registered listeners. + + If this method doesn't actually change the column ID, then no re-sort will take place (you can + call reSortTable() to force a re-sort to happen if you've modified the table's contents). + + @see getSortColumnId, isSortedForwards, reSortTable + */ + void setSortColumnId (const int columnId, const bool sortForwards); + + /** Returns the column ID by which the table is currently sorted, or 0 if it is unsorted. + + @see setSortColumnId, isSortedForwards + */ + int getSortColumnId() const throw(); + + /** Returns true if the table is currently sorted forwards, or false if it's backwards. + @see setSortColumnId + */ + bool isSortedForwards() const throw(); + + /** Triggers a re-sort of the table according to the current sort-column. + + If you modifiy the table's contents, you can call this to signal that the table needs + to be re-sorted. + + (This doesn't do any sorting synchronously - it just asynchronously sends a call to the + tableSortOrderChanged() method of any listeners). + */ + void reSortTable(); + + /** Returns the total width of all the visible columns in the table. + */ + int getTotalWidth() const throw(); + + /** Returns the index of a given column. + + If there's no such column ID, this will return -1. + + If onlyCountVisibleColumns is true, this will return the index amoungst the visible columns; + otherwise it'll return the index amongst all the columns, including any hidden ones. + */ + int getIndexOfColumnId (const int columnId, const bool onlyCountVisibleColumns) const throw(); + + /** Returns the ID of the column at a given index. + + If onlyCountVisibleColumns is true, this will count the index amoungst the visible columns; + otherwise it'll count it amongst all the columns, including any hidden ones. + + If the index is out-of-range, it'll return 0. + */ + int getColumnIdOfIndex (int index, const bool onlyCountVisibleColumns) const throw(); + + /** Returns the rectangle containing of one of the columns. + + The index is an index from 0 to the number of columns that are currently visible (hidden + ones are not counted). It returns a rectangle showing the position of the column relative + to this component's top-left. If the index is out-of-range, an empty rectangle is retrurned. + */ + const Rectangle getColumnPosition (const int index) const throw(); + + /** Finds the column ID at a given x-position in the component. + + If there is a column at this point this returns its ID, or if not, it will return 0. + */ + int getColumnIdAtX (const int xToFind) const throw(); + + /** If set to true, this indicates that the columns should be expanded or shrunk to fill the + entire width of the component. + + By default this is disabled. Turning it on also means that when resizing a column, those + on the right will be squashed to fit. + */ + void setStretchToFitActive (const bool shouldStretchToFit); + + /** Returns true if stretch-to-fit has been enabled. + @see setStretchToFitActive + */ + bool isStretchToFitActive() const throw(); + + /** If stretch-to-fit is enabled, this will resize all the columns to make them fit into the + specified width, keeping their relative proportions the same. + + If the minimum widths of the columns are too wide to fit into this space, it may + actually end up wider. + */ + void resizeAllColumnsToFit (int targetTotalWidth); + + /** Enables or disables the pop-up menu. + + The default menu allows the user to show or hide columns. You can add custom + items to this menu by overloading the addMenuItems() and reactToMenuItem() methods. + + By default the menu is enabled. + + @see isPopupMenuActive, addMenuItems, reactToMenuItem + */ + void setPopupMenuActive (const bool hasMenu); + + /** Returns true if the pop-up menu is enabled. + @see setPopupMenuActive + */ + bool isPopupMenuActive() const throw(); + + /** Returns a string that encapsulates the table's current layout. + + This can be restored later using restoreFromString(). It saves the order of + the columns, the currently-sorted column, and the widths. + + @see restoreFromString + */ + const String toString() const; + + /** Restores the state of the table, based on a string previously created with + toString(). + + @see toString + */ + void restoreFromString (const String& storedVersion); + + /** Adds a listener to be informed about things that happen to the header. */ + void addListener (TableHeaderListener* const newListener) throw(); + + /** Removes a previously-registered listener. */ + void removeListener (TableHeaderListener* const listenerToRemove) throw(); + + /** This can be overridden to handle a mouse-click on one of the column headers. + + The default implementation will use this click to call getSortColumnId() and + change the sort order. + */ + virtual void columnClicked (int columnId, const ModifierKeys& mods); + + /** This can be overridden to add custom items to the pop-up menu. + + If you override this, you should call the superclass's method to add its + column show/hide items, if you want them on the menu as well. + + Then to handle the result, override reactToMenuItem(). + + @see reactToMenuItem + */ + virtual void addMenuItems (PopupMenu& menu, const int columnIdClicked); + + /** Override this to handle any custom items that you have added to the + pop-up menu with an addMenuItems() override. + + If the menuReturnId isn't one of your own custom menu items, you'll need to + call TableHeaderComponent::reactToMenuItem() to allow the base class to + handle the items that it had added. + + @see addMenuItems + */ + virtual void reactToMenuItem (const int menuReturnId, const int columnIdClicked); + + /** @internal */ + void paint (Graphics& g); + /** @internal */ + void resized(); + /** @internal */ + void mouseMove (const MouseEvent&); + /** @internal */ + void mouseEnter (const MouseEvent&); + /** @internal */ + void mouseExit (const MouseEvent&); + /** @internal */ + void mouseDown (const MouseEvent&); + /** @internal */ + void mouseDrag (const MouseEvent&); + /** @internal */ + void mouseUp (const MouseEvent&); + /** @internal */ + const MouseCursor getMouseCursor(); + + juce_UseDebuggingNewOperator + +private: + struct ColumnInfo + { + String name; + int id, propertyFlags, width, minimumWidth, maximumWidth; + double lastDeliberateWidth; + + bool isVisible() const throw(); + }; + + OwnedArray columns; + Array listeners; + Component* dragOverlayComp; + + bool columnsChanged, columnsResized, sortChanged, menuActive, stretchToFit; + int columnIdBeingResized, columnIdBeingDragged, initialColumnWidth; + int columnIdUnderMouse, draggingColumnOffset, draggingColumnOriginalIndex, lastDeliberateWidth; + + ColumnInfo* getInfoForId (const int columnId) const throw(); + int visibleIndexToTotalIndex (const int visibleIndex) const throw(); + void sendColumnsChanged(); + void handleAsyncUpdate(); + void beginDrag (const MouseEvent&); + void endDrag (const int finalIndex); + int getResizeDraggerAt (const int mouseX) const throw(); + void updateColumnUnderMouse (int x, int y); + void showColumnChooserMenu (const int); + void resizeColumnsToFit (int firstColumnIndex, int targetTotalWidth); + + TableHeaderComponent (const TableHeaderComponent&); + const TableHeaderComponent operator= (const TableHeaderComponent&); +}; + +#endif // __JUCE_TABLEHEADERCOMPONENT_JUCEHEADER__ +/********* End of inlined file: juce_TableHeaderComponent.h *********/ + +#endif +#ifndef __JUCE_TABLELISTBOX_JUCEHEADER__ + +/********* Start of inlined file: juce_TableListBox.h *********/ +#ifndef __JUCE_TABLELISTBOX_JUCEHEADER__ +#define __JUCE_TABLELISTBOX_JUCEHEADER__ + +/** + One of these is used by a TableListBox as the data model for the table's contents. + + The virtual methods that you override in this class take care of drawing the + table cells, and reacting to events. + + @see TableListBox +*/ +class JUCE_API TableListBoxModel +{ +public: + + TableListBoxModel() {} + + /** Destructor. */ + virtual ~TableListBoxModel() {} + + /** This must return the number of rows currently in the table. + + If the number of rows changes, you must call TableListBox::updateContent() to + cause it to refresh the list. + */ + virtual int getNumRows() = 0; + + /** This must draw the background behind one of the rows in the table. + + The graphics context has its origin at the row's top-left, and your method + should fill the area specified by the width and height parameters. + */ + virtual void paintRowBackground (Graphics& g, + int rowNumber, + int width, int height, + bool rowIsSelected) = 0; + + /** This must draw one of the cells. + + The graphics context's origin will already be set to the top-left of the cell, + whose size is specified by (width, height). + */ + virtual void paintCell (Graphics& g, + int rowNumber, + int columnId, + int width, int height, + bool rowIsSelected) = 0; + + /** This is used to create or update a custom component to go in a cell. + + Any cell may contain a custom component, or can just be drawn with the paintCell() method + and handle mouse clicks with cellClicked(). + + This method will be called whenever a custom component might need to be updated - e.g. + when the table is changed, or TableListBox::updateContent() is called. + + If you don't need a custom component for the specified cell, then return 0. + + If you do want a custom component, and the existingComponentToUpdate is null, then + this method must create a new component suitable for the cell, and return it. + + If the existingComponentToUpdate is non-null, it will be a pointer to a component previously created + by this method. In this case, the method must either update it to make sure it's correctly representing + the given cell (which may be different from the one that the component was created for), or it can + delete this component and return a new one. + */ + virtual Component* refreshComponentForCell (int rowNumber, int columnId, bool isRowSelected, + Component* existingComponentToUpdate); + + /** This callback is made when the user clicks on one of the cells in the table. + + The mouse event's coordinates will be relative to the entire table row. + @see cellDoubleClicked, backgroundClicked + */ + virtual void cellClicked (int rowNumber, int columnId, const MouseEvent& e); + + /** This callback is made when the user clicks on one of the cells in the table. + + The mouse event's coordinates will be relative to the entire table row. + @see cellClicked, backgroundClicked + */ + virtual void cellDoubleClicked (int rowNumber, int columnId, const MouseEvent& e); + + /** This can be overridden to react to the user double-clicking on a part of the list where + there are no rows. + + @see cellClicked + */ + virtual void backgroundClicked(); + + /** This callback is made when the table's sort order is changed. + + This could be because the user has clicked a column header, or because the + TableHeaderComponent::setSortColumnId() method was called. + + If you implement this, your method should re-sort the table using the given + column as the key. + */ + virtual void sortOrderChanged (int newSortColumnId, const bool isForwards); + + /** Returns the best width for one of the columns. + + If you implement this method, you should measure the width of all the items + in this column, and return the best size. + + Returning 0 means that the column shouldn't be changed. + + This is used by TableListBox::autoSizeColumn() and TableListBox::autoSizeAllColumns(). + */ + virtual int getColumnAutoSizeWidth (int columnId); + + /** Override this to be informed when rows are selected or deselected. + + @see ListBox::selectedRowsChanged() + */ + virtual void selectedRowsChanged (int lastRowSelected); + + /** Override this to be informed when the delete key is pressed. + + @see ListBox::deleteKeyPressed() + */ + virtual void deleteKeyPressed (int lastRowSelected); + + /** Override this to be informed when the return key is pressed. + + @see ListBox::returnKeyPressed() + */ + virtual void returnKeyPressed (int lastRowSelected); + + /** Override this to be informed when the list is scrolled. + + This might be caused by the user moving the scrollbar, or by programmatic changes + to the list position. + */ + virtual void listWasScrolled(); + + /** To allow rows from your table to be dragged-and-dropped, implement this method. + + If this returns a non-empty name then when the user drags a row, the table will try to + find a DragAndDropContainer in its parent hierarchy, and will use it to trigger a + drag-and-drop operation, using this string as the source description, and the listbox + itself as the source component. + + @see DragAndDropContainer::startDragging + */ + virtual const String getDragSourceDescription (const SparseSet& currentlySelectedRows); +}; + +/** + A table of cells, using a TableHeaderComponent as its header. + + This component makes it easy to create a table by providing a TableListBoxModel as + the data source. + + @see TableListBoxModel, TableHeaderComponent +*/ +class JUCE_API TableListBox : public ListBox, + private ListBoxModel, + private TableHeaderListener +{ +public: + + /** Creates a TableListBox. + + The model pointer passed-in can be null, in which case you can set it later + with setModel(). + */ + TableListBox (const String& componentName, + TableListBoxModel* const model); + + /** Destructor. */ + ~TableListBox(); + + /** Changes the TableListBoxModel that is being used for this table. + */ + void setModel (TableListBoxModel* const newModel); + + /** Returns the model currently in use. */ + TableListBoxModel* getModel() const throw() { return model; } + + /** Returns the header component being used in this table. */ + TableHeaderComponent* getHeader() const throw() { return header; } + + /** Changes the height of the table header component. + @see getHeaderHeight + */ + void setHeaderHeight (const int newHeight); + + /** Returns the height of the table header. + @see setHeaderHeight + */ + int getHeaderHeight() const throw(); + + /** Resizes a column to fit its contents. + + This uses TableListBoxModel::getColumnAutoSizeWidth() to find the best width, + and applies that to the column. + + @see autoSizeAllColumns, TableHeaderComponent::setColumnWidth + */ + void autoSizeColumn (const int columnId); + + /** Calls autoSizeColumn() for all columns in the table. */ + void autoSizeAllColumns(); + + /** Enables or disables the auto size options on the popup menu. + + By default, these are enabled. + */ + void setAutoSizeMenuOptionShown (const bool shouldBeShown); + + /** True if the auto-size options should be shown on the menu. + @see setAutoSizeMenuOptionsShown + */ + bool isAutoSizeMenuOptionShown() const throw(); + + /** Returns the position of one of the cells in the table. + + If relativeToComponentTopLeft is true, the co-ordinates are relative to + the table component's top-left. The row number isn't checked to see if it's + in-range, but the column ID must exist or this will return an empty rectangle. + + If relativeToComponentTopLeft is false, the co-ords are relative to the + top-left of the table's top-left cell. + */ + const Rectangle getCellPosition (const int columnId, + const int rowNumber, + const bool relativeToComponentTopLeft) const; + + /** Scrolls horizontally if necessary to make sure that a particular column is visible. + + @see ListBox::scrollToEnsureRowIsOnscreen + */ + void scrollToEnsureColumnIsOnscreen (const int columnId); + + /** @internal */ + int getNumRows(); + /** @internal */ + void paintListBoxItem (int, Graphics&, int, int, bool); + /** @internal */ + Component* refreshComponentForRow (int rowNumber, bool isRowSelected, Component* existingComponentToUpdate); + /** @internal */ + void selectedRowsChanged (int lastRowSelected); + /** @internal */ + void deleteKeyPressed (int currentSelectedRow); + /** @internal */ + void returnKeyPressed (int currentSelectedRow); + /** @internal */ + void backgroundClicked(); + /** @internal */ + void listWasScrolled(); + /** @internal */ + void tableColumnsChanged (TableHeaderComponent*); + /** @internal */ + void tableColumnsResized (TableHeaderComponent*); + /** @internal */ + void tableSortOrderChanged (TableHeaderComponent*); + /** @internal */ + void tableColumnDraggingChanged (TableHeaderComponent*, int); + /** @internal */ + void resized(); + + juce_UseDebuggingNewOperator + +private: + TableHeaderComponent* header; + TableListBoxModel* model; + int columnIdNowBeingDragged; + bool autoSizeOptionsShown; + + void updateColumnComponents() const; + + TableListBox (const TableListBox&); + const TableListBox& operator= (const TableListBox&); +}; + +#endif // __JUCE_TABLELISTBOX_JUCEHEADER__ +/********* End of inlined file: juce_TableListBox.h *********/ + +#endif +#ifndef __JUCE_TEXTEDITOR_JUCEHEADER__ + +#endif +#ifndef __JUCE_TOOLBAR_JUCEHEADER__ + +#endif +#ifndef __JUCE_TOOLBARITEMCOMPONENT_JUCEHEADER__ + +#endif +#ifndef __JUCE_TOOLBARITEMFACTORY_JUCEHEADER__ + +/********* Start of inlined file: juce_ToolbarItemFactory.h *********/ +#ifndef __JUCE_TOOLBARITEMFACTORY_JUCEHEADER__ +#define __JUCE_TOOLBARITEMFACTORY_JUCEHEADER__ + +/** + A factory object which can create ToolbarItemComponent objects. + + A subclass of ToolbarItemFactory publishes a set of types of toolbar item + that it can create. + + Each type of item is identified by a unique ID, and multiple instances of an + item type can exist at once (even on the same toolbar, e.g. spacers or separator + bars). + + @see Toolbar, ToolbarItemComponent, ToolbarButton +*/ +class JUCE_API ToolbarItemFactory +{ +public: + + ToolbarItemFactory(); + + /** Destructor. */ + virtual ~ToolbarItemFactory(); + + /** A set of reserved item ID values, used for the built-in item types. + */ + enum SpecialItemIds + { + separatorBarId = -1, /**< The item ID for a vertical (or horizontal) separator bar that + can be placed between sets of items to break them into groups. */ + spacerId = -2, /**< The item ID for a fixed-width space that can be placed between + items.*/ + flexibleSpacerId = -3 /**< The item ID for a gap that pushes outwards against the things on + either side of it, filling any available space. */ + }; + + /** Must return a list of the IDs for all the item types that this factory can create. + + The ids should be added to the array that is passed-in. + + An item ID can be any integer you choose, except for 0, which is considered a null ID, + and the predefined IDs in the SpecialItemIds enum. + + You should also add the built-in types (separatorBarId, spacerId and flexibleSpacerId) + to this list if you want your toolbar to be able to contain those items. + + The list returned here is used by the ToolbarItemPalette class to obtain its list + of available items, and their order on the palette will reflect the order in which + they appear on this list. + + @see ToolbarItemPalette + */ + virtual void getAllToolbarItemIds (Array & ids) = 0; + + /** Must return the set of items that should be added to a toolbar as its default set. + + This method is used by Toolbar::addDefaultItems() to determine which items to + create. + + The items that your method adds to the array that is passed-in will be added to the + toolbar in the same order. Items can appear in the list more than once. + */ + virtual void getDefaultItemSet (Array & ids) = 0; + + /** Must create an instance of one of the items that the factory lists in its + getAllToolbarItemIds() method. + + The itemId parameter can be any of the values listed by your getAllToolbarItemIds() + method, except for the built-in item types from the SpecialItemIds enum, which + are created internally by the toolbar code. + + Try not to keep a pointer to the object that is returned, as it will be deleted + automatically by the toolbar, and remember that multiple instances of the same + item type are likely to exist at the same time. + */ + virtual ToolbarItemComponent* createItem (const int itemId) = 0; +}; + +#endif // __JUCE_TOOLBARITEMFACTORY_JUCEHEADER__ +/********* End of inlined file: juce_ToolbarItemFactory.h *********/ + +#endif +#ifndef __JUCE_TOOLBARITEMPALETTE_JUCEHEADER__ + +/********* Start of inlined file: juce_ToolbarItemPalette.h *********/ +#ifndef __JUCE_TOOLBARITEMPALETTE_JUCEHEADER__ +#define __JUCE_TOOLBARITEMPALETTE_JUCEHEADER__ + +/** + A component containing a list of toolbar items, which the user can drag onto + a toolbar to add them. + + You can use this class directly, but it's a lot easier to call Toolbar::showCustomisationDialog(), + which automatically shows one of these in a dialog box with lots of extra controls. + + @see Toolbar +*/ +class JUCE_API ToolbarItemPalette : public Component, + public DragAndDropContainer +{ +public: + + /** Creates a palette of items for a given factory, with the aim of adding them + to the specified toolbar. + + The ToolbarItemFactory::getAllToolbarItemIds() method is used to create the + set of items that are shown in this palette. + + The toolbar and factory must not be deleted while this object exists. + */ + ToolbarItemPalette (ToolbarItemFactory& factory, + Toolbar* const toolbar); + + /** Destructor. */ + ~ToolbarItemPalette(); + + /** @internal */ + void resized(); + + juce_UseDebuggingNewOperator + +private: + ToolbarItemFactory& factory; + Toolbar* toolbar; + Viewport* viewport; + + friend class Toolbar; + void replaceComponent (ToolbarItemComponent* const comp); + + ToolbarItemPalette (const ToolbarItemPalette&); + const ToolbarItemPalette& operator= (const ToolbarItemPalette&); +}; + +#endif // __JUCE_TOOLBARITEMPALETTE_JUCEHEADER__ +/********* End of inlined file: juce_ToolbarItemPalette.h *********/ + +#endif +#ifndef __JUCE_TREEVIEW_JUCEHEADER__ + +#endif +#ifndef __JUCE_BOOLEANPROPERTYCOMPONENT_JUCEHEADER__ + +/********* Start of inlined file: juce_BooleanPropertyComponent.h *********/ +#ifndef __JUCE_BOOLEANPROPERTYCOMPONENT_JUCEHEADER__ +#define __JUCE_BOOLEANPROPERTYCOMPONENT_JUCEHEADER__ + +/** + A PropertyComponent that contains an on/off toggle button. + + This type of property component can be used if you have a boolean value to + toggle on/off. + + @see PropertyComponent +*/ +class JUCE_API BooleanPropertyComponent : public PropertyComponent, + private ButtonListener +{ +public: + + /** Creates a button component. + + @param propertyName the property name to be passed to the PropertyComponent + @param buttonTextWhenTrue the text shown in the button when the value is true + @param buttonTextWhenFalse the text shown in the button when the value is false + */ + BooleanPropertyComponent (const String& propertyName, + const String& buttonTextWhenTrue, + const String& buttonTextWhenFalse); + + /** Destructor. */ + ~BooleanPropertyComponent(); + + /** Called to change the state of the boolean value. */ + virtual void setState (const bool newState) = 0; + + /** Must return the current value of the property. */ + virtual bool getState() const = 0; + + /** @internal */ + void paint (Graphics& g); + /** @internal */ + void refresh(); + /** @internal */ + void buttonClicked (Button*); + + juce_UseDebuggingNewOperator + +private: + ToggleButton* button; + String onText, offText; +}; + +#endif // __JUCE_BOOLEANPROPERTYCOMPONENT_JUCEHEADER__ +/********* End of inlined file: juce_BooleanPropertyComponent.h *********/ + +#endif +#ifndef __JUCE_BUTTONPROPERTYCOMPONENT_JUCEHEADER__ + +/********* Start of inlined file: juce_ButtonPropertyComponent.h *********/ +#ifndef __JUCE_BUTTONPROPERTYCOMPONENT_JUCEHEADER__ +#define __JUCE_BUTTONPROPERTYCOMPONENT_JUCEHEADER__ + +/** + A PropertyComponent that contains a button. + + This type of property component can be used if you need a button to trigger some + kind of action. + + @see PropertyComponent +*/ +class JUCE_API ButtonPropertyComponent : public PropertyComponent, + private ButtonListener +{ +public: + + /** Creates a button component. + + @param propertyName the property name to be passed to the PropertyComponent + @param triggerOnMouseDown this is passed to the Button::setTriggeredOnMouseDown() method + */ + ButtonPropertyComponent (const String& propertyName, + const bool triggerOnMouseDown); + + /** Destructor. */ + ~ButtonPropertyComponent(); + + /** Called when the user clicks the button. + */ + virtual void buttonClicked() = 0; + + /** Returns the string that should be displayed in the button. + + If you need to change this string, call refresh() to update the component. + */ + virtual const String getButtonText() const = 0; + + /** @internal */ + void refresh(); + /** @internal */ + void buttonClicked (Button*); + + juce_UseDebuggingNewOperator + +private: + TextButton* button; +}; + +#endif // __JUCE_BUTTONPROPERTYCOMPONENT_JUCEHEADER__ +/********* End of inlined file: juce_ButtonPropertyComponent.h *********/ + +#endif +#ifndef __JUCE_CHOICEPROPERTYCOMPONENT_JUCEHEADER__ + +/********* Start of inlined file: juce_ChoicePropertyComponent.h *********/ +#ifndef __JUCE_CHOICEPROPERTYCOMPONENT_JUCEHEADER__ +#define __JUCE_CHOICEPROPERTYCOMPONENT_JUCEHEADER__ + +/** + A PropertyComponent that shows its value as a combo box. + + This type of property component contains a list of options and has a + combo box to choose one. + + Your subclass's constructor must add some strings to the choices StringArray + and these are shown in the list. + + The getIndex() method will be called to find out which option is the currently + selected one. If you call refresh() it will call getIndex() to check whether + the value has changed, and will update the combo box if needed. + + If the user selects a different item from the list, setIndex() will be + called to let your class process this. + + @see PropertyComponent, PropertyPanel +*/ +class JUCE_API ChoicePropertyComponent : public PropertyComponent, + private ComboBoxListener +{ +public: + /** Creates the component. + + Your subclass's constructor must add a list of options to the choices + member variable. + */ + ChoicePropertyComponent (const String& propertyName); + + /** Destructor. */ + ~ChoicePropertyComponent(); + + /** Called when the user selects an item from the combo box. + + Your subclass must use this callback to update the value that this component + represents. The index is the index of the chosen item in the choices + StringArray. + */ + virtual void setIndex (const int newIndex) = 0; + + /** Returns the index of the item that should currently be shown. + + This is the index of the item in the choices StringArray that will be + shown. + */ + virtual int getIndex() const = 0; + + /** Returns the list of options. */ + const StringArray& getChoices() const throw(); + + /** @internal */ + void refresh(); + /** @internal */ + void comboBoxChanged (ComboBox*); + + juce_UseDebuggingNewOperator + +protected: + /** The list of options that will be shown in the combo box. + + Your subclass must populate this array in its constructor. If any empty + strings are added, these will be replaced with horizontal separators (see + ComboBox::addSeparator() for more info). + */ + StringArray choices; + +private: + ComboBox* comboBox; +}; + +#endif // __JUCE_CHOICEPROPERTYCOMPONENT_JUCEHEADER__ +/********* End of inlined file: juce_ChoicePropertyComponent.h *********/ + +#endif +#ifndef __JUCE_PROPERTYCOMPONENT_JUCEHEADER__ + +#endif +#ifndef __JUCE_PROPERTYPANEL_JUCEHEADER__ + +#endif +#ifndef __JUCE_SLIDERPROPERTYCOMPONENT_JUCEHEADER__ + +/********* Start of inlined file: juce_SliderPropertyComponent.h *********/ +#ifndef __JUCE_SLIDERPROPERTYCOMPONENT_JUCEHEADER__ +#define __JUCE_SLIDERPROPERTYCOMPONENT_JUCEHEADER__ + +/** + A PropertyComponent that shows its value as a slider. + + @see PropertyComponent, Slider +*/ +class JUCE_API SliderPropertyComponent : public PropertyComponent, + private SliderListener +{ +public: + + /** Creates the property component. + + The ranges, interval and skew factor are passed to the Slider component. + + If you need to customise the slider in other ways, your constructor can + access the slider member variable and change it directly. + */ + SliderPropertyComponent (const String& propertyName, + const double rangeMin, + const double rangeMax, + const double interval, + const double skewFactor = 1.0); + + /** Destructor. */ + ~SliderPropertyComponent(); + + /** Called when the user moves the slider to change its value. + + Your subclass must use this method to update whatever item this property + represents. + */ + virtual void setValue (const double newValue) = 0; + + /** Returns the value that the slider should show. */ + virtual const double getValue() const = 0; + + /** @internal */ + void refresh(); + /** @internal */ + void changeListenerCallback (void*); + /** @internal */ + void sliderValueChanged (Slider*); + + juce_UseDebuggingNewOperator + +protected: + + /** The slider component being used in this component. + + Your subclass has access to this in case it needs to customise it in some way. + */ + Slider* slider; +}; + +#endif // __JUCE_SLIDERPROPERTYCOMPONENT_JUCEHEADER__ +/********* End of inlined file: juce_SliderPropertyComponent.h *********/ + +#endif +#ifndef __JUCE_TEXTPROPERTYCOMPONENT_JUCEHEADER__ + +/********* Start of inlined file: juce_TextPropertyComponent.h *********/ +#ifndef __JUCE_TEXTPROPERTYCOMPONENT_JUCEHEADER__ +#define __JUCE_TEXTPROPERTYCOMPONENT_JUCEHEADER__ + +/** + A PropertyComponent that shows its value as editable text. + + @see PropertyComponent +*/ +class JUCE_API TextPropertyComponent : public PropertyComponent +{ +public: + + /** Creates a text property component. + + The maxNumChars is used to set the length of string allowable, and isMultiLine + sets whether the text editor allows carriage returns. + + @see TextEditor + */ + TextPropertyComponent (const String& propertyName, + const int maxNumChars, + const bool isMultiLine); + + /** Destructor. */ + ~TextPropertyComponent(); + + /** Called when the user edits the text. + + Your subclass must use this callback to change the value of whatever item + this property component represents. + */ + virtual void setText (const String& newText) = 0; + + /** Returns the text that should be shown in the text editor. + */ + virtual const String getText() const = 0; + + /** @internal */ + void refresh(); + /** @internal */ + void textWasEdited(); + + juce_UseDebuggingNewOperator + +private: + Label* textEditor; +}; + +#endif // __JUCE_TEXTPROPERTYCOMPONENT_JUCEHEADER__ +/********* End of inlined file: juce_TextPropertyComponent.h *********/ + +#endif +#ifndef __JUCE_COMPONENTANIMATOR_JUCEHEADER__ + +#endif +#ifndef __JUCE_COMPONENTBOUNDSCONSTRAINER_JUCEHEADER__ + +#endif +#ifndef __JUCE_COMPONENTMOVEMENTWATCHER_JUCEHEADER__ + +/********* Start of inlined file: juce_ComponentMovementWatcher.h *********/ +#ifndef __JUCE_COMPONENTMOVEMENTWATCHER_JUCEHEADER__ +#define __JUCE_COMPONENTMOVEMENTWATCHER_JUCEHEADER__ + +/** An object that watches for any movement of a component or any of its parent components. + + This makes it easy to check when a component is moved relative to its top-level + peer window. The normal Component::moved() method is only called when a component + moves relative to its immediate parent, and sometimes you want to know if any of + components higher up the tree have moved (which of course will affect the overall + position of all their sub-components). + + It also includes a callback that lets you know when the top-level peer is changed. + + This class is used by specialised components like OpenGLComponent or QuickTimeComponent + because they need to keep their custom windows in the right place and respond to + changes in the peer. +*/ +class JUCE_API ComponentMovementWatcher : public ComponentListener +{ +public: + + /** Creates a ComponentMovementWatcher to watch a given target component. */ + ComponentMovementWatcher (Component* const component); + + /** Destructor. */ + ~ComponentMovementWatcher(); + + /** This callback happens when the component that is being watched is moved + relative to its top-level peer window, or when it is resized. + */ + virtual void componentMovedOrResized (bool wasMoved, bool wasResized) = 0; + + /** This callback happens when the component's top-level peer is changed. + */ + virtual void componentPeerChanged() = 0; + + juce_UseDebuggingNewOperator + + /** @internal */ + void componentParentHierarchyChanged (Component& component); + /** @internal */ + void componentMovedOrResized (Component& component, bool wasMoved, bool wasResized); + +private: + + Component* const component; + ComponentPeer* lastPeer; + VoidArray registeredParentComps; + bool reentrant; + int lastX, lastY, lastWidth, lastHeight; +#ifdef JUCE_DEBUG + ComponentDeletionWatcher* deletionWatcher; +#endif + + void unregister() throw(); + void registerWithParentComps() throw(); + + ComponentMovementWatcher (const ComponentMovementWatcher&); + const ComponentMovementWatcher& operator= (const ComponentMovementWatcher&); +}; + +#endif // __JUCE_COMPONENTMOVEMENTWATCHER_JUCEHEADER__ +/********* End of inlined file: juce_ComponentMovementWatcher.h *********/ + +#endif +#ifndef __JUCE_GROUPCOMPONENT_JUCEHEADER__ + +/********* Start of inlined file: juce_GroupComponent.h *********/ +#ifndef __JUCE_GROUPCOMPONENT_JUCEHEADER__ +#define __JUCE_GROUPCOMPONENT_JUCEHEADER__ + +/** + A component that draws an outline around itself and has an optional title at + the top, for drawing an outline around a group of controls. + +*/ +class JUCE_API GroupComponent : public Component +{ +public: + + /** Creates a GroupComponent. + + @param componentName the name to give the component + @param labelText the text to show at the top of the outline + */ + GroupComponent (const String& componentName, + const String& labelText); + + /** Destructor. */ + ~GroupComponent(); + + /** Changes the text that's shown at the top of the component. */ + void setText (const String& newText) throw(); + + /** Returns the currently displayed text label. */ + const String getText() const throw(); + + /** Sets the positioning of the text label. + + (The default is Justification::left) + + @see getTextLabelPosition + */ + void setTextLabelPosition (const Justification& justification); + + /** Returns the current text label position. + + @see setTextLabelPosition + */ + const Justification getTextLabelPosition() const throw() { return justification; } + + /** A set of colour IDs to use to change the colour of various aspects of the component. + + These constants can be used either via the Component::setColour(), or LookAndFeel::setColour() + methods. + + @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour + */ + enum ColourIds + { + outlineColourId = 0x1005400, /**< The colour to use for drawing the line around the edge. */ + textColourId = 0x1005410 /**< The colour to use to draw the text label. */ + }; + + /** @internal */ + void paint (Graphics& g); + /** @internal */ + void enablementChanged(); + /** @internal */ + void colourChanged(); + +private: + String text; + Justification justification; + + GroupComponent (const GroupComponent&); + const GroupComponent& operator= (const GroupComponent&); +}; + +#endif // __JUCE_GROUPCOMPONENT_JUCEHEADER__ +/********* End of inlined file: juce_GroupComponent.h *********/ + +#endif +#ifndef __JUCE_MULTIDOCUMENTPANEL_JUCEHEADER__ + +/********* Start of inlined file: juce_MultiDocumentPanel.h *********/ +#ifndef __JUCE_MULTIDOCUMENTPANEL_JUCEHEADER__ +#define __JUCE_MULTIDOCUMENTPANEL_JUCEHEADER__ + +/********* Start of inlined file: juce_TabbedComponent.h *********/ +#ifndef __JUCE_TABBEDCOMPONENT_JUCEHEADER__ +#define __JUCE_TABBEDCOMPONENT_JUCEHEADER__ + +/********* Start of inlined file: juce_TabbedButtonBar.h *********/ +#ifndef __JUCE_TABBEDBUTTONBAR_JUCEHEADER__ +#define __JUCE_TABBEDBUTTONBAR_JUCEHEADER__ + +class TabbedButtonBar; + +/** In a TabbedButtonBar, this component is used for each of the buttons. + + If you want to create a TabbedButtonBar with custom tab components, derive + your component from this class, and override the TabbedButtonBar::createTabButton() + method to create it instead of the default one. + + @see TabbedButtonBar +*/ +class JUCE_API TabBarButton : public Button +{ +public: + + /** Creates the tab button. */ + TabBarButton (const String& name, + TabbedButtonBar* const ownerBar, + const int tabIndex); + + /** Destructor. */ + ~TabBarButton(); + + /** Chooses the best length for the tab, given the specified depth. + + If the tab is horizontal, this should return its width, and the depth + specifies its height. If it's vertical, it should return the height, and + the depth is actually its width. + */ + virtual int getBestTabLength (const int depth); + + void paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown); + void clicked (const ModifierKeys& mods); + bool hitTest (int x, int y); + + juce_UseDebuggingNewOperator + +protected: + friend class TabbedButtonBar; + TabbedButtonBar* const owner; + int tabIndex, overlapPixels; + DropShadowEffect shadow; + + /** Returns an area of the component that's safe to draw in. + + This deals with the orientation of the tabs, which affects which side is + touching the tabbed box's content component. + */ + void getActiveArea (int& x, int& y, int& w, int& h); + +private: + TabBarButton (const TabBarButton&); + const TabBarButton& operator= (const TabBarButton&); +}; + +/** + A vertical or horizontal bar containing tabs that you can select. + + You can use one of these to generate things like a dialog box that has + tabbed pages you can flip between. Attach a ChangeListener to the + button bar to be told when the user changes the page. + + An easier method than doing this is to use a TabbedComponent, which + contains its own TabbedButtonBar and which takes care of the layout + and other housekeeping. + + @see TabbedComponent +*/ +class JUCE_API TabbedButtonBar : public Component, + public ChangeBroadcaster, + public ButtonListener +{ +public: + + /** The placement of the tab-bar + + @see setOrientation, getOrientation + */ + enum Orientation + { + TabsAtTop, + TabsAtBottom, + TabsAtLeft, + TabsAtRight + }; + + /** Creates a TabbedButtonBar with a given placement. + + You can change the orientation later if you need to. + */ + TabbedButtonBar (const Orientation orientation); + + /** Destructor. */ + ~TabbedButtonBar(); + + /** Changes the bar's orientation. + + This won't change the bar's actual size - you'll need to do that yourself, + but this determines which direction the tabs go in, and which side they're + stuck to. + */ + void setOrientation (const Orientation orientation); + + /** Returns the current orientation. + + @see setOrientation + */ + Orientation getOrientation() const throw() { return orientation; } + + /** Deletes all the tabs from the bar. + + @see addTab + */ + void clearTabs(); + + /** Adds a tab to the bar. + + Tabs are added in left-to-right reading order. + + If this is the first tab added, it'll also be automatically selected. + */ + void addTab (const String& tabName, + const Colour& tabBackgroundColour, + int insertIndex = -1); + + /** Changes the name of one of the tabs. */ + void setTabName (const int tabIndex, + const String& newName); + + /** Gets rid of one of the tabs. */ + void removeTab (const int tabIndex); + + /** Moves a tab to a new index in the list. + + Pass -1 as the index to move it to the end of the list. + */ + void moveTab (const int currentIndex, + const int newIndex); + + /** Returns the number of tabs in the bar. */ + int getNumTabs() const; + + /** Returns a list of all the tab names in the bar. */ + const StringArray getTabNames() const; + + /** Changes the currently selected tab. + + This will send a change message and cause a synchronous callback to + the currentTabChanged() method. (But if the given tab is already selected, + nothing will be done). + + To deselect all the tabs, use an index of -1. + */ + void setCurrentTabIndex (int newTabIndex); + + /** Returns the name of the currently selected tab. + + This could be an empty string if none are selected. + */ + const String& getCurrentTabName() const throw() { return tabs [currentTabIndex]; } + + /** Returns the index of the currently selected tab. + + This could return -1 if none are selected. + */ + int getCurrentTabIndex() const throw() { return currentTabIndex; } + + /** Callback method to indicate the selected tab has been changed. + + @see setCurrentTabIndex + */ + virtual void currentTabChanged (const int newCurrentTabIndex, + const String& newCurrentTabName); + + /** Callback method to indicate that the user has right-clicked on a tab. + + (Or ctrl-clicked on the Mac) + */ + virtual void popupMenuClickOnTab (const int tabIndex, + const String& tabName); + + /** Returns the colour of a tab. + + This is the colour that was specified in addTab(). + */ + const Colour getTabBackgroundColour (const int tabIndex); + + /** Changes the background colour of a tab. + + @see addTab, getTabBackgroundColour + */ + void setTabBackgroundColour (const int tabIndex, const Colour& newColour); + + /** @internal */ + void resized(); + /** @internal */ + void buttonClicked (Button* button); + /** @internal */ + void lookAndFeelChanged(); + + juce_UseDebuggingNewOperator + +protected: + + /** This creates one of the tabs. + + If you need to use custom tab components, you can override this method and + return your own class instead of the default. + */ + virtual TabBarButton* createTabButton (const String& tabName, + const int tabIndex); + +private: + Orientation orientation; + + StringArray tabs; + Array tabColours; + int currentTabIndex; + Component* behindFrontTab; + Button* extraTabsButton; + + TabBarButton* getTabButton (const int index) const; + + TabbedButtonBar (const TabbedButtonBar&); + const TabbedButtonBar& operator= (const TabbedButtonBar&); +}; + +#endif // __JUCE_TABBEDBUTTONBAR_JUCEHEADER__ +/********* End of inlined file: juce_TabbedButtonBar.h *********/ + +/** + A component with a TabbedButtonBar along one of its sides. + + This makes it easy to create a set of tabbed pages, just add a bunch of tabs + with addTab(), and this will take care of showing the pages for you when the + user clicks on a different tab. + + @see TabbedButtonBar +*/ +class JUCE_API TabbedComponent : public Component +{ +public: + + /** Creates a TabbedComponent, specifying where the tabs should be placed. + + Once created, add some tabs with the addTab() method. + */ + TabbedComponent (const TabbedButtonBar::Orientation orientation); + + /** Destructor. */ + ~TabbedComponent(); + + /** Changes the placement of the tabs. + + This will rearrange the layout to place the tabs along the appropriate + side of this component, and will shift the content component accordingly. + + @see TabbedButtonBar::setOrientation + */ + void setOrientation (const TabbedButtonBar::Orientation orientation); + + /** Returns the current tab placement. + + @see setOrientation, TabbedButtonBar::getOrientation + */ + TabbedButtonBar::Orientation getOrientation() const throw(); + + /** Specifies how many pixels wide or high the tab-bar should be. + + If the tabs are placed along the top or bottom, this specified the height + of the bar; if they're along the left or right edges, it'll be the width + of the bar. + */ + void setTabBarDepth (const int newDepth); + + /** Returns the current thickness of the tab bar. + + @see setTabBarDepth + */ + int getTabBarDepth() const throw() { return tabDepth; } + + /** Specifies an outline that should be drawn around the entire content component. + + If this thickness is > 0, a line of the specified colour will be drawn around + the three sides of the content component which don't touch the tab-bar, and + the content component will be inset by this amount. + */ + void setOutline (const Colour& newOutlineColour, + const int newThickness); + + /** Specifies a gap to leave around the edge of the content component. + + Each edge of the content component will be indented by the given number of pixels. + */ + void setIndent (const int indentThickness); + + /** Removes all the tabs from the bar. + + @see TabbedButtonBar::clearTabs + */ + void clearTabs(); + + /** Adds a tab to the tab-bar. + + The component passed in will be shown for the tab, and if deleteComponentWhenNotNeeded + is true, it will be deleted when the tab is removed or when this object is + deleted. + + @see TabbedButtonBar::addTab + */ + void addTab (const String& tabName, + const Colour& tabBackgroundColour, + Component* const contentComponent, + const bool deleteComponentWhenNotNeeded, + const int insertIndex = -1); + + /** Changes the name of one of the tabs. */ + void setTabName (const int tabIndex, + const String& newName); + + /** Gets rid of one of the tabs. */ + void removeTab (const int tabIndex); + + /** Returns the number of tabs in the bar. */ + int getNumTabs() const; + + /** Returns a list of all the tab names in the bar. */ + const StringArray getTabNames() const; + + /** Returns the content component that was added for the given index. + + Be sure not to use or delete the components that are returned, as this may interfere + with the TabbedComponent's use of them. + */ + Component* getTabContentComponent (const int tabIndex) const throw(); + + /** Returns the colour of one of the tabs. */ + const Colour getTabBackgroundColour (const int tabIndex) const throw(); + + /** Changes the background colour of one of the tabs. */ + void setTabBackgroundColour (const int tabIndex, const Colour& newColour); + + /** Changes the currently-selected tab. + + To deselect all the tabs, pass -1 as the index. + + @see TabbedButtonBar::setCurrentTabIndex + */ + void setCurrentTabIndex (const int newTabIndex); + + /** Returns the index of the currently selected tab. + + @see addTab, TabbedButtonBar::getCurrentTabIndex() + */ + int getCurrentTabIndex() const; + + /** Returns the name of the currently selected tab. + + @see addTab, TabbedButtonBar::getCurrentTabName() + */ + const String& getCurrentTabName() const; + + /** Returns the current component that's filling the panel. + + This will return 0 if there isn't one. + */ + Component* getCurrentContentComponent() const throw() { return panelComponent; } + + /** Callback method to indicate the selected tab has been changed. + + @see setCurrentTabIndex + */ + virtual void currentTabChanged (const int newCurrentTabIndex, + const String& newCurrentTabName); + + /** Callback method to indicate that the user has right-clicked on a tab. + + (Or ctrl-clicked on the Mac) + */ + virtual void popupMenuClickOnTab (const int tabIndex, + const String& tabName); + + /** Returns the tab button bar component that is being used. + */ + TabbedButtonBar& getTabbedButtonBar() const throw() { return *tabs; } + + /** @internal */ + void paint (Graphics& g); + /** @internal */ + void resized(); + + juce_UseDebuggingNewOperator + +protected: + + TabbedButtonBar* tabs; + + /** This creates one of the tab buttons. + + If you need to use custom tab components, you can override this method and + return your own class instead of the default. + */ + virtual TabBarButton* createTabButton (const String& tabName, + const int tabIndex); + +private: + + Array contentComponents; + Component* panelComponent; + int tabDepth; + Colour outlineColour; + int outlineThickness, edgeIndent; + + friend class TabCompButtonBar; + void changeCallback (const int newCurrentTabIndex, const String& newTabName); + + TabbedComponent (const TabbedComponent&); + const TabbedComponent& operator= (const TabbedComponent&); +}; + +#endif // __JUCE_TABBEDCOMPONENT_JUCEHEADER__ +/********* End of inlined file: juce_TabbedComponent.h *********/ + +/********* Start of inlined file: juce_DocumentWindow.h *********/ +#ifndef __JUCE_DOCUMENTWINDOW_JUCEHEADER__ +#define __JUCE_DOCUMENTWINDOW_JUCEHEADER__ + +/********* Start of inlined file: juce_ResizableWindow.h *********/ +#ifndef __JUCE_RESIZABLEWINDOW_JUCEHEADER__ +#define __JUCE_RESIZABLEWINDOW_JUCEHEADER__ + +/********* Start of inlined file: juce_TopLevelWindow.h *********/ +#ifndef __JUCE_TOPLEVELWINDOW_JUCEHEADER__ +#define __JUCE_TOPLEVELWINDOW_JUCEHEADER__ + +/********* Start of inlined file: juce_DropShadower.h *********/ +#ifndef __JUCE_DROPSHADOWER_JUCEHEADER__ +#define __JUCE_DROPSHADOWER_JUCEHEADER__ + +/** + Adds a drop-shadow to a component. + + This object creates and manages a set of components which sit around a + component, creating a gaussian shadow around it. The components will track + the position of the component and if it's brought to the front they'll also + follow this. + + For desktop windows you don't need to use this class directly - just + set the Component::windowHasDropShadow flag when calling + Component::addToDesktop(), and the system will create one of these if it's + needed (which it obviously isn't on the Mac, for example). +*/ +class JUCE_API DropShadower : public ComponentListener +{ +public: + + /** Creates a DropShadower. + + @param alpha the opacity of the shadows, from 0 to 1.0 + @param xOffset the horizontal displacement of the shadow, in pixels + @param yOffset the vertical displacement of the shadow, in pixels + @param blurRadius the radius of the blur to use for creating the shadow + */ + DropShadower (const float alpha = 0.5f, + const int xOffset = 1, + const int yOffset = 5, + const float blurRadius = 10.0f); + + /** Destructor. */ + virtual ~DropShadower(); + + /** Attaches the DropShadower to the component you want to shadow. */ + void setOwner (Component* componentToFollow); + + /** @internal */ + void componentMovedOrResized (Component& component, bool wasMoved, bool wasResized); + /** @internal */ + void componentBroughtToFront (Component& component); + /** @internal */ + void componentChildrenChanged (Component& component); + /** @internal */ + void componentParentHierarchyChanged (Component& component); + /** @internal */ + void componentVisibilityChanged (Component& component); + + juce_UseDebuggingNewOperator + +private: + + Component* owner; + int numShadows; + Component* shadowWindows[4]; + Image* shadowImageSections[12]; + const int shadowEdge, xOffset, yOffset; + const float alpha, blurRadius; + bool inDestructor, reentrant; + + void updateShadows(); + void setShadowImage (Image* const src, + const int num, + const int w, const int h, + const int sx, const int sy) throw(); + + void bringShadowWindowsToFront(); + void deleteShadowWindows(); + + DropShadower (const DropShadower&); + const DropShadower& operator= (const DropShadower&); +}; + +#endif // __JUCE_DROPSHADOWER_JUCEHEADER__ +/********* End of inlined file: juce_DropShadower.h *********/ + +/** + A base class for top-level windows. + + This class is used for components that are considered a major part of your + application - e.g. ResizableWindow, DocumentWindow, DialogWindow, AlertWindow, + etc. Things like menus that pop up briefly aren't derived from it. + + A TopLevelWindow is probably on the desktop, but this isn't mandatory - it + could itself be the child of another component. + + The class manages a list of all instances of top-level windows that are in use, + and each one is also given the concept of being "active". The active window is + one that is actively being used by the user. This isn't quite the same as the + component with the keyboard focus, because there may be a popup menu or other + temporary window which gets keyboard focus while the active top level window is + unchanged. + + A top-level window also has an optional drop-shadow. + + @see ResizableWindow, DocumentWindow, DialogWindow +*/ +class JUCE_API TopLevelWindow : public Component +{ +public: + + /** Creates a TopLevelWindow. + + @param name the name to give the component + @param addToDesktop if true, the window will be automatically added to the + desktop; if false, you can use it as a child component + */ + TopLevelWindow (const String& name, + const bool addToDesktop); + + /** Destructor. */ + ~TopLevelWindow(); + + /** True if this is currently the TopLevelWindow that is actively being used. + + This isn't quite the same as having keyboard focus, because the focus may be + on a child component or a temporary pop-up menu, etc, while this window is + still considered to be active. + + @see activeWindowStatusChanged + */ + bool isActiveWindow() const throw() { return windowIsActive_; } + + /** This will set the bounds of the window so that it's centred in front of another + window. + + If your app has a few windows open and want to pop up a dialog box for one of + them, you can use this to show it in front of the relevent parent window, which + is a bit neater than just having it appear in the middle of the screen. + + If componentToCentreAround is 0, then the currently active TopLevelWindow will + be used instead. If no window is focused, it'll just default to the middle of the + screen. + */ + void centreAroundComponent (Component* componentToCentreAround, + const int width, const int height); + + /** Turns the drop-shadow on and off. */ + void setDropShadowEnabled (const bool useShadow); + + /** Sets whether an OS-native title bar will be used, or a Juce one. + + @see isUsingNativeTitleBar + */ + void setUsingNativeTitleBar (const bool useNativeTitleBar); + + /** Returns true if the window is currently using an OS-native title bar. + + @see setUsingNativeTitleBar + */ + bool isUsingNativeTitleBar() const throw() { return useNativeTitleBar && isOnDesktop(); } + + /** Returns the number of TopLevelWindow objects currently in use. + + @see getTopLevelWindow + */ + static int getNumTopLevelWindows() throw(); + + /** Returns one of the TopLevelWindow objects currently in use. + + The index is 0 to (getNumTopLevelWindows() - 1). + */ + static TopLevelWindow* getTopLevelWindow (const int index) throw(); + + /** Returns the currently-active top level window. + + There might not be one, of course, so this can return 0. + */ + static TopLevelWindow* getActiveTopLevelWindow() throw(); + + juce_UseDebuggingNewOperator + + /** @internal */ + virtual void addToDesktop (int windowStyleFlags, void* nativeWindowToAttachTo = 0); + +protected: + + /** This callback happens when this window becomes active or inactive. + + @see isActiveWindow + */ + virtual void activeWindowStatusChanged(); + + /** @internal */ + void focusOfChildComponentChanged (FocusChangeType cause); + /** @internal */ + void parentHierarchyChanged(); + /** @internal */ + void visibilityChanged(); + /** @internal */ + virtual int getDesktopWindowStyleFlags() const; + /** @internal */ + void recreateDesktopWindow(); + +private: + friend class TopLevelWindowManager; + bool useDropShadow, useNativeTitleBar, windowIsActive_; + DropShadower* shadower; + + void setWindowActive (const bool isNowActive) throw(); + + TopLevelWindow (const TopLevelWindow&); + const TopLevelWindow& operator= (const TopLevelWindow&); +}; + +#endif // __JUCE_TOPLEVELWINDOW_JUCEHEADER__ +/********* End of inlined file: juce_TopLevelWindow.h *********/ + +/********* Start of inlined file: juce_ResizableBorderComponent.h *********/ +#ifndef __JUCE_RESIZABLEBORDERCOMPONENT_JUCEHEADER__ +#define __JUCE_RESIZABLEBORDERCOMPONENT_JUCEHEADER__ + +/** + A component that resizes its parent window when dragged. + + This component forms a frame around the edge of a component, allowing it to + be dragged by the edges or corners to resize it - like the way windows are + resized in MSWindows or Linux. + + To use it, just add it to your component, making it fill the entire parent component + (there's a mouse hit-test that only traps mouse-events which land around the + edge of the component, so it's even ok to put it on top of any other components + you're using). Make sure you rescale the resizer component to fill the parent + each time the parent's size changes. + + @see ResizableCornerComponent +*/ +class JUCE_API ResizableBorderComponent : public Component +{ +public: + + /** Creates a resizer. + + Pass in the target component which you want to be resized when this one is + dragged. + + The target component will usually be a parent of the resizer component, but this + isn't mandatory. + + Remember that when the target component is resized, it'll need to move and + resize this component to keep it in place, as this won't happen automatically. + + If the constrainer parameter is non-zero, then this object will be used to enforce + limits on the size and position that the component can be stretched to. Make sure + that the constrainer isn't deleted while still in use by this object. + + @see ComponentBoundsConstrainer + */ + ResizableBorderComponent (Component* const componentToResize, + ComponentBoundsConstrainer* const constrainer); + + /** Destructor. */ + ~ResizableBorderComponent(); + + /** Specifies how many pixels wide the draggable edges of this component are. + + @see getBorderThickness + */ + void setBorderThickness (const BorderSize& newBorderSize) throw(); + + /** Returns the number of pixels wide that the draggable edges of this component are. + + @see setBorderThickness + */ + const BorderSize getBorderThickness() const throw(); + + juce_UseDebuggingNewOperator + +protected: + /** @internal */ + void paint (Graphics& g); + /** @internal */ + void mouseEnter (const MouseEvent& e); + /** @internal */ + void mouseMove (const MouseEvent& e); + /** @internal */ + void mouseDown (const MouseEvent& e); + /** @internal */ + void mouseDrag (const MouseEvent& e); + /** @internal */ + void mouseUp (const MouseEvent& e); + /** @internal */ + bool hitTest (int x, int y); + +private: + Component* const component; + ComponentBoundsConstrainer* constrainer; + BorderSize borderSize; + int originalX, originalY, originalW, originalH; + int mouseZone; + + void updateMouseZone (const MouseEvent& e) throw(); + + ResizableBorderComponent (const ResizableBorderComponent&); + const ResizableBorderComponent& operator= (const ResizableBorderComponent&); +}; + +#endif // __JUCE_RESIZABLEBORDERCOMPONENT_JUCEHEADER__ +/********* End of inlined file: juce_ResizableBorderComponent.h *********/ + +/********* Start of inlined file: juce_ResizableCornerComponent.h *********/ +#ifndef __JUCE_RESIZABLECORNERCOMPONENT_JUCEHEADER__ +#define __JUCE_RESIZABLECORNERCOMPONENT_JUCEHEADER__ + +/** A component that resizes a parent window when dragged. + + This is the small triangular stripey resizer component you get in the bottom-right + of windows (more commonly on the Mac than Windows). Put one in the corner of + a larger component and it will automatically resize its parent when it gets dragged + around. + + @see ResizableFrameComponent +*/ +class JUCE_API ResizableCornerComponent : public Component +{ +public: + + /** Creates a resizer. + + Pass in the target component which you want to be resized when this one is + dragged. + + The target component will usually be a parent of the resizer component, but this + isn't mandatory. + + Remember that when the target component is resized, it'll need to move and + resize this component to keep it in place, as this won't happen automatically. + + If the constrainer parameter is non-zero, then this object will be used to enforce + limits on the size and position that the component can be stretched to. Make sure + that the constrainer isn't deleted while still in use by this object. If you + pass a zero in here, no limits will be put on the sizes it can be stretched to. + + @see ComponentBoundsConstrainer + */ + ResizableCornerComponent (Component* const componentToResize, + ComponentBoundsConstrainer* const constrainer); + + /** Destructor. */ + ~ResizableCornerComponent(); + + juce_UseDebuggingNewOperator + +protected: + /** @internal */ + void paint (Graphics& g); + /** @internal */ + void mouseDown (const MouseEvent& e); + /** @internal */ + void mouseDrag (const MouseEvent& e); + /** @internal */ + void mouseUp (const MouseEvent& e); + /** @internal */ + bool hitTest (int x, int y); + +private: + + Component* const component; + ComponentBoundsConstrainer* constrainer; + int originalX, originalY, originalW, originalH; + + ResizableCornerComponent (const ResizableCornerComponent&); + const ResizableCornerComponent& operator= (const ResizableCornerComponent&); +}; + +#endif // __JUCE_RESIZABLECORNERCOMPONENT_JUCEHEADER__ +/********* End of inlined file: juce_ResizableCornerComponent.h *********/ + +/** + A base class for top-level windows that can be dragged around and resized. + + To add content to the window, use its setContentComponent() method to + give it a component that will remain positioned inside it (leaving a gap around + the edges for a border). + + It's not advisable to add child components directly to a ResizableWindow: put them + inside your content component instead. And overriding methods like resized(), moved(), etc + is also not recommended - instead override these methods for your content component. + (If for some obscure reason you do need to override these methods, always remember to + call the super-class's resized() method too, otherwise it'll fail to lay out the window + decorations correctly). + + By default resizing isn't enabled - use the setResizable() method to enable it and + to choose the style of resizing to use. + + @see TopLevelWindow +*/ +class JUCE_API ResizableWindow : public TopLevelWindow +{ +public: + + /** Creates a ResizableWindow. + + @param name the name to give the component + @param backgroundColour the colour to use for filling the window's background. + @param addToDesktop if true, the window will be automatically added to the + desktop; if false, you can use it as a child component + */ + ResizableWindow (const String& name, + const Colour& backgroundColour, + const bool addToDesktop); + + /** Destructor. + + If a content component has been set with setContentComponent(), it + will be deleted. + */ + ~ResizableWindow(); + + /** Returns the colour currently being used for the window's background. + + As a convenience the window will fill itself with this colour, but you + can override the paint() method if you need more customised behaviour. + + @see setBackgroundColour + */ + const Colour& getBackgroundColour() const throw() { return backgroundColour; } + + /** Changes the colour currently being used for the window's background. + + As a convenience the window will fill itself with this colour, but you + can override the paint() method if you need more customised behaviour. + + Note that the opaque state of this window is altered by this call to reflect + the opacity of the colour passed-in. On window systems which can't support + semi-transparent windows this might cause problems, (though it's unlikely you'll + be using this class as a base for a semi-transparent component anyway). + + @see getBackgroundColour + */ + void setBackgroundColour (const Colour& newColour); + + /** Make the window resizable or fixed. + + @param shouldBeResizable whether it's resizable at all + @param useBottomRightCornerResizer if true, it'll add a ResizableCornerComponent at the + bottom-right; if false, it'll use a ResizableBorderComponent + around the edge + @see setResizeLimits, isResizable + */ + void setResizable (const bool shouldBeResizable, + const bool useBottomRightCornerResizer); + + /** True if resizing is enabled. + + @see setResizable + */ + bool isResizable() const throw(); + + /** This sets the maximum and minimum sizes for the window. + + If the window's current size is outside these limits, it will be resized to + make sure it's within them. + + Calling setBounds() on the component will bypass any size checking - it's only when + the window is being resized by the user that these values are enforced. + + @see setResizable, setFixedAspectRatio + */ + void setResizeLimits (const int newMinimumWidth, + const int newMinimumHeight, + const int newMaximumWidth, + const int newMaximumHeight) throw(); + + /** Returns the bounds constrainer object that this window is using. + + You can access this to change its properties. + */ + ComponentBoundsConstrainer* getConstrainer() throw() { return constrainer; } + + /** Sets the bounds-constrainer object to use for resizing and dragging this window. + + A pointer to the object you pass in will be kept, but it won't be deleted + by this object, so it's the caller's responsiblity to manage it. + + If you pass 0, then no contraints will be placed on the positioning of the window. + */ + void setConstrainer (ComponentBoundsConstrainer* newConstrainer); + + /** Calls the window's setBounds method, after first checking these bounds + with the current constrainer. + + @see setConstrainer + */ + void setBoundsConstrained (int x, int y, int width, int height); + + /** Returns true if the window is currently in full-screen mode. + + @see setFullScreen + */ + bool isFullScreen() const; + + /** Puts the window into full-screen mode, or restores it to its normal size. + + If true, the window will become full-screen; if false, it will return to the + last size it was before being made full-screen. + + @see isFullScreen + */ + void setFullScreen (const bool shouldBeFullScreen); + + /** Returns true if the window is currently minimised. + + @see setMinimised + */ + bool isMinimised() const; + + /** Minimises the window, or restores it to its previous position and size. + + When being un-minimised, it'll return to the last position and size it + was in before being minimised. + + @see isMinimised + */ + void setMinimised (const bool shouldMinimise); + + /** Returns a string which encodes the window's current size and position. + + This string will encapsulate the window's size, position, and whether it's + in full-screen mode. It's intended for letting your application save and + restore a window's position. + + Use the restoreWindowStateFromString() to restore from a saved state. + + @see restoreWindowStateFromString + */ + const String getWindowStateAsString(); + + /** Restores the window to a previously-saved size and position. + + This restores the window's size, positon and full-screen status from an + string that was previously created with the getWindowStateAsString() + method. + + @returns false if the string wasn't a valid window state + @see getWindowStateAsString + */ + bool restoreWindowStateFromString (const String& previousState); + + /** Returns the current content component. + + This will be the component set by setContentComponent(), or 0 if none + has yet been specified. + + @see setContentComponent + */ + Component* getContentComponent() const throw() { return contentComponent; } + + /** Changes the current content component. + + This sets a component that will be placed in the centre of the ResizableWindow, + (leaving a space around the edge for the border). + + You should never add components directly to a ResizableWindow (or any of its subclasses) + with addChildComponent(). Instead, add them to the content component. + + @param newContentComponent the new component to use (or null to not use one) - this + component will be deleted either when replaced by another call + to this method, or when the ResizableWindow is deleted. + To remove a content component without deleting it, use + setContentComponent (0, false). + @param deleteOldOne if true, the previous content component will be deleted; if + false, the previous component will just be removed without + deleting it. + @param resizeToFit if true, the ResizableWindow will maintain its size such that + it always fits around the size of the content component. If false, the + new content will be resized to fit the current space available. + */ + void setContentComponent (Component* const newContentComponent, + const bool deleteOldOne = true, + const bool resizeToFit = false); + + /** Changes the window so that the content component ends up with the specified size. + + This is basically a setSize call on the window, but which adds on the borders, + so you can specify the content component's target size. + */ + void setContentComponentSize (int width, int height); + + juce_UseDebuggingNewOperator + +protected: + /** @internal */ + void paint (Graphics& g); + /** (if overriding this, make sure you call ResizableWindow::resized() in your subclass) */ + void moved(); + /** (if overriding this, make sure you call ResizableWindow::resized() in your subclass) */ + void resized(); + /** @internal */ + void mouseDown (const MouseEvent& e); + /** @internal */ + void mouseDrag (const MouseEvent& e); + /** @internal */ + void lookAndFeelChanged(); + /** @internal */ + void childBoundsChanged (Component* child); + /** @internal */ + void parentSizeChanged(); + /** @internal */ + void visibilityChanged(); + /** @internal */ + void activeWindowStatusChanged(); + /** @internal */ + int getDesktopWindowStyleFlags() const; + + /** Returns the width of the border to use around the window. + + @see getContentComponentBorder + */ + virtual const BorderSize getBorderThickness(); + + /** Returns the insets to use when positioning the content component. + + @see getBorderThickness + */ + virtual const BorderSize getContentComponentBorder(); + +#ifdef JUCE_DEBUG + /** Overridden to warn people about adding components directly to this component + instead of using setContentComponent(). + + If you know what you're doing and are sure you really want to add a component, specify + a base-class method call to Component::addAndMakeVisible(), to side-step this warning. + */ + void addChildComponent (Component* const child, int zOrder = -1); + /** Overridden to warn people about adding components directly to this component + instead of using setContentComponent(). + + If you know what you're doing and are sure you really want to add a component, specify + a base-class method call to Component::addAndMakeVisible(), to side-step this warning. + */ + void addAndMakeVisible (Component* const child, int zOrder = -1); + +#endif + + ResizableCornerComponent* resizableCorner; + ResizableBorderComponent* resizableBorder; + +private: + Component* contentComponent; + bool resizeToFitContent, fullscreen; + Colour backgroundColour; + ComponentDragger dragger; + Rectangle lastNonFullScreenPos; + ComponentBoundsConstrainer defaultConstrainer; + ComponentBoundsConstrainer* constrainer; + #ifdef JUCE_DEBUG + bool hasBeenResized; + #endif + + void updateLastPos(); + + ResizableWindow (const ResizableWindow&); + const ResizableWindow& operator= (const ResizableWindow&); + + // (xxx remove these eventually) + // temporarily here to stop old code compiling, as the parameters for these methods have changed.. + void getBorderThickness (int& left, int& top, int& right, int& bottom); + // temporarily here to stop old code compiling, as the parameters for these methods have changed.. + void getContentComponentBorder (int& left, int& top, int& right, int& bottom); +}; + +#endif // __JUCE_RESIZABLEWINDOW_JUCEHEADER__ +/********* End of inlined file: juce_ResizableWindow.h *********/ + +/** + A resizable window with a title bar and maximise, minimise and close buttons. + + This subclass of ResizableWindow creates a fairly standard type of window with + a title bar and various buttons. The name of the component is shown in the + title bar, and an icon can optionally be specified with setIcon(). + + All the methods available to a ResizableWindow are also available to this, + so it can easily be made resizable, minimised, maximised, etc. + + It's not advisable to add child components directly to a DocumentWindow: put them + inside your content component instead. And overriding methods like resized(), moved(), etc + is also not recommended - instead override these methods for your content component. + (If for some obscure reason you do need to override these methods, always remember to + call the super-class's resized() method too, otherwise it'll fail to lay out the window + decorations correctly). + + You can also automatically add a menu bar to the window, using the setMenuBar() + method. + + @see ResizableWindow, DialogWindow +*/ +class JUCE_API DocumentWindow : public ResizableWindow +{ +public: + + /** The set of available button-types that can be put on the title bar. + + @see setTitleBarButtonsRequired + */ + enum TitleBarButtons + { + minimiseButton = 1, + maximiseButton = 2, + closeButton = 4, + + /** A combination of all the buttons above. */ + allButtons = 7 + }; + + /** Creates a DocumentWindow. + + @param name the name to give the component - this is also + the title shown at the top of the window. To change + this later, use setName() + @param backgroundColour the colour to use for filling the window's background. + @param requiredButtons specifies which of the buttons (close, minimise, maximise) + should be shown on the title bar. This value is a bitwise + combination of values from the TitleBarButtons enum. Note + that it can be "allButtons" to get them all. You + can change this later with the setTitleBarButtonsRequired() + method, which can also specify where they are positioned. + @param addToDesktop if true, the window will be automatically added to the + desktop; if false, you can use it as a child component + @see TitleBarButtons + */ + DocumentWindow (const String& name, + const Colour& backgroundColour, + const int requiredButtons, + const bool addToDesktop = true); + + /** Destructor. + + If a content component has been set with setContentComponent(), it + will be deleted. + */ + ~DocumentWindow(); + + /** Changes the component's name. + + (This is overridden from Component::setName() to cause a repaint, as + the name is what gets drawn across the window's title bar). + */ + void setName (const String& newName); + + /** Sets an icon to show in the title bar, next to the title. + + A copy is made internally of the image, so the caller can delete the + image after calling this. If 0 is passed-in, any existing icon will be + removed. + */ + void setIcon (const Image* imageToUse); + + /** Changes the height of the title-bar. */ + void setTitleBarHeight (const int newHeight); + + /** Returns the current title bar height. */ + int getTitleBarHeight() const; + + /** Changes the set of title-bar buttons being shown. + + @param requiredButtons specifies which of the buttons (close, minimise, maximise) + should be shown on the title bar. This value is a bitwise + combination of values from the TitleBarButtons enum. Note + that it can be "allButtons" to get them all. + @param positionTitleBarButtonsOnLeft if true, the buttons should go at the + left side of the bar; if false, they'll be placed at the right + */ + void setTitleBarButtonsRequired (const int requiredButtons, + const bool positionTitleBarButtonsOnLeft); + + /** Sets whether the title should be centred within the window. + + If true, the title text is shown in the middle of the title-bar; if false, + it'll be shown at the left of the bar. + */ + void setTitleBarTextCentred (const bool textShouldBeCentred); + + /** Creates a menu inside this window. + + @param menuBarModel this specifies a MenuBarModel that should be used to + generate the contents of a menu bar that will be placed + just below the title bar, and just above any content + component. If this value is zero, any existing menu bar + will be removed from the component; if non-zero, one will + be added if it's required. + @param menuBarHeight the height of the menu bar component, if one is needed. Pass a value of zero + or less to use the look-and-feel's default size. + */ + void setMenuBar (MenuBarModel* menuBarModel, + const int menuBarHeight = 0); + + /** This method is called when the user tries to close the window. + + This is triggered by the user clicking the close button, or using some other + OS-specific key shortcut or OS menu for getting rid of a window. + + If the window is just a pop-up, you should override this closeButtonPressed() + method and make it delete the window in whatever way is appropriate for your + app. E.g. you might just want to call "delete this". + + If your app is centred around this window such that the whole app should quit when + the window is closed, then you will probably want to use this method as an opportunity + to call JUCEApplication::quit(), and leave the window to be deleted later by your + JUCEApplication::shutdown() method. (Doing it this way means that your window will + still get cleaned-up if the app is quit by some other means (e.g. a cmd-Q on the mac + or closing it via the taskbar icon on Windows). + + (Note that the DocumentWindow class overrides Component::userTriedToCloseWindow() and + redirects it to call this method, so any methods of closing the window that are + caught by userTriedToCloseWindow() will also end up here). + */ + virtual void closeButtonPressed(); + + /** Callback that is triggered when the minimise button is pressed. + + The default implementation of this calls ResizableWindow::setMinimised(), but + you can override it to do more customised behaviour. + */ + virtual void minimiseButtonPressed(); + + /** Callback that is triggered when the maximise button is pressed, or when the + title-bar is double-clicked. + + The default implementation of this calls ResizableWindow::setFullScreen(), but + you can override it to do more customised behaviour. + */ + virtual void maximiseButtonPressed(); + + /** Returns the close button, (or 0 if there isn't one). */ + Button* getCloseButton() const throw(); + + /** Returns the minimise button, (or 0 if there isn't one). */ + Button* getMinimiseButton() const throw(); + + /** Returns the maximise button, (or 0 if there isn't one). */ + Button* getMaximiseButton() const throw(); + + /** @internal */ + void paint (Graphics& g); + /** @internal */ + void resized(); + /** @internal */ + void lookAndFeelChanged(); + /** @internal */ + const BorderSize getBorderThickness(); + /** @internal */ + const BorderSize getContentComponentBorder(); + /** @internal */ + void mouseDoubleClick (const MouseEvent& e); + /** @internal */ + void userTriedToCloseWindow(); + /** @internal */ + void activeWindowStatusChanged(); + /** @internal */ + int getDesktopWindowStyleFlags() const; + /** @internal */ + void parentHierarchyChanged(); + + juce_UseDebuggingNewOperator + +private: + int titleBarHeight, menuBarHeight, requiredButtons; + bool positionTitleBarButtonsOnLeft, drawTitleTextCentred; + Button* titleBarButtons [3]; + Image* titleBarIcon; + MenuBarComponent* menuBar; + MenuBarModel* menuBarModel; + + class ButtonListenerProxy : public ButtonListener + { + public: + ButtonListenerProxy(); + void buttonClicked (Button* button); + + DocumentWindow* owner; + + } buttonListener; + + int getBorderSize() const; + void repaintTitleBar(); + + DocumentWindow (const DocumentWindow&); + const DocumentWindow& operator= (const DocumentWindow&); +}; + +#endif // __JUCE_DOCUMENTWINDOW_JUCEHEADER__ +/********* End of inlined file: juce_DocumentWindow.h *********/ + +class MultiDocumentPanel; +class MDITabbedComponentInternal; + +/** + This is a derivative of DocumentWindow that is used inside a MultiDocumentPanel + component. + + It's like a normal DocumentWindow but has some extra functionality to make sure + everything works nicely inside a MultiDocumentPanel. + + @see MultiDocumentPanel +*/ +class JUCE_API MultiDocumentPanelWindow : public DocumentWindow +{ +public: + + /** + */ + MultiDocumentPanelWindow (const Colour& backgroundColour); + + /** Destructor. */ + ~MultiDocumentPanelWindow(); + + /** @internal */ + void maximiseButtonPressed(); + /** @internal */ + void closeButtonPressed(); + /** @internal */ + void activeWindowStatusChanged(); + /** @internal */ + void broughtToFront(); + + juce_UseDebuggingNewOperator + +private: + void updateOrder(); + MultiDocumentPanel* getOwner() const throw(); +}; + +/** + A component that contains a set of other components either in floating windows + or tabs. + + This acts as a panel that can be used to hold a set of open document windows, with + different layout modes. + + Use addDocument() and closeDocument() to add or remove components from the + panel - never use any of the Component methods to access the panel's child + components directly, as these are managed internally. +*/ +class JUCE_API MultiDocumentPanel : public Component, + private ComponentListener +{ +public: + + /** Creates an empty panel. + + Use addDocument() and closeDocument() to add or remove components from the + panel - never use any of the Component methods to access the panel's child + components directly, as these are managed internally. + */ + MultiDocumentPanel(); + + /** Destructor. + + When deleted, this will call closeAllDocuments (false) to make sure all its + components are deleted. If you need to make sure all documents are saved + before closing, then you should call closeAllDocuments (true) and check that + it returns true before deleting the panel. + */ + ~MultiDocumentPanel(); + + /** Tries to close all the documents. + + If checkItsOkToCloseFirst is true, then the tryToCloseDocument() method will + be called for each open document, and any of these calls fails, this method + will stop and return false, leaving some documents still open. + + If checkItsOkToCloseFirst is false, then all documents will be closed + unconditionally. + + @see closeDocument + */ + bool closeAllDocuments (const bool checkItsOkToCloseFirst); + + /** Adds a document component to the panel. + + If the number of documents would exceed the limit set by setMaximumNumDocuments() then + this will fail and return false. (If it does fail, the component passed-in will not be + deleted, even if deleteWhenRemoved was set to true). + + The MultiDocumentPanel will deal with creating a window border to go around your component, + so just pass in the bare content component here, no need to give it a ResizableWindow + or DocumentWindow. + + @param component the component to add + @param backgroundColour the background colour to use to fill the component's + window or tab + @param deleteWhenRemoved if true, then when the component is removed by closeDocument() + or closeAllDocuments(), then it will be deleted. If false, then + the caller must handle the component's deletion + */ + bool addDocument (Component* const component, + const Colour& backgroundColour, + const bool deleteWhenRemoved); + + /** Closes one of the documents. + + If checkItsOkToCloseFirst is true, then the tryToCloseDocument() method will + be called, and if it fails, this method will return false without closing the + document. + + If checkItsOkToCloseFirst is false, then the documents will be closed + unconditionally. + + The component will be deleted if the deleteWhenRemoved parameter was set to + true when it was added with addDocument. + + @see addDocument, closeAllDocuments + */ + bool closeDocument (Component* component, + const bool checkItsOkToCloseFirst); + + /** Returns the number of open document windows. + + @see getDocument + */ + int getNumDocuments() const throw(); + + /** Returns one of the open documents. + + The order of the documents in this array may change when they are added, removed + or moved around. + + @see getNumDocuments + */ + Component* getDocument (const int index) const throw(); + + /** Returns the document component that is currently focused or on top. + + If currently using floating windows, then this will be the component in the currently + active window, or the top component if none are active. + + If it's currently in tabbed mode, then it'll return the component in the active tab. + + @see setActiveDocument + */ + Component* getActiveDocument() const throw(); + + /** Makes one of the components active and brings it to the top. + + @see getActiveDocument + */ + void setActiveDocument (Component* component); + + /** Callback which gets invoked when the currently-active document changes. */ + virtual void activeDocumentChanged(); + + /** Sets a limit on how many windows can be open at once. + + If this is zero or less there's no limit (the default). addDocument() will fail + if this number is exceeded. + */ + void setMaximumNumDocuments (const int maximumNumDocuments); + + /** Sets an option to make the document fullscreen if there's only one document open. + + If set to true, then if there's only one document, it'll fill the whole of this + component without tabs or a window border. If false, then tabs or a window + will always be shown, even if there's only one document. If there's more than + one document open, then this option makes no difference. + */ + void useFullscreenWhenOneDocument (const bool shouldUseTabs); + + /** Returns the result of the last time useFullscreenWhenOneDocument() was called. + */ + bool isFullscreenWhenOneDocument() const throw(); + + /** The different layout modes available. */ + enum LayoutMode + { + FloatingWindows, /**< In this mode, there are overlapping DocumentWindow components for each document. */ + MaximisedWindowsWithTabs /**< In this mode, a TabbedComponent is used to show one document at a time. */ + }; + + /** Changes the panel's mode. + + @see LayoutMode, getLayoutMode + */ + void setLayoutMode (const LayoutMode newLayoutMode); + + /** Returns the current layout mode. */ + LayoutMode getLayoutMode() const throw() { return mode; } + + /** Sets the background colour for the whole panel. + + Each document has its own background colour, but this is the one used to fill the areas + behind them. + */ + void setBackgroundColour (const Colour& newBackgroundColour); + + /** Returns the current background colour. + + @see setBackgroundColour + */ + const Colour& getBackgroundColour() const throw() { return backgroundColour; } + + /** A subclass must override this to say whether its currently ok for a document + to be closed. + + This method is called by closeDocument() and closeAllDocuments() to indicate that + a document should be saved if possible, ready for it to be closed. + + If this method returns true, then it means the document is ok and can be closed. + + If it returns false, then it means that the closeDocument() method should stop + and not close. + + Normally, you'd use this method to ask the user if they want to save any changes, + then return true if the save operation went ok. If the user cancelled the save + operation you could return false here to abort the close operation. + + If your component is based on the FileBasedDocument class, then you'd probably want + to call FileBasedDocument::saveIfNeededAndUserAgrees() and return true if this returned + FileBasedDocument::savedOk + + @see closeDocument, FileBasedDocument::saveIfNeededAndUserAgrees() + */ + virtual bool tryToCloseDocument (Component* component) = 0; + + /** Creates a new window to be used for a document. + + The default implementation of this just returns a basic MultiDocumentPanelWindow object, + but you might want to override it to return a custom component. + */ + virtual MultiDocumentPanelWindow* createNewDocumentWindow(); + + /** @internal */ + void paint (Graphics& g); + /** @internal */ + void resized(); + /** @internal */ + void componentNameChanged (Component&); + + juce_UseDebuggingNewOperator + +private: + LayoutMode mode; + Array components; + TabbedComponent* tabComponent; + Colour backgroundColour; + int maximumNumDocuments, numDocsBeforeTabsUsed; + + friend class MultiDocumentPanelWindow; + friend class MDITabbedComponentInternal; + + Component* getContainerComp (Component* c) const; + void updateOrder(); + + void addWindow (Component* component); +}; + +#endif // __JUCE_MULTIDOCUMENTPANEL_JUCEHEADER__ +/********* End of inlined file: juce_MultiDocumentPanel.h *********/ + +#endif +#ifndef __JUCE_RESIZABLEBORDERCOMPONENT_JUCEHEADER__ + +#endif +#ifndef __JUCE_RESIZABLECORNERCOMPONENT_JUCEHEADER__ + +#endif +#ifndef __JUCE_SCROLLBAR_JUCEHEADER__ + +#endif +#ifndef __JUCE_STRETCHABLELAYOUTMANAGER_JUCEHEADER__ + +/********* Start of inlined file: juce_StretchableLayoutManager.h *********/ +#ifndef __JUCE_STRETCHABLELAYOUTMANAGER_JUCEHEADER__ +#define __JUCE_STRETCHABLELAYOUTMANAGER_JUCEHEADER__ + +/** + For laying out a set of components, where the components have preferred sizes + and size limits, but where they are allowed to stretch to fill the available + space. + + For example, if you have a component containing several other components, and + each one should be given a share of the total size, you could use one of these + to resize the child components when the parent component is resized. Then + you could add a StretchableLayoutResizerBar to easily let the user rescale them. + + A StretchableLayoutManager operates only in one dimension, so if you have a set + of components stacked vertically on top of each other, you'd use one to manage their + heights. To build up complex arrangements of components, e.g. for applications + with multiple nested panels, you would use more than one StretchableLayoutManager. + E.g. by using two (one vertical, one horizontal), you could create a resizable + spreadsheet-style table. + + E.g. + @code + class MyComp : public Component + { + StretchableLayoutManager myLayout; + + MyComp() + { + myLayout.setItemLayout (0, // for item 0 + 50, 100, // must be between 50 and 100 pixels in size + -0.6); // and its preferred size is 60% of the total available space + + myLayout.setItemLayout (1, // for item 1 + -0.2, -0.6, // size must be between 20% and 60% of the available space + 50); // and its preferred size is 50 pixels + } + + void resized() + { + // make a list of two of our child components that we want to reposition + Component* comps[] = { myComp1, myComp2 }; + + // this will position the 2 components, one above the other, to fit + // vertically into the rectangle provided. + myLayout.layOutComponents (comps, 2, + 0, 0, getWidth(), getHeight(), + true); + } + }; + @endcode + + @see StretchableLayoutResizerBar +*/ +class JUCE_API StretchableLayoutManager +{ +public: + + /** Creates an empty layout. + + You'll need to add some item properties to the layout before it can be used + to resize things - see setItemLayout(). + */ + StretchableLayoutManager(); + + /** Destructor. */ + ~StretchableLayoutManager(); + + /** For a numbered item, this sets its size limits and preferred size. + + @param itemIndex the index of the item to change. + @param minimumSize the minimum size that this item is allowed to be - a positive number + indicates an absolute size in pixels. A negative number indicates a + proportion of the available space (e.g -0.5 is 50%) + @param maximumSize the maximum size that this item is allowed to be - a positive number + indicates an absolute size in pixels. A negative number indicates a + proportion of the available space + @param preferredSize the size that this item would like to be, if there's enough room. A + positive number indicates an absolute size in pixels. A negative number + indicates a proportion of the available space + @see getItemLayout + */ + void setItemLayout (const int itemIndex, + const double minimumSize, + const double maximumSize, + const double preferredSize); + + /** For a numbered item, this returns its size limits and preferred size. + + @param itemIndex the index of the item. + @param minimumSize the minimum size that this item is allowed to be - a positive number + indicates an absolute size in pixels. A negative number indicates a + proportion of the available space (e.g -0.5 is 50%) + @param maximumSize the maximum size that this item is allowed to be - a positive number + indicates an absolute size in pixels. A negative number indicates a + proportion of the available space + @param preferredSize the size that this item would like to be, if there's enough room. A + positive number indicates an absolute size in pixels. A negative number + indicates a proportion of the available space + @returns false if the item's properties hadn't been set + @see setItemLayout + */ + bool getItemLayout (const int itemIndex, + double& minimumSize, + double& maximumSize, + double& preferredSize) const; + + /** Clears all the properties that have been set with setItemLayout() and resets + this object to its initial state. + */ + void clearAllItems(); + + /** Takes a set of components that correspond to the layout's items, and positions + them to fill a space. + + This will try to give each item its preferred size, whether that's a relative size + or an absolute one. + + @param components an array of components that correspond to each of the + numbered items that the StretchableLayoutManager object + has been told about with setItemLayout() + @param numComponents the number of components in the array that is passed-in. This + should be the same as the number of items this object has been + told about. + @param x the left of the rectangle in which the components should + be laid out + @param y the top of the rectangle in which the components should + be laid out + @param width the width of the rectangle in which the components should + be laid out + @param height the height of the rectangle in which the components should + be laid out + @param vertically if true, the components will be positioned in a vertical stack, + so that they fill the height of the rectangle. If false, they + will be placed side-by-side in a horizontal line, filling the + available width + @param resizeOtherDimension if true, this means that the components will have their + other dimension resized to fit the space - i.e. if the 'vertically' + parameter is true, their x-positions and widths are adjusted to fit + the x and width parameters; if 'vertically' is false, their y-positions + and heights are adjusted to fit the y and height parameters. + */ + void layOutComponents (Component** const components, + int numComponents, + int x, int y, int width, int height, + const bool vertically, + const bool resizeOtherDimension); + + /** Returns the current position of one of the items. + + This is only a valid call after layOutComponents() has been called, as it + returns the last position that this item was placed at. If the layout was + vertical, the value returned will be the y position of the top of the item, + relative to the top of the rectangle in which the items were placed (so for + example, item 0 will always have position of 0, even in the rectangle passed + in to layOutComponents() wasn't at y = 0). If the layout was done horizontally, + the position returned is the item's left-hand position, again relative to the + x position of the rectangle used. + + @see getItemCurrentSize, setItemPosition + */ + int getItemCurrentPosition (const int itemIndex) const; + + /** Returns the current size of one of the items. + + This is only meaningful after layOutComponents() has been called, as it + returns the last size that this item was given. If the layout was done + vertically, it'll return the item's height in pixels; if it was horizontal, + it'll return its width. + + @see getItemCurrentRelativeSize + */ + int getItemCurrentAbsoluteSize (const int itemIndex) const; + + /** Returns the current size of one of the items. + + This is only meaningful after layOutComponents() has been called, as it + returns the last size that this item was given. If the layout was done + vertically, it'll return a negative value representing the item's height relative + to the last size used for laying the components out; if the layout was done + horizontally it'll be the proportion of its width. + + @see getItemCurrentAbsoluteSize + */ + double getItemCurrentRelativeSize (const int itemIndex) const; + + /** Moves one of the items, shifting along any other items as necessary in + order to get it to the desired position. + + Calling this method will also update the preferred sizes of the items it + shuffles along, so that they reflect their new positions. + + (This is the method that a StretchableLayoutResizerBar uses to shift the items + about when it's dragged). + + @param itemIndex the item to move + @param newPosition the absolute position that you'd like this item to move + to. The item might not be able to always reach exactly this position, + because other items may have minimum sizes that constrain how + far it can go + */ + void setItemPosition (const int itemIndex, + int newPosition); + + juce_UseDebuggingNewOperator + +private: + struct ItemLayoutProperties + { + int itemIndex; + int currentSize; + double minSize, maxSize, preferredSize; + }; + + OwnedArray items; + int totalSize; + + static int sizeToRealSize (double size, int totalSpace); + + ItemLayoutProperties* getInfoFor (const int itemIndex) const; + + void setTotalSize (const int newTotalSize); + + int fitComponentsIntoSpace (const int startIndex, + const int endIndex, + const int availableSpace, + int startPos); + + int getMinimumSizeOfItems (const int startIndex, const int endIndex) const; + int getMaximumSizeOfItems (const int startIndex, const int endIndex) const; + + void updatePrefSizesToMatchCurrentPositions(); + + StretchableLayoutManager (const StretchableLayoutManager&); + const StretchableLayoutManager& operator= (const StretchableLayoutManager&); +}; + +#endif // __JUCE_STRETCHABLELAYOUTMANAGER_JUCEHEADER__ +/********* End of inlined file: juce_StretchableLayoutManager.h *********/ + +#endif +#ifndef __JUCE_STRETCHABLELAYOUTRESIZERBAR_JUCEHEADER__ + +/********* Start of inlined file: juce_StretchableLayoutResizerBar.h *********/ +#ifndef __JUCE_STRETCHABLELAYOUTRESIZERBAR_JUCEHEADER__ +#define __JUCE_STRETCHABLELAYOUTRESIZERBAR_JUCEHEADER__ + +/** + A component that acts as one of the vertical or horizontal bars you see being + used to resize panels in a window. + + One of these acts with a StretchableLayoutManager to resize the other components. + + @see StretchableLayoutManager +*/ +class JUCE_API StretchableLayoutResizerBar : public Component +{ +public: + + /** Creates a resizer bar for use on a specified layout. + + @param layoutToUse the layout that will be affected when this bar + is dragged + @param itemIndexInLayout the item index in the layout that corresponds to + this bar component. You'll need to set up the item + properties in a suitable way for a divider bar, e.g. + for an 8-pixel wide bar which, you could call + myLayout->setItemLayout (barIndex, 8, 8, 8) + @param isBarVertical true if it's an upright bar that you drag left and + right; false for a horizontal one that you drag up and + down + */ + StretchableLayoutResizerBar (StretchableLayoutManager* const layoutToUse, + const int itemIndexInLayout, + const bool isBarVertical); + + /** Destructor. */ + ~StretchableLayoutResizerBar(); + + /** This is called when the bar is dragged. + + This method must update the positions of any components whose position is + determined by the StretchableLayoutManager, because they might have just + moved. + + The default implementation calls the resized() method of this component's + parent component, because that's often where you're likely to apply the + layout, but it can be overridden for more specific needs. + */ + virtual void hasBeenMoved(); + + /** @internal */ + void paint (Graphics& g); + /** @internal */ + void mouseDown (const MouseEvent& e); + /** @internal */ + void mouseDrag (const MouseEvent& e); + + juce_UseDebuggingNewOperator + +private: + StretchableLayoutManager* layout; + int itemIndex, mouseDownPos; + bool isVertical; + + StretchableLayoutResizerBar (const StretchableLayoutResizerBar&); + const StretchableLayoutResizerBar& operator= (const StretchableLayoutResizerBar&); +}; + +#endif // __JUCE_STRETCHABLELAYOUTRESIZERBAR_JUCEHEADER__ +/********* End of inlined file: juce_StretchableLayoutResizerBar.h *********/ + +#endif +#ifndef __JUCE_STRETCHABLEOBJECTRESIZER_JUCEHEADER__ + +/********* Start of inlined file: juce_StretchableObjectResizer.h *********/ +#ifndef __JUCE_STRETCHABLEOBJECTRESIZER_JUCEHEADER__ +#define __JUCE_STRETCHABLEOBJECTRESIZER_JUCEHEADER__ + +/** + A utility class for fitting a set of objects whose sizes can vary between + a minimum and maximum size, into a space. + + This is a trickier algorithm than it would first seem, so I've put it in this + class to allow it to be shared by various bits of code. + + To use it, create one of these objects, call addItem() to add the list of items + you need, then call resizeToFit(), which will change all their sizes. You can + then retrieve the new sizes with getItemSize() and getNumItems(). + + It's currently used by the TableHeaderComponent for stretching out the table + headings to fill the table's width. +*/ +class StretchableObjectResizer +{ +public: + + /** Creates an empty object resizer. */ + StretchableObjectResizer(); + + /** Destructor. */ + ~StretchableObjectResizer(); + + /** Adds an item to the list. + + The order parameter lets you specify groups of items that are resized first when some + space needs to be found. Those items with an order of 0 will be the first ones to be + resized, and if that doesn't provide enough space to meet the requirements, the algorithm + will then try resizing the items with an order of 1, then 2, and so on. + */ + void addItem (const double currentSize, + const double minSize, + const double maxSize, + const int order = 0); + + /** Resizes all the items to fit this amount of space. + + This will attempt to fit them in without exceeding each item's miniumum and + maximum sizes. In cases where none of the items can be expanded or enlarged any + further, the final size may be greater or less than the size passed in. + + After calling this method, you can retrieve the new sizes with the getItemSize() + method. + */ + void resizeToFit (const double targetSize); + + /** Returns the number of items that have been added. */ + int getNumItems() const throw() { return items.size(); } + + /** Returns the size of one of the items. */ + double getItemSize (const int index) const throw(); + + juce_UseDebuggingNewOperator + +private: + struct Item + { + double size; + double minSize; + double maxSize; + int order; + }; + + OwnedArray items; + + StretchableObjectResizer (const StretchableObjectResizer&); + const StretchableObjectResizer& operator= (const StretchableObjectResizer&); +}; + +#endif // __JUCE_STRETCHABLEOBJECTRESIZER_JUCEHEADER__ +/********* End of inlined file: juce_StretchableObjectResizer.h *********/ + +#endif +#ifndef __JUCE_TABBEDBUTTONBAR_JUCEHEADER__ + +#endif +#ifndef __JUCE_TABBEDCOMPONENT_JUCEHEADER__ + +#endif +#ifndef __JUCE_VIEWPORT_JUCEHEADER__ + +#endif +#ifndef __JUCE_DIRECTORYCONTENTSDISPLAYCOMPONENT_JUCEHEADER__ + +/********* Start of inlined file: juce_DirectoryContentsDisplayComponent.h *********/ +#ifndef __JUCE_DIRECTORYCONTENTSDISPLAYCOMPONENT_JUCEHEADER__ +#define __JUCE_DIRECTORYCONTENTSDISPLAYCOMPONENT_JUCEHEADER__ + +/********* Start of inlined file: juce_DirectoryContentsList.h *********/ +#ifndef __JUCE_DIRECTORYCONTENTSLIST_JUCEHEADER__ +#define __JUCE_DIRECTORYCONTENTSLIST_JUCEHEADER__ + +/********* Start of inlined file: juce_FileFilter.h *********/ +#ifndef __JUCE_FILEFILTER_JUCEHEADER__ +#define __JUCE_FILEFILTER_JUCEHEADER__ + +/** + Interface for deciding which files are suitable for something. + + For example, this is used by DirectoryContentsList to select which files + go into the list. + + @see WildcardFileFilter, DirectoryContentsList, FileListComponent, FileBrowserComponent +*/ +class JUCE_API FileFilter +{ +public: + + /** Creates a filter with the given description. + + The description can be returned later with the getDescription() method. + */ + FileFilter (const String& filterDescription); + + /** Destructor. */ + virtual ~FileFilter(); + + /** Returns the description that the filter was created with. */ + const String& getDescription() const throw(); + + /** Should return true if this file is suitable for inclusion in whatever context + the object is being used. + */ + virtual bool isFileSuitable (const File& file) const = 0; + + /** Should return true if this directory is suitable for inclusion in whatever context + the object is being used. + */ + virtual bool isDirectorySuitable (const File& file) const = 0; + +protected: + + String description; +}; + +#endif // __JUCE_FILEFILTER_JUCEHEADER__ +/********* End of inlined file: juce_FileFilter.h *********/ + +/** + A class to asynchronously scan for details about the files in a directory. + + This keeps a list of files and some information about them, using a background + thread to scan for more files. As files are found, it broadcasts change messages + to tell any listeners. + + @see FileListComponent, FileBrowserComponent +*/ +class JUCE_API DirectoryContentsList : public ChangeBroadcaster, + public TimeSliceClient +{ +public: + + /** Creates a directory list. + + To set the directory it should point to, use setDirectory(), which will + also start it scanning for files on the background thread. + + When the background thread finds and adds new files to this list, the + ChangeBroadcaster class will send a change message, so you can register + listeners and update them when the list changes. + + @param fileFilter an optional filter to select which files are + included in the list. If this is 0, then all files + and directories are included. Make sure that the + filter doesn't get deleted during the lifetime of this + object + @param threadToUse a thread object that this list can use + to scan for files as a background task. Make sure + that the thread you give it has been started, or you + won't get any files! + */ + DirectoryContentsList (const FileFilter* const fileFilter, + TimeSliceThread& threadToUse); + + /** Destructor. */ + ~DirectoryContentsList(); + + /** Sets the directory to look in for files. + + If the directory that's passed in is different to the current one, this will + also start the background thread scanning it for files. + */ + void setDirectory (const File& directory, + const bool includeDirectories, + const bool includeFiles); + + /** Returns the directory that's currently being used. */ + const File& getDirectory() const throw(); + + /** Clears the list, and stops the thread scanning for files. */ + void clear(); + + /** Clears the list and restarts scanning the directory for files. */ + void refresh(); + + /** True if the background thread hasn't yet finished scanning for files. */ + bool isStillLoading() const; + + /** Tells the list whether or not to ignore hidden files. + + By default these are ignored. + */ + void setIgnoresHiddenFiles (const bool shouldIgnoreHiddenFiles); + + /** Contains cached information about one of the files in a DirectoryContentsList. + */ + struct FileInfo + { + + /** The filename. + + This isn't a full pathname, it's just the last part of the path, same as you'd + get from File::getFileName(). + + To get the full pathname, use DirectoryContentsList::getDirectory().getChildFile (filename). + */ + String filename; + + /** File size in bytes. */ + int64 fileSize; + + /** File modification time. + + As supplied by File::getLastModificationTime(). + */ + Time modificationTime; + + /** File creation time. + + As supplied by File::getCreationTime(). + */ + Time creationTime; + + /** True if the file is a directory. */ + bool isDirectory; + + /** True if the file is read-only. */ + bool isReadOnly; + }; + + /** Returns the number of files currently available in the list. + + The info about one of these files can be retrieved with getFileInfo() or + getFile(). + + Obviously as the background thread runs and scans the directory for files, this + number will change. + + @see getFileInfo, getFile + */ + int getNumFiles() const; + + /** Returns the cached information about one of the files in the list. + + If the index is in-range, this will return true and will copy the file's details + to the structure that is passed-in. + + If it returns false, then the index wasn't in range, and the structure won't + be affected. + + @see getNumFiles, getFile + */ + bool getFileInfo (const int index, + FileInfo& resultInfo) const; + + /** Returns one of the files in the list. + + @param index should be less than getNumFiles(). If this is out-of-range, the + return value will be File::nonexistent + @see getNumFiles, getFileInfo + */ + const File getFile (const int index) const; + + /** Returns the file filter being used. + + The filter is specified in the constructor. + */ + const FileFilter* getFilter() const throw() { return fileFilter; } + + /** @internal */ + bool useTimeSlice(); + /** @internal */ + TimeSliceThread& getTimeSliceThread() throw() { return thread; } + /** @internal */ + static int compareElements (const DirectoryContentsList::FileInfo* const first, + const DirectoryContentsList::FileInfo* const second) throw(); + + juce_UseDebuggingNewOperator + +private: + File root; + const FileFilter* fileFilter; + TimeSliceThread& thread; + bool includeDirectories, includeFiles, ignoreHiddenFiles; + + CriticalSection fileListLock; + OwnedArray files; + + void* volatile fileFindHandle; + bool volatile shouldStop; + + void changed(); + bool checkNextFile (bool& hasChanged); + bool addFile (const String& filename, const bool isDir, const bool isHidden, + const int64 fileSize, const Time& modTime, + const Time& creationTime, const bool isReadOnly); + + DirectoryContentsList (const DirectoryContentsList&); + const DirectoryContentsList& operator= (const DirectoryContentsList&); +}; + +#endif // __JUCE_DIRECTORYCONTENTSLIST_JUCEHEADER__ +/********* End of inlined file: juce_DirectoryContentsList.h *********/ + +/********* Start of inlined file: juce_FileBrowserListener.h *********/ +#ifndef __JUCE_FILEBROWSERLISTENER_JUCEHEADER__ +#define __JUCE_FILEBROWSERLISTENER_JUCEHEADER__ + +/** + A listener for user selection events in a file browser. + + This is used by a FileBrowserComponent or FileListComponent. +*/ +class JUCE_API FileBrowserListener +{ +public: + + /** Destructor. */ + virtual ~FileBrowserListener(); + + /** Callback when the user selects a different file in the browser. */ + virtual void selectionChanged() = 0; + + /** Callback when the user clicks on a file in the browser. */ + virtual void fileClicked (const File& file, const MouseEvent& e) = 0; + + /** Callback when the user double-clicks on a file in the browser. */ + virtual void fileDoubleClicked (const File& file) = 0; +}; + +#endif // __JUCE_FILEBROWSERLISTENER_JUCEHEADER__ +/********* End of inlined file: juce_FileBrowserListener.h *********/ + +/** + A base class for components that display a list of the files in a directory. + + @see DirectoryContentsList +*/ +class JUCE_API DirectoryContentsDisplayComponent +{ +public: + + /** + */ + DirectoryContentsDisplayComponent (DirectoryContentsList& listToShow); + + /** Destructor. */ + virtual ~DirectoryContentsDisplayComponent(); + + /** Returns the file that the user has currently selected. + + Returns File::nonexistent if none is selected. + */ + virtual const File getSelectedFile() const = 0; + + /** Scrolls this view to the top. */ + virtual void scrollToTop() = 0; + + /** Adds a listener to be told when files are selected or clicked. + + @see removeListener + */ + void addListener (FileBrowserListener* const listener) throw(); + + /** Removes a listener. + + @see addListener + */ + void removeListener (FileBrowserListener* const listener) throw(); + + /** A set of colour IDs to use to change the colour of various aspects of the label. + + These constants can be used either via the Component::setColour(), or LookAndFeel::setColour() + methods. + + Note that you can also use the constants from TextEditor::ColourIds to change the + colour of the text editor that is opened when a label is editable. + + @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour + */ + enum ColourIds + { + highlightColourId = 0x1000540, /**< The colour to use to fill a highlighted row of the list. */ + textColourId = 0x1000541, /**< The colour for the text. */ + }; + + /** @internal */ + void sendSelectionChangeMessage(); + /** @internal */ + void sendDoubleClickMessage (const File& file); + /** @internal */ + void sendMouseClickMessage (const File& file, const MouseEvent& e); + + juce_UseDebuggingNewOperator + +protected: + DirectoryContentsList& fileList; + SortedSet listeners; + + DirectoryContentsDisplayComponent (const DirectoryContentsDisplayComponent&); + const DirectoryContentsDisplayComponent& operator= (const DirectoryContentsDisplayComponent&); +}; + +#endif // __JUCE_DIRECTORYCONTENTSDISPLAYCOMPONENT_JUCEHEADER__ +/********* End of inlined file: juce_DirectoryContentsDisplayComponent.h *********/ + +#endif +#ifndef __JUCE_DIRECTORYCONTENTSLIST_JUCEHEADER__ + +#endif +#ifndef __JUCE_FILEBROWSERCOMPONENT_JUCEHEADER__ + +/********* Start of inlined file: juce_FileBrowserComponent.h *********/ +#ifndef __JUCE_FILEBROWSERCOMPONENT_JUCEHEADER__ +#define __JUCE_FILEBROWSERCOMPONENT_JUCEHEADER__ + +/********* Start of inlined file: juce_FilePreviewComponent.h *********/ +#ifndef __JUCE_FILEPREVIEWCOMPONENT_JUCEHEADER__ +#define __JUCE_FILEPREVIEWCOMPONENT_JUCEHEADER__ + +/** + Base class for components that live inside a file chooser dialog box and + show previews of the files that get selected. + + One of these allows special extra information to be displayed for files + in a dialog box as the user selects them. Each time the current file or + directory is changed, the selectedFileChanged() method will be called + to allow it to update itself appropriately. + + @see FileChooser, ImagePreviewComponent +*/ +class JUCE_API FilePreviewComponent : public Component +{ +public: + + /** Creates a FilePreviewComponent. */ + FilePreviewComponent(); + + /** Destructor. */ + ~FilePreviewComponent(); + + /** Called to indicate that the user's currently selected file has changed. + + @param newSelectedFile the newly selected file or directory, which may be + File::nonexistent if none is selected. + */ + virtual void selectedFileChanged (const File& newSelectedFile) = 0; + + juce_UseDebuggingNewOperator + +private: + FilePreviewComponent (const FilePreviewComponent&); + const FilePreviewComponent& operator= (const FilePreviewComponent&); +}; + +#endif // __JUCE_FILEPREVIEWCOMPONENT_JUCEHEADER__ +/********* End of inlined file: juce_FilePreviewComponent.h *********/ + +/** + A component for browsing and selecting a file or directory to open or save. + + This contains a FileListComponent and adds various boxes and controls for + navigating and selecting a file. It can work in different modes so that it can + be used for loading or saving a file, or for choosing a directory. + + @see FileChooserDialogBox, FileChooser, FileListComponent +*/ +class JUCE_API FileBrowserComponent : public Component, + public ChangeBroadcaster, + private FileBrowserListener, + private TextEditorListener, + private ButtonListener, + private ComboBoxListener +{ +public: + + /** Various modes that the browser can be used in. + + One of these is passed into the constructor. + */ + enum FileChooserMode + { + loadFileMode, /**< the component should allow the user to choose an existing + file with the intention of opening it. */ + saveFileMode, /**< the component should allow the user to specify the name of + a file that will be used to save something. */ + chooseDirectoryMode /**< the component should allow the user to select an existing + directory. */ + }; + + /** Creates a FileBrowserComponent. + + @param browserMode The intended purpose for the browser - see the + FileChooserMode enum for the various options + @param initialFileOrDirectory The file or directory that should be selected when + the component begins. If this is File::nonexistent, + a default directory will be chosen. + @param fileFilter an optional filter to use to determine which files + are shown. If this is 0 then all files are displayed. Note + that a pointer is kept internally to this object, so + make sure that it is not deleted before the browser object + is deleted. + @param previewComp an optional preview component that will be used to + show previews of files that the user selects + @param useTreeView if this is false, the files are shown in a list; if true, + they are shown in a treeview + @param filenameTextBoxIsReadOnly if true, the user won't be allowed to type their own + text into the filename box. + */ + FileBrowserComponent (FileChooserMode browserMode, + const File& initialFileOrDirectory, + const FileFilter* fileFilter, + FilePreviewComponent* previewComp, + const bool useTreeView = false, + const bool filenameTextBoxIsReadOnly = false); + + /** Destructor. */ + ~FileBrowserComponent(); + + /** + */ + const File getCurrentFile() const throw(); + + /** Returns true if the current file is usable. + + This can be used to decide whether the user can press "ok" for the + current file. What it does depends on the mode, so for example in an "open" + mode, the current file is only valid if one has been selected and if the file + exists. In a "save" mode, a non-existent file would also be valid. + */ + bool currentFileIsValid() const; + + /** Returns the directory whose contents are currently being shown in the listbox. */ + const File getRoot() const; + + /** Changes the directory that's being shown in the listbox. */ + void setRoot (const File& newRootDirectory); + + /** Equivalent to pressing the "up" button to browse the parent directory. */ + void goUp(); + + /** Refreshes the directory that's currently being listed. */ + void refresh(); + + /** Returns the browser's current mode. */ + FileChooserMode getMode() const throw() { return mode; } + + /** Returns a verb to describe what should happen when the file is accepted. + + E.g. if browsing in "load file" mode, this will be "Open", if in "save file" + mode, it'll be "Save", etc. + */ + virtual const String getActionVerb() const; + + /** Adds a listener to be told when the user selects and clicks on files. + + @see removeListener + */ + void addListener (FileBrowserListener* const listener) throw(); + + /** Removes a listener. + + @see addListener + */ + void removeListener (FileBrowserListener* const listener) throw(); + + /** @internal */ + void resized(); + /** @internal */ + void buttonClicked (Button* b); + /** @internal */ + void comboBoxChanged (ComboBox*); + + /** @internal */ + void textEditorTextChanged (TextEditor& editor); + /** @internal */ + void textEditorReturnKeyPressed (TextEditor& editor); + /** @internal */ + void textEditorEscapeKeyPressed (TextEditor& editor); + /** @internal */ + void textEditorFocusLost (TextEditor& editor); + + /** @internal */ + void selectionChanged(); + /** @internal */ + void fileClicked (const File& f, const MouseEvent& e); + /** @internal */ + void fileDoubleClicked (const File& f); + + /** @internal */ + FilePreviewComponent* getPreviewComponent() const throw(); + + juce_UseDebuggingNewOperator + +protected: + virtual const BitArray getRoots (StringArray& rootNames, StringArray& rootPaths); + +private: + + DirectoryContentsList* fileList; + FileFilter* directoriesOnlyFilter; + + FileChooserMode mode; + File currentRoot; + SortedSet listeners; + + DirectoryContentsDisplayComponent* fileListComponent; + FilePreviewComponent* previewComp; + ComboBox* currentPathBox; + TextEditor* filenameBox; + Button* goUpButton; + + TimeSliceThread thread; + + void sendListenerChangeMessage(); + + FileBrowserComponent (const FileBrowserComponent&); + const FileBrowserComponent& operator= (const FileBrowserComponent&); +}; + +#endif // __JUCE_FILEBROWSERCOMPONENT_JUCEHEADER__ +/********* End of inlined file: juce_FileBrowserComponent.h *********/ + +#endif +#ifndef __JUCE_FILEBROWSERLISTENER_JUCEHEADER__ + +#endif +#ifndef __JUCE_FILECHOOSER_JUCEHEADER__ + +/********* Start of inlined file: juce_FileChooser.h *********/ +#ifndef __JUCE_FILECHOOSER_JUCEHEADER__ +#define __JUCE_FILECHOOSER_JUCEHEADER__ + +/** + Creates a dialog box to choose a file or directory to load or save. + + To use a FileChooser: + - create one (as a local stack variable is the neatest way) + - call one of its browseFor.. methods + - if this returns true, the user has selected a file, so you can retrieve it + with the getResult() method. + + e.g. @code + void loadMooseFile() + { + FileChooser myChooser ("Please select the moose you want to load...", + File::getSpecialLocation (File::userHomeDirectory), + "*.moose"); + + if (myChooser.browseForFileToOpen()) + { + File mooseFile (myChooser.getResult()); + + loadMoose (mooseFile); + } + } + @endcode +*/ +class JUCE_API FileChooser +{ +public: + + /** Creates a FileChooser. + + After creating one of these, use one of the browseFor... methods to display it. + + @param dialogBoxTitle a text string to display in the dialog box to + tell the user what's going on + @param initialFileOrDirectory the file or directory that should be selected when + the dialog box opens. If this parameter is set to + File::nonexistent, a sensible default directory + will be used instead. + @param filePatternsAllowed a set of file patterns to specify which files can be + selected - each pattern should be separated by a + comma or semi-colon, e.g. "*" or "*.jpg;*.gif". An + empty string means that all files are allowed + @param useOSNativeDialogBox if true, then a native dialog box will be used if + possible; if false, then a Juce-based browser dialog + box will always be used + @see browseForFileToOpen, browseForFileToSave, browseForDirectory + */ + FileChooser (const String& dialogBoxTitle, + const File& initialFileOrDirectory = File::nonexistent, + const String& filePatternsAllowed = String::empty, + const bool useOSNativeDialogBox = true); + + /** Destructor. */ + ~FileChooser(); + + /** Shows a dialog box to choose a file to open. + + This will display the dialog box modally, using an "open file" mode, so that + it won't allow non-existent files or directories to be chosen. + + @param previewComponent an optional component to display inside the dialog + box to show special info about the files that the user + is browsing. The component will not be deleted by this + object, so the caller must take care of it. + @returns true if the user selected a file, in which case, use the getResult() + method to find out what it was. Returns false if they cancelled instead. + @see browseForFileToSave, browseForDirectory + */ + bool browseForFileToOpen (FilePreviewComponent* previewComponent = 0); + + /** Same as browseForFileToOpen, but allows the user to select multiple files. + + The files that are returned can be obtained by calling getResults(). See + browseForFileToOpen() for more info about the behaviour of this method. + */ + bool browseForMultipleFilesToOpen (FilePreviewComponent* previewComponent = 0); + + /** Shows a dialog box to choose a file to save. + + This will display the dialog box modally, using an "save file" mode, so it + will allow non-existent files to be chosen, but not directories. + + @param warnAboutOverwritingExistingFiles if true, the dialog box will ask + the user if they're sure they want to overwrite a file that already + exists + @returns true if the user chose a file and pressed 'ok', in which case, use + the getResult() method to find out what the file was. Returns false + if they cancelled instead. + @see browseForFileToOpen, browseForDirectory + */ + bool browseForFileToSave (const bool warnAboutOverwritingExistingFiles); + + /** Shows a dialog box to choose a directory. + + This will display the dialog box modally, using an "open directory" mode, so it + will only allow directories to be returned, not files. + + @returns true if the user chose a directory and pressed 'ok', in which case, use + the getResult() method to find out what they chose. Returns false + if they cancelled instead. + @see browseForFileToOpen, browseForFileToSave + */ + bool browseForDirectory(); + + /** Returns the last file that was chosen by one of the browseFor methods. + + After calling the appropriate browseFor... method, this method lets you + find out what file or directory they chose. + + Note that the file returned is only valid if the browse method returned true (i.e. + if the user pressed 'ok' rather than cancelling). + + If you're using a multiple-file select, then use the getResults() method instead, + to obtain the list of all files chosen. + + @see getResults + */ + const File getResult() const; + + /** Returns a list of all the files that were chosen during the last call to a + browse method. + + This array may be empty if no files were chosen, or can contain multiple entries + if multiple files were chosen. + + @see getResult + */ + const OwnedArray & getResults() const; + + juce_UseDebuggingNewOperator + +private: + String title, filters; + File startingFile; + OwnedArray results; + bool useNativeDialogBox; + + bool showDialog (const bool isDirectory, + const bool isSave, + const bool warnAboutOverwritingExistingFiles, + const bool selectMultipleFiles, + FilePreviewComponent* const previewComponent); + + static void showPlatformDialog (OwnedArray& results, + const String& title, + const File& file, + const String& filters, + bool isDirectory, + bool isSave, + bool warnAboutOverwritingExistingFiles, + bool selectMultipleFiles, + FilePreviewComponent* previewComponent); +}; + +#endif // __JUCE_FILECHOOSER_JUCEHEADER__ +/********* End of inlined file: juce_FileChooser.h *********/ + +#endif +#ifndef __JUCE_FILECHOOSERDIALOGBOX_JUCEHEADER__ + +/********* Start of inlined file: juce_FileChooserDialogBox.h *********/ +#ifndef __JUCE_FILECHOOSERDIALOGBOX_JUCEHEADER__ +#define __JUCE_FILECHOOSERDIALOGBOX_JUCEHEADER__ + +/** + A file open/save dialog box. + + This is a Juce-based file dialog box; to use a native file chooser, see the + FileChooser class. + + To use one of these, create it and call its show() method. e.g. + + @code + { + WildcardFileFilter wildcardFilter (T("*.foo"), T("Foo files")); + + FileBrowserComponent browser (FileBrowserComponent::loadFileMode, + File::nonexistent, + &wildcardFilter, + 0); + + FileChooserDialogBox dialogBox (T("Open some kind of file"), + T("Please choose some kind of file that you want to open..."), + browser, + getLookAndFeel().alertWindowBackground); + + if (dialogBox.show()) + { + File selectedFile = browser.getCurrentFile(); + ... + } + } + @endcode + + @see FileChooser +*/ +class JUCE_API FileChooserDialogBox : public ResizableWindow, + public ButtonListener, + public FileBrowserListener +{ +public: + + /** Creates a file chooser box. + + @param title the main title to show at the top of the box + @param instructions an optional longer piece of text to show below the title in + a smaller font, describing in more detail what's required. + @param browserComponent a FileBrowserComponent that will be shown inside this dialog + box. Make sure you delete this after (but not before!) the + dialog box has been deleted. + @param warnAboutOverwritingExistingFiles if true, then the user will be asked to confirm + if they try to select a file that already exists. (This + flag is only used when saving files) + @param backgroundColour the background colour for the top level window + + @see FileBrowserComponent, FilePreviewComponent + */ + FileChooserDialogBox (const String& title, + const String& instructions, + FileBrowserComponent& browserComponent, + const bool warnAboutOverwritingExistingFiles, + const Colour& backgroundColour); + + /** Destructor. */ + ~FileChooserDialogBox(); + + /** Displays and runs the dialog box modally. + + This will show the box with the specified size, returning true if the user + pressed 'ok', or false if they cancelled. + + Leave the width or height as 0 to use the default size + */ + bool show (int width = 0,int height = 0); + + /** @internal */ + void buttonClicked (Button* button); + /** @internal */ + void closeButtonPressed(); + /** @internal */ + void selectionChanged(); + /** @internal */ + void fileClicked (const File& file, const MouseEvent& e); + /** @internal */ + void fileDoubleClicked (const File& file); + + juce_UseDebuggingNewOperator + +private: + class ContentComponent : public Component + { + public: + ContentComponent(); + ~ContentComponent(); + + void paint (Graphics& g); + void resized(); + + String instructions; + GlyphArrangement text; + + FileBrowserComponent* chooserComponent; + FilePreviewComponent* previewComponent; + TextButton* okButton; + TextButton* cancelButton; + }; + + ContentComponent* content; + const bool warnAboutOverwritingExistingFiles; + + FileChooserDialogBox (const FileChooserDialogBox&); + const FileChooserDialogBox& operator= (const FileChooserDialogBox&); +}; + +#endif // __JUCE_FILECHOOSERDIALOGBOX_JUCEHEADER__ +/********* End of inlined file: juce_FileChooserDialogBox.h *********/ + +#endif +#ifndef __JUCE_FILEFILTER_JUCEHEADER__ + +#endif +#ifndef __JUCE_FILELISTCOMPONENT_JUCEHEADER__ + +/********* Start of inlined file: juce_FileListComponent.h *********/ +#ifndef __JUCE_FILELISTCOMPONENT_JUCEHEADER__ +#define __JUCE_FILELISTCOMPONENT_JUCEHEADER__ + +/** + A component that displays the files in a directory as a listbox. + + This implements the DirectoryContentsDisplayComponent base class so that + it can be used in a FileBrowserComponent. + + To attach a listener to it, use its DirectoryContentsDisplayComponent base + class and the FileBrowserListener class. + + @see DirectoryContentsList, FileTreeComponent +*/ +class JUCE_API FileListComponent : public DirectoryContentsDisplayComponent, + public ListBox, + private ListBoxModel, + private ChangeListener +{ +public: + + /** Creates a listbox to show the contents of a specified directory. + */ + FileListComponent (DirectoryContentsList& listToShow); + + /** Destructor. */ + ~FileListComponent(); + + /** Returns the file that the user has currently selected. + + Returns File::nonexistent if none is selected. + */ + const File getSelectedFile() const; + + /** Scrolls to the top of the list. */ + void scrollToTop(); + + /** @internal */ + void changeListenerCallback (void*); + /** @internal */ + int getNumRows(); + /** @internal */ + void paintListBoxItem (int, Graphics&, int, int, bool); + /** @internal */ + Component* refreshComponentForRow (int rowNumber, bool isRowSelected, Component* existingComponentToUpdate); + /** @internal */ + void selectedRowsChanged (int lastRowSelected); + /** @internal */ + void deleteKeyPressed (int currentSelectedRow); + /** @internal */ + void returnKeyPressed (int currentSelectedRow); + + juce_UseDebuggingNewOperator + +private: + FileListComponent (const FileListComponent&); + const FileListComponent& operator= (const FileListComponent&); +}; + +#endif // __JUCE_FILELISTCOMPONENT_JUCEHEADER__ +/********* End of inlined file: juce_FileListComponent.h *********/ + +#endif +#ifndef __JUCE_FILENAMECOMPONENT_JUCEHEADER__ + +/********* Start of inlined file: juce_FilenameComponent.h *********/ +#ifndef __JUCE_FILENAMECOMPONENT_JUCEHEADER__ +#define __JUCE_FILENAMECOMPONENT_JUCEHEADER__ + +class FilenameComponent; + +/** + Listens for events happening to a FilenameComponent. + + Use FilenameComponent::addListener() and FilenameComponent::removeListener() to + register one of these objects for event callbacks when the filename is changed. + + @See FilenameComponent +*/ +class JUCE_API FilenameComponentListener +{ +public: + /** Destructor. */ + virtual ~FilenameComponentListener() {} + + /** This method is called after the FilenameComponent's file has been changed. */ + virtual void filenameComponentChanged (FilenameComponent* fileComponentThatHasChanged) = 0; +}; + +/** + Shows a filename as an editable text box, with a 'browse' button and a + drop-down list for recently selected files. + + A handy component for dialogue boxes where you want the user to be able to + select a file or directory. + + Attach an FilenameComponentListener using the addListener() method, and it will + get called each time the user changes the filename, either by browsing for a file + and clicking 'ok', or by typing a new filename into the box and pressing return. + + @see FileChooser, ComboBox +*/ +class JUCE_API FilenameComponent : public Component, + public SettableTooltipClient, + public FileDragAndDropTarget, + private AsyncUpdater, + private ButtonListener, + private ComboBoxListener +{ +public: + + /** Creates a FilenameComponent. + + @param name the name for this component. + @param currentFile the file to initially show in the box + @param canEditFilename if true, the user can manually edit the filename; if false, + they can only change it by browsing for a new file + @param isDirectory if true, the file will be treated as a directory, and + an appropriate directory browser used + @param isForSaving if true, the file browser will allow non-existent files to + be picked, as the file is assumed to be used for saving rather + than loading + @param fileBrowserWildcard a wildcard pattern to use in the file browser - e.g. "*.txt;*.foo". + If an empty string is passed in, then the pattern is assumed to be "*" + @param enforcedSuffix if this is non-empty, it is treated as a suffix that will be added + to any filenames that are entered or chosen + @param textWhenNothingSelected the message to display in the box before any filename is entered. (This + will only appear if the initial file isn't valid) + */ + FilenameComponent (const String& name, + const File& currentFile, + const bool canEditFilename, + const bool isDirectory, + const bool isForSaving, + const String& fileBrowserWildcard, + const String& enforcedSuffix, + const String& textWhenNothingSelected); + + /** Destructor. */ + ~FilenameComponent(); + + /** Returns the currently displayed filename. */ + const File getCurrentFile() const; + + /** Changes the current filename. + + If addToRecentlyUsedList is true, the filename will also be added to the + drop-down list of recent files. + + If sendChangeNotification is false, then the listeners won't be told of the + change. + */ + void setCurrentFile (File newFile, + const bool addToRecentlyUsedList, + const bool sendChangeNotification = true); + + /** Changes whether the use can type into the filename box. + */ + void setFilenameIsEditable (const bool shouldBeEditable); + + /** Sets a file or directory to be the default starting point for the browser to show. + + This is only used if the current file hasn't been set. + */ + void setDefaultBrowseTarget (const File& newDefaultDirectory) throw(); + + /** Returns all the entries on the recent files list. + + This can be used in conjunction with setRecentlyUsedFilenames() for saving the + state of this list. + + @see setRecentlyUsedFilenames + */ + const StringArray getRecentlyUsedFilenames() const; + + /** Sets all the entries on the recent files list. + + This can be used in conjunction with getRecentlyUsedFilenames() for saving the + state of this list. + + @see getRecentlyUsedFilenames, addRecentlyUsedFile + */ + void setRecentlyUsedFilenames (const StringArray& filenames); + + /** Adds an entry to the recently-used files dropdown list. + + If the file is already in the list, it will be moved to the top. A limit + is also placed on the number of items that are kept in the list. + + @see getRecentlyUsedFilenames, setRecentlyUsedFilenames, setMaxNumberOfRecentFiles + */ + void addRecentlyUsedFile (const File& file); + + /** Changes the limit for the number of files that will be stored in the recent-file list. + */ + void setMaxNumberOfRecentFiles (const int newMaximum); + + /** Changes the text shown on the 'browse' button. + + By default this button just says "..." but you can change it. The button itself + can be changed using the look-and-feel classes, so it might not actually have any + text on it. + */ + void setBrowseButtonText (const String& browseButtonText); + + /** Adds a listener that will be called when the selected file is changed. */ + void addListener (FilenameComponentListener* const listener) throw(); + + /** Removes a previously-registered listener. */ + void removeListener (FilenameComponentListener* const listener) throw(); + + /** Gives the component a tooltip. */ + void setTooltip (const String& newTooltip); + + /** @internal */ + void paintOverChildren (Graphics& g); + /** @internal */ + void resized(); + /** @internal */ + void lookAndFeelChanged(); + /** @internal */ + bool isInterestedInFileDrag (const StringArray& files); + /** @internal */ + void filesDropped (const StringArray& files, int, int); + /** @internal */ + void fileDragEnter (const StringArray& files, int, int); + /** @internal */ + void fileDragExit (const StringArray& files); + + juce_UseDebuggingNewOperator + +private: + + ComboBox* filenameBox; + String lastFilename; + Button* browseButton; + int maxRecentFiles; + bool isDir, isSaving, isFileDragOver; + String wildcard, enforcedSuffix, browseButtonText; + SortedSet listeners; + File defaultBrowseFile; + + void comboBoxChanged (ComboBox*); + void buttonClicked (Button* button); + void handleAsyncUpdate(); + + FilenameComponent (const FilenameComponent&); + const FilenameComponent& operator= (const FilenameComponent&); +}; + +#endif // __JUCE_FILENAMECOMPONENT_JUCEHEADER__ +/********* End of inlined file: juce_FilenameComponent.h *********/ + +#endif +#ifndef __JUCE_FILEPREVIEWCOMPONENT_JUCEHEADER__ + +#endif +#ifndef __JUCE_FILESEARCHPATHLISTCOMPONENT_JUCEHEADER__ + +/********* Start of inlined file: juce_FileSearchPathListComponent.h *********/ +#ifndef __JUCE_FILESEARCHPATHLISTCOMPONENT_JUCEHEADER__ +#define __JUCE_FILESEARCHPATHLISTCOMPONENT_JUCEHEADER__ + +/** + Shows a set of file paths in a list, allowing them to be added, removed or + re-ordered. + + @see FileSearchPath +*/ +class JUCE_API FileSearchPathListComponent : public Component, + public SettableTooltipClient, + public FileDragAndDropTarget, + private ButtonListener, + private ListBoxModel +{ +public: + + /** Creates an empty FileSearchPathListComponent. + + */ + FileSearchPathListComponent(); + + /** Destructor. */ + ~FileSearchPathListComponent(); + + /** Returns the path as it is currently shown. */ + const FileSearchPath& getPath() const throw() { return path; } + + /** Changes the current path. */ + void setPath (const FileSearchPath& newPath); + + /** Sets a file or directory to be the default starting point for the browser to show. + + This is only used if the current file hasn't been set. + */ + void setDefaultBrowseTarget (const File& newDefaultDirectory) throw(); + + /** A set of colour IDs to use to change the colour of various aspects of the label. + + These constants can be used either via the Component::setColour(), or LookAndFeel::setColour() + methods. + + @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour + */ + enum ColourIds + { + backgroundColourId = 0x1004100, /**< The background colour to fill the component with. + Make this transparent if you don't want the background to be filled. */ + }; + + /** @internal */ + int getNumRows(); + /** @internal */ + void paintListBoxItem (int rowNumber, Graphics& g, int width, int height, bool rowIsSelected); + /** @internal */ + void deleteKeyPressed (int lastRowSelected); + /** @internal */ + void returnKeyPressed (int lastRowSelected); + /** @internal */ + void listBoxItemDoubleClicked (int row, const MouseEvent&); + /** @internal */ + void selectedRowsChanged (int lastRowSelected); + /** @internal */ + void resized(); + /** @internal */ + void paint (Graphics& g); + /** @internal */ + bool isInterestedInFileDrag (const StringArray& files); + /** @internal */ + void filesDropped (const StringArray& files, int, int); + /** @internal */ + void buttonClicked (Button* button); + + juce_UseDebuggingNewOperator + +private: + + FileSearchPath path; + File defaultBrowseTarget; + + ListBox* listBox; + Button* addButton; + Button* removeButton; + Button* changeButton; + Button* upButton; + Button* downButton; + + void changed() throw(); + void updateButtons() throw(); + + FileSearchPathListComponent (const FileSearchPathListComponent&); + const FileSearchPathListComponent& operator= (const FileSearchPathListComponent&); +}; + +#endif // __JUCE_FILESEARCHPATHLISTCOMPONENT_JUCEHEADER__ +/********* End of inlined file: juce_FileSearchPathListComponent.h *********/ + +#endif +#ifndef __JUCE_FILETREECOMPONENT_JUCEHEADER__ + +/********* Start of inlined file: juce_FileTreeComponent.h *********/ +#ifndef __JUCE_FILETREECOMPONENT_JUCEHEADER__ +#define __JUCE_FILETREECOMPONENT_JUCEHEADER__ + +/** + A component that displays the files in a directory as a treeview. + + This implements the DirectoryContentsDisplayComponent base class so that + it can be used in a FileBrowserComponent. + + To attach a listener to it, use its DirectoryContentsDisplayComponent base + class and the FileBrowserListener class. + + @see DirectoryContentsList, FileListComponent +*/ +class JUCE_API FileTreeComponent : public DirectoryContentsDisplayComponent, + public TreeView +{ +public: + + /** Creates a listbox to show the contents of a specified directory. + */ + FileTreeComponent (DirectoryContentsList& listToShow); + + /** Destructor. */ + ~FileTreeComponent(); + + /** Returns the number of selected files in the tree. + */ + int getNumSelectedFiles() const throw() { return TreeView::getNumSelectedItems(); } + + /** Returns one of the files that the user has currently selected. + + Returns File::nonexistent if none is selected. + */ + const File getSelectedFile (int index) const throw(); + + /** Returns the first of the files that the user has currently selected. + + Returns File::nonexistent if none is selected. + */ + const File getSelectedFile() const; + + /** Scrolls the list to the top. */ + void scrollToTop(); + + /** Setting a name for this allows tree items to be dragged. + + The string that you pass in here will be returned by the getDragSourceDescription() + of the items in the tree. For more info, see TreeViewItem::getDragSourceDescription(). + */ + void setDragAndDropDescription (const String& description) throw(); + + /** Returns the last value that was set by setDragAndDropDescription(). + */ + const String& getDragAndDropDescription() const throw() { return dragAndDropDescription; } + + juce_UseDebuggingNewOperator + +private: + String dragAndDropDescription; + + FileTreeComponent (const FileTreeComponent&); + const FileTreeComponent& operator= (const FileTreeComponent&); +}; + +#endif // __JUCE_FILETREECOMPONENT_JUCEHEADER__ +/********* End of inlined file: juce_FileTreeComponent.h *********/ + +#endif +#ifndef __JUCE_IMAGEPREVIEWCOMPONENT_JUCEHEADER__ + +/********* Start of inlined file: juce_ImagePreviewComponent.h *********/ +#ifndef __JUCE_IMAGEPREVIEWCOMPONENT_JUCEHEADER__ +#define __JUCE_IMAGEPREVIEWCOMPONENT_JUCEHEADER__ + +/** + A simple preview component that shows thumbnails of image files. + + @see FileChooserDialogBox, FilePreviewComponent +*/ +class JUCE_API ImagePreviewComponent : public FilePreviewComponent, + private Timer +{ +public: + + /** Creates an ImagePreviewComponent. */ + ImagePreviewComponent(); + + /** Destructor. */ + ~ImagePreviewComponent(); + + /** @internal */ + void selectedFileChanged (const File& newSelectedFile); + /** @internal */ + void paint (Graphics& g); + /** @internal */ + void timerCallback(); + + juce_UseDebuggingNewOperator + +private: + File fileToLoad; + Image* currentThumbnail; + String currentDetails; + + void getThumbSize (int& w, int& h) const; + + ImagePreviewComponent (const ImagePreviewComponent&); + const ImagePreviewComponent& operator= (const ImagePreviewComponent&); +}; + +#endif // __JUCE_IMAGEPREVIEWCOMPONENT_JUCEHEADER__ +/********* End of inlined file: juce_ImagePreviewComponent.h *********/ + +#endif +#ifndef __JUCE_WILDCARDFILEFILTER_JUCEHEADER__ + +/********* Start of inlined file: juce_WildcardFileFilter.h *********/ +#ifndef __JUCE_WILDCARDFILEFILTER_JUCEHEADER__ +#define __JUCE_WILDCARDFILEFILTER_JUCEHEADER__ + +/** + A type of FileFilter that works by wildcard pattern matching. + + This filter only allows files that match one of the specified patterns, but + allows all directories through. + + @see FileFilter, DirectoryContentsList, FileListComponent, FileBrowserComponent +*/ +class JUCE_API WildcardFileFilter : public FileFilter +{ +public: + + /** + Creates a wildcard filter for one or more patterns. + + The wildcardPatterns parameter is a comma or semicolon-delimited set of + patterns, e.g. "*.wav;*.aiff" would look for files ending in either .wav + or .aiff. + + The description is a name to show the user in a list of possible patterns, so + for the wav/aiff example, your description might be "audio files". + */ + WildcardFileFilter (const String& wildcardPatterns, + const String& description); + + /** Destructor. */ + ~WildcardFileFilter(); + + /** Returns true if the filename matches one of the patterns specified. */ + bool isFileSuitable (const File& file) const; + + /** This always returns true. */ + bool isDirectorySuitable (const File& file) const; + + juce_UseDebuggingNewOperator + +private: + StringArray wildcards; +}; + +#endif // __JUCE_WILDCARDFILEFILTER_JUCEHEADER__ +/********* End of inlined file: juce_WildcardFileFilter.h *********/ + +#endif +#ifndef __JUCE_ALERTWINDOW_JUCEHEADER__ + +/********* Start of inlined file: juce_AlertWindow.h *********/ +#ifndef __JUCE_ALERTWINDOW_JUCEHEADER__ +#define __JUCE_ALERTWINDOW_JUCEHEADER__ + +/** A window that displays a message and has buttons for the user to react to it. + + For simple dialog boxes with just a couple of buttons on them, there are + some static methods for running these. + + For more complex dialogs, an AlertWindow can be created, then it can have some + buttons and components added to it, and its runModalLoop() method is then used to + show it. The value returned by runModalLoop() shows which button the + user pressed to dismiss the box. + + @see ThreadWithProgressWindow +*/ +class JUCE_API AlertWindow : public TopLevelWindow, + private ButtonListener +{ +public: + + /** The type of icon to show in the dialog box. */ + enum AlertIconType + { + NoIcon, /**< No icon will be shown on the dialog box. */ + QuestionIcon, /**< A question-mark icon, for dialog boxes that need the + user to answer a question. */ + WarningIcon, /**< An exclamation mark to indicate that the dialog is a + warning about something and shouldn't be ignored. */ + InfoIcon /**< An icon that indicates that the dialog box is just + giving the user some information, which doesn't require + a response from them. */ + }; + + /** Creates an AlertWindow. + + @param title the headline to show at the top of the dialog box + @param message a longer, more descriptive message to show underneath the + headline + @param iconType the type of icon to display + */ + AlertWindow (const String& title, + const String& message, + AlertIconType iconType); + + /** Destroys the AlertWindow */ + ~AlertWindow(); + + /** Returns the type of alert icon that was specified when the window + was created. */ + AlertIconType getAlertType() const throw() { return alertIconType; } + + /** Changes the dialog box's message. + + This will also resize the window to fit the new message if required. + */ + void setMessage (const String& message); + + /** Adds a button to the window. + + @param name the text to show on the button + @param returnValue the value that should be returned from runModalLoop() + if this is the button that the user presses. + @param shortcutKey1 an optional key that can be pressed to trigger this button + @param shortcutKey2 a second optional key that can be pressed to trigger this button + */ + void addButton (const String& name, + const int returnValue, + const KeyPress& shortcutKey1 = KeyPress(), + const KeyPress& shortcutKey2 = KeyPress()); + + /** Returns the number of buttons that the window currently has. */ + int getNumButtons() const; + + /** Adds a textbox to the window for entering strings. + + @param name an internal name for the text-box. This is the name to pass to + the getTextEditorContents() method to find out what the + user typed-in. + @param initialContents a string to show in the text box when it's first shown + @param onScreenLabel if this is non-empty, it will be displayed next to the + text-box to label it. + @param isPasswordBox if true, the text editor will display asterisks instead of + the actual text + @see getTextEditorContents + */ + void addTextEditor (const String& name, + const String& initialContents, + const String& onScreenLabel = String::empty, + const bool isPasswordBox = false); + + /** Returns the contents of a named textbox. + + After showing an AlertWindow that contains a text editor, this can be + used to find out what the user has typed into it. + + @param nameOfTextEditor the name of the text box that you're interested in + @see addTextEditor + */ + const String getTextEditorContents (const String& nameOfTextEditor) const; + + /** Adds a drop-down list of choices to the box. + + After the box has been shown, the getComboBoxComponent() method can + be used to find out which item the user picked. + + @param name the label to use for the drop-down list + @param items the list of items to show in it + @param onScreenLabel if this is non-empty, it will be displayed next to the + combo-box to label it. + @see getComboBoxComponent + */ + void addComboBox (const String& name, + const StringArray& items, + const String& onScreenLabel = String::empty); + + /** Returns a drop-down list that was added to the AlertWindow. + + @param nameOfList the name that was passed into the addComboBox() method + when creating the drop-down + @returns the ComboBox component, or 0 if none was found for the given name. + */ + ComboBox* getComboBoxComponent (const String& nameOfList) const; + + /** Adds a block of text. + + This is handy for adding a multi-line note next to a textbox or combo-box, + to provide more details about what's going on. + */ + void addTextBlock (const String& text); + + /** Adds a progress-bar to the window. + + @param progressValue a variable that will be repeatedly checked while the + dialog box is visible, to see how far the process has + got. The value should be in the range 0 to 1.0 + */ + void addProgressBarComponent (double& progressValue); + + /** Adds a user-defined component to the dialog box. + + @param component the component to add - its size should be set up correctly + before it is passed in. The caller is responsible for deleting + the component later on - the AlertWindow won't delete it. + */ + void addCustomComponent (Component* const component); + + /** Returns the number of custom components in the dialog box. + + @see getCustomComponent, addCustomComponent + */ + int getNumCustomComponents() const; + + /** Returns one of the custom components in the dialog box. + + @param index a value 0 to (getNumCustomComponents() - 1). Out-of-range indexes + will return 0 + @see getNumCustomComponents, addCustomComponent + */ + Component* getCustomComponent (const int index) const; + + /** Removes one of the custom components in the dialog box. + + Note that this won't delete it, it just removes the component from the window + + @param index a value 0 to (getNumCustomComponents() - 1). Out-of-range indexes + will return 0 + @returns the component that was removed (or zero) + @see getNumCustomComponents, addCustomComponent + */ + Component* removeCustomComponent (const int index); + + /** Returns true if the window contains any components other than just buttons.*/ + bool containsAnyExtraComponents() const; + + // easy-to-use message box functions: + + /** Shows a dialog box that just has a message and a single button to get rid of it. + + The box is shown modally, and the method returns after the user + has clicked the button (or pressed the escape or return keys). + + @param iconType the type of icon to show + @param title the headline to show at the top of the box + @param message a longer, more descriptive message to show underneath the + headline + @param buttonText the text to show in the button - if this string is empty, the + default string "ok" (or a localised version) will be used. + */ + static void JUCE_CALLTYPE showMessageBox (AlertIconType iconType, + const String& title, + const String& message, + const String& buttonText = String::empty); + + /** Shows a dialog box with two buttons. + + Ideal for ok/cancel or yes/no choices. The return key can also be used + to trigger the first button, and the escape key for the second button. + + @param iconType the type of icon to show + @param title the headline to show at the top of the box + @param message a longer, more descriptive message to show underneath the + headline + @param button1Text the text to show in the first button - if this string is + empty, the default string "ok" (or a localised version of it) + will be used. + @param button2Text the text to show in the second button - if this string is + empty, the default string "cancel" (or a localised version of it) + will be used. + @returns true if button 1 was clicked, false if it was button 2 + */ + static bool JUCE_CALLTYPE showOkCancelBox (AlertIconType iconType, + const String& title, + const String& message, + const String& button1Text = String::empty, + const String& button2Text = String::empty); + + /** Shows a dialog box with three buttons. + + Ideal for yes/no/cancel boxes. + + The escape key can be used to trigger the third button. + + @param iconType the type of icon to show + @param title the headline to show at the top of the box + @param message a longer, more descriptive message to show underneath the + headline + @param button1Text the text to show in the first button - if an empty string, then + "yes" will be used (or a localised version of it) + @param button2Text the text to show in the first button - if an empty string, then + "no" will be used (or a localised version of it) + @param button3Text the text to show in the first button - if an empty string, then + "cancel" will be used (or a localised version of it) + + @returns one of the following values: + - 0 if the third button was pressed (normally used for 'cancel') + - 1 if the first button was pressed (normally used for 'yes') + - 2 if the middle button was pressed (normally used for 'no') + */ + static int JUCE_CALLTYPE showYesNoCancelBox (AlertIconType iconType, + const String& title, + const String& message, + const String& button1Text = String::empty, + const String& button2Text = String::empty, + const String& button3Text = String::empty); + + /** Shows an operating-system native dialog box. + + @param title the title to use at the top + @param bodyText the longer message to show + @param isOkCancel if true, this will show an ok/cancel box, if false, + it'll show a box with just an ok button + @returns true if the ok button was pressed, false if they pressed cancel. + */ + static bool JUCE_CALLTYPE showNativeDialogBox (const String& title, + const String& bodyText, + bool isOkCancel); + + /** A set of colour IDs to use to change the colour of various aspects of the alert box. + + These constants can be used either via the Component::setColour(), or LookAndFeel::setColour() + methods. + + @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour + */ + enum ColourIds + { + backgroundColourId = 0x1001800, /**< The background colour for the window. */ + textColourId = 0x1001810, /**< The colour for the text. */ + outlineColourId = 0x1001820 /**< An optional colour to use to draw a border around the window. */ + }; + + juce_UseDebuggingNewOperator + +protected: + /** @internal */ + void paint (Graphics& g); + /** @internal */ + void mouseDown (const MouseEvent& e); + /** @internal */ + void mouseDrag (const MouseEvent& e); + /** @internal */ + bool keyPressed (const KeyPress& key); + /** @internal */ + void buttonClicked (Button* button); + /** @internal */ + void lookAndFeelChanged(); + +private: + String text; + TextLayout textLayout; + AlertIconType alertIconType; + ComponentBoundsConstrainer constrainer; + ComponentDragger dragger; + Rectangle textArea; + VoidArray buttons, textBoxes, comboBoxes; + VoidArray progressBars, customComps, textBlocks, allComps; + StringArray textboxNames, comboBoxNames; + Font font; + + void updateLayout (const bool onlyIncreaseSize); + + // disable copy constructor + AlertWindow (const AlertWindow&); + const AlertWindow& operator= (const AlertWindow&); +}; + +#endif // __JUCE_ALERTWINDOW_JUCEHEADER__ +/********* End of inlined file: juce_AlertWindow.h *********/ + +#endif +#ifndef __JUCE_COMPONENTPEER_JUCEHEADER__ + +#endif +#ifndef __JUCE_DIALOGWINDOW_JUCEHEADER__ + +/********* Start of inlined file: juce_DialogWindow.h *********/ +#ifndef __JUCE_DIALOGWINDOW_JUCEHEADER__ +#define __JUCE_DIALOGWINDOW_JUCEHEADER__ + +/** + A dialog-box style window. + + This class is a convenient way of creating a DocumentWindow with a close button + that can be triggered by pressing the escape key. + + Any of the methods available to a DocumentWindow or ResizableWindow are also + available to this, so it can be made resizable, have a menu bar, etc. + + To add items to the box, see the ResizableWindow::setContentComponent() method. + Don't add components directly to this class - always put them in a content component! + + You'll need to override the DocumentWindow::closeButtonPressed() method to handle + the user clicking the close button - for more info, see the DocumentWindow + help. + + @see DocumentWindow, ResizableWindow +*/ +class JUCE_API DialogWindow : public DocumentWindow +{ +public: + + /** Creates a DialogWindow. + + @param name the name to give the component - this is also + the title shown at the top of the window. To change + this later, use setName() + @param backgroundColour the colour to use for filling the window's background. + @param escapeKeyTriggersCloseButton if true, then pressing the escape key will cause the + close button to be triggered + @param addToDesktop if true, the window will be automatically added to the + desktop; if false, you can use it as a child component + */ + DialogWindow (const String& name, + const Colour& backgroundColour, + const bool escapeKeyTriggersCloseButton, + const bool addToDesktop = true); + + /** Destructor. + + If a content component has been set with setContentComponent(), it + will be deleted. + */ + ~DialogWindow(); + + /** Easy way of quickly showing a dialog box containing a given component. + + This will open and display a DialogWindow containing a given component, returning + when the user clicks its close button. + + It returns the value that was returned by the dialog box's runModalLoop() call. + + To close the dialog programatically, you should call exitModalState (returnValue) on + the DialogWindow that is created. To find a pointer to this window from your + contentComponent, you can do something like this: + @code + Dialogwindow* dw = contentComponent->findParentComponentOfClass ((DialogWindow*) 0); + + if (dw != 0) + dw->exitModalState (1234); + @endcode + + @param dialogTitle the dialog box's title + @param contentComponent the content component for the dialog box. Make sure + that this has been set to the size you want it to + be before calling this method. The component won't + be deleted by this call, so you can re-use it or delete + it afterwards + @param componentToCentreAround if this is non-zero, it indicates a component that + you'd like to show this dialog box in front of. See the + DocumentWindow::centreAroundComponent() method for more + info on this parameter + @param backgroundColour a colour to use for the dialog box's background colour + @param escapeKeyTriggersCloseButton if true, then pressing the escape key will cause the + close button to be triggered + @param shouldBeResizable if true, the dialog window has either a resizable border, or + a corner resizer + @param useBottomRightCornerResizer if shouldBeResizable is true, this indicates whether + to use a border or corner resizer component. See ResizableWindow::setResizable() + */ + static int showModalDialog (const String& dialogTitle, + Component* contentComponent, + Component* componentToCentreAround, + const Colour& backgroundColour, + const bool escapeKeyTriggersCloseButton, + const bool shouldBeResizable = false, + const bool useBottomRightCornerResizer = false); + + juce_UseDebuggingNewOperator + +protected: + /** @internal */ + void resized(); + +private: + bool escapeKeyTriggersCloseButton; + + DialogWindow (const DialogWindow&); + const DialogWindow& operator= (const DialogWindow&); +}; + +#endif // __JUCE_DIALOGWINDOW_JUCEHEADER__ +/********* End of inlined file: juce_DialogWindow.h *********/ + +#endif +#ifndef __JUCE_DOCUMENTWINDOW_JUCEHEADER__ + +#endif +#ifndef __JUCE_RESIZABLEWINDOW_JUCEHEADER__ + +#endif +#ifndef __JUCE_SPLASHSCREEN_JUCEHEADER__ + +/********* Start of inlined file: juce_SplashScreen.h *********/ +#ifndef __JUCE_SPLASHSCREEN_JUCEHEADER__ +#define __JUCE_SPLASHSCREEN_JUCEHEADER__ + +/** A component for showing a splash screen while your app starts up. + + This will automatically position itself, and delete itself when the app has + finished initialising (it uses the JUCEApplication::isInitialising() to detect + this). + + To use it, just create one of these in your JUCEApplication::initialise() method, + call its show() method and let the object delete itself later. + + E.g. @code + + void MyApp::initialise (const String& commandLine) + { + SplashScreen* splash = new SplashScreen(); + + splash->show (T("welcome to my app"), + ImageCache::getFromFile (File ("/foobar/splash.jpg")), + 4000, false); + + .. no need to delete the splash screen - it'll do that itself. + } + + @endcode +*/ +class JUCE_API SplashScreen : public Component, + public Timer, + private DeletedAtShutdown +{ +public: + + /** Creates a SplashScreen object. + + After creating one of these (or your subclass of it), call one of the show() + methods to display it. + */ + SplashScreen(); + + /** Destructor. */ + ~SplashScreen(); + + /** Creates a SplashScreen object that will display an image. + + As soon as this is called, the SplashScreen will be displayed in the centre of the + screen. This method will also dispatch any pending messages to make sure that when + it returns, the splash screen has been completely drawn, and your initialisation + code can carry on. + + @param title the name to give the component + @param backgroundImage an image to draw on the component. The component's size + will be set to the size of this image, and if the image is + semi-transparent, the component will be made semi-transparent + too. This image will be deleted (or released from the ImageCache + if that's how it was created) by the splash screen object when + it is itself deleted. + @param minimumTimeToDisplayFor how long (in milliseconds) the splash screen + should stay visible for. If the initialisation takes longer than + this time, the splash screen will wait for it to finish before + disappearing, but if initialisation is very quick, this lets + you make sure that people get a good look at your splash. + @param useDropShadow if true, the window will have a drop shadow + @param removeOnMouseClick if true, the window will go away as soon as the user clicks + the mouse (anywhere) + */ + void show (const String& title, + Image* const backgroundImage, + const int minimumTimeToDisplayFor, + const bool useDropShadow, + const bool removeOnMouseClick = true); + + /** Creates a SplashScreen object with a specified size. + + For a custom splash screen, you can use this method to display it at a certain size + and then override the paint() method yourself to do whatever's necessary. + + As soon as this is called, the SplashScreen will be displayed in the centre of the + screen. This method will also dispatch any pending messages to make sure that when + it returns, the splash screen has been completely drawn, and your initialisation + code can carry on. + + @param title the name to give the component + @param width the width to use + @param height the height to use + @param minimumTimeToDisplayFor how long (in milliseconds) the splash screen + should stay visible for. If the initialisation takes longer than + this time, the splash screen will wait for it to finish before + disappearing, but if initialisation is very quick, this lets + you make sure that people get a good look at your splash. + @param useDropShadow if true, the window will have a drop shadow + @param removeOnMouseClick if true, the window will go away as soon as the user clicks + the mouse (anywhere) + */ + void show (const String& title, + const int width, + const int height, + const int minimumTimeToDisplayFor, + const bool useDropShadow, + const bool removeOnMouseClick = true); + + /** @internal */ + void paint (Graphics& g); + /** @internal */ + void timerCallback(); + + juce_UseDebuggingNewOperator + +private: + Image* backgroundImage; + Time earliestTimeToDelete; + int originalClickCounter; + bool isImageInCache; + + SplashScreen (const SplashScreen&); + const SplashScreen& operator= (const SplashScreen&); +}; + +#endif // __JUCE_SPLASHSCREEN_JUCEHEADER__ +/********* End of inlined file: juce_SplashScreen.h *********/ + +#endif +#ifndef __JUCE_THREADWITHPROGRESSWINDOW_JUCEHEADER__ + +/********* Start of inlined file: juce_ThreadWithProgressWindow.h *********/ +#ifndef __JUCE_THREADWITHPROGRESSWINDOW_JUCEHEADER__ +#define __JUCE_THREADWITHPROGRESSWINDOW_JUCEHEADER__ + +/** + A thread that automatically pops up a modal dialog box with a progress bar + and cancel button while it's busy running. + + These are handy for performing some sort of task while giving the user feedback + about how long there is to go, etc. + + E.g. @code + class MyTask : public ThreadWithProgressWindow + { + public: + MyTask() : ThreadWithProgressWindow (T("busy..."), true, true) + { + } + + ~MyTask() + { + } + + void run() + { + for (int i = 0; i < thingsToDo; ++i) + { + // must check this as often as possible, because this is + // how we know if the user's pressed 'cancel' + if (threadShouldExit()) + break; + + // this will update the progress bar on the dialog box + setProgress (i / (double) thingsToDo); + + // ... do the business here... + } + } + }; + + void doTheTask() + { + MyTask m; + + if (m.runThread()) + { + // thread finished normally.. + } + else + { + // user pressed the cancel button.. + } + } + + @endcode + + @see Thread, AlertWindow +*/ +class JUCE_API ThreadWithProgressWindow : public Thread, + private Timer +{ +public: + + /** Creates the thread. + + Initially, the dialog box won't be visible, it'll only appear when the + runThread() method is called. + + @param windowTitle the title to go at the top of the dialog box + @param hasProgressBar whether the dialog box should have a progress bar (see + setProgress() ) + @param hasCancelButton whether the dialog box should have a cancel button + @param timeOutMsWhenCancelling when 'cancel' is pressed, this is how long to wait for + the thread to stop before killing it forcibly (see + Thread::stopThread() ) + */ + ThreadWithProgressWindow (const String& windowTitle, + const bool hasProgressBar, + const bool hasCancelButton, + const int timeOutMsWhenCancelling = 10000, + const String& cancelButtonText = JUCE_T("Cancel")); + + /** Destructor. */ + ~ThreadWithProgressWindow(); + + /** Starts the thread and waits for it to finish. + + This will start the thread, make the dialog box appear, and wait until either + the thread finishes normally, or until the cancel button is pressed. + + Before returning, the dialog box will be hidden. + + @param threadPriority the priority to use when starting the thread - see + Thread::startThread() for values + @returns true if the thread finished normally; false if the user pressed cancel + */ + bool runThread (const int threadPriority = 5); + + /** The thread should call this periodically to update the position of the progress bar. + + @param newProgress the progress, from 0.0 to 1.0 + @see setStatusMessage + */ + void setProgress (const double newProgress); + + /** The thread can call this to change the message that's displayed in the dialog box. + */ + void setStatusMessage (const String& newStatusMessage); + + juce_UseDebuggingNewOperator + +private: + void timerCallback(); + + double progress; + AlertWindow alertWindow; + String message; + const int timeOutMsWhenCancelling; + + ThreadWithProgressWindow (const ThreadWithProgressWindow&); + const ThreadWithProgressWindow& operator= (const ThreadWithProgressWindow&); +}; + +#endif // __JUCE_THREADWITHPROGRESSWINDOW_JUCEHEADER__ +/********* End of inlined file: juce_ThreadWithProgressWindow.h *********/ + +#endif +#ifndef __JUCE_TOOLTIPWINDOW_JUCEHEADER__ + +#endif +#ifndef __JUCE_TOPLEVELWINDOW_JUCEHEADER__ + +#endif +#ifndef __JUCE_ACTIVEXCONTROLCOMPONENT_JUCEHEADER__ + +/********* Start of inlined file: juce_ActiveXControlComponent.h *********/ +#ifndef __JUCE_ACTIVEXCONTROLCOMPONENT_JUCEHEADER__ +#define __JUCE_ACTIVEXCONTROLCOMPONENT_JUCEHEADER__ + +#if JUCE_WIN32 || DOXYGEN + +/** + A Windows-specific class that can create and embed an ActiveX control inside + itself. + + To use it, create one of these, put it in place and make sure it's visible in a + window, then use createControl() to instantiate an ActiveX control. The control + will then be moved and resized to follow the movements of this component. + + Of course, since the control is a heavyweight window, it'll obliterate any + juce components that may overlap this component, but that's life. +*/ +class JUCE_API ActiveXControlComponent : public Component +{ +public: + + /** Create an initially-empty container. */ + ActiveXControlComponent(); + + /** Destructor. */ + ~ActiveXControlComponent(); + + /** Tries to create an ActiveX control and embed it in this peer. + + The peer controlIID is a pointer to an IID structure - it's treated + as a void* because when including the Juce headers, you might not always + have included windows.h first, in which case IID wouldn't be defined. + + e.g. @code + const IID myIID = __uuidof (QTControl); + myControlComp->createControl (&myIID); + @endcode + */ + bool createControl (const void* controlIID); + + /** Deletes the ActiveX control, if one has been created. + */ + void deleteControl(); + + /** Returns true if a control is currently in use. */ + bool isControlOpen() const throw() { return control != 0; } + + /** Does a QueryInterface call on the embedded control object. + + This allows you to cast the control to whatever type of COM object you need. + + The iid parameter is a pointer to an IID structure - it's treated + as a void* because when including the Juce headers, you might not always + have included windows.h first, in which case IID wouldn't be defined, but + you should just pass a pointer to an IID. + + e.g. @code + const IID iid = __uuidof (IOleWindow); + + IOleWindow* oleWindow = (IOleWindow*) myControlComp->queryInterface (&iid); + + if (oleWindow != 0) + { + HWND hwnd; + oleWindow->GetWindow (&hwnd); + + ... + + oleWindow->Release(); + } + @endcode + */ + void* queryInterface (const void* iid) const; + + /** Set this to false to stop mouse events being allowed through to the control. + */ + void setMouseEventsAllowed (const bool eventsCanReachControl); + + /** Returns true if mouse events are allowed to get through to the control. + */ + bool areMouseEventsAllowed() const throw() { return mouseEventsAllowed; } + + /** @internal */ + void paint (Graphics& g); + /** @internal */ + void* originalWndProc; + + juce_UseDebuggingNewOperator + +private: + friend class ActiveXControlData; + void* control; + bool mouseEventsAllowed; + + ActiveXControlComponent (const ActiveXControlComponent&); + const ActiveXControlComponent& operator= (const ActiveXControlComponent&); + + void setControlBounds (const Rectangle& bounds) const; + void setControlVisible (const bool b) const; +}; + +#endif + +#endif // __JUCE_ACTIVEXCONTROLCOMPONENT_JUCEHEADER__ +/********* End of inlined file: juce_ActiveXControlComponent.h *********/ + +#endif +#ifndef __JUCE_AUDIODEVICESELECTORCOMPONENT_JUCEHEADER__ + +/********* Start of inlined file: juce_AudioDeviceSelectorComponent.h *********/ +#ifndef __JUCE_AUDIODEVICESELECTORCOMPONENT_JUCEHEADER__ +#define __JUCE_AUDIODEVICESELECTORCOMPONENT_JUCEHEADER__ + +class AudioDeviceSelectorComponentListBox; + +/** + A component containing controls to let the user change the audio settings of + an AudioDeviceManager object. + + Very easy to use - just create one of these and show it to the user. + + @see AudioDeviceManager +*/ +class JUCE_API AudioDeviceSelectorComponent : public Component, + public ComboBoxListener, + public ButtonListener, + public ChangeListener +{ +public: + + /** Creates the component. + + If your app needs only output channels, you might ask for a maximum of 0 input + channels, and the component won't display any options for choosing the input + channels. And likewise if you're doing an input-only app. + + @param deviceManager the device manager that this component should control + @param minAudioInputChannels the minimum number of audio input channels that the application needs + @param maxAudioInputChannels the maximum number of audio input channels that the application needs + @param minAudioOutputChannels the minimum number of audio output channels that the application needs + @param maxAudioOutputChannels the maximum number of audio output channels that the application needs + @param showMidiInputOptions if true, the component will allow the user to select which midi inputs are enabled + @param showMidiOutputSelector if true, the component will let the user choose a default midi output device + */ + AudioDeviceSelectorComponent (AudioDeviceManager& deviceManager, + const int minAudioInputChannels, + const int maxAudioInputChannels, + const int minAudioOutputChannels, + const int maxAudioOutputChannels, + const bool showMidiInputOptions, + const bool showMidiOutputSelector); + + /** Destructor */ + ~AudioDeviceSelectorComponent(); + + /** @internal */ + void resized(); + /** @internal */ + void comboBoxChanged (ComboBox*); + /** @internal */ + void buttonClicked (Button*); + /** @internal */ + void changeListenerCallback (void*); + + juce_UseDebuggingNewOperator + +private: + AudioDeviceManager& deviceManager; + ComboBox* audioDeviceDropDown; + const int minOutputChannels, maxOutputChannels, minInputChannels, maxInputChannels; + + ComboBox* sampleRateDropDown; + AudioDeviceSelectorComponentListBox* inputChansBox; + Label* inputsLabel; + AudioDeviceSelectorComponentListBox* outputChansBox; + Label* outputsLabel; + Label* sampleRateLabel; + ComboBox* bufferSizeDropDown; + Label* bufferSizeLabel; + Button* launchUIButton; + AudioDeviceSelectorComponentListBox* midiInputsList; + Label* midiInputsLabel; + ComboBox* midiOutputSelector; + Label* midiOutputLabel; + + AudioDeviceSelectorComponent (const AudioDeviceSelectorComponent&); + const AudioDeviceSelectorComponent& operator= (const AudioDeviceSelectorComponent&); +}; + +#endif // __JUCE_AUDIODEVICESELECTORCOMPONENT_JUCEHEADER__ +/********* End of inlined file: juce_AudioDeviceSelectorComponent.h *********/ + +#endif +#ifndef __JUCE_BUBBLECOMPONENT_JUCEHEADER__ + +/********* Start of inlined file: juce_BubbleComponent.h *********/ +#ifndef __JUCE_BUBBLECOMPONENT_JUCEHEADER__ +#define __JUCE_BUBBLECOMPONENT_JUCEHEADER__ + +/** + A component for showing a message or other graphics inside a speech-bubble-shaped + outline, pointing at a location on the screen. + + This is a base class that just draws and positions the bubble shape, but leaves + the drawing of any content up to a subclass. See BubbleMessageComponent for a subclass + that draws a text message. + + To use it, create your subclass, then either add it to a parent component or + put it on the desktop with addToDesktop (0), use setPosition() to + resize and position it, then make it visible. + + @see BubbleMessageComponent +*/ +class JUCE_API BubbleComponent : public Component +{ +protected: + + /** Creates a BubbleComponent. + + Your subclass will need to implement the getContentSize() and paintContent() + methods to draw the bubble's contents. + */ + BubbleComponent(); + +public: + /** Destructor. */ + ~BubbleComponent(); + + /** A list of permitted placements for the bubble, relative to the co-ordinates + at which it should be pointing. + + @see setAllowedPlacement + */ + enum BubblePlacement + { + above = 1, + below = 2, + left = 4, + right = 8 + }; + + /** Tells the bubble which positions it's allowed to put itself in, relative to the + point at which it's pointing. + + By default when setPosition() is called, the bubble will place itself either + above, below, left, or right of the target area. You can pass in a bitwise-'or' of + the values in BubblePlacement to restrict this choice. + + E.g. if you only want your bubble to appear above or below the target area, + use setAllowedPlacement (above | below); + + @see BubblePlacement + */ + void setAllowedPlacement (const int newPlacement); + + /** Moves and resizes the bubble to point at a given component. + + This will resize the bubble to fit its content, then find a position for it + so that it's next to, but doesn't overlap the given component. + + It'll put itself either above, below, or to the side of the component depending + on where there's the most space, honouring any restrictions that were set + with setAllowedPlacement(). + */ + void setPosition (Component* componentToPointTo); + + /** Moves and resizes the bubble to point at a given point. + + This will resize the bubble to fit its content, then position it + so that the tip of the bubble points to the given co-ordinate. The co-ordinates + are relative to either the bubble component's parent component if it has one, or + they are screen co-ordinates if not. + + It'll put itself either above, below, or to the side of this point, depending + on where there's the most space, honouring any restrictions that were set + with setAllowedPlacement(). + */ + void setPosition (const int arrowTipX, + const int arrowTipY); + + /** Moves and resizes the bubble to point at a given rectangle. + + This will resize the bubble to fit its content, then find a position for it + so that it's next to, but doesn't overlap the given rectangle. The rectangle's + co-ordinates are relative to either the bubble component's parent component + if it has one, or they are screen co-ordinates if not. + + It'll put itself either above, below, or to the side of the component depending + on where there's the most space, honouring any restrictions that were set + with setAllowedPlacement(). + */ + void setPosition (const Rectangle& rectangleToPointTo); + +protected: + + /** Subclasses should override this to return the size of the content they + want to draw inside the bubble. + */ + virtual void getContentSize (int& width, int& height) = 0; + + /** Subclasses should override this to draw their bubble's contents. + + The graphics object's clip region and the dimensions passed in here are + set up to paint just the rectangle inside the bubble. + */ + virtual void paintContent (Graphics& g, int width, int height) = 0; + +public: + + /** @internal */ + void paint (Graphics& g); + + juce_UseDebuggingNewOperator + +private: + Rectangle content; + int side, allowablePlacements; + float arrowTipX, arrowTipY; + DropShadowEffect shadow; + + BubbleComponent (const BubbleComponent&); + const BubbleComponent& operator= (const BubbleComponent&); +}; + +#endif // __JUCE_BUBBLECOMPONENT_JUCEHEADER__ +/********* End of inlined file: juce_BubbleComponent.h *********/ + +#endif +#ifndef __JUCE_BUBBLEMESSAGECOMPONENT_JUCEHEADER__ + +/********* Start of inlined file: juce_BubbleMessageComponent.h *********/ +#ifndef __JUCE_BUBBLEMESSAGECOMPONENT_JUCEHEADER__ +#define __JUCE_BUBBLEMESSAGECOMPONENT_JUCEHEADER__ + +/** + A speech-bubble component that displays a short message. + + This can be used to show a message with the tail of the speech bubble + pointing to a particular component or location on the screen. + + @see BubbleComponent +*/ +class JUCE_API BubbleMessageComponent : public BubbleComponent, + private Timer +{ +public: + + /** Creates a bubble component. + + After creating one a BubbleComponent, do the following: + - add it to an appropriate parent component, or put it on the + desktop with Component::addToDesktop (0). + - use the showAt() method to show a message. + - it will make itself invisible after it times-out (and can optionally + also delete itself), or you can reuse it somewhere else by calling + showAt() again. + */ + BubbleMessageComponent (const int fadeOutLengthMs = 150); + + /** Destructor. */ + ~BubbleMessageComponent(); + + /** Shows a message bubble at a particular position. + + This shows the bubble with its stem pointing to the given location + (co-ordinates being relative to its parent component). + + For details about exactly how it decides where to position itself, see + BubbleComponent::updatePosition(). + + @param x the x co-ordinate of end of the bubble's tail + @param y the y co-ordinate of end of the bubble's tail + @param message the text to display + @param numMillisecondsBeforeRemoving how long to leave it on the screen before removing itself + from its parent compnent. If this is 0 or less, it + will stay there until manually removed. + @param removeWhenMouseClicked if this is true, the bubble will disappear as soon as a + mouse button is pressed (anywhere on the screen) + @param deleteSelfAfterUse if true, then the component will delete itself after + it becomes invisible + */ + void showAt (int x, int y, + const String& message, + const int numMillisecondsBeforeRemoving, + const bool removeWhenMouseClicked = true, + const bool deleteSelfAfterUse = false); + + /** Shows a message bubble next to a particular component. + + This shows the bubble with its stem pointing at the given component. + + For details about exactly how it decides where to position itself, see + BubbleComponent::updatePosition(). + + @param component the component that you want to point at + @param message the text to display + @param numMillisecondsBeforeRemoving how long to leave it on the screen before removing itself + from its parent compnent. If this is 0 or less, it + will stay there until manually removed. + @param removeWhenMouseClicked if this is true, the bubble will disappear as soon as a + mouse button is pressed (anywhere on the screen) + @param deleteSelfAfterUse if true, then the component will delete itself after + it becomes invisible + */ + void showAt (Component* const component, + const String& message, + const int numMillisecondsBeforeRemoving, + const bool removeWhenMouseClicked = true, + const bool deleteSelfAfterUse = false); + + /** @internal */ + void getContentSize (int& w, int& h); + /** @internal */ + void paintContent (Graphics& g, int w, int h); + /** @internal */ + void timerCallback(); + + juce_UseDebuggingNewOperator + +private: + int fadeOutLength, mouseClickCounter; + TextLayout textLayout; + int64 expiryTime; + bool deleteAfterUse; + + void init (const int numMillisecondsBeforeRemoving, + const bool removeWhenMouseClicked, + const bool deleteSelfAfterUse); + + BubbleMessageComponent (const BubbleMessageComponent&); + const BubbleMessageComponent& operator= (const BubbleMessageComponent&); +}; + +#endif // __JUCE_BUBBLEMESSAGECOMPONENT_JUCEHEADER__ +/********* End of inlined file: juce_BubbleMessageComponent.h *********/ + +#endif +#ifndef __JUCE_COLOURSELECTOR_JUCEHEADER__ + +/********* Start of inlined file: juce_ColourSelector.h *********/ +#ifndef __JUCE_COLOURSELECTOR_JUCEHEADER__ +#define __JUCE_COLOURSELECTOR_JUCEHEADER__ + +/** + A component that lets the user choose a colour. + + This shows RGB sliders and a colourspace that the user can pick colours from. + + This class is also a ChangeBroadcaster, so listeners can register to be told + when the colour changes. +*/ +class JUCE_API ColourSelector : public Component, + public ChangeBroadcaster, + protected SliderListener +{ +public: + + /** Options for the type of selector to show. These are passed into the constructor. */ + enum ColourSelectorOptions + { + showAlphaChannel = 1 << 0, /**< if set, the colour's alpha channel can be changed as well as its RGB. */ + + showColourAtTop = 1 << 1, /**< if set, a swatch of the colour is shown at the top of the component. */ + showSliders = 1 << 2, /**< if set, RGB sliders are shown at the bottom of the component. */ + showColourspace = 1 << 3 /**< if set, a big HSV selector is shown. */ + }; + + /** Creates a ColourSelector object. + + The flags are a combination of values from the ColourSelectorOptions enum, specifying + which of the selector's features should be visible. + + The edgeGap value specifies the amount of space to leave around the edge. + + gapAroundColourSpaceComponent indicates how much of a gap to put around the + colourspace and hue selector components. + */ + ColourSelector (const int sectionsToShow = (showAlphaChannel | showColourAtTop | showSliders | showColourspace), + const int edgeGap = 4, + const int gapAroundColourSpaceComponent = 7); + + /** Destructor. */ + ~ColourSelector(); + + /** Returns the colour that the user has currently selected. + + The ColourSelector class is also a ChangeBroadcaster, so listeners can + register to be told when the colour changes. + + @see setCurrentColour + */ + const Colour getCurrentColour() const; + + /** Changes the colour that is currently being shown. + */ + void setCurrentColour (const Colour& newColour); + + /** Tells the selector how many preset colour swatches you want to have on the component. + + To enable swatches, you'll need to override getNumSwatches(), getSwatchColour(), and + setSwatchColour(), to return the number of colours you want, and to set and retrieve + their values. + */ + virtual int getNumSwatches() const; + + /** Called by the selector to find out the colour of one of the swatches. + + Your subclass should return the colour of the swatch with the given index. + + To enable swatches, you'll need to override getNumSwatches(), getSwatchColour(), and + setSwatchColour(), to return the number of colours you want, and to set and retrieve + their values. + */ + virtual const Colour getSwatchColour (const int index) const; + + /** Called by the selector when the user puts a new colour into one of the swatches. + + Your subclass should change the colour of the swatch with the given index. + + To enable swatches, you'll need to override getNumSwatches(), getSwatchColour(), and + setSwatchColour(), to return the number of colours you want, and to set and retrieve + their values. + */ + virtual void setSwatchColour (const int index, const Colour& newColour) const; + + /** A set of colour IDs to use to change the colour of various aspects of the keyboard. + + These constants can be used either via the Component::setColour(), or LookAndFeel::setColour() + methods. + + @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour + */ + enum ColourIds + { + backgroundColourId = 0x1007000, /**< the colour used to fill the component's background. */ + labelTextColourId = 0x1007001 /**< the colour used for the labels next to the sliders. */ + }; + + juce_UseDebuggingNewOperator + +private: + friend class ColourSpaceView; + friend class HueSelectorComp; + Colour colour; + float h, s, v; + Slider* sliders[4]; + Component* colourSpace; + Component* hueSelector; + VoidArray swatchComponents; + const int flags; + int topSpace, edgeGap; + + void setHue (float newH); + void setSV (float newS, float newV); + void updateHSV(); + void update(); + void sliderValueChanged (Slider*); + void paint (Graphics& g); + void resized(); + + ColourSelector (const ColourSelector&); + const ColourSelector& operator= (const ColourSelector&); + + // this constructor is here temporarily to prevent old code compiling, because the parameters + // have changed - if you get an error here, update your code to use the new constructor instead.. + // (xxx - note to self: remember to remove this at some point in the future) + ColourSelector (const bool); +}; + +#endif // __JUCE_COLOURSELECTOR_JUCEHEADER__ +/********* End of inlined file: juce_ColourSelector.h *********/ + +#endif +#ifndef __JUCE_DROPSHADOWER_JUCEHEADER__ + +#endif +#ifndef __JUCE_MAGNIFIERCOMPONENT_JUCEHEADER__ + +/********* Start of inlined file: juce_MagnifierComponent.h *********/ +#ifndef __JUCE_MAGNIFIERCOMPONENT_JUCEHEADER__ +#define __JUCE_MAGNIFIERCOMPONENT_JUCEHEADER__ + +/** + A component that contains another component, and can magnify or shrink it. + + This component will continually update its size so that it fits the zoomed + version of the content component that you put inside it, so don't try to + change the size of this component directly - instead change that of the + content component. + + To make it all work, the magnifier uses extremely cunning ComponentPeer tricks + to remap mouse events correctly. This means that the content component won't + appear to be a direct child of this component, and instead will think its + on the desktop. +*/ +class JUCE_API MagnifierComponent : public Component +{ +public: + + /** Creates a MagnifierComponent. + + This component will continually update its size so that it fits the zoomed + version of the content component that you put inside it, so don't try to + change the size of this component directly - instead change that of the + content component. + + @param contentComponent the component to add as the magnified one + @param deleteContentCompWhenNoLongerNeeded if true, the content component will + be deleted when this component is deleted. If false, + it's the caller's responsibility to delete it later. + */ + MagnifierComponent (Component* const contentComponent, + const bool deleteContentCompWhenNoLongerNeeded); + + /** Destructor. */ + ~MagnifierComponent(); + + /** Returns the current content component. */ + Component* getContentComponent() const throw() { return content; } + + /** Changes the zoom level. + + The scale factor must be greater than zero. Values less than 1 will shrink the + image; values greater than 1 will multiply its size by this amount. + + When this is called, this component will change its size to fit the full extent + of the newly zoomed content. + */ + void setScaleFactor (double newScaleFactor); + + /** Returns the current zoom factor. */ + double getScaleFactor() const throw() { return scaleFactor; } + + juce_UseDebuggingNewOperator + + /** @internal */ + void childBoundsChanged (Component*); + +private: + Component* content; + Component* holderComp; + double scaleFactor; + ComponentPeer* peer; + bool deleteContent; + + void paint (Graphics& g); + void mouseDown (const MouseEvent& e); + void mouseUp (const MouseEvent& e); + void mouseDrag (const MouseEvent& e); + void mouseMove (const MouseEvent& e); + void mouseEnter (const MouseEvent& e); + void mouseExit (const MouseEvent& e); + void mouseWheelMove (const MouseEvent& e, float, float); + + int scaleInt (const int n) const throw(); + + MagnifierComponent (const MagnifierComponent&); + const MagnifierComponent& operator= (const MagnifierComponent&); +}; + +#endif // __JUCE_MAGNIFIERCOMPONENT_JUCEHEADER__ +/********* End of inlined file: juce_MagnifierComponent.h *********/ + +#endif +#ifndef __JUCE_MIDIKEYBOARDCOMPONENT_JUCEHEADER__ + +/********* Start of inlined file: juce_MidiKeyboardComponent.h *********/ +#ifndef __JUCE_MIDIKEYBOARDCOMPONENT_JUCEHEADER__ +#define __JUCE_MIDIKEYBOARDCOMPONENT_JUCEHEADER__ + +/** + A component that displays a piano keyboard, whose notes can be clicked on. + + This component will mimic a physical midi keyboard, showing the current state of + a MidiKeyboardState object. When the on-screen keys are clicked on, it will play these + notes by calling the noteOn() and noteOff() methods of its MidiKeyboardState object. + + Another feature is that the computer keyboard can also be used to play notes. By + default it maps the top two rows of a standard querty keyboard to the notes, but + these can be remapped if needed. It will only respond to keypresses when it has + the keyboard focus, so to disable this feature you can call setWantsKeyboardFocus (false). + + The component is also a ChangeBroadcaster, so if you want to be informed when the + keyboard is scrolled, you can register a ChangeListener for callbacks. + + @see MidiKeyboardState +*/ +class JUCE_API MidiKeyboardComponent : public Component, + public MidiKeyboardStateListener, + public ChangeBroadcaster, + private Timer, + private AsyncUpdater +{ +public: + + /** The direction of the keyboard. + + @see setOrientation + */ + enum Orientation + { + horizontalKeyboard, + verticalKeyboardFacingLeft, + verticalKeyboardFacingRight, + }; + + /** Creates a MidiKeyboardComponent. + + @param state the midi keyboard model that this component will represent + @param orientation whether the keyboard is horizonal or vertical + */ + MidiKeyboardComponent (MidiKeyboardState& state, + const Orientation orientation); + + /** Destructor. */ + ~MidiKeyboardComponent(); + + /** Changes the velocity used in midi note-on messages that are triggered by clicking + on the component. + + Values are 0 to 1.0, where 1.0 is the heaviest. + + @see setMidiChannel + */ + void setVelocity (const float velocity); + + /** Changes the midi channel number that will be used for events triggered by clicking + on the component. + + The channel must be between 1 and 16 (inclusive). This is the channel that will be + passed on to the MidiKeyboardState::noteOn() method when the user clicks the component. + + Although this is the channel used for outgoing events, the component can display + incoming events from more than one channel - see setMidiChannelsToDisplay() + + @see setVelocity + */ + void setMidiChannel (const int midiChannelNumber); + + /** Returns the midi channel that the keyboard is using for midi messages. + + @see setMidiChannel + */ + int getMidiChannel() const throw() { return midiChannel; } + + /** Sets a mask to indicate which incoming midi channels should be represented by + key movements. + + The mask is a set of bits, where bit 0 = midi channel 1, bit 1 = midi channel 2, etc. + + If the MidiKeyboardState has a key down for any of the channels whose bits are set + in this mask, the on-screen keys will also go down. + + By default, this mask is set to 0xffff (all channels displayed). + + @see setMidiChannel + */ + void setMidiChannelsToDisplay (const int midiChannelMask); + + /** Returns the current set of midi channels represented by the component. + + This is the value that was set with setMidiChannelsToDisplay(). + */ + int getMidiChannelsToDisplay() const throw() { return midiInChannelMask; } + + /** Changes the width used to draw the white keys. */ + void setKeyWidth (const float widthInPixels); + + /** Returns the width that was set by setKeyWidth(). */ + float getKeyWidth() const throw() { return keyWidth; } + + /** Changes the keyboard's current direction. */ + void setOrientation (const Orientation newOrientation); + + /** Returns the keyboard's current direction. */ + const Orientation getOrientation() const throw() { return orientation; } + + /** Sets the range of midi notes that the keyboard will be limited to. + + By default the range is 0 to 127 (inclusive), but you can limit this if you + only want a restricted set of the keys to be shown. + + Note that the values here are inclusive and must be between 0 and 127. + */ + void setAvailableRange (const int lowestNote, + const int highestNote); + + /** Returns the first note in the available range. + + @see setAvailableRange + */ + int getRangeStart() const throw() { return rangeStart; } + + /** Returns the last note in the available range. + + @see setAvailableRange + */ + int getRangeEnd() const throw() { return rangeEnd; } + + /** If the keyboard extends beyond the size of the component, this will scroll + it to show the given key at the start. + + Whenever the keyboard's position is changed, this will use the ChangeBroadcaster + base class to send a callback to any ChangeListeners that have been registered. + */ + void setLowestVisibleKey (int noteNumber); + + /** Returns the number of the first key shown in the component. + + @see setLowestVisibleKey + */ + int getLowestVisibleKey() const throw() { return firstKey; } + + /** If set to true, then scroll buttons will appear at either end of the keyboard + if there are too many notes to fit them all in the component at once. + */ + void setScrollButtonsVisible (const bool canScroll); + + /** A set of colour IDs to use to change the colour of various aspects of the keyboard. + + These constants can be used either via the Component::setColour(), or LookAndFeel::setColour() + methods. + + @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour + */ + enum ColourIds + { + whiteNoteColourId = 0x1005000, + blackNoteColourId = 0x1005001, + keySeparatorLineColourId = 0x1005002, + mouseOverKeyOverlayColourId = 0x1005003, /**< This colour will be overlaid on the normal note colour. */ + keyDownOverlayColourId = 0x1005004, /**< This colour will be overlaid on the normal note colour. */ + textLabelColourId = 0x1005005, + upDownButtonBackgroundColourId = 0x1005006, + upDownButtonArrowColourId = 0x1005007 + }; + + /** Returns the position within the component of the left-hand edge of a key. + + Depending on the keyboard's orientation, this may be a horizontal or vertical + distance, in either direction. + */ + int getKeyStartPosition (const int midiNoteNumber) const; + + /** Deletes all key-mappings. + + @see setKeyPressForNote + */ + void clearKeyMappings(); + + /** Maps a key-press to a given note. + + @param key the key that should trigger the note + @param midiNoteOffsetFromC how many semitones above C the triggered note should + be. The actual midi note that gets played will be + this value + (12 * the current base octave). To change + the base octave, see setKeyPressBaseOctave() + */ + void setKeyPressForNote (const KeyPress& key, + const int midiNoteOffsetFromC); + + /** Removes any key-mappings for a given note. + + For a description of what the note number means, see setKeyPressForNote(). + */ + void removeKeyPressForNote (const int midiNoteOffsetFromC); + + /** Changes the base note above which key-press-triggered notes are played. + + The set of key-mappings that trigger notes can be moved up and down to cover + the entire scale using this method. + + The value passed in is an octave number between 0 and 10 (inclusive), and + indicates which C is the base note to which the key-mapped notes are + relative. + */ + void setKeyPressBaseOctave (const int newOctaveNumber); + + /** This sets the octave number which is shown as the octave number for middle C. + + This affects only the default implementation of getWhiteNoteText(), which + passes this octave number to MidiMessage::getMidiNoteName() in order to + get the note text. See MidiMessage::getMidiNoteName() for more info about + the parameter. + + By default this value is set to 3. + + @see getOctaveForMiddleC + */ + void setOctaveForMiddleC (const int octaveNumForMiddleC) throw(); + + /** This returns the value set by setOctaveForMiddleC(). + @see setOctaveForMiddleC + */ + int getOctaveForMiddleC() const throw() { return octaveNumForMiddleC; } + + /** @internal */ + void paint (Graphics& g); + /** @internal */ + void resized(); + /** @internal */ + void mouseMove (const MouseEvent& e); + /** @internal */ + void mouseDrag (const MouseEvent& e); + /** @internal */ + void mouseDown (const MouseEvent& e); + /** @internal */ + void mouseUp (const MouseEvent& e); + /** @internal */ + void mouseEnter (const MouseEvent& e); + /** @internal */ + void mouseExit (const MouseEvent& e); + /** @internal */ + void mouseWheelMove (const MouseEvent& e, float wheelIncrementX, float wheelIncrementY); + /** @internal */ + void timerCallback(); + /** @internal */ + bool keyStateChanged(); + /** @internal */ + void focusLost (FocusChangeType cause); + /** @internal */ + void handleNoteOn (MidiKeyboardState* source, int midiChannel, int midiNoteNumber, float velocity); + /** @internal */ + void handleNoteOff (MidiKeyboardState* source, int midiChannel, int midiNoteNumber); + /** @internal */ + void handleAsyncUpdate(); + /** @internal */ + void colourChanged(); + + juce_UseDebuggingNewOperator + +protected: + friend class MidiKeyboardUpDownButton; + + /** Draws a white note in the given rectangle. + + isOver indicates whether the mouse is over the key, isDown indicates whether the key is + currently pressed down. + + When doing this, be sure to note the keyboard's orientation. + */ + virtual void drawWhiteNote (int midiNoteNumber, + Graphics& g, + int x, int y, int w, int h, + bool isDown, bool isOver, + const Colour& lineColour, + const Colour& textColour); + + /** Draws a black note in the given rectangle. + + isOver indicates whether the mouse is over the key, isDown indicates whether the key is + currently pressed down. + + When doing this, be sure to note the keyboard's orientation. + */ + virtual void drawBlackNote (int midiNoteNumber, + Graphics& g, + int x, int y, int w, int h, + bool isDown, bool isOver, + const Colour& noteFillColour); + + /** Allows text to be drawn on the white notes. + + By default this is used to label the C in each octave, but could be used for other things. + + @see setOctaveForMiddleC + */ + virtual const String getWhiteNoteText (const int midiNoteNumber); + + /** Draws the up and down buttons that change the base note. */ + virtual void drawUpDownButton (Graphics& g, int w, int h, + const bool isMouseOver, + const bool isButtonPressed, + const bool movesOctavesUp); + + /** Callback when the mouse is clicked on a key. + + You could use this to do things like handle right-clicks on keys, etc. + + Return true if you want the click to trigger the note, or false if you + want to handle it yourself and not have the note played. + + @see mouseDraggedToKey + */ + virtual bool mouseDownOnKey (int midiNoteNumber, const MouseEvent& e); + + /** Callback when the mouse is dragged from one key onto another. + + @see mouseDownOnKey + */ + virtual void mouseDraggedToKey (int midiNoteNumber, const MouseEvent& e); + + /** Calculates the positon of a given midi-note. + + This can be overridden to create layouts with custom key-widths. + + @param midiNoteNumber the note to find + @param keyWidth the desired width in pixels of one key - see setKeyWidth() + @param x the x position of the left-hand edge of the key (this method + always works in terms of a horizontal keyboard) + @param w the width of the key + */ + virtual void getKeyPosition (int midiNoteNumber, float keyWidth, + int& x, int& w) const; + +private: + + MidiKeyboardState& state; + int xOffset, blackNoteLength; + float keyWidth; + Orientation orientation; + + int midiChannel, midiInChannelMask; + float velocity; + int noteUnderMouse, mouseDownNote; + BitArray keysPressed, keysCurrentlyDrawnDown; + + int rangeStart, rangeEnd, firstKey; + bool canScroll, mouseDragging; + Button* scrollDown; + Button* scrollUp; + + Array keyPresses; + Array keyPressNotes; + int keyMappingOctave; + int octaveNumForMiddleC; + + void getKeyPos (int midiNoteNumber, int& x, int& w) const; + int xyToNote (int x, int y); + int remappedXYToNote (int x, int y) const; + void resetAnyKeysInUse(); + void updateNoteUnderMouse (int x, int y); + void repaintNote (const int midiNoteNumber); + + MidiKeyboardComponent (const MidiKeyboardComponent&); + const MidiKeyboardComponent& operator= (const MidiKeyboardComponent&); +}; + +#endif // __JUCE_MIDIKEYBOARDCOMPONENT_JUCEHEADER__ +/********* End of inlined file: juce_MidiKeyboardComponent.h *********/ + +#endif +#ifndef __JUCE_OPENGLCOMPONENT_JUCEHEADER__ + +/********* Start of inlined file: juce_OpenGLComponent.h *********/ +#ifndef __JUCE_OPENGLCOMPONENT_JUCEHEADER__ +#define __JUCE_OPENGLCOMPONENT_JUCEHEADER__ + +// this is used to disable OpenGL, and is defined in juce_Config.h +#if JUCE_OPENGL || DOXYGEN + +class OpenGLComponentWatcher; + +/** + Represents the various properties of an OpenGL bitmap format. + + @see OpenGLComponent::setPixelFormat +*/ +struct OpenGLPixelFormat +{ + + /** Creates an OpenGLPixelFormat. + + The default constructor just initialises the object as a simple 8-bit + RGBA format. + */ + OpenGLPixelFormat (const int bitsPerRGBComponent = 8, + const int alphaBits = 8, + const int depthBufferBits = 16, + const int stencilBufferBits = 0) throw(); + + int redBits; /**< The number of bits per pixel to use for the red channel. */ + int greenBits; /**< The number of bits per pixel to use for the green channel. */ + int blueBits; /**< The number of bits per pixel to use for the blue channel. */ + int alphaBits; /**< The number of bits per pixel to use for the alpha channel. */ + + int depthBufferBits; /**< The number of bits per pixel to use for a depth buffer. */ + int stencilBufferBits; /**< The number of bits per pixel to use for a stencil buffer. */ + + int accumulationBufferRedBits; /**< The number of bits per pixel to use for an accumulation buffer's red channel. */ + int accumulationBufferGreenBits; /**< The number of bits per pixel to use for an accumulation buffer's green channel. */ + int accumulationBufferBlueBits; /**< The number of bits per pixel to use for an accumulation buffer's blue channel. */ + int accumulationBufferAlphaBits; /**< The number of bits per pixel to use for an accumulation buffer's alpha channel. */ + + uint8 fullSceneAntiAliasingNumSamples; /**< The number of samples to use in full-scene anti-aliasing (if available). */ + + /** Returns a list of all the pixel formats that can be used in this system. + + A reference component is needed in case there are multiple screens with different + capabilities - in which case, the one that the component is on will be used. + */ + static void getAvailablePixelFormats (Component* component, + OwnedArray & results); + + bool operator== (const OpenGLPixelFormat&) const throw(); + + juce_UseDebuggingNewOperator +}; + +/** + A base class for types of OpenGL context. + + An OpenGLComponent will supply its own context for drawing in its window. +*/ +class OpenGLContext +{ +public: + + /** Destructor. */ + virtual ~OpenGLContext(); + + /** Makes this context the currently active one. */ + virtual bool makeActive() const throw() = 0; + /** If this context is currently active, it is disactivated. */ + virtual bool makeInactive() const throw() = 0; + /** Returns true if this context is currently active. */ + virtual bool isActive() const throw() = 0; + + /** Swaps the buffers (if the context can do this). */ + virtual void swapBuffers() = 0; + + /** Sets whether the context checks the vertical sync before swapping. + + The value is the number of frames to allow between buffer-swapping. This is + fairly system-dependent, but 0 turns off syncing, 1 makes it swap on frame-boundaries, + and greater numbers indicate that it should swap less often. + + Returns true if it sets the value successfully. + */ + virtual bool setSwapInterval (const int numFramesPerSwap) = 0; + + /** Returns the current swap-sync interval. + See setSwapInterval() for info about the value returned. + */ + virtual int getSwapInterval() const = 0; + + /** Returns the pixel format being used by this context. */ + virtual const OpenGLPixelFormat getPixelFormat() const = 0; + + /** For windowed contexts, this moves the context within the bounds of + its parent window. + */ + virtual void updateWindowPosition (int x, int y, int w, int h, int outerWindowHeight) = 0; + + /** For windowed contexts, this triggers a repaint of the window. + + (Not relevent on all platforms). + */ + virtual void repaint() = 0; + + /** Returns an OS-dependent handle to the raw GL context. + + On win32, this will be a HGLRC; on the Mac, an AGLContext; on Linux, + a GLXContext. + */ + virtual void* getRawContext() const throw() = 0; + + /** This tries to create a context that can be used for drawing into the + area occupied by the specified component. + + Note that you probably shouldn't use this method directly unless you know what + you're doing - the OpenGLComponent calls this and manages the context for you. + */ + static OpenGLContext* createContextForWindow (Component* componentToDrawTo, + const OpenGLPixelFormat& pixelFormat, + const OpenGLContext* const contextToShareWith); + + /** Returns the context that's currently in active use by the calling thread. + + Returns 0 if there isn't an active context. + */ + static OpenGLContext* getCurrentContext(); + + juce_UseDebuggingNewOperator + +protected: + OpenGLContext() throw(); +}; + +/** + A component that contains an OpenGL canvas. + + Override this, add it to whatever component you want to, and use the renderOpenGL() + method to draw its contents. + +*/ +class JUCE_API OpenGLComponent : public Component +{ +public: + + /** Creates an OpenGLComponent. + */ + OpenGLComponent(); + + /** Destructor. */ + ~OpenGLComponent(); + + /** Changes the pixel format used by this component. + + @see OpenGLPixelFormat::getAvailablePixelFormats() + */ + void setPixelFormat (const OpenGLPixelFormat& formatToUse); + + /** Returns the pixel format that this component is currently using. */ + const OpenGLPixelFormat getPixelFormat() const; + + /** Specifies an OpenGL context which should be shared with the one that this + component is using. + + This is an OpenGL feature that lets two contexts share their texture data. + + Note that this pointer is stored by the component, and when the component + needs to recreate its internal context for some reason, the same context + will be used again to share lists. So if you pass a context in here, + don't delete the context while this component is still using it! You can + call shareWith (0) to stop this component from sharing with it. + */ + void shareWith (OpenGLContext* contextToShareListsWith); + + /** Flips the openGL buffers over. */ + void swapBuffers(); + + /** This replaces the normal paint() callback - use it to draw your openGL stuff. + + When this is called, makeCurrentContextActive() will already have been called + for you, so you just need to draw. + */ + virtual void renderOpenGL() = 0; + + /** This method is called when the component creates a new OpenGL context. + + A new context may be created when the component is first used, or when it + is moved to a different window, or when the window is hidden and re-shown, + etc. + + You can use this callback as an opportunity to set up things like textures + that your context needs. + + New contexts are created on-demand by the makeCurrentContextActive() method - so + if the context is deleted, e.g. by changing the pixel format or window, no context + will be created until the next call to makeCurrentContextActive(), which will + synchronously create one and call this method. This means that if you're using + a non-GUI thread for rendering, you can make sure this method is be called by + your renderer thread. + + When this callback happens, the context will already have been made current + using the makeCurrentContextActive() method, so there's no need to call it + again in your code. + */ + virtual void newOpenGLContextCreated() = 0; + + /** Returns the context that will draw into this component. + + This may return 0 if the component is currently invisible or hasn't currently + got a context. The context object can be deleted and a new one created during + the lifetime of this component, and there may be times when it doesn't have one. + + @see newOpenGLContextCreated() + */ + OpenGLContext* getCurrentContext() const throw(); + + /** Makes this component the current openGL context. + + You might want to use this in things like your resize() method, before calling + GL commands. + + If this returns false, then the context isn't active, so you should avoid + making any calls. + + This call may actually create a context if one isn't currently initialised. If + it does this, it will also synchronously call the newOpenGLContextCreated() + method to let you initialise it as necessary. + + @see OpenGLContext::makeActive + */ + bool makeCurrentContextActive(); + + /** Stops the current component being the active OpenGL context. + + This is the opposite of makeCurrentContextActive() + + @see OpenGLContext::makeInactive + */ + void makeCurrentContextInactive(); + + /** Returns true if this component is the active openGL context for the + current thread. + + @see OpenGLContext::isActive + */ + bool isActiveContext() const throw(); + + /** Calls the rendering callback, and swaps the buffers afterwards. + + This is called automatically by paint() when the component needs to be rendered. + + It can be overridden if you need to decouple the rendering from the paint callback + and render with a custom thread. + + Returns true if the operation succeeded. + */ + virtual bool renderAndSwapBuffers(); + + /** This returns a critical section that can be used to lock the current context. + + Because the context that is used by this component can change, e.g. when the + component is shown or hidden, then if you're rendering to it on a background + thread, this allows you to lock the context for the duration of your rendering + routine. + */ + CriticalSection& getContextLock() throw() { return contextLock; } + + /** @internal */ + void paint (Graphics& g); + + juce_UseDebuggingNewOperator + +private: + friend class OpenGLComponentWatcher; + OpenGLComponentWatcher* componentWatcher; + + OpenGLContext* context; + OpenGLContext* contextToShareListsWith; + + CriticalSection contextLock; + OpenGLPixelFormat preferredPixelFormat; + bool needToUpdateViewport; + + void deleteContext(); + void updateContextPosition(); + void internalRepaint (int x, int y, int w, int h); + + OpenGLComponent (const OpenGLComponent&); + const OpenGLComponent& operator= (const OpenGLComponent&); +}; + +#endif +#endif // __JUCE_OPENGLCOMPONENT_JUCEHEADER__ +/********* End of inlined file: juce_OpenGLComponent.h *********/ + +#endif +#ifndef __JUCE_PREFERENCESPANEL_JUCEHEADER__ + +/********* Start of inlined file: juce_PreferencesPanel.h *********/ +#ifndef __JUCE_PREFERENCESPANEL_JUCEHEADER__ +#define __JUCE_PREFERENCESPANEL_JUCEHEADER__ + +/** + A component with a set of buttons at the top for changing between pages of + preferences. + + This is just a handy way of writing a Mac-style preferences panel where you + have a row of buttons along the top for the different preference categories, + each button having an icon above its name. Clicking these will show an + appropriate prefs page below it. + + You can either put one of these inside your own component, or just use the + showInDialogBox() method to show it in a window and run it modally. + + To use it, just add a set of named pages with the addSettingsPage() method, + and implement the createComponentForPage() method to create suitable components + for each of these pages. +*/ +class JUCE_API PreferencesPanel : public Component, + private ButtonListener +{ +public: + + /** Creates an empty panel. + + Use addSettingsPage() to add some pages to it in your constructor. + */ + PreferencesPanel(); + + /** Destructor. */ + ~PreferencesPanel(); + + /** Creates a page using a set of drawables to define the page's icon. + + Note that the other version of this method is much easier if you're using + an image instead of a custom drawable. + + @param pageTitle the name of this preferences page - you'll need to + make sure your createComponentForPage() method creates + a suitable component when it is passed this name + @param normalIcon the drawable to display in the page's button normally + @param overIcon the drawable to display in the page's button when the mouse is over + @param downIcon the drawable to display in the page's button when the button is down + @see DrawableButton + */ + void addSettingsPage (const String& pageTitle, + const Drawable* normalIcon, + const Drawable* overIcon, + const Drawable* downIcon); + + /** Creates a page using a set of drawables to define the page's icon. + + The other version of this method gives you more control over the icon, but this + one is much easier if you're just loading it from a file. + + @param pageTitle the name of this preferences page - you'll need to + make sure your createComponentForPage() method creates + a suitable component when it is passed this name + @param imageData a block of data containing an image file, e.g. a jpeg, png or gif. + For this to look good, you'll probably want to use a nice + transparent png file. + @param imageDataSize the size of the image data, in bytes + */ + void addSettingsPage (const String& pageTitle, + const char* imageData, + const int imageDataSize); + + /** Utility method to display this panel in a DialogWindow. + + Calling this will create a DialogWindow containing this panel with the + given size and title, and will run it modally, returning when the user + closes the dialog box. + */ + void showInDialogBox (const String& dialogtitle, + int dialogWidth, + int dialogHeight, + const Colour& backgroundColour = Colours::white); + + /** Subclasses must override this to return a component for each preferences page. + + The subclass should return a pointer to a new component representing the named + page, which the panel will then display. + + The panel will delete the component later when the user goes to another page + or deletes the panel. + */ + virtual Component* createComponentForPage (const String& pageName) = 0; + + /** Changes the current page being displayed. */ + void setCurrentPage (const String& pageName); + + /** @internal */ + void resized(); + /** @internal */ + void paint (Graphics& g); + /** @internal */ + void buttonClicked (Button* button); + + juce_UseDebuggingNewOperator + +private: + + String currentPageName; + Component* currentPage; + int buttonSize; + + PreferencesPanel (const PreferencesPanel&); + const PreferencesPanel& operator= (const PreferencesPanel&); +}; + +#endif // __JUCE_PREFERENCESPANEL_JUCEHEADER__ +/********* End of inlined file: juce_PreferencesPanel.h *********/ + +#endif +#ifndef __JUCE_QUICKTIMEMOVIECOMPONENT_JUCEHEADER__ + +/********* Start of inlined file: juce_QuickTimeMovieComponent.h *********/ +#ifndef __JUCE_QUICKTIMEMOVIECOMPONENT_JUCEHEADER__ +#define __JUCE_QUICKTIMEMOVIECOMPONENT_JUCEHEADER__ + +// this is used to disable QuickTime, and is defined in juce_Config.h +#if JUCE_QUICKTIME || DOXYGEN + +#if JUCE_WIN32 + + typedef ActiveXControlComponent QTWinBaseClass; +#else + + typedef Component QTWinBaseClass; +#endif + +/** + A window that can play back a QuickTime movie. + +*/ +class JUCE_API QuickTimeMovieComponent : public QTWinBaseClass + #if JUCE_MAC + , private Timer + #endif +{ +public: + + /** Creates a QuickTimeMovieComponent, initially blank. + + Use the loadMovie() method to load a movie once you've added the + component to a window, (or put it on the desktop as a heavyweight window). + Loading a movie when the component isn't visible can cause problems, as + QuickTime needs a window handle to initialise properly. + */ + QuickTimeMovieComponent(); + + /** Destructor. */ + ~QuickTimeMovieComponent(); + + /** Returns true if QT is installed and working on this machine. + */ + static bool isQuickTimeAvailable() throw(); + + /** Tries to load a QuickTime movie into the player. + + It's best to call this function once you've added the component to a window, + (or put it on the desktop as a heavyweight window). Loading a movie when the + component isn't visible can cause problems, because QuickTime needs a window + handle to do its stuff. + + @param movieFile the .mov file to open + @param isControllerVisible whether to show a controller bar at the bottom + @returns true if the movie opens successfully + */ + bool loadMovie (const File& movieFile, + const bool isControllerVisible); + + bool loadMovie (InputStream* movieStream, + const bool isControllerVisible); + + /** Closes the movie, if one is open. */ + void closeMovie(); + + /** Returns the movie file that is currently open. + + If there isn't one, this returns File::nonexistent + */ + const File getCurrentMovieFile() const; + + /** Returns true if there's currently a movie open. */ + bool isMovieOpen() const; + + /** Returns the length of the movie, in seconds. */ + double getMovieDuration() const; + + /** Returns the movie's natural size, in pixels. + + You can use this to resize the component to show the movie at its preferred + scale. + + If no movie is loaded, the size returned will be 0 x 0. + */ + void getMovieNormalSize (int& width, int& height) const; + + /** This will position the component within a given area, keeping its aspect + ratio correct according to the movie's normal size. + + The component will be made as large as it can go within the space, and will + be aligned according to the justification value if this means there are gaps at + the top or sides. + */ + void setBoundsWithCorrectAspectRatio (const Rectangle& spaceToFitWithin, + const RectanglePlacement& placement); + + /** Starts the movie playing. */ + void play(); + + /** Stops the movie playing. */ + void stop(); + + /** Returns true if the movie is currently playing. */ + bool isPlaying() const; + + /** Moves the movie's position back to the start. */ + void goToStart(); + + /** Sets the movie's position to a given time. */ + void setPosition (const double seconds); + + /** Returns the current play position of the movie. */ + double getPosition() const; + + /** Changes the movie playback rate. + + A value of 1 is normal speed, greater values play it proportionately faster, + smaller values play it slower. + */ + void setSpeed (const float newSpeed); + + /** Changes the movie's playback volume. + + @param newVolume the volume in the range 0 (silent) to 1.0 (full) + */ + void setMovieVolume (const float newVolume); + + /** Returns the movie's playback volume. + + @returns the volume in the range 0 (silent) to 1.0 (full) + */ + float getMovieVolume() const; + + /** Tells the movie whether it should loop. */ + void setLooping (const bool shouldLoop); + + /** Returns true if the movie is currently looping. + + @see setLooping + */ + bool isLooping() const; + + /** True if the native QuickTime controller bar is shown in the window. + + @see loadMovie + */ + bool isControllerVisible() const; + + /** @internal */ + void paint (Graphics& g); + /** @internal */ + void parentHierarchyChanged(); + /** @internal */ + void visibilityChanged(); +#if JUCE_MAC + /** @internal */ + void handleMCEvent (void*); + /** @internal */ + void assignMovieToWindow(); + /** @internal */ + void timerCallback(); + /** @internal */ + void moved(); + /** @internal */ + void resized(); +#endif + + juce_UseDebuggingNewOperator + +private: + File movieFile; + bool movieLoaded, controllerVisible; + + void* internal; + +#if JUCE_MAC + void* associatedWindow; + Rectangle lastPositionApplied; + bool controllerAssignedToWindow, reentrant, looping; + void checkWindowAssociation(); +#endif + + void createControlIfNeeded(); + bool isControlCreated() const; + + QuickTimeMovieComponent (const QuickTimeMovieComponent&); + const QuickTimeMovieComponent& operator= (const QuickTimeMovieComponent&); +}; + +#endif +#endif // __JUCE_QUICKTIMEMOVIECOMPONENT_JUCEHEADER__ +/********* End of inlined file: juce_QuickTimeMovieComponent.h *********/ + +#endif +#ifndef __JUCE_SYSTEMTRAYICONCOMPONENT_JUCEHEADER__ + +/********* Start of inlined file: juce_SystemTrayIconComponent.h *********/ +#ifndef __JUCE_SYSTEMTRAYICONCOMPONENT_JUCEHEADER__ +#define __JUCE_SYSTEMTRAYICONCOMPONENT_JUCEHEADER__ + +#if JUCE_WIN32 || JUCE_LINUX + +/** + On Windows only, this component sits in the taskbar tray as a small icon. + + To use it, just create one of these components, but don't attempt to make it + visible, add it to a parent, or put it on the desktop. + + You can then call setIconImage() to create an icon for it in the taskbar. + + To change the icon's tooltip, you can use setIconTooltip(). + + To respond to mouse-events, you can override the normal mouseDown(), + mouseUp(), mouseDoubleClick() and mouseMove() methods, and although the x, y + position will not be valid, you can use this to respond to clicks. Traditionally + you'd use a left-click to show your application's window, and a right-click + to show a pop-up menu. +*/ +class JUCE_API SystemTrayIconComponent : public Component +{ +public: + + SystemTrayIconComponent(); + + /** Destructor. */ + ~SystemTrayIconComponent(); + + /** Changes the image shown in the taskbar. + */ + void setIconImage (const Image& newImage); + + /** Changes the tooltip that Windows shows above the icon. */ + void setIconTooltip (const String& tooltip); + +#if JUCE_LINUX + /** @internal */ + void paint (Graphics& g); +#endif + + juce_UseDebuggingNewOperator + +private: + + SystemTrayIconComponent (const SystemTrayIconComponent&); + const SystemTrayIconComponent& operator= (const SystemTrayIconComponent&); +}; + +#endif +#endif // __JUCE_SYSTEMTRAYICONCOMPONENT_JUCEHEADER__ +/********* End of inlined file: juce_SystemTrayIconComponent.h *********/ + +#endif +#ifndef __JUCE_WEBBROWSERCOMPONENT_JUCEHEADER__ + +/********* Start of inlined file: juce_WebBrowserComponent.h *********/ +#ifndef __JUCE_WEBBROWSERCOMPONENT_JUCEHEADER__ +#define __JUCE_WEBBROWSERCOMPONENT_JUCEHEADER__ + +class WebBrowserComponentInternal; + +/** + A component that displays an embedded web browser. + + The browser itself will be platform-dependent. On the Mac, probably Safari, on + Windows, probably IE. + +*/ +class WebBrowserComponent : public Component +{ +public: + + /** Creates a WebBrowserComponent. + + Once it's created and visible, send the browser to a URL using goToURL(). + */ + WebBrowserComponent(); + + /** Destructor. */ + ~WebBrowserComponent(); + + /** Sends the browser to a particular URL. + + @param url the URL to go to. + @param headers an optional set of parameters to put in the HTTP header. If + you supply this, it should be a set of string in the form + "HeaderKey: HeaderValue" + @param postData an optional block of data that will be attached to the HTTP + POST request + */ + void goToURL (const String& url, + const StringArray* headers = 0, + const MemoryBlock* postData = 0); + + /** Stops the current page loading. + */ + void stop(); + + /** Sends the browser back one page. + */ + void goBack(); + + /** Sends the browser forward one page. + */ + void goForward(); + + /** This callback is called when the browser is about to navigate + to a new location. + + You can override this method to perform some action when the user + tries to go to a particular URL. To allow the operation to carry on, + return true, or return false to stop the navigation happening. + */ + virtual bool pageAboutToLoad (const String& newURL); + + /** @internal */ + void paint (Graphics& g); + /** @internal */ + void moved(); + /** @internal */ + void resized(); + /** @internal */ + void parentHierarchyChanged(); + /** @internal */ + void visibilityChanged(); + + juce_UseDebuggingNewOperator + +private: + WebBrowserComponentInternal* browser; + bool blankPageShown; + + String lastURL; + StringArray lastHeaders; + MemoryBlock lastPostData; + +#if JUCE_MAC + void* associatedWindow; + void updateBrowserPosition(); + void createBrowser(); + void deleteBrowser(); +#endif + + void reloadLastURL(); + void checkWindowAssociation(); + + WebBrowserComponent (const WebBrowserComponent&); + const WebBrowserComponent& operator= (const WebBrowserComponent&); +}; + +#endif // __JUCE_WEBBROWSERCOMPONENT_JUCEHEADER__ +/********* End of inlined file: juce_WebBrowserComponent.h *********/ + +#endif +#ifndef __JUCE_LOOKANDFEEL_JUCEHEADER__ + +/********* Start of inlined file: juce_LookAndFeel.h *********/ +#ifndef __JUCE_LOOKANDFEEL_JUCEHEADER__ +#define __JUCE_LOOKANDFEEL_JUCEHEADER__ + +class ToggleButton; +class TextButton; +class AlertWindow; +class TextLayout; +class ScrollBar; +class BubbleComponent; +class ComboBox; +class Button; +class FilenameComponent; +class DocumentWindow; +class ResizableWindow; +class GroupComponent; +class MenuBarComponent; +class DropShadower; +class GlyphArrangement; +class PropertyComponent; +class TableHeaderComponent; +class Toolbar; +class ToolbarItemComponent; +class PopupMenu; +class ProgressBar; +class FileBrowserComponent; +class DirectoryContentsDisplayComponent; +class FilePreviewComponent; + +/** + LookAndFeel objects define the appearance of all the JUCE widgets, and subclasses + can be used to apply different 'skins' to the application. + +*/ +class JUCE_API LookAndFeel +{ +public: + + /** Creates the default JUCE look and feel. */ + LookAndFeel(); + + /** Destructor. */ + virtual ~LookAndFeel(); + + /** Returns the current default look-and-feel for a component to use when it + hasn't got one explicitly set. + + @see setDefaultLookAndFeel + */ + static LookAndFeel& getDefaultLookAndFeel() throw(); + + /** Changes the default look-and-feel. + + @param newDefaultLookAndFeel the new look-and-feel object to use - if this is + set to 0, it will revert to using the default one. The + object passed-in must be deleted by the caller when + it's no longer needed. + @see getDefaultLookAndFeel + */ + static void setDefaultLookAndFeel (LookAndFeel* newDefaultLookAndFeel) throw(); + + /** Looks for a colour that has been registered with the given colour ID number. + + If a colour has been set for this ID number using setColour(), then it is + returned. If none has been set, it will just return Colours::black. + + The colour IDs for various purposes are stored as enums in the components that + they are relevent to - for an example, see Slider::ColourIds, + Label::ColourIds, TextEditor::ColourIds, TreeView::ColourIds, etc. + + If you're looking up a colour for use in drawing a component, it's usually + best not to call this directly, but to use the Component::findColour() method + instead. That will first check whether a suitable colour has been registered + directly with the component, and will fall-back on calling the component's + LookAndFeel's findColour() method if none is found. + + @see setColour, Component::findColour, Component::setColour + */ + const Colour findColour (const int colourId) const throw(); + + /** Registers a colour to be used for a particular purpose. + + For more details, see the comments for findColour(). + + @see findColour, Component::findColour, Component::setColour + */ + void setColour (const int colourId, const Colour& colour) throw(); + + /** Draws the lozenge-shaped background for a standard button. */ + virtual void drawButtonBackground (Graphics& g, + Button& button, + const Colour& backgroundColour, + bool isMouseOverButton, + bool isButtonDown); + + /** Draws the text for a TextButton. */ + virtual void drawButtonText (Graphics& g, + TextButton& button, + bool isMouseOverButton, + bool isButtonDown); + + /** Draws the contents of a standard ToggleButton. */ + virtual void drawToggleButton (Graphics& g, + ToggleButton& button, + bool isMouseOverButton, + bool isButtonDown); + + virtual void changeToggleButtonWidthToFitText (ToggleButton& button); + + virtual void drawTickBox (Graphics& g, + Component& component, + int x, int y, int w, int h, + const bool ticked, + const bool isEnabled, + const bool isMouseOverButton, + const bool isButtonDown); + + /** Draws the contents of a message box. + */ + virtual void drawAlertBox (Graphics& g, + AlertWindow& alert, + const Rectangle& textArea, + TextLayout& textLayout); + + virtual int getAlertBoxWindowFlags(); + + /** Draws a progress bar. + + If the progress value is less than 0 or greater than 1.0, this should draw a spinning + bar that fills the whole space (i.e. to say that the app is still busy but the progress + isn't known). It can use the current time as a basis for playing an animation. + + (Used by progress bars in AlertWindow). + */ + virtual void drawProgressBar (Graphics& g, ProgressBar& progressBar, + int width, int height, + double progress, const String& textToShow); + + /** Draws one of the buttons on a scrollbar. + + @param g the context to draw into + @param scrollbar the bar itself + @param width the width of the button + @param height the height of the button + @param buttonDirection the direction of the button, where 0 = up, 1 = right, 2 = down, 3 = left + @param isScrollbarVertical true if it's a vertical bar, false if horizontal + @param isMouseOverButton whether the mouse is currently over the button (also true if it's held down) + @param isButtonDown whether the mouse button's held down + */ + virtual void drawScrollbarButton (Graphics& g, + ScrollBar& scrollbar, + int width, int height, + int buttonDirection, + bool isScrollbarVertical, + bool isMouseOverButton, + bool isButtonDown); + + /** Draws the thumb area of a scrollbar. + + @param g the context to draw into + @param scrollbar the bar itself + @param x the x position of the left edge of the thumb area to draw in + @param y the y position of the top edge of the thumb area to draw in + @param width the width of the thumb area to draw in + @param height the height of the thumb area to draw in + @param isScrollbarVertical true if it's a vertical bar, false if horizontal + @param thumbStartPosition for vertical bars, the y co-ordinate of the top of the + thumb, or its x position for horizontal bars + @param thumbSize for vertical bars, the height of the thumb, or its width for + horizontal bars. This may be 0 if the thumb shouldn't be drawn. + @param isMouseOver whether the mouse is over the thumb area, also true if the mouse is + currently dragging the thumb + @param isMouseDown whether the mouse is currently dragging the scrollbar + */ + virtual void drawScrollbar (Graphics& g, + ScrollBar& scrollbar, + int x, int y, + int width, int height, + bool isScrollbarVertical, + int thumbStartPosition, + int thumbSize, + bool isMouseOver, + bool isMouseDown); + + /** Returns the component effect to use for a scrollbar */ + virtual ImageEffectFilter* getScrollbarEffect(); + + /** Returns the minimum length in pixels to use for a scrollbar thumb. */ + virtual int getMinimumScrollbarThumbSize (ScrollBar& scrollbar); + + /** Returns the default thickness to use for a scrollbar. */ + virtual int getDefaultScrollbarWidth(); + + /** Returns the length in pixels to use for a scrollbar button. */ + virtual int getScrollbarButtonSize (ScrollBar& scrollbar); + + /** Returns a tick shape for use in yes/no boxes, etc. */ + virtual const Path getTickShape (const float height); + /** Returns a cross shape for use in yes/no boxes, etc. */ + virtual const Path getCrossShape (const float height); + + /** Draws the + or - box in a treeview. */ + virtual void drawTreeviewPlusMinusBox (Graphics& g, int x, int y, int w, int h, bool isPlus); + + virtual void fillTextEditorBackground (Graphics& g, int width, int height, TextEditor& textEditor); + virtual void drawTextEditorOutline (Graphics& g, int width, int height, TextEditor& textEditor); + + // these return an image from the ImageCache, so use ImageCache::release() to free it + virtual Image* getDefaultFolderImage(); + virtual Image* getDefaultDocumentFileImage(); + + virtual void createFileChooserHeaderText (const String& title, + const String& instructions, + GlyphArrangement& destArrangement, + int width); + + virtual void drawFileBrowserRow (Graphics& g, int width, int height, + const String& filename, Image* icon, + const String& fileSizeDescription, + const String& fileTimeDescription, + const bool isDirectory, + const bool isItemSelected); + + virtual Button* createFileBrowserGoUpButton(); + + virtual void layoutFileBrowserComponent (FileBrowserComponent& browserComp, + DirectoryContentsDisplayComponent* fileListComponent, + FilePreviewComponent* previewComp, + ComboBox* currentPathBox, + TextEditor* filenameBox, + Button* goUpButton); + + virtual void drawBubble (Graphics& g, + float tipX, float tipY, + float boxX, float boxY, float boxW, float boxH); + + /** Fills the background of a popup menu component. */ + virtual void drawPopupMenuBackground (Graphics& g, int width, int height); + + /** Draws one of the items in a popup menu. */ + virtual void drawPopupMenuItem (Graphics& g, + int width, int height, + const bool isSeparator, + const bool isActive, + const bool isHighlighted, + const bool isTicked, + const bool hasSubMenu, + const String& text, + const String& shortcutKeyText, + Image* image, + const Colour* const textColour); + + /** Returns the size and style of font to use in popup menus. */ + virtual const Font getPopupMenuFont(); + + virtual void drawPopupMenuUpDownArrow (Graphics& g, + int width, int height, + bool isScrollUpArrow); + + /** Finds the best size for an item in a popup menu. */ + virtual void getIdealPopupMenuItemSize (const String& text, + const bool isSeparator, + int standardMenuItemHeight, + int& idealWidth, + int& idealHeight); + + virtual int getMenuWindowFlags(); + + virtual void drawMenuBarBackground (Graphics& g, int width, int height, + bool isMouseOverBar, + MenuBarComponent& menuBar); + + virtual int getMenuBarItemWidth (MenuBarComponent& menuBar, int itemIndex, const String& itemText); + + virtual const Font getMenuBarFont (MenuBarComponent& menuBar, int itemIndex, const String& itemText); + + virtual void drawMenuBarItem (Graphics& g, + int width, int height, + int itemIndex, + const String& itemText, + bool isMouseOverItem, + bool isMenuOpen, + bool isMouseOverBar, + MenuBarComponent& menuBar); + + virtual void drawComboBox (Graphics& g, int width, int height, + const bool isButtonDown, + int buttonX, int buttonY, + int buttonW, int buttonH, + ComboBox& box); + + virtual const Font getComboBoxFont (ComboBox& box); + + virtual Label* createComboBoxTextBox (ComboBox& box); + + virtual void drawLinearSlider (Graphics& g, + int x, int y, + int width, int height, + float sliderPos, + float minSliderPos, + float maxSliderPos, + const Slider::SliderStyle style, + Slider& slider); + + virtual void drawLinearSliderBackground (Graphics& g, + int x, int y, + int width, int height, + float sliderPos, + float minSliderPos, + float maxSliderPos, + const Slider::SliderStyle style, + Slider& slider); + + virtual void drawLinearSliderThumb (Graphics& g, + int x, int y, + int width, int height, + float sliderPos, + float minSliderPos, + float maxSliderPos, + const Slider::SliderStyle style, + Slider& slider); + + virtual int getSliderThumbRadius (Slider& slider); + + virtual void drawRotarySlider (Graphics& g, + int x, int y, + int width, int height, + float sliderPosProportional, + const float rotaryStartAngle, + const float rotaryEndAngle, + Slider& slider); + + virtual Button* createSliderButton (const bool isIncrement); + virtual Label* createSliderTextBox (Slider& slider); + + virtual ImageEffectFilter* getSliderEffect(); + + virtual void getTooltipSize (const String& tipText, int& width, int& height); + + virtual void drawTooltip (Graphics& g, const String& text, int width, int height); + + virtual Button* createFilenameComponentBrowseButton (const String& text); + + virtual void layoutFilenameComponent (FilenameComponent& filenameComp, + ComboBox* filenameBox, Button* browseButton); + + virtual void drawCornerResizer (Graphics& g, + int w, int h, + bool isMouseOver, + bool isMouseDragging); + + virtual void drawResizableFrame (Graphics& g, + int w, int h, + const BorderSize& borders); + + virtual void drawResizableWindowBorder (Graphics& g, + int w, int h, + const BorderSize& border, + ResizableWindow& window); + + virtual void drawDocumentWindowTitleBar (DocumentWindow& window, + Graphics& g, int w, int h, + int titleSpaceX, int titleSpaceW, + const Image* icon, + bool drawTitleTextOnLeft); + + virtual Button* createDocumentWindowButton (int buttonType); + + virtual void positionDocumentWindowButtons (DocumentWindow& window, + int titleBarX, int titleBarY, + int titleBarW, int titleBarH, + Button* minimiseButton, + Button* maximiseButton, + Button* closeButton, + bool positionTitleBarButtonsOnLeft); + + virtual int getDefaultMenuBarHeight(); + + virtual DropShadower* createDropShadowerForComponent (Component* component); + + virtual void drawStretchableLayoutResizerBar (Graphics& g, + int w, int h, + bool isVerticalBar, + bool isMouseOver, + bool isMouseDragging); + + virtual void drawGroupComponentOutline (Graphics& g, int w, int h, + const String& text, + const Justification& position, + GroupComponent& group); + + virtual void createTabButtonShape (Path& p, + int width, int height, + int tabIndex, + const String& text, + Button& button, + TabbedButtonBar::Orientation orientation, + const bool isMouseOver, + const bool isMouseDown, + const bool isFrontTab); + + virtual void fillTabButtonShape (Graphics& g, + const Path& path, + const Colour& preferredBackgroundColour, + int tabIndex, + const String& text, + Button& button, + TabbedButtonBar::Orientation orientation, + const bool isMouseOver, + const bool isMouseDown, + const bool isFrontTab); + + virtual void drawTabButtonText (Graphics& g, + int x, int y, int w, int h, + const Colour& preferredBackgroundColour, + int tabIndex, + const String& text, + Button& button, + TabbedButtonBar::Orientation orientation, + const bool isMouseOver, + const bool isMouseDown, + const bool isFrontTab); + + virtual int getTabButtonOverlap (int tabDepth); + virtual int getTabButtonSpaceAroundImage(); + + virtual int getTabButtonBestWidth (int tabIndex, + const String& text, + int tabDepth, + Button& button); + + virtual void drawTabButton (Graphics& g, + int w, int h, + const Colour& preferredColour, + int tabIndex, + const String& text, + Button& button, + TabbedButtonBar::Orientation orientation, + const bool isMouseOver, + const bool isMouseDown, + const bool isFrontTab); + + virtual void drawTabAreaBehindFrontButton (Graphics& g, + int w, int h, + TabbedButtonBar& tabBar, + TabbedButtonBar::Orientation orientation); + + virtual Button* createTabBarExtrasButton(); + + virtual void drawTableHeaderBackground (Graphics& g, TableHeaderComponent& header); + + virtual void drawTableHeaderColumn (Graphics& g, const String& columnName, int columnId, + int width, int height, + bool isMouseOver, bool isMouseDown, + int columnFlags); + + virtual void paintToolbarBackground (Graphics& g, int width, int height, Toolbar& toolbar); + + virtual Button* createToolbarMissingItemsButton (Toolbar& toolbar); + + virtual void paintToolbarButtonBackground (Graphics& g, int width, int height, + bool isMouseOver, bool isMouseDown, + ToolbarItemComponent& component); + + virtual void paintToolbarButtonLabel (Graphics& g, int x, int y, int width, int height, + const String& text, ToolbarItemComponent& component); + + virtual void drawPropertyPanelSectionHeader (Graphics& g, const String& name, + bool isOpen, int width, int height); + + virtual void drawPropertyComponentBackground (Graphics& g, int width, int height, + PropertyComponent& component); + + virtual void drawPropertyComponentLabel (Graphics& g, int width, int height, + PropertyComponent& component); + + virtual const Rectangle getPropertyComponentContentPosition (PropertyComponent& component); + + /** + */ + virtual void playAlertSound(); + + /** Utility function to draw a shiny, glassy circle (for round LED-type buttons). */ + static void drawGlassSphere (Graphics& g, + const float x, const float y, + const float diameter, + const Colour& colour, + const float outlineThickness) throw(); + + static void drawGlassPointer (Graphics& g, + const float x, const float y, + const float diameter, + const Colour& colour, const float outlineThickness, + const int direction) throw(); + + /** Utility function to draw a shiny, glassy oblong (for text buttons). */ + static void drawGlassLozenge (Graphics& g, + const float x, const float y, + const float width, const float height, + const Colour& colour, + const float outlineThickness, + const float cornerSize, + const bool flatOnLeft, const bool flatOnRight, + const bool flatOnTop, const bool flatOnBottom) throw(); + + juce_UseDebuggingNewOperator + +protected: + // xxx the following methods are only here to cause a compiler error, because they've been + // deprecated or their parameters have changed. Hopefully these definitions should cause an + // error if you try to build a subclass with the old versions. + + virtual int drawTickBox (Graphics&, int, int, int, int, bool, const bool, const bool, const bool) { return 0; } + + virtual int drawProgressBar (Graphics&, int, int, int, int, float) { return 0; } + virtual int drawProgressBar (Graphics&, ProgressBar&, int, int, int, int, float) { return 0; } + + virtual void getTabButtonBestWidth (int, const String&, int) {} + +private: + friend void JUCE_PUBLIC_FUNCTION shutdownJuce_GUI(); + static void clearDefaultLookAndFeel() throw(); // called at shutdown + + Array colourIds; + Array colours; + + void drawShinyButtonShape (Graphics& g, + float x, float y, float w, float h, float maxCornerSize, + const Colour& baseColour, + const float strokeWidth, + const bool flatOnLeft, + const bool flatOnRight, + const bool flatOnTop, + const bool flatOnBottom) throw(); + + LookAndFeel (const LookAndFeel&); + const LookAndFeel& operator= (const LookAndFeel&); +}; + +#endif // __JUCE_LOOKANDFEEL_JUCEHEADER__ +/********* End of inlined file: juce_LookAndFeel.h *********/ + +#endif +#ifndef __JUCE_OLDSCHOOLLOOKANDFEEL_JUCEHEADER__ + +/********* Start of inlined file: juce_OldSchoolLookAndFeel.h *********/ +#ifndef __JUCE_OLDSCHOOLLOOKANDFEEL_JUCEHEADER__ +#define __JUCE_OLDSCHOOLLOOKANDFEEL_JUCEHEADER__ + +/** + The original Juce look-and-feel. + +*/ +class JUCE_API OldSchoolLookAndFeel : public LookAndFeel +{ +public: + + /** Creates the default JUCE look and feel. */ + OldSchoolLookAndFeel(); + + /** Destructor. */ + virtual ~OldSchoolLookAndFeel(); + + /** Draws the lozenge-shaped background for a standard button. */ + virtual void drawButtonBackground (Graphics& g, + Button& button, + const Colour& backgroundColour, + bool isMouseOverButton, + bool isButtonDown); + + /** Draws the contents of a standard ToggleButton. */ + virtual void drawToggleButton (Graphics& g, + ToggleButton& button, + bool isMouseOverButton, + bool isButtonDown); + + virtual void drawTickBox (Graphics& g, + Component& component, + int x, int y, int w, int h, + const bool ticked, + const bool isEnabled, + const bool isMouseOverButton, + const bool isButtonDown); + + virtual void drawProgressBar (Graphics& g, ProgressBar& progressBar, + int width, int height, + double progress, const String& textToShow); + + virtual void drawScrollbarButton (Graphics& g, + ScrollBar& scrollbar, + int width, int height, + int buttonDirection, + bool isScrollbarVertical, + bool isMouseOverButton, + bool isButtonDown); + + virtual void drawScrollbar (Graphics& g, + ScrollBar& scrollbar, + int x, int y, + int width, int height, + bool isScrollbarVertical, + int thumbStartPosition, + int thumbSize, + bool isMouseOver, + bool isMouseDown); + + virtual ImageEffectFilter* getScrollbarEffect(); + + virtual void drawTextEditorOutline (Graphics& g, + int width, int height, + TextEditor& textEditor); + + /** Fills the background of a popup menu component. */ + virtual void drawPopupMenuBackground (Graphics& g, int width, int height); + + virtual void drawMenuBarBackground (Graphics& g, int width, int height, + bool isMouseOverBar, + MenuBarComponent& menuBar); + + virtual void drawComboBox (Graphics& g, int width, int height, + const bool isButtonDown, + int buttonX, int buttonY, + int buttonW, int buttonH, + ComboBox& box); + + virtual const Font getComboBoxFont (ComboBox& box); + + virtual void drawLinearSlider (Graphics& g, + int x, int y, + int width, int height, + float sliderPos, + float minSliderPos, + float maxSliderPos, + const Slider::SliderStyle style, + Slider& slider); + + virtual int getSliderThumbRadius (Slider& slider); + + virtual Button* createSliderButton (const bool isIncrement); + + virtual ImageEffectFilter* getSliderEffect(); + + virtual void drawCornerResizer (Graphics& g, + int w, int h, + bool isMouseOver, + bool isMouseDragging); + + virtual Button* createDocumentWindowButton (int buttonType); + + virtual void positionDocumentWindowButtons (DocumentWindow& window, + int titleBarX, int titleBarY, + int titleBarW, int titleBarH, + Button* minimiseButton, + Button* maximiseButton, + Button* closeButton, + bool positionTitleBarButtonsOnLeft); + + juce_UseDebuggingNewOperator + +private: + DropShadowEffect scrollbarShadow; + + OldSchoolLookAndFeel (const OldSchoolLookAndFeel&); + const OldSchoolLookAndFeel& operator= (const OldSchoolLookAndFeel&); +}; + +#endif // __JUCE_OLDSCHOOLLOOKANDFEEL_JUCEHEADER__ +/********* End of inlined file: juce_OldSchoolLookAndFeel.h *********/ + +#endif +#ifndef __JUCE_FILEBASEDDOCUMENT_JUCEHEADER__ + +/********* Start of inlined file: juce_FileBasedDocument.h *********/ +#ifndef __JUCE_FILEBASEDDOCUMENT_JUCEHEADER__ +#define __JUCE_FILEBASEDDOCUMENT_JUCEHEADER__ + +/** + A class to take care of the logic involved with the loading/saving of some kind + of document. + + There's quite a lot of tedious logic involved in writing all the load/save/save-as + functions you need for documents that get saved to a file, so this class attempts + to abstract most of the boring stuff. + + Your subclass should just implement all the pure virtual methods, and you can + then use the higher-level public methods to do the load/save dialogs, to warn the user + about overwriting files, etc. + + The document object keeps track of whether it has changed since it was last saved or + loaded, so when you change something, call its changed() method. This will set a + flag so it knows it needs saving, and will also broadcast a change message using the + ChangeBroadcaster base class. + + @see ChangeBroadcaster +*/ +class JUCE_API FileBasedDocument : public ChangeBroadcaster +{ +public: + /** Creates a FileBasedDocument. + + @param fileExtension the extension to use when loading/saving files, e.g. ".doc" + @param fileWildCard the wildcard to use in file dialogs, e.g. "*.doc" + @param openFileDialogTitle the title to show on an open-file dialog, e.g. "Choose a file to open.." + @param saveFileDialogTitle the title to show on an save-file dialog, e.g. "Choose a file to save as.." + */ + FileBasedDocument (const String& fileExtension, + const String& fileWildCard, + const String& openFileDialogTitle, + const String& saveFileDialogTitle); + + /** Destructor. */ + virtual ~FileBasedDocument(); + + /** Returns true if the changed() method has been called since the file was + last saved or loaded. + + @see resetChangedFlag, changed + */ + bool hasChangedSinceSaved() const throw() { return changedSinceSave; } + + /** Called to indicate that the document has changed and needs saving. + + This method will also trigger a change message to be sent out using the + ChangeBroadcaster base class. + + After calling the method, the hasChangedSinceSaved() method will return true, until + it is reset either by saving to a file or using the resetChangedFlag() method. + + @see hasChangedSinceSaved, resetChangedFlag + */ + virtual void changed(); + + /** Sets the state of the 'changed' flag. + + The 'changed' flag is set to true when the changed() method is called - use this method + to reset it or to set it without also broadcasting a change message. + + @see changed, hasChangedSinceSaved + */ + void setChangedFlag (const bool hasChanged); + + /** Tries to open a file. + + If the file opens correctly, the document's file (see the getFile() method) is set + to this new one; if it fails, the document's file is left unchanged, and optionally + a message box is shown telling the user there was an error. + + @returns true if the new file loaded successfully + @see loadDocument, loadFromUserSpecifiedFile + */ + bool loadFrom (const File& fileToLoadFrom, + const bool showMessageOnFailure); + + /** Asks the user for a file and tries to load it. + + This will pop up a dialog box using the title, file extension and + wildcard specified in the document's constructor, and asks the user + for a file. If they pick one, the loadFrom() method is used to + try to load it, optionally showing a message if it fails. + + @returns true if a file was loaded; false if the user cancelled or if they + picked a file which failed to load correctly + @see loadFrom + */ + bool loadFromUserSpecifiedFile (const bool showMessageOnFailure); + + /** A set of possible outcomes of one of the save() methods + */ + enum SaveResult + { + savedOk = 0, /**< indicates that a file was saved successfully. */ + userCancelledSave, /**< indicates that the user aborted the save operation. */ + failedToWriteToFile /**< indicates that it tried to write to a file but this failed. */ + }; + + /** Tries to save the document to the last file it was saved or loaded from. + + This will always try to write to the file, even if the document isn't flagged as + having changed. + + @param askUserForFileIfNotSpecified if there's no file currently specified and this is + true, it will prompt the user to pick a file, as if + saveAsInteractive() was called. + @param showMessageOnFailure if true it will show a warning message when if the + save operation fails + @see saveIfNeededAndUserAgrees, saveAs, saveAsInteractive + */ + SaveResult save (const bool askUserForFileIfNotSpecified, + const bool showMessageOnFailure); + + /** If the file needs saving, it'll ask the user if that's what they want to do, and save + it if they say yes. + + If you've got a document open and want to close it (e.g. to quit the app), this is the + method to call. + + If the document doesn't need saving it'll return the value savedOk so + you can go ahead and delete the document. + + If it does need saving it'll prompt the user, and if they say "discard changes" it'll + return savedOk, so again, you can safely delete the document. + + If the user clicks "cancel", it'll return userCancelledSave, so if you can abort the + close-document operation. + + And if they click "save changes", it'll try to save and either return savedOk, or + failedToWriteToFile if there was a problem. + + @see save, saveAs, saveAsInteractive + */ + SaveResult saveIfNeededAndUserAgrees(); + + /** Tries to save the document to a specified file. + + If this succeeds, it'll also change the document's internal file (as returned by + the getFile() method). If it fails, the file will be left unchanged. + + @param newFile the file to try to write to + @param warnAboutOverwritingExistingFiles if true and the file exists, it'll ask + the user first if they want to overwrite it + @param askUserForFileIfNotSpecified if the file is non-existent and this is true, it'll + use the saveAsInteractive() method to ask the user for a + filename + @param showMessageOnFailure if true and the write operation fails, it'll show + a message box to warn the user + @see saveIfNeededAndUserAgrees, save, saveAsInteractive + */ + SaveResult saveAs (const File& newFile, + const bool warnAboutOverwritingExistingFiles, + const bool askUserForFileIfNotSpecified, + const bool showMessageOnFailure); + + /** Prompts the user for a filename and tries to save to it. + + This will pop up a dialog box using the title, file extension and + wildcard specified in the document's constructor, and asks the user + for a file. If they pick one, the saveAs() method is used to try to save + to this file. + + @param warnAboutOverwritingExistingFiles if true and the file exists, it'll ask + the user first if they want to overwrite it + @see saveIfNeededAndUserAgrees, save, saveAs + */ + SaveResult saveAsInteractive (const bool warnAboutOverwritingExistingFiles); + + /** Returns the file that this document was last successfully saved or loaded from. + + When the document object is created, this will be set to File::nonexistent. + + It is changed when one of the load or save methods is used, or when setFile() + is used to explicitly set it. + */ + const File getFile() const throw() { return documentFile; } + + /** Sets the file that this document thinks it was loaded from. + + This won't actually load anything - it just changes the file stored internally. + + @see getFile + */ + void setFile (const File& newFile); + +protected: + + /** Overload this to return the title of the document. + + This is used in message boxes, filenames and file choosers, so it should be + something sensible. + */ + virtual const String getDocumentTitle() = 0; + + /** This method should try to load your document from the given file. + + If it fails, it should return an error message. If it succeeds, it should return + an empty string. + */ + virtual const String loadDocument (const File& file) = 0; + + /** This method should try to write your document to the given file. + + If it fails, it should return an error message. If it succeeds, it should return + an empty string. + */ + virtual const String saveDocument (const File& file) = 0; + + /** This is used for dialog boxes to make them open at the last folder you + were using. + + getLastDocumentOpened() and setLastDocumentOpened() are used to store + the last document that was used - you might want to store this value + in a static variable, or even in your application's properties. It should + be a global setting rather than a property of this object. + + This method works very well in conjunction with a RecentlyOpenedFilesList + object to manage your recent-files list. + + As a default value, it's ok to return File::nonexistent, and the document + object will use a sensible one instead. + + @see RecentlyOpenedFilesList + */ + virtual const File getLastDocumentOpened() = 0; + + /** This is used for dialog boxes to make them open at the last folder you + were using. + + getLastDocumentOpened() and setLastDocumentOpened() are used to store + the last document that was used - you might want to store this value + in a static variable, or even in your application's properties. It should + be a global setting rather than a property of this object. + + This method works very well in conjunction with a RecentlyOpenedFilesList + object to manage your recent-files list. + + @see RecentlyOpenedFilesList + */ + virtual void setLastDocumentOpened (const File& file) = 0; + +public: + + juce_UseDebuggingNewOperator + +private: + + File documentFile; + bool changedSinceSave; + String fileExtension, fileWildcard, openFileDialogTitle, saveFileDialogTitle; + + FileBasedDocument (const FileBasedDocument&); + const FileBasedDocument& operator= (const FileBasedDocument&); +}; + +#endif // __JUCE_FILEBASEDDOCUMENT_JUCEHEADER__ +/********* End of inlined file: juce_FileBasedDocument.h *********/ + +#endif +#ifndef __JUCE_RECENTLYOPENEDFILESLIST_JUCEHEADER__ + +/********* Start of inlined file: juce_RecentlyOpenedFilesList.h *********/ +#ifndef __JUCE_RECENTLYOPENEDFILESLIST_JUCEHEADER__ +#define __JUCE_RECENTLYOPENEDFILESLIST_JUCEHEADER__ + +/** + Manages a set of files for use as a list of recently-opened documents. + + This is a handy class for holding your list of recently-opened documents, with + helpful methods for things like purging any non-existent files, automatically + adding them to a menu, and making persistence easy. + + @see File, FileBasedDocument +*/ +class JUCE_API RecentlyOpenedFilesList +{ +public: + + /** Creates an empty list. + */ + RecentlyOpenedFilesList(); + + /** Destructor. */ + ~RecentlyOpenedFilesList(); + + /** Sets a limit for the number of files that will be stored in the list. + + When addFile() is called, then if there is no more space in the list, the + least-recently added file will be dropped. + + @see getMaxNumberOfItems + */ + void setMaxNumberOfItems (const int newMaxNumber); + + /** Returns the number of items that this list will store. + @see setMaxNumberOfItems + */ + int getMaxNumberOfItems() const throw() { return maxNumberOfItems; } + + /** Returns the number of files in the list. + + The most recently added file is always at index 0. + */ + int getNumFiles() const; + + /** Returns one of the files in the list. + + The most recently added file is always at index 0. + */ + const File getFile (const int index) const; + + /** Returns an array of all the absolute pathnames in the list. + */ + const StringArray& getAllFilenames() const throw() { return files; } + + /** Clears all the files from the list. */ + void clear(); + + /** Adds a file to the list. + + The file will be added at index 0. If this file is already in the list, it will + be moved up to index 0, but a file can only appear once in the list. + + If the list already contains the maximum number of items that is permitted, the + least-recently added file will be dropped from the end. + */ + void addFile (const File& file); + + /** Checks each of the files in the list, removing any that don't exist. + + You might want to call this after reloading a list of files, or before putting them + on a menu. + */ + void removeNonExistentFiles(); + + /** Adds entries to a menu, representing each of the files in the list. + + This is handy for creating an "open recent file..." menu in your app. The + menu items are numbered consecutively starting with the baseItemId value, + and can either be added as complete pathnames, or just the last part of the + filename. + + If dontAddNonExistentFiles is true, then each file will be checked and only those + that exist will be added. + + If filesToAvoid is non-zero, then it is considered to be a zero-terminated array of + pointers to file objects. Any files that appear in this list will not be added to the + menu - the reason for this is that you might have a number of files already open, so + might not want these to be shown in the menu. + + It returns the number of items that were added. + */ + int createPopupMenuItems (PopupMenu& menuToAddItemsTo, + const int baseItemId, + const bool showFullPaths, + const bool dontAddNonExistentFiles, + const File** filesToAvoid = 0); + + /** Returns a string that encapsulates all the files in the list. + + The string that is returned can later be passed into restoreFromString() in + order to recreate the list. This is handy for persisting your list, e.g. in + a PropertiesFile object. + + @see restoreFromString + */ + const String toString() const; + + /** Restores the list from a previously stringified version of the list. + + Pass in a stringified version created with toString() in order to persist/restore + your list. + + @see toString + */ + void restoreFromString (const String& stringifiedVersion); + + juce_UseDebuggingNewOperator + +private: + + StringArray files; + int maxNumberOfItems; +}; + +#endif // __JUCE_RECENTLYOPENEDFILESLIST_JUCEHEADER__ +/********* End of inlined file: juce_RecentlyOpenedFilesList.h *********/ + +#endif +#ifndef __JUCE_SELECTEDITEMSET_JUCEHEADER__ + +#endif +#ifndef __JUCE_UNDOABLEACTION_JUCEHEADER__ + +#endif +#ifndef __JUCE_UNDOMANAGER_JUCEHEADER__ + +#endif + +#endif +/********* End of inlined file: juce_app_includes.h *********/ + +#endif + +#if JUCE_MSVC + #pragma warning (pop) + #pragma pack (pop) +#endif + +#if JUCE_MAC + #pragma align=reset +#endif + +END_JUCE_NAMESPACE + +#ifndef DONT_SET_USING_JUCE_NAMESPACE +#ifdef JUCE_NAMESPACE + + // this will obviously save a lot of typing, but can be disabled by + // defining DONT_SET_USING_JUCE_NAMESPACE, in case there are conflicts. + using namespace JUCE_NAMESPACE; + + /* On the Mac, these symbols are defined in the Mac libraries, so + these macros make it easier to reference them without writing out + the namespace every time. + + If you run into difficulties where these macros interfere with the contents + of 3rd party header files, you may need to use the juce_WithoutMacros.h file - see + the comments in that file for more information. + */ + #if JUCE_MAC && ! JUCE_DONT_DEFINE_MACROS + #define Component JUCE_NAMESPACE::Component + #define MemoryBlock JUCE_NAMESPACE::MemoryBlock + #define Point JUCE_NAMESPACE::Point + #define Button JUCE_NAMESPACE::Button + #endif + + /* "Rectangle" is defined in some of the newer windows header files, so this makes + it easier to use the juce version explicitly. + + If you run into difficulties where this macro interferes with other 3rd party header + files, you may need to use the juce_WithoutMacros.h file - see the comments in that + file for more information. + */ + #if JUCE_WIN32 && ! JUCE_DONT_DEFINE_MACROS + #define Rectangle JUCE_NAMESPACE::Rectangle + #endif +#endif +#endif + +/* Easy autolinking to the right JUCE libraries under win32. + + Note that this can be disabled by defining DONT_AUTOLINK_TO_JUCE_LIBRARY before + including this header file. +*/ +#if JUCE_MSVC + + #ifndef DONT_AUTOLINK_TO_JUCE_LIBRARY + + /** If you want your application to link to Juce as a DLL instead of + a static library (on win32), just define the JUCE_DLL macro before + including juce.h + */ + #ifdef JUCE_DLL + #ifdef JUCE_DEBUG + #define AUTOLINKEDLIB "JUCE_debug.lib" + #else + #define AUTOLINKEDLIB "JUCE.lib" + #endif + #else + #ifdef JUCE_DEBUG + #ifdef _WIN64 + #define AUTOLINKEDLIB "jucelib_static_x64_debug.lib" + #else + #define AUTOLINKEDLIB "jucelib_static_Win32_debug.lib" + #endif + #else + #ifdef _WIN64 + #define AUTOLINKEDLIB "jucelib_static_x64.lib" + #else + #define AUTOLINKEDLIB "jucelib_static_Win32.lib" + #endif + #endif + #endif + + #pragma comment(lib, AUTOLINKEDLIB) + + #if ! DONT_LIST_JUCE_AUTOLINKEDLIBS + #pragma message("JUCE! Library to link to: " AUTOLINKEDLIB) + #endif + + // Auto-link the other win32 libs that are needed by library calls.. + #if ! (defined (DONT_AUTOLINK_TO_WIN32_LIBRARIES) || defined (JUCE_DLL)) + +/********* Start of inlined file: juce_win32_AutoLinkLibraries.h *********/ +// Auto-links to various win32 libs that are needed by library calls.. +#pragma comment(lib, "kernel32.lib") +#pragma comment(lib, "user32.lib") +#pragma comment(lib, "shell32.lib") +#pragma comment(lib, "gdi32.lib") +#pragma comment(lib, "vfw32.lib") +#pragma comment(lib, "comdlg32.lib") +#pragma comment(lib, "winmm.lib") +#pragma comment(lib, "wininet.lib") +#pragma comment(lib, "ole32.lib") +#pragma comment(lib, "advapi32.lib") +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "comsupp.lib") + +#if JUCE_OPENGL + #pragma comment(lib, "OpenGL32.Lib") + #pragma comment(lib, "GlU32.Lib") +#endif + +#if JUCE_QUICKTIME + #pragma comment (lib, "QTMLClient.lib") +#endif +/********* End of inlined file: juce_win32_AutoLinkLibraries.h *********/ + + #endif + + #endif + +#endif + +/* + To start a JUCE app, use this macro: START_JUCE_APPLICATION (AppSubClass) where + AppSubClass is the name of a class derived from JUCEApplication. + + See the JUCEApplication class documentation (juce_Application.h) for more details. + +*/ +#if defined (JUCE_GCC) || defined (__MWERKS__) + + #define START_JUCE_APPLICATION(AppClass) \ + int main (int argc, char* argv[]) \ + { \ + return JUCE_NAMESPACE::JUCEApplication::main (argc, argv, new AppClass()); \ + } + +#elif JUCE_WIN32 + + #ifdef _CONSOLE + #define START_JUCE_APPLICATION(AppClass) \ + int main (int argc, char* argv[]) \ + { \ + return JUCE_NAMESPACE::JUCEApplication::main (argc, argv, new AppClass()); \ + } + #elif ! defined (_AFXDLL) + #ifdef _WINDOWS_ + #define START_JUCE_APPLICATION(AppClass) \ + int WINAPI WinMain (HINSTANCE, HINSTANCE, LPSTR commandLine, int) \ + { \ + JUCE_NAMESPACE::String commandLineString (commandLine); \ + return JUCE_NAMESPACE::JUCEApplication::main (commandLineString, new AppClass()); \ + } + #else + #define START_JUCE_APPLICATION(AppClass) \ + int __stdcall WinMain (int, int, const char* commandLine, int) \ + { \ + JUCE_NAMESPACE::String commandLineString (commandLine); \ + return JUCE_NAMESPACE::JUCEApplication::main (commandLineString, new AppClass()); \ + } + #endif + #endif + +#endif + +#endif // __JUCE_JUCEHEADER__ +/********* End of inlined file: juce.h *********/ + +#endif diff --git a/juce_amalgamated.mm b/juce_amalgamated.mm new file mode 100644 index 0000000000..628dd987e6 --- /dev/null +++ b/juce_amalgamated.mm @@ -0,0 +1,10 @@ + +/* + When using XCode, you should add this file to your project + instead of using juce_amalgamated.cpp directly. + + This is because on the Mac we need to force the compiler to + treat the code as mixed C++/objective-C. +*/ + +#include "juce_amalgamated.cpp" diff --git a/src/juce_amalgamated_template.cpp b/src/juce_amalgamated_template.cpp index e51daf1220..ce8a104069 100644 --- a/src/juce_amalgamated_template.cpp +++ b/src/juce_amalgamated_template.cpp @@ -57,9 +57,6 @@ #endif #elif defined (LINUX) - #include "../build/linux/platform_specific_code/linuxincludes.h" - #include - #include FT_FREETYPE_H #else #include diff --git a/src/juce_appframework/audio/plugins/formats/juce_VSTPluginFormat.cpp b/src/juce_appframework/audio/plugins/formats/juce_VSTPluginFormat.cpp index e2a74c0480..8cd6bc6ac4 100644 --- a/src/juce_appframework/audio/plugins/formats/juce_VSTPluginFormat.cpp +++ b/src/juce_appframework/audio/plugins/formats/juce_VSTPluginFormat.cpp @@ -34,7 +34,9 @@ #if JUCE_PLUGINHOST_VST #ifdef _WIN32 + #undef #define _WIN32_WINNT #define _WIN32_WINNT 0x500 + #undef STRICT #define STRICT #include #include